Correctly adjust mirror url (#6593) (#6595)

This commit is contained in:
zeripath 2019-04-13 02:26:09 +01:00 committed by techknowlogick
parent 4ff6effe04
commit bac388d27c
12 changed files with 2102 additions and 21 deletions

View file

@ -82,7 +82,7 @@ pipeline:
when: when:
event: [ push, tag, pull_request ] event: [ push, tag, pull_request ]
test: unit-test:
image: golang:1.11 image: golang:1.11
pull: true pull: true
group: test group: test
@ -94,7 +94,7 @@ pipeline:
event: [ push, pull_request ] event: [ push, pull_request ]
branch: [ master ] branch: [ master ]
test: release-test:
image: golang:1.11 image: golang:1.11
pull: true pull: true
group: test group: test
@ -106,7 +106,7 @@ pipeline:
event: [ push, pull_request ] event: [ push, pull_request ]
branch: [ release/* ] branch: [ release/* ]
test: tag-test:
image: golang:1.11 image: golang:1.11
pull: true pull: true
group: test group: test
@ -146,7 +146,7 @@ pipeline:
event: [ push, pull_request ] event: [ push, pull_request ]
branch: [ master ] branch: [ master ]
test-mysql: tag-test-mysql:
image: golang:1.11 image: golang:1.11
pull: true pull: true
group: test group: test
@ -240,12 +240,13 @@ pipeline:
event: [ push ] event: [ push ]
branch: [ master ] branch: [ master ]
docker: release-docker:
image: plugins/docker:17.12 image: plugins/docker:17.12
pull: true pull: true
secrets: [ docker_username, docker_password ] secrets: [ docker_username, docker_password ]
repo: gitea/gitea repo: gitea/gitea
tags: [ '${DRONE_BRANCH##release/v}' ] tags: [ '${DRONE_BRANCH##release/v}' ]
cache_from: gitea/gitea
when: when:
event: [ push ] event: [ push ]
branch: [ release/* ] branch: [ release/* ]
@ -255,6 +256,7 @@ pipeline:
secrets: [ docker_username, docker_password ] secrets: [ docker_username, docker_password ]
pull: true pull: true
repo: gitea/gitea repo: gitea/gitea
cache_from: gitea/gitea
default_tags: true default_tags: true
when: when:
event: [ push, tag ] event: [ push, tag ]
@ -271,7 +273,7 @@ pipeline:
when: when:
event: [ push, tag ] event: [ push, tag ]
release: tag-release:
image: plugins/s3:1 image: plugins/s3:1
pull: true pull: true
secrets: [ aws_access_key_id, aws_secret_access_key ] secrets: [ aws_access_key_id, aws_secret_access_key ]
@ -285,7 +287,7 @@ pipeline:
when: when:
event: [ tag ] event: [ tag ]
release: release-branch-release:
image: plugins/s3:1 image: plugins/s3:1
pull: true pull: true
secrets: [ aws_access_key_id, aws_secret_access_key ] secrets: [ aws_access_key_id, aws_secret_access_key ]

9
Gopkg.lock generated
View file

@ -666,6 +666,14 @@
pruneopts = "NUT" pruneopts = "NUT"
revision = "02ccfbfaf0cc627aa3aec8ef7ed5cfeec5b43f63" revision = "02ccfbfaf0cc627aa3aec8ef7ed5cfeec5b43f63"
[[projects]]
digest = "1:63953ffb90bbc880c612d576fcfd973a5904277d25ec9e2d8d5719bf67969662"
name = "github.com/mvdan/xurls"
packages = ["."]
pruneopts = "NUT"
revision = "e52e821cbfe8fe163ff6f8628ab5869b11fc05af"
version = "v2.0.0"
[[projects]] [[projects]]
digest = "1:2be1d891535ce3d6d2a3db9087f07415e909744e9eff1a30f8f0b2519df60ae6" digest = "1:2be1d891535ce3d6d2a3db9087f07415e909744e9eff1a30f8f0b2519df60ae6"
name = "github.com/nfnt/resize" name = "github.com/nfnt/resize"
@ -1190,6 +1198,7 @@
"github.com/mcuadros/go-version", "github.com/mcuadros/go-version",
"github.com/microcosm-cc/bluemonday", "github.com/microcosm-cc/bluemonday",
"github.com/msteinert/pam", "github.com/msteinert/pam",
"github.com/mvdan/xurls",
"github.com/nfnt/resize", "github.com/nfnt/resize",
"github.com/pquerna/otp", "github.com/pquerna/otp",
"github.com/pquerna/otp/totp", "github.com/pquerna/otp/totp",

View file

@ -1066,9 +1066,11 @@ func CleanUpMigrateInfo(repo *Repository) (*Repository, error) {
} }
} }
if err := cleanUpMigrateGitConfig(repo.GitConfigPath()); err != nil { _, err := git.NewCommand("remote", "remove", "origin").RunInDir(repoPath)
return repo, fmt.Errorf("cleanUpMigrateGitConfig: %v", err) 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)

View file

@ -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() repoPath := m.Repo.RepoPath()
cfg, err := ini.Load(configPath) // Remove old origin
if err != nil { _, err := git.NewCommand("remote", "remove", "origin").RunInDir(repoPath)
return fmt.Errorf("Load: %v", err) if err != nil && !strings.HasPrefix(err.Error(), "exit status 128 - fatal: No such remote ") {
return err
} }
cfg.Section("remote \"origin\"").Key("url").SetValue(addr) _, err = git.NewCommand("remote", "add", "origin", addr).RunInDir(repoPath)
return cfg.SaveToIndent(configPath, "\t") return err
} }
// gitShortEmptySha Git short empty SHA // gitShortEmptySha Git short empty SHA

View file

@ -528,7 +528,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

View file

@ -7,6 +7,8 @@ package repo
import ( import (
"errors" "errors"
"net/url"
"regexp"
"strings" "strings"
"time" "time"
@ -21,6 +23,8 @@ import (
"code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/validation" "code.gitea.io/gitea/modules/validation"
"code.gitea.io/gitea/routers/utils" "code.gitea.io/gitea/routers/utils"
"github.com/mvdan/xurls"
) )
const ( const (
@ -33,6 +37,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")
@ -140,7 +146,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
} }
@ -618,3 +655,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)
}
}

View file

@ -58,7 +58,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>

27
vendor/github.com/mvdan/xurls/LICENSE generated vendored Normal file
View file

@ -0,0 +1,27 @@
Copyright (c) 2015, Daniel Martí. 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.
* Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
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
OWNER 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.

299
vendor/github.com/mvdan/xurls/schemes.go generated vendored Normal file
View file

@ -0,0 +1,299 @@
// Generated by schemesgen
package xurls
// Schemes is a sorted list of all IANA assigned schemes.
//
// Source:
// https://www.iana.org/assignments/uri-schemes/uri-schemes-1.csv
var Schemes = []string{
`aaa`,
`aaas`,
`about`,
`acap`,
`acct`,
`acr`,
`adiumxtra`,
`afp`,
`afs`,
`aim`,
`appdata`,
`apt`,
`attachment`,
`aw`,
`barion`,
`beshare`,
`bitcoin`,
`bitcoincash`,
`blob`,
`bolo`,
`browserext`,
`callto`,
`cap`,
`chrome`,
`chrome-extension`,
`cid`,
`coap`,
`coap+tcp`,
`coap+ws`,
`coaps`,
`coaps+tcp`,
`coaps+ws`,
`com-eventbrite-attendee`,
`content`,
`conti`,
`crid`,
`cvs`,
`data`,
`dav`,
`diaspora`,
`dict`,
`did`,
`dis`,
`dlna-playcontainer`,
`dlna-playsingle`,
`dns`,
`dntp`,
`dtn`,
`dvb`,
`ed2k`,
`elsi`,
`example`,
`facetime`,
`fax`,
`feed`,
`feedready`,
`file`,
`filesystem`,
`finger`,
`fish`,
`ftp`,
`geo`,
`gg`,
`git`,
`gizmoproject`,
`go`,
`gopher`,
`graph`,
`gtalk`,
`h323`,
`ham`,
`hcap`,
`hcp`,
`http`,
`https`,
`hxxp`,
`hxxps`,
`hydrazone`,
`iax`,
`icap`,
`icon`,
`im`,
`imap`,
`info`,
`iotdisco`,
`ipn`,
`ipp`,
`ipps`,
`irc`,
`irc6`,
`ircs`,
`iris`,
`iris.beep`,
`iris.lwz`,
`iris.xpc`,
`iris.xpcs`,
`isostore`,
`itms`,
`jabber`,
`jar`,
`jms`,
`keyparc`,
`lastfm`,
`ldap`,
`ldaps`,
`lvlt`,
`magnet`,
`mailserver`,
`mailto`,
`maps`,
`market`,
`message`,
`microsoft.windows.camera`,
`microsoft.windows.camera.multipicker`,
`microsoft.windows.camera.picker`,
`mid`,
`mms`,
`modem`,
`mongodb`,
`moz`,
`ms-access`,
`ms-browser-extension`,
`ms-drive-to`,
`ms-enrollment`,
`ms-excel`,
`ms-gamebarservices`,
`ms-gamingoverlay`,
`ms-getoffice`,
`ms-help`,
`ms-infopath`,
`ms-inputapp`,
`ms-lockscreencomponent-config`,
`ms-media-stream-id`,
`ms-mixedrealitycapture`,
`ms-officeapp`,
`ms-people`,
`ms-project`,
`ms-powerpoint`,
`ms-publisher`,
`ms-restoretabcompanion`,
`ms-screenclip`,
`ms-screensketch`,
`ms-search`,
`ms-search-repair`,
`ms-secondary-screen-controller`,
`ms-secondary-screen-setup`,
`ms-settings`,
`ms-settings-airplanemode`,
`ms-settings-bluetooth`,
`ms-settings-camera`,
`ms-settings-cellular`,
`ms-settings-cloudstorage`,
`ms-settings-connectabledevices`,
`ms-settings-displays-topology`,
`ms-settings-emailandaccounts`,
`ms-settings-language`,
`ms-settings-location`,
`ms-settings-lock`,
`ms-settings-nfctransactions`,
`ms-settings-notifications`,
`ms-settings-power`,
`ms-settings-privacy`,
`ms-settings-proximity`,
`ms-settings-screenrotation`,
`ms-settings-wifi`,
`ms-settings-workplace`,
`ms-spd`,
`ms-sttoverlay`,
`ms-transit-to`,
`ms-useractivityset`,
`ms-virtualtouchpad`,
`ms-visio`,
`ms-walk-to`,
`ms-whiteboard`,
`ms-whiteboard-cmd`,
`ms-word`,
`msnim`,
`msrp`,
`msrps`,
`mtqp`,
`mumble`,
`mupdate`,
`mvn`,
`news`,
`nfs`,
`ni`,
`nih`,
`nntp`,
`notes`,
`ocf`,
`oid`,
`onenote`,
`onenote-cmd`,
`opaquelocktoken`,
`openpgp4fpr`,
`pack`,
`palm`,
`paparazzi`,
`pkcs11`,
`platform`,
`pop`,
`pres`,
`prospero`,
`proxy`,
`pwid`,
`psyc`,
`qb`,
`query`,
`redis`,
`rediss`,
`reload`,
`res`,
`resource`,
`rmi`,
`rsync`,
`rtmfp`,
`rtmp`,
`rtsp`,
`rtsps`,
`rtspu`,
`secondlife`,
`service`,
`session`,
`sftp`,
`sgn`,
`shttp`,
`sieve`,
`simpleledger`,
`sip`,
`sips`,
`skype`,
`smb`,
`sms`,
`smtp`,
`snews`,
`snmp`,
`soap.beep`,
`soap.beeps`,
`soldat`,
`spiffe`,
`spotify`,
`ssh`,
`steam`,
`stun`,
`stuns`,
`submit`,
`svn`,
`tag`,
`teamspeak`,
`tel`,
`teliaeid`,
`telnet`,
`tftp`,
`things`,
`thismessage`,
`tip`,
`tn3270`,
`tool`,
`turn`,
`turns`,
`tv`,
`udp`,
`unreal`,
`urn`,
`ut2004`,
`v-event`,
`vemmi`,
`ventrilo`,
`videotex`,
`vnc`,
`view-source`,
`wais`,
`webcal`,
`wpid`,
`ws`,
`wss`,
`wtai`,
`wyciwyg`,
`xcon`,
`xcon-userid`,
`xfire`,
`xmlrpc.beep`,
`xmlrpc.beeps`,
`xmpp`,
`xri`,
`ymsgr`,
`z39.50`,
`z39.50r`,
`z39.50s`,
}

1557
vendor/github.com/mvdan/xurls/tlds.go generated vendored Normal file

File diff suppressed because it is too large Load diff

24
vendor/github.com/mvdan/xurls/tlds_pseudo.go generated vendored Normal file
View file

@ -0,0 +1,24 @@
// Copyright (c) 2015, Daniel Martí <mvdan@mvdan.cc>
// See LICENSE for licensing information
package xurls
// PseudoTLDs is a sorted list of some widely used unofficial TLDs.
//
// Sources:
// * https://en.wikipedia.org/wiki/Pseudo-top-level_domain
// * https://en.wikipedia.org/wiki/Category:Pseudo-top-level_domains
// * https://tools.ietf.org/html/draft-grothoff-iesg-special-use-p2p-names-00
// * https://www.iana.org/assignments/special-use-domain-names/special-use-domain-names.xhtml
var PseudoTLDs = []string{
`bit`, // Namecoin
`example`, // Example domain
`exit`, // Tor exit node
`gnu`, // GNS by public key
`i2p`, // I2P network
`invalid`, // Invalid domain
`local`, // Local network
`localhost`, // Local network
`test`, // Test domain
`zkey`, // GNS domain name
}

107
vendor/github.com/mvdan/xurls/xurls.go generated vendored Normal file
View file

@ -0,0 +1,107 @@
// Copyright (c) 2015, Daniel Martí <mvdan@mvdan.cc>
// See LICENSE for licensing information
// Package xurls extracts urls from plain text using regular expressions.
package xurls
import (
"bytes"
"regexp"
)
//go:generate go run generate/tldsgen/main.go
//go:generate go run generate/schemesgen/main.go
const (
letter = `\p{L}`
mark = `\p{M}`
number = `\p{N}`
iriChar = letter + mark + number
currency = `\p{Sc}`
otherSymb = `\p{So}`
endChar = iriChar + `/\-+_&~*%=#` + currency + otherSymb
otherPunc = `\p{Po}`
midChar = endChar + `|` + otherPunc
wellParen = `\([` + midChar + `]*(\([` + midChar + `]*\)[` + midChar + `]*)*\)`
wellBrack = `\[[` + midChar + `]*(\[[` + midChar + `]*\][` + midChar + `]*)*\]`
wellBrace = `\{[` + midChar + `]*(\{[` + midChar + `]*\}[` + midChar + `]*)*\}`
wellAll = wellParen + `|` + wellBrack + `|` + wellBrace
pathCont = `([` + midChar + `]*(` + wellAll + `|[` + endChar + `])+)+`
iri = `[` + iriChar + `]([` + iriChar + `\-]*[` + iriChar + `])?`
domain = `(` + iri + `\.)+`
octet = `(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]|[0-9])`
ipv4Addr = `\b` + octet + `\.` + octet + `\.` + octet + `\.` + octet + `\b`
ipv6Addr = `([0-9a-fA-F]{1,4}:([0-9a-fA-F]{1,4}:([0-9a-fA-F]{1,4}:([0-9a-fA-F]{1,4}:([0-9a-fA-F]{1,4}:[0-9a-fA-F]{0,4}|:[0-9a-fA-F]{1,4})?|(:[0-9a-fA-F]{1,4}){0,2})|(:[0-9a-fA-F]{1,4}){0,3})|(:[0-9a-fA-F]{1,4}){0,4})|:(:[0-9a-fA-F]{1,4}){0,5})((:[0-9a-fA-F]{1,4}){2}|:(25[0-5]|(2[0-4]|1[0-9]|[1-9])?[0-9])(\.(25[0-5]|(2[0-4]|1[0-9]|[1-9])?[0-9])){3})|(([0-9a-fA-F]{1,4}:){1,6}|:):[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){7}:`
ipAddr = `(` + ipv4Addr + `|` + ipv6Addr + `)`
port = `(:[0-9]*)?`
)
// AnyScheme can be passed to StrictMatchingScheme to match any possibly valid
// scheme, and not just the known ones.
var AnyScheme = `([a-zA-Z][a-zA-Z.\-+]*://|` + anyOf(SchemesNoAuthority...) + `:)`
// SchemesNoAuthority is a sorted list of some well-known url schemes that are
// followed by ":" instead of "://".
var SchemesNoAuthority = []string{
`bitcoin`, // Bitcoin
`file`, // Files
`magnet`, // Torrent magnets
`mailto`, // Mail
`sms`, // SMS
`tel`, // Telephone
`xmpp`, // XMPP
}
func anyOf(strs ...string) string {
var b bytes.Buffer
b.WriteByte('(')
for i, s := range strs {
if i != 0 {
b.WriteByte('|')
}
b.WriteString(regexp.QuoteMeta(s))
}
b.WriteByte(')')
return b.String()
}
func strictExp() string {
schemes := `(` + anyOf(Schemes...) + `://|` + anyOf(SchemesNoAuthority...) + `:)`
return `(?i)` + schemes + `(?-i)` + pathCont
}
func relaxedExp() string {
site := domain + `(?i)` + anyOf(append(TLDs, PseudoTLDs...)...) + `(?-i)`
hostName := `(` + site + `|` + ipAddr + `)`
webURL := hostName + port + `(/|/` + pathCont + `?|\b|$)`
return strictExp() + `|` + webURL
}
// Strict produces a regexp that matches any URL with a scheme in either the
// Schemes or SchemesNoAuthority lists.
func Strict() *regexp.Regexp {
re := regexp.MustCompile(strictExp())
re.Longest()
return re
}
// Relaxed produces a regexp that matches any URL matched by Strict, plus any
// URL with no scheme.
func Relaxed() *regexp.Regexp {
re := regexp.MustCompile(relaxedExp())
re.Longest()
return re
}
// StrictMatchingScheme produces a regexp similar to Strict, but requiring that
// the scheme match the given regular expression. See AnyScheme too.
func StrictMatchingScheme(exp string) (*regexp.Regexp, error) {
strictMatching := `(?i)(` + exp + `)(?-i)` + pathCont
re, err := regexp.Compile(strictMatching)
if err != nil {
return nil, err
}
re.Longest()
return re, nil
}