| @@ -1077,9 +1077,11 @@ func CleanUpMigrateInfo(repo *Repository) (*Repository, error) { | |||
| } | |||
| } | |||
| if err := cleanUpMigrateGitConfig(repo.GitConfigPath()); err != nil { | |||
| return repo, fmt.Errorf("cleanUpMigrateGitConfig: %v", err) | |||
| _, err := git.NewCommand("remote", "remove", "origin").RunInDir(repoPath) | |||
| if err != nil && !strings.HasPrefix(err.Error(), "exit status 128 - fatal: No such remote ") { | |||
| return repo, fmt.Errorf("CleanUpMigrateInfo: %v", err) | |||
| } | |||
| if repo.HasWiki() { | |||
| if err := cleanUpMigrateGitConfig(path.Join(repo.WikiPath(), "config")); err != nil { | |||
| return repo, fmt.Errorf("cleanUpMigrateGitConfig (wiki): %v", err) | |||
| @@ -20,7 +20,6 @@ import ( | |||
| "github.com/Unknwon/com" | |||
| "github.com/go-xorm/xorm" | |||
| "gopkg.in/ini.v1" | |||
| ) | |||
| // MirrorQueue holds an UniqueQueue object of the mirror | |||
| @@ -71,11 +70,18 @@ func (m *Mirror) ScheduleNextUpdate() { | |||
| } | |||
| func remoteAddress(repoPath string) (string, error) { | |||
| cfg, err := ini.Load(GitConfigPath(repoPath)) | |||
| cmd := git.NewCommand("remote", "get-url", "origin") | |||
| result, err := cmd.RunInDir(repoPath) | |||
| if err != nil { | |||
| if strings.HasPrefix(err.Error(), "exit status 128 - fatal: No such remote ") { | |||
| return "", nil | |||
| } | |||
| return "", err | |||
| } | |||
| return cfg.Section("remote \"origin\"").Key("url").Value(), nil | |||
| if len(result) > 0 { | |||
| return result[:len(result)-1], nil | |||
| } | |||
| return "", nil | |||
| } | |||
| func (m *Mirror) readAddress() { | |||
| @@ -115,14 +121,15 @@ func (m *Mirror) FullAddress() string { | |||
| // SaveAddress writes new address to Git repository config. | |||
| func (m *Mirror) SaveAddress(addr string) error { | |||
| configPath := m.Repo.GitConfigPath() | |||
| cfg, err := ini.Load(configPath) | |||
| if err != nil { | |||
| return fmt.Errorf("Load: %v", err) | |||
| repoPath := m.Repo.RepoPath() | |||
| // Remove old origin | |||
| _, err := git.NewCommand("remote", "remove", "origin").RunInDir(repoPath) | |||
| if err != nil && !strings.HasPrefix(err.Error(), "exit status 128 - fatal: No such remote ") { | |||
| return err | |||
| } | |||
| cfg.Section("remote \"origin\"").Key("url").SetValue(addr) | |||
| return cfg.SaveToIndent(configPath, "\t") | |||
| _, err = git.NewCommand("remote", "add", "origin", addr).RunInDir(repoPath) | |||
| return err | |||
| } | |||
| // gitShortEmptySha Git short empty SHA | |||
| @@ -567,7 +567,9 @@ mirror_prune_desc = Remove obsolete remote-tracking references | |||
| mirror_interval = Mirror Interval (valid time units are 'h', 'm', 's'). 0 to disable automatic sync. | |||
| mirror_interval_invalid = The mirror interval is not valid. | |||
| mirror_address = Clone From URL | |||
| mirror_address_desc = Include any required authorization credentials in the URL. | |||
| mirror_address_desc = Include any required authorization credentials in the URL. These must be url escaped as appropriate | |||
| mirror_address_url_invalid = The provided url is invalid. You must escape all components of the url correctly. | |||
| mirror_address_protocol_invalid = The provided url is invalid. Only http(s):// or git:// locations can be mirrored from. | |||
| mirror_last_synced = Last Synchronized | |||
| watchers = Watchers | |||
| stargazers = Stargazers | |||
| @@ -7,9 +7,13 @@ package repo | |||
| import ( | |||
| "errors" | |||
| "net/url" | |||
| "regexp" | |||
| "strings" | |||
| "time" | |||
| "mvdan.cc/xurls/v2" | |||
| "code.gitea.io/gitea/models" | |||
| "code.gitea.io/gitea/modules/auth" | |||
| "code.gitea.io/gitea/modules/base" | |||
| @@ -32,6 +36,8 @@ const ( | |||
| tplProtectedBranch base.TplName = "repo/settings/protected_branch" | |||
| ) | |||
| var validFormAddress *regexp.Regexp | |||
| // Settings show a repository's settings page | |||
| func Settings(ctx *context.Context) { | |||
| ctx.Data["Title"] = ctx.Tr("repo.settings") | |||
| @@ -145,7 +151,38 @@ func SettingsPost(ctx *context.Context, form auth.RepoSettingForm) { | |||
| return | |||
| } | |||
| } | |||
| if err := ctx.Repo.Mirror.SaveAddress(form.MirrorAddress); err != nil { | |||
| // Validate the form.MirrorAddress | |||
| u, err := url.Parse(form.MirrorAddress) | |||
| if err != nil { | |||
| ctx.Data["Err_MirrorAddress"] = true | |||
| ctx.RenderWithErr(ctx.Tr("repo.mirror_address_url_invalid"), tplSettingsOptions, &form) | |||
| return | |||
| } | |||
| if u.Opaque != "" || !(u.Scheme == "http" || u.Scheme == "https" || u.Scheme == "git") { | |||
| ctx.Data["Err_MirrorAddress"] = true | |||
| ctx.RenderWithErr(ctx.Tr("repo.mirror_address_protocol_invalid"), tplSettingsOptions, &form) | |||
| return | |||
| } | |||
| // Now use xurls | |||
| address := validFormAddress.FindString(form.MirrorAddress) | |||
| if address != form.MirrorAddress && form.MirrorAddress != "" { | |||
| ctx.Data["Err_MirrorAddress"] = true | |||
| ctx.RenderWithErr(ctx.Tr("repo.mirror_address_url_invalid"), tplSettingsOptions, &form) | |||
| return | |||
| } | |||
| if u.EscapedPath() == "" || u.Host == "" || !u.IsAbs() { | |||
| ctx.Data["Err_MirrorAddress"] = true | |||
| ctx.RenderWithErr(ctx.Tr("repo.mirror_address_url_invalid"), tplSettingsOptions, &form) | |||
| return | |||
| } | |||
| address = u.String() | |||
| if err := ctx.Repo.Mirror.SaveAddress(address); err != nil { | |||
| ctx.ServerError("SaveAddress", err) | |||
| return | |||
| } | |||
| @@ -682,3 +719,11 @@ func DeleteDeployKey(ctx *context.Context) { | |||
| "redirect": ctx.Repo.RepoLink + "/settings/keys", | |||
| }) | |||
| } | |||
| func init() { | |||
| var err error | |||
| validFormAddress, err = xurls.StrictMatchingScheme(`(https?)|(git)://`) | |||
| if err != nil { | |||
| panic(err) | |||
| } | |||
| } | |||
| @@ -62,7 +62,7 @@ | |||
| <label for="interval">{{.i18n.Tr "repo.mirror_interval"}}</label> | |||
| <input id="interval" name="interval" value="{{.MirrorInterval}}"> | |||
| </div> | |||
| <div class="field"> | |||
| <div class="field {{if .Err_MirrorAddress}}error{{end}}"> | |||
| <label for="mirror_address">{{.i18n.Tr "repo.mirror_address"}}</label> | |||
| <input id="mirror_address" name="mirror_address" value="{{.Mirror.FullAddress}}" required> | |||
| <p class="help">{{.i18n.Tr "repo.mirror_address_desc"}}</p> | |||