source: filezilla/trunk/fuentes/src/putty/windows/winsftp.c @ 3185

Last change on this file since 3185 was 3185, checked in by jrpelegrina, 2 years ago

Update new version: 3.15.02

File size: 16.9 KB
Line 
1/*
2 * winsftp.c: the Windows-specific parts of PSFTP and PSCP.
3 */
4
5#include <assert.h>
6
7#include "putty.h"
8#include "psftp.h"
9#include "ssh.h"
10#include "int64.h"
11
12char *get_ttymode(void *frontend, const char *mode) { return NULL; }
13
14int get_userpass_input(prompts_t *p, const unsigned char *in, int inlen)
15{
16    int ret;
17    ret = cmdline_get_passwd_input(p, in, inlen);
18    if (ret == -1)
19        ret = console_get_userpass_input(p, in, inlen);
20    return ret;
21}
22
23void platform_get_x11_auth(struct X11Display *display, Conf *conf)
24{
25    /* Do nothing, therefore no auth. */
26}
27const int platform_uses_x11_unix_by_default = TRUE;
28
29/* ----------------------------------------------------------------------
30 * File access abstraction.
31 */
32
33/*
34 * Set local current directory. Returns NULL on success, or else an
35 * error message which must be freed after printing.
36 */
37char *psftp_lcd(char *dir)
38{
39    char *ret = NULL;
40
41    wchar_t* w = utf8_to_wide(dir);
42    if (!w)
43        return dupstr("Failed to convert to wide character set");
44
45    if (!SetCurrentDirectoryW(w)) {
46        LPVOID message;
47        int i;
48        FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
49                      FORMAT_MESSAGE_FROM_SYSTEM |
50                      FORMAT_MESSAGE_IGNORE_INSERTS,
51                      NULL, GetLastError(),
52                      MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
53                      (LPTSTR)&message, 0, NULL);
54        i = strcspn((char *)message, "\n");
55        ret = dupprintf("%.*s", i, (LPCTSTR)message);
56        LocalFree(message);
57    }
58    sfree(w);
59
60    return ret;
61}
62
63/*
64 * Get local current directory. Returns a string which must be
65 * freed.
66 */
67char *psftp_getcwd(void)
68{
69    char* ret;
70    wchar_t *w = snewn(256, wchar_t);
71    int len = GetCurrentDirectoryW(256, w);
72    if (len > 256)
73        w = sresize(w, len, wchar_t);
74    GetCurrentDirectoryW(len, w);
75
76    ret = wide_to_utf8(w);
77    sfree(w);
78
79    return ret;
80}
81
82#define TIME_POSIX_TO_WIN(t, ft) do { \
83    ULARGE_INTEGER uli; \
84    uli.QuadPart = ((ULONGLONG)(t) + 11644473600ull) * 10000000ull; \
85    (ft).dwLowDateTime  = uli.LowPart; \
86    (ft).dwHighDateTime = uli.HighPart; \
87} while(0)
88#define TIME_WIN_TO_POSIX(ft, t) do { \
89    ULARGE_INTEGER uli; \
90    uli.LowPart  = (ft).dwLowDateTime; \
91    uli.HighPart = (ft).dwHighDateTime; \
92    uli.QuadPart = uli.QuadPart / 10000000ull - 11644473600ull; \
93    (t) = (unsigned long) uli.QuadPart; \
94} while(0)
95
96struct RFile {
97    HANDLE h;
98};
99
100RFile *open_existing_file(const char *name, uint64 *size,
101                          unsigned long *mtime, unsigned long *atime,
102                          long *perms)
103{
104    HANDLE h;
105    RFile *ret;
106
107    wchar_t* wname = utf8_to_wide(name);
108    if (!wname)
109        return NULL;
110
111    h = CreateFileW(wname, GENERIC_READ, FILE_SHARE_READ, NULL,
112                   OPEN_EXISTING, 0, 0);
113    sfree(wname);
114    if (h == INVALID_HANDLE_VALUE)
115        return NULL;
116
117    ret = snew(RFile);
118    ret->h = h;
119
120    if (size)
121        size->lo=GetFileSize(h, &(size->hi));
122
123    if (mtime || atime) {
124        FILETIME actime, wrtime;
125        GetFileTime(h, NULL, &actime, &wrtime);
126        if (atime)
127            TIME_WIN_TO_POSIX(actime, *atime);
128        if (mtime)
129            TIME_WIN_TO_POSIX(wrtime, *mtime);
130    }
131
132    if (perms)
133        *perms = -1;
134
135    return ret;
136}
137
138int read_from_file(RFile *f, void *buffer, int length)
139{
140    int ret;
141    DWORD read;
142    ret = ReadFile(f->h, buffer, length, &read, NULL);
143    if (!ret)
144        return -1;                     /* error */
145    else
146        return (int)read;
147}
148
149void close_rfile(RFile *f)
150{
151    CloseHandle(f->h);
152    sfree(f);
153}
154
155struct WFile {
156    HANDLE h;
157};
158
159WFile *open_new_file(const char *name, long perms)
160{
161    HANDLE h;
162    WFile *ret;
163
164    wchar_t* wname = utf8_to_wide(name);
165    if (!wname)
166        return NULL;
167
168    h = CreateFileW(wname, GENERIC_WRITE, FILE_SHARE_READ, NULL,
169                   CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
170    sfree(wname);
171    if (h == INVALID_HANDLE_VALUE)
172        return NULL;
173
174    ret = snew(WFile);
175    ret->h = h;
176
177    return ret;
178}
179
180WFile *open_existing_wfile(const char *name, uint64 *size)
181{
182    HANDLE h;
183    WFile *ret;
184
185    wchar_t* wname = utf8_to_wide(name);
186    if (!wname)
187        return NULL;
188
189    h = CreateFileW(wname, GENERIC_WRITE, FILE_SHARE_READ, NULL,
190                   OPEN_EXISTING, 0, 0);
191    sfree(wname);
192    if (h == INVALID_HANDLE_VALUE)
193        return NULL;
194
195    ret = snew(WFile);
196    ret->h = h;
197
198    if (size)
199        size->lo=GetFileSize(h, &(size->hi));
200
201    return ret;
202}
203
204int write_to_file(WFile *f, void *buffer, int length)
205{
206    int ret;
207    DWORD written;
208    ret = WriteFile(f->h, buffer, length, &written, NULL);
209    if (!ret)
210        return -1;                     /* error */
211    else
212        return (int)written;
213}
214
215void set_file_times(WFile *f, unsigned long mtime, unsigned long atime)
216{
217    FILETIME actime, wrtime;
218    TIME_POSIX_TO_WIN(atime, actime);
219    TIME_POSIX_TO_WIN(mtime, wrtime);
220    SetFileTime(f->h, NULL, &actime, &wrtime);
221}
222
223void close_wfile(WFile *f)
224{
225    CloseHandle(f->h);
226    sfree(f);
227}
228
229/* Seek offset bytes through file, from whence, where whence is
230   FROM_START, FROM_CURRENT, or FROM_END */
231int seek_file(WFile *f, uint64 offset, int whence)
232{
233    DWORD movemethod;
234
235    switch (whence) {
236    case FROM_START:
237        movemethod = FILE_BEGIN;
238        break;
239    case FROM_CURRENT:
240        movemethod = FILE_CURRENT;
241        break;
242    case FROM_END:
243        movemethod = FILE_END;
244        break;
245    default:
246        return -1;
247    }
248
249    SetFilePointer(f->h, offset.lo, &(offset.hi), movemethod);
250   
251    if (GetLastError() != NO_ERROR)
252        return -1;
253    else 
254        return 0;
255}
256
257uint64 get_file_posn(WFile *f)
258{
259    uint64 ret;
260
261    ret.hi = 0L;
262    ret.lo = SetFilePointer(f->h, 0L, &(ret.hi), FILE_CURRENT);
263
264    return ret;
265}
266
267int file_type(const char *name)
268{
269    DWORD attr;
270
271    wchar_t* wname = utf8_to_wide(name);
272    if (!wname)
273        return FILE_TYPE_NONEXISTENT;
274
275    attr = GetFileAttributesW(wname);
276    sfree(wname);
277    /* We know of no `weird' files under Windows. */
278    if (attr == (DWORD)-1)
279        return FILE_TYPE_NONEXISTENT;
280    else if (attr & FILE_ATTRIBUTE_DIRECTORY)
281        return FILE_TYPE_DIRECTORY;
282    else
283        return FILE_TYPE_FILE;
284}
285
286struct DirHandle {
287    HANDLE h;
288    char *name;
289};
290
291DirHandle *open_directory(const char *name)
292{
293    HANDLE h;
294    WIN32_FIND_DATAW fdat;
295    char *findfile;
296    wchar_t *wfindfile;
297    DirHandle *ret;
298
299    /* Enumerate files in dir `foo'. */
300    findfile = dupcat(name, "/*", NULL);
301
302    wfindfile = utf8_to_wide(findfile);
303    if (!wfindfile)
304        return NULL;
305
306    h = FindFirstFileW(wfindfile, &fdat);
307    if (h == INVALID_HANDLE_VALUE)
308        return NULL;
309    sfree(wfindfile);
310    sfree(findfile);
311
312    ret = snew(DirHandle);
313    ret->h = h;
314    ret->name = wide_to_utf8(fdat.cFileName);
315    return ret;
316}
317
318char *read_filename(DirHandle *dir)
319{
320    do {
321
322        if (!dir->name) {
323            WIN32_FIND_DATAW fdat;
324            int ok = FindNextFileW(dir->h, &fdat);
325            if (!ok)
326                return NULL;
327            else
328                dir->name = wide_to_utf8(fdat.cFileName);
329        }
330
331        assert(dir->name);
332        if (dir->name[0] == '.' &&
333            (dir->name[1] == '\0' ||
334             (dir->name[1] == '.' && dir->name[2] == '\0'))) {
335            sfree(dir->name);
336            dir->name = NULL;
337        }
338
339    } while (!dir->name);
340
341    if (dir->name) {
342        char *ret = dir->name;
343        dir->name = NULL;
344        return ret;
345    } else
346        return NULL;
347}
348
349void close_directory(DirHandle *dir)
350{
351    FindClose(dir->h);
352    if (dir->name)
353        sfree(dir->name);
354    sfree(dir);
355}
356
357int test_wildcard(const char *name, int cmdline)
358{
359    HANDLE fh;
360    WIN32_FIND_DATAW fdat;
361
362    wchar_t* wname = utf8_to_wide(name);
363    if (!wname)
364        return WCTYPE_NONEXISTENT;
365
366    /* First see if the exact name exists. */
367    if (GetFileAttributesW(wname) != (DWORD)-1)
368    {
369        sfree(wname);
370        return WCTYPE_FILENAME;
371    }
372
373    /* Otherwise see if a wildcard match finds anything. */
374    fh = FindFirstFileW(wname, &fdat);
375    if (fh == INVALID_HANDLE_VALUE)
376    {
377        sfree(wname);
378        return WCTYPE_NONEXISTENT;
379    }
380
381    sfree(wname);
382
383    FindClose(fh);
384    return WCTYPE_WILDCARD;
385}
386
387struct WildcardMatcher {
388    HANDLE h;
389    char *name;
390    char *srcpath;
391};
392
393char *stripslashes(const char *str, int local)
394{
395    char *p;
396
397    /*
398     * On Windows, \ / : are all path component separators.
399     */
400
401    if (local) {
402        p = strchr(str, ':');
403        if (p) str = p+1;
404    }
405
406    p = strrchr(str, '/');
407    if (p) str = p+1;
408
409    if (local) {
410        p = strrchr(str, '\\');
411        if (p) str = p+1;
412    }
413
414    return (char *)str;
415}
416
417WildcardMatcher *begin_wildcard_matching(const char *name)
418{
419    HANDLE h;
420    WIN32_FIND_DATA fdat;
421    WildcardMatcher *ret;
422    char *last;
423
424    h = FindFirstFile(name, &fdat);
425    if (h == INVALID_HANDLE_VALUE)
426        return NULL;
427
428    ret = snew(WildcardMatcher);
429    ret->h = h;
430    ret->srcpath = dupstr(name);
431    last = stripslashes(ret->srcpath, 1);
432    *last = '\0';
433    if (fdat.cFileName[0] == '.' &&
434        (fdat.cFileName[1] == '\0' ||
435         (fdat.cFileName[1] == '.' && fdat.cFileName[2] == '\0')))
436        ret->name = NULL;
437    else
438        ret->name = dupcat(ret->srcpath, fdat.cFileName, NULL);
439
440    return ret;
441}
442
443char *wildcard_get_filename(WildcardMatcher *dir)
444{
445    while (!dir->name) {
446        WIN32_FIND_DATA fdat;
447        int ok = FindNextFile(dir->h, &fdat);
448
449        if (!ok)
450            return NULL;
451
452        if (fdat.cFileName[0] == '.' &&
453            (fdat.cFileName[1] == '\0' ||
454             (fdat.cFileName[1] == '.' && fdat.cFileName[2] == '\0')))
455            dir->name = NULL;
456        else
457            dir->name = dupcat(dir->srcpath, fdat.cFileName, NULL);
458    }
459
460    if (dir->name) {
461        char *ret = dir->name;
462        dir->name = NULL;
463        return ret;
464    } else
465        return NULL;
466}
467
468void finish_wildcard_matching(WildcardMatcher *dir)
469{
470    FindClose(dir->h);
471    if (dir->name)
472        sfree(dir->name);
473    sfree(dir->srcpath);
474    sfree(dir);
475}
476
477int vet_filename(const char *name)
478{
479    if (strchr(name, '/') || strchr(name, '\\') || strchr(name, ':'))
480        return FALSE;
481
482    if (!name[strspn(name, ".")])      /* entirely composed of dots */
483        return FALSE;
484
485    return TRUE;
486}
487
488int create_directory(const char *name)
489{
490    int res;
491
492    wchar_t *wname = utf8_to_wide(name);
493    if (!wname)
494        return 0;
495
496    res = CreateDirectoryW(wname, NULL) != 0;
497    sfree(wname);
498
499    return res;
500}
501
502char *dir_file_cat(const char *dir, const char *file)
503{
504    return dupcat(dir, "\\", file, NULL);
505}
506
507/* ----------------------------------------------------------------------
508 * Platform-specific network handling.
509 */
510
511/*
512 * Be told what socket we're supposed to be using.
513 */
514static SOCKET sftp_ssh_socket = INVALID_SOCKET;
515static HANDLE netevent = INVALID_HANDLE_VALUE;
516char *do_select(SOCKET skt, int startup)
517{
518    int events;
519    if (startup)
520        sftp_ssh_socket = skt;
521    else
522        sftp_ssh_socket = INVALID_SOCKET;
523
524    if (p_WSAEventSelect) {
525        if (startup) {
526            events = (FD_CONNECT | FD_READ | FD_WRITE |
527                      FD_OOB | FD_CLOSE | FD_ACCEPT);
528            netevent = CreateEvent(NULL, FALSE, FALSE, NULL);
529        } else {
530            events = 0;
531        }
532        if (p_WSAEventSelect(skt, netevent, events) == SOCKET_ERROR) {
533            switch (p_WSAGetLastError()) {
534              case WSAENETDOWN:
535                return "Network is down";
536              default:
537                return "WSAEventSelect(): unknown error";
538            }
539        }
540    }
541    return NULL;
542}
543extern int select_result(WPARAM, LPARAM);
544
545int do_eventsel_loop(HANDLE other_event)
546{
547    int n, nhandles, nallhandles, netindex, otherindex;
548    unsigned long next, then;
549    long ticks;
550    HANDLE *handles;
551    SOCKET *sklist;
552    int skcount;
553    unsigned long now = GETTICKCOUNT();
554
555    if (toplevel_callback_pending()) {
556        ticks = 0;
557        next = now;
558    } else if (run_timers(now, &next)) {
559        then = now;
560        now = GETTICKCOUNT();
561        if (now - then > next - then)
562            ticks = 0;
563        else
564            ticks = next - now;
565    } else {
566        ticks = INFINITE;
567        /* no need to initialise next here because we can never get
568         * WAIT_TIMEOUT */
569    }
570
571    handles = handle_get_events(&nhandles);
572    handles = sresize(handles, nhandles+2, HANDLE);
573    nallhandles = nhandles;
574
575    if (netevent != INVALID_HANDLE_VALUE)
576        handles[netindex = nallhandles++] = netevent;
577    else
578        netindex = -1;
579    if (other_event != INVALID_HANDLE_VALUE)
580        handles[otherindex = nallhandles++] = other_event;
581    else
582        otherindex = -1;
583
584    n = WaitForMultipleObjects(nallhandles, handles, FALSE, ticks);
585
586    if ((unsigned)(n - WAIT_OBJECT_0) < (unsigned)nhandles) {
587        handle_got_event(handles[n - WAIT_OBJECT_0]);
588    } else if (netindex >= 0 && n == WAIT_OBJECT_0 + netindex) {
589        WSANETWORKEVENTS things;
590        SOCKET socket;
591        extern SOCKET first_socket(int *), next_socket(int *);
592        extern int select_result(WPARAM, LPARAM);
593        int i, socketstate;
594
595        /*
596         * We must not call select_result() for any socket
597         * until we have finished enumerating within the
598         * tree. This is because select_result() may close
599         * the socket and modify the tree.
600         */
601        /* Count the active sockets. */
602        i = 0;
603        for (socket = first_socket(&socketstate);
604             socket != INVALID_SOCKET;
605             socket = next_socket(&socketstate)) i++;
606
607        /* Expand the buffer if necessary. */
608        sklist = snewn(i, SOCKET);
609
610        /* Retrieve the sockets into sklist. */
611        skcount = 0;
612        for (socket = first_socket(&socketstate);
613             socket != INVALID_SOCKET;
614             socket = next_socket(&socketstate)) {
615            sklist[skcount++] = socket;
616        }
617
618        /* Now we're done enumerating; go through the list. */
619        for (i = 0; i < skcount; i++) {
620            WPARAM wp;
621            socket = sklist[i];
622            wp = (WPARAM) socket;
623            if (!p_WSAEnumNetworkEvents(socket, NULL, &things)) {
624                static const struct { int bit, mask; } eventtypes[] = {
625                    {FD_CONNECT_BIT, FD_CONNECT},
626                    {FD_READ_BIT, FD_READ},
627                    {FD_CLOSE_BIT, FD_CLOSE},
628                    {FD_OOB_BIT, FD_OOB},
629                    {FD_WRITE_BIT, FD_WRITE},
630                    {FD_ACCEPT_BIT, FD_ACCEPT},
631                };
632                int e;
633
634                noise_ultralight(socket);
635                noise_ultralight(things.lNetworkEvents);
636
637                for (e = 0; e < lenof(eventtypes); e++)
638                    if (things.lNetworkEvents & eventtypes[e].mask) {
639                        LPARAM lp;
640                        int err = things.iErrorCode[eventtypes[e].bit];
641                        lp = WSAMAKESELECTREPLY(eventtypes[e].mask, err);
642                        select_result(wp, lp);
643                    }
644            }
645        }
646
647        sfree(sklist);
648    }
649
650    sfree(handles);
651
652    run_toplevel_callbacks();
653
654    if (n == WAIT_TIMEOUT) {
655        now = next;
656    } else {
657        now = GETTICKCOUNT();
658    }
659
660    if (otherindex >= 0 && n == WAIT_OBJECT_0 + otherindex)
661        return 1;
662
663    return 0;
664}
665
666/*
667 * Wait for some network data and process it.
668 *
669 * We have two variants of this function. One uses select() so that
670 * it's compatible with WinSock 1. The other uses WSAEventSelect
671 * and MsgWaitForMultipleObjects, so that we can consistently use
672 * WSAEventSelect throughout; this enables us to also implement
673 * ssh_sftp_get_cmdline() using a parallel mechanism.
674 */
675int ssh_sftp_loop_iteration(void)
676{
677    if (p_WSAEventSelect == NULL) {
678        fd_set readfds;
679        int ret;
680        unsigned long now = GETTICKCOUNT(), then;
681
682        if (sftp_ssh_socket == INVALID_SOCKET)
683            return -1;                 /* doom */
684
685        if (socket_writable(sftp_ssh_socket))
686            select_result((WPARAM) sftp_ssh_socket, (LPARAM) FD_WRITE);
687
688        do {
689            unsigned long next;
690            long ticks;
691            struct timeval tv, *ptv;
692
693            if (run_timers(now, &next)) {
694                then = now;
695                now = GETTICKCOUNT();
696                if (now - then > next - then)
697                    ticks = 0;
698                else
699                    ticks = next - now;
700                tv.tv_sec = ticks / 1000;
701                tv.tv_usec = ticks % 1000 * 1000;
702                ptv = &tv;
703            } else {
704                ptv = NULL;
705            }
706
707            FD_ZERO(&readfds);
708            FD_SET(sftp_ssh_socket, &readfds);
709            ret = p_select(1, &readfds, NULL, NULL, ptv);
710
711            if (ret < 0)
712                return -1;                     /* doom */
713            else if (ret == 0)
714                now = next;
715            else
716                now = GETTICKCOUNT();
717
718        } while (ret == 0);
719
720        select_result((WPARAM) sftp_ssh_socket, (LPARAM) FD_READ);
721
722        return 0;
723    } else {
724        return do_eventsel_loop(INVALID_HANDLE_VALUE);
725    }
726}
727
728/*
729 * Read a command line from standard input.
730 *
731 * In the presence of WinSock 2, we can use WSAEventSelect to
732 * mediate between the socket and stdin, meaning we can send
733 * keepalives and respond to server events even while waiting at
734 * the PSFTP command prompt. Without WS2, we fall back to a simple
735 * fgets.
736 */
737struct command_read_ctx {
738    HANDLE event;
739    char *line;
740};
741
742static DWORD WINAPI command_read_thread(void *param)
743{
744    struct command_read_ctx *ctx = (struct command_read_ctx *) param;
745
746    ctx->line = fgetline(stdin);
747
748    SetEvent(ctx->event);
749
750    return 0;
751}
752
753char *ssh_sftp_get_cmdline(const char *prompt, int no_fds_ok)
754{
755    int ret;
756    struct command_read_ctx actx, *ctx = &actx;
757    DWORD threadid;
758    HANDLE hThread;
759
760    /* Not used in fzsftp
761    fputs(prompt, stdout);
762    fflush(stdout);
763    */
764
765    if ((sftp_ssh_socket == INVALID_SOCKET && no_fds_ok) ||
766        p_WSAEventSelect == NULL) {
767        return fgetline(stdin);        /* very simple */
768    }
769
770    /*
771     * Create a second thread to read from stdin. Process network
772     * and timing events until it terminates.
773     */
774    ctx->event = CreateEvent(NULL, FALSE, FALSE, NULL);
775    ctx->line = NULL;
776
777    hThread = CreateThread(NULL, 0, command_read_thread, ctx, 0, &threadid);
778    if (!hThread) {
779        CloseHandle(ctx->event);
780        fzprintf(sftpError, "Unable to create command input thread");
781        cleanup_exit(1);
782    }
783
784    do {
785        ret = do_eventsel_loop(ctx->event);
786
787        /* Error return can only occur if netevent==NULL, and it ain't. */
788        assert(ret >= 0);
789    } while (ret == 0);
790
791    CloseHandle(hThread);
792    CloseHandle(ctx->event);
793
794    return ctx->line;
795}
796
797/* ----------------------------------------------------------------------
798 * Main program. Parse arguments etc.
799 */
800int main(int argc, char *argv[])
801{
802    int ret;
803
804    ret = psftp_main(argc, argv);
805
806    return ret;
807}
Note: See TracBrowser for help on using the repository browser.