;;; erc-dcc.el --- CTCP DCC module for ERC
-;; Copyright (C) 1993-1995, 1998, 2002-2004, 2006-2011
-;; Free Software Foundation, Inc.
+;; Copyright (C) 1993-1995, 1998, 2002-2004, 2006-2014 Free Software
+;; Foundation, Inc.
;; Author: Ben A. Mesander <ben@gnu.ai.mit.edu>
;; Noah Friedman <friedman@prep.ai.mit.edu>
;; Per Persson <pp@sno.pp.se>
-;; Maintainer: mlang@delysid.org
+;; Maintainer: emacs-devel@gnu.org
;; Keywords: comm, processes
;; Created: 1994-01-23
;;; Code:
(require 'erc)
-(eval-when-compile
- (require 'cl)
- (require 'pcomplete))
+(eval-when-compile (require 'pcomplete))
;;;###autoload (autoload 'erc-dcc-mode "erc-dcc")
(define-erc-module dcc nil
:group 'erc)
(defcustom erc-dcc-verbose nil
- "*If non-nil, be verbose about DCC activity reporting."
+ "If non-nil, be verbose about DCC activity reporting."
:group 'erc-dcc
:type 'boolean)
(* (nth 1 ips) 65536.0)
(* (nth 2 ips) 256.0)
(nth 3 ips))))
- (if (interactive-p)
+ (if (called-interactively-p 'interactive)
(message "%s is %.0f" ip res)
(format "%.0f" res)))))
:valid-regexp erc-dcc-ipv4-regexp)))
(defcustom erc-dcc-send-request 'ask
- "*How to treat incoming DCC Send requests.
+ "How to treat incoming DCC Send requests.
'ask - Report the Send request, and wait for the user to manually accept it
You might want to set `erc-dcc-auto-masks' for this.
'auto - Automatically accept the request and begin downloading the file
(with-no-warnings ; obsolete since 23.1
(set-process-filter-multibyte process nil)))))
(file-error
- (unless (and (string= "Cannot bind server socket" (cadr err))
- (string= "address already in use" (caddr err)))
+ (unless (and (string= "Cannot bind server socket" (nth 1 err))
+ (string= "address already in use" (nth 2 err)))
(signal (car err) (cdr err)))
(setq port (1+ port))
(unless (< port upper)
;;; Interactive command handling
(defcustom erc-dcc-get-default-directory nil
- "*Default directory for incoming DCC file transfers.
+ "Default directory for incoming DCC file transfers.
If this is nil, then the current value of `default-directory' is used."
:group 'erc-dcc
:type '(choice (const nil :tag "Default directory") directory))
(pcomplete-here (append '("chat" "close" "get" "list")
(when (fboundp 'make-network-process) '("send"))))
(pcomplete-here
- (case (intern (downcase (pcomplete-arg 1)))
- (chat (mapcar (lambda (elt) (plist-get elt :nick))
+ (pcase (intern (downcase (pcomplete-arg 1)))
+ (`chat (mapcar (lambda (elt) (plist-get elt :nick))
+ (erc-remove-if-not
+ #'(lambda (elt)
+ (eq (plist-get elt :type) 'CHAT))
+ erc-dcc-list)))
+ (`close (erc-delete-dups
+ (mapcar (lambda (elt) (symbol-name (plist-get elt :type)))
+ erc-dcc-list)))
+ (`get (mapcar #'erc-dcc-nick
(erc-remove-if-not
#'(lambda (elt)
- (eq (plist-get elt :type) 'CHAT))
+ (eq (plist-get elt :type) 'GET))
erc-dcc-list)))
- (close (erc-delete-dups
- (mapcar (lambda (elt) (symbol-name (plist-get elt :type)))
- erc-dcc-list)))
- (get (mapcar #'erc-dcc-nick
- (erc-remove-if-not
- #'(lambda (elt)
- (eq (plist-get elt :type) 'GET))
- erc-dcc-list)))
- (send (pcomplete-erc-all-nicks))))
+ (`send (pcomplete-erc-all-nicks))))
(pcomplete-here
- (case (intern (downcase (pcomplete-arg 2)))
- (get (mapcar (lambda (elt) (plist-get elt :file))
- (erc-remove-if-not
- #'(lambda (elt)
- (and (eq (plist-get elt :type) 'GET)
- (erc-nick-equal-p (erc-extract-nick
- (plist-get elt :nick))
- (pcomplete-arg 1))))
- erc-dcc-list)))
- (close (mapcar #'erc-dcc-nick
- (erc-remove-if-not
- #'(lambda (elt)
- (eq (plist-get elt :type)
- (intern (upcase (pcomplete-arg 1)))))
- erc-dcc-list)))
- (send (pcomplete-entries)))))
+ (pcase (intern (downcase (pcomplete-arg 2)))
+ (`get (mapcar (lambda (elt) (plist-get elt :file))
+ (erc-remove-if-not
+ #'(lambda (elt)
+ (and (eq (plist-get elt :type) 'GET)
+ (erc-nick-equal-p (erc-extract-nick
+ (plist-get elt :nick))
+ (pcomplete-arg 1))))
+ erc-dcc-list)))
+ (`close (mapcar #'erc-dcc-nick
+ (erc-remove-if-not
+ #'(lambda (elt)
+ (eq (plist-get elt :type)
+ (intern (upcase (pcomplete-arg 1)))))
+ erc-dcc-list)))
+ (`send (pcomplete-entries)))))
(defun erc-dcc-do-CHAT-command (proc &optional nick)
(when nick
;;;###autoload
(defvar erc-ctcp-query-DCC-hook '(erc-ctcp-query-DCC)
- "Hook variable for CTCP DCC queries")
+ "Hook variable for CTCP DCC queries.")
(defvar erc-dcc-query-handler-alist
'(("SEND" . erc-dcc-handle-ctcp-send)
?q query ?n nick ?u login ?h host))))
(defconst erc-dcc-ctcp-query-send-regexp
- "^DCC SEND \\([^ ]+\\) \\([0-9]+\\) \\([0-9]+\\) *\\([0-9]*\\)")
+ (concat "^DCC SEND \\("
+ ;; Following part matches either filename without spaces
+ ;; or filename enclosed in double quotes with any number
+ ;; of escaped double quotes inside.
+ "\"\\(\\(.*?\\(\\\\\"\\)?\\)+?\\)\"\\|\\([^ ]+\\)"
+ "\\) \\([0-9]+\\) \\([0-9]+\\) *\\([0-9]*\\)"))
+
+(defsubst erc-dcc-unquote-filename (filename)
+ (erc-replace-regexp-in-string "\\\\\\\\" "\\"
+ (erc-replace-regexp-in-string "\\\\\"" "\"" filename t t) t t))
(defun erc-dcc-handle-ctcp-send (proc query nick login host to)
"This is called if a CTCP DCC SEND subcommand is sent to the client.
'dcc-request-bogus
?r "SEND" ?n nick ?u login ?h host))
((string-match erc-dcc-ctcp-query-send-regexp query)
- (let ((filename (match-string 1 query))
- (ip (erc-decimal-to-ip (match-string 2 query)))
- (port (match-string 3 query))
- (size (match-string 4 query)))
+ (let ((filename
+ (or (match-string 5 query)
+ (erc-dcc-unquote-filename (match-string 2 query))))
+ (ip (erc-decimal-to-ip (match-string 6 query)))
+ (port (match-string 7 query))
+ (size (match-string 8 query)))
;; FIXME: a warning really should also be sent
;; if the ip address != the host the dcc sender is on.
(erc-display-message
"^DCC CHAT +chat +\\([0-9]+\\) +\\([0-9]+\\)")
(defcustom erc-dcc-chat-request 'ask
- "*How to treat incoming DCC Chat requests.
+ "How to treat incoming DCC Chat requests.
'ask - Report the Chat request, and wait for the user to manually accept it
'auto - Automatically accept the request and open a new chat window
'ignore - Ignore incoming DCC chat requests completely."
;;; SEND handling
(defcustom erc-dcc-block-size 1024
- "*Block size to use for DCC SEND sessions."
+ "Block size to use for DCC SEND sessions."
:group 'erc-dcc
:type 'integer)
(defcustom erc-dcc-pump-bytes nil
- "*If set to an integer, keep sending until that number of bytes are
+ "If set to an integer, keep sending until that number of bytes are
unconfirmed."
:group 'erc-dcc
:type '(choice (const nil) integer))
(defcustom erc-dcc-send-connect-hook
'(erc-dcc-display-send erc-dcc-send-block)
- "*Hook run whenever the remote end of a DCC SEND offer connected to your
+ "Hook run whenever the remote end of a DCC SEND offer connected to your
listening port."
:group 'erc-dcc
:type 'hook)
(let* ((buffer (erc-dcc-find-file file))
(size (buffer-size buffer))
(start (with-current-buffer buffer
- (set-marker (make-marker) (point-min))))
+ (point-min-marker)))
(sproc (erc-dcc-server "dcc-send"
'erc-dcc-send-filter
'erc-dcc-send-sentinel))
(set-buffer-multibyte nil))
(setq mode-line-process '(":%s")
- buffer-file-type t
buffer-read-only t)
(setq erc-dcc-file-name file)
;;; CHAT handling
(defcustom erc-dcc-chat-buffer-name-format "DCC-CHAT-%s"
- "*Format to use for DCC Chat buffer names."
+ "Format to use for DCC Chat buffer names."
:group 'erc-dcc
:type 'string)
(defcustom erc-dcc-chat-mode-hook nil
- "*Hook calls when `erc-dcc-chat-mode' finished setting up the buffer."
+ "Hook calls when `erc-dcc-chat-mode' finished setting up the buffer."
:group 'erc-dcc
:type 'hook)
(pcomplete-here '("auto" "ask" "ignore")))
(defalias 'pcomplete/erc-mode/SREQ 'pcomplete/erc-mode/CREQ)
-(defvar erc-dcc-chat-filter-hook '(erc-dcc-chat-parse-output)
- "*Hook to run after doing parsing (and possible insertion) of DCC messages.")
+(defvar erc-dcc-chat-filter-functions '(erc-dcc-chat-parse-output)
+ "Abnormal hook run after parsing (and maybe inserting) a DCC message.
+Each function is called with two arguments: the ERC process and
+the unprocessed output.")
+
+(define-obsolete-variable-alias 'erc-dcc-chat-filter-hook
+ 'erc-dcc-chat-filter-functions "24.3")
(defvar erc-dcc-chat-mode-map
(let ((map (make-sparse-keymap)))
(setq erc-dcc-from nick)
(setq erc-dcc-entry-data entry)
(setq erc-dcc-unprocessed-output "")
- (setq erc-insert-marker (set-marker (make-marker) (point-max)))
+ (setq erc-insert-marker (point-max-marker))
(setq erc-input-marker (make-marker))
(erc-display-prompt buffer (point-max))
(set-process-buffer proc buffer)
(set-buffer (process-buffer proc))
(setq erc-dcc-unprocessed-output
(concat erc-dcc-unprocessed-output str))
- (run-hook-with-args 'erc-dcc-chat-filter-hook proc
- erc-dcc-unprocessed-output))
+ (run-hook-with-args 'erc-dcc-chat-filter-functions
+ proc erc-dcc-unprocessed-output))
(set-buffer orig-buffer))))
(defun erc-dcc-chat-parse-output (proc str)
(defun erc-dcc-no-such-nick (proc parsed)
"Detect and handle no-such-nick replies from the IRC server."
- (let* ((elt (erc-dcc-member :nick (second (erc-response.command-args parsed))
+ (let* ((elt (erc-dcc-member :nick (nth 1 (erc-response.command-args parsed))
:parent proc))
(peer (plist-get elt :peer)))
(when (or (and (processp peer) (not (eq (process-status peer) 'open)))