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/path.h"
51 #include "base/unicodestrings.h"
52 #include "base/utils/string.h"
56 const struct { const char *source
; const char *comment
; } units
[] =
58 QT_TRANSLATE_NOOP3("misc", "B", "bytes"),
59 QT_TRANSLATE_NOOP3("misc", "KiB", "kibibytes (1024 bytes)"),
60 QT_TRANSLATE_NOOP3("misc", "MiB", "mebibytes (1024 kibibytes)"),
61 QT_TRANSLATE_NOOP3("misc", "GiB", "gibibytes (1024 mibibytes)"),
62 QT_TRANSLATE_NOOP3("misc", "TiB", "tebibytes (1024 gibibytes)"),
63 QT_TRANSLATE_NOOP3("misc", "PiB", "pebibytes (1024 tebibytes)"),
64 QT_TRANSLATE_NOOP3("misc", "EiB", "exbibytes (1024 pebibytes)")
67 // return best userfriendly storage unit (B, KiB, MiB, GiB, TiB, ...)
68 // use Binary prefix standards from IEC 60027-2
69 // see http://en.wikipedia.org/wiki/Kilobyte
70 // value must be given in bytes
71 // to send numbers instead of strings with suffixes
72 struct SplitToFriendlyUnitResult
75 Utils::Misc::SizeUnit unit
;
78 std::optional
<SplitToFriendlyUnitResult
> splitToFriendlyUnit(const qint64 bytes
, const int unitThreshold
= 1024)
84 auto value
= static_cast<qreal
>(bytes
);
86 while ((value
>= unitThreshold
) && (i
< static_cast<int>(Utils::Misc::SizeUnit::ExbiByte
)))
91 return {{value
, static_cast<Utils::Misc::SizeUnit
>(i
)}};
95 QString
Utils::Misc::unitString(const SizeUnit unit
, const bool isSpeed
)
97 const auto &unitString
= units
[static_cast<int>(unit
)];
98 QString ret
= QCoreApplication::translate("misc", unitString
.source
, unitString
.comment
);
100 ret
+= QCoreApplication::translate("misc", "/s", "per second");
104 QString
Utils::Misc::friendlyUnit(const qint64 bytes
, const bool isSpeed
, const int precision
)
106 const std::optional
<SplitToFriendlyUnitResult
> result
= splitToFriendlyUnit(bytes
);
108 return QCoreApplication::translate("misc", "Unknown", "Unknown (size)");
110 const int digitPrecision
= (precision
>= 0) ? precision
: friendlyUnitPrecision(result
->unit
);
111 return Utils::String::fromDouble(result
->value
, digitPrecision
)
112 + QChar::Nbsp
+ unitString(result
->unit
, isSpeed
);
115 QString
Utils::Misc::friendlyUnitCompact(const qint64 bytes
)
117 // avoid 1000-1023 values, use next larger unit instead
118 const std::optional
<SplitToFriendlyUnitResult
> result
= splitToFriendlyUnit(bytes
, 1000);
120 return QCoreApplication::translate("misc", "Unknown", "Unknown (size)");
122 int precision
= 0; // >= 100
123 if (result
->value
< 10)
124 precision
= 2; // 0 - 9.99
125 if (result
->value
< 100)
126 precision
= 1; // 10 - 99.9
128 return Utils::String::fromDouble(result
->value
, precision
)
129 // use only one character for unit representation
130 + QChar::Nbsp
+ unitString(result
->unit
, false)[0];
133 int Utils::Misc::friendlyUnitPrecision(const SizeUnit unit
)
135 // friendlyUnit's number of digits after the decimal point
140 case SizeUnit::KibiByte
:
141 case SizeUnit::MebiByte
:
143 case SizeUnit::GibiByte
:
150 qlonglong
Utils::Misc::sizeInBytes(qreal size
, const Utils::Misc::SizeUnit unit
)
152 for (int i
= 0; i
< static_cast<int>(unit
); ++i
)
157 bool Utils::Misc::isPreviewable(const Path
&filePath
)
159 const QString mime
= QMimeDatabase().mimeTypeForFile(filePath
.data(), QMimeDatabase::MatchExtension
).name();
161 if (mime
.startsWith(u
"audio", Qt::CaseInsensitive
)
162 || mime
.startsWith(u
"video", Qt::CaseInsensitive
))
167 const QSet
<QString
> multimediaExtensions
=
212 return multimediaExtensions
.contains(filePath
.extension().toUpper());
215 QString
Utils::Misc::userFriendlyDuration(const qlonglong seconds
, const qlonglong maxCap
, const TimeResolution resolution
)
219 if ((maxCap
>= 0) && (seconds
>= maxCap
))
227 if (resolution
== TimeResolution::Minutes
)
228 return QCoreApplication::translate("misc", "< 1m", "< 1 minute");
230 return QCoreApplication::translate("misc", "%1s", "e.g: 10 seconds").arg(QString::number(seconds
));
233 qlonglong minutes
= (seconds
/ 60);
235 return QCoreApplication::translate("misc", "%1m", "e.g: 10 minutes").arg(QString::number(minutes
));
237 qlonglong hours
= (minutes
/ 60);
240 minutes
-= (hours
* 60);
241 return QCoreApplication::translate("misc", "%1h %2m", "e.g: 3 hours 5 minutes").arg(QString::number(hours
), QString::number(minutes
));
244 qlonglong days
= (hours
/ 24);
247 hours
-= (days
* 24);
248 return QCoreApplication::translate("misc", "%1d %2h", "e.g: 2 days 10 hours").arg(QString::number(days
), QString::number(hours
));
251 qlonglong years
= (days
/ 365);
252 days
-= (years
* 365);
253 return QCoreApplication::translate("misc", "%1y %2d", "e.g: 2 years 10 days").arg(QString::number(years
), QString::number(days
));
256 QString
Utils::Misc::languageToLocalizedString(const QStringView localeStr
)
258 if (localeStr
.startsWith(u
"eo", Qt::CaseInsensitive
))
260 // QLocale doesn't work with that locale. Esperanto isn't a "real" language.
261 return C_LOCALE_ESPERANTO
;
264 if (localeStr
.startsWith(u
"ltg", Qt::CaseInsensitive
))
266 // QLocale doesn't work with that locale.
267 return C_LOCALE_LATGALIAN
;
270 const QLocale locale
{localeStr
};
271 switch (locale
.language())
273 case QLocale::Arabic
: return C_LOCALE_ARABIC
;
274 case QLocale::Armenian
: return C_LOCALE_ARMENIAN
;
275 case QLocale::Azerbaijani
: return C_LOCALE_AZERBAIJANI
;
276 case QLocale::Basque
: return C_LOCALE_BASQUE
;
277 case QLocale::Bulgarian
: return C_LOCALE_BULGARIAN
;
278 case QLocale::Byelorussian
: return C_LOCALE_BYELORUSSIAN
;
279 case QLocale::Catalan
: return C_LOCALE_CATALAN
;
280 case QLocale::Chinese
:
281 switch (locale
.territory())
283 case QLocale::China
: return C_LOCALE_CHINESE_SIMPLIFIED
;
284 case QLocale::HongKong
: return C_LOCALE_CHINESE_TRADITIONAL_HK
;
285 default: return C_LOCALE_CHINESE_TRADITIONAL_TW
;
287 case QLocale::Croatian
: return C_LOCALE_CROATIAN
;
288 case QLocale::Czech
: return C_LOCALE_CZECH
;
289 case QLocale::Danish
: return C_LOCALE_DANISH
;
290 case QLocale::Dutch
: return C_LOCALE_DUTCH
;
291 case QLocale::English
:
292 switch (locale
.territory())
294 case QLocale::Australia
: return C_LOCALE_ENGLISH_AUSTRALIA
;
295 case QLocale::UnitedKingdom
: return C_LOCALE_ENGLISH_UNITEDKINGDOM
;
296 default: return C_LOCALE_ENGLISH
;
298 case QLocale::Estonian
: return C_LOCALE_ESTONIAN
;
299 case QLocale::Finnish
: return C_LOCALE_FINNISH
;
300 case QLocale::French
: return C_LOCALE_FRENCH
;
301 case QLocale::Galician
: return C_LOCALE_GALICIAN
;
302 case QLocale::Georgian
: return C_LOCALE_GEORGIAN
;
303 case QLocale::German
: return C_LOCALE_GERMAN
;
304 case QLocale::Greek
: return C_LOCALE_GREEK
;
305 case QLocale::Hebrew
: return C_LOCALE_HEBREW
;
306 case QLocale::Hindi
: return C_LOCALE_HINDI
;
307 case QLocale::Hungarian
: return C_LOCALE_HUNGARIAN
;
308 case QLocale::Icelandic
: return C_LOCALE_ICELANDIC
;
309 case QLocale::Indonesian
: return C_LOCALE_INDONESIAN
;
310 case QLocale::Italian
: return C_LOCALE_ITALIAN
;
311 case QLocale::Japanese
: return C_LOCALE_JAPANESE
;
312 case QLocale::Korean
: return C_LOCALE_KOREAN
;
313 case QLocale::Latvian
: return C_LOCALE_LATVIAN
;
314 case QLocale::Lithuanian
: return C_LOCALE_LITHUANIAN
;
315 case QLocale::Malay
: return C_LOCALE_MALAY
;
316 case QLocale::Mongolian
: return C_LOCALE_MONGOLIAN
;
317 case QLocale::NorwegianBokmal
: return C_LOCALE_NORWEGIAN
;
318 case QLocale::Occitan
: return C_LOCALE_OCCITAN
;
319 case QLocale::Persian
: return C_LOCALE_PERSIAN
;
320 case QLocale::Polish
: return C_LOCALE_POLISH
;
321 case QLocale::Portuguese
:
322 if (locale
.territory() == QLocale::Brazil
)
323 return C_LOCALE_PORTUGUESE_BRAZIL
;
324 return C_LOCALE_PORTUGUESE
;
325 case QLocale::Romanian
: return C_LOCALE_ROMANIAN
;
326 case QLocale::Russian
: return C_LOCALE_RUSSIAN
;
327 case QLocale::Serbian
: return C_LOCALE_SERBIAN
;
328 case QLocale::Slovak
: return C_LOCALE_SLOVAK
;
329 case QLocale::Slovenian
: return C_LOCALE_SLOVENIAN
;
330 case QLocale::Spanish
: return C_LOCALE_SPANISH
;
331 case QLocale::Swedish
: return C_LOCALE_SWEDISH
;
332 case QLocale::Thai
: return C_LOCALE_THAI
;
333 case QLocale::Turkish
: return C_LOCALE_TURKISH
;
334 case QLocale::Ukrainian
: return C_LOCALE_UKRAINIAN
;
335 case QLocale::Uzbek
: return C_LOCALE_UZBEK
;
336 case QLocale::Vietnamese
: return C_LOCALE_VIETNAMESE
;
338 const QString lang
= QLocale::languageToString(locale
.language());
339 qWarning() << "Unrecognized language name: " << lang
;
344 QString
Utils::Misc::parseHtmlLinks(const QString
&rawText
)
346 QString result
= rawText
;
347 static const QRegularExpression
reURL(
348 u
"(\\s|^)" // start with whitespace or beginning of line
350 u
"(" // case 1 -- URL with scheme
351 u
"(http(s?))\\://" // start with scheme
352 u
"([a-zA-Z0-9_-]+\\.)+" // domainpart. at least one of these must exist
353 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)
356 u
"(" // case 2a -- no scheme, contains common TLD example.com
357 u
"([a-zA-Z0-9_-]+\\.)+" // domainpart. at least one of these must exist
358 u
"(?=" // must be followed by TLD
359 u
"AERO|aero|" // N.B. assertions are non-capturing
383 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)
386 u
"(" // case 2b no scheme, no TLD, must have at least 2 alphanum strings plus uncommon TLD string --> del.icio.us
387 u
"([a-zA-Z0-9_-]+\\.) {2,}" // 2 or more domainpart. --> del.icio.
388 u
"[a-zA-Z]{2,}" // one ab (2 char or longer) --> us
389 u
"([a-zA-Z0-9\\?%=&/_\\.:#;-]*)" // everything to 1st non-URI char, maybe nothing in case of del.icio.us/path
395 result
.replace(reURL
, u
"\\1<a href=\"\\2\">\\2</a>"_s
);
397 // Capture links without scheme
398 const QRegularExpression
reNoScheme(u
"<a\\s+href=\"(?!https?)([a-zA-Z0-9\\?%=&/_\\.-:#]+)\\s*\">"_s
);
399 result
.replace(reNoScheme
, u
"<a href=\"http://\\1\">"_s
);
401 // to preserve plain text formatting
402 result
= u
"<p style=\"white-space: pre-wrap;\">" + result
+ u
"</p>";
406 QString
Utils::Misc::osName()
408 // static initialization for usage in signal handler
409 static const QString name
=
411 .arg(QSysInfo::prettyProductName()
412 , QSysInfo::kernelVersion()
413 , QSysInfo::currentCpuArchitecture());
417 QString
Utils::Misc::boostVersionString()
419 // static initialization for usage in signal handler
420 static const QString ver
= u
"%1.%2.%3"_s
421 .arg(QString::number(BOOST_VERSION
/ 100000)
422 , QString::number((BOOST_VERSION
/ 100) % 1000)
423 , QString::number(BOOST_VERSION
% 100));
427 QString
Utils::Misc::libtorrentVersionString()
429 // static initialization for usage in signal handler
430 static const auto version
{QString::fromLatin1(lt::version())};
434 QString
Utils::Misc::opensslVersionString()
436 // static initialization for usage in signal handler
437 static const auto version
{QString::fromLatin1(::OpenSSL_version(OPENSSL_VERSION
))
438 .section(u
' ', 1, 1)};
442 QString
Utils::Misc::zlibVersionString()
444 // static initialization for usage in signal handler
445 static const auto version
{QString::fromLatin1(zlibVersion())};