From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mp10.migadu.com ([2001:41d0:306:2d92::]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) by ms9.migadu.com with LMTPS id MLgOIjPoE2UZKgEAG6o9tA:P1 (envelope-from ) for ; Wed, 27 Sep 2023 10:30:43 +0200 Received: from aspmx1.migadu.com ([2001:41d0:306:2d92::]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) by mp10.migadu.com with LMTPS id MLgOIjPoE2UZKgEAG6o9tA (envelope-from ) for ; Wed, 27 Sep 2023 10:30:43 +0200 Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by aspmx1.migadu.com (Postfix) with ESMTPS id 056F03E44C for ; Wed, 27 Sep 2023 10:30:42 +0200 (CEST) Authentication-Results: aspmx1.migadu.com; dkim=pass header.d=gmail.com header.s=20230601 header.b=BhApLtoT; spf=pass (aspmx1.migadu.com: domain of "emacs-orgmode-bounces+larch=yhetil.org@gnu.org" designates 209.51.188.17 as permitted sender) smtp.mailfrom="emacs-orgmode-bounces+larch=yhetil.org@gnu.org"; dmarc=pass (policy=none) header.from=gmail.com ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=yhetil.org; s=key1; t=1695803443; h=from:from:sender:sender:reply-to:subject:subject:date:date: message-id:message-id:to:to:cc:cc:mime-version:mime-version: content-type:content-type:in-reply-to:in-reply-to: references:references:list-id:list-help:list-unsubscribe: list-subscribe:list-post:dkim-signature; bh=JdZcdoz6qt2NHT3RQ0zhW8P6AUXDS228B+5CW2j9ZIs=; b=SJxYZH3BRbhd/hwaks7CO0znwvSll0WQz6PtOvdGrp7M6VecDyn4ZTv3+9nh8DU5k39Ubl DT/qv6y142vbjHB7saQjio1WNechmj/qUxDYb0PREC9c6WOpiI8lpVqOK6roy1SYjWKmkK sl0vHFHczOU1KiCytcScSmfVdPE/S/pDiUPONgrs0jbhFt4qvymGEqMN92f2YESvroU9cH 8e1rd28NGknrTSzCRIeiBWypvvkzhEFxOeb91JWJzgqpxeQ0v8rNZmk1GV8NdtJMaZaw9f M/d2dOHlRI23YLnU5Wt/+CzooYCQ9FR9tJxQuT7OgplYWpeSXxx+3eHHjbvpxw== ARC-Authentication-Results: i=1; aspmx1.migadu.com; dkim=pass header.d=gmail.com header.s=20230601 header.b=BhApLtoT; spf=pass (aspmx1.migadu.com: domain of "emacs-orgmode-bounces+larch=yhetil.org@gnu.org" designates 209.51.188.17 as permitted sender) smtp.mailfrom="emacs-orgmode-bounces+larch=yhetil.org@gnu.org"; dmarc=pass (policy=none) header.from=gmail.com ARC-Seal: i=1; s=key1; d=yhetil.org; t=1695803443; a=rsa-sha256; cv=none; b=awYjPdl5j7Jwb7mvDxcQKTu8gFxd82UmsSrH8vmpqQzPuUNsfk8f3xGV8AS4jVEviWdohB nt+Ag/Za9Xitw0IQmD1tjv91rFjVNM3VoD/trKvGXNMpoCsWT0azc25n88gx/4t1l1Qvai 7LPy0+VXl99l6c2IAn1DLpUPVea7z9q+VOE3hmWt1hIrPBQdxWTiv7zPePlYH00L3w5GoQ 5ZzUvBPZIgxLPYs9QqWKOm0LkwSQF4YfrJgGQ8E9DInC8AUcwbOPyeRNH4puNfNzARK1nw elbJfZN4p+CNdJCvRpMQ+PXYaYaIUqZH5tQ73Vsboqd0/w1E4e95TQA+XNIXeA== Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1qlPvj-0000PM-Sk; Wed, 27 Sep 2023 04:29:59 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1qlPvi-0000NC-8T for emacs-orgmode@gnu.org; Wed, 27 Sep 2023 04:29:58 -0400 Received: from mail-qk1-x744.google.com ([2607:f8b0:4864:20::744]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1qlPvg-0007iW-0F for emacs-orgmode@gnu.org; Wed, 27 Sep 2023 04:29:58 -0400 Received: by mail-qk1-x744.google.com with SMTP id af79cd13be357-7742be66bd3so439653585a.3 for ; Wed, 27 Sep 2023 01:29:53 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1695803393; x=1696408193; darn=gnu.org; h=mime-version:user-agent:message-id:date:references:in-reply-to :subject:cc:to:from:from:to:cc:subject:date:message-id:reply-to; bh=JdZcdoz6qt2NHT3RQ0zhW8P6AUXDS228B+5CW2j9ZIs=; b=BhApLtoTmKyLnTLEyLat06MYsxHCO+krMY+TreHT88BOwwEPnybWOnWlfGYnGDZXtL u4B/YkjbwxZlV/YiMpFTsSzGm00zl52mezG+WAc9CtEWS2X+Q5yQJ9aZvucCTcv8PfqV b5epm9yIgOuz5TdtU+KHfJL/0fW3z0DuJdg4iqgrleHe+U4Lw33L/SwgERL+Y5UVudqq AnRccbDV/lPcCBSdODkqQEvIj5G7rm2hPU1LoAHx9HbrNZBme6PnDODQsrRtLufMuLtM y1O3cI2m/Xsx1DEdvkAiD4cSQG+CdcGZNGwB4q1oD94JYQ+4awMACnH0Olnv45KAzDQq VxJQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1695803393; x=1696408193; h=mime-version:user-agent:message-id:date:references:in-reply-to :subject:cc:to:from:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=JdZcdoz6qt2NHT3RQ0zhW8P6AUXDS228B+5CW2j9ZIs=; b=S9ieTo+U90XiANyj9kVZ55FPBiubxL5xT/nuhdqGzuLOp1srMJcwScenhFJ9wEeITf pNHCz6X2DW6A7xYBwr/vI8qjNJiu80u08Z6rO0Ugnj5d3ho1sA0worLwCOlnJP0Tl+IL 6hc7wIBXmieOtMuhLSe37+I1eF5epS/nIUlMk174RL/mVMZjIHjw0zPuAk84YDbk+k4s IsocA9V8kxWyj3Pb+C7K1PrxwqVNC/24f/5MPTf7ThLO6CCHWGo44xPLEpLSMLzeDzNg AOgKiwVcBbFDUAXrESUcVJ+37C2gbiSr2xWB8JHRfPXN79cOo48CnUThURRnzu9ML7oP 42mQ== X-Gm-Message-State: AOJu0YyqKhcUURj4HarRs+N9mvV9sUJR/2qliZ0ZVYgAW5uKR1RZkDa5 4GUWgoOkpuWJPnqSMZqaZMc= X-Google-Smtp-Source: AGHT+IHGpUR5/jvXwEreZuDhQIJ4+F08kCAnFf/zJrHLQXJrKioz5jT8BNAGR/IeM9HRQ02e1xRZfQ== X-Received: by 2002:a05:620a:3951:b0:76c:6f89:fea7 with SMTP id qs17-20020a05620a395100b0076c6f89fea7mr1126796qkn.23.1695803392900; Wed, 27 Sep 2023 01:29:52 -0700 (PDT) Received: from localhost ([118.185.152.162]) by smtp.gmail.com with ESMTPSA id i10-20020a63bf4a000000b005646e6634dcsm9494391pgo.83.2023.09.27.01.29.51 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 27 Sep 2023 01:29:52 -0700 (PDT) From: Visuwesh To: Ihor Radchenko Cc: Max Nikulin , emacs-orgmode@gnu.org Subject: Re: [BUG] [PATCH] Add yank-media and DND handler [9.6.7 (9.6.7-g6eb773 @ /home/viz/lib/emacs/straight/build/org/)] In-Reply-To: <878r8t9qrh.fsf@localhost> (Ihor Radchenko's message of "Tue, 26 Sep 2023 10:24:02 +0000") References: <87jzsintv0.fsf@gmail.com> <87lecx2nff.fsf@localhost> <87y1gul4oq.fsf@gmail.com> <878r8t9qrh.fsf@localhost> Date: Wed, 27 Sep 2023 13:59:49 +0530 Message-ID: <878r8sj9xe.fsf@gmail.com> User-Agent: Gnus/5.13 (Gnus v5.13) MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" Received-SPF: pass client-ip=2607:f8b0:4864:20::744; envelope-from=visuweshm@gmail.com; helo=mail-qk1-x744.google.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, FREEMAIL_FROM=0.001, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: emacs-orgmode@gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: "General discussions about Org-mode." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: emacs-orgmode-bounces+larch=yhetil.org@gnu.org Sender: emacs-orgmode-bounces+larch=yhetil.org@gnu.org X-Migadu-Flow: FLOW_IN X-Migadu-Country: US X-Migadu-Spam-Score: -6.52 X-Migadu-Scanner: mx2.migadu.com X-Migadu-Queue-Id: 056F03E44C X-Spam-Score: -6.52 X-TUID: OSW7Dcc/0/87 --=-=-= Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable [=E0=AE=9A=E0=AF=86=E0=AE=B5=E0=AF=8D=E0=AE=B5=E0=AE=BE=E0=AE=AF=E0=AF=8D = =E0=AE=9A=E0=AF=86=E0=AE=AA=E0=AF=8D=E0=AE=9F=E0=AE=AE=E0=AF=8D=E0=AE=AA=E0= =AE=B0=E0=AF=8D 26, 2023] Ihor Radchenko wrote: > Visuwesh writes: > >>> Please, use `make-temp-file' to create files in the temporary >>> directory that may be world writable. Predictable file names there are >>> undesired from security point of view. >> >> What harm does it cause? > > /tmp directory can be written by any program and the file, while kept > there, might be modified by malicious code. > > Not that I know a concrete example what harm can be done in this > particular case, but it is generally a good practice to make file names > in /tmp random. I don't see a way in org-attach-attach's mv method to change the basename of the file post attachment so we have to live with this harm. >>> I am in doubts if the following code is more suitable for org.el or >>> for org-attach.el >> >> I have the same doubts. > > The patch implements dnd and media handlers, which constitute Org mode > integration with the rest of Emacs. So, they are a part of major mode > definition. If we want to keep things clean, we may create org-dnd.el > and org-yank-media.el and put the relevant functions there, only leaving > the major mode setup in org.el I think it would be better to keep the registration part in org-mode's definition in that case since if a user wants to override this functionality, they can easily do so in org-mode-hook. > I do not think that org-attach is the right place for this new > functionality. > >>> Could it be extended to any mime type? If so I would avoid "image" in >>> its name. `org-yank-media-save-dir'? >> >> It should be easy enough to do it but do we want to go there? > > Isn't the patch handling non-images as well? > AFAIU, the only case when images are considered separately is when image > data is in clipboard. The patch handles non-images in the sense that they are simply attached using cp/mv method when they are copy/cut to the clipboard from a file manager. But if your clipboard data contains video/mpeg for example, then the patch does nothing. yank-media would complain about no registered handlers for video/mpeg. BTW, before I forget again for the Nth time: there's an issue with how yank-media works so cut files will not be handled properly unless bug#65892 gets its clearance. I hope the description in the linked bug is clear enough. >>>> + "Method to save images yanked from clipboard and dropped to Emacs. >>>> +It can be the symbol `attach' to add it as an attachment, or a >>>> +directory name to copy/cut the image to that directory." >>>> + :group 'org >>>> + :package-version '(Org . "9.7") >>>> + :type '(choice (const :tag "Add it as attachment" attach) >>>> + (directory :tag "Save it in directory")) >>>> + :safe (lambda (x) (or (stringp x) (eq x 'attach)))) >>> >>> Unsure if every directory may be considered as safe (e.g. ~/.ssh/) >> >> Is there a reliable way to know which directory is "safe"? If not, then >> let the users shoot themselves in the foot. > > We can just say :safe nil (omit the keyword). Then, users will be > prompted and can decide which directories are truly safe for them. That would be quite annoying IMO. I say we let the user shoot themselves in the foot. >>>> +(declare-function org-attach-attach "org-attach" (file &optional visi= t-dir method)) >>>> + >>>> +(defun org--image-yank-media-handler (mimetype data) >>>> + "Save image DATA of mime-type MIMETYPE and insert link at point. >>>> +It is saved as per `org-media-image-save-type'. The name for the >>>> +image is prompted and the extension is automatically added to the >>>> +end." >>>> + (let* ((ext (symbol-name (mailcap-mime-type-to-extension mimetype))) >>>> + (iname (read-string "Insert filename for image: ")) >>> >>> Unless 'attach is used, `read-file-name' would allow saving to >>> arbitrary directory with path completion. >> >> Sorry, I don't understand what you mean here. I am not really familiar >> with attachment facilities of org-mode. > > Imagine that user enters something like > "/tmp/non-existing-directory/image-file" as an answer to > "Insert filename for image: " prompt. How about the following prompt instead? Basename for image file without extension: Attached patch has several of the reviews considered. --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=0001-Add-support-for-yank-media-and-DND.patch >From a11658f82ce71850b52b853a4b44055b8f917486 Mon Sep 17 00:00:00 2001 From: Visuwesh Date: Fri, 22 Sep 2023 20:11:41 +0530 Subject: [PATCH] Add support for yank-media and DND * lisp/org.el (org-mode): Call the setup function for yank-media and DND. (org-setup-yank-dnd-handlers): Register yank-media-handler and DND handler. (org-yank-image-save-type, org-dnd-default-attach-method) (org-yank-image-file-name-function): New defcustoms. (org--image-yank-media-handler, org--copied-files-yank-media-handler) (org--dnd-local-file-handler): Add yank-media and DND handlers. * etc/ORG-NEWS: Advertise the new features. --- etc/ORG-NEWS | 20 ++++++ lisp/org.el | 170 ++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 189 insertions(+), 1 deletion(-) diff --git a/etc/ORG-NEWS b/etc/ORG-NEWS index 252c5a9f9..c4a58dd4d 100644 --- a/etc/ORG-NEWS +++ b/etc/ORG-NEWS @@ -596,6 +596,26 @@ return a matplotlib Figure object to plot. For output results, the current figure (as returned by =pyplot.gcf()=) is cleared before evaluation, and then plotted afterwards. +*** Images and files in clipboard can be attached + +Org can now attach images in clipboard and files copied/cut to the +clipboard from file managers using the ~yank-media~ command which also +inserts a link to the attached file. This command was added in Emacs 29. + +Images can be saved to a separate directory instead of being attached, +customize ~org-yank-image-save-type~. + +Image filename chosen can be customized by setting +~org-yank-image-file-name-function~ which by default autogenerates a +filename based on the current time. + +*** Files and images can be attached by dropping onto Emacs + +Attachment method other than ~org-attach-method~ for dropped files can +be specified using ~org-dnd-default-attach-method~. + +Images dropped also respect the value of ~org-yank-image-save-type~. + ** New functions and changes in function arguments *** =TYPES= argument in ~org-element-lineage~ can now be a symbol diff --git a/lisp/org.el b/lisp/org.el index d0b2355ea..bd36ec4e8 100644 --- a/lisp/org.el +++ b/lisp/org.el @@ -4999,7 +4999,10 @@ The following commands are available: (org--set-faces-extend '(org-block-begin-line org-block-end-line) org-fontify-whole-block-delimiter-line) (org--set-faces-extend org-level-faces org-fontify-whole-heading-line) - (setq-local org-mode-loading nil)) + (setq-local org-mode-loading nil) + + ;; `yank-media' handler and DND support. + (org-setup-yank-dnd-handlers)) ;; Update `customize-package-emacs-version-alist' (add-to-list 'customize-package-emacs-version-alist @@ -20254,6 +20257,171 @@ it has a `diary' type." (org-format-timestamp timestamp fmt t)) (org-format-timestamp timestamp fmt (eq boundary 'end))))))) +;;; Yank media handler and DND +(defun org-setup-yank-dnd-handlers () + "Setup the `yank-media' and DND handlers for buffer." + (setq-local dnd-protocol-alist + (cons '("^file:///" . org--dnd-local-file-handler) + dnd-protocol-alist)) + (when (fboundp 'yank-media-handler) + (yank-media-handler "image/.*" #'org--image-yank-media-handler) + ;; Looks like different DEs go for different handler names, + ;; https://larsee.com/blog/2019/05/clipboard-files/. + (yank-media-handler "x/special-\\(?:gnome\|KDE\|mate\\)-files" + #'org--copied-files-yank-media-handler))) + +(defcustom org-yank-image-save-type 'attach + "Method to save images yanked from clipboard and dropped to Emacs. +It can be the symbol `attach' to add it as an attachment, or a +directory name to copy/cut the image to that directory." + :group 'org + :package-version '(Org . "9.7") + :type '(choice (const :tag "Add it as attachment" attach) + (directory :tag "Save it in directory")) + :safe (lambda (x) (or (stringp x) (eq x 'attach)))) + +(defcustom org-yank-image-file-name-function #'org-yank-image-autogen-filename + "Function to generate filename for image yanked from clipboard. +By default, this autogenerates a filename based on the current +time. +It is called with no arguments and should return a string without +any extension which is used as the filename." + :group 'org + :package-version '(Org . "9.7") + :type '(radio (function-item :doc "Autogenerate filename" + org-yank-image-autogen-filename) + (function-item :doc "Ask for filename" + org-yank-image-read-filename) + function)) + +(defun org-yank-image-autogen-filename () + "Autogenerate filename for image in clipboard." + (format-time-string "clipboard-%Y-%m-%d-%H:%M")) + +(defun org-yank-image-read-filename () + "Read filename for image in clipboard." + (read-string "Basename for image file without extension: ")) + +(declare-function org-attach-attach "org-attach" (file &optional visit-dir method)) + +(defun org--image-yank-media-handler (mimetype data) + "Save image DATA of mime-type MIMETYPE and insert link at point. +It is saved as per `org-yank-image-save-type'. The name for the +image is prompted and the extension is automatically added to the +end." + (let* ((ext (symbol-name (mailcap-mime-type-to-extension mimetype))) + (iname (funcall org-yank-image-file-name-function)) + (filename (file-name-with-extension iname ext)) + (absname (expand-file-name + filename + (if (eq org-yank-image-save-type 'attach) + temporary-file-directory + org-yank-image-save-type))) + link) + (when (and (not (eq org-yank-image-save-type 'attach)) + (not (file-directory-p org-yank-image-save-type))) + (make-directory org-yank-image-save-type t)) + (with-temp-file absname + (insert data)) + (if (null (eq org-yank-image-save-type 'attach)) + (setq link (org-link-make-string + (concat "file:" (file-relative-name absname)) + filename)) + (require 'org-attach) + (org-attach-attach absname nil 'mv) + (setq link (org-link-make-string + (concat "attachment:" filename) + filename))) + (insert link))) + +;; I cannot find a spec for this but +;; https://indigo.re/posts/2021-12-21-clipboard-data.html and pcmanfm +;; suggests that this is the format. +(defun org--copied-files-yank-media-handler (_mimetype data) + "Attach copied or cut files from file manager. +If the files were cut from the file manager, then the `mv' attach +method is used; `cp' otherwise. + +DATA is a string where the first line is the operation to +perform: copy or cut. Rest of the lines are file: links to the +concerned files." + (require 'org-attach) + ;; pcmanfm adds a null byte at the end for some reason. + (let* ((data (split-string data "[\0\n\r]" t "^file://")) + (files (cdr data)) + (method (if (equal (car data) "cut") + 'mv + 'cp))) + (dolist (f files) + (setq f (url-unhex-string f)) + (if (file-readable-p f) + (org-attach-attach f nil method) + (message "File `%s' is not readable, skipping" f))))) + +(defcustom org-dnd-default-attach-method nil + "Default attach method to use when DND action is unspecified. +This attach method is used when the DND action is `private'. +This is also used when `org-yank-image-save-type' is nil. +When nil, use `org-attach-method'." + :group 'org + :package-version '(Org . "9.7") + :type '(choice (const :tag "Default attach method" nil) + (const :tag "Copy" cp) + (const :tag "Move" mv) + (const :tag "Hard link" ln) + (const :tag "Symbolic link" lns))) + +(declare-function mailcap-file-name-to-mime-type "mailcap" (file-name)) +(defvar org-attach-method) + +(defun org--dnd-local-file-handler (url action) + "Attach filename given by URL using method pertaining to ACTION. +If ACTION is `move', use `mv' attach method. +If `copy', use `cp' attach method. +If `ask', ask the user. +If `private', use the method denoted in `org-dnd-default-attach-action'. +The action `private' is always returned." + (require 'mailcap) + (let* ((filename (dnd-get-local-file-name url)) + (mimetype (mailcap-file-name-to-mime-type filename)) + (separatep (and (string-prefix-p "image/" mimetype) + (not (eq 'attach org-yank-image-save-type)))) + (method (pcase action + ('copy 'cp) + ('move 'mv) + ('ask (caddr (org-mks + '(("c" "Copy" cp) + ("m" "Move" mv) + ("l" "Hard link" ln) + ("s" "Symbolic link" lns)) + "How to attach?" + "Attach using method"))) + ('private (or org-dnd-default-attach-method + org-attach-method))))) + (if separatep + (funcall + (pcase method + ('cp #'copy-file) + ('mv #'rename-file) + ('ln #'add-name-to-file) + ('lns #'make-symbolic-link)) + filename + (expand-file-name (file-name-nondirectory filename) + org-yank-image-save-type)) + (org-attach-attach filename nil method)) + (insert + (org-link-make-string + (concat (if separatep + "file:" + "attachment:") + (if separatep + (expand-file-name (file-name-nondirectory filename) + org-yank-image-save-type) + (file-name-nondirectory filename))) + (file-name-nondirectory filename)) + "\n") + 'private)) + ;;; Other stuff (defvar reftex-docstruct-symbol) -- 2.40.1 --=-=-=--