;;; epop3manage.el --- manage mail on a POP3 mailbox
;;
;; Author:        Franklin Lee (lee.franklin@worldnet.att.net)
;; Created:       11/1998
;; Keywords:      mail pop3 epop3
;; Version:       0.4
;;
;; Copyright (C) 1997, 1998, 1999, 2000 Franklin Lee
;;
;; This program 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.
;;
;; epop3manage.el 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, 675 Mass Ave, Cambridge, MA 02139, USA.
;;

;;; Commentary:

;;; {{{

;;
;; Description
;; -----------
;;
;; 'epop3manage' provides an RMAIL-like interface for managing messages
;; on a POP3 server.  It's intended for use with epop3mail when the
;; 'leave-mail-on-server' mode is active.  Leave-mail-on-server can become
;; a problem after a while if there's no way to delete them.
;;
;; You can now selectively delete messages from your POP3 server
;; with epop3manage.
;;
;;
;; INSTALLATION:
;; =============
;;
;; (0) First, get epop3mail working.  See the installation notes
;; in epop3mail.el.  epop3manage relies on the data structures
;; and data gathered by epop3mail.
;;
;;
;; (1) In order to get epop3manage to work, you need to add the
;; elib-1.0 library to your load-path.  elib-1.0 is included in the
;; epop3mail distribution.  It is best to byte-compile those files.
;;
;;
;; (2) byte compile epop3manage.
;;
;;
;; (3) add the following line to your .emacs or initialization files:
;;
;;    (autoload 'epop3-manage-mailbox "epop3manage" "manage POP3 mailbox" t)
;;
;;
;; (4) to run epop3manage, type in
;;
;;    M-x epop3-manage-mailbox [ret]
;;
;;
;; (5) you may wish to alias this command.  I do it via
;;
;;    (defalias 'manage-mbx 'epop3-manage-mailbox)
;;
;; so I can use command-completion via [tab] after typing must 'mana'
;;
;;
;; (6) epop3manage will prompt you for the POP3 mailbox to manage.
;; If there is only one, just hit [tab] and then [ret].  epop3manage
;; will do completions to save typing.  Any mailbox which you've
;; defined in your mailbox list (see epop3mail documentation) starting
;; with "po:" will be in the set.
;;
;; Note: you should NOT enter the "po:" when telling epop3manage the
;; mailbox name.
;;
;; You should get a buffer which looks like an rmail-summary buffer.
;; Tag messages for deletion with 'd' or C-d, undo the tags with 'u',
;; and actually execute the deletions with 'x'.  You will be prompted
;; to confirm that you really want to delete.
;;
;;
;; (7) AS A SAFETY MEASURE, epop3manage will go through all the motions
;; of deleting mail from your POP3 mailbox, but won't until you
;; have set the following:
;;
;;     (setq epop3-manage-really-delete t)
;;
;; The default is 'nil' to give you a chance to get epop3manage working
;; and learn how it works without fear of nuking your mail until you
;; are ready.
;;
;;
;;
;; This has been tested on:
;; - FSF Emacs 19.34.6 on Solaris 2.5.1 and Windows 95
;; - FSF Emacs 20.3 on Win32
;; - FSF Emacs 20.4 on Win32 os/2 (thanks to jeremy bowen <jbowen@mas.co.nz>
;;
;; Bug reports and suggestions are welcome -- send them to Franklin Lee
;; <lee.franklin@worldnet.att.net> OR use the epop3-manage-submit-bug-report
;; function.
;;
;; THANKS TO:
;; ==========
;; - pdavis@bitstream.com for courageous beta testing
;;
;;
;;; History:
;;  --------
;;
;; 11/??/1998 versions 0.0000001 through 0.1: initial versions
;;
;; 02/15/1999 version 0.1: finally! got around to documenting this so
;; that it can be distributed.
;;
;; 07/05/1999 version 0.2: fiddling with defmacro and added 'X'
;; command (quits after deleting messages).
;;
;; 11/17/1999 version 0.3-beta:  started 'skip message' logic corresponding
;; with changes in epop3mail v 0.9.9-beta.
;;
;; 04/09/2000 version 0.4: release version corresponding to epop3mail
;; v 0.9.9.5
;;
;; --------------------------------------------------------------------------
;;
;; ======
;; TO-DO:
;; ======

;;; }}}

;;; Code:

(eval-when-compile (or (featurep 'cl) (load "cl")))
(eval-when-compile (require 'epop3mail))
(eval-when-compile (require 'epop3hash))
(eval-when-compile (require 'cookie))
(eval-when-compile (require 'string))

(or (featurep 'cl) (require 'cl))
(or (featurep 'cookie) (require 'cookie))
(or (featurep 'epopmail) (require 'epop3mail))
(load "string") ;; from elib-1.0

(defvar epop3-manage-debug t
  "*Set this to non-nil if debugging mail management via pop3.")

(defvar epop3-manage-really-delete nil
  "*Set this to non-nil if you REALLY WANT epop3manage TO DELETE THINGS.")

;;;--------------------------------------------------------------------------
;;  following are INTERNAL variables
;;;--------------------------------------------------------------------------

(defvar epop3-manage-mode-map nil
  "Keymap for the Epop3 mail management mode.")

(defvar epop3-manage-buffer nil
  "Buffer for bugtrack-summary -- NOT to be user changed!")

(defvar epop3-manage-collection nil
  "Internal elib object for holding the message summaries.")

(defvar epop3-manage-current-tin nil
  "Internal pointer for the current message 'tin' looked at.")

(defvar epop3-manage-current-po nil
  "Which post-office mailbox are we managing at the moment?.")

(defvar epop3-manage-was-biffing nil
  "Keep track of biffing status until we exit the mode.")

(defconst epop3-manage-buffer-name "*POP3-MANAGE*")

(defconst epop3-manage-summary-header
  "  # D ?  date        from             #chars subject"
  "Heading for the epop3 message management summary buffer.")

(defconst epop3-manage-version "0.3-beta" "Version of epop3manage.")



(defun epop3-manage-build-tables (po:user@host &optional savebiffstate)
  "Get stats from pop server for PO:USER@HOST and display it."
  (when epop3-manage-debug
    (message "starting epop3-manage...")
    (sit-for 1))

  (let ((tmpbuf (get-buffer-create "*pop3-manage-retr*"))
        process)

    (when savebiffstate
      (setq epop3-manage-was-biffing epop3-biffing))

    (multiple-value-bind (user host) (epop3-parse-po:user@host po:user@host)
      (setq process (epop3-open-server host pop3-port t))
      (when epop3-manage-was-biffing
        (epop3-stop-biff))

      (unwind-protect
          (save-excursion
            (epop3-init-tables (concat user "." host))
            (when epop3-manage-debug
              (switch-to-buffer (process-buffer process)))
            (epop3-login process user host epop3-quietly)

            (case (epop3-uidl-support user host)
              (no (error "Won't manage POP3 mailboxes without UIDL support"))
              (otherwise
               (epop3-get-message-numbers process user host epop3-quietly)))

            (unless epop3-quietly (message "list..."))
            (let ((listpairs (cdr (pop3-list process)))
                  (msgnums nil))

              (mapcar 'epop3-update-list-results listpairs)
              (setq msgnums (epop3-manage-get-message-numbers))

              (when msgnums
                (let ((msgsleft (1- (length msgnums))))
                  (mapc

;;; {{{ the main message retrieval lambda
                   (lambda (msgno)
                     ;; get the from, subject, date
                     (message
                      (format "getting mail headers for # %d; %d remaining"
                              msgno msgsleft))

                     (pop3-top process msgno 0 tmpbuf)
                     (epop3-update-uid-header-fields-from-buffer tmpbuf msgno)
                     (epop3-clear-buffer tmpbuf)
                     (decf msgsleft))
;;; }}}
                   msgnums))
                (epop3-save-uidls))))

        (save-excursion
          (let ((proc-buffer (process-buffer process)))
            (pop3-quit process)
            (unless epop3-manage-debug
              (kill-buffer tmpbuf)
              (kill-buffer proc-buffer))
;;;             (when biffing
;;;               (epop3-start-biff epop3-biff-interval t))
            ))))))

(defun epop3-manage-get-message-numbers ()
  "Return a sorted list of msg numbers to get mail management data.
Message numbers for messages we haven't seen or which have header data
that we don't know about qualify as eligible to be returned."
  (let ((tmplist '())
        (msgno nil)
        (gotten nil)
        (frm-p nil)
        (date-p nil)
        (onserver nil))
    (epop3-dohash (uid u-entry epop3-utab (sort tmplist '<))
      (setq msgno (epop3-uid-entry-msgno u-entry)
            gotten (epop3-uid-entry-gotten u-entry)
            frm-p (not (string= "" (epop3-uid-entry-from u-entry)))
            date-p (not (string= "" (epop3-uid-entry-date u-entry)))
            onserver (epop3-uid-entry-onserver u-entry))
      (when (and onserver
                 (> msgno 0)
                 (not (and frm-p date-p)))
        (push msgno tmplist)))))

(defvar epop3-manage-mode-map nil
  "Keymap for the Epop3 mail management mode.")

(unless epop3-manage-mode-map
  (setq epop3-manage-mode-map (make-sparse-keymap "Epop3 Manage"))
  (define-prefix-command 'epop3-manage-mode-prefix-map)
  (suppress-keymap epop3-manage-mode-map)
  (define-key epop3-manage-mode-map "\C-p" 'epop3-manage-prev-line)
  (define-key epop3-manage-mode-map "P" 'epop3-manage-prev-line)
  (define-key epop3-manage-mode-map "p" 'epop3-manage-prev-line)
  (define-key epop3-manage-mode-map [up] 'epop3-manage-prev-line)

  (define-key epop3-manage-mode-map "\C-n" 'epop3-manage-next-line)
  (define-key epop3-manage-mode-map "N" 'epop3-manage-next-line)
  (define-key epop3-manage-mode-map "n" 'epop3-manage-next-line)
  (define-key epop3-manage-mode-map [down] 'epop3-manage-next-line)

  (define-key epop3-manage-mode-map "d" 'epop3-manage-mark-for-delete-next)
  (define-key epop3-manage-mode-map "D" 'epop3-manage-mark-for-delete-next)

  (define-key epop3-manage-mode-map "\C-d" 'epop3-manage-mark-for-delete-prev)

  (define-key epop3-manage-mode-map "u" 'epop3-manage-unmark)
  (define-key epop3-manage-mode-map "U" 'epop3-manage-unmark)

  (define-key epop3-manage-mode-map "x" 'epop3-manage-execute-deletes)
  (define-key epop3-manage-mode-map "X" 'epop3-manage-execute-deletes-and-quit)

  (define-key epop3-manage-mode-map "q" 'epop3-manage-quit)
  (define-key epop3-manage-mode-map "Q" 'epop3-manage-quit)

  (define-key epop3-manage-mode-map "j" 'epop3-manage-goto-msgno)

  (define-key epop3-manage-mode-map "s" 'epop3-manage-toggle-skip-next)
  (define-key epop3-manage-mode-map "S" 'epop3-manage-toggle-skip-prev)

  (define-key epop3-manage-mode-map "g" 'epop3-manage-refresh)
  (define-key epop3-manage-mode-map "G" 'epop3-manage-refresh)
  (define-key epop3-manage-mode-map "r" 'epop3-manage-refresh)
  (define-key epop3-manage-mode-map "R" 'epop3-manage-refresh)

  (define-key epop3-manage-mode-map [linefeed] 'ignore)
  (define-key epop3-manage-mode-map [return] 'ignore)
  (define-key epop3-manage-mode-map [tab] 'ignore))

(defun epop3-manage-goto-msgno (msgno)
  "Docstring here...
Argument MSGNO ..."
  (interactive "P")
  (when msgno
    (setq msgno (prefix-numeric-value msgno))
    (let ((tin (car (collection-collect-tin epop3-manage-collection
                                            'epop3-manage-uentry-msgno-eq-p
                                            msgno))))
      (if tin
          (setq epop3-manage-current-tin
                (tin-goto epop3-manage-collection tin))))))

(defun epop3-manage-uentry-msgno-eq-p (uentry msgno)
  "Predicate returning 't' if UENTRY has MSGNO == msgno."
  (= (epop3-uid-entry-msgno uentry) msgno))

(defun epop3-manage-next-line (arg)
  "Go to the next line.
If a prefix argument is given, move by that many lines.
Argument ARG sadfk."
  (interactive "p")
  (setq epop3-manage-current-tin
        (tin-goto-next epop3-manage-collection (point) arg)))

(defun epop3-manage-prev-line (arg)
  "Go to the previous line.
If a prefix argument is given, move by that many lines.
Argument ARG asdfk."
  (interactive "p")
  (setq epop3-manage-current-tin
        (tin-goto-previous epop3-manage-collection (point) arg)))

(defun epop3-manage-mark-for-delete ()
  "Mark the current message for deletion from the server.
Do this only if it is not marked for skipping"
  (unless (collection-empty epop3-manage-collection)
    (let ((uentry
           (tin-cookie epop3-manage-collection epop3-manage-current-tin)))
      (unless (epop3-uid-entry-skipthis uentry)
        (setf (epop3-uid-entry-todelete uentry) t)
        (tin-invalidate epop3-manage-collection epop3-manage-current-tin)))))

(defun epop3-manage-mark-for-delete-next ()
  "Mark the current message for deletion from the server and move to next."
  (interactive)
  (epop3-manage-mark-for-delete)
  (epop3-manage-next-line 1))

(defun epop3-manage-mark-for-delete-prev ()
  "Mark the current message for deletion from the server and move to previous."
  (interactive)
  (epop3-manage-mark-for-delete)
  (epop3-manage-prev-line 1))

(defun epop3-manage-unmark ()
  "Remove all marks from the current message from those to be skipped/deleted."
  (interactive)
  (unless (collection-empty epop3-manage-collection)
    (let ((uentry
           (tin-cookie epop3-manage-collection epop3-manage-current-tin)))
      (setf (epop3-uid-entry-todelete uentry) nil)
      (setf (epop3-uid-entry-skipthis uentry) nil)
      (tin-invalidate epop3-manage-collection epop3-manage-current-tin))))

(defun epop3-manage-toggle-skip ()
  "Toggle the `skipthis' status of a message.
Do so only if it is not marked for delete."
  (interactive)
  (unless (collection-empty epop3-manage-collection)
    (let ((uentry
           (tin-cookie epop3-manage-collection epop3-manage-current-tin))
          (skip nil))
      (unless (epop3-uid-entry-todelete uentry)
        (setq skip (epop3-uid-entry-skipthis uentry))
        (setf (epop3-uid-entry-skipthis uentry) (not skip))
        (tin-invalidate epop3-manage-collection epop3-manage-current-tin)))))

(defun epop3-manage-toggle-skip-next ()
  "Toggle the skip status and move to next message."
  (interactive)
  (epop3-manage-toggle-skip)
  (epop3-manage-next-line 1))

(defun epop3-manage-toggle-skip-prev ()
  "Toggle the skip status and move to next message."
  (interactive)
  (epop3-manage-toggle-skip)
  (epop3-manage-prev-line 1))

(defun epop3-manage-get-msgnos-2-delete ()
  "Get the message numbers marked for deletion."
  (let ((tmplist '()))
    (epop3-dohash (u n epop3-utab (sort tmplist '<))
      (when (and (epop3-uid-entry-todelete n)
                 (not (zerop (epop3-uid-entry-msgno n))))
        (push (epop3-uid-entry-msgno n) tmplist)))))

(defmacro epop3-manage-do-the-deletes (msg &rest body)
  `(when (yes-or-no-p ,msg)
    (let ((msgs2del (epop3-manage-get-msgnos-2-delete)))
      (when msgs2del
        (multiple-value-bind (user host)
            (epop3-parse-po:user@host epop3-manage-current-po)
          (epop3-save-uidls)
          (epop3-manage-delete-messages user host msgs2del)
          (sit-for 1);; give the server time to catch its breath!
          ,@body)))))

(put 'epop3-manage-do-the-deletes 'common-lisp-indent-function 1)
(put 'epop3-manage-do-the-deletes 'lisp-indent-function 1)

(defun epop3-manage-execute-deletes ()
  "Effect the actual deletion of marked messages from the server."
  (interactive)
  (unless (collection-empty epop3-manage-collection)
    (when (epop3-manage-get-msgnos-2-delete)
      (epop3-manage-do-the-deletes "Really delete marked messages? "
        (epop3-manage-build-tables epop3-manage-current-po)
        (epop3-manage-create-summary)))))

(defun epop3-manage-execute-deletes-and-quit ()
  "Delete the marked messages from the server and then quit."
  (interactive)
  (unless (collection-empty epop3-manage-collection)
    (when (epop3-manage-get-msgnos-2-delete)
      (epop3-manage-do-the-deletes "Really delete marked messages and quit? "
        (epop3-manage-quit)))))

(defun epop3-manage-refresh ()
  "Re-poll the server and re-display the list of messages on the server."
  (interactive)
  (message "refreshing summary from mailbox...")
  (epop3-manage-build-tables epop3-manage-current-po)
  (epop3-manage-create-summary))

(defun epop3-manage-delete-messages (user host msgnums)
  "Actually delete the server messages for USER, HOST, and MSGNUMS.
Note: this only goes through the motions unless `epop3-manage-really-delete'
is non-nil."
  (when msgnums
    (when epop3-manage-debug
      (message "starting epop3-manage-delete-messages...")
      (sit-for 1))

    (let ((tmpbuf (get-buffer-create "*pop3-manage-retr*"))
;;;           (biffing epop3-biffing) ;; SAVE THIS STATE!!!!
          (process (epop3-open-server host pop3-port t)))

;;;       (when biffing
;;;         (epop3-stop-biff))

      (unwind-protect
          (save-excursion
            (when epop3-manage-debug
              (switch-to-buffer (process-buffer process)))
            (epop3-login process user host epop3-quietly)

            (when msgnums
              (let ((msgsleft (1- (length msgnums))))
                (mapc

;;; {{{ the main message deletion lambda
                 (lambda (msgno)
                   ;; get the from, subject, date
                   (message
                    (format "deleting msg # %d; %d remaining to delete"
                            msgno msgsleft))

                   (when epop3-manage-really-delete
                     (pop3-dele process msgno))

                   (decf msgsleft))
;;; }}}
                 msgnums))
              (message "deleting...done")))

      (save-excursion
        (let ((proc-buffer (process-buffer process)))
          (pop3-quit process)
          (unless epop3-manage-debug
            (kill-buffer tmpbuf)
            (kill-buffer proc-buffer))

          ;; don't restart the biffing but remember that we were doing it!
;;;           (setq epop3-biffing biffing)
          ))))))


(defun epop3-manage-mode ()
  "Mode for epop3 mailbox  management.

\\[epop3-manage-prev-line]  Move up
\\[epop3-manage-next-line]  Move down

\\[epop3-manage-mark-for-delete-next] Mark for deletion and move down
\\[epop3-manage-mark-for-delete-prev] Mark for deletion and move up

\\[epop3-manage-refresh] Refresh message headers from server

\\[epop3-manage-goto-msgno] with prefix, go to numbered message header

\\[epop3-manage-unmark-for-delete] Remove deletion mark on current line

\\[epop3-manage-execute-deletes] Actually delete marked messages from server

\\[epop3-manage-quit] Quit manage mode."
  (interactive)
  (kill-all-local-variables)
  (make-variable-buffer-local 'epop3-manage-collection)
  (setq truncate-lines t)
  (use-local-map epop3-manage-mode-map)
  (setq mode-name "epop3 manage")
  (setq major-mode 'epop3-manage-mode)
  (setq buffer-read-only t)
  (run-hooks 'epop3-manage-mode-hook))

(defun epop3-manage-uid-entry-pretty-print (u-entry)
  "Format U-ENTRY as a summary line."
  (let ((beg (point))
        (frmlength (length (epop3-uid-entry-from u-entry)))
        (from (epop3-uid-entry-from u-entry)))
    (insert
     (format
      "%4d%c %c %6s  %19s %8s %-s"
      (epop3-uid-entry-msgno u-entry)
      (if (epop3-uid-entry-todelete u-entry)
          ?D
        (if (epop3-uid-entry-skipthis u-entry)
            ?S
          ? ))
      (if (epop3-uid-entry-gotten u-entry) ?  ??)
      (epop3-uid-entry-date u-entry)
      (if (>= 19 frmlength) from (substring from 0 19))
      (format "[%d]" (epop3-uid-entry-nchars u-entry))
      (epop3-uid-entry-subj u-entry)))))

(defun epop3-manage-create-summary ()
  "Create the summary list of messages on the server."
  (message "Creating summary...")
  ;; Find or create a buffer for the summary and
  ;; Clear or create the cookie structure.
  (let ((sum-buffer nil))
    (cond
     ((or (not epop3-manage-collection)
          (null (collection-buffer epop3-manage-collection)))
      (setq sum-buffer (or (get-buffer epop3-manage-buffer-name)
                           (generate-new-buffer epop3-manage-buffer-name)))
      (epop3-clear-buffer sum-buffer)
      (setq epop3-manage-collection
            (collection-create sum-buffer
                               'epop3-manage-uid-entry-pretty-print
                               epop3-manage-summary-header "")))
     (t
      (setq sum-buffer (collection-buffer epop3-manage-collection))
      (collection-clear epop3-manage-collection)))

    ;; Set up the buffer in summary mode.
    ;;
    ;; the following "let" form weirdness is necessary for reasons I don't
    ;; quite understand about the elib package.  but without this "let"
    ;; form dance, it won't work!
    ;;
    (let ((handle epop3-manage-collection))
      (save-excursion
        (set-buffer sum-buffer)
        (epop3-manage-mode)
        (setq epop3-manage-collection handle)))

    ;; Create all the summary cookies.
    (epop3-dohash (uid u-entry epop3-utab)
      (when (and (epop3-uid-entry-onserver u-entry)
                 (< 0 (epop3-uid-entry-msgno u-entry)))
        (cookie-enter-last epop3-manage-collection u-entry)))

    (message "sorting summary...")
    (cookie-sort epop3-manage-collection
                 (function (lambda (x1 x2)
                             (< (epop3-uid-entry-msgno x1)
                                (epop3-uid-entry-msgno x2)))))

    (unless (collection-empty epop3-manage-collection)
      (setq epop3-manage-current-tin
            (tin-goto epop3-manage-collection
                      (tin-nth epop3-manage-collection 0))))
    (message "Creating summary...done")

    (set-window-start (display-buffer sum-buffer) 0)
    (setq epop3-manage-buffer sum-buffer)
    (switch-to-buffer epop3-manage-buffer)
    (setq truncate-lines t)
    (delete-other-windows)))

(defun epop3-manage-mailbox (po:user@host)
  "Manage a pop3 mailbox PO:USER@HOST.
This will prompt for a mailbox name in the form of mailbox@hostname.domain.
You do not need to add the 'po:' portion."
  (interactive (epop3-manage-read-mailbox-prompt "mailbox to manage: "
                                                 epop3-mailbox-list))
  (when (and po:user@host (< 0 (length po:user@host)))
    (setq epop3-manage-current-po po:user@host)
    (epop3-manage-build-tables po:user@host t)
    (epop3-manage-create-summary)))

(defun epop3-manage-extract-pos-from-mailbox-list (mboxlist)
  "Extracts only the po: entries from MBOXLIST."
  (let ((tlist '()))
    (dolist (s mboxlist (nreverse tlist))
      (if (string-match "po:" s)
          (push s tlist)))))

(defun epop3-manage-make-po:alist-from-mailbox-list (mboxlist)
  "Extract only POP3 mailboxes from MBOXLIST."
    (mapcar (function (lambda (s) (cons s s)))
            (epop3-manage-extract-pos-from-mailbox-list mboxlist)))


(defun epop3-manage-read-mailbox-prompt (prompt mbox-list)
  "Tbd.
Argument PROMPT tbd.
Argument MBOX-LIST tbd."
  (let ((mblalist (mapc (lambda (p)
                         (setf (car p)
                               (epop3-strip-through-semicolon (car p))))
                        (epop3-manage-make-po:alist-from-mailbox-list
                         mbox-list)))
        (key nil)
        (pair nil))
    (setq key (completing-read prompt mblalist))
    (remove-text-properties 0 (length key) (text-properties-at 0 key) key)
    (setq pair (assoc (substring key 0) mblalist))
    (if pair
        (list (cdr pair)))))

(defun epop3-manage-quit ()
  "Kill the summary buffer.  and exits manage mode."
  (interactive)
  (epop3-save-uidls)
  (when epop3-manage-was-biffing
    (epop3-start-biff epop3-biff-interval t))
  (setq epop3-manage-current-po nil)
  (kill-buffer (current-buffer)))


(defun epop3-manage-count-matching-utab-entries (dt frm sub)
  "Tbd DT FRM SUB."
  (interactive)
  (let ((cnt 0))
    (epop3-dohash (uid ntry epop3-utab cnt)
      (and (epop3-manage-date-from-sub-matches-p dt frm sub ntry)
           (incf cnt)))))

(defun epop3-manage-date-from-sub-matches-p (dt frm sub uidentry)
  "Tbd DT FRM SUB UIDENTRY."
  ;; don't match against things already deleted or which
  ;; aren't on the server or which have msgno 0
  (and (not (epop3-uid-entry-todelete uidentry))
       (epop3-uid-entry-onserver uidentry)
       (not (zerop (epop3-uid-entry-msgno uidentry)))
       (string-match (epop3-string-trim dt) (epop3-uid-entry-date uidentry))
       (string-match (epop3-string-trim frm) (epop3-uid-entry-from uidentry))
       (string-match (epop3-string-trim sub) (epop3-uid-entry-subj uidentry))))

(defun epop3-manage-mark-matching-utab-entries-for-delete (dt frm sub)
  "Tbd DT FRM SUB."
  (interactive)
  (epop3-dohash (uid ntry epop3-utab)
    (when (epop3-manage-date-from-sub-matches-p dt frm sub ntry)
      (setf (epop3-uid-entry-todelete ntry) t))))

(defun epop3-manage-split-sumline (sumline)
  "Extract the fields from the summary line.
Argument SUMLINE tbd."
  (let ((date nil)
        (from nil)
        (f nil)
        (subj nil))
    (string-match " +\\([^ ]*\\) +\\(.*\\)\\[.*\\] #\\(.*\\)" sumline)
    (setq date (substring sumline (match-beginning 1) (match-end 1)))
    (setq f (substring sumline (match-beginning 2) (match-end 2)))
    (setq subj (substring sumline (match-beginning 3) (match-end 3)))
    (setq from
          (epop3-strip-to-space-if-any (substring f 0 (string-match "," f))))
    (list date from subj)))

(defun epop3-mark-msg-for-manage-delete (sumline)
  "Mark tbd SUMLINE."
  (let ((count nil))
    (multiple-value-bind (dt frm sbj) (epop3-manage-split-sumline sumline)
    (setq count (epop3-manage-count-matching-utab-entries dt frm sbj))

    (cond
     ((= count 0)
      (message "No matching entries found to delete from server."))
     ((= count 1)
      (epop3-manage-mark-matching-utab-entries-for-delete dt frm sbj)
      (message (format "%d message marked for deletion from server." count)))
     ((> count 1)
      (when (yes-or-no-p
             (format "%d matches found -- mark all of them for server delete? "
                     count))
        (epop3-manage-mark-matching-utab-entries-for-delete dt frm sbj)
        (message (format "%d messages marked for deletion from server."
                         count))))))))


(defun epop3-manage-rmail-mark-msg-for-manage-delete ()
  "Mark current RMAIL message for deletion from server (rmail-mode only)."
  (interactive)
  (epop3-mark-msg-for-manage-delete (epop3-rmail-getsumline)))

(defun epop3-manage-rmail-summary-mark-msg-for-manage-delete ()
  "Mark current message for deletion from server (rmail-summary-mode only)."
  (interactive)
  (epop3-mark-msg-for-manage-delete (epop3-rmail-summary-getsumline)))

;;; {{{ submitting bug reports -- made easier

(defun epop3-manage-submit-bug-report ()
  "Submit via mail a bug report on epop3manage."
  (interactive)
  (require 'reporter)
  ;; load in reporter
  (let ((reporter-prompt-for-summary-p t))
    (and
     (if (y-or-n-p "Do you want to submit a report on epop3manage? ")
         t (message "") nil)
     (require 'reporter)
     (reporter-submit-bug-report
      epop3-mail-help-address
      (concat "epop3manage " epop3-manage-version " ")
      (let ((vars (list
                   'epop3-authentication-always-use-default
                   'epop3-authentication-default
                   'epop3-authentication-timeout-seconds
                   'epop3-biff-absolutely-silent
                   'epop3-biff-debug
                   'epop3-biff-differential-mode
                   'epop3-biff-ding
                   'epop3-biff-hook
                   'epop3-biff-idle-grace-seconds
                   'epop3-biff-interval
                   'epop3-biff-linear-bark-mode
                   'epop3-biff-optimize-if-possible
                   'epop3-biff-optimized-and-read
                   'epop3-biff-show-barks
                   'epop3-biff-show-mail-string
                   'epop3-biff-show-numbers
                   'epop3-biff-show-off-vocabulary
                   'epop3-biff-show-progress
                   'epop3-biff-show-snooze
                   'epop3-biff-show-time
                   'epop3-biffed-at-least-once
                   'epop3-biffing
                   'epop3-current-bark
                   'epop3-last-biff-at
                   'epop3-leave-mail-on-server
                   'epop3-mail-debug
                   'epop3-mail-package
                   'epop3-mailbox-list
                   'epop3-manage-current-po
                   'epop3-manage-debug
                   'epop3-manage-really-delete
                   'epop3-manage-was-biffing
                   'epop3-mode-line-info
                   'epop3-old-n
                   'epop3-open-server-timeout
                   'epop3-override-pop3s-read-response
                   'epop3-password-style
                   'epop3-quietly
                   'epop3-unix-mail-delimiter
                   'epop3-utab
                   )))
        (if (not (boundp 'defun-prompt-regexp))
            (delq 'defun-prompt-regexp vars)
          vars))
      nil
      nil
      "Dear Franklin,"
      ))))

;;; }}}


(provide 'epop3manage)

;;; epop3manage.el ends here
