Warn before running executable files.

This commit is contained in:
John Preston 2018-12-05 12:07:17 +04:00
parent edadc51e05
commit b10ccce44a
17 changed files with 209 additions and 92 deletions

View File

@ -1825,6 +1825,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_language_not_ready_about" = "Unfortunately, this custom language pack ({lang_name}) doesn't contain data for Telegram Desktop. You can contribute to this language pack using the {link}."; "lng_language_not_ready_about" = "Unfortunately, this custom language pack ({lang_name}) doesn't contain data for Telegram Desktop. You can contribute to this language pack using the {link}.";
"lng_language_not_ready_link" = "translations platform"; "lng_language_not_ready_link" = "translations platform";
"lng_launch_exe_warning" = "This file has a {extension} extension.\nAre you sure you want to run it?";
"lng_launch_exe_sure" = "Run";
"lng_launch_exe_dont_ask" = "Don't ask me again";
// Wnd specific // Wnd specific
"lng_wnd_choose_program_menu" = "Choose Default Program..."; "lng_wnd_choose_program_menu" = "Choose Default Program...";

View File

@ -45,7 +45,7 @@ AuthSessionSettings::Variables::Variables()
} }
QByteArray AuthSessionSettings::serialize() const { QByteArray AuthSessionSettings::serialize() const {
auto size = sizeof(qint32) * 10; auto size = sizeof(qint32) * 23;
for (auto i = _variables.soundOverrides.cbegin(), e = _variables.soundOverrides.cend(); i != e; ++i) { for (auto i = _variables.soundOverrides.cbegin(), e = _variables.soundOverrides.cend(); i != e; ++i) {
size += Serialize::stringSize(i.key()) + Serialize::stringSize(i.value()); size += Serialize::stringSize(i.key()) + Serialize::stringSize(i.value());
} }
@ -87,6 +87,7 @@ QByteArray AuthSessionSettings::serialize() const {
stream << qint32(_variables.supportChatsTimeSlice.current()); stream << qint32(_variables.supportChatsTimeSlice.current());
stream << qint32(_variables.includeMutedCounter ? 1 : 0); stream << qint32(_variables.includeMutedCounter ? 1 : 0);
stream << qint32(_variables.countUnreadMessages ? 1 : 0); stream << qint32(_variables.countUnreadMessages ? 1 : 0);
stream << qint32(_variables.exeLaunchWarning ? 1 : 0);
} }
return result; return result;
} }
@ -120,6 +121,7 @@ void AuthSessionSettings::constructFromSerialized(const QByteArray &serialized)
qint32 supportChatsTimeSlice = _variables.supportChatsTimeSlice.current(); qint32 supportChatsTimeSlice = _variables.supportChatsTimeSlice.current();
qint32 includeMutedCounter = _variables.includeMutedCounter ? 1 : 0; qint32 includeMutedCounter = _variables.includeMutedCounter ? 1 : 0;
qint32 countUnreadMessages = _variables.countUnreadMessages ? 1 : 0; qint32 countUnreadMessages = _variables.countUnreadMessages ? 1 : 0;
qint32 exeLaunchWarning = _variables.exeLaunchWarning ? 1 : 0;
stream >> selectorTab; stream >> selectorTab;
stream >> lastSeenWarningSeen; stream >> lastSeenWarningSeen;
@ -190,6 +192,9 @@ void AuthSessionSettings::constructFromSerialized(const QByteArray &serialized)
stream >> includeMutedCounter; stream >> includeMutedCounter;
stream >> countUnreadMessages; stream >> countUnreadMessages;
} }
if (!stream.atEnd()) {
stream >> exeLaunchWarning;
}
if (stream.status() != QDataStream::Ok) { if (stream.status() != QDataStream::Ok) {
LOG(("App Error: " LOG(("App Error: "
"Bad data for AuthSessionSettings::constructFromSerialized()")); "Bad data for AuthSessionSettings::constructFromSerialized()"));
@ -253,6 +258,7 @@ void AuthSessionSettings::constructFromSerialized(const QByteArray &serialized)
_variables.hadLegacyCallsPeerToPeerNobody = (legacyCallsPeerToPeer == kLegacyCallsPeerToPeerNobody); _variables.hadLegacyCallsPeerToPeerNobody = (legacyCallsPeerToPeer == kLegacyCallsPeerToPeerNobody);
_variables.includeMutedCounter = (includeMutedCounter == 1); _variables.includeMutedCounter = (includeMutedCounter == 1);
_variables.countUnreadMessages = (countUnreadMessages == 1); _variables.countUnreadMessages = (countUnreadMessages == 1);
_variables.exeLaunchWarning = (exeLaunchWarning == 1);
} }
void AuthSessionSettings::setSupportChatsTimeSlice(int slice) { void AuthSessionSettings::setSupportChatsTimeSlice(int slice) {

View File

@ -199,6 +199,12 @@ public:
void setCountUnreadMessages(bool value) { void setCountUnreadMessages(bool value) {
_variables.countUnreadMessages = value; _variables.countUnreadMessages = value;
} }
bool exeLaunchWarning() const {
return _variables.exeLaunchWarning;
}
void setExeLaunchWarning(bool warning) {
_variables.exeLaunchWarning = warning;
}
private: private:
struct Variables { struct Variables {
@ -227,6 +233,7 @@ private:
bool hadLegacyCallsPeerToPeerNobody = false; bool hadLegacyCallsPeerToPeerNobody = false;
bool includeMutedCounter = true; bool includeMutedCounter = true;
bool countUnreadMessages = true; bool countUnreadMessages = true;
bool exeLaunchWarning = true;
static constexpr auto kDefaultSupportChatsLimitSlice static constexpr auto kDefaultSupportChatsLimitSlice
= 7 * 24 * 60 * 60; = 7 * 24 * 60 * 60;

View File

@ -145,6 +145,16 @@ void BoxContent::onInnerResize() {
updateShadowsVisibility(); updateShadowsVisibility();
} }
void BoxContent::setDimensionsToContent(
int newWidth,
not_null<Ui::RpWidget*> content) {
content->resizeToWidth(newWidth);
content->heightValue(
) | rpl::start_with_next([=](int height) {
setDimensions(newWidth, height);
}, content->lifetime());
}
void BoxContent::setInnerTopSkip(int innerTopSkip, bool scrollBottomFixed) { void BoxContent::setInnerTopSkip(int innerTopSkip, bool scrollBottomFixed) {
if (_innerTopSkip != innerTopSkip) { if (_innerTopSkip != innerTopSkip) {
auto delta = innerTopSkip - _innerTopSkip; auto delta = innerTopSkip - _innerTopSkip;

View File

@ -152,6 +152,9 @@ protected:
void setDimensions(int newWidth, int maxHeight) { void setDimensions(int newWidth, int maxHeight) {
getDelegate()->setDimensions(newWidth, maxHeight); getDelegate()->setDimensions(newWidth, maxHeight);
} }
void setDimensionsToContent(
int newWidth,
not_null<Ui::RpWidget*> content);
void setInnerTopSkip(int topSkip, bool scrollBottomFixed = false); void setInnerTopSkip(int topSkip, bool scrollBottomFixed = false);
void setInnerBottomSkip(int bottomSkip); void setInnerBottomSkip(int bottomSkip);

View File

@ -18,6 +18,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/widgets/checkbox.h" #include "ui/widgets/checkbox.h"
#include "ui/widgets/buttons.h" #include "ui/widgets/buttons.h"
#include "ui/widgets/labels.h" #include "ui/widgets/labels.h"
#include "ui/wrap/vertical_layout.h"
#include "ui/toast/toast.h" #include "ui/toast/toast.h"
#include "ui/image/image.h" #include "ui/image/image.h"
#include "ui/empty_userpic.h" #include "ui/empty_userpic.h"
@ -803,3 +804,51 @@ void ConfirmInviteBox::paintEvent(QPaintEvent *e) {
} }
ConfirmInviteBox::~ConfirmInviteBox() = default; ConfirmInviteBox::~ConfirmInviteBox() = default;
ConfirmDontWarnBox::ConfirmDontWarnBox(
QWidget*,
const QString &text,
const QString &checkbox,
const QString &confirm,
FnMut<void(bool)> callback)
: _confirm(confirm)
, _content(setupContent(text, checkbox, std::move(callback))) {
}
void ConfirmDontWarnBox::prepare() {
setDimensionsToContent(st::boxWidth, _content);
addButton([=] { return _confirm; }, [=] { _callback(); });
addButton(langFactory(lng_cancel), [=] { closeBox(); });
}
not_null<Ui::RpWidget*> ConfirmDontWarnBox::setupContent(
const QString &text,
const QString &checkbox,
FnMut<void(bool)> callback) {
const auto result = Ui::CreateChild<Ui::VerticalLayout>(this);
result->add(
object_ptr<Ui::FlatLabel>(
result,
text,
Ui::FlatLabel::InitType::Rich,
st::boxLabel),
st::boxPadding);
const auto control = result->add(
object_ptr<Ui::Checkbox>(
result,
checkbox,
false,
st::defaultBoxCheckbox),
style::margins(
st::boxPadding.left(),
st::boxPadding.bottom(),
st::boxPadding.right(),
st::boxPadding.bottom()));
_callback = [=, callback = std::move(callback)]() mutable {
const auto checked = control->checked();
auto local = std::move(callback);
closeBox();
local(checked);
};
return result;
}

View File

@ -232,3 +232,27 @@ private:
int _userWidth = 0; int _userWidth = 0;
}; };
class ConfirmDontWarnBox : public BoxContent {
public:
ConfirmDontWarnBox(
QWidget*,
const QString &text,
const QString &checkbox,
const QString &confirm,
FnMut<void(bool)> callback);
protected:
void prepare() override;
private:
not_null<Ui::RpWidget*> setupContent(
const QString &text,
const QString &checkbox,
FnMut<void(bool)> callback);
QString _confirm;
FnMut<void()> _callback;
not_null<Ui::RpWidget*> _content;
};

View File

@ -718,11 +718,7 @@ void ProxyBox::prepare() {
setTitle(langFactory(lng_proxy_edit)); setTitle(langFactory(lng_proxy_edit));
refreshButtons(); refreshButtons();
setDimensionsToContent(st::boxWideWidth, _content);
_content->heightValue(
) | rpl::start_with_next([=](int height) {
setDimensions(st::boxWideWidth, height);
}, _content->lifetime());
} }
void ProxyBox::refreshButtons() { void ProxyBox::refreshButtons() {
@ -905,8 +901,6 @@ void ProxyBox::setupControls(const ProxyData &data) {
setupCredentials(data); setupCredentials(data);
setupMtprotoCredentials(data); setupMtprotoCredentials(data);
_content->resizeToWidth(st::boxWideWidth);
const auto handleType = [=](Type type) { const auto handleType = [=](Type type) {
_credentials->toggle( _credentials->toggle(
type == Type::Http || type == Type::Socks5, type == Type::Http || type == Type::Socks5,
@ -1032,15 +1026,7 @@ void AutoDownloadBox::setupContent() {
}); });
addButton(langFactory(lng_cancel), [=] { closeBox(); }); addButton(langFactory(lng_cancel), [=] { closeBox(); });
widthValue( setDimensionsToContent(st::boxWideWidth, content);
) | rpl::start_with_next([=](int width) {
content->resizeToWidth(width);
}, content->lifetime());
content->heightValue(
) | rpl::start_with_next([=](int height) {
setDimensions(st::boxWideWidth, height);
}, content->lifetime());
} }
ProxiesBoxController::ProxiesBoxController() ProxiesBoxController::ProxiesBoxController()

View File

@ -221,8 +221,6 @@ object_ptr<Ui::VerticalLayout> Controller::createContent() {
_wrap->add(createUpgradeButton()); _wrap->add(createUpgradeButton());
_wrap->add(createDeleteButton()); _wrap->add(createDeleteButton());
_wrap->resizeToWidth(st::boxWideWidth);
return result; return result;
} }
@ -1451,10 +1449,7 @@ void EditPeerInfoBox::prepare() {
[=] { controller->setFocus(); }, [=] { controller->setFocus(); },
lifetime()); lifetime());
auto content = controller->createContent(); auto content = controller->createContent();
content->heightValue( setDimensionsToContent(st::boxWideWidth, content);
) | rpl::start_with_next([this](int height) {
setDimensions(st::boxWideWidth, height);
}, content->lifetime());
setInnerWidget(object_ptr<Ui::OverrideMargins>( setInnerWidget(object_ptr<Ui::OverrideMargins>(
this, this,
std::move(content))); std::move(content)));

View File

@ -218,14 +218,7 @@ void ManagePeerBox::prepare() {
} }
void ManagePeerBox::setupContent() { void ManagePeerBox::setupContent() {
auto content = Ui::CreateChild<Ui::VerticalLayout>(this); const auto content = Ui::CreateChild<Ui::VerticalLayout>(this);
FillManageBox(App::wnd()->controller(), _channel, content); FillManageBox(App::wnd()->controller(), _channel, content);
widthValue( setDimensionsToContent(st::boxWidth, content);
) | rpl::start_with_next([=](int width) {
content->resizeToWidth(width);
}, content->lifetime());
content->heightValue(
) | rpl::start_with_next([=](int height) {
setDimensions(st::boxWidth, height);
}, content->lifetime());
} }

View File

@ -23,6 +23,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/history_media_types.h" #include "history/history_media_types.h"
#include "window/window_controller.h" #include "window/window_controller.h"
#include "storage/cache/storage_cache_database.h" #include "storage/cache/storage_cache_database.h"
#include "boxes/confirm_box.h"
#include "ui/image/image.h" #include "ui/image/image.h"
#include "ui/image/image_source.h" #include "ui/image/image_source.h"
#include "auth_session.h" #include "auth_session.h"
@ -65,6 +66,29 @@ QString JoinStringList(const QStringList &list, const QString &separator) {
return result; return result;
} }
void LaunchWithWarning(const QString &name) {
if (!Data::IsExecutableName(name)
|| !Auth().settings().exeLaunchWarning()) {
File::Launch(name);
return;
}
const auto extension = '.' + Data::FileExtension(name);
const auto callback = [=](bool checked) {
if (checked) {
Auth().settings().setExeLaunchWarning(false);
Auth().saveSettingsDelayed();
}
File::Launch(name);
};
Ui::show(Box<ConfirmDontWarnBox>(
lng_launch_exe_warning(
lt_extension,
textcmdStartSemibold() + extension + textcmdStopSemibold()),
lang(lng_launch_exe_dont_ask),
lang(lng_launch_exe_sure),
callback));
}
} // namespace } // namespace
bool fileIsImage(const QString &name, const QString &mime) { bool fileIsImage(const QString &name, const QString &mime) {
@ -308,15 +332,15 @@ void DocumentOpenClickHandler::Open(
Messenger::Instance().showDocument(data, context); Messenger::Instance().showDocument(data, context);
location.accessDisable(); location.accessDisable();
} else { } else {
auto filepath = location.name(); const auto filepath = location.name();
if (documentIsValidMediaFile(filepath)) { if (Data::IsValidMediaFile(filepath)) {
File::Launch(filepath); File::Launch(filepath);
} }
} }
data->session()->data().markMediaRead(data); data->session()->data().markMediaRead(data);
} else if (data->isVoiceMessage() || data->isAudioFile() || data->isVideoFile()) { } else if (data->isVoiceMessage() || data->isAudioFile() || data->isVideoFile()) {
auto filepath = location.name(); const auto filepath = location.name();
if (documentIsValidMediaFile(filepath)) { if (Data::IsValidMediaFile(filepath)) {
File::Launch(filepath); File::Launch(filepath);
} }
data->session()->data().markMediaRead(data); data->session()->data().markMediaRead(data);
@ -335,14 +359,14 @@ void DocumentOpenClickHandler::Open(
Messenger::Instance().showDocument(data, context); Messenger::Instance().showDocument(data, context);
} }
} else { } else {
File::Launch(location.name()); LaunchWithWarning(location.name());
} }
location.accessDisable(); location.accessDisable();
} else { } else {
File::Launch(location.name()); LaunchWithWarning(location.name());
} }
} else { } else {
File::Launch(location.name()); LaunchWithWarning(location.name());
} }
return; return;
} }
@ -703,7 +727,7 @@ void DocumentData::performActionOnLoad() {
File::OpenWith(already, QCursor::pos()); File::OpenWith(already, QCursor::pos());
} else if (_actionOnLoad == ActionOnLoadOpen || _actionOnLoad == ActionOnLoadPlayInline) { } else if (_actionOnLoad == ActionOnLoadOpen || _actionOnLoad == ActionOnLoadPlayInline) {
if (isVoiceMessage() || isAudioFile() || isVideoFile()) { if (isVoiceMessage() || isAudioFile() || isVideoFile()) {
if (documentIsValidMediaFile(already)) { if (Data::IsValidMediaFile(already)) {
File::Launch(already); File::Launch(already);
} }
_session->data().markMediaRead(this); _session->data().markMediaRead(this);
@ -711,11 +735,11 @@ void DocumentData::performActionOnLoad() {
if (showImage && QImageReader(loc.name()).canRead()) { if (showImage && QImageReader(loc.name()).canRead()) {
Messenger::Instance().showDocument(this, item); Messenger::Instance().showDocument(this, item);
} else { } else {
File::Launch(already); LaunchWithWarning(already);
} }
loc.accessDisable(); loc.accessDisable();
} else { } else {
File::Launch(already); LaunchWithWarning(already);
} }
} }
} }
@ -1405,3 +1429,57 @@ QString DocumentData::ComposeNameString(
auto trackTitle = (songTitle.isEmpty() ? qsl("Unknown Track") : songTitle); auto trackTitle = (songTitle.isEmpty() ? qsl("Unknown Track") : songTitle);
return songPerformer + QString::fromUtf8(" \xe2\x80\x93 ") + trackTitle; return songPerformer + QString::fromUtf8(" \xe2\x80\x93 ") + trackTitle;
} }
namespace Data {
QString FileExtension(const QString &filepath) {
const auto reversed = ranges::view::reverse(filepath);
const auto last = ranges::find_first_of(reversed, ".\\/");
if (last == reversed.end() || *last != '.') {
return QString();
}
return QString(last.base(), last - reversed.begin());
}
bool IsValidMediaFile(const QString &filepath) {
static const auto kExtensions = [] {
const auto list = qsl("\
webm mkv flv vob ogv ogg drc gif gifv mng avi mov qt wmv yuv rm rmvb asf \
amv mp4 m4p m4v mpg mp2 mpeg mpe mpv m2v svi 3gp 3g2 mxf roq nsv f4v f4p \
f4a f4b wma divx evo mk3d mka mks mcf m2p ps ts m2ts ifo aaf avchd cam dat \
dsh dvr-ms m1v fla flr sol wrap smi swf wtv 8svx 16svx iff aiff aif aifc \
au bwf cdda raw wav flac la pac m4a ape ofr ofs off rka shn tak tta wv \
brstm dts dtshd dtsma ast amr mp3 spx gsm aac mpc vqf ra ots swa vox voc \
dwd smp aup cust mid mus sib sid ly gym vgm psf nsf mod ptb s3m xm it mt2 \
minipsf psflib 2sf dsf gsf psf2 qsf ssf usf rmj spc niff mxl xml txm ym \
jam mp1 mscz").split(' ');
return base::flat_set<QString>(list.begin(), list.end());
}();
return ranges::binary_search(
kExtensions,
FileExtension(filepath).toLower());
}
bool IsExecutableName(const QString &filepath) {
static const auto kExtensions = [] {
const auto joined =
#ifdef Q_OS_MAC
qsl("action app bin command csh osx workflow");
#elif defined Q_OS_LINUX // Q_OS_MAC
qsl("bin csh ksh out run");
#else // Q_OS_MAC || Q_OS_LINUX
qsl("\
bat bin cmd com cpl exe gadget inf ins inx isu job jse lnk msc msi msp mst \
paf pif ps1 reg rgs scr sct shb shs u3p vb vbe vbs vbscript ws wsf");
#endif // !Q_OS_MAC && !Q_OS_LINUX
const auto list = joined.split(' ');
return base::flat_set<QString>(list.begin(), list.end());
}();
return ranges::binary_search(
kExtensions,
FileExtension(filepath).toLower());
}
} // namespace Data

