Find file
Fetching contributors…
Cannot retrieve contributors at this time
246 lines (205 sloc) 9.15 KB
;;; scpaste.el --- Paste to the web via scp.
;; Copyright © 2008-2012 Phil Hagelberg and contributors
;; Author: Phil Hagelberg
;; URL:
;; Version: 0.6.5
;; Created: 2008-04-02
;; Keywords: convenience hypermedia
;; EmacsWiki: SCPaste
;; Package-Requires: ((htmlize "1.39"))
;; This file is NOT part of GNU Emacs.
;;; Commentary:
;; This will place an HTML copy of a buffer on the web on a server
;; that the user has shell access on.
;; It's similar in purpose to services such as
;; or, but it's much simpler since it assumes the user
;; has an account on a publicly-accessible HTTP server. It uses `scp'
;; as its transport and uses Emacs' font-lock as its syntax
;; highlighter instead of relying on a third-party syntax highlighter
;; for which individual language support must be added one-by-one.
;;; Install
;; Add Marmalade as a package source, and then run M-x package-install
;; scpaste.
;; Set `scpaste-http-destination' and `scpaste-scp-destination' to
;; appropriate values, and add this to your Emacs config:
;; (setq scpaste-http-destination ""
;; scpaste-scp-destination "")
;; If you have a different keyfile, you can set that, too:
;; (setq scpaste-scp-pubkey "~/.ssh/")
;; If you use a non-standard ssh port, you can specify it by setting
;; `scpaste-scp-port'.
;; If you need to use alternative scp and ssh programs, you can set
;; `scpaste-scp' and `scpaste-ssh'. For example, scpaste works with the Putty
;; suite on Windows if you set these to pscp and plink, respectively.
;; Optionally you can set the displayed name for the footer and where
;; it should link to:
;; (setq scpaste-user-name "Technomancy"
;; scpaste-user-address "")
;;; Usage
;; M-x scpaste, enter a name, and press return. The name will be
;; incorporated into the URL by escaping it and adding it to the end
;; of `scpaste-http-destination'. The URL for the pasted file will be
;; pushed onto the kill ring.
;; You can autogenerate a splash page that gets uploaded as index.html
;; in `scpaste-http-destination' by invoking M-x scpaste-index. This
;; will upload an explanation as well as a listing of existing
;; pastes. If a paste's filename includes "private" it will be skipped.
;; You probably want to set up SSH keys for your destination to avoid
;; having to enter your password once for each paste. Also be sure the
;; key of the host referenced in `scpaste-scp-destination' is in your
;; known hosts file--scpaste will not prompt you to add it but will
;; simply hang.
;;; License:
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 3, or (at your option)
;; any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs; see the file COPYING. If not, write to the
;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
;; Boston, MA 02110-1301, USA.
;;; Code:
(require 'url)
(require 'htmlize)
(defvar scpaste-scp-port
(defvar scpaste-scp
"The scp program to use.")
(defvar scpaste-ssh
"The ssh program to use when running remote shell commands.")
(defvar scpaste-http-destination
"Publicly-accessible (via HTTP) location for pasted files.")
(defvar scpaste-scp-destination
"SSH-accessible directory corresponding to `scpaste-http-destination'.
You must have write-access to this directory via `scp'.")
(defvar scpaste-scp-pubkey
"Identity file for the server.
Corresponds to ssh’s `-i` option Example: \"~/.ssh/\"")
(defvar scpaste-user-name
"Name displayed under the paste.")
(defvar scpaste-user-address
"Link to the user’s homebase (can be a mailto:).")
;; To set defvar while developing: (load-file (buffer-file-name))
(defvar scpaste-el-location (replace-regexp-in-string "\.elc$" ".el"
(defun scpaste-footer ()
"HTML message to place at the bottom of each file."
(concat "<p style='font-size: 8pt; font-family: monospace;'>Generated by "
(let ((user (or scpaste-user-name user-full-name)))
;;(concat "DEBUG" user scpaste-user-name scpaste-user-address)
(if scpaste-user-address
(concat "<a href='" scpaste-user-address "'>" user "</a>")
" using <a href=''>scpaste</a> at %s. "
(cadr (current-time-zone)) ". (<a href='%s'>original</a>)</p>"))
(defun scpaste (original-name)
"Paste the current buffer via `scp' to `scpaste-http-destination'.
If ORIGINAL-NAME is an empty string, then the buffer name is used
for the file name."
(interactive "MName (defaults to buffer name): ")
(let* ((b (generate-new-buffer (generate-new-buffer-name "b")))
(hb (htmlize-buffer))
(name (replace-regexp-in-string "[/\\%*:|\"<> ]+" "_"
(if (equal "" original-name)
(full-url (concat scpaste-http-destination
"/" (url-hexify-string name) ".html"))
(scp-destination (concat scpaste-scp-destination
"/" name ".html"))
(scp-original-destination (concat scpaste-scp-destination
"/" name))
(tmp-file (concat temporary-file-directory name))
(tmp-hfile (concat temporary-file-directory name ".html")))
;; Save the files (while adding a footer to html file)
(copy-to-buffer b (point-min) (point-max))
(switch-to-buffer b)
(write-file tmp-file)
(kill-buffer b)
(switch-to-buffer hb)
(goto-char (point-min))
(search-forward "</body>\n</html>")
(insert (format (scpaste-footer)
(substring full-url 0 -5)))
(write-file tmp-hfile)
(kill-buffer hb))
(let* ((identity (if scpaste-scp-pubkey
(concat "-i " scpaste-scp-pubkey) ""))
(port (if scpaste-scp-port (concat "-P " scpaste-scp-port)))
(invocation (concat scpaste-scp " -q " identity " " port))
(command-1 (concat invocation " " tmp-file " "
(command-2 (concat invocation " " tmp-hfile " "
(let* ((error-buffer "*scp-error*")
(retval (+
(format "Executing %s" command-1)
(shell-command command-1 nil error-buffer))
(format "Executing %s" command-2)
(shell-command command-2 nil error-buffer))))
;; (select-enable-primary t))
(x-select-enable-primary t))
(delete-file tmp-file)
(delete-file tmp-hfile)
;; Notify user and put the URL on the kill ring
(if (= retval 0)
(progn (kill-new full-url)
(message "Pasted to %s (on kill ring)" full-url))
(pop-to-buffer error-buffer)
(defun scpaste-region (name)
"Paste the current region via `scpaste'.
NAME is used for the file name."
(interactive "MName: ")
(let ((region-contents (buffer-substring (mark) (point))))
(insert region-contents)
(scpaste name))))
(defun scpaste-index ()
"Generate an index of all existing pastes on server on the splash page."
(let* ((dest-parts (split-string scpaste-scp-destination ":"))
(files (shell-command-to-string (concat scpaste-ssh " " (car dest-parts)
" ls " (cadr dest-parts))))
(file-list (split-string files "\n")))
(insert-file-contents scpaste-el-location)
(goto-char (point-min))
(search-forward ";;; Commentary")
(forward-line -1)
(insert "\n;;; Pasted Files\n\n")
(dolist (file file-list)
(when (and (string-match "\\.html$" file)
(not (string-match "private" file)))
(insert (concat ";; * <" scpaste-http-destination "/" file ">\n"))))
(if (fboundp 'font-lock-ensure)
(rename-buffer "SCPaste")
(write-file (concat temporary-file-directory "scpaste-index"))
(scpaste "index")))))
(provide 'scpaste)
;;; scpaste.el ends here