;;; gnus-offline.el --- To process mail & news at offline environment.

;;; Copyright (C) 1998, 2001 Tatsuya Ichikawa
;;; Copyright (C) 1998, 2001 Yukihiro Ito
;;; Author: Tatsuya Ichikawa <t-ichi@po.shiojiri.ne.jp>
;;;         Yukihiro Ito <ito@rs.civil.tohoku.ac.jp>
;;;         Hidekazu Nakamura <u90121@uis-inf.co.jp>
;;;         Tsukamoto Tetsuo <czkmt@remus.dti.ne.jp>

;;; Version: 2.20
;;; Keywords: news , mail , offline , gnus
;;;
;;; SPECIAL THANKS
;;;    Keiichi Suzuki <kei-suzu@mail.wbs.or.jp>
;;;    KORIYAMA Naohiro <kory@ba2.so-net.or.jp>
;;;    Katsumi Yamaoka <yamaoka@jpl.org>

;;; This file is part of Semi-gnus.
;;;
;;; GNU Emacs 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.

;;; GNU Emacs 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:
;;; Note.
;;;   This file works only with after version of Emacs 19.30.
;;;   This file needs miee.el and SEMI.
;;;   If you set gnus-offline-drafts-queue-type to 'agent , you don't need
;;;   miee.el
;;;   You must use T-gnus 6.12.0 or later.
;;;
;;; How to use.
;;;
;;; Add following code at the end in your .emacs
;;;
;;;    (load "gnus-ofsetup")
;;;    (gnus-setup-for-offline)
;;;
;;; If you use gnus-agent as souper , put gnus-agent setup code in you .gnus.el
;;;
;;; If you use nnspool as souper , put following code in your .emacs before
;;; gnus-offline setting.
;;;
;;; Then , put hang.exe in exec-path directory.
;;;
;;; In Gnus group buffer , type g to get all news and mail.
;;; Then send mail and news in spool directory.
;;;
;;; Variables.
;;;  gnus-offline-dialup-program-arguments
;;;                                   ... List of dialup program arguments.
;;;  gnus-offline-hangup-program-arguments
;;;                                   ... List of hangup program arguments.
;;;  gnus-offline-mail-treat-environ  ... toggle sending mail online/offline.
;;;  gnus-offline-articles-to-fetch   ... toggle fetch articles.
;;;                                        both->mail->news->both...
;;;  gnus-offline-load-hook           ... hook before gnus-offline load.
;;;  gnus-offline-before-online-hook  ... hook before all online jobs.
;;;  gnus-offline-after-online-hook   ... hook after all online jobs.
;;;  gnus-offline-interval-time       ... Interval time to do all online jobs.
;;;                                        (minutes)
;;;  gnus-offline-dialup-function     ... Function to diualup.
;;;  gnus-offline-hangup-function     ... Function to hangup.

;;; Code:

(eval '(run-hooks 'gnus-offline-load-hook))

