1 #include "singletons/Paths.hpp"
2 
3 #include "singletons/Settings.hpp"
4 
5 #include <QCoreApplication>
6 #include <QCryptographicHash>
7 #include <QDir>
8 #include <QStandardPaths>
9 #include <cassert>
10 
11 #include "common/Modes.hpp"
12 #include "util/CombinePath.hpp"
13 
14 using namespace std::literals;
15 
16 namespace chatterino {
17 
18 Paths *Paths::instance = nullptr;
19 
Paths()20 Paths::Paths()
21 {
22     this->instance = this;
23 
24     this->initAppFilePathHash();
25 
26     this->initCheckPortable();
27     this->initRootDirectory();
28     this->initSubDirectories();
29 }
30 
createFolder(const QString & folderPath)31 bool Paths::createFolder(const QString &folderPath)
32 {
33     return QDir().mkpath(folderPath);
34 }
35 
isPortable()36 bool Paths::isPortable()
37 {
38     return Modes::instance().isPortable;
39 }
40 
cacheDirectory()41 QString Paths::cacheDirectory()
42 {
43     static const auto pathSetting = [] {
44         QStringSetting cachePathSetting("/cache/path");
45 
46         cachePathSetting.connect([](const auto &newPath, auto) {
47             if (!newPath.isEmpty())
48             {
49                 QDir().mkpath(newPath);
50             }
51         });
52 
53         return cachePathSetting;
54     }();
55 
56     auto path = pathSetting.getValue();
57 
58     if (path.isEmpty())
59     {
60         return this->cacheDirectory_;
61     }
62 
63     return path;
64 }
65 
initAppFilePathHash()66 void Paths::initAppFilePathHash()
67 {
68     this->applicationFilePathHash =
69         QCryptographicHash::hash(
70             QCoreApplication::applicationFilePath().toUtf8(),
71             QCryptographicHash::Sha224)
72             .toBase64()
73             .mid(0, 32)
74             .replace("+", "-")
75             .replace("/", "x");
76 }
77 
initCheckPortable()78 void Paths::initCheckPortable()
79 {
80     this->portable_ = QFileInfo::exists(
81         combinePath(QCoreApplication::applicationDirPath(), "portable"));
82 }
83 
initRootDirectory()84 void Paths::initRootDirectory()
85 {
86     assert(this->portable_.is_initialized());
87 
88     // Root path = %APPDATA%/Chatterino or the folder that the executable
89     // resides in
90 
91     this->rootAppDataDirectory = [&]() -> QString {
92         // portable
93         if (this->isPortable())
94         {
95             return QCoreApplication::applicationDirPath();
96         }
97 
98         // permanent installation
99         QString path =
100             QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
101         if (path.isEmpty())
102         {
103             throw std::runtime_error("Could not create directory \""s +
104                                      path.toStdString() + "\"");
105         }
106 
107 // create directory Chatterino2 instead of chatterino on windows because the
108 // ladder one is takes by chatterino 1 already
109 #ifdef Q_OS_WIN
110         path.replace("chatterino", "Chatterino");
111 
112         path += "2";
113 #endif
114         return path;
115     }();
116 }
117 
initSubDirectories()118 void Paths::initSubDirectories()
119 {
120     // required the app data directory to be set first
121     assert(!this->rootAppDataDirectory.isEmpty());
122 
123     // create settings subdirectories and validate that they are created
124     // properly
125     auto makePath = [&](const std::string &name) -> QString {
126         auto path = combinePath(this->rootAppDataDirectory,
127                                 QString::fromStdString(name));
128 
129         if (!QDir().mkpath(path))
130         {
131             throw std::runtime_error("Could not create directory \""s +
132                                      path.toStdString() + "\"");
133         }
134 
135         return path;
136     };
137 
138     makePath("");
139     this->settingsDirectory = makePath("Settings");
140     this->cacheDirectory_ = makePath("Cache");
141     this->messageLogDirectory = makePath("Logs");
142     this->miscDirectory = makePath("Misc");
143     this->twitchProfileAvatars = makePath("ProfileAvatars");
144     //QDir().mkdir(this->twitchProfileAvatars + "/twitch");
145 }
146 
getPaths()147 Paths *getPaths()
148 {
149     return Paths::instance;
150 }
151 
152 }  // namespace chatterino
153