2019-05-02 18:17:27 -04:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2015-06-12 06:02:36 -04:00
|
|
|
class UrlHelper
|
2013-11-20 07:10:08 -05:00
|
|
|
|
2018-12-11 02:03:13 -05:00
|
|
|
# At the moment this handles invalid URLs that browser address bar accepts
|
|
|
|
# where second # is not encoded
|
|
|
|
#
|
|
|
|
# Longer term we can add support of simpleidn and encode unicode domains
|
|
|
|
def self.relaxed_parse(url)
|
|
|
|
url, fragment = url.split("#", 2)
|
|
|
|
uri = URI.parse(url)
|
|
|
|
if uri
|
|
|
|
fragment = URI.escape(fragment) if fragment&.include?('#')
|
|
|
|
uri.fragment = fragment
|
|
|
|
uri
|
|
|
|
end
|
|
|
|
rescue URI::Error
|
|
|
|
end
|
|
|
|
|
2015-06-12 06:02:36 -04:00
|
|
|
def self.is_local(url)
|
2014-07-18 11:54:18 -04:00
|
|
|
url.present? && (
|
|
|
|
Discourse.store.has_been_uploaded?(url) ||
|
2019-04-13 13:35:23 -04:00
|
|
|
!!(url =~ Regexp.new("^#{Discourse.base_uri}/(assets|plugins|images)/")) ||
|
2014-07-18 11:54:18 -04:00
|
|
|
url.start_with?(Discourse.asset_host || Discourse.base_url_no_prefix)
|
|
|
|
)
|
2013-11-20 07:10:08 -05:00
|
|
|
end
|
|
|
|
|
2015-06-12 06:02:36 -04:00
|
|
|
def self.absolute(url, cdn = Discourse.asset_host)
|
2019-05-02 18:17:27 -04:00
|
|
|
cdn = "https:#{cdn}" if cdn && cdn =~ /^\/\//
|
2013-12-16 18:35:34 -05:00
|
|
|
url =~ /^\/[^\/]/ ? (cdn || Discourse.base_url_no_prefix) + url : url
|
|
|
|
end
|
|
|
|
|
2015-06-12 06:02:36 -04:00
|
|
|
def self.absolute_without_cdn(url)
|
|
|
|
self.absolute(url, nil)
|
2013-11-20 07:10:08 -05:00
|
|
|
end
|
|
|
|
|
2015-06-12 06:02:36 -04:00
|
|
|
def self.schemaless(url)
|
2016-06-30 10:55:01 -04:00
|
|
|
url.sub(/^http:/i, "")
|
2013-11-20 07:10:08 -05:00
|
|
|
end
|
|
|
|
|
2019-11-17 20:25:42 -05:00
|
|
|
def self.secure_proxy_without_cdn(url)
|
|
|
|
url = url.sub(SiteSetting.Upload.absolute_base_url, "/secure-media-uploads")
|
|
|
|
self.absolute(url, nil)
|
|
|
|
end
|
|
|
|
|
2017-12-12 11:50:39 -05:00
|
|
|
DOUBLE_ESCAPED_REGEXP ||= /%25([0-9a-f]{2})/i
|
|
|
|
|
|
|
|
# Prevents double URL encode
|
|
|
|
# https://stackoverflow.com/a/37599235
|
|
|
|
def self.escape_uri(uri, pattern = URI::UNSAFE)
|
|
|
|
encoded = URI.encode(uri, pattern)
|
|
|
|
encoded.gsub!(DOUBLE_ESCAPED_REGEXP, '%\1')
|
|
|
|
encoded
|
|
|
|
end
|
|
|
|
|
2019-11-17 20:25:42 -05:00
|
|
|
def self.cook_url(url, secure: false)
|
2018-08-14 06:23:32 -04:00
|
|
|
return url unless is_local(url)
|
|
|
|
|
|
|
|
uri = URI.parse(url)
|
|
|
|
filename = File.basename(uri.path)
|
2019-11-17 20:25:42 -05:00
|
|
|
is_attachment = !FileHelper.is_supported_media?(filename)
|
2018-08-14 06:23:32 -04:00
|
|
|
|
|
|
|
no_cdn = SiteSetting.login_required || SiteSetting.prevent_anons_from_downloading_files
|
|
|
|
|
2019-11-17 20:25:42 -05:00
|
|
|
url = secure ? secure_proxy_without_cdn(url) : absolute_without_cdn(url)
|
2019-02-20 13:24:38 -05:00
|
|
|
|
2019-11-22 00:29:31 -05:00
|
|
|
# we always want secure media to come from
|
|
|
|
# Discourse.base_url_no_prefix/secure-media-uploads
|
|
|
|
# to avoid asset_host mixups
|
|
|
|
return schemaless(url) if secure
|
|
|
|
|
2019-02-20 13:24:38 -05:00
|
|
|
unless is_attachment && no_cdn
|
|
|
|
url = Discourse.store.cdn_url(url)
|
|
|
|
url = local_cdn_url(url) if Discourse.store.external?
|
|
|
|
end
|
2018-08-14 06:23:32 -04:00
|
|
|
|
|
|
|
schemaless(url)
|
|
|
|
rescue URI::Error
|
|
|
|
url
|
|
|
|
end
|
|
|
|
|
2019-02-20 13:24:38 -05:00
|
|
|
def self.local_cdn_url(url)
|
|
|
|
return url if Discourse.asset_host.blank?
|
2019-10-14 02:09:16 -04:00
|
|
|
if url.start_with?("/#{Discourse.store.upload_path}/")
|
|
|
|
"#{Discourse.asset_host}#{url}"
|
|
|
|
else
|
|
|
|
url.sub(Discourse.base_url_no_prefix, Discourse.asset_host)
|
|
|
|
end
|
2019-02-20 13:24:38 -05:00
|
|
|
end
|
|
|
|
|
2013-11-20 07:10:08 -05:00
|
|
|
end
|