guile feature
[bpt/emacs.git] / lisp / erc / erc-services.el
CommitLineData
c6b99621 1;;; erc-services.el --- Identify to NickServ
597993cf 2
ba318903 3;; Copyright (C) 2002-2004, 2006-2014 Free Software Foundation, Inc.
597993cf 4
34dc21db 5;; Maintainer: emacs-devel@gnu.org
df5d5f59 6
597993cf
MB
7;; This file is part of GNU Emacs.
8
4ee57b2a 9;; GNU Emacs is free software: you can redistribute it and/or modify
597993cf 10;; it under the terms of the GNU General Public License as published by
4ee57b2a
GM
11;; the Free Software Foundation, either version 3 of the License, or
12;; (at your option) any later version.
597993cf
MB
13
14;; GNU Emacs is distributed in the hope that it will be useful,
15;; but WITHOUT ANY WARRANTY; without even the implied warranty of
16;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17;; GNU General Public License for more details.
18
19;; You should have received a copy of the GNU General Public License
4ee57b2a 20;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
597993cf
MB
21
22;;; Commentary:
23
24;; There are two ways to go about identifying yourself automatically to
25;; NickServ with this module. The more secure way is to listen for identify
26;; requests from the user NickServ. Another way is to identify yourself to
27;; NickServ directly after a successful connection and every time you change
28;; your nickname. This method is rather insecure, though, because no checks
29;; are made to test if NickServ is the real NickServ for a given network or
30;; server.
31
1c36df97
MO
32;; As a default, ERC has the data for the official nickname services on
33;; the networks Austnet, BrasNET, Dalnet, freenode, GalaxyNet, GRnet,
34;; and Slashnet. You can add more by using M-x customize-variable RET
35;; erc-nickserv-alist.
597993cf
MB
36
37;; Usage:
38;;
39;; Put into your .emacs:
40;;
c6b99621 41;; (require 'erc-services)
597993cf
MB
42;; (erc-services-mode 1)
43;;
44;; Add your nickname and NickServ password to `erc-nickserv-passwords'.
45;; Using the freenode network as an example:
46;;
47;; (setq erc-nickserv-passwords '((freenode (("nickname" "password")))))
48;;
49;; The default automatic identification mode is autodetection of NickServ
50;; identify requests. Set the variable `erc-nickserv-identify-mode' if
51;; you'd like to change this behavior. You can also change the way
52;; automatic identification is handled by using:
53;;
54;; M-x erc-nickserv-identify-mode
55;;
56;; If you'd rather not identify yourself automatically but would like access
57;; to the functions contained in this file, just load this file without
58;; enabling `erc-services-mode'.
59;;
60
61;;; Code:
62
63(require 'erc)
c6b99621 64(require 'erc-networks)
19dc7206 65(eval-when-compile (require 'cl-lib))
597993cf
MB
66
67;; Customization:
68
69(defgroup erc-services nil
70 "Configuration for IRC services.
71
72On some networks, there exists a special type of automated irc bot,
73called Services. Those usually allow you to register your nickname,
74post/read memos to other registered users who are currently offline,
75and do various other things.
76
77This group allows you to set variables to somewhat automate
78communication with those Services."
79 :group 'erc)
80
6904f7fe
MB
81(defcustom erc-nickserv-identify-mode 'both
82 "The mode which is used when identifying to Nickserv.
83
84Possible settings are:.
85
86'autodetect - Identify when the real Nickserv sends an identify request.
87'nick-change - Identify when you log in or change your nickname.
88'both - Do the former if the network supports it, otherwise do the
89 latter.
90nil - Disables automatic Nickserv identification.
91
92You can also use M-x erc-nickserv-identify-mode to change modes."
93 :group 'erc-services
94 :type '(choice (const autodetect)
95 (const nick-change)
96 (const both)
97 (const nil))
98 :set (lambda (sym val)
99 (set sym val)
100 ;; avoid recursive load at startup
101 (when (featurep 'erc-services)
102 (erc-nickserv-identify-mode val))))
103
c6b99621 104;;;###autoload (autoload 'erc-services-mode "erc-services" nil t)
597993cf
MB
105(define-erc-module services nickserv
106 "This mode automates communication with services."
107 ((erc-nickserv-identify-mode erc-nickserv-identify-mode))
108 ((remove-hook 'erc-server-NOTICE-functions
109 'erc-nickserv-identify-autodetect)
110 (remove-hook 'erc-after-connect
111 'erc-nickserv-identify-on-connect)
112 (remove-hook 'erc-nick-changed-functions
1c36df97
MO
113 'erc-nickserv-identify-on-nick-change)
114 (remove-hook 'erc-server-NOTICE-functions
115 'erc-nickserv-identification-autodetect)))
597993cf
MB
116
117;;;###autoload
118(defun erc-nickserv-identify-mode (mode)
119 "Set up hooks according to which MODE the user has chosen."
120 (interactive
121 (list (intern (completing-read
122 "Choose Nickserv identify mode (RET to disable): "
10dc9f9e 123 '(("autodetect") ("nick-change") ("both")) nil t))))
1c36df97
MO
124 (add-hook 'erc-server-NOTICE-functions
125 'erc-nickserv-identification-autodetect)
5e56b3fb
MO
126 (unless erc-networks-mode
127 ;; Force-enable networks module, because we need it to set
128 ;; erc-network for us.
129 (erc-networks-enable))
597993cf
MB
130 (cond ((eq mode 'autodetect)
131 (setq erc-nickserv-identify-mode 'autodetect)
132 (add-hook 'erc-server-NOTICE-functions
133 'erc-nickserv-identify-autodetect)
134 (remove-hook 'erc-nick-changed-functions
135 'erc-nickserv-identify-on-nick-change)
136 (remove-hook 'erc-after-connect
137 'erc-nickserv-identify-on-connect))
138 ((eq mode 'nick-change)
139 (setq erc-nickserv-identify-mode 'nick-change)
140 (add-hook 'erc-after-connect
141 'erc-nickserv-identify-on-connect)
142 (add-hook 'erc-nick-changed-functions
143 'erc-nickserv-identify-on-nick-change)
144 (remove-hook 'erc-server-NOTICE-functions
145 'erc-nickserv-identify-autodetect))
10dc9f9e
MB
146 ((eq mode 'both)
147 (setq erc-nickserv-identify-mode 'both)
148 (add-hook 'erc-server-NOTICE-functions
149 'erc-nickserv-identify-autodetect)
150 (add-hook 'erc-after-connect
151 'erc-nickserv-identify-on-connect)
152 (add-hook 'erc-nick-changed-functions
153 'erc-nickserv-identify-on-nick-change))
597993cf
MB
154 (t
155 (setq erc-nickserv-identify-mode nil)
156 (remove-hook 'erc-server-NOTICE-functions
157 'erc-nickserv-identify-autodetect)
158 (remove-hook 'erc-after-connect
159 'erc-nickserv-identify-on-connect)
160 (remove-hook 'erc-nick-changed-functions
1c36df97
MO
161 'erc-nickserv-identify-on-nick-change)
162 (remove-hook 'erc-server-NOTICE-functions
163 'erc-nickserv-identification-autodetect))))
597993cf 164
597993cf
MB
165(defcustom erc-prompt-for-nickserv-password t
166 "Ask for the password when identifying to NickServ."
167 :group 'erc-services
168 :type 'boolean)
169
170(defcustom erc-nickserv-passwords nil
171 "Passwords used when identifying to NickServ automatically.
172
173Example of use:
174 (setq erc-nickserv-passwords
175 '((freenode ((\"nick-one\" . \"password\")
176 (\"nick-two\" . \"password\")))
177 (DALnet ((\"nick\" . \"password\")))))"
178 :group 'erc-services
179 :type '(repeat
180 (list :tag "Network"
181 (choice :tag "Network name"
ff59d266
MB
182 (const Ars)
183 (const Austnet)
184 (const Azzurra)
185 (const BitlBee)
186 (const BRASnet)
597993cf 187 (const DALnet)
ff59d266 188 (const freenode)
597993cf 189 (const GalaxyNet)
1c36df97 190 (const GRnet)
597993cf 191 (const iip)
ff59d266
MB
192 (const OFTC)
193 (const QuakeNet)
5e56b3fb 194 (const Rizon)
ff59d266 195 (const SlashNET)
597993cf
MB
196 (symbol :tag "Network name"))
197 (repeat :tag "Nickname and password"
198 (cons :tag "Identity"
199 (string :tag "Nick")
68d32a51
VD
200 (string :tag "Password"
201 :secret ?*))))))
597993cf
MB
202
203;; Variables:
204
205(defcustom erc-nickserv-alist
ff59d266
MB
206 '((Ars
207 nil nil
208 "Census"
1c36df97 209 "IDENTIFY" nil nil nil)
ff59d266
MB
210 (Austnet
211 "NickOP!service@austnet.org"
212 "/msg\\s-NickOP@austnet.org\\s-identify\\s-<password>"
213 "nickop@austnet.org"
1c36df97 214 "identify" nil nil nil)
ff59d266
MB
215 (Azzurra
216 "NickServ!service@azzurra.org"
217 "\ 2/ns\\s-IDENTIFY\\s-password\ 2"
218 "NickServ"
1c36df97 219 "IDENTIFY" nil nil nil)
ff59d266
MB
220 (BitlBee
221 nil nil
6904f7fe 222 "&bitlbee"
1c36df97 223 "identify" nil nil nil)
ff59d266
MB
224 (BRASnet
225 "NickServ!services@brasnet.org"
226 "\ 2/NickServ\\s-IDENTIFY\\s-\1fsenha\1f\ 2"
227 "NickServ"
1c36df97 228 "IDENTIFY" nil "" nil)
6904f7fe 229 (DALnet
597993cf
MB
230 "NickServ!service@dal.net"
231 "/msg\\s-NickServ@services.dal.net\\s-IDENTIFY\\s-<password>"
232 "NickServ@services.dal.net"
1c36df97 233 "IDENTIFY" nil nil nil)
597993cf
MB
234 (freenode
235 "NickServ!NickServ@services."
712e2b05
MO
236 ;; freenode also accepts a password at login, see the `erc'
237 ;; :password argument.
40ef8242 238 "This\\s-nickname\\s-is\\s-registered.\\s-Please\\s-choose"
597993cf 239 "NickServ"
1c36df97 240 "IDENTIFY" nil nil
40ef8242
MO
241 ;; See also the 901 response code message.
242 "You\\s-are\\s-now\\s-identified\\s-for\\s-")
597993cf
MB
243 (GalaxyNet
244 "NS!nickserv@galaxynet.org"
245 "Please\\s-change\\s-nicks\\s-or\\s-authenticate."
246 "NS@services.galaxynet.org"
1c36df97
MO
247 "AUTH" t nil nil)
248 (GRnet
249 "NickServ!service@irc.gr"
250 "This\\s-nickname\\s-is\\s-registered\\s-and\\s-protected."
251 "NickServ"
252 "IDENTIFY" nil nil
253 "Password\\s-accepted\\s--\\s-you\\s-are\\s-now\\s-recognized.")
597993cf
MB
254 (iip
255 "Trent@anon.iip"
256 "type\\s-/squery\\s-Trent\\s-identify\\s-<password>"
257 "Trent@anon.iip"
1c36df97 258 "IDENTIFY" nil "SQUERY" nil)
10dc9f9e
MB
259 (OFTC
260 "NickServ!services@services.oftc.net"
712e2b05
MO
261 ;; OFTC's NickServ doesn't ask you to identify anymore.
262 nil
10dc9f9e 263 "NickServ"
712e2b05
MO
264 "IDENTIFY" nil nil
265 "You\\s-are\\s-successfully\\s-identified\\s-as\\s-\ 2")
5e56b3fb
MO
266 (Rizon
267 "NickServ!service@rizon.net"
268 "This\\s-nickname\\s-is\\s-registered\\s-and\\s-protected."
269 "NickServ"
270 "IDENTIFY" nil nil
271 "Password\\s-accepted\\s--\\s-you\\s-are\\s-now\\s-recognized.")
ff59d266
MB
272 (QuakeNet
273 nil nil
274 "Q@CServe.quakenet.org"
1c36df97 275 "auth" t nil nil)
ff59d266
MB
276 (SlashNET
277 "NickServ!services@services.slashnet.org"
278 "/msg\\s-NickServ\\s-IDENTIFY\\s-\1fpassword"
279 "NickServ@services.slashnet.org"
1c36df97 280 "IDENTIFY" nil nil nil))
597993cf
MB
281 "Alist of NickServer details, sorted by network.
282Every element in the list has the form
1c36df97 283 \(SYMBOL NICKSERV REGEXP NICK KEYWORD USE-CURRENT ANSWER SUCCESS-REGEXP)
597993cf
MB
284
285SYMBOL is a network identifier, a symbol, as used in `erc-networks-alist'.
286NICKSERV is the description of the nickserv in the form nick!user@host.
287REGEXP is a regular expression matching the message from nickserv.
288NICK is nickserv's nickname. Use nick@server where necessary/possible.
289KEYWORD is the keyword to use in the reply message to identify yourself.
290USE-CURRENT indicates whether the current nickname must be used when
291 identifying.
292ANSWER is the command to use for the answer. The default is 'privmsg.
1c36df97
MO
293SUCCESS-REGEXP is a regular expression matching the message nickserv
294 sends when you've successfully identified.
295The last two elements are optional."
597993cf
MB
296 :group 'erc-services
297 :type '(repeat
298 (list :tag "Nickserv data"
299 (symbol :tag "Network name")
ff59d266
MB
300 (choice (string :tag "Nickserv's nick!user@host")
301 (const :tag "No message sent by Nickserv" nil))
302 (choice (regexp :tag "Identify request sent by Nickserv")
303 (const :tag "No message sent by Nickserv" nil))
597993cf
MB
304 (string :tag "Identify to")
305 (string :tag "Identify keyword")
306 (boolean :tag "Use current nick in identify message?")
307 (choice :tag "Command to use (optional)"
308 (string :tag "Command")
5cb9f674
CY
309 (const :tag "No special command necessary" nil))
310 (choice :tag "Detect Success"
311 (regexp :tag "Pattern to match")
312 (const :tag "Do not try to detect success" nil)))))
313
597993cf 314
6904f7fe
MB
315(defsubst erc-nickserv-alist-sender (network &optional entry)
316 (nth 1 (or entry (assoc network erc-nickserv-alist))))
317
318(defsubst erc-nickserv-alist-regexp (network &optional entry)
319 (nth 2 (or entry (assoc network erc-nickserv-alist))))
320
321(defsubst erc-nickserv-alist-nickserv (network &optional entry)
322 (nth 3 (or entry (assoc network erc-nickserv-alist))))
323
324(defsubst erc-nickserv-alist-ident-keyword (network &optional entry)
325 (nth 4 (or entry (assoc network erc-nickserv-alist))))
326
327(defsubst erc-nickserv-alist-use-nick-p (network &optional entry)
328 (nth 5 (or entry (assoc network erc-nickserv-alist))))
329
330(defsubst erc-nickserv-alist-ident-command (network &optional entry)
331 (nth 6 (or entry (assoc network erc-nickserv-alist))))
332
1c36df97
MO
333(defsubst erc-nickserv-alist-identified-regexp (network &optional entry)
334 (nth 7 (or entry (assoc network erc-nickserv-alist))))
335
597993cf
MB
336;; Functions:
337
1c36df97
MO
338(defcustom erc-nickserv-identified-hook nil
339 "Run this hook when NickServ acknowledged successful identification.
340Hooks are called with arguments (NETWORK NICK)."
341 :group 'erc-services
342 :type 'hook)
343
344(defun erc-nickserv-identification-autodetect (proc parsed)
345 "Check for NickServ's successful identification notice.
346Make sure it is the real NickServ for this network and that it has
347specifically confirmed a successful identification attempt.
348If this is the case, run `erc-nickserv-identified-hook'."
349 (let* ((network (erc-network))
350 (sender (erc-nickserv-alist-sender network))
351 (success-regex (erc-nickserv-alist-identified-regexp network))
352 (sspec (erc-response.sender parsed))
353 (nick (car (erc-response.command-args parsed)))
354 (msg (erc-response.contents parsed)))
355 ;; continue only if we're sure it's the real nickserv for this network
356 ;; and it's told us we've successfully identified
357 (when (and sender (equal sspec sender)
712e2b05 358 success-regex
1c36df97
MO
359 (string-match success-regex msg))
360 (erc-log "NickServ IDENTIFY success notification detected")
361 (run-hook-with-args 'erc-nickserv-identified-hook network nick)
362 nil)))
363
597993cf 364(defun erc-nickserv-identify-autodetect (proc parsed)
712e2b05
MO
365 "Identify to NickServ when an identify request is received.
366Make sure it is the real NickServ for this network.
597993cf
MB
367If `erc-prompt-for-nickserv-password' is non-nil, prompt the user for the
368password for this nickname, otherwise try to send it automatically."
369 (unless (and (null erc-nickserv-passwords)
370 (null erc-prompt-for-nickserv-password))
371 (let* ((network (erc-network))
6904f7fe
MB
372 (sender (erc-nickserv-alist-sender network))
373 (identify-regex (erc-nickserv-alist-regexp network))
597993cf
MB
374 (sspec (erc-response.sender parsed))
375 (nick (car (erc-response.command-args parsed)))
376 (msg (erc-response.contents parsed)))
377 ;; continue only if we're sure it's the real nickserv for this network
378 ;; and it's asked us to identify
6904f7fe 379 (when (and sender (equal sspec sender)
712e2b05 380 identify-regex
597993cf
MB
381 (string-match identify-regex msg))
382 (erc-log "NickServ IDENTIFY request detected")
383 (erc-nickserv-call-identify-function nick)
384 nil))))
385
386(defun erc-nickserv-identify-on-connect (server nick)
387 "Identify to Nickserv after the connection to the server is established."
10dc9f9e
MB
388 (unless (or (and (null erc-nickserv-passwords)
389 (null erc-prompt-for-nickserv-password))
390 (and (eq erc-nickserv-identify-mode 'both)
6904f7fe 391 (erc-nickserv-alist-regexp (erc-network))))
597993cf
MB
392 (erc-nickserv-call-identify-function nick)))
393
394(defun erc-nickserv-identify-on-nick-change (nick old-nick)
395 "Identify to Nickserv whenever your nick changes."
10dc9f9e
MB
396 (unless (or (and (null erc-nickserv-passwords)
397 (null erc-prompt-for-nickserv-password))
398 (and (eq erc-nickserv-identify-mode 'both)
6904f7fe 399 (erc-nickserv-alist-regexp (erc-network))))
597993cf
MB
400 (erc-nickserv-call-identify-function nick)))
401
402(defun erc-nickserv-call-identify-function (nickname)
403 "Call `erc-nickserv-identify' interactively or run it with NICKNAME's
404password.
405The action is determined by the value of `erc-prompt-for-nickserv-password'."
406 (if erc-prompt-for-nickserv-password
407 (call-interactively 'erc-nickserv-identify)
408 (when erc-nickserv-passwords
409 (erc-nickserv-identify
410 (cdr (assoc nickname
411 (nth 1 (assoc (erc-network)
412 erc-nickserv-passwords))))))))
413
414;;;###autoload
415(defun erc-nickserv-identify (password)
416 "Send an \"identify <PASSWORD>\" message to NickServ.
417When called interactively, read the password using `read-passwd'."
418 (interactive
419 (list (read-passwd
420 (format "NickServ password for %s on %s (RET to cancel): "
421 (erc-current-nick)
422 (or (and (erc-network)
423 (symbol-name (erc-network)))
424 "Unknown network")))))
425 (when (and password (not (string= "" password)))
426 (let* ((erc-auto-discard-away nil)
427 (network (erc-network))
428 (nickserv-info (assoc network erc-nickserv-alist))
6904f7fe
MB
429 (nickserv (or (erc-nickserv-alist-nickserv nil nickserv-info)
430 "NickServ"))
431 (identify-word (or (erc-nickserv-alist-ident-keyword
432 nil nickserv-info)
433 "IDENTIFY"))
434 (nick (if (erc-nickserv-alist-use-nick-p nil nickserv-info)
597993cf
MB
435 (concat (erc-current-nick) " ")
436 ""))
6904f7fe
MB
437 (msgtype (or (erc-nickserv-alist-ident-command nil nickserv-info)
438 "PRIVMSG")))
597993cf
MB
439 (erc-message msgtype
440 (concat nickserv " " identify-word " " nick password)))))
441
c6b99621 442(provide 'erc-services)
597993cf 443
c6b99621 444;;; erc-services.el ends here
597993cf
MB
445;;
446;; Local Variables:
447;; indent-tabs-mode: t
448;; tab-width: 8
449;; End:
450