;;; erc-nickserv.el --- Identify to NickServ

;; Copyright (C) 2002  Mario Lang

;; This file is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 2, or (at your option)
;; any later version.

;; This file is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.

;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs; see the file COPYING.  If not, write to
;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
;; Boston, MA 02111-1307, USA.

;;; Commentary:

;; Listens for notices from NickServ, and if it detects an identify
;; request, it will either prompt for the password in the minibuffer
;; or identify automatically depending upon how the variables
;; `erc-prompt-for-nickserv-password' and `erc-nickserv-passwords' are
;; set.

;; At this time ERC only recognizes the official nickname services on the
;; networks Dalnet, GalaxyNet, Freenode, Slashnet and BrasNET.

;; Usage:
;;
;; Put into your .emacs:
;;
;; (require 'erc-nickserv)
;; (erc-nickserv-mode 1)

;;; Code:

(require 'erc)

(defconst erc-nickserv-version "Revision: 1.13"
  "ERC NickServ revision.")

;; Customization:

(defgroup erc-services nil
  "Configuration for IRC services.

On some networks, there exists a special type of automated irc bot,
called Services.  Those usually allow you to register your nickname,
post/read memos to other registered users who are currently offline,
and do various other things.

This group allows you to set variables to somewhat automate
communication with those Services."
  :group 'erc)

;;;###autoload (autoload 'erc-services-mode "erc-nickserv" nil t)
(define-erc-module services nickserv
  "This mode automates communication with services."
  ((add-hook 'erc-server-NOTICE-hook
	     'erc-nickserv-identify-autodetect))
  ((remove-hook 'erc-server-NOTICE-hook
		'erc-nickserv-identify-autodetect)))

(defcustom erc-prompt-for-nickserv-password t
  "Ask for the NickServ password when a NickServ identify request has been
detected.  At this time ERC recognizes NickServ on the networks
Open Projects, DalNet, Slashnet and GalaxyNet.

If both this and `erc-nickserv-passwords' are nil, ERC will not check for
NickServ identify requests, and you can identify yourself manually."
  :group 'erc-services
  :type 'boolean)

(defcustom erc-nickserv-passwords nil
  "Passwords used when identifying oneself to NickServ (not necessary).

Example of use:
  (setq erc-nickserv-passwords
        '((freenode ((\"nick-one\" . \"password\")
                     (\"nick-two\" . \"password\")))
          (dalnet ((\"nick\" . \"password\")))))

If both this and `erc-prompt-for-nickserv-password' are nil, ERC will not
check for NickServ identify requests, and you can identify yourself manually."
  :group 'erc-services
  :type '(repeat
	  (list :tag "Network"
		(choice (const freenode)
			(const dalnet)
			(const galaxynet)
			(const slashdot)
                        (const brasnet)
			(const iip))
		(repeat (cons :tag "Identity"
			      (string :tag "Nick")
			      (string :tag "Password"))))))

;; Variables:

(defvar erc-nickserv-alist
  '((dalnet
     "NickServ!service@dal.net"
     "/msg\\s-NickServ@services.dal.net\\s-IDENTIFY\\s-<password>"
     "NickServ@services.dal.net"
     "IDENTIFY"
     nil)
    (freenode
     "NickServ!NickServ@services."
     "/msg\\s-NickServ\\s-IDENTIFY\\s-<password>"
     "NickServ"
     "IDENTIFY"
     nil)
    (galaxynet
     "NS!nickserv@galaxynet.org"
     "Please\\s-change\\s-nicks\\s-or\\s-authenticate."
     "NS@services.galaxynet.org"
     "AUTH"
     t)
    (slashnet
     "NickServ!services@services.slashnet.org"
     "/msg\\s-NickServ\\s-IDENTIFY\\s-password"
     "NickServ@services.slashnet.org"
     "IDENTIFY"
     nil)
    (iip
     "Trent@anon.iip"
     "type\\s-/squery\\s-Trent\\s-identify\\s-<password>"
     "Trent@anon.iip"
     "IDENTIFY"
     nil
     "SQUERY")
    (brasnet
     "NickServ!services@brasnet.org"
     "/NickServ\\s-IDENTIFY\\s-senha"
     "NickServ"
     "IDENTIFY"
     nil
     "")
     (austnet "NickOP!service@austnet.org"
      "/msg\\s-NickOP@austnet.org\\s-identify\\s-<password>"
      "nickop@austnet.org"
      "identify"
      nil))
   "Alist of NickServer details, sorted by network.
Every element in the list has the form
  \(SYMBOL NICKSERV REGEXP NICK KEYWORD USE-CURRENT ANSWER)

SYMBOL is a network identifier, a symbol, as used in `erc-networks'.
NICKSERV is the description of the nickserv in the form nick!user@host.
REGEXP is a regular expression matching the message from nickserv.
NICK is nickserv's nickname.  Use nick@server where necessary/possible.
KEYWORD is the keyword to use in the reply message to identify yourself.
USE-CURRENT indicates whether the current nickname must be used when
  identifying.
ANSWER is the command to use for the answer.  The default is 'privmsg.
  This last element is optional.")

;; Functions:

(defun erc-nickserv-identify-autodetect (proc parsed)
  "Check for a NickServ identify request everytime a notice is sent to this
user.  Make sure it is the real NickServ for this network and that it has
specifically asked the user to IDENTIFY.
If `erc-prompt-for-nickserv-password' is non-nil, prompt the user for the
password for this nickname, otherwise try to send it automatically."
  (unless (and (null erc-nickserv-passwords)
               (null erc-prompt-for-nickserv-password))
    (let* ((network (erc-current-network))
           (nickserv (nth 1 (assoc network erc-nickserv-alist)))
           (identify-regex (nth 2 (assoc network erc-nickserv-alist)))
           (sspec (aref parsed 1))
           (nick (aref parsed 2))
           (msg (aref parsed 3)))
      ;; continue only if we're sure it's the real nickserv for this network
      ;; and it's asked us to identify
      (when (and nickserv (equal sspec nickserv)
                 (string-match identify-regex msg))
        (erc-log (format "NickServ IDENTIFY request detected"))
        (if erc-prompt-for-nickserv-password
            (call-interactively 'erc-nickserv-identify)
          (when erc-nickserv-passwords
            (erc-nickserv-identify
             (cdr (assoc nick
                         (nth 1 (assoc network
                                       erc-nickserv-passwords)))))))
	nil))))

(defun erc-nickserv-identify (&optional password)
  "Send a \"identify <PASSWORD>\" to NickServ.
When called interactively, read the password using `read-passwd'."
  (interactive
   (list (read-passwd (format "Password for %S: " (erc-current-network)))))
  (let* ((erc-auto-discard-away nil)
         (network (erc-current-network))
         (nickserv-info (assoc network erc-nickserv-alist))
         (nickserv (or (nth 3 nickserv-info) "NickServ"))
         (identify-word (or (nth 4 nickserv-info) "IDENTIFY"))
         (nick (if (nth 5 nickserv-info)
                   (concat (erc-current-nick) " ")
                 ""))
	 (msgtype (or (nth 6 nickserv-info) "PRIVMSG")))
    (erc-message msgtype
		 (concat nickserv " " identify-word " " nick password))))
;: TODO: change this to erc-determine-current-network
;;       make a (buffer local) variable `erc-current-network'
;;       set `erc-current-network' when we set `erc-announced-server-name'
;; this is still a temporary solution and should be part of erc-nets.el

(defvar erc-networks
  '(("dal.net" dalnet)
    ("galaxynet.org" galaxynet)
    ("freenode.net" freenode)
    ("slashnet.org" slashnet)
    ("brasnet.org" brasnet)
    ("austnet.org" austnet))
  "Alist network names to nickserv server used.
The nickserv servers used are specified in `erc-nickserv-alist'.")

(defun erc-current-network ()
  "Parse the current server to determine the network, if possible"
  ;; get the last two parts of the server's domain
  ;; check which network it is by matching the last part of the domain
  (when erc-announced-server-name
    (if (string= erc-announced-server-name "anon.iip")
	'iip
      (string-match "\\.\\(.*\\)$" erc-announced-server-name)
      (let* ((domain erc-announced-server-name)
	     (server (downcase (match-string 1 domain)))
	     (network (nth 1 (assoc server erc-networks))))
	(or network 'unknown)))))

(provide 'erc-nickserv)

;;; erc-nickserve.el ends here
