1 ;;; liece-dcc.el --- DCC handlers and commands.
2 ;; Copyright (C) 1998-2000 Daiki Ueno
4 ;; Author: Daiki Ueno <ueno@unixuser.org>
7 ;; Keywords: IRC, liece, DCC
9 ;; This file is part of Liece.
11 ;; This program is free software; you can redistribute it and/or modify
12 ;; it under the terms of the GNU General Public License as published by
13 ;; the Free Software Foundation; either version 2, or (at your option)
16 ;; This program is distributed in the hope that it will be useful,
17 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
18 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 ;; GNU General Public License for more details.
21 ;; You should have received a copy of the GNU General Public License
22 ;; along with GNU Emacs; see the file COPYING. If not, write to the
23 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
24 ;; Boston, MA 02111-1307, USA.
34 (require 'liece-inlines)
35 (require 'liece-channel)
36 (require 'liece-misc))
38 (eval-when-compile (require 'queue-m))
40 (require 'liece-coding)
42 (require 'liece-minibuf)
44 (defvar liece-dcc-requests (queue-create))
45 (defvar liece-dcc-receive-direct t)
46 (defvar liece-dcc-process-alist nil)
48 (defconst liece-dcc-acceptable-messages '("SEND" "CHAT"))
50 (defstruct liece-dcc-object type from host port file size)
52 (defun liece-dcc-start-process (args)
54 (or (car-safe liece-dcc-program)
56 (apply #'start-process " *DCC*" nil program args)))
58 (defun liece-dcc-enqueue-request (type &rest args)
59 (let ((request (apply #'make-liece-dcc-object :type type args)))
60 (inline (queue-enqueue liece-dcc-requests request))))
62 (defun liece-dcc-dequeue-request (&optional type)
64 (eq (liece-dcc-object-type
65 (queue-first liece-dcc-requests))
67 (inline (queue-dequeue liece-dcc-requests))))
69 (defmacro liece-dcc-add-to-process-alist (process type &rest args)
70 `(push (cons (process-name ,process)
71 (make-liece-dcc-object :type ,type ,@args))
72 liece-dcc-process-alist))
74 (defmacro liece-dcc-get-process-object (process)
75 `(cdr (assoc (process-name ,process) liece-dcc-process-alist)))
77 (defmacro liece-dcc-message (&rest msg)
78 `(message "DCC %s" (format ,@msg)))
80 (defun* liece-ctcp-dcc-message (from chnl rest)
82 ((string-match "^SEND +" rest)
83 (multiple-value-bind (filename host port size)
84 (split-string (substring rest (match-end 0)))
85 (setq filename (file-name-nondirectory filename))
87 (append liece-O-buffer liece-D-buffer)
88 (format (_ "SEND request from %s: %s (%s bytes)\n")
90 (liece-dcc-enqueue-request
91 'send :from from :host host :port port :file filename :size size)
92 (when liece-dcc-receive-direct
94 (append liece-O-buffer liece-D-buffer)
95 (format (_ "SEND applied autoreceive: %s (%s bytes)\n")
97 (liece-command-dcc-receive))))
98 ((string-match "^CHAT [^ ]+ +" rest)
99 (multiple-value-bind (host port)
100 (split-string (substring rest (match-end 0)))
101 (liece-dcc-enqueue-request 'chat :from from :host host :port port)
103 (append liece-O-buffer liece-D-buffer)
104 (concat "CHAT request from " from "\n"))))))
106 (defun liece-command-dcc-send (filename towhom)
109 (list (expand-file-name
112 default-directory nil))
113 (liece-minibuffer-completing-read
115 (append liece-nick-alist liece-channel-alist)
116 nil nil nil nil liece-privmsg-partner)))
118 (setq liece-privmsg-partner towhom)
120 (liece-dcc-start-process
121 (list "send" (int-to-string liece-dcc-port) filename))))
122 (set-process-filter process #'liece-dcc-send-filter)
123 (set-process-sentinel process #'liece-dcc-sentinel))
124 (or (zerop liece-dcc-port)
125 (incf liece-dcc-port)))
127 (defun liece-dcc-sentinel (process output)
128 (let* ((object (liece-dcc-get-process-object process))
129 (type (liece-dcc-object-type object)))
131 (delete-process process)
132 (if (string-match "^finished" output)
135 (liece-dcc-message (_ "Sent file to %s: %s (%s bytes)")
136 (liece-dcc-object-from object)
137 (liece-dcc-object-file object)
138 (liece-dcc-object-size object)))
140 (liece-dcc-message (_ "Received file from %s: %s (%s bytes)")
141 (liece-dcc-object-from object)
142 (liece-dcc-object-file object)
143 (liece-dcc-object-size object)))
145 (liece-dcc-message (_ "Chat connection with %s finished")
146 (liece-dcc-object-from object))))
148 (_ "%s error (%s %s %s) is %s\n")
149 (capitalize (downcase (prin1-to-string
150 (liece-dcc-object-type object))))
151 (or (liece-dcc-object-file object) "")
152 (cond ((eq type 'send) "to")
153 ((eq type 'receive) "from")
154 ((eq type 'chat) "with"))
155 (liece-dcc-object-from object)
156 (substring output 0 (1- (length output))))))))
158 (defun liece-dcc-send-filter (process output)
159 (if (string-match "DCC send +" output)
160 (multiple-value-bind (filename port host size)
161 (split-string (substring output (match-end 0)))
162 (setq filename (file-name-nondirectory filename))
163 (liece-send "PRIVMSG %s :\001DCC SEND %s %s %s %s\001"
164 liece-privmsg-partner filename host port size)
165 (liece-dcc-message (_ "Sending file to %s: %s (%s bytes)")
166 liece-privmsg-partner filename size)
167 (liece-dcc-add-to-process-alist process 'send
170 :from liece-privmsg-partner
173 (liece-dcc-message (_ "send error to %s: %s")
174 liece-privmsg-partner
175 (substring output 0 (1- (length output))))))
177 (defmacro liece-dcc-prepare-directory ()
178 '(or (file-directory-p (expand-file-name liece-dcc-directory))
179 (and (y-or-n-p (_ "DCC directory does not exist. Create it? "))
180 (make-directory (expand-file-name liece-dcc-directory)))))
182 (defun liece-command-dcc-receive (&optional number)
183 "Receive next file from list."
185 (let ((object (liece-dcc-dequeue-request 'send)))
187 (liece-message (_ "DCC No send request has been arrived."))
188 (liece-dcc-message (_ "Getting file from %s: %s (%s bytes)")
189 (liece-dcc-object-from object)
190 (liece-dcc-object-file object)
191 (liece-dcc-object-size object))
192 (liece-dcc-prepare-directory)
195 (liece-dcc-object-file object)
196 liece-dcc-directory))
198 (liece-dcc-start-process
200 (liece-dcc-object-host object)
201 (liece-dcc-object-port object)
202 (liece-dcc-object-size object)
204 (liece-dcc-object-file object)
205 liece-dcc-directory)))))
206 (set-process-filter process #'liece-dcc-receive-filter)
207 (set-process-sentinel process #'liece-dcc-sentinel)
208 (liece-dcc-add-to-process-alist
210 :from (liece-dcc-object-from object)
211 :host (liece-dcc-object-host object)
212 :port (liece-dcc-object-port object)
214 :size (liece-dcc-object-size object))))))
216 (defun liece-dcc-receive-filter (process output)
217 (liece-dcc-message "%s" (substring output 0 (1- (length output)))))
219 (defun liece-command-dcc-chat-listen (towhom)
221 (list (liece-minibuffer-completing-read
223 (append liece-nick-alist liece-channel-alist)
224 nil nil nil nil liece-privmsg-partner)))
225 (setq liece-privmsg-partner towhom)
228 (liece-dcc-start-process
229 (list "chat" "listen" (int-to-string liece-dcc-port))))))
232 (liece-get-buffer-create (format " DCC:%s" (process-id process))))
233 (set-process-filter process 'liece-dcc-chat-listen-filter)
234 (set-process-sentinel process 'liece-dcc-sentinel))
235 (unless (zerop liece-dcc-port)
236 (setq liece-dcc-port (1+ liece-dcc-port))))
238 (defun liece-dcc-chat-listen-filter (process output)
240 ((string-match "DCC chat +" output)
241 (multiple-value-bind (host port)
242 (split-string (substring output (match-end 0)))
243 (liece-send "PRIVMSG %s :\001DCC CHAT chat %s %s\001"
244 liece-privmsg-partner host port)
245 (liece-dcc-message (_ "Ringing user %s")
246 liece-privmsg-partner)
247 (liece-dcc-add-to-process-alist
248 process 'chat :from liece-privmsg-partner)))
249 ((string-match "^DCC chat established" output)
250 (set-process-filter process 'liece-dcc-chat-filter)
251 (let* ((object (liece-dcc-get-process-object process))
252 (nick (liece-dcc-object-from object)))
253 (setq nick (liece-channel-prepare-representation nick 'dcc))
254 (liece-channel-prepare-partner nick)
255 (liece-dcc-message (_ "Chat connection established with: %s")
259 (liece-dcc-message (_ "listen error to %s: %s")
260 liece-privmsg-partner
261 (substring output 0 (1- (length output)))))))
263 (defun liece-command-dcc-chat-connect (&optional number)
265 (let* ((object (liece-dcc-dequeue-request 'chat))
266 (nick (liece-dcc-object-from object))
269 (liece-message (_ "DCC No chat request has been arrived."))
270 (liece-dcc-message (_ "Connecting to: %s") nick)
271 (setq liece-privmsg-partner nick)
274 (liece-dcc-start-process
275 (list "chat" "connect"
276 (liece-dcc-object-host object)
277 (liece-dcc-object-port object)))))
280 (liece-get-buffer-create
281 (format " DCC:%s" (process-id process))))
282 (set-process-filter process #'liece-dcc-chat-connect-filter)
283 (set-process-sentinel process #'liece-dcc-sentinel)
284 (liece-dcc-add-to-process-alist
285 process 'chat :from liece-privmsg-partner))))
287 (defun liece-dcc-chat-connect-filter (process output)
288 (if (string-match "^DCC chat established" output)
289 (let* ((object (liece-dcc-get-process-object process))
290 (nick (liece-dcc-object-from object)))
291 (set-process-filter process #'liece-dcc-chat-filter)
292 (setq nick (liece-channel-prepare-representation nick 'dcc))
293 (liece-channel-prepare-partner nick)
294 (liece-dcc-message (_ "Chat connection established with: %s")
298 (_ "connect error to %s: %s")
299 liece-privmsg-partner
300 (substring output 0 (1- (length output))))))
302 (defun liece-dcc-chat-filter (process output)
304 (with-current-buffer (process-buffer process)
305 (let* ((object (liece-dcc-get-process-object process))
306 (nick (liece-channel-prepare-representation
307 (liece-dcc-object-from object) 'dcc)))
308 (goto-char (point-max))
310 (goto-char (point-min))
311 (while (search-forward "\n\n" (point-max) t)
313 (goto-char (point-min))
314 (when (string-match "\n" output)
316 (while (looking-at ".*\n")
317 (setq st (match-beginning 0) nd (match-end 0)
318 line (liece-coding-decode-charset-string
319 (buffer-substring st (1- nd))))
320 (delete-region st nd)
321 (let ((liece-message-target (liece-current-nickname))
322 (liece-message-speaker nick))
323 (liece-display-message line)))))))))
325 (defun liece-dcc-chat-nick-to-process (nick)
326 "Convert NICK to process symbol."
327 (let ((alist liece-dcc-process-alist)
331 (setq pair (pop alist))
332 (if (and (eq 'chat (cadr pair))
333 (liece-nick-equal nick (caddr pair)))
334 (throw 'found (car pair))))
337 (defun liece-dcc-chat-send (nick message)
338 "Send MSG string to NICK via DCC chat."
339 (let ((process (liece-dcc-chat-nick-to-process nick)))
341 (liece-message (_ "DCC chat has not been started."))
342 (with-current-buffer liece-command-buffer
343 (setq message (liece-coding-encode-charset-string message)
344 message (if (string-match "\r$" message) message
345 (concat message "\r\n")))
346 (process-send-string process message)))))
348 (defun liece-command-dcc-accept ()
349 "Dispatch one DCC request."
351 (let* ((object (queue-first liece-dcc-requests))
352 (type (liece-dcc-object-type object)))
353 (cond ((eq type 'send)
354 (liece-command-dcc-receive))
356 (liece-command-dcc-chat-connect))
359 (_ "DCC No request has been arrived."))))))
361 (defun liece-command-dcc-list ()
362 "List files in receive queue."
364 (if (queue-empty liece-dcc-requests)
365 (liece-dcc-message (_ "No DCC request here"))
366 (let ((i 0) (objects (queue-all liece-dcc-requests)) type)
367 (dolist (object objects)
368 (setq type (liece-dcc-object-type object))
369 (cond ((eq type 'send)
371 (_ "(%d) %s request %s: %s (%s bytes)")
372 i (upcase (symbol-name type))
373 (liece-dcc-object-from object)
374 (liece-dcc-object-file object)
375 (liece-dcc-object-size object)))
378 (_ "(%d) %s request from %s")
379 i (upcase (symbol-name type))
380 (liece-dcc-object-from object))))
383 (defun liece-dcc-compare-hostnames (h1 h2)
384 "Compare two internet domain hostnames. Return true iff they resolve to the
387 (string-equal-ignore-case h1 h2)
388 (if liece-dcc-program
389 (let ((pob (liece-get-buffer-create "*IRC DCC resolve*"))
390 (output) (domatch nil))
392 (call-process liece-dcc-program nil pob nil "resolve" h1 h2)
394 (goto-char (point-min))
395 (setq output (buffer-substring (point-min) (point-max)))
396 (if (string-match "\\([^ ]+\\)\n\\([^ ]+\\)\n" output)
397 (if (string= (match-string 1 output)
398 (match-string 2 output))
402 (string-equal-ignore-case h1 h2))))
406 ;;; liece-dcc.el ends here