;;; rcirc.el --- default, simple IRC client.
-;; Copyright (C) 2005-2011
-;; Free Software Foundation, Inc.
+;; Copyright (C) 2005-2011 Free Software Foundation, Inc.
;; Author: Ryan Yeske <rcyeske@gmail.com>
;; Maintainers: Ryan Yeske <rcyeske@gmail.com>,
-;; Deniz Dogan <deniz.a.m.dogan@gmail.com>
+;; Deniz Dogan <deniz@dogan.se>
;; Keywords: comm
;; This file is part of GNU Emacs.
:group 'applications)
(defcustom rcirc-server-alist
- '(("irc.freenode.net" :channels ("#rcirc")))
+ '(("irc.freenode.net" :channels ("#rcirc")
+ ;; Don't use the TLS port by default, in case gnutls is not available.
+ ;; :port 7000 :encryption tls
+ ))
"An alist of IRC connections to establish when running `rcirc'.
Each element looks like (SERVER-NAME PARAMETERS).
VALUE must be a list of strings describing which channels to join
when connecting to this server. If absent, no channels will be
-connected to automatically."
+connected to automatically.
+
+`:encryption'
+
+VALUE must be `plain' (the default) for unencrypted connections, or `tls'
+for connections using SSL/TLS."
:type '(alist :key-type string
- :value-type (plist :options ((:nick string)
- (:port integer)
- (:user-name string)
- (:password string)
- (:full-name string)
- (:channels (repeat string)))))
+ :value-type (plist :options
+ ((:nick string)
+ (:port integer)
+ (:user-name string)
+ (:password string)
+ (:full-name string)
+ (:channels (repeat string))
+ (:encryption (choice (const tls)
+ (const plain))))))
:group 'rcirc)
(defcustom rcirc-default-port 6667
`nickserv': NICK PASSWORD [NICKSERV-NICK]
`chanserv': NICK CHANNEL PASSWORD
`bitlbee': NICK PASSWORD
+ `quakenet': ACCOUNT PASSWORD
Examples:
((\"freenode\" nickserv \"bob\" \"p455w0rd\")
(\"freenode\" chanserv \"bob\" \"#bobland\" \"passwd99\")
(\"bitlbee\" bitlbee \"robert\" \"sekrit\")
- (\"dal.net\" nickserv \"bob\" \"sekrit\" \"NickServ@services.dal.net\"))"
+ (\"dal.net\" nickserv \"bob\" \"sekrit\" \"NickServ@services.dal.net\")
+ (\"quakenet.org\" quakenet \"bobby\" \"sekrit\"))"
:type '(alist :key-type (string :tag "Server")
:value-type (choice (list :tag "NickServ"
(const nickserv)
(list :tag "BitlBee"
(const bitlbee)
(string :tag "Nick")
- (string :tag "Password"))))
+ (string :tag "Password"))
+ (list :tag "QuakeNet"
+ (const quakenet)
+ (string :tag "Account")
+ (string :tag "Password"))))
:group 'rcirc)
(defcustom rcirc-auto-authenticate-flag t
:type 'boolean
:group 'rcirc)
+(defcustom rcirc-authenticate-before-join t
+ "*Non-nil means authenticate to services before joining channels.
+Currently only works with NickServ on some networks."
+ :version "24.1"
+ :type 'boolean
+ :group 'rcirc)
+
(defcustom rcirc-prompt "> "
"Prompt string to use in IRC buffers.
:type 'hook
:group 'rcirc)
+(defvar rcirc-authenticated-hook nil
+ "Hook run after successfully authenticated.")
+
(defcustom rcirc-always-use-server-buffer-flag nil
"Non-nil means messages without a channel target will go to the server buffer."
:type 'boolean
:type 'function
:group 'rcirc)
+(defcustom rcirc-nick-completion-format "%s: "
+ "Format string to use in nick completions.
+
+The format string is only used when completing at the beginning
+of a line. The string is passed as the first argument to
+`format' with the nickname as the second argument."
+ :version "24.1"
+ :type 'string
+ :group 'rcirc)
+
(defvar rcirc-nick nil)
(defvar rcirc-prompt-start-marker nil)
(plist-get server-plist
:channels)
" "))
- "[, ]+" t)))
+ "[, ]+" t))
+ (encryption
+ (intern (completing-read "Encryption (default plain): "
+ '("plain" "tls")
+ nil t nil nil "plain"))))
(rcirc-connect server port nick user-name
rcirc-default-full-name
- channels password))
+ channels password encryption))
;; connect to servers in `rcirc-server-alist'
(let (connected-servers)
(dolist (c rcirc-server-alist)
(full-name (or (plist-get (cdr c) :full-name)
rcirc-default-full-name))
(channels (plist-get (cdr c) :channels))
- (password (plist-get (cdr c) :password)))
+ (password (plist-get (cdr c) :password))
+ (encryption (plist-get (cdr c) :encryption)))
(when server
(let (connected)
(dolist (p (rcirc-process-list))
(if (not connected)
(condition-case e
(rcirc-connect server port nick user-name
- full-name channels password)
+ full-name channels password encryption)
(quit (message "Quit connecting to %s" server)))
(with-current-buffer (process-buffer connected)
(setq connected-servers
(defvar rcirc-server nil) ; server provided by server
(defvar rcirc-server-name nil) ; server name given by 001 response
(defvar rcirc-timeout-timer nil)
+(defvar rcirc-user-authenticated nil)
(defvar rcirc-user-disconnect nil)
(defvar rcirc-connecting nil)
(defvar rcirc-process nil)
;;;###autoload
(defun rcirc-connect (server &optional port nick user-name
- full-name startup-channels password)
+ full-name startup-channels password encryption)
(save-excursion
(message "Connecting to %s..." server)
(let* ((inhibit-eol-conversion)
(user-name (or user-name rcirc-default-user-name))
(full-name (or full-name rcirc-default-full-name))
(startup-channels startup-channels)
- (process (make-network-process :name server :host server :service port-number)))
+ (process (open-network-stream
+ server nil server port-number
+ :type (or encryption 'plain))))
;; set up process
(set-process-coding-system process 'raw-text 'raw-text)
(switch-to-buffer (rcirc-generate-new-buffer-name process nil))
(rcirc-mode process nil)
(set-process-sentinel process 'rcirc-sentinel)
(set-process-filter process 'rcirc-filter)
- (make-local-variable 'rcirc-process)
- (setq rcirc-process process)
- (make-local-variable 'rcirc-server)
- (setq rcirc-server server)
- (make-local-variable 'rcirc-server-name)
- (setq rcirc-server-name server) ; update when we get 001 response
- (make-local-variable 'rcirc-buffer-alist)
- (setq rcirc-buffer-alist nil)
- (make-local-variable 'rcirc-nick-table)
- (setq rcirc-nick-table (make-hash-table :test 'equal))
- (make-local-variable 'rcirc-nick)
- (setq rcirc-nick nick)
- (make-local-variable 'rcirc-process-output)
- (setq rcirc-process-output nil)
- (make-local-variable 'rcirc-startup-channels)
- (setq rcirc-startup-channels startup-channels)
- (make-local-variable 'rcirc-last-server-message-time)
- (setq rcirc-last-server-message-time (current-time))
- (make-local-variable 'rcirc-timeout-timer)
- (setq rcirc-timeout-timer nil)
- (make-local-variable 'rcirc-user-disconnect)
- (setq rcirc-user-disconnect nil)
- (make-local-variable 'rcirc-connecting)
- (setq rcirc-connecting t)
+
+ (set (make-local-variable 'rcirc-process) process)
+ (set (make-local-variable 'rcirc-server) server)
+ (set (make-local-variable 'rcirc-server-name) server) ; Update when we get 001 response.
+ (set (make-local-variable 'rcirc-buffer-alist) nil)
+ (set (make-local-variable 'rcirc-nick-table)
+ (make-hash-table :test 'equal))
+ (set (make-local-variable 'rcirc-nick) nick)
+ (set (make-local-variable 'rcirc-process-output) nil)
+ (set (make-local-variable 'rcirc-startup-channels) startup-channels)
+ (set (make-local-variable 'rcirc-last-server-message-time)
+ (current-time))
+
+ (set (make-local-variable 'rcirc-timeout-timer) nil)
+ (set (make-local-variable 'rcirc-user-disconnect) nil)
+ (set (make-local-variable 'rcirc-user-authenticated) nil)
+ (set (make-local-variable 'rcirc-connecting) t)
(add-hook 'auto-save-hook 'rcirc-log-write)
;; identify
- (when password
+ (unless (zerop (length password))
(rcirc-send-string process (concat "PASS " password)))
(rcirc-send-string process (concat "NICK " nick))
(rcirc-send-string process (concat "USER " user-name
`(with-current-buffer rcirc-server-buffer
,@body))
+(defun rcirc-float-time ()
+ (if (featurep 'xemacs)
+ (time-to-seconds (current-time))
+ (float-time)))
+
(defun rcirc-keepalive ()
"Send keep alive pings to active rcirc processes.
Kill processes that have not received a server message since the
(mapc (lambda (process)
(with-rcirc-process-buffer process
(when (not rcirc-connecting)
- (rcirc-send-string process
- (format "PRIVMSG %s :\C-aKEEPALIVE %f\C-a"
- rcirc-nick
- (if (featurep 'xemacs)
- (time-to-seconds
- (current-time))
- (float-time)))))))
+ (rcirc-send-ctcp process
+ rcirc-nick
+ (format "KEEPALIVE %f"
+ (rcirc-float-time))))))
(rcirc-process-list))
;; no processes, clean up timer
(cancel-timer rcirc-keepalive-timer)
(defun rcirc-handler-ctcp-KEEPALIVE (process target sender message)
(with-rcirc-process-buffer process
- (setq header-line-format (format "%f" (- (if (featurep 'xemacs)
- (time-to-seconds
- (current-time))
- (float-time))
+ (setq header-line-format (format "%f" (- (rcirc-float-time)
(string-to-number message))))))
(defvar rcirc-debug-buffer " *rcirc debug*")
(mapconcat 'identity (cdr args) " ")
(not (member response rcirc-responses-no-activity))))
+(defun rcirc--connection-open-p (process)
+ (memq (process-status process) '(run open)))
+
(defun rcirc-send-string (process string)
"Send PROCESS a STRING plus a newline."
(let ((string (concat (encode-coding-string string rcirc-encode-coding-system)
"\n")))
- (unless (eq (process-status process) 'open)
+ (unless (rcirc--connection-open-p process)
(error "Network connection to %s is not open"
(process-name process)))
(rcirc-debug process string)
(process-send-string process string)))
+(defun rcirc-send-privmsg (process target string)
+ (rcirc-send-string process (format "PRIVMSG %s :%s" target string)))
+
+(defun rcirc-send-ctcp (process target request &optional args)
+ (let ((args (if args (concat " " args) "")))
+ (rcirc-send-privmsg process target
+ (format "\C-a%s%s\C-a" request args))))
+
(defun rcirc-buffer-process (&optional buffer)
"Return the process associated with channel BUFFER.
With no argument or nil as argument, use the current buffer."
(defun rcirc-completion-at-point ()
"Function used for `completion-at-point-functions' in `rcirc-mode'."
- (let* ((beg (save-excursion
- (if (re-search-backward " " rcirc-prompt-end-marker t)
- (1+ (point))
- rcirc-prompt-end-marker)))
- (table (if (and (= beg rcirc-prompt-end-marker)
- (eq (char-after beg) ?/))
- (delete-dups
- (nconc
- (sort (copy-sequence rcirc-client-commands) 'string-lessp)
- (sort (copy-sequence rcirc-server-commands) 'string-lessp)))
- (rcirc-channel-nicks (rcirc-buffer-process) rcirc-target))))
- (list beg (point) table)))
+ (and (rcirc-looking-at-input)
+ (let* ((beg (save-excursion
+ (if (re-search-backward " " rcirc-prompt-end-marker t)
+ (1+ (point))
+ rcirc-prompt-end-marker)))
+ (table (if (and (= beg rcirc-prompt-end-marker)
+ (eq (char-after beg) ?/))
+ (delete-dups
+ (nconc (sort (copy-sequence rcirc-client-commands)
+ 'string-lessp)
+ (sort (copy-sequence rcirc-server-commands)
+ 'string-lessp)))
+ (rcirc-channel-nicks (rcirc-buffer-process)
+ rcirc-target))))
+ (list beg (point) table))))
(defvar rcirc-completions nil)
(defvar rcirc-completion-start nil)
"Cycle through completions from list of nicks in channel or IRC commands.
IRC command completion is performed only if '/' is the first input char."
(interactive)
+ (unless (rcirc-looking-at-input)
+ (error "Point not located after rcirc prompt"))
(if (eq last-command this-command)
(setq rcirc-completions
(append (cdr rcirc-completions) (list (car rcirc-completions))))
(table (rcirc-completion-at-point)))
(setq rcirc-completion-start (car table))
(setq rcirc-completions
- (all-completions (buffer-substring rcirc-completion-start
- (cadr table))
- (nth 2 table)))))
+ (and rcirc-completion-start
+ (all-completions (buffer-substring rcirc-completion-start
+ (cadr table))
+ (nth 2 table))))))
(let ((completion (car rcirc-completions)))
(when completion
(delete-region rcirc-completion-start (point))
(insert
- (concat completion
- (cond
- ((= (aref completion 0) ?/) " ")
- ((= rcirc-completion-start rcirc-prompt-end-marker) ": ")
- (t "")))))))
+ (cond
+ ((= (aref completion 0) ?/) (concat completion " "))
+ ((= rcirc-completion-start rcirc-prompt-end-marker)
+ (format rcirc-nick-completion-format completion))
+ (t completion))))))
(defun set-rcirc-decode-coding-system (coding-system)
"Set the decode coding system used in this channel."
(interactive "zCoding system for incoming messages: ")
- (setq rcirc-decode-coding-system coding-system))
+ (set (make-local-variable 'rcirc-decode-coding-system) coding-system))
(defun set-rcirc-encode-coding-system (coding-system)
"Set the encode coding system used in this channel."
(interactive "zCoding system for outgoing messages: ")
- (setq rcirc-encode-coding-system coding-system))
-
-(defvar rcirc-mode-map (make-sparse-keymap)
+ (set (make-local-variable 'rcirc-encode-coding-system) coding-system))
+
+(defvar rcirc-mode-map
+ (let ((map (make-sparse-keymap)))
+ (define-key map (kbd "RET") 'rcirc-send-input)
+ (define-key map (kbd "M-p") 'rcirc-insert-prev-input)
+ (define-key map (kbd "M-n") 'rcirc-insert-next-input)
+ (define-key map (kbd "TAB") 'rcirc-complete)
+ (define-key map (kbd "C-c C-b") 'rcirc-browse-url)
+ (define-key map (kbd "C-c C-c") 'rcirc-edit-multiline)
+ (define-key map (kbd "C-c C-j") 'rcirc-cmd-join)
+ (define-key map (kbd "C-c C-k") 'rcirc-cmd-kick)
+ (define-key map (kbd "C-c C-l") 'rcirc-toggle-low-priority)
+ (define-key map (kbd "C-c C-d") 'rcirc-cmd-mode)
+ (define-key map (kbd "C-c C-m") 'rcirc-cmd-msg)
+ (define-key map (kbd "C-c C-r") 'rcirc-cmd-nick) ; rename
+ (define-key map (kbd "C-c C-o") 'rcirc-omit-mode)
+ (define-key map (kbd "C-c C-p") 'rcirc-cmd-part)
+ (define-key map (kbd "C-c C-q") 'rcirc-cmd-query)
+ (define-key map (kbd "C-c C-t") 'rcirc-cmd-topic)
+ (define-key map (kbd "C-c C-n") 'rcirc-cmd-names)
+ (define-key map (kbd "C-c C-w") 'rcirc-cmd-whois)
+ (define-key map (kbd "C-c C-x") 'rcirc-cmd-quit)
+ (define-key map (kbd "C-c TAB") ; C-i
+ 'rcirc-toggle-ignore-buffer-activity)
+ (define-key map (kbd "C-c C-s") 'rcirc-switch-to-server-buffer)
+ (define-key map (kbd "C-c C-a") 'rcirc-jump-to-first-unread-line)
+ map)
"Keymap for rcirc mode.")
-(define-key rcirc-mode-map (kbd "RET") 'rcirc-send-input)
-(define-key rcirc-mode-map (kbd "M-p") 'rcirc-insert-prev-input)
-(define-key rcirc-mode-map (kbd "M-n") 'rcirc-insert-next-input)
-(define-key rcirc-mode-map (kbd "TAB") 'rcirc-complete)
-(define-key rcirc-mode-map (kbd "C-c C-b") 'rcirc-browse-url)
-(define-key rcirc-mode-map (kbd "C-c C-c") 'rcirc-edit-multiline)
-(define-key rcirc-mode-map (kbd "C-c C-j") 'rcirc-cmd-join)
-(define-key rcirc-mode-map (kbd "C-c C-k") 'rcirc-cmd-kick)
-(define-key rcirc-mode-map (kbd "C-c C-l") 'rcirc-toggle-low-priority)
-(define-key rcirc-mode-map (kbd "C-c C-d") 'rcirc-cmd-mode)
-(define-key rcirc-mode-map (kbd "C-c C-m") 'rcirc-cmd-msg)
-(define-key rcirc-mode-map (kbd "C-c C-r") 'rcirc-cmd-nick) ; rename
-(define-key rcirc-mode-map (kbd "C-c C-o") 'rcirc-omit-mode)
-(define-key rcirc-mode-map (kbd "M-o") 'rcirc-omit-mode)
-(define-key rcirc-mode-map (kbd "C-c C-p") 'rcirc-cmd-part)
-(define-key rcirc-mode-map (kbd "C-c C-q") 'rcirc-cmd-query)
-(define-key rcirc-mode-map (kbd "C-c C-t") 'rcirc-cmd-topic)
-(define-key rcirc-mode-map (kbd "C-c C-n") 'rcirc-cmd-names)
-(define-key rcirc-mode-map (kbd "C-c C-w") 'rcirc-cmd-whois)
-(define-key rcirc-mode-map (kbd "C-c C-x") 'rcirc-cmd-quit)
-(define-key rcirc-mode-map (kbd "C-c TAB") ; C-i
- 'rcirc-toggle-ignore-buffer-activity)
-(define-key rcirc-mode-map (kbd "C-c C-s") 'rcirc-switch-to-server-buffer)
-(define-key rcirc-mode-map (kbd "C-c C-a") 'rcirc-jump-to-first-unread-line)
-
-(defvar rcirc-browse-url-map (make-sparse-keymap)
+(defvar rcirc-browse-url-map
+ (let ((map (make-sparse-keymap)))
+ (define-key map (kbd "RET") 'rcirc-browse-url-at-point)
+ (define-key map (kbd "<mouse-2>") 'rcirc-browse-url-at-mouse)
+ (define-key map [follow-link] 'mouse-face)
+ map)
"Keymap used for browsing URLs in `rcirc-mode'.")
-(define-key rcirc-browse-url-map (kbd "RET") 'rcirc-browse-url-at-point)
-(define-key rcirc-browse-url-map (kbd "<mouse-2>") 'rcirc-browse-url-at-mouse)
-(define-key rcirc-browse-url-map [follow-link] 'mouse-face)
-
(defvar rcirc-short-buffer-name nil
"Generated abbreviation to use to indicate buffer activity.")
(setq major-mode 'rcirc-mode)
(setq mode-line-process nil)
- (make-local-variable 'rcirc-input-ring)
- (setq rcirc-input-ring (make-ring rcirc-input-ring-size))
- (make-local-variable 'rcirc-server-buffer)
- (setq rcirc-server-buffer (process-buffer process))
- (make-local-variable 'rcirc-target)
- (setq rcirc-target target)
- (make-local-variable 'rcirc-topic)
- (setq rcirc-topic nil)
- (make-local-variable 'rcirc-last-post-time)
- (setq rcirc-last-post-time (current-time))
- (make-local-variable 'fill-paragraph-function)
- (setq fill-paragraph-function 'rcirc-fill-paragraph)
- (make-local-variable 'rcirc-recent-quit-alist)
- (setq rcirc-recent-quit-alist nil)
- (make-local-variable 'rcirc-current-line)
- (setq rcirc-current-line 0)
-
- (make-local-variable 'rcirc-short-buffer-name)
- (setq rcirc-short-buffer-name nil)
- (make-local-variable 'rcirc-urls)
- (setq use-hard-newlines t)
+ (set (make-local-variable 'rcirc-input-ring)
+ (make-ring rcirc-input-ring-size))
+ (set (make-local-variable 'rcirc-server-buffer) (process-buffer process))
+ (set (make-local-variable 'rcirc-target) target)
+ (set (make-local-variable 'rcirc-topic) nil)
+ (set (make-local-variable 'rcirc-last-post-time) (current-time))
+ (set (make-local-variable 'fill-paragraph-function) 'rcirc-fill-paragraph)
+ (set (make-local-variable 'rcirc-recent-quit-alist) nil)
+ (set (make-local-variable 'rcirc-current-line) 0)
+
+ (set (make-local-variable 'rcirc-short-buffer-name) nil)
+ (set (make-local-variable 'rcirc-urls) nil)
;; setup for omitting responses
(setq buffer-invisibility-spec '())
?. 'font-lock-keyword-face)))
(make-vector 3 glyph)))
- (make-local-variable 'rcirc-decode-coding-system)
- (make-local-variable 'rcirc-encode-coding-system)
(dolist (i rcirc-coding-system-alist)
(let ((chan (if (consp (car i)) (caar i) (car i)))
(serv (if (consp (car i)) (cdar i) "")))
(when (and (string-match chan (or target ""))
(string-match serv (rcirc-server-name process)))
- (setq rcirc-decode-coding-system (if (consp (cdr i)) (cadr i) (cdr i))
- rcirc-encode-coding-system (if (consp (cdr i)) (cddr i) (cdr i))))))
+ (set (make-local-variable 'rcirc-decode-coding-system)
+ (if (consp (cdr i)) (cadr i) (cdr i)))
+ (set (make-local-variable 'rcirc-encode-coding-system)
+ (if (consp (cdr i)) (cddr i) (cdr i))))))
;; setup the prompt and markers
- (make-local-variable 'rcirc-prompt-start-marker)
- (setq rcirc-prompt-start-marker (make-marker))
- (set-marker rcirc-prompt-start-marker (point-max))
- (make-local-variable 'rcirc-prompt-end-marker)
- (setq rcirc-prompt-end-marker (make-marker))
- (set-marker rcirc-prompt-end-marker (point-max))
+ (set (make-local-variable 'rcirc-prompt-start-marker) (point-max-marker))
+ (set (make-local-variable 'rcirc-prompt-end-marker) (point-max-marker))
(rcirc-update-prompt)
(goto-char rcirc-prompt-end-marker)
- (make-local-variable 'overlay-arrow-position)
- (setq overlay-arrow-position (make-marker))
- (set-marker overlay-arrow-position nil)
+
+ (set (make-local-variable 'overlay-arrow-position) (make-marker))
;; if the user changes the major mode or kills the buffer, there is
;; cleanup work to do
(or (eq (aref target 0) ?#)
(eq (aref target 0) ?&))))
+(defcustom rcirc-log-directory "~/.emacs.d/rcirc-log"
+ "Directory to keep IRC logfiles."
+ :type 'directory
+ :group 'rcirc)
+
+(defcustom rcirc-log-flag nil
+ "Non-nil means log IRC activity to disk.
+Logfiles are kept in `rcirc-log-directory'."
+ :type 'boolean
+ :group 'rcirc)
+
(defun rcirc-kill-buffer-hook ()
"Part the channel when killing an rcirc buffer."
(when (eq major-mode 'rcirc-mode)
(let ((buffer (current-buffer)))
(rcirc-clear-activity buffer)
(when (and (rcirc-buffer-process)
- (eq (process-status (rcirc-buffer-process)) 'open))
+ (rcirc--connection-open-p (rcirc-buffer-process)))
(with-rcirc-server-buffer
(setq rcirc-buffer-alist
(rassq-delete-all buffer rcirc-buffer-alist)))
(concat command " :" args)))))))
(defvar rcirc-parent-buffer nil)
+(make-variable-buffer-local 'rcirc-parent-buffer)
+(put 'rcirc-parent-buffer 'permanent-local t)
(defvar rcirc-window-configuration nil)
(defun rcirc-edit-multiline ()
"Move current edit to a dedicated buffer."
(and (> pos 0) (goto-char pos))
(message "Type C-c C-c to return text to %s, or C-c C-k to cancel" parent))))
-(defvar rcirc-multiline-minor-mode-map (make-sparse-keymap)
+(defvar rcirc-multiline-minor-mode-map
+ (let ((map (make-sparse-keymap)))
+ (define-key map (kbd "C-c C-c") 'rcirc-multiline-minor-submit)
+ (define-key map (kbd "C-x C-s") 'rcirc-multiline-minor-submit)
+ (define-key map (kbd "C-c C-k") 'rcirc-multiline-minor-cancel)
+ (define-key map (kbd "ESC ESC ESC") 'rcirc-multiline-minor-cancel)
+ map)
"Keymap for multiline mode in rcirc.")
-(define-key rcirc-multiline-minor-mode-map
- (kbd "C-c C-c") 'rcirc-multiline-minor-submit)
-(define-key rcirc-multiline-minor-mode-map
- (kbd "C-x C-s") 'rcirc-multiline-minor-submit)
-(define-key rcirc-multiline-minor-mode-map
- (kbd "C-c C-k") 'rcirc-multiline-minor-cancel)
-(define-key rcirc-multiline-minor-mode-map
- (kbd "ESC ESC ESC") 'rcirc-multiline-minor-cancel)
(define-minor-mode rcirc-multiline-minor-mode
"Minor mode for editing multiple lines in rcirc."
:keymap rcirc-multiline-minor-mode-map
:global nil
:group 'rcirc
- (make-local-variable 'rcirc-parent-buffer)
- (put 'rcirc-parent-buffer 'permanent-local t)
(setq fill-column rcirc-max-message-length))
(defun rcirc-multiline-minor-submit ()
(defvar rcirc-last-sender nil)
(make-variable-buffer-local 'rcirc-last-sender)
-(defcustom rcirc-log-directory "~/.emacs.d/rcirc-log"
- "Directory to keep IRC logfiles."
- :type 'directory
- :group 'rcirc)
-
-(defcustom rcirc-log-flag nil
- "Non-nil means log IRC activity to disk.
-Logfiles are kept in `rcirc-log-directory'."
- :type 'boolean
- :group 'rcirc)
-
(defcustom rcirc-omit-threshold 100
"Number of lines since last activity from a nick before `rcirc-omit-responses' are omitted."
:type 'integer
session.
If the returned filename is absolute (`file-name-absolute-p'
-returns true), then it is used as-is, otherwise the resulting
-file is put into `rcirc-log-directory'."
+returns t), then it is used as-is, otherwise the resulting file
+is put into `rcirc-log-directory'.
+
+The filename is then cleaned using `convert-standard-filename' to
+guarantee valid filenames for the current OS."
:group 'rcirc
:type 'function)
Log data is written to `rcirc-log-directory', except for
log-files with absolute names (see `rcirc-log-filename-function')."
(dolist (cell rcirc-log-alist)
- (let ((filename (expand-file-name (car cell) rcirc-log-directory))
+ (let ((filename (convert-standard-filename
+ (expand-file-name (car cell)
+ rcirc-log-directory)))
(coding-system-for-write 'utf-8))
(make-directory (file-name-directory filename) t)
(with-temp-buffer
(mapconcat 'identity sorted sep)))
\f
;;; activity tracking
-(defvar rcirc-track-minor-mode-map (make-sparse-keymap)
+(defvar rcirc-track-minor-mode-map
+ (let ((map (make-sparse-keymap)))
+ (define-key map (kbd "C-c C-@") 'rcirc-next-active-buffer)
+ (define-key map (kbd "C-c C-SPC") 'rcirc-next-active-buffer)
+ map)
"Keymap for rcirc track minor mode.")
-(define-key rcirc-track-minor-mode-map (kbd "C-c C-@") 'rcirc-next-active-buffer)
-(define-key rcirc-track-minor-mode-map (kbd "C-c C-SPC") 'rcirc-next-active-buffer)
-
;;;###autoload
(define-minor-mode rcirc-track-minor-mode
"Global minor mode for tracking activity in rcirc buffers."
(defun rcirc-switch-to-server-buffer ()
"Switch to the server buffer associated with current channel buffer."
(interactive)
+ (unless (buffer-live-p rcirc-server-buffer)
+ (error "No such buffer"))
(switch-to-buffer rcirc-server-buffer))
(defun rcirc-jump-to-first-unread-line ()
(when (not existing-buffer)
(rcirc-cmd-whois nick))))
-(defun-rcirc-command join (channel)
- "Join CHANNEL."
- (interactive "sJoin channel: ")
- (let ((buffer (rcirc-get-buffer-create process
- (car (split-string channel)))))
- (rcirc-send-string process (concat "JOIN " channel))
+(defun-rcirc-command join (channels)
+ "Join CHANNELS.
+CHANNELS is a comma- or space-separated string of channel names."
+ (interactive "sJoin channels: ")
+ (let* ((split-channels (split-string channels "[ ,]" t))
+ (buffers (mapcar (lambda (ch)
+ (rcirc-get-buffer-create process ch))
+ split-channels))
+ (channels (mapconcat 'identity split-channels ",")))
+ (rcirc-send-string process (concat "JOIN " channels))
(when (not (eq (selected-window) (minibuffer-window)))
- (switch-to-buffer buffer))))
+ (dolist (b buffers) ;; order the new channel buffers in the buffer list
+ (switch-to-buffer b)))))
;; TODO: /part #channel reason, or consider removing #channel altogether
(defun-rcirc-command part (channel)
(defun rcirc-cmd-ctcp (args &optional process target)
(if (string-match "^\\([^ ]+\\)\\s-+\\(.+\\)$" args)
- (let ((target (match-string 1 args))
- (request (match-string 2 args)))
- (rcirc-send-string process
- (format "PRIVMSG %s \C-a%s\C-a"
- target (upcase request))))
+ (let* ((target (match-string 1 args))
+ (request (upcase (match-string 2 args)))
+ (function (intern-soft (concat "rcirc-ctcp-sender-" request))))
+ (if (fboundp function) ;; use special function if available
+ (funcall function process target request)
+ (rcirc-send-ctcp process target request)))
(rcirc-print process (rcirc-nick process) "ERROR" nil
"usage: /ctcp NICK REQUEST")))
+(defun rcirc-ctcp-sender-PING (process target request)
+ "Send a CTCP PING message to TARGET."
+ (let ((timestamp (format "%.0f" (rcirc-float-time))))
+ (rcirc-send-ctcp process target "PING" timestamp)))
+
(defun rcirc-cmd-me (args &optional process target)
- (rcirc-send-string process (format "PRIVMSG %s :\C-aACTION %s\C-a"
- target args)))
+ (rcirc-send-ctcp process target "ACTION" args))
(defun rcirc-add-or-remove (set &rest elements)
(dolist (elt elements)
(rcirc-record-activity (current-buffer) 'nick)))))
(defun rcirc-markup-urls (sender response)
- (while (re-search-forward rcirc-url-regexp nil t)
+ (while (and rcirc-url-regexp ;; nil means disable URL catching
+ (re-search-forward rcirc-url-regexp nil t))
(let ((start (match-beginning 0))
(end (match-end 0)))
(rcirc-add-face start end 'rcirc-url)
(setq rcirc-server-name sender)
(setq rcirc-nick (car args))
(rcirc-update-prompt)
- (when rcirc-auto-authenticate-flag (rcirc-authenticate))
+ (if rcirc-auto-authenticate-flag
+ (if (and rcirc-authenticate-before-join
+ ;; We have to ensure that there's an authentication
+ ;; entry for that server. Else,
+ ;; rcirc-authenticated-hook won't be triggered, and
+ ;; autojoin won't happen at all.
+ (let (auth-required)
+ (dolist (s rcirc-authinfo auth-required)
+ (when (string-match (car s) rcirc-server-name)
+ (setq auth-required t)))))
+ (progn
+ (add-hook 'rcirc-authenticated-hook 'rcirc-join-channels-post-auth t t)
+ (rcirc-authenticate))
+ (rcirc-authenticate)
+ (rcirc-join-channels process rcirc-startup-channels))
+ (rcirc-join-channels process rcirc-startup-channels))))
+
+(defun rcirc-join-channels-post-auth (process)
+ "Join `rcirc-startup-channels' after authenticating."
+ (with-rcirc-process-buffer process
(rcirc-join-channels process rcirc-startup-channels)))
(defun rcirc-handler-PRIVMSG (process sender args text)
+ (rcirc-check-auth-status process sender args text)
(let ((target (if (rcirc-channel-p (car args))
(car args)
sender))
(rcirc-put-nick-channel process sender target rcirc-current-line))))
(defun rcirc-handler-NOTICE (process sender args text)
+ (rcirc-check-auth-status process sender args text)
(let ((target (car args))
(message (cadr args)))
(if (string-match "^\C-a\\(.*\\)\C-a$" message)
sender)))
message t))))
+(defun rcirc-check-auth-status (process sender args text)
+ "Check if the user just authenticated.
+If authenticated, runs `rcirc-authenticated-hook' with PROCESS as
+the only argument."
+ (with-rcirc-process-buffer process
+ (when (and (not rcirc-user-authenticated)
+ rcirc-authenticate-before-join
+ rcirc-auto-authenticate-flag)
+ (let ((target (car args))
+ (message (cadr args)))
+ (when (or
+ (and ;; nickserv
+ (string= sender "NickServ")
+ (string= target rcirc-nick)
+ (member message
+ (list
+ (format "You are now identified for \C-b%s\C-b." rcirc-nick)
+ "Password accepted - you are now recognized."
+ )))
+ (and ;; quakenet
+ (string= sender "Q")
+ (string= target rcirc-nick)
+ (string-match "\\`You are now logged in as .+\\.\\'" message)))
+ (setq rcirc-user-authenticated t)
+ (run-hook-with-args 'rcirc-authenticated-hook process)
+ (remove-hook 'rcirc-authenticated-hook 'rcirc-join-channels-post-auth t))))))
+
(defun rcirc-handler-WALLOPS (process sender args text)
(rcirc-print process sender "WALLOPS" sender (car args) t))
(rcirc-elapsed-lines process sender channel)))
(when (and last-activity-lines
(< last-activity-lines rcirc-omit-threshold))
- (rcirc-last-line process sender channel)))))
+ (rcirc-last-line process sender channel))))
+ ;; reset mode-line-process in case joining a channel with an
+ ;; already open buffer (after getting kicked e.g.)
+ (setq mode-line-process nil))
(rcirc-print process sender "JOIN" channel "")
(setq rcirc-nick-away-alist (cons (cons nick away-message)
rcirc-nick-away-alist))))))
+(defun rcirc-handler-317 (process sender args text)
+ "RPL_WHOISIDLE"
+ (let* ((nick (nth 1 args))
+ (idle-secs (string-to-number (nth 2 args)))
+ (idle-string
+ (if (< idle-secs most-positive-fixnum)
+ (format-seconds "%yy %dd %hh %mm %z%ss" idle-secs)
+ "a very long time"))
+ (signon-time (seconds-to-time (string-to-number (nth 3 args))))
+ (signon-string (format-time-string "%c" signon-time))
+ (message (format "%s idle for %s, signed on %s"
+ nick idle-string signon-string)))
+ (rcirc-print process sender "317" nil message t)))
+
(defun rcirc-handler-332 (process sender args text)
"RPL_TOPIC"
(let ((buffer (or (rcirc-get-buffer process (cadr args))
(defun rcirc-handler-353 (process sender args text)
"RPL_NAMREPLY"
- (let ((channel (caddr args)))
+ (let ((channel (nth 2 args))
+ (names (or (nth 3 args) "")))
(mapc (lambda (nick)
(rcirc-put-nick-channel process nick channel))
- (split-string (cadddr args) " " t))
+ (split-string names " " t))
+ ;; create a temporary buffer to insert the names into
+ ;; rcirc-handler-366 (RPL_ENDOFNAMES) will handle it
(with-current-buffer (rcirc-get-temp-buffer-create process channel)
(goto-char (point-max))
(insert (car (last args)) " "))))
(nick (caddr i))
(method (cadr i))
(args (cdddr i)))
- (when (and (string-match server rcirc-server)
- (string-match nick rcirc-nick))
- (cond ((equal method 'nickserv)
- (rcirc-send-string
- process
- (concat "PRIVMSG " (or (cadr args) "nickserv")
- " :identify " (car args))))
- ((equal method 'chanserv)
- (rcirc-send-string
- process
- (concat
- "PRIVMSG chanserv :identify "
- (car args) " " (cadr args))))
- ((equal method 'bitlbee)
- (rcirc-send-string
- process
- (concat "PRIVMSG &bitlbee :identify " (car args))))
- (t
- (message "No %S authentication method defined"
- method))))))))
+ (when (and (string-match server rcirc-server))
+ (if (and (memq method '(nickserv chanserv bitlbee))
+ (string-match nick rcirc-nick))
+ ;; the following methods rely on the user's nickname.
+ (case method
+ (nickserv
+ (rcirc-send-privmsg
+ process
+ (or (cadr args) "NickServ")
+ (concat "IDENTIFY " (car args))))
+ (chanserv
+ (rcirc-send-privmsg
+ process
+ "ChanServ"
+ (format "IDENTIFY %s %s" (car args) (cadr args))))
+ (bitlbee
+ (rcirc-send-privmsg
+ process
+ "&bitlbee"
+ (concat "IDENTIFY " (car args)))))
+ ;; quakenet authentication doesn't rely on the user's nickname.
+ ;; the variable `nick' here represents the Q account name.
+ (when (eq method 'quakenet)
+ (rcirc-send-privmsg
+ process
+ "Q@CServe.quakenet.org"
+ (format "AUTH %s %s" nick (car args))))))))))
(defun rcirc-handler-INVITE (process sender args text)
(rcirc-print process sender "INVITE" nil (mapconcat 'identity args " ") t))