diff options
Diffstat (limited to 'lisp/erc/erc-dcc.el')
| -rw-r--r-- | lisp/erc/erc-dcc.el | 133 |
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 |
| 65 | friend's client programs connect directly to each other, | 71 | friend'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 | |||
| 70 | IRC users." | 76 | IRC 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. |
| 261 | Should be set to a string or nil, if nil, automatic detection of the | 269 | Should be set to a string or nil. If nil, automatic detection of |
| 262 | host interface to use will be attempted." | 270 | the 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. | ||
| 278 | Should 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. |
| 283 | If variable `erc-dcc-host' is non-nil, use it. Otherwise call | 300 | If 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 |
| 756 | listening port." | 783 | listening 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. | ||
| 899 | The 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. |
| 861 | It reads incoming bytes from the network and stores them in the DCC | 907 | It 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 |