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