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))
37 (eval-when-compile (require 'queue-m))
39 (require 'liece-coding)
41 (require 'liece-minibuf)
43 (defvar liece-dcc-requests (queue-create))
44 (defvar liece-dcc-receive-direct t)
45 (defvar liece-dcc-process-alist nil)
47 (defconst liece-dcc-acceptable-messages '("SEND" "CHAT"))
49 (defstruct liece-dcc-object type from host port file size)
51 (defun liece-dcc-enqueue-request (type &rest args)
52 (let ((request (apply #'make-liece-dcc-object :type type args)))
53 (inline (queue-enqueue liece-dcc-requests request))))
55 (defun liece-dcc-dequeue-request (&optional type)
57 (eq (liece-dcc-object-type
58 (queue-first liece-dcc-requests))
60 (inline (queue-dequeue liece-dcc-requests))))
62 (defmacro liece-dcc-add-to-process-alist (process type &rest args)
63 `(push (cons (process-name ,process)
64 (make-liece-dcc-object :type ,type ,@args))
65 liece-dcc-process-alist))
67 (defmacro liece-dcc-get-process-object (process)
68 `(cdr (assoc (process-name ,process) liece-dcc-process-alist)))
70 (defmacro liece-dcc-message (&rest msg)
71 `(message "DCC %s" (format ,@msg)))
73 (defun* liece-ctcp-dcc-message (from chnl rest)
75 ((string-match "^SEND +" rest)
76 (multiple-value-bind (filename host port size)
77 (split-string (substring rest (match-end 0)))
78 (setq filename (file-name-nondirectory filename))
80 (append liece-O-buffer liece-D-buffer)
81 (format (_ "SEND request from %s: %s (%s bytes)\n")
83 (liece-dcc-enqueue-request
84 'send :from from :host host :port port :file filename :size size)
85 (when liece-dcc-receive-direct
87 (append liece-O-buffer liece-D-buffer)
88 (format (_ "SEND applied autoreceive: %s (%s bytes)\n")
90 (liece-command-dcc-receive))))
91 ((string-match "^CHAT [^ ]+ +" rest)
92 (multiple-value-bind (host port)
93 (split-string (substring rest (match-end 0)))
94 (liece-dcc-enqueue-request 'chat :from from :host host :port port)
96 (append liece-O-buffer liece-D-buffer)
97 (concat "CHAT request from " from "\n"))))))
99 (defun liece-command-dcc-send (filename towhom)
102 (list (expand-file-name
105 default-directory nil))
106 (liece-minibuffer-completing-default-read
108 (append liece-nick-alist liece-channel-alist)
109 nil nil liece-privmsg-partner)))
111 (setq liece-privmsg-partner towhom)
113 (setq process (start-process
114 liece-dcc-program nil liece-dcc-program
115 "send" (int-to-string liece-dcc-port) filename))
116 (set-process-filter process #'liece-dcc-send-filter)
117 (set-process-sentinel process #'liece-dcc-sentinel))
118 (or (zerop liece-dcc-port)
119 (incf liece-dcc-port)))
121 (defun liece-dcc-sentinel (process output)
122 (let* ((object (liece-dcc-get-process-object process))
123 (type (liece-dcc-object-type object)))
125 (delete-process process)
126 (if (string-match "^finished" output)
129 (liece-dcc-message (_ "Sent file to %s: %s (%s bytes)")
130 (liece-dcc-object-from object)
131 (liece-dcc-object-file object)
132 (liece-dcc-object-size object)))
134 (liece-dcc-message (_ "Received file from %s: %s (%s bytes)")
135 (liece-dcc-object-from object)
136 (liece-dcc-object-file object)
137 (liece-dcc-object-size object)))
139 (liece-dcc-message (_ "Chat connection with %s finished")
140 (liece-dcc-object-from object))))
142 (_ "%s error (%s %s %s) is %s\n")
143 (capitalize (downcase (prin1-to-string
144 (liece-dcc-object-type object))))
145 (or (liece-dcc-object-file object) "")
146 (cond ((eq type 'send) "to")
147 ((eq type 'receive) "from")
148 ((eq type 'chat) "with"))
149 (liece-dcc-object-from object)
150 (substring output 0 (1- (length output))))))))
152 (defun liece-dcc-send-filter (process output)
153 (if (string-match "DCC send +" output)
154 (multiple-value-bind (filename port host size)
155 (split-string (substring output (match-end 0)))
156 (setq filename (file-name-nondirectory filename))
157 (liece-send "PRIVMSG %s :\001DCC SEND %s %s %s %s\001"
158 liece-privmsg-partner filename host port size)
159 (liece-dcc-message (_ "Sending file to %s: %s (%s bytes)")
160 liece-privmsg-partner filename size)
161 (liece-dcc-add-to-process-alist process 'send
164 :from liece-privmsg-partner
167 (liece-dcc-message (_ "send error to %s: %s")
168 liece-privmsg-partner
169 (substring output 0 (1- (length output))))))
171 (defmacro liece-dcc-prepare-directory ()
172 '(or (file-directory-p (expand-file-name liece-dcc-directory))
173 (and (y-or-n-p (_ "DCC directory does not exist. Create it? "))
174 (make-directory (expand-file-name liece-dcc-directory)))))
176 (defun liece-command-dcc-receive (&optional number)
177 "Receive next file from list."
179 (let ((object (liece-dcc-dequeue-request 'send)))
181 (liece-message (_ "DCC No send request has been arrived."))
182 (liece-dcc-message (_ "Getting file from %s: %s (%s bytes)")
183 (liece-dcc-object-from object)
184 (liece-dcc-object-file object)
185 (liece-dcc-object-size object))
186 (liece-dcc-prepare-directory)
189 (liece-dcc-object-file object)
190 liece-dcc-directory))
194 liece-dcc-program nil liece-dcc-program
196 (liece-dcc-object-host object)
197 (liece-dcc-object-port object)
198 (liece-dcc-object-size object)
200 (liece-dcc-object-file object)
201 liece-dcc-directory)))
202 (set-process-filter process #'liece-dcc-receive-filter)
203 (set-process-sentinel process #'liece-dcc-sentinel)
204 (liece-dcc-add-to-process-alist
206 :from (liece-dcc-object-from object)
207 :host (liece-dcc-object-host object)
208 :port (liece-dcc-object-port object)
210 :size (liece-dcc-object-size object))))))
212 (defun liece-dcc-receive-filter (process output)
213 (liece-dcc-message "%s" (substring output 0 (1- (length output)))))
215 (defun liece-command-dcc-chat-listen (towhom)
217 (list (liece-minibuffer-completing-default-read
219 (append liece-nick-alist liece-channel-alist)
220 nil nil liece-privmsg-partner)))
221 (setq liece-privmsg-partner towhom)
226 liece-dcc-program nil
227 liece-dcc-program "chat" "listen"
228 (int-to-string liece-dcc-port)))
231 (liece-get-buffer-create (format " DCC:%s" (process-id process))))
232 (set-process-filter process 'liece-dcc-chat-listen-filter)
233 (set-process-sentinel process 'liece-dcc-sentinel)))
234 (unless (zerop liece-dcc-port)
235 (setq liece-dcc-port (1+ liece-dcc-port))))
237 (defun liece-dcc-chat-listen-filter (process output)
239 ((string-match "DCC chat +" output)
240 (multiple-value-bind (host port)
241 (split-string (substring output (match-end 0)))
242 (liece-send "PRIVMSG %s :\001DCC CHAT chat %s %s\001"
243 liece-privmsg-partner host port)
244 (liece-dcc-message (_ "Ringing user %s")
245 liece-privmsg-partner)
246 (liece-dcc-add-to-process-alist
247 process 'chat :from liece-privmsg-partner)))
248 ((string-match "^DCC chat established" output)
249 (set-process-filter process 'liece-dcc-chat-filter)
250 (let* ((object (liece-dcc-get-process-object process))
251 (nick (liece-dcc-object-from object)))
252 (setq nick (liece-channel-prepare-representation nick 'dcc))
253 (liece-channel-prepare-partner nick)
254 (liece-dcc-message (_ "Chat connection established with: %s")
258 (liece-dcc-message (_ "listen error to %s: %s")
259 liece-privmsg-partner
260 (substring output 0 (1- (length output)))))))
262 (defun liece-command-dcc-chat-connect (&optional number)
264 (let* ((object (liece-dcc-dequeue-request 'chat))
265 (nick (liece-dcc-object-from object))
268 (liece-message (_ "DCC No chat request has been arrived."))
269 (liece-dcc-message (_ "Connecting to: %s") nick)
270 (setq liece-privmsg-partner nick)
273 (start-process liece-dcc-program nil
274 liece-dcc-program "chat" "connect"
275 (liece-dcc-object-host object)
276 (liece-dcc-object-port object)))
279 (liece-get-buffer-create
280 (format " DCC:%s" (process-id process))))
281 (set-process-filter process #'liece-dcc-chat-connect-filter)
282 (set-process-sentinel process #'liece-dcc-sentinel)
283 (liece-dcc-add-to-process-alist
284 process 'chat :from liece-privmsg-partner)))))
286 (defun liece-dcc-chat-connect-filter (process output)
287 (if (string-match "^DCC chat established" output)
288 (let* ((object (liece-dcc-get-process-object process))
289 (nick (liece-dcc-object-from object)))
290 (set-process-filter process #'liece-dcc-chat-filter)
291 (setq nick (liece-channel-prepare-representation nick 'dcc))
292 (liece-channel-prepare-partner nick)
293 (liece-dcc-message (_ "Chat connection established with: %s")
297 (_ "connect error to %s: %s")
298 liece-privmsg-partner
299 (substring output 0 (1- (length output))))))
301 (defun liece-dcc-chat-filter (process output)
303 (with-current-buffer (process-buffer process)
304 (let* ((object (liece-dcc-get-process-object process))
305 (nick (liece-channel-prepare-representation
306 (liece-dcc-object-from object) 'dcc)))
307 (goto-char (point-max))
309 (goto-char (point-min))
310 (while (search-forward "\n\n" (point-max) t)
312 (goto-char (point-min))
313 (when (string-match "\n" output)
315 (while (looking-at ".*\n")
316 (setq st (match-beginning 0) nd (match-end 0)
317 line (liece-coding-decode-charset-string
318 (buffer-substring st (1- nd))))
319 (delete-region st nd)
320 (let ((liece-message-target (liece-current-nickname))
321 (liece-message-speaker nick))
322 (liece-display-message line)))))))))
324 (defun liece-dcc-chat-nick-to-process (nick)
325 "Convert NICK to process symbol."
326 (let ((alist liece-dcc-process-alist)
330 (setq pair (pop alist))
331 (if (and (eq 'chat (cadr pair))
332 (liece-nick-equal nick (caddr pair)))
333 (throw 'found (car pair))))
336 (defun liece-dcc-chat-send (nick message)
337 "Send MSG string to NICK via DCC chat."
338 (let ((process (liece-dcc-chat-nick-to-process nick)))
340 (liece-message (_ "DCC chat has not been started."))
341 (with-current-buffer liece-command-buffer
342 (setq message (liece-coding-encode-charset-string message)
343 message (if (string-match "\r$" message) message
344 (concat message "\r\n")))
345 (process-send-string process message)))))
347 (defun liece-command-dcc-accept ()
348 "Dispatch one DCC request."
350 (let* ((object (queue-first liece-dcc-requests))
351 (type (liece-dcc-object-type object)))
352 (cond ((eq type 'send)
353 (liece-command-dcc-receive))
355 (liece-command-dcc-chat-connect))
358 (_ "DCC No request has been arrived."))))))
360 (defun liece-command-dcc-list ()
361 "List files in receive queue."
363 (if (queue-empty liece-dcc-requests)
364 (liece-dcc-message (_ "No DCC request here"))
365 (let ((i 0) (objects (queue-all liece-dcc-requests)) type)
366 (dolist (object objects)
367 (setq type (liece-dcc-object-type object))
368 (cond ((eq type 'send)
370 (_ "(%d) %s request %s: %s (%s bytes)")
371 i (upcase (symbol-name type))
372 (liece-dcc-object-from object)
373 (liece-dcc-object-file object)
374 (liece-dcc-object-size object)))
377 (_ "(%d) %s request from %s")
378 i (upcase (symbol-name type))
379 (liece-dcc-object-from object))))
382 (defun liece-dcc-compare-hostnames (h1 h2)
383 "Compare two internet domain hostnames. Return true iff they resolve to the
386 (string-equal-ignore-case h1 h2)
387 (if liece-dcc-program
388 (let ((pob (liece-get-buffer-create "*IRC DCC resolve*"))
389 (output) (domatch nil))
391 (call-process liece-dcc-program nil pob nil "resolve" h1 h2)
393 (goto-char (point-min))
394 (setq output (buffer-substring (point-min) (point-max)))
395 (if (string-match "\\([^ ]+\\)\n\\([^ ]+\\)\n" output)
396 (if (string= (match-string 1 output)
397 (match-string 2 output))
401 (string-equal-ignore-case h1 h2))))
405 ;;; liece-dcc.el ends here