mirror of
https://github.com/kovidgoyal/kitty
synced 2026-06-06 01:05:48 +02:00
Modify drop protocol to allow transmission of broken symlinks in the URI list
This commit is contained in:
@@ -169,10 +169,14 @@ If the client requests an entry that is not a supported URI type the
|
||||
terminal must reply with ``EUNKNOWN``.
|
||||
|
||||
Terminals must ONLY send data for regular files or directories. Symbolic links must be
|
||||
resolved and the corresponding file or directory read. If the terminal does not have
|
||||
permission to read the file it must reply with ``EPERM``. Terminals
|
||||
must respond with ``EINVAL`` if the file is not a regular file after
|
||||
resolving symlinks and ``ENOENT`` if the file does not exist. If an
|
||||
resolved and the corresponding file or directory read. Only if the symbolic
|
||||
link cannot be resolved must it be transmitted as a symbolic link (in which
|
||||
case ``X=1`` and the payload is the base64 encoded target of the symlink. See
|
||||
below for more details about sending symlinks.
|
||||
|
||||
If the terminal does not have permission to read the file it must reply with
|
||||
``EPERM``. Terminals must respond with ``EINVAL`` if the file is not a regular
|
||||
file after resolving symlinks and ``ENOENT`` if the file does not exist. If an
|
||||
I/O error occurs the terminal must send ``EIO``.
|
||||
|
||||
For security reasons, terminals must reply with ``EPERM`` if the drag
|
||||
|
||||
@@ -396,7 +396,10 @@ func (dnd *dnd) on_remote_drop_data(cmd DC) (err error) {
|
||||
}
|
||||
if e.dest == nil { // this entry is finished
|
||||
drop_status.open_remote_dir.num_children_finished++
|
||||
if e.item_type != 0 && e.item_type != 1 {
|
||||
if len(e.children) > 0 {
|
||||
if e.item_type != 0 && e.item_type != 1 {
|
||||
dnd.lp.QueueDnDData(DC{Type: 'r', Yp: e.item_type}) // close directory in terminal
|
||||
}
|
||||
// TODO: request the children
|
||||
}
|
||||
}
|
||||
|
||||
42
kitty/dnd.c
42
kitty/dnd.c
@@ -690,18 +690,7 @@ get_nth_file_url(const char *uri_list, size_t uri_list_sz, int n, char **path_ou
|
||||
*query_or_fragment_start = 0;
|
||||
url_decode_inplace(path);
|
||||
if (path[0] != '/') { *error_out = "EINVAL"; return false; }
|
||||
|
||||
char resolved[PATH_MAX];
|
||||
if (!realpath(path, resolved)) {
|
||||
switch (errno) {
|
||||
case ENOENT: case ENOTDIR: case ELOOP: *error_out = "ENOENT"; break;
|
||||
case EACCES: case EPERM: *error_out = "EPERM"; break;
|
||||
default: *error_out = "EINVAL"; break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
*path_out = strdup(resolved);
|
||||
*path_out = strdup(path);
|
||||
if (!*path_out) { *error_out = "ENOMEM"; return false; }
|
||||
return true;
|
||||
}
|
||||
@@ -949,6 +938,16 @@ drop_send_dir_listing(Window *w, const char *path) {
|
||||
queue_payload_to_child(w->id, w->drop.client_id, &w->drop.pending, hdr, hdr_sz, NULL, 0, true);
|
||||
}
|
||||
|
||||
static void
|
||||
drop_send_symlink(Window *w, const char *target, size_t sz) {
|
||||
char hdr[128];
|
||||
int hdr_sz = snprintf(hdr, sizeof(hdr), "\x1b]%d;t=r", DND_CODE);
|
||||
hdr_sz += drop_append_request_keys(w, hdr + hdr_sz, sizeof(hdr) - hdr_sz);
|
||||
hdr_sz += snprintf(hdr + hdr_sz, sizeof(hdr) - hdr_sz, ":X=1");
|
||||
queue_payload_to_child(w->id, w->drop.client_id, &w->drop.pending, hdr, hdr_sz, target, sz, true);
|
||||
queue_payload_to_child(w->id, w->drop.client_id, &w->drop.pending, hdr, hdr_sz, NULL, 0, true);
|
||||
}
|
||||
|
||||
/* Send the file/directory at URI-list index idx.
|
||||
* Returns true if completed synchronously, false if async file I/O started. */
|
||||
static bool
|
||||
@@ -970,7 +969,7 @@ do_drop_request_uri_data(Window *w, int32_t mime_idx, int32_t file_idx) {
|
||||
if (file_idx < 1) { drop_send_error(w, EINVAL); return true; }
|
||||
int file_n = file_idx - 1;
|
||||
|
||||
char *path = NULL;
|
||||
RAII_ALLOC(char, path, NULL);
|
||||
const char *err = NULL;
|
||||
if (!get_nth_file_url(w->drop.uri_list, w->drop.uri_list_sz, file_n, &path, &err)) {
|
||||
drop_send_error_str(w, err);
|
||||
@@ -979,12 +978,18 @@ do_drop_request_uri_data(Window *w, int32_t mime_idx, int32_t file_idx) {
|
||||
|
||||
struct stat st;
|
||||
if (stat(path, &st) < 0) {
|
||||
free(path);
|
||||
switch (errno) {
|
||||
case ENOENT: case ENOTDIR: drop_send_error(w, ENOENT); break;
|
||||
case EACCES: case EPERM: drop_send_error(w, EPERM); break;
|
||||
default: drop_send_error(w, EIO); break;
|
||||
if (lstat(path, &st) < 0) {
|
||||
switch (errno) {
|
||||
case ENOENT: case ENOTDIR: drop_send_error(w, ENOENT); break;
|
||||
case EACCES: case EPERM: drop_send_error(w, EPERM); break;
|
||||
default: drop_send_error(w, EIO); break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
// We have a broken symlink
|
||||
char target[PATH_MAX]; ssize_t tgtsz;
|
||||
if ((tgtsz = readlink(path, target, sizeof(target)-1)) < 0) drop_send_error(w, ENOENT);
|
||||
drop_send_symlink(w, target, tgtsz);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -998,7 +1003,6 @@ do_drop_request_uri_data(Window *w, int32_t mime_idx, int32_t file_idx) {
|
||||
drop_send_error(w, EINVAL);
|
||||
sync = true;
|
||||
}
|
||||
free(path);
|
||||
return sync;
|
||||
}
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ def create_fs(base):
|
||||
f.write(b'x' * sz)
|
||||
os.makedirs(join('d1', 'sd', 'ssd'))
|
||||
os.mkdir(join('d2'))
|
||||
# os.symlink('/does-not-exist', join('s1'))
|
||||
os.symlink('/does-not-exist', join('s1'))
|
||||
os.symlink('d1', join('sd'))
|
||||
os.symlink('/', join('sr'))
|
||||
os.symlink('../d1', join('d1', 'sr'))
|
||||
|
||||
Reference in New Issue
Block a user