func (this *service) ServeHTTP(w http.ResponseWriter, r *http.Request) { urlPath := r.URL.Path hash := urlPath[strings.LastIndex(urlPath, "/")+1:] size := this.mustInt(r, 80, "s", "size") // default size = 80*80 avatar := New(hash, this.cacheDir) avatar.AlterImage = this.altImage if avatar.Expired() { if err := avatar.UpdateTimeout(time.Millisecond * 1000); err != nil { log.Trace("avatar update error: %v", err) return } } if modtime, err := avatar.Modtime(); err == nil { etag := fmt.Sprintf("size(%d)", size) if t, err := time.Parse(http.TimeFormat, r.Header.Get("If-Modified-Since")); err == nil && modtime.Before(t.Add(1*time.Second)) && etag == r.Header.Get("If-None-Match") { h := w.Header() delete(h, "Content-Type") delete(h, "Content-Length") w.WriteHeader(http.StatusNotModified) return } w.Header().Set("Last-Modified", modtime.UTC().Format(http.TimeFormat)) w.Header().Set("ETag", etag) } w.Header().Set("Content-Type", "image/jpeg") if err := avatar.Encode(w, size); err != nil { log.Warn("avatar encode error: %v", err) w.WriteHeader(500) } }
// GitFsck calls 'git fsck' to check repository health. func GitFsck() { if isGitFscking { return } isGitFscking = true defer func() { isGitFscking = false }() log.Trace("Doing: GitFsck") args := append([]string{"fsck"}, setting.Cron.RepoHealthCheck.Args...) if err := x.Where("id>0").Iterate(new(Repository), func(idx int, bean interface{}) error { repo := bean.(*Repository) repoPath, err := repo.RepoPath() if err != nil { return fmt.Errorf("RepoPath: %v", err) } _, _, err = process.ExecDir(-1, repoPath, "Repository health check", "git", args...) if err != nil { desc := fmt.Sprintf("Fail to health check repository(%s)", repoPath) log.Warn(desc) if err = CreateRepositoryNotice(desc); err != nil { log.Error(4, "CreateRepositoryNotice: %v", err) } } return nil }); err != nil { log.Error(4, "GitFsck: %v", err) } }
func newNotifyMailService() { if !Cfg.Section("service").Key("ENABLE_NOTIFY_MAIL").MustBool() { return } else if MailService == nil { log.Warn("Notify Mail Service: Mail Service is not enabled") return } Service.EnableNotifyMail = true log.Info("Notify Mail Service Enabled") }
func newRegisterMailService() { if !Cfg.Section("service").Key("REGISTER_EMAIL_CONFIRM").MustBool() { return } else if MailService == nil { log.Warn("Register Mail Service: Mail Service is not enabled") return } Service.RegisterEmailConfirm = true log.Info("Register Mail Service Enabled") }
func ParsePatch(pid int64, maxlines int, cmd *exec.Cmd, reader io.Reader) (*Diff, error) { scanner := bufio.NewScanner(reader) var ( curFile *DiffFile curSection = &DiffSection{ Lines: make([]*DiffLine, 0, 10), } leftLine, rightLine int isTooLong bool // FIXME: Should use cache in the future. buf bytes.Buffer ) diff := &Diff{Files: make([]*DiffFile, 0)} var i int for scanner.Scan() { line := scanner.Text() // fmt.Println(i, line) if strings.HasPrefix(line, "+++ ") || strings.HasPrefix(line, "--- ") { continue } if line == "" { continue } i = i + 1 // Diff data too large, we only show the first about maxlines lines if i == maxlines { isTooLong = true log.Warn("Diff data too large") } switch { case line[0] == ' ': diffLine := &DiffLine{Type: DIFF_LINE_PLAIN, Content: line, LeftIdx: leftLine, RightIdx: rightLine} leftLine++ rightLine++ curSection.Lines = append(curSection.Lines, diffLine) continue case line[0] == '@': if isTooLong { break } curSection = &DiffSection{} curFile.Sections = append(curFile.Sections, curSection) ss := strings.Split(line, "@@") diffLine := &DiffLine{Type: DIFF_LINE_SECTION, Content: line} curSection.Lines = append(curSection.Lines, diffLine) // Parse line number. ranges := strings.Split(ss[1][1:], " ") leftLine, _ = com.StrTo(strings.Split(ranges[0], ",")[0][1:]).Int() if len(ranges) > 1 { rightLine, _ = com.StrTo(strings.Split(ranges[1], ",")[0]).Int() } else { log.Warn("Parse line number failed: %v", line) rightLine = leftLine } continue case line[0] == '+': curFile.Addition++ diff.TotalAddition++ diffLine := &DiffLine{Type: DIFF_LINE_ADD, Content: line, RightIdx: rightLine} rightLine++ curSection.Lines = append(curSection.Lines, diffLine) continue case line[0] == '-': curFile.Deletion++ diff.TotalDeletion++ diffLine := &DiffLine{Type: DIFF_LINE_DEL, Content: line, LeftIdx: leftLine} if leftLine > 0 { leftLine++ } curSection.Lines = append(curSection.Lines, diffLine) case strings.HasPrefix(line, "Binary"): curFile.IsBin = true continue } // Get new file. if strings.HasPrefix(line, DIFF_HEAD) { if isTooLong { break } beg := len(DIFF_HEAD) a := line[beg : (len(line)-beg)/2+beg] // In case file name is surrounded by double quotes(it happens only in git-shell). if a[0] == '"' { a = a[1 : len(a)-1] a = strings.Replace(a, `\"`, `"`, -1) } curFile = &DiffFile{ Name: a[strings.Index(a, "/")+1:], Index: len(diff.Files) + 1, Type: DIFF_FILE_CHANGE, Sections: make([]*DiffSection, 0, 10), } diff.Files = append(diff.Files, curFile) // Check file diff type. for scanner.Scan() { switch { case strings.HasPrefix(scanner.Text(), "new file"): curFile.Type = DIFF_FILE_ADD curFile.IsDeleted = false curFile.IsCreated = true case strings.HasPrefix(scanner.Text(), "deleted"): curFile.Type = DIFF_FILE_DEL curFile.IsCreated = false curFile.IsDeleted = true case strings.HasPrefix(scanner.Text(), "index"): curFile.Type = DIFF_FILE_CHANGE curFile.IsCreated = false curFile.IsDeleted = false } if curFile.Type > 0 { break } } } } for _, f := range diff.Files { buf.Reset() for _, sec := range f.Sections { for _, l := range sec.Lines { buf.WriteString(l.Content) buf.WriteString("\n") } } charsetLabel, err := base.DetectEncoding(buf.Bytes()) if charsetLabel != "UTF-8" && err == nil { encoding, _ := charset.Lookup(charsetLabel) if encoding != nil { d := encoding.NewDecoder() for _, sec := range f.Sections { for _, l := range sec.Lines { if c, _, err := transform.String(d, l.Content); err == nil { l.Content = c } } } } } } return diff, nil }
// DeleteRepository deletes a repository for a user or organization. func DeleteRepository(uid, repoID int64) error { repo := &Repository{ID: repoID, OwnerID: uid} has, err := x.Get(repo) if err != nil { return err } else if !has { return ErrRepoNotExist{repoID, uid, ""} } // In case is a organization. org, err := GetUserByID(uid) if err != nil { return err } if org.IsOrganization() { if err = org.GetTeams(); err != nil { return err } } sess := x.NewSession() defer sessionRelease(sess) if err = sess.Begin(); err != nil { return err } if org.IsOrganization() { for _, t := range org.Teams { if !t.hasRepository(sess, repoID) { continue } else if err = t.removeRepository(sess, repo, false); err != nil { return err } } } if _, err = sess.Delete(&Repository{ID: repoID}); err != nil { return err } else if _, err = sess.Delete(&Access{RepoID: repo.ID}); err != nil { return err } else if _, err = sess.Delete(&Action{RepoID: repo.ID}); err != nil { return err } else if _, err = sess.Delete(&Watch{RepoID: repoID}); err != nil { return err } else if _, err = sess.Delete(&Mirror{RepoID: repoID}); err != nil { return err } else if _, err = sess.Delete(&IssueUser{RepoID: repoID}); err != nil { return err } else if _, err = sess.Delete(&Milestone{RepoID: repoID}); err != nil { return err } else if _, err = sess.Delete(&Release{RepoId: repoID}); err != nil { return err } else if _, err = sess.Delete(&Collaboration{RepoID: repoID}); err != nil { return err } // Delete comments and attachments. issues := make([]*Issue, 0, 25) attachmentPaths := make([]string, 0, len(issues)) if err = sess.Where("repo_id=?", repoID).Find(&issues); err != nil { return err } for i := range issues { if _, err = sess.Delete(&Comment{IssueID: issues[i].ID}); err != nil { return err } attachments := make([]*Attachment, 0, 5) if err = sess.Where("issue_id=?", issues[i].ID).Find(&attachments); err != nil { return err } for j := range attachments { attachmentPaths = append(attachmentPaths, attachments[j].LocalPath()) } if _, err = sess.Delete(&Attachment{IssueID: issues[i].ID}); err != nil { return err } } if _, err = sess.Delete(&Issue{RepoID: repoID}); err != nil { return err } if repo.IsFork { if _, err = sess.Exec("UPDATE `repository` SET num_forks=num_forks-1 WHERE id=?", repo.ForkID); err != nil { return fmt.Errorf("decrease fork count: %v", err) } else if _, err = sess.Delete(&ForkInfo{RepoID: repo.ID}); err != nil { return fmt.Errorf("delete fork info: %v", err) } } if _, err = sess.Exec("UPDATE `user` SET num_repos=num_repos-1 WHERE id=?", uid); err != nil { return err } // Remove repository files. repoPath, err := repo.RepoPath() if err != nil { return fmt.Errorf("RepoPath: %v", err) } if err = os.RemoveAll(repoPath); err != nil { desc := fmt.Sprintf("delete repository files[%s]: %v", repoPath, err) log.Warn(desc) if err = CreateRepositoryNotice(desc); err != nil { log.Error(4, "add notice: %v", err) } } // Remove attachment files. for i := range attachmentPaths { if err = os.Remove(attachmentPaths[i]); err != nil { log.Warn("delete attachment: %v", err) } } if err = sess.Commit(); err != nil { return fmt.Errorf("Commit: %v", err) } if repo.NumForks > 0 { if repo.IsPrivate { forkRepos, err := GetRepositoriesByForkID(repo.ID) if err != nil { return fmt.Errorf("getRepositoriesByForkID: %v", err) } for i := range forkRepos { if err = DeleteRepository(forkRepos[i].OwnerID, forkRepos[i].ID); err != nil { log.Error(4, "updateRepository[%d]: %v", forkRepos[i].ID, err) } } } else { if _, err = x.Exec("UPDATE `repository` SET fork_id=0,is_fork=? WHERE fork_id=?", false, repo.ID); err != nil { log.Error(4, "reset 'fork_id' and 'is_fork': %v", err) } if _, err = x.Delete(&ForkInfo{ForkID: repo.ID}); err != nil { log.Error(4, "clear fork infos: %v", err) } } } return nil }
// NewConfigContext initializes configuration context. // NOTE: do not print any log except error. func NewConfigContext() { workDir, err := WorkDir() if err != nil { log.Fatal(4, "Fail to get work directory: %v", err) } Cfg, err = ini.Load(bindata.MustAsset("conf/app.ini")) if err != nil { log.Fatal(4, "Fail to parse 'conf/app.ini': %v", err) } CustomPath = os.Getenv("GOGS_CUSTOM") if len(CustomPath) == 0 { CustomPath = workDir + "/custom" } if len(CustomConf) == 0 { CustomConf = CustomPath + "/conf/app.ini" } if com.IsFile(CustomConf) { if err = Cfg.Append(CustomConf); err != nil { log.Fatal(4, "Fail to load custom conf '%s': %v", CustomConf, err) } } else { log.Warn("Custom config (%s) not found, ignore this if you're running first time", CustomConf) } Cfg.NameMapper = ini.AllCapsUnderscore LogRootPath = Cfg.Section("log").Key("ROOT_PATH").MustString(path.Join(workDir, "log")) forcePathSeparator(LogRootPath) sec := Cfg.Section("server") AppName = Cfg.Section("").Key("APP_NAME").MustString("Gogs: Go Git Service") AppUrl = sec.Key("ROOT_URL").MustString("http://localhost:3000/") if AppUrl[len(AppUrl)-1] != '/' { AppUrl += "/" } // Check if has app suburl. url, err := url.Parse(AppUrl) if err != nil { log.Fatal(4, "Invalid ROOT_URL(%s): %s", AppUrl, err) } AppSubUrl = strings.TrimSuffix(url.Path, "/") Protocol = HTTP if sec.Key("PROTOCOL").String() == "https" { Protocol = HTTPS CertFile = sec.Key("CERT_FILE").String() KeyFile = sec.Key("KEY_FILE").String() } else if sec.Key("PROTOCOL").String() == "fcgi" { Protocol = FCGI } Domain = sec.Key("DOMAIN").MustString("localhost") HttpAddr = sec.Key("HTTP_ADDR").MustString("0.0.0.0") HttpPort = sec.Key("HTTP_PORT").MustString("3000") DisableSSH = sec.Key("DISABLE_SSH").MustBool() SSHDomain = sec.Key("SSH_DOMAIN").MustString(Domain) SSHPort = sec.Key("SSH_PORT").MustInt(22) OfflineMode = sec.Key("OFFLINE_MODE").MustBool() DisableRouterLog = sec.Key("DISABLE_ROUTER_LOG").MustBool() StaticRootPath = sec.Key("STATIC_ROOT_PATH").MustString(workDir) EnableGzip = sec.Key("ENABLE_GZIP").MustBool() switch sec.Key("LANDING_PAGE").MustString("home") { case "explore": LandingPageUrl = LANDING_PAGE_EXPLORE default: LandingPageUrl = LANDING_PAGE_HOME } sec = Cfg.Section("security") InstallLock = sec.Key("INSTALL_LOCK").MustBool() SecretKey = sec.Key("SECRET_KEY").String() LogInRememberDays = sec.Key("LOGIN_REMEMBER_DAYS").MustInt() CookieUserName = sec.Key("COOKIE_USERNAME").String() CookieRememberName = sec.Key("COOKIE_REMEMBER_NAME").String() ReverseProxyAuthUser = sec.Key("REVERSE_PROXY_AUTHENTICATION_USER").MustString("X-WEBAUTH-USER") sec = Cfg.Section("attachment") AttachmentPath = sec.Key("PATH").MustString("data/attachments") if !filepath.IsAbs(AttachmentPath) { AttachmentPath = path.Join(workDir, AttachmentPath) } AttachmentAllowedTypes = strings.Replace(sec.Key("ALLOWED_TYPES").MustString("image/jpeg,image/png"), "|", ",", -1) AttachmentMaxSize = sec.Key("MAX_SIZE").MustInt64(32) AttachmentMaxFiles = sec.Key("MAX_FILES").MustInt(5) AttachmentEnabled = sec.Key("ENABLE").MustBool(true) TimeFormat = map[string]string{ "ANSIC": time.ANSIC, "UnixDate": time.UnixDate, "RubyDate": time.RubyDate, "RFC822": time.RFC822, "RFC822Z": time.RFC822Z, "RFC850": time.RFC850, "RFC1123": time.RFC1123, "RFC1123Z": time.RFC1123Z, "RFC3339": time.RFC3339, "RFC3339Nano": time.RFC3339Nano, "Kitchen": time.Kitchen, "Stamp": time.Stamp, "StampMilli": time.StampMilli, "StampMicro": time.StampMicro, "StampNano": time.StampNano, }[Cfg.Section("time").Key("FORMAT").MustString("RFC1123")] RunUser = Cfg.Section("").Key("RUN_USER").String() curUser := user.CurrentUsername() // Does not check run user when the install lock is off. if InstallLock && RunUser != curUser { log.Fatal(4, "Expect user(%s) but current user is: %s", RunUser, curUser) } // Determine and create root git repository path. homeDir, err := com.HomeDir() if err != nil { log.Fatal(4, "Fail to get home directory: %v", err) } homeDir = strings.Replace(homeDir, "\\", "/", -1) sec = Cfg.Section("repository") RepoRootPath = sec.Key("ROOT").MustString(path.Join(homeDir, "gogs-repositories")) forcePathSeparator(RepoRootPath) if !filepath.IsAbs(RepoRootPath) { RepoRootPath = path.Join(workDir, RepoRootPath) } else { RepoRootPath = path.Clean(RepoRootPath) } ScriptType = sec.Key("SCRIPT_TYPE").MustString("bash") AnsiCharset = sec.Key("ANSI_CHARSET").MustString("") // UI settings. sec = Cfg.Section("ui") ExplorePagingNum = sec.Key("EXPLORE_PAGING_NUM").MustInt(20) IssuePagingNum = sec.Key("ISSUE_PAGING_NUM").MustInt(10) sec = Cfg.Section("picture") PictureService = sec.Key("SERVICE").In("server", []string{"server"}) AvatarUploadPath = sec.Key("AVATAR_UPLOAD_PATH").MustString("data/avatars") forcePathSeparator(AvatarUploadPath) if !filepath.IsAbs(AvatarUploadPath) { AvatarUploadPath = path.Join(workDir, AvatarUploadPath) } switch source := sec.Key("GRAVATAR_SOURCE").MustString("gravatar"); source { case "duoshuo": GravatarSource = "http://gravatar.duoshuo.com/avatar/" case "gravatar": GravatarSource = "//1.gravatar.com/avatar/" default: GravatarSource = source } DisableGravatar = sec.Key("DISABLE_GRAVATAR").MustBool() if OfflineMode { DisableGravatar = true } if err = Cfg.Section("markdown").MapTo(&Markdown); err != nil { log.Fatal(4, "Fail to map Markdown settings: %v", err) } else if err = Cfg.Section("git").MapTo(&Git); err != nil { log.Fatal(4, "Fail to map Git settings: %v", err) } else if Cfg.Section("cron").MapTo(&Cron); err != nil { log.Fatal(4, "Fail to map Cron settings: %v", err) } Langs = Cfg.Section("i18n").Key("LANGS").Strings(",") Names = Cfg.Section("i18n").Key("NAMES").Strings(",") dateLangs = Cfg.Section("i18n.datelang").KeysHash() ShowFooterBranding = Cfg.Section("other").Key("SHOW_FOOTER_BRANDING").MustBool() HasRobotsTxt = com.IsFile(path.Join(CustomPath, "robots.txt")) }
// UserSignIn validates user name and password. func UserSignIn(uname, passwd string) (*User, error) { u := new(User) if strings.Contains(uname, "@") { u = &User{Email: uname} } else { u = &User{LowerName: strings.ToLower(uname)} } has, err := x.Get(u) if err != nil { return nil, err } if u.LoginType == NOTYPE && has { u.LoginType = PLAIN } // For plain login, user must exist to reach this line. // Now verify password. if u.LoginType == PLAIN { if !u.ValidatePassword(passwd) { return nil, ErrUserNotExist{u.Id, u.Name} } return u, nil } if !has { var sources []LoginSource if err = x.UseBool().Find(&sources, &LoginSource{IsActived: true, AllowAutoRegister: true}); err != nil { return nil, err } for _, source := range sources { if source.Type == LDAP { u, err := LoginUserLdapSource(nil, uname, passwd, source.ID, source.Cfg.(*LDAPConfig), true) if err == nil { return u, nil } log.Warn("Fail to login(%s) by LDAP(%s): %v", uname, source.Name, err) } else if source.Type == SMTP { u, err := LoginUserSMTPSource(nil, uname, passwd, source.ID, source.Cfg.(*SMTPConfig), true) if err == nil { return u, nil } log.Warn("Fail to login(%s) by SMTP(%s): %v", uname, source.Name, err) } else if source.Type == PAM { u, err := LoginUserPAMSource(nil, uname, passwd, source.ID, source.Cfg.(*PAMConfig), true) if err == nil { return u, nil } log.Warn("Fail to login(%s) by PAM(%s): %v", uname, source.Name, err) } } return nil, ErrUserNotExist{u.Id, u.Name} } var source LoginSource hasSource, err := x.Id(u.LoginSource).Get(&source) if err != nil { return nil, err } else if !hasSource { return nil, ErrLoginSourceNotExist } else if !source.IsActived { return nil, ErrLoginSourceNotActived } switch u.LoginType { case LDAP: return LoginUserLdapSource(u, u.LoginName, passwd, source.ID, source.Cfg.(*LDAPConfig), false) case SMTP: return LoginUserSMTPSource(u, u.LoginName, passwd, source.ID, source.Cfg.(*SMTPConfig), false) case PAM: return LoginUserPAMSource(u, u.LoginName, passwd, source.ID, source.Cfg.(*PAMConfig), false) } return nil, ErrUnsupportedLoginType }