comparison 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
comparison
equal deleted inserted replaced
21:b88636d63495 22:8fc7fe29b841
1 ;; -*-Emacs-Lisp-*-
2 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
3 ;;
4 ;; File: efs-netrc.el
5 ;; Release: $efs release: 1.15 $
6 ;; Version: $Revision: 1.1 $
7 ;; RCS:
8 ;; Description: Parses ~/.netrc file, and does completion in /.
9 ;; Author: Sandy Rutherford <sandy@ibm550.sissa.it>
10 ;; Created: Fri Jan 28 19:32:47 1994 by sandy on ibm550
11 ;; Modified: Sun Nov 27 18:38:50 1994 by sandy on gandalf
12 ;; Language: Emacs-Lisp
13 ;;
14 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
15
16 ;;; This file is part of efs. See efs.el for copyright
17 ;;; (it's copylefted) and warrranty (there isn't one) information.
18
19 ;;;; ------------------------------------------------------------
20 ;;;; Provisions and requirements.
21 ;;;; ------------------------------------------------------------
22
23 (provide 'efs-netrc)
24 (require 'efs-cu)
25 (require 'efs-ovwrt)
26 (require 'passwd)
27 (require 'efs-fnh)
28
29 ;;;; ------------------------------------------------------------
30 ;;;; Internal Variables
31 ;;;; ------------------------------------------------------------
32
33 (defconst efs-netrc-version
34 (concat (substring "$efs release: 1.15 $" 14 -2)
35 "/"
36 (substring "$Revision: 1.1 $" 11 -2)))
37
38 ;; Make the byte compiler happy.
39 (defvar dired-directory)
40
41 ;;;; ------------------------------------------------------------
42 ;;;; Use configuration variables.
43 ;;;; ------------------------------------------------------------
44
45 (defvar efs-netrc-filename "~/.netrc"
46 "*File in .netrc format to search for passwords.
47 If you encrypt this file, name it something other than ~/.netrc. Otherwise,
48 ordinary FTP will bomb.
49
50 If you have any cryption package running off of find-file-hooks
51 (such as crypt.el or crypt++.el), efs will use it to decrypt this file.
52 Encrypting this file is a good idea!")
53
54 (defvar efs-disable-netrc-security-check nil
55 "*If non-nil avoid checking permissions for `efs-netrc-filename'.")
56
57 ;;;; ------------------------------------------------------------
58 ;;;; Host / User / Account mapping support.
59 ;;;; ------------------------------------------------------------
60
61 (defun efs-set-passwd (host user passwd)
62 "For a given HOST and USER, set or change the associated PASSWORD."
63 (interactive (list (read-string "Host: ")
64 (read-string "User: ")
65 (read-passwd "Password: ")))
66 (efs-set-host-user-property host user 'passwd
67 (and passwd (efs-code-string passwd))))
68
69 (defun efs-set-account (host user minidisk account)
70 "Given HOST, USER, and MINIDISK, set or change the ACCOUNT password.
71 The minidisk is only relevant for CMS. If minidisk is irrelevant,
72 give the null string for it. In lisp programs, give the minidisk as nil."
73 (interactive (efs-save-match-data
74 (let* ((path (or buffer-file-name
75 (and (eq major-mode 'dired-mode)
76 dired-directory)))
77 (parsed (and path (efs-ftp-path path)))
78 (default-host (car parsed))
79 (default-user (nth 1 parsed))
80 (default-minidisk
81 (and parsed
82 (eq (efs-host-type default-host) 'cms)
83 (string-match "^/[^/]+/" (nth 2 parsed))
84 (substring (nth 2 parsed) 1
85 (1- (match-end 0)))))
86 (host (read-string "Host: " default-host))
87 (user (read-string "User: " default-user))
88 (minidisk
89 (read-string
90 "Minidisk (enter null string if inapplicable): "
91 default-minidisk))
92 (account (read-passwd "Account password: ")))
93 (if (string-match "^ *$" minidisk)
94 (setq minidisk nil))
95 (list host user minidisk account))))
96 (and account (setq account (efs-code-string account)))
97 (if minidisk
98 (efs-put-hash-entry (concat (downcase host) "/" user "/" minidisk)
99 account efs-minidisk-hashtable)
100 (efs-set-host-user-property host user 'account account)))
101
102 ;;;; ------------------------------------------------------------
103 ;;;; Parsing the ~/.netrc.
104 ;;;; ------------------------------------------------------------
105
106 (defconst efs-netrc-modtime nil)
107 ;; Last modified time of the netrc file from file-attributes.
108
109 (defun efs-netrc-next-token ()
110 ;; Gets the next token plus it's value.
111 ;; Returns \(token value-1 value-2 ...\)
112 (skip-chars-forward " \t\n")
113 (while (char-equal (following-char) ?#)
114 (forward-line 1)
115 (skip-chars-forward " \t\n"))
116 (let ((tok (and (not (eobp))
117 (downcase (buffer-substring
118 (point)
119 (progn
120 (skip-chars-forward "^ \n\t")
121 (point)))))))
122 (cond
123 ((null tok) nil)
124 ((string-equal tok "default")
125 (list tok))
126 ((member tok (list "machine" "login" "password" "account"))
127 (list tok (efs-netrc-read-token-value)))
128 ((string-equal tok "minidisk")
129 (list tok (efs-netrc-read-token-value)
130 (efs-netrc-read-token-value)))
131 ((string-equal tok "include")
132 (let ((start (- (point) 7))
133 (path (expand-file-name (efs-netrc-read-token-value))))
134 (delete-region start (point))
135 (save-excursion (insert (efs-netrc-get-include path))))
136 (efs-netrc-next-token))
137 ;; Deal with tokens that we skip
138 ((string-equal tok "macdef")
139 (efs-save-match-data
140 (search-forward "\n\n" nil 'move))
141 (if (eobp)
142 nil
143 (efs-netrc-next-token)))
144 (t (error "efs netrc file error: Invalid token %s." tok)))))
145
146 (defun efs-netrc-read-token-value ()
147 ;; Read the following word as a token value.
148 (skip-chars-forward " \t\n")
149 (while (char-equal (following-char) ?#)
150 (forward-line 1)
151 (skip-chars-forward " \t\n"))
152 (if (eq (following-char) ?\") ;quoted token value
153 (prog2
154 (forward-char 1)
155 (buffer-substring (point)
156 (progn (skip-chars-forward "^\"") (point)))
157 (forward-char 1))
158 (buffer-substring (point)
159 (progn (skip-chars-forward "^ \n\t") (point)))))
160
161 (defun efs-netrc-get-include (path)
162 ;; Returns the text of an include file.
163 (let ((buff (create-file-buffer path)))
164 (unwind-protect
165 (save-excursion
166 (set-buffer buff)
167 (setq buffer-file-name path
168 default-directory (file-name-directory path))
169 (insert-file-contents path)
170 (normal-mode t)
171 (mapcar 'funcall find-file-hooks)
172 (setq buffer-file-name nil)
173 (buffer-string))
174 (condition-case nil
175 ;; go through this rigamoroll, because who knows
176 ;; where an interrupt in find-file-hooks leaves us.
177 (save-excursion
178 (set-buffer buff)
179 (set-buffer-modified-p nil)
180 (passwd-kill-buffer buff))
181 (error nil)))))
182
183 (defun efs-parse-netrc-group (&optional machine)
184 ;; Extract the values for the tokens "machine", "login", "password",
185 ;; "account" and "minidisk" in the current buffer. If successful,
186 ;; record the information found.
187 (let (data login)
188 ;; Get a machine token.
189 (if (or machine (setq data (efs-netrc-next-token)))
190 (progn
191 (cond
192 (machine) ; noop
193 ((string-equal (car data) "machine")
194 (setq machine (nth 1 data)))
195 ((string-equal (car data) "default")
196 (setq machine 'default))
197 (error
198 "efs netrc file error: %s"
199 "Token group must start with machine or default."))
200 ;; Next look for a login token.
201 (setq data (efs-netrc-next-token))
202 (cond
203 ((null data)
204 ;; This just interns in the hashtable for completion to
205 ;; work. The username gets set later by efs-get-user.
206 (if (stringp machine) (efs-set-user machine nil))
207 nil)
208 ((string-equal (car data) "machine")
209 (if (stringp machine) (efs-set-user machine nil))
210 (nth 1 data))
211 ((string-equal (car data) "default")
212 'default)
213 ((not (string-equal (car data) "login"))
214 (error "efs netrc file error: Expected login token for %s."
215 (if (eq machine 'default)
216 "default"
217 (format "machine %s" machine))))
218 (t
219 (setq login (nth 1 data))
220 (if (eq machine 'default)
221 (setq efs-default-user login)
222 (efs-set-user machine login)
223 ;; Since an explicit login entry is given, intern an entry
224 ;; in the efs-host-user-hashtable for completion purposes.
225 (efs-set-host-user-property machine login nil nil))
226 (while (and (setq data (efs-netrc-next-token))
227 (not (or (string-equal (car data) "machine")
228 (string-equal (car data) "default"))))
229 (cond
230 ((string-equal (car data) "password")
231 (if (eq machine 'default)
232 (setq efs-default-password (nth 1 data))
233 (efs-set-passwd machine login (nth 1 data))))
234 ((string-equal (car data) "account")
235 (if (eq machine 'default)
236 (setq efs-default-account (nth 1 data))
237 (efs-set-account machine login nil (nth 1 data))))
238 ((string-equal (car data) "minidisk")
239 (if (eq machine 'default)
240 (error "efs netrc file error: %s."
241 "Minidisk token is not allowed for default entry.")
242 (apply 'efs-set-account machine login (cdr data))))
243 ((string-equal (car data) "login")
244 (error "efs netrc file error: Second login token for %s."
245 (if (eq machine 'default)
246 "default"
247 (format "machine %s" machine))))))
248 (and data (if (string-equal (car data) "machine")
249 (nth 1 data)
250 'default))))))))
251
252 (defun efs-parse-netrc ()
253 "Parse the users ~/.netrc file, or file specified `by efs-netrc-filename'.
254 If the file exists and has the correct permissions then extract the
255 \`machine\', \`login\', \`password\', \`account\', and \`minidisk\'
256 information from within."
257 (interactive)
258 (and efs-netrc-filename
259 (let* ((file (expand-file-name efs-netrc-filename))
260 ;; Set to nil to avoid an infinite recursion if the
261 ;; .netrc file is remote.
262 (efs-netrc-filename nil)
263 (file (efs-chase-symlinks file))
264 (attr (file-attributes file))
265 netrc-buffer next)
266 (if (or (interactive-p) ; If interactive, really do something.
267 (and attr ; file exists.
268 ;; file changed
269 (not (equal (nth 5 attr) efs-netrc-modtime))))
270 (efs-save-match-data
271 (or efs-disable-netrc-security-check
272 (and (eq (nth 2 attr) (user-uid)) ; Same uids.
273 (string-match ".r..------" (nth 8 attr)))
274 (efs-netrc-scream-and-yell file attr))
275 (unwind-protect
276 (save-excursion
277 ;; we are cheating a bit here. I'm trying to do the
278 ;; equivalent of find-file on the .netrc file, but
279 ;; then nuke it afterwards.
280 ;; with the bit of logic below we should be able to have
281 ;; encrypted .netrc files.
282 (set-buffer (setq netrc-buffer
283 (generate-new-buffer "*ftp-.netrc*")))
284 (insert-file-contents file)
285 (setq buffer-file-name file)
286 (setq default-directory (file-name-directory file))
287 (normal-mode t)
288 (mapcar 'funcall find-file-hooks)
289 (setq buffer-file-name nil)
290 (goto-char (point-min))
291 (while (and (not (eobp))
292 (setq next (efs-parse-netrc-group next)))))
293 (condition-case nil
294 ;; go through this rigamoroll, because we knows
295 ;; where an interrupt in find-file-hooks leaves us.
296 (save-excursion
297 (set-buffer netrc-buffer)
298 (set-buffer-modified-p nil)
299 (passwd-kill-buffer netrc-buffer))
300 (error nil)))
301 (setq efs-netrc-modtime (nth 5 attr)))))))
302
303 (defun efs-netrc-scream-and-yell (file attr)
304 ;; Complain about badly protected netrc files.
305 (let* ((bad-own (/= (nth 2 attr) (user-uid)))
306 (modes (nth 8 attr))
307 (bad-protect (not (string-match ".r..------" modes))))
308 (if (or bad-own bad-protect)
309 (save-window-excursion
310 (with-output-to-temp-buffer "*Help*"
311 (if bad-own
312 (princ
313 (format
314 "Beware that your .netrc file %s is not owned by you.\n"
315 file)))
316 (if bad-protect
317 (progn
318 (if bad-own
319 (princ "\nAlso,")
320 (princ "Beware that"))
321 (princ
322 " your .netrc file ")
323 (or bad-own (princ (concat file " ")))
324 (princ
325 (format
326 "has permissions\n %s.\n" modes))))
327 (princ
328 "\nIf this is intentional, then setting \
329 efs-disable-netrc-security-check
330 to t will inhibit this warning in the future.\n"))
331 (select-window (get-buffer-window "*Help*"))
332 (enlarge-window (- (count-lines (point-min) (point-max))
333 (window-height) -1))
334 (if (and bad-protect
335 (y-or-n-p (format "Set permissions on %s to 600? " file)))
336 (set-file-modes file 384))))))
337
338 ;;;; ----------------------------------------------------------------
339 ;;;; Completion in the root directory.
340 ;;;; ----------------------------------------------------------------
341
342 (defun efs-generate-root-prefixes ()
343 "Return a list of prefixes of the form \"user@host:\".
344 Used when completion is done in the root directory."
345 (efs-parse-netrc)
346 (efs-save-match-data
347 (let (res)
348 (efs-map-hashtable
349 (function
350 (lambda (key value)
351 (if (string-match "^[^/]+\\(/\\).+$" key)
352 ;; efs-passwd-hashtable may have entries of the type
353 ;; "machine/" to indicate a password assigned to the default
354 ;; user for "machine". Don't use these entries for completion.
355 (let ((host (substring key 0 (match-beginning 1)))
356 (user (substring key (match-end 1))))
357 (setq res (cons (list (format
358 efs-path-user-at-host-format
359 user host))
360 res))))))
361 efs-host-user-hashtable)
362 (efs-map-hashtable
363 (function (lambda (host user)
364 (setq res (cons (list (format efs-path-host-format
365 host))
366 res))))
367 efs-host-hashtable)
368 (if (and (null res)
369 (string-match "^1[0-8]\\.\\|^[0-9]\\." emacs-version))
370 (list nil)
371 res))))
372
373 (defun efs-root-file-name-all-completions (file dir)
374 ;; Generates all completions in the root directory.
375 (let ((file-name-handler-alist (efs-file-name-handler-alist-sans-fn
376 'efs-root-handler-function)))
377 (nconc (all-completions file (efs-generate-root-prefixes))
378 (file-name-all-completions file dir))))
379
380
381 (defun efs-root-file-name-completion (file dir)
382 ;; Calculates completions in the root directory to include remote hosts.
383 (let ((file-name-handler-alist (efs-file-name-handler-alist-sans-fn
384 'efs-root-handler-function)))
385 (try-completion
386 file
387 (nconc (efs-generate-root-prefixes)
388 (mapcar 'list (file-name-all-completions file "/"))))))
389
390
391 ;;; end of efs-netrc.el