1 /**
2 * UGENE - Integrated Bioinformatics Tools.
3 * Copyright (C) 2008-2021 UniPro <ugene@unipro.ru>
4 * http://ugene.net
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 * MA 02110-1301, USA.
20 */
21
22 #include "GUrl.h"
23
24 #include <QDataStream>
25 #include <QDir>
26
27 #include "U2SafePoints.h"
28
29 #ifdef Q_OS_WIN
30 # include <windows.h>
31 #endif
32
33 namespace U2 {
34
makeFilePathCanonical(const QString & originalUrl)35 static QString makeFilePathCanonical(const QString &originalUrl) {
36 // ensure that name is canonical
37 QString result = originalUrl.trimmed();
38
39 QString fileUrlPrefix = "file://";
40 if (result.startsWith(fileUrlPrefix)) {
41 result = result.mid(fileUrlPrefix.length());
42 #ifdef Q_OS_WIN
43 // on Windows, all slashes after "file:" can be trimmed, on Unix/Mac one must be kept to specify that it's an absolute path
44 if (result.startsWith("/")) {
45 while (result.startsWith("/")) {
46 result = result.mid(1);
47 }
48 } else {
49 result.prepend("//");
50 }
51 #endif
52 }
53
54 // Windows drive letter, Qt resource designation or Samba share designation and name
55 QString prefix;
56
57 if (originalUrl.startsWith(':')) { // is a Qt resource
58 prefix = ":";
59 result = result.mid(1);
60 } else {
61 result = QFileInfo(result).absoluteFilePath();
62 }
63
64 #ifdef Q_OS_WIN
65 bool isSambaPath = false;
66 if (result.startsWith("//") && prefix.isEmpty()) {
67 // keep Samba share designation
68 prefix = "//";
69 isSambaPath = true;
70 }
71 #endif
72
73 QStringList parts = result.split('/', QString::SkipEmptyParts);
74 if (parts.size() > 0) {
75 QStringList canonicalParts;
76 #ifdef Q_OS_WIN
77 // append drive spec letter or Samba server name to the prefix
78 if (isSambaPath) {
79 prefix += parts.takeFirst();
80 } else if (parts.at(0).endsWith(':') && parts.at(0).length() == 2 && prefix.isEmpty()) { // Windows drive letter designation
81 prefix = parts.takeFirst();
82 }
83 #endif
84 // get rid of redundant '.' and '..' now
85 QStringListIterator it(parts);
86 while (it.hasNext()) {
87 QString part = it.next();
88 if (part == ".") {
89 continue;
90 } else if (part == "..") {
91 if (!canonicalParts.isEmpty()) {
92 canonicalParts.removeLast();
93 }
94 } else if (!part.isEmpty()) {
95 canonicalParts.append(part);
96 }
97 }
98 result = prefix + "/" + canonicalParts.join("/");
99 }
100
101 return result;
102 }
103
getURLType(const QString & rawUrl)104 GUrlType GUrl::getURLType(const QString &rawUrl) {
105 GUrlType result = GUrl_File;
106 if (rawUrl.startsWith("http://") || rawUrl.startsWith("https://")) {
107 result = GUrl_Http;
108 } else if (rawUrl.startsWith("ftp://")) {
109 result = GUrl_Ftp;
110 } else if (!rawUrl.startsWith("file://") && rawUrl.contains(QRegExp("^([\\.\\w-]+@)?[\\.\\w-]+:\\d*(/[\\w-]*)?$"))) {
111 return GUrl_Network;
112 } else if (rawUrl.startsWith(U2_VFS_URL_PREFIX)) {
113 result = GUrl_VFSFile;
114 }
115 return result;
116 }
117
118 // constructs url specified by string. The type is parsed
GUrl(const QString & _urlString)119 GUrl::GUrl(const QString &_urlString) {
120 urlString = _urlString;
121 type = getURLType(urlString);
122 if (type == GUrl_File) {
123 urlString = makeFilePathCanonical(urlString);
124 }
125 }
126
127 // constructs url specified by string. The type provided as param
GUrl(const QString & _urlString,const GUrlType _type)128 GUrl::GUrl(const QString &_urlString, const GUrlType _type) {
129 urlString = _urlString;
130 type = _type;
131 if (type == GUrl_File) {
132 urlString = makeFilePathCanonical(urlString);
133 }
134 }
135
GUrl(const GUrl & anotherUrl)136 GUrl::GUrl(const GUrl &anotherUrl) {
137 urlString = anotherUrl.getURLString();
138 type = anotherUrl.getType();
139 }
140
operator ==(const GUrl & url) const141 bool GUrl::operator==(const GUrl &url) const {
142 return urlString == url.getURLString();
143 }
144
operator !=(const GUrl & url) const145 bool GUrl::operator!=(const GUrl &url) const {
146 return !(*this == url);
147 }
148
getURLStringAnsi(int codePage) const149 QByteArray GUrl::getURLStringAnsi(int codePage) const {
150 #ifdef Q_OS_WIN
151 std::wstring wPath = getURLString().toStdWString();
152 codePage = codePage < 0 ? CP_THREAD_ACP : codePage;
153
154 DWORD buffSize = WideCharToMultiByte(codePage, 0, wPath.c_str(), -1, nullptr, 0, nullptr, nullptr);
155 if (!buffSize) {
156 return QByteArray();
157 }
158
159 char *buffer = new char[buffSize + 1];
160 if (!WideCharToMultiByte(codePage, 0, wPath.c_str(), -1, buffer, buffSize, nullptr, nullptr)) {
161 delete[] buffer;
162 return QByteArray();
163 }
164 QByteArray bytes = QByteArray(buffer, buffSize + 1);
165 delete[] buffer;
166 return bytes;
167 #else
168 Q_UNUSED(codePage);
169 return getURLString().toLocal8Bit();
170 #endif // Q_OS_WIN
171 }
172
path(const GUrl * url)173 static QString path(const GUrl *url) {
174 // TODO: parse HTTP and other formats for path part
175 QString result;
176 if (url->isVFSFile()) {
177 return result;
178 }
179 result = url->getURLString();
180 return result;
181 }
182
dirPath() const183 QString GUrl::dirPath() const {
184 QString result;
185 if (isVFSFile()) {
186 return result;
187 }
188 CHECK(!isNetworkSource(), result);
189
190 result = QFileInfo(path(this)).absoluteDir().absolutePath();
191 return result;
192 }
193
fileName() const194 QString GUrl::fileName() const {
195 QString result;
196 if (isVFSFile()) {
197 return result;
198 }
199 CHECK(!isNetworkSource(), result);
200
201 result = QFileInfo(path(this)).fileName();
202 return result;
203 }
204
baseFileName() const205 QString GUrl::baseFileName() const {
206 QString result;
207 CHECK(!isNetworkSource(), result);
208
209 if (isVFSFile()) {
210 QStringList args = urlString.split(U2_VFS_FILE_SEPARATOR, QString::SkipEmptyParts, Qt::CaseSensitive);
211 if (2 == args.size()) {
212 result = QFileInfo(args.at(1)).baseName();
213 if (!result.length()) {
214 result = QFileInfo(args.at(1)).fileName();
215 }
216 }
217 } else {
218 result = QFileInfo(path(this)).baseName();
219 if (!result.length()) {
220 result = QFileInfo(path(this)).fileName();
221 }
222 }
223 return result;
224 }
225
lastFileSuffix() const226 QString GUrl::lastFileSuffix() const {
227 QString result;
228 if (isVFSFile()) {
229 return result;
230 }
231 CHECK(!isNetworkSource(), result);
232
233 result = QFileInfo(path(this)).suffix();
234 return result;
235 }
236
completeFileSuffix() const237 QString GUrl::completeFileSuffix() const {
238 QString result;
239 if (isVFSFile()) {
240 return result;
241 }
242 CHECK(!isNetworkSource(), result);
243
244 result = QFileInfo(path(this)).completeSuffix();
245 return result;
246 }
247
registerMetas()248 static bool registerMetas() {
249 qRegisterMetaType<GUrl>("GUrl");
250 qRegisterMetaTypeStreamOperators<GUrl>("U2::GUrl");
251
252 return true;
253 }
254
operator <<(QDataStream & out,const GUrl & myObj)255 QDataStream &operator<<(QDataStream &out, const GUrl &myObj) {
256 out << myObj.getURLString() << myObj.getType();
257 return out;
258 }
259
operator >>(QDataStream & in,GUrl & myObj)260 QDataStream &operator>>(QDataStream &in, GUrl &myObj) {
261 QString urlString;
262 in >> urlString;
263 int t;
264 in >> t;
265 GUrlType type = (GUrlType)t;
266 myObj = GUrl(urlString, type);
267 return in;
268 }
269
270 bool GUrl::registerMeta = registerMetas();
271
272 } // namespace U2
273