Fix ERC bug introduced in last patch
[bpt/emacs.git] / lisp / erc / erc-backend.el
CommitLineData
597993cf
MB
1;;; erc-backend.el --- Backend network communication for ERC
2
3;; Copyright (C) 2004, 2005, 2006 Free Software Foundation, Inc.
4
5;; Filename: erc-backend.el
6;; Author: Lawrence Mitchell <wence@gmx.li>
7;; Created: 2004-05-7
8;; Keywords: IRC chat client internet
9
10;; This file is part of GNU Emacs.
11
12;; GNU Emacs is free software; you can redistribute it and/or modify
13;; it under the terms of the GNU General Public License as published by
14;; the Free Software Foundation; either version 2, or (at your option)
15;; any later version.
16
17;; GNU Emacs is distributed in the hope that it will be useful,
18;; but WITHOUT ANY WARRANTY; without even the implied warranty of
19;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20;; GNU General Public License for more details.
21
22;; You should have received a copy of the GNU General Public License
23;; along with GNU Emacs; see the file COPYING. If not, write to the
24;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
25;; Boston, MA 02110-1301, USA.
26
27;;; Commentary:
28
29;; This file defines backend network communication handlers for ERC.
30;;
31;; How things work:
32;;
33;; You define a new handler with `define-erc-response-handler'. This
34;; defines a function, a corresponding hook variable, and populates a
35;; global hash table `erc-server-responses' with a map from response
36;; to hook variable. See the function documentation for more
37;; information.
38;;
39;; Upon receiving a line from the server, `erc-parse-server-response'
40;; is called on it.
41;;
42;; A line generally looks like:
43;;
44;; LINE := ':' SENDER ' ' COMMAND ' ' (COMMAND-ARGS ' ')* ':' CONTENTS
45;; SENDER := Not ':' | ' '
46;; COMMAND := Not ':' | ' '
47;; COMMAND-ARGS := Not ':' | ' '
48;;
49;; This gets parsed and stuffed into an `erc-response' struct. You
50;; can access the fields of the struct with:
51;;
52;; COMMAND --- `erc-response.command'
53;; COMMAND-ARGS --- `erc-response.command-args'
54;; CONTENTS --- `erc-response.contents'
55;; SENDER --- `erc-response.sender'
56;; LINE --- `erc-response.unparsed'
57;;
58;; WARNING, WARNING!!
59;; It's probably not a good idea to destructively modify the list
60;; of command-args in your handlers, since other functions down the
61;; line may well need to access the arguments too.
62;;
63;; That is, unless you're /absolutely/ sure that your handler doesn't
64;; invoke some other function that needs to use COMMAND-ARGS, don't do
65;; something like
66;;
67;; (while (erc-response.command-args parsed)
68;; (let ((a (pop (erc-response.command-args parsed))))
69;; ...))
70;;
71;; The parsed response is handed over to
72;; `erc-handle-parsed-server-response', which checks whether it should
73;; carry out duplicate suppression, and then runs `erc-call-hooks'.
74;; `erc-call-hooks' retrieves the relevant hook variable from
75;; `erc-server-responses' and runs it.
76;;
77;; Most handlers then destructure the parsed response in some way
78;; (depending on what the handler is, the arguments have different
79;; meanings), and generally display something, usually using
80;; `erc-display-message'.
81
82;;; TODO:
83
84;; o Generalise the display-line code so that we can use it to
85;; display the stuff we send, as well as the stuff we receive.
86;; Then, move all display-related code into another backend-like
87;; file, erc-display.el, say.
88;;
89;; o Clean up the handlers using new display code (has to be written
90;; first).
91
92;;; History:
93
94;; 2004/05/10 -- Handler bodies taken out of erc.el and ported to new
95;; interface.
96
97;; 2005-08-13 -- Moved sending commands from erc.el.
98
99;;; Code:
100
101(require 'erc-compat)
102(eval-when-compile (require 'cl))
103(autoload 'erc-with-buffer "erc" nil nil 'macro)
104(autoload 'erc-log "erc" nil nil 'macro)
105
106;;;; Variables and options
107
108(defvar erc-server-responses (make-hash-table :test #'equal)
109 "Hashtable mapping server responses to their handler hooks.")
110
111(defstruct (erc-response (:conc-name erc-response.))
112 (unparsed "" :type string)
113 (sender "" :type string)
114 (command "" :type string)
115 (command-args '() :type list)
116 (contents "" :type string))
117
118;;; User data
119
120(defvar erc-server-current-nick nil
121 "Nickname on the current server.
122Use `erc-current-nick' to access this.")
123(make-variable-buffer-local 'erc-server-current-nick)
124
125;;; Server attributes
126
127(defvar erc-server-process nil
128 "The process object of the corresponding server connection.")
129(make-variable-buffer-local 'erc-server-process)
130
131(defvar erc-session-server nil
132 "The server name used to connect to for this session.")
133(make-variable-buffer-local 'erc-session-server)
134
135(defvar erc-session-port nil
136 "The port used to connect to.")
137(make-variable-buffer-local 'erc-session-port)
138
139(defvar erc-server-announced-name nil
140 "The name the server announced to use.")
141(make-variable-buffer-local 'erc-server-announced-name)
142
143(defvar erc-server-version nil
144 "The name and version of the server's ircd.")
145(make-variable-buffer-local 'erc-server-version)
146
147(defvar erc-server-parameters nil
148 "Alist listing the supported server parameters.
149
150This is only set if the server sends 005 messages saying what is
151supported on the server.
152
153Entries are of the form:
154 (PARAMETER . VALUE)
155or
156 (PARAMETER) if no value is provided.
157
158Some examples of possible parameters sent by servers:
159CHANMODES=b,k,l,imnpst - list of supported channel modes
160CHANNELLEN=50 - maximum length of channel names
161CHANTYPES=#&!+ - supported channel prefixes
162CHARMAPPING=rfc1459 - character mapping used for nickname and channels
163KICKLEN=160 - maximum allowed kick message length
164MAXBANS=30 - maximum number of bans per channel
165MAXCHANNELS=10 - maximum number of channels allowed to join
166NETWORK=EFnet - the network identifier
167NICKLEN=9 - maximum allowed length of nicknames
168PREFIX=(ov)@+ - list of channel modes and the user prefixes if user has mode
169RFC2812 - server supports RFC 2812 features
170SILENCE=10 - supports the SILENCE command, maximum allowed number of entries
171TOPICLEN=160 - maximum allowed topic length
172WALLCHOPS - supports sending messages to all operators in a channel")
173(make-variable-buffer-local 'erc-server-parameters)
174
175;;; Server and connection state
176
177(defvar erc-server-connected nil
178 "Non-nil if the `current-buffer' is associated with an open IRC connection.
179This variable is buffer-local.")
180(make-variable-buffer-local 'erc-server-connected)
181
182(defvar erc-server-quitting nil
183 "Non-nil if the user requests a quit.")
184(make-variable-buffer-local 'erc-server-quitting)
185
186(defvar erc-server-lines-sent nil
187 "Line counter.")
188(make-variable-buffer-local 'erc-server-lines-sent)
189
190(defvar erc-server-last-peers '(nil . nil)
191 "Last peers used, both sender and receiver.
192Those are used for /MSG destination shortcuts.")
193(make-variable-buffer-local 'erc-server-last-peers)
194
195(defvar erc-server-last-sent-time nil
196 "Time the message was sent.
197This is useful for flood protection.")
198(make-variable-buffer-local 'erc-server-last-sent-time)
199
200(defvar erc-server-last-ping-time nil
201 "Time the last ping was sent.
202This is useful for flood protection.")
203(make-variable-buffer-local 'erc-server-last-ping-time)
204
205(defvar erc-server-lag nil
206 "Calculated server lag time in seconds.
207This variable is only set in a server buffer.")
208(make-variable-buffer-local 'erc-server-lag)
209
210(defvar erc-server-filter-data nil
211 "The data that arrived from the server
212but has not been processed yet.")
213(make-variable-buffer-local 'erc-server-filter-data)
214
215(defvar erc-server-duplicates (make-hash-table :test 'equal)
216 "Internal variable used to track duplicate messages.")
217(make-variable-buffer-local 'erc-server-duplicates)
218
219;; From Circe
220(defvar erc-server-processing-p nil
221 "Non-nil when we're currently processing a message.
222
223When ERC receives a private message, it sets up a new buffer for
224this query. These in turn, though, do start flyspell. This
225involves starting an external process, in which case Emacs will
226wait - and when it waits, it does accept other stuff from, say,
227network exceptions. So, if someone sends you two messages
228quickly after each other, ispell is started for the first, but
229might take long enough for the second message to be processed
230first.")
231(make-variable-buffer-local 'erc-server-processing-p)
232
233(defvar erc-server-flood-last-message 0
234 "When we sent the last message.
235See `erc-server-flood-margin' for an explanation of the flood
236protection algorithm.")
237(make-variable-buffer-local 'erc-server-flood-last-message)
238
239(defvar erc-server-flood-queue nil
240 "The queue of messages waiting to be sent to the server.
241See `erc-server-flood-margin' for an explanation of the flood
242protection algorithm.")
243(make-variable-buffer-local 'erc-server-flood-queue)
244
245(defvar erc-server-flood-timer nil
246 "The timer to resume sending.")
247(make-variable-buffer-local 'erc-server-flood-timer)
248
249;;; IRC protocol and misc options
250
251(defgroup erc-server nil
252 "Parameters for dealing with IRC servers."
253 :group 'erc)
254
255(defcustom erc-server-auto-reconnect t
256 "Non-nil means that ERC will attempt to reestablish broken connections.
257
258Reconnection will happen automatically for any unexpected disconnection."
259 :group 'erc-server
260 :type 'boolean)
261
262(defcustom erc-split-line-length 440
263 "*The maximum length of a single message.
264If a message exceeds this size, it is broken into multiple ones.
265
266IRC allows for lines up to 512 bytes. Two of them are CR LF.
267And a typical message looks like this:
268
269 :nicky!uhuser@host212223.dialin.fnordisp.net PRIVMSG #lazybastards :Hello!
270
271You can limit here the maximum length of the \"Hello!\" part.
272Good luck."
273 :type 'integer
274 :group 'erc-server)
275
276(defcustom erc-server-coding-system (if (and (fboundp 'coding-system-p)
277 (coding-system-p 'undecided)
278 (coding-system-p 'utf-8))
279 '(utf-8 . undecided)
280 nil)
281 "The default coding system for incoming and outgoing text.
282This is either a coding system, a cons, a function, or nil.
283
284If a cons, the encoding system for outgoing text is in the car
285and the decoding system for incoming text is in the cdr. The most
286interesting use for this is to put `undecided' in the cdr. If a
287function, it is called with no arguments and should return a
288coding system or a cons as described above. Note that you can use
289the dynamically bound variable `target' to get the current
290target. See `erc-coding-system-for-target'.
291
292If you need to send non-ASCII text to people not using a client that
293does decoding on its own, you must tell ERC what encoding to use.
294Emacs cannot guess it, since it does not know what the people on the
295other end of the line are using."
296 :group 'erc-server
297 :type '(choice (const :tag "None" nil)
298 coding-system
299 (cons (coding-system :tag "encoding" :value utf-8)
300 (coding-system :tag "decoding" :value undecided))
301 function))
302
303(defcustom erc-encoding-coding-alist nil
304 "Alist of target regexp and coding-system pairs to use.
305This overrides `erc-server-coding-system' depending on the
306current target as returned by `erc-default-target'.
307
308Example: If you know that the channel #linux-ru uses the coding-system
309`cyrillic-koi8', then add '(\"#linux-ru\" . cyrillic-koi8) to the
310alist."
311 :group 'erc-server
312 :type '(repeat (cons (string :tag "Target")
313 coding-system)))
314
21bc768b 315(defcustom erc-server-connect-function 'open-network-stream
597993cf
MB
316 "Function used to initiate a connection.
317It should take same arguments as `open-network-stream' does."
318 :group 'erc-server
319 :type 'function)
320
321(defcustom erc-server-prevent-duplicates '("301")
322 "*Either nil or a list of strings.
323Each string is a IRC message type, like PRIVMSG or NOTICE.
324All Message types in that list of subjected to duplicate prevention."
325 :type '(choice (const nil) (list string))
326 :group 'erc-server)
327
328(defcustom erc-server-duplicate-timeout 60
329 "*The time allowed in seconds between duplicate messages.
330
331If two identical messages arrive within this value of one another, the second
332isn't displayed."
333 :type 'integer
334 :group 'erc-server)
335
336;;; Flood-related
337
338;; Most of this is courtesy of Jorgen Schaefer and Circe
339;; (http://www.nongnu.org/circe)
340
341(defcustom erc-server-flood-margin 10
342 "*A margin on how much excess data we send.
343The flood protection algorithm of ERC works like the one
344detailed in RFC 2813, section 5.8 \"Flood control of clients\".
345
346 * If `erc-server-flood-last-message' is less than the current
347 time, set it equal.
348 * While `erc-server-flood-last-message' is less than
349 `erc-server-flood-margin' seconds ahead of the current
350 time, send a message, and increase
351 `erc-server-flood-last-message' by
352 `erc-server-flood-penalty' for each message."
353 :type 'integer
354 :group 'erc-server)
355
356(defcustom erc-server-flood-penalty 3
357 "How much we penalize a message.
358See `erc-server-flood-margin' for an explanation of the flood
359protection algorithm."
360 :type 'integer
361 :group 'erc-server)
362
363;; Ping handling
364
365(defcustom erc-server-send-ping-interval 90
366 "*Interval of sending pings to the server, in seconds.
367If this is set to nil, pinging the server is disabled."
368 :group 'erc-server
369 :type '(choice (const nil) (integer :tag "Seconds")))
370
371(defvar erc-server-ping-handler nil
372 "This variable holds the periodic ping timer.")
373(make-variable-buffer-local 'erc-server-ping-handler)
374
375;;;; Helper functions
376
377;; From Circe
378(defun erc-split-line (longline)
379 "Return a list of lines which are not too long for IRC.
380The length is specified in `erc-split-line-length'.
381
382Currently this is called by `erc-send-input'."
383 (if (< (length longline)
384 erc-split-line-length)
385 (list longline)
386 (with-temp-buffer
387 (insert longline)
388 (let ((fill-column erc-split-line-length))
389 (fill-region (point-min) (point-max)
390 nil t))
391 (split-string (buffer-string) "\n"))))
392
393;; Used by CTCP functions
394(defun erc-upcase-first-word (str)
395 "Upcase the first word in STR."
396 (with-temp-buffer
397 (insert str)
398 (goto-char (point-min))
399 (upcase-word 1)
400 (buffer-string)))
401
402(defun erc-server-setup-periodical-server-ping (&rest ignore)
403 "Set up a timer to periodically ping the current server."
404 (and erc-server-ping-handler (erc-cancel-timer erc-server-ping-handler))
405 (when erc-server-send-ping-interval
406 (setq erc-server-ping-handler
407 (run-with-timer
408 4 erc-server-send-ping-interval
409 (lambda (buf)
410 (when (buffer-live-p buf)
411 (with-current-buffer buf
412 (erc-server-send
413 (format "PING %.0f"
414 (erc-current-time))))))
415 (current-buffer)))))
416
417(defun erc-server-process-alive ()
418 "Return non-nil when `erc-server-process' is open or running."
419 (and (boundp 'erc-server-process)
420 (processp erc-server-process)
421 (memq (process-status erc-server-process) '(run open))))
422
423;;;; Connecting to a server
424
425(defun erc-server-connect (server port)
426 "Perform the connection and login.
427We will store server variables in the current buffer."
428 (let ((msg (erc-format-message 'connect ?S server ?p port)))
429 (message "%s" msg)
430 (setq erc-server-process
431 (funcall erc-server-connect-function
432 (format "erc-%s-%s" server port)
433 (current-buffer) server port))
434 (message "%s...done" msg))
435 ;; Misc server variables
436 (setq erc-server-quitting nil)
437 (setq erc-server-last-sent-time (erc-current-time))
438 (setq erc-server-last-ping-time (erc-current-time))
439 (setq erc-server-lines-sent 0)
440 ;; last peers (sender and receiver)
441 (setq erc-server-last-peers '(nil . nil))
442 ;; process handlers
443 (set-process-sentinel erc-server-process 'erc-process-sentinel)
444 (set-process-filter erc-server-process 'erc-server-filter-function)
445 ;; we do our own encoding and decoding
446 (when (fboundp 'set-process-coding-system)
447 (set-process-coding-system erc-server-process 'raw-text))
448 (set-marker (process-mark erc-server-process) (point))
449 (erc-log "\n\n\n********************************************\n")
450 (message (erc-format-message 'login ?n (erc-current-nick)))
451 ;; wait with script loading until we receive a confirmation (first
452 ;; MOTD line)
453 (if (eq erc-server-connect-function 'open-network-stream-nowait)
454 ;; it's a bit unclear otherwise that it's attempting to establish a
455 ;; connection
456 (erc-display-message nil nil (current-buffer)
457 "Opening connection..\n")
458 (erc-login)))
459
460(defun erc-server-filter-function (process string)
461 "The process filter for the ERC server."
462 (with-current-buffer (process-buffer process)
463 ;; If you think this is written in a weird way - please refer to the
464 ;; docstring of `erc-server-processing-p'
465 (if erc-server-processing-p
466 (setq erc-server-filter-data
467 (if erc-server-filter-data
468 (concat erc-server-filter-data string)
469 string))
470 ;; This will be true even if another process is spawned!
471 (let ((erc-server-processing-p t))
472 (setq erc-server-filter-data (if erc-server-filter-data
473 (concat erc-server-filter-data
474 string)
475 string))
476 (while (and erc-server-filter-data
477 (string-match "[\n\r]+" erc-server-filter-data))
478 (let ((line (substring erc-server-filter-data
479 0 (match-beginning 0))))
480 (setq erc-server-filter-data
481 (if (= (match-end 0)
482 (length erc-server-filter-data))
483 nil
484 (substring erc-server-filter-data
485 (match-end 0))))
486 (erc-parse-server-response process line)))))))
487
488(defun erc-process-sentinel-1 (event)
489 "This will be called when erc-process-sentinel has decided that we
490are going to quit. Determine whether user has quit or whether erc has
491been terminated. Conditionally try to reconnect and take appropriate
492action."
493 (if erc-server-quitting
494 ;; normal quit
495 (progn
496 (let ((string "\n\n*** ERC finished ***\n")
497 (inhibit-read-only t))
498 (erc-put-text-property 0 (length string)
499 'face 'erc-error-face string)
500 (insert string))
501 (when erc-kill-server-buffer-on-quit
502 (set-buffer-modified-p nil)
503 (kill-buffer (current-buffer))))
504 ;; unexpected disconnect
505 (erc-display-message nil 'error (current-buffer)
506 (if erc-server-auto-reconnect
507 'disconnected
508 'disconnected-noreconnect))
509 (erc-update-mode-line)
510 (erc-set-active-buffer (current-buffer))
511 (setq erc-server-last-sent-time 0)
512 (setq erc-server-lines-sent 0)
513 (if (and erc-server-auto-reconnect
514 (not (string-match "^deleted" event))
515 ;; open-network-stream-nowait error for connection refused
516 (not (string-match "^failed with code 111" event)))
517 ;; Yuck, this should perhaps funcall
518 ;; erc-server-reconnect-function with no args
519 (erc erc-session-server erc-session-port erc-server-current-nick
520 erc-session-user-full-name t erc-session-password)
521 ;; terminate, do not reconnect
522 (let ((string (concat "\n\n*** ERC terminated: " event
523 "\n"))
524 (inhibit-read-only t))
525 (erc-put-text-property 0 (length string)
526 'face 'erc-error-face string)
527 (insert string)))))
528
529(defun erc-process-sentinel (cproc event)
530 "Sentinel function for ERC process."
531 (with-current-buffer (process-buffer cproc)
532 (erc-log (format
533 "SENTINEL: proc: %S status: %S event: %S (quitting: %S)"
534 cproc (process-status cproc) event erc-server-quitting))
535 (if (string-match "^open" event)
536 ;; newly opened connection (no wait)
537 (erc-login)
538 ;; assume event is 'failed
539 (let ((buf (process-buffer cproc)))
540 (erc-with-all-buffers-of-server cproc nil
541 (setq erc-server-connected nil))
542 (when erc-server-ping-handler
543 (progn (erc-cancel-timer erc-server-ping-handler)
544 (setq erc-server-ping-handler nil)))
545 (run-hook-with-args 'erc-disconnected-hook
546 (erc-current-nick) (system-name) "")
547 ;; Remove the prompt
548 (forward-line 0)
549 (erc-remove-text-properties-region (point) (point-max))
550 (delete-region (point) (point-max))
551 ;; Decide what to do with the buffer
552 ;; Restart if disconnected
553 (erc-process-sentinel-1 event)
554 ;; Make sure we don't write to the buffer if it has been
555 ;; killed
556 (when (buffer-live-p buf)
557 (erc-update-mode-line)
558 (set-buffer-modified-p nil))))))
559
560;;;; Sending messages
561
562(defun erc-coding-system-for-target (target)
563 "Return the coding system or cons cell appropriate for TARGET.
564This is determined via `erc-encoding-coding-alist' or
565`erc-server-coding-system'."
2e3ef421
MB
566 (or (when target
567 (let ((case-fold-search t))
568 (catch 'match
569 (dolist (pat erc-encoding-coding-alist)
570 (when (string-match (car pat) target)
571 (throw 'match (cdr pat)))))))
597993cf
MB
572 (and (functionp erc-server-coding-system)
573 (funcall erc-server-coding-system))
574 erc-server-coding-system))
575
576(defun erc-decode-string-from-target (str target)
577 "Decode STR as appropriate for TARGET.
578This is indicated by `erc-encoding-coding-alist', defaulting to the value of
579`erc-server-coding-system'."
580 (unless (stringp str)
581 (setq str ""))
582 (let ((coding (erc-coding-system-for-target target)))
583 (when (consp coding)
584 (setq coding (cdr coding)))
585 (erc-decode-coding-string str coding)))
586
587;; proposed name, not used by anything yet
588(defun erc-send-line (text display-fn)
589 "Send TEXT to the current server. Wrapping and flood control apply.
590Use DISPLAY-FN to show the results."
591 (mapc (lambda (line)
592 (erc-server-send line)
593 (funcall display-fn))
594 (erc-split-line text)))
595
596;; From Circe, with modifications
597(defun erc-server-send (string &optional forcep target)
598 "Send STRING to the current server.
599If FORCEP is non-nil, no flood protection is done - the string is
600sent directly. This might cause the messages to arrive in a wrong
601order.
602
603If TARGET is specified, look up encoding information for that
604channel in `erc-encoding-coding-alist' or
605`erc-server-coding-system'.
606
607See `erc-server-flood-margin' for an explanation of the flood
608protection algorithm."
609 (erc-log (concat "erc-server-send: " string "(" (buffer-name) ")"))
610 (setq erc-server-last-sent-time (erc-current-time))
611 (let ((buf (erc-server-buffer))
612 (encoding (erc-coding-system-for-target
613 (or target (erc-default-target)))))
614 (when (consp encoding)
615 (setq encoding (car encoding)))
616 (if (and buf
617 (erc-server-process-alive))
618 (with-current-buffer buf
619 (let ((str (concat string "\r\n")))
620 (if forcep
621 (progn
622 (setq erc-server-flood-last-message
623 (+ erc-server-flood-penalty
624 erc-server-flood-last-message))
625 (erc-log-irc-protocol str 'outbound)
626 (condition-case err
627 (progn
628 ;; Set encoding just before sending the string
629 (when (fboundp 'set-process-coding-system)
630 (set-process-coding-system erc-server-process
631 'raw-text encoding))
632 (process-send-string erc-server-process str))
633 ;; See `erc-server-send-queue' for full
634 ;; explanation of why we need this condition-case
635 (error nil)))
636 (setq erc-server-flood-queue
637 (append erc-server-flood-queue
638 (list (cons str encoding))))
639 (erc-server-send-queue (current-buffer))))
640 t)
641 (message "ERC: No process running")
642 nil)))
643
644;; From Circe
645(defun erc-server-send-queue (buffer)
646 "Send messages in `erc-server-flood-queue'.
647See `erc-server-flood-margin' for an explanation of the flood
648protection algorithm."
649 (with-current-buffer buffer
650 (let ((now (erc-current-time)))
651 (when erc-server-flood-timer
652 (erc-cancel-timer erc-server-flood-timer)
653 (setq erc-server-flood-timer nil))
654 (when (< erc-server-flood-last-message
655 now)
656 (setq erc-server-flood-last-message now))
657 (while (and erc-server-flood-queue
658 (< erc-server-flood-last-message
659 (+ now erc-server-flood-margin)))
660 (let ((msg (caar erc-server-flood-queue))
661 (encoding (cdar erc-server-flood-queue)))
662 (setq erc-server-flood-queue (cdr erc-server-flood-queue)
663 erc-server-flood-last-message
664 (+ erc-server-flood-last-message
665 erc-server-flood-penalty))
666 (erc-log-irc-protocol msg 'outbound)
667 (erc-log (concat "erc-server-send-queue: "
668 msg "(" (buffer-name buffer) ")"))
669 (when (erc-server-process-alive)
670 (condition-case err
671 ;; Set encoding just before sending the string
672 (progn
673 (when (fboundp 'set-process-coding-system)
674 (set-process-coding-system erc-server-process
675 'raw-text encoding))
676 (process-send-string erc-server-process msg))
677 ;; Sometimes the send can occur while the process is
678 ;; being killed, which results in a weird SIGPIPE error.
679 ;; Catch this and ignore it.
680 (error nil)))))
681 (when erc-server-flood-queue
682 (setq erc-server-flood-timer
683 (run-at-time 2 nil #'erc-server-send-queue buffer))))))
684
685(defun erc-message (message-command line &optional force)
686 "Send LINE to the server as a privmsg or a notice.
687MESSAGE-COMMAND should be either \"PRIVMSG\" or \"NOTICE\".
688If the target is \",\", the last person you've got a message from will
689be used. If the target is \".\", the last person you've sent a message
690to will be used."
691 (cond
692 ((string-match "^\\s-*\\(\\S-+\\) ?\\(.*\\)" line)
693 (let ((tgt (match-string 1 line))
694 (s (match-string 2 line)))
695 (erc-log (format "cmd: MSG(%s): [%s] %s" message-command tgt s))
696 (cond
697 ((string= tgt ",")
698 (if (car erc-server-last-peers)
699 (setq tgt (car erc-server-last-peers))
700 (setq tgt nil)))
701 ((string= tgt ".")
702 (if (cdr erc-server-last-peers)
703 (setq tgt (cdr erc-server-last-peers))
704 (setq tgt nil))))
705 (cond
706 (tgt
707 (setcdr erc-server-last-peers tgt)
708 (erc-server-send (format "%s %s :%s" message-command tgt s)
709 force))
710 (t
711 (erc-display-message nil 'error (current-buffer) 'no-target))))
712 t)
713 (t nil)))
714
715;;; CTCP
716
717(defun erc-send-ctcp-message (tgt l &optional force)
718 "Send CTCP message L to TGT.
719
720If TGT is nil the message is not sent.
721The command must contain neither a prefix nor a trailing `\\n'.
722
723See also `erc-server-send'."
724 (let ((l (erc-upcase-first-word l)))
725 (cond
726 (tgt
727 (erc-log (format "erc-send-CTCP-message: [%s] %s" tgt l))
728 (erc-server-send (format "PRIVMSG %s :\C-a%s\C-a" tgt l)
729 force)))))
730
731(defun erc-send-ctcp-notice (tgt l &optional force)
732 "Send CTCP notice L to TGT.
733
734If TGT is nil the message is not sent.
735The command must contain neither a prefix nor a trailing `\\n'.
736
737See also `erc-server-send'."
738 (let ((l (erc-upcase-first-word l)))
739 (cond
740 (tgt
741 (erc-log (format "erc-send-CTCP-notice: [%s] %s" tgt l))
742 (erc-server-send (format "NOTICE %s :\C-a%s\C-a" tgt l)
743 force)))))
744
745;;;; Handling responses
746
747(defun erc-parse-server-response (proc string)
748 "Parse and act upon a complete line from an IRC server.
749PROC is the process (connection) from which STRING was received.
750PROCs `process-buffer' is `current-buffer' when this function is called."
751 (unless (string= string "") ;; Ignore empty strings
752 (save-match-data
753 (let ((posn (if (eq (aref string 0) ?:)
754 (string-match " " string)
755 0))
756 (msg (make-erc-response :unparsed string)))
757
758 (setf (erc-response.sender msg)
759 (if (eq posn 0)
760 erc-session-server
761 (substring string 1 posn)))
762
763 (setf (erc-response.command msg)
21bc768b 764 (let* ((bposn (string-match "[^ \n]" string posn))
597993cf
MB
765 (eposn (string-match " " string bposn)))
766 (setq posn (and eposn
21bc768b 767 (string-match "[^ \n]" string eposn)))
597993cf
MB
768 (substring string bposn eposn)))
769
770 (while (and posn
771 (not (eq (aref string posn) ?:)))
772 (push (let* ((bposn posn)
773 (eposn (string-match " " string bposn)))
774 (setq posn (and eposn
21bc768b 775 (string-match "[^ \n]" string eposn)))
597993cf
MB
776 (substring string bposn eposn))
777 (erc-response.command-args msg)))
778 (when posn
779 (let ((str (substring string (1+ posn))))
780 (push str (erc-response.command-args msg))))
781
782 (setf (erc-response.contents msg)
783 (first (erc-response.command-args msg)))
784
785 (setf (erc-response.command-args msg)
786 (nreverse (erc-response.command-args msg)))
787
788 (erc-decode-parsed-server-response msg)
789
790 (erc-handle-parsed-server-response proc msg)))))
791
792(defun erc-decode-parsed-server-response (parsed-response)
793 "Decode a pre-parsed PARSED-RESPONSE before it can be handled.
794
795If there is a channel name in `erc-response.command-args', decode
796`erc-response' according to this channel name and
797`erc-encoding-coding-alist', or use `erc-server-coding-system'
798for decoding."
799 (let ((args (erc-response.command-args parsed-response))
800 (decode-target nil)
801 (decoded-args ()))
802 (dolist (arg args nil)
803 (when (string-match "^[#&].*" arg)
804 (setq decode-target arg)))
805 (when (stringp decode-target)
806 (setq decode-target (erc-decode-string-from-target decode-target nil)))
807 (setf (erc-response.unparsed parsed-response)
808 (erc-decode-string-from-target
809 (erc-response.unparsed parsed-response)
810 decode-target))
811 (setf (erc-response.sender parsed-response)
812 (erc-decode-string-from-target
813 (erc-response.sender parsed-response)
814 decode-target))
815 (setf (erc-response.command parsed-response)
816 (erc-decode-string-from-target
817 (erc-response.command parsed-response)
818 decode-target))
819 (dolist (arg (nreverse args) nil)
820 (push (erc-decode-string-from-target arg decode-target)
821 decoded-args))
822 (setf (erc-response.command-args parsed-response) decoded-args)
823 (setf (erc-response.contents parsed-response)
824 (erc-decode-string-from-target
825 (erc-response.contents parsed-response)
826 decode-target))))
827
828(defun erc-handle-parsed-server-response (process parsed-response)
829 "Handle a pre-parsed PARSED-RESPONSE from PROCESS.
830
831Hands off to helper functions via `erc-call-hooks'."
832 (if (member (erc-response.command parsed-response)
833 erc-server-prevent-duplicates)
834 (let ((m (erc-response.unparsed parsed-response)))
835 ;; duplicate supression
836 (if (< (or (gethash m erc-server-duplicates) 0)
837 (- (erc-current-time) erc-server-duplicate-timeout))
838 (erc-call-hooks process parsed-response))
839 (puthash m (erc-current-time) erc-server-duplicates))
840 ;; Hand off to the relevant handler.
841 (erc-call-hooks process parsed-response)))
842
843(defun erc-get-hook (command)
844 "Return the hook variable associated with COMMAND.
845
846See also `erc-server-responses'."
847 (gethash (format (if (numberp command) "%03i" "%s") command)
848 erc-server-responses))
849
850(defun erc-call-hooks (process message)
851 "Call hooks associated with MESSAGE in PROCESS.
852
853Finds hooks by looking in the `erc-server-responses' hashtable."
854 (let ((hook (or (erc-get-hook (erc-response.command message))
855 'erc-default-server-functions)))
856 (run-hook-with-args-until-success hook process message)
0b6bb130
MB
857 (let ((server-buffer (erc-server-buffer)))
858 (when (buffer-live-p server-buffer)
859 (with-current-buffer server-buffer
860 (run-hook-with-args 'erc-timer-hook (erc-current-time)))))))
597993cf
MB
861
862(add-hook 'erc-default-server-functions 'erc-handle-unknown-server-response)
863
864(defun erc-handle-unknown-server-response (proc parsed)
865 "Display unknown server response's message."
866 (let ((line (concat (erc-response.sender parsed)
867 " "
868 (erc-response.command parsed)
869 " "
870 (mapconcat 'identity (erc-response.command-args parsed)
871 " "))))
872 (erc-display-message parsed 'notice proc line)))
873
874
875(put 'define-erc-response-handler 'edebug-form-spec
876 '(&define :name erc-response-handler
877 (name &rest name)
878 &optional sexp sexp def-body))
879
880(defmacro* define-erc-response-handler ((name &rest aliases)
881 &optional extra-fn-doc extra-var-doc
882 &rest fn-body)
883 "Define an ERC handler hook/function pair.
884NAME is the response name as sent by the server (see the IRC RFC for
885meanings).
886
887This creates:
888 - a hook variable `erc-server-NAME-functions' initialised to `erc-server-NAME'.
889 - a function `erc-server-NAME' with body FN-BODY.
890
891If ALIASES is non-nil, each alias in ALIASES is `defalias'ed to
892`erc-server-NAME'.
893Alias hook variables are created as `erc-server-ALIAS-functions' and
894initialised to the same default value as `erc-server-NAME-functions'.
895
896FN-BODY is the body of `erc-server-NAME' it may refer to the two
897function arguments PROC and PARSED.
898
899If EXTRA-FN-DOC is non-nil, it is inserted at the beginning of the
900defined function's docstring.
901
902If EXTRA-VAR-DOC is non-nil, it is inserted at the beginning of the
903defined variable's docstring.
904
905As an example:
906
907 (define-erc-response-handler (311 WHOIS WI)
908 \"Some non-generic function documentation.\"
909 \"Some non-generic variable documentation.\"
910 (do-stuff-with-whois proc parsed))
911
912Would expand to:
913
914 (prog2
915 (defvar erc-server-311-functions 'erc-server-311
916 \"Some non-generic variable documentation.
917
918 Hook called upon receiving a 311 server response.
919 Each function is called with two arguments, the process associated
920 with the response and the parsed response.
921 See also `erc-server-311'.\")
922
923 (defun erc-server-311 (proc parsed)
924 \"Some non-generic function documentation.
925
926 Handler for a 311 server response.
927 PROC is the server process which returned the response.
928 PARSED is the actual response as an `erc-response' struct.
929 If you want to add responses don't modify this function, but rather
930 add things to `erc-server-311-functions' instead.\"
931 (do-stuff-with-whois proc parsed))
932
933 (puthash \"311\" 'erc-server-311-functions erc-server-responses)
934 (puthash \"WHOIS\" 'erc-server-WHOIS-functions erc-server-responses)
935 (puthash \"WI\" 'erc-server-WI-functions erc-server-responses)
936
937 (defalias 'erc-server-WHOIS 'erc-server-311)
938 (defvar erc-server-WHOIS-functions 'erc-server-311
939 \"Some non-generic variable documentation.
940
941 Hook called upon receiving a WHOIS server response.
942 Each function is called with two arguments, the process associated
943 with the response and the parsed response.
944 See also `erc-server-311'.\")
945
946 (defalias 'erc-server-WI 'erc-server-311)
947 (defvar erc-server-WI-functions 'erc-server-311
948 \"Some non-generic variable documentation.
949
950 Hook called upon receiving a WI server response.
951 Each function is called with two arguments, the process associated
952 with the response and the parsed response.
953 See also `erc-server-311'.\"))
954
955\(fn (NAME &rest ALIASES) &optional EXTRA-FN-DOC EXTRA-VAR-DOC &rest FN-BODY)"
956 (if (numberp name) (setq name (intern (format "%03i" name))))
957 (setq aliases (mapcar (lambda (a)
958 (if (numberp a)
959 (format "%03i" a)
960 a))
961 aliases))
962 (let* ((hook-name (intern (format "erc-server-%s-functions" name)))
963 (fn-name (intern (format "erc-server-%s" name)))
964 (hook-doc (format "%sHook called upon receiving a %%s server response.
965Each function is called with two arguments, the process associated
966with the response and the parsed response.
967See also `%s'."
968 (if extra-var-doc
969 (concat extra-var-doc "\n\n")
970 "")
971 fn-name))
972 (fn-doc (format "%sHandler for a %s server response.
973PROC is the server process which returned the response.
974PARSED is the actual response as an `erc-response' struct.
975If you want to add responses don't modify this function, but rather
976add things to `%s' instead."
977 (if extra-fn-doc
978 (concat extra-fn-doc "\n\n")
979 "")
980 name hook-name))
981 (fn-alternates
982 (loop for alias in aliases
983 collect (intern (format "erc-server-%s" alias))))
984 (var-alternates
985 (loop for alias in aliases
986 collect (intern (format "erc-server-%s-functions" alias)))))
987 `(prog2
988 ;; Normal hook variable.
989 (defvar ,hook-name ',fn-name ,(format hook-doc name))
990 ;; Handler function
991 (defun ,fn-name (proc parsed)
992 ,fn-doc
993 ,@fn-body)
994
995 ;; Make find-function and find-variable find them
996 (put ',fn-name 'definition-name ',name)
997 (put ',hook-name 'definition-name ',name)
998
999 ;; Hashtable map of responses to hook variables
1000 ,@(loop for response in (cons name aliases)
1001 for var in (cons hook-name var-alternates)
1002 collect `(puthash ,(format "%s" response) ',var
1003 erc-server-responses))
1004 ;; Alternates.
1005 ;; Functions are defaliased, hook variables are defvared so we
1006 ;; can add hooks to one alias, but not another.
1007 ,@(loop for fn in fn-alternates
1008 for var in var-alternates
1009 for a in aliases
1010 nconc (list `(defalias ',fn ',fn-name)
1011 `(defvar ,var ',fn-name ,(format hook-doc a))
1012 `(put ',var 'definition-name ',hook-name))))))
1013
1014(define-erc-response-handler (ERROR)
1015 "Handle an ERROR command from the server." nil
1016 (erc-display-message
1017 parsed 'error nil 'ERROR
1018 ?s (erc-response.sender parsed) ?c (erc-response.contents parsed)))
1019
1020(define-erc-response-handler (INVITE)
1021 "Handle invitation messages."
1022 nil
1023 (let ((target (first (erc-response.command-args parsed)))
1024 (chnl (erc-response.contents parsed)))
1025 (multiple-value-bind (nick login host)
1026 (erc-parse-user (erc-response.sender parsed))
1027 (setq erc-invitation chnl)
1028 (when (string= target (erc-current-nick))
1029 (erc-display-message
1030 parsed 'notice 'active
1031 'INVITE ?n nick ?u login ?h host ?c chnl)))))
1032
1033
1034(define-erc-response-handler (JOIN)
1035 "Handle join messages."
1036 nil
1037 (let ((chnl (erc-response.contents parsed))
1038 (buffer nil))
1039 (multiple-value-bind (nick login host)
1040 (erc-parse-user (erc-response.sender parsed))
1041 ;; strip the stupid combined JOIN facility (IRC 2.9)
1042 (if (string-match "^\\(.*\\)?\^g.*$" chnl)
1043 (setq chnl (match-string 1 chnl)))
1044 (save-excursion
1045 (let* ((str (cond
1046 ;; If I have joined a channel
1047 ((erc-current-nick-p nick)
1048 (setq buffer (erc erc-session-server erc-session-port
1049 nick erc-session-user-full-name
1050 nil nil
1051 erc-default-recipients chnl
1052 erc-server-process))
1053 (when buffer
1054 (set-buffer buffer)
1055 (erc-add-default-channel chnl)
1056 (erc-server-send (format "MODE %s" chnl)))
1057 (erc-with-buffer (chnl proc)
1058 (erc-channel-begin-receiving-names))
1059 (erc-update-mode-line)
1060 (run-hooks 'erc-join-hook)
1061 (erc-make-notice
1062 (erc-format-message 'JOIN-you ?c chnl)))
1063 (t
1064 (setq buffer (erc-get-buffer chnl proc))
1065 (erc-make-notice
1066 (erc-format-message
1067 'JOIN ?n nick ?u login ?h host ?c chnl))))))
1068 (when buffer (set-buffer buffer))
1069 (erc-update-channel-member chnl nick nick t nil nil host login)
1070 ;; on join, we want to stay in the new channel buffer
1071 ;;(set-buffer ob)
1072 (erc-display-message parsed nil buffer str))))))
1073
1074(define-erc-response-handler (KICK)
1075 "Handle kick messages received from the server." nil
1076 (let* ((ch (first (erc-response.command-args parsed)))
1077 (tgt (second (erc-response.command-args parsed)))
1078 (reason (erc-trim-string (erc-response.contents parsed)))
1079 (buffer (erc-get-buffer ch proc)))
1080 (multiple-value-bind (nick login host)
1081 (erc-parse-user (erc-response.sender parsed))
1082 (erc-remove-channel-member buffer tgt)
1083 (cond
1084 ((string= tgt (erc-current-nick))
1085 (erc-display-message
1086 parsed 'notice buffer
1087 'KICK-you ?n nick ?u login ?h host ?c ch ?r reason)
1088 (run-hook-with-args 'erc-kick-hook buffer)
1089 (erc-with-buffer
1090 (buffer)
1091 (erc-remove-channel-users))
1092 (erc-delete-default-channel ch buffer)
1093 (erc-update-mode-line buffer))
1094 ((string= nick (erc-current-nick))
1095 (erc-display-message
1096 parsed 'notice buffer
1097 'KICK-by-you ?k tgt ?c ch ?r reason))
1098 (t (erc-display-message
1099 parsed 'notice buffer
1100 'KICK ?k tgt ?n nick ?u login ?h host ?c ch ?r reason))))))
1101
1102(define-erc-response-handler (MODE)
1103 "Handle server mode changes." nil
1104 (let ((tgt (first (erc-response.command-args parsed)))
1105 (mode (mapconcat 'identity (cdr (erc-response.command-args parsed))
1106 " ")))
1107 (multiple-value-bind (nick login host)
1108 (erc-parse-user (erc-response.sender parsed))
1109 (erc-log (format "MODE: %s -> %s: %s" nick tgt mode))
1110 ;; dirty hack
1111 (let ((buf (cond ((erc-channel-p tgt)
1112 (erc-get-buffer tgt proc))
1113 ((string= tgt (erc-current-nick)) nil)
1114 ((erc-active-buffer) (erc-active-buffer))
1115 (t (erc-get-buffer tgt)))))
1116 (with-current-buffer (or buf
1117 (current-buffer))
1118 (erc-update-modes tgt mode nick host login))
1119 (if (or (string= login "") (string= host ""))
1120 (erc-display-message parsed 'notice buf
1121 'MODE-nick ?n nick
1122 ?t tgt ?m mode)
1123 (erc-display-message parsed 'notice buf
1124 'MODE ?n nick ?u login
1125 ?h host ?t tgt ?m mode)))
1126 (erc-banlist-update proc parsed))))
1127
1128(define-erc-response-handler (NICK)
1129 "Handle nick change messages." nil
1130 (let ((nn (erc-response.contents parsed))
1131 bufs)
1132 (multiple-value-bind (nick login host)
1133 (erc-parse-user (erc-response.sender parsed))
1134 (setq bufs (erc-buffer-list-with-nick nick proc))
1135 (erc-log (format "NICK: %s -> %s" nick nn))
1136 ;; if we had a query with this user, make sure future messages will be
1137 ;; sent to the correct nick. also add to bufs, since the user will want
1138 ;; to see the nick change in the query, and if it's a newly begun query,
1139 ;; erc-channel-users won't contain it
1140 (erc-buffer-filter
1141 (lambda ()
1142 (when (equal (erc-default-target) nick)
1143 (setq erc-default-recipients
1144 (cons nn (cdr erc-default-recipients)))
1145 (rename-buffer nn)
1146 (erc-update-mode-line)
1147 (add-to-list 'bufs (current-buffer)))))
1148 (erc-update-user-nick nick nn host nil nil login)
1149 (cond
1150 ((string= nick (erc-current-nick))
1151 (add-to-list 'bufs (erc-server-buffer))
1152 (erc-set-current-nick nn)
1153 (erc-update-mode-line)
1154 (setq erc-nick-change-attempt-count 0)
1155 (setq erc-default-nicks (if (consp erc-nick) erc-nick (list erc-nick)))
1156 (erc-display-message
1157 parsed 'notice bufs
1158 'NICK-you ?n nick ?N nn)
1159 (run-hook-with-args 'erc-nick-changed-functions nn nick))
1160 (t
1161 (erc-handle-user-status-change 'nick (list nick login host) (list nn))
1162 (erc-display-message parsed 'notice bufs 'NICK ?n nick
1163 ?u login ?h host ?N nn))))))
1164
1165(define-erc-response-handler (PART)
1166 "Handle part messages." nil
1167 (let* ((chnl (first (erc-response.command-args parsed)))
1168 (reason (erc-trim-string (erc-response.contents parsed)))
1169 (buffer (erc-get-buffer chnl proc)))
1170 (multiple-value-bind (nick login host)
1171 (erc-parse-user (erc-response.sender parsed))
1172 (erc-remove-channel-member buffer nick)
1173 (erc-display-message parsed 'notice buffer
1174 'PART ?n nick ?u login
1175 ?h host ?c chnl ?r (or reason ""))
1176 (when (string= nick (erc-current-nick))
1177 (run-hook-with-args 'erc-part-hook buffer)
1178 (erc-with-buffer
1179 (buffer)
1180 (erc-remove-channel-users))
1181 (erc-delete-default-channel chnl buffer)
1182 (erc-update-mode-line buffer)
1183 (when erc-kill-buffer-on-part
1184 (kill-buffer buffer))))))
1185
1186(define-erc-response-handler (PING)
1187 "Handle ping messages." nil
1188 (let ((pinger (first (erc-response.command-args parsed))))
1189 (erc-log (format "PING: %s" pinger))
1190 ;; ping response to the server MUST be forced, or you can lose big
1191 (erc-server-send (format "PONG :%s" pinger) t)
1192 (when erc-verbose-server-ping
1193 (erc-display-message
1194 parsed 'error proc
1195 'PING ?s (erc-time-diff erc-server-last-ping-time (erc-current-time))))
1196 (setq erc-server-last-ping-time (erc-current-time))))
1197
1198(define-erc-response-handler (PONG)
1199 "Handle pong messages." nil
1200 (let ((time (string-to-number (erc-response.contents parsed))))
1201 (when (> time 0)
1202 (setq erc-server-lag (erc-time-diff time (erc-current-time)))
1203 (when erc-verbose-server-ping
1204 (erc-display-message
1205 parsed 'notice proc 'PONG
1206 ?h (first (erc-response.command-args parsed)) ?i erc-server-lag
1207 ?s (if (/= erc-server-lag 1) "s" "")))
1208 (erc-update-mode-line))))
1209
1210(define-erc-response-handler (PRIVMSG NOTICE)
1211 nil nil
1212 (let ((sender-spec (erc-response.sender parsed))
1213 (cmd (erc-response.command parsed))
1214 (tgt (car (erc-response.command-args parsed)))
1215 (msg (erc-response.contents parsed)))
1216 (if (or (erc-ignored-user-p sender-spec)
1217 (erc-ignored-reply-p msg tgt proc))
1218 (when erc-minibuffer-ignored
1219 (message "Ignored %s from %s to %s" cmd sender-spec tgt))
1220 (let* ((sndr (erc-parse-user sender-spec))
1221 (nick (nth 0 sndr))
1222 (login (nth 1 sndr))
1223 (host (nth 2 sndr))
1224 (msgp (string= cmd "PRIVMSG"))
1225 (noticep (string= cmd "NOTICE"))
1226 ;; S.B. downcase *both* tgt and current nick
1227 (privp (erc-current-nick-p tgt))
1228 s buffer
1229 fnick)
1230 (setf (erc-response.contents parsed) msg)
1231 (setq buffer (erc-get-buffer (if privp nick tgt) proc))
1232 (when buffer
1233 (with-current-buffer buffer
1234 ;; update the chat partner info. Add to the list if private
1235 ;; message. We will accumulate private identities indefinitely
1236 ;; at this point.
1237 (erc-update-channel-member (if privp nick tgt) nick nick
1238 privp nil nil host login nil nil t)
1239 (let ((cdata (erc-get-channel-user nick)))
1240 (setq fnick (funcall erc-format-nick-function
1241 (car cdata) (cdr cdata))))))
1242 (cond
1243 ((erc-is-message-ctcp-p msg)
1244 (setq s (if msgp
1245 (erc-process-ctcp-query proc parsed nick login host)
1246 (erc-process-ctcp-reply proc parsed nick login host
1247 (match-string 1 msg)))))
1248 (t
1249 (setcar erc-server-last-peers nick)
1250 (setq s (erc-format-privmessage
1251 (or fnick nick) msg
1252 ;; If buffer is a query buffer,
1253 ;; format the nick as for a channel.
1254 (and (not (and buffer
1255 (erc-query-buffer-p buffer)
1256 erc-format-query-as-channel-p))
1257 privp)
1258 msgp))))
1259 (when s
1260 (if (and noticep privp)
1261 (progn
1262 (run-hook-with-args 'erc-echo-notice-always-hook
1263 s parsed buffer nick)
1264 (run-hook-with-args-until-success
1265 'erc-echo-notice-hook s parsed buffer nick))
1266 (erc-display-message parsed nil buffer s)))
1267 (when (string= cmd "PRIVMSG")
1268 (erc-auto-query proc parsed))))))
1269
1270;; FIXME: need clean way of specifiying extra hooks in
1271;; define-erc-response-handler.
1272(add-hook 'erc-server-PRIVMSG-functions 'erc-auto-query)
1273
1274(define-erc-response-handler (QUIT)
1275 nil nil
1276 (let ((reason (erc-response.contents parsed))
1277 bufs)
1278 (multiple-value-bind (nick login host)
1279 (erc-parse-user (erc-response.sender parsed))
1280 (setq bufs (erc-buffer-list-with-nick nick proc))
1281 (erc-remove-user nick)
1282 (setq reason (erc-wash-quit-reason reason nick login host))
1283 (erc-display-message parsed 'notice bufs
1284 'QUIT ?n nick ?u login
1285 ?h host ?r reason))))
1286
1287(define-erc-response-handler (TOPIC)
1288 nil nil
1289 (let* ((ch (first (erc-response.command-args parsed)))
1290 (topic (erc-trim-string (erc-response.contents parsed)))
1291 (time (format-time-string "%T %m/%d/%y" (current-time))))
1292 (multiple-value-bind (nick login host)
1293 (erc-parse-user (erc-response.sender parsed))
1294 (erc-update-channel-member ch nick nick nil nil nil host login)
1295 (erc-update-channel-topic ch (format "%s\C-o (%s, %s)" topic nick time))
1296 (erc-display-message parsed 'notice (erc-get-buffer ch proc)
1297 'TOPIC ?n nick ?u login ?h host
1298 ?c ch ?T topic))))
1299
1300(define-erc-response-handler (WALLOPS)
1301 nil nil
1302 (let ((message (erc-response.contents parsed)))
1303 (multiple-value-bind (nick login host)
1304 (erc-parse-user (erc-response.sender parsed))
1305 (erc-display-message
1306 parsed 'notice nil
1307 'WALLOPS ?n nick ?m message))))
1308
1309(define-erc-response-handler (001)
1310 "Set `erc-server-current-nick' to reflect server settings and display the welcome message."
1311 nil
1312 (erc-set-current-nick (first (erc-response.command-args parsed)))
1313 (erc-update-mode-line) ; needed here?
1314 (setq erc-nick-change-attempt-count 0)
1315 (setq erc-default-nicks (if (consp erc-nick) erc-nick (list erc-nick)))
1316 (erc-display-message
1317 parsed 'notice 'active (erc-response.contents parsed)))
1318
1319(define-erc-response-handler (MOTD 002 003 371 372 374 375)
1320 "Display the server's message of the day." nil
1321 (erc-handle-login)
1322 (erc-display-message
1323 parsed 'notice (if erc-server-connected 'active proc)
1324 (erc-response.contents parsed)))
1325
1326(define-erc-response-handler (376 422)
1327 nil nil
1328 (erc-server-MOTD proc parsed)
1329 (erc-connection-established proc parsed))
1330
1331(define-erc-response-handler (004)
1332 nil nil
1333 (multiple-value-bind (server-name server-version)
1334 (cdr (erc-response.command-args parsed))
1335 (setq erc-server-version server-version)
1336 (setq erc-server-announced-name server-name)
1337 (erc-update-mode-line-buffer (process-buffer proc))
1338 (erc-display-message
1339 parsed 'notice proc
1340 's004 ?s server-name ?v server-version
1341 ?U (fourth (erc-response.command-args parsed))
1342 ?C (fifth (erc-response.command-args parsed)))))
1343
1344(define-erc-response-handler (005)
1345 "Set the variable `erc-server-parameters' and display the received message.
1346
1347According to RFC 2812, suggests alternate servers on the network.
1348Many servers, however, use this code to show which parameters they have set,
1349for example, the network identifier, maximum allowed topic length, whether
1350certain commands are accepted and more. See documentation for
1351`erc-server-parameters' for more information on the parameters sent.
1352
1353A server may send more than one 005 message."
1354 nil
1355 (let ((line (mapconcat 'identity
1356 (setf (erc-response.command-args parsed)
1357 (cdr (erc-response.command-args parsed)))
1358 " ")))
1359 (while (erc-response.command-args parsed)
1360 (let ((section (pop (erc-response.command-args parsed))))
1361 ;; fill erc-server-parameters
1362 (when (string-match "^\\([A-Z]+\\)\=\\(.*\\)$\\|^\\([A-Z]+\\)$"
1363 section)
1364 (add-to-list 'erc-server-parameters
1365 `(,(or (match-string 1 section)
1366 (match-string 3 section))
1367 .
1368 ,(match-string 2 section))))))
1369 (erc-display-message parsed 'notice proc line)))
1370
1371(define-erc-response-handler (221)
1372 nil nil
1373 (let* ((nick (first (erc-response.command-args parsed)))
1374 (modes (mapconcat 'identity
1375 (cdr (erc-response.command-args parsed)) " ")))
1376 (erc-set-modes nick modes)
1377 (erc-display-message parsed 'notice 'active 's221 ?n nick ?m modes)))
1378
1379(define-erc-response-handler (252)
1380 "Display the number of IRC operators online." nil
1381 (erc-display-message parsed 'notice 'active 's252
1382 ?i (second (erc-response.command-args parsed))))
1383
1384(define-erc-response-handler (253)
1385 "Display the number of unknown connections." nil
1386 (erc-display-message parsed 'notice 'active 's253
1387 ?i (second (erc-response.command-args parsed))))
1388
1389(define-erc-response-handler (254)
1390 "Display the number of channels formed." nil
1391 (erc-display-message parsed 'notice 'active 's254
1392 ?i (second (erc-response.command-args parsed))))
1393
1394(define-erc-response-handler (250 251 255 256 257 258 259 265 266 377 378)
1395 "Generic display of server messages as notices.
1396
1397See `erc-display-server-message'." nil
1398 (erc-display-server-message proc parsed))
1399
1400(define-erc-response-handler (301)
1401 "AWAY notice." nil
1402 (erc-display-message parsed 'notice 'active 's301
1403 ?n (second (erc-response.command-args parsed))
1404 ?r (erc-response.contents parsed)))
1405
1406(define-erc-response-handler (303)
1407 "ISON reply" nil
1408 (erc-display-message parsed 'notice 'active 's303
1409 ?n (second (erc-response.command-args parsed))))
1410
1411(define-erc-response-handler (305)
1412 "Return from AWAYness." nil
1413 (erc-process-away proc nil)
1414 (erc-display-message parsed 'notice 'active
1415 's305 ?m (erc-response.contents parsed)))
1416
1417(define-erc-response-handler (306)
1418 "Set AWAYness." nil
1419 (erc-process-away proc t)
1420 (erc-display-message parsed 'notice 'active
1421 's306 ?m (erc-response.contents parsed)))
1422
1423(define-erc-response-handler (311 314)
1424 "WHOIS/WHOWAS notices." nil
1425 (let ((fname (erc-response.contents parsed))
1426 (catalog-entry (intern (format "s%s" (erc-response.command parsed)))))
1427 (multiple-value-bind (nick user host)
1428 (cdr (erc-response.command-args parsed))
1429 (erc-update-user-nick nick nick host nil fname user)
1430 (erc-display-message
1431 parsed 'notice 'active catalog-entry
1432 ?n nick ?f fname ?u user ?h host))))
1433
1434(define-erc-response-handler (312)
1435 nil nil
1436 (multiple-value-bind (nick server-host)
1437 (cdr (erc-response.command-args parsed))
1438 (erc-display-message
1439 parsed 'notice 'active 's312
1440 ?n nick ?s server-host ?c (erc-response.contents parsed))))
1441
1442(define-erc-response-handler (313)
1443 "IRC Operator response in WHOIS." nil
1444 (erc-display-message
1445 parsed 'notice 'active 's313
1446 ?n (second (erc-response.command-args parsed))))
1447
1448(define-erc-response-handler (315 318 323 369)
1449 ;; 315 - End of WHO
1450 ;; 318 - End of WHOIS list
1451 ;; 323 - End of channel LIST
1452 ;; 369 - End of WHOWAS
1453 nil nil
1454 (ignore proc parsed))
1455
1456(define-erc-response-handler (317)
1457 "IDLE notice." nil
1458 (multiple-value-bind (nick seconds-idle on-since time)
1459 (cdr (erc-response.command-args parsed))
1460 (setq time (when on-since
1461 (format-time-string "%T %Y/%m/%d"
1462 (erc-string-to-emacs-time on-since))))
1463 (erc-update-user-nick nick nick nil nil nil
1464 (and time (format "on since %s" time)))
1465 (if time
1466 (erc-display-message
1467 parsed 'notice 'active 's317-on-since
1468 ?n nick ?i (erc-sec-to-time (string-to-number seconds-idle)) ?t time)
1469 (erc-display-message
1470 parsed 'notice 'active 's317
1471 ?n nick ?i (erc-sec-to-time (string-to-number seconds-idle))))))
1472
1473(define-erc-response-handler (319)
1474 nil nil
1475 (erc-display-message
1476 parsed 'notice 'active 's319
1477 ?n (second (erc-response.command-args parsed))
1478 ?c (erc-response.contents parsed)))
1479
1480(define-erc-response-handler (320)
1481 "Identified user in WHOIS." nil
1482 (erc-display-message
1483 parsed 'notice 'active 's320
1484 ?n (second (erc-response.command-args parsed))))
1485
1486(define-erc-response-handler (321)
1487 "LIST header." nil
1488 (setq erc-channel-list nil)
1489 (erc-display-message parsed 'notice 'active 's321))
1490
1491(define-erc-response-handler (322)
1492 "LIST notice." nil
1493 (let ((topic (erc-response.contents parsed)))
1494 (multiple-value-bind (channel num-users)
1495 (cdr (erc-response.command-args parsed))
1496 (add-to-list 'erc-channel-list (list channel))
1497 (erc-update-channel-topic channel topic)
1498 (erc-display-message
1499 parsed 'notice 'active 's322
1500 ?c channel ?u num-users ?t (or topic "")))))
1501
1502(define-erc-response-handler (324)
1503 "Channel or nick modes." nil
1504 (let ((channel (second (erc-response.command-args parsed)))
1505 (modes (mapconcat 'identity (cddr (erc-response.command-args parsed))
1506 " ")))
1507 (erc-set-modes channel modes)
1508 (erc-display-message
1509 parsed 'notice (erc-get-buffer channel proc)
1510 's324 ?c channel ?m modes)))
1511
1512(define-erc-response-handler (329)
1513 "Channel creation date." nil
1514 (let ((channel (second (erc-response.command-args parsed)))
1515 (time (erc-string-to-emacs-time
1516 (third (erc-response.command-args parsed)))))
1517 (erc-display-message
1518 parsed 'notice (erc-get-buffer channel proc)
1519 's329 ?c channel ?t (format-time-string "%A %Y/%m/%d %X" time))))
1520
1521(define-erc-response-handler (330)
1522 nil nil
1523 ;; FIXME: I don't know what the magic numbers mean. Mummy, make
1524 ;; the magic numbers go away.
1525 ;; No seriously, I have no clue about the format of this command,
1526 ;; and don't sit on Quakenet, so can't test. Originally we had:
1527 ;; nick == (aref parsed 3)
1528 ;; authaccount == (aref parsed 4)
1529 ;; authmsg == (aref parsed 5)
1530 ;; The guesses below are, well, just that. -- Lawrence 2004/05/10
1531 (let ((nick (second (erc-response.command-args parsed)))
1532 (authaccount (third (erc-response.command-args parsed)))
1533 (authmsg (erc-response.contents parsed)))
1534 (erc-display-message parsed 'notice 'active 's330
1535 ?n nick ?a authmsg ?i authaccount)))
1536
1537(define-erc-response-handler (331)
1538 "Channel topic." nil
1539 (let ((channel (second (erc-response.command-args parsed)))
1540 (topic (erc-response.contents parsed)))
1541 ;; FIXME: why don't we do anything with the topic? -- Lawrence 2004/05/10
1542 (erc-display-message parsed 'notice (erc-get-buffer channel proc)
1543 's331 ?c channel)))
1544
1545(define-erc-response-handler (332)
1546 "TOPIC notice." nil
1547 (let ((channel (second (erc-response.command-args parsed)))
1548 (topic (erc-response.contents parsed)))
1549 (erc-update-channel-topic channel topic)
1550 (erc-display-message parsed 'notice (erc-get-buffer channel proc)
1551 's332 ?c channel ?T topic)))
1552
1553(define-erc-response-handler (333)
1554 ;; Who set the topic, and when
1555 nil nil
1556 (multiple-value-bind (channel nick time)
1557 (cdr (erc-response.command-args parsed))
1558 (setq time (format-time-string "%T %Y/%m/%d"
1559 (erc-string-to-emacs-time time)))
1560 (erc-update-channel-topic channel
1561 (format "\C-o (%s, %s)" nick time)
1562 'append)
1563 (erc-display-message parsed 'notice (erc-get-buffer channel proc)
1564 's333 ?c channel ?n nick ?t time)))
1565
1566(define-erc-response-handler (341)
1567 "Let user know when an INVITE attempt has been sent successfully."
1568 nil
1569 (multiple-value-bind (nick channel)
1570 (cdr (erc-response.command-args parsed))
1571 (erc-display-message parsed 'notice (erc-get-buffer channel proc)
1572 's341 ?n nick ?c channel)))
1573
1574(define-erc-response-handler (352)
1575 "WHO notice." nil
1576 (multiple-value-bind (channel user host server nick away-flag)
1577 (cdr (erc-response.command-args parsed))
1578 (let ((full-name (erc-response.contents parsed))
1579 hopcount)
1580 (when (string-match "\\(^[0-9]+ \\)\\(.*\\)$" full-name)
1581 (setq hopcount (match-string 1 full-name))
1582 (setq full-name (match-string 2 full-name)))
1583 (erc-update-channel-member channel nick nick nil nil nil host
1584 user full-name)
1585 (erc-display-message parsed 'notice 'active 's352
1586 ?c channel ?n nick ?a away-flag
1587 ?u user ?h host ?f full-name))))
1588
1589(define-erc-response-handler (353)
1590 "NAMES notice." nil
1591 (let ((channel (third (erc-response.command-args parsed)))
1592 (users (erc-response.contents parsed)))
1593 (erc-with-buffer (channel proc)
1594 (erc-channel-receive-names users))
1595 (erc-display-message parsed 'notice (or (erc-get-buffer channel proc)
1596 'active)
1597 's353 ?c channel ?u users)))
1598
1599(define-erc-response-handler (366)
1600 "End of NAMES." nil
1601 (erc-with-buffer ((second (erc-response.command-args parsed)) proc)
1602 (erc-channel-end-receiving-names)))
1603
1604(define-erc-response-handler (367)
1605 "Channel ban list entries" nil
1606 (multiple-value-bind (channel banmask setter time)
1607 (cdr (erc-response.command-args parsed))
1608 (erc-display-message parsed 'notice 'active 's367
1609 ?c channel
1610 ?b banmask
1611 ?s setter
1612 ?t time)))
1613
1614(define-erc-response-handler (368)
1615 "End of channel ban list" nil
1616 (let ((channel (second (erc-response.command-args parsed))))
1617 (erc-display-message parsed 'notice 'active 's368
1618 ?c channel)))
1619
1620(define-erc-response-handler (379)
1621 "Forwarding to another channel." nil
1622 ;; FIXME: Yet more magic numbers in original code, I'm guessing this
1623 ;; command takes two arguments, and doesn't have any "contents". --
1624 ;; Lawrence 2004/05/10
1625 (multiple-value-bind (from to)
1626 (cdr (erc-response.command-args parsed))
1627 (erc-display-message parsed 'notice 'active
1628 's379 ?c from ?f to)))
1629
1630(define-erc-response-handler (391)
1631 "Server's time string" nil
1632 (erc-display-message
1633 parsed 'notice 'active
1634 's391 ?s (second (erc-response.command-args parsed))
1635 ?t (third (erc-response.command-args parsed))))
1636
1637(define-erc-response-handler (401)
1638 "No such nick/channel." nil
1639 (let ((nick/channel (second (erc-response.command-args parsed))))
1640 (when erc-whowas-on-nosuchnick
1641 (erc-log (format "cmd: WHOWAS: %s" nick/channel))
1642 (erc-server-send (format "WHOWAS %s 1" nick/channel)))
1643 (erc-display-message parsed '(notice error) 'active
1644 's401 ?n nick/channel)))
1645
1646(define-erc-response-handler (403)
1647 "No such channel." nil
1648 (erc-display-message parsed '(notice error) 'active
1649 's403 ?c (second (erc-response.command-args parsed))))
1650
1651(define-erc-response-handler (404)
1652 "Cannot send to channel." nil
1653 (erc-display-message parsed '(notice error) 'active
1654 's404 ?c (second (erc-response.command-args parsed))))
1655
1656
1657(define-erc-response-handler (405)
1658 ;; Can't join that many channels.
1659 nil nil
1660 (erc-display-message parsed '(notice error) 'active
1661 's405 ?c (second (erc-response.command-args parsed))))
1662
1663(define-erc-response-handler (406)
1664 ;; No such nick
1665 nil nil
1666 (erc-display-message parsed '(notice error) 'active
1667 's406 ?n (second (erc-response.command-args parsed))))
1668
1669(define-erc-response-handler (412)
1670 ;; No text to send
1671 nil nil
1672 (erc-display-message parsed '(notice error) 'active 's412))
1673
1674(define-erc-response-handler (421)
1675 ;; Unknown command
1676 nil nil
1677 (erc-display-message parsed '(notice error) 'active 's421
1678 ?c (second (erc-response.command-args parsed))))
1679
1680(define-erc-response-handler (432)
1681 ;; Bad nick.
1682 nil nil
1683 (erc-display-message parsed '(notice error) 'active 's432
1684 ?n (second (erc-response.command-args parsed))))
1685
1686(define-erc-response-handler (433)
1687 ;; Login-time "nick in use"
1688 nil nil
1689 (erc-nickname-in-use (second (erc-response.command-args parsed))
1690 "already in use"))
1691
1692(define-erc-response-handler (437)
1693 ;; Nick temporarily unavailable (IRCnet)
1694 nil nil
1695 (let ((nick/channel (second (erc-response.command-args parsed))))
1696 (unless (erc-channel-p nick/channel)
1697 (erc-nickname-in-use nick/channel "temporarily unavailable"))))
1698
1699(define-erc-response-handler (442)
1700 ;; Not on channel
1701 nil nil
1702 (erc-display-message parsed '(notice error) 'active 's442
1703 ?c (second (erc-response.command-args parsed))))
1704
1705(define-erc-response-handler (461)
1706 ;; Not enough params for command.
1707 nil nil
1708 (erc-display-message parsed '(notice error) 'active 's461
1709 ?c (second (erc-response.command-args parsed))
1710 ?m (erc-response.contents parsed)))
1711
1712(define-erc-response-handler (474)
1713 "Banned from channel errors" nil
1714 (erc-display-message parsed '(notice error) nil
1715 (intern (format "s%s"
1716 (erc-response.command parsed)))
1717 ?c (second (erc-response.command-args parsed))))
1718
1719(define-erc-response-handler (475)
1720 "Channel key needed." nil
1721 (erc-display-message parsed '(notice error) nil 's475
1722 ?c (second (erc-response.command-args parsed)))
1723 (when erc-prompt-for-channel-key
1724 (let ((channel (second (erc-response.command-args parsed)))
1725 (key (read-from-minibuffer
1726 (format "Channel %s is mode +k. Enter key (RET to cancel): "
1727 (second (erc-response.command-args parsed))))))
1728 (when (and key (> (length key) 0))
1729 (erc-cmd-JOIN channel key)))))
1730
1731(define-erc-response-handler (477)
1732 nil nil
1733 (let ((channel (second (erc-response.command-args parsed)))
1734 (message (erc-response.contents parsed)))
1735 (erc-display-message parsed 'notice (erc-get-buffer channel proc)
1736 (format "%s: %s" channel message))))
1737
1738(define-erc-response-handler (482)
1739 nil nil
1740 (let ((channel (second (erc-response.command-args parsed)))
1741 (message (erc-response.contents parsed)))
1742 (erc-display-message parsed '(error notice) 'active 's482
1743 ?c channel ?m message)))
1744
1745(define-erc-response-handler (431 445 446 451 462 463 464 465 481 483 484 485
1746 491 501 502)
1747 ;; 431 - No nickname given
1748 ;; 445 - SUMMON has been disabled
1749 ;; 446 - USERS has been disabled
1750 ;; 451 - You have not registered
1751 ;; 462 - Unauthorized command (already registered)
1752 ;; 463 - Your host isn't among the privileged
1753 ;; 464 - Password incorrect
1754 ;; 465 - You are banned from this server
1755 ;; 481 - Need IRCop privileges
1756 ;; 483 - You can't kill a server!
1757 ;; 484 - Your connection is restricted!
1758 ;; 485 - You're not the original channel operator
1759 ;; 491 - No O-lines for your host
1760 ;; 501 - Unknown MODE flag
1761 ;; 502 - Cannot change mode for other users
1762 nil nil
1763 (erc-display-error-notice
1764 parsed
1765 (intern (format "s%s" (erc-response.command parsed)))))
1766
1767;; FIXME: These are yet to be implemented, they're just stubs for now
1768;; -- Lawrence 2004/05/12
1769
1770;; response numbers left here for reference
1771
1772;; (define-erc-response-handler (323 364 365 381 382 392 393 394 395
1773;; 200 201 202 203 204 205 206 208 209 211 212 213
1774;; 214 215 216 217 218 219 241 242 243 244 249 261
1775;; 262 302 342 351 402 407 409 411 413 414 415
1776;; 423 424 436 441 443 444 467 471 472 473 KILL)
1777;; nil nil
1778;; (ignore proc parsed))
1779
1780(provide 'erc-backend)
1781
1782;;; erc-backend.el ends here
1783;; Local Variables:
1784;; indent-tabs-mode: nil
1785;; End:
1786
1787;; arch-tag: a64e6bb7-a780-4efd-8f98-083b18c7c84a