... and there's a Unix port of PSCP. Ooh.
[u/mdw/putty] / unix / uxsftp.c
1 /*
2 * uxsftp.c: the Unix-specific parts of PSFTP and PSCP.
3 */
4
5 #include <sys/time.h>
6 #include <sys/types.h>
7 #include <sys/stat.h>
8 #include <fcntl.h>
9 #include <dirent.h>
10 #include <unistd.h>
11 #include <utime.h>
12 #include <errno.h>
13 #include <assert.h>
14
15 #include "putty.h"
16 #include "psftp.h"
17
18 /*
19 * In PSFTP our selects are synchronous, so these functions are
20 * empty stubs.
21 */
22 int uxsel_input_add(int fd, int rwx) { return 0; }
23 void uxsel_input_remove(int id) { }
24
25 char *x_get_default(const char *key)
26 {
27 return NULL; /* this is a stub */
28 }
29
30 void platform_get_x11_auth(char *display, int *protocol,
31 unsigned char *data, int *datalen)
32 {
33 /* Do nothing, therefore no auth. */
34 }
35
36 /*
37 * Default settings that are specific to PSFTP.
38 */
39 char *platform_default_s(const char *name)
40 {
41 return NULL;
42 }
43
44 int platform_default_i(const char *name, int def)
45 {
46 return def;
47 }
48
49 FontSpec platform_default_fontspec(const char *name)
50 {
51 FontSpec ret;
52 *ret.name = '\0';
53 return ret;
54 }
55
56 Filename platform_default_filename(const char *name)
57 {
58 Filename ret;
59 if (!strcmp(name, "LogFileName"))
60 strcpy(ret.path, "putty.log");
61 else
62 *ret.path = '\0';
63 return ret;
64 }
65
66 /*
67 * Stubs for the GUI feedback mechanism in Windows PSCP.
68 */
69 void gui_update_stats(char *name, unsigned long size,
70 int percentage, unsigned long elapsed,
71 unsigned long done, unsigned long eta,
72 unsigned long ratebs) {}
73 void gui_send_errcount(int list, int errs) {}
74 void gui_send_char(int is_stderr, int c) {}
75 void gui_enable(char *arg) {}
76
77
78 /*
79 * Set local current directory. Returns NULL on success, or else an
80 * error message which must be freed after printing.
81 */
82 char *psftp_lcd(char *dir)
83 {
84 if (chdir(dir) < 0)
85 return dupprintf("%s: chdir: %s", dir, strerror(errno));
86 else
87 return NULL;
88 }
89
90 /*
91 * Get local current directory. Returns a string which must be
92 * freed.
93 */
94 char *psftp_getcwd(void)
95 {
96 char *buffer, *ret;
97 int size = 256;
98
99 buffer = snewn(size, char);
100 while (1) {
101 ret = getcwd(buffer, size);
102 if (ret != NULL)
103 return ret;
104 if (errno != ERANGE) {
105 sfree(buffer);
106 return dupprintf("[cwd unavailable: %s]", strerror(errno));
107 }
108 /*
109 * Otherwise, ERANGE was returned, meaning the buffer
110 * wasn't big enough.
111 */
112 size = size * 3 / 2;
113 buffer = sresize(buffer, size, char);
114 }
115 }
116
117 struct RFile {
118 int fd;
119 };
120
121 RFile *open_existing_file(char *name, unsigned long *size,
122 unsigned long *mtime, unsigned long *atime)
123 {
124 int fd;
125 RFile *ret;
126
127 fd = open(name, O_RDONLY);
128 if (fd < 0)
129 return NULL;
130
131 ret = snew(RFile);
132 ret->fd = fd;
133
134 if (size || mtime || atime) {
135 struct stat statbuf;
136 if (fstat(fd, &statbuf) < 0) {
137 fprintf(stderr, "%s: stat: %s\n", name, strerror(errno));
138 memset(&statbuf, 0, sizeof(statbuf));
139 }
140
141 if (size)
142 *size = statbuf.st_size;
143
144 if (mtime)
145 *mtime = statbuf.st_mtime;
146
147 if (atime)
148 *atime = statbuf.st_atime;
149 }
150
151 return ret;
152 }
153
154 int read_from_file(RFile *f, void *buffer, int length)
155 {
156 return read(f->fd, buffer, length);
157 }
158
159 void close_rfile(RFile *f)
160 {
161 close(f->fd);
162 sfree(f);
163 }
164
165 struct WFile {
166 int fd;
167 char *name;
168 };
169
170 WFile *open_new_file(char *name)
171 {
172 int fd;
173 WFile *ret;
174
175 fd = open(name, O_CREAT | O_TRUNC | O_WRONLY, 0666);
176 if (fd < 0)
177 return NULL;
178
179 ret = snew(WFile);
180 ret->fd = fd;
181 ret->name = dupstr(name);
182
183 return ret;
184 }
185
186 int write_to_file(WFile *f, void *buffer, int length)
187 {
188 char *p = (char *)buffer;
189 int so_far = 0;
190
191 /* Keep trying until we've really written as much as we can. */
192 while (length > 0) {
193 int ret = write(f->fd, p, length);
194
195 if (ret < 0)
196 return ret;
197
198 if (ret == 0)
199 break;
200
201 p += ret;
202 length -= ret;
203 so_far += ret;
204 }
205
206 return so_far;
207 }
208
209 void set_file_times(WFile *f, unsigned long mtime, unsigned long atime)
210 {
211 struct utimbuf ut;
212
213 ut.actime = atime;
214 ut.modtime = mtime;
215
216 utime(f->name, &ut);
217 }
218
219 /* Closes and frees the WFile */
220 void close_wfile(WFile *f)
221 {
222 close(f->fd);
223 sfree(f->name);
224 sfree(f);
225 }
226
227 int file_type(char *name)
228 {
229 struct stat statbuf;
230
231 if (stat(name, &statbuf) < 0) {
232 if (errno != ENOENT)
233 fprintf(stderr, "%s: stat: %s\n", name, strerror(errno));
234 return FILE_TYPE_NONEXISTENT;
235 }
236
237 if (S_ISREG(statbuf.st_mode))
238 return FILE_TYPE_FILE;
239
240 if (S_ISDIR(statbuf.st_mode))
241 return FILE_TYPE_DIRECTORY;
242
243 return FILE_TYPE_WEIRD;
244 }
245
246 struct DirHandle {
247 DIR *dir;
248 };
249
250 DirHandle *open_directory(char *name)
251 {
252 DIR *dir;
253 DirHandle *ret;
254
255 dir = opendir(name);
256 if (!dir)
257 return NULL;
258
259 ret = snew(DirHandle);
260 ret->dir = dir;
261 return ret;
262 }
263
264 char *read_filename(DirHandle *dir)
265 {
266 struct dirent *de;
267
268 do {
269 de = readdir(dir->dir);
270 if (de == NULL)
271 return NULL;
272 } while ((de->d_name[0] == '.' &&
273 (de->d_name[1] == '\0' ||
274 (de->d_name[1] == '.' && de->d_name[2] == '\0'))));
275
276 return dupstr(de->d_name);
277 }
278
279 void close_directory(DirHandle *dir)
280 {
281 closedir(dir->dir);
282 sfree(dir);
283 }
284
285 int test_wildcard(char *name, int cmdline)
286 {
287 /*
288 * On Unix, we currently don't support local wildcards at all.
289 * We will have to do so (FIXME) once PSFTP starts implementing
290 * mput, but until then we can assume `cmdline' is always set.
291 */
292 struct stat statbuf;
293
294 assert(cmdline);
295 if (stat(name, &statbuf) < 0)
296 return WCTYPE_NONEXISTENT;
297 else
298 return WCTYPE_FILENAME;
299 }
300
301 /*
302 * Actually return matching file names for a local wildcard. FIXME:
303 * we currently don't support this at all.
304 */
305 struct WildcardMatcher {
306 int x;
307 };
308 WildcardMatcher *begin_wildcard_matching(char *name) { return NULL; }
309 char *wildcard_get_filename(WildcardMatcher *dir) { return NULL; }
310 void finish_wildcard_matching(WildcardMatcher *dir) {}
311
312 int create_directory(char *name)
313 {
314 return mkdir(name, 0777) == 0;
315 }
316
317 char *dir_file_cat(char *dir, char *file)
318 {
319 return dupcat(dir, "/", file, NULL);
320 }
321
322 /*
323 * Wait for some network data and process it.
324 */
325 int ssh_sftp_loop_iteration(void)
326 {
327 fd_set rset, wset, xset;
328 int i, fdcount, fdsize, *fdlist;
329 int fd, fdstate, rwx, ret, maxfd;
330
331 fdlist = NULL;
332 fdcount = fdsize = 0;
333
334 /* Count the currently active fds. */
335 i = 0;
336 for (fd = first_fd(&fdstate, &rwx); fd >= 0;
337 fd = next_fd(&fdstate, &rwx)) i++;
338
339 if (i < 1)
340 return -1; /* doom */
341
342 /* Expand the fdlist buffer if necessary. */
343 if (i > fdsize) {
344 fdsize = i + 16;
345 fdlist = sresize(fdlist, fdsize, int);
346 }
347
348 FD_ZERO(&rset);
349 FD_ZERO(&wset);
350 FD_ZERO(&xset);
351 maxfd = 0;
352
353 /*
354 * Add all currently open fds to the select sets, and store
355 * them in fdlist as well.
356 */
357 fdcount = 0;
358 for (fd = first_fd(&fdstate, &rwx); fd >= 0;
359 fd = next_fd(&fdstate, &rwx)) {
360 fdlist[fdcount++] = fd;
361 if (rwx & 1)
362 FD_SET_MAX(fd, maxfd, rset);
363 if (rwx & 2)
364 FD_SET_MAX(fd, maxfd, wset);
365 if (rwx & 4)
366 FD_SET_MAX(fd, maxfd, xset);
367 }
368
369 do {
370 ret = select(maxfd, &rset, &wset, &xset, NULL);
371 } while (ret < 0 && errno == EINTR);
372
373 if (ret < 0) {
374 perror("select");
375 exit(1);
376 }
377
378 for (i = 0; i < fdcount; i++) {
379 fd = fdlist[i];
380 /*
381 * We must process exceptional notifications before
382 * ordinary readability ones, or we may go straight
383 * past the urgent marker.
384 */
385 if (FD_ISSET(fd, &xset))
386 select_result(fd, 4);
387 if (FD_ISSET(fd, &rset))
388 select_result(fd, 1);
389 if (FD_ISSET(fd, &wset))
390 select_result(fd, 2);
391 }
392
393 sfree(fdlist);
394
395 return 0;
396 }
397
398 /*
399 * Main program: do platform-specific initialisation and then call
400 * psftp_main().
401 */
402 int main(int argc, char *argv[])
403 {
404 uxsel_init();
405 return psftp_main(argc, argv);
406 }