;; -*- emacs-lisp -*-
;;; erc-mst-banlist.el --- list bans

;; Copyright (C) 2003  Free Software Foundation, Inc.

;; Author: Mark Triggs <mst@dishevelled.net>

;; 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:

;; store ban lists

(add-hook 'erc-server-367-hook 'erc-store-banlist)
(add-hook 'erc-server-MODE-hook 'erc-parse-bans)

(add-hook 'erc-server-JOIN-hook
          (lambda (proc parsed)
            (let* ((chnl (aref parsed 2))
                   (sndr (erc-parse-user (aref parsed 1)))
                   (nick (nth 0 sndr)))

              (when (string= (erc-current-nick) nick)
                (erc-send-command (format "MODE %s b" chnl)))))
          t nil)


(defvar channel-ban-list nil "A list of bans seen for the current channel. Each
ban is represented by a dotted pair whose car is the nickname who set the ban,
and whose cdr is the ban mask.")

(make-variable-buffer-local 'channel-ban-list)


(defun erc-store-banlist (proc parsed)
  "Record the ban list for a channel"
  (let ((whoset (car (erc-parse-user (aref parsed 5))))
        (channel (aref parsed 3))
        (mask (aref parsed 4)))

    ;; Determine to which buffer the message corresponds
    (let ((buffer (erc-get-buffer channel proc)))
      (save-excursion
        (set-buffer buffer)
        (when (not (member `(,whoset . ,mask) channel-ban-list))
          (push `(,whoset . ,mask) channel-ban-list))))))


;; Keep track of bans as they are added and removed
(defun erc-parse-bans (proc parsed)
  "Check MODE commands for ban references and record them as appropriate"

  (let* ((tgt (aref parsed 2))
         (mode (mapconcat 'identity (delete* nil (subseq parsed 3)) " "))
         (sndr (erc-parse-user (aref parsed 1)))
         (nick (nth 0 sndr))
         (buffer (erc-get-buffer tgt proc)))

    (when buffer
      (save-excursion
        (set-buffer buffer)

        (when (string-match "^\\([+-]\\)b" mode)

          ;; This is a ban
          (cond
           ((string-match "^-" mode)
            ;; Remove the unbanned masks from the ban list
            (setq channel-ban-list
                  (remove-if
                   (lambda (y)
                     (member (upcase (cdr y))
                             (mapcar #'upcase (cdr (split-string mode)))))
                   channel-ban-list)))

           ((string-match "^+" mode)
            ;; Add the banned mask(s) to the ban list
            (mapc
             (lambda (mask)
               (when (not (member `(,nick . ,mask) channel-ban-list))
                 (push `(,nick . ,mask) channel-ban-list)))
             (cdr (split-string mode))))

           (t nil)))
        nil))))


(defun erc-cmd-LISTBANS ()
  "list all bans for the current channel"

  (cond
   ((not (erc-channel-p (erc-default-target))) t)
   ((null channel-ban-list)
    (erc-display-line (erc-make-notice (format "No bans for channel: %s\n"
                                               (erc-default-target)))
                      'active))
   (t
    (let ((separator (do ((i 0 (1+ i))
                          (j nil (concat j "=")))
                         ((> i fill-column) j))))

      (erc-display-line (erc-make-notice (format "Ban list for channel: %s\n"
                                                 (erc-default-target)))
                        'active)

      (erc-display-line (format "%s" separator) 'active)

      (erc-display-line
       (format (concat "%-" (number-to-string (/ fill-column 2)) "s "
                       "%" (number-to-string (/ fill-column 2)) "s")
               "Ban Mask" "Banned By")
       'active)

      (erc-display-line (format "%s" separator) 'active)

      (mapc
       (lambda (x)
         (erc-display-line
          (format (concat "%-" (number-to-string (/ fill-column 2)) "s"
                          "%" (number-to-string (/ fill-column 2)) "s")
                  (truncate-string (cdr x) (/ fill-column 2))
                  (truncate-string (car x) (/ fill-column 2)))
          'active))
       channel-ban-list)

      (erc-display-line (erc-make-notice "End of Ban List") 'active))))
  t)


(defun group-list (lst num)
  "Group 'lst' into sublists of length 'num'"

  (cond ((null lst) nil)
        ((null (nthcdr num lst)) (list lst))
        (t (cons (subseq lst 0 num) (group-list (nthcdr num lst) num)))))


(defun erc-cmd-MUB ()
  "Mass Unban"
  (let ((bans (mapcar (lambda (x) (cdr x)) channel-ban-list)))
    (when bans
      ;; Glob the bans into groups of three, and carry out the unban.
      ;; eg. /mode #foo -bbb a*!*@* b*!*@* c*!*@*
      (mapc
       (lambda (x)
         (erc-send-command
          (format "MODE %s -%s %s" (erc-default-target)
                  (make-string (length x) (string-to-char "b"))
                  (reduce (lambda (a &optional b)
                            (concat a (if (boundp 'b) (concat " " b)))) x))))
       (group-list bans 3))))

  t)

(provide 'erc-mst-banlist)

;;; erc-mst-banlist.el ends here
