@@ -62,9 +62,19 @@ local REASON_UPDATE = "update"
6262local REASON_DEPENDENCY = " dependency"
6363
6464
65+ -- encodes for use as URL parameter or path component
66+ local function urlencode (str )
67+ return str :gsub (" [^%a%d()._~-]" , function (char )
68+ return string.format (" %%%02X" , string.byte (char ))
69+ end )
70+ end
71+ assert (urlencode (" sample text?" ) == " sample%20text%3F" )
72+
73+
6574local function get_download_url (package , reason )
6675 local base_url = core .settings :get (" contentdb_url" )
67- local ret = base_url .. (" /packages/%s/%s/releases/%d/download/" ):format (package .author , package .name , package .release )
76+ local ret = base_url .. (" /packages/%s/releases/%d/download/" ):format (
77+ package .url_part , package .release )
6878 if reason then
6979 ret = ret .. " ?reason=" .. reason
7080 end
@@ -199,7 +209,7 @@ local function get_raw_dependencies(package)
199209 local url_fmt = " /api/packages/%s/dependencies/?only_hard=1&protocol_version=%s&engine_version=%s"
200210 local version = core .get_version ()
201211 local base_url = core .settings :get (" contentdb_url" )
202- local url = base_url .. url_fmt :format (package .id , core .get_max_supp_proto (), version .string )
212+ local url = base_url .. url_fmt :format (package .url_part , core .get_max_supp_proto (), urlencode ( version .string ) )
203213
204214 local response = http .fetch_sync ({ url = url })
205215 if not response .succeeded then
@@ -574,17 +584,16 @@ function store.load()
574584 local base_url = core .settings :get (" contentdb_url" )
575585 local url = base_url ..
576586 " /api/packages/?type=mod&type=game&type=txp&protocol_version=" ..
577- core .get_max_supp_proto () .. " &engine_version=" .. version .string
587+ core .get_max_supp_proto () .. " &engine_version=" .. urlencode ( version .string )
578588
579589 for _ , item in pairs (core .settings :get (" contentdb_flag_blacklist" ):split (" ," )) do
580590 item = item :trim ()
581591 if item ~= " " then
582- url = url .. " &hide=" .. item
592+ url = url .. " &hide=" .. urlencode ( item )
583593 end
584594 end
585595
586- local timeout = tonumber (core .settings :get (" curl_file_download_timeout" ))
587- local response = http .fetch_sync ({ url = url , timeout = timeout })
596+ local response = http .fetch_sync ({ url = url })
588597 if not response .succeeded then
589598 return
590599 end
@@ -594,12 +603,16 @@ function store.load()
594603
595604 for _ , package in pairs (store .packages_full ) do
596605 local name_len = # package .name
606+ -- This must match what store.update_paths() does!
607+ package .id = package .author :lower () .. " /"
597608 if package .type == " game" and name_len > 5 and package .name :sub (name_len - 4 ) == " _game" then
598- package .id = package .author : lower () .. " / " .. package .name :sub (1 , name_len - 5 )
609+ package .id = package .id .. package .name :sub (1 , name_len - 5 )
599610 else
600- package .id = package .author : lower () .. " / " .. package .name
611+ package .id = package .id .. package .name
601612 end
602613
614+ package .url_part = urlencode (package .author ) .. " /" .. urlencode (package .name )
615+
603616 if package .aliases then
604617 for _ , alias in ipairs (package .aliases ) do
605618 -- We currently don't support name changing
@@ -1013,9 +1026,9 @@ function store.handle_submit(this, fields)
10131026 end
10141027
10151028 if fields [" view_" .. i ] then
1016- local url = (" %s/packages/%s/%s ?protocol_version=%d" ):format (
1017- core .settings :get (" contentdb_url" ),
1018- package . author , package . name , core .get_max_supp_proto ())
1029+ local url = (" %s/packages/%s?protocol_version=%d" ):format (
1030+ core .settings :get (" contentdb_url" ), package . url_part ,
1031+ core .get_max_supp_proto ())
10191032 core .open_url (url )
10201033 return true
10211034 end
0 commit comments