View File

@ -331,3 +331,11 @@ QString FileNameForSave(
QString name, QString name,
bool savingAs, bool savingAs,
const QDir &dir = QDir()); const QDir &dir = QDir());
namespace Data {
QString FileExtension(const QString &filepath);
bool IsValidMediaFile(const QString &filepath);
bool IsExecutableName(const QString &filepath);
} // namespace Data

View File

@ -1349,10 +1349,10 @@ void HistoryDocument::createComponents(bool caption) {
} else { } else {
mask |= HistoryDocumentNamed::Bit(); mask |= HistoryDocumentNamed::Bit();
if (!_data->isSong() if (!_data->isSong()
&& !documentIsExecutableName(_data->filename())
&& !_data->thumb->isNull() && !_data->thumb->isNull()
&& _data->thumb->width() && _data->thumb->width()
&& _data->thumb->height()) { && _data->thumb->height()
&& !Data::IsExecutableName(_data->filename())) {
mask |= HistoryDocumentThumbed::Bit(); mask |= HistoryDocumentThumbed::Bit();
} }
} }

View File

@ -179,50 +179,6 @@ RoundCorners documentCorners(int32 colorIndex) {
return RoundCorners(Doc1Corners + (colorIndex & 3)); return RoundCorners(Doc1Corners + (colorIndex & 3));
} }
bool documentIsValidMediaFile(const QString &filepath) {
static StaticNeverFreedPointer<QList<QString>> validMediaTypes(([] {
std::unique_ptr<QList<QString>> result = std::make_unique<QList<QString>>();
*result = qsl("\
webm mkv flv vob ogv ogg drc gif gifv mng avi mov qt wmv yuv rm rmvb asf amv mp4 m4p \
m4v mpg mp2 mpeg mpe mpv m2v svi 3gp 3g2 mxf roq nsv f4v f4p f4a f4b wma divx evo mk3d \
mka mks mcf m2p ps ts m2ts ifo aaf avchd cam dat dsh dvr-ms m1v fla flr sol wrap smi swf \
wtv 8svx 16svx iff aiff aif aifc au bwf cdda raw wav flac la pac m4a ape ofr ofs off rka \
shn tak tta wv brstm dts dtshd dtsma ast amr mp3 spx gsm aac mpc vqf ra ots swa vox voc \
dwd smp aup cust mid mus sib sid ly gym vgm psf nsf mod ptb s3m xm it mt2 minipsf psflib \
2sf dsf gsf psf2 qsf ssf usf rmj spc niff mxl xml txm ym jam mp1 mscz\
").split(' ');
return result.release();
})());
QFileInfo info(filepath);
auto parts = info.fileName().split('.', QString::SkipEmptyParts);
return !parts.isEmpty() && (validMediaTypes->indexOf(parts.back().toLower()) >= 0);
}
bool documentIsExecutableName(const QString &filename) {
static StaticNeverFreedPointer<QList<QString>> executableTypes(([] {
std::unique_ptr<QList<QString>> result = std::make_unique<QList<QString>>();
#ifdef Q_OS_MAC
*result = qsl("\
action app bin command csh osx workflow\
").split(' ');
#elif defined Q_OS_LINUX // Q_OS_MAC
*result = qsl("\
bin csh ksh out run\
").split(' ');
#else // Q_OS_MAC || Q_OS_LINUX
*result = qsl("\
bat bin cmd com cpl exe gadget inf ins inx isu job jse lnk msc msi \
msp mst paf pif ps1 reg rgs sct shb shs u3p vb vbe vbs vbscript ws wsf\
").split(' ');
#endif // !Q_OS_MAC && !Q_OS_LINUX
return result.release();
})());
auto lastDotIndex = filename.lastIndexOf('.');
return (lastDotIndex >= 0) && (executableTypes->indexOf(filename.mid(lastDotIndex + 1).toLower()) >= 0);
}
[[nodiscard]] HistoryView::TextState LayoutItemBase::getState( [[nodiscard]] HistoryView::TextState LayoutItemBase::getState(
QPoint point, QPoint point,
StateRequest request) const { StateRequest request) const {

View File

@ -68,8 +68,6 @@ style::color documentDarkColor(int colorIndex);
style::color documentOverColor(int colorIndex); style::color documentOverColor(int colorIndex);
style::color documentSelectedColor(int colorIndex); style::color documentSelectedColor(int colorIndex);
RoundCorners documentCorners(int colorIndex); RoundCorners documentCorners(int colorIndex);
bool documentIsValidMediaFile(const QString &filepath);
bool documentIsExecutableName(const QString &filename);
class PaintContextBase { class PaintContextBase {
public: public:

View File

@ -1141,7 +1141,7 @@ void MainWidget::handleAudioUpdate(const AudioMsgId &audioId) {
auto filepath = document->filepath(DocumentData::FilePathResolveSaveFromData); auto filepath = document->filepath(DocumentData::FilePathResolveSaveFromData);
if (!filepath.isEmpty()) { if (!filepath.isEmpty()) {
if (documentIsValidMediaFile(filepath)) { if (Data::IsValidMediaFile(filepath)) {
File::Launch(filepath); File::Launch(filepath);
} }
} }

View File

@ -1183,7 +1183,7 @@ bool Document::withThumb() const {
&& !_data->thumb->isNull() && !_data->thumb->isNull()
&& _data->thumb->width() && _data->thumb->width()
&& _data->thumb->height() && _data->thumb->height()
&& !documentIsExecutableName(_data->filename()); && !Data::IsExecutableName(_data->filename());
} }
bool Document::updateStatusText() { bool Document::updateStatusText() {