Conflicts: modules/base/tool.gotags/v1.2.0-rc1
| @@ -0,0 +1,17 @@ | |||
| [run] | |||
| init_cmds = [["./gogs", "web"]] | |||
| watch_all = true | |||
| watch_dirs = [ | |||
| "$WORKDIR/conf/locale", | |||
| "$WORKDIR/cmd", | |||
| "$WORKDIR/models", | |||
| "$WORKDIR/modules", | |||
| "$WORKDIR/routers" | |||
| ] | |||
| watch_exts = [".go", ".ini"] | |||
| build_delay = 1500 | |||
| cmds = [ | |||
| ["go", "install"], | |||
| ["go", "build"], | |||
| ["./gogs", "web"] | |||
| ] | |||
| @@ -36,3 +36,4 @@ gogs | |||
| __pycache__ | |||
| *.pem | |||
| output* | |||
| config.codekit | |||
| @@ -1,11 +1,12 @@ | |||
| filesets: | |||
| includes: | |||
| - templates | |||
| - conf | |||
| - etc | |||
| - public | |||
| - scripts | |||
| - templates | |||
| - LICENSE | |||
| - README.md | |||
| - README_ZH.md | |||
| - start.bat | |||
| - start.sh | |||
| excludes: | |||
| - \.git | |||
| @@ -2,24 +2,25 @@ | |||
| path = github.com/gogits/gogs | |||
| [deps] | |||
| github.com/Unknwon/cae = `commit:a1fa53b` | |||
| github.com/Unknwon/com = `commit:019c36f` | |||
| github.com/Unknwon/goconfig = `commit:c4e325f` | |||
| github.com/codegangsta/cli = `commit:bb91895` | |||
| github.com/go-martini/martini = `commit:49411a5` | |||
| github.com/go-sql-driver/mysql = `commit:b44cac6` | |||
| github.com/go-xorm/core = `commit:267e375` | |||
| github.com/go-xorm/xorm = `commit:bd1487b` | |||
| github.com/gogits/cache = `commit:f9bb61f` | |||
| github.com/gogits/gfm = `commit:40f747a` | |||
| github.com/gogits/git = `commit:3d9e771` | |||
| github.com/gogits/logs = `commit:0a97a46` | |||
| github.com/gogits/oauth2 = `commit:99cbec8` | |||
| github.com/gogits/session = `commit:7ab78d4` | |||
| github.com/juju2013/goldap = `commit:f4a7f67` | |||
| github.com/lib/pq = `commit:529edd9` | |||
| github.com/nfnt/resize = `commit:8aee0d9` | |||
| github.com/Unknwon/cae = | |||
| github.com/Unknwon/com = | |||
| github.com/Unknwon/goconfig = | |||
| github.com/Unknwon/i18n = | |||
| github.com/Unknwon/macaron = | |||
| github.com/codegangsta/cli = | |||
| github.com/go-sql-driver/mysql = | |||
| github.com/go-xorm/core = | |||
| github.com/go-xorm/xorm = | |||
| github.com/gogits/cache = | |||
| github.com/gogits/gfm = | |||
| github.com/gogits/git = | |||
| github.com/gogits/oauth2 = | |||
| github.com/juju2013/goldap = | |||
| github.com/lib/pq = | |||
| github.com/macaron-contrib/i18n = | |||
| github.com/macaron-contrib/session = | |||
| github.com/nfnt/resize = | |||
| [res] | |||
| include = templates|public | |||
| include = conf|etc|public|scripts|templates | |||
| @@ -1,11 +1,11 @@ | |||
| Gogs - Go Git Service [](https://app.wercker.com/project/bykey/ad0bdb0bc450ac6f09bc56b9640a50aa) [](https://drone.io/github.com/gogits/gogs/latest) | |||
| ===================== | |||
| Gogs(Go Git Service) is a Self Hosted Git Service in the Go Programming Language. | |||
| Gogs(Go Git Service) is a painless self-hosted Git Service written in Go. | |||
|  | |||
| ##### Current version: 0.4.5 Alpha | |||
| ##### Current version: 0.4.7 Alpha | |||
| ### NOTICES | |||
| @@ -18,9 +18,7 @@ Gogs(Go Git Service) is a Self Hosted Git Service in the Go Programming Language | |||
| ## Purpose | |||
| Since we choose to use pure Go implementation of Git manipulation, Gogs certainly supports **ALL platforms** that Go supports, including Linux, Mac OS X, and Windows with **ZERO** dependency. | |||
| More importantly, Gogs only needs one binary to setup your own project hosting on the fly! | |||
| The goal of this project is to make the easiest, fastest and most painless way to set up a self-hosted Git service. With Go, this can be done in independent binary distribution across **ALL platforms** that Go supports, including Linux, Mac OS X, and Windows. | |||
| ## Overview | |||
| @@ -1,17 +1,15 @@ | |||
| Gogs - Go Git Service [](https://app.wercker.com/project/bykey/ad0bdb0bc450ac6f09bc56b9640a50aa) [](https://drone.io/github.com/gogits/gogs/latest) | |||
| ===================== | |||
| Gogs(Go Git Service) 是一个由 Go 语言编写的自助 Git 托管服务。 | |||
| Gogs(Go Git Service) 是一个基于 Go 语言的自助 Git 服务。 | |||
|  | |||
| ##### 当前版本:0.4.5 Alpha | |||
| ##### 当前版本:0.4.7 Alpha | |||
| ## 开发目的 | |||
| Gogs 完全使用 Go 语言来实现对 Git 数据的操作,实现 **零** 依赖,并且支持 Go 语言所支持的 **所有平台**,包括 Linux、Mac OS X 以及 Windows。 | |||
| 更重要的是,您只需要一个可执行文件就能借助 Gogs 快速搭建属于您自己的代码托管服务! | |||
| Gogs 的目标是打造一个最简单、最快速和最轻松的方式搭建自助 Git 服务。使用 Go 语言开发使得 Gogs 能够通过独立的二进制分发,并且支持 Go 语言支持的 **所有平台**,包括 Linux、Mac OS X 以及 Windows。 | |||
| ## 项目概览 | |||
| @@ -1,23 +0,0 @@ | |||
| { | |||
| "version": 0, | |||
| "gopm": { | |||
| "enable": false, | |||
| "install": false | |||
| }, | |||
| "go_install": true, | |||
| "watch_ext": [], | |||
| "dir_structure": { | |||
| "watch_all": true, | |||
| "controllers": "routers", | |||
| "models": "", | |||
| "others": [ | |||
| "modules", | |||
| "$GOPATH/src/github.com/gogits/logs", | |||
| "$GOPATH/src/github.com/gogits/git" | |||
| ] | |||
| }, | |||
| "cmd_args": [ | |||
| "web" | |||
| ], | |||
| "envs": [] | |||
| } | |||
| @@ -10,11 +10,12 @@ import ( | |||
| "os/exec" | |||
| "path" | |||
| "path/filepath" | |||
| "strconv" | |||
| "strings" | |||
| "github.com/codegangsta/cli" | |||
| "github.com/Unknwon/com" | |||
| "github.com/gogits/gogs/models" | |||
| "github.com/gogits/gogs/modules/log" | |||
| "github.com/gogits/gogs/modules/setting" | |||
| @@ -81,22 +82,22 @@ func runServ(k *cli.Context) { | |||
| keys := strings.Split(os.Args[2], "-") | |||
| if len(keys) != 2 { | |||
| println("Gogs: auth file format error") | |||
| log.GitLogger.Fatal("Invalid auth file format: %s", os.Args[2]) | |||
| log.GitLogger.Fatal(2, "Invalid auth file format: %s", os.Args[2]) | |||
| } | |||
| keyId, err := strconv.ParseInt(keys[1], 10, 64) | |||
| keyId, err := com.StrTo(keys[1]).Int64() | |||
| if err != nil { | |||
| println("Gogs: auth file format error") | |||
| log.GitLogger.Fatal("Invalid auth file format: %v", err) | |||
| log.GitLogger.Fatal(2, "Invalid auth file format: %v", err) | |||
| } | |||
| user, err := models.GetUserByKeyId(keyId) | |||
| if err != nil { | |||
| if err == models.ErrUserNotKeyOwner { | |||
| println("Gogs: you are not the owner of SSH key") | |||
| log.GitLogger.Fatal("Invalid owner of SSH key: %d", keyId) | |||
| log.GitLogger.Fatal(2, "Invalid owner of SSH key: %d", keyId) | |||
| } | |||
| println("Gogs: internal error:", err) | |||
| log.GitLogger.Fatal("Fail to get user by key ID(%d): %v", keyId, err) | |||
| log.GitLogger.Fatal(2, "Fail to get user by key ID(%d): %v", keyId, err) | |||
| } | |||
| cmd := os.Getenv("SSH_ORIGINAL_COMMAND") | |||
| @@ -110,7 +111,7 @@ func runServ(k *cli.Context) { | |||
| rr := strings.SplitN(repoPath, "/", 2) | |||
| if len(rr) != 2 { | |||
| println("Gogs: unavailable repository", args) | |||
| log.GitLogger.Fatal("Unavailable repository: %v", args) | |||
| log.GitLogger.Fatal(2, "Unavailable repository: %v", args) | |||
| } | |||
| repoUserName := rr[0] | |||
| repoName := strings.TrimSuffix(rr[1], ".git") | |||
| @@ -122,10 +123,10 @@ func runServ(k *cli.Context) { | |||
| if err != nil { | |||
| if err == models.ErrUserNotExist { | |||
| println("Gogs: given repository owner are not registered") | |||
| log.GitLogger.Fatal("Unregistered owner: %s", repoUserName) | |||
| log.GitLogger.Fatal(2, "Unregistered owner: %s", repoUserName) | |||
| } | |||
| println("Gogs: internal error:", err) | |||
| log.GitLogger.Fatal("Fail to get repository owner(%s): %v", repoUserName, err) | |||
| log.GitLogger.Fatal(2, "Fail to get repository owner(%s): %v", repoUserName, err) | |||
| } | |||
| // Access check. | |||
| @@ -134,20 +135,20 @@ func runServ(k *cli.Context) { | |||
| has, err := models.HasAccess(user.Name, path.Join(repoUserName, repoName), models.WRITABLE) | |||
| if err != nil { | |||
| println("Gogs: internal error:", err) | |||
| log.GitLogger.Fatal("Fail to check write access:", err) | |||
| log.GitLogger.Fatal(2, "Fail to check write access:", err) | |||
| } else if !has { | |||
| println("You have no right to write this repository") | |||
| log.GitLogger.Fatal("User %s has no right to write repository %s", user.Name, repoPath) | |||
| log.GitLogger.Fatal(2, "User %s has no right to write repository %s", user.Name, repoPath) | |||
| } | |||
| case isRead: | |||
| repo, err := models.GetRepositoryByName(repoUser.Id, repoName) | |||
| if err != nil { | |||
| if err == models.ErrRepoNotExist { | |||
| println("Gogs: given repository does not exist") | |||
| log.GitLogger.Fatal("Repository does not exist: %s/%s", repoUser.Name, repoName) | |||
| log.GitLogger.Fatal(2, "Repository does not exist: %s/%s", repoUser.Name, repoName) | |||
| } | |||
| println("Gogs: internal error:", err) | |||
| log.GitLogger.Fatal("Fail to get repository: %v", err) | |||
| log.GitLogger.Fatal(2, "Fail to get repository: %v", err) | |||
| } | |||
| if !repo.IsPrivate { | |||
| @@ -157,10 +158,10 @@ func runServ(k *cli.Context) { | |||
| has, err := models.HasAccess(user.Name, path.Join(repoUserName, repoName), models.READABLE) | |||
| if err != nil { | |||
| println("Gogs: internal error:", err) | |||
| log.GitLogger.Fatal("Fail to check read access:", err) | |||
| log.GitLogger.Fatal(2, "Fail to check read access:", err) | |||
| } else if !has { | |||
| println("You have no right to access this repository") | |||
| log.GitLogger.Fatal("User %s has no right to read repository %s", user.Name, repoPath) | |||
| log.GitLogger.Fatal(2, "User %s has no right to read repository %s", user.Name, repoPath) | |||
| } | |||
| default: | |||
| println("Unknown command") | |||
| @@ -175,29 +176,27 @@ func runServ(k *cli.Context) { | |||
| gitcmd.Stdout = os.Stdout | |||
| gitcmd.Stdin = os.Stdin | |||
| gitcmd.Stderr = os.Stderr | |||
| err = gitcmd.Run() | |||
| if err != nil { | |||
| println("Gogs: internal error:", err) | |||
| log.GitLogger.Fatal("Fail to execute git command: %v", err) | |||
| if err = gitcmd.Run(); err != nil { | |||
| println("Gogs: internal error:", err.Error()) | |||
| log.GitLogger.Fatal(2, "Fail to execute git command: %v", err) | |||
| } | |||
| if isWrite { | |||
| tasks, err := models.GetUpdateTasksByUuid(uuid) | |||
| if err != nil { | |||
| log.GitLogger.Fatal("Fail to get update task: %v", err) | |||
| log.GitLogger.Fatal(2, "Fail to get update task: %v", err) | |||
| } | |||
| for _, task := range tasks { | |||
| err = models.Update(task.RefName, task.OldCommitId, task.NewCommitId, | |||
| user.Name, repoUserName, repoName, user.Id) | |||
| if err != nil { | |||
| log.GitLogger.Fatal("Fail to update: %v", err) | |||
| log.GitLogger.Fatal(2, "Fail to update: %v", err) | |||
| } | |||
| } | |||
| err = models.DelUpdateTasksByUuid(uuid) | |||
| if err != nil { | |||
| log.GitLogger.Fatal("Fail to del update task: %v", err) | |||
| if err = models.DelUpdateTasksByUuid(uuid); err != nil { | |||
| log.GitLogger.Fatal(2, "Fail to del update task: %v", err) | |||
| } | |||
| } | |||
| } | |||
| @@ -8,6 +8,7 @@ import ( | |||
| "os" | |||
| "github.com/codegangsta/cli" | |||
| "github.com/gogits/gogs/models" | |||
| "github.com/gogits/gogs/modules/log" | |||
| ) | |||
| @@ -30,9 +31,9 @@ func runUpdate(c *cli.Context) { | |||
| args := c.Args() | |||
| if len(args) != 3 { | |||
| log.GitLogger.Fatal("received less 3 parameters") | |||
| log.GitLogger.Fatal(2, "received less 3 parameters") | |||
| } else if args[0] == "" { | |||
| log.GitLogger.Fatal("refName is empty, shouldn't use") | |||
| log.GitLogger.Fatal(2, "refName is empty, shouldn't use") | |||
| } | |||
| uuid := os.Getenv("uuid") | |||
| @@ -45,6 +46,6 @@ func runUpdate(c *cli.Context) { | |||
| } | |||
| if err := models.AddUpdateTask(&task); err != nil { | |||
| log.GitLogger.Fatal(err.Error()) | |||
| log.GitLogger.Fatal(2, err.Error()) | |||
| } | |||
| } | |||
| @@ -12,13 +12,16 @@ import ( | |||
| "os" | |||
| "path" | |||
| "github.com/Unknwon/macaron" | |||
| "github.com/codegangsta/cli" | |||
| "github.com/go-martini/martini" | |||
| "github.com/macaron-contrib/i18n" | |||
| "github.com/macaron-contrib/session" | |||
| "github.com/gogits/gogs/modules/auth" | |||
| "github.com/gogits/gogs/modules/auth/apiv1" | |||
| "github.com/gogits/gogs/modules/avatar" | |||
| "github.com/gogits/gogs/modules/base" | |||
| "github.com/gogits/gogs/modules/captcha" | |||
| "github.com/gogits/gogs/modules/log" | |||
| "github.com/gogits/gogs/modules/middleware" | |||
| "github.com/gogits/gogs/modules/middleware/binding" | |||
| @@ -43,45 +46,55 @@ and it takes care of all the other things for you`, | |||
| // checkVersion checks if binary matches the version of temolate files. | |||
| func checkVersion() { | |||
| data, err := ioutil.ReadFile(path.Join(setting.StaticRootPath, "templates/VERSION")) | |||
| data, err := ioutil.ReadFile(path.Join(setting.StaticRootPath, "templates/.VERSION")) | |||
| if err != nil { | |||
| log.Fatal("Fail to read 'templates/VERSION': %v", err) | |||
| log.Fatal(4, "Fail to read 'templates/.VERSION': %v", err) | |||
| } | |||
| if string(data) != setting.AppVer { | |||
| log.Fatal("Binary and template file version does not match, did you forget to recompile?") | |||
| log.Fatal(4, "Binary and template file version does not match, did you forget to recompile?") | |||
| } | |||
| } | |||
| func newMartini() *martini.ClassicMartini { | |||
| r := martini.NewRouter() | |||
| m := martini.New() | |||
| m.Use(middleware.Logger()) | |||
| m.Use(martini.Recovery()) | |||
| m.Use(middleware.Static("public", | |||
| middleware.StaticOptions{SkipLogging: !setting.DisableRouterLog})) | |||
| m.MapTo(r, (*martini.Routes)(nil)) | |||
| m.Action(r.Handle) | |||
| return &martini.ClassicMartini{m, r} | |||
| // newMacaron initializes Macaron instance. | |||
| func newMacaron() *macaron.Macaron { | |||
| m := macaron.New() | |||
| m.Use(macaron.Logger()) | |||
| m.Use(macaron.Recovery()) | |||
| if setting.EnableGzip { | |||
| m.Use(macaron.Gzip()) | |||
| } | |||
| m.Use(macaron.Static("public", | |||
| macaron.StaticOptions{ | |||
| SkipLogging: !setting.DisableRouterLog, | |||
| }, | |||
| )) | |||
| 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.LocaleOptions{ | |||
| Langs: setting.Langs, | |||
| Names: setting.Names, | |||
| Redirect: true, | |||
| })) | |||
| m.Use(session.Sessioner(session.Options{ | |||
| Provider: setting.SessionProvider, | |||
| Config: *setting.SessionConfig, | |||
| })) | |||
| m.Use(middleware.Contexter()) | |||
| return m | |||
| } | |||
| func runWeb(*cli.Context) { | |||
| routers.GlobalInit() | |||
| checkVersion() | |||
| m := newMartini() | |||
| // Middlewares. | |||
| m.Use(middleware.Renderer(middleware.RenderOptions{ | |||
| Directory: path.Join(setting.StaticRootPath, "templates"), | |||
| Funcs: []template.FuncMap{base.TemplateFuncs}, | |||
| IndentJSON: true, | |||
| })) | |||
| m.Use(middleware.InitContext()) | |||
| m := newMacaron() | |||
| reqSignIn := middleware.Toggle(&middleware.ToggleOptions{SignInRequire: true}) | |||
| ignSignIn := middleware.Toggle(&middleware.ToggleOptions{SignInRequire: setting.Service.RequireSignInView}) | |||
| ignSignInAndCsrf := middleware.Toggle(&middleware.ToggleOptions{DisableCsrf: true}) | |||
| reqSignOut := middleware.Toggle(&middleware.ToggleOptions{SignOutRequire: true}) | |||
| bindIgnErr := binding.BindIgnErr | |||
| @@ -90,14 +103,15 @@ func runWeb(*cli.Context) { | |||
| m.Get("/", ignSignIn, routers.Home) | |||
| m.Get("/install", bindIgnErr(auth.InstallForm{}), routers.Install) | |||
| m.Post("/install", bindIgnErr(auth.InstallForm{}), routers.InstallPost) | |||
| m.Group("", func(r martini.Router) { | |||
| m.Group("", func(r *macaron.Router) { | |||
| r.Get("/issues", user.Issues) | |||
| r.Get("/pulls", user.Pulls) | |||
| r.Get("/stars", user.Stars) | |||
| }, reqSignIn) | |||
| m.Group("/api", func(_ martini.Router) { | |||
| m.Group("/v1", func(r martini.Router) { | |||
| // API routers. | |||
| m.Group("/api", func(_ *macaron.Router) { | |||
| m.Group("/v1", func(r *macaron.Router) { | |||
| // Miscellaneous. | |||
| r.Post("/markdown", bindIgnErr(apiv1.MarkdownForm{}), v1.Markdown) | |||
| r.Post("/markdown/raw", v1.MarkdownRaw) | |||
| @@ -118,41 +132,46 @@ func runWeb(*cli.Context) { | |||
| os.MkdirAll("public/img/avatar/", os.ModePerm) | |||
| m.Get("/avatar/:hash", avt.ServeHTTP) | |||
| m.Group("/user", func(r martini.Router) { | |||
| // User routers. | |||
| m.Group("/user", func(r *macaron.Router) { | |||
| r.Get("/login", user.SignIn) | |||
| r.Post("/login", bindIgnErr(auth.LogInForm{}), user.SignInPost) | |||
| r.Post("/login", bindIgnErr(auth.SignInForm{}), user.SignInPost) | |||
| r.Get("/login/:name", user.SocialSignIn) | |||
| r.Get("/sign_up", user.SignUp) | |||
| r.Post("/sign_up", bindIgnErr(auth.RegisterForm{}), user.SignUpPost) | |||
| r.Get("/reset_password", user.ResetPasswd) | |||
| r.Post("/reset_password", user.ResetPasswdPost) | |||
| }, reqSignOut) | |||
| m.Group("/user", func(r martini.Router) { | |||
| r.Get("/delete", user.Delete) | |||
| r.Post("/delete", user.DeletePost) | |||
| r.Get("/settings", user.Setting) | |||
| r.Post("/settings", bindIgnErr(auth.UpdateProfileForm{}), user.SettingPost) | |||
| m.Group("/user", func(r *macaron.Router) { | |||
| r.Get("/settings", user.Settings) | |||
| r.Post("/settings", bindIgnErr(auth.UpdateProfileForm{}), user.SettingsPost) | |||
| m.Group("/settings", func(r *macaron.Router) { | |||
| r.Get("/password", user.SettingsPassword) | |||
| r.Post("/password", bindIgnErr(auth.ChangePasswordForm{}), user.SettingsPasswordPost) | |||
| r.Get("/ssh", user.SettingsSSHKeys) | |||
| r.Post("/ssh", bindIgnErr(auth.AddSSHKeyForm{}), user.SettingsSSHKeysPost) | |||
| r.Get("/social", user.SettingsSocial) | |||
| r.Get("/orgs", user.SettingsOrgs) | |||
| r.Route("/delete", "GET,POST", user.SettingsDelete) | |||
| }) | |||
| }, reqSignIn) | |||
| m.Group("/user", func(r martini.Router) { | |||
| r.Get("/feeds", binding.Bind(auth.FeedsForm{}), user.Feeds) | |||
| m.Group("/user", func(r *macaron.Router) { | |||
| // r.Get("/feeds", binding.Bind(auth.FeedsForm{}), user.Feeds) | |||
| r.Any("/activate", user.Activate) | |||
| r.Get("/email2user", user.Email2User) | |||
| r.Get("/forget_password", user.ForgotPasswd) | |||
| r.Post("/forget_password", user.ForgotPasswdPost) | |||
| r.Get("/logout", user.SignOut) | |||
| }) | |||
| m.Group("/user/settings", func(r martini.Router) { | |||
| r.Get("/social", user.SettingSocial) | |||
| r.Get("/password", user.SettingPassword) | |||
| r.Post("/password", bindIgnErr(auth.UpdatePasswdForm{}), user.SettingPasswordPost) | |||
| r.Any("/ssh", bindIgnErr(auth.AddSSHKeyForm{}), user.SettingSSHKeys) | |||
| r.Get("/notification", user.SettingNotification) | |||
| r.Get("/security", user.SettingSecurity) | |||
| }, reqSignIn) | |||
| m.Get("/user/:username", ignSignIn, user.Profile) | |||
| m.Get("/user/:username", ignSignIn, user.Profile) // TODO: Legacy | |||
| // Captcha service. | |||
| cpt := captcha.NewCaptcha("/captcha/", setting.Cache) | |||
| m.Map(cpt) | |||
| m.Get("/captcha/*", cpt.Handler) | |||
| m.Group("/repo", func(r martini.Router) { | |||
| m.Group("/repo", func(r *macaron.Router) { | |||
| r.Get("/create", repo.Create) | |||
| r.Post("/create", bindIgnErr(auth.CreateRepoForm{}), repo.CreatePost) | |||
| r.Get("/migrate", repo.Migrate) | |||
| @@ -162,14 +181,14 @@ func runWeb(*cli.Context) { | |||
| adminReq := middleware.Toggle(&middleware.ToggleOptions{SignInRequire: true, AdminRequire: true}) | |||
| m.Get("/admin", adminReq, admin.Dashboard) | |||
| m.Group("/admin", func(r martini.Router) { | |||
| m.Group("/admin", func(r *macaron.Router) { | |||
| r.Get("/users", admin.Users) | |||
| r.Get("/repos", admin.Repositories) | |||
| r.Get("/auths", admin.Auths) | |||
| r.Get("/config", admin.Config) | |||
| r.Get("/monitor", admin.Monitor) | |||
| }, adminReq) | |||
| m.Group("/admin/users", func(r martini.Router) { | |||
| m.Group("/admin/users", func(r *macaron.Router) { | |||
| r.Get("/new", admin.NewUser) | |||
| r.Post("/new", bindIgnErr(auth.RegisterForm{}), admin.NewUserPost) | |||
| r.Get("/:userid", admin.EditUser) | |||
| @@ -177,7 +196,7 @@ func runWeb(*cli.Context) { | |||
| r.Get("/:userid/delete", admin.DeleteUser) | |||
| }, adminReq) | |||
| m.Group("/admin/auths", func(r martini.Router) { | |||
| m.Group("/admin/auths", func(r *macaron.Router) { | |||
| r.Get("/new", admin.NewAuthSource) | |||
| r.Post("/new", bindIgnErr(auth.AuthenticationForm{}), admin.NewAuthSourcePost) | |||
| r.Get("/:authid", admin.EditAuthSource) | |||
| @@ -187,14 +206,15 @@ func runWeb(*cli.Context) { | |||
| m.Get("/:username", ignSignIn, user.Profile) | |||
| if martini.Env == martini.Dev { | |||
| if macaron.Env == macaron.DEV { | |||
| m.Get("/template/**", dev.TemplatePreview) | |||
| dev.RegisterDebugRoutes(m) | |||
| } | |||
| reqTrueOwner := middleware.RequireTrueOwner() | |||
| m.Group("/org", func(r martini.Router) { | |||
| // Organization routers. | |||
| m.Group("/org", func(r *macaron.Router) { | |||
| r.Get("/create", org.New) | |||
| r.Post("/create", bindIgnErr(auth.CreateOrgForm{}), org.NewPost) | |||
| r.Get("/:org", org.Home) | |||
| @@ -213,11 +233,11 @@ func runWeb(*cli.Context) { | |||
| r.Post("/:org/settings/delete", org.DeletePost) | |||
| }, reqSignIn) | |||
| m.Group("/:username/:reponame", func(r martini.Router) { | |||
| m.Group("/:username/:reponame", func(r *macaron.Router) { | |||
| r.Get("/settings", repo.Setting) | |||
| r.Post("/settings", bindIgnErr(auth.RepoSettingForm{}), repo.SettingPost) | |||
| m.Group("/settings", func(r martini.Router) { | |||
| m.Group("/settings", func(r *macaron.Router) { | |||
| r.Get("/collaboration", repo.Collaboration) | |||
| r.Post("/collaboration", repo.CollaborationPost) | |||
| r.Get("/hooks", repo.WebHooks) | |||
| @@ -228,10 +248,10 @@ func runWeb(*cli.Context) { | |||
| }) | |||
| }, reqSignIn, middleware.RepoAssignment(true), reqTrueOwner) | |||
| m.Group("/:username/:reponame", func(r martini.Router) { | |||
| r.Get("/action/:action", repo.Action) | |||
| m.Group("/:username/:reponame", func(r *macaron.Router) { | |||
| // r.Get("/action/:action", repo.Action) | |||
| m.Group("/issues", func(r martini.Router) { | |||
| m.Group("/issues", func(r *macaron.Router) { | |||
| r.Get("/new", repo.CreateIssue) | |||
| r.Post("/new", bindIgnErr(auth.CreateIssueForm{}), repo.CreateIssuePost) | |||
| r.Post("/:index", bindIgnErr(auth.CreateIssueForm{}), repo.UpdateIssue) | |||
| @@ -255,35 +275,36 @@ func runWeb(*cli.Context) { | |||
| r.Get("/releases/edit/:tagname", repo.EditRelease) | |||
| }, reqSignIn, middleware.RepoAssignment(true)) | |||
| m.Group("/:username/:reponame", func(r martini.Router) { | |||
| m.Group("/:username/:reponame", func(r *macaron.Router) { | |||
| r.Post("/releases/new", bindIgnErr(auth.NewReleaseForm{}), repo.NewReleasePost) | |||
| r.Post("/releases/edit/:tagname", bindIgnErr(auth.EditReleaseForm{}), repo.EditReleasePost) | |||
| }, reqSignIn, middleware.RepoAssignment(true, true)) | |||
| m.Group("/:username/:reponame", func(r martini.Router) { | |||
| m.Group("/:username/:reponame", func(r *macaron.Router) { | |||
| r.Get("/issues", repo.Issues) | |||
| r.Get("/issues/:index", repo.ViewIssue) | |||
| r.Get("/pulls", repo.Pulls) | |||
| r.Get("/branches", repo.Branches) | |||
| }, ignSignIn, middleware.RepoAssignment(true)) | |||
| m.Group("/:username/:reponame", func(r martini.Router) { | |||
| r.Get("/src/:branchname", repo.Single) | |||
| r.Get("/src/:branchname/**", repo.Single) | |||
| r.Get("/raw/:branchname/**", repo.SingleDownload) | |||
| m.Group("/:username/:reponame", func(r *macaron.Router) { | |||
| r.Get("/src/:branchname", repo.Home) | |||
| r.Get("/src/:branchname/*", repo.Home) | |||
| r.Get("/raw/:branchname/*", repo.SingleDownload) | |||
| r.Get("/commits/:branchname", repo.Commits) | |||
| r.Get("/commits/:branchname/search", repo.SearchCommits) | |||
| r.Get("/commits/:branchname/**", repo.FileHistory) | |||
| r.Get("/commits/:branchname/*", repo.FileHistory) | |||
| r.Get("/commit/:branchname", repo.Diff) | |||
| r.Get("/commit/:branchname/**", repo.Diff) | |||
| r.Get("/commit/:branchname/*", repo.Diff) | |||
| r.Get("/releases", repo.Releases) | |||
| r.Get("/archive/:branchname/:reponame.zip", repo.ZipDownload) | |||
| r.Get("/archive/:branchname/:reponame.tar.gz", repo.TarGzDownload) | |||
| r.Get("/archive/*.*", repo.Download) | |||
| }, ignSignIn, middleware.RepoAssignment(true, true)) | |||
| m.Group("/:username", func(r martini.Router) { | |||
| r.Get("/:reponame", middleware.RepoAssignment(true, true, true), repo.Single) | |||
| r.Any("/:reponame/**", repo.Http) | |||
| 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) | |||
| // Not found handler. | |||
| @@ -298,10 +319,10 @@ func runWeb(*cli.Context) { | |||
| case setting.HTTPS: | |||
| err = http.ListenAndServeTLS(listenAddr, setting.CertFile, setting.KeyFile, m) | |||
| default: | |||
| log.Fatal("Invalid protocol: %s", setting.Protocol) | |||
| log.Fatal(4, "Invalid protocol: %s", setting.Protocol) | |||
| } | |||
| if err != nil { | |||
| log.Fatal("Fail to start server: %v", err) | |||
| log.Fatal(4, "Fail to start server: %v", err) | |||
| } | |||
| } | |||
| @@ -1,9 +0,0 @@ | |||
| ## NOTICE | |||
| This directory only used for development, and us [go-bindata](https://github.com/jteeuwen/go-bindata) to store in memory for releases. | |||
| To apply any change in this directory, install [go-bindata](https://github.com/jteeuwen/go-bindata), and then execute following command in root of source directory: | |||
| ``` | |||
| $ go-bindata -ignore="\\.DS_Store|README.md" -o modules/bin/conf.go -pkg="bin" conf/... | |||
| ``` | |||
| @@ -28,6 +28,8 @@ KEY_FILE = custom/https/key.pem | |||
| ; Upper level of template and static file path | |||
| ; default is the path where Gogs is executed | |||
| STATIC_ROOT_PATH = | |||
| ; Application level GZIP support | |||
| ENABLE_GZIP = false | |||
| [database] | |||
| ; Either "mysql", "postgres" or "sqlite3", it's your choice | |||
| @@ -125,14 +127,6 @@ SCOPES = all | |||
| AUTH_URL = https://open.t.qq.com/cgi-bin/oauth2/authorize | |||
| TOKEN_URL = https://open.t.qq.com/cgi-bin/oauth2/access_token | |||
| [oauth.twitter] | |||
| ENABLED = false | |||
| CLIENT_ID = | |||
| CLIENT_SECRET = | |||
| SCOPES = all | |||
| AUTH_URL = https://api.twitter.com/oauth/authorize | |||
| TOKEN_URL = https://api.twitter.com/oauth/access_token | |||
| [oauth.weibo] | |||
| ENABLED = false | |||
| CLIENT_ID = | |||
| @@ -147,8 +141,8 @@ ADAPTER = memory | |||
| ; For "memory" only, GC interval in seconds, default is 60 | |||
| INTERVAL = 60 | |||
| ; For "redis" and "memcache", connection host address | |||
| ; redis: ":6039" | |||
| ; memcache: "127.0.0.1:11211" | |||
| ; redis: `:6039` | |||
| ; memcache: `127.0.0.1:11211` | |||
| HOST = | |||
| [session] | |||
| @@ -156,9 +150,9 @@ HOST = | |||
| PROVIDER = file | |||
| ; Provider config options | |||
| ; memory: not have any config yet | |||
| ; file: session file path, e.g. "data/sessions" | |||
| ; redis: config like redis server addr, poolSize, password, e.g. "127.0.0.1:6379,100,astaxie" | |||
| ; mysql: go-sql-driver/mysql dsn config string, e.g. "root:password@/session_table" | |||
| ; file: session file path, e.g. `data/sessions` | |||
| ; redis: config like redis server addr, poolSize, password, e.g. `127.0.0.1:6379,100,gogs` | |||
| ; mysql: go-sql-driver/mysql dsn config string, e.g. `root:password@/session_table` | |||
| PROVIDER_CONFIG = data/sessions | |||
| ; Session cookie name | |||
| COOKIE_NAME = i_like_gogits | |||
| @@ -182,15 +176,15 @@ DISABLE_GRAVATAR = false | |||
| [attachment] | |||
| ; Whether attachments are enabled. Defaults to `true` | |||
| ENABLE = | |||
| ; Path for attachments. Defaults to files/attachments | |||
| PATH = | |||
| ENABLE = true | |||
| ; Path for attachments. Defaults to `data/attachments` | |||
| PATH = data/attachments | |||
| ; One or more allowed types, e.g. image/jpeg|image/png | |||
| ALLOWED_TYPES = | |||
| ALLOWED_TYPES = image/jpeg|image/png | |||
| ; Max size of each file. Defaults to 32MB | |||
| MAX_SIZE | |||
| MAX_SIZE = 32 | |||
| ; Max number of files per upload. Defaults to 10 | |||
| MAX_FILES = | |||
| MAX_FILES = 10 | |||
| [time] | |||
| ; Specifies the format for fully outputed dates. Defaults to RFC1123 | |||
| @@ -215,7 +209,6 @@ LEVEL = | |||
| ; For "file" mode only | |||
| [log.file] | |||
| LEVEL = | |||
| FILE_NAME = log/gogs.log | |||
| ; This enables automated log rotate(switch of following options), default is true | |||
| LOG_ROTATE = true | |||
| ; Max line number of single file, default is 1000000 | |||
| @@ -259,3 +252,7 @@ LEVEL = | |||
| DRIVER = | |||
| ; Based on xorm, e.g.: root:root@localhost/gogs?charset=utf8 | |||
| CONN = | |||
| [i18n] | |||
| LANGS = en-US,zh-CN | |||
| NAMES = English,简体中文 | |||
| @@ -0,0 +1,23 @@ | |||
| Copyright (c) 2014 | |||
| All rights reserved. | |||
| Redistribution and use in source and binary forms, with or without | |||
| modification, are permitted provided that the following conditions are met: | |||
| * Redistributions of source code must retain the above copyright notice, this | |||
| list of conditions and the following disclaimer. | |||
| * Redistributions in binary form must reproduce the above copyright notice, | |||
| this list of conditions and the following disclaimer in the documentation | |||
| and/or other materials provided with the distribution. | |||
| THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | |||
| AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |||
| IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |||
| DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE | |||
| FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |||
| DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | |||
| SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | |||
| CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | |||
| OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |||
| OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
| @@ -0,0 +1,116 @@ | |||
| CC0 1.0 Universal | |||
| Statement of Purpose | |||
| The laws of most jurisdictions throughout the world automatically confer | |||
| exclusive Copyright and Related Rights (defined below) upon the creator and | |||
| subsequent owner(s) (each and all, an "owner") of an original work of | |||
| authorship and/or a database (each, a "Work"). | |||
| Certain owners wish to permanently relinquish those rights to a Work for the | |||
| purpose of contributing to a commons of creative, cultural and scientific | |||
| works ("Commons") that the public can reliably and without fear of later | |||
| claims of infringement build upon, modify, incorporate in other works, reuse | |||
| and redistribute as freely as possible in any form whatsoever and for any | |||
| purposes, including without limitation commercial purposes. These owners may | |||
| contribute to the Commons to promote the ideal of a free culture and the | |||
| further production of creative, cultural and scientific works, or to gain | |||
| reputation or greater distribution for their Work in part through the use and | |||
| efforts of others. | |||
| For these and/or other purposes and motivations, and without any expectation | |||
| of additional consideration or compensation, the person associating CC0 with a | |||
| Work (the "Affirmer"), to the extent that he or she is an owner of Copyright | |||
| and Related Rights in the Work, voluntarily elects to apply CC0 to the Work | |||
| and publicly distribute the Work under its terms, with knowledge of his or her | |||
| Copyright and Related Rights in the Work and the meaning and intended legal | |||
| effect of CC0 on those rights. | |||
| 1. Copyright and Related Rights. A Work made available under CC0 may be | |||
| protected by copyright and related or neighboring rights ("Copyright and | |||
| Related Rights"). Copyright and Related Rights include, but are not limited | |||
| to, the following: | |||
| i. the right to reproduce, adapt, distribute, perform, display, communicate, | |||
| and translate a Work; | |||
| ii. moral rights retained by the original author(s) and/or performer(s); | |||
| iii. publicity and privacy rights pertaining to a person's image or likeness | |||
| depicted in a Work; | |||
| iv. rights protecting against unfair competition in regards to a Work, | |||
| subject to the limitations in paragraph 4(a), below; | |||
| v. rights protecting the extraction, dissemination, use and reuse of data in | |||
| a Work; | |||
| vi. database rights (such as those arising under Directive 96/9/EC of the | |||
| European Parliament and of the Council of 11 March 1996 on the legal | |||
| protection of databases, and under any national implementation thereof, | |||
| including any amended or successor version of such directive); and | |||
| vii. other similar, equivalent or corresponding rights throughout the world | |||
| based on applicable law or treaty, and any national implementations thereof. | |||
| 2. Waiver. To the greatest extent permitted by, but not in contravention of, | |||
| applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and | |||
| unconditionally waives, abandons, and surrenders all of Affirmer's Copyright | |||
| and Related Rights and associated claims and causes of action, whether now | |||
| known or unknown (including existing as well as future claims and causes of | |||
| action), in the Work (i) in all territories worldwide, (ii) for the maximum | |||
| duration provided by applicable law or treaty (including future time | |||
| extensions), (iii) in any current or future medium and for any number of | |||
| copies, and (iv) for any purpose whatsoever, including without limitation | |||
| commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes | |||
| the Waiver for the benefit of each member of the public at large and to the | |||
| detriment of Affirmer's heirs and successors, fully intending that such Waiver | |||
| shall not be subject to revocation, rescission, cancellation, termination, or | |||
| any other legal or equitable action to disrupt the quiet enjoyment of the Work | |||
| by the public as contemplated by Affirmer's express Statement of Purpose. | |||
| 3. Public License Fallback. Should any part of the Waiver for any reason be | |||
| judged legally invalid or ineffective under applicable law, then the Waiver | |||
| shall be preserved to the maximum extent permitted taking into account | |||
| Affirmer's express Statement of Purpose. In addition, to the extent the Waiver | |||
| is so judged Affirmer hereby grants to each affected person a royalty-free, | |||
| non transferable, non sublicensable, non exclusive, irrevocable and | |||
| unconditional license to exercise Affirmer's Copyright and Related Rights in | |||
| the Work (i) in all territories worldwide, (ii) for the maximum duration | |||
| provided by applicable law or treaty (including future time extensions), (iii) | |||
| in any current or future medium and for any number of copies, and (iv) for any | |||
| purpose whatsoever, including without limitation commercial, advertising or | |||
| promotional purposes (the "License"). The License shall be deemed effective as | |||
| of the date CC0 was applied by Affirmer to the Work. Should any part of the | |||
| License for any reason be judged legally invalid or ineffective under | |||
| applicable law, such partial invalidity or ineffectiveness shall not | |||
| invalidate the remainder of the License, and in such case Affirmer hereby | |||
| affirms that he or she will not (i) exercise any of his or her remaining | |||
| Copyright and Related Rights in the Work or (ii) assert any associated claims | |||
| and causes of action with respect to the Work, in either case contrary to | |||
| Affirmer's express Statement of Purpose. | |||
| 4. Limitations and Disclaimers. | |||
| a. No trademark or patent rights held by Affirmer are waived, abandoned, | |||
| surrendered, licensed or otherwise affected by this document. | |||
| b. Affirmer offers the Work as-is and makes no representations or warranties | |||
| of any kind concerning the Work, express, implied, statutory or otherwise, | |||
| including without limitation warranties of title, merchantability, fitness | |||
| for a particular purpose, non infringement, or the absence of latent or | |||
| other defects, accuracy, or the present or absence of errors, whether or not | |||
| discoverable, all to the greatest extent permissible under applicable law. | |||
| c. Affirmer disclaims responsibility for clearing rights of other persons | |||
| that may apply to the Work or any use thereof, including without limitation | |||
| any person's Copyright and Related Rights in the Work. Further, Affirmer | |||
| disclaims responsibility for obtaining any necessary consents, permissions | |||
| or other rights required for any use of the Work. | |||
| d. Affirmer understands and acknowledges that Creative Commons is not a | |||
| party to this document and has no duty or obligation with respect to this | |||
| CC0 or use of the Work. | |||
| For more information, please see | |||
| <http://creativecommons.org/publicdomain/zero/1.0/> | |||
| @@ -0,0 +1,203 @@ | |||
| Eclipse Public License - v 1.0 | |||
| THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC | |||
| LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM | |||
| CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. | |||
| 1. DEFINITIONS | |||
| "Contribution" means: | |||
| a) in the case of the initial Contributor, the initial code and documentation | |||
| distributed under this Agreement, and | |||
| b) in the case of each subsequent Contributor: | |||
| i) changes to the Program, and | |||
| ii) additions to the Program; | |||
| where such changes and/or additions to the Program originate from and are | |||
| distributed by that particular Contributor. A Contribution 'originates' | |||
| from a Contributor if it was added to the Program by such Contributor | |||
| itself or anyone acting on such Contributor's behalf. Contributions do not | |||
| include additions to the Program which: (i) are separate modules of | |||
| software distributed in conjunction with the Program under their own | |||
| license agreement, and (ii) are not derivative works of the Program. | |||
| "Contributor" means any person or entity that distributes the Program. | |||
| "Licensed Patents" mean patent claims licensable by a Contributor which are | |||
| necessarily infringed by the use or sale of its Contribution alone or when | |||
| combined with the Program. | |||
| "Program" means the Contributions distributed in accordance with this | |||
| Agreement. | |||
| "Recipient" means anyone who receives the Program under this Agreement, | |||
| including all Contributors. | |||
| 2. GRANT OF RIGHTS | |||
| a) Subject to the terms of this Agreement, each Contributor hereby grants | |||
| Recipient a non-exclusive, worldwide, royalty-free copyright license to | |||
| reproduce, prepare derivative works of, publicly display, publicly | |||
| perform, distribute and sublicense the Contribution of such Contributor, | |||
| if any, and such derivative works, in source code and object code form. | |||
| b) Subject to the terms of this Agreement, each Contributor hereby grants | |||
| Recipient a non-exclusive, worldwide, royalty-free patent license under | |||
| Licensed Patents to make, use, sell, offer to sell, import and otherwise | |||
| transfer the Contribution of such Contributor, if any, in source code and | |||
| object code form. This patent license shall apply to the combination of | |||
| the Contribution and the Program if, at the time the Contribution is | |||
| added by the Contributor, such addition of the Contribution causes such | |||
| combination to be covered by the Licensed Patents. The patent license | |||
| shall not apply to any other combinations which include the Contribution. | |||
| No hardware per se is licensed hereunder. | |||
| c) Recipient understands that although each Contributor grants the licenses | |||
| to its Contributions set forth herein, no assurances are provided by any | |||
| Contributor that the Program does not infringe the patent or other | |||
| intellectual property rights of any other entity. Each Contributor | |||
| disclaims any liability to Recipient for claims brought by any other | |||
| entity based on infringement of intellectual property rights or | |||
| otherwise. As a condition to exercising the rights and licenses granted | |||
| hereunder, each Recipient hereby assumes sole responsibility to secure | |||
| any other intellectual property rights needed, if any. For example, if a | |||
| third party patent license is required to allow Recipient to distribute | |||
| the Program, it is Recipient's responsibility to acquire that license | |||
| before distributing the Program. | |||
| d) Each Contributor represents that to its knowledge it has sufficient | |||
| copyright rights in its Contribution, if any, to grant the copyright | |||
| license set forth in this Agreement. | |||
| 3. REQUIREMENTS | |||
| A Contributor may choose to distribute the Program in object code form under | |||
| its own license agreement, provided that: | |||
| a) it complies with the terms and conditions of this Agreement; and | |||
| b) its license agreement: | |||
| i) effectively disclaims on behalf of all Contributors all warranties | |||
| and conditions, express and implied, including warranties or | |||
| conditions of title and non-infringement, and implied warranties or | |||
| conditions of merchantability and fitness for a particular purpose; | |||
| ii) effectively excludes on behalf of all Contributors all liability for | |||
| damages, including direct, indirect, special, incidental and | |||
| consequential damages, such as lost profits; | |||
| iii) states that any provisions which differ from this Agreement are | |||
| offered by that Contributor alone and not by any other party; and | |||
| iv) states that source code for the Program is available from such | |||
| Contributor, and informs licensees how to obtain it in a reasonable | |||
| manner on or through a medium customarily used for software exchange. | |||
| When the Program is made available in source code form: | |||
| a) it must be made available under this Agreement; and | |||
| b) a copy of this Agreement must be included with each copy of the Program. | |||
| Contributors may not remove or alter any copyright notices contained | |||
| within the Program. | |||
| Each Contributor must identify itself as the originator of its Contribution, | |||
| if | |||
| any, in a manner that reasonably allows subsequent Recipients to identify the | |||
| originator of the Contribution. | |||
| 4. COMMERCIAL DISTRIBUTION | |||
| Commercial distributors of software may accept certain responsibilities with | |||
| respect to end users, business partners and the like. While this license is | |||
| intended to facilitate the commercial use of the Program, the Contributor who | |||
| includes the Program in a commercial product offering should do so in a manner | |||
| which does not create potential liability for other Contributors. Therefore, | |||
| if a Contributor includes the Program in a commercial product offering, such | |||
| Contributor ("Commercial Contributor") hereby agrees to defend and indemnify | |||
| every other Contributor ("Indemnified Contributor") against any losses, | |||
| damages and costs (collectively "Losses") arising from claims, lawsuits and | |||
| other legal actions brought by a third party against the Indemnified | |||
| Contributor to the extent caused by the acts or omissions of such Commercial | |||
| Contributor in connection with its distribution of the Program in a commercial | |||
| product offering. The obligations in this section do not apply to any claims | |||
| or Losses relating to any actual or alleged intellectual property | |||
| infringement. In order to qualify, an Indemnified Contributor must: | |||
| a) promptly notify the Commercial Contributor in writing of such claim, and | |||
| b) allow the Commercial Contributor to control, and cooperate with the | |||
| Commercial Contributor in, the defense and any related settlement | |||
| negotiations. The Indemnified Contributor may participate in any such claim at | |||
| its own expense. | |||
| For example, a Contributor might include the Program in a commercial product | |||
| offering, Product X. That Contributor is then a Commercial Contributor. If | |||
| that Commercial Contributor then makes performance claims, or offers | |||
| warranties related to Product X, those performance claims and warranties are | |||
| such Commercial Contributor's responsibility alone. Under this section, the | |||
| Commercial Contributor would have to defend claims against the other | |||
| Contributors related to those performance claims and warranties, and if a | |||
| court requires any other Contributor to pay any damages as a result, the | |||
| Commercial Contributor must pay those damages. | |||
| 5. NO WARRANTY | |||
| EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN | |||
| "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR | |||
| IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, | |||
| NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each | |||
| Recipient is solely responsible for determining the appropriateness of using | |||
| and distributing the Program and assumes all risks associated with its | |||
| exercise of rights under this Agreement , including but not limited to the | |||
| risks and costs of program errors, compliance with applicable laws, damage to | |||
| or loss of data, programs or equipment, and unavailability or interruption of | |||
| operations. | |||
| 6. DISCLAIMER OF LIABILITY | |||
| EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY | |||
| CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |||
| SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION | |||
| LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | |||
| CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |||
| ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE | |||
| EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY | |||
| OF SUCH DAMAGES. | |||
| 7. GENERAL | |||
| If any provision of this Agreement is invalid or unenforceable under | |||
| applicable law, it shall not affect the validity or enforceability of the | |||
| remainder of the terms of this Agreement, and without further action by the | |||
| parties hereto, such provision shall be reformed to the minimum extent | |||
| necessary to make such provision valid and enforceable. | |||
| If Recipient institutes patent litigation against any entity (including a | |||
| cross-claim or counterclaim in a lawsuit) alleging that the Program itself | |||
| (excluding combinations of the Program with other software or hardware) | |||
| infringes such Recipient's patent(s), then such Recipient's rights granted | |||
| under Section 2(b) shall terminate as of the date such litigation is filed. | |||
| All Recipient's rights under this Agreement shall terminate if it fails to | |||
| comply with any of the material terms or conditions of this Agreement and does | |||
| not cure such failure in a reasonable period of time after becoming aware of | |||
| such noncompliance. If all Recipient's rights under this Agreement terminate, | |||
| Recipient agrees to cease use and distribution of the Program as soon as | |||
| reasonably practicable. However, Recipient's obligations under this Agreement | |||
| and any licenses granted by Recipient relating to the Program shall continue | |||
| and survive. | |||
| Everyone is permitted to copy and distribute copies of this Agreement, but in | |||
| order to avoid inconsistency the Agreement is copyrighted and may only be | |||
| modified in the following manner. The Agreement Steward reserves the right to | |||
| publish new versions (including revisions) of this Agreement from time to | |||
| time. No one other than the Agreement Steward has the right to modify this | |||
| Agreement. The Eclipse Foundation is the initial Agreement Steward. The | |||
| Eclipse Foundation may assign the responsibility to serve as the Agreement | |||
| Steward to a suitable separate entity. Each new version of the Agreement will | |||
| be given a distinguishing version number. The Program (including | |||
| Contributions) may always be distributed subject to the version of the | |||
| Agreement under which it was received. In addition, after a new version of the | |||
| Agreement is published, Contributor may elect to distribute the Program | |||
| (including its Contributions) under the new version. Except as expressly | |||
| stated in Sections 2(a) and 2(b) above, Recipient receives no rights or | |||
| licenses to the intellectual property of any Contributor under this Agreement, | |||
| whether expressly, by implication, estoppel or otherwise. All rights in the | |||
| Program not expressly granted under this Agreement are reserved. | |||
| This Agreement is governed by the laws of the State of New York and the | |||
| intellectual property laws of the United States of America. No party to this | |||
| Agreement will bring a legal action under this Agreement more than one year | |||
| after the cause of action arose. Each party waives its rights to a jury trial in | |||
| any resulting litigation. | |||
| @@ -0,0 +1,674 @@ | |||
| GNU GENERAL PUBLIC LICENSE | |||
| Version 3, 29 June 2007 | |||
| Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> | |||
| Everyone is permitted to copy and distribute verbatim copies | |||
| of this license document, but changing it is not allowed. | |||
| Preamble | |||
| The GNU General Public License is a free, copyleft license for | |||
| software and other kinds of works. | |||
| The licenses for most software and other practical works are designed | |||
| to take away your freedom to share and change the works. By contrast, | |||
| the GNU General Public License is intended to guarantee your freedom to | |||
| share and change all versions of a program--to make sure it remains free | |||
| software for all its users. We, the Free Software Foundation, use the | |||
| GNU General Public License for most of our software; it applies also to | |||
| any other work released this way by its authors. You can apply it to | |||
| your programs, too. | |||
| When we speak of free software, we are referring to freedom, not | |||
| price. Our General Public Licenses are designed to make sure that you | |||
| have the freedom to distribute copies of free software (and charge for | |||
| them if you wish), that you receive source code or can get it if you | |||
| want it, that you can change the software or use pieces of it in new | |||
| free programs, and that you know you can do these things. | |||
| To protect your rights, we need to prevent others from denying you | |||
| these rights or asking you to surrender the rights. Therefore, you have | |||
| certain responsibilities if you distribute copies of the software, or if | |||
| you modify it: responsibilities to respect the freedom of others. | |||
| For example, if you distribute copies of such a program, whether | |||
| gratis or for a fee, you must pass on to the recipients the same | |||
| freedoms that you received. You must make sure that they, too, receive | |||
| or can get the source code. And you must show them these terms so they | |||
| know their rights. | |||
| Developers that use the GNU GPL protect your rights with two steps: | |||
| (1) assert copyright on the software, and (2) offer you this License | |||
| giving you legal permission to copy, distribute and/or modify it. | |||
| For the developers' and authors' protection, the GPL clearly explains | |||
| that there is no warranty for this free software. For both users' and | |||
| authors' sake, the GPL requires that modified versions be marked as | |||
| changed, so that their problems will not be attributed erroneously to | |||
| authors of previous versions. | |||
| Some devices are designed to deny users access to install or run | |||
| modified versions of the software inside them, although the manufacturer | |||
| can do so. This is fundamentally incompatible with the aim of | |||
| protecting users' freedom to change the software. The systematic | |||
| pattern of such abuse occurs in the area of products for individuals to | |||
| use, which is precisely where it is most unacceptable. Therefore, we | |||
| have designed this version of the GPL to prohibit the practice for those | |||
| products. If such problems arise substantially in other domains, we | |||
| stand ready to extend this provision to those domains in future versions | |||
| of the GPL, as needed to protect the freedom of users. | |||
| Finally, every program is threatened constantly by software patents. | |||
| States should not allow patents to restrict development and use of | |||
| software on general-purpose computers, but in those that do, we wish to | |||
| avoid the special danger that patents applied to a free program could | |||
| make it effectively proprietary. To prevent this, the GPL assures that | |||
| patents cannot be used to render the program non-free. | |||
| The precise terms and conditions for copying, distribution and | |||
| modification follow. | |||
| TERMS AND CONDITIONS | |||
| 0. Definitions. | |||
| "This License" refers to version 3 of the GNU General Public License. | |||
| "Copyright" also means copyright-like laws that apply to other kinds of | |||
| works, such as semiconductor masks. | |||
| "The Program" refers to any copyrightable work licensed under this | |||
| License. Each licensee is addressed as "you". "Licensees" and | |||
| "recipients" may be individuals or organizations. | |||
| To "modify" a work means to copy from or adapt all or part of the work | |||
| in a fashion requiring copyright permission, other than the making of an | |||
| exact copy. The resulting work is called a "modified version" of the | |||
| earlier work or a work "based on" the earlier work. | |||
| A "covered work" means either the unmodified Program or a work based | |||
| on the Program. | |||
| To "propagate" a work means to do anything with it that, without | |||
| permission, would make you directly or secondarily liable for | |||
| infringement under applicable copyright law, except executing it on a | |||
| computer or modifying a private copy. Propagation includes copying, | |||
| distribution (with or without modification), making available to the | |||
| public, and in some countries other activities as well. | |||
| To "convey" a work means any kind of propagation that enables other | |||
| parties to make or receive copies. Mere interaction with a user through | |||
| a computer network, with no transfer of a copy, is not conveying. | |||
| An interactive user interface displays "Appropriate Legal Notices" | |||
| to the extent that it includes a convenient and prominently visible | |||
| feature that (1) displays an appropriate copyright notice, and (2) | |||
| tells the user that there is no warranty for the work (except to the | |||
| extent that warranties are provided), that licensees may convey the | |||
| work under this License, and how to view a copy of this License. If | |||
| the interface presents a list of user commands or options, such as a | |||
| menu, a prominent item in the list meets this criterion. | |||
| 1. Source Code. | |||
| The "source code" for a work means the preferred form of the work | |||
| for making modifications to it. "Object code" means any non-source | |||
| form of a work. | |||
| A "Standard Interface" means an interface that either is an official | |||
| standard defined by a recognized standards body, or, in the case of | |||
| interfaces specified for a particular programming language, one that | |||
| is widely used among developers working in that language. | |||
| The "System Libraries" of an executable work include anything, other | |||
| than the work as a whole, that (a) is included in the normal form of | |||
| packaging a Major Component, but which is not part of that Major | |||
| Component, and (b) serves only to enable use of the work with that | |||
| Major Component, or to implement a Standard Interface for which an | |||
| implementation is available to the public in source code form. A | |||
| "Major Component", in this context, means a major essential component | |||
| (kernel, window system, and so on) of the specific operating system | |||
| (if any) on which the executable work runs, or a compiler used to | |||
| produce the work, or an object code interpreter used to run it. | |||
| The "Corresponding Source" for a work in object code form means all | |||
| the source code needed to generate, install, and (for an executable | |||
| work) run the object code and to modify the work, including scripts to | |||
| control those activities. However, it does not include the work's | |||
| System Libraries, or general-purpose tools or generally available free | |||
| programs which are used unmodified in performing those activities but | |||
| which are not part of the work. For example, Corresponding Source | |||
| includes interface definition files associated with source files for | |||
| the work, and the source code for shared libraries and dynamically | |||
| linked subprograms that the work is specifically designed to require, | |||
| such as by intimate data communication or control flow between those | |||
| subprograms and other parts of the work. | |||
| The Corresponding Source need not include anything that users | |||
| can regenerate automatically from other parts of the Corresponding | |||
| Source. | |||
| The Corresponding Source for a work in source code form is that | |||
| same work. | |||
| 2. Basic Permissions. | |||
| All rights granted under this License are granted for the term of | |||
| copyright on the Program, and are irrevocable provided the stated | |||
| conditions are met. This License explicitly affirms your unlimited | |||
| permission to run the unmodified Program. The output from running a | |||
| covered work is covered by this License only if the output, given its | |||
| content, constitutes a covered work. This License acknowledges your | |||
| rights of fair use or other equivalent, as provided by copyright law. | |||
| You may make, run and propagate covered works that you do not | |||
| convey, without conditions so long as your license otherwise remains | |||
| in force. You may convey covered works to others for the sole purpose | |||
| of having them make modifications exclusively for you, or provide you | |||
| with facilities for running those works, provided that you comply with | |||
| the terms of this License in conveying all material for which you do | |||
| not control copyright. Those thus making or running the covered works | |||
| for you must do so exclusively on your behalf, under your direction | |||
| and control, on terms that prohibit them from making any copies of | |||
| your copyrighted material outside their relationship with you. | |||
| Conveying under any other circumstances is permitted solely under | |||
| the conditions stated below. Sublicensing is not allowed; section 10 | |||
| makes it unnecessary. | |||
| 3. Protecting Users' Legal Rights From Anti-Circumvention Law. | |||
| No covered work shall be deemed part of an effective technological | |||
| measure under any applicable law fulfilling obligations under article | |||
| 11 of the WIPO copyright treaty adopted on 20 December 1996, or | |||
| similar laws prohibiting or restricting circumvention of such | |||
| measures. | |||
| When you convey a covered work, you waive any legal power to forbid | |||
| circumvention of technological measures to the extent such circumvention | |||
| is effected by exercising rights under this License with respect to | |||
| the covered work, and you disclaim any intention to limit operation or | |||
| modification of the work as a means of enforcing, against the work's | |||
| users, your or third parties' legal rights to forbid circumvention of | |||
| technological measures. | |||
| 4. Conveying Verbatim Copies. | |||
| You may convey verbatim copies of the Program's source code as you | |||
| receive it, in any medium, provided that you conspicuously and | |||
| appropriately publish on each copy an appropriate copyright notice; | |||
| keep intact all notices stating that this License and any | |||
| non-permissive terms added in accord with section 7 apply to the code; | |||
| keep intact all notices of the absence of any warranty; and give all | |||
| recipients a copy of this License along with the Program. | |||
| You may charge any price or no price for each copy that you convey, | |||
| and you may offer support or warranty protection for a fee. | |||
| 5. Conveying Modified Source Versions. | |||
| You may convey a work based on the Program, or the modifications to | |||
| produce it from the Program, in the form of source code under the | |||
| terms of section 4, provided that you also meet all of these conditions: | |||
| a) The work must carry prominent notices stating that you modified | |||
| it, and giving a relevant date. | |||
| b) The work must carry prominent notices stating that it is | |||
| released under this License and any conditions added under section | |||
| 7. This requirement modifies the requirement in section 4 to | |||
| "keep intact all notices". | |||
| c) You must license the entire work, as a whole, under this | |||
| License to anyone who comes into possession of a copy. This | |||
| License will therefore apply, along with any applicable section 7 | |||
| additional terms, to the whole of the work, and all its parts, | |||
| regardless of how they are packaged. This License gives no | |||
| permission to license the work in any other way, but it does not | |||
| invalidate such permission if you have separately received it. | |||
| d) If the work has interactive user interfaces, each must display | |||
| Appropriate Legal Notices; however, if the Program has interactive | |||
| interfaces that do not display Appropriate Legal Notices, your | |||
| work need not make them do so. | |||
| A compilation of a covered work with other separate and independent | |||
| works, which are not by their nature extensions of the covered work, | |||
| and which are not combined with it such as to form a larger program, | |||
| in or on a volume of a storage or distribution medium, is called an | |||
| "aggregate" if the compilation and its resulting copyright are not | |||
| used to limit the access or legal rights of the compilation's users | |||
| beyond what the individual works permit. Inclusion of a covered work | |||
| in an aggregate does not cause this License to apply to the other | |||
| parts of the aggregate. | |||
| 6. Conveying Non-Source Forms. | |||
| You may convey a covered work in object code form under the terms | |||
| of sections 4 and 5, provided that you also convey the | |||
| machine-readable Corresponding Source under the terms of this License, | |||
| in one of these ways: | |||
| a) Convey the object code in, or embodied in, a physical product | |||
| (including a physical distribution medium), accompanied by the | |||
| Corresponding Source fixed on a durable physical medium | |||
| customarily used for software interchange. | |||
| b) Convey the object code in, or embodied in, a physical product | |||
| (including a physical distribution medium), accompanied by a | |||
| written offer, valid for at least three years and valid for as | |||
| long as you offer spare parts or customer support for that product | |||
| model, to give anyone who possesses the object code either (1) a | |||
| copy of the Corresponding Source for all the software in the | |||
| product that is covered by this License, on a durable physical | |||
| medium customarily used for software interchange, for a price no | |||
| more than your reasonable cost of physically performing this | |||
| conveying of source, or (2) access to copy the | |||
| Corresponding Source from a network server at no charge. | |||
| c) Convey individual copies of the object code with a copy of the | |||
| written offer to provide the Corresponding Source. This | |||
| alternative is allowed only occasionally and noncommercially, and | |||
| only if you received the object code with such an offer, in accord | |||
| with subsection 6b. | |||
| d) Convey the object code by offering access from a designated | |||
| place (gratis or for a charge), and offer equivalent access to the | |||
| Corresponding Source in the same way through the same place at no | |||
| further charge. You need not require recipients to copy the | |||
| Corresponding Source along with the object code. If the place to | |||
| copy the object code is a network server, the Corresponding Source | |||
| may be on a different server (operated by you or a third party) | |||
| that supports equivalent copying facilities, provided you maintain | |||
| clear directions next to the object code saying where to find the | |||
| Corresponding Source. Regardless of what server hosts the | |||
| Corresponding Source, you remain obligated to ensure that it is | |||
| available for as long as needed to satisfy these requirements. | |||
| e) Convey the object code using peer-to-peer transmission, provided | |||
| you inform other peers where the object code and Corresponding | |||
| Source of the work are being offered to the general public at no | |||
| charge under subsection 6d. | |||
| A separable portion of the object code, whose source code is excluded | |||
| from the Corresponding Source as a System Library, need not be | |||
| included in conveying the object code work. | |||
| A "User Product" is either (1) a "consumer product", which means any | |||
| tangible personal property which is normally used for personal, family, | |||
| or household purposes, or (2) anything designed or sold for incorporation | |||
| into a dwelling. In determining whether a product is a consumer product, | |||
| doubtful cases shall be resolved in favor of coverage. For a particular | |||
| product received by a particular user, "normally used" refers to a | |||
| typical or common use of that class of product, regardless of the status | |||
| of the particular user or of the way in which the particular user | |||
| actually uses, or expects or is expected to use, the product. A product | |||
| is a consumer product regardless of whether the product has substantial | |||
| commercial, industrial or non-consumer uses, unless such uses represent | |||
| the only significant mode of use of the product. | |||
| "Installation Information" for a User Product means any methods, | |||
| procedures, authorization keys, or other information required to install | |||
| and execute modified versions of a covered work in that User Product from | |||
| a modified version of its Corresponding Source. The information must | |||
| suffice to ensure that the continued functioning of the modified object | |||
| code is in no case prevented or interfered with solely because | |||
| modification has been made. | |||
| If you convey an object code work under this section in, or with, or | |||
| specifically for use in, a User Product, and the conveying occurs as | |||
| part of a transaction in which the right of possession and use of the | |||
| User Product is transferred to the recipient in perpetuity or for a | |||
| fixed term (regardless of how the transaction is characterized), the | |||
| Corresponding Source conveyed under this section must be accompanied | |||
| by the Installation Information. But this requirement does not apply | |||
| if neither you nor any third party retains the ability to install | |||
| modified object code on the User Product (for example, the work has | |||
| been installed in ROM). | |||
| The requirement to provide Installation Information does not include a | |||
| requirement to continue to provide support service, warranty, or updates | |||
| for a work that has been modified or installed by the recipient, or for | |||
| the User Product in which it has been modified or installed. Access to a | |||
| network may be denied when the modification itself materially and | |||
| adversely affects the operation of the network or violates the rules and | |||
| protocols for communication across the network. | |||
| Corresponding Source conveyed, and Installation Information provided, | |||
| in accord with this section must be in a format that is publicly | |||
| documented (and with an implementation available to the public in | |||
| source code form), and must require no special password or key for | |||
| unpacking, reading or copying. | |||
| 7. Additional Terms. | |||
| "Additional permissions" are terms that supplement the terms of this | |||
| License by making exceptions from one or more of its conditions. | |||
| Additional permissions that are applicable to the entire Program shall | |||
| be treated as though they were included in this License, to the extent | |||
| that they are valid under applicable law. If additional permissions | |||
| apply only to part of the Program, that part may be used separately | |||
| under those permissions, but the entire Program remains governed by | |||
| this License without regard to the additional permissions. | |||
| When you convey a copy of a covered work, you may at your option | |||
| remove any additional permissions from that copy, or from any part of | |||
| it. (Additional permissions may be written to require their own | |||
| removal in certain cases when you modify the work.) You may place | |||
| additional permissions on material, added by you to a covered work, | |||
| for which you have or can give appropriate copyright permission. | |||
| Notwithstanding any other provision of this License, for material you | |||
| add to a covered work, you may (if authorized by the copyright holders of | |||
| that material) supplement the terms of this License with terms: | |||
| a) Disclaiming warranty or limiting liability differently from the | |||
| terms of sections 15 and 16 of this License; or | |||
| b) Requiring preservation of specified reasonable legal notices or | |||
| author attributions in that material or in the Appropriate Legal | |||
| Notices displayed by works containing it; or | |||
| c) Prohibiting misrepresentation of the origin of that material, or | |||
| requiring that modified versions of such material be marked in | |||
| reasonable ways as different from the original version; or | |||
| d) Limiting the use for publicity purposes of names of licensors or | |||
| authors of the material; or | |||
| e) Declining to grant rights under trademark law for use of some | |||
| trade names, trademarks, or service marks; or | |||
| f) Requiring indemnification of licensors and authors of that | |||
| material by anyone who conveys the material (or modified versions of | |||
| it) with contractual assumptions of liability to the recipient, for | |||
| any liability that these contractual assumptions directly impose on | |||
| those licensors and authors. | |||
| All other non-permissive additional terms are considered "further | |||
| restrictions" within the meaning of section 10. If the Program as you | |||
| received it, or any part of it, contains a notice stating that it is | |||
| governed by this License along with a term that is a further | |||
| restriction, you may remove that term. If a license document contains | |||
| a further restriction but permits relicensing or conveying under this | |||
| License, you may add to a covered work material governed by the terms | |||
| of that license document, provided that the further restriction does | |||
| not survive such relicensing or conveying. | |||
| If you add terms to a covered work in accord with this section, you | |||
| must place, in the relevant source files, a statement of the | |||
| additional terms that apply to those files, or a notice indicating | |||
| where to find the applicable terms. | |||
| Additional terms, permissive or non-permissive, may be stated in the | |||
| form of a separately written license, or stated as exceptions; | |||
| the above requirements apply either way. | |||
| 8. Termination. | |||
| You may not propagate or modify a covered work except as expressly | |||
| provided under this License. Any attempt otherwise to propagate or | |||
| modify it is void, and will automatically terminate your rights under | |||
| this License (including any patent licenses granted under the third | |||
| paragraph of section 11). | |||
| However, if you cease all violation of this License, then your | |||
| license from a particular copyright holder is reinstated (a) | |||
| provisionally, unless and until the copyright holder explicitly and | |||
| finally terminates your license, and (b) permanently, if the copyright | |||
| holder fails to notify you of the violation by some reasonable means | |||
| prior to 60 days after the cessation. | |||
| Moreover, your license from a particular copyright holder is | |||
| reinstated permanently if the copyright holder notifies you of the | |||
| violation by some reasonable means, this is the first time you have | |||
| received notice of violation of this License (for any work) from that | |||
| copyright holder, and you cure the violation prior to 30 days after | |||
| your receipt of the notice. | |||
| Termination of your rights under this section does not terminate the | |||
| licenses of parties who have received copies or rights from you under | |||
| this License. If your rights have been terminated and not permanently | |||
| reinstated, you do not qualify to receive new licenses for the same | |||
| material under section 10. | |||
| 9. Acceptance Not Required for Having Copies. | |||
| You are not required to accept this License in order to receive or | |||
| run a copy of the Program. Ancillary propagation of a covered work | |||
| occurring solely as a consequence of using peer-to-peer transmission | |||
| to receive a copy likewise does not require acceptance. However, | |||
| nothing other than this License grants you permission to propagate or | |||
| modify any covered work. These actions infringe copyright if you do | |||
| not accept this License. Therefore, by modifying or propagating a | |||
| covered work, you indicate your acceptance of this License to do so. | |||
| 10. Automatic Licensing of Downstream Recipients. | |||
| Each time you convey a covered work, the recipient automatically | |||
| receives a license from the original licensors, to run, modify and | |||
| propagate that work, subject to this License. You are not responsible | |||
| for enforcing compliance by third parties with this License. | |||
| An "entity transaction" is a transaction transferring control of an | |||
| organization, or substantially all assets of one, or subdividing an | |||
| organization, or merging organizations. If propagation of a covered | |||
| work results from an entity transaction, each party to that | |||
| transaction who receives a copy of the work also receives whatever | |||
| licenses to the work the party's predecessor in interest had or could | |||
| give under the previous paragraph, plus a right to possession of the | |||
| Corresponding Source of the work from the predecessor in interest, if | |||
| the predecessor has it or can get it with reasonable efforts. | |||
| You may not impose any further restrictions on the exercise of the | |||
| rights granted or affirmed under this License. For example, you may | |||
| not impose a license fee, royalty, or other charge for exercise of | |||
| rights granted under this License, and you may not initiate litigation | |||
| (including a cross-claim or counterclaim in a lawsuit) alleging that | |||
| any patent claim is infringed by making, using, selling, offering for | |||
| sale, or importing the Program or any portion of it. | |||
| 11. Patents. | |||
| A "contributor" is a copyright holder who authorizes use under this | |||
| License of the Program or a work on which the Program is based. The | |||
| work thus licensed is called the contributor's "contributor version". | |||
| A contributor's "essential patent claims" are all patent claims | |||
| owned or controlled by the contributor, whether already acquired or | |||
| hereafter acquired, that would be infringed by some manner, permitted | |||
| by this License, of making, using, or selling its contributor version, | |||
| but do not include claims that would be infringed only as a | |||
| consequence of further modification of the contributor version. For | |||
| purposes of this definition, "control" includes the right to grant | |||
| patent sublicenses in a manner consistent with the requirements of | |||
| this License. | |||
| Each contributor grants you a non-exclusive, worldwide, royalty-free | |||
| patent license under the contributor's essential patent claims, to | |||
| make, use, sell, offer for sale, import and otherwise run, modify and | |||
| propagate the contents of its contributor version. | |||
| In the following three paragraphs, a "patent license" is any express | |||
| agreement or commitment, however denominated, not to enforce a patent | |||
| (such as an express permission to practice a patent or covenant not to | |||
| sue for patent infringement). To "grant" such a patent license to a | |||
| party means to make such an agreement or commitment not to enforce a | |||
| patent against the party. | |||
| If you convey a covered work, knowingly relying on a patent license, | |||
| and the Corresponding Source of the work is not available for anyone | |||
| to copy, free of charge and under the terms of this License, through a | |||
| publicly available network server or other readily accessible means, | |||
| then you must either (1) cause the Corresponding Source to be so | |||
| available, or (2) arrange to deprive yourself of the benefit of the | |||
| patent license for this particular work, or (3) arrange, in a manner | |||
| consistent with the requirements of this License, to extend the patent | |||
| license to downstream recipients. "Knowingly relying" means you have | |||
| actual knowledge that, but for the patent license, your conveying the | |||
| covered work in a country, or your recipient's use of the covered work | |||
| in a country, would infringe one or more identifiable patents in that | |||
| country that you have reason to believe are valid. | |||
| If, pursuant to or in connection with a single transaction or | |||
| arrangement, you convey, or propagate by procuring conveyance of, a | |||
| covered work, and grant a patent license to some of the parties | |||
| receiving the covered work authorizing them to use, propagate, modify | |||
| or convey a specific copy of the covered work, then the patent license | |||
| you grant is automatically extended to all recipients of the covered | |||
| work and works based on it. | |||
| A patent license is "discriminatory" if it does not include within | |||
| the scope of its coverage, prohibits the exercise of, or is | |||
| conditioned on the non-exercise of one or more of the rights that are | |||
| specifically granted under this License. You may not convey a covered | |||
| work if you are a party to an arrangement with a third party that is | |||
| in the business of distributing software, under which you make payment | |||
| to the third party based on the extent of your activity of conveying | |||
| the work, and under which the third party grants, to any of the | |||
| parties who would receive the covered work from you, a discriminatory | |||
| patent license (a) in connection with copies of the covered work | |||
| conveyed by you (or copies made from those copies), or (b) primarily | |||
| for and in connection with specific products or compilations that | |||
| contain the covered work, unless you entered into that arrangement, | |||
| or that patent license was granted, prior to 28 March 2007. | |||
| Nothing in this License shall be construed as excluding or limiting | |||
| any implied license or other defenses to infringement that may | |||
| otherwise be available to you under applicable patent law. | |||
| 12. No Surrender of Others' Freedom. | |||
| If conditions are imposed on you (whether by court order, agreement or | |||
| otherwise) that contradict the conditions of this License, they do not | |||
| excuse you from the conditions of this License. If you cannot convey a | |||
| covered work so as to satisfy simultaneously your obligations under this | |||
| License and any other pertinent obligations, then as a consequence you may | |||
| not convey it at all. For example, if you agree to terms that obligate you | |||
| to collect a royalty for further conveying from those to whom you convey | |||
| the Program, the only way you could satisfy both those terms and this | |||
| License would be to refrain entirely from conveying the Program. | |||
| 13. Use with the GNU Affero General Public License. | |||
| Notwithstanding any other provision of this License, you have | |||
| permission to link or combine any covered work with a work licensed | |||
| under version 3 of the GNU Affero General Public License into a single | |||
| combined work, and to convey the resulting work. The terms of this | |||
| License will continue to apply to the part which is the covered work, | |||
| but the special requirements of the GNU Affero General Public License, | |||
| section 13, concerning interaction through a network will apply to the | |||
| combination as such. | |||
| 14. Revised Versions of this License. | |||
| The Free Software Foundation may publish revised and/or new versions of | |||
| the GNU General Public License from time to time. Such new versions will | |||
| be similar in spirit to the present version, but may differ in detail to | |||
| address new problems or concerns. | |||
| Each version is given a distinguishing version number. If the | |||
| Program specifies that a certain numbered version of the GNU General | |||
| Public License "or any later version" applies to it, you have the | |||
| option of following the terms and conditions either of that numbered | |||
| version or of any later version published by the Free Software | |||
| Foundation. If the Program does not specify a version number of the | |||
| GNU General Public License, you may choose any version ever published | |||
| by the Free Software Foundation. | |||
| If the Program specifies that a proxy can decide which future | |||
| versions of the GNU General Public License can be used, that proxy's | |||
| public statement of acceptance of a version permanently authorizes you | |||
| to choose that version for the Program. | |||
| Later license versions may give you additional or different | |||
| permissions. However, no additional obligations are imposed on any | |||
| author or copyright holder as a result of your choosing to follow a | |||
| later version. | |||
| 15. Disclaimer of Warranty. | |||
| THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY | |||
| APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT | |||
| HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY | |||
| OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, | |||
| THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | |||
| PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM | |||
| IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF | |||
| ALL NECESSARY SERVICING, REPAIR OR CORRECTION. | |||
| 16. Limitation of Liability. | |||
| IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING | |||
| WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS | |||
| THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY | |||
| GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE | |||
| USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF | |||
| DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD | |||
| PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), | |||
| EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF | |||
| SUCH DAMAGES. | |||
| 17. Interpretation of Sections 15 and 16. | |||
| If the disclaimer of warranty and limitation of liability provided | |||
| above cannot be given local legal effect according to their terms, | |||
| reviewing courts shall apply local law that most closely approximates | |||
| an absolute waiver of all civil liability in connection with the | |||
| Program, unless a warranty or assumption of liability accompanies a | |||
| copy of the Program in return for a fee. | |||
| END OF TERMS AND CONDITIONS | |||
| How to Apply These Terms to Your New Programs | |||
| If you develop a new program, and you want it to be of the greatest | |||
| possible use to the public, the best way to achieve this is to make it | |||
| free software which everyone can redistribute and change under these terms. | |||
| To do so, attach the following notices to the program. It is safest | |||
| to attach them to the start of each source file to most effectively | |||
| state the exclusion of warranty; and each file should have at least | |||
| the "copyright" line and a pointer to where the full notice is found. | |||
| {one line to give the program's name and a brief idea of what it does.} | |||
| Copyright (C) {year} {name of author} | |||
| This program is free software: you can redistribute it and/or modify | |||
| it under the terms of the GNU General Public License as published by | |||
| the Free Software Foundation, either version 3 of the License, or | |||
| (at your option) any later version. | |||
| This program is distributed in the hope that it will be useful, | |||
| but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| GNU General Public License for more details. | |||
| You should have received a copy of the GNU General Public License | |||
| along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
| Also add information on how to contact you by electronic and paper mail. | |||
| If the program does terminal interaction, make it output a short | |||
| notice like this when it starts in an interactive mode: | |||
| {project} Copyright (C) {year} {fullname} | |||
| This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. | |||
| This is free software, and you are welcome to redistribute it | |||
| under certain conditions; type `show c' for details. | |||
| The hypothetical commands `show w' and `show c' should show the appropriate | |||
| parts of the General Public License. Of course, your program's commands | |||
| might be different; for a GUI interface, you would use an "about box". | |||
| You should also get your employer (if you work as a programmer) or school, | |||
| if any, to sign a "copyright disclaimer" for the program, if necessary. | |||
| For more information on this, and how to apply and follow the GNU GPL, see | |||
| <http://www.gnu.org/licenses/>. | |||
| The GNU General Public License does not permit incorporating your program | |||
| into proprietary programs. If your program is a subroutine library, you | |||
| may consider it more useful to permit linking proprietary applications with | |||
| the library. If this is what you want to do, use the GNU Lesser General | |||
| Public License instead of this License. But first, please read | |||
| <http://www.gnu.org/philosophy/why-not-lgpl.html>. | |||
| @@ -0,0 +1,13 @@ | |||
| Copyright (c) 2014 | |||
| Permission to use, copy, modify, and/or distribute this software for any | |||
| purpose with or without fee is hereby granted, provided that the above | |||
| copyright notice and this permission notice appear in all copies. | |||
| THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||
| WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||
| MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||
| ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||
| WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||
| ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||
| OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
| @@ -0,0 +1,504 @@ | |||
| GNU LESSER GENERAL PUBLIC LICENSE | |||
| Version 2.1, February 1999 | |||
| Copyright (C) 1991, 1999 Free Software Foundation, Inc. | |||
| 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |||
| Everyone is permitted to copy and distribute verbatim copies | |||
| of this license document, but changing it is not allowed. | |||
| (This is the first released version of the Lesser GPL. It also counts | |||
| as the successor of the GNU Library Public License, version 2, hence | |||
| the version number 2.1.) | |||
| Preamble | |||
| The licenses for most software are designed to take away your | |||
| freedom to share and change it. By contrast, the GNU General Public | |||
| Licenses are intended to guarantee your freedom to share and change | |||
| free software--to make sure the software is free for all its users. | |||
| This license, the Lesser General Public License, applies to some | |||
| specially designated software packages--typically libraries--of the | |||
| Free Software Foundation and other authors who decide to use it. You | |||
| can use it too, but we suggest you first think carefully about whether | |||
| this license or the ordinary General Public License is the better | |||
| strategy to use in any particular case, based on the explanations below. | |||
| When we speak of free software, we are referring to freedom of use, | |||
| not price. Our General Public Licenses are designed to make sure that | |||
| you have the freedom to distribute copies of free software (and charge | |||
| for this service if you wish); that you receive source code or can get | |||
| it if you want it; that you can change the software and use pieces of | |||
| it in new free programs; and that you are informed that you can do | |||
| these things. | |||
| To protect your rights, we need to make restrictions that forbid | |||
| distributors to deny you these rights or to ask you to surrender these | |||
| rights. These restrictions translate to certain responsibilities for | |||
| you if you distribute copies of the library or if you modify it. | |||
| For example, if you distribute copies of the library, whether gratis | |||
| or for a fee, you must give the recipients all the rights that we gave | |||
| you. You must make sure that they, too, receive or can get the source | |||
| code. If you link other code with the library, you must provide | |||
| complete object files to the recipients, so that they can relink them | |||
| with the library after making changes to the library and recompiling | |||
| it. And you must show them these terms so they know their rights. | |||
| We protect your rights with a two-step method: (1) we copyright the | |||
| library, and (2) we offer you this license, which gives you legal | |||
| permission to copy, distribute and/or modify the library. | |||
| To protect each distributor, we want to make it very clear that | |||
| there is no warranty for the free library. Also, if the library is | |||
| modified by someone else and passed on, the recipients should know | |||
| that what they have is not the original version, so that the original | |||
| author's reputation will not be affected by problems that might be | |||
| introduced by others. | |||
| Finally, software patents pose a constant threat to the existence of | |||
| any free program. We wish to make sure that a company cannot | |||
| effectively restrict the users of a free program by obtaining a | |||
| restrictive license from a patent holder. Therefore, we insist that | |||
| any patent license obtained for a version of the library must be | |||
| consistent with the full freedom of use specified in this license. | |||
| Most GNU software, including some libraries, is covered by the | |||
| ordinary GNU General Public License. This license, the GNU Lesser | |||
| General Public License, applies to certain designated libraries, and | |||
| is quite different from the ordinary General Public License. We use | |||
| this license for certain libraries in order to permit linking those | |||
| libraries into non-free programs. | |||
| When a program is linked with a library, whether statically or using | |||
| a shared library, the combination of the two is legally speaking a | |||
| combined work, a derivative of the original library. The ordinary | |||
| General Public License therefore permits such linking only if the | |||
| entire combination fits its criteria of freedom. The Lesser General | |||
| Public License permits more lax criteria for linking other code with | |||
| the library. | |||
| We call this license the "Lesser" General Public License because it | |||
| does Less to protect the user's freedom than the ordinary General | |||
| Public License. It also provides other free software developers Less | |||
| of an advantage over competing non-free programs. These disadvantages | |||
| are the reason we use the ordinary General Public License for many | |||
| libraries. However, the Lesser license provides advantages in certain | |||
| special circumstances. | |||
| For example, on rare occasions, there may be a special need to | |||
| encourage the widest possible use of a certain library, so that it becomes | |||
| a de-facto standard. To achieve this, non-free programs must be | |||
| allowed to use the library. A more frequent case is that a free | |||
| library does the same job as widely used non-free libraries. In this | |||
| case, there is little to gain by limiting the free library to free | |||
| software only, so we use the Lesser General Public License. | |||
| In other cases, permission to use a particular library in non-free | |||
| programs enables a greater number of people to use a large body of | |||
| free software. For example, permission to use the GNU C Library in | |||
| non-free programs enables many more people to use the whole GNU | |||
| operating system, as well as its variant, the GNU/Linux operating | |||
| system. | |||
| Although the Lesser General Public License is Less protective of the | |||
| users' freedom, it does ensure that the user of a program that is | |||
| linked with the Library has the freedom and the wherewithal to run | |||
| that program using a modified version of the Library. | |||
| The precise terms and conditions for copying, distribution and | |||
| modification follow. Pay close attention to the difference between a | |||
| "work based on the library" and a "work that uses the library". The | |||
| former contains code derived from the library, whereas the latter must | |||
| be combined with the library in order to run. | |||
| GNU LESSER GENERAL PUBLIC LICENSE | |||
| TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION | |||
| 0. This License Agreement applies to any software library or other | |||
| program which contains a notice placed by the copyright holder or | |||
| other authorized party saying it may be distributed under the terms of | |||
| this Lesser General Public License (also called "this License"). | |||
| Each licensee is addressed as "you". | |||
| A "library" means a collection of software functions and/or data | |||
| prepared so as to be conveniently linked with application programs | |||
| (which use some of those functions and data) to form executables. | |||
| The "Library", below, refers to any such software library or work | |||
| which has been distributed under these terms. A "work based on the | |||
| Library" means either the Library or any derivative work under | |||
| copyright law: that is to say, a work containing the Library or a | |||
| portion of it, either verbatim or with modifications and/or translated | |||
| straightforwardly into another language. (Hereinafter, translation is | |||
| included without limitation in the term "modification".) | |||
| "Source code" for a work means the preferred form of the work for | |||
| making modifications to it. For a library, complete source code means | |||
| all the source code for all modules it contains, plus any associated | |||
| interface definition files, plus the scripts used to control compilation | |||
| and installation of the library. | |||
| Activities other than copying, distribution and modification are not | |||
| covered by this License; they are outside its scope. The act of | |||
| running a program using the Library is not restricted, and output from | |||
| such a program is covered only if its contents constitute a work based | |||
| on the Library (independent of the use of the Library in a tool for | |||
| writing it). Whether that is true depends on what the Library does | |||
| and what the program that uses the Library does. | |||
| 1. You may copy and distribute verbatim copies of the Library's | |||
| complete source code as you receive it, in any medium, provided that | |||
| you conspicuously and appropriately publish on each copy an | |||
| appropriate copyright notice and disclaimer of warranty; keep intact | |||
| all the notices that refer to this License and to the absence of any | |||
| warranty; and distribute a copy of this License along with the | |||
| Library. | |||
| You may charge a fee for the physical act of transferring a copy, | |||
| and you may at your option offer warranty protection in exchange for a | |||
| fee. | |||
| 2. You may modify your copy or copies of the Library or any portion | |||
| of it, thus forming a work based on the Library, and copy and | |||
| distribute such modifications or work under the terms of Section 1 | |||
| above, provided that you also meet all of these conditions: | |||
| a) The modified work must itself be a software library. | |||
| b) You must cause the files modified to carry prominent notices | |||
| stating that you changed the files and the date of any change. | |||
| c) You must cause the whole of the work to be licensed at no | |||
| charge to all third parties under the terms of this License. | |||
| d) If a facility in the modified Library refers to a function or a | |||
| table of data to be supplied by an application program that uses | |||
| the facility, other than as an argument passed when the facility | |||
| is invoked, then you must make a good faith effort to ensure that, | |||
| in the event an application does not supply such function or | |||
| table, the facility still operates, and performs whatever part of | |||
| its purpose remains meaningful. | |||
| (For example, a function in a library to compute square roots has | |||
| a purpose that is entirely well-defined independent of the | |||
| application. Therefore, Subsection 2d requires that any | |||
| application-supplied function or table used by this function must | |||
| be optional: if the application does not supply it, the square | |||
| root function must still compute square roots.) | |||
| These requirements apply to the modified work as a whole. If | |||
| identifiable sections of that work are not derived from the Library, | |||
| and can be reasonably considered independent and separate works in | |||
| themselves, then this License, and its terms, do not apply to those | |||
| sections when you distribute them as separate works. But when you | |||
| distribute the same sections as part of a whole which is a work based | |||
| on the Library, the distribution of the whole must be on the terms of | |||
| this License, whose permissions for other licensees extend to the | |||
| entire whole, and thus to each and every part regardless of who wrote | |||
| it. | |||
| Thus, it is not the intent of this section to claim rights or contest | |||
| your rights to work written entirely by you; rather, the intent is to | |||
| exercise the right to control the distribution of derivative or | |||
| collective works based on the Library. | |||
| In addition, mere aggregation of another work not based on the Library | |||
| with the Library (or with a work based on the Library) on a volume of | |||
| a storage or distribution medium does not bring the other work under | |||
| the scope of this License. | |||
| 3. You may opt to apply the terms of the ordinary GNU General Public | |||
| License instead of this License to a given copy of the Library. To do | |||
| this, you must alter all the notices that refer to this License, so | |||
| that they refer to the ordinary GNU General Public License, version 2, | |||
| instead of to this License. (If a newer version than version 2 of the | |||
| ordinary GNU General Public License has appeared, then you can specify | |||
| that version instead if you wish.) Do not make any other change in | |||
| these notices. | |||
| Once this change is made in a given copy, it is irreversible for | |||
| that copy, so the ordinary GNU General Public License applies to all | |||
| subsequent copies and derivative works made from that copy. | |||
| This option is useful when you wish to copy part of the code of | |||
| the Library into a program that is not a library. | |||
| 4. You may copy and distribute the Library (or a portion or | |||
| derivative of it, under Section 2) in object code or executable form | |||
| under the terms of Sections 1 and 2 above provided that you accompany | |||
| it with the complete corresponding machine-readable source code, which | |||
| must be distributed under the terms of Sections 1 and 2 above on a | |||
| medium customarily used for software interchange. | |||
| If distribution of object code is made by offering access to copy | |||
| from a designated place, then offering equivalent access to copy the | |||
| source code from the same place satisfies the requirement to | |||
| distribute the source code, even though third parties are not | |||
| compelled to copy the source along with the object code. | |||
| 5. A program that contains no derivative of any portion of the | |||
| Library, but is designed to work with the Library by being compiled or | |||
| linked with it, is called a "work that uses the Library". Such a | |||
| work, in isolation, is not a derivative work of the Library, and | |||
| therefore falls outside the scope of this License. | |||
| However, linking a "work that uses the Library" with the Library | |||
| creates an executable that is a derivative of the Library (because it | |||
| contains portions of the Library), rather than a "work that uses the | |||
| library". The executable is therefore covered by this License. | |||
| Section 6 states terms for distribution of such executables. | |||
| When a "work that uses the Library" uses material from a header file | |||
| that is part of the Library, the object code for the work may be a | |||
| derivative work of the Library even though the source code is not. | |||
| Whether this is true is especially significant if the work can be | |||
| linked without the Library, or if the work is itself a library. The | |||
| threshold for this to be true is not precisely defined by law. | |||
| If such an object file uses only numerical parameters, data | |||
| structure layouts and accessors, and small macros and small inline | |||
| functions (ten lines or less in length), then the use of the object | |||
| file is unrestricted, regardless of whether it is legally a derivative | |||
| work. (Executables containing this object code plus portions of the | |||
| Library will still fall under Section 6.) | |||
| Otherwise, if the work is a derivative of the Library, you may | |||
| distribute the object code for the work under the terms of Section 6. | |||
| Any executables containing that work also fall under Section 6, | |||
| whether or not they are linked directly with the Library itself. | |||
| 6. As an exception to the Sections above, you may also combine or | |||
| link a "work that uses the Library" with the Library to produce a | |||
| work containing portions of the Library, and distribute that work | |||
| under terms of your choice, provided that the terms permit | |||
| modification of the work for the customer's own use and reverse | |||
| engineering for debugging such modifications. | |||
| You must give prominent notice with each copy of the work that the | |||
| Library is used in it and that the Library and its use are covered by | |||
| this License. You must supply a copy of this License. If the work | |||
| during execution displays copyright notices, you must include the | |||
| copyright notice for the Library among them, as well as a reference | |||
| directing the user to the copy of this License. Also, you must do one | |||
| of these things: | |||
| a) Accompany the work with the complete corresponding | |||
| machine-readable source code for the Library including whatever | |||
| changes were used in the work (which must be distributed under | |||
| Sections 1 and 2 above); and, if the work is an executable linked | |||
| with the Library, with the complete machine-readable "work that | |||
| uses the Library", as object code and/or source code, so that the | |||
| user can modify the Library and then relink to produce a modified | |||
| executable containing the modified Library. (It is understood | |||
| that the user who changes the contents of definitions files in the | |||
| Library will not necessarily be able to recompile the application | |||
| to use the modified definitions.) | |||
| b) Use a suitable shared library mechanism for linking with the | |||
| Library. A suitable mechanism is one that (1) uses at run time a | |||
| copy of the library already present on the user's computer system, | |||
| rather than copying library functions into the executable, and (2) | |||
| will operate properly with a modified version of the library, if | |||
| the user installs one, as long as the modified version is | |||
| interface-compatible with the version that the work was made with. | |||
| c) Accompany the work with a written offer, valid for at | |||
| least three years, to give the same user the materials | |||
| specified in Subsection 6a, above, for a charge no more | |||
| than the cost of performing this distribution. | |||
| d) If distribution of the work is made by offering access to copy | |||
| from a designated place, offer equivalent access to copy the above | |||
| specified materials from the same place. | |||
| e) Verify that the user has already received a copy of these | |||
| materials or that you have already sent this user a copy. | |||
| For an executable, the required form of the "work that uses the | |||
| Library" must include any data and utility programs needed for | |||
| reproducing the executable from it. However, as a special exception, | |||
| the materials to be distributed need not include anything that is | |||
| normally distributed (in either source or binary form) with the major | |||
| components (compiler, kernel, and so on) of the operating system on | |||
| which the executable runs, unless that component itself accompanies | |||
| the executable. | |||
| It may happen that this requirement contradicts the license | |||
| restrictions of other proprietary libraries that do not normally | |||
| accompany the operating system. Such a contradiction means you cannot | |||
| use both them and the Library together in an executable that you | |||
| distribute. | |||
| 7. You may place library facilities that are a work based on the | |||
| Library side-by-side in a single library together with other library | |||
| facilities not covered by this License, and distribute such a combined | |||
| library, provided that the separate distribution of the work based on | |||
| the Library and of the other library facilities is otherwise | |||
| permitted, and provided that you do these two things: | |||
| a) Accompany the combined library with a copy of the same work | |||
| based on the Library, uncombined with any other library | |||
| facilities. This must be distributed under the terms of the | |||
| Sections above. | |||
| b) Give prominent notice with the combined library of the fact | |||
| that part of it is a work based on the Library, and explaining | |||
| where to find the accompanying uncombined form of the same work. | |||
| 8. You may not copy, modify, sublicense, link with, or distribute | |||
| the Library except as expressly provided under this License. Any | |||
| attempt otherwise to copy, modify, sublicense, link with, or | |||
| distribute the Library is void, and will automatically terminate your | |||
| rights under this License. However, parties who have received copies, | |||
| or rights, from you under this License will not have their licenses | |||
| terminated so long as such parties remain in full compliance. | |||
| 9. You are not required to accept this License, since you have not | |||
| signed it. However, nothing else grants you permission to modify or | |||
| distribute the Library or its derivative works. These actions are | |||
| prohibited by law if you do not accept this License. Therefore, by | |||
| modifying or distributing the Library (or any work based on the | |||
| Library), you indicate your acceptance of this License to do so, and | |||
| all its terms and conditions for copying, distributing or modifying | |||
| the Library or works based on it. | |||
| 10. Each time you redistribute the Library (or any work based on the | |||
| Library), the recipient automatically receives a license from the | |||
| original licensor to copy, distribute, link with or modify the Library | |||
| subject to these terms and conditions. You may not impose any further | |||
| restrictions on the recipients' exercise of the rights granted herein. | |||
| You are not responsible for enforcing compliance by third parties with | |||
| this License. | |||
| 11. If, as a consequence of a court judgment or allegation of patent | |||
| infringement or for any other reason (not limited to patent issues), | |||
| conditions are imposed on you (whether by court order, agreement or | |||
| otherwise) that contradict the conditions of this License, they do not | |||
| excuse you from the conditions of this License. If you cannot | |||
| distribute so as to satisfy simultaneously your obligations under this | |||
| License and any other pertinent obligations, then as a consequence you | |||
| may not distribute the Library at all. For example, if a patent | |||
| license would not permit royalty-free redistribution of the Library by | |||
| all those who receive copies directly or indirectly through you, then | |||
| the only way you could satisfy both it and this License would be to | |||
| refrain entirely from distribution of the Library. | |||
| If any portion of this section is held invalid or unenforceable under any | |||
| particular circumstance, the balance of the section is intended to apply, | |||
| and the section as a whole is intended to apply in other circumstances. | |||
| It is not the purpose of this section to induce you to infringe any | |||
| patents or other property right claims or to contest validity of any | |||
| such claims; this section has the sole purpose of protecting the | |||
| integrity of the free software distribution system which is | |||
| implemented by public license practices. Many people have made | |||
| generous contributions to the wide range of software distributed | |||
| through that system in reliance on consistent application of that | |||
| system; it is up to the author/donor to decide if he or she is willing | |||
| to distribute software through any other system and a licensee cannot | |||
| impose that choice. | |||
| This section is intended to make thoroughly clear what is believed to | |||
| be a consequence of the rest of this License. | |||
| 12. If the distribution and/or use of the Library is restricted in | |||
| certain countries either by patents or by copyrighted interfaces, the | |||
| original copyright holder who places the Library under this License may add | |||
| an explicit geographical distribution limitation excluding those countries, | |||
| so that distribution is permitted only in or among countries not thus | |||
| excluded. In such case, this License incorporates the limitation as if | |||
| written in the body of this License. | |||
| 13. The Free Software Foundation may publish revised and/or new | |||
| versions of the Lesser General Public License from time to time. | |||
| Such new versions will be similar in spirit to the present version, | |||
| but may differ in detail to address new problems or concerns. | |||
| Each version is given a distinguishing version number. If the Library | |||
| specifies a version number of this License which applies to it and | |||
| "any later version", you have the option of following the terms and | |||
| conditions either of that version or of any later version published by | |||
| the Free Software Foundation. If the Library does not specify a | |||
| license version number, you may choose any version ever published by | |||
| the Free Software Foundation. | |||
| 14. If you wish to incorporate parts of the Library into other free | |||
| programs whose distribution conditions are incompatible with these, | |||
| write to the author to ask for permission. For software which is | |||
| copyrighted by the Free Software Foundation, write to the Free | |||
| Software Foundation; we sometimes make exceptions for this. Our | |||
| decision will be guided by the two goals of preserving the free status | |||
| of all derivatives of our free software and of promoting the sharing | |||
| and reuse of software generally. | |||
| NO WARRANTY | |||
| 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO | |||
| WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. | |||
| EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR | |||
| OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY | |||
| KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE | |||
| IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | |||
| PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE | |||
| LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME | |||
| THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. | |||
| 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN | |||
| WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY | |||
| AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU | |||
| FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR | |||
| CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE | |||
| LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING | |||
| RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A | |||
| FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF | |||
| SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH | |||
| DAMAGES. | |||
| END OF TERMS AND CONDITIONS | |||
| How to Apply These Terms to Your New Libraries | |||
| If you develop a new library, and you want it to be of the greatest | |||
| possible use to the public, we recommend making it free software that | |||
| everyone can redistribute and change. You can do so by permitting | |||
| redistribution under these terms (or, alternatively, under the terms of the | |||
| ordinary General Public License). | |||
| To apply these terms, attach the following notices to the library. It is | |||
| safest to attach them to the start of each source file to most effectively | |||
| convey the exclusion of warranty; and each file should have at least the | |||
| "copyright" line and a pointer to where the full notice is found. | |||
| {description} | |||
| Copyright (C) {year} {fullname} | |||
| This library is free software; you can redistribute it and/or | |||
| modify it under the terms of the GNU Lesser General Public | |||
| License as published by the Free Software Foundation; either | |||
| version 2.1 of the License, or (at your option) any later version. | |||
| This library is distributed in the hope that it will be useful, | |||
| but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
| Lesser General Public License for more details. | |||
| You should have received a copy of the GNU Lesser General Public | |||
| License along with this library; if not, write to the Free Software | |||
| Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 | |||
| USA | |||
| Also add information on how to contact you by electronic and paper mail. | |||
| You should also get your employer (if you work as a programmer) or your | |||
| school, if any, to sign a "copyright disclaimer" for the library, if | |||
| necessary. Here is a sample; alter the names: | |||
| Yoyodyne, Inc., hereby disclaims all copyright interest in the | |||
| library `Frob' (a library for tweaking knobs) written by James Random | |||
| Hacker. | |||
| {signature of Ty Coon}, 1 April 1990 | |||
| Ty Coon, President of Vice | |||
| That's all there is to it! | |||
| @@ -0,0 +1,165 @@ | |||
| GNU LESSER GENERAL PUBLIC LICENSE | |||
| Version 3, 29 June 2007 | |||
| Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> | |||
| Everyone is permitted to copy and distribute verbatim copies | |||
| of this license document, but changing it is not allowed. | |||
| This version of the GNU Lesser General Public License incorporates | |||
| the terms and conditions of version 3 of the GNU General Public | |||
| License, supplemented by the additional permissions listed below. | |||
| 0. Additional Definitions. | |||
| As used herein, "this License" refers to version 3 of the GNU Lesser | |||
| General Public License, and the "GNU GPL" refers to version 3 of the GNU | |||
| General Public License. | |||
| "The Library" refers to a covered work governed by this License, | |||
| other than an Application or a Combined Work as defined below. | |||
| An "Application" is any work that makes use of an interface provided | |||
| by the Library, but which is not otherwise based on the Library. | |||
| Defining a subclass of a class defined by the Library is deemed a mode | |||
| of using an interface provided by the Library. | |||
| A "Combined Work" is a work produced by combining or linking an | |||
| Application with the Library. The particular version of the Library | |||
| with which the Combined Work was made is also called the "Linked | |||
| Version". | |||
| The "Minimal Corresponding Source" for a Combined Work means the | |||
| Corresponding Source for the Combined Work, excluding any source code | |||
| for portions of the Combined Work that, considered in isolation, are | |||
| based on the Application, and not on the Linked Version. | |||
| The "Corresponding Application Code" for a Combined Work means the | |||
| object code and/or source code for the Application, including any data | |||
| and utility programs needed for reproducing the Combined Work from the | |||
| Application, but excluding the System Libraries of the Combined Work. | |||
| 1. Exception to Section 3 of the GNU GPL. | |||
| You may convey a covered work under sections 3 and 4 of this License | |||
| without being bound by section 3 of the GNU GPL. | |||
| 2. Conveying Modified Versions. | |||
| If you modify a copy of the Library, and, in your modifications, a | |||
| facility refers to a function or data to be supplied by an Application | |||
| that uses the facility (other than as an argument passed when the | |||
| facility is invoked), then you may convey a copy of the modified | |||
| version: | |||
| a) under this License, provided that you make a good faith effort to | |||
| ensure that, in the event an Application does not supply the | |||
| function or data, the facility still operates, and performs | |||
| whatever part of its purpose remains meaningful, or | |||
| b) under the GNU GPL, with none of the additional permissions of | |||
| this License applicable to that copy. | |||
| 3. Object Code Incorporating Material from Library Header Files. | |||
| The object code form of an Application may incorporate material from | |||
| a header file that is part of the Library. You may convey such object | |||
| code under terms of your choice, provided that, if the incorporated | |||
| material is not limited to numerical parameters, data structure | |||
| layouts and accessors, or small macros, inline functions and templates | |||
| (ten or fewer lines in length), you do both of the following: | |||
| a) Give prominent notice with each copy of the object code that the | |||
| Library is used in it and that the Library and its use are | |||
| covered by this License. | |||
| b) Accompany the object code with a copy of the GNU GPL and this license | |||
| document. | |||
| 4. Combined Works. | |||
| You may convey a Combined Work under terms of your choice that, | |||
| taken together, effectively do not restrict modification of the | |||
| portions of the Library contained in the Combined Work and reverse | |||
| engineering for debugging such modifications, if you also do each of | |||
| the following: | |||
| a) Give prominent notice with each copy of the Combined Work that | |||
| the Library is used in it and that the Library and its use are | |||
| covered by this License. | |||
| b) Accompany the Combined Work with a copy of the GNU GPL and this license | |||
| document. | |||
| c) For a Combined Work that displays copyright notices during | |||
| execution, include the copyright notice for the Library among | |||
| these notices, as well as a reference directing the user to the | |||
| copies of the GNU GPL and this license document. | |||
| d) Do one of the following: | |||
| 0) Convey the Minimal Corresponding Source under the terms of this | |||
| License, and the Corresponding Application Code in a form | |||
| suitable for, and under terms that permit, the user to | |||
| recombine or relink the Application with a modified version of | |||
| the Linked Version to produce a modified Combined Work, in the | |||
| manner specified by section 6 of the GNU GPL for conveying | |||
| Corresponding Source. | |||
| 1) Use a suitable shared library mechanism for linking with the | |||
| Library. A suitable mechanism is one that (a) uses at run time | |||
| a copy of the Library already present on the user's computer | |||
| system, and (b) will operate properly with a modified version | |||
| of the Library that is interface-compatible with the Linked | |||
| Version. | |||
| e) Provide Installation Information, but only if you would otherwise | |||
| be required to provide such information under section 6 of the | |||
| GNU GPL, and only to the extent that such information is | |||
| necessary to install and execute a modified version of the | |||
| Combined Work produced by recombining or relinking the | |||
| Application with a modified version of the Linked Version. (If | |||
| you use option 4d0, the Installation Information must accompany | |||
| the Minimal Corresponding Source and Corresponding Application | |||
| Code. If you use option 4d1, you must provide the Installation | |||
| Information in the manner specified by section 6 of the GNU GPL | |||
| for conveying Corresponding Source.) | |||
| 5. Combined Libraries. | |||
| You may place library facilities that are a work based on the | |||
| Library side by side in a single library together with other library | |||
| facilities that are not Applications and are not covered by this | |||
| License, and convey such a combined library under terms of your | |||
| choice, if you do both of the following: | |||
| a) Accompany the combined library with a copy of the same work based | |||
| on the Library, uncombined with any other library facilities, | |||
| conveyed under the terms of this License. | |||
| b) Give prominent notice with the combined library that part of it | |||
| is a work based on the Library, and explaining where to find the | |||
| accompanying uncombined form of the same work. | |||
| 6. Revised Versions of the GNU Lesser General Public License. | |||
| The Free Software Foundation may publish revised and/or new versions | |||
| of the GNU Lesser General Public License from time to time. Such new | |||
| versions will be similar in spirit to the present version, but may | |||
| differ in detail to address new problems or concerns. | |||
| Each version is given a distinguishing version number. If the | |||
| Library as you received it specifies that a certain numbered version | |||
| of the GNU Lesser General Public License "or any later version" | |||
| applies to it, you have the option of following the terms and | |||
| conditions either of that published version or of any later version | |||
| published by the Free Software Foundation. If the Library as you | |||
| received it does not specify a version number of the GNU Lesser | |||
| General Public License, you may choose any version of the GNU Lesser | |||
| General Public License ever published by the Free Software Foundation. | |||
| If the Library as you received it specifies that a proxy can decide | |||
| whether future versions of the GNU Lesser General Public License shall | |||
| apply, that proxy's public statement of acceptance of any version is | |||
| permanent authorization for you to choose that version for the | |||
| Library. | |||
| @@ -0,0 +1,362 @@ | |||
| Mozilla Public License, version 2.0 | |||
| 1. Definitions | |||
| 1.1. "Contributor" | |||
| means each individual or legal entity that creates, contributes to the | |||
| creation of, or owns Covered Software. | |||
| 1.2. "Contributor Version" | |||
| means the combination of the Contributions of others (if any) used by a | |||
| Contributor and that particular Contributor's Contribution. | |||
| 1.3. "Contribution" | |||
| means Covered Software of a particular Contributor. | |||
| 1.4. "Covered Software" | |||
| means Source Code Form to which the initial Contributor has attached the | |||
| notice in Exhibit A, the Executable Form of such Source Code Form, and | |||
| Modifications of such Source Code Form, in each case including portions | |||
| thereof. | |||
| 1.5. "Incompatible With Secondary Licenses" | |||
| means | |||
| a. that the initial Contributor has attached the notice described in | |||
| Exhibit B to the Covered Software; or | |||
| b. that the Covered Software was made available under the terms of | |||
| version 1.1 or earlier of the License, but not also under the terms of | |||
| a Secondary License. | |||
| 1.6. "Executable Form" | |||
| means any form of the work other than Source Code Form. | |||
| 1.7. "Larger Work" | |||
| means a work that combines Covered Software with other material, in a | |||
| separate file or files, that is not Covered Software. | |||
| 1.8. "License" | |||
| means this document. | |||
| 1.9. "Licensable" | |||
| means having the right to grant, to the maximum extent possible, whether | |||
| at the time of the initial grant or subsequently, any and all of the | |||
| rights conveyed by this License. | |||
| 1.10. "Modifications" | |||
| means any of the following: | |||
| a. any file in Source Code Form that results from an addition to, | |||
| deletion from, or modification of the contents of Covered Software; or | |||
| b. any new file in Source Code Form that contains any Covered Software. | |||
| 1.11. "Patent Claims" of a Contributor | |||
| means any patent claim(s), including without limitation, method, | |||
| process, and apparatus claims, in any patent Licensable by such | |||
| Contributor that would be infringed, but for the grant of the License, | |||
| by the making, using, selling, offering for sale, having made, import, | |||
| or transfer of either its Contributions or its Contributor Version. | |||
| 1.12. "Secondary License" | |||
| means either the GNU General Public License, Version 2.0, the GNU Lesser | |||
| General Public License, Version 2.1, the GNU Affero General Public | |||
| License, Version 3.0, or any later versions of those licenses. | |||
| 1.13. "Source Code Form" | |||
| means the form of the work preferred for making modifications. | |||
| 1.14. "You" (or "Your") | |||
| means an individual or a legal entity exercising rights under this | |||
| License. For legal entities, "You" includes any entity that controls, is | |||
| controlled by, or is under common control with You. For purposes of this | |||
| definition, "control" means (a) the power, direct or indirect, to cause | |||
| the direction or management of such entity, whether by contract or | |||
| otherwise, or (b) ownership of more than fifty percent (50%) of the | |||
| outstanding shares or beneficial ownership of such entity. | |||
| 2. License Grants and Conditions | |||
| 2.1. Grants | |||
| Each Contributor hereby grants You a world-wide, royalty-free, | |||
| non-exclusive license: | |||
| a. under intellectual property rights (other than patent or trademark) | |||
| Licensable by such Contributor to use, reproduce, make available, | |||
| modify, display, perform, distribute, and otherwise exploit its | |||
| Contributions, either on an unmodified basis, with Modifications, or | |||
| as part of a Larger Work; and | |||
| b. under Patent Claims of such Contributor to make, use, sell, offer for | |||
| sale, have made, import, and otherwise transfer either its | |||
| Contributions or its Contributor Version. | |||
| 2.2. Effective Date | |||
| The licenses granted in Section 2.1 with respect to any Contribution | |||
| become effective for each Contribution on the date the Contributor first | |||
| distributes such Contribution. | |||
| 2.3. Limitations on Grant Scope | |||
| The licenses granted in this Section 2 are the only rights granted under | |||
| this License. No additional rights or licenses will be implied from the | |||
| distribution or licensing of Covered Software under this License. | |||
| Notwithstanding Section 2.1(b) above, no patent license is granted by a | |||
| Contributor: | |||
| a. for any code that a Contributor has removed from Covered Software; or | |||
| b. for infringements caused by: (i) Your and any other third party's | |||
| modifications of Covered Software, or (ii) the combination of its | |||
| Contributions with other software (except as part of its Contributor | |||
| Version); or | |||
| c. under Patent Claims infringed by Covered Software in the absence of | |||
| its Contributions. | |||
| This License does not grant any rights in the trademarks, service marks, | |||
| or logos of any Contributor (except as may be necessary to comply with | |||
| the notice requirements in Section 3.4). | |||
| 2.4. Subsequent Licenses | |||
| No Contributor makes additional grants as a result of Your choice to | |||
| distribute the Covered Software under a subsequent version of this | |||
| License (see Section 10.2) or under the terms of a Secondary License (if | |||
| permitted under the terms of Section 3.3). | |||
| 2.5. Representation | |||
| Each Contributor represents that the Contributor believes its | |||
| Contributions are its original creation(s) or it has sufficient rights to | |||
| grant the rights to its Contributions conveyed by this License. | |||
| 2.6. Fair Use | |||
| This License is not intended to limit any rights You have under | |||
| applicable copyright doctrines of fair use, fair dealing, or other | |||
| equivalents. | |||
| 2.7. Conditions | |||
| Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in | |||
| Section 2.1. | |||
| 3. Responsibilities | |||
| 3.1. Distribution of Source Form | |||
| All distribution of Covered Software in Source Code Form, including any | |||
| Modifications that You create or to which You contribute, must be under | |||
| the terms of this License. You must inform recipients that the Source | |||
| Code Form of the Covered Software is governed by the terms of this | |||
| License, and how they can obtain a copy of this License. You may not | |||
| attempt to alter or restrict the recipients' rights in the Source Code | |||
| Form. | |||
| 3.2. Distribution of Executable Form | |||
| If You distribute Covered Software in Executable Form then: | |||
| a. such Covered Software must also be made available in Source Code Form, | |||
| as described in Section 3.1, and You must inform recipients of the | |||
| Executable Form how they can obtain a copy of such Source Code Form by | |||
| reasonable means in a timely manner, at a charge no more than the cost | |||
| of distribution to the recipient; and | |||
| b. You may distribute such Executable Form under the terms of this | |||
| License, or sublicense it under different terms, provided that the | |||
| license for the Executable Form does not attempt to limit or alter the | |||
| recipients' rights in the Source Code Form under this License. | |||
| 3.3. Distribution of a Larger Work | |||
| You may create and distribute a Larger Work under terms of Your choice, | |||
| provided that You also comply with the requirements of this License for | |||
| the Covered Software. If the Larger Work is a combination of Covered | |||
| Software with a work governed by one or more Secondary Licenses, and the | |||
| Covered Software is not Incompatible With Secondary Licenses, this | |||
| License permits You to additionally distribute such Covered Software | |||
| under the terms of such Secondary License(s), so that the recipient of | |||
| the Larger Work may, at their option, further distribute the Covered | |||
| Software under the terms of either this License or such Secondary | |||
| License(s). | |||
| 3.4. Notices | |||
| You may not remove or alter the substance of any license notices | |||
| (including copyright notices, patent notices, disclaimers of warranty, or | |||
| limitations of liability) contained within the Source Code Form of the | |||
| Covered Software, except that You may alter any license notices to the | |||
| extent required to remedy known factual inaccuracies. | |||
| 3.5. Application of Additional Terms | |||
| You may choose to offer, and to charge a fee for, warranty, support, | |||
| indemnity or liability obligations to one or more recipients of Covered | |||
| Software. However, You may do so only on Your own behalf, and not on | |||
| behalf of any Contributor. You must make it absolutely clear that any | |||
| such warranty, support, indemnity, or liability obligation is offered by | |||
| You alone, and You hereby agree to indemnify every Contributor for any | |||
| liability incurred by such Contributor as a result of warranty, support, | |||
| indemnity or liability terms You offer. You may include additional | |||
| disclaimers of warranty and limitations of liability specific to any | |||
| jurisdiction. | |||
| 4. Inability to Comply Due to Statute or Regulation | |||
| If it is impossible for You to comply with any of the terms of this License | |||
| with respect to some or all of the Covered Software due to statute, | |||
| judicial order, or regulation then You must: (a) comply with the terms of | |||
| this License to the maximum extent possible; and (b) describe the | |||
| limitations and the code they affect. Such description must be placed in a | |||
| text file included with all distributions of the Covered Software under | |||
| this License. Except to the extent prohibited by statute or regulation, | |||
| such description must be sufficiently detailed for a recipient of ordinary | |||
| skill to be able to understand it. | |||
| 5. Termination | |||
| 5.1. The rights granted under this License will terminate automatically if You | |||
| fail to comply with any of its terms. However, if You become compliant, | |||
| then the rights granted under this License from a particular Contributor | |||
| are reinstated (a) provisionally, unless and until such Contributor | |||
| explicitly and finally terminates Your grants, and (b) on an ongoing | |||
| basis, if such Contributor fails to notify You of the non-compliance by | |||
| some reasonable means prior to 60 days after You have come back into | |||
| compliance. Moreover, Your grants from a particular Contributor are | |||
| reinstated on an ongoing basis if such Contributor notifies You of the | |||
| non-compliance by some reasonable means, this is the first time You have | |||
| received notice of non-compliance with this License from such | |||
| Contributor, and You become compliant prior to 30 days after Your receipt | |||
| of the notice. | |||
| 5.2. If You initiate litigation against any entity by asserting a patent | |||
| infringement claim (excluding declaratory judgment actions, | |||
| counter-claims, and cross-claims) alleging that a Contributor Version | |||
| directly or indirectly infringes any patent, then the rights granted to | |||
| You by any and all Contributors for the Covered Software under Section | |||
| 2.1 of this License shall terminate. | |||
| 5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user | |||
| license agreements (excluding distributors and resellers) which have been | |||
| validly granted by You or Your distributors under this License prior to | |||
| termination shall survive termination. | |||
| 6. Disclaimer of Warranty | |||
| Covered Software is provided under this License on an "as is" basis, | |||
| without warranty of any kind, either expressed, implied, or statutory, | |||
| including, without limitation, warranties that the Covered Software is free | |||
| of defects, merchantable, fit for a particular purpose or non-infringing. | |||
| The entire risk as to the quality and performance of the Covered Software | |||
| is with You. Should any Covered Software prove defective in any respect, | |||
| You (not any Contributor) assume the cost of any necessary servicing, | |||
| repair, or correction. This disclaimer of warranty constitutes an essential | |||
| part of this License. No use of any Covered Software is authorized under | |||
| this License except under this disclaimer. | |||
| 7. Limitation of Liability | |||
| Under no circumstances and under no legal theory, whether tort (including | |||
| negligence), contract, or otherwise, shall any Contributor, or anyone who | |||
| distributes Covered Software as permitted above, be liable to You for any | |||
| direct, indirect, special, incidental, or consequential damages of any | |||
| character including, without limitation, damages for lost profits, loss of | |||
| goodwill, work stoppage, computer failure or malfunction, or any and all | |||
| other commercial damages or losses, even if such party shall have been | |||
| informed of the possibility of such damages. This limitation of liability | |||
| shall not apply to liability for death or personal injury resulting from | |||
| such party's negligence to the extent applicable law prohibits such | |||
| limitation. Some jurisdictions do not allow the exclusion or limitation of | |||
| incidental or consequential damages, so this exclusion and limitation may | |||
| not apply to You. | |||
| 8. Litigation | |||
| Any litigation relating to this License may be brought only in the courts | |||
| of a jurisdiction where the defendant maintains its principal place of | |||
| business and such litigation shall be governed by laws of that | |||
| jurisdiction, without reference to its conflict-of-law provisions. Nothing | |||
| in this Section shall prevent a party's ability to bring cross-claims or | |||
| counter-claims. | |||
| 9. Miscellaneous | |||
| This License represents the complete agreement concerning the subject | |||
| matter hereof. If any provision of this License is held to be | |||
| unenforceable, such provision shall be reformed only to the extent | |||
| necessary to make it enforceable. Any law or regulation which provides that | |||
| the language of a contract shall be construed against the drafter shall not | |||
| be used to construe this License against a Contributor. | |||
| 10. Versions of the License | |||
| 10.1. New Versions | |||
| Mozilla Foundation is the license steward. Except as provided in Section | |||
| 10.3, no one other than the license steward has the right to modify or | |||
| publish new versions of this License. Each version will be given a | |||
| distinguishing version number. | |||
| 10.2. Effect of New Versions | |||
| You may distribute the Covered Software under the terms of the version | |||
| of the License under which You originally received the Covered Software, | |||
| or under the terms of any subsequent version published by the license | |||
| steward. | |||
| 10.3. Modified Versions | |||
| If you create software not governed by this License, and you want to | |||
| create a new license for such software, you may create and use a | |||
| modified version of this License if you rename the license and remove | |||
| any references to the name of the license steward (except to note that | |||
| such modified license differs from this License). | |||
| 10.4. Distributing Source Code Form that is Incompatible With Secondary | |||
| Licenses If You choose to distribute Source Code Form that is | |||
| Incompatible With Secondary Licenses under the terms of this version of | |||
| the License, the notice described in Exhibit B of this License must be | |||
| attached. | |||
| Exhibit A - Source Code Form License Notice | |||
| This Source Code Form is subject to the | |||
| terms of the Mozilla Public License, v. | |||
| 2.0. If a copy of the MPL was not | |||
| distributed with this file, You can | |||
| obtain one at | |||
| http://mozilla.org/MPL/2.0/. | |||
| If it is not possible or desirable to put the notice in a particular file, | |||
| then You may include the notice in a location (such as a LICENSE file in a | |||
| relevant directory) where a recipient would be likely to look for such a | |||
| notice. | |||
| You may add additional accurate notices of copyright ownership. | |||
| Exhibit B - "Incompatible With Secondary Licenses" Notice | |||
| This Source Code Form is "Incompatible | |||
| With Secondary Licenses", as defined by | |||
| the Mozilla Public License, v. 2.0. | |||
| @@ -0,0 +1,179 @@ | |||
| app_desc = A painless self-hosted Git service written in Go | |||
| home = Home | |||
| dashboard = Dashboard | |||
| explore = Explore | |||
| help = Help | |||
| sign_in = Sign In | |||
| sign_out = Sign Out | |||
| sign_up = Sign Up | |||
| register = Register | |||
| website = Website | |||
| version = Version | |||
| page = Page | |||
| template = Template | |||
| language = Language | |||
| username = Username | |||
| email = E-mail | |||
| password = Password | |||
| re_type = Re-Type | |||
| captcha = Captcha | |||
| repository = Repository | |||
| organization = Organization | |||
| mirror = Mirror | |||
| new_repo = New Repository | |||
| new_migrate = New Migration | |||
| new_org = New Organization | |||
| manage_org = Manage Organizations | |||
| admin_panel = Admin Panel | |||
| account_settings = Account Settings | |||
| settings = Settings | |||
| news_feed = News Feed | |||
| pull_requests = Pull Requests | |||
| issues = Issues | |||
| cancel = Cancel | |||
| [home] | |||
| uname_holder = Username or E-mail | |||
| password_holder = Password | |||
| switch_dashboard_context = Switch Dashboard Context | |||
| my_repos = My Repositories | |||
| collaborative_repos = Collaborative Repositories | |||
| my_orgs = My Organizations | |||
| my_mirrors = My Mirrors | |||
| [auth] | |||
| create_new_account = Create New Account | |||
| register_hepler_msg = Already have an account? Sign in now! | |||
| disable_register_prompt = Sorry, registration has been disabled. Please contact the site administrator. | |||
| remember_me = Remember Me | |||
| forget_password = Fotget password? | |||
| sign_up_now = Need an account? Sign up now. | |||
| [form] | |||
| UserName = Username | |||
| Email = E-mail address | |||
| Password = Password | |||
| Retype = Re-type password | |||
| SSHTitle = SSH key name | |||
| require_error = ` cannot be empty.` | |||
| alpha_dash_error = ` must be valid alpha or numeric or dash(-_) characters.` | |||
| alpha_dash_dot_error = ` must be valid alpha or numeric or dash(-_) or dot characters.` | |||
| min_size_error = ` must contain at least %s characters.` | |||
| max_size_error = ` must contain at most %s characters.` | |||
| email_error = ` is not a valid e-mail address.` | |||
| url_error = ` is not a valid URL.` | |||
| unknown_error = Unknown error: | |||
| captcha_incorrect = Captcha didn't match. | |||
| password_not_match = Password and re-type password are not same. | |||
| username_been_taken = Username has been already taken. | |||
| repo_name_been_taken = Repository name has been already taken. | |||
| email_been_used = E-mail address has been already used. | |||
| ssh_key_been_used = Public key name has been used. | |||
| illegal_username = Your username contains illegal characters. | |||
| illegal_repo_name = Repository name contains illegal characters. | |||
| username_password_incorrect = Username or password is not correct. | |||
| invalid_ssh_key = Sorry, we're not able to verify your SSH key: %s | |||
| still_own_repo = Your account still have ownership of repository, you have to delete or transfer them first. | |||
| [settings] | |||
| profile = Profile | |||
| password = Password | |||
| ssh_keys = SSH Keys | |||
| social = Social Accounts | |||
| orgs = Organizations | |||
| delete = Delete Accoount | |||
| public_profile = Public Profile | |||
| profile_desc = Your Email address is public and will be used for any account related notifications, and any web based operations made via the site. | |||
| full_name = Full Name | |||
| website = Website | |||
| location = Location | |||
| update_profile = Update Profile | |||
| update_profile_success = Your profile has been successfully updated. | |||
| change_password = Change Password | |||
| old_password = Current Password | |||
| new_password = New Password | |||
| password_incorrect = Current password is not correct. | |||
| change_password_success = Password is changed successfully. You can now sign in via new password. | |||
| manage_ssh_keys = Manage SSH Keys | |||
| add_key = Add Key | |||
| ssh_desc = This is a list of SSH keys associated with your account. Remove any keys that you do not recognize. | |||
| ssh_helper = <strong>Need help?</strong> Check out our guide to <a href="https://help.github.com/articles/generating-ssh-keys">generating SSH keys</a> or troubleshoot <a href="https://help.github.com/ssh-issues/">common SSH Problems</a>. | |||
| add_new_key = Add SSH Key | |||
| key_name = Key Name | |||
| key_content = Content | |||
| add_key_success = New SSH Key has been added! | |||
| delete_key = Delete | |||
| add_on = Added on | |||
| last_used = Last used on | |||
| no_activity = No recent activity | |||
| manage_orgs = Manage Organizations | |||
| manage_social = Manage Associated Social Accounts | |||
| delete_account = Delete Your Account | |||
| delete_prompt = The operation will delete your account permanently, and <strong>CANNOT</strong> be undo! | |||
| confirm_delete_account = Confirm Deletion | |||
| [repo] | |||
| owner = Owner | |||
| repo_name = Repository Name | |||
| repo_name_helper = Great repository names are short, memorable and <strong>unique</strong>. | |||
| visibility = Visibility | |||
| visiblity_helper = This repository is <span class="label label-red label-radius">Private</span> | |||
| repo_desc = Description | |||
| repo_lang = Language | |||
| repo_lang_helper = Select a .gitignore file | |||
| license = License | |||
| license_helper = Select a license file | |||
| init_readme = Initialize this repository with a README.md | |||
| create_repo = Create Repository | |||
| [action] | |||
| create_repo = created repository <a href="/%s">%s</a> | |||
| commit_repo = pushed to <a href="/%s/src/%s">%s</a> at <a href="/%s">%s</a> | |||
| create_issue = opened issue <a href="/%s/issues/%s">%s#%s</a> | |||
| comment_issue = commented on issue <a href="/%s/issues/%s">%s#%s</a> | |||
| [tool] | |||
| ago = ago | |||
| from_now = from now | |||
| now = now | |||
| 1s = 1 second %s | |||
| 1m = 1 mintue %s | |||
| 1h = 1 hour %s | |||
| 1d = 1 day %s | |||
| 1w = 1 week %s | |||
| 1mon = 1 month %s | |||
| 1y = 1 year %s | |||
| seconds = %d seconds %s | |||
| minutes = %d minutes %s | |||
| hours = %d hours %s | |||
| days = %d days %s | |||
| weeks = %d weeks %s | |||
| months = %d months %s | |||
| years = %d years %s | |||
| @@ -0,0 +1,178 @@ | |||
| app_desc = 基于 Go 语言的自助 Git 服务 | |||
| home = 首页 | |||
| dashboard = 控制面板 | |||
| explore = 探索 | |||
| help = 帮助 | |||
| sign_in = 登录 | |||
| sign_out = 退出 | |||
| sign_up = 注册 | |||
| register = 注册 | |||
| website = 官方网站 | |||
| version = 当前版本 | |||
| page = 页面 | |||
| template = 模板 | |||
| language = 语言选项 | |||
| username = 用户名 | |||
| email = 邮箱 | |||
| password = 密码 | |||
| re_type = 确认密码 | |||
| captcha = 验证码 | |||
| repository = 仓库 | |||
| organization = 组织 | |||
| mirror = 镜像 | |||
| new_repo = 创建新的仓库 | |||
| new_migrate = 迁移外部仓库 | |||
| new_org = 创建新的组织 | |||
| manage_org = 管理我的组织 | |||
| admin_panel = 管理面板 | |||
| account_settings = 帐户设置 | |||
| settings = 帐户设置 | |||
| news_feed = 最新活动 | |||
| pull_requests = 合并请求 | |||
| issues = 工单管理 | |||
| cancel = 取消 | |||
| [home] | |||
| uname_holder = 用户名或邮箱 | |||
| password_holder = 密码 | |||
| switch_dashboard_context = 切换控制面板用户 | |||
| my_repos = 我的仓库 | |||
| collaborative_repos = 参与协作的仓库 | |||
| my_orgs = 我的组织 | |||
| my_mirrors = 我的镜像 | |||
| [auth] | |||
| create_new_account = 创建帐户 | |||
| register_hepler_msg = 已经注册?立即登录! | |||
| disable_register_prompt = 对不起,注册功能已被关闭。请联系网站管理员。 | |||
| remember_me = 记住登录 | |||
| forget_password = 忘记密码? | |||
| sign_up_now = 还没帐户?马上注册。 | |||
| [form] | |||
| UserName = 用户名 | |||
| Email = 邮箱地址 | |||
| Password = 密码 | |||
| Retype = 确认密码 | |||
| SSHTitle = SSH 密钥名称 | |||
| require_error = 不能为空。 | |||
| alpha_dash_error = 必须为英文字母、阿拉伯数字或横线(-_)。 | |||
| alpha_dash_dot_error = 必须为英文字母、阿拉伯数字、横线(-_)或点。 | |||
| min_size_error = 长度最小为 %s 个字符。 | |||
| max_size_error = 长度最大为 %s 个字符。 | |||
| email_error = 不是一个有效的邮箱地址。 | |||
| url_error = 不是一个有效的 URL。 | |||
| unknown_error = 未知错误: | |||
| captcha_incorrect = 验证码未匹配。 | |||
| password_not_match = 密码与确认密码未匹配。 | |||
| username_been_taken = 用户名已经被占用。 | |||
| repo_name_been_taken = 仓库名称已经被占用。 | |||
| email_been_used = 邮箱地址已经被使用。 | |||
| ssh_key_been_used = SSH 密钥已经被使用。 | |||
| illegal_username = 您的用户名包含非法字符。 | |||
| illegal_repo_name = 仓库名称包含非法字符。 | |||
| username_password_incorrect = 用户名或密码不正确。 | |||
| invalid_ssh_key = 很抱歉,我们无法验证您输入的 SSH 密钥:%s | |||
| still_own_repo = 您的帐户仍然是某些仓库的拥有者,您必须先转移或删除它们才能执行删除帐户操作! | |||
| [settings] | |||
| profile = 个人信息 | |||
| password = 修改密码 | |||
| ssh_keys = 管理 SSH 密钥 | |||
| social = 社交帐号绑定 | |||
| orgs = 管理组织 | |||
| delete = 删除帐户 | |||
| public_profile = 公开信息 | |||
| profile_desc = 您的邮箱地址将会被公开,并被用于接收帐户的所有提醒和通知。 | |||
| full_name = 自定义名称 | |||
| website = 个人网站 | |||
| location = 所在地区 | |||
| update_profile = 更新信息 | |||
| update_profile_success = 您的个人信息已经更新成功! | |||
| change_password = 修改密码 | |||
| old_password = 当前密码 | |||
| new_password = 新的密码 | |||
| password_incorrect = 当前密码不正确! | |||
| change_password_success = 密码修改成功!您现在可以使用新的密码登录。 | |||
| manage_ssh_keys = 管理 SSH 密钥 | |||
| add_key = 增加密钥 | |||
| ssh_desc = 以下是与您帐户所关联的 SSH 密钥,如果您发现有陌生的密钥,请立即删除它! | |||
| ssh_helper = <strong>需要帮助?</strong> 请查看有关 <a href="https://help.github.com/articles/generating-ssh-keys">如何生成 SSH 密钥</a> 或 <a href="https://help.github.com/ssh-issues/">常见 SSH 问题</a> 寻找答案。 | |||
| add_new_key = 增加 SSH 密钥 | |||
| key_name = 密钥名称 | |||
| key_content = 密钥内容 | |||
| add_key_success = 新的 SSH 密钥添加成功! | |||
| delete_key = 删除 | |||
| add_on = 增加于 | |||
| last_used = 上次使用在 | |||
| no_activity = 没有最近活动 | |||
| manage_orgs = 管理我的组织 | |||
| manage_social = 管理关联社交帐户 | |||
| delete_account = 删除当前帐户 | |||
| delete_prompt = 删除操作会永久清除您的帐户信息,并且 <strong>不可恢复</strong>! | |||
| confirm_delete_account = 确认删除帐户 | |||
| [repo] | |||
| owner = 拥有者 | |||
| repo_name = 仓库名称 | |||
| repo_name_helper = 伟大的仓库名称一般都较短、令人深刻并且 <strong>独一无二</strong> 的。 | |||
| visibility = 可见性 | |||
| visiblity_helper = 本仓库将是 <span class="label label-red label-radius">私有的</span> | |||
| repo_desc = 仓库描述 | |||
| repo_lang = 仓库语言 | |||
| repo_lang_helper = 请选择 .gitignore 文件 | |||
| license = 授权许可 | |||
| license_helper = 请选择授权许可文件 | |||
| init_readme = 使用 README.md 文件初始化仓库 | |||
| create_repo = 创建仓库 | |||
| [action] | |||
| create_repo = 创建了仓库 <a href="/%s">%s</a> | |||
| commit_repo = 推送了 <a href="/%s/src/%s">%s</a> 分支的代码到 <a href="/%s">%s</a> | |||
| create_issue = 创建了工单 <a href="/%s/issues/%s">%s#%s</a> | |||
| comment_issue = 评论了工单 <a href="/%s/issues/%s">%s#%s</a> | |||
| [tool] | |||
| ago = 之前 | |||
| from_now = 之后 | |||
| now = 现在 | |||
| 1s = 1 秒%s | |||
| 1m = 1 分钟%s | |||
| 1h = 1 小时%s | |||
| 1d = 1 天%s | |||
| 1w = 1 周%s | |||
| 1mon = 1 月%s | |||
| 1y = 1 年%s | |||
| seconds = %d 秒%s | |||
| minutes = %d 分钟%s | |||
| hours = %d 小时%s | |||
| days = %d 天%s | |||
| weeks = %d 周%s | |||
| months = %d 月%s | |||
| years = %d 年%s | |||
| @@ -1,8 +0,0 @@ | |||
| [program:gogs] | |||
| user=git | |||
| command = /home/git/gogs/start.sh | |||
| directory = /home/git/gogs | |||
| autostart = true | |||
| stdout_logfile = /var/gogs.log | |||
| stderr_logfile = /var/gogs-error.log | |||
| environment=HOME="/home/git" | |||
| @@ -1,40 +0,0 @@ | |||
| ### Install Gogs With Docker | |||
| Deploying gogs in [Docker](http://www.docker.io/) is just as easy as eating a pie, what you do is just open the `dockerfiles/build.sh` file, replace the configs: | |||
| ``` | |||
| DB_TYPE="YOUR_DB_TYPE" # type of database, support 'mysql' and 'postgres' | |||
| MEM_TYPE="YOUR_MEM_TYPE" # type of memory database, support 'redis' and 'memcache' | |||
| DB_PASSWORD="YOUR_DB_PASSWORD" # The database password. | |||
| DB_RUN_NAME="YOUR_DB_RUN_NAME" # The --name option value when run the database image. | |||
| MEM_RUN_NAME="YOUR_MEM_RUN_NAME" # The --name option value when run the mem database image. | |||
| HOST_PORT="YOUR_HOST_PORT" # The port on host, which will be redirected to the port 3000 inside gogs container. | |||
| ``` | |||
| And run: | |||
| ``` | |||
| cd dockerfiles | |||
| ./build.sh | |||
| ``` | |||
| The build might take some time, just be paient. After it finishes, you will receive the message: | |||
| ``` | |||
| Now we have the MySQL image(running) and gogs image, use the follow command to start gogs service( the content might be different, according to your own configs): | |||
| docker run -i -t --link YOUR_DB_RUN_NAME:db --link YOUR_MEM_RUN_NAME:mem -p YOUR_HOST_PORT:3000 gogits/gogs | |||
| ``` | |||
| Just follow the message, run: | |||
| ``` | |||
| docker run -i -t --link YOUR_DB_RUN_NAME:db --link YOUR_MEM_RUN_NAME:mem -p YOUR_HOST_PORT:3000 gogits/gogs | |||
| ``` | |||
| Now we have gogs running! Open the browser and navigate to: | |||
| ``` | |||
| http://YOUR_HOST_IP:YOUR_HOST_PORT | |||
| ``` | |||
| Let's 'gogs'! | |||
| Ouya~ | |||
| @@ -4,7 +4,7 @@ | |||
| // Use of this source code is governed by a MIT-style | |||
| // license that can be found in the LICENSE file. | |||
| // Gogs(Go Git Service) is a Self Hosted Git Service in the Go Programming Language. | |||
| // Gogs(Go Git Service) is a painless self-hosted Git Service written in Go. | |||
| package main | |||
| import ( | |||
| @@ -17,7 +17,7 @@ import ( | |||
| "github.com/gogits/gogs/modules/setting" | |||
| ) | |||
| const APP_VER = "0.4.5.0712 Alpha" | |||
| const APP_VER = "0.4.7.0726 Alpha" | |||
| func init() { | |||
| runtime.GOMAXPROCS(runtime.NumCPU()) | |||
| @@ -8,30 +8,31 @@ import ( | |||
| "encoding/json" | |||
| "errors" | |||
| "fmt" | |||
| "path" | |||
| "regexp" | |||
| "strings" | |||
| "time" | |||
| "unicode" | |||
| "github.com/gogits/git" | |||
| "github.com/gogits/gogs/modules/base" | |||
| "github.com/gogits/gogs/modules/git" | |||
| "github.com/gogits/gogs/modules/log" | |||
| "github.com/gogits/gogs/modules/setting" | |||
| ) | |||
| // Operation types of user action. | |||
| type ActionType int | |||
| const ( | |||
| OP_CREATE_REPO = iota + 1 | |||
| OP_DELETE_REPO | |||
| OP_STAR_REPO | |||
| OP_FOLLOW_REPO | |||
| OP_COMMIT_REPO | |||
| OP_CREATE_ISSUE | |||
| OP_PULL_REQUEST | |||
| OP_TRANSFER_REPO | |||
| OP_PUSH_TAG | |||
| OP_COMMENT_ISSUE | |||
| CREATE_REPO ActionType = iota + 1 // 1 | |||
| DELETE_REPO // 2 | |||
| STAR_REPO // 3 | |||
| FOLLOW_REPO // 4 | |||
| COMMIT_REPO // 5 | |||
| CREATE_ISSUE // 6 | |||
| PULL_REQUEST // 7 | |||
| TRANSFER_REPO // 8 | |||
| PUSH_TAG // 9 | |||
| COMMENT_ISSUE // 10 | |||
| ) | |||
| var ( | |||
| @@ -53,7 +54,7 @@ func init() { | |||
| type Action struct { | |||
| Id int64 | |||
| UserId int64 // Receiver user id. | |||
| OpType int | |||
| OpType ActionType | |||
| ActUserId int64 // Action user id. | |||
| ActUserName string // Action user name. | |||
| ActEmail string | |||
| @@ -67,7 +68,7 @@ type Action struct { | |||
| } | |||
| func (a Action) GetOpType() int { | |||
| return a.OpType | |||
| return int(a.OpType) | |||
| } | |||
| func (a Action) GetActUserName() string { | |||
| @@ -86,6 +87,10 @@ func (a Action) GetRepoName() string { | |||
| return a.RepoName | |||
| } | |||
| func (a Action) GetRepoLink() string { | |||
| return path.Join(a.RepoUserName, a.RepoName) | |||
| } | |||
| func (a Action) GetBranch() string { | |||
| return a.RefName | |||
| } | |||
| @@ -94,6 +99,14 @@ func (a Action) GetContent() string { | |||
| return a.Content | |||
| } | |||
| func (a Action) GetCreate() time.Time { | |||
| return a.Created | |||
| } | |||
| func (a Action) GetIssueInfos() []string { | |||
| return strings.SplitN(a.Content, "|", 2) | |||
| } | |||
| func updateIssuesCommit(userId, repoId int64, repoUserName, repoName string, commits []*base.PushCommit) error { | |||
| for _, c := range commits { | |||
| refs := IssueKeywordsPat.FindAllString(c.Message, -1) | |||
| @@ -160,12 +173,11 @@ func updateIssuesCommit(userId, repoId int64, repoUserName, repoName string, com | |||
| // CommitRepoAction adds new action for committing repository. | |||
| func CommitRepoAction(userId, repoUserId int64, userName, actEmail string, | |||
| repoId int64, repoUserName, repoName string, refFullName string, commit *base.PushCommits) error { | |||
| // log.Trace("action.CommitRepoAction(start): %d/%s", userId, repoName) | |||
| opType := OP_COMMIT_REPO | |||
| opType := COMMIT_REPO | |||
| // Check it's tag push or branch. | |||
| if strings.HasPrefix(refFullName, "refs/tags/") { | |||
| opType = OP_PUSH_TAG | |||
| opType = PUSH_TAG | |||
| commit = &base.PushCommits{} | |||
| } | |||
| @@ -269,26 +281,26 @@ func CommitRepoAction(userId, repoUserId int64, userName, actEmail string, | |||
| // NewRepoAction adds new action for creating repository. | |||
| func NewRepoAction(u *User, repo *Repository) (err error) { | |||
| if err = NotifyWatchers(&Action{ActUserId: u.Id, ActUserName: u.Name, ActEmail: u.Email, | |||
| OpType: OP_CREATE_REPO, RepoId: repo.Id, RepoUserName: repo.Owner.Name, RepoName: repo.Name, | |||
| OpType: CREATE_REPO, RepoId: repo.Id, RepoUserName: repo.Owner.Name, RepoName: repo.Name, | |||
| IsPrivate: repo.IsPrivate}); err != nil { | |||
| log.Error("action.NewRepoAction(notify watchers): %d/%s", u.Id, repo.Name) | |||
| log.Error(4, "NotifyWatchers: %d/%s", u.Id, repo.Name) | |||
| return err | |||
| } | |||
| log.Trace("action.NewRepoAction: %s/%s", u.LowerName, repo.LowerName) | |||
| log.Trace("action.NewRepoAction: %s/%s", u.Name, repo.Name) | |||
| return err | |||
| } | |||
| // TransferRepoAction adds new action for transfering repository. | |||
| func TransferRepoAction(user, newUser *User, repo *Repository) (err error) { | |||
| if err = NotifyWatchers(&Action{ActUserId: user.Id, ActUserName: user.Name, ActEmail: user.Email, | |||
| OpType: OP_TRANSFER_REPO, RepoId: repo.Id, RepoName: repo.Name, Content: newUser.Name, | |||
| func TransferRepoAction(u, newUser *User, repo *Repository) (err error) { | |||
| if err = NotifyWatchers(&Action{ActUserId: u.Id, ActUserName: u.Name, ActEmail: u.Email, | |||
| OpType: TRANSFER_REPO, RepoId: repo.Id, RepoName: repo.Name, Content: newUser.Name, | |||
| IsPrivate: repo.IsPrivate}); err != nil { | |||
| log.Error("action.TransferRepoAction(notify watchers): %d/%s", user.Id, repo.Name) | |||
| log.Error(4, "NotifyWatchers: %d/%s", u.Id, repo.Name) | |||
| return err | |||
| } | |||
| log.Trace("action.TransferRepoAction: %s/%s", user.LowerName, repo.LowerName) | |||
| log.Trace("action.TransferRepoAction: %s/%s", u.Name, repo.Name) | |||
| return err | |||
| } | |||
| @@ -13,9 +13,10 @@ import ( | |||
| "strings" | |||
| "time" | |||
| "github.com/Unknwon/com" | |||
| "github.com/gogits/git" | |||
| "github.com/gogits/gogs/modules/base" | |||
| "github.com/gogits/gogs/modules/log" | |||
| "github.com/gogits/gogs/modules/process" | |||
| ) | |||
| @@ -118,8 +119,8 @@ func ParsePatch(pid int64, cmd *exec.Cmd, reader io.Reader) (*Diff, error) { | |||
| // Parse line number. | |||
| ranges := strings.Split(ss[len(ss)-2][1:], " ") | |||
| leftLine, _ = base.StrTo(strings.Split(ranges[0], ",")[0][1:]).Int() | |||
| rightLine, _ = base.StrTo(strings.Split(ranges[1], ",")[0]).Int() | |||
| leftLine, _ = com.StrTo(strings.Split(ranges[0], ",")[0][1:]).Int() | |||
| rightLine, _ = com.StrTo(strings.Split(ranges[1], ",")[0]).Int() | |||
| continue | |||
| case line[0] == '+': | |||
| curFile.Addition++ | |||
| @@ -214,7 +215,7 @@ func GetDiff(repoPath, commitid string) (*Diff, error) { | |||
| select { | |||
| case <-time.After(5 * time.Minute): | |||
| if errKill := process.Kill(pid); errKill != nil { | |||
| log.Error("git_diff.ParsePatch(Kill): %v", err) | |||
| log.Error(4, "git_diff.ParsePatch(Kill): %v", err) | |||
| } | |||
| <-done | |||
| // return "", ErrExecTimeout.Error(), ErrExecTimeout | |||
| @@ -13,9 +13,9 @@ import ( | |||
| "strings" | |||
| "time" | |||
| "github.com/Unknwon/com" | |||
| "github.com/go-xorm/xorm" | |||
| "github.com/gogits/gogs/modules/base" | |||
| "github.com/gogits/gogs/modules/log" | |||
| ) | |||
| @@ -72,7 +72,7 @@ func (i *Issue) GetLabels() error { | |||
| strIds := strings.Split(strings.TrimSuffix(i.LabelIds[1:], "|"), "|$") | |||
| i.Labels = make([]*Label, 0, len(strIds)) | |||
| for _, strId := range strIds { | |||
| id, _ := base.StrTo(strId).Int64() | |||
| id, _ := com.StrTo(strId).Int64() | |||
| if id > 0 { | |||
| l, err := GetLabelById(id) | |||
| if err != nil { | |||
| @@ -337,7 +337,7 @@ func GetIssueUserPairsByRepoIds(rids []int64, isClosed bool, page int) ([]*Issue | |||
| buf := bytes.NewBufferString("") | |||
| for _, rid := range rids { | |||
| buf.WriteString("repo_id=") | |||
| buf.WriteString(base.ToStr(rid)) | |||
| buf.WriteString(com.ToStr(rid)) | |||
| buf.WriteString(" OR ") | |||
| } | |||
| cond := strings.TrimSuffix(buf.String(), " OR ") | |||
| @@ -564,7 +564,7 @@ func UpdateLabel(l *Label) error { | |||
| // DeleteLabel delete a label of given repository. | |||
| func DeleteLabel(repoId int64, strId string) error { | |||
| id, _ := base.StrTo(strId).Int64() | |||
| id, _ := com.StrTo(strId).Int64() | |||
| l, err := GetLabelById(id) | |||
| if err != nil { | |||
| if err == ErrLabelNotExist { | |||
| @@ -768,7 +768,7 @@ func ChangeMilestoneAssign(oldMid, mid int64, issue *Issue) (err error) { | |||
| m.Completeness = 0 | |||
| } | |||
| if _, err = sess.Id(m.Id).Update(m); err != nil { | |||
| if _, err = sess.Id(m.Id).Cols("num_issues,num_completeness,num_closed_issues").Update(m); err != nil { | |||
| sess.Rollback() | |||
| return err | |||
| } | |||
| @@ -796,7 +796,7 @@ func ChangeMilestoneAssign(oldMid, mid int64, issue *Issue) (err error) { | |||
| } | |||
| m.Completeness = m.NumClosedIssues * 100 / m.NumIssues | |||
| if _, err = sess.Id(m.Id).Update(m); err != nil { | |||
| if _, err = sess.Id(m.Id).Cols("num_issues,num_completeness,num_closed_issues").Update(m); err != nil { | |||
| sess.Rollback() | |||
| return err | |||
| } | |||
| @@ -164,15 +164,14 @@ func UserSignIn(uname, passwd string) (*User, error) { | |||
| if u.LoginType == NOTYPE { | |||
| if has { | |||
| u.LoginType = PLAIN | |||
| } else { | |||
| return nil, ErrUserNotExist | |||
| } | |||
| } | |||
| // for plain login, user must have existed. | |||
| // For plain login, user must exist to reach this line. | |||
| // Now verify password. | |||
| if u.LoginType == PLAIN { | |||
| if !has { | |||
| return nil, ErrUserNotExist | |||
| } | |||
| newUser := &User{Passwd: passwd, Salt: u.Salt} | |||
| newUser.EncodePasswd() | |||
| if u.Passwd != newUser.Passwd { | |||
| @@ -233,18 +232,18 @@ func UserSignIn(uname, passwd string) (*User, error) { | |||
| // Query if name/passwd can login against the LDAP direcotry pool | |||
| // Create a local user if success | |||
| // Return the same LoginUserPlain semantic | |||
| func LoginUserLdapSource(user *User, name, passwd string, sourceId int64, cfg *LDAPConfig, autoRegister bool) (*User, error) { | |||
| func LoginUserLdapSource(u *User, name, passwd string, sourceId int64, cfg *LDAPConfig, autoRegister bool) (*User, error) { | |||
| mail, logged := cfg.Ldapsource.SearchEntry(name, passwd) | |||
| if !logged { | |||
| // user not in LDAP, do nothing | |||
| return nil, ErrUserNotExist | |||
| } | |||
| if !autoRegister { | |||
| return user, nil | |||
| return u, nil | |||
| } | |||
| // fake a local user creation | |||
| user = &User{ | |||
| u = &User{ | |||
| LowerName: strings.ToLower(name), | |||
| Name: strings.ToLower(name), | |||
| LoginType: LDAP, | |||
| @@ -255,7 +254,8 @@ func LoginUserLdapSource(user *User, name, passwd string, sourceId int64, cfg *L | |||
| Email: mail, | |||
| } | |||
| return CreateUser(user) | |||
| err := CreateUser(u) | |||
| return u, err | |||
| } | |||
| type loginAuth struct { | |||
| @@ -322,7 +322,7 @@ func SmtpAuth(host string, port int, a smtp.Auth, useTls bool) error { | |||
| // Query if name/passwd can login against the LDAP direcotry pool | |||
| // Create a local user if success | |||
| // Return the same LoginUserPlain semantic | |||
| func LoginUserSMTPSource(user *User, name, passwd string, sourceId int64, cfg *SMTPConfig, autoRegister bool) (*User, error) { | |||
| func LoginUserSMTPSource(u *User, name, passwd string, sourceId int64, cfg *SMTPConfig, autoRegister bool) (*User, error) { | |||
| var auth smtp.Auth | |||
| if cfg.Auth == SMTP_PLAIN { | |||
| auth = smtp.PlainAuth("", name, passwd, cfg.Host) | |||
| @@ -340,7 +340,7 @@ func LoginUserSMTPSource(user *User, name, passwd string, sourceId int64, cfg *S | |||
| } | |||
| if !autoRegister { | |||
| return user, nil | |||
| return u, nil | |||
| } | |||
| var loginName = name | |||
| @@ -349,7 +349,7 @@ func LoginUserSMTPSource(user *User, name, passwd string, sourceId int64, cfg *S | |||
| loginName = name[:idx] | |||
| } | |||
| // fake a local user creation | |||
| user = &User{ | |||
| u = &User{ | |||
| LowerName: strings.ToLower(loginName), | |||
| Name: strings.ToLower(loginName), | |||
| LoginType: SMTP, | |||
| @@ -359,5 +359,6 @@ func LoginUserSMTPSource(user *User, name, passwd string, sourceId int64, cfg *S | |||
| Passwd: passwd, | |||
| Email: name, | |||
| } | |||
| return CreateUser(user) | |||
| err := CreateUser(u) | |||
| return u, err | |||
| } | |||
| @@ -54,7 +54,7 @@ func exePath() (string, error) { | |||
| func homeDir() string { | |||
| home, err := com.HomeDir() | |||
| if err != nil { | |||
| log.Fatal("Fail to get home directory: %v", err) | |||
| log.Fatal(4, "Fail to get home directory: %v", err) | |||
| } | |||
| return home | |||
| } | |||
| @@ -63,25 +63,28 @@ func init() { | |||
| var err error | |||
| if appPath, err = exePath(); err != nil { | |||
| log.Fatal("publickey.init(fail to get app path): %v\n", err) | |||
| log.Fatal(4, "fail to get app path: %v\n", err) | |||
| } | |||
| appPath = strings.Replace(appPath, "\\", "/", -1) | |||
| // Determine and create .ssh path. | |||
| SshPath = filepath.Join(homeDir(), ".ssh") | |||
| if err = os.MkdirAll(SshPath, os.ModePerm); err != nil { | |||
| log.Fatal("publickey.init(fail to create SshPath(%s)): %v\n", SshPath, err) | |||
| log.Fatal(4, "fail to create SshPath(%s): %v\n", SshPath, err) | |||
| } | |||
| } | |||
| // PublicKey represents a SSH key. | |||
| type PublicKey struct { | |||
| Id int64 | |||
| OwnerId int64 `xorm:"UNIQUE(s) INDEX NOT NULL"` | |||
| Name string `xorm:"UNIQUE(s) NOT NULL"` | |||
| Fingerprint string | |||
| Content string `xorm:"TEXT NOT NULL"` | |||
| Created time.Time `xorm:"CREATED"` | |||
| Updated time.Time `xorm:"UPDATED"` | |||
| Id int64 | |||
| OwnerId int64 `xorm:"UNIQUE(s) INDEX NOT NULL"` | |||
| Name string `xorm:"UNIQUE(s) NOT NULL"` | |||
| Fingerprint string | |||
| Content string `xorm:"TEXT NOT NULL"` | |||
| Created time.Time `xorm:"CREATED"` | |||
| Updated time.Time | |||
| HasRecentActivity bool `xorm:"-"` | |||
| HasUsed bool `xorm:"-"` | |||
| } | |||
| // GetAuthorizedString generates and returns formatted public key string for authorized_keys file. | |||
| @@ -89,6 +92,59 @@ func (key *PublicKey) GetAuthorizedString() string { | |||
| return fmt.Sprintf(_TPL_PUBLICK_KEY, appPath, key.Id, key.Content) | |||
| } | |||
| var ( | |||
| MinimumKeySize = map[string]int{ | |||
| "(ED25519)": 256, | |||
| "(ECDSA)": 256, | |||
| "(NTRU)": 1087, | |||
| "(MCE)": 1702, | |||
| "(McE)": 1702, | |||
| "(RSA)": 2048, | |||
| } | |||
| ) | |||
| // CheckPublicKeyString checks if the given public key string is recognized by SSH. | |||
| func CheckPublicKeyString(content string) (bool, error) { | |||
| if strings.ContainsAny(content, "\n\r") { | |||
| return false, errors.New("Only a single line with a single key please") | |||
| } | |||
| // write the key to a file… | |||
| tmpFile, err := ioutil.TempFile(os.TempDir(), "keytest") | |||
| if err != nil { | |||
| return false, err | |||
| } | |||
| tmpPath := tmpFile.Name() | |||
| defer os.Remove(tmpPath) | |||
| tmpFile.WriteString(content) | |||
| tmpFile.Close() | |||
| // … see if ssh-keygen recognizes its contents | |||
| stdout, stderr, err := process.Exec("CheckPublicKeyString", "ssh-keygen", "-l", "-f", tmpPath) | |||
| if err != nil { | |||
| return false, errors.New("ssh-keygen -l -f: " + stderr) | |||
| } else if len(stdout) < 2 { | |||
| return false, errors.New("ssh-keygen returned not enough output to evaluate the key") | |||
| } | |||
| sshKeygenOutput := strings.Split(stdout, " ") | |||
| if len(sshKeygenOutput) < 4 { | |||
| return false, errors.New("Not enough fields returned by ssh-keygen -l -f") | |||
| } | |||
| keySize, err := com.StrTo(sshKeygenOutput[0]).Int() | |||
| if err != nil { | |||
| return false, errors.New("Cannot get key size of the given key") | |||
| } | |||
| keyType := strings.TrimSpace(sshKeygenOutput[len(sshKeygenOutput)-1]) | |||
| if minimumKeySize := MinimumKeySize[keyType]; minimumKeySize == 0 { | |||
| return false, errors.New("Sorry, unrecognized public key type") | |||
| } else if keySize < minimumKeySize { | |||
| return false, fmt.Errorf("The minimum accepted size of a public key %s is %d", keyType, minimumKeySize) | |||
| } | |||
| return true, nil | |||
| } | |||
| // saveAuthorizedKeyFile writes SSH key content to authorized_keys file. | |||
| func saveAuthorizedKeyFile(key *PublicKey) error { | |||
| sshOpLocker.Lock() | |||
| @@ -144,10 +200,18 @@ func AddPublicKey(key *PublicKey) (err error) { | |||
| } | |||
| // ListPublicKey returns a list of all public keys that user has. | |||
| func ListPublicKey(uid int64) ([]PublicKey, error) { | |||
| keys := make([]PublicKey, 0, 5) | |||
| func ListPublicKey(uid int64) ([]*PublicKey, error) { | |||
| keys := make([]*PublicKey, 0, 5) | |||
| err := x.Find(&keys, &PublicKey{OwnerId: uid}) | |||
| return keys, err | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| for _, key := range keys { | |||
| key.HasUsed = key.Updated.After(key.Created) | |||
| key.HasRecentActivity = key.Updated.Add(7 * 24 * time.Hour).After(time.Now()) | |||
| } | |||
| return keys, nil | |||
| } | |||
| // rewriteAuthorizedKeys finds and deletes corresponding line in authorized_keys file. | |||
| @@ -218,8 +282,6 @@ func DeletePublicKey(key *PublicKey) error { | |||
| fpath := filepath.Join(SshPath, "authorized_keys") | |||
| tmpPath := filepath.Join(SshPath, "authorized_keys.tmp") | |||
| log.Trace("publickey.DeletePublicKey(authorized_keys): %s", fpath) | |||
| if err = rewriteAuthorizedKeys(key, fpath, tmpPath); err != nil { | |||
| return err | |||
| } else if err = os.Remove(fpath); err != nil { | |||
| @@ -10,7 +10,7 @@ import ( | |||
| "strings" | |||
| "time" | |||
| "github.com/gogits/git" | |||
| "github.com/gogits/gogs/modules/git" | |||
| ) | |||
| var ( | |||
| @@ -14,7 +14,6 @@ import ( | |||
| "path" | |||
| "path/filepath" | |||
| "regexp" | |||
| "runtime" | |||
| "sort" | |||
| "strings" | |||
| "time" | |||
| @@ -23,10 +22,7 @@ import ( | |||
| "github.com/Unknwon/cae/zip" | |||
| "github.com/Unknwon/com" | |||
| "github.com/gogits/git" | |||
| "github.com/gogits/gogs/modules/base" | |||
| "github.com/gogits/gogs/modules/bin" | |||
| "github.com/gogits/gogs/modules/git" | |||
| "github.com/gogits/gogs/modules/log" | |||
| "github.com/gogits/gogs/modules/process" | |||
| "github.com/gogits/gogs/modules/setting" | |||
| @@ -47,35 +43,27 @@ var ( | |||
| ) | |||
| var ( | |||
| LanguageIgns, Licenses []string | |||
| Gitignores, Licenses []string | |||
| ) | |||
| var ( | |||
| DescriptionPattern = regexp.MustCompile(`https?://\S+`) | |||
| ) | |||
| // getAssetList returns corresponding asset list in 'conf'. | |||
| func getAssetList(prefix string) []string { | |||
| assets := make([]string, 0, 15) | |||
| for _, name := range bin.AssetNames() { | |||
| if strings.HasPrefix(name, prefix) { | |||
| assets = append(assets, strings.TrimPrefix(name, prefix+"/")) | |||
| } | |||
| } | |||
| return assets | |||
| } | |||
| func LoadRepoConfig() { | |||
| // Load .gitignore and license files. | |||
| types := []string{"gitignore", "license"} | |||
| typeFiles := make([][]string, 2) | |||
| for i, t := range types { | |||
| files := getAssetList(path.Join("conf", t)) | |||
| files, err := com.StatDir(path.Join("conf", t)) | |||
| if err != nil { | |||
| log.Fatal(4, "Fail to get %s files: %v", t, err) | |||
| } | |||
| customPath := path.Join(setting.CustomPath, "conf", t) | |||
| if com.IsDir(customPath) { | |||
| customFiles, err := com.StatDir(customPath) | |||
| if err != nil { | |||
| log.Fatal("Fail to get custom %s files: %v", t, err) | |||
| log.Fatal(4, "Fail to get custom %s files: %v", t, err) | |||
| } | |||
| for _, f := range customFiles { | |||
| @@ -87,34 +75,33 @@ func LoadRepoConfig() { | |||
| typeFiles[i] = files | |||
| } | |||
| LanguageIgns = typeFiles[0] | |||
| Gitignores = typeFiles[0] | |||
| Licenses = typeFiles[1] | |||
| sort.Strings(LanguageIgns) | |||
| sort.Strings(Gitignores) | |||
| sort.Strings(Licenses) | |||
| } | |||
| func NewRepoContext() { | |||
| zip.Verbose = false | |||
| // Check Git version. | |||
| ver, err := git.GetVersion() | |||
| if err != nil { | |||
| log.Fatal(4, "Fail to get Git version: %v", err) | |||
| } | |||
| if ver.Major < 2 && ver.Minor < 8 { | |||
| log.Fatal(4, "Gogs requires Git version greater or equal to 1.8.0") | |||
| } | |||
| // Check if server has basic git setting. | |||
| stdout, stderr, err := process.Exec("NewRepoContext(get setting)", "git", "config", "--get", "user.name") | |||
| if err != nil { | |||
| log.Fatal("repo.NewRepoContext(fail to get git user.name): %s", stderr) | |||
| log.Fatal(4, "Fail to get git user.name: %s", stderr) | |||
| } else if err != nil || len(strings.TrimSpace(stdout)) == 0 { | |||
| if _, stderr, err = process.Exec("NewRepoContext(set email)", "git", "config", "--global", "user.email", "gogitservice@gmail.com"); err != nil { | |||
| log.Fatal("repo.NewRepoContext(fail to set git user.email): %s", stderr) | |||
| log.Fatal(4, "Fail to set git user.email: %s", stderr) | |||
| } else if _, stderr, err = process.Exec("NewRepoContext(set name)", "git", "config", "--global", "user.name", "Gogs"); err != nil { | |||
| log.Fatal("repo.NewRepoContext(fail to set git user.name): %s", stderr) | |||
| } | |||
| } | |||
| barePath := path.Join(setting.RepoRootPath, "git-bare.zip") | |||
| if !com.IsExist(barePath) { | |||
| data, err := bin.Asset("conf/content/git-bare.zip") | |||
| if err != nil { | |||
| log.Fatal("Fail to get asset 'git-bare.zip': %v", err) | |||
| } else if err := ioutil.WriteFile(barePath, data, os.ModePerm); err != nil { | |||
| log.Fatal("Fail to write asset 'git-bare.zip': %v", err) | |||
| log.Fatal(4, "Fail to set git user.name: %s", stderr) | |||
| } | |||
| } | |||
| } | |||
| @@ -135,12 +122,16 @@ type Repository struct { | |||
| NumIssues int | |||
| NumClosedIssues int | |||
| NumOpenIssues int `xorm:"-"` | |||
| NumPulls int | |||
| NumClosedPulls int | |||
| NumOpenPulls int `xorm:"-"` | |||
| NumMilestones int `xorm:"NOT NULL DEFAULT 0"` | |||
| NumClosedMilestones int `xorm:"NOT NULL DEFAULT 0"` | |||
| NumOpenMilestones int `xorm:"-"` | |||
| NumTags int `xorm:"-"` | |||
| IsPrivate bool | |||
| IsMirror bool | |||
| IsFork bool `xorm:"NOT NULL DEFAULT false"` | |||
| IsBare bool | |||
| IsGoget bool | |||
| DefaultBranch string | |||
| @@ -153,11 +144,11 @@ func (repo *Repository) GetOwner() (err error) { | |||
| return err | |||
| } | |||
| // DescriptionHtml does special handles to description and return HTML string. | |||
| func (repo *Repository) DescriptionHtml() template.HTML { | |||
| sanitize := func(s string) string { | |||
| // TODO(nuss-justin): Improve sanitization. Strip all tags? | |||
| ss := html.EscapeString(s) | |||
| return fmt.Sprintf(`<a href="%s" target="_blank">%s</a>`, ss, ss) | |||
| } | |||
| return template.HTML(DescriptionPattern.ReplaceAllStringFunc(repo.Description, sanitize)) | |||
| @@ -225,7 +216,8 @@ func MirrorRepository(repoId int64, userName, repoName, repoPath, url string) er | |||
| return err | |||
| } | |||
| return git.UnpackRefs(repoPath) | |||
| // return git.UnpackRefs(repoPath) | |||
| return nil | |||
| } | |||
| func GetMirror(repoId int64) (*Mirror, error) { | |||
| @@ -257,14 +249,14 @@ func MirrorUpdate() { | |||
| repoPath, fmt.Sprintf("MirrorUpdate: %s", repoPath), | |||
| "git", "remote", "update"); err != nil { | |||
| return errors.New("git remote update: " + stderr) | |||
| } else if err = git.UnpackRefs(repoPath); err != nil { | |||
| return errors.New("UnpackRefs: " + err.Error()) | |||
| } | |||
| } // else if err = git.UnpackRefs(repoPath); err != nil { | |||
| // return errors.New("UnpackRefs: " + err.Error()) | |||
| // } | |||
| m.NextUpdate = time.Now().Add(time.Duration(m.Interval) * time.Hour) | |||
| return UpdateMirror(m) | |||
| }); err != nil { | |||
| log.Error("repo.MirrorUpdate: %v", err) | |||
| log.Error(4, "repo.MirrorUpdate: %v", err) | |||
| } | |||
| } | |||
| @@ -317,7 +309,7 @@ func MigrateRepository(u *User, name, desc string, private, mirror bool, url str | |||
| // extractGitBareZip extracts git-bare.zip to repository path. | |||
| func extractGitBareZip(repoPath string) error { | |||
| z, err := zip.Open(filepath.Join(setting.RepoRootPath, "git-bare.zip")) | |||
| z, err := zip.Open(path.Join(setting.ConfRootPath, "content/git-bare.zip")) | |||
| if err != nil { | |||
| return err | |||
| } | |||
| @@ -361,34 +353,18 @@ func createHookUpdate(hookPath, content string) error { | |||
| return err | |||
| } | |||
| // SetRepoEnvs sets environment variables for command update. | |||
| func SetRepoEnvs(userId int64, userName, repoName, repoUserName string) { | |||
| os.Setenv("userId", base.ToStr(userId)) | |||
| os.Setenv("userName", userName) | |||
| os.Setenv("repoName", repoName) | |||
| os.Setenv("repoUserName", repoUserName) | |||
| } | |||
| // InitRepository initializes README and .gitignore if needed. | |||
| func initRepository(f string, user *User, repo *Repository, initReadme bool, repoLang, license string) error { | |||
| repoPath := RepoPath(user.Name, repo.Name) | |||
| func initRepository(f string, u *User, repo *Repository, initReadme bool, repoLang, license string) error { | |||
| repoPath := RepoPath(u.Name, repo.Name) | |||
| // Create bare new repository. | |||
| if err := extractGitBareZip(repoPath); err != nil { | |||
| return err | |||
| } | |||
| if runtime.GOOS == "windows" { | |||
| rp := strings.NewReplacer("\\", "/") | |||
| appPath = "\"" + rp.Replace(appPath) + "\"" | |||
| } else { | |||
| rp := strings.NewReplacer("\\", "/", " ", "\\ ") | |||
| appPath = rp.Replace(appPath) | |||
| } | |||
| // hook/post-update | |||
| if err := createHookUpdate(filepath.Join(repoPath, "hooks", "update"), | |||
| fmt.Sprintf(TPL_UPDATE_HOOK, setting.ScriptType, appPath)); err != nil { | |||
| fmt.Sprintf(TPL_UPDATE_HOOK, setting.ScriptType, "\""+appPath+"\"")); err != nil { | |||
| return err | |||
| } | |||
| @@ -405,7 +381,7 @@ func initRepository(f string, user *User, repo *Repository, initReadme bool, rep | |||
| } | |||
| // Clone to temprory path and do the init commit. | |||
| tmpDir := filepath.Join(os.TempDir(), base.ToStr(time.Now().Nanosecond())) | |||
| tmpDir := filepath.Join(os.TempDir(), com.ToStr(time.Now().Nanosecond())) | |||
| os.MkdirAll(tmpDir, os.ModePerm) | |||
| _, stderr, err := process.Exec( | |||
| @@ -426,12 +402,11 @@ func initRepository(f string, user *User, repo *Repository, initReadme bool, rep | |||
| } | |||
| // .gitignore | |||
| if repoLang != "" { | |||
| filePath := "conf/gitignore/" + repoLang | |||
| filePath := "conf/gitignore/" + repoLang | |||
| if com.IsFile(filePath) { | |||
| targetPath := path.Join(tmpDir, fileName["gitign"]) | |||
| data, err := bin.Asset(filePath) | |||
| if err == nil { | |||
| if err = ioutil.WriteFile(targetPath, data, os.ModePerm); err != nil { | |||
| if com.IsFile(filePath) { | |||
| if err = com.Copy(filePath, targetPath); err != nil { | |||
| return err | |||
| } | |||
| } else { | |||
| @@ -443,15 +418,16 @@ func initRepository(f string, user *User, repo *Repository, initReadme bool, rep | |||
| } | |||
| } | |||
| } | |||
| } else { | |||
| delete(fileName, "gitign") | |||
| } | |||
| // LICENSE | |||
| if license != "" { | |||
| filePath := "conf/license/" + license | |||
| filePath = "conf/license/" + license | |||
| if com.IsFile(filePath) { | |||
| targetPath := path.Join(tmpDir, fileName["license"]) | |||
| data, err := bin.Asset(filePath) | |||
| if err == nil { | |||
| if err = ioutil.WriteFile(targetPath, data, os.ModePerm); err != nil { | |||
| if com.IsFile(filePath) { | |||
| if err = com.Copy(filePath, targetPath); err != nil { | |||
| return err | |||
| } | |||
| } else { | |||
| @@ -463,16 +439,16 @@ func initRepository(f string, user *User, repo *Repository, initReadme bool, rep | |||
| } | |||
| } | |||
| } | |||
| } else { | |||
| delete(fileName, "license") | |||
| } | |||
| if len(fileName) == 0 { | |||
| return nil | |||
| } | |||
| SetRepoEnvs(user.Id, user.Name, repo.Name, user.Name) | |||
| // Apply changes and commit. | |||
| return initRepoCommit(tmpDir, user.NewGitSig()) | |||
| return initRepoCommit(tmpDir, u.NewGitSig()) | |||
| } | |||
| // CreateRepository creates a repository for given user or organization. | |||
| @@ -549,15 +525,15 @@ func CreateRepository(u *User, name, desc, lang, license string, private, mirror | |||
| } | |||
| } | |||
| rawSql := "UPDATE `user` SET num_repos = num_repos + 1 WHERE id = ?" | |||
| if _, err = sess.Exec(rawSql, u.Id); err != nil { | |||
| if _, err = sess.Exec( | |||
| "UPDATE `user` SET num_repos = num_repos + 1 WHERE id = ?", u.Id); err != nil { | |||
| sess.Rollback() | |||
| return nil, err | |||
| } | |||
| // Update owner team info and count. | |||
| if u.IsOrganization() { | |||
| t.RepoIds += "$" + base.ToStr(repo.Id) + "|" | |||
| t.RepoIds += "$" + com.ToStr(repo.Id) + "|" | |||
| t.NumRepos++ | |||
| if _, err = sess.Id(t.Id).AllCols().Update(t); err != nil { | |||
| sess.Rollback() | |||
| @@ -572,24 +548,24 @@ func CreateRepository(u *User, name, desc, lang, license string, private, mirror | |||
| if u.IsOrganization() { | |||
| ous, err := GetOrgUsersByOrgId(u.Id) | |||
| if err != nil { | |||
| log.Error("repo.CreateRepository(GetOrgUsersByOrgId): %v", err) | |||
| log.Error(4, "repo.CreateRepository(GetOrgUsersByOrgId): %v", err) | |||
| } else { | |||
| for _, ou := range ous { | |||
| if err = WatchRepo(ou.Uid, repo.Id, true); err != nil { | |||
| log.Error("repo.CreateRepository(WatchRepo): %v", err) | |||
| log.Error(4, "repo.CreateRepository(WatchRepo): %v", err) | |||
| } | |||
| } | |||
| } | |||
| } | |||
| if err = WatchRepo(u.Id, repo.Id, true); err != nil { | |||
| log.Error("repo.CreateRepository(WatchRepo2): %v", err) | |||
| log.Error(4, "WatchRepo2: %v", err) | |||
| } | |||
| if err = NewRepoAction(u, repo); err != nil { | |||
| log.Error("repo.CreateRepository(NewRepoAction): %v", err) | |||
| log.Error(4, "NewRepoAction: %v", err) | |||
| } | |||
| // No need for init for mirror. | |||
| // No need for init mirror. | |||
| if mirror { | |||
| return repo, nil | |||
| } | |||
| @@ -597,11 +573,11 @@ func CreateRepository(u *User, name, desc, lang, license string, private, mirror | |||
| repoPath := RepoPath(u.Name, repo.Name) | |||
| if err = initRepository(repoPath, u, repo, initReadme, lang, license); err != nil { | |||
| if err2 := os.RemoveAll(repoPath); err2 != nil { | |||
| log.Error("repo.CreateRepository(initRepository): %v", err) | |||
| return nil, errors.New(fmt.Sprintf( | |||
| "delete repo directory %s/%s failed(2): %v", u.Name, repo.Name, err2)) | |||
| log.Error(4, "initRepository: %v", err) | |||
| return nil, fmt.Errorf( | |||
| "delete repo directory %s/%s failed(2): %v", u.Name, repo.Name, err2) | |||
| } | |||
| return nil, err | |||
| return nil, fmt.Errorf("initRepository: %v", err) | |||
| } | |||
| _, stderr, err := process.ExecDir(-1, | |||
| @@ -982,15 +958,12 @@ func WatchRepo(uid, rid int64, watch bool) (err error) { | |||
| if _, err = x.Insert(&Watch{RepoId: rid, UserId: uid}); err != nil { | |||
| return err | |||
| } | |||
| rawSql := "UPDATE `repository` SET num_watches = num_watches + 1 WHERE id = ?" | |||
| _, err = x.Exec(rawSql, rid) | |||
| _, err = x.Exec("UPDATE `repository` SET num_watches = num_watches + 1 WHERE id = ?", rid) | |||
| } else { | |||
| if _, err = x.Delete(&Watch{0, uid, rid}); err != nil { | |||
| return err | |||
| } | |||
| rawSql := "UPDATE `repository` SET num_watches = num_watches - 1 WHERE id = ?" | |||
| _, err = x.Exec(rawSql, rid) | |||
| _, err = x.Exec("UPDATE `repository` SET num_watches = num_watches - 1 WHERE id = ?", rid) | |||
| } | |||
| return err | |||
| } | |||
| @@ -10,9 +10,8 @@ import ( | |||
| "os/exec" | |||
| "strings" | |||
| "github.com/gogits/git" | |||
| "github.com/gogits/gogs/modules/base" | |||
| "github.com/gogits/gogs/modules/git" | |||
| "github.com/gogits/gogs/modules/log" | |||
| ) | |||
| @@ -47,8 +46,6 @@ func DelUpdateTasksByUuid(uuid string) error { | |||
| } | |||
| func Update(refName, oldCommitId, newCommitId, userName, repoUserName, repoName string, userId int64) error { | |||
| //fmt.Println(refName, oldCommitId, newCommitId) | |||
| //fmt.Println(userName, repoUserName, repoName) | |||
| isNew := strings.HasPrefix(oldCommitId, "0000000") | |||
| if isNew && | |||
| strings.HasPrefix(newCommitId, "0000000") { | |||
| @@ -82,12 +79,12 @@ func Update(refName, oldCommitId, newCommitId, userName, repoUserName, repoName | |||
| return fmt.Errorf("runUpdate.GetRepositoryByName userId: %v", err) | |||
| } | |||
| // if tags push | |||
| // Push tags. | |||
| if strings.HasPrefix(refName, "refs/tags/") { | |||
| tagName := git.RefEndName(refName) | |||
| tag, err := repo.GetTag(tagName) | |||
| if err != nil { | |||
| log.GitLogger.Fatal("runUpdate.GetTag: %v", err) | |||
| log.GitLogger.Fatal(4, "runUpdate.GetTag: %v", err) | |||
| } | |||
| var actEmail string | |||
| @@ -96,7 +93,7 @@ func Update(refName, oldCommitId, newCommitId, userName, repoUserName, repoName | |||
| } else { | |||
| cmt, err := tag.Commit() | |||
| if err != nil { | |||
| log.GitLogger.Fatal("runUpdate.GetTag Commit: %v", err) | |||
| log.GitLogger.Fatal(4, "runUpdate.GetTag Commit: %v", err) | |||
| } | |||
| actEmail = cmt.Committer.Email | |||
| } | |||
| @@ -105,7 +102,7 @@ func Update(refName, oldCommitId, newCommitId, userName, repoUserName, repoName | |||
| if err = CommitRepoAction(userId, ru.Id, userName, actEmail, | |||
| repos.Id, repoUserName, repoName, refName, commit); err != nil { | |||
| log.GitLogger.Fatal("runUpdate.models.CommitRepoAction: %s/%s:%v", repoUserName, repoName, err) | |||
| log.GitLogger.Fatal(4, "runUpdate.models.CommitRepoAction: %s/%s:%v", repoUserName, repoName, err) | |||
| } | |||
| return err | |||
| } | |||
| @@ -135,7 +132,7 @@ func Update(refName, oldCommitId, newCommitId, userName, repoUserName, repoName | |||
| // if commits push | |||
| commits := make([]*base.PushCommit, 0) | |||
| var maxCommits = 3 | |||
| var maxCommits = 2 | |||
| var actEmail string | |||
| for e := l.Front(); e != nil; e = e.Next() { | |||
| commit := e.Value.(*git.Commit) | |||
| @@ -14,9 +14,10 @@ import ( | |||
| "strings" | |||
| "time" | |||
| "github.com/gogits/git" | |||
| "github.com/Unknwon/com" | |||
| "github.com/gogits/gogs/modules/base" | |||
| "github.com/gogits/gogs/modules/git" | |||
| "github.com/gogits/gogs/modules/log" | |||
| "github.com/gogits/gogs/modules/setting" | |||
| ) | |||
| @@ -77,6 +78,14 @@ type User struct { | |||
| Members []*User `xorm:"-"` | |||
| } | |||
| // DashboardLink returns the user dashboard page link. | |||
| func (u *User) DashboardLink() string { | |||
| if u.IsOrganization() { | |||
| return "/org/" + u.Name + "/dashboard" | |||
| } | |||
| return "/" | |||
| } | |||
| // HomeLink returns the user home page link. | |||
| func (u *User) HomeLink() string { | |||
| return "/user/" + u.Name | |||
| @@ -157,23 +166,23 @@ func GetUserSalt() string { | |||
| } | |||
| // CreateUser creates record of a new user. | |||
| func CreateUser(u *User) (*User, error) { | |||
| func CreateUser(u *User) error { | |||
| if !IsLegalName(u.Name) { | |||
| return nil, ErrUserNameIllegal | |||
| return ErrUserNameIllegal | |||
| } | |||
| isExist, err := IsUserExist(u.Name) | |||
| if err != nil { | |||
| return nil, err | |||
| return err | |||
| } else if isExist { | |||
| return nil, ErrUserAlreadyExist | |||
| return ErrUserAlreadyExist | |||
| } | |||
| isExist, err = IsEmailUsed(u.Email) | |||
| if err != nil { | |||
| return nil, err | |||
| return err | |||
| } else if isExist { | |||
| return nil, ErrEmailAlreadyUsed | |||
| return ErrEmailAlreadyUsed | |||
| } | |||
| u.LowerName = strings.ToLower(u.Name) | |||
| @@ -186,21 +195,17 @@ func CreateUser(u *User) (*User, error) { | |||
| sess := x.NewSession() | |||
| defer sess.Close() | |||
| if err = sess.Begin(); err != nil { | |||
| return nil, err | |||
| return err | |||
| } | |||
| if _, err = sess.Insert(u); err != nil { | |||
| sess.Rollback() | |||
| return nil, err | |||
| } | |||
| if err = os.MkdirAll(UserPath(u.Name), os.ModePerm); err != nil { | |||
| return err | |||
| } else if err = os.MkdirAll(UserPath(u.Name), os.ModePerm); err != nil { | |||
| sess.Rollback() | |||
| return nil, err | |||
| } | |||
| if err = sess.Commit(); err != nil { | |||
| return nil, err | |||
| return err | |||
| } else if err = sess.Commit(); err != nil { | |||
| return err | |||
| } | |||
| // Auto-set admin for user whose ID is 1. | |||
| @@ -209,7 +214,7 @@ func CreateUser(u *User) (*User, error) { | |||
| u.IsActive = true | |||
| _, err = x.Id(u.Id).UseBool().Update(u) | |||
| } | |||
| return u, err | |||
| return err | |||
| } | |||
| // CountUsers returns number of users. | |||
| @@ -237,7 +242,7 @@ func getVerifyUser(code string) (user *User) { | |||
| if user, err = GetUserByName(string(b)); user != nil { | |||
| return user | |||
| } | |||
| log.Error("user.getVerifyUser: %v", err) | |||
| log.Error(4, "user.getVerifyUser: %v", err) | |||
| } | |||
| return nil | |||
| @@ -250,7 +255,7 @@ func VerifyUserActiveCode(code string) (user *User) { | |||
| if user = getVerifyUser(code); user != nil { | |||
| // time limit code | |||
| prefix := code[:base.TimeLimitCodeLength] | |||
| data := base.ToStr(user.Id) + user.Email + user.LowerName + user.Passwd + user.Rands | |||
| data := com.ToStr(user.Id) + user.Email + user.LowerName + user.Passwd + user.Rands | |||
| if base.VerifyTimeLimitCode(data, minutes, prefix) { | |||
| return user | |||
| @@ -260,12 +265,16 @@ func VerifyUserActiveCode(code string) (user *User) { | |||
| } | |||
| // ChangeUserName changes all corresponding setting from old user name to new one. | |||
| func ChangeUserName(user *User, newUserName string) (err error) { | |||
| func ChangeUserName(u *User, newUserName string) (err error) { | |||
| if !IsLegalName(newUserName) { | |||
| return ErrUserNameIllegal | |||
| } | |||
| newUserName = strings.ToLower(newUserName) | |||
| // Update accesses of user. | |||
| accesses := make([]Access, 0, 10) | |||
| if err = x.Find(&accesses, &Access{UserName: user.LowerName}); err != nil { | |||
| if err = x.Find(&accesses, &Access{UserName: u.LowerName}); err != nil { | |||
| return err | |||
| } | |||
| @@ -277,28 +286,28 @@ func ChangeUserName(user *User, newUserName string) (err error) { | |||
| for i := range accesses { | |||
| accesses[i].UserName = newUserName | |||
| if strings.HasPrefix(accesses[i].RepoName, user.LowerName+"/") { | |||
| accesses[i].RepoName = strings.Replace(accesses[i].RepoName, user.LowerName, newUserName, 1) | |||
| if strings.HasPrefix(accesses[i].RepoName, u.LowerName+"/") { | |||
| accesses[i].RepoName = strings.Replace(accesses[i].RepoName, u.LowerName, newUserName, 1) | |||
| } | |||
| if err = UpdateAccessWithSession(sess, &accesses[i]); err != nil { | |||
| return err | |||
| } | |||
| } | |||
| repos, err := GetRepositories(user.Id, true) | |||
| repos, err := GetRepositories(u.Id, true) | |||
| if err != nil { | |||
| return err | |||
| } | |||
| for i := range repos { | |||
| accesses = make([]Access, 0, 10) | |||
| // Update accesses of user repository. | |||
| if err = x.Find(&accesses, &Access{RepoName: user.LowerName + "/" + repos[i].LowerName}); err != nil { | |||
| if err = x.Find(&accesses, &Access{RepoName: u.LowerName + "/" + repos[i].LowerName}); err != nil { | |||
| return err | |||
| } | |||
| for j := range accesses { | |||
| // if the access is not the user's access (already updated above) | |||
| if accesses[j].UserName != user.LowerName { | |||
| if accesses[j].UserName != u.LowerName { | |||
| accesses[j].RepoName = newUserName + "/" + repos[i].LowerName | |||
| if err = UpdateAccessWithSession(sess, &accesses[j]); err != nil { | |||
| return err | |||
| @@ -308,7 +317,7 @@ func ChangeUserName(user *User, newUserName string) (err error) { | |||
| } | |||
| // Change user directory name. | |||
| if err = os.Rename(UserPath(user.LowerName), UserPath(newUserName)); err != nil { | |||
| if err = os.Rename(UserPath(u.LowerName), UserPath(newUserName)); err != nil { | |||
| sess.Rollback() | |||
| return err | |||
| } | |||
| @@ -317,7 +326,7 @@ func ChangeUserName(user *User, newUserName string) (err error) { | |||
| } | |||
| // UpdateUser updates user's information. | |||
| func UpdateUser(u *User) (err error) { | |||
| func UpdateUser(u *User) error { | |||
| u.LowerName = strings.ToLower(u.Name) | |||
| if len(u.Location) > 255 { | |||
| @@ -330,7 +339,7 @@ func UpdateUser(u *User) (err error) { | |||
| u.Description = u.Description[:255] | |||
| } | |||
| _, err = x.Id(u.Id).AllCols().Update(u) | |||
| _, err := x.Id(u.Id).AllCols().Update(u) | |||
| return err | |||
| } | |||
| @@ -340,7 +349,7 @@ func DeleteUser(u *User) error { | |||
| // Check ownership of repository. | |||
| count, err := GetRepositoryCount(u) | |||
| if err != nil { | |||
| return errors.New("modesl.GetRepositories(GetRepositoryCount): " + err.Error()) | |||
| return errors.New("GetRepositoryCount: " + err.Error()) | |||
| } else if count > 0 { | |||
| return ErrUserOwnRepos | |||
| } | |||
| @@ -562,3 +571,45 @@ func UnFollowUser(userId int64, unFollowId int64) (err error) { | |||
| } | |||
| return session.Commit() | |||
| } | |||
| func UpdateMentions(userNames []string, issueId int64) error { | |||
| users := make([]*User, 0, len(userNames)) | |||
| if err := x.Where("name IN (?)", strings.Join(userNames, "\",\"")).OrderBy("name ASC").Find(&users); err != nil { | |||
| return err | |||
| } | |||
| ids := make([]int64, 0, len(userNames)) | |||
| for _, user := range users { | |||
| ids = append(ids, user.Id) | |||
| if user.Type == INDIVIDUAL { | |||
| continue | |||
| } | |||
| if user.NumMembers == 0 { | |||
| continue | |||
| } | |||
| tempIds := make([]int64, 0, user.NumMembers) | |||
| orgUsers, err := GetOrgUsersByOrgId(user.Id) | |||
| if err != nil { | |||
| return err | |||
| } | |||
| for _, orgUser := range orgUsers { | |||
| tempIds = append(tempIds, orgUser.Id) | |||
| } | |||
| ids = append(ids, tempIds...) | |||
| } | |||
| if err := UpdateIssueUserPairsByMentions(ids, issueId); err != nil { | |||
| return err | |||
| } | |||
| return nil | |||
| } | |||
| @@ -47,7 +47,7 @@ type Webhook struct { | |||
| func (w *Webhook) GetEvent() { | |||
| w.HookEvent = &HookEvent{} | |||
| if err := json.Unmarshal([]byte(w.Events), w.HookEvent); err != nil { | |||
| log.Error("webhook.GetEvent(%d): %v", w.Id, err) | |||
| log.Error(4, "webhook.GetEvent(%d): %v", w.Id, err) | |||
| } | |||
| } | |||
| @@ -193,13 +193,13 @@ func DeliverHooks() { | |||
| // Only support JSON now. | |||
| if _, err := httplib.Post(t.Url).SetTimeout(timeout, timeout). | |||
| Body([]byte(t.PayloadContent)).Response(); err != nil { | |||
| log.Error("webhook.DeliverHooks(Delivery): %v", err) | |||
| log.Error(4, "webhook.DeliverHooks(Delivery): %v", err) | |||
| return nil | |||
| } | |||
| t.IsDeliveried = true | |||
| if err := UpdateHookTask(t); err != nil { | |||
| log.Error("webhook.DeliverHooks(UpdateHookTask): %v", err) | |||
| log.Error(4, "webhook.DeliverHooks(UpdateHookTask): %v", err) | |||
| return nil | |||
| } | |||
| @@ -5,12 +5,9 @@ | |||
| package auth | |||
| import ( | |||
| "net/http" | |||
| "reflect" | |||
| "github.com/Unknwon/macaron" | |||
| "github.com/macaron-contrib/i18n" | |||
| "github.com/go-martini/martini" | |||
| "github.com/gogits/gogs/modules/base" | |||
| "github.com/gogits/gogs/modules/middleware/binding" | |||
| ) | |||
| @@ -25,17 +22,6 @@ type AdminEditUserForm struct { | |||
| LoginType int `form:"login_type"` | |||
| } | |||
| func (f *AdminEditUserForm) Name(field string) string { | |||
| names := map[string]string{ | |||
| "Email": "E-mail address", | |||
| "Website": "Website", | |||
| "Location": "Location", | |||
| "Avatar": "Gravatar Email", | |||
| } | |||
| return names[field] | |||
| } | |||
| func (f *AdminEditUserForm) Validate(errors *binding.Errors, req *http.Request, context martini.Context) { | |||
| data := context.Get(reflect.TypeOf(base.TmplData{})).Interface().(base.TmplData) | |||
| validate(errors, data, f) | |||
| func (f *AdminEditUserForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) { | |||
| validate(errs, ctx.Data, f, l) | |||
| } | |||
| @@ -5,13 +5,12 @@ | |||
| package apiv1 | |||
| import ( | |||
| "net/http" | |||
| "reflect" | |||
| "github.com/go-martini/martini" | |||
| "github.com/Unknwon/macaron" | |||
| "github.com/macaron-contrib/i18n" | |||
| "github.com/gogits/gogs/modules/auth" | |||
| "github.com/gogits/gogs/modules/base" | |||
| "github.com/gogits/gogs/modules/log" | |||
| "github.com/gogits/gogs/modules/middleware/binding" | |||
| ) | |||
| @@ -22,17 +21,16 @@ type MarkdownForm struct { | |||
| Context string `form:"context"` | |||
| } | |||
| func (f *MarkdownForm) Validate(errs *binding.Errors, req *http.Request, ctx martini.Context) { | |||
| data := ctx.Get(reflect.TypeOf(base.TmplData{})).Interface().(base.TmplData) | |||
| validateApiReq(errs, data, f) | |||
| func (f *MarkdownForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) { | |||
| validateApiReq(errs, ctx.Data, f, l) | |||
| } | |||
| func validateApiReq(errs *binding.Errors, data base.TmplData, f interface{}) { | |||
| func validateApiReq(errs *binding.Errors, data map[string]interface{}, f interface{}, l i18n.Locale) { | |||
| if errs.Count() == 0 { | |||
| return | |||
| } else if len(errs.Overall) > 0 { | |||
| for _, err := range errs.Overall { | |||
| log.Error("%s: %v", reflect.TypeOf(f), err) | |||
| log.Error(4, "%s: %v", reflect.TypeOf(f), err) | |||
| } | |||
| return | |||
| } | |||
| @@ -7,49 +7,152 @@ package auth | |||
| import ( | |||
| "net/http" | |||
| "reflect" | |||
| "strings" | |||
| "github.com/go-martini/martini" | |||
| "github.com/macaron-contrib/i18n" | |||
| "github.com/macaron-contrib/session" | |||
| "github.com/gogits/gogs/modules/base" | |||
| "github.com/gogits/gogs/models" | |||
| "github.com/gogits/gogs/modules/log" | |||
| "github.com/gogits/gogs/modules/middleware/binding" | |||
| "github.com/gogits/gogs/modules/setting" | |||
| ) | |||
| type AuthenticationForm struct { | |||
| Id int64 `form:"id"` | |||
| Type int `form:"type"` | |||
| AuthName string `form:"name" binding:"Required;MaxSize(50)"` | |||
| Domain string `form:"domain"` | |||
| Host string `form:"host"` | |||
| Port int `form:"port"` | |||
| UseSSL bool `form:"usessl"` | |||
| BaseDN string `form:"base_dn"` | |||
| Attributes string `form:"attributes"` | |||
| Filter string `form:"filter"` | |||
| MsAdSA string `form:"ms_ad_sa"` | |||
| IsActived bool `form:"is_actived"` | |||
| SmtpAuth string `form:"smtpauth"` | |||
| SmtpHost string `form:"smtphost"` | |||
| SmtpPort int `form:"smtpport"` | |||
| Tls bool `form:"tls"` | |||
| AllowAutoRegister bool `form:"allowautoregister"` | |||
| // SignedInId returns the id of signed in user. | |||
| func SignedInId(header http.Header, sess session.Store) int64 { | |||
| if !models.HasEngine { | |||
| return 0 | |||
| } | |||
| if setting.Service.EnableReverseProxyAuth { | |||
| webAuthUser := header.Get(setting.ReverseProxyAuthUser) | |||
| if len(webAuthUser) > 0 { | |||
| u, err := models.GetUserByName(webAuthUser) | |||
| if err != nil { | |||
| if err != models.ErrUserNotExist { | |||
| log.Error(4, "GetUserByName: %v", err) | |||
| } | |||
| return 0 | |||
| } | |||
| return u.Id | |||
| } | |||
| } | |||
| uid := sess.Get("uid") | |||
| if uid == nil { | |||
| return 0 | |||
| } | |||
| if id, ok := uid.(int64); ok { | |||
| if _, err := models.GetUserById(id); err != nil { | |||
| if err != models.ErrUserNotExist { | |||
| log.Error(4, "GetUserById: %v", err) | |||
| } | |||
| return 0 | |||
| } | |||
| return id | |||
| } | |||
| return 0 | |||
| } | |||
| func (f *AuthenticationForm) Name(field string) string { | |||
| names := map[string]string{ | |||
| "AuthName": "Authentication's name", | |||
| "Domain": "Domain name", | |||
| "Host": "Host address", | |||
| "Port": "Port Number", | |||
| "UseSSL": "Use SSL", | |||
| "BaseDN": "Base DN", | |||
| "Attributes": "Search attributes", | |||
| "Filter": "Search filter", | |||
| "MsAdSA": "Ms Ad SA", | |||
| } | |||
| return names[field] | |||
| // SignedInUser returns the user object of signed user. | |||
| func SignedInUser(header http.Header, sess session.Store) *models.User { | |||
| uid := SignedInId(header, sess) | |||
| if uid <= 0 { | |||
| return nil | |||
| } | |||
| u, err := models.GetUserById(uid) | |||
| if err != nil { | |||
| log.Error(4, "GetUserById: %v", err) | |||
| return nil | |||
| } | |||
| return u | |||
| } | |||
| func (f *AuthenticationForm) Validate(errors *binding.Errors, req *http.Request, context martini.Context) { | |||
| data := context.Get(reflect.TypeOf(base.TmplData{})).Interface().(base.TmplData) | |||
| validate(errors, data, f) | |||
| // AssignForm assign form values back to the template data. | |||
| func AssignForm(form interface{}, data map[string]interface{}) { | |||
| typ := reflect.TypeOf(form) | |||
| val := reflect.ValueOf(form) | |||
| if typ.Kind() == reflect.Ptr { | |||
| typ = typ.Elem() | |||
| val = val.Elem() | |||
| } | |||
| for i := 0; i < typ.NumField(); i++ { | |||
| field := typ.Field(i) | |||
| fieldName := field.Tag.Get("form") | |||
| // Allow ignored fields in the struct | |||
| if fieldName == "-" { | |||
| continue | |||
| } | |||
| data[fieldName] = val.Field(i).Interface() | |||
| } | |||
| } | |||
| func GetMinMaxSize(field reflect.StructField) string { | |||
| for _, rule := range strings.Split(field.Tag.Get("binding"), ";") { | |||
| if strings.HasPrefix(rule, "MinSize(") || strings.HasPrefix(rule, "MaxSize(") { | |||
| return rule[8 : len(rule)-1] | |||
| } | |||
| } | |||
| return "" | |||
| } | |||
| func validate(errs *binding.Errors, data map[string]interface{}, f interface{}, l i18n.Locale) { | |||
| if errs.Count() == 0 { | |||
| return | |||
| } else if len(errs.Overall) > 0 { | |||
| for _, err := range errs.Overall { | |||
| log.Error(4, "%s: %v", reflect.TypeOf(f), err) | |||
| } | |||
| return | |||
| } | |||
| data["HasError"] = true | |||
| AssignForm(f, data) | |||
| typ := reflect.TypeOf(f) | |||
| val := reflect.ValueOf(f) | |||
| if typ.Kind() == reflect.Ptr { | |||
| typ = typ.Elem() | |||
| val = val.Elem() | |||
| } | |||
| for i := 0; i < typ.NumField(); i++ { | |||
| field := typ.Field(i) | |||
| fieldName := field.Tag.Get("form") | |||
| // Allow ignored fields in the struct | |||
| if fieldName == "-" { | |||
| continue | |||
| } | |||
| if err, ok := errs.Fields[field.Name]; ok { | |||
| data["Err_"+field.Name] = true | |||
| trName := l.Tr("form." + field.Name) | |||
| switch err { | |||
| case binding.BindingRequireError: | |||
| data["ErrorMsg"] = trName + l.Tr("form.require_error") | |||
| case binding.BindingAlphaDashError: | |||
| data["ErrorMsg"] = trName + l.Tr("form.alpha_dash_error") | |||
| case binding.BindingAlphaDashDotError: | |||
| data["ErrorMsg"] = trName + l.Tr("form.alpha_dash_dot_error") | |||
| case binding.BindingMinSizeError: | |||
| data["ErrorMsg"] = trName + l.Tr("form.min_size_error", GetMinMaxSize(field)) | |||
| case binding.BindingMaxSizeError: | |||
| data["ErrorMsg"] = trName + l.Tr("form.max_size_error", GetMinMaxSize(field)) | |||
| case binding.BindingEmailError: | |||
| data["ErrorMsg"] = trName + l.Tr("form.email_error") | |||
| case binding.BindingUrlError: | |||
| data["ErrorMsg"] = trName + l.Tr("form.url_error") | |||
| default: | |||
| data["ErrorMsg"] = l.Tr("form.unknown_error") + " " + err | |||
| } | |||
| return | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,36 @@ | |||
| // 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 auth | |||
| import ( | |||
| "github.com/Unknwon/macaron" | |||
| "github.com/macaron-contrib/i18n" | |||
| "github.com/gogits/gogs/modules/middleware/binding" | |||
| ) | |||
| type AuthenticationForm struct { | |||
| Id int64 `form:"id"` | |||
| Type int `form:"type"` | |||
| AuthName string `form:"name" binding:"Required;MaxSize(50)"` | |||
| Domain string `form:"domain"` | |||
| Host string `form:"host"` | |||
| Port int `form:"port"` | |||
| UseSSL bool `form:"usessl"` | |||
| BaseDN string `form:"base_dn"` | |||
| Attributes string `form:"attributes"` | |||
| Filter string `form:"filter"` | |||
| MsAdSA string `form:"ms_ad_sa"` | |||
| IsActived bool `form:"is_actived"` | |||
| SmtpAuth string `form:"smtpauth"` | |||
| SmtpHost string `form:"smtphost"` | |||
| SmtpPort int `form:"smtpport"` | |||
| Tls bool `form:"tls"` | |||
| AllowAutoRegister bool `form:"allowautoregister"` | |||
| } | |||
| func (f *AuthenticationForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) { | |||
| validate(errs, ctx.Data, f, l) | |||
| } | |||
| @@ -55,7 +55,7 @@ func LoginUser(name, passwd string) (a string, r bool) { | |||
| func (ls Ldapsource) SearchEntry(name, passwd string) (string, bool) { | |||
| l, err := ldapDial(ls) | |||
| if err != nil { | |||
| log.Error("LDAP Connect error, %s:%v", ls.Host, err) | |||
| log.Error(4, "LDAP Connect error, %s:%v", ls.Host, err) | |||
| ls.Enabled = false | |||
| return "", false | |||
| } | |||
| @@ -5,12 +5,9 @@ | |||
| package auth | |||
| import ( | |||
| "net/http" | |||
| "reflect" | |||
| "github.com/Unknwon/macaron" | |||
| "github.com/macaron-contrib/i18n" | |||
| "github.com/go-martini/martini" | |||
| "github.com/gogits/gogs/modules/base" | |||
| "github.com/gogits/gogs/modules/middleware/binding" | |||
| ) | |||
| @@ -26,17 +23,8 @@ type CreateOrgForm struct { | |||
| Email string `form:"email" binding:"Required;Email;MaxSize(50)"` | |||
| } | |||
| func (f *CreateOrgForm) Name(field string) string { | |||
| names := map[string]string{ | |||
| "OrgName": "Organization name", | |||
| "Email": "E-mail address", | |||
| } | |||
| return names[field] | |||
| } | |||
| func (f *CreateOrgForm) Validate(errs *binding.Errors, req *http.Request, ctx martini.Context) { | |||
| data := ctx.Get(reflect.TypeOf(base.TmplData{})).Interface().(base.TmplData) | |||
| validate(errs, data, f) | |||
| func (f *CreateOrgForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) { | |||
| validate(errs, ctx.Data, f, l) | |||
| } | |||
| type OrgSettingForm struct { | |||
| @@ -47,20 +35,8 @@ type OrgSettingForm struct { | |||
| Location string `form:"location" binding:"MaxSize(50)"` | |||
| } | |||
| func (f *OrgSettingForm) Name(field string) string { | |||
| names := map[string]string{ | |||
| "DisplayName": "Display name", | |||
| "Email": "E-mail address", | |||
| "Description": "Description", | |||
| "Website": "Website address", | |||
| "Location": "Location", | |||
| } | |||
| return names[field] | |||
| } | |||
| func (f *OrgSettingForm) Validate(errors *binding.Errors, req *http.Request, context martini.Context) { | |||
| data := context.Get(reflect.TypeOf(base.TmplData{})).Interface().(base.TmplData) | |||
| validate(errors, data, f) | |||
| func (f *OrgSettingForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) { | |||
| validate(errs, ctx.Data, f, l) | |||
| } | |||
| // ___________ | |||
| @@ -76,15 +52,6 @@ type CreateTeamForm struct { | |||
| Permission string `form:"permission"` | |||
| } | |||
| func (f *CreateTeamForm) Name(field string) string { | |||
| names := map[string]string{ | |||
| "TeamName": "Team name", | |||
| "Description": "Team description", | |||
| } | |||
| return names[field] | |||
| } | |||
| func (f *CreateTeamForm) Validate(errs *binding.Errors, req *http.Request, ctx martini.Context) { | |||
| data := ctx.Get(reflect.TypeOf(base.TmplData{})).Interface().(base.TmplData) | |||
| validate(errs, data, f) | |||
| func (f *CreateTeamForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) { | |||
| validate(errs, ctx.Data, f, l) | |||
| } | |||
| @@ -1,33 +0,0 @@ | |||
| // 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 auth | |||
| import ( | |||
| "net/http" | |||
| "reflect" | |||
| "github.com/go-martini/martini" | |||
| "github.com/gogits/gogs/modules/base" | |||
| "github.com/gogits/gogs/modules/middleware/binding" | |||
| ) | |||
| type AddSSHKeyForm struct { | |||
| KeyName string `form:"keyname" binding:"Required"` | |||
| KeyContent string `form:"key_content" binding:"Required"` | |||
| } | |||
| func (f *AddSSHKeyForm) Name(field string) string { | |||
| names := map[string]string{ | |||
| "KeyName": "SSH key name", | |||
| "KeyContent": "SSH key content", | |||
| } | |||
| return names[field] | |||
| } | |||
| func (f *AddSSHKeyForm) Validate(errors *binding.Errors, req *http.Request, context martini.Context) { | |||
| data := context.Get(reflect.TypeOf(base.TmplData{})).Interface().(base.TmplData) | |||
| validate(errors, data, f) | |||
| } | |||
| @@ -0,0 +1,21 @@ | |||
| // 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 auth | |||
| import ( | |||
| "github.com/Unknwon/macaron" | |||
| "github.com/macaron-contrib/i18n" | |||
| "github.com/gogits/gogs/modules/middleware/binding" | |||
| ) | |||
| type AddSSHKeyForm struct { | |||
| SSHTitle string `form:"title" binding:"Required"` | |||
| Content string `form:"content" binding:"Required"` | |||
| } | |||
| func (f *AddSSHKeyForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) { | |||
| validate(errs, ctx.Data, f, l) | |||
| } | |||
| @@ -1,252 +0,0 @@ | |||
| // 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 auth | |||
| import ( | |||
| "net/http" | |||
| "reflect" | |||
| "github.com/go-martini/martini" | |||
| "github.com/gogits/gogs/modules/base" | |||
| "github.com/gogits/gogs/modules/middleware/binding" | |||
| ) | |||
| // __________ .__ __ | |||
| // \______ \ ____ ______ ____ _____|__|/ |_ ___________ ___.__. | |||
| // | _// __ \\____ \ / _ \/ ___/ \ __\/ _ \_ __ < | | | |||
| // | | \ ___/| |_> > <_> )___ \| || | ( <_> ) | \/\___ | | |||
| // |____|_ /\___ > __/ \____/____ >__||__| \____/|__| / ____| | |||
| // \/ \/|__| \/ \/ | |||
| type CreateRepoForm struct { | |||
| Uid int64 `form:"uid" binding:"Required"` | |||
| RepoName string `form:"repo" binding:"Required;AlphaDash;MaxSize(100)"` | |||
| Private bool `form:"private"` | |||
| Description string `form:"desc" binding:"MaxSize(255)"` | |||
| Language string `form:"language"` | |||
| License string `form:"license"` | |||
| InitReadme bool `form:"initReadme"` | |||
| } | |||
| func (f *CreateRepoForm) Name(field string) string { | |||
| names := map[string]string{ | |||
| "RepoName": "Repository name", | |||
| "Description": "Description", | |||
| } | |||
| return names[field] | |||
| } | |||
| func (f *CreateRepoForm) Validate(errors *binding.Errors, req *http.Request, context martini.Context) { | |||
| data := context.Get(reflect.TypeOf(base.TmplData{})).Interface().(base.TmplData) | |||
| validate(errors, data, f) | |||
| } | |||
| type MigrateRepoForm struct { | |||
| Url string `form:"url" binding:"Url"` | |||
| AuthUserName string `form:"auth_username"` | |||
| AuthPasswd string `form:"auth_password"` | |||
| Uid int64 `form:"uid" binding:"Required"` | |||
| RepoName string `form:"repo" binding:"Required;AlphaDash;MaxSize(100)"` | |||
| Mirror bool `form:"mirror"` | |||
| Private bool `form:"private"` | |||
| Description string `form:"desc" binding:"MaxSize(255)"` | |||
| } | |||
| func (f *MigrateRepoForm) Name(field string) string { | |||
| names := map[string]string{ | |||
| "Url": "Migration URL", | |||
| "RepoName": "Repository name", | |||
| "Description": "Description", | |||
| } | |||
| return names[field] | |||
| } | |||
| func (f *MigrateRepoForm) Validate(errors *binding.Errors, req *http.Request, context martini.Context) { | |||
| data := context.Get(reflect.TypeOf(base.TmplData{})).Interface().(base.TmplData) | |||
| validate(errors, data, f) | |||
| } | |||
| type RepoSettingForm struct { | |||
| RepoName string `form:"name" binding:"Required;AlphaDash;MaxSize(100)"` | |||
| Description string `form:"desc" binding:"MaxSize(255)"` | |||
| Website string `form:"site" binding:"Url;MaxSize(100)"` | |||
| Branch string `form:"branch"` | |||
| Interval int `form:"interval"` | |||
| Private bool `form:"private"` | |||
| GoGet bool `form:"goget"` | |||
| } | |||
| func (f *RepoSettingForm) Name(field string) string { | |||
| names := map[string]string{ | |||
| "RepoName": "Repository name", | |||
| "Description": "Description", | |||
| "Website": "Website address", | |||
| } | |||
| return names[field] | |||
| } | |||
| func (f *RepoSettingForm) Validate(errors *binding.Errors, req *http.Request, context martini.Context) { | |||
| data := context.Get(reflect.TypeOf(base.TmplData{})).Interface().(base.TmplData) | |||
| validate(errors, data, f) | |||
| } | |||
| // __ __ ___. .__ .__ __ | |||
| // / \ / \ ____\_ |__ | |__ | |__ ____ | | __ | |||
| // \ \/\/ // __ \| __ \| | \| | \ / _ \| |/ / | |||
| // \ /\ ___/| \_\ \ Y \ Y ( <_> ) < | |||
| // \__/\ / \___ >___ /___| /___| /\____/|__|_ \ | |||
| // \/ \/ \/ \/ \/ \/ | |||
| type NewWebhookForm struct { | |||
| Url string `form:"url" binding:"Required;Url"` | |||
| ContentType string `form:"content_type" binding:"Required"` | |||
| Secret string `form:"secret""` | |||
| PushOnly bool `form:"push_only"` | |||
| Active bool `form:"active"` | |||
| } | |||
| func (f *NewWebhookForm) Name(field string) string { | |||
| names := map[string]string{ | |||
| "Url": "Payload URL", | |||
| "ContentType": "Content type", | |||
| } | |||
| return names[field] | |||
| } | |||
| func (f *NewWebhookForm) Validate(errors *binding.Errors, req *http.Request, context martini.Context) { | |||
| data := context.Get(reflect.TypeOf(base.TmplData{})).Interface().(base.TmplData) | |||
| validate(errors, data, f) | |||
| } | |||
| // .___ | |||
| // | | ______ ________ __ ____ | |||
| // | |/ ___// ___/ | \_/ __ \ | |||
| // | |\___ \ \___ \| | /\ ___/ | |||
| // |___/____ >____ >____/ \___ > | |||
| // \/ \/ \/ | |||
| type CreateIssueForm struct { | |||
| IssueName string `form:"title" binding:"Required;MaxSize(50)"` | |||
| MilestoneId int64 `form:"milestoneid"` | |||
| AssigneeId int64 `form:"assigneeid"` | |||
| Labels string `form:"labels"` | |||
| Content string `form:"content"` | |||
| } | |||
| func (f *CreateIssueForm) Name(field string) string { | |||
| names := map[string]string{ | |||
| "IssueName": "Issue name", | |||
| } | |||
| return names[field] | |||
| } | |||
| func (f *CreateIssueForm) Validate(errors *binding.Errors, req *http.Request, context martini.Context) { | |||
| data := context.Get(reflect.TypeOf(base.TmplData{})).Interface().(base.TmplData) | |||
| validate(errors, data, f) | |||
| } | |||
| // _____ .__.__ __ | |||
| // / \ |__| | ____ _______/ |_ ____ ____ ____ | |||
| // / \ / \| | | _/ __ \ / ___/\ __\/ _ \ / \_/ __ \ | |||
| // / Y \ | |_\ ___/ \___ \ | | ( <_> ) | \ ___/ | |||
| // \____|__ /__|____/\___ >____ > |__| \____/|___| /\___ > | |||
| // \/ \/ \/ \/ \/ | |||
| type CreateMilestoneForm struct { | |||
| Title string `form:"title" binding:"Required;MaxSize(50)"` | |||
| Content string `form:"content"` | |||
| Deadline string `form:"due_date"` | |||
| } | |||
| func (f *CreateMilestoneForm) Name(field string) string { | |||
| names := map[string]string{ | |||
| "Title": "Milestone name", | |||
| } | |||
| return names[field] | |||
| } | |||
| func (f *CreateMilestoneForm) Validate(errors *binding.Errors, req *http.Request, context martini.Context) { | |||
| data := context.Get(reflect.TypeOf(base.TmplData{})).Interface().(base.TmplData) | |||
| validate(errors, data, f) | |||
| } | |||
| // .____ ___. .__ | |||
| // | | _____ \_ |__ ____ | | | |||
| // | | \__ \ | __ \_/ __ \| | | |||
| // | |___ / __ \| \_\ \ ___/| |__ | |||
| // |_______ (____ /___ /\___ >____/ | |||
| // \/ \/ \/ \/ | |||
| type CreateLabelForm struct { | |||
| Title string `form:"title" binding:"Required;MaxSize(50)"` | |||
| Color string `form:"color" binding:"Required;Size(7)"` | |||
| } | |||
| func (f *CreateLabelForm) Name(field string) string { | |||
| names := map[string]string{ | |||
| "Title": "Label name", | |||
| "Color": "Label color", | |||
| } | |||
| return names[field] | |||
| } | |||
| func (f *CreateLabelForm) Validate(errors *binding.Errors, req *http.Request, context martini.Context) { | |||
| data := context.Get(reflect.TypeOf(base.TmplData{})).Interface().(base.TmplData) | |||
| validate(errors, data, f) | |||
| } | |||
| // __________ .__ | |||
| // \______ \ ____ | | ____ _____ ______ ____ | |||
| // | _// __ \| | _/ __ \\__ \ / ___// __ \ | |||
| // | | \ ___/| |_\ ___/ / __ \_\___ \\ ___/ | |||
| // |____|_ /\___ >____/\___ >____ /____ >\___ > | |||
| // \/ \/ \/ \/ \/ \/ | |||
| type NewReleaseForm struct { | |||
| TagName string `form:"tag_name" binding:"Required"` | |||
| Target string `form:"tag_target" binding:"Required"` | |||
| Title string `form:"title" binding:"Required"` | |||
| Content string `form:"content" binding:"Required"` | |||
| Draft string `form:"draft"` | |||
| Prerelease bool `form:"prerelease"` | |||
| } | |||
| func (f *NewReleaseForm) Name(field string) string { | |||
| names := map[string]string{ | |||
| "TagName": "Tag name", | |||
| "Target": "Target", | |||
| "Title": "Release title", | |||
| "Content": "Release content", | |||
| } | |||
| return names[field] | |||
| } | |||
| func (f *NewReleaseForm) Validate(errors *binding.Errors, req *http.Request, context martini.Context) { | |||
| data := context.Get(reflect.TypeOf(base.TmplData{})).Interface().(base.TmplData) | |||
| validate(errors, data, f) | |||
| } | |||
| type EditReleaseForm struct { | |||
| Target string `form:"tag_target" binding:"Required"` | |||
| Title string `form:"title" binding:"Required"` | |||
| Content string `form:"content" binding:"Required"` | |||
| Draft string `form:"draft"` | |||
| Prerelease bool `form:"prerelease"` | |||
| } | |||
| func (f *EditReleaseForm) Name(field string) string { | |||
| names := map[string]string{ | |||
| "Target": "Target", | |||
| "Title": "Release title", | |||
| "Content": "Release content", | |||
| } | |||
| return names[field] | |||
| } | |||
| func (f *EditReleaseForm) Validate(errors *binding.Errors, req *http.Request, context martini.Context) { | |||
| data := context.Get(reflect.TypeOf(base.TmplData{})).Interface().(base.TmplData) | |||
| validate(errors, data, f) | |||
| } | |||
| @@ -0,0 +1,165 @@ | |||
| // 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 auth | |||
| import ( | |||
| "github.com/Unknwon/macaron" | |||
| "github.com/macaron-contrib/i18n" | |||
| "github.com/gogits/gogs/modules/middleware/binding" | |||
| ) | |||
| // _______________________________________ _________.______________________ _______________.___. | |||
| // \______ \_ _____/\______ \_____ \ / _____/| \__ ___/\_____ \\______ \__ | | | |||
| // | _/| __)_ | ___// | \ \_____ \ | | | | / | \| _// | | | |||
| // | | \| \ | | / | \/ \| | | | / | \ | \\____ | | |||
| // |____|_ /_______ / |____| \_______ /_______ /|___| |____| \_______ /____|_ // ______| | |||
| // \/ \/ \/ \/ \/ \/ \/ | |||
| type CreateRepoForm struct { | |||
| Uid int64 `form:"uid" binding:"Required"` | |||
| RepoName string `form:"repo_name" binding:"Required;AlphaDash;MaxSize(100)"` | |||
| Private bool `form:"private"` | |||
| Description string `form:"desc" binding:"MaxSize(255)"` | |||
| Gitignore string `form:"gitignore"` | |||
| License string `form:"license"` | |||
| InitReadme bool `form:"init_readme"` | |||
| } | |||
| func (f *CreateRepoForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) { | |||
| validate(errs, ctx.Data, f, l) | |||
| } | |||
| type MigrateRepoForm struct { | |||
| Url string `form:"url" binding:"Url"` | |||
| AuthUserName string `form:"auth_username"` | |||
| AuthPasswd string `form:"auth_password"` | |||
| Uid int64 `form:"uid" binding:"Required"` | |||
| RepoName string `form:"repo" binding:"Required;AlphaDash;MaxSize(100)"` | |||
| Mirror bool `form:"mirror"` | |||
| Private bool `form:"private"` | |||
| Description string `form:"desc" binding:"MaxSize(255)"` | |||
| } | |||
| func (f *MigrateRepoForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) { | |||
| validate(errs, ctx.Data, f, l) | |||
| } | |||
| type RepoSettingForm struct { | |||
| RepoName string `form:"name" binding:"Required;AlphaDash;MaxSize(100)"` | |||
| Description string `form:"desc" binding:"MaxSize(255)"` | |||
| Website string `form:"site" binding:"Url;MaxSize(100)"` | |||
| Branch string `form:"branch"` | |||
| Interval int `form:"interval"` | |||
| Private bool `form:"private"` | |||
| GoGet bool `form:"goget"` | |||
| } | |||
| func (f *RepoSettingForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) { | |||
| validate(errs, ctx.Data, f, l) | |||
| } | |||
| // __ __ ___. .__ .__ __ | |||
| // / \ / \ ____\_ |__ | |__ | |__ ____ | | __ | |||
| // \ \/\/ // __ \| __ \| | \| | \ / _ \| |/ / | |||
| // \ /\ ___/| \_\ \ Y \ Y ( <_> ) < | |||
| // \__/\ / \___ >___ /___| /___| /\____/|__|_ \ | |||
| // \/ \/ \/ \/ \/ \/ | |||
| type NewWebhookForm struct { | |||
| Url string `form:"url" binding:"Required;Url"` | |||
| ContentType string `form:"content_type" binding:"Required"` | |||
| Secret string `form:"secret""` | |||
| PushOnly bool `form:"push_only"` | |||
| Active bool `form:"active"` | |||
| } | |||
| func (f *NewWebhookForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) { | |||
| validate(errs, ctx.Data, f, l) | |||
| } | |||
| // .___ | |||
| // | | ______ ________ __ ____ | |||
| // | |/ ___// ___/ | \_/ __ \ | |||
| // | |\___ \ \___ \| | /\ ___/ | |||
| // |___/____ >____ >____/ \___ > | |||
| // \/ \/ \/ | |||
| type CreateIssueForm struct { | |||
| IssueName string `form:"title" binding:"Required;MaxSize(50)"` | |||
| MilestoneId int64 `form:"milestoneid"` | |||
| AssigneeId int64 `form:"assigneeid"` | |||
| Labels string `form:"labels"` | |||
| Content string `form:"content"` | |||
| } | |||
| func (f *CreateIssueForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) { | |||
| validate(errs, ctx.Data, f, l) | |||
| } | |||
| // _____ .__.__ __ | |||
| // / \ |__| | ____ _______/ |_ ____ ____ ____ | |||
| // / \ / \| | | _/ __ \ / ___/\ __\/ _ \ / \_/ __ \ | |||
| // / Y \ | |_\ ___/ \___ \ | | ( <_> ) | \ ___/ | |||
| // \____|__ /__|____/\___ >____ > |__| \____/|___| /\___ > | |||
| // \/ \/ \/ \/ \/ | |||
| type CreateMilestoneForm struct { | |||
| Title string `form:"title" binding:"Required;MaxSize(50)"` | |||
| Content string `form:"content"` | |||
| Deadline string `form:"due_date"` | |||
| } | |||
| func (f *CreateMilestoneForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) { | |||
| validate(errs, ctx.Data, f, l) | |||
| } | |||
| // .____ ___. .__ | |||
| // | | _____ \_ |__ ____ | | | |||
| // | | \__ \ | __ \_/ __ \| | | |||
| // | |___ / __ \| \_\ \ ___/| |__ | |||
| // |_______ (____ /___ /\___ >____/ | |||
| // \/ \/ \/ \/ | |||
| type CreateLabelForm struct { | |||
| Title string `form:"title" binding:"Required;MaxSize(50)"` | |||
| Color string `form:"color" binding:"Required;Size(7)"` | |||
| } | |||
| func (f *CreateLabelForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) { | |||
| validate(errs, ctx.Data, f, l) | |||
| } | |||
| // __________ .__ | |||
| // \______ \ ____ | | ____ _____ ______ ____ | |||
| // | _// __ \| | _/ __ \\__ \ / ___// __ \ | |||
| // | | \ ___/| |_\ ___/ / __ \_\___ \\ ___/ | |||
| // |____|_ /\___ >____/\___ >____ /____ >\___ > | |||
| // \/ \/ \/ \/ \/ \/ | |||
| type NewReleaseForm struct { | |||
| TagName string `form:"tag_name" binding:"Required"` | |||
| Target string `form:"tag_target" binding:"Required"` | |||
| Title string `form:"title" binding:"Required"` | |||
| Content string `form:"content" binding:"Required"` | |||
| Draft string `form:"draft"` | |||
| Prerelease bool `form:"prerelease"` | |||
| } | |||
| func (f *NewReleaseForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) { | |||
| validate(errs, ctx.Data, f, l) | |||
| } | |||
| type EditReleaseForm struct { | |||
| Target string `form:"tag_target" binding:"Required"` | |||
| Title string `form:"title" binding:"Required"` | |||
| Content string `form:"content" binding:"Required"` | |||
| Draft string `form:"draft"` | |||
| Prerelease bool `form:"prerelease"` | |||
| } | |||
| func (f *EditReleaseForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) { | |||
| validate(errs, ctx.Data, f, l) | |||
| } | |||
| @@ -1,299 +0,0 @@ | |||
| // 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 auth | |||
| import ( | |||
| "net/http" | |||
| "reflect" | |||
| "strings" | |||
| "github.com/go-martini/martini" | |||
| "github.com/gogits/session" | |||
| "github.com/gogits/gogs/models" | |||
| "github.com/gogits/gogs/modules/base" | |||
| "github.com/gogits/gogs/modules/log" | |||
| "github.com/gogits/gogs/modules/middleware/binding" | |||
| "github.com/gogits/gogs/modules/setting" | |||
| ) | |||
| // Web form interface. | |||
| type Form interface { | |||
| Name(field string) string | |||
| } | |||
| type RegisterForm struct { | |||
| UserName string `form:"username" binding:"Required;AlphaDashDot;MaxSize(30)"` | |||
| Email string `form:"email" binding:"Required;Email;MaxSize(50)"` | |||
| Password string `form:"passwd" binding:"Required;MinSize(6);MaxSize(30)"` | |||
| RetypePasswd string `form:"retypepasswd"` | |||
| LoginType string `form:"logintype"` | |||
| LoginName string `form:"loginname"` | |||
| } | |||
| func (f *RegisterForm) Name(field string) string { | |||
| names := map[string]string{ | |||
| "UserName": "Username", | |||
| "Email": "E-mail address", | |||
| "Password": "Password", | |||
| "RetypePasswd": "Re-type password", | |||
| } | |||
| return names[field] | |||
| } | |||
| func (f *RegisterForm) Validate(errs *binding.Errors, req *http.Request, ctx martini.Context) { | |||
| data := ctx.Get(reflect.TypeOf(base.TmplData{})).Interface().(base.TmplData) | |||
| validate(errs, data, f) | |||
| } | |||
| type LogInForm struct { | |||
| UserName string `form:"username" binding:"Required;MaxSize(35)"` | |||
| Password string `form:"passwd" binding:"Required;MinSize(6);MaxSize(30)"` | |||
| Remember bool `form:"remember"` | |||
| } | |||
| func (f *LogInForm) Name(field string) string { | |||
| names := map[string]string{ | |||
| "UserName": "Username", | |||
| "Password": "Password", | |||
| } | |||
| return names[field] | |||
| } | |||
| func (f *LogInForm) Validate(errs *binding.Errors, req *http.Request, ctx martini.Context) { | |||
| data := ctx.Get(reflect.TypeOf(base.TmplData{})).Interface().(base.TmplData) | |||
| validate(errs, data, f) | |||
| } | |||
| func GetMinMaxSize(field reflect.StructField) string { | |||
| for _, rule := range strings.Split(field.Tag.Get("binding"), ";") { | |||
| if strings.HasPrefix(rule, "MinSize(") || strings.HasPrefix(rule, "MaxSize(") { | |||
| return rule[8 : len(rule)-1] | |||
| } | |||
| } | |||
| return "" | |||
| } | |||
| func validate(errs *binding.Errors, data base.TmplData, f Form) { | |||
| if errs.Count() == 0 { | |||
| return | |||
| } else if len(errs.Overall) > 0 { | |||
| for _, err := range errs.Overall { | |||
| log.Error("%s: %v", reflect.TypeOf(f), err) | |||
| } | |||
| return | |||
| } | |||
| data["HasError"] = true | |||
| AssignForm(f, data) | |||
| typ := reflect.TypeOf(f) | |||
| val := reflect.ValueOf(f) | |||
| if typ.Kind() == reflect.Ptr { | |||
| typ = typ.Elem() | |||
| val = val.Elem() | |||
| } | |||
| for i := 0; i < typ.NumField(); i++ { | |||
| field := typ.Field(i) | |||
| fieldName := field.Tag.Get("form") | |||
| // Allow ignored fields in the struct | |||
| if fieldName == "-" { | |||
| continue | |||
| } | |||
| if err, ok := errs.Fields[field.Name]; ok { | |||
| data["Err_"+field.Name] = true | |||
| switch err { | |||
| case binding.BindingRequireError: | |||
| data["ErrorMsg"] = f.Name(field.Name) + " cannot be empty" | |||
| case binding.BindingAlphaDashError: | |||
| data["ErrorMsg"] = f.Name(field.Name) + " must be valid alpha or numeric or dash(-_) characters" | |||
| case binding.BindingAlphaDashDotError: | |||
| data["ErrorMsg"] = f.Name(field.Name) + " must be valid alpha or numeric or dash(-_) or dot characters" | |||
| case binding.BindingMinSizeError: | |||
| data["ErrorMsg"] = f.Name(field.Name) + " must contain at least " + GetMinMaxSize(field) + " characters" | |||
| case binding.BindingMaxSizeError: | |||
| data["ErrorMsg"] = f.Name(field.Name) + " must contain at most " + GetMinMaxSize(field) + " characters" | |||
| case binding.BindingEmailError: | |||
| data["ErrorMsg"] = f.Name(field.Name) + " is not a valid e-mail address" | |||
| case binding.BindingUrlError: | |||
| data["ErrorMsg"] = f.Name(field.Name) + " is not a valid URL" | |||
| default: | |||
| data["ErrorMsg"] = "Unknown error: " + err | |||
| } | |||
| return | |||
| } | |||
| } | |||
| } | |||
| // AssignForm assign form values back to the template data. | |||
| func AssignForm(form interface{}, data base.TmplData) { | |||
| typ := reflect.TypeOf(form) | |||
| val := reflect.ValueOf(form) | |||
| if typ.Kind() == reflect.Ptr { | |||
| typ = typ.Elem() | |||
| val = val.Elem() | |||
| } | |||
| for i := 0; i < typ.NumField(); i++ { | |||
| field := typ.Field(i) | |||
| fieldName := field.Tag.Get("form") | |||
| // Allow ignored fields in the struct | |||
| if fieldName == "-" { | |||
| continue | |||
| } | |||
| data[fieldName] = val.Field(i).Interface() | |||
| } | |||
| } | |||
| type InstallForm struct { | |||
| Database string `form:"database" binding:"Required"` | |||
| Host string `form:"host"` | |||
| User string `form:"user"` | |||
| Passwd string `form:"passwd"` | |||
| DatabaseName string `form:"database_name"` | |||
| SslMode string `form:"ssl_mode"` | |||
| DatabasePath string `form:"database_path"` | |||
| RepoRootPath string `form:"repo_path"` | |||
| RunUser string `form:"run_user"` | |||
| Domain string `form:"domain"` | |||
| AppUrl string `form:"app_url"` | |||
| AdminName string `form:"admin_name" binding:"Required;AlphaDashDot;MaxSize(30)"` | |||
| AdminPasswd string `form:"admin_pwd" binding:"Required;MinSize(6);MaxSize(30)"` | |||
| AdminEmail string `form:"admin_email" binding:"Required;Email;MaxSize(50)"` | |||
| SmtpHost string `form:"smtp_host"` | |||
| SmtpEmail string `form:"mailer_user"` | |||
| SmtpPasswd string `form:"mailer_pwd"` | |||
| RegisterConfirm string `form:"register_confirm"` | |||
| MailNotify string `form:"mail_notify"` | |||
| } | |||
| func (f *InstallForm) Name(field string) string { | |||
| names := map[string]string{ | |||
| "Database": "Database name", | |||
| "AdminName": "Admin user name", | |||
| "AdminPasswd": "Admin password", | |||
| "AdminEmail": "Admin e-maill address", | |||
| } | |||
| return names[field] | |||
| } | |||
| func (f *InstallForm) Validate(errors *binding.Errors, req *http.Request, context martini.Context) { | |||
| data := context.Get(reflect.TypeOf(base.TmplData{})).Interface().(base.TmplData) | |||
| validate(errors, data, f) | |||
| } | |||
| // SignedInId returns the id of signed in user. | |||
| func SignedInId(header http.Header, sess session.SessionStore) int64 { | |||
| if !models.HasEngine { | |||
| return 0 | |||
| } | |||
| if setting.Service.EnableReverseProxyAuth { | |||
| webAuthUser := header.Get(setting.ReverseProxyAuthUser) | |||
| if len(webAuthUser) > 0 { | |||
| u, err := models.GetUserByName(webAuthUser) | |||
| if err != nil { | |||
| if err != models.ErrUserNotExist { | |||
| log.Error("auth.user.SignedInId(GetUserByName): %v", err) | |||
| } | |||
| return 0 | |||
| } | |||
| return u.Id | |||
| } | |||
| } | |||
| uid := sess.Get("userId") | |||
| if uid == nil { | |||
| return 0 | |||
| } | |||
| if id, ok := uid.(int64); ok { | |||
| if _, err := models.GetUserById(id); err != nil { | |||
| if err != models.ErrUserNotExist { | |||
| log.Error("auth.user.SignedInId(GetUserById): %v", err) | |||
| } | |||
| return 0 | |||
| } | |||
| return id | |||
| } | |||
| return 0 | |||
| } | |||
| // SignedInUser returns the user object of signed user. | |||
| func SignedInUser(header http.Header, sess session.SessionStore) *models.User { | |||
| uid := SignedInId(header, sess) | |||
| if uid <= 0 { | |||
| return nil | |||
| } | |||
| u, err := models.GetUserById(uid) | |||
| if err != nil { | |||
| log.Error("user.SignedInUser: %v", err) | |||
| return nil | |||
| } | |||
| return u | |||
| } | |||
| // IsSignedIn check if any user has signed in. | |||
| func IsSignedIn(header http.Header, sess session.SessionStore) bool { | |||
| return SignedInId(header, sess) > 0 | |||
| } | |||
| type FeedsForm struct { | |||
| UserId int64 `form:"userid" binding:"Required"` | |||
| Page int64 `form:"p"` | |||
| } | |||
| type UpdateProfileForm struct { | |||
| UserName string `form:"username" binding:"Required;AlphaDash;MaxSize(30)"` | |||
| FullName string `form:"fullname" binding:"MaxSize(40)"` | |||
| Email string `form:"email" binding:"Required;Email;MaxSize(50)"` | |||
| Website string `form:"website" binding:"Url;MaxSize(50)"` | |||
| Location string `form:"location" binding:"MaxSize(50)"` | |||
| Avatar string `form:"avatar" binding:"Required;Email;MaxSize(50)"` | |||
| } | |||
| func (f *UpdateProfileForm) Name(field string) string { | |||
| names := map[string]string{ | |||
| "UserName": "Username", | |||
| "Email": "E-mail address", | |||
| "Website": "Website address", | |||
| "Location": "Location", | |||
| "Avatar": "Gravatar Email", | |||
| } | |||
| return names[field] | |||
| } | |||
| func (f *UpdateProfileForm) Validate(errs *binding.Errors, req *http.Request, ctx martini.Context) { | |||
| data := ctx.Get(reflect.TypeOf(base.TmplData{})).Interface().(base.TmplData) | |||
| validate(errs, data, f) | |||
| } | |||
| type UpdatePasswdForm struct { | |||
| OldPasswd string `form:"oldpasswd" binding:"Required;MinSize(6);MaxSize(30)"` | |||
| NewPasswd string `form:"newpasswd" binding:"Required;MinSize(6);MaxSize(30)"` | |||
| RetypePasswd string `form:"retypepasswd"` | |||
| } | |||
| func (f *UpdatePasswdForm) Name(field string) string { | |||
| names := map[string]string{ | |||
| "OldPasswd": "Old password", | |||
| "NewPasswd": "New password", | |||
| "RetypePasswd": "Re-type password", | |||
| } | |||
| return names[field] | |||
| } | |||
| func (f *UpdatePasswdForm) Validate(errs *binding.Errors, req *http.Request, ctx martini.Context) { | |||
| data := ctx.Get(reflect.TypeOf(base.TmplData{})).Interface().(base.TmplData) | |||
| validate(errs, data, f) | |||
| } | |||
| @@ -0,0 +1,98 @@ | |||
| // 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 auth | |||
| import ( | |||
| "github.com/Unknwon/macaron" | |||
| "github.com/macaron-contrib/i18n" | |||
| "github.com/gogits/gogs/modules/middleware/binding" | |||
| ) | |||
| type InstallForm struct { | |||
| Database string `form:"database" binding:"Required"` | |||
| Host string `form:"host"` | |||
| User string `form:"user"` | |||
| Passwd string `form:"passwd"` | |||
| DatabaseName string `form:"database_name"` | |||
| SslMode string `form:"ssl_mode"` | |||
| DatabasePath string `form:"database_path"` | |||
| RepoRootPath string `form:"repo_path"` | |||
| RunUser string `form:"run_user"` | |||
| Domain string `form:"domain"` | |||
| AppUrl string `form:"app_url"` | |||
| AdminName string `form:"admin_name" binding:"Required;AlphaDashDot;MaxSize(30)"` | |||
| AdminPasswd string `form:"admin_pwd" binding:"Required;MinSize(6);MaxSize(30)"` | |||
| AdminEmail string `form:"admin_email" binding:"Required;Email;MaxSize(50)"` | |||
| SmtpHost string `form:"smtp_host"` | |||
| SmtpEmail string `form:"mailer_user"` | |||
| SmtpPasswd string `form:"mailer_pwd"` | |||
| RegisterConfirm string `form:"register_confirm"` | |||
| MailNotify string `form:"mail_notify"` | |||
| } | |||
| func (f *InstallForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) { | |||
| validate(errs, ctx.Data, f, l) | |||
| } | |||
| // _____ ____ _________________ ___ | |||
| // / _ \ | | \__ ___/ | \ | |||
| // / /_\ \| | / | | / ~ \ | |||
| // / | \ | / | | \ Y / | |||
| // \____|__ /______/ |____| \___|_ / | |||
| // \/ \/ | |||
| type RegisterForm struct { | |||
| UserName string `form:"uname" binding:"Required;AlphaDashDot;MaxSize(35)"` | |||
| Email string `form:"email" binding:"Required;Email;MaxSize(50)"` | |||
| Password string `form:"password" binding:"Required;MinSize(6);MaxSize(30)"` | |||
| Retype string `form:"retype"` | |||
| LoginType string `form:"logintype"` | |||
| LoginName string `form:"loginname"` | |||
| } | |||
| func (f *RegisterForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) { | |||
| validate(errs, ctx.Data, f, l) | |||
| } | |||
| type SignInForm struct { | |||
| UserName string `form:"uname" binding:"Required;MaxSize(35)"` | |||
| Password string `form:"password" binding:"Required;MinSize(6);MaxSize(30)"` | |||
| Remember bool `form:"remember"` | |||
| } | |||
| func (f *SignInForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) { | |||
| validate(errs, ctx.Data, f, l) | |||
| } | |||
| // __________________________________________.___ _______ ________ _________ | |||
| // / _____/\_ _____/\__ ___/\__ ___/| |\ \ / _____/ / _____/ | |||
| // \_____ \ | __)_ | | | | | |/ | \/ \ ___ \_____ \ | |||
| // / \ | \ | | | | | / | \ \_\ \/ \ | |||
| // /_______ //_______ / |____| |____| |___\____|__ /\______ /_______ / | |||
| // \/ \/ \/ \/ \/ | |||
| type UpdateProfileForm struct { | |||
| UserName string `form:"uname" binding:"Required;MaxSize(35)"` | |||
| FullName string `form:"fullname" binding:"MaxSize(40)"` | |||
| Email string `form:"email" binding:"Required;Email;MaxSize(50)"` | |||
| Website string `form:"website" binding:"Url;MaxSize(50)"` | |||
| Location string `form:"location" binding:"MaxSize(50)"` | |||
| Avatar string `form:"avatar" binding:"Required;Email;MaxSize(50)"` | |||
| } | |||
| func (f *UpdateProfileForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) { | |||
| validate(errs, ctx.Data, f, l) | |||
| } | |||
| type ChangePasswordForm struct { | |||
| OldPassword string `form:"old_password" binding:"Required;MinSize(6);MaxSize(30)"` | |||
| Password string `form:"password" binding:"Required;MinSize(6);MaxSize(30)"` | |||
| Retype string `form:"retype"` | |||
| } | |||
| func (f *ChangePasswordForm) Validate(ctx *macaron.Context, errs *binding.Errors, l i18n.Locale) { | |||
| validate(errs, ctx.Data, f, l) | |||
| } | |||
| @@ -5,9 +5,7 @@ | |||
| package base | |||
| type ( | |||
| // Type TmplData represents data in the templates. | |||
| TmplData map[string]interface{} | |||
| TplName string | |||
| TplName string | |||
| ApiJsonErr struct { | |||
| Message string `json:"message"` | |||
| @@ -51,7 +51,7 @@ var mailDomains = map[string]string{ | |||
| var TemplateFuncs template.FuncMap = map[string]interface{}{ | |||
| "GoVer": func() string { | |||
| return runtime.Version() | |||
| return strings.Title(runtime.Version()) | |||
| }, | |||
| "AppName": func() string { | |||
| return setting.AppName | |||
| @@ -69,7 +69,8 @@ var TemplateFuncs template.FuncMap = map[string]interface{}{ | |||
| return fmt.Sprint(time.Since(startTime).Nanoseconds()/1e6) + "ms" | |||
| }, | |||
| "AvatarLink": AvatarLink, | |||
| "str2html": Str2html, | |||
| "str2html": Str2html, // TODO: Legacy | |||
| "Str2html": Str2html, | |||
| "TimeSince": TimeSince, | |||
| "FileSize": FileSize, | |||
| "Subtract": Subtract, | |||
| @@ -98,8 +99,11 @@ var TemplateFuncs template.FuncMap = map[string]interface{}{ | |||
| "DiffTypeToStr": DiffTypeToStr, | |||
| "DiffLineTypeToStr": DiffLineTypeToStr, | |||
| "ShortSha": ShortSha, | |||
| "Oauth2Icon": Oauth2Icon, | |||
| "Oauth2Name": Oauth2Name, | |||
| "Md5": EncodeMd5, | |||
| "ActionContent2Commits": ActionContent2Commits, | |||
| "Oauth2Icon": Oauth2Icon, | |||
| "Oauth2Name": Oauth2Name, | |||
| "CreateCaptcha": func() string { return "" }, | |||
| } | |||
| type Actioner interface { | |||
| @@ -117,11 +121,11 @@ type Actioner interface { | |||
| func ActionIcon(opType int) string { | |||
| switch opType { | |||
| case 1: // Create repository. | |||
| return "plus-circle" | |||
| return "repo" | |||
| case 5, 9: // Commit repository. | |||
| return "arrow-circle-o-right" | |||
| return "git-commit" | |||
| case 6: // Create issue. | |||
| return "exclamation-circle" | |||
| return "issue-opened" | |||
| case 8: // Transfer repository. | |||
| return "share" | |||
| case 10: // Comment issue. | |||
| @@ -131,6 +135,7 @@ func ActionIcon(opType int) string { | |||
| } | |||
| } | |||
| // TODO: Legacy | |||
| const ( | |||
| TPL_CREATE_REPO = `<a href="/user/%s">%s</a> created repository <a href="/%s">%s</a>` | |||
| TPL_COMMIT_REPO = `<a href="/user/%s">%s</a> pushed to <a href="/%s/src/%s">%s</a> at <a href="/%s">%s</a>%s` | |||
| @@ -155,6 +160,15 @@ type PushCommits struct { | |||
| Commits []*PushCommit | |||
| } | |||
| func ActionContent2Commits(act Actioner) *PushCommits { | |||
| var push *PushCommits | |||
| if err := json.Unmarshal([]byte(act.GetContent()), &push); err != nil { | |||
| return nil | |||
| } | |||
| return push | |||
| } | |||
| // TODO: Legacy | |||
| // ActionDesc accepts int that represents action operation type | |||
| // and returns the description. | |||
| func ActionDesc(act Actioner) string { | |||
| @@ -14,10 +14,13 @@ import ( | |||
| "hash" | |||
| "html/template" | |||
| "math" | |||
| "strconv" | |||
| r "math/rand" | |||
| "strings" | |||
| "time" | |||
| "github.com/Unknwon/com" | |||
| "github.com/Unknwon/i18n" | |||
| "github.com/gogits/gogs/modules/setting" | |||
| ) | |||
| @@ -43,6 +46,33 @@ func GetRandomString(n int, alphabets ...byte) string { | |||
| return string(bytes) | |||
| } | |||
| // RandomCreateBytes generate random []byte by specify chars. | |||
| func RandomCreateBytes(n int, alphabets ...byte) []byte { | |||
| const alphanum = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" | |||
| var bytes = make([]byte, n) | |||
| var randby bool | |||
| if num, err := rand.Read(bytes); num != n || err != nil { | |||
| r.Seed(time.Now().UnixNano()) | |||
| randby = true | |||
| } | |||
| for i, b := range bytes { | |||
| if len(alphabets) == 0 { | |||
| if randby { | |||
| bytes[i] = alphanum[r.Intn(len(alphanum))] | |||
| } else { | |||
| bytes[i] = alphanum[b%byte(len(alphanum))] | |||
| } | |||
| } else { | |||
| if randby { | |||
| bytes[i] = alphabets[r.Intn(len(alphabets))] | |||
| } else { | |||
| bytes[i] = alphabets[b%byte(len(alphabets))] | |||
| } | |||
| } | |||
| } | |||
| return bytes | |||
| } | |||
| // http://code.google.com/p/go/source/browse/pbkdf2/pbkdf2.go?repo=crypto | |||
| func PBKDF2(password, salt []byte, iter, keyLen int, h func() hash.Hash) []byte { | |||
| prf := hmac.New(h, password) | |||
| @@ -90,7 +120,7 @@ func VerifyTimeLimitCode(data string, minutes int, code string) bool { | |||
| // split code | |||
| start := code[:12] | |||
| lives := code[12:18] | |||
| if d, err := StrTo(lives).Int(); err == nil { | |||
| if d, err := com.StrTo(lives).Int(); err == nil { | |||
| minutes = d | |||
| } | |||
| @@ -134,7 +164,7 @@ func CreateTimeLimitCode(data string, minutes int, startInf interface{}) string | |||
| // create sha1 encode string | |||
| sh := sha1.New() | |||
| sh.Write([]byte(data + setting.SecretKey + startStr + endStr + ToStr(minutes))) | |||
| sh.Write([]byte(data + setting.SecretKey + startStr + endStr + com.ToStr(minutes))) | |||
| encoded := hex.EncodeToString(sh.Sum(nil)) | |||
| code := fmt.Sprintf("%s%06d%s", startStr, minutes, encoded) | |||
| @@ -240,60 +270,59 @@ func TimeSincePro(then time.Time) string { | |||
| return strings.TrimPrefix(timeStr, ", ") | |||
| } | |||
| // timeSince calculates the time interval and generate user-friendly string. | |||
| func timeSince(then time.Time) string { | |||
| func timeSince(then time.Time, lang string) string { | |||
| now := time.Now() | |||
| lbl := "ago" | |||
| lbl := i18n.Tr(lang, "tool.ago") | |||
| diff := now.Unix() - then.Unix() | |||
| if then.After(now) { | |||
| lbl = "from now" | |||
| lbl = i18n.Tr(lang, "tool.from_now") | |||
| diff = then.Unix() - now.Unix() | |||
| } | |||
| switch { | |||
| case diff <= 0: | |||
| return "now" | |||
| return i18n.Tr(lang, "tool.now") | |||
| case diff <= 2: | |||
| return fmt.Sprintf("1 second %s", lbl) | |||
| return i18n.Tr(lang, "tool.1s", lbl) | |||
| case diff < 1*Minute: | |||
| return fmt.Sprintf("%d seconds %s", diff, lbl) | |||
| return i18n.Tr(lang, "tool.seconds", diff, lbl) | |||
| case diff < 2*Minute: | |||
| return fmt.Sprintf("1 minute %s", lbl) | |||
| return i18n.Tr(lang, "tool.1m", lbl) | |||
| case diff < 1*Hour: | |||
| return fmt.Sprintf("%d minutes %s", diff/Minute, lbl) | |||
| return i18n.Tr(lang, "tool.minutes", diff/Minute, lbl) | |||
| case diff < 2*Hour: | |||
| return fmt.Sprintf("1 hour %s", lbl) | |||
| return i18n.Tr(lang, "tool.1h", lbl) | |||
| case diff < 1*Day: | |||
| return fmt.Sprintf("%d hours %s", diff/Hour, lbl) | |||
| return i18n.Tr(lang, "tool.hours", diff/Hour, lbl) | |||
| case diff < 2*Day: | |||
| return fmt.Sprintf("1 day %s", lbl) | |||
| return i18n.Tr(lang, "tool.1d", lbl) | |||
| case diff < 1*Week: | |||
| return fmt.Sprintf("%d days %s", diff/Day, lbl) | |||
| return i18n.Tr(lang, "tool.days", diff/Day, lbl) | |||
| case diff < 2*Week: | |||
| return fmt.Sprintf("1 week %s", lbl) | |||
| return i18n.Tr(lang, "tool.1w", lbl) | |||
| case diff < 1*Month: | |||
| return fmt.Sprintf("%d weeks %s", diff/Week, lbl) | |||
| return i18n.Tr(lang, "tool.weeks", diff/Week, lbl) | |||
| case diff < 2*Month: | |||
| return fmt.Sprintf("1 month %s", lbl) | |||
| return i18n.Tr(lang, "tool.1mon", lbl) | |||
| case diff < 1*Year: | |||
| return fmt.Sprintf("%d months %s", diff/Month, lbl) | |||
| return i18n.Tr(lang, "tool.months", diff/Month, lbl) | |||
| case diff < 2*Year: | |||
| return fmt.Sprintf("1 year %s", lbl) | |||
| return i18n.Tr(lang, "tool.1y", lbl) | |||
| default: | |||
| return fmt.Sprintf("%d years %s", diff/Year, lbl) | |||
| return i18n.Tr(lang, "tool.years", diff/Year, lbl) | |||
| } | |||
| } | |||
| // TimeSince calculates the time interval and generate user-friendly string. | |||
| func TimeSince(t time.Time) template.HTML { | |||
| return template.HTML(fmt.Sprintf(`<span class="time-since" title="%s">%s</span>`, t.Format(setting.TimeFormat), timeSince(t))) | |||
| func TimeSince(t time.Time, lang string) template.HTML { | |||
| return template.HTML(fmt.Sprintf(`<span class="time-since" title="%s">%s</span>`, t.Format(setting.TimeFormat), timeSince(t, lang))) | |||
| } | |||
| const ( | |||
| @@ -445,88 +474,3 @@ func DateFormat(t time.Time, format string) string { | |||
| format = replacer.Replace(format) | |||
| return t.Format(format) | |||
| } | |||
| // convert string to specify type | |||
| type StrTo string | |||
| func (f StrTo) Exist() bool { | |||
| return string(f) != string(0x1E) | |||
| } | |||
| func (f StrTo) Int() (int, error) { | |||
| v, err := strconv.ParseInt(f.String(), 10, 32) | |||
| return int(v), err | |||
| } | |||
| func (f StrTo) Int64() (int64, error) { | |||
| v, err := strconv.ParseInt(f.String(), 10, 64) | |||
| return int64(v), err | |||
| } | |||
| func (f StrTo) MustInt() int { | |||
| v, _ := f.Int() | |||
| return v | |||
| } | |||
| func (f StrTo) MustInt64() int64 { | |||
| v, _ := f.Int64() | |||
| return v | |||
| } | |||
| func (f StrTo) String() string { | |||
| if f.Exist() { | |||
| return string(f) | |||
| } | |||
| return "" | |||
| } | |||
| // convert any type to string | |||
| func ToStr(value interface{}, args ...int) (s string) { | |||
| switch v := value.(type) { | |||
| case bool: | |||
| s = strconv.FormatBool(v) | |||
| case float32: | |||
| s = strconv.FormatFloat(float64(v), 'f', argInt(args).Get(0, -1), argInt(args).Get(1, 32)) | |||
| case float64: | |||
| s = strconv.FormatFloat(v, 'f', argInt(args).Get(0, -1), argInt(args).Get(1, 64)) | |||
| case int: | |||
| s = strconv.FormatInt(int64(v), argInt(args).Get(0, 10)) | |||
| case int8: | |||
| s = strconv.FormatInt(int64(v), argInt(args).Get(0, 10)) | |||
| case int16: | |||
| s = strconv.FormatInt(int64(v), argInt(args).Get(0, 10)) | |||
| case int32: | |||
| s = strconv.FormatInt(int64(v), argInt(args).Get(0, 10)) | |||
| case int64: | |||
| s = strconv.FormatInt(v, argInt(args).Get(0, 10)) | |||
| case uint: | |||
| s = strconv.FormatUint(uint64(v), argInt(args).Get(0, 10)) | |||
| case uint8: | |||
| s = strconv.FormatUint(uint64(v), argInt(args).Get(0, 10)) | |||
| case uint16: | |||
| s = strconv.FormatUint(uint64(v), argInt(args).Get(0, 10)) | |||
| case uint32: | |||
| s = strconv.FormatUint(uint64(v), argInt(args).Get(0, 10)) | |||
| case uint64: | |||
| s = strconv.FormatUint(v, argInt(args).Get(0, 10)) | |||
| case string: | |||
| s = v | |||
| case []byte: | |||
| s = string(v) | |||
| default: | |||
| s = fmt.Sprintf("%v", v) | |||
| } | |||
| return s | |||
| } | |||
| type argInt []int | |||
| func (a argInt) Get(i int, args ...int) (r int) { | |||
| if i >= 0 && i < len(a) { | |||
| r = a[i] | |||
| } else if len(args) > 0 { | |||
| r = args[0] | |||
| } | |||
| return | |||
| } | |||
| @@ -0,0 +1,201 @@ | |||
| // 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 captcha a middleware that provides captcha service for Macaron. | |||
| package captcha | |||
| import ( | |||
| "fmt" | |||
| "html/template" | |||
| "net/http" | |||
| "path" | |||
| "strings" | |||
| "github.com/Unknwon/macaron" | |||
| "github.com/gogits/cache" | |||
| "github.com/gogits/gogs/modules/base" | |||
| "github.com/gogits/gogs/modules/log" | |||
| ) | |||
| var ( | |||
| defaultChars = []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9} | |||
| ) | |||
| const ( | |||
| // default captcha attributes | |||
| challengeNums = 6 | |||
| expiration = 600 | |||
| fieldIdName = "captcha_id" | |||
| fieldCaptchaName = "captcha" | |||
| cachePrefix = "captcha_" | |||
| defaultURLPrefix = "/captcha/" | |||
| ) | |||
| // Captcha struct | |||
| type Captcha struct { | |||
| store cache.Cache | |||
| // url prefix for captcha image | |||
| URLPrefix string | |||
| // specify captcha id input field name | |||
| FieldIdName string | |||
| // specify captcha result input field name | |||
| FieldCaptchaName string | |||
| // captcha image width and height | |||
| StdWidth int | |||
| StdHeight int | |||
| // captcha chars nums | |||
| ChallengeNums int | |||
| // captcha expiration seconds | |||
| Expiration int64 | |||
| // cache key prefix | |||
| CachePrefix string | |||
| } | |||
| // generate key string | |||
| func (c *Captcha) key(id string) string { | |||
| return c.CachePrefix + id | |||
| } | |||
| // generate rand chars with default chars | |||
| func (c *Captcha) genRandChars() []byte { | |||
| return base.RandomCreateBytes(c.ChallengeNums, defaultChars...) | |||
| } | |||
| // beego filter handler for serve captcha image | |||
| func (c *Captcha) Handler(ctx *macaron.Context) { | |||
| var chars []byte | |||
| id := path.Base(ctx.Req.RequestURI) | |||
| if i := strings.Index(id, "."); i != -1 { | |||
| id = id[:i] | |||
| } | |||
| key := c.key(id) | |||
| if v, ok := c.store.Get(key).([]byte); ok { | |||
| chars = v | |||
| } else { | |||
| ctx.Status(404) | |||
| ctx.Write([]byte("captcha not found")) | |||
| return | |||
| } | |||
| // reload captcha | |||
| if len(ctx.Query("reload")) > 0 { | |||
| chars = c.genRandChars() | |||
| if err := c.store.Put(key, chars, c.Expiration); err != nil { | |||
| ctx.Status(500) | |||
| ctx.Write([]byte("captcha reload error")) | |||
| log.Error(4, "Reload Create Captcha Error: %v", err) | |||
| return | |||
| } | |||
| } | |||
| img := NewImage(chars, c.StdWidth, c.StdHeight) | |||
| if _, err := img.WriteTo(ctx.RW()); err != nil { | |||
| log.Error(4, "Write Captcha Image Error: %v", err) | |||
| } | |||
| } | |||
| // tempalte func for output html | |||
| func (c *Captcha) CreateCaptchaHtml() template.HTML { | |||
| value, err := c.CreateCaptcha() | |||
| if err != nil { | |||
| log.Error(4, "Create Captcha Error: %v", err) | |||
| return "" | |||
| } | |||
| // create html | |||
| return template.HTML(fmt.Sprintf(`<input type="hidden" name="%s" value="%s">`+ | |||
| `<a class="captcha" href="javascript:">`+ | |||
| `<img onclick="this.src=('%s%s.png?reload='+(new Date()).getTime())" class="captcha-img" src="%s%s.png">`+ | |||
| `</a>`, c.FieldIdName, value, c.URLPrefix, value, c.URLPrefix, value)) | |||
| } | |||
| // create a new captcha id | |||
| func (c *Captcha) CreateCaptcha() (string, error) { | |||
| // generate captcha id | |||
| id := string(base.RandomCreateBytes(15)) | |||
| // get the captcha chars | |||
| chars := c.genRandChars() | |||
| // save to store | |||
| if err := c.store.Put(c.key(id), chars, c.Expiration); err != nil { | |||
| return "", err | |||
| } | |||
| return id, nil | |||
| } | |||
| // verify from a request | |||
| func (c *Captcha) VerifyReq(req *http.Request) bool { | |||
| req.ParseForm() | |||
| return c.Verify(req.Form.Get(c.FieldIdName), req.Form.Get(c.FieldCaptchaName)) | |||
| } | |||
| // direct verify id and challenge string | |||
| func (c *Captcha) Verify(id string, challenge string) (success bool) { | |||
| if len(challenge) == 0 || len(id) == 0 { | |||
| return | |||
| } | |||
| var chars []byte | |||
| key := c.key(id) | |||
| if v, ok := c.store.Get(key).([]byte); ok && len(v) == len(challenge) { | |||
| chars = v | |||
| } else { | |||
| return | |||
| } | |||
| defer func() { | |||
| // finally remove it | |||
| c.store.Delete(key) | |||
| }() | |||
| // verify challenge | |||
| for i, c := range chars { | |||
| if c != challenge[i]-48 { | |||
| return | |||
| } | |||
| } | |||
| return true | |||
| } | |||
| // create a new captcha.Captcha | |||
| func NewCaptcha(urlPrefix string, store cache.Cache) *Captcha { | |||
| cpt := &Captcha{} | |||
| cpt.store = store | |||
| cpt.FieldIdName = fieldIdName | |||
| cpt.FieldCaptchaName = fieldCaptchaName | |||
| cpt.ChallengeNums = challengeNums | |||
| cpt.Expiration = expiration | |||
| cpt.CachePrefix = cachePrefix | |||
| cpt.StdWidth = stdWidth | |||
| cpt.StdHeight = stdHeight | |||
| if len(urlPrefix) == 0 { | |||
| urlPrefix = defaultURLPrefix | |||
| } | |||
| if urlPrefix[len(urlPrefix)-1] != '/' { | |||
| urlPrefix += "/" | |||
| } | |||
| cpt.URLPrefix = urlPrefix | |||
| base.TemplateFuncs["CreateCaptcha"] = cpt.CreateCaptchaHtml | |||
| return cpt | |||
| } | |||
| @@ -0,0 +1,487 @@ | |||
| // 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 captcha | |||
| import ( | |||
| "bytes" | |||
| "image" | |||
| "image/color" | |||
| "image/png" | |||
| "io" | |||
| "math" | |||
| ) | |||
| const ( | |||
| fontWidth = 11 | |||
| fontHeight = 18 | |||
| blackChar = 1 | |||
| // Standard width and height of a captcha image. | |||
| stdWidth = 240 | |||
| stdHeight = 80 | |||
| // Maximum absolute skew factor of a single digit. | |||
| maxSkew = 0.7 | |||
| // Number of background circles. | |||
| circleCount = 20 | |||
| ) | |||
| var font = [][]byte{ | |||
| { // 0 | |||
| 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, | |||
| 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, | |||
| 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, | |||
| 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, | |||
| 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, | |||
| 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, | |||
| 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, | |||
| 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, | |||
| 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, | |||
| 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, | |||
| 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, | |||
| 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, | |||
| 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, | |||
| 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, | |||
| 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, | |||
| 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, | |||
| 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, | |||
| 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, | |||
| }, | |||
| { // 1 | |||
| 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, | |||
| 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, | |||
| 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, | |||
| 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, | |||
| 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, | |||
| 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, | |||
| 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, | |||
| 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, | |||
| 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, | |||
| 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, | |||
| 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, | |||
| 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, | |||
| 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, | |||
| 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, | |||
| 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, | |||
| 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, | |||
| 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, | |||
| 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, | |||
| }, | |||
| { // 2 | |||
| 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, | |||
| 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, | |||
| 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, | |||
| 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, | |||
| 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, | |||
| 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, | |||
| 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, | |||
| 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, | |||
| 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, | |||
| 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, | |||
| 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, | |||
| 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, | |||
| 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, | |||
| 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, | |||
| 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, | |||
| 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, | |||
| 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, | |||
| 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, | |||
| }, | |||
| { // 3 | |||
| 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, | |||
| 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, | |||
| 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, | |||
| 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, | |||
| 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, | |||
| 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, | |||
| 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, | |||
| 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, | |||
| 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, | |||
| 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, | |||
| 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, | |||
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, | |||
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, | |||
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, | |||
| 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, | |||
| 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, | |||
| 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, | |||
| 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, | |||
| }, | |||
| { // 4 | |||
| 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, | |||
| 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, | |||
| 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, | |||
| 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, | |||
| 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, | |||
| 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, | |||
| 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, | |||
| 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, | |||
| 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, | |||
| 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, | |||
| 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, | |||
| 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, | |||
| 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, | |||
| 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, | |||
| 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, | |||
| 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, | |||
| 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, | |||
| 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, | |||
| }, | |||
| { // 5 | |||
| 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, | |||
| 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, | |||
| 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, | |||
| 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, | |||
| 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, | |||
| 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, | |||
| 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, | |||
| 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, | |||
| 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, | |||
| 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, | |||
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, | |||
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, | |||
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, | |||
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, | |||
| 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, | |||
| 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, | |||
| 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, | |||
| 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, | |||
| }, | |||
| { // 6 | |||
| 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, | |||
| 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, | |||
| 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, | |||
| 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, | |||
| 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, | |||
| 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |||
| 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, | |||
| 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, | |||
| 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, | |||
| 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, | |||
| 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, | |||
| 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, | |||
| 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, | |||
| 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, | |||
| 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, | |||
| 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, | |||
| 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, | |||
| 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, | |||
| }, | |||
| { // 7 | |||
| 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, | |||
| 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, | |||
| 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, | |||
| 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, | |||
| 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, | |||
| 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, | |||
| 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, | |||
| 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, | |||
| 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, | |||
| 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, | |||
| 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, | |||
| 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, | |||
| 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, | |||
| 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, | |||
| 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, | |||
| 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, | |||
| 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, | |||
| 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, | |||
| }, | |||
| { // 8 | |||
| 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, | |||
| 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, | |||
| 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, | |||
| 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, | |||
| 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, | |||
| 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, | |||
| 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, | |||
| 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, | |||
| 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, | |||
| 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, | |||
| 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, | |||
| 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, | |||
| 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, | |||
| 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, | |||
| 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, | |||
| 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, | |||
| 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, | |||
| 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, | |||
| }, | |||
| { // 9 | |||
| 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, | |||
| 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, | |||
| 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, | |||
| 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, | |||
| 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, | |||
| 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, | |||
| 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, | |||
| 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, | |||
| 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, | |||
| 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, | |||
| 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, | |||
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, | |||
| 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, | |||
| 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, | |||
| 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, | |||
| 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, | |||
| 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, | |||
| 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, | |||
| }, | |||
| } | |||
| type Image struct { | |||
| *image.Paletted | |||
| numWidth int | |||
| numHeight int | |||
| dotSize int | |||
| } | |||
| var prng = &siprng{} | |||
| // randIntn returns a pseudorandom non-negative int in range [0, n). | |||
| func randIntn(n int) int { | |||
| return prng.Intn(n) | |||
| } | |||
| // randInt returns a pseudorandom int in range [from, to]. | |||
| func randInt(from, to int) int { | |||
| return prng.Intn(to+1-from) + from | |||
| } | |||
| // randFloat returns a pseudorandom float64 in range [from, to]. | |||
| func randFloat(from, to float64) float64 { | |||
| return (to-from)*prng.Float64() + from | |||
| } | |||
| func randomPalette() color.Palette { | |||
| p := make([]color.Color, circleCount+1) | |||
| // Transparent color. | |||
| p[0] = color.RGBA{0xFF, 0xFF, 0xFF, 0x00} | |||
| // Primary color. | |||
| prim := color.RGBA{ | |||
| uint8(randIntn(129)), | |||
| uint8(randIntn(129)), | |||
| uint8(randIntn(129)), | |||
| 0xFF, | |||
| } | |||
| p[1] = prim | |||
| // Circle colors. | |||
| for i := 2; i <= circleCount; i++ { | |||
| p[i] = randomBrightness(prim, 255) | |||
| } | |||
| return p | |||
| } | |||
| // NewImage returns a new captcha image of the given width and height with the | |||
| // given digits, where each digit must be in range 0-9. | |||
| func NewImage(digits []byte, width, height int) *Image { | |||
| m := new(Image) | |||
| m.Paletted = image.NewPaletted(image.Rect(0, 0, width, height), randomPalette()) | |||
| m.calculateSizes(width, height, len(digits)) | |||
| // Randomly position captcha inside the image. | |||
| maxx := width - (m.numWidth+m.dotSize)*len(digits) - m.dotSize | |||
| maxy := height - m.numHeight - m.dotSize*2 | |||
| var border int | |||
| if width > height { | |||
| border = height / 5 | |||
| } else { | |||
| border = width / 5 | |||
| } | |||
| x := randInt(border, maxx-border) | |||
| y := randInt(border, maxy-border) | |||
| // Draw digits. | |||
| for _, n := range digits { | |||
| m.drawDigit(font[n], x, y) | |||
| x += m.numWidth + m.dotSize | |||
| } | |||
| // Draw strike-through line. | |||
| m.strikeThrough() | |||
| // Apply wave distortion. | |||
| m.distort(randFloat(5, 10), randFloat(100, 200)) | |||
| // Fill image with random circles. | |||
| m.fillWithCircles(circleCount, m.dotSize) | |||
| return m | |||
| } | |||
| // encodedPNG encodes an image to PNG and returns | |||
| // the result as a byte slice. | |||
| func (m *Image) encodedPNG() []byte { | |||
| var buf bytes.Buffer | |||
| if err := png.Encode(&buf, m.Paletted); err != nil { | |||
| panic(err.Error()) | |||
| } | |||
| return buf.Bytes() | |||
| } | |||
| // WriteTo writes captcha image in PNG format into the given writer. | |||
| func (m *Image) WriteTo(w io.Writer) (int64, error) { | |||
| n, err := w.Write(m.encodedPNG()) | |||
| return int64(n), err | |||
| } | |||
| func (m *Image) calculateSizes(width, height, ncount int) { | |||
| // Goal: fit all digits inside the image. | |||
| var border int | |||
| if width > height { | |||
| border = height / 4 | |||
| } else { | |||
| border = width / 4 | |||
| } | |||
| // Convert everything to floats for calculations. | |||
| w := float64(width - border*2) | |||
| h := float64(height - border*2) | |||
| // fw takes into account 1-dot spacing between digits. | |||
| fw := float64(fontWidth + 1) | |||
| fh := float64(fontHeight) | |||
| nc := float64(ncount) | |||
| // Calculate the width of a single digit taking into account only the | |||
| // width of the image. | |||
| nw := w / nc | |||
| // Calculate the height of a digit from this width. | |||
| nh := nw * fh / fw | |||
| // Digit too high? | |||
| if nh > h { | |||
| // Fit digits based on height. | |||
| nh = h | |||
| nw = fw / fh * nh | |||
| } | |||
| // Calculate dot size. | |||
| m.dotSize = int(nh / fh) | |||
| // Save everything, making the actual width smaller by 1 dot to account | |||
| // for spacing between digits. | |||
| m.numWidth = int(nw) - m.dotSize | |||
| m.numHeight = int(nh) | |||
| } | |||
| func (m *Image) drawHorizLine(fromX, toX, y int, colorIdx uint8) { | |||
| for x := fromX; x <= toX; x++ { | |||
| m.SetColorIndex(x, y, colorIdx) | |||
| } | |||
| } | |||
| func (m *Image) drawCircle(x, y, radius int, colorIdx uint8) { | |||
| f := 1 - radius | |||
| dfx := 1 | |||
| dfy := -2 * radius | |||
| xo := 0 | |||
| yo := radius | |||
| m.SetColorIndex(x, y+radius, colorIdx) | |||
| m.SetColorIndex(x, y-radius, colorIdx) | |||
| m.drawHorizLine(x-radius, x+radius, y, colorIdx) | |||
| for xo < yo { | |||
| if f >= 0 { | |||
| yo-- | |||
| dfy += 2 | |||
| f += dfy | |||
| } | |||
| xo++ | |||
| dfx += 2 | |||
| f += dfx | |||
| m.drawHorizLine(x-xo, x+xo, y+yo, colorIdx) | |||
| m.drawHorizLine(x-xo, x+xo, y-yo, colorIdx) | |||
| m.drawHorizLine(x-yo, x+yo, y+xo, colorIdx) | |||
| m.drawHorizLine(x-yo, x+yo, y-xo, colorIdx) | |||
| } | |||
| } | |||
| func (m *Image) fillWithCircles(n, maxradius int) { | |||
| maxx := m.Bounds().Max.X | |||
| maxy := m.Bounds().Max.Y | |||
| for i := 0; i < n; i++ { | |||
| colorIdx := uint8(randInt(1, circleCount-1)) | |||
| r := randInt(1, maxradius) | |||
| m.drawCircle(randInt(r, maxx-r), randInt(r, maxy-r), r, colorIdx) | |||
| } | |||
| } | |||
| func (m *Image) strikeThrough() { | |||
| maxx := m.Bounds().Max.X | |||
| maxy := m.Bounds().Max.Y | |||
| y := randInt(maxy/3, maxy-maxy/3) | |||
| amplitude := randFloat(5, 20) | |||
| period := randFloat(80, 180) | |||
| dx := 2.0 * math.Pi / period | |||
| for x := 0; x < maxx; x++ { | |||
| xo := amplitude * math.Cos(float64(y)*dx) | |||
| yo := amplitude * math.Sin(float64(x)*dx) | |||
| for yn := 0; yn < m.dotSize; yn++ { | |||
| r := randInt(0, m.dotSize) | |||
| m.drawCircle(x+int(xo), y+int(yo)+(yn*m.dotSize), r/2, 1) | |||
| } | |||
| } | |||
| } | |||
| func (m *Image) drawDigit(digit []byte, x, y int) { | |||
| skf := randFloat(-maxSkew, maxSkew) | |||
| xs := float64(x) | |||
| r := m.dotSize / 2 | |||
| y += randInt(-r, r) | |||
| for yo := 0; yo < fontHeight; yo++ { | |||
| for xo := 0; xo < fontWidth; xo++ { | |||
| if digit[yo*fontWidth+xo] != blackChar { | |||
| continue | |||
| } | |||
| m.drawCircle(x+xo*m.dotSize, y+yo*m.dotSize, r, 1) | |||
| } | |||
| xs += skf | |||
| x = int(xs) | |||
| } | |||
| } | |||
| func (m *Image) distort(amplude float64, period float64) { | |||
| w := m.Bounds().Max.X | |||
| h := m.Bounds().Max.Y | |||
| oldm := m.Paletted | |||
| newm := image.NewPaletted(image.Rect(0, 0, w, h), oldm.Palette) | |||
| dx := 2.0 * math.Pi / period | |||
| for x := 0; x < w; x++ { | |||
| for y := 0; y < h; y++ { | |||
| xo := amplude * math.Sin(float64(y)*dx) | |||
| yo := amplude * math.Cos(float64(x)*dx) | |||
| newm.SetColorIndex(x, y, oldm.ColorIndexAt(x+int(xo), y+int(yo))) | |||
| } | |||
| } | |||
| m.Paletted = newm | |||
| } | |||
| func randomBrightness(c color.RGBA, max uint8) color.RGBA { | |||
| minc := min3(c.R, c.G, c.B) | |||
| maxc := max3(c.R, c.G, c.B) | |||
| if maxc > max { | |||
| return c | |||
| } | |||
| n := randIntn(int(max-maxc)) - int(minc) | |||
| return color.RGBA{ | |||
| uint8(int(c.R) + n), | |||
| uint8(int(c.G) + n), | |||
| uint8(int(c.B) + n), | |||
| uint8(c.A), | |||
| } | |||
| } | |||
| func min3(x, y, z uint8) (m uint8) { | |||
| m = x | |||
| if y < m { | |||
| m = y | |||
| } | |||
| if z < m { | |||
| m = z | |||
| } | |||
| return | |||
| } | |||
| func max3(x, y, z uint8) (m uint8) { | |||
| m = x | |||
| if y > m { | |||
| m = y | |||
| } | |||
| if z > m { | |||
| m = z | |||
| } | |||
| return | |||
| } | |||
| @@ -0,0 +1,42 @@ | |||
| // 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 captcha | |||
| import ( | |||
| "testing" | |||
| "github.com/gogits/gogs/modules/base" | |||
| ) | |||
| type byteCounter struct { | |||
| n int64 | |||
| } | |||
| func (bc *byteCounter) Write(b []byte) (int, error) { | |||
| bc.n += int64(len(b)) | |||
| return len(b), nil | |||
| } | |||
| func BenchmarkNewImage(b *testing.B) { | |||
| b.StopTimer() | |||
| d := base.RandomCreateBytes(challengeNums, defaultChars...) | |||
| b.StartTimer() | |||
| for i := 0; i < b.N; i++ { | |||
| NewImage(d, stdWidth, stdHeight) | |||
| } | |||
| } | |||
| func BenchmarkImageWriteTo(b *testing.B) { | |||
| b.StopTimer() | |||
| d := base.RandomCreateBytes(challengeNums, defaultChars...) | |||
| b.StartTimer() | |||
| counter := &byteCounter{} | |||
| for i := 0; i < b.N; i++ { | |||
| img := NewImage(d, stdWidth, stdHeight) | |||
| img.WriteTo(counter) | |||
| b.SetBytes(counter.n) | |||
| counter.n = 0 | |||
| } | |||
| } | |||
| @@ -0,0 +1,267 @@ | |||
| // 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 captcha | |||
| import ( | |||
| "crypto/rand" | |||
| "encoding/binary" | |||
| "io" | |||
| "sync" | |||
| ) | |||
| // siprng is PRNG based on SipHash-2-4. | |||
| type siprng struct { | |||
| mu sync.Mutex | |||
| k0, k1, ctr uint64 | |||
| } | |||
| // siphash implements SipHash-2-4, accepting a uint64 as a message. | |||
| func siphash(k0, k1, m uint64) uint64 { | |||
| // Initialization. | |||
| v0 := k0 ^ 0x736f6d6570736575 | |||
| v1 := k1 ^ 0x646f72616e646f6d | |||
| v2 := k0 ^ 0x6c7967656e657261 | |||
| v3 := k1 ^ 0x7465646279746573 | |||
| t := uint64(8) << 56 | |||
| // Compression. | |||
| v3 ^= m | |||
| // Round 1. | |||
| v0 += v1 | |||
| v1 = v1<<13 | v1>>(64-13) | |||
| v1 ^= v0 | |||
| v0 = v0<<32 | v0>>(64-32) | |||
| v2 += v3 | |||
| v3 = v3<<16 | v3>>(64-16) | |||
| v3 ^= v2 | |||
| v0 += v3 | |||
| v3 = v3<<21 | v3>>(64-21) | |||
| v3 ^= v0 | |||
| v2 += v1 | |||
| v1 = v1<<17 | v1>>(64-17) | |||
| v1 ^= v2 | |||
| v2 = v2<<32 | v2>>(64-32) | |||
| // Round 2. | |||
| v0 += v1 | |||
| v1 = v1<<13 | v1>>(64-13) | |||
| v1 ^= v0 | |||
| v0 = v0<<32 | v0>>(64-32) | |||
| v2 += v3 | |||
| v3 = v3<<16 | v3>>(64-16) | |||
| v3 ^= v2 | |||
| v0 += v3 | |||
| v3 = v3<<21 | v3>>(64-21) | |||
| v3 ^= v0 | |||
| v2 += v1 | |||
| v1 = v1<<17 | v1>>(64-17) | |||
| v1 ^= v2 | |||
| v2 = v2<<32 | v2>>(64-32) | |||
| v0 ^= m | |||
| // Compress last block. | |||
| v3 ^= t | |||
| // Round 1. | |||
| v0 += v1 | |||
| v1 = v1<<13 | v1>>(64-13) | |||
| v1 ^= v0 | |||
| v0 = v0<<32 | v0>>(64-32) | |||
| v2 += v3 | |||
| v3 = v3<<16 | v3>>(64-16) | |||
| v3 ^= v2 | |||
| v0 += v3 | |||
| v3 = v3<<21 | v3>>(64-21) | |||
| v3 ^= v0 | |||
| v2 += v1 | |||
| v1 = v1<<17 | v1>>(64-17) | |||
| v1 ^= v2 | |||
| v2 = v2<<32 | v2>>(64-32) | |||
| // Round 2. | |||
| v0 += v1 | |||
| v1 = v1<<13 | v1>>(64-13) | |||
| v1 ^= v0 | |||
| v0 = v0<<32 | v0>>(64-32) | |||
| v2 += v3 | |||
| v3 = v3<<16 | v3>>(64-16) | |||
| v3 ^= v2 | |||
| v0 += v3 | |||
| v3 = v3<<21 | v3>>(64-21) | |||
| v3 ^= v0 | |||
| v2 += v1 | |||
| v1 = v1<<17 | v1>>(64-17) | |||
| v1 ^= v2 | |||
| v2 = v2<<32 | v2>>(64-32) | |||
| v0 ^= t | |||
| // Finalization. | |||
| v2 ^= 0xff | |||
| // Round 1. | |||
| v0 += v1 | |||
| v1 = v1<<13 | v1>>(64-13) | |||
| v1 ^= v0 | |||
| v0 = v0<<32 | v0>>(64-32) | |||
| v2 += v3 | |||
| v3 = v3<<16 | v3>>(64-16) | |||
| v3 ^= v2 | |||
| v0 += v3 | |||
| v3 = v3<<21 | v3>>(64-21) | |||
| v3 ^= v0 | |||
| v2 += v1 | |||
| v1 = v1<<17 | v1>>(64-17) | |||
| v1 ^= v2 | |||
| v2 = v2<<32 | v2>>(64-32) | |||
| // Round 2. | |||
| v0 += v1 | |||
| v1 = v1<<13 | v1>>(64-13) | |||
| v1 ^= v0 | |||
| v0 = v0<<32 | v0>>(64-32) | |||
| v2 += v3 | |||
| v3 = v3<<16 | v3>>(64-16) | |||
| v3 ^= v2 | |||
| v0 += v3 | |||
| v3 = v3<<21 | v3>>(64-21) | |||
| v3 ^= v0 | |||
| v2 += v1 | |||
| v1 = v1<<17 | v1>>(64-17) | |||
| v1 ^= v2 | |||
| v2 = v2<<32 | v2>>(64-32) | |||
| // Round 3. | |||
| v0 += v1 | |||
| v1 = v1<<13 | v1>>(64-13) | |||
| v1 ^= v0 | |||
| v0 = v0<<32 | v0>>(64-32) | |||
| v2 += v3 | |||
| v3 = v3<<16 | v3>>(64-16) | |||
| v3 ^= v2 | |||
| v0 += v3 | |||
| v3 = v3<<21 | v3>>(64-21) | |||
| v3 ^= v0 | |||
| v2 += v1 | |||
| v1 = v1<<17 | v1>>(64-17) | |||
| v1 ^= v2 | |||
| v2 = v2<<32 | v2>>(64-32) | |||
| // Round 4. | |||
| v0 += v1 | |||
| v1 = v1<<13 | v1>>(64-13) | |||
| v1 ^= v0 | |||
| v0 = v0<<32 | v0>>(64-32) | |||
| v2 += v3 | |||
| v3 = v3<<16 | v3>>(64-16) | |||
| v3 ^= v2 | |||
| v0 += v3 | |||
| v3 = v3<<21 | v3>>(64-21) | |||
| v3 ^= v0 | |||
| v2 += v1 | |||
| v1 = v1<<17 | v1>>(64-17) | |||
| v1 ^= v2 | |||
| v2 = v2<<32 | v2>>(64-32) | |||
| return v0 ^ v1 ^ v2 ^ v3 | |||
| } | |||
| // rekey sets a new PRNG key, which is read from crypto/rand. | |||
| func (p *siprng) rekey() { | |||
| var k [16]byte | |||
| if _, err := io.ReadFull(rand.Reader, k[:]); err != nil { | |||
| panic(err.Error()) | |||
| } | |||
| p.k0 = binary.LittleEndian.Uint64(k[0:8]) | |||
| p.k1 = binary.LittleEndian.Uint64(k[8:16]) | |||
| p.ctr = 1 | |||
| } | |||
| // Uint64 returns a new pseudorandom uint64. | |||
| // It rekeys PRNG on the first call and every 64 MB of generated data. | |||
| func (p *siprng) Uint64() uint64 { | |||
| p.mu.Lock() | |||
| if p.ctr == 0 || p.ctr > 8*1024*1024 { | |||
| p.rekey() | |||
| } | |||
| v := siphash(p.k0, p.k1, p.ctr) | |||
| p.ctr++ | |||
| p.mu.Unlock() | |||
| return v | |||
| } | |||
| func (p *siprng) Int63() int64 { | |||
| return int64(p.Uint64() & 0x7fffffffffffffff) | |||
| } | |||
| func (p *siprng) Uint32() uint32 { | |||
| return uint32(p.Uint64()) | |||
| } | |||
| func (p *siprng) Int31() int32 { | |||
| return int32(p.Uint32() & 0x7fffffff) | |||
| } | |||
| func (p *siprng) Intn(n int) int { | |||
| if n <= 0 { | |||
| panic("invalid argument to Intn") | |||
| } | |||
| if n <= 1<<31-1 { | |||
| return int(p.Int31n(int32(n))) | |||
| } | |||
| return int(p.Int63n(int64(n))) | |||
| } | |||
| func (p *siprng) Int63n(n int64) int64 { | |||
| if n <= 0 { | |||
| panic("invalid argument to Int63n") | |||
| } | |||
| max := int64((1 << 63) - 1 - (1<<63)%uint64(n)) | |||
| v := p.Int63() | |||
| for v > max { | |||
| v = p.Int63() | |||
| } | |||
| return v % n | |||
| } | |||
| func (p *siprng) Int31n(n int32) int32 { | |||
| if n <= 0 { | |||
| panic("invalid argument to Int31n") | |||
| } | |||
| max := int32((1 << 31) - 1 - (1<<31)%uint32(n)) | |||
| v := p.Int31() | |||
| for v > max { | |||
| v = p.Int31() | |||
| } | |||
| return v % n | |||
| } | |||
| func (p *siprng) Float64() float64 { return float64(p.Int63()) / (1 << 63) } | |||
| @@ -0,0 +1,23 @@ | |||
| // 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 captcha | |||
| import "testing" | |||
| func TestSiphash(t *testing.T) { | |||
| good := uint64(0xe849e8bb6ffe2567) | |||
| cur := siphash(0, 0, 0) | |||
| if cur != good { | |||
| t.Fatalf("siphash: expected %x, got %x", good, cur) | |||
| } | |||
| } | |||
| func BenchmarkSiprng(b *testing.B) { | |||
| b.SetBytes(8) | |||
| p := &siprng{} | |||
| for i := 0; i < b.N; i++ { | |||
| p.Uint64() | |||
| } | |||
| } | |||
| @@ -0,0 +1,26 @@ | |||
| // 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 git | |||
| import ( | |||
| "bytes" | |||
| "errors" | |||
| "io" | |||
| "github.com/Unknwon/com" | |||
| ) | |||
| type Blob struct { | |||
| repo *Repository | |||
| *TreeEntry | |||
| } | |||
| func (b *Blob) Data() (io.Reader, error) { | |||
| stdout, stderr, err := com.ExecCmdDirBytes(b.repo.Path, "git", "show", b.Id.String()) | |||
| if err != nil { | |||
| return nil, errors.New(string(stderr)) | |||
| } | |||
| return bytes.NewBuffer(stdout), nil | |||
| } | |||
| @@ -0,0 +1,86 @@ | |||
| // 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 git | |||
| import ( | |||
| "container/list" | |||
| "strings" | |||
| ) | |||
| // Commit represents a git commit. | |||
| type Commit struct { | |||
| Tree | |||
| Id sha1 // The id of this commit object | |||
| Author *Signature | |||
| Committer *Signature | |||
| CommitMessage string | |||
| parents []sha1 // sha1 strings | |||
| } | |||
| // Return the commit message. Same as retrieving CommitMessage directly. | |||
| func (c *Commit) Message() string { | |||
| return c.CommitMessage | |||
| } | |||
| func (c *Commit) Summary() string { | |||
| return strings.Split(c.CommitMessage, "\n")[0] | |||
| } | |||
| // Return oid of the parent number n (0-based index). Return nil if no such parent exists. | |||
| func (c *Commit) ParentId(n int) (id sha1, err error) { | |||
| if n >= len(c.parents) { | |||
| err = IdNotExist | |||
| return | |||
| } | |||
| return c.parents[n], nil | |||
| } | |||
| // Return parent number n (0-based index) | |||
| func (c *Commit) Parent(n int) (*Commit, error) { | |||
| id, err := c.ParentId(n) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| parent, err := c.repo.getCommit(id) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| return parent, nil | |||
| } | |||
| // Return the number of parents of the commit. 0 if this is the | |||
| // root commit, otherwise 1,2,... | |||
| func (c *Commit) ParentCount() int { | |||
| return len(c.parents) | |||
| } | |||
| func (c *Commit) CommitsBefore() (*list.List, error) { | |||
| return c.repo.getCommitsBefore(c.Id) | |||
| } | |||
| func (c *Commit) CommitsBeforeUntil(commitId string) (*list.List, error) { | |||
| ec, err := c.repo.GetCommit(commitId) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| return c.repo.CommitsBetween(c, ec) | |||
| } | |||
| func (c *Commit) CommitsCount() (int, error) { | |||
| return c.repo.commitsCount(c.Id) | |||
| } | |||
| func (c *Commit) SearchCommits(keyword string) (*list.List, error) { | |||
| return c.repo.searchCommits(c.Id, keyword) | |||
| } | |||
| func (c *Commit) CommitsByRange(page int) (*list.List, error) { | |||
| return c.repo.commitsByRange(c.Id, page) | |||
| } | |||
| func (c *Commit) GetCommitOfRelPath(relPath string) (*Commit, error) { | |||
| return c.repo.getCommitOfRelPath(c.Id, relPath) | |||
| } | |||
| @@ -0,0 +1,36 @@ | |||
| // 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 git | |||
| import ( | |||
| "fmt" | |||
| "github.com/Unknwon/com" | |||
| ) | |||
| type ArchiveType int | |||
| const ( | |||
| ZIP ArchiveType = iota + 1 | |||
| TARGZ | |||
| ) | |||
| func (c *Commit) CreateArchive(path string, archiveType ArchiveType) error { | |||
| var format string | |||
| switch archiveType { | |||
| case ZIP: | |||
| format = "zip" | |||
| case TARGZ: | |||
| format = "tar.gz" | |||
| default: | |||
| return fmt.Errorf("unknown format: %v", archiveType) | |||
| } | |||
| _, stderr, err := com.ExecCmdDir(c.repo.Path, "git", "archive", "--format="+format, "-o", path, c.Id.String()) | |||
| if err != nil { | |||
| return fmt.Errorf("%s", stderr) | |||
| } | |||
| return nil | |||
| } | |||
| @@ -0,0 +1,27 @@ | |||
| // 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 git | |||
| import ( | |||
| "path/filepath" | |||
| ) | |||
| // Repository represents a Git repository. | |||
| type Repository struct { | |||
| Path string | |||
| commitCache map[sha1]*Commit | |||
| tagCache map[sha1]*Tag | |||
| } | |||
| // OpenRepository opens the repository at the given path. | |||
| func OpenRepository(repoPath string) (*Repository, error) { | |||
| repoPath, err := filepath.Abs(repoPath) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| return &Repository{Path: repoPath}, nil | |||
| } | |||
| @@ -0,0 +1,38 @@ | |||
| // 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 git | |||
| import ( | |||
| "errors" | |||
| "strings" | |||
| "github.com/Unknwon/com" | |||
| ) | |||
| func IsBranchExist(repoPath, branchName string) bool { | |||
| _, _, err := com.ExecCmdDir(repoPath, "git", "show-ref", "--verify", "refs/heads/"+branchName) | |||
| return err == nil | |||
| } | |||
| func (repo *Repository) IsBranchExist(branchName string) bool { | |||
| return IsBranchExist(repo.Path, branchName) | |||
| } | |||
| func (repo *Repository) GetBranches() ([]string, error) { | |||
| stdout, stderr, err := com.ExecCmdDir(repo.Path, "git", "show-ref", "--heads") | |||
| if err != nil { | |||
| return nil, errors.New(stderr) | |||
| } | |||
| infos := strings.Split(stdout, "\n") | |||
| branches := make([]string, len(infos)-1) | |||
| for i, info := range infos[:len(infos)-1] { | |||
| parts := strings.Split(info, " ") | |||
| if len(parts) != 2 { | |||
| continue // NOTE: I should believe git will not give me wrong string. | |||
| } | |||
| branches[i] = strings.TrimPrefix(parts[1], "refs/heads/") | |||
| } | |||
| return branches, nil | |||
| } | |||
| @@ -0,0 +1,291 @@ | |||
| // 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 git | |||
| import ( | |||
| "bytes" | |||
| "container/list" | |||
| "errors" | |||
| "strings" | |||
| "sync" | |||
| "github.com/Unknwon/com" | |||
| ) | |||
| func (repo *Repository) getCommitIdOfRef(refpath string) (string, error) { | |||
| stdout, stderr, err := com.ExecCmdDir(repo.Path, "git", "show-ref", "--verify", refpath) | |||
| if err != nil { | |||
| return "", errors.New(stderr) | |||
| } | |||
| return strings.Split(stdout, " ")[0], nil | |||
| } | |||
| func (repo *Repository) GetCommitIdOfBranch(branchName string) (string, error) { | |||
| return repo.getCommitIdOfRef("refs/heads/" + branchName) | |||
| } | |||
| // get branch's last commit or a special commit by id string | |||
| func (repo *Repository) GetCommitOfBranch(branchName string) (*Commit, error) { | |||
| commitId, err := repo.GetCommitIdOfBranch(branchName) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| return repo.GetCommit(commitId) | |||
| } | |||
| func (repo *Repository) GetCommitIdOfTag(tagName string) (string, error) { | |||
| return repo.getCommitIdOfRef("refs/tags/" + tagName) | |||
| } | |||
| func (repo *Repository) GetCommitOfTag(tagName string) (*Commit, error) { | |||
| commitId, err := repo.GetCommitIdOfTag(tagName) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| return repo.GetCommit(commitId) | |||
| } | |||
| // Parse commit information from the (uncompressed) raw | |||
| // data from the commit object. | |||
| // \n\n separate headers from message | |||
| func parseCommitData(data []byte) (*Commit, error) { | |||
| commit := new(Commit) | |||
| commit.parents = make([]sha1, 0, 1) | |||
| // we now have the contents of the commit object. Let's investigate... | |||
| nextline := 0 | |||
| l: | |||
| for { | |||
| eol := bytes.IndexByte(data[nextline:], '\n') | |||
| switch { | |||
| case eol > 0: | |||
| line := data[nextline : nextline+eol] | |||
| spacepos := bytes.IndexByte(line, ' ') | |||
| reftype := line[:spacepos] | |||
| switch string(reftype) { | |||
| case "tree": | |||
| id, err := NewIdFromString(string(line[spacepos+1:])) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| commit.Tree.Id = id | |||
| case "parent": | |||
| // A commit can have one or more parents | |||
| oid, err := NewIdFromString(string(line[spacepos+1:])) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| commit.parents = append(commit.parents, oid) | |||
| case "author": | |||
| sig, err := newSignatureFromCommitline(line[spacepos+1:]) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| commit.Author = sig | |||
| case "committer": | |||
| sig, err := newSignatureFromCommitline(line[spacepos+1:]) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| commit.Committer = sig | |||
| } | |||
| nextline += eol + 1 | |||
| case eol == 0: | |||
| commit.CommitMessage = string(data[nextline+1:]) | |||
| break l | |||
| default: | |||
| break l | |||
| } | |||
| } | |||
| return commit, nil | |||
| } | |||
| func (repo *Repository) getCommit(id sha1) (*Commit, error) { | |||
| if repo.commitCache != nil { | |||
| if c, ok := repo.commitCache[id]; ok { | |||
| return c, nil | |||
| } | |||
| } else { | |||
| repo.commitCache = make(map[sha1]*Commit, 10) | |||
| } | |||
| data, bytErr, err := com.ExecCmdDirBytes(repo.Path, "git", "cat-file", "-p", id.String()) | |||
| if err != nil { | |||
| return nil, errors.New(string(bytErr)) | |||
| } | |||
| commit, err := parseCommitData(data) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| commit.repo = repo | |||
| commit.Id = id | |||
| repo.commitCache[id] = commit | |||
| return commit, nil | |||
| } | |||
| // Find the commit object in the repository. | |||
| func (repo *Repository) GetCommit(commitId string) (*Commit, error) { | |||
| id, err := NewIdFromString(commitId) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| return repo.getCommit(id) | |||
| } | |||
| func (repo *Repository) commitsCount(id sha1) (int, error) { | |||
| stdout, stderr, err := com.ExecCmdDir(repo.Path, "git", "rev-list", "--count", id.String()) | |||
| if err != nil { | |||
| return 0, errors.New(stderr) | |||
| } | |||
| return com.StrTo(strings.TrimSpace(stdout)).Int() | |||
| } | |||
| // used only for single tree, (] | |||
| func (repo *Repository) CommitsBetween(last *Commit, before *Commit) (*list.List, error) { | |||
| l := list.New() | |||
| if last == nil || last.ParentCount() == 0 { | |||
| return l, nil | |||
| } | |||
| var err error | |||
| cur := last | |||
| for { | |||
| if cur.Id.Equal(before.Id) { | |||
| break | |||
| } | |||
| l.PushBack(cur) | |||
| if cur.ParentCount() == 0 { | |||
| break | |||
| } | |||
| cur, err = cur.Parent(0) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| } | |||
| return l, nil | |||
| } | |||
| func (repo *Repository) commitsBefore(lock *sync.Mutex, l *list.List, parent *list.Element, id sha1, limit int) error { | |||
| commit, err := repo.getCommit(id) | |||
| if err != nil { | |||
| return err | |||
| } | |||
| var e *list.Element | |||
| if parent == nil { | |||
| e = l.PushBack(commit) | |||
| } else { | |||
| var in = parent | |||
| for { | |||
| if in == nil { | |||
| break | |||
| } else if in.Value.(*Commit).Id.Equal(commit.Id) { | |||
| return nil | |||
| } else { | |||
| if in.Next() == nil { | |||
| break | |||
| } | |||
| if in.Value.(*Commit).Committer.When.Equal(commit.Committer.When) { | |||
| break | |||
| } | |||
| if in.Value.(*Commit).Committer.When.After(commit.Committer.When) && | |||
| in.Next().Value.(*Commit).Committer.When.Before(commit.Committer.When) { | |||
| break | |||
| } | |||
| } | |||
| in = in.Next() | |||
| } | |||
| e = l.InsertAfter(commit, in) | |||
| } | |||
| var pr = parent | |||
| if commit.ParentCount() > 1 { | |||
| pr = e | |||
| } | |||
| for i := 0; i < commit.ParentCount(); i++ { | |||
| id, err := commit.ParentId(i) | |||
| if err != nil { | |||
| return err | |||
| } | |||
| err = repo.commitsBefore(lock, l, pr, id, 0) | |||
| if err != nil { | |||
| return err | |||
| } | |||
| } | |||
| return nil | |||
| } | |||
| func (repo *Repository) CommitsCount(commitId string) (int, error) { | |||
| id, err := NewIdFromString(commitId) | |||
| if err != nil { | |||
| return 0, err | |||
| } | |||
| return repo.commitsCount(id) | |||
| } | |||
| func (repo *Repository) FileCommitsCount(branch, file string) (int, error) { | |||
| stdout, stderr, err := com.ExecCmdDir(repo.Path, "git", "rev-list", "--count", | |||
| branch, "--", file) | |||
| if err != nil { | |||
| return 0, errors.New(stderr) | |||
| } | |||
| return com.StrTo(strings.TrimSpace(stdout)).Int() | |||
| } | |||
| func (repo *Repository) CommitsByFileAndRange(branch, file string, page int) (*list.List, error) { | |||
| stdout, stderr, err := com.ExecCmdDirBytes(repo.Path, "git", "log", branch, | |||
| "--skip="+com.ToStr((page-1)*50), "--max-count=50", prettyLogFormat, "--", file) | |||
| if err != nil { | |||
| return nil, errors.New(string(stderr)) | |||
| } | |||
| return parsePrettyFormatLog(repo, stdout) | |||
| } | |||
| func (repo *Repository) getCommitsBefore(id sha1) (*list.List, error) { | |||
| l := list.New() | |||
| lock := new(sync.Mutex) | |||
| err := repo.commitsBefore(lock, l, nil, id, 0) | |||
| return l, err | |||
| } | |||
| func (repo *Repository) searchCommits(id sha1, keyword string) (*list.List, error) { | |||
| stdout, stderr, err := com.ExecCmdDirBytes(repo.Path, "git", "log", id.String(), "-100", | |||
| "-i", "--grep="+keyword, prettyLogFormat) | |||
| if err != nil { | |||
| return nil, err | |||
| } else if len(stderr) > 0 { | |||
| return nil, errors.New(string(stderr)) | |||
| } | |||
| return parsePrettyFormatLog(repo, stdout) | |||
| } | |||
| func (repo *Repository) commitsByRange(id sha1, page int) (*list.List, error) { | |||
| stdout, stderr, err := com.ExecCmdDirBytes(repo.Path, "git", "log", id.String(), | |||
| "--skip="+com.ToStr((page-1)*50), "--max-count=50", prettyLogFormat) | |||
| if err != nil { | |||
| return nil, errors.New(string(stderr)) | |||
| } | |||
| return parsePrettyFormatLog(repo, stdout) | |||
| } | |||
| func (repo *Repository) getCommitOfRelPath(id sha1, relPath string) (*Commit, error) { | |||
| stdout, _, err := com.ExecCmdDir(repo.Path, "git", "log", "-1", prettyLogFormat, id.String(), "--", relPath) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| id, err = NewIdFromString(string(stdout)) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| return repo.getCommit(id) | |||
| } | |||
| @@ -2,9 +2,13 @@ | |||
| // Use of this source code is governed by a MIT-style | |||
| // license that can be found in the LICENSE file. | |||
| package workers | |||
| package git | |||
| // Work represents a background work interface of any kind. | |||
| type Work interface { | |||
| Do() error | |||
| } | |||
| type ObjectType string | |||
| const ( | |||
| COMMIT ObjectType = "commit" | |||
| TREE ObjectType = "tree" | |||
| BLOB ObjectType = "blob" | |||
| TAG ObjectType = "tag" | |||
| ) | |||
| @@ -0,0 +1,104 @@ | |||
| // 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 git | |||
| import ( | |||
| "errors" | |||
| "strings" | |||
| "github.com/Unknwon/com" | |||
| ) | |||
| func IsTagExist(repoPath, tagName string) bool { | |||
| _, _, err := com.ExecCmdDir(repoPath, "git", "show-ref", "--verify", "refs/tags/"+tagName) | |||
| return err == nil | |||
| } | |||
| func (repo *Repository) IsTagExist(tagName string) bool { | |||
| return IsTagExist(repo.Path, tagName) | |||
| } | |||
| // GetTags returns all tags of given repository. | |||
| func (repo *Repository) GetTags() ([]string, error) { | |||
| stdout, stderr, err := com.ExecCmdDir(repo.Path, "git", "tag", "-l") | |||
| if err != nil { | |||
| return nil, errors.New(stderr) | |||
| } | |||
| tags := strings.Split(stdout, "\n") | |||
| return tags[:len(tags)-1], nil | |||
| } | |||
| func (repo *Repository) CreateTag(tagName, idStr string) error { | |||
| _, stderr, err := com.ExecCmdDir(repo.Path, "git", "tag", tagName, idStr) | |||
| if err != nil { | |||
| return errors.New(stderr) | |||
| } | |||
| return nil | |||
| } | |||
| func (repo *Repository) getTag(id sha1) (*Tag, error) { | |||
| if repo.tagCache != nil { | |||
| if t, ok := repo.tagCache[id]; ok { | |||
| return t, nil | |||
| } | |||
| } else { | |||
| repo.tagCache = make(map[sha1]*Tag, 10) | |||
| } | |||
| // Get tag type. | |||
| tp, stderr, err := com.ExecCmdDir(repo.Path, "git", "cat-file", "-t", id.String()) | |||
| if err != nil { | |||
| return nil, errors.New(stderr) | |||
| } | |||
| // Tag is a commit. | |||
| if ObjectType(tp) == COMMIT { | |||
| tag := &Tag{ | |||
| Id: id, | |||
| Object: id, | |||
| Type: string(COMMIT), | |||
| repo: repo, | |||
| } | |||
| repo.tagCache[id] = tag | |||
| return tag, nil | |||
| } | |||
| // Tag with message. | |||
| data, bytErr, err := com.ExecCmdDirBytes(repo.Path, "git", "cat-file", "-p", id.String()) | |||
| if err != nil { | |||
| return nil, errors.New(string(bytErr)) | |||
| } | |||
| tag, err := parseTagData(data) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| tag.Id = id | |||
| tag.repo = repo | |||
| repo.tagCache[id] = tag | |||
| return tag, nil | |||
| } | |||
| // GetTag returns a Git tag by given name. | |||
| func (repo *Repository) GetTag(tagName string) (*Tag, error) { | |||
| stdout, stderr, err := com.ExecCmdDir(repo.Path, "git", "show-ref", "--tags", tagName) | |||
| if err != nil { | |||
| return nil, errors.New(stderr) | |||
| } | |||
| id, err := NewIdFromString(strings.Split(stdout, " ")[0]) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| tag, err := repo.getTag(id) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| tag.Name = tagName | |||
| return tag, nil | |||
| } | |||
| @@ -0,0 +1,32 @@ | |||
| // 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 git | |||
| import ( | |||
| "fmt" | |||
| "github.com/Unknwon/com" | |||
| ) | |||
| // Find the tree object in the repository. | |||
| func (repo *Repository) GetTree(idStr string) (*Tree, error) { | |||
| id, err := NewIdFromString(idStr) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| return repo.getTree(id) | |||
| } | |||
| func (repo *Repository) getTree(id sha1) (*Tree, error) { | |||
| treePath := filepathFromSHA1(repo.Path, id.String()) | |||
| if !com.IsFile(treePath) { | |||
| _, _, err := com.ExecCmdDir(repo.Path, "git", "ls-tree", id.String()) | |||
| if err != nil { | |||
| return nil, fmt.Errorf("repo.getTree: %v", ErrNotExist) | |||
| } | |||
| } | |||
| return NewTree(repo, id), nil | |||
| } | |||
| @@ -0,0 +1,87 @@ | |||
| // 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 git | |||
| import ( | |||
| "encoding/hex" | |||
| "errors" | |||
| "fmt" | |||
| "strings" | |||
| ) | |||
| var ( | |||
| IdNotExist = errors.New("sha1 id not exist") | |||
| ) | |||
| type sha1 [20]byte | |||
| // Return true if s has the same sha1 as caller. | |||
| // Support 40-length-string, []byte, sha1 | |||
| func (id sha1) Equal(s2 interface{}) bool { | |||
| switch v := s2.(type) { | |||
| case string: | |||
| if len(v) != 40 { | |||
| return false | |||
| } | |||
| return v == id.String() | |||
| case []byte: | |||
| if len(v) != 20 { | |||
| return false | |||
| } | |||
| for i, v := range v { | |||
| if id[i] != v { | |||
| return false | |||
| } | |||
| } | |||
| case sha1: | |||
| for i, v := range v { | |||
| if id[i] != v { | |||
| return false | |||
| } | |||
| } | |||
| default: | |||
| return false | |||
| } | |||
| return true | |||
| } | |||
| // Return string (hex) representation of the Oid | |||
| func (s sha1) String() string { | |||
| result := make([]byte, 0, 40) | |||
| hexvalues := []byte("0123456789abcdef") | |||
| for i := 0; i < 20; i++ { | |||
| result = append(result, hexvalues[s[i]>>4]) | |||
| result = append(result, hexvalues[s[i]&0xf]) | |||
| } | |||
| return string(result) | |||
| } | |||
| // Create a new sha1 from a 20 byte slice. | |||
| func NewId(b []byte) (sha1, error) { | |||
| var id sha1 | |||
| if len(b) != 20 { | |||
| return id, errors.New("Length must be 20") | |||
| } | |||
| for i := 0; i < 20; i++ { | |||
| id[i] = b[i] | |||
| } | |||
| return id, nil | |||
| } | |||
| // Create a new sha1 from a Sha1 string of length 40. | |||
| func NewIdFromString(s string) (sha1, error) { | |||
| s = strings.TrimSpace(s) | |||
| var id sha1 | |||
| if len(s) != 40 { | |||
| return id, fmt.Errorf("Length must be 40") | |||
| } | |||
| b, err := hex.DecodeString(s) | |||
| if err != nil { | |||
| return id, err | |||
| } | |||
| return NewId(b) | |||
| } | |||
| @@ -0,0 +1,40 @@ | |||
| // 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 git | |||
| import ( | |||
| "bytes" | |||
| "strconv" | |||
| "time" | |||
| ) | |||
| // Author and Committer information | |||
| type Signature struct { | |||
| Email string | |||
| Name string | |||
| When time.Time | |||
| } | |||
| // Helper to get a signature from the commit line, which looks like this: | |||
| // author Patrick Gundlach <gundlach@speedata.de> 1378823654 +0200 | |||
| // but without the "author " at the beginning (this method should) | |||
| // be used for author and committer. | |||
| // | |||
| // FIXME: include timezone! | |||
| func newSignatureFromCommitline(line []byte) (*Signature, error) { | |||
| sig := new(Signature) | |||
| emailstart := bytes.IndexByte(line, '<') | |||
| sig.Name = string(line[:emailstart-1]) | |||
| emailstop := bytes.IndexByte(line, '>') | |||
| sig.Email = string(line[emailstart+1 : emailstop]) | |||
| timestop := bytes.IndexByte(line[emailstop+2:], ' ') | |||
| timestring := string(line[emailstop+2 : emailstop+2+timestop]) | |||
| seconds, err := strconv.ParseInt(timestring, 10, 64) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| sig.When = time.Unix(seconds, 0) | |||
| return sig, nil | |||
| } | |||
| @@ -0,0 +1,67 @@ | |||
| // 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 git | |||
| import ( | |||
| "bytes" | |||
| ) | |||
| // Tag represents a Git tag. | |||
| type Tag struct { | |||
| Name string | |||
| Id sha1 | |||
| repo *Repository | |||
| Object sha1 // The id of this commit object | |||
| Type string | |||
| Tagger *Signature | |||
| TagMessage string | |||
| } | |||
| func (tag *Tag) Commit() (*Commit, error) { | |||
| return tag.repo.getCommit(tag.Object) | |||
| } | |||
| // Parse commit information from the (uncompressed) raw | |||
| // data from the commit object. | |||
| // \n\n separate headers from message | |||
| func parseTagData(data []byte) (*Tag, error) { | |||
| tag := new(Tag) | |||
| // we now have the contents of the commit object. Let's investigate... | |||
| nextline := 0 | |||
| l: | |||
| for { | |||
| eol := bytes.IndexByte(data[nextline:], '\n') | |||
| switch { | |||
| case eol > 0: | |||
| line := data[nextline : nextline+eol] | |||
| spacepos := bytes.IndexByte(line, ' ') | |||
| reftype := line[:spacepos] | |||
| switch string(reftype) { | |||
| case "object": | |||
| id, err := NewIdFromString(string(line[spacepos+1:])) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| tag.Object = id | |||
| case "type": | |||
| // A commit can have one or more parents | |||
| tag.Type = string(line[spacepos+1:]) | |||
| case "tagger": | |||
| sig, err := newSignatureFromCommitline(line[spacepos+1:]) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| tag.Tagger = sig | |||
| } | |||
| nextline += eol + 1 | |||
| case eol == 0: | |||
| tag.TagMessage = string(data[nextline+1:]) | |||
| break l | |||
| default: | |||
| break l | |||
| } | |||
| } | |||
| return tag, nil | |||
| } | |||
| @@ -0,0 +1,124 @@ | |||
| // 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 git | |||
| import ( | |||
| "bytes" | |||
| "errors" | |||
| "strings" | |||
| "github.com/Unknwon/com" | |||
| ) | |||
| var ( | |||
| ErrNotExist = errors.New("error not exist") | |||
| ) | |||
| // A tree is a flat directory listing. | |||
| type Tree struct { | |||
| Id sha1 | |||
| repo *Repository | |||
| // parent tree | |||
| ptree *Tree | |||
| entries Entries | |||
| entriesParsed bool | |||
| } | |||
| // Parse tree information from the (uncompressed) raw | |||
| // data from the tree object. | |||
| func parseTreeData(tree *Tree, data []byte) ([]*TreeEntry, error) { | |||
| entries := make([]*TreeEntry, 0, 10) | |||
| l := len(data) | |||
| pos := 0 | |||
| for pos < l { | |||
| entry := new(TreeEntry) | |||
| entry.ptree = tree | |||
| step := 6 | |||
| switch string(data[pos : pos+step]) { | |||
| case "100644": | |||
| entry.mode = ModeBlob | |||
| entry.Type = BLOB | |||
| case "100755": | |||
| entry.mode = ModeExec | |||
| entry.Type = BLOB | |||
| case "120000": | |||
| entry.mode = ModeSymlink | |||
| entry.Type = BLOB | |||
| case "160000": | |||
| entry.mode = ModeCommit | |||
| entry.Type = COMMIT | |||
| case "040000": | |||
| entry.mode = ModeTree | |||
| entry.Type = TREE | |||
| default: | |||
| return nil, errors.New("unknown type: " + string(data[pos:pos+step])) | |||
| } | |||
| pos += step + 6 // Skip string type of entry type. | |||
| step = 40 | |||
| id, err := NewIdFromString(string(data[pos : pos+step])) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| entry.Id = id | |||
| pos += step + 1 // Skip half of sha1. | |||
| step = bytes.IndexByte(data[pos:], '\n') | |||
| entry.name = string(data[pos : pos+step]) | |||
| pos += step + 1 | |||
| entries = append(entries, entry) | |||
| } | |||
| return entries, nil | |||
| } | |||
| func (t *Tree) SubTree(rpath string) (*Tree, error) { | |||
| if len(rpath) == 0 { | |||
| return t, nil | |||
| } | |||
| paths := strings.Split(rpath, "/") | |||
| var err error | |||
| var g = t | |||
| var p = t | |||
| var te *TreeEntry | |||
| for _, name := range paths { | |||
| te, err = p.GetTreeEntryByPath(name) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| g, err = t.repo.getTree(te.Id) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| g.ptree = p | |||
| p = g | |||
| } | |||
| return g, nil | |||
| } | |||
| func (t *Tree) ListEntries(relpath string) (Entries, error) { | |||
| if t.entriesParsed { | |||
| return t.entries, nil | |||
| } | |||
| t.entriesParsed = true | |||
| stdout, _, err := com.ExecCmdDirBytes(t.repo.Path, | |||
| "git", "ls-tree", t.Id.String()) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| t.entries, err = parseTreeData(t, stdout) | |||
| return t.entries, err | |||
| } | |||
| func NewTree(repo *Repository, id sha1) *Tree { | |||
| tree := new(Tree) | |||
| tree.Id = id | |||
| tree.repo = repo | |||
| return tree | |||
| } | |||
| @@ -0,0 +1,59 @@ | |||
| // 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 git | |||
| import ( | |||
| "fmt" | |||
| "path" | |||
| "strings" | |||
| ) | |||
| func (t *Tree) GetTreeEntryByPath(relpath string) (*TreeEntry, error) { | |||
| if len(relpath) == 0 { | |||
| return &TreeEntry{ | |||
| Id: t.Id, | |||
| Type: TREE, | |||
| mode: ModeTree, | |||
| }, nil | |||
| // return nil, fmt.Errorf("GetTreeEntryByPath(empty relpath): %v", ErrNotExist) | |||
| } | |||
| relpath = path.Clean(relpath) | |||
| parts := strings.Split(relpath, "/") | |||
| var err error | |||
| tree := t | |||
| for i, name := range parts { | |||
| if i == len(parts)-1 { | |||
| entries, err := tree.ListEntries(path.Dir(relpath)) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| for _, v := range entries { | |||
| if v.name == name { | |||
| return v, nil | |||
| } | |||
| } | |||
| } else { | |||
| tree, err = tree.SubTree(name) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| } | |||
| } | |||
| return nil, fmt.Errorf("GetTreeEntryByPath: %v", ErrNotExist) | |||
| } | |||
| func (t *Tree) GetBlobByPath(rpath string) (*Blob, error) { | |||
| entry, err := t.GetTreeEntryByPath(rpath) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| if !entry.IsDir() { | |||
| return entry.Blob(), nil | |||
| } | |||
| return nil, ErrNotExist | |||
| } | |||
| @@ -0,0 +1,109 @@ | |||
| // 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 git | |||
| import ( | |||
| "sort" | |||
| "strings" | |||
| "github.com/Unknwon/com" | |||
| ) | |||
| type EntryMode int | |||
| // There are only a few file modes in Git. They look like unix file modes, but they can only be | |||
| // one of these. | |||
| const ( | |||
| ModeBlob EntryMode = 0100644 | |||
| ModeExec EntryMode = 0100755 | |||
| ModeSymlink EntryMode = 0120000 | |||
| ModeCommit EntryMode = 0160000 | |||
| ModeTree EntryMode = 0040000 | |||
| ) | |||
| type TreeEntry struct { | |||
| Id sha1 | |||
| Type ObjectType | |||
| mode EntryMode | |||
| name string | |||
| ptree *Tree | |||
| commited bool | |||
| size int64 | |||
| sized bool | |||
| } | |||
| func (te *TreeEntry) Name() string { | |||
| return te.name | |||
| } | |||
| func (te *TreeEntry) Size() int64 { | |||
| if te.IsDir() { | |||
| return 0 | |||
| } | |||
| if te.sized { | |||
| return te.size | |||
| } | |||
| stdout, _, err := com.ExecCmdDir(te.ptree.repo.Path, "git", "cat-file", "-s", te.Id.String()) | |||
| if err != nil { | |||
| return 0 | |||
| } | |||
| te.sized = true | |||
| te.size = com.StrTo(strings.TrimSpace(stdout)).MustInt64() | |||
| return te.size | |||
| } | |||
| func (te *TreeEntry) IsDir() bool { | |||
| return te.mode == ModeTree | |||
| } | |||
| func (te *TreeEntry) EntryMode() EntryMode { | |||
| return te.mode | |||
| } | |||
| func (te *TreeEntry) Blob() *Blob { | |||
| return &Blob{ | |||
| repo: te.ptree.repo, | |||
| TreeEntry: te, | |||
| } | |||
| } | |||
| type Entries []*TreeEntry | |||
| var sorter = []func(t1, t2 *TreeEntry) bool{ | |||
| func(t1, t2 *TreeEntry) bool { | |||
| return t1.IsDir() && !t2.IsDir() | |||
| }, | |||
| func(t1, t2 *TreeEntry) bool { | |||
| return t1.name < t2.name | |||
| }, | |||
| } | |||
| func (bs Entries) Len() int { return len(bs) } | |||
| func (bs Entries) Swap(i, j int) { bs[i], bs[j] = bs[j], bs[i] } | |||
| func (bs Entries) Less(i, j int) bool { | |||
| t1, t2 := bs[i], bs[j] | |||
| var k int | |||
| for k = 0; k < len(sorter)-1; k++ { | |||
| sort := sorter[k] | |||
| switch { | |||
| case sort(t1, t2): | |||
| return true | |||
| case sort(t2, t1): | |||
| return false | |||
| } | |||
| } | |||
| return sorter[k](t1, t2) | |||
| } | |||
| func (bs Entries) Sort() { | |||
| sort.Sort(bs) | |||
| } | |||
| @@ -0,0 +1,48 @@ | |||
| // 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 git | |||
| import ( | |||
| "bytes" | |||
| "container/list" | |||
| "path/filepath" | |||
| "strings" | |||
| ) | |||
| const prettyLogFormat = `--pretty=format:%H` | |||
| func parsePrettyFormatLog(repo *Repository, logByts []byte) (*list.List, error) { | |||
| l := list.New() | |||
| if len(logByts) == 0 { | |||
| return l, nil | |||
| } | |||
| parts := bytes.Split(logByts, []byte{'\n'}) | |||
| for _, commitId := range parts { | |||
| commit, err := repo.GetCommit(string(commitId)) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| l.PushBack(commit) | |||
| } | |||
| return l, nil | |||
| } | |||
| func RefEndName(refStr string) string { | |||
| index := strings.LastIndex(refStr, "/") | |||
| if index != -1 { | |||
| return refStr[index+1:] | |||
| } | |||
| return refStr | |||
| } | |||
| // If the object is stored in its own file (i.e not in a pack file), | |||
| // this function returns the full path to the object file. | |||
| // It does not test if the file exists. | |||
| func filepathFromSHA1(rootdir, sha1 string) string { | |||
| return filepath.Join(rootdir, "objects", sha1[:2], sha1[2:]) | |||
| } | |||
| @@ -0,0 +1,43 @@ | |||
| // 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 git | |||
| import ( | |||
| "errors" | |||
| "strings" | |||
| "github.com/Unknwon/com" | |||
| ) | |||
| // Version represents version of Git. | |||
| type Version struct { | |||
| Major, Minor, Patch int | |||
| } | |||
| // GetVersion returns current Git version installed. | |||
| func GetVersion() (Version, error) { | |||
| stdout, stderr, err := com.ExecCmd("git", "version") | |||
| if err != nil { | |||
| return Version{}, errors.New(stderr) | |||
| } | |||
| infos := strings.Split(stdout, " ") | |||
| if len(infos) < 3 { | |||
| return Version{}, errors.New("not enough output") | |||
| } | |||
| v := Version{} | |||
| for i, s := range strings.Split(strings.TrimSpace(infos[2]), ".") { | |||
| switch i { | |||
| case 0: | |||
| v.Major, _ = com.StrTo(s).Int() | |||
| case 1: | |||
| v.Minor, _ = com.StrTo(s).Int() | |||
| case 2: | |||
| v.Patch, _ = com.StrTo(s).Int() | |||
| } | |||
| } | |||
| return v, nil | |||
| } | |||
| @@ -0,0 +1,73 @@ | |||
| // 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 log | |||
| import ( | |||
| "encoding/json" | |||
| "log" | |||
| "os" | |||
| "runtime" | |||
| ) | |||
| type Brush func(string) string | |||
| func NewBrush(color string) Brush { | |||
| pre := "\033[" | |||
| reset := "\033[0m" | |||
| return func(text string) string { | |||
| return pre + color + "m" + text + reset | |||
| } | |||
| } | |||
| var colors = []Brush{ | |||
| NewBrush("1;36"), // Trace cyan | |||
| NewBrush("1;34"), // Debug blue | |||
| NewBrush("1;32"), // Info green | |||
| NewBrush("1;33"), // Warn yellow | |||
| NewBrush("1;31"), // Error red | |||
| NewBrush("1;35"), // Critical purple | |||
| NewBrush("1;31"), // Fatal red | |||
| } | |||
| // ConsoleWriter implements LoggerInterface and writes messages to terminal. | |||
| type ConsoleWriter struct { | |||
| lg *log.Logger | |||
| Level int `json:"level"` | |||
| } | |||
| // create ConsoleWriter returning as LoggerInterface. | |||
| func NewConsole() LoggerInterface { | |||
| return &ConsoleWriter{ | |||
| lg: log.New(os.Stdout, "", log.Ldate|log.Ltime), | |||
| Level: TRACE, | |||
| } | |||
| } | |||
| func (cw *ConsoleWriter) Init(config string) error { | |||
| return json.Unmarshal([]byte(config), cw) | |||
| } | |||
| func (cw *ConsoleWriter) WriteMsg(msg string, skip, level int) error { | |||
| if cw.Level > level { | |||
| return nil | |||
| } | |||
| if runtime.GOOS == "windows" { | |||
| cw.lg.Println(msg) | |||
| } else { | |||
| cw.lg.Println(colors[level](msg)) | |||
| } | |||
| return nil | |||
| } | |||
| func (_ *ConsoleWriter) Destroy() { | |||
| } | |||
| func (_ *ConsoleWriter) Flush() { | |||
| } | |||
| func init() { | |||
| Register("console", NewConsole) | |||
| } | |||
| @@ -0,0 +1,237 @@ | |||
| // 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 log | |||
| import ( | |||
| "encoding/json" | |||
| "errors" | |||
| "fmt" | |||
| "io/ioutil" | |||
| "log" | |||
| "os" | |||
| "path/filepath" | |||
| "strings" | |||
| "sync" | |||
| "time" | |||
| ) | |||
| // FileLogWriter implements LoggerInterface. | |||
| // It writes messages by lines limit, file size limit, or time frequency. | |||
| type FileLogWriter struct { | |||
| *log.Logger | |||
| mw *MuxWriter | |||
| // The opened file | |||
| Filename string `json:"filename"` | |||
| Maxlines int `json:"maxlines"` | |||
| maxlines_curlines int | |||
| // Rotate at size | |||
| Maxsize int `json:"maxsize"` | |||
| maxsize_cursize int | |||
| // Rotate daily | |||
| Daily bool `json:"daily"` | |||
| Maxdays int64 `json:"maxdays` | |||
| daily_opendate int | |||
| Rotate bool `json:"rotate"` | |||
| startLock sync.Mutex // Only one log can write to the file | |||
| Level int `json:"level"` | |||
| } | |||
| // an *os.File writer with locker. | |||
| type MuxWriter struct { | |||
| sync.Mutex | |||
| fd *os.File | |||
| } | |||
| // write to os.File. | |||
| func (l *MuxWriter) Write(b []byte) (int, error) { | |||
| l.Lock() | |||
| defer l.Unlock() | |||
| return l.fd.Write(b) | |||
| } | |||
| // set os.File in writer. | |||
| func (l *MuxWriter) SetFd(fd *os.File) { | |||
| if l.fd != nil { | |||
| l.fd.Close() | |||
| } | |||
| l.fd = fd | |||
| } | |||
| // create a FileLogWriter returning as LoggerInterface. | |||
| func NewFileWriter() LoggerInterface { | |||
| w := &FileLogWriter{ | |||
| Filename: "", | |||
| Maxlines: 1000000, | |||
| Maxsize: 1 << 28, //256 MB | |||
| Daily: true, | |||
| Maxdays: 7, | |||
| Rotate: true, | |||
| Level: TRACE, | |||
| } | |||
| // use MuxWriter instead direct use os.File for lock write when rotate | |||
| w.mw = new(MuxWriter) | |||
| // set MuxWriter as Logger's io.Writer | |||
| w.Logger = log.New(w.mw, "", log.Ldate|log.Ltime) | |||
| return w | |||
| } | |||
| // Init file logger with json config. | |||
| // config like: | |||
| // { | |||
| // "filename":"log/gogs.log", | |||
| // "maxlines":10000, | |||
| // "maxsize":1<<30, | |||
| // "daily":true, | |||
| // "maxdays":15, | |||
| // "rotate":true | |||
| // } | |||
| func (w *FileLogWriter) Init(config string) error { | |||
| if err := json.Unmarshal([]byte(config), w); err != nil { | |||
| return err | |||
| } | |||
| if len(w.Filename) == 0 { | |||
| return errors.New("config must have filename") | |||
| } | |||
| return w.StartLogger() | |||
| } | |||
| // start file logger. create log file and set to locker-inside file writer. | |||
| func (w *FileLogWriter) StartLogger() error { | |||
| fd, err := w.createLogFile() | |||
| if err != nil { | |||
| return err | |||
| } | |||
| w.mw.SetFd(fd) | |||
| if err = w.initFd(); err != nil { | |||
| return err | |||
| } | |||
| return nil | |||
| } | |||
| func (w *FileLogWriter) docheck(size int) { | |||
| w.startLock.Lock() | |||
| defer w.startLock.Unlock() | |||
| if w.Rotate && ((w.Maxlines > 0 && w.maxlines_curlines >= w.Maxlines) || | |||
| (w.Maxsize > 0 && w.maxsize_cursize >= w.Maxsize) || | |||
| (w.Daily && time.Now().Day() != w.daily_opendate)) { | |||
| if err := w.DoRotate(); err != nil { | |||
| fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.Filename, err) | |||
| return | |||
| } | |||
| } | |||
| w.maxlines_curlines++ | |||
| w.maxsize_cursize += size | |||
| } | |||
| // write logger message into file. | |||
| func (w *FileLogWriter) WriteMsg(msg string, skip, level int) error { | |||
| if level < w.Level { | |||
| return nil | |||
| } | |||
| n := 24 + len(msg) // 24 stand for the length "2013/06/23 21:00:22 [T] " | |||
| w.docheck(n) | |||
| w.Logger.Println(msg) | |||
| return nil | |||
| } | |||
| func (w *FileLogWriter) createLogFile() (*os.File, error) { | |||
| // Open the log file | |||
| return os.OpenFile(w.Filename, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0660) | |||
| } | |||
| func (w *FileLogWriter) initFd() error { | |||
| fd := w.mw.fd | |||
| finfo, err := fd.Stat() | |||
| if err != nil { | |||
| return fmt.Errorf("get stat: %s\n", err) | |||
| } | |||
| w.maxsize_cursize = int(finfo.Size()) | |||
| w.daily_opendate = time.Now().Day() | |||
| if finfo.Size() > 0 { | |||
| content, err := ioutil.ReadFile(w.Filename) | |||
| if err != nil { | |||
| return err | |||
| } | |||
| w.maxlines_curlines = len(strings.Split(string(content), "\n")) | |||
| } else { | |||
| w.maxlines_curlines = 0 | |||
| } | |||
| return nil | |||
| } | |||
| // DoRotate means it need to write file in new file. | |||
| // new file name like xx.log.2013-01-01.2 | |||
| func (w *FileLogWriter) DoRotate() error { | |||
| _, err := os.Lstat(w.Filename) | |||
| if err == nil { // file exists | |||
| // Find the next available number | |||
| num := 1 | |||
| fname := "" | |||
| for ; err == nil && num <= 999; num++ { | |||
| fname = w.Filename + fmt.Sprintf(".%s.%03d", time.Now().Format("2006-01-02"), num) | |||
| _, err = os.Lstat(fname) | |||
| } | |||
| // return error if the last file checked still existed | |||
| if err == nil { | |||
| return fmt.Errorf("rotate: cannot find free log number to rename %s\n", w.Filename) | |||
| } | |||
| // block Logger's io.Writer | |||
| w.mw.Lock() | |||
| defer w.mw.Unlock() | |||
| fd := w.mw.fd | |||
| fd.Close() | |||
| // close fd before rename | |||
| // Rename the file to its newfound home | |||
| if err = os.Rename(w.Filename, fname); err != nil { | |||
| return fmt.Errorf("Rotate: %s\n", err) | |||
| } | |||
| // re-start logger | |||
| if err = w.StartLogger(); err != nil { | |||
| return fmt.Errorf("Rotate StartLogger: %s\n", err) | |||
| } | |||
| go w.deleteOldLog() | |||
| } | |||
| return nil | |||
| } | |||
| func (w *FileLogWriter) deleteOldLog() { | |||
| dir := filepath.Dir(w.Filename) | |||
| filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { | |||
| if !info.IsDir() && info.ModTime().Unix() < (time.Now().Unix()-60*60*24*w.Maxdays) { | |||
| if strings.HasPrefix(filepath.Base(path), filepath.Base(w.Filename)) { | |||
| os.Remove(path) | |||
| } | |||
| } | |||
| return nil | |||
| }) | |||
| } | |||
| // destroy file logger, close file writer. | |||
| func (w *FileLogWriter) Destroy() { | |||
| w.mw.fd.Close() | |||
| } | |||
| // flush file logger. | |||
| // there are no buffering messages in file logger in memory. | |||
| // flush file means sync file from disk. | |||
| func (w *FileLogWriter) Flush() { | |||
| w.mw.fd.Sync() | |||
| } | |||
| func init() { | |||
| Register("file", NewFileWriter) | |||
| } | |||
| @@ -2,32 +2,29 @@ | |||
| // Use of this source code is governed by a MIT-style | |||
| // license that can be found in the LICENSE file. | |||
| // Package log is a wrapper of logs for short calling name. | |||
| package log | |||
| import ( | |||
| "fmt" | |||
| "os" | |||
| "path" | |||
| "github.com/gogits/logs" | |||
| "path/filepath" | |||
| "runtime" | |||
| "strings" | |||
| "sync" | |||
| ) | |||
| var ( | |||
| loggers []*logs.BeeLogger | |||
| GitLogger *logs.BeeLogger | |||
| loggers []*Logger | |||
| GitLogger *Logger | |||
| ) | |||
| func init() { | |||
| NewLogger(0, "console", `{"level": 0}`) | |||
| } | |||
| func NewLogger(bufLen int64, mode, config string) { | |||
| logger := logs.NewLogger(bufLen) | |||
| logger := newLogger(bufLen) | |||
| isExist := false | |||
| for _, l := range loggers { | |||
| if l.Adapter == mode { | |||
| if l.adapter == mode { | |||
| isExist = true | |||
| l = logger | |||
| } | |||
| @@ -35,15 +32,14 @@ func NewLogger(bufLen int64, mode, config string) { | |||
| if !isExist { | |||
| loggers = append(loggers, logger) | |||
| } | |||
| logger.SetLogFuncCallDepth(3) | |||
| if err := logger.SetLogger(mode, config); err != nil { | |||
| Fatal("Fail to set logger(%s): %v", mode, err) | |||
| Fatal(1, "Fail to set logger(%s): %v", mode, err) | |||
| } | |||
| } | |||
| func NewGitLogger(logPath string) { | |||
| os.MkdirAll(path.Dir(logPath), os.ModePerm) | |||
| GitLogger = logs.NewLogger(0) | |||
| GitLogger = newLogger(0) | |||
| GitLogger.SetLogger("file", fmt.Sprintf(`{"level":0,"filename":"%s","rotate":false}`, logPath)) | |||
| } | |||
| @@ -65,28 +61,237 @@ func Info(format string, v ...interface{}) { | |||
| } | |||
| } | |||
| func Error(format string, v ...interface{}) { | |||
| func Warn(format string, v ...interface{}) { | |||
| for _, logger := range loggers { | |||
| logger.Error(format, v...) | |||
| logger.Warn(format, v...) | |||
| } | |||
| } | |||
| func Warn(format string, v ...interface{}) { | |||
| func Error(skip int, format string, v ...interface{}) { | |||
| for _, logger := range loggers { | |||
| logger.Warn(format, v...) | |||
| logger.Error(skip, format, v...) | |||
| } | |||
| } | |||
| func Critical(format string, v ...interface{}) { | |||
| func Critical(skip int, format string, v ...interface{}) { | |||
| for _, logger := range loggers { | |||
| logger.Critical(format, v...) | |||
| logger.Critical(skip, format, v...) | |||
| } | |||
| } | |||
| func Fatal(format string, v ...interface{}) { | |||
| Error(format, v...) | |||
| func Fatal(skip int, format string, v ...interface{}) { | |||
| Error(skip, format, v...) | |||
| for _, l := range loggers { | |||
| l.Close() | |||
| } | |||
| os.Exit(2) | |||
| os.Exit(1) | |||
| } | |||
| // .___ _______________________________________________________ _________ ___________ | |||
| // | |\ \__ ___/\_ _____/\______ \_ _____/ _ \ \_ ___ \\_ _____/ | |||
| // | |/ | \| | | __)_ | _/| __)/ /_\ \/ \ \/ | __)_ | |||
| // | / | \ | | \ | | \| \/ | \ \____| \ | |||
| // |___\____|__ /____| /_______ / |____|_ /\___ /\____|__ /\______ /_______ / | |||
| // \/ \/ \/ \/ \/ \/ \/ | |||
| type LogLevel int | |||
| const ( | |||
| TRACE = iota | |||
| DEBUG | |||
| INFO | |||
| WARN | |||
| ERROR | |||
| CRITICAL | |||
| FATAL | |||
| ) | |||
| // LoggerInterface represents behaviors of a logger provider. | |||
| type LoggerInterface interface { | |||
| Init(config string) error | |||
| WriteMsg(msg string, skip, level int) error | |||
| Destroy() | |||
| Flush() | |||
| } | |||
| type loggerType func() LoggerInterface | |||
| var adapters = make(map[string]loggerType) | |||
| // Register registers given logger provider to adapters. | |||
| func Register(name string, log loggerType) { | |||
| if log == nil { | |||
| panic("log: register provider is nil") | |||
| } | |||
| if _, dup := adapters[name]; dup { | |||
| panic("log: register called twice for provider \"" + name + "\"") | |||
| } | |||
| adapters[name] = log | |||
| } | |||
| type logMsg struct { | |||
| skip, level int | |||
| msg string | |||
| } | |||
| // Logger is default logger in beego application. | |||
| // it can contain several providers and log message into all providers. | |||
| type Logger struct { | |||
| adapter string | |||
| lock sync.Mutex | |||
| level int | |||
| msg chan *logMsg | |||
| outputs map[string]LoggerInterface | |||
| quit chan bool | |||
| } | |||
| // newLogger initializes and returns a new logger. | |||
| func newLogger(buffer int64) *Logger { | |||
| l := &Logger{ | |||
| msg: make(chan *logMsg, buffer), | |||
| outputs: make(map[string]LoggerInterface), | |||
| quit: make(chan bool), | |||
| } | |||
| go l.StartLogger() | |||
| return l | |||
| } | |||
| // SetLogger sets new logger instanse with given logger adapter and config. | |||
| func (l *Logger) SetLogger(adapter string, config string) error { | |||
| l.lock.Lock() | |||
| defer l.lock.Unlock() | |||
| if log, ok := adapters[adapter]; ok { | |||
| lg := log() | |||
| if err := lg.Init(config); err != nil { | |||
| return err | |||
| } | |||
| l.outputs[adapter] = lg | |||
| l.adapter = adapter | |||
| } else { | |||
| panic("log: unknown adapter \"" + adapter + "\" (forgotten Register?)") | |||
| } | |||
| return nil | |||
| } | |||
| // DelLogger removes a logger adapter instance. | |||
| func (l *Logger) DelLogger(adapter string) error { | |||
| l.lock.Lock() | |||
| defer l.lock.Unlock() | |||
| if lg, ok := l.outputs[adapter]; ok { | |||
| lg.Destroy() | |||
| delete(l.outputs, adapter) | |||
| } else { | |||
| panic("log: unknown adapter \"" + adapter + "\" (forgotten Register?)") | |||
| } | |||
| return nil | |||
| } | |||
| func (l *Logger) writerMsg(skip, level int, msg string) error { | |||
| if l.level > level { | |||
| return nil | |||
| } | |||
| lm := &logMsg{ | |||
| skip: skip, | |||
| level: level, | |||
| } | |||
| // Only error information needs locate position for debugging. | |||
| if lm.level >= ERROR { | |||
| pc, file, line, ok := runtime.Caller(skip) | |||
| if ok { | |||
| // Get caller function name. | |||
| fn := runtime.FuncForPC(pc) | |||
| var fnName string | |||
| if fn == nil { | |||
| fnName = "?()" | |||
| } else { | |||
| fnName = strings.TrimLeft(filepath.Ext(fn.Name()), ".") + "()" | |||
| } | |||
| lm.msg = fmt.Sprintf("[%s:%d %s] %s", filepath.Base(file), line, fnName, msg) | |||
| } else { | |||
| lm.msg = msg | |||
| } | |||
| } else { | |||
| lm.msg = msg | |||
| } | |||
| l.msg <- lm | |||
| return nil | |||
| } | |||
| // StartLogger starts logger chan reading. | |||
| func (l *Logger) StartLogger() { | |||
| for { | |||
| select { | |||
| case bm := <-l.msg: | |||
| for _, l := range l.outputs { | |||
| l.WriteMsg(bm.msg, bm.skip, bm.level) | |||
| } | |||
| case <-l.quit: | |||
| return | |||
| } | |||
| } | |||
| } | |||
| // Flush flushs all chan data. | |||
| func (l *Logger) Flush() { | |||
| for _, l := range l.outputs { | |||
| l.Flush() | |||
| } | |||
| } | |||
| // Close closes logger, flush all chan data and destroy all adapter instances. | |||
| func (l *Logger) Close() { | |||
| l.quit <- true | |||
| for { | |||
| if len(l.msg) > 0 { | |||
| bm := <-l.msg | |||
| for _, l := range l.outputs { | |||
| l.WriteMsg(bm.msg, bm.skip, bm.level) | |||
| } | |||
| } else { | |||
| break | |||
| } | |||
| } | |||
| for _, l := range l.outputs { | |||
| l.Flush() | |||
| l.Destroy() | |||
| } | |||
| } | |||
| func (l *Logger) Trace(format string, v ...interface{}) { | |||
| msg := fmt.Sprintf("[T] "+format, v...) | |||
| l.writerMsg(0, TRACE, msg) | |||
| } | |||
| func (l *Logger) Debug(format string, v ...interface{}) { | |||
| msg := fmt.Sprintf("[D] "+format, v...) | |||
| l.writerMsg(0, DEBUG, msg) | |||
| } | |||
| func (l *Logger) Info(format string, v ...interface{}) { | |||
| msg := fmt.Sprintf("[I] "+format, v...) | |||
| l.writerMsg(0, INFO, msg) | |||
| } | |||
| func (l *Logger) Warn(format string, v ...interface{}) { | |||
| msg := fmt.Sprintf("[W] "+format, v...) | |||
| l.writerMsg(0, WARN, msg) | |||
| } | |||
| func (l *Logger) Error(skip int, format string, v ...interface{}) { | |||
| msg := fmt.Sprintf("[E] "+format, v...) | |||
| l.writerMsg(skip, ERROR, msg) | |||
| } | |||
| func (l *Logger) Critical(skip int, format string, v ...interface{}) { | |||
| msg := fmt.Sprintf("[C] "+format, v...) | |||
| l.writerMsg(skip, CRITICAL, msg) | |||
| } | |||
| func (l *Logger) Fatal(skip int, format string, v ...interface{}) { | |||
| msg := fmt.Sprintf("[F] "+format, v...) | |||
| l.writerMsg(skip, FATAL, msg) | |||
| l.Close() | |||
| os.Exit(1) | |||
| } | |||
| @@ -10,10 +10,12 @@ import ( | |||
| "fmt" | |||
| "path" | |||
| "github.com/Unknwon/com" | |||
| "github.com/Unknwon/macaron" | |||
| "github.com/gogits/gogs/models" | |||
| "github.com/gogits/gogs/modules/base" | |||
| "github.com/gogits/gogs/modules/log" | |||
| "github.com/gogits/gogs/modules/middleware" | |||
| "github.com/gogits/gogs/modules/setting" | |||
| ) | |||
| @@ -55,7 +57,7 @@ func GetMailTmplData(u *models.User) map[interface{}]interface{} { | |||
| // create a time limit code for user active | |||
| func CreateUserActiveCode(u *models.User, startInf interface{}) string { | |||
| minutes := setting.Service.ActiveCodeLives | |||
| data := base.ToStr(u.Id) + u.Email + u.LowerName + u.Passwd + u.Rands | |||
| data := com.ToStr(u.Id) + u.Email + u.LowerName + u.Passwd + u.Rands | |||
| code := base.CreateTimeLimitCode(data, minutes, startInf) | |||
| // add tail hex username | |||
| @@ -64,7 +66,7 @@ func CreateUserActiveCode(u *models.User, startInf interface{}) string { | |||
| } | |||
| // Send user register mail with active code | |||
| func SendRegisterMail(r *middleware.Render, u *models.User) { | |||
| func SendRegisterMail(r macaron.Render, u *models.User) { | |||
| code := CreateUserActiveCode(u, nil) | |||
| subject := "Register success, Welcome" | |||
| @@ -72,7 +74,7 @@ func SendRegisterMail(r *middleware.Render, u *models.User) { | |||
| data["Code"] = code | |||
| body, err := r.HTMLString(string(AUTH_REGISTER_SUCCESS), data) | |||
| if err != nil { | |||
| log.Error("mail.SendRegisterMail(fail to render): %v", err) | |||
| log.Error(4, "mail.SendRegisterMail(fail to render): %v", err) | |||
| return | |||
| } | |||
| @@ -83,7 +85,7 @@ func SendRegisterMail(r *middleware.Render, u *models.User) { | |||
| } | |||
| // Send email verify active email. | |||
| func SendActiveMail(r *middleware.Render, u *models.User) { | |||
| func SendActiveMail(r macaron.Render, u *models.User) { | |||
| code := CreateUserActiveCode(u, nil) | |||
| subject := "Verify your e-mail address" | |||
| @@ -92,7 +94,7 @@ func SendActiveMail(r *middleware.Render, u *models.User) { | |||
| data["Code"] = code | |||
| body, err := r.HTMLString(string(AUTH_ACTIVE), data) | |||
| if err != nil { | |||
| log.Error("mail.SendActiveMail(fail to render): %v", err) | |||
| log.Error(4, "mail.SendActiveMail(fail to render): %v", err) | |||
| return | |||
| } | |||
| @@ -103,7 +105,7 @@ func SendActiveMail(r *middleware.Render, u *models.User) { | |||
| } | |||
| // Send reset password email. | |||
| func SendResetPasswdMail(r *middleware.Render, u *models.User) { | |||
| func SendResetPasswdMail(r macaron.Render, u *models.User) { | |||
| code := CreateUserActiveCode(u, nil) | |||
| subject := "Reset your password" | |||
| @@ -112,7 +114,7 @@ func SendResetPasswdMail(r *middleware.Render, u *models.User) { | |||
| data["Code"] = code | |||
| body, err := r.HTMLString(string(AUTH_RESET_PASSWORD), data) | |||
| if err != nil { | |||
| log.Error("mail.SendResetPasswdMail(fail to render): %v", err) | |||
| log.Error(4, "mail.SendResetPasswdMail(fail to render): %v", err) | |||
| return | |||
| } | |||
| @@ -157,7 +159,7 @@ func SendIssueNotifyMail(u, owner *models.User, repo *models.Repository, issue * | |||
| } | |||
| // SendIssueMentionMail sends mail notification for who are mentioned in issue. | |||
| func SendIssueMentionMail(r *middleware.Render, u, owner *models.User, | |||
| func SendIssueMentionMail(r macaron.Render, u, owner *models.User, | |||
| repo *models.Repository, issue *models.Issue, tos []string) error { | |||
| if len(tos) == 0 { | |||
| @@ -182,7 +184,7 @@ func SendIssueMentionMail(r *middleware.Render, u, owner *models.User, | |||
| } | |||
| // SendCollaboratorMail sends mail notification to new collaborator. | |||
| func SendCollaboratorMail(r *middleware.Render, u, owner *models.User, | |||
| func SendCollaboratorMail(r macaron.Render, u, owner *models.User, | |||
| repo *models.Repository) error { | |||
| subject := fmt.Sprintf("%s added you to %s", owner.Name, repo.Name) | |||
| @@ -56,7 +56,7 @@ func processMailQueue() { | |||
| if len(msg.Info) > 0 { | |||
| info = ", info: " + msg.Info | |||
| } | |||
| log.Error(fmt.Sprintf("Async sent email %d succeed, not send emails: %s%s err: %s", num, tos, info, err)) | |||
| log.Error(4, fmt.Sprintf("Async sent email %d succeed, not send emails: %s%s err: %s", num, tos, info, err)) | |||
| } else { | |||
| log.Trace(fmt.Sprintf("Async sent email %d succeed, sent emails: %s%s", num, tos, info)) | |||
| } | |||
| @@ -8,7 +8,7 @@ import ( | |||
| "net/url" | |||
| "strings" | |||
| "github.com/go-martini/martini" | |||
| "github.com/Unknwon/macaron" | |||
| "github.com/gogits/gogs/modules/setting" | |||
| ) | |||
| @@ -20,7 +20,7 @@ type ToggleOptions struct { | |||
| DisableCsrf bool | |||
| } | |||
| func Toggle(options *ToggleOptions) martini.Handler { | |||
| func Toggle(options *ToggleOptions) macaron.Handler { | |||
| return func(ctx *Context) { | |||
| // Cannot view any page before installation. | |||
| if !setting.InstallLock { | |||
| @@ -49,7 +49,7 @@ func Toggle(options *ToggleOptions) martini.Handler { | |||
| ctx.Redirect("/user/login") | |||
| return | |||
| } else if !ctx.User.IsActive && setting.Service.RegisterEmailConfirm { | |||
| ctx.Data["Title"] = "Activate Your Account" | |||
| // ctx.Data["Title"] = "Activate Your Account" | |||
| ctx.HTML(200, "user/activate") | |||
| return | |||
| } | |||
| @@ -16,7 +16,8 @@ import ( | |||
| "strings" | |||
| "unicode/utf8" | |||
| "github.com/go-martini/martini" | |||
| "github.com/Unknwon/macaron" | |||
| "github.com/macaron-contrib/i18n" | |||
| ) | |||
| /* | |||
| @@ -37,44 +38,44 @@ import ( | |||
| // your own error handling, use Form or Json middleware directly. | |||
| // An interface pointer can be added as a second argument in order | |||
| // to map the struct to a specific interface. | |||
| func Bind(obj interface{}, ifacePtr ...interface{}) martini.Handler { | |||
| return func(context martini.Context, req *http.Request) { | |||
| contentType := req.Header.Get("Content-Type") | |||
| func Bind(obj interface{}, ifacePtr ...interface{}) macaron.Handler { | |||
| return func(ctx *macaron.Context) { | |||
| contentType := ctx.Req.Header.Get("Content-Type") | |||
| if strings.Contains(contentType, "form-urlencoded") { | |||
| context.Invoke(Form(obj, ifacePtr...)) | |||
| ctx.Invoke(Form(obj, ifacePtr...)) | |||
| } else if strings.Contains(contentType, "multipart/form-data") { | |||
| context.Invoke(MultipartForm(obj, ifacePtr...)) | |||
| ctx.Invoke(MultipartForm(obj, ifacePtr...)) | |||
| } else if strings.Contains(contentType, "json") { | |||
| context.Invoke(Json(obj, ifacePtr...)) | |||
| ctx.Invoke(Json(obj, ifacePtr...)) | |||
| } else { | |||
| context.Invoke(Json(obj, ifacePtr...)) | |||
| if getErrors(context).Count() > 0 { | |||
| context.Invoke(Form(obj, ifacePtr...)) | |||
| ctx.Invoke(Json(obj, ifacePtr...)) | |||
| if getErrors(ctx).Count() > 0 { | |||
| ctx.Invoke(Form(obj, ifacePtr...)) | |||
| } | |||
| } | |||
| context.Invoke(ErrorHandler) | |||
| ctx.Invoke(ErrorHandler) | |||
| } | |||
| } | |||
| // BindIgnErr will do the exactly same thing as Bind but without any | |||
| // error handling, which user has freedom to deal with them. | |||
| // This allows user take advantages of validation. | |||
| func BindIgnErr(obj interface{}, ifacePtr ...interface{}) martini.Handler { | |||
| return func(context martini.Context, req *http.Request) { | |||
| func BindIgnErr(obj interface{}, ifacePtr ...interface{}) macaron.Handler { | |||
| return func(ctx *macaron.Context, req *http.Request) { | |||
| contentType := req.Header.Get("Content-Type") | |||
| if strings.Contains(contentType, "form-urlencoded") { | |||
| context.Invoke(Form(obj, ifacePtr...)) | |||
| ctx.Invoke(Form(obj, ifacePtr...)) | |||
| } else if strings.Contains(contentType, "multipart/form-data") { | |||
| context.Invoke(MultipartForm(obj, ifacePtr...)) | |||
| ctx.Invoke(MultipartForm(obj, ifacePtr...)) | |||
| } else if strings.Contains(contentType, "json") { | |||
| context.Invoke(Json(obj, ifacePtr...)) | |||
| ctx.Invoke(Json(obj, ifacePtr...)) | |||
| } else { | |||
| context.Invoke(Json(obj, ifacePtr...)) | |||
| if getErrors(context).Count() > 0 { | |||
| context.Invoke(Form(obj, ifacePtr...)) | |||
| ctx.Invoke(Json(obj, ifacePtr...)) | |||
| if getErrors(ctx).Count() > 0 { | |||
| ctx.Invoke(Form(obj, ifacePtr...)) | |||
| } | |||
| } | |||
| } | |||
| @@ -89,12 +90,12 @@ func BindIgnErr(obj interface{}, ifacePtr ...interface{}) martini.Handler { | |||
| // keys, for example: key=val1&key=val2&key=val3 | |||
| // An interface pointer can be added as a second argument in order | |||
| // to map the struct to a specific interface. | |||
| func Form(formStruct interface{}, ifacePtr ...interface{}) martini.Handler { | |||
| return func(context martini.Context, req *http.Request) { | |||
| func Form(formStruct interface{}, ifacePtr ...interface{}) macaron.Handler { | |||
| return func(ctx *macaron.Context) { | |||
| ensureNotPointer(formStruct) | |||
| formStruct := reflect.New(reflect.TypeOf(formStruct)) | |||
| errors := newErrors() | |||
| parseErr := req.ParseForm() | |||
| parseErr := ctx.Req.ParseForm() | |||
| // Format validation of the request body or the URL would add considerable overhead, | |||
| // and ParseForm does not complain when URL encoding is off. | |||
| @@ -104,14 +105,14 @@ func Form(formStruct interface{}, ifacePtr ...interface{}) martini.Handler { | |||
| errors.Overall[BindingDeserializationError] = parseErr.Error() | |||
| } | |||
| mapForm(formStruct, req.Form, errors) | |||
| mapForm(formStruct, ctx.Req.Form, errors) | |||
| validateAndMap(formStruct, context, errors, ifacePtr...) | |||
| validateAndMap(formStruct, ctx, errors, ifacePtr...) | |||
| } | |||
| } | |||
| func MultipartForm(formStruct interface{}, ifacePtr ...interface{}) martini.Handler { | |||
| return func(context martini.Context, req *http.Request) { | |||
| func MultipartForm(formStruct interface{}, ifacePtr ...interface{}) macaron.Handler { | |||
| return func(ctx *macaron.Context) { | |||
| ensureNotPointer(formStruct) | |||
| formStruct := reflect.New(reflect.TypeOf(formStruct)) | |||
| errors := newErrors() | |||
| @@ -119,7 +120,7 @@ func MultipartForm(formStruct interface{}, ifacePtr ...interface{}) martini.Hand | |||
| // Workaround for multipart forms returning nil instead of an error | |||
| // when content is not multipart | |||
| // https://code.google.com/p/go/issues/detail?id=6334 | |||
| multipartReader, err := req.MultipartReader() | |||
| multipartReader, err := ctx.Req.MultipartReader() | |||
| if err != nil { | |||
| errors.Overall[BindingDeserializationError] = err.Error() | |||
| } else { | |||
| @@ -129,12 +130,12 @@ func MultipartForm(formStruct interface{}, ifacePtr ...interface{}) martini.Hand | |||
| errors.Overall[BindingDeserializationError] = parseErr.Error() | |||
| } | |||
| req.MultipartForm = form | |||
| ctx.Req.MultipartForm = form | |||
| } | |||
| mapForm(formStruct, req.MultipartForm.Value, errors) | |||
| mapForm(formStruct, ctx.Req.MultipartForm.Value, errors) | |||
| validateAndMap(formStruct, context, errors, ifacePtr...) | |||
| validateAndMap(formStruct, ctx, errors, ifacePtr...) | |||
| } | |||
| } | |||
| @@ -143,21 +144,21 @@ func MultipartForm(formStruct interface{}, ifacePtr ...interface{}) martini.Hand | |||
| // validated, but no error handling is actually performed here. | |||
| // An interface pointer can be added as a second argument in order | |||
| // to map the struct to a specific interface. | |||
| func Json(jsonStruct interface{}, ifacePtr ...interface{}) martini.Handler { | |||
| return func(context martini.Context, req *http.Request) { | |||
| func Json(jsonStruct interface{}, ifacePtr ...interface{}) macaron.Handler { | |||
| return func(ctx *macaron.Context) { | |||
| ensureNotPointer(jsonStruct) | |||
| jsonStruct := reflect.New(reflect.TypeOf(jsonStruct)) | |||
| errors := newErrors() | |||
| if req.Body != nil { | |||
| defer req.Body.Close() | |||
| if ctx.Req.Body != nil { | |||
| defer ctx.Req.Body.Close() | |||
| } | |||
| if err := json.NewDecoder(req.Body).Decode(jsonStruct.Interface()); err != nil && err != io.EOF { | |||
| if err := json.NewDecoder(ctx.Req.Body).Decode(jsonStruct.Interface()); err != nil && err != io.EOF { | |||
| errors.Overall[BindingDeserializationError] = err.Error() | |||
| } | |||
| validateAndMap(jsonStruct, context, errors, ifacePtr...) | |||
| validateAndMap(jsonStruct, ctx, errors, ifacePtr...) | |||
| } | |||
| } | |||
| @@ -165,15 +166,15 @@ func Json(jsonStruct interface{}, ifacePtr ...interface{}) martini.Handler { | |||
| // passed in is a Validator, then the user-defined Validate method | |||
| // is executed, and its errors are mapped to the context. This middleware | |||
| // performs no error handling: it merely detects them and maps them. | |||
| func Validate(obj interface{}) martini.Handler { | |||
| return func(context martini.Context, req *http.Request) { | |||
| func Validate(obj interface{}) macaron.Handler { | |||
| return func(ctx *macaron.Context, l i18n.Locale) { | |||
| errors := newErrors() | |||
| validateStruct(errors, obj) | |||
| if validator, ok := obj.(Validator); ok { | |||
| validator.Validate(errors, req, context) | |||
| validator.Validate(ctx, errors, l) | |||
| } | |||
| context.Map(*errors) | |||
| ctx.Map(*errors) | |||
| } | |||
| } | |||
| @@ -387,9 +388,7 @@ func setWithProperType(valueKind reflect.Kind, val string, structField reflect.V | |||
| } | |||
| } | |||
| // Don't pass in pointers to bind to. Can lead to bugs. See: | |||
| // https://github.com/codegangsta/martini-contrib/issues/40 | |||
| // https://github.com/codegangsta/martini-contrib/pull/34#issuecomment-29683659 | |||
| // Don't pass in pointers to bind to. Can lead to bugs. | |||
| func ensureNotPointer(obj interface{}) { | |||
| if reflect.TypeOf(obj).Kind() == reflect.Ptr { | |||
| panic("Pointers are not accepted as binding models") | |||
| @@ -399,13 +398,13 @@ func ensureNotPointer(obj interface{}) { | |||
| // Performs validation and combines errors from validation | |||
| // with errors from deserialization, then maps both the | |||
| // resulting struct and the errors to the context. | |||
| func validateAndMap(obj reflect.Value, context martini.Context, errors *Errors, ifacePtr ...interface{}) { | |||
| context.Invoke(Validate(obj.Interface())) | |||
| errors.Combine(getErrors(context)) | |||
| context.Map(*errors) | |||
| context.Map(obj.Elem().Interface()) | |||
| func validateAndMap(obj reflect.Value, ctx *macaron.Context, errors *Errors, ifacePtr ...interface{}) { | |||
| ctx.Invoke(Validate(obj.Interface())) | |||
| errors.Combine(getErrors(ctx)) | |||
| ctx.Map(*errors) | |||
| ctx.Map(obj.Elem().Interface()) | |||
| if len(ifacePtr) > 0 { | |||
| context.MapTo(obj.Elem().Interface(), ifacePtr[0]) | |||
| ctx.MapTo(obj.Elem().Interface(), ifacePtr[0]) | |||
| } | |||
| } | |||
| @@ -413,8 +412,8 @@ func newErrors() *Errors { | |||
| return &Errors{make(map[string]string), make(map[string]string)} | |||
| } | |||
| func getErrors(context martini.Context) Errors { | |||
| return context.Get(reflect.TypeOf(Errors{})).Interface().(Errors) | |||
| func getErrors(ctx *macaron.Context) Errors { | |||
| return ctx.GetVal(reflect.TypeOf(Errors{})).Interface().(Errors) | |||
| } | |||
| type ( | |||
| @@ -422,7 +421,7 @@ type ( | |||
| // validation before the request even gets to your application. | |||
| // The Validate method will be executed during the validation phase. | |||
| Validator interface { | |||
| Validate(*Errors, *http.Request, martini.Context) | |||
| Validate(*macaron.Context, *Errors, i18n.Locale) | |||
| } | |||
| ) | |||
| @@ -5,42 +5,33 @@ | |||
| package middleware | |||
| import ( | |||
| "crypto/hmac" | |||
| "crypto/sha1" | |||
| "encoding/base64" | |||
| "fmt" | |||
| "html/template" | |||
| "io" | |||
| "net/http" | |||
| "net/url" | |||
| "path/filepath" | |||
| "strconv" | |||
| "path" | |||
| "strings" | |||
| "time" | |||
| "github.com/go-martini/martini" | |||
| "github.com/gogits/cache" | |||
| "github.com/gogits/git" | |||
| "github.com/gogits/session" | |||
| "github.com/Unknwon/macaron" | |||
| "github.com/macaron-contrib/i18n" | |||
| "github.com/macaron-contrib/session" | |||
| "github.com/gogits/gogs/models" | |||
| "github.com/gogits/gogs/modules/auth" | |||
| "github.com/gogits/gogs/modules/base" | |||
| "github.com/gogits/gogs/modules/git" | |||
| "github.com/gogits/gogs/modules/log" | |||
| "github.com/gogits/gogs/modules/setting" | |||
| ) | |||
| // Context represents context of a request. | |||
| type Context struct { | |||
| *Render | |||
| c martini.Context | |||
| p martini.Params | |||
| Req *http.Request | |||
| Res http.ResponseWriter | |||
| Flash *Flash | |||
| Session session.SessionStore | |||
| Cache cache.Cache | |||
| *macaron.Context | |||
| i18n.Locale | |||
| Flash *session.Flash | |||
| Session session.Store | |||
| User *models.User | |||
| IsSigned bool | |||
| @@ -68,7 +59,8 @@ type Context struct { | |||
| HTTPS string | |||
| Git string | |||
| } | |||
| Mirror *models.Mirror | |||
| CommitsCount int | |||
| Mirror *models.Mirror | |||
| } | |||
| } | |||
| @@ -107,12 +99,12 @@ func (ctx *Context) HasError() bool { | |||
| } | |||
| // HTML calls render.HTML underlying but reduce one argument. | |||
| func (ctx *Context) HTML(status int, name base.TplName, htmlOpt ...HTMLOptions) { | |||
| ctx.Render.HTML(status, string(name), ctx.Data, htmlOpt...) | |||
| func (ctx *Context) HTML(status int, name base.TplName) { | |||
| ctx.Render.HTML(status, string(name), ctx.Data) | |||
| } | |||
| // RenderWithErr used for page has form validation but need to prompt error to users. | |||
| func (ctx *Context) RenderWithErr(msg string, tpl base.TplName, form auth.Form) { | |||
| func (ctx *Context) RenderWithErr(msg string, tpl base.TplName, form interface{}) { | |||
| if form != nil { | |||
| auth.AssignForm(form, ctx.Data) | |||
| } | |||
| @@ -124,8 +116,8 @@ func (ctx *Context) RenderWithErr(msg string, tpl base.TplName, form auth.Form) | |||
| // Handle handles and logs error by given status. | |||
| func (ctx *Context) Handle(status int, title string, err error) { | |||
| if err != nil { | |||
| log.Error("%s: %v", title, err) | |||
| if martini.Dev != martini.Prod { | |||
| log.Error(4, "%s: %v", title, err) | |||
| if macaron.Env != macaron.PROD { | |||
| ctx.Data["ErrorMsg"] = err | |||
| } | |||
| } | |||
| @@ -139,106 +131,6 @@ func (ctx *Context) Handle(status int, title string, err error) { | |||
| ctx.HTML(status, base.TplName(fmt.Sprintf("status/%d", status))) | |||
| } | |||
| func (ctx *Context) GetCookie(name string) string { | |||
| cookie, err := ctx.Req.Cookie(name) | |||
| if err != nil { | |||
| return "" | |||
| } | |||
| return cookie.Value | |||
| } | |||
| func (ctx *Context) SetCookie(name string, value string, others ...interface{}) { | |||
| cookie := http.Cookie{} | |||
| cookie.Name = name | |||
| cookie.Value = value | |||
| if len(others) > 0 { | |||
| switch v := others[0].(type) { | |||
| case int: | |||
| cookie.MaxAge = v | |||
| case int64: | |||
| cookie.MaxAge = int(v) | |||
| case int32: | |||
| cookie.MaxAge = int(v) | |||
| } | |||
| } | |||
| // default "/" | |||
| if len(others) > 1 { | |||
| if v, ok := others[1].(string); ok && len(v) > 0 { | |||
| cookie.Path = v | |||
| } | |||
| } else { | |||
| cookie.Path = "/" | |||
| } | |||
| // default empty | |||
| if len(others) > 2 { | |||
| if v, ok := others[2].(string); ok && len(v) > 0 { | |||
| cookie.Domain = v | |||
| } | |||
| } | |||
| // default empty | |||
| if len(others) > 3 { | |||
| switch v := others[3].(type) { | |||
| case bool: | |||
| cookie.Secure = v | |||
| default: | |||
| if others[3] != nil { | |||
| cookie.Secure = true | |||
| } | |||
| } | |||
| } | |||
| // default false. for session cookie default true | |||
| if len(others) > 4 { | |||
| if v, ok := others[4].(bool); ok && v { | |||
| cookie.HttpOnly = true | |||
| } | |||
| } | |||
| ctx.Res.Header().Add("Set-Cookie", cookie.String()) | |||
| } | |||
| // Get secure cookie from request by a given key. | |||
| func (ctx *Context) GetSecureCookie(Secret, key string) (string, bool) { | |||
| val := ctx.GetCookie(key) | |||
| if val == "" { | |||
| return "", false | |||
| } | |||
| parts := strings.SplitN(val, "|", 3) | |||
| if len(parts) != 3 { | |||
| return "", false | |||
| } | |||
| vs := parts[0] | |||
| timestamp := parts[1] | |||
| sig := parts[2] | |||
| h := hmac.New(sha1.New, []byte(Secret)) | |||
| fmt.Fprintf(h, "%s%s", vs, timestamp) | |||
| if fmt.Sprintf("%02x", h.Sum(nil)) != sig { | |||
| return "", false | |||
| } | |||
| res, _ := base64.URLEncoding.DecodeString(vs) | |||
| return string(res), true | |||
| } | |||
| // Set Secure cookie for response. | |||
| func (ctx *Context) SetSecureCookie(Secret, name, value string, others ...interface{}) { | |||
| vs := base64.URLEncoding.EncodeToString([]byte(value)) | |||
| timestamp := strconv.FormatInt(time.Now().UnixNano(), 10) | |||
| h := hmac.New(sha1.New, []byte(Secret)) | |||
| fmt.Fprintf(h, "%s%s", vs, timestamp) | |||
| sig := fmt.Sprintf("%02x", h.Sum(nil)) | |||
| cookie := strings.Join([]string{vs, timestamp, sig}, "|") | |||
| ctx.SetCookie(name, cookie, others...) | |||
| } | |||
| func (ctx *Context) CsrfToken() string { | |||
| if len(ctx.csrfToken) > 0 { | |||
| return ctx.csrfToken | |||
| @@ -271,16 +163,16 @@ func (ctx *Context) ServeFile(file string, names ...string) { | |||
| if len(names) > 0 { | |||
| name = names[0] | |||
| } else { | |||
| name = filepath.Base(file) | |||
| name = path.Base(file) | |||
| } | |||
| ctx.Res.Header().Set("Content-Description", "File Transfer") | |||
| ctx.Res.Header().Set("Content-Type", "application/octet-stream") | |||
| ctx.Res.Header().Set("Content-Disposition", "attachment; filename="+name) | |||
| ctx.Res.Header().Set("Content-Transfer-Encoding", "binary") | |||
| ctx.Res.Header().Set("Expires", "0") | |||
| ctx.Res.Header().Set("Cache-Control", "must-revalidate") | |||
| ctx.Res.Header().Set("Pragma", "public") | |||
| http.ServeFile(ctx.Res, ctx.Req, file) | |||
| ctx.Resp.Header().Set("Content-Description", "File Transfer") | |||
| ctx.Resp.Header().Set("Content-Type", "application/octet-stream") | |||
| ctx.Resp.Header().Set("Content-Disposition", "attachment; filename="+name) | |||
| ctx.Resp.Header().Set("Content-Transfer-Encoding", "binary") | |||
| ctx.Resp.Header().Set("Expires", "0") | |||
| ctx.Resp.Header().Set("Cache-Control", "must-revalidate") | |||
| ctx.Resp.Header().Set("Pragma", "public") | |||
| http.ServeFile(ctx.Resp, ctx.Req, file) | |||
| } | |||
| func (ctx *Context) ServeContent(name string, r io.ReadSeeker, params ...interface{}) { | |||
| @@ -291,87 +183,52 @@ func (ctx *Context) ServeContent(name string, r io.ReadSeeker, params ...interfa | |||
| modtime = v | |||
| } | |||
| } | |||
| ctx.Res.Header().Set("Content-Description", "File Transfer") | |||
| ctx.Res.Header().Set("Content-Type", "application/octet-stream") | |||
| ctx.Res.Header().Set("Content-Disposition", "attachment; filename="+name) | |||
| ctx.Res.Header().Set("Content-Transfer-Encoding", "binary") | |||
| ctx.Res.Header().Set("Expires", "0") | |||
| ctx.Res.Header().Set("Cache-Control", "must-revalidate") | |||
| ctx.Res.Header().Set("Pragma", "public") | |||
| http.ServeContent(ctx.Res, ctx.Req, name, modtime, r) | |||
| } | |||
| type Flash struct { | |||
| url.Values | |||
| ErrorMsg, SuccessMsg string | |||
| } | |||
| func (f *Flash) Error(msg string) { | |||
| f.Set("error", msg) | |||
| f.ErrorMsg = msg | |||
| ctx.Resp.Header().Set("Content-Description", "File Transfer") | |||
| ctx.Resp.Header().Set("Content-Type", "application/octet-stream") | |||
| ctx.Resp.Header().Set("Content-Disposition", "attachment; filename="+name) | |||
| ctx.Resp.Header().Set("Content-Transfer-Encoding", "binary") | |||
| ctx.Resp.Header().Set("Expires", "0") | |||
| ctx.Resp.Header().Set("Cache-Control", "must-revalidate") | |||
| ctx.Resp.Header().Set("Pragma", "public") | |||
| http.ServeContent(ctx.Resp, ctx.Req, name, modtime, r) | |||
| } | |||
| func (f *Flash) Success(msg string) { | |||
| f.Set("success", msg) | |||
| f.SuccessMsg = msg | |||
| } | |||
| // InitContext initializes a classic context for a request. | |||
| func InitContext() martini.Handler { | |||
| return func(res http.ResponseWriter, r *http.Request, c martini.Context, rd *Render) { | |||
| // Contexter initializes a classic context for a request. | |||
| func Contexter() macaron.Handler { | |||
| return func(c *macaron.Context, l i18n.Locale, sess session.Store, f *session.Flash) { | |||
| ctx := &Context{ | |||
| c: c, | |||
| // p: p, | |||
| Req: r, | |||
| Res: res, | |||
| Cache: setting.Cache, | |||
| Render: rd, | |||
| Context: c, | |||
| Locale: l, | |||
| Flash: f, | |||
| Session: sess, | |||
| } | |||
| ctx.Data["PageStartTime"] = time.Now() | |||
| // start session | |||
| ctx.Session = setting.SessionManager.SessionStart(res, r) | |||
| // Cache: setting.Cache, | |||
| // Get flash. | |||
| values, err := url.ParseQuery(ctx.GetCookie("gogs_flash")) | |||
| if err != nil { | |||
| log.Error("InitContext.ParseQuery(flash): %v", err) | |||
| } else if len(values) > 0 { | |||
| ctx.Flash = &Flash{Values: values} | |||
| ctx.Flash.ErrorMsg = ctx.Flash.Get("error") | |||
| ctx.Flash.SuccessMsg = ctx.Flash.Get("success") | |||
| ctx.Data["Flash"] = ctx.Flash | |||
| ctx.SetCookie("gogs_flash", "", -1) | |||
| // Compute current URL for real-time change language. | |||
| link := ctx.Req.RequestURI | |||
| i := strings.Index(link, "?") | |||
| if i > -1 { | |||
| link = link[:i] | |||
| } | |||
| ctx.Flash = &Flash{Values: url.Values{}} | |||
| ctx.Data["Link"] = link | |||
| rw := res.(martini.ResponseWriter) | |||
| rw.Before(func(martini.ResponseWriter) { | |||
| ctx.Session.SessionRelease(res) | |||
| if flash := ctx.Flash.Encode(); len(flash) > 0 { | |||
| ctx.SetCookie("gogs_flash", flash, 0) | |||
| } | |||
| }) | |||
| ctx.Data["PageStartTime"] = time.Now() | |||
| // Get user from session if logined. | |||
| user := auth.SignedInUser(ctx.req.Header, ctx.Session) | |||
| ctx.User = user | |||
| ctx.IsSigned = user != nil | |||
| ctx.Data["IsSigned"] = ctx.IsSigned | |||
| if user != nil { | |||
| ctx.Data["SignedUser"] = user | |||
| ctx.Data["SignedUserId"] = user.Id | |||
| ctx.Data["SignedUserName"] = user.Name | |||
| ctx.User = auth.SignedInUser(ctx.Req.Header, ctx.Session) | |||
| if ctx.User != nil { | |||
| ctx.IsSigned = true | |||
| ctx.Data["IsSigned"] = ctx.IsSigned | |||
| ctx.Data["SignedUser"] = ctx.User | |||
| ctx.Data["SignedUserId"] = ctx.User.Id | |||
| ctx.Data["SignedUserName"] = ctx.User.Name | |||
| ctx.Data["IsAdmin"] = ctx.User.IsAdmin | |||
| } | |||
| // If request sends files, parse them here otherwise the Query() can't be parsed and the CsrfToken will be invalid. | |||
| if strings.Contains(r.Header.Get("Content-Type"), "multipart/form-data") { | |||
| if err = ctx.Req.ParseMultipartForm(setting.AttachmentMaxSize << 20); err != nil { // 32MB max size | |||
| ctx.Handle(500, "issue.Comment(ctx.Req.ParseMultipartForm)", err) | |||
| if ctx.Req.Method == "POST" && strings.Contains(ctx.Req.Header.Get("Content-Type"), "multipart/form-data") { | |||
| if err := ctx.Req.ParseMultipartForm(setting.AttachmentMaxSize << 20); err != nil && !strings.Contains(err.Error(), "EOF") { // 32MB max size | |||
| ctx.Handle(500, "ParseMultipartForm", err) | |||
| return | |||
| } | |||
| } | |||
| @@ -381,7 +238,5 @@ func InitContext() martini.Handler { | |||
| ctx.Data["CsrfTokenHtml"] = template.HTML(`<input type="hidden" name="_csrf" value="` + ctx.csrfToken + `">`) | |||
| c.Map(ctx) | |||
| c.Next() | |||
| } | |||
| } | |||
| @@ -1,52 +0,0 @@ | |||
| // 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 middleware | |||
| import ( | |||
| "fmt" | |||
| "log" | |||
| "net/http" | |||
| "runtime" | |||
| "time" | |||
| "github.com/go-martini/martini" | |||
| "github.com/gogits/gogs/modules/setting" | |||
| ) | |||
| var isWindows bool | |||
| func init() { | |||
| isWindows = runtime.GOOS == "windows" | |||
| } | |||
| func Logger() martini.Handler { | |||
| return func(res http.ResponseWriter, req *http.Request, ctx martini.Context, log *log.Logger) { | |||
| if setting.DisableRouterLog { | |||
| return | |||
| } | |||
| start := time.Now() | |||
| log.Printf("Started %s %s", req.Method, req.URL.Path) | |||
| rw := res.(martini.ResponseWriter) | |||
| ctx.Next() | |||
| content := fmt.Sprintf("Completed %v %s in %v", rw.Status(), http.StatusText(rw.Status()), time.Since(start)) | |||
| if !isWindows { | |||
| switch rw.Status() { | |||
| case 200: | |||
| content = fmt.Sprintf("\033[1;32m%s\033[0m", content) | |||
| case 304: | |||
| content = fmt.Sprintf("\033[1;33m%s\033[0m", content) | |||
| case 404: | |||
| content = fmt.Sprintf("\033[1;31m%s\033[0m", content) | |||
| case 500: | |||
| content = fmt.Sprintf("\033[1;36m%s\033[0m", content) | |||
| } | |||
| } | |||
| log.Println(content) | |||
| } | |||
| } | |||
| @@ -1,281 +0,0 @@ | |||
| // 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. | |||
| // foked from https://github.com/martini-contrib/render/blob/master/render.go | |||
| package middleware | |||
| import ( | |||
| "bytes" | |||
| "encoding/json" | |||
| "fmt" | |||
| "html/template" | |||
| "io" | |||
| "io/ioutil" | |||
| "net/http" | |||
| "os" | |||
| "path/filepath" | |||
| "time" | |||
| "github.com/go-martini/martini" | |||
| "github.com/gogits/gogs/modules/base" | |||
| ) | |||
| const ( | |||
| ContentType = "Content-Type" | |||
| ContentLength = "Content-Length" | |||
| ContentJSON = "application/json" | |||
| ContentHTML = "text/html" | |||
| ContentXHTML = "application/xhtml+xml" | |||
| defaultCharset = "UTF-8" | |||
| ) | |||
| var helperFuncs = template.FuncMap{ | |||
| "yield": func() (string, error) { | |||
| return "", fmt.Errorf("yield called with no layout defined") | |||
| }, | |||
| } | |||
| type Delims struct { | |||
| Left string | |||
| Right string | |||
| } | |||
| type RenderOptions struct { | |||
| Directory string | |||
| Layout string | |||
| Extensions []string | |||
| Funcs []template.FuncMap | |||
| Delims Delims | |||
| Charset string | |||
| IndentJSON bool | |||
| HTMLContentType string | |||
| } | |||
| type HTMLOptions struct { | |||
| Layout string | |||
| } | |||
| func Renderer(options ...RenderOptions) martini.Handler { | |||
| opt := prepareOptions(options) | |||
| cs := prepareCharset(opt.Charset) | |||
| t := compile(opt) | |||
| return func(res http.ResponseWriter, req *http.Request, c martini.Context) { | |||
| var tc *template.Template | |||
| if martini.Env == martini.Dev { | |||
| tc = compile(opt) | |||
| } else { | |||
| tc, _ = t.Clone() | |||
| } | |||
| rd := &Render{res, req, tc, opt, cs, base.TmplData{}, time.Time{}} | |||
| rd.Data["TmplLoadTimes"] = func() string { | |||
| if rd.startTime.IsZero() { | |||
| return "" | |||
| } | |||
| return fmt.Sprint(time.Since(rd.startTime).Nanoseconds()/1e6) + "ms" | |||
| } | |||
| c.Map(rd.Data) | |||
| c.Map(rd) | |||
| } | |||
| } | |||
| func prepareCharset(charset string) string { | |||
| if len(charset) != 0 { | |||
| return "; charset=" + charset | |||
| } | |||
| return "; charset=" + defaultCharset | |||
| } | |||
| func prepareOptions(options []RenderOptions) RenderOptions { | |||
| var opt RenderOptions | |||
| if len(options) > 0 { | |||
| opt = options[0] | |||
| } | |||
| if len(opt.Directory) == 0 { | |||
| opt.Directory = "templates" | |||
| } | |||
| if len(opt.Extensions) == 0 { | |||
| opt.Extensions = []string{".tmpl"} | |||
| } | |||
| if len(opt.HTMLContentType) == 0 { | |||
| opt.HTMLContentType = ContentHTML | |||
| } | |||
| return opt | |||
| } | |||
| func compile(options RenderOptions) *template.Template { | |||
| dir := options.Directory | |||
| t := template.New(dir) | |||
| t.Delims(options.Delims.Left, options.Delims.Right) | |||
| template.Must(t.Parse("Martini")) | |||
| filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { | |||
| r, err := filepath.Rel(dir, path) | |||
| if err != nil { | |||
| return err | |||
| } | |||
| ext := filepath.Ext(r) | |||
| for _, extension := range options.Extensions { | |||
| if ext == extension { | |||
| buf, err := ioutil.ReadFile(path) | |||
| if err != nil { | |||
| panic(err) | |||
| } | |||
| name := (r[0 : len(r)-len(ext)]) | |||
| tmpl := t.New(filepath.ToSlash(name)) | |||
| for _, funcs := range options.Funcs { | |||
| tmpl = tmpl.Funcs(funcs) | |||
| } | |||
| template.Must(tmpl.Funcs(helperFuncs).Parse(string(buf))) | |||
| break | |||
| } | |||
| } | |||
| return nil | |||
| }) | |||
| return t | |||
| } | |||
| type Render struct { | |||
| http.ResponseWriter | |||
| req *http.Request | |||
| t *template.Template | |||
| opt RenderOptions | |||
| compiledCharset string | |||
| Data base.TmplData | |||
| startTime time.Time | |||
| } | |||
| func (r *Render) JSON(status int, v interface{}) { | |||
| var result []byte | |||
| var err error | |||
| if r.opt.IndentJSON { | |||
| result, err = json.MarshalIndent(v, "", " ") | |||
| } else { | |||
| result, err = json.Marshal(v) | |||
| } | |||
| if err != nil { | |||
| http.Error(r, err.Error(), 500) | |||
| return | |||
| } | |||
| r.Header().Set(ContentType, ContentJSON+r.compiledCharset) | |||
| r.WriteHeader(status) | |||
| r.Write(result) | |||
| } | |||
| func (r *Render) JSONString(v interface{}) (string, error) { | |||
| var result []byte | |||
| var err error | |||
| if r.opt.IndentJSON { | |||
| result, err = json.MarshalIndent(v, "", " ") | |||
| } else { | |||
| result, err = json.Marshal(v) | |||
| } | |||
| if err != nil { | |||
| return "", err | |||
| } | |||
| return string(result), nil | |||
| } | |||
| func (r *Render) renderBytes(name string, binding interface{}, htmlOpt ...HTMLOptions) (*bytes.Buffer, error) { | |||
| opt := r.prepareHTMLOptions(htmlOpt) | |||
| if len(opt.Layout) > 0 { | |||
| r.addYield(name, binding) | |||
| name = opt.Layout | |||
| } | |||
| out, err := r.execute(name, binding) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| return out, nil | |||
| } | |||
| func (r *Render) HTML(status int, name string, binding interface{}, htmlOpt ...HTMLOptions) { | |||
| r.startTime = time.Now() | |||
| out, err := r.renderBytes(name, binding, htmlOpt...) | |||
| if err != nil { | |||
| http.Error(r, err.Error(), http.StatusInternalServerError) | |||
| return | |||
| } | |||
| r.Header().Set(ContentType, r.opt.HTMLContentType+r.compiledCharset) | |||
| r.WriteHeader(status) | |||
| io.Copy(r, out) | |||
| } | |||
| func (r *Render) HTMLString(name string, binding interface{}, htmlOpt ...HTMLOptions) (string, error) { | |||
| if out, err := r.renderBytes(name, binding, htmlOpt...); err != nil { | |||
| return "", err | |||
| } else { | |||
| return out.String(), nil | |||
| } | |||
| } | |||
| func (r *Render) Error(status int, message ...string) { | |||
| r.WriteHeader(status) | |||
| if len(message) > 0 { | |||
| r.Write([]byte(message[0])) | |||
| } | |||
| } | |||
| func (r *Render) Redirect(location string, status ...int) { | |||
| code := http.StatusFound | |||
| if len(status) == 1 { | |||
| code = status[0] | |||
| } | |||
| http.Redirect(r, r.req, location, code) | |||
| } | |||
| func (r *Render) Template() *template.Template { | |||
| return r.t | |||
| } | |||
| func (r *Render) execute(name string, binding interface{}) (*bytes.Buffer, error) { | |||
| buf := new(bytes.Buffer) | |||
| return buf, r.t.ExecuteTemplate(buf, name, binding) | |||
| } | |||
| func (r *Render) addYield(name string, binding interface{}) { | |||
| funcs := template.FuncMap{ | |||
| "yield": func() (template.HTML, error) { | |||
| buf, err := r.execute(name, binding) | |||
| return template.HTML(buf.String()), err | |||
| }, | |||
| } | |||
| r.t.Funcs(funcs) | |||
| } | |||
| func (r *Render) prepareHTMLOptions(htmlOpt []HTMLOptions) HTMLOptions { | |||
| if len(htmlOpt) > 0 { | |||
| return htmlOpt[0] | |||
| } | |||
| return HTMLOptions{ | |||
| Layout: r.opt.Layout, | |||
| } | |||
| } | |||
| @@ -10,20 +10,19 @@ import ( | |||
| "net/url" | |||
| "strings" | |||
| "github.com/go-martini/martini" | |||
| "github.com/gogits/git" | |||
| "github.com/Unknwon/macaron" | |||
| "github.com/gogits/gogs/models" | |||
| "github.com/gogits/gogs/modules/git" | |||
| "github.com/gogits/gogs/modules/log" | |||
| "github.com/gogits/gogs/modules/setting" | |||
| ) | |||
| func RepoAssignment(redirect bool, args ...bool) martini.Handler { | |||
| return func(ctx *Context, params martini.Params) { | |||
| // valid brachname | |||
| func RepoAssignment(redirect bool, args ...bool) macaron.Handler { | |||
| return func(ctx *Context) { | |||
| // To valid brach name. | |||
| var validBranch bool | |||
| // display bare quick start if it is a bare repo | |||
| // To display bare quick start if it is a bare repo. | |||
| var displayBare bool | |||
| if len(args) >= 1 { | |||
| @@ -35,51 +34,53 @@ func RepoAssignment(redirect bool, args ...bool) martini.Handler { | |||
| } | |||
| var ( | |||
| user *models.User | |||
| err error | |||
| u *models.User | |||
| err error | |||
| ) | |||
| userName := params["username"] | |||
| repoName := params["reponame"] | |||
| refName := params["branchname"] | |||
| userName := ctx.Params(":username") | |||
| repoName := ctx.Params(":reponame") | |||
| refName := ctx.Params(":branchname") | |||
| if len(refName) == 0 { | |||
| refName = ctx.Params(":path") | |||
| } | |||
| // TODO: need more advanced onwership and access level check. | |||
| // Collaborators who have write access can be seen as owners. | |||
| if ctx.IsSigned { | |||
| ctx.Repo.IsOwner, err = models.HasAccess(ctx.User.Name, userName+"/"+repoName, models.WRITABLE) | |||
| if err != nil { | |||
| ctx.Handle(500, "RepoAssignment(HasAccess)", err) | |||
| ctx.Handle(500, "HasAccess", err) | |||
| return | |||
| } | |||
| ctx.Repo.IsTrueOwner = ctx.User.LowerName == strings.ToLower(userName) | |||
| } | |||
| if !ctx.Repo.IsTrueOwner { | |||
| user, err = models.GetUserByName(userName) | |||
| u, err = models.GetUserByName(userName) | |||
| if err != nil { | |||
| if err == models.ErrUserNotExist { | |||
| ctx.Handle(404, "RepoAssignment(GetUserByName)", err) | |||
| ctx.Handle(404, "GetUserByName", err) | |||
| return | |||
| } else if redirect { | |||
| ctx.Redirect("/") | |||
| return | |||
| } | |||
| ctx.Handle(500, "RepoAssignment(GetUserByName)", err) | |||
| ctx.Handle(500, "GetUserByName", err) | |||
| return | |||
| } | |||
| } else { | |||
| user = ctx.User | |||
| u = ctx.User | |||
| } | |||
| if user == nil { | |||
| if u == nil { | |||
| if redirect { | |||
| ctx.Redirect("/") | |||
| return | |||
| } | |||
| ctx.Handle(403, "RepoAssignment", errors.New("invliad user account for single repository")) | |||
| ctx.Handle(404, "RepoAssignment", errors.New("invliad user account for single repository")) | |||
| return | |||
| } | |||
| ctx.Repo.Owner = user | |||
| ctx.Repo.Owner = u | |||
| // Organization owner team members are true owners as well. | |||
| if ctx.IsSigned && ctx.Repo.Owner.IsOrganization() && ctx.Repo.Owner.IsOrgOwner(ctx.User.Id) { | |||
| @@ -87,16 +88,19 @@ func RepoAssignment(redirect bool, args ...bool) martini.Handler { | |||
| } | |||
| // get repository | |||
| repo, err := models.GetRepositoryByName(user.Id, repoName) | |||
| repo, err := models.GetRepositoryByName(u.Id, repoName) | |||
| if err != nil { | |||
| if err == models.ErrRepoNotExist { | |||
| ctx.Handle(404, "RepoAssignment", err) | |||
| ctx.Handle(404, "GetRepositoryByName", err) | |||
| return | |||
| } else if redirect { | |||
| ctx.Redirect("/") | |||
| return | |||
| } | |||
| ctx.Handle(500, "RepoAssignment", err) | |||
| ctx.Handle(500, "GetRepositoryByName", err) | |||
| return | |||
| } else if err = repo.GetOwner(); err != nil { | |||
| ctx.Handle(500, "GetOwner", err) | |||
| return | |||
| } | |||
| @@ -108,16 +112,16 @@ func RepoAssignment(redirect bool, args ...bool) martini.Handler { | |||
| // Check access. | |||
| if repo.IsPrivate && !ctx.Repo.IsOwner { | |||
| if ctx.User == nil { | |||
| ctx.Handle(404, "RepoAssignment(HasAccess)", nil) | |||
| ctx.Handle(404, "HasAccess", nil) | |||
| return | |||
| } | |||
| hasAccess, err := models.HasAccess(ctx.User.Name, ctx.Repo.Owner.Name+"/"+repo.Name, models.READABLE) | |||
| if err != nil { | |||
| ctx.Handle(500, "RepoAssignment(HasAccess)", err) | |||
| ctx.Handle(500, "HasAccess", err) | |||
| return | |||
| } else if !hasAccess { | |||
| ctx.Handle(404, "RepoAssignment(HasAccess)", nil) | |||
| ctx.Handle(404, "HasAccess", nil) | |||
| return | |||
| } | |||
| } | |||
| @@ -127,7 +131,7 @@ func RepoAssignment(redirect bool, args ...bool) martini.Handler { | |||
| if repo.IsMirror { | |||
| ctx.Repo.Mirror, err = models.GetMirror(repo.Id) | |||
| if err != nil { | |||
| ctx.Handle(500, "RepoAssignment(GetMirror)", err) | |||
| ctx.Handle(500, "GetMirror", err) | |||
| return | |||
| } | |||
| ctx.Data["MirrorInterval"] = ctx.Repo.Mirror.Interval | |||
| @@ -144,34 +148,33 @@ func RepoAssignment(redirect bool, args ...bool) martini.Handler { | |||
| return | |||
| } | |||
| ctx.Repo.GitRepo = gitRepo | |||
| ctx.Repo.RepoLink = "/" + user.Name + "/" + repo.Name | |||
| ctx.Repo.RepoLink = "/" + u.Name + "/" + repo.Name | |||
| tags, err := ctx.Repo.GitRepo.GetTags() | |||
| if err != nil { | |||
| ctx.Handle(500, "RepoAssignment(GetTags))", err) | |||
| ctx.Handle(500, "GetTags", err) | |||
| return | |||
| } | |||
| ctx.Repo.Repository.NumTags = len(tags) | |||
| ctx.Data["Title"] = user.Name + "/" + repo.Name | |||
| ctx.Data["Title"] = u.Name + "/" + repo.Name | |||
| ctx.Data["Repository"] = repo | |||
| ctx.Data["Owner"] = user | |||
| ctx.Data["Owner"] = ctx.Repo.Repository.Owner | |||
| ctx.Data["RepoLink"] = ctx.Repo.RepoLink | |||
| ctx.Data["IsRepositoryOwner"] = ctx.Repo.IsOwner | |||
| ctx.Data["IsRepositoryTrueOwner"] = ctx.Repo.IsTrueOwner | |||
| ctx.Data["BranchName"] = "" | |||
| if setting.SshPort != 22 { | |||
| ctx.Repo.CloneLink.SSH = fmt.Sprintf("ssh://%s@%s/%s/%s.git", setting.RunUser, setting.Domain, user.LowerName, repo.LowerName) | |||
| ctx.Repo.CloneLink.SSH = fmt.Sprintf("ssh://%s@%s/%s/%s.git", setting.RunUser, setting.Domain, u.LowerName, repo.LowerName) | |||
| } else { | |||
| ctx.Repo.CloneLink.SSH = fmt.Sprintf("%s@%s:%s/%s.git", setting.RunUser, setting.Domain, user.LowerName, repo.LowerName) | |||
| ctx.Repo.CloneLink.SSH = fmt.Sprintf("%s@%s:%s/%s.git", setting.RunUser, setting.Domain, u.LowerName, repo.LowerName) | |||
| } | |||
| ctx.Repo.CloneLink.HTTPS = fmt.Sprintf("%s%s/%s.git", setting.AppUrl, user.LowerName, repo.LowerName) | |||
| ctx.Repo.CloneLink.HTTPS = fmt.Sprintf("%s%s/%s.git", setting.AppUrl, u.LowerName, repo.LowerName) | |||
| ctx.Data["CloneLink"] = ctx.Repo.CloneLink | |||
| if ctx.Repo.Repository.IsGoget { | |||
| ctx.Data["GoGetLink"] = fmt.Sprintf("%s%s/%s", setting.AppUrl, user.LowerName, repo.LowerName) | |||
| ctx.Data["GoGetImport"] = fmt.Sprintf("%s/%s/%s", setting.Domain, user.LowerName, repo.LowerName) | |||
| ctx.Data["GoGetLink"] = fmt.Sprintf("%s%s/%s", setting.AppUrl, u.LowerName, repo.LowerName) | |||
| ctx.Data["GoGetImport"] = fmt.Sprintf("%s/%s/%s", setting.Domain, u.LowerName, repo.LowerName) | |||
| } | |||
| // when repo is bare, not valid branch | |||
| @@ -211,7 +214,7 @@ func RepoAssignment(redirect bool, args ...bool) martini.Handler { | |||
| return | |||
| } | |||
| } else { | |||
| ctx.Handle(404, "RepoAssignment invalid repo", nil) | |||
| ctx.Handle(404, "RepoAssignment invalid repo", errors.New("branch or tag not exist")) | |||
| return | |||
| } | |||
| @@ -222,7 +225,7 @@ func RepoAssignment(redirect bool, args ...bool) martini.Handler { | |||
| } else { | |||
| brs, err := gitRepo.GetBranches() | |||
| if err != nil { | |||
| ctx.Handle(500, "RepoAssignment(GetBranches))", err) | |||
| ctx.Handle(500, "GetBranches", err) | |||
| return | |||
| } | |||
| refName = brs[0] | |||
| @@ -233,6 +236,13 @@ func RepoAssignment(redirect bool, args ...bool) martini.Handler { | |||
| ctx.Data["IsBranch"] = ctx.Repo.IsBranch | |||
| ctx.Data["IsCommit"] = ctx.Repo.IsCommit | |||
| ctx.Repo.CommitsCount, err = ctx.Repo.Commit.CommitsCount() | |||
| if err != nil { | |||
| ctx.Handle(500, "CommitsCount", err) | |||
| return | |||
| } | |||
| ctx.Data["CommitsCount"] = ctx.Repo.CommitsCount | |||
| } | |||
| log.Debug("displayBare: %v; IsBare: %v", displayBare, ctx.Repo.Repository.IsBare) | |||
| @@ -240,7 +250,7 @@ func RepoAssignment(redirect bool, args ...bool) martini.Handler { | |||
| // repo is bare and display enable | |||
| if displayBare && ctx.Repo.Repository.IsBare { | |||
| log.Debug("Bare repository: %s", ctx.Repo.RepoLink) | |||
| ctx.HTML(200, "repo/single_bare") | |||
| ctx.HTML(200, "repo/bare") | |||
| return | |||
| } | |||
| @@ -251,9 +261,10 @@ func RepoAssignment(redirect bool, args ...bool) martini.Handler { | |||
| ctx.Data["TagName"] = ctx.Repo.TagName | |||
| brs, err := ctx.Repo.GitRepo.GetBranches() | |||
| if err != nil { | |||
| log.Error("RepoAssignment(GetBranches): %v", err) | |||
| log.Error(4, "GetBranches: %v", err) | |||
| } | |||
| ctx.Data["Branches"] = brs | |||
| ctx.Data["BrancheCount"] = len(brs) | |||
| // If not branch selected, try default one. | |||
| // If default branch doesn't exists, fall back to some other branch. | |||
| @@ -267,11 +278,11 @@ func RepoAssignment(redirect bool, args ...bool) martini.Handler { | |||
| ctx.Data["BranchName"] = ctx.Repo.BranchName | |||
| ctx.Data["CommitId"] = ctx.Repo.CommitId | |||
| ctx.Data["IsRepositoryWatching"] = ctx.Repo.IsWatching | |||
| ctx.Data["IsWatchingRepo"] = ctx.Repo.IsWatching | |||
| } | |||
| } | |||
| func RequireTrueOwner() martini.Handler { | |||
| func RequireTrueOwner() macaron.Handler { | |||
| return func(ctx *Context) { | |||
| if !ctx.Repo.IsTrueOwner { | |||
| if !ctx.IsSigned { | |||
| @@ -1,127 +0,0 @@ | |||
| // Copyright 2013 The Martini 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 middleware | |||
| import ( | |||
| "log" | |||
| "net/http" | |||
| "path" | |||
| "runtime" | |||
| "strings" | |||
| "github.com/go-martini/martini" | |||
| "github.com/gogits/gogs/modules/setting" | |||
| ) | |||
| // StaticOptions is a struct for specifying configuration options for the martini.Static middleware. | |||
| type StaticOptions struct { | |||
| // Prefix is the optional prefix used to serve the static directory content | |||
| Prefix string | |||
| // SkipLogging will disable [Static] log messages when a static file is served. | |||
| SkipLogging bool | |||
| // IndexFile defines which file to serve as index if it exists. | |||
| IndexFile string | |||
| // Expires defines which user-defined function to use for producing a HTTP Expires Header | |||
| // https://developers.google.com/speed/docs/insights/LeverageBrowserCaching | |||
| Expires func() string | |||
| } | |||
| func prepareStaticOptions(options []StaticOptions) StaticOptions { | |||
| var opt StaticOptions | |||
| if len(options) > 0 { | |||
| opt = options[0] | |||
| } | |||
| // Defaults | |||
| if len(opt.IndexFile) == 0 { | |||
| opt.IndexFile = "index.html" | |||
| } | |||
| // Normalize the prefix if provided | |||
| if opt.Prefix != "" { | |||
| // Ensure we have a leading '/' | |||
| if opt.Prefix[0] != '/' { | |||
| opt.Prefix = "/" + opt.Prefix | |||
| } | |||
| // Remove any trailing '/' | |||
| opt.Prefix = strings.TrimRight(opt.Prefix, "/") | |||
| } | |||
| return opt | |||
| } | |||
| // Static returns a middleware handler that serves static files in the given directory. | |||
| func Static(directory string, staticOpt ...StaticOptions) martini.Handler { | |||
| if runtime.GOOS == "windows" { | |||
| if len(directory) < 2 || directory[1] != ':' { | |||
| directory = path.Join(setting.StaticRootPath, directory) | |||
| } | |||
| } else if !path.IsAbs(directory) { | |||
| directory = path.Join(setting.StaticRootPath, directory) | |||
| } | |||
| dir := http.Dir(directory) | |||
| opt := prepareStaticOptions(staticOpt) | |||
| return func(res http.ResponseWriter, req *http.Request, log *log.Logger) { | |||
| if req.Method != "GET" && req.Method != "HEAD" { | |||
| return | |||
| } | |||
| file := req.URL.Path | |||
| // if we have a prefix, filter requests by stripping the prefix | |||
| if opt.Prefix != "" { | |||
| if !strings.HasPrefix(file, opt.Prefix) { | |||
| return | |||
| } | |||
| file = file[len(opt.Prefix):] | |||
| if file != "" && file[0] != '/' { | |||
| return | |||
| } | |||
| } | |||
| f, err := dir.Open(file) | |||
| if err != nil { | |||
| // discard the error? | |||
| return | |||
| } | |||
| defer f.Close() | |||
| fi, err := f.Stat() | |||
| if err != nil { | |||
| return | |||
| } | |||
| // try to serve index file | |||
| if fi.IsDir() { | |||
| // redirect if missing trailing slash | |||
| if !strings.HasSuffix(req.URL.Path, "/") { | |||
| http.Redirect(res, req, req.URL.Path+"/", http.StatusFound) | |||
| return | |||
| } | |||
| file = path.Join(file, opt.IndexFile) | |||
| f, err = dir.Open(file) | |||
| if err != nil { | |||
| return | |||
| } | |||
| defer f.Close() | |||
| fi, err = f.Stat() | |||
| if err != nil || fi.IsDir() { | |||
| return | |||
| } | |||
| } | |||
| if !opt.SkipLogging { | |||
| log.Println("[Static] Serving " + file) | |||
| } | |||
| // Add an Expires header to the static content | |||
| if opt.Expires != nil { | |||
| res.Header().Set("Expires", opt.Expires()) | |||
| } | |||
| http.ServeContent(res, req, file, fi.ModTime(), f) | |||
| } | |||
| } | |||
| @@ -78,7 +78,7 @@ func ExecDir(timeout time.Duration, dir, desc, cmdName string, args ...string) ( | |||
| select { | |||
| case <-time.After(timeout): | |||
| if errKill := Kill(pid); errKill != nil { | |||
| log.Error("Exec(%d:%s): %v", pid, desc, errKill) | |||
| log.Error(4, "Exec(%d:%s): %v", pid, desc, errKill) | |||
| } | |||
| <-done | |||
| return "", ErrExecTimeout.Error(), ErrExecTimeout | |||
| @@ -15,12 +15,12 @@ import ( | |||
| "github.com/Unknwon/com" | |||
| "github.com/Unknwon/goconfig" | |||
| "github.com/macaron-contrib/session" | |||
| "github.com/gogits/cache" | |||
| "github.com/gogits/session" | |||
| "github.com/gogits/gogs/modules/bin" | |||
| "github.com/gogits/gogs/modules/log" | |||
| // "github.com/gogits/gogs-ng/modules/ssh" | |||
| ) | |||
| type Scheme string | |||
| @@ -46,6 +46,7 @@ var ( | |||
| DisableRouterLog bool | |||
| CertFile, KeyFile string | |||
| StaticRootPath string | |||
| EnableGzip bool | |||
| // Security settings. | |||
| InstallLock bool | |||
| @@ -93,15 +94,22 @@ var ( | |||
| // Session settings. | |||
| SessionProvider string | |||
| SessionConfig *session.Config | |||
| SessionManager *session.Manager | |||
| // Global setting objects. | |||
| Cfg *goconfig.ConfigFile | |||
| CustomPath string // Custom directory path. | |||
| ProdMode bool | |||
| RunUser string | |||
| Cfg *goconfig.ConfigFile | |||
| ConfRootPath string | |||
| CustomPath string // Custom directory path. | |||
| ProdMode bool | |||
| RunUser string | |||
| // I18n settings. | |||
| Langs, Names []string | |||
| ) | |||
| func init() { | |||
| log.NewLogger(0, "console", `{"level": 0}`) | |||
| } | |||
| func ExecPath() (string, error) { | |||
| file, err := exec.LookPath(os.Args[0]) | |||
| if err != nil { | |||
| @@ -125,16 +133,13 @@ func WorkDir() (string, error) { | |||
| func NewConfigContext() { | |||
| workDir, err := WorkDir() | |||
| if err != nil { | |||
| log.Fatal("Fail to get work directory: %v", err) | |||
| log.Fatal(4, "Fail to get work directory: %v", err) | |||
| } | |||
| ConfRootPath = path.Join(workDir, "conf") | |||
| data, err := bin.Asset("conf/app.ini") | |||
| Cfg, err = goconfig.LoadConfigFile(path.Join(workDir, "conf/app.ini")) | |||
| if err != nil { | |||
| log.Fatal("Fail to read 'conf/app.ini': %v", err) | |||
| } | |||
| Cfg, err = goconfig.LoadFromData(data) | |||
| if err != nil { | |||
| log.Fatal("Fail to parse 'conf/app.ini': %v", err) | |||
| log.Fatal(4, "Fail to parse 'conf/app.ini': %v", err) | |||
| } | |||
| CustomPath = os.Getenv("GOGS_CUSTOM") | |||
| @@ -145,10 +150,10 @@ func NewConfigContext() { | |||
| cfgPath := path.Join(CustomPath, "conf/app.ini") | |||
| if com.IsFile(cfgPath) { | |||
| if err = Cfg.AppendFiles(cfgPath); err != nil { | |||
| log.Fatal("Fail to load custom 'conf/app.ini': %v", err) | |||
| log.Fatal(4, "Fail to load custom 'conf/app.ini': %v", err) | |||
| } | |||
| } else { | |||
| log.Warn("No custom 'conf/app.ini' found") | |||
| log.Warn("No custom 'conf/app.ini' found, please go to '/install'") | |||
| } | |||
| AppName = Cfg.MustValue("", "APP_NAME", "Gogs: Go Git Service") | |||
| @@ -169,6 +174,7 @@ func NewConfigContext() { | |||
| DisableRouterLog = Cfg.MustBool("server", "DISABLE_ROUTER_LOG") | |||
| StaticRootPath = Cfg.MustValue("server", "STATIC_ROOT_PATH", workDir) | |||
| LogRootPath = Cfg.MustValue("log", "ROOT_PATH", path.Join(workDir, "log")) | |||
| EnableGzip = Cfg.MustBool("server", "ENABLE_GZIP") | |||
| InstallLock = Cfg.MustBool("security", "INSTALL_LOCK") | |||
| SecretKey = Cfg.MustValue("security", "SECRET_KEY") | |||
| @@ -177,8 +183,8 @@ func NewConfigContext() { | |||
| CookieRememberName = Cfg.MustValue("security", "COOKIE_REMEMBER_NAME") | |||
| ReverseProxyAuthUser = Cfg.MustValue("security", "REVERSE_PROXY_AUTHENTICATION_USER", "X-WEBAUTH-USER") | |||
| AttachmentPath = Cfg.MustValue("attachment", "PATH", "files/attachments") | |||
| AttachmentAllowedTypes = Cfg.MustValue("attachment", "ALLOWED_TYPES", "*/*") | |||
| AttachmentPath = Cfg.MustValue("attachment", "PATH", "data/attachments") | |||
| AttachmentAllowedTypes = Cfg.MustValue("attachment", "ALLOWED_TYPES", "image/jpeg|image/png") | |||
| AttachmentMaxSize = Cfg.MustInt64("attachment", "MAX_SIZE", 32) | |||
| AttachmentMaxFiles = Cfg.MustInt("attachment", "MAX_FILES", 10) | |||
| AttachmentEnabled = Cfg.MustBool("attachment", "ENABLE", true) | |||
| @@ -233,7 +239,7 @@ func NewConfigContext() { | |||
| } | |||
| if err = os.MkdirAll(AttachmentPath, os.ModePerm); err != nil { | |||
| log.Fatal("Could not create directory %s: %s", AttachmentPath, err) | |||
| log.Fatal(4, "Could not create directory %s: %s", AttachmentPath, err) | |||
| } | |||
| RunUser = Cfg.MustValue("", "RUN_USER") | |||
| @@ -243,13 +249,13 @@ func NewConfigContext() { | |||
| } | |||
| // Does not check run user when the install lock is off. | |||
| if InstallLock && RunUser != curUser { | |||
| log.Fatal("Expect user(%s) but current user is: %s", RunUser, curUser) | |||
| log.Fatal(4, "Expect user(%s) but current user is: %s", RunUser, curUser) | |||
| } | |||
| // Determine and create root git reposiroty path. | |||
| homeDir, err := com.HomeDir() | |||
| if err != nil { | |||
| log.Fatal("Fail to get home directory: %v", err) | |||
| log.Fatal(4, "Fail to get home directory: %v", err) | |||
| } | |||
| RepoRootPath = Cfg.MustValue("repository", "ROOT", filepath.Join(homeDir, "gogs-repositories")) | |||
| if !filepath.IsAbs(RepoRootPath) { | |||
| @@ -259,13 +265,16 @@ func NewConfigContext() { | |||
| } | |||
| if err = os.MkdirAll(RepoRootPath, os.ModePerm); err != nil { | |||
| log.Fatal("Fail to create repository root path(%s): %v", RepoRootPath, err) | |||
| log.Fatal(4, "Fail to create repository root path(%s): %v", RepoRootPath, err) | |||
| } | |||
| ScriptType = Cfg.MustValue("repository", "SCRIPT_TYPE", "bash") | |||
| PictureService = Cfg.MustValueRange("picture", "SERVICE", "server", | |||
| []string{"server"}) | |||
| DisableGravatar = Cfg.MustBool("picture", "DISABLE_GRAVATAR") | |||
| Langs = Cfg.MustValueArray("i18n", "LANGS", ",") | |||
| Names = Cfg.MustValueArray("i18n", "NAMES", ",") | |||
| } | |||
| var Service struct { | |||
| @@ -308,7 +317,7 @@ func newLogService() { | |||
| mode = strings.TrimSpace(mode) | |||
| modeSec := "log." + mode | |||
| if _, err := Cfg.GetSection(modeSec); err != nil { | |||
| log.Fatal("Unknown log mode: %s", mode) | |||
| log.Fatal(4, "Unknown log mode: %s", mode) | |||
| } | |||
| // Log level. | |||
| @@ -316,7 +325,7 @@ func newLogService() { | |||
| []string{"Trace", "Debug", "Info", "Warn", "Error", "Critical"}) | |||
| level, ok := logLevels[levelName] | |||
| if !ok { | |||
| log.Fatal("Unknown log level: %s", levelName) | |||
| log.Fatal(4, "Unknown log level: %s", levelName) | |||
| } | |||
| // Generate log configuration. | |||
| @@ -371,15 +380,15 @@ func newCacheService() { | |||
| case "memory": | |||
| CacheConfig = fmt.Sprintf(`{"interval":%d}`, Cfg.MustInt("cache", "INTERVAL", 60)) | |||
| case "redis", "memcache": | |||
| CacheConfig = fmt.Sprintf(`{"conn":"%s"}`, Cfg.MustValue("cache", "HOST")) | |||
| CacheConfig = fmt.Sprintf(`{"conn":"%s"}`, strings.Trim(Cfg.MustValue("cache", "HOST"), "\" ")) | |||
| default: | |||
| log.Fatal("Unknown cache adapter: %s", CacheAdapter) | |||
| log.Fatal(4, "Unknown cache adapter: %s", CacheAdapter) | |||
| } | |||
| var err error | |||
| Cache, err = cache.NewCache(CacheAdapter, CacheConfig) | |||
| if err != nil { | |||
| log.Fatal("Init cache system failed, adapter: %s, config: %s, %v\n", | |||
| log.Fatal(4, "Init cache system failed, adapter: %s, config: %s, %v\n", | |||
| CacheAdapter, CacheConfig, err) | |||
| } | |||
| @@ -391,12 +400,12 @@ func newSessionService() { | |||
| []string{"memory", "file", "redis", "mysql"}) | |||
| SessionConfig = new(session.Config) | |||
| SessionConfig.ProviderConfig = Cfg.MustValue("session", "PROVIDER_CONFIG") | |||
| SessionConfig.ProviderConfig = strings.Trim(Cfg.MustValue("session", "PROVIDER_CONFIG"), "\" ") | |||
| SessionConfig.CookieName = Cfg.MustValue("session", "COOKIE_NAME", "i_like_gogits") | |||
| SessionConfig.CookieSecure = Cfg.MustBool("session", "COOKIE_SECURE") | |||
| SessionConfig.Secure = Cfg.MustBool("session", "COOKIE_SECURE") | |||
| SessionConfig.EnableSetCookie = Cfg.MustBool("session", "ENABLE_SET_COOKIE", true) | |||
| SessionConfig.GcIntervalTime = Cfg.MustInt64("session", "GC_INTERVAL_TIME", 86400) | |||
| SessionConfig.SessionLifeTime = Cfg.MustInt64("session", "SESSION_LIFE_TIME", 86400) | |||
| SessionConfig.Gclifetime = Cfg.MustInt64("session", "GC_INTERVAL_TIME", 86400) | |||
| SessionConfig.Maxlifetime = Cfg.MustInt64("session", "SESSION_LIFE_TIME", 86400) | |||
| SessionConfig.SessionIDHashFunc = Cfg.MustValueRange("session", "SESSION_ID_HASHFUNC", | |||
| "sha1", []string{"sha1", "sha256", "md5"}) | |||
| SessionConfig.SessionIDHashKey = Cfg.MustValue("session", "SESSION_ID_HASHKEY") | |||
| @@ -405,14 +414,6 @@ func newSessionService() { | |||
| os.MkdirAll(path.Dir(SessionConfig.ProviderConfig), os.ModePerm) | |||
| } | |||
| var err error | |||
| SessionManager, err = session.NewManager(SessionProvider, *SessionConfig) | |||
| if err != nil { | |||
| log.Fatal("Init session system failed, provider: %s, %v", | |||
| SessionProvider, err) | |||
| } | |||
| go SessionManager.GC() | |||
| log.Info("Session Service Enabled") | |||
| } | |||
| @@ -494,4 +495,5 @@ func NewServices() { | |||
| newRegisterMailService() | |||
| newNotifyMailService() | |||
| newWebhookService() | |||
| // ssh.Listen("2022") | |||
| } | |||
| @@ -0,0 +1,119 @@ | |||
| // 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. | |||
| // Prototype, git client looks like do not recognize req.Reply. | |||
| package ssh | |||
| import ( | |||
| "fmt" | |||
| "io/ioutil" | |||
| "net" | |||
| "os" | |||
| "os/exec" | |||
| "strings" | |||
| "code.google.com/p/go.crypto/ssh" | |||
| "github.com/Unknwon/com" | |||
| "github.com/gogits/gogs/modules/log" | |||
| ) | |||
| func handleServerConn(keyId string, chans <-chan ssh.NewChannel) { | |||
| for newChan := range chans { | |||
| if newChan.ChannelType() != "session" { | |||
| newChan.Reject(ssh.UnknownChannelType, "unknown channel type") | |||
| continue | |||
| } | |||
| channel, requests, err := newChan.Accept() | |||
| if err != nil { | |||
| log.Error(3, "Could not accept channel: %v", err) | |||
| continue | |||
| } | |||
| go func(in <-chan *ssh.Request) { | |||
| defer channel.Close() | |||
| for req := range in { | |||
| ok, payload := false, strings.TrimLeft(string(req.Payload), "\x00") | |||
| fmt.Println("Request:", req.Type, req.WantReply, payload) | |||
| switch req.Type { | |||
| case "env": | |||
| args := strings.Split(strings.Replace(payload, "\x00", "", -1), "\v") | |||
| if len(args) != 2 { | |||
| break | |||
| } | |||
| args[0] = strings.TrimLeft(args[0], "\x04") | |||
| _, _, err := com.ExecCmdBytes("env", args[0]+"="+args[1]) | |||
| if err != nil { | |||
| log.Error(3, "env: %v", err) | |||
| channel.Stderr().Write([]byte(err.Error())) | |||
| break | |||
| } | |||
| ok = true | |||
| case "exec": | |||
| os.Setenv("SSH_ORIGINAL_COMMAND", strings.TrimLeft(payload, "'(")) | |||
| log.Info("Payload: %v", strings.TrimLeft(payload, "'(")) | |||
| cmd := exec.Command("/Users/jiahuachen/Applications/Go/src/github.com/gogits/gogs-ng/gogs-ng", "serv", "key-"+keyId) | |||
| cmd.Stdout = channel | |||
| cmd.Stdin = channel | |||
| cmd.Stderr = channel.Stderr() | |||
| if err := cmd.Run(); err != nil { | |||
| log.Error(3, "exec: %v", err) | |||
| } else { | |||
| ok = true | |||
| } | |||
| } | |||
| fmt.Println("Done:", ok) | |||
| req.Reply(ok, nil) // BUG: Git on Mac seems not know this reply and hang? | |||
| } | |||
| fmt.Println("Done!!!") | |||
| }(requests) | |||
| } | |||
| } | |||
| func listen(config *ssh.ServerConfig, port string) { | |||
| listener, err := net.Listen("tcp", "0.0.0.0:"+port) | |||
| if err != nil { | |||
| panic(err) | |||
| } | |||
| for { | |||
| // Once a ServerConfig has been configured, connections can be accepted. | |||
| conn, err := listener.Accept() | |||
| if err != nil { | |||
| log.Error(3, "Fail to accept incoming connection: %v", err) | |||
| continue | |||
| } | |||
| // Before use, a handshake must be performed on the incoming net.Conn. | |||
| sConn, chans, reqs, err := ssh.NewServerConn(conn, config) | |||
| if err != nil { | |||
| log.Error(3, "Fail to handshake: %v", err) | |||
| continue | |||
| } | |||
| // The incoming Request channel must be serviced. | |||
| go ssh.DiscardRequests(reqs) | |||
| go handleServerConn(sConn.Permissions.Extensions["key-id"], chans) | |||
| } | |||
| } | |||
| // Listen starts a SSH server listens on given port. | |||
| func Listen(port string) { | |||
| config := &ssh.ServerConfig{ | |||
| PublicKeyCallback: func(conn ssh.ConnMetadata, key ssh.PublicKey) (*ssh.Permissions, error) { | |||
| // keyCache[string(ssh.MarshalAuthorizedKey(key))] = 2 | |||
| return &ssh.Permissions{Extensions: map[string]string{"key-id": "2"}}, nil | |||
| }, | |||
| } | |||
| privateBytes, err := ioutil.ReadFile("/Users/jiahuachen/.ssh/id_rsa") | |||
| if err != nil { | |||
| panic("failed to load private key") | |||
| } | |||
| private, err := ssh.ParsePrivateKey(privateBytes) | |||
| if err != nil { | |||
| panic("failed to parse private key") | |||
| } | |||
| config.AddHostKey(private) | |||
| go listen(config, port) | |||
| } | |||
| @@ -0,0 +1 @@ | |||
| .hljs{display:block;overflow-x:auto;padding:.5em;color:#333;background:#f8f8f8}.hljs-comment,.hljs-template_comment,.diff .hljs-header,.hljs-javadoc{color:#998;font-style:italic}.hljs-keyword,.css .rule .hljs-keyword,.hljs-winutils,.javascript .hljs-title,.nginx .hljs-title,.hljs-subst,.hljs-request,.hljs-status{color:#333;font-weight:bold}.hljs-number,.hljs-hexcolor,.ruby .hljs-constant{color:#099}.hljs-string,.hljs-tag .hljs-value,.hljs-phpdoc,.tex .hljs-formula{color:#d14}.hljs-title,.hljs-id,.coffeescript .hljs-params,.scss .hljs-preprocessor{color:#900;font-weight:bold}.javascript .hljs-title,.lisp .hljs-title,.clojure .hljs-title,.hljs-subst{font-weight:normal}.hljs-class .hljs-title,.haskell .hljs-type,.vhdl .hljs-literal,.tex .hljs-command{color:#458;font-weight:bold}.hljs-tag,.hljs-tag .hljs-title,.hljs-rules .hljs-property,.django .hljs-tag .hljs-keyword{color:#000080;font-weight:normal}.hljs-attribute,.hljs-variable,.lisp .hljs-body{color:#008080}.hljs-regexp{color:#009926}.hljs-symbol,.ruby .hljs-symbol .hljs-string,.lisp .hljs-keyword,.tex .hljs-special,.hljs-prompt{color:#990073}.hljs-built_in,.lisp .hljs-title,.clojure .hljs-built_in{color:#0086b3}.hljs-preprocessor,.hljs-pragma,.hljs-pi,.hljs-doctype,.hljs-shebang,.hljs-cdata{color:#999;font-weight:bold}.hljs-deletion{background:#fdd}.hljs-addition{background:#dfd}.diff .hljs-change{background:#0086b3}.hljs-chunk{color:#aaa} | |||
| @@ -1836,4 +1836,11 @@ body { | |||
| #issue-create-form #attached { | |||
| margin-bottom: 0; | |||
| } | |||
| #submit-error { | |||
| display: none; | |||
| padding: 10px 15px 15px 15px; | |||
| font-weight: bold; | |||
| text-align: center; | |||
| } | |||
| @@ -520,6 +520,50 @@ function initIssue() { | |||
| }); | |||
| }()); | |||
| // store unsend text in session storage. | |||
| (function() { | |||
| var $textArea = $("#issue-content,#issue-reply-content"); | |||
| var current = ""; | |||
| if ($textArea == null || !('sessionStorage' in window)) { | |||
| return; | |||
| } | |||
| var path = location.pathname.split("/"); | |||
| var key = "issue-" + path[1] + "-" + path[2] + "-"; | |||
| if (/\/issues\/\d+$/.test(location.pathname)) { | |||
| key = key + path[4]; | |||
| } else { | |||
| key = key + "new"; | |||
| } | |||
| if ($textArea.val() !== undefined && $textArea.val() !== "") { | |||
| sessionStorage.setItem(key, $textArea.val()); | |||
| } else { | |||
| $textArea.val(sessionStorage.getItem(key) || ""); | |||
| if ($textArea.attr("id") == "issue-reply-content") { | |||
| var $closeBtn = $('#issue-close-btn'); | |||
| var $openBtn = $('#issue-open-btn'); | |||
| if ($textArea.val().length) { | |||
| $closeBtn.val($closeBtn.data("text")); | |||
| $openBtn.val($openBtn.data("text")); | |||
| } else { | |||
| $closeBtn.val($closeBtn.data("origin")); | |||
| $openBtn.val($openBtn.data("origin")); | |||
| } | |||
| } | |||
| } | |||
| $textArea.on("keyup", function() { | |||
| if ($textArea.val() !== current) { | |||
| sessionStorage.setItem(key, current = $textArea.val()); | |||
| } | |||
| }); | |||
| }()); | |||
| // Preview for images. | |||
| (function() { | |||
| var $hoverElement = $("<div></div>"); | |||
| @@ -536,7 +580,7 @@ function initIssue() { | |||
| var over = function() { | |||
| var $this = $(this); | |||
| if ($this.text().match(/\.(png|jpg|jpeg|gif)$/i) == false) { | |||
| if ((/\.(png|jpg|jpeg|gif)$/i).test($this.text()) == false) { | |||
| return; | |||
| } | |||
| @@ -579,23 +623,135 @@ function initIssue() { | |||
| var $attachedList = $("#attached-list"); | |||
| var $addButton = $("#attachments-button"); | |||
| var fileInput = $("#attachments-input")[0]; | |||
| var files = []; | |||
| fileInput.addEventListener("change", function(event) { | |||
| $attachedList.empty(); | |||
| $attachedList.append("<b>Attachments:</b> "); | |||
| var fileInput = document.getElementById("attachments-input"); | |||
| if (fileInput === null) { | |||
| return; | |||
| } | |||
| $attachedList.on("click", "span.attachment-remove", function(event) { | |||
| var $parent = $(this).parent(); | |||
| files.splice($parent.data("index"), 1); | |||
| $parent.remove(); | |||
| }); | |||
| var clickedButton = undefined; | |||
| $("button,input[type=\"submit\"]", fileInput.form).on("click", function() { | |||
| clickedButton = this; | |||
| var $button = $(this); | |||
| $button.removeClass("btn-success"); | |||
| $button.addClass("btn-warning"); | |||
| $button.text("Submiting..."); | |||
| }); | |||
| fileInput.form.addEventListener("submit", function(event) { | |||
| event.stopImmediatePropagation(); | |||
| event.preventDefault(); | |||
| //var data = new FormData(this); | |||
| // Internet Explorer ... -_- | |||
| var data = new FormData(); | |||
| $.each($("[name]", this), function(i, e) { | |||
| if (e.name == "attachments" || e.type == "submit") { | |||
| return; | |||
| } | |||
| data.append(e.name, $(e).val()); | |||
| }); | |||
| data.append(clickedButton.name, $(clickedButton).val()); | |||
| files.forEach(function(file) { | |||
| data.append("attachments", file); | |||
| }); | |||
| var xhr = new XMLHttpRequest(); | |||
| xhr.addEventListener("error", function() { | |||
| debugger; | |||
| }); | |||
| xhr.addEventListener("load", function() { | |||
| var response = xhr.response; | |||
| if (typeof response == "string") { | |||
| try { | |||
| response = JSON.parse(response); | |||
| } catch (err) { | |||
| response = { ok: false, error: "Could not parse JSON" }; | |||
| } | |||
| } | |||
| if (response.ok === false) { | |||
| $("#submit-error").text(response.error); | |||
| $("#submit-error").show(); | |||
| var $button = $(clickedButton); | |||
| $button.removeClass("btn-warning"); | |||
| $button.addClass("btn-danger"); | |||
| $button.text("An error encoured!") | |||
| return; | |||
| } | |||
| if (!('sessionStorage' in window)) { | |||
| return; | |||
| } | |||
| var path = location.pathname.split("/"); | |||
| var key = "issue-" + path[1] + "-" + path[2] + "-"; | |||
| if (/\/issues\/\d+$/.test(location.pathname)) { | |||
| key = key + path[4]; | |||
| } else { | |||
| key = key + "new"; | |||
| } | |||
| sessionStorage.removeItem(key); | |||
| window.location.href = response.data; | |||
| }); | |||
| xhr.open("POST", this.action, true); | |||
| xhr.send(data); | |||
| return false; | |||
| }); | |||
| fileInput.addEventListener("change", function(event) { | |||
| for (var index = 0; index < fileInput.files.length; index++) { | |||
| var file = fileInput.files[index]; | |||
| if (files.indexOf(file) > -1) { | |||
| continue; | |||
| } | |||
| var $span = $("<span></span>"); | |||
| $span.addClass("label"); | |||
| $span.addClass("label-default"); | |||
| $span.append(file.name.toLowerCase()); | |||
| $span.data("index", files.length); | |||
| $span.append(file.name); | |||
| $span.append(" <span class=\"attachment-remove fa fa-times-circle\"></span>"); | |||
| $attachedList.append($span); | |||
| files.push(file); | |||
| } | |||
| this.value = ""; | |||
| }); | |||
| $addButton.on("click", function() { | |||
| @@ -828,11 +984,17 @@ function initIssue() { | |||
| $(item).addClass("no-checked"); | |||
| $("#label-" + id, $labels).remove(); | |||
| if ($labels.children(".label-item").length == 0) { | |||
| $labels.append("<p>None yet</p>"); | |||
| } | |||
| } else { | |||
| $(item).prepend('<span class="check pull-left"><i class="fa fa-check"></i></span>'); | |||
| $(item).removeClass("no-checked"); | |||
| $(item).addClass("checked"); | |||
| $("p:not([class])", $labels).remove(); | |||
| var $l = $("<p></p>"); | |||
| var c = $("span.color", item).css("background-color"); | |||