aboutsummaryrefslogtreecommitdiffstats
path: root/lisp/erc/erc-dcc.el
diff options
context:
space:
mode:
Diffstat (limited to 'lisp/erc/erc-dcc.el')
-rw-r--r--lisp/erc/erc-dcc.el133
1 files changed, 90 insertions, 43 deletions
diff --git a/lisp/erc/erc-dcc.el b/lisp/erc/erc-dcc.el
index 8158c0999d3..2aca06479f6 100644
--- a/lisp/erc/erc-dcc.el
+++ b/lisp/erc/erc-dcc.el
@@ -60,6 +60,12 @@
60 (require 'cl) 60 (require 'cl)
61 (require 'pcomplete)) 61 (require 'pcomplete))
62 62
63;;;###autoload (autoload 'erc-dcc-mode "erc-dcc")
64(define-erc-module dcc nil
65 "Provide Direct Client-to-Client support for ERC."
66 ((add-hook 'erc-server-401-functions 'erc-dcc-no-such-nick))
67 ((remove-hook 'erc-server-401-functions 'erc-dcc-no-such-nick)))
68
63(defgroup erc-dcc nil 69(defgroup erc-dcc nil
64 "DCC stands for Direct Client Communication, where you and your 70 "DCC stands for Direct Client Communication, where you and your
65friend's client programs connect directly to each other, 71friend's client programs connect directly to each other,
@@ -70,7 +76,7 @@ Using DCC get and send, you can transfer files directly from and to other
70IRC users." 76IRC users."
71 :group 'erc) 77 :group 'erc)
72 78
73(defcustom erc-verbose-dcc t 79(defcustom erc-dcc-verbose nil
74 "*If non-nil, be verbose about DCC activity reporting." 80 "*If non-nil, be verbose about DCC activity reporting."
75 :group 'erc-dcc 81 :group 'erc-dcc
76 :type 'boolean) 82 :type 'boolean)
@@ -195,20 +201,22 @@ compared with `erc-nick-equal-p' which is IRC case-insensitive."
195 (setq list (cdr list))))) 201 (setq list (cdr list)))))
196 result)) 202 result))
197 203
198;; msa wrote this nifty little frob to convert an n-byte integer to a packed 204(defun erc-pack-int (value)
199;; string. 205 "Convert an integer into a packed string."
200(defun erc-pack-int (value count) 206 (let* ((len (ceiling (/ value 256.0)))
201 (if (> count 0) 207 (str (make-string len ?a))
202 (concat (erc-pack-int (/ value 256) (1- count)) 208 (i (1- len)))
203 (char-to-string (% value 256))) 209 (while (>= i 0)
204 "")) 210 (aset str i (% value 256))
211 (setq value (/ value 256))
212 (setq i (1- i)))
213 str))
205 214
206(defun erc-unpack-int (str) 215(defun erc-unpack-int (str)
207 "Unpack a 1-4 character packed string into an integer." 216 "Unpack a packed string into an integer."
208 (let ((len (length str)) 217 (let ((len (length str))
209 (num 0) 218 (num 0)
210 (count 0)) 219 (count 0))
211 (erc-assert (<= len 4)) ;; this isn't going to fit in elisp bounds
212 (while (< count len) 220 (while (< count len)
213 (setq num (+ num (lsh (aref str (- len count 1)) (* 8 count)))) 221 (setq num (+ num (lsh (aref str (- len count 1)) (* 8 count))))
214 (setq count (1+ count))) 222 (setq count (1+ count)))
@@ -256,15 +264,24 @@ The result is also a string."
256 264
257;;; Server code 265;;; Server code
258 266
259(defcustom erc-dcc-host nil 267(defcustom erc-dcc-listen-host nil
260 "*IP address to use for outgoing DCC offers. 268 "IP address to listen on when offering files.
261Should be set to a string or nil, if nil, automatic detection of the 269Should be set to a string or nil. If nil, automatic detection of
262host interface to use will be attempted." 270the host interface to use will be attempted."
263 :group 'erc-dcc 271 :group 'erc-dcc
264 :type (list 'choice (list 'const :tag "Auto-detect" nil) 272 :type (list 'choice (list 'const :tag "Auto-detect" nil)
265 (list 'string :tag "IP-address" 273 (list 'string :tag "IP-address"
266 :valid-regexp erc-dcc-ipv4-regexp))) 274 :valid-regexp erc-dcc-ipv4-regexp)))
267 275
276(defcustom erc-dcc-public-host nil
277 "IP address to use for outgoing DCC offers.
278Should be set to a string or nil. If nil, use the value of
279`erc-dcc-listen-host'."
280 :group 'erc-dcc
281 :type (list 'choice (list 'const :tag "Same as erc-dcc-listen-host" nil)
282 (list 'string :tag "IP-address"
283 :valid-regexp erc-dcc-ipv4-regexp)))
284
268(defcustom erc-dcc-send-request 'ask 285(defcustom erc-dcc-send-request 'ask
269 "*How to treat incoming DCC Send requests. 286 "*How to treat incoming DCC Send requests.
270'ask - Report the Send request, and wait for the user to manually accept it 287'ask - Report the Send request, and wait for the user to manually accept it
@@ -282,7 +299,7 @@ host interface to use will be attempted."
282 "Determine the IP address we are using. 299 "Determine the IP address we are using.
283If variable `erc-dcc-host' is non-nil, use it. Otherwise call 300If variable `erc-dcc-host' is non-nil, use it. Otherwise call
284`erc-dcc-get-host' on the erc-server-process." 301`erc-dcc-get-host' on the erc-server-process."
285 (or erc-dcc-host (erc-dcc-get-host erc-server-process) 302 (or erc-dcc-listen-host (erc-dcc-get-host erc-server-process)
286 (error "Unable to determine local address"))) 303 (error "Unable to determine local address")))
287 304
288(defcustom erc-dcc-port-range nil 305(defcustom erc-dcc-port-range nil
@@ -311,6 +328,7 @@ created subprocess, or nil."
311 process) 328 process)
312 (while (not process) 329 (while (not process)
313 (condition-case err 330 (condition-case err
331 (progn
314 (setq process 332 (setq process
315 (make-network-process :name name 333 (make-network-process :name name
316 :buffer nil 334 :buffer nil
@@ -322,6 +340,11 @@ created subprocess, or nil."
322 :sentinel sentinel 340 :sentinel sentinel
323 :log #'erc-dcc-server-accept 341 :log #'erc-dcc-server-accept
324 :server t)) 342 :server t))
343 (when (processp process)
344 (when (fboundp 'set-process-coding-system)
345 (set-process-coding-system process 'binary 'binary))
346 (when (fboundp 'set-process-filter-multibyte)
347 (set-process-filter-multibyte process nil))))
325 (file-error 348 (file-error
326 (unless (and (string= "Cannot bind server socket" (cadr err)) 349 (unless (and (string= "Cannot bind server socket" (cadr err))
327 (string= "address already in use" (caddr err))) 350 (string= "address already in use" (caddr err)))
@@ -698,7 +721,7 @@ bytes sent."
698 (confirmed-marker (plist-get elt :sent)) 721 (confirmed-marker (plist-get elt :sent))
699 (sent-marker (plist-get elt :sent))) 722 (sent-marker (plist-get elt :sent)))
700 (with-current-buffer (process-buffer proc) 723 (with-current-buffer (process-buffer proc)
701 (when erc-verbose-dcc 724 (when erc-dcc-verbose
702 (erc-display-message 725 (erc-display-message
703 nil 'notice (erc-dcc-get-parent proc) 726 nil 'notice (erc-dcc-get-parent proc)
704 (format "DCC: Confirmed %d, sent %d, sending block now" 727 (format "DCC: Confirmed %d, sent %d, sending block now"
@@ -713,8 +736,7 @@ bytes sent."
713 (length string))))) 736 (length string)))))
714 737
715(defun erc-dcc-send-filter (proc string) 738(defun erc-dcc-send-filter (proc string)
716 (erc-assert (= (% (length string) 4) 0)) 739 (let* ((size (erc-unpack-int string))
717 (let* ((size (erc-unpack-int (substring string (- (length string) 4))))
718 (elt (erc-dcc-member :peer proc)) 740 (elt (erc-dcc-member :peer proc))
719 (parent (plist-get elt :parent)) 741 (parent (plist-get elt :parent))
720 (sent-marker (plist-get elt :sent)) 742 (sent-marker (plist-get elt :sent))
@@ -742,16 +764,21 @@ bytes sent."
742 ((> confirmed-marker sent-marker) 764 ((> confirmed-marker sent-marker)
743 (erc-display-message 765 (erc-display-message
744 nil 'notice parent 766 nil 'notice parent
745 (format "DCC: Client confirmed too much!")) 767 (format "DCC: Client confirmed too much (%s vs %s)!"
768 (marker-position confirmed-marker)
769 (marker-position sent-marker)))
770 (set-buffer-modified-p nil)
771 (kill-buffer (current-buffer))
746 (delete-process proc)))))) 772 (delete-process proc))))))
747 773
774(defun erc-dcc-display-send (proc)
775 (erc-display-message
776 nil 'notice (erc-dcc-get-parent proc)
777 (format "DCC: SEND connect from %s"
778 (format-network-address (process-contact proc :remote)))))
779
748(defcustom erc-dcc-send-connect-hook 780(defcustom erc-dcc-send-connect-hook
749 '((lambda (proc) 781 '(erc-dcc-display-send erc-dcc-send-block)
750 (erc-display-message
751 nil 'notice (erc-dcc-get-parent proc)
752 (format "DCC: SEND connect from %s"
753 (format-network-address (process-contact proc :remote)))))
754 erc-dcc-send-block)
755 "*Hook run whenever the remote end of a DCC SEND offer connected to your 782 "*Hook run whenever the remote end of a DCC SEND offer connected to your
756listening port." 783listening port."
757 :group 'erc-dcc 784 :group 'erc-dcc
@@ -762,14 +789,14 @@ listening port."
762 (erc-extract-nick (plist-get plist :nick))) 789 (erc-extract-nick (plist-get plist :nick)))
763 790
764(defun erc-dcc-send-sentinel (proc event) 791(defun erc-dcc-send-sentinel (proc event)
765 (let* ((elt (erc-dcc-member :peer proc)) 792 (let* ((elt (erc-dcc-member :peer proc)))
766 (buf (marker-buffer (plist-get elt :sent))))
767 (cond 793 (cond
768 ((string-match "^open from " event) 794 ((string-match "^open from " event)
769 (when elt 795 (when elt
770 (with-current-buffer buf 796 (let ((buf (marker-buffer (plist-get elt :sent))))
771 (set-process-buffer proc buf) 797 (with-current-buffer buf
772 (setq erc-dcc-entry-data elt)) 798 (set-process-buffer proc buf)
799 (setq erc-dcc-entry-data elt)))
773 (run-hook-with-args 'erc-dcc-send-connect-hook proc)))))) 800 (run-hook-with-args 'erc-dcc-send-connect-hook proc))))))
774 801
775(defun erc-dcc-find-file (file) 802(defun erc-dcc-find-file (file)
@@ -807,15 +834,23 @@ other client."
807 (process-send-string 834 (process-send-string
808 pproc (format "PRIVMSG %s :\C-aDCC SEND %s %s %d %d\C-a\n" 835 pproc (format "PRIVMSG %s :\C-aDCC SEND %s %s %d %d\C-a\n"
809 nick (erc-dcc-file-to-name file) 836 nick (erc-dcc-file-to-name file)
810 (erc-ip-to-decimal (nth 0 contact)) 837 (erc-ip-to-decimal (or erc-dcc-public-host
838 (nth 0 contact)))
811 (nth 1 contact) 839 (nth 1 contact)
812 size))) 840 size)))
813 (error "`make-network-process' not supported by your Emacs"))) 841 (error "`make-network-process' not supported by your Emacs")))
814 842
815;;; GET handling 843;;; GET handling
816 844
845(defcustom erc-dcc-receive-cache (* 1024 512)
846 "Number of bytes to let the receive buffer grow before flushing it."
847 :group 'erc-dcc
848 :type 'integer)
849
817(defvar erc-dcc-byte-count nil) 850(defvar erc-dcc-byte-count nil)
818(make-variable-buffer-local 'erc-dcc-byte-count) 851(make-variable-buffer-local 'erc-dcc-byte-count)
852(defvar erc-dcc-file-name nil)
853(make-variable-buffer-local 'erc-dcc-file-name)
819 854
820(defun erc-dcc-get-file (entry file parent-proc) 855(defun erc-dcc-get-file (entry file parent-proc)
821 "This function does the work of setting up a transfer from the remote client 856 "This function does the work of setting up a transfer from the remote client
@@ -825,6 +860,7 @@ filter and a process sentinel, and making the connection."
825 proc) 860 proc)
826 (with-current-buffer buffer 861 (with-current-buffer buffer
827 (fundamental-mode) 862 (fundamental-mode)
863 (buffer-disable-undo (current-buffer))
828 ;; This is necessary to have the buffer saved as-is in GNU 864 ;; This is necessary to have the buffer saved as-is in GNU
829 ;; Emacs. 865 ;; Emacs.
830 ;; XEmacs change: We don't have `set-buffer-multibyte', setting 866 ;; XEmacs change: We don't have `set-buffer-multibyte', setting
@@ -835,7 +871,10 @@ filter and a process sentinel, and making the connection."
835 (setq mode-line-process '(":%s") 871 (setq mode-line-process '(":%s")
836 buffer-file-type t 872 buffer-file-type t
837 buffer-read-only t) 873 buffer-read-only t)
838 (set-visited-file-name file) 874 (setq erc-dcc-file-name file)
875
876 ;; Truncate the given file to size 0 before appending to it.
877 (write-region (point) (point) erc-dcc-file-name nil 'nomessage)
839 878
840 (setq erc-server-process parent-proc 879 (setq erc-server-process parent-proc
841 erc-dcc-entry-data entry) 880 erc-dcc-entry-data entry)
@@ -847,7 +886,6 @@ filter and a process sentinel, and making the connection."
847 (string-to-number (plist-get entry :port)) 886 (string-to-number (plist-get entry :port))
848 entry)) 887 entry))
849 (set-process-buffer proc buffer) 888 (set-process-buffer proc buffer)
850 ;; The following two lines make saving as-is work under Windows
851 (set-process-coding-system proc 'binary 'binary) 889 (set-process-coding-system proc 'binary 'binary)
852 (set-buffer-file-coding-system 'binary t) 890 (set-buffer-file-coding-system 'binary t)
853 891
@@ -856,6 +894,14 @@ filter and a process sentinel, and making the connection."
856 (setq entry (plist-put entry :start-time (erc-current-time))) 894 (setq entry (plist-put entry :start-time (erc-current-time)))
857 (setq entry (plist-put entry :peer proc))))) 895 (setq entry (plist-put entry :peer proc)))))
858 896
897(defun erc-dcc-append-contents (buffer file)
898 "Append the contents of BUFFER to FILE.
899The contents of the BUFFER will then be erased."
900 (with-current-buffer buffer
901 (let ((coding-system-for-write 'binary))
902 (write-region (point-min) (point-max) erc-dcc-file-name t 'nomessage)
903 (erase-buffer))))
904
859(defun erc-dcc-get-filter (proc str) 905(defun erc-dcc-get-filter (proc str)
860 "This is the process filter for transfers from other clients to this one. 906 "This is the process filter for transfers from other clients to this one.
861It reads incoming bytes from the network and stores them in the DCC 907It reads incoming bytes from the network and stores them in the DCC
@@ -868,8 +914,10 @@ rather than every 1024 byte block, but nobody seems to care."
868 (insert (string-make-unibyte str)) 914 (insert (string-make-unibyte str))
869 915
870 (setq erc-dcc-byte-count (+ (length str) erc-dcc-byte-count)) 916 (setq erc-dcc-byte-count (+ (length str) erc-dcc-byte-count))
871 (erc-assert (= erc-dcc-byte-count (1- (point-max)))) 917 (when (> (point-max) erc-dcc-receive-cache)
872 (and erc-verbose-dcc 918 (erc-dcc-append-contents (current-buffer) erc-dcc-file-name))
919
920 (and erc-dcc-verbose
873 (erc-display-message 921 (erc-display-message
874 nil 'notice erc-server-process 922 nil 'notice erc-server-process
875 'dcc-get-bytes-received 923 'dcc-get-bytes-received
@@ -885,7 +933,7 @@ rather than every 1024 byte block, but nobody seems to care."
885 (delete-process proc)) 933 (delete-process proc))
886 (t 934 (t
887 (process-send-string 935 (process-send-string
888 proc (erc-pack-int erc-dcc-byte-count 4))))))) 936 proc (erc-pack-int erc-dcc-byte-count)))))))
889 937
890 938
891(defun erc-dcc-get-sentinel (proc event) 939(defun erc-dcc-get-sentinel (proc event)
@@ -895,17 +943,18 @@ transfer is complete."
895 ;; FIXME, we should look at EVENT, and also check size. 943 ;; FIXME, we should look at EVENT, and also check size.
896 (with-current-buffer (process-buffer proc) 944 (with-current-buffer (process-buffer proc)
897 (delete-process proc) 945 (delete-process proc)
898 (setq buffer-read-only nil)
899 (setq erc-dcc-list (delete erc-dcc-entry-data erc-dcc-list)) 946 (setq erc-dcc-list (delete erc-dcc-entry-data erc-dcc-list))
947 (unless (= (point-min) (point-max))
948 (setq erc-dcc-byte-count (+ (buffer-size) erc-dcc-byte-count))
949 (erc-dcc-append-contents (current-buffer) erc-dcc-file-name))
900 (erc-display-message 950 (erc-display-message
901 nil 'notice erc-server-process 951 nil 'notice erc-server-process
902 'dcc-get-complete 952 'dcc-get-complete
903 ?f (file-name-nondirectory buffer-file-name) 953 ?f erc-dcc-file-name
904 ?s (number-to-string (buffer-size)) 954 ?s (number-to-string erc-dcc-byte-count)
905 ?t (format "%.0f" 955 ?t (format "%.0f"
906 (erc-time-diff (plist-get erc-dcc-entry-data :start-time) 956 (erc-time-diff (plist-get erc-dcc-entry-data :start-time)
907 (erc-current-time)))) 957 (erc-current-time)))))
908 (save-buffer))
909 (kill-buffer (process-buffer proc)) 958 (kill-buffer (process-buffer proc))
910 (delete-process proc)) 959 (delete-process proc))
911 960
@@ -1126,8 +1175,6 @@ other client."
1126 (if (processp peer) (delete-process peer))) 1175 (if (processp peer) (delete-process peer)))
1127 nil)) 1176 nil))
1128 1177
1129(add-hook 'erc-server-401-functions 'erc-dcc-no-such-nick)
1130
1131(provide 'erc-dcc) 1178(provide 'erc-dcc)
1132 1179
1133;;; erc-dcc.el ends here 1180;;; erc-dcc.el ends here