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