mirror of
https://github.com/aseprite/aseprite.git
synced 2025-03-01 01:13:40 +00:00
Clipboard access check, content
This commit is contained in:
parent
7331398203
commit
55b3e3c41c
@ -1667,6 +1667,7 @@ script_label = The following script:
|
||||
file_label = wants to access to this file:
|
||||
command_label = wants to execute the following command:
|
||||
websocket_label = wants to open a WebSocket connection to this URL:
|
||||
clipboard_label = wants to access the system clipboard
|
||||
dont_show_for_this_access = Don't show this specific alert again for this script
|
||||
dont_show_for_this_script = Give full trust to this script
|
||||
allow_execute_access = &Allow Execute Access
|
||||
|
@ -6,7 +6,7 @@
|
||||
<label text="@.script_label" />
|
||||
<hbox border="4"><link id="script" /></hbox>
|
||||
<label id="file_label" />
|
||||
<hbox border="4"><link id="file" /></hbox>
|
||||
<hbox id="file_container" border="4"><link id="file" /></hbox>
|
||||
<separator horizontal="true" />
|
||||
<check id="dont_show" text="@.dont_show_for_this_access" />
|
||||
<check id="full" text="@.dont_show_for_this_script" />
|
||||
|
@ -12,44 +12,56 @@
|
||||
#include "app/script/luacpp.h"
|
||||
#include "app/util/clipboard.h"
|
||||
#include "clip/clip.h"
|
||||
#include "doc/mask.h"
|
||||
#include "doc/palette.h"
|
||||
#include "doc/tileset.h"
|
||||
#include "engine.h"
|
||||
|
||||
#include "docobj.h"
|
||||
#include "security.h"
|
||||
|
||||
namespace app { namespace script {
|
||||
|
||||
namespace {
|
||||
|
||||
struct Clipboard {};
|
||||
|
||||
#define CLIPBOARD_GATE(mode) \
|
||||
if (!ask_access(L, nullptr, mode, ResourceType::Clipboard)) \
|
||||
return luaL_error(L, \
|
||||
"the script doesn't have %s access to the clipboard", \
|
||||
mode == FileAccessMode::Read ? "read" : "write");
|
||||
|
||||
int Clipboard_clear(lua_State* L)
|
||||
{
|
||||
CLIPBOARD_GATE(FileAccessMode::Read);
|
||||
|
||||
if (!clip::clear())
|
||||
return luaL_error(L, "failed to clear the clipboard");
|
||||
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Clipboard_is_text(lua_State* L)
|
||||
int Clipboard_hasText(lua_State* L)
|
||||
{
|
||||
CLIPBOARD_GATE(FileAccessMode::Read);
|
||||
|
||||
lua_pushboolean(L, clip::has(clip::text_format()));
|
||||
return 1;
|
||||
}
|
||||
|
||||
int Clipboard_is_image(lua_State* L)
|
||||
int Clipboard_hasImage(lua_State* L)
|
||||
{
|
||||
lua_pushboolean(L, clip::has(clip::image_format()));
|
||||
return 1;
|
||||
}
|
||||
CLIPBOARD_GATE(FileAccessMode::Read);
|
||||
|
||||
int Clipboard_is_empty(lua_State* L)
|
||||
{
|
||||
// Using clip::has(clip::empty_format()) had inconsistent results, might as well avoid false
|
||||
// positives by just checking the two formats we support
|
||||
lua_pushboolean(L, !clip::has(clip::image_format()) && !clip::has(clip::text_format()));
|
||||
lua_pushboolean(L, clip::has(clip::image_format()));
|
||||
return 1;
|
||||
}
|
||||
|
||||
int Clipboard_get_image(lua_State* L)
|
||||
{
|
||||
CLIPBOARD_GATE(FileAccessMode::Read);
|
||||
|
||||
doc::Image* image = nullptr;
|
||||
doc::Mask* mask = nullptr;
|
||||
doc::Palette* palette = nullptr;
|
||||
@ -57,8 +69,12 @@ int Clipboard_get_image(lua_State* L)
|
||||
const bool result =
|
||||
app::Clipboard::instance()->getNativeBitmap(&image, &mask, &palette, &tileset);
|
||||
|
||||
// TODO: If we get a tileset, should we convert it to an image?
|
||||
if (image == nullptr || !result)
|
||||
if (image == nullptr) {
|
||||
lua_pushnil(L);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!result) // TODO: Can we have a false "nil" value without an error?
|
||||
return luaL_error(L, "failed to get image from clipboard");
|
||||
|
||||
push_image(L, image);
|
||||
@ -67,16 +83,21 @@ int Clipboard_get_image(lua_State* L)
|
||||
|
||||
int Clipboard_get_text(lua_State* L)
|
||||
{
|
||||
std::string str;
|
||||
if (!clip::get_text(str))
|
||||
return luaL_error(L, "failed to get text from clipboard");
|
||||
CLIPBOARD_GATE(FileAccessMode::Read);
|
||||
|
||||
std::string str;
|
||||
if (clip::get_text(str))
|
||||
lua_pushstring(L, str.c_str());
|
||||
else
|
||||
lua_pushnil(L);
|
||||
|
||||
lua_pushstring(L, str.c_str());
|
||||
return 1;
|
||||
}
|
||||
|
||||
int Clipboard_set_image(lua_State* L)
|
||||
{
|
||||
CLIPBOARD_GATE(FileAccessMode::Read);
|
||||
|
||||
auto* image = may_get_image_from_arg(L, 2);
|
||||
if (!image)
|
||||
return luaL_error(L, "invalid image");
|
||||
@ -84,7 +105,7 @@ int Clipboard_set_image(lua_State* L)
|
||||
const bool result = app::Clipboard::instance()->setNativeBitmap(
|
||||
image,
|
||||
nullptr,
|
||||
get_current_palette(), // TODO: Not sure if there's any way to get the palette from the image
|
||||
get_current_palette(),
|
||||
nullptr,
|
||||
image->maskColor() // TODO: Unsure if this is sufficient.
|
||||
);
|
||||
@ -97,25 +118,131 @@ int Clipboard_set_image(lua_State* L)
|
||||
|
||||
int Clipboard_set_text(lua_State* L)
|
||||
{
|
||||
CLIPBOARD_GATE(FileAccessMode::Read);
|
||||
|
||||
const char* text = lua_tostring(L, 2);
|
||||
if (text != NULL && strlen(text) > 0 && !clip::set_text(text))
|
||||
if (!clip::set_text(text ? text : ""))
|
||||
return luaL_error(L, "failed to set the clipboard text to '%s'", text);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Clipboard_get_content(lua_State* L)
|
||||
{
|
||||
CLIPBOARD_GATE(FileAccessMode::Read);
|
||||
|
||||
doc::Image* image = nullptr;
|
||||
doc::Mask* mask = nullptr;
|
||||
doc::Palette* palette = nullptr;
|
||||
doc::Tileset* tileset = nullptr;
|
||||
const bool bitmapResult =
|
||||
app::Clipboard::instance()->getNativeBitmap(&image, &mask, &palette, &tileset);
|
||||
|
||||
std::string text;
|
||||
const bool clipResult = clip::get_text(text);
|
||||
|
||||
lua_createtable(L, 0, 5);
|
||||
|
||||
if (bitmapResult && image)
|
||||
push_image(L, image);
|
||||
else
|
||||
lua_pushnil(L);
|
||||
lua_setfield(L, -2, "image");
|
||||
|
||||
if (bitmapResult && mask)
|
||||
push_docobj<Mask>(L, mask);
|
||||
else
|
||||
lua_pushnil(L);
|
||||
lua_setfield(L, -2, "mask");
|
||||
|
||||
if (bitmapResult && palette)
|
||||
push_docobj<Palette>(L, palette);
|
||||
else
|
||||
lua_pushnil(L);
|
||||
lua_setfield(L, -2, "palette");
|
||||
|
||||
if (bitmapResult && tileset)
|
||||
push_docobj<Tileset>(L, tileset);
|
||||
else
|
||||
lua_pushnil(L);
|
||||
lua_setfield(L, -2, "tileset");
|
||||
|
||||
if (clipResult)
|
||||
lua_pushstring(L, text.c_str());
|
||||
else
|
||||
lua_pushnil(L);
|
||||
lua_setfield(L, -2, "text");
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int Clipboard_set_content(lua_State* L)
|
||||
{
|
||||
CLIPBOARD_GATE(FileAccessMode::Read);
|
||||
|
||||
doc::Image* image = nullptr;
|
||||
doc::Mask* mask = nullptr;
|
||||
doc::Palette* palette = nullptr;
|
||||
doc::Tileset* tileset = nullptr;
|
||||
std::optional<std::string> text = std::nullopt;
|
||||
|
||||
if (!lua_istable(L, 2))
|
||||
return luaL_error(L, "app.clipboard.content must be a table");
|
||||
|
||||
int type = lua_getfield(L, 2, "image");
|
||||
if (type != LUA_TNIL)
|
||||
image = may_get_image_from_arg(L, -1);
|
||||
lua_pop(L, 1);
|
||||
|
||||
type = lua_getfield(L, 2, "mask");
|
||||
if (type != LUA_TNIL)
|
||||
mask = may_get_docobj<Mask>(L, -1);
|
||||
lua_pop(L, 1);
|
||||
|
||||
type = lua_getfield(L, 2, "palette");
|
||||
if (type != LUA_TNIL)
|
||||
palette = may_get_docobj<Palette>(L, -1);
|
||||
lua_pop(L, 1);
|
||||
|
||||
type = lua_getfield(L, 2, "tileset");
|
||||
if (type != LUA_TNIL)
|
||||
tileset = may_get_docobj<Tileset>(L, -1);
|
||||
lua_pop(L, 1);
|
||||
|
||||
type = lua_getfield(L, 2, "text");
|
||||
if (type != LUA_TNIL) {
|
||||
const char* tableText = lua_tostring(L, -1);
|
||||
if (tableText != nullptr && strlen(tableText) > 0)
|
||||
text = std::string(tableText);
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
|
||||
if (image &&
|
||||
!app::Clipboard::instance()->setNativeBitmap(image,
|
||||
mask,
|
||||
palette ? palette : get_current_palette(),
|
||||
tileset,
|
||||
image ? image->maskColor() : -1))
|
||||
return luaL_error(L, "failed to set data to clipboard");
|
||||
|
||||
if (text != std::nullopt && !clip::set_text(*text))
|
||||
return luaL_error(L, "failed to set the clipboard text to '%s'", (*text).c_str());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const luaL_Reg Clipboard_methods[] = {
|
||||
{ "clear", Clipboard_clear },
|
||||
{ nullptr, nullptr }
|
||||
};
|
||||
|
||||
const Property Clipboard_properties[] = {
|
||||
{ "isText", Clipboard_is_text, nullptr },
|
||||
{ "isImage", Clipboard_is_image, nullptr },
|
||||
{ "isEmpty", Clipboard_is_empty, nullptr },
|
||||
{ "text", Clipboard_get_text, Clipboard_set_text },
|
||||
{ "image", Clipboard_get_image, Clipboard_set_image },
|
||||
{ nullptr, nullptr, nullptr }
|
||||
{ "hasText", Clipboard_hasText, nullptr },
|
||||
{ "hasImage", Clipboard_hasImage, nullptr },
|
||||
{ "text", Clipboard_get_text, Clipboard_set_text },
|
||||
{ "image", Clipboard_get_image, Clipboard_set_image },
|
||||
{ "content", Clipboard_get_content, Clipboard_set_content },
|
||||
{ nullptr, nullptr, nullptr }
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include "base/fs.h"
|
||||
#include "base/sha1.h"
|
||||
#include "fmt/format.h"
|
||||
#include "ui/widget_type.h"
|
||||
|
||||
#include "script_access.xml.h"
|
||||
|
||||
@ -91,7 +92,7 @@ std::string get_script_filename(lua_State* L)
|
||||
const char* source = lua_tostring(L, -1);
|
||||
std::string script;
|
||||
if (source && *source)
|
||||
script = source + 1;
|
||||
script = source;
|
||||
lua_pop(L, 2);
|
||||
return script;
|
||||
}
|
||||
@ -242,12 +243,25 @@ bool ask_access(lua_State* L,
|
||||
if ((access & int(mode)) == int(mode))
|
||||
return true;
|
||||
|
||||
std::string allowButtonText =
|
||||
(mode == FileAccessMode::LoadLib ? Strings::script_access_allow_load_lib_access() :
|
||||
mode == FileAccessMode::OpenSocket ? Strings::script_access_allow_open_conn_access() :
|
||||
mode == FileAccessMode::Execute ? Strings::script_access_allow_execute_access() :
|
||||
mode == FileAccessMode::Write ? Strings::script_access_allow_write_access() :
|
||||
Strings::script_access_allow_read_access());
|
||||
std::string allowButtonText;
|
||||
switch (mode) {
|
||||
case FileAccessMode::LoadLib:
|
||||
allowButtonText = Strings::script_access_allow_load_lib_access();
|
||||
break;
|
||||
case FileAccessMode::OpenSocket:
|
||||
allowButtonText = Strings::script_access_allow_open_conn_access();
|
||||
break;
|
||||
case FileAccessMode::Execute:
|
||||
allowButtonText = Strings::script_access_allow_execute_access();
|
||||
break;
|
||||
case FileAccessMode::Write:
|
||||
allowButtonText = Strings::script_access_allow_write_access();
|
||||
break;
|
||||
case FileAccessMode::Read:
|
||||
allowButtonText = Strings::script_access_allow_read_access();
|
||||
break;
|
||||
default: return luaL_error(L, "invalid access request");
|
||||
}
|
||||
|
||||
app::gen::ScriptAccess dlg;
|
||||
dlg.script()->setText(script);
|
||||
@ -258,15 +272,26 @@ bool ask_access(lua_State* L,
|
||||
case ResourceType::File: label = Strings::script_access_file_label(); break;
|
||||
case ResourceType::Command: label = Strings::script_access_command_label(); break;
|
||||
case ResourceType::WebSocket: label = Strings::script_access_websocket_label(); break;
|
||||
case ResourceType::Clipboard: label = Strings::script_access_clipboard_label(); break;
|
||||
}
|
||||
dlg.fileLabel()->setText(label);
|
||||
}
|
||||
|
||||
dlg.file()->setText(filename);
|
||||
if (filename && strlen(filename) > 0)
|
||||
dlg.file()->setText(filename);
|
||||
else
|
||||
dlg.fileContainer()->setVisible(false);
|
||||
|
||||
dlg.allow()->setText(allowButtonText);
|
||||
dlg.allow()->processMnemonicFromText();
|
||||
|
||||
dlg.script()->Click.connect([&dlg] { app::launcher::open_folder(dlg.script()->text()); });
|
||||
if (script == "internal") {
|
||||
// Make it look like a normal label
|
||||
dlg.script()->setType(ui::WidgetType::kLabelWidget);
|
||||
dlg.script()->initTheme();
|
||||
}
|
||||
else
|
||||
dlg.script()->Click.connect([&dlg] { app::launcher::open_folder(dlg.script()->text()); });
|
||||
|
||||
dlg.full()->Click.connect([&dlg, &allowButtonText]() {
|
||||
if (dlg.full()->isSelected()) {
|
||||
|
@ -30,6 +30,7 @@ enum class ResourceType {
|
||||
File,
|
||||
Command,
|
||||
WebSocket,
|
||||
Clipboard,
|
||||
};
|
||||
|
||||
void overwrite_unsecure_functions(lua_State* L);
|
||||
|
@ -5,46 +5,90 @@
|
||||
|
||||
dofile('./test_utils.lua')
|
||||
|
||||
do -- Clipboard clearing
|
||||
app.clipboard.text = "hello world"
|
||||
expect_eq(false, app.clipboard.isEmpty)
|
||||
do -- Text clearing
|
||||
app.clipboard.text = "clear me"
|
||||
expect_eq(true, app.clipboard.hasText)
|
||||
app.clipboard.clear()
|
||||
expect_eq(true, app.clipboard.isEmpty)
|
||||
expect_eq(false, app.clipboard.hasText)
|
||||
end
|
||||
|
||||
do -- Text clearing (with content)
|
||||
app.clipboard.content = { text = "clear me 2" }
|
||||
expect_eq(true, app.clipboard.hasText)
|
||||
app.clipboard.clear()
|
||||
expect_eq(false, app.clipboard.hasText)
|
||||
end
|
||||
|
||||
do -- Text copying and access
|
||||
app.clipboard.clear()
|
||||
|
||||
expect_eq(false, app.clipboard.isText)
|
||||
expect_eq(false, app.clipboard.isImage)
|
||||
expect_eq(true, app.clipboard.isEmpty)
|
||||
expect_eq(false, app.clipboard.hasText)
|
||||
expect_eq(false, app.clipboard.hasImage)
|
||||
|
||||
app.clipboard.text = "hello world"
|
||||
|
||||
expect_eq(true, app.clipboard.isText)
|
||||
expect_eq(false, app.clipboard.isImage)
|
||||
expect_eq(false, app.clipboard.isEmpty)
|
||||
expect_eq(true, app.clipboard.hasText)
|
||||
expect_eq(false, app.clipboard.hasImage)
|
||||
|
||||
expect_eq("hello world", app.clipboard.text)
|
||||
end
|
||||
|
||||
do -- Text copying and access (with .content)
|
||||
app.clipboard.clear()
|
||||
|
||||
expect_eq(false, app.clipboard.hasText)
|
||||
expect_eq(false, app.clipboard.hasImage)
|
||||
|
||||
app.clipboard.content = { text = "hello world 2"}
|
||||
|
||||
expect_eq(true, app.clipboard.hasText)
|
||||
expect_eq(false, app.clipboard.hasImage)
|
||||
|
||||
expect_eq("hello world 2", app.clipboard.content.text)
|
||||
end
|
||||
|
||||
do -- Image copying and access
|
||||
local sprite = Sprite{ fromFile="sprites/abcd.aseprite" }
|
||||
|
||||
app.clipboard.clear()
|
||||
|
||||
expect_eq(false, app.clipboard.isText)
|
||||
expect_eq(false, app.clipboard.isImage)
|
||||
expect_eq(true, app.clipboard.isEmpty)
|
||||
expect_eq(false, app.clipboard.hasText)
|
||||
expect_eq(false, app.clipboard.hasImage)
|
||||
|
||||
assert(app.image ~= nil)
|
||||
app.clipboard.image = app.image
|
||||
|
||||
expect_eq(false, app.clipboard.isText)
|
||||
expect_eq(true, app.clipboard.isImage)
|
||||
expect_eq(false, app.clipboard.isEmpty)
|
||||
expect_eq(false, app.clipboard.hasText)
|
||||
expect_eq(true, app.clipboard.hasImage)
|
||||
|
||||
expect_eq(app.image.width, app.clipboard.image.width)
|
||||
expect_eq(app.image.height, app.clipboard.image.height)
|
||||
expect_eq(app.image.bytes, app.clipboard.image.bytes)
|
||||
end
|
||||
|
||||
do -- Image copying and access (with .content)
|
||||
local sprite = Sprite{ fromFile="sprites/abcd.aseprite" }
|
||||
|
||||
app.clipboard.clear()
|
||||
|
||||
expect_eq(false, app.clipboard.hasText)
|
||||
expect_eq(false, app.clipboard.hasImage)
|
||||
assert(app.image ~= nil)
|
||||
|
||||
app.clipboard.content = {
|
||||
image = app.image,
|
||||
palettte = sprite.palettes[1],
|
||||
mask = sprite.spec.transparentColor,
|
||||
tileset = nil,
|
||||
text = nil, -- TODO: Error when this happens
|
||||
}
|
||||
|
||||
expect_eq(false, app.clipboard.hasText)
|
||||
expect_eq(true, app.clipboard.hasImage)
|
||||
|
||||
local result = app.clipboard.content
|
||||
assert(result ~= nil)
|
||||
|
||||
expect_eq(sprite.image.bytes, c.image.bytes)
|
||||
-- TODO: the rest
|
||||
end
|
||||
|
Loading…
x
Reference in New Issue
Block a user