From 6522247be90894f5871c8bd225572e7f1028030a Mon Sep 17 00:00:00 2001
From: Sude <lgogdownloader@gmail.com>
Date: Fri, 28 Feb 2020 11:57:08 +0200
Subject: [PATCH] Fix filenames with ampersand

Fixes issue with filenames containing ampersand.
Also fixes issue with --download-file not assigning proper filename automatically.

New method detects the path more accurately when encountering the currently used url format but in case of unknown url format it fallbacks to the old method.
---
 include/galaxyapi.h |  1 +
 src/downloader.cpp  |  8 ++---
 src/galaxyapi.cpp   | 78 ++++++++++++++++++++++++++++-----------------
 3 files changed, 52 insertions(+), 35 deletions(-)

diff --git a/include/galaxyapi.h b/include/galaxyapi.h
index 178dafc..dcb677b 100644
--- a/include/galaxyapi.h
+++ b/include/galaxyapi.h
@@ -63,6 +63,7 @@ class galaxyAPI
         Json::Value getUserData();
         Json::Value getDependenciesJson();
         std::vector<galaxyDepotItem> getFilteredDepotItemsVectorFromJson(const Json::Value& depot_json, const std::string& galaxy_language, const std::string& galaxy_arch, const bool& is_dependency = false);
+        std::string getPathFromDownlinkUrl(const std::string& downlink_url, const std::string& gamename);
     protected:
     private:
         CurlConfig curlConf;
diff --git a/src/downloader.cpp b/src/downloader.cpp
index 37ad154..a5e382e 100644
--- a/src/downloader.cpp
+++ b/src/downloader.cpp
@@ -2402,7 +2402,6 @@ int Downloader::downloadFileWithId(const std::string& fileid_string, const std::
 
         gameDetails gd = gogGalaxy->productInfoJsonToGameDetails(productInfo, dlConf);
 
-
         auto vFiles = gd.getGameFileVector();
         gameFile gf;
         bool bFoundMatchingFile = false;
@@ -2458,15 +2457,14 @@ int Downloader::downloadFileWithId(const std::string& fileid_string, const std::
             }
         }
 
-
         std::string filename, filepath;
-        filename.assign(url.begin()+url.find_last_of("/")+1, url.begin()+url.find_first_of("?"));
+        filename = gogGalaxy->getPathFromDownlinkUrl(url, gf.gamename);
         if (output_filepath.empty())
-            filepath = Util::makeFilepath(Globals::globalConfig.dirConf.sDirectory, filename, gamename);
+            filepath = Util::makeFilepath(Globals::globalConfig.dirConf.sDirectory, filename, gf.gamename);
         else
             filepath = output_filepath;
         std::cout << "Downloading: " << filepath << std::endl;
-        result = this->downloadFile(url, filepath, xml_data, gamename);
+        result = this->downloadFile(url, filepath, xml_data, gf.gamename);
         std::cout << std::endl;
     }
 
diff --git a/src/galaxyapi.cpp b/src/galaxyapi.cpp
index 695236e..f48d552 100644
--- a/src/galaxyapi.cpp
+++ b/src/galaxyapi.cpp
@@ -390,36 +390,7 @@ std::vector<gameFile> galaxyAPI::fileJsonNodeToGameFileVector(const std::string&
                 continue;
 
             std::string downlink_url = downlinkJson["downlink"].asString();
-            std::string downlink_url_unescaped = (std::string)curl_easy_unescape(curlhandle, downlink_url.c_str(), downlink_url.size(), NULL);
-            std::string path;
-
-            // GOG has changed the url formatting few times between 2 different formats.
-            // Try to get proper file name in both cases.
-            size_t filename_end_pos;
-            if (downlink_url_unescaped.find("?path=") != std::string::npos)
-                filename_end_pos = downlink_url_unescaped.find_first_of("&");
-            else
-                filename_end_pos = downlink_url_unescaped.find_first_of("?");
-
-            if (downlink_url_unescaped.find("/" + gamename + "/") != std::string::npos)
-            {
-                path.assign(downlink_url_unescaped.begin()+downlink_url_unescaped.find("/" + gamename + "/"), downlink_url_unescaped.begin()+filename_end_pos);
-            }
-            else
-            {
-                path.assign(downlink_url_unescaped.begin()+downlink_url_unescaped.find_last_of("/")+1, downlink_url_unescaped.begin()+filename_end_pos);
-                path = "/" + gamename + "/" + path;
-            }
-
-            // Workaround for filename issue caused by different (currently unknown) url formatting scheme
-            // https://github.com/Sude-/lgogdownloader/issues/126
-            if (path.find("?") != std::string::npos)
-            {
-                if (path.find_last_of("?") > path.find_last_of("/"))
-                {
-                    path.assign(path.begin(), path.begin()+path.find_last_of("?"));
-                }
-            }
+            std::string path = this->getPathFromDownlinkUrl(downlink_url, gamename);
 
             gameFile gf;
             gf.gamename = gamename;
@@ -534,3 +505,50 @@ std::vector<galaxyDepotItem> galaxyAPI::getFilteredDepotItemsVectorFromJson(cons
 
     return items;
 }
+
+std::string galaxyAPI::getPathFromDownlinkUrl(const std::string& downlink_url, const std::string& gamename)
+{
+    std::string path;
+    std::string downlink_url_unescaped = (std::string)curl_easy_unescape(curlhandle, downlink_url.c_str(), downlink_url.size(), NULL);
+
+    // GOG has changed the url formatting few times between 2 different formats.
+    // Try to get proper file name in both cases.
+    size_t filename_end_pos;
+    if (downlink_url_unescaped.find("?path=") != std::string::npos)
+    {
+        size_t token_pos = downlink_url_unescaped.find("&token=");
+        size_t access_token_pos = downlink_url_unescaped.find("&access_token=");
+        if ((token_pos != std::string::npos) && (access_token_pos != std::string::npos))
+        {
+            filename_end_pos = std::min(token_pos, access_token_pos);
+        }
+        else
+        {
+            filename_end_pos = downlink_url_unescaped.find_first_of("&");
+        }
+    }
+    else
+        filename_end_pos = downlink_url_unescaped.find_first_of("?");
+
+    if (downlink_url_unescaped.find("/" + gamename + "/") != std::string::npos)
+    {
+        path.assign(downlink_url_unescaped.begin()+downlink_url_unescaped.find("/" + gamename + "/"), downlink_url_unescaped.begin()+filename_end_pos);
+    }
+    else
+    {
+        path.assign(downlink_url_unescaped.begin()+downlink_url_unescaped.find_last_of("/")+1, downlink_url_unescaped.begin()+filename_end_pos);
+        path = "/" + gamename + "/" + path;
+    }
+
+    // Workaround for filename issue caused by different (currently unknown) url formatting scheme
+    // https://github.com/Sude-/lgogdownloader/issues/126
+    if (path.find("?") != std::string::npos)
+    {
+        if (path.find_last_of("?") > path.find_last_of("/"))
+        {
+            path.assign(path.begin(), path.begin()+path.find_last_of("?"));
+        }
+    }
+
+    return path;
+}