;;; rfcr.el - Interface to RFC repository at www.ietf.org

;; Copyright (C) 2001 Sami Salkosuo
;; Author: Sami Salkosuo 
;; Version: 0.3 Thu Oct 18 08:50:35 2001

;; This file is not part of GNU Emacs.

;; 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:
;;
;; This is EMACS interface to http://www.ietf.org/rfc.html RFC
;; repository.
;;
;; Installation:
;;
;; Add rfcr.el to your load path and add
;; (require 'rfcr)
;; to .emacs
;;
;; If using rfcr from behind proxy
;; (setq rfcr-proxy-host )
;; (setq rfcr-proxy-port )
;;
;; Usage:
;; 
;; M-x rfcr-index displays RFC index, if index is not 
;; saved in rfcr directory then it's loaded from the IETF.

;; M-x rfcr-index-reload gets RFC index from IETF and saves it
;; to rfcr directory.
;;
;; M-x rfcr-get gets specified RFC from IETF and saves it
;; to rfcr directory. 
;; 
;; M-x rfcr lists RFCs in rfcr directory. If used with
;; prefix lists RFCs in reverse order.
;;
;; CHANGES
;;
;; v. 0.3
;;  -added index button to local repository user interface
;;  -renamed rfcr-local to rfcr
;;  -fixed bug when viewing local repository containing RFC(s) < 1000
;;  -added get RFC field & button to local repository user interface
;;  -local repository user interface tweaks
;;
;; v. 0.2
;;  -changed function name rfcr-reload to rfcr-index-reload
;;  -added rfcr-local function
;;
(require 'widget)

(defvar rfcr-proxy-host nil
  "HTTP proxy host")

(defvar rfcr-proxy-port nil
  "HTTP proxy port")

(defvar rfcr-directory "~/.rfcr"
  "RFC directory")

(defvar rfcr-url "www.ietf.org/iesg/1rfc_index.txt"
  "RFC index url")

(defvar rfcr-local-prefix nil
  "")

(defvar	rfcr-get-widget nil
  "")

(defun rfcr-index ()
  "Shows stored RFC index."
  (interactive)
  (let (

	)
    (if (not (file-exists-p (concat rfcr-directory "/RFC-index.txt")))
	(progn
	  (rfcr-index-reload)
	  )
      (progn
	(find-file (concat rfcr-directory "/RFC-index.txt"))
	(setq buffer-read-only t)
	)
      )

    )
  )

(defun rfcr-index-reload ()
  "Loads RFC index from http://www.ietf.org/iesg/1rfc_index.txt and saves it to rfcr directory"
  (interactive)
  (let (
	(host)
	(port 80)
	(buffer)
	(tcp-connection)
	(request)
	(rfcr-url "www.ietf.org/iesg/1rfc_index.txt")
	(rfcr-host "www.ietf.org")
	(rfcr-file "/iesg/1rfc_index.txt")
	)
    ;;set proxy if needed
    (if rfcr-proxy-host
	(progn
	  (setq file (concat "http://" rfcr-url))
	  (setq host rfcr-proxy-host)
	  (setq port rfcr-proxy-port)
	  )
      (progn
	(setq host rfcr-host)
	(setq file rfcr-file)
	)
      )
    (setq buffer (get-buffer-create "*RFC Index*"))
    (set-buffer buffer)
    (erase-buffer)
    (goto-char 0)

    (setq tcp-connection
	  (open-network-stream
	   "GET process-name"
	   buffer
	   host
	   port
	   ))

    (set-marker (process-mark tcp-connection) (point-min))
    (set-process-sentinel tcp-connection 'rfcr-sentinel)
    
    (setq request (concat "GET " file " HTTP/1.0\n\n"))
    (process-send-string tcp-connection request)
    (rfcr-parse tcp-connection)
    (delete-process tcp-connection)
    (switch-to-buffer buffer)
    (if (not (file-exists-p rfcr-directory))
	(make-directory rfcr-directory)
      )
    (write-file (concat rfcr-directory "/RFC-index.txt"))
    (setq buffer-read-only t)
    )
  )

(defun rfcr-parse (process)
  ""
  (let (
	(buffer)
	(header-end)
	(msg)
	(i)
	)
    (setq i 0)
    (while (eq (process-status process) 'open)
      (sit-for 0 200)
      (setq msg (concat "Downloading RFC index" (make-string i ?. )))
      (if (>= (length msg) (frame-width))
	  (progn
	    (setq i 0)
	    (setq msg (concat "Downloading RFC index" (make-string i ?. )))
	    )
	)
      (message msg)
      (setq i (1+ i))
      )
    (setq buffer (get-buffer-create "*RFC Index*"))
    (goto-char 0)
    (setq header-end (re-search-forward "\n\n" nil t))
    (delete-region 1 header-end)
    (setq header-end (re-search-forward "~$" nil t))
    (delete-region 1 (+ 2 header-end))
    )
  )

(defun rfcr-sentinel (process string)
  "Process the results from the efine network connection.
process - The process object that is being notified.
string - The string that describes the notification."
  )

(defun rfcr-get (rfc-number)
  "Gets specified RFC. Uses: http://www.ietf.org/rfc/rfcNNNN.txt"
  (interactive "nRFC number (nnnn): ")
  (let (
	(host)
	(port 80)
	(buffer)
	(buffer-name)
	(tcp-connection)
	(request)
	(rfc-url "www.ietf.org/rfc/rfc")
	(rfc-host "www.ietf.org")
	(rfc-file "rfc/rfc")
	)
    (setq rfc-number (number-to-string rfc-number ))
    (if (file-exists-p (concat rfcr-directory "/RFC" rfc-number ".txt"))
	(progn
	  (find-file (concat rfcr-directory "/RFC" rfc-number ".txt"))
	  (setq buffer-read-only t)
	  )
      (progn
      
	;;set proxy if needed
	(if rfcr-proxy-host
	    (progn
	      (setq file (concat "http://" rfc-url rfc-number ".txt"))
	      (setq host rfcr-proxy-host)
	      (setq port rfcr-proxy-port)
	      )
	  (progn
	    (setq host rfc-host)
	    (setq file (concat rfc-file rfc-number ".txt"))
	    )
	  )
	(setq buffer-name (concat "*RFC " rfc-number "*"))
	(setq buffer (get-buffer-create buffer-name))
	(set-buffer buffer)
	(erase-buffer)
	(goto-char 0)

	(setq tcp-connection
	      (open-network-stream
	       "GET process-name"
	       buffer
	       host
	       port
	       ))

	(set-marker (process-mark tcp-connection) (point-min))
	(set-process-sentinel tcp-connection 'rfcr-sentinel)
    
	(setq request (concat "GET " file " HTTP/1.0\n\n"))
	(process-send-string tcp-connection request)
	(rfcr-parse-rfc buffer-name tcp-connection rfc-number)
	(delete-process tcp-connection)
	(switch-to-buffer buffer)
	(if (not (file-exists-p rfcr-directory))
	    (make-directory rfcr-directory)
	  )
	(while (< (length rfc-number) 4)
	  (setq rfc-number (concat "0" rfc-number))
	  )
	(write-file (concat rfcr-directory "/RFC" rfc-number ".txt"))
	(setq buffer-read-only t)
	)
      )
    )
      
  )

(defun rfcr-parse-rfc (buffer-name process rfc-number)
  ""
  (let (
	(buffer)
	(header-end)
	(msg)
	(i 0)
	)
    (setq i 0)
    (while (eq (process-status process) 'open)
      (sit-for 0 200)
      (setq msg (concat "Downloading RFC" rfc-number (make-string i ?. )))
      (if (>= (length msg) (frame-width))
	  (progn
	    (setq i 0)
	    (setq msg (concat "Downloading RFC" rfc-number (make-string i ?. )))
	    )
	)
      (message msg)
      (setq i (1+ i))
      )
    (setq buffer (get-buffer-create buffer-name))
    (goto-char 0)
    ;;delete http headers
    (setq header-end (re-search-forward "\n\n" nil t))
    (delete-region 1 header-end)

    ;;delete empty lines
    (setq header-end  (re-search-forward "^[a-zA-Z0-9]" nil t))
    (delete-region 1 (1- header-end))

    )  
  )

(defun rfcr (arg &optional current-widget)
  "Local RFC repository. Lists files in rfcr directory.
   If used with prefix displays repositiroy newest rfc first."
  (interactive "P")
  (let (
	(buffer)
	(index-buffer)
	(rfc-files)
	(rfc-file)
	(rfc-number)
	(rfc-title)
	(index)
	
	)
    (if (null current-widget)
	(setq current-widget 1)
      )

    (if (bufferp (get-buffer "*Local RFC repository*"))
	(kill-buffer "*Local RFC repository*")
      )
    (if (not (file-exists-p (concat rfcr-directory "/RFC-index.txt")))
	(progn
	  (message "RFC index not found. Download RFC index with M-x rfcr-index.")
	  )
      (progn
	(setq buffer (get-buffer-create "*Local RFC repository*"))
	(setq rfcr-local-prefix arg)
	(setq index-buffer (get-buffer-create "*Local RFC repository TEMP*"))
	(set-buffer index-buffer)
	(insert-file-contents (concat rfcr-directory "/RFC-index.txt"))

	(set-buffer buffer)
	(widget-insert "Local RFC repository\n")
	(widget-create 'push-button	
			 :notify (lambda (&rest ignore)
				   (rfcr-index))
				   ;;(find-file (concat rfcr-directory "/RFC-index.txt")))
			 "RFC index")
	(widget-insert "\t")
	(widget-create 'push-button		   
		       :notify (lambda (&rest ignore)
				 (if rfcr-local-prefix
				     (rfcr t 2)
				   (rfcr nil 2))
				 )
		       "Refresh")    
	;;(widget-insert "\t")
 	(widget-insert "\n\nRFC: ")
 	(setq rfcr-get-widget (widget-create 'editable-field
 		       :size 4
		       :notify (lambda (widget &rest ignore)
				 (widget-setup)
				 (setq rfcr-get-widget widget)
				 ;(message (widget-value widget))
				   )
 		       ))
	(widget-insert "\t")
	(widget-create 'push-button	
			 :notify (lambda (&rest ignore)
				   (rfcr-get (string-to-number (widget-value rfcr-get-widget)))
				   ;;(message  (concat "Value: " (widget-value rfcr-get-widget)))
				   )
			 "Get")
	(widget-insert "\n\n")	
	(widget-create 'push-button		   
		       :notify (lambda (&rest ignore)
				 (if rfcr-local-prefix
				     (rfcr (not t) 5)
				   (rfcr (not nil) 5))
				 )
		       "Reverse list")
	(widget-insert "\n")	
	(setq rfc-files (directory-files rfcr-directory nil "RFC[0-9]+\\.txt\\'"))
	(if rfcr-local-prefix
	    (setq rfc-files (reverse rfc-files))
	  )
	(while rfc-files
	  (setq rfc-file (car rfc-files))
	  (setq rfc-number (substring rfc-file 3 (string-match "\\." rfc-file)))
	  (widget-create 'push-button
			 :value rfc-number
			 :notify (lambda (widget &rest ignore)
				   (find-file (concat rfcr-directory "/RFC" (widget-value widget) ".txt")))
			 rfc-number)
	  ;;get rfc title
	  (set-buffer index-buffer)
	  (goto-char 0)
	  (setq index (re-search-forward (concat "^" rfc-number ) nil t))
	  (setq rfc-title (buffer-substring index (re-search-forward "\\. " nil t)))
	  (set-buffer buffer)
	  (widget-insert "\t" rfc-title "\n")
	  (setq rfc-files (cdr rfc-files))
	  )
	(widget-minor-mode 1)
	(widget-setup)
	(goto-char 0)
	(widget-forward current-widget)
	(kill-buffer index-buffer)
	(switch-to-buffer buffer)
	)
      )
    )
  )

(provide 'rfcr)