1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
|
;;; sftp.scm -- Procedures for working with SFTP.
;; Copyright (C) 2015 Artyom V. Poptsov <poptsov.artyom@gmail.com>
;;
;; This file is a part of Guile-SSH.
;;
;; Guile-SSH 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 3 of the
;; License, or (at your option) any later version.
;;
;; Guile-SSH 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 Guile-SSH. If not, see <http://www.gnu.org/licenses/>.
;;; Commentary:
;; This module contains SFTP API procedures.
;;
;; The module exports:
;; sftp-session?
;; make-sftp-session
;; sftp-init
;; sftp-get-session
;; sftp-get-error
;; sftp-mkdir
;; sftp-rmdir
;; sftp-mv
;; sftp-symlink
;; sftp-readlink
;; sftp-chmod
;; sftp-unlink
;; %make-sftp-session
;; %sftp-init
;; sftp-open
;; sftp-file?
;; call-with-remote-input-file
;; call-with-remote-output-file
;; with-input-from-remote-file
;; with-output-to-remote-file
;;
;; See the Info documentation for the detailed description of these
;; procedures.
;;; Code:
(define-module (ssh sftp)
#:use-module (ice-9 receive)
#:export (sftp-session?
make-sftp-session
sftp-init
sftp-get-session
sftp-get-error
sftp-mkdir
sftp-rmdir
sftp-mv
sftp-symlink
sftp-readlink
sftp-chmod
sftp-unlink
;; Low-level SFTP session procedures
%make-sftp-session
%sftp-init
;; File ports
sftp-open
sftp-file?
;; High-level operations on remote files
call-with-remote-input-file
call-with-remote-output-file
with-input-from-remote-file
with-output-to-remote-file))
;;; Low-level SFTP session procedures.
(define (%make-sftp-session ssh-session)
"Make a new SFTP session using an SSH-SESSION without initialization of the
session with a server. Throw 'guile-ssh-error' exception on an error."
(%gssh-make-sftp-session ssh-session))
(define (%sftp-init sftp-session)
"Initialize a SFTP-SESSION with the server. Throw 'guile-ssh-error'
exception on an error, return value is undefined."
(%gssh-sftp-init sftp-session))
;;; Main SFTP session API.
(define (make-sftp-session ssh-session)
"Make a new SFTP session using an SSH-SESSION, initialize the session with a
server. Return initialized SFTP session or throw 'guile-ssh-error' exception
on an error"
(let ((sftp-session (%gssh-make-sftp-session ssh-session)))
(%gssh-sftp-init sftp-session)
sftp-session))
(define (sftp-session? x)
"Return #t if X is a SFTP session, #f otherwise."
(%gssh-sftp-session? x))
(define (sftp-get-session sftp-session)
"Get the parent SSH session for a SFTP-SESSION."
(%gssh-sftp-get-session sftp-session))
(define (sftp-get-error sftp-session)
"Get the last SFTP error from a SFTP-SESSION. Return the error name as a symbol,
or throw 'guile-ssh-error' on if an error occured in the procedure itself."
(%gssh-sftp-get-error sftp-session))
(define* (sftp-mkdir sftp-session dirname #:optional (mode #o777))
"Create a directory DIRNAME using a SFTP-SESSION with permissions specified
by a MODE. The permissions of the created file are (MODE & ~umask). If the
MODE is omitted, #o777 is used."
(%gssh-sftp-mkdir sftp-session dirname mode))
(define (sftp-rmdir sftp-session dirname)
"Remove a directory DIRNAME. Throw 'guile-ssh-error' on an error. Return
value is undefined."
(%gssh-sftp-rmdir sftp-session dirname))
(define (sftp-mv sftp-session source dest)
"Move or rename a file SOURCE into a DEST. Throw 'guile-ssh-error' on an
error. Return value is undefined."
(%gssh-sftp-mv sftp-session source dest))
(define (sftp-symlink sftp-session target dest)
"Create a symbolic link to a TARGET in a DEST. Throw 'guile-ssh-error' on an
error. Return value is undefined."
(%gssh-sftp-symlink sftp-session target dest))
(define (sftp-readlink sftp-session path)
"Read the value of a symbolic link pointed by a PATH. Return the value or
'#f' on an error."
(%gssh-sftp-readlink sftp-session path))
(define* (sftp-chmod sftp-session filename mode)
"Change permissions of a FILENAME. Permissions are set to 'MODE & ~umask'.
Throw 'guile-ssh-error' on an error. Return value is undefined."
(%gssh-sftp-chmod sftp-session filename mode))
(define (sftp-unlink sftp-session filename)
"Unlink (delete) a FILENAME. Throw 'guile-ssh-error' on an error. Return
value is undefined."
(%gssh-sftp-unlink sftp-session filename))
;;; SFTP file API.
(define* (sftp-open sftp-session filename flags #:optional (mode #o666))
"Open a FILENAME with permissions specified by MODE, return an open file
port. Permissions are set to 'MODE & ~umask'; the default MODE is #o666.
Throw 'guile-ssh-error' on an error."
(%gssh-sftp-open sftp-session filename flags mode))
(define (sftp-file? x)
"Return #t if X is an SFTP file port, #f otherwise."
(%gssh-sftp-file? x))
;;; High-Level operations on remote files.
;; Those procedures are partly based on GNU Guile's 'r4rs.scm'; the goal is to
;; provide a convenient API similar to Guile I/O API.
(define (with-input-from-port port thunk)
(let ((swaports (lambda () (set! port (set-current-input-port port)))))
(dynamic-wind swaports thunk swaports)))
(define (with-output-to-port port thunk)
(let ((swaports (lambda () (set! port (set-current-output-port port)))))
(dynamic-wind swaports thunk swaports)))
(define (call-with-remote-input-file sftp-session filename proc)
"Call a PROC with a remote file port opened for input using an SFTP-SESSION.
PROC should be a procedure of one argument, FILENAME should be a string naming
a file. The behaviour is unspecified if a file already exists.
The procedure calls PROC with one argument: the port obtained by opening the
named remote file for input.
If the procedure returns, then the port is closed automatically and the values
yielded by the procedure are returned. If the procedure does not return, then
the port will not be closed automatically unless it is possible to prove that
the port will never again be used for a read or write operation."
(let ((input-file (sftp-open sftp-session filename O_RDONLY)))
(call-with-values
(lambda () (proc input-file))
(lambda vals
(close-port input-file)
(apply values vals)))))
(define (call-with-remote-output-file sftp-session filename proc)
"Call a PROC with a remote file port opened for output using an
SFTP-SESSION. PROC should be a procedure of one argument, FILENAME should be
a string naming a file. The behaviour is unspecified if a file already
exists.
The procedure calls PROC with one argument: the port obtained by opening the
named remote file for output.
If the procedure returns, then the port is closed automatically and the values
yielded by the procedure are returned. If the procedure does not return, then
the port will not be closed automatically unless it is possible to prove that
the port will never again be used for a read or write operation."
(let ((output-file-port (sftp-open sftp-session filename
(logior O_WRONLY O_CREAT))))
(call-with-values
(lambda () (proc output-file-port))
(lambda vals
(close-port output-file-port)
(apply values vals)))))
(define (with-input-from-remote-file sftp-session filename thunk)
"THUNK must be a procedure of no arguments, and FILENAME must be a string
naming a file. The file must already exist. The file is opened for input, an
input port connected to it is made the default value returned by
'current-input-port', and the THUNK is called with no arguments. When the
THUNK returns, the port is closed and the previous default is restored.
Returns the values yielded by THUNK. If an escape procedure is used to escape
from the continuation of these procedures, their behavior is implementation
dependent."
(call-with-remote-input-file sftp-session filename
(lambda (p) (with-input-from-port p thunk))))
(define (with-output-to-remote-file sftp-session filename thunk)
"THUNK must be a procedure of no arguments, and FILENAME must be a string
naming a file. The effect is unspecified if the file already exists. The
file is opened for output, an output port connected to it is made the default
value returned by 'current-output-port', and the THUNK is called with no
arguments. When the THUNK returns, the port is closed and the previous
default is restored. Returns the values yielded by THUNK. If an escape
procedure is used to escape from the continuation of these procedures, their
behavior is implementation dependent."
(call-with-remote-output-file sftp-session filename
(lambda (p) (with-output-to-port p thunk))))
;;; Load libraries.
(load-extension "libguile-ssh" "init_sftp_session")
(load-extension "libguile-ssh" "init_sftp_file")
;;; sftp-session.scm ends here.
|