diff lisp/efs/efs-netrc.el @ 22:8fc7fe29b841 r19-15b94

Import from CVS: tag r19-15b94
author cvs
date Mon, 13 Aug 2007 08:50:29 +0200
parents
children 7e54bd776075
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lisp/efs/efs-netrc.el	Mon Aug 13 08:50:29 2007 +0200
@@ -0,0 +1,391 @@
+;; -*-Emacs-Lisp-*-
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;
+;; File:         efs-netrc.el
+;; Release:      $efs release: 1.15 $
+;; Version:      $Revision: 1.1 $
+;; RCS:          
+;; Description:  Parses ~/.netrc file, and does completion in /.
+;; Author:       Sandy Rutherford <sandy@ibm550.sissa.it>
+;; Created:      Fri Jan 28 19:32:47 1994 by sandy on ibm550
+;; Modified:     Sun Nov 27 18:38:50 1994 by sandy on gandalf
+;; Language:     Emacs-Lisp
+;;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+;;; This file is part of efs. See efs.el for copyright
+;;; (it's copylefted) and warrranty (there isn't one) information.
+
+;;;; ------------------------------------------------------------
+;;;; Provisions and requirements.
+;;;; ------------------------------------------------------------
+
+(provide 'efs-netrc)
+(require 'efs-cu)
+(require 'efs-ovwrt)
+(require 'passwd)
+(require 'efs-fnh)
+
+;;;; ------------------------------------------------------------
+;;;; Internal Variables
+;;;; ------------------------------------------------------------
+
+(defconst efs-netrc-version
+  (concat (substring "$efs release: 1.15 $" 14 -2)
+	  "/"
+	  (substring "$Revision: 1.1 $" 11 -2)))
+
+;; Make the byte compiler happy.
+(defvar dired-directory)
+
+;;;; ------------------------------------------------------------
+;;;; Use configuration variables.
+;;;; ------------------------------------------------------------
+
+(defvar efs-netrc-filename "~/.netrc"
+  "*File in .netrc format to search for passwords.
+If you encrypt this file, name it something other than ~/.netrc. Otherwise,
+ordinary FTP will bomb.
+
+If you have any cryption package running off of find-file-hooks
+(such as crypt.el or crypt++.el), efs will use it to decrypt this file.
+Encrypting this file is a good idea!")
+
+(defvar efs-disable-netrc-security-check nil
+  "*If non-nil avoid checking permissions for `efs-netrc-filename'.")
+
+;;;; ------------------------------------------------------------
+;;;; Host / User / Account mapping support.
+;;;; ------------------------------------------------------------
+
+(defun efs-set-passwd (host user passwd)
+  "For a given HOST and USER, set or change the associated PASSWORD."
+  (interactive (list (read-string "Host: ")
+		     (read-string "User: ")
+		     (read-passwd "Password: ")))
+  (efs-set-host-user-property host user 'passwd
+			      (and passwd (efs-code-string passwd))))
+
+(defun efs-set-account (host user minidisk account)
+  "Given HOST, USER, and MINIDISK, set or change the ACCOUNT password.
+The minidisk is only relevant for CMS. If minidisk is irrelevant,
+give the null string for it. In lisp programs, give the minidisk as nil."
+  (interactive (efs-save-match-data
+		 (let* ((path (or buffer-file-name
+				  (and (eq major-mode 'dired-mode)
+				       dired-directory)))
+			(parsed (and path (efs-ftp-path path)))
+			(default-host (car parsed))
+			(default-user (nth 1 parsed))
+			(default-minidisk
+			  (and parsed
+			       (eq (efs-host-type default-host) 'cms)
+			       (string-match "^/[^/]+/" (nth 2 parsed))
+			       (substring (nth 2 parsed) 1
+					  (1- (match-end 0)))))
+			(host (read-string "Host: " default-host))
+			(user (read-string "User: " default-user))
+			(minidisk
+			 (read-string
+			  "Minidisk (enter null string if inapplicable): "
+			  default-minidisk))
+			(account (read-passwd "Account password: ")))
+		   (if (string-match "^ *$" minidisk)
+		       (setq minidisk nil))
+		   (list host user minidisk account))))
+  (and account (setq account (efs-code-string account)))
+  (if minidisk
+      (efs-put-hash-entry (concat (downcase host) "/" user "/" minidisk)
+			  account efs-minidisk-hashtable)
+    (efs-set-host-user-property host user 'account account)))
+
+;;;; ------------------------------------------------------------
+;;;; Parsing the ~/.netrc.
+;;;; ------------------------------------------------------------
+
+(defconst efs-netrc-modtime nil)
+;; Last modified time of the netrc file from file-attributes.
+
+(defun efs-netrc-next-token ()
+  ;; Gets the next token plus it's value.
+  ;; Returns \(token value-1 value-2 ...\)
+  (skip-chars-forward " \t\n")
+  (while (char-equal  (following-char) ?#)
+    (forward-line 1)
+    (skip-chars-forward " \t\n"))
+  (let ((tok (and (not (eobp))
+		  (downcase (buffer-substring
+			     (point)
+			     (progn
+			       (skip-chars-forward "^ \n\t")
+			       (point)))))))
+    (cond
+     ((null tok) nil)
+     ((string-equal tok "default")
+      (list tok))
+     ((member tok (list "machine" "login" "password" "account"))
+      (list tok (efs-netrc-read-token-value)))
+     ((string-equal tok "minidisk")
+      (list tok (efs-netrc-read-token-value)
+	    (efs-netrc-read-token-value)))
+     ((string-equal tok "include")
+      (let ((start (- (point) 7))
+	    (path (expand-file-name (efs-netrc-read-token-value))))
+	(delete-region start (point))
+	(save-excursion (insert (efs-netrc-get-include path))))
+      (efs-netrc-next-token))
+     ;; Deal with tokens that we skip
+     ((string-equal tok "macdef")
+      (efs-save-match-data
+	(search-forward "\n\n" nil 'move))
+      (if (eobp)
+	  nil
+	(efs-netrc-next-token)))
+     (t (error "efs netrc file error: Invalid token %s." tok)))))
+
+(defun efs-netrc-read-token-value ()
+  ;; Read the following word as a token value.
+  (skip-chars-forward " \t\n")
+  (while (char-equal (following-char) ?#)
+    (forward-line 1)
+    (skip-chars-forward " \t\n"))
+  (if (eq (following-char) ?\")	;quoted token value
+      (prog2
+       (forward-char 1)
+       (buffer-substring (point)
+			 (progn (skip-chars-forward "^\"") (point)))
+       (forward-char 1))
+    (buffer-substring (point)
+		      (progn (skip-chars-forward "^ \n\t") (point)))))
+
+(defun efs-netrc-get-include (path)
+  ;; Returns the text of an include file.
+  (let ((buff (create-file-buffer path)))
+    (unwind-protect
+	(save-excursion
+	  (set-buffer buff)
+	  (setq buffer-file-name path
+		default-directory (file-name-directory path))
+	  (insert-file-contents path)
+	  (normal-mode t)
+	  (mapcar 'funcall find-file-hooks)
+	  (setq buffer-file-name nil)
+	  (buffer-string))
+      (condition-case nil
+	  ;; go through this rigamoroll, because who knows
+	  ;; where an interrupt in find-file-hooks leaves us.
+	  (save-excursion
+	    (set-buffer buff)
+	    (set-buffer-modified-p nil)
+	    (passwd-kill-buffer buff))
+	(error nil)))))
+
+(defun efs-parse-netrc-group (&optional machine)
+  ;; Extract the values for the tokens  "machine", "login", "password",
+  ;; "account" and "minidisk"  in the current buffer.  If successful, 
+  ;; record the information found.
+  (let (data login)
+    ;; Get a machine token.
+    (if (or machine (setq data (efs-netrc-next-token)))
+	(progn
+	  (cond
+	   (machine) ; noop
+	   ((string-equal (car data) "machine")
+	    (setq machine (nth 1 data)))
+	   ((string-equal (car data) "default")
+	    (setq machine 'default))
+	   (error
+	    "efs netrc file error: %s"
+	    "Token group must start with machine or default."))
+	  ;; Next look for a login token.
+	  (setq data (efs-netrc-next-token))
+	  (cond
+	   ((null data)
+	    ;; This just interns in the hashtable for completion to
+	    ;; work.  The username gets set later by efs-get-user.
+	    (if (stringp machine) (efs-set-user machine nil))
+	    nil)
+	   ((string-equal (car data) "machine")
+	    (if (stringp machine) (efs-set-user machine nil))
+	    (nth 1 data))
+	   ((string-equal (car data) "default")
+	    'default)
+	   ((not (string-equal (car data) "login"))
+	    (error "efs netrc file error: Expected login token for %s."
+		   (if (eq machine 'default)
+		       "default"
+		     (format "machine %s" machine))))
+	   (t
+	    (setq login (nth 1 data))
+	    (if (eq machine 'default)
+		(setq efs-default-user login)
+	      (efs-set-user machine login)
+	      ;; Since an explicit login entry is given, intern an entry
+	      ;; in the efs-host-user-hashtable for completion purposes.
+	      (efs-set-host-user-property machine login nil nil))
+	    (while (and (setq data (efs-netrc-next-token))
+			(not (or (string-equal (car data) "machine")
+				 (string-equal (car data) "default"))))
+	      (cond
+	       ((string-equal (car data) "password")
+		(if (eq machine 'default)
+		    (setq efs-default-password (nth 1 data))
+		  (efs-set-passwd machine login (nth 1 data))))
+	       ((string-equal (car data) "account")
+		(if (eq machine 'default)
+		    (setq efs-default-account (nth 1 data))
+		  (efs-set-account machine login nil (nth 1 data))))
+	       ((string-equal (car data) "minidisk")
+		(if (eq machine 'default)
+		    (error "efs netrc file error: %s."
+			   "Minidisk token is not allowed for default entry.")
+		  (apply 'efs-set-account machine login (cdr data))))
+	       ((string-equal (car data) "login")
+		(error "efs netrc file error: Second login token for %s."
+		       (if (eq machine 'default)
+			   "default"
+			 (format "machine %s" machine))))))
+	    (and data (if (string-equal (car data) "machine")
+			  (nth 1 data)
+			'default))))))))
+
+(defun efs-parse-netrc ()
+  "Parse the users ~/.netrc file, or file specified `by efs-netrc-filename'.
+If the file exists and has the correct permissions then extract the
+\`machine\', \`login\', \`password\', \`account\', and \`minidisk\'
+information from within."
+  (interactive)
+  (and efs-netrc-filename
+       (let* ((file (expand-file-name efs-netrc-filename))
+	      ;; Set to nil to avoid an infinite recursion if the
+	      ;; .netrc file is remote.
+	      (efs-netrc-filename nil)
+	      (file (efs-chase-symlinks file))
+	      (attr (file-attributes file))
+	      netrc-buffer next)
+	 (if (or (interactive-p) ; If interactive, really do something.
+		 (and attr	 ; file exists.
+		      ;; file changed
+		      (not (equal (nth 5 attr) efs-netrc-modtime))))
+	     (efs-save-match-data
+	       (or efs-disable-netrc-security-check
+		   (and (eq (nth 2 attr) (user-uid)) ; Same uids.
+			(string-match ".r..------" (nth 8 attr)))
+		   (efs-netrc-scream-and-yell file attr))
+	       (unwind-protect
+		   (save-excursion
+		     ;; we are cheating a bit here.  I'm trying to do the
+		     ;; equivalent of find-file on the .netrc file, but
+		     ;; then nuke it afterwards.
+		     ;; with the bit of logic below we should be able to have
+		     ;; encrypted .netrc files.
+		     (set-buffer (setq netrc-buffer
+				       (generate-new-buffer "*ftp-.netrc*")))
+		     (insert-file-contents file)
+		     (setq buffer-file-name file)
+		     (setq default-directory (file-name-directory file))
+		     (normal-mode t)
+		     (mapcar 'funcall find-file-hooks)
+		     (setq buffer-file-name nil)
+		     (goto-char (point-min))
+		     (while (and (not (eobp))
+				 (setq next (efs-parse-netrc-group next)))))
+		 (condition-case nil
+		     ;; go through this rigamoroll, because we knows
+		     ;; where an interrupt in find-file-hooks leaves us.
+		     (save-excursion
+		       (set-buffer netrc-buffer)
+		       (set-buffer-modified-p nil)
+		       (passwd-kill-buffer netrc-buffer))
+		   (error nil)))
+	       (setq efs-netrc-modtime (nth 5 attr)))))))
+
+(defun efs-netrc-scream-and-yell (file attr)
+  ;; Complain about badly protected netrc files.
+  (let* ((bad-own (/= (nth 2 attr) (user-uid)))
+	 (modes (nth 8 attr))
+	 (bad-protect (not (string-match ".r..------" modes))))
+    (if (or bad-own bad-protect)
+	(save-window-excursion
+	  (with-output-to-temp-buffer "*Help*"
+	    (if bad-own
+		(princ
+		 (format
+		  "Beware that your .netrc file %s is not owned by you.\n"
+		  file)))
+	    (if bad-protect
+		(progn
+		  (if bad-own
+		      (princ "\nAlso,")
+		    (princ "Beware that"))
+		  (princ
+		   " your .netrc file ")
+		  (or bad-own (princ (concat file " ")))
+		  (princ
+		   (format
+		    "has permissions\n %s.\n" modes))))
+	    (princ
+	     "\nIf this is intentional, then setting \
+efs-disable-netrc-security-check
+to t will inhibit this warning in the future.\n"))
+	  (select-window (get-buffer-window "*Help*"))
+	  (enlarge-window (- (count-lines (point-min) (point-max))
+			     (window-height) -1))
+	  (if (and bad-protect
+		   (y-or-n-p (format "Set permissions on %s to 600? " file)))
+	      (set-file-modes file 384))))))
+
+;;;; ----------------------------------------------------------------
+;;;; Completion in the root directory.
+;;;; ----------------------------------------------------------------
+
+(defun efs-generate-root-prefixes ()
+  "Return a list of prefixes of the form \"user@host:\".
+Used when completion is done in the root directory."
+  (efs-parse-netrc)
+  (efs-save-match-data
+    (let (res)
+      (efs-map-hashtable
+       (function
+	(lambda (key value)
+	  (if (string-match "^[^/]+\\(/\\).+$" key)
+	      ;; efs-passwd-hashtable may have entries of the type
+	      ;; "machine/" to indicate a password assigned to the default
+	      ;; user for "machine". Don't use these entries for completion.
+	      (let ((host (substring key 0 (match-beginning 1)))
+		    (user (substring key (match-end 1))))
+		(setq res (cons (list (format
+				       efs-path-user-at-host-format
+				       user host))
+				res))))))
+       efs-host-user-hashtable)
+      (efs-map-hashtable
+       (function (lambda (host user)
+		   (setq res (cons (list (format efs-path-host-format
+						 host))
+				   res))))
+       efs-host-hashtable)
+      (if (and (null res)
+	       (string-match "^1[0-8]\\.\\|^[0-9]\\." emacs-version))
+	  (list nil)
+	res))))
+
+(defun efs-root-file-name-all-completions (file dir)
+  ;; Generates all completions in the root directory.
+  (let ((file-name-handler-alist (efs-file-name-handler-alist-sans-fn
+				  'efs-root-handler-function)))
+    (nconc (all-completions file (efs-generate-root-prefixes))
+	   (file-name-all-completions file dir))))
+
+
+(defun efs-root-file-name-completion (file dir)
+  ;; Calculates completions in the root directory to include remote hosts.
+  (let ((file-name-handler-alist (efs-file-name-handler-alist-sans-fn
+				  'efs-root-handler-function)))
+    (try-completion
+     file
+     (nconc (efs-generate-root-prefixes)
+	    (mapcar 'list (file-name-all-completions file "/"))))))
+
+
+;;; end of efs-netrc.el