(eval-when-compile (require 'cl))

(eval-when-compile
  (require 'static)
  (require 'gnus-agent)
  (require 'gnus-group))
(require 'custom)
(require 'easymenu)
(provide 'gnus-offline)

(defgroup gnus-offline nil
  "Offline backend utility for Gnus."
  :prefix "gnus-offline-"
  :group 'gnus
  :group 'mail
  :group 'news)

(defconst gnus-offline-version-number "2.20")
(defconst gnus-offline-codename
;;  "Beta5"			; Beta
;;  "This is the time"		; 2.00
;;  "A matter of trust"
;;  "Modern Woman"
;;  "Ahhhhhhh!!"		; 2.10b1
  "Cup of life"			; 2.20
;;  "Code of silence"
  )

(defconst gnus-offline-version (format "Gnus offline backend utiliy v%s"
				       gnus-offline-version-number))

(eval-when-compile
  (defvar nnagent-version)
  (defvar nnspool-version)
  (defvar msspool-news-server)
  (defvar msspool-news-service)
  (defvar miee-popup-menu))

(if (featurep 'meadow)
    (define-process-argument-editing "/hang\\.exe\\'"
      (lambda (x)
	(general-process-argument-editing-function
	 x nil t t nil t t))))

(defcustom gnus-offline-auto-ppp '(connect disconnect)
  "*This variable decides whether to connect and/or disconnect automatically."
  :group 'gnus-offline
  :type '(choice
	  (const :tag "Connection and Disconnection" (connect disconnect))
	  (const :tag "Connection Only" (connect))
	  (const :tag "Do Everything Manually" nil)))

(defcustom gnus-offline-load-hook nil
  "*Hook to be run after the gnus-offline package has been loaded."
  :group 'gnus-offline
  :type 'hook)

(defcustom gnus-offline-before-online-hook nil
  "*Hook to be run before all online jobs."
  :group 'gnus-offline
  :type 'hook)

(defcustom gnus-offline-after-online-hook nil
  "*Hook to be run after all online jobs."
  :group 'gnus-offline
  :type 'hook)

(defcustom gnus-offline-mail-treat-environ 'offline
  "*If online , gnus-offline send all mail under online environ.
If offline , gnus-offline send all mail temporary to spool dir."
  :group 'gnus-offline
  :type '(choice (const offline)
		 (const online)))

(defcustom gnus-offline-articles-to-fetch 'both
  "*If both , gnus-offline fetch mail and news articles.
If mail , gnus-offline only fetch mail articles.
 If news , gnus-offline only fetch news articles."
  :group 'gnus-offline
  :type '(choice (const both)
		 (const mail)
		 (const news)))

(defcustom gnus-offline-mail-group-level 1
  "*Group level for mail group."
  :group 'gnus-offline
  :type 'integer)

(defcustom gnus-offline-after-empting-spool-hook nil
  "*Hook to be run before empting spool."
  :group 'gnus-offline
  :type 'hook)

(defcustom gnus-offline-before-empting-spool-hook nil
  "*Hook to be run after empting spool."
  :group 'gnus-offline
  :type 'hook)

(defcustom gnus-offline-dialup-function 'gnus-offline-connect-server
  "*Function to dialup."
  :group 'gnus-offline
  :type 'function)

(defcustom gnus-offline-hangup-function 'gnus-offline-hangup-line
  "*Function to hangup."
  :group 'gnus-offline
  :type 'function)

(defcustom gnus-offline-auto-expire t
  "*Non-nil means expire articles on every session."
  :group 'gnus-offline
  :type 'boolean)

;; These variables should be customized using `gnus-offline-customize',
;; not by `customize'.

(defvar gnus-offline-dialup-program nil
  "*Program name for dialup.")

(defvar gnus-offline-hangup-program nil
  "*Program name for hangup.")

(defvar gnus-offline-dialup-program-arguments nil
  "*Program arguments of gnus-offline-dialup-program.")

(defvar gnus-offline-hangup-program-arguments nil
  "*Program arguments of gnus-offline-hangup-program.")

(defvar gnus-offline-interval-time 0
  "*Interval time(minutes) to do online jobs.
If set to 0 , timer call is disabled.")

(defvar gnus-offline-drafts-queue-type 'agent
  "*Queuing function used for draft messages.")

(defvar gnus-offline-MTA-type 'smtp
  "*Type of MTA, sendmail or smtp.el.")

;;; Internal variables.
(defvar gnus-offline-connected nil
  "*If value is t , dialup line is connected status.
If value is nil , dialup line is disconnected status.")

(defvar gnus-offline-news-fetch-method nil
  "*Method to fetch news articles.")

(defvar gnus-offline-mail-fetch-method nil
  "*Method to fetch mail articles.")

(defvar gnus-offline-header-string
  (format "%s - \"%s\""
	  gnus-offline-version
	  gnus-offline-codename)
  "*Header string for gnus-offline.")

(defvar gnus-offline-stored-group-level nil
  "*Mail Group level before changing.")

(defvar gnus-offline-mail-source nil
  "*mail-sources save variable.")

(defvar gnus-offline-lang)

(defvar gnus-offline-resource-en
  '((error-check-1
     . "WARNING!!: gnus-agent.el or nnagent.el is not loaded.
Please check your .emacs or .gnus.el to work gnus-agent fine.")
    (error-check-2 ."WARNING!!: nnspool.el is not loaded.
Please check your .emacs or .gnus.el to work nnspool fine.")
    (connect-server-1 . "Dialing ...")
    (connect-server-2 . "Dialing ... done.")
    (get-new-news-function-1 . "Set to online status.")
    (hangup-line-1 . "Hang up line ... ")
    (hangup-line-2 . "Hang up line ... done.")
    (after-jobs-done-1 . "All online jobs has done.")
    (set-auto-ppp-1 . "Connect and disconnect automatically.")
    (set-auto-ppp-2 . "Connect automatically.")
    (set-auto-ppp-3 . "Connect and disconnect manually.")
    (set-auto-ppp-menu-1 . "Automatically Connect/Disconnect")
    (set-auto-ppp-menu-2 . "Automatically Connect")
    (set-auto-ppp-menu-3 . "Manually Connect/Disconnect")
    (toggle-on/off-send-mail-1 . "Sending mail immidiately.")
    (toggle-on/off-send-mail-2 . "Sending mail temporary to spool directory.")
    (toggle-articles-to-fetch-1 . "Articles fetch from server.")
    (toggle-articles-to-fetch-2 . "Only Mail")
    (toggle-articles-to-fetch-3 . "Only News")
    (toggle-articles-to-fetch-4 . "Mail/News both")
    (empting-spool-1 . "Sending mails in spool ...")
    (empting-spool-2 . "Sending mails in spool ... done.")
    (empting-spool-3 . "Posting news in spool ...")
    (empting-spool-4 . "Posting news in spool ... done.")
    (empting-spool-5 . "Sending messages in spool ...")
    (empting-spool-6 . "Sending messages in spool ... done.")
    (interval-time-1 . "Interval time (now %d minutes) : ")
    (interval-time-2 . "Retrieving message logic by timer is disabled.")
    (interval-time-3 . "Interval time set to %d minutes")
    (menu-miee-1 . "Post news in spool")
    (menu-miee-2 . "Send mails in spool")
    (menu-miee-3 . "Message Offline")
    (menu-miee-4 . "Message Online")
    (menu-1 . "Toggle articles to fetch")
    (menu-2 . "Toggle online/offline send mail")
    (menu-3 . "Set auto PPP")
    (menu-4 . "Expire articles")
    (menu-5 . "Set interval time")
    (menu-6 . "Hang up Line.")
    (menu-7 . "Customize options...")))

(defvar gnus-offline-resource-ja
  '((error-check-1
     . "警告!!: gnus-agent.el または nnagent.el がロードされていません。
.emacs または .gnus.el の gnus-agent の設定を正しくしてください。")
    (error-check-2 ."警告!!: nnspool.el がロードされていません。
.emacs または .gnus.el の nnspool の設定を正しくしてください。")
    (connect-server-1 . "接続しています...")
    (connect-server-2 . "接続しています...完了。")
    (get-new-news-function-1 . "オンライン状態です。")
    (set-auto-ppp-1 . "自動的に PPP 接続・切断します。")
    (set-auto-ppp-2 . "自動的に PPP 接続します。")
    (set-auto-ppp-3 . "手動で PPP 接続・切断します。")
    (hangup-line-1 . "切断しています...")
    (hangup-line-2 . "切断しています...完了。")
    (after-jobs-done-1 . "全てのオンライン処理を完了しました。")
    (toggle-on/off-send-mail-1 . "メールを直接送信します。")
    (toggle-on/off-send-mail-2 . "メールはキューに送られます。")
    (toggle-articles-to-fetch-1 . "受信するメッセージは... ")
    (toggle-articles-to-fetch-2 . "メールのみです。")
    (toggle-articles-to-fetch-3 . "ニュースのみです。")
    (toggle-articles-to-fetch-4 . "メールとニュースの両方です。")
    (empting-spool-1 . "キューのメールを送信中...")
    (empting-spool-2 . "キューのメールを送信中... 完了。")
    (empting-spool-3 . "キューのニュース記事を送信中...")
    (empting-spool-4 . "キューのニュース記事を送信中... 完了。")
    (empting-spool-5 . "キューのメッセージを送信中...")
    (empting-spool-6 . "キューのメッセージを送信中... 完了。")
    (interval-time-1 . "送受信ジョブの間隔 (現在の設定は %d 分です) : ")
    (interval-time-2 . "自動送受信機能を オフ にしました。")
    (interval-time-3 . "自動送受信の間隔を %d 分に設定しました。")))

(defvar gnus-offline-resource-ja_complete
  (append
   gnus-offline-resource-ja
   '((menu-miee-1 . "Spool にある記事の送信")
     (menu-miee-2 . "Spool にある Mail の送信")
     (menu-miee-3 . "Offline 状態へ")
     (menu-miee-4 . "Online 状態へ")
     (menu-1 . "取得記事種類の変更")
     (menu-2 . "Mail 送信方法(On/Off)の切替え")
     (menu-3 . "自動 PPP 制御の設定")
     (menu-4 . "取得済記事を消す")
     (menu-5 . "記事取得間隔時間の設定")
     (menu-6 . "回線の切断")
     (menu-7 . "プロパティ...")
     (set-auto-ppp-menu-1 . "自動的に PPP 接続・切断")
     (set-auto-ppp-menu-2 . "自動的に PPP 接続")
     (set-auto-ppp-menu-3 . "手動で PPP 接続・切断"))))

;;; Functions

;; Inline functions.
(defsubst gnus-offline-gettext (symbol &optional lang)
  (setq lang (or lang gnus-offline-lang))
  (or
   (cdr (assq symbol (symbol-value
		      (intern (format "gnus-offline-resource-%s" lang)))))
   (cdr (assq symbol gnus-offline-resource-en))))

(defsubst gnus-offline-set-online-sendmail-function ()
  "*Initialize sendmail-function when plugged status."
  (if (eq gnus-offline-MTA-type 'smtp)
      (setq message-send-mail-function 'message-send-mail-with-smtp)
    (setq message-send-mail-function 'message-send-mail-with-sendmail)))

(defsubst gnus-offline-set-offline-sendmail-function ()
  "*Initialize sendmail-function when unplugged status."
  (cond ((eq gnus-offline-drafts-queue-type 'miee)
	 (if (eq gnus-offline-news-fetch-method 'nnagent)
	     (setq gnus-agent-send-mail-function
		   'sendmail-to-spool-in-gnspool-format))
	 (setq message-send-mail-function 'sendmail-to-spool-in-gnspool-format))
	(t
	 (setq gnus-agent-send-mail-function
	       (gnus-offline-set-online-sendmail-function)
	       message-send-mail-function 'gnus-agent-send-mail))))

(defsubst gnus-offline-set-offline-post-news-function ()
  "*Initialize sendnews-function when unplugged status."
  (if (eq gnus-offline-drafts-queue-type 'miee)
      (setq message-send-news-function 'gnspool-request-post)))

(defsubst gnus-offline-set-online-post-news-function ()
  "*Initialize sendnews-function when plugged status."
  (setq message-send-news-function 'message-send-news-with-gnus))

(defsubst gnus-offline-disable-fetch-mail ()
  "*Set do not fetch mail."
  (setq mail-sources nil
	nnmail-spool-file nil))

(defsubst gnus-offline-enable-fetch-mail ()
  "*Set to fetch mail."
  (setq gnus-offline-mail-fetch-method 'nnmail)
  (setq mail-sources gnus-offline-mail-source))

(defsubst gnus-offline-enable-fetch-news ()
  "*Set to fetch news."
  (if (eq gnus-offline-news-fetch-method 'nnagent)
      (progn
	(setq gnus-agent-handle-level gnus-level-subscribed)
	(gnus-agent-toggle-plugged t))))

(when (featurep 'gnus-ofsetup)
  ;; Advice to Gnus functions.
  (defadvice gnus-group-get-new-news (before gnus-offline-advice
					     activate preactivate)
    "When called interactively, dial up and get online automatically."
    (when (interactive-p)
      (run-hooks 'gnus-offline-before-online-hook)
      (if (and (memq 'connect gnus-offline-auto-ppp)
	       (functionp gnus-offline-dialup-function))
	  (funcall gnus-offline-dialup-function))
      (gnus-offline-get-new-news-function)))

  (defadvice gnus-agent-toggle-plugged (around gnus-offline-advice
					       activate preactivate)
    "Also toggle gnus-offline `connected--disconnected' status."
    (interactive (list (not gnus-offline-connected)))
    (cond ((ad-get-arg 0)
	   (setq gnus-offline-connected (ad-get-arg 0))
	   ad-do-it
	   ;; Set send mail/news function to offline functions.
	   (gnus-offline-set-online-sendmail-function)
	   (gnus-offline-set-online-post-news-function))
	  (t
	   ;; Set to offline status
	   (gnus-offline-set-unplugged-state))))

  (defadvice gnus-agent-expire (around gnus-offline-advice activate preactivate)
    "Advice not to delete new articles."
    (cond ((eq 0 gnus-agent-expire-days)
	   (let (gnus-agent-expire-all)
	     ad-do-it))
	  (t
	   ad-do-it)))

  (defadvice gnus-agent-mode (around gnus-offline-advice activate preactivate)
    "Advice not to close PPP connection."
    (let (gnus-offline-hangup-function)
      ad-do-it)))

;;
;; Setting up...
;;
(defun gnus-offline-setup ()
  "*Initialize gnus-offline function"

  (when (eq gnus-offline-drafts-queue-type 'agent)
    (setq gnus-offline-connected gnus-plugged))

  (gnus-offline-processed-by-timer)
  (gnus-offline-error-check)

  ;; To transfer Mail/News function.
  (cond	((or (and (eq 'gnus-offline-drafts-queue-type 'agent)
		  gnus-offline-connected)
	     (eq gnus-offline-mail-treat-environ 'online))
	 ;; send mail under offline environ.
	 (gnus-offline-set-online-sendmail-function))
	(t
	 ;; send mail under offline environ.
	 (gnus-offline-set-offline-sendmail-function))))

;;
;; Setting Error check.
(defun gnus-offline-error-check ()
  ;; Check gnus-agent and nnspool setting.
  (let ((buffer " *Offline Error*"))
    (cond ((eq gnus-offline-news-fetch-method 'nnagent)
	   ;; nnagent and gnus-agent loaded ??
	   (unless (and (featurep 'gnus-agent)
			(featurep 'nnagent))
	     (set-buffer (gnus-get-buffer-create buffer))
	     (erase-buffer)
	     (insert (gnus-offline-gettext 'error-check-1))
	     (pop-to-buffer buffer)))

	  ((eq gnus-offline-news-fetch-method 'nnspool)
	   (unless (featurep 'nnspool)
	     (set-buffer (gnus-get-buffer-create buffer))
	     (erase-buffer)
	     (insert (gnus-offline-gettext 'error-check-2))
	     (pop-to-buffer buffer)))
	  (t
	   nil))))

;;
;; dialup...
;;
(defun gnus-offline-connect-server ()
  "*Dialup function."
  ;; Dialup if gnus-offline-dialup-program is specified
  (if (stringp gnus-offline-dialup-program)
      (progn
	(message "%s" (gnus-offline-gettext 'connect-server-1))
	(apply 'call-process gnus-offline-dialup-program nil nil nil
	       gnus-offline-dialup-program-arguments)
	(sleep-for 1)
	(message "%s" (gnus-offline-gettext 'connect-server-2)))))

;;
;; Jobs before get new news , send mail and post news.
;;
(defun gnus-offline-get-new-news-function ()
  "*Prepare to get new news/mail."
  ;; Set mail group level
  (if (eq gnus-offline-articles-to-fetch 'mail)
      (gnus-offline-set-mail-group-level gnus-offline-mail-group-level))

  ;; Set to online environ.
  (setq gnus-offline-connected t)

  ;; Set send mail/news functions to online functions.
  (gnus-offline-set-online-sendmail-function)
  (gnus-offline-set-online-post-news-function)
  (message "%s" (gnus-offline-gettext 'get-new-news-function-1))

  ;; fetch only news
  (if (eq gnus-offline-articles-to-fetch 'news)
      (gnus-offline-disable-fetch-mail))

  ;; fetch both mail and news. or Only mail.
  (gnus-offline-enable-fetch-news)
  (if (memq gnus-offline-articles-to-fetch '(both mail))
      (gnus-offline-enable-fetch-mail))

  ;; fetch only mail for gnus-agent
  (if (and (eq gnus-offline-news-fetch-method 'nnagent)
	   (eq gnus-offline-articles-to-fetch 'mail))
      (setq gnus-agent-handle-level gnus-offline-mail-group-level)))

;;
;; Change mail group level to handle only mail.
;;
(defun gnus-offline-set-mail-group-level (level)
  "*Set nnm* group level."
  (switch-to-buffer gnus-group-buffer)
  (goto-char (point-min))

  ;; Save current level
  (if (not gnus-offline-stored-group-level)
      (while (re-search-forward " nnm" nil t)
	(setq gnus-offline-stored-group-level
	      (append gnus-offline-stored-group-level
		      (list (gnus-group-group-level)))))
    (forward-line 1)
    (beginning-of-line))
  ;;
  (goto-char (point-min))
  (while (re-search-forward " nnm" nil t)
    (gnus-group-set-current-level 1 level)
    (forward-line 1)
    (beginning-of-line))
  t)
;;
;; Restore mail group level
;;
(defun gnus-offline-restore-mail-group-level ()
  "*Restore nnm* group level."
  (switch-to-buffer gnus-group-buffer)
  (goto-char (point-min))
  (let ((num 0))
    (while (re-search-forward " nnm" nil t)
      (gnus-group-set-current-level 1 (nth num gnus-offline-stored-group-level))
      (forward-line 1)
      (setq num (+ num 1))
      (beginning-of-line))))
;;
;; Jobs after getting new news.
;;
(defun gnus-offline-after-get-new-news ()
  "*After getting news and mail jobs."
  (cond (gnus-offline-connected
	 (when (memq gnus-offline-articles-to-fetch '(both mail))
	   ;; Mail/both
	   ;; send mail/news in spool
	   (gnus-offline-empting-spool)
	   (when (eq gnus-offline-articles-to-fetch 'mail)
	     ;; Send only mail and hang up...
	     (if gnus-offline-connected
		 (gnus-offline-set-unplugged-state))
	     ;; Disable fetch mail.
	     (gnus-offline-disable-fetch-mail)
	     (gnus-offline-after-jobs-done)))
	 (when (memq gnus-offline-articles-to-fetch '(both news))
	   ;; News/Both
	   (cond ((eq gnus-offline-news-fetch-method 'nnagent)
		  ;; Get New News (gnus-agent)
		  (gnus-agent-toggle-plugged t)
		  ;; fetch articles
		  (gnus-agent-fetch-session)
		  ;; Hang Up line. then set to offline status.
		  (gnus-offline-set-unplugged-state)
		  ;; All online jobs has done.
		  (gnus-offline-after-jobs-done))
		 (t
		  (if (eq gnus-offline-news-fetch-method 'nnspool)
		      ;; Get New News (nnspool)
		      (gnspool-get-news))))))
	(t
	 nil)))

;;
;; Add your custom header.
;;
(defun gnus-offline-add-custom-header (header string)
  "*Add X-Gnus-Offline-Backend header to Mail/News message."
  (let ((delimline
	 (progn (goto-char (point-min))
		(re-search-forward
		 (concat "^" (regexp-quote mail-header-separator) "\n"))
		(point-marker)))
	hdr str)
    (goto-char (point-min))
    (unless (re-search-forward (concat "^" header) delimline t)
      (goto-char delimline)
      (forward-line -1)
      (beginning-of-line)
      (setq hdr (concat header " "))
      (setq str (concat hdr string))
      (setq hdr (concat str "\n"))
      (insert hdr))))
;;
;; Add X-Offline-Backend header.
;;
(defun gnus-offline-message-add-header ()
  "*Add X-Gnus-Offline-Backend header to Mail/News message."
  (when (eq gnus-offline-mail-treat-environ 'offline)
    (let* ((ver (if (eq gnus-offline-news-fetch-method 'nnagent)
		    nnagent-version
		  nnspool-version))
	   (str (format "\n                        with %s" ver)))
      (gnus-offline-add-custom-header
       "X-Gnus-Offline-Backend:" (concat gnus-offline-header-string str)))))


;;
;; Function of hang up line.
;;
(defun gnus-offline-set-unplugged-state ()
  "*Set to unplugged state."
  (interactive)
  ;; Hang Up Line.
  (if (and (memq 'disconnect gnus-offline-auto-ppp)
	   (functionp gnus-offline-hangup-function))
      (funcall gnus-offline-hangup-function))
  (setq gnus-offline-connected nil)
  (if (eq gnus-offline-news-fetch-method 'nnagent)
      (ad-Orig-gnus-agent-toggle-plugged nil))

  ;; Set send mail/news function to offline functions.
  (gnus-offline-set-offline-sendmail-function)
  (gnus-offline-set-offline-post-news-function)
  ;;
  (setenv "MAILHOST" nil))
;;
;; Hangup line function
;;
(defun gnus-offline-hangup-line ()
  "*Hangup line function."
  (message "%s" (gnus-offline-gettext 'hangup-line-1))
  (if (stringp gnus-offline-hangup-program)
      (apply 'start-process "hup" nil gnus-offline-hangup-program
	     gnus-offline-hangup-program-arguments))
  (message "%s" (gnus-offline-gettext 'hangup-line-2)))
;;
;; Hang Up line routine whe using nnspool
;;
(defun gnus-offline-nnspool-hangup-line ()
  (if gnus-offline-connected
      (gnus-offline-set-unplugged-state))
  (gnus-offline-after-jobs-done))
;;
;; Function of all jobs has done.
;;
(defun gnus-offline-after-jobs-done ()
  "*Jobs after all online jobs."
  (run-hooks 'gnus-offline-after-online-hook)
  (if (eq gnus-offline-articles-to-fetch 'mail)
      (gnus-offline-restore-mail-group-level))
  (if (and (eq gnus-offline-news-fetch-method 'nnagent)
	   gnus-offline-auto-expire)
      (gnus-agent-expire))
  (static-if (featurep 'xemacs)
      (if (fboundp 'play-sound-file)
	  (ding nil 'drum)
	(ding))
    (ding))
  (gnus-group-save-newsrc)
  (message "%s" (gnus-offline-gettext 'after-jobs-done-1)))


;;
;; Set auto PPP
;;
(defun gnus-offline-set-auto-ppp ()
  "*Decide whether to connect and/or disconnect automatically."
  (interactive)
  (let ((keys (key-description (this-command-keys)))
	menu title str)
    (cond ((or (string= "misc-user" keys)
	       (string= "S-mouse-2" keys)
	       (string-match "^menu-bar" keys)
	       (string-match "^mouse" keys))
	   (setq title (gnus-offline-gettext 'menu-3))
	   (setq menu
		 (cons title
		       (gnus-offline-get-menu-items
			'((set-auto-ppp-menu-1
			   (progn
			     (setq gnus-offline-auto-ppp '(connect disconnect))
			     (message "%s"
				      (gnus-offline-gettext 'set-auto-ppp-1)))
			   t)
			  (set-auto-ppp-menu-2
			   (progn
			     (setq gnus-offline-auto-ppp '(connect))
			     (message "%s"
				      (gnus-offline-gettext 'set-auto-ppp-2)))
			   t)
			  (set-auto-ppp-menu-3
			   (progn
			     (setq gnus-offline-auto-ppp nil)
			     (message "%s"
				      (gnus-offline-gettext 'set-auto-ppp-3)))
			   t)))))
	   (gnus-offline-popup menu title))
	  (t
	   (cond ((eq gnus-offline-auto-ppp nil)
		  (setq gnus-offline-auto-ppp '(connect disconnect))
		  (setq str (gnus-offline-gettext 'set-auto-ppp-1)))
		 ((memq 'connect gnus-offline-auto-ppp)
		  (cond ((memq 'disconnect gnus-offline-auto-ppp)
			 (setq gnus-offline-auto-ppp '(connect))
			 (setq str
			       (gnus-offline-gettext 'set-auto-ppp-2)))
			(t
			 (setq gnus-offline-auto-ppp nil)
			 (setq str
			       (gnus-offline-gettext 'set-auto-ppp-3))))))
	   (message "%s" str)))))
;;
;; Toggle offline/online to send mail.
;;
(defun gnus-offline-toggle-on/off-send-mail ()
  "*Toggel online/offline sendmail."
  (interactive)
  (if (eq gnus-offline-mail-treat-environ 'offline)
      (progn
	;; Sending mail under online environ.
	(gnus-offline-set-online-sendmail-function)
	(setq gnus-offline-mail-treat-environ 'online)
	(message "%s" (gnus-offline-gettext 'toggle-on/off-send-mail-1)))
    ;; Sending mail under offline environ.
    (gnus-offline-set-offline-sendmail-function)
    (setq gnus-offline-mail-treat-environ 'offline)
    (message "%s" (gnus-offline-gettext 'toggle-on/off-send-mail-2))))
;;
;; Toggle articles to fetch ... both -> mail -> news -> both
;;
(defun gnus-offline-toggle-articles-to-fetch ()
  "*Set articles to fetch... both(Mail/News) -> mail only -> News only -> both"
  (interactive)
  (let ((string (gnus-offline-gettext 'toggle-articles-to-fetch-1))
	str)
    (cond ((eq gnus-offline-articles-to-fetch 'both)
	   (setq gnus-offline-articles-to-fetch 'mail
		 str (gnus-offline-gettext 'toggle-articles-to-fetch-2)))
	  ((eq gnus-offline-articles-to-fetch 'mail)
	   (setq gnus-offline-articles-to-fetch 'news
		 str (gnus-offline-gettext 'toggle-articles-to-fetch-3)))
	  (t
	   (setq gnus-offline-articles-to-fetch 'both
		 str (gnus-offline-gettext 'toggle-articles-to-fetch-4))))
    (message "%s %s" string str)))
;;
;; Send mail and Post news using Miee or gnus-agent.
;;
(defun gnus-offline-empting-spool ()
  "*Send all drafts on queue."
  (run-hooks 'gnus-offline-before-empting-spool-hook)
  (if (eq gnus-offline-drafts-queue-type 'miee)
      ;; Send queued message by miee.el.
      (progn
	(if (eq gnus-offline-mail-treat-environ 'offline)
	    (progn
	      (message "%s" (gnus-offline-gettext 'empting-spool-1))
	      ;; Using miee to send mail.
	      (mail-spool-send)
	      (message "%s" (gnus-offline-gettext 'empting-spool-2))))
	(message "%s" (gnus-offline-gettext 'empting-spool-3))
	;; Using miee to post news.
	(if (and (not (stringp msspool-news-server))
		 (not msspool-news-service))
	    (progn
	      (setq msspool-news-server (nth 1 gnus-select-method))
	      (setq msspool-news-service 119)))
	(news-spool-post)
	(message "%s" (gnus-offline-gettext 'empting-spool-4)))
    ;; Send queued message by gnus-agent
    (message "%s" (gnus-offline-gettext 'empting-spool-5))
    (gnus-group-send-queue)
    (message "%s" (gnus-offline-gettext 'empting-spool-6)))
  ;;
  (run-hooks 'gnus-offline-after-empting-spool-hook))
;;
;; Set interval time
;;
(defun gnus-offline-set-interval-time ()
  "*Set interval time for gnus-daemon."
  (interactive)
  (setq gnus-offline-interval-time
	(string-to-int (read-from-minibuffer
			(format (gnus-offline-gettext 'interval-time-1)
				gnus-offline-interval-time)
			nil)))
  (if (< gnus-offline-interval-time 2)
      (progn
	(message "%s" (gnus-offline-gettext 'interval-time-2))
	(setq gnus-offline-interval-time 0))
    (message
     (format (gnus-offline-gettext 'interval-time-3)
	     gnus-offline-interval-time)))
  (gnus-offline-processed-by-timer))

;;
;; Menu.
;;
(defun gnus-offline-define-menu-and-key ()
  "*Set key and menu."
  (cond ((eq gnus-offline-drafts-queue-type 'miee)
	 (static-cond
	  ((featurep 'xemacs)
	   (add-hook 'gnus-group-mode-hook 'gnus-offline-define-menu-on-miee))
	  (t
	   (gnus-offline-define-menu-on-miee))))
	(t
	 (add-hook 'gnus-group-mode-hook 'gnus-offline-define-menu-on-agent)))
  ;;
  (add-hook 'gnus-group-mode-hook
	    #'(lambda ()
		(local-set-key "\C-coh" 'gnus-offline-set-unplugged-state)
		(local-set-key "\C-cof" 'gnus-offline-toggle-articles-to-fetch)
		(local-set-key "\C-coo" 'gnus-offline-toggle-on/off-send-mail)
		(local-set-key "\C-cox" 'gnus-offline-set-auto-ppp)
		(local-set-key "\C-cos" 'gnus-offline-set-interval-time)
		(if (eq gnus-offline-news-fetch-method 'nnagent)
		    (local-set-key "\C-coe" 'gnus-agent-expire))
		(static-unless (featurep 'xemacs)
		  (local-set-key
		   (if (eq system-type 'windows-nt) [S-mouse-2] [mouse-3])
		   'gnus-offline-popup-menu)))))

;;
;;
(defun gnus-offline-popup (menu &optional title)
  (static-cond
   ((featurep 'xemacs)
    (popup-menu menu))
   (t
    (let ((menu-func (or (and (fboundp 'easy-menu-create-menu)
			      'easy-menu-create-menu)
			 'easy-menu-create-keymaps))
	  keymap pop func)
      (static-cond ((< emacs-major-version 20)
		    ;; For Emacsen from 19.34 down to 19.28.
		    ;; Seems the first item in MENU will be ignored.
		    (or (keymapp menu)
			(setq menu
			      (append (list ""  ;; This will be ignored.
					    (or title "Popup Menu")
					    "-----"
					    "-----")
				      (cdr menu))))
		    (setq keymap
			  (if (keymapp menu)
			      (append (list 'keymap
					    (if title
						`(nil ,title)
					      '(nil "Popup Menu"))
					    '(nil "")
					    '(nil ""))
				      (cdr menu))
			    (funcall menu-func (car menu) (cdr menu)))))
		   (t
		    (setq keymap
			  (if (keymapp menu)
			      menu
			    (funcall menu-func (car menu) (cdr menu))))))
      ;; Display the popup menu.
      (if (and (setq pop (x-popup-menu t keymap))
	       (setq func (lookup-key keymap
				      (apply 'vector pop))))
	  (funcall func))))))

(defun gnus-offline-get-menu-items (list)
  (let (result)
    (dolist (elem list)
      (setq result
	    (nconc result
		   (list (if (listp elem)
			     (progn
			       (setcar elem (gnus-offline-gettext (car elem)))
			       (vconcat elem))
			   elem)))))
    result))

(defvar gnus-offline-menu
  (gnus-offline-get-menu-items
   '((menu-1 gnus-offline-toggle-articles-to-fetch t)
     (menu-2 gnus-offline-toggle-on/off-send-mail t)
     (menu-3 gnus-offline-set-auto-ppp t)
     "----"
     (menu-4 gnus-agent-expire
	     (eq gnus-offline-news-fetch-method 'nnagent))
     (menu-5 gnus-offline-set-interval-time t)
     "----"
     (menu-6 gnus-offline-set-unplugged-state gnus-offline-connected)
     "----"
     (menu-7 gnus-ofsetup-customize t))))

(defun gnus-offline-define-menu-on-miee ()
  "*Set and change menu bar on MIEE menu."
  (let ((miee-menu
	 (gnus-offline-get-menu-items
	  '((menu-miee-1 news-spool-post t)
	    (menu-miee-2 mail-spool-send t)
	    "----"
	    (menu-miee-3 message-offline-state (not message-offline-state))
	    (menu-miee-4 message-online-state message-offline-state)
	    "----")))
	menu)
    (setq menu
	  (easy-menu-change
	   nil "Miee"
	   (append miee-menu
		   (list (cons "Gnus Offline" gnus-offline-menu)))))
    (static-if (featurep 'xemacs)
	(easy-menu-add menu))))
;;
;; define menu without miee.
;;
(defun gnus-offline-define-menu-on-agent ()
  "*Set menu bar on OFFLINE menu."
  (easy-menu-define
   gnus-offline-menu-on-agent gnus-group-mode-map "Gnus offline Menu"
   (cons "Offline" gnus-offline-menu))
  (static-if (featurep 'xemacs)
      (easy-menu-add gnus-offline-menu-on-agent)))
;;
;; Popup menu within the group buffer (under Emacs).
;;
(static-unless (featurep 'xemacs)
  (defun gnus-offline-popup-menu (event)
    "Popup menu for Gnus Offline."
    (interactive "e")
    (apply 'gnus-offline-popup
	   (if (boundp 'miee-popup-menu)
	       (list (or (assq 'keymap
			       (assq 'Miee (assq 'menu-bar global-map)))
			 miee-popup-menu)
		     "Miee")
	     (list (symbol-value 'gnus-offline-menu-on-agent)
		   "Offline")))))

;;
;; Timer Function
(defun gnus-offline-processed-by-timer ()
  "*Set timer interval."
  (let ((func (lambda () (call-interactively 'gnus-group-get-new-news)))
	(time gnus-offline-interval-time))
    (cond ((and (> time 0) (not gnus-offline-connected))
	   ;; Timer call
	   (gnus-demon-add-handler func time time))
	  ((= gnus-offline-interval-time 0)
	   (gnus-demon-remove-handler func t)))))
;;
;; Code for making Gnus and Gnus Offline cooperate with each other.
;;

;; Display `X-Gnus-Offline-Backend' message header aesthetically.
(eval-after-load "eword-decode"
  '(mime-set-field-decoder 'X-Gnus-Offline-Backend nil nil))

;; Enable key and menu definitions here.
(eval '(funcall 'gnus-offline-define-menu-and-key))

;;
;;
;;; gnus-offline.el ends here
