;;; woof.el --- Emacs interface to exchange files on different computers via woof ;; Copyright (C) 2007-2008 by Stefan Reichoer ;; Author: Stefan Reichoer, ;; woof.el 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 2, or (at your option) ;; any later version. ;; woof.el 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 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., 59 Temple Place - Suite 330, ;; Boston, MA 02111-1307, USA. ;;; Commentary: ;; The problem: ;; You work on different computers and you want to send files or ;; the selected text to other hosts. ;; The solution: ;; woof (Web Offer One File): http://www.home.unix-ag.org/simon/woof.html ;; woof is a small webserver that serves a given file ;; you can download the file on a different host from this webserver ;; woof.el integrates the woof script in your emacs work-flow: ;; You can easily provide a file to download (M-x woof-provide-file) ;; And it is also easy to receive such a file on a different host ;; (M-x woof-receive or C-u M-x woof-receive) ;; woof.el needs the woof script from: ;; http://www.home.unix-ag.org/simon/woof ;; woof.el also needs wget ;; It should work on GNU/Linux systems, I have also added some hacks to make it work on cygwin. ;; The latest version of woof.el can be found at: ;; http://www.xsteve.at/prg/emacs/ ;; Usage: ;; put the following in your .emacs: ;; (require 'woof) ;; (woof-set-download-url-list '("http://xxx.xxx.xxx.xxx:8080" "http://yyy.yyy.yyy.yyy:8080" ;; ("nice-name" "http://zzz.zzz.zzz.zzz:8080"))) ;; (global-set-key [] 'woof-provide-dwim) ;; (global-set-key [] 'woof-receive) ;; If you are using cygwin, you probably need the following also: ;; (setq woof-cygwin-bin-path "c:/cygwin/bin") ;; (setq woof-wget-cmd "c:/cygwin/bin/wget") ;; (setq woof-cmd "c:/cygwin/usr/local/bin/woof") ;; Woof is also available via the Tools menu ;; Please contact me, if you find woof.el useful, or if you have ideas for improvements. ;;; History: ;; ;;; Code: (defvar woof-cmd "woof") (defvar woof-wget-cmd "wget") (defvar woof-insert-clip-after-download t "Insert a downloaded clipboard content in the current buffer when possible") (defvar woof-cygwin-bin-path "c:/cygwin/bin") (defvar woof-download-url-list nil) (defvar woof-completing-read-function (if (fboundp 'ido-completing-read) 'ido-completing-read 'completing-read)) (defvar woof-use-cygwin (eq system-type 'windows-nt)) (defvar woof-temp-dir (expand-file-name (or (when (boundp 'temporary-file-directory) temporary-file-directory) (when (fboundp 'temp-directory) (file-name-as-directory (temp-directory))) "/tmp/")) "The directory that is used to store temporary files for woof.") ;;; Internal variables (defvar woof-process-buffer-name "*woof*") (defvar woof-wget-process-buffer-name "*woof-wget*") (defvar woof-clip-file-name "woof.el-Clipboard") (defvar woof-download-url nil) (defvar woof-current-download-url nil) (defvar woof-last-provided-file nil) (defvar woof-delete-last-provided-file nil) (defvar woof-last-provided-message nil) (defvar woof-provide-url nil) ;; compatibility (defun woof-mark-active() (if (boundp 'region-active-p) (region-active-p) ;; XEmacs mark-active)) ;; GNU Emacs (defun woof-add-to-global-menu () "Add a woof menu to the Tools main menu" (interactive) (condition-case nil (easy-menu-add-item nil '("tools") (append '("Woof") '(["Provide DWIM" woof-provide-dwim (not woof-last-provided-message)] ["Provide the woof script" woof-provide-woof (not woof-last-provided-message)] ["Provide woof.el" woof-provide-woof-el (not woof-last-provided-message)] ["Cancel woof service" woof-provide-cancel woof-last-provided-message]) '("---") (mapcar '(lambda (url) (vector (format "Receive from %s" (if (consp url) (format "%s: %s" (car url) (cadr url)) url)) `(lambda () (interactive) (woof-receive-from (quote ,url)) t))) woof-download-url-list) )) (error (message "woof: could not install menu")))) (defun woof-provide-dwim () "When mark is active, send the selected region, otherwise the current file. When woof is still serving a file, ask if this server should be killed." (interactive) (when woof-last-provided-message (when (y-or-n-p (format "woof is still serving '%s' - kill this server? " woof-last-provided-message)) (woof-provide-cancel) (sit-for 0.5))) (cond ((woof-mark-active) (woof-provide-clip (buffer-substring-no-properties (region-beginning) (region-end)))) ((member major-mode '(dired-mode locate-mode)) (woof-provide-file (dired-get-filename))) ((eq major-mode 'gnus-article-mode) (woof-provide-gnus-attachment)) (t (save-buffer) (woof-provide-file (buffer-file-name))))) (defvar woof-cygwin-cygdrive-prefix "/cygdrive") (defun woof-fixup-cygwin-path (file-name) ;; convert c:/ -> /cygdrive/c/, for drive letters a..z (string-match "\\([a-z]\\):/\\(.+\\)" file-name) (format (concat woof-cygwin-cygdrive-prefix "/%s/%s") (match-string 1 file-name) (match-string 2 file-name))) ;;(woof-fixup-cygwin-path "c:/readme.txt") (defvar woof-current-ip nil) (defun woof-get-ip-on-windows () (unless woof-current-ip (with-temp-buffer (shell-command "ipconfig" (current-buffer)) (goto-char (point-min)) (re-search-forward "IP-.+: \\([0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+\\)") (setq woof-current-ip (match-string 1)))) woof-current-ip) (defun woof-provide-file (file-name) "Provide a file to download via woof." (interactive "fProvide File via woof: ") (let ((delete-provided-file nil) (full-cmd (if woof-use-cygwin (progn (when (numberp (string-match " " file-name)) (copy-file file-name (concat woof-temp-dir (file-name-nondirectory file-name)) t) (setq file-name (concat woof-temp-dir (file-name-nondirectory file-name))) (setq delete-provided-file t)) (let* ((cygwin-bin-path woof-cygwin-bin-path) (sh-cmd (format "%s/sh -c" cygwin-bin-path)) (python-cmd (format "%s/python" cygwin-bin-path)) (cyg-file-name (woof-fixup-cygwin-path file-name)) (cmd (format "%s %s" sh-cmd (shell-quote-argument (format "%s %s %s" python-cmd woof-cmd cyg-file-name))))) cmd)) (format "%s %s" woof-cmd (shell-quote-argument file-name))))) (let ((woof-proc (start-process-shell-command "woof" woof-process-buffer-name full-cmd))) (setq woof-last-provided-file (expand-file-name file-name)) (setq woof-delete-last-provided-file delete-provided-file) (setq woof-last-provided-message file-name) (set-process-sentinel woof-proc 'woof-process-sentinel) (set-process-filter woof-proc 'woof-process-filter) (woof-provide-mode 1)) (when woof-use-cygwin (setq woof-provide-url (format "http://%s:8080/" (woof-get-ip-on-windows))) (message "woof: Now serving '%s' on '%s'" file-name woof-provide-url)))) (defun woof-provide-clip (clip-text) "Provide a string to download via woof. This string is saved in a file named `woof-clip-file-name'. If the receiver also uses woof.el, the received data is immediately copied to the kill-ring." (interactive "sProvide string via woof: ") (let ((clip-file-name (concat woof-temp-dir woof-clip-file-name))) (with-current-buffer (find-file-noselect clip-file-name) (delete-region (point-min) (point-max)) (insert clip-text) (save-buffer) (kill-buffer (current-buffer))) (woof-provide-file clip-file-name) (setq woof-delete-last-provided-file t) (setq woof-last-provided-message ""))) (defun woof-provide-woof () "Provide the woof script to download via woof." (interactive) (let ((full-cmd (if woof-use-cygwin (let* ((cygwin-bin-path woof-cygwin-bin-path) (sh-cmd (format "%s/sh -c" cygwin-bin-path)) (python-cmd (format "%s/python" cygwin-bin-path)) (cmd (format "%s %s" sh-cmd (shell-quote-argument (format "%s %s" python-cmd woof-cmd))))) cmd) (format "%s -s" woof-cmd)))) (let ((woof-proc (start-process-shell-command "woof" woof-process-buffer-name full-cmd))) (setq woof-last-provided-file 'woof) (setq woof-last-provided-message "woof") (set-process-sentinel woof-proc 'woof-process-sentinel) (set-process-filter woof-proc 'woof-process-filter)))) (defun woof-provide-woof-el () "Provide woof.el to download via woof." (interactive) (woof-provide-file (locate-library "woof.el"))) (defun woof-provide-gnus-attachment () "Provide the attachment at point from a gnus article buffer" (let ((handle (get-text-property (point) 'gnus-data)) (file-name)) (when handle (setq filename (or (mail-content-type-get (mm-handle-disposition handle) 'filename) (mail-content-type-get (mm-handle-type handle) 'name))) (when filename (setq filename (concat woof-temp-dir (gnus-map-function mm-file-name-rewrite-functions (file-name-nondirectory filename)))) (mm-save-part-to-file handle filename) (woof-provide-file filename) (setq woof-delete-last-provided-file t) (setq woof-last-provided-message (format "Gnus Attachment <%s>" (file-name-nondirectory filename))))))) (defun woof-provide-cancel () "Cancel the serving of a provided file" (interactive) (kill-buffer (get-buffer woof-process-buffer-name))) (defun woof-provide-show-status () "Show a status of the currently provided woof file." (interactive) (message "woof: providing '%s' on '%s'" woof-last-provided-message woof-provide-url)) (unless (boundp 'woof-provide-mode-map) (setq woof-provide-mode-map (make-sparse-keymap))) (easy-menu-define woof-provide-mode-menu woof-provide-mode-map "Menu used when woof minor mode is active." '("woof" ["Cancel provided" woof-provide-cancel t] ["Providing status" woof-provide-show-status t])) (define-minor-mode woof-provide-mode "Minor mode indicating that woof provides a file" :lighter (" woof") :keymap woof-provide-mode-map) (defun woof-process-filter (process str) (save-window-excursion (set-buffer woof-process-buffer-name) (let ((start-position (point-max))) ;;(message "woof-process-filter: %s" str) (goto-char start-position) (insert str) (save-excursion (goto-char start-position) (when (looking-at "Now serving on \\(.+\\)") (message "woof: Now serving '%s' on '%s'" woof-last-provided-message (match-string 1)) (setq woof-provide-url (match-string 1))))))) (defun woof-process-sentinel (process event) ;;(princ (format "Process: %s had the event `%s'" process event)) (cond ((string= event "finished\n") (when woof-delete-last-provided-file (when (file-exists-p woof-last-provided-file) (delete-file woof-last-provided-file))) (if (string= (file-name-nondirectory woof-last-provided-file) (file-name-nondirectory woof-clip-file-name)) (message "woof: serving region content done") (message "woof: serving '%s' finished" woof-last-provided-message))) ((string= event "killed\n") (message "woof process killed")) ((string= event "hangup\n") (message "woof process hangup: '%s'" woof-last-provided-message)) ((string-match "exited abnormally" event) (while (accept-process-output process 0 100)) ;; find last error message and show it. (goto-char (point-max)) (message "woof failed: %s" (replace-regexp-in-string "\n" "" event))) (t (message "woof process had unknown event: %s" event))) (woof-provide-mode 0) (setq woof-last-provided-message nil)) ;; ------------------------------------------------------------------------------------------ ;; woof wget download interface ;; ------------------------------------------------------------------------------------------ (defun woof-select-dowload-url () "Select the default download Url for `woof-receive' calls." (interactive) (let ((selected (funcall woof-completing-read-function "Select woof download Url: " woof-download-url-list))) (setq woof-download-url (or (assoc selected woof-download-url-list) selected)))) (defun woof-receive-from (url) "Download using `woof-receive'. Use URL as address." (let ((woof-download-url url)) (woof-receive nil))) (defun woof-receive (arg) "Download via wget from the given url in `woof-download-url'. When called with a prefix argument, select the download url first via `woof-select-dowload-url'." (interactive "P") (when arg (woof-select-dowload-url)) (setq woof-current-download-url (or woof-download-url (car woof-download-url-list))) (when (consp woof-current-download-url) (setq woof-current-download-url (cadr woof-current-download-url))) (woof-wget-run (format "%s -nv %s" woof-wget-cmd woof-current-download-url))) (defun woof-wget-run (cmd) (setq woof-download-file nil) (let ((woof-proc (start-process-shell-command "woof-wget" woof-wget-process-buffer-name cmd)) (directory default-directory)) (with-current-buffer woof-wget-process-buffer-name (setq default-directory directory)) ;;seems to be needed on windows (set-process-sentinel woof-proc 'woof-wget-process-sentinel) (set-process-filter woof-proc 'woof-wget-process-filter))) (defun woof-wget-process-filter (process str) (save-window-excursion (set-buffer woof-wget-process-buffer-name) (let ((start-position (point-max))) ;; (message "woof-wget-process-filter: %s" str) (goto-char start-position) (setq start-position (line-beginning-position)) (insert str) (save-excursion (goto-char start-position) ;; 10:42:26 URL:http://123.45.67.89:8080/woof.el [4124/4124] -> "woof.el" [1] (when (looking-at ".+ -> \"\\(.+\\)\"") (setq woof-download-file (expand-file-name (match-string 1)))))))) (defun woof-wget-process-sentinel (process event) ;;(princ (format "Process: %s had the event `%s'" process event)) (cond ((string= event "finished\n") (if (string= (file-name-nondirectory woof-download-file) (file-name-nondirectory woof-clip-file-name)) (progn (with-current-buffer (find-file-noselect woof-download-file) (kill-new (buffer-substring-no-properties (point-min) (point-max))) (kill-buffer (current-buffer)) (delete-file woof-download-file)) (when woof-insert-clip-after-download (unless buffer-read-only (yank))) (message "woof: Copied received data to clipboard")) (message "woof-wget downloaded: %s" woof-download-file) (when (member major-mode '(dired-mode locate-mode)) (when (string= (file-name-directory woof-download-file) (expand-file-name default-directory)) (revert-buffer) (dired-goto-file woof-download-file))))) ((string= event "killed\n") (message "woof-wget process killed")) ((string= event "hangup\n") (message "woof-wget process hangup")) ((string-match "exited abnormally" event) (message "woof-wget failed: %s, %s probably not serving" (replace-regexp-in-string "\n" "" event) woof-current-download-url)) (t (message "woof-wget process had unknown event: %s" event)))) (defun woof-set-download-url-list (l) "Set `woof-download-url-list' to L and update the woof menu entry" (setq woof-download-url-list l) (woof-add-to-global-menu)) ;; Runtime initialization (woof-add-to-global-menu) (provide 'woof) ;;; arch-tag: f3b53108-383c-49a7-a70f-3c8a7fa6b27c ;;; woof.el ends here