erc-services.el 16 KB

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