2 * Bittorrent Client using Qt and libtorrent.
3 * Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 * In addition, as a special exception, the copyright holders give permission to
20 * link this program with the OpenSSL project's "OpenSSL" library (or with
21 * modified versions of it that use the same license as the "OpenSSL" library),
22 * and distribute the linked executables. You must obey the GNU General Public
23 * License in all respects for all of the code used other than "OpenSSL". If you
24 * modify file(s), you may extend this exception to your version of the file(s),
25 * but you are not obligated to do so. If you do not wish to do so, delete this
26 * exception statement from your version.
33 #include <boost/version.hpp>
34 #include <libtorrent/version.hpp>
35 #include <openssl/crypto.h>
36 #include <openssl/opensslv.h>
40 #include <QCoreApplication>
43 #include <QMimeDatabase>
44 #include <QRegularExpression>
47 #include <QStringView>
50 #include "base/net/downloadmanager.h"
51 #include "base/path.h"
52 #include "base/unicodestrings.h"
53 #include "base/utils/string.h"
57 const struct { const char *source
; const char *comment
; } units
[] =
59 QT_TRANSLATE_NOOP3("misc", "B", "bytes"),
60 QT_TRANSLATE_NOOP3("misc", "KiB", "kibibytes (1024 bytes)"),
61 QT_TRANSLATE_NOOP3("misc", "MiB", "mebibytes (1024 kibibytes)"),
62 QT_TRANSLATE_NOOP3("misc", "GiB", "gibibytes (1024 mibibytes)"),
63 QT_TRANSLATE_NOOP3("misc", "TiB", "tebibytes (1024 gibibytes)"),
64 QT_TRANSLATE_NOOP3("misc", "PiB", "pebibytes (1024 tebibytes)"),
65 QT_TRANSLATE_NOOP3("misc", "EiB", "exbibytes (1024 pebibytes)")
68 // return best userfriendly storage unit (B, KiB, MiB, GiB, TiB, ...)
69 // use Binary prefix standards from IEC 60027-2
70 // see http://en.wikipedia.org/wiki/Kilobyte
71 // value must be given in bytes
72 // to send numbers instead of strings with suffixes
73 struct SplitToFriendlyUnitResult
76 Utils::Misc::SizeUnit unit
;
79 std::optional
<SplitToFriendlyUnitResult
> splitToFriendlyUnit(const qint64 bytes
, const int unitThreshold
= 1024)
85 auto value
= static_cast<qreal
>(bytes
);
87 while ((value
>= unitThreshold
) && (i
< static_cast<int>(Utils::Misc::SizeUnit::ExbiByte
)))
92 return {{value
, static_cast<Utils::Misc::SizeUnit
>(i
)}};
96 QString
Utils::Misc::unitString(const SizeUnit unit
, const bool isSpeed
)
98 const auto &unitString
= units
[static_cast<int>(unit
)];
99 QString ret
= QCoreApplication::translate("misc", unitString
.source
, unitString
.comment
);
101 ret
+= QCoreApplication::translate("misc", "/s", "per second");
105 QString
Utils::Misc::friendlyUnit(const qint64 bytes
, const bool isSpeed
, const int precision
)
107 const std::optional
<SplitToFriendlyUnitResult
> result
= splitToFriendlyUnit(bytes
);
109 return QCoreApplication::translate("misc", "Unknown", "Unknown (size)");
111 const int digitPrecision
= (precision
>= 0) ? precision
: friendlyUnitPrecision(result
->unit
);
112 return Utils::String::fromDouble(result
->value
, digitPrecision
)
113 + QChar::Nbsp
+ unitString(result
->unit
, isSpeed
);
116 QString
Utils::Misc::friendlyUnitCompact(const qint64 bytes
)
118 // avoid 1000-1023 values, use next larger unit instead
119 const std::optional
<SplitToFriendlyUnitResult
> result
= splitToFriendlyUnit(bytes
, 1000);
121 return QCoreApplication::translate("misc", "Unknown", "Unknown (size)");
123 int precision
= 0; // >= 100
124 if (result
->value
< 10)
125 precision
= 2; // 0 - 9.99
126 if (result
->value
< 100)
127 precision
= 1; // 10 - 99.9
129 return Utils::String::fromDouble(result
->value
, precision
)
130 // use only one character for unit representation
131 + QChar::Nbsp
+ unitString(result
->unit
, false)[0];
134 int Utils::Misc::friendlyUnitPrecision(const SizeUnit unit
)
136 // friendlyUnit's number of digits after the decimal point
141 case SizeUnit::KibiByte
:
142 case SizeUnit::MebiByte
:
144 case SizeUnit::GibiByte
:
151 qlonglong
Utils::Misc::sizeInBytes(qreal size
, const Utils::Misc::SizeUnit unit
)
153 for (int i
= 0; i
< static_cast<int>(unit
); ++i
)
158 bool Utils::Misc::isPreviewable(const Path
&filePath
)
160 const QString mime
= QMimeDatabase().mimeTypeForFile(filePath
.data(), QMimeDatabase::MatchExtension
).name();
162 if (mime
.startsWith(u
"audio", Qt::CaseInsensitive
)
163 || mime
.startsWith(u
"video", Qt::CaseInsensitive
))
168 const QSet
<QString
> multimediaExtensions
=
213 return multimediaExtensions
.contains(filePath
.extension().toUpper());
216 bool Utils::Misc::isTorrentLink(const QString
&str
)
218 return str
.startsWith(u
"magnet:", Qt::CaseInsensitive
)
219 || str
.endsWith(TORRENT_FILE_EXTENSION
, Qt::CaseInsensitive
)
220 || (!str
.startsWith(u
"file:", Qt::CaseInsensitive
)
221 && Net::DownloadManager::hasSupportedScheme(str
));
224 QString
Utils::Misc::userFriendlyDuration(const qlonglong seconds
, const qlonglong maxCap
, const TimeResolution resolution
)
228 if ((maxCap
>= 0) && (seconds
>= maxCap
))
236 if (resolution
== TimeResolution::Minutes
)
237 return QCoreApplication::translate("misc", "< 1m", "< 1 minute");
239 return QCoreApplication::translate("misc", "%1s", "e.g: 10 seconds").arg(QString::number(seconds
));
242 qlonglong minutes
= (seconds
/ 60);
244 return QCoreApplication::translate("misc", "%1m", "e.g: 10 minutes").arg(QString::number(minutes
));
246 qlonglong hours
= (minutes
/ 60);
249 minutes
-= (hours
* 60);
250 return QCoreApplication::translate("misc", "%1h %2m", "e.g: 3 hours 5 minutes").arg(QString::number(hours
), QString::number(minutes
));
253 qlonglong days
= (hours
/ 24);
256 hours
-= (days
* 24);
257 return QCoreApplication::translate("misc", "%1d %2h", "e.g: 2 days 10 hours").arg(QString::number(days
), QString::number(hours
));
260 qlonglong years
= (days
/ 365);
261 days
-= (years
* 365);
262 return QCoreApplication::translate("misc", "%1y %2d", "e.g: 2 years 10 days").arg(QString::number(years
), QString::number(days
));
265 QString
Utils::Misc::languageToLocalizedString(const QStringView localeStr
)
267 if (localeStr
.startsWith(u
"eo", Qt::CaseInsensitive
))
269 // QLocale doesn't work with that locale. Esperanto isn't a "real" language.
270 return C_LOCALE_ESPERANTO
;
273 if (localeStr
.startsWith(u
"ltg", Qt::CaseInsensitive
))
275 // QLocale doesn't work with that locale.
276 return C_LOCALE_LATGALIAN
;
279 const QLocale locale
{localeStr
};
280 switch (locale
.language())
282 case QLocale::Arabic
: return C_LOCALE_ARABIC
;
283 case QLocale::Armenian
: return C_LOCALE_ARMENIAN
;
284 case QLocale::Azerbaijani
: return C_LOCALE_AZERBAIJANI
;
285 case QLocale::Basque
: return C_LOCALE_BASQUE
;
286 case QLocale::Bulgarian
: return C_LOCALE_BULGARIAN
;
287 case QLocale::Byelorussian
: return C_LOCALE_BYELORUSSIAN
;
288 case QLocale::Catalan
: return C_LOCALE_CATALAN
;
289 case QLocale::Chinese
:
290 switch (locale
.territory())
292 case QLocale::China
: return C_LOCALE_CHINESE_SIMPLIFIED
;
293 case QLocale::HongKong
: return C_LOCALE_CHINESE_TRADITIONAL_HK
;
294 default: return C_LOCALE_CHINESE_TRADITIONAL_TW
;
296 case QLocale::Croatian
: return C_LOCALE_CROATIAN
;
297 case QLocale::Czech
: return C_LOCALE_CZECH
;
298 case QLocale::Danish
: return C_LOCALE_DANISH
;
299 case QLocale::Dutch
: return C_LOCALE_DUTCH
;
300 case QLocale::English
:
301 switch (locale
.territory())
303 case QLocale::Australia
: return C_LOCALE_ENGLISH_AUSTRALIA
;
304 case QLocale::UnitedKingdom
: return C_LOCALE_ENGLISH_UNITEDKINGDOM
;
305 default: return C_LOCALE_ENGLISH
;
307 case QLocale::Estonian
: return C_LOCALE_ESTONIAN
;
308 case QLocale::Finnish
: return C_LOCALE_FINNISH
;
309 case QLocale::French
: return C_LOCALE_FRENCH
;
310 case QLocale::Galician
: return C_LOCALE_GALICIAN
;
311 case QLocale::Georgian
: return C_LOCALE_GEORGIAN
;
312 case QLocale::German
: return C_LOCALE_GERMAN
;
313 case QLocale::Greek
: return C_LOCALE_GREEK
;
314 case QLocale::Hebrew
: return C_LOCALE_HEBREW
;
315 case QLocale::Hindi
: return C_LOCALE_HINDI
;
316 case QLocale::Hungarian
: return C_LOCALE_HUNGARIAN
;
317 case QLocale::Icelandic
: return C_LOCALE_ICELANDIC
;
318 case QLocale::Indonesian
: return C_LOCALE_INDONESIAN
;
319 case QLocale::Italian
: return C_LOCALE_ITALIAN
;
320 case QLocale::Japanese
: return C_LOCALE_JAPANESE
;
321 case QLocale::Korean
: return C_LOCALE_KOREAN
;
322 case QLocale::Latvian
: return C_LOCALE_LATVIAN
;
323 case QLocale::Lithuanian
: return C_LOCALE_LITHUANIAN
;
324 case QLocale::Malay
: return C_LOCALE_MALAY
;
325 case QLocale::Mongolian
: return C_LOCALE_MONGOLIAN
;
326 case QLocale::NorwegianBokmal
: return C_LOCALE_NORWEGIAN
;
327 case QLocale::Occitan
: return C_LOCALE_OCCITAN
;
328 case QLocale::Persian
: return C_LOCALE_PERSIAN
;
329 case QLocale::Polish
: return C_LOCALE_POLISH
;
330 case QLocale::Portuguese
:
331 if (locale
.territory() == QLocale::Brazil
)
332 return C_LOCALE_PORTUGUESE_BRAZIL
;
333 return C_LOCALE_PORTUGUESE
;
334 case QLocale::Romanian
: return C_LOCALE_ROMANIAN
;
335 case QLocale::Russian
: return C_LOCALE_RUSSIAN
;
336 case QLocale::Serbian
: return C_LOCALE_SERBIAN
;
337 case QLocale::Slovak
: return C_LOCALE_SLOVAK
;
338 case QLocale::Slovenian
: return C_LOCALE_SLOVENIAN
;
339 case QLocale::Spanish
: return C_LOCALE_SPANISH
;
340 case QLocale::Swedish
: return C_LOCALE_SWEDISH
;
341 case QLocale::Thai
: return C_LOCALE_THAI
;
342 case QLocale::Turkish
: return C_LOCALE_TURKISH
;
343 case QLocale::Ukrainian
: return C_LOCALE_UKRAINIAN
;
344 case QLocale::Uzbek
: return C_LOCALE_UZBEK
;
345 case QLocale::Vietnamese
: return C_LOCALE_VIETNAMESE
;
347 const QString lang
= QLocale::languageToString(locale
.language());
348 qWarning() << "Unrecognized language name: " << lang
;
353 QString
Utils::Misc::parseHtmlLinks(const QString
&rawText
)
355 QString result
= rawText
;
356 static const QRegularExpression
reURL(
357 u
"(\\s|^)" // start with whitespace or beginning of line
359 u
"(" // case 1 -- URL with scheme
360 u
"(http(s?))\\://" // start with scheme
361 u
"([a-zA-Z0-9_-]+\\.)+" // domainpart. at least one of these must exist
362 u
"([a-zA-Z0-9\\?%=&/_\\.:#;-]+)" // everything to 1st non-URI char, must be at least one char after the previous dot (cannot use ".*" because it can be too greedy)
365 u
"(" // case 2a -- no scheme, contains common TLD example.com
366 u
"([a-zA-Z0-9_-]+\\.)+" // domainpart. at least one of these must exist
367 u
"(?=" // must be followed by TLD
368 u
"AERO|aero|" // N.B. assertions are non-capturing
392 u
"([a-zA-Z0-9\\?%=&/_\\.:#;-]+)" // everything to 1st non-URI char, must be at least one char after the previous dot (cannot use ".*" because it can be too greedy)
395 u
"(" // case 2b no scheme, no TLD, must have at least 2 alphanum strings plus uncommon TLD string --> del.icio.us
396 u
"([a-zA-Z0-9_-]+\\.) {2,}" // 2 or more domainpart. --> del.icio.
397 u
"[a-zA-Z]{2,}" // one ab (2 char or longer) --> us
398 u
"([a-zA-Z0-9\\?%=&/_\\.:#;-]*)" // everything to 1st non-URI char, maybe nothing in case of del.icio.us/path
404 result
.replace(reURL
, u
"\\1<a href=\"\\2\">\\2</a>"_s
);
406 // Capture links without scheme
407 const QRegularExpression
reNoScheme(u
"<a\\s+href=\"(?!https?)([a-zA-Z0-9\\?%=&/_\\.-:#]+)\\s*\">"_s
);
408 result
.replace(reNoScheme
, u
"<a href=\"http://\\1\">"_s
);
410 // to preserve plain text formatting
411 result
= u
"<p style=\"white-space: pre-wrap;\">" + result
+ u
"</p>";
415 QString
Utils::Misc::osName()
417 // static initialization for usage in signal handler
418 static const QString name
=
420 .arg(QSysInfo::prettyProductName()
421 , QSysInfo::kernelVersion()
422 , QSysInfo::currentCpuArchitecture());
426 QString
Utils::Misc::boostVersionString()
428 // static initialization for usage in signal handler
429 static const QString ver
= u
"%1.%2.%3"_s
430 .arg(QString::number(BOOST_VERSION
/ 100000)
431 , QString::number((BOOST_VERSION
/ 100) % 1000)
432 , QString::number(BOOST_VERSION
% 100));
436 QString
Utils::Misc::libtorrentVersionString()
438 // static initialization for usage in signal handler
439 static const auto version
{QString::fromLatin1(lt::version())};
443 QString
Utils::Misc::opensslVersionString()
445 // static initialization for usage in signal handler
446 static const auto version
{QString::fromLatin1(::OpenSSL_version(OPENSSL_VERSION
))
447 .section(u
' ', 1, 1)};
451 QString
Utils::Misc::zlibVersionString()
453 // static initialization for usage in signal handler
454 static const auto version
{QString::fromLatin1(zlibVersion())};