| @@ -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 repo.HasWiki() { | ||||
| if err := cleanUpMigrateGitConfig(path.Join(repo.WikiPath(), "config")); err != nil { | if err := cleanUpMigrateGitConfig(path.Join(repo.WikiPath(), "config")); err != nil { | ||||
| return repo, fmt.Errorf("cleanUpMigrateGitConfig (wiki): %v", err) | return repo, fmt.Errorf("cleanUpMigrateGitConfig (wiki): %v", err) | ||||
| @@ -20,7 +20,6 @@ import ( | |||||
| "github.com/Unknwon/com" | "github.com/Unknwon/com" | ||||
| "github.com/go-xorm/xorm" | "github.com/go-xorm/xorm" | ||||
| "gopkg.in/ini.v1" | |||||
| ) | ) | ||||
| // MirrorQueue holds an UniqueQueue object of the mirror | // MirrorQueue holds an UniqueQueue object of the mirror | ||||
| @@ -71,11 +70,18 @@ func (m *Mirror) ScheduleNextUpdate() { | |||||
| } | } | ||||
| func remoteAddress(repoPath string) (string, error) { | 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 err != nil { | ||||
| if strings.HasPrefix(err.Error(), "exit status 128 - fatal: No such remote ") { | |||||
| return "", nil | |||||
| } | |||||
| return "", err | 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() { | func (m *Mirror) readAddress() { | ||||
| @@ -115,14 +121,15 @@ func (m *Mirror) FullAddress() string { | |||||
| // SaveAddress writes new address to Git repository config. | // SaveAddress writes new address to Git repository config. | ||||
| func (m *Mirror) SaveAddress(addr string) error { | 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 | // 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 = Mirror Interval (valid time units are 'h', 'm', 's'). 0 to disable automatic sync. | ||||
| mirror_interval_invalid = The mirror interval is not valid. | mirror_interval_invalid = The mirror interval is not valid. | ||||
| mirror_address = Clone From URL | 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 | mirror_last_synced = Last Synchronized | ||||
| watchers = Watchers | watchers = Watchers | ||||
| stargazers = Stargazers | stargazers = Stargazers | ||||
| @@ -7,9 +7,13 @@ package repo | |||||
| import ( | import ( | ||||
| "errors" | "errors" | ||||
| "net/url" | |||||
| "regexp" | |||||
| "strings" | "strings" | ||||
| "time" | "time" | ||||
| "mvdan.cc/xurls/v2" | |||||
| "code.gitea.io/gitea/models" | "code.gitea.io/gitea/models" | ||||
| "code.gitea.io/gitea/modules/auth" | "code.gitea.io/gitea/modules/auth" | ||||
| "code.gitea.io/gitea/modules/base" | "code.gitea.io/gitea/modules/base" | ||||
| @@ -32,6 +36,8 @@ const ( | |||||
| tplProtectedBranch base.TplName = "repo/settings/protected_branch" | tplProtectedBranch base.TplName = "repo/settings/protected_branch" | ||||
| ) | ) | ||||
| var validFormAddress *regexp.Regexp | |||||
| // Settings show a repository's settings page | // Settings show a repository's settings page | ||||
| func Settings(ctx *context.Context) { | func Settings(ctx *context.Context) { | ||||
| ctx.Data["Title"] = ctx.Tr("repo.settings") | ctx.Data["Title"] = ctx.Tr("repo.settings") | ||||
| @@ -145,7 +151,38 @@ func SettingsPost(ctx *context.Context, form auth.RepoSettingForm) { | |||||
| return | 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) | ctx.ServerError("SaveAddress", err) | ||||
| return | return | ||||
| } | } | ||||
| @@ -682,3 +719,11 @@ func DeleteDeployKey(ctx *context.Context) { | |||||
| "redirect": ctx.Repo.RepoLink + "/settings/keys", | "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> | <label for="interval">{{.i18n.Tr "repo.mirror_interval"}}</label> | ||||
| <input id="interval" name="interval" value="{{.MirrorInterval}}"> | <input id="interval" name="interval" value="{{.MirrorInterval}}"> | ||||
| </div> | </div> | ||||
| <div class="field"> | |||||
| <div class="field {{if .Err_MirrorAddress}}error{{end}}"> | |||||
| <label for="mirror_address">{{.i18n.Tr "repo.mirror_address"}}</label> | <label for="mirror_address">{{.i18n.Tr "repo.mirror_address"}}</label> | ||||
| <input id="mirror_address" name="mirror_address" value="{{.Mirror.FullAddress}}" required> | <input id="mirror_address" name="mirror_address" value="{{.Mirror.FullAddress}}" required> | ||||
| <p class="help">{{.i18n.Tr "repo.mirror_address_desc"}}</p> | <p class="help">{{.i18n.Tr "repo.mirror_address_desc"}}</p> | ||||