;;; emacs-upload.el --- Upload regions, buffers, and files to various hosts. -*- lexical-binding: t; -*- ;;; emacs-upload - Upload the region, buffer, or file to various hosts ;;; Copyright (C) <2019,2020> ;;; This program is free software: you can redistribute it and/or ;;; modify it under the terms of the GNU Affero General Public License ;;; v3.0 as published by the Free Software Foundation. ;;; This program is distributed in the hope that it will be useful, but ;;; WITHOUT ANY WARRANTY; without even the implied warranty of ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ;;; Affero General Public License for more details. ;;; You should have received a copy of the GNU Affero General Public License ;;; along with this program. If not, see . ;;; Dependencies: cURL, cl, json ;;; _Usage_ ;;; ;;; Place emacs-upload.el in your `load-path' and load the package: ;;; ;;; (require 'emacs-upload) ;;; ;;; Bind the interactive function `emacs-upload' to some key: ;;; ;;; (global-set-key (kbd "C-c e") 'emacs-upload) ;;; ;;; Alternatively, with `use-package' you can easily bind and set a default host. ;;; ;;; (use-package emacs-upload ;;; :demand t ;;; :bind ;;; ("C-c e" . emacs-upload) ;;; :config ;;; (emacs-upload/set-host "ix")) ;;; `emacs-upload/set-host' is also an interactive command, so you can ;;; change your host on the fly. ;;; To upload the region, simply select the region and call emacs-upload. To ;;; upload the whole buffer, have no region selected and call emacs-upload. For ;;; a file, use the universal argument `C-u' before calling emacs-upload. (require 'cl) (require 'json) (defcustom emacs-upload/host '("http://w1r3.net" "upload=@%s") "Current host to upload to.") (defcustom emacs-upload/hosts '(("w1r3" . ("https://w1r3.net" "upload=@%s")) ("ix" . ("http://ix.io" "f:1=@%s")) ("0x0" . ("https://0x0.st" "file=@%s")) ("sprunge" . ("http://sprunge.us" "sprunge=@%s")) ("youdieifyou.work" . ("http://youdieifyou.work/upload.php" "files[]=@%s")) ("catbox" . ("https://catbox.moe/user/api.php" "reqtype=fileupload" "fileToUpload=@%s")) ("fiery" . ("https://safe.fiery.me/api/upload" "files[]=@%s")) ("dmca.gripe" . ("https://dmca.gripe/api/upload" "files[]=@%s"))) "List of hosts you can set emacs-upload/host to.") (defun emacs-upload/buffer-list () "Return a list consisting of the minimum and maximum point values." (list (point-min) (point-max))) (defun emacs-upload/region-list () "Return a list consisting of the beginning and end of the active region." (list (region-beginning) (region-end))) (defun emacs-upload/make-temp-file (file-name) "Return a string of a temporarily created file with the suffix either being the filename's extension, if it exists, or `.txt' for buffers not associated with a file." (make-temp-file "emacs-upload" nil (if file-name (file-name-extension file-name t) ".txt"))) (defun emacs-upload/write-content-to-file (content file) "Write content to file, content being a list of the beginning and ending points." (apply 'write-region (append content (list file)))) (defun emacs-upload/list-curl-forms (file) "Generate a list of curl forms" (if (cddr emacs-upload/host) (let* ((forms (mapcan (lambda (form) (list "-F" form)) (rest emacs-upload/host))) (file-form (car (last forms)))) (append (butlast forms) (list (format file-form file) (first emacs-upload/host)))) (list "-F" (format (second emacs-upload/host) file) (first emacs-upload/host)))) (defun emacs-upload/start-curl-process (file) "Start curl process" (apply 'start-process "emacs-upload" nil "curl" (emacs-upload/list-curl-forms file))) (defun emacs-upload/pomf-parse (json) "Parse json and return a URL string or nil if an error occurred" (let* ((pretty-json (json-read-from-string json)) (success (equal (cdr (assoc 'success pretty-json)) t))) (if success (cdr (assoc 'url (aref (cdr (cadr pretty-json)) 0))) (let ((inhibit-message t)) (mapc (lambda (desc) (message "%s: %s" (car desc) (cdr desc))) pretty-json) nil)))) (defun emacs-upload/filter (process output) "Determine whether or not the output is json, either parsing the json or simply stripping the newlines from output and killing that." (if (string-match "{" output) (let ((result (emacs-upload/pomf-parse output))) (if result (message "File uploaded at %s and saved to your kill-ring." (kill-new result)) (message "Error while uploading file. See *Messages* buffer for more information."))) (if (string-match "http" output) (message "File uploaded at %s and saved to your kill ring." (kill-new (replace-regexp-in-string "\n$" "" output))) (progn (let ((inhibit-message t)) (message output) nil) (message "Error while uploading file. See *Messages* buffer for more information"))))) ;;;###autoload (defun emacs-upload/set-host (host) "Set host. Call this interactively or as function from your init to set a default from the list of available hosts, such as: (emacs-upload/set-host \"ix\")" (interactive (list (completing-read "Set the host: " (mapcar 'first emacs-upload/hosts) nil t))) (setq emacs-upload/host (cdr (assoc host emacs-upload/hosts))) (message "emacs-upload/host is now set to %s" host)) ;;;###autoload (defun emacs-upload (&optional arg) "Upload the region if selected, the buffer if it is not, or if called with ARG (C-u) select a file." (interactive "P") (if (executable-find "curl") (let ((file nil)) (cond ((and (not arg) (region-active-p)) (setq file (emacs-upload/make-temp-file nil)) (emacs-upload/write-content-to-file (emacs-upload/region-list) file)) (arg (setq file (read-file-name "File to upload: "))) (t (setq file (emacs-upload/make-temp-file nil)) (emacs-upload/write-content-to-file (emacs-upload/buffer-list) file))) (message "Uploading...") (set-process-filter (emacs-upload/start-curl-process file) 'emacs-upload/filter)) (message "cURL not found"))) (provide 'emacs-upload) ;;; emacs-upload.el ends here