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") }
// rewriteAuthorizedKeys finds and deletes corresponding line in authorized_keys file. func rewriteAuthorizedKeys(key *PublicKey, p, tmpP string) error { fr, err := os.Open(p) if err != nil { return err } defer fr.Close() fw, err := os.OpenFile(tmpP, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0600) if err != nil { return err } defer fw.Close() isFound := false keyword := fmt.Sprintf("key-%d", key.ID) buf := bufio.NewReader(fr) for { line, errRead := buf.ReadString('\n') line = strings.TrimSpace(line) if errRead != nil { if errRead != io.EOF { return errRead } // Reached end of file, if nothing to read then break, // otherwise handle the last line. if len(line) == 0 { break } } // Found the line and copy rest of file. if !isFound && strings.Contains(line, keyword) && strings.Contains(line, key.Content) { isFound = true continue } // Still finding the line, copy the line that currently read. if _, err = fw.WriteString(line + "\n"); err != nil { return err } if errRead == io.EOF { break } } if !isFound { log.Warn("SSH key %d not found in authorized_keys file for deletion", key.ID) } return nil }
// UserSignIn validates user name and password. func UserSignIn(uname, passwd string) (*User, error) { var u *User if strings.Contains(uname, "@") { u = &User{Email: strings.ToLower(uname)} } else { u = &User{LowerName: strings.ToLower(uname)} } userExists, err := x.Get(u) if err != nil { return nil, err } if userExists { switch u.LoginType { case LOGIN_NOTYPE, LOGIN_PLAIN: if u.ValidatePassword(passwd) { return u, nil } return nil, ErrUserNotExist{u.Id, u.Name} default: var source LoginSource hasSource, err := x.Id(u.LoginSource).Get(&source) if err != nil { return nil, err } else if !hasSource { return nil, ErrLoginSourceNotExist } return ExternalUserLogin(u, u.LoginName, passwd, &source, false) } } var sources []LoginSource if err = x.UseBool().Find(&sources, &LoginSource{IsActived: true}); err != nil { return nil, err } for _, source := range sources { u, err := ExternalUserLogin(nil, uname, passwd, &source, true) if err == nil { return u, nil } log.Warn("Failed to login '%s' via '%s': %v", uname, source.Name, err) } return nil, ErrUserNotExist{u.Id, u.Name} }
// RemoveAllWithNotice removes all directories in given path and // creates a system notice when error occurs. func RemoveAllWithNotice(title, path string) { var err error if setting.IsWindows { // usually Go automatically converts "/" to "\" in path on Windows // but since we are running it manually, it's better to convert to prevent problems path = strings.Replace(path, "/", "\\", -1) err = exec.Command("cmd", "/C", "rmdir", "/S", "/Q", path).Run() } else { err = os.RemoveAll(path) } if err != nil { desc := fmt.Sprintf("%s [%s]: %v", title, path, err) log.Warn(desc) if err = CreateRepositoryNotice(desc); err != nil { log.Error(4, "CreateRepositoryNotice: %v", err) } } }
func listen(config *ssh.ServerConfig, port int) { listener, err := net.Listen("tcp", "0.0.0.0:"+com.ToStr(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, "SSH: Error accepting incoming connection: %v", err) continue } // Before use, a handshake must be performed on the incoming net.Conn. // It must be handled in a separate goroutine, // otherwise one user could easily block entire loop. // For example, user could be asked to trust server key fingerprint and hangs. go func() { log.Trace("SSH: Handshaking for %s", conn.RemoteAddr()) sConn, chans, reqs, err := ssh.NewServerConn(conn, config) if err != nil { if err == io.EOF { log.Warn("SSH: Handshaking was terminated: %v", err) } else { log.Error(3, "SSH: Error on handshaking: %v", err) } return } log.Trace("SSH: Connection from %s (%s)", sConn.RemoteAddr(), sConn.ClientVersion()) // The incoming Request channel must be serviced. go ssh.DiscardRequests(reqs) go handleServerConn(sConn.Permissions.Extensions["key-id"], chans) }() } }
// NewContext initializes configuration context. // NOTE: do not print any log except error. func NewContext() { 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 homeDir, err := com.HomeDir() if err != nil { log.Fatal(4, "Fail to get home directory: %v", err) } homeDir = strings.Replace(homeDir, "\\", "/", -1) 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) } // Suburl should start with '/' and end without '/', such as '/{subpath}'. AppSubUrl = strings.TrimSuffix(url.Path, "/") AppSubUrlDepth = strings.Count(AppSubUrl, "/") 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") LocalURL = sec.Key("LOCAL_ROOT_URL").MustString("http://localhost:" + HttpPort + "/") OfflineMode = sec.Key("OFFLINE_MODE").MustBool() DisableRouterLog = sec.Key("DISABLE_ROUTER_LOG").MustBool() StaticRootPath = sec.Key("STATIC_ROOT_PATH").MustString(workDir) AppDataPath = sec.Key("APP_DATA_PATH").MustString("data") EnableGzip = sec.Key("ENABLE_GZIP").MustBool() switch sec.Key("LANDING_PAGE").MustString("home") { case "explore": LandingPageUrl = LANDING_PAGE_EXPLORE default: LandingPageUrl = LANDING_PAGE_HOME } SSH.RootPath = path.Join(homeDir, ".ssh") SSH.KeyTestPath = os.TempDir() if err = Cfg.Section("server").MapTo(&SSH); err != nil { log.Fatal(4, "Fail to map SSH settings: %v", err) } // When disable SSH, start builtin server value is ignored. if SSH.Disabled { SSH.StartBuiltinServer = false } if !SSH.Disabled && !SSH.StartBuiltinServer { if err := os.MkdirAll(SSH.RootPath, 0700); err != nil { log.Fatal(4, "Fail to create '%s': %v", SSH.RootPath, err) } else if err = os.MkdirAll(SSH.KeyTestPath, 0644); err != nil { log.Fatal(4, "Fail to create '%s': %v", SSH.KeyTestPath, err) } } SSH.MinimumKeySizeCheck = sec.Key("MINIMUM_KEY_SIZE_CHECK").MustBool() SSH.MinimumKeySizes = map[string]int{} minimumKeySizes := Cfg.Section("ssh.minimum_key_sizes").Keys() for _, key := range minimumKeySizes { if key.MustInt() != -1 { SSH.MinimumKeySizes[strings.ToLower(key.Name())] = key.MustInt() } } 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(path.Join(AppDataPath, "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(4) 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. 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") if err = Cfg.Section("repository").MapTo(&Repository); err != nil { log.Fatal(4, "Fail to map Repository settings: %v", err) } // UI settings. sec = Cfg.Section("ui") ExplorePagingNum = sec.Key("EXPLORE_PAGING_NUM").MustInt(20) IssuePagingNum = sec.Key("ISSUE_PAGING_NUM").MustInt(10) FeedMaxCommitNum = sec.Key("FEED_MAX_COMMIT_NUM").MustInt(5) sec = Cfg.Section("ui.admin") AdminUserPagingNum = sec.Key("USER_PAGING_NUM").MustInt(50) AdminRepoPagingNum = sec.Key("REPO_PAGING_NUM").MustInt(50) AdminNoticePagingNum = sec.Key("NOTICE_PAGING_NUM").MustInt(50) AdminOrgPagingNum = sec.Key("ORG_PAGING_NUM").MustInt(50) ThemeColorMetaTag = sec.Key("THEME_COLOR_META_TAG").MustString("#ff5343") sec = Cfg.Section("picture") AvatarUploadPath = sec.Key("AVATAR_UPLOAD_PATH").MustString(path.Join(AppDataPath, "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 = "https://secure.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 err = 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() ShowFooterVersion = Cfg.Section("other").Key("SHOW_FOOTER_VERSION").MustBool() HasRobotsTxt = com.IsFile(path.Join(CustomPath, "robots.txt")) }
func handleServerConn(keyID string, chans <-chan ssh.NewChannel) { for newChan := range chans { if newChan.ChannelType() != "session" { newChan.Reject(ssh.UnknownChannelType, "unknown channel type") continue } ch, reqs, err := newChan.Accept() if err != nil { log.Error(3, "Error accepting channel: %v", err) continue } go func(in <-chan *ssh.Request) { defer ch.Close() for req := range in { payload := cleanCommand(string(req.Payload)) switch req.Type { case "env": args := strings.Split(strings.Replace(payload, "\x00", "", -1), "\v") if len(args) != 2 { log.Warn("SSH: Invalid env arguments: '%#v'", args) continue } args[0] = strings.TrimLeft(args[0], "\x04") _, _, err := com.ExecCmdBytes("env", args[0]+"="+args[1]) if err != nil { log.Error(3, "env: %v", err) return } case "exec": cmdName := strings.TrimLeft(payload, "'()") log.Trace("SSH: Payload: %v", cmdName) args := []string{"serv", "key-" + keyID, "--config=" + setting.CustomConf} log.Trace("SSH: Arguments: %v", args) cmd := exec.Command(setting.AppPath, args...) cmd.Env = append(os.Environ(), "SSH_ORIGINAL_COMMAND="+cmdName) stdout, err := cmd.StdoutPipe() if err != nil { log.Error(3, "SSH: StdoutPipe: %v", err) return } stderr, err := cmd.StderrPipe() if err != nil { log.Error(3, "SSH: StderrPipe: %v", err) return } input, err := cmd.StdinPipe() if err != nil { log.Error(3, "SSH: StdinPipe: %v", err) return } // FIXME: check timeout if err = cmd.Start(); err != nil { log.Error(3, "SSH: Start: %v", err) return } req.Reply(true, nil) go io.Copy(input, ch) io.Copy(ch, stdout) io.Copy(ch.Stderr(), stderr) if err = cmd.Wait(); err != nil { log.Error(3, "SSH: Wait: %v", err) return } ch.SendRequest("exit-status", false, []byte{0, 0, 0, 0}) return default: } } }(reqs) } }
func ParsePatch(maxlines int, reader io.Reader) (*Diff, error) { var ( diff = &Diff{Files: make([]*DiffFile, 0)} curFile *DiffFile curSection = &DiffSection{ Lines: make([]*DiffLine, 0, 10), } leftLine, rightLine int lineCount int ) input := bufio.NewReader(reader) isEOF := false for { if isEOF { break } line, err := input.ReadString('\n') if err != nil { if err == io.EOF { isEOF = true } else { return nil, fmt.Errorf("ReadString: %v", err) } } if len(line) > 0 && line[len(line)-1] == '\n' { // Remove line break. line = line[:len(line)-1] } if strings.HasPrefix(line, "+++ ") || strings.HasPrefix(line, "--- ") { continue } else if len(line) == 0 { continue } lineCount++ // Diff data too large, we only show the first about maxlines lines if lineCount >= maxlines { log.Warn("Diff data too large") io.Copy(ioutil.Discard, reader) diff.Files = nil return diff, nil } 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] == '@': 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) { middle := -1 // Note: In case file name is surrounded by double quotes (it happens only in git-shell). // e.g. diff --git "a/xxx" "b/xxx" hasQuote := line[len(DIFF_HEAD)] == '"' if hasQuote { middle = strings.Index(line, ` "b/`) } else { middle = strings.Index(line, " b/") } beg := len(DIFF_HEAD) a := line[beg+2 : middle] b := line[middle+3:] if hasQuote { a = string(git.UnescapeChars([]byte(a[1 : len(a)-1]))) b = string(git.UnescapeChars([]byte(b[1 : len(b)-1]))) } curFile = &DiffFile{ Name: a, 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 { line, err := input.ReadString('\n') if err != nil { if err == io.EOF { isEOF = true } else { return nil, fmt.Errorf("ReadString: %v", err) } } switch { case strings.HasPrefix(line, "new file"): curFile.Type = DIFF_FILE_ADD curFile.IsCreated = true case strings.HasPrefix(line, "deleted"): curFile.Type = DIFF_FILE_DEL curFile.IsDeleted = true case strings.HasPrefix(line, "index"): curFile.Type = DIFF_FILE_CHANGE case strings.HasPrefix(line, "similarity index 100%"): curFile.Type = DIFF_FILE_RENAME curFile.IsRenamed = true curFile.OldName = curFile.Name curFile.Name = b } if curFile.Type > 0 { break } } } } // FIXME: detect encoding while parsing. var buf bytes.Buffer 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 }