|
|
1.1 root 1: ;; File input and output over Internet using FTP
2: ;; Copyright (C) 1987 Free Software Foundation, Inc.
3: ;; Author [email protected].
4:
5: ;; This file is part of GNU Emacs.
6:
7: ;; GNU Emacs is distributed in the hope that it will be useful,
8: ;; but WITHOUT ANY WARRANTY. No author or distributor
9: ;; accepts responsibility to anyone for the consequences of using it
10: ;; or for whether it serves any particular purpose or works at all,
11: ;; unless he says so in writing. Refer to the GNU Emacs General Public
12: ;; License for full details.
13:
14: ;; Everyone is granted permission to copy, modify and redistribute
15: ;; GNU Emacs, but only under the conditions described in the
16: ;; GNU Emacs General Public License. A copy of this license is
17: ;; supposed to have been given to you along with GNU Emacs so you
18: ;; can know your rights and responsibilities. It should be in a
19: ;; file named COPYING. Among other things, the copyright notice
20: ;; and this notice must be preserved on all copies.
21:
22:
23: ;; you can turn this off by doing
24: ;; (setq ftp-password-alist 'compulsory-urinalysis)
25: (defvar ftp-password-alist () "Security sucks")
26:
27: (defun read-ftp-user-password (host user new)
28: (let (tem)
29: (if (and (not new)
30: (listp ftp-password-alist)
31: (setq tem (cdr (assoc host ftp-password-alist)))
32: (or (null user)
33: (string= user (car tem))))
34: tem
35: (or user
36: (progn
37: (setq tem (or (and (listp ftp-password-alist)
38: (car (cdr (assoc host ftp-password-alist))))
39: (user-login-name)))
40: (setq user (read-string (format
41: "User-name for %s (default \"%s\"): "
42: host tem)))
43: (if (equal user "") (setq user tem))))
44: (setq tem (cons user
45: ;; If you want to use some non-echoing string-reader,
46: ;; feel free to write it yourself. I don't care enough.
47: (read-string (format "Password for %s@%s: " user host)
48: (if (not (listp ftp-password-alist))
49: ""
50: (or (cdr (cdr (assoc host ftp-password-alist)))
51: (let ((l ftp-password-alist))
52: (catch 'foo
53: (while l
54: (if (string= (car (cdr (car l))) user)
55: (throw 'foo (cdr (cdr (car l))))
56: (setq l (cdr l))))
57: nil))
58: "")))))
59: (message "")
60: (if (and (listp ftp-password-alist)
61: (not (string= (cdr tem) "")))
62: (setq ftp-password-alist (cons (cons host tem)
63: ftp-password-alist)))
64: tem)))
65:
66: (defun ftp-read-file-name (prompt)
67: (let ((s ""))
68: (while (not (string-match "\\`[ \t]*\\([^ \t:]+\\)[ \t]*:\\(.+\\)\\'" s))
69: (setq s (read-string prompt s)))
70: (list (substring s (match-beginning 1) (match-end 1))
71: (substring s (match-beginning 2) (match-end 2)))))
72:
73:
74: (defun ftp-find-file (host file &optional user password)
75: "FTP to HOST to get FILE, logging in as USER with password PASSWORD.
76: Interactively, HOST and FILE are specified by reading a string with
77: a colon character separating the host from the filename.
78: USER and PASSWORD are defaulted from the values used when
79: last ftping from HOST (unless password-remembering is disabled).
80: Supply a password of the symbol `t' to override this default
81: (interactively, this is done by giving a prefix arg)"
82: (interactive
83: (append (ftp-read-file-name "FTP get host:file: ")
84: (list nil (not (null current-prefix-arg)))))
85: (ftp-find-file-or-directory host file t user password))
86:
87: (defun ftp-list-directory (host file &optional user password)
88: "FTP to HOST to list DIRECTORY, logging in as USER with password PASSWORD.
89: Interactively, HOST and FILE are specified by reading a string with
90: a colon character separating the host from the filename.
91: USER and PASSWORD are defaulted from the values used when
92: last ftping from HOST (unless password-remembering is disabled).
93: Supply a password of the symbol `t' to override this default
94: (interactively, this is done by giving a prefix arg)"
95: (interactive
96: (append (ftp-read-file-name "FTP get host:directory: ")
97: (list nil (not (null current-prefix-arg)))))
98: (ftp-find-file-or-directory host file nil user password))
99:
100: (defun ftp-find-file-or-directory (host file filep &optional user password)
101: "FTP to HOST to get FILE. Third arg is t for file, nil for directory.
102: Log in as USER with PASSWORD. If USER is nil or PASSWORD is nil or t,
103: we prompt for the user name and password."
104: (or (and user password (not (eq password t)))
105: (progn (setq user (read-ftp-user-password host user (eq password t))
106: password (cdr user)
107: user (car user))))
108: (let ((buffer (get-buffer-create (format "*ftp%s %s:%s*"
109: (if filep "" "-directory")
110: host file))))
111: (set-buffer buffer)
112: (let ((process (ftp-setup-buffer host file))
113: (case-fold-search nil))
114: (let ((win nil))
115: (unwind-protect
116: (if (setq win (ftp-login process host user password))
117: (message "Logged in")
118: (error "Ftp login lost"))
119: (or win (delete-process process))))
120: (message "Opening %s %s:%s..." (if filep "file" "directory")
121: host file)
122: (if (ftp-command process
123: (format "%s \"%s\" -\nquit\n" (if filep "get" "dir")
124: file)
125: "\\(150\\|125\\).*\n"
126: "200.*\n")
127: (progn (forward-line 1)
128: (let ((buffer-read-only nil))
129: (delete-region (point-min) (point)))
130: (message "Retrieving %s:%s in background. Bye!" host file)
131: (set-process-sentinel process
132: 'ftp-asynchronous-input-sentinel)
133: process)
134: (switch-to-buffer buffer)
135: (let ((buffer-read-only nil))
136: (insert-before-markers "<<<Ftp lost>>>"))
137: (delete-process process)
138: (error "Ftp %s:%s lost" host file)))))
139:
140:
141: (defun ftp-write-file (host file &optional user password)
142: "FTP to HOST to write FILE, logging in as USER with password PASSWORD.
143: Interactively, HOST and FILE are specified by reading a string with colon
144: separating the host from the filename.
145: USER and PASSWORD are defaulted from the values used when
146: last ftping from HOST (unless password-remembering is disabled).
147: Supply a password of the symbol `t' to override this default
148: (interactively, this is done by giving a prefix arg)"
149: (interactive
150: (append (ftp-read-file-name "FTP write host:file: ")
151: (list nil (not (null current-prefix-arg)))))
152: (or (and user password (not (eq password t)))
153: (progn (setq user (read-ftp-user-password host user (eq password t))
154: password (cdr user)
155: user (car user))))
156: (let ((buffer (get-buffer-create (format "*ftp %s:%s*" host file)))
157: (tmp (make-temp-name "/tmp/emacsftp")))
158: (write-region (point-min) (point-max) tmp)
159: (set-buffer buffer)
160: (make-local-variable 'ftp-temp-file-name)
161: (setq ftp-temp-file-name tmp)
162: (let ((process (ftp-setup-buffer host file))
163: (case-fold-search nil))
164: (let ((win nil))
165: (unwind-protect
166: (if (setq win (ftp-login process host user password))
167: (message "Logged in")
168: (error "Ftp login lost"))
169: (or win (delete-process process))))
170: (message "Opening file %s:%s..." host file)
171: (if (ftp-command process
172: (format "send \"%s\" \"%s\"\nquit\n" tmp file)
173: "150.*\n"
174: "200.*\n")
175: (progn (forward-line 1)
176: (let ((buffer-read-only nil))
177: (delete-region (point-min) (point)))
178: (message "Saving %s:%s in background. Bye!" host file)
179: (set-process-sentinel process
180: 'ftp-asynchronous-output-sentinel)
181: process)
182: (switch-to-buffer buffer)
183: (let ((buffer-read-only nil))
184: (insert-before-markers "<<<Ftp lost>>>"))
185: (delete-process process)
186: (error "Ftp write %s:%s lost" host file)))))
187:
188:
189: (defun ftp-setup-buffer (host file)
190: (fundamental-mode)
191: (and (get-buffer-process (current-buffer))
192: (progn (discard-input)
193: (if (y-or-n-p (format "Kill process \"%s\" in %s? "
194: (process-name (get-buffer-process
195: (current-buffer)))
196: (buffer-name (current-buffer))))
197: (while (get-buffer-process (current-buffer))
198: (kill-process (get-buffer-process (current-buffer))))
199: (error "Foo"))))
200: ;(buffer-flush-undo (current-buffer))
201: (setq buffer-read-only nil)
202: (erase-buffer)
203: (make-local-variable 'ftp-host)
204: (setq ftp-host host)
205: (make-local-variable 'ftp-file)
206: (setq ftp-file file)
207: (setq buffer-read-only t)
208: (start-process "ftp" (current-buffer) "ftp" "-i" "-n" "-g"))
209:
210:
211: (defun ftp-login (process host user password)
212: (message "FTP logging in as %s@%s..." user host)
213: (if (ftp-command process
214: (format "open %s\nuser %s %s\n" host user password)
215: "230.*\n"
216: "\\(Connected to \\|220\\|331\\).*\n")
217: t
218: (switch-to-buffer (process-buffer process))
219: (delete-process process)
220: (if (listp ftp-password-alist)
221: (setq ftp-password-alist (delq (assoc host ftp-password-alist)
222: ftp-password-alist)))
223: nil))
224:
225: (defun ftp-command (process command win ignore)
226: (process-send-string process command)
227: (let ((p 1))
228: (while (numberp p)
229: (cond ;((not (bolp)))
230: ((looking-at win)
231: (goto-char (point-max))
232: (setq p t))
233: ((looking-at "^ftp> \\|^\n")
234: (goto-char (match-end 0)))
235: ((looking-at ignore)
236: (forward-line 1))
237: ((not (search-forward "\n" nil t))
238: ;; the way asynchronous process-output fucks with (point)
239: ;; is really really disgusting.
240: (setq p (point))
241: (condition-case ()
242: (accept-process-output process)
243: (error nil))
244: (goto-char p))
245: (t
246: (setq p nil))))
247: p))
248:
249:
250: (defun ftp-asynchronous-input-sentinel (process msg)
251: (ftp-sentinel process msg t t))
252: (defun ftp-synchronous-input-sentinel (process msg)
253: (ftp-sentinel process msg nil t))
254: (defun ftp-asynchronous-output-sentinel (process msg)
255: (ftp-sentinel process msg t nil))
256: (defun ftp-synchronous-output-sentinel (process msg)
257: (ftp-sentinel process msg nil nil))
258:
259: (defun ftp-sentinel (process msg asynchronous input)
260: (cond ((null (buffer-name (process-buffer process)))
261: ;; deleted buffer
262: (set-process-buffer process nil))
263: ((and (eq (process-status process) 'exit)
264: (= (process-exit-status process) 0))
265: (save-excursion
266: (set-buffer (process-buffer process))
267: (let (msg
268: (r (if input "[0-9]+ bytes received in [0-9]+\\.[0-9]+ seconds.*$" "[0-9]+ bytes sent in [0-9]+\\.[0-9]+ seconds.*$"))
269: (buffer-read-only nil))
270: (goto-char (point-max))
271: (search-backward "226 ")
272: (if (looking-at r)
273: (search-backward "226 "))
274: (let ((p (point)))
275: (setq msg (concat (format "ftp %s %s:%s done"
276: (if input "read" "write")
277: ftp-host ftp-file)
278: (if (re-search-forward r nil t)
279: (concat ": " (buffer-substring
280: (match-beginning 0)
281: (match-end 0)))
282: "")))
283: (delete-region p (point-max))
284: (save-excursion
285: (set-buffer (get-buffer-create "*ftp log*"))
286: (let ((buffer-read-only nil))
287: (insert msg ?\n)))
288: (set-buffer-modified-p nil))
289: (if (not input)
290: (progn
291: (condition-case ()
292: (and (boundp 'ftp-temp-file-name)
293: ftp-temp-file-name
294: (delete-file ftp-temp-file-name))
295: (error nil))
296: (kill-buffer (current-buffer)))
297: ;; You don't want to look at this.
298: (let ((kludge (generate-new-buffer (format "%s:%s (ftp)"
299: ftp-host ftp-file))))
300: (setq kludge (prog1 (buffer-name kludge) (kill-buffer kludge)))
301: (rename-buffer kludge)
302: ;; ok, you can look again now.
303: (ftp-setup-write-file-hooks)))
304: (if (and asynchronous
305: ;(waiting-for-user-input-p)
306: )
307: (progn (message "%s" msg)
308: (sleep-for 2))))))
309: ((memq (process-status process) '(exit signal))
310: (save-excursion
311: (set-buffer (process-buffer process))
312: (setq msg (format "Ftp died (buffer %s): %s"
313: (buffer-name (current-buffer))
314: msg))
315: (let ((buffer-read-only nil))
316: (goto-char (point-max))
317: (insert ?\n ?\n msg))
318: (delete-process proc)
319: (set-buffer (get-buffer-create "*ftp log*"))
320: (let ((buffer-read-only nil))
321: (goto-char (point-max))
322: (insert msg))
323: (if (waiting-for-user-input-p)
324: (error "%s" msg))))))
325:
326: (defun ftp-setup-write-file-hooks ()
327: (let ((hooks write-file-hooks))
328: (make-local-variable 'write-file-hooks)
329: (setq write-file-hooks (append write-file-hooks
330: '(ftp-write-file-hook))))
331: (make-local-variable 'revert-buffer-function)
332: (setq revert-buffer-function 'ftp-revert-buffer)
333: (setq default-directory "/tmp/")
334: (setq buffer-file-name (concat default-directory
335: (make-temp-name
336: (buffer-name (current-buffer)))))
337: (setq buffer-read-only nil))
338:
339: (defun ftp-write-file-hook ()
340: (let ((process (ftp-write-file ftp-host ftp-file)))
341: (set-process-sentinel process 'ftp-synchronous-output-sentinel)
342: (message "FTP writing %s:%s..." ftp-host ftp-file)
343: (while (eq (process-status process) 'run)
344: (condition-case ()
345: (accept-process-output process)
346: (error nil)))
347: (and (eq (process-status process) 'exit)
348: (= (process-exit-status process) 0)
349: (set-buffer-modified-p nil)))
350: (message "Written")
351: t)
352:
353: (defun ftp-revert-buffer (&rest ignore)
354: (let ((process (ftp-find-file ftp-host ftp-file)))
355: (set-process-sentinel process 'ftp-synchronous-input-sentinel)
356: (message "FTP reverting %s:%s" ftp-host ftp-file)
357: (while (eq (process-status process) 'run)
358: (condition-case ()
359: (accept-process-output process)
360: (error nil)))
361: (and (eq (process-status process) 'exit)
362: (= (process-exit-status process) 0)
363: (set-buffer-modified-p nil))
364: (message "Reverted")))
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.