Download Reference Manual
The Developer's Library for D
About Wiki Forums Source Search Contact

Changeset 3498

Show
Ignore:
Timestamp:
05/11/08 09:32:27 (2 months ago)
Author:
lmartin92
Message:

FtpFolder? and FtpClient? are finaly finished. Testing by others besides the developer is needed.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • branches/lmartin/ftp/tango/io/vfs/FtpFolder.d

    r3484 r3498  
    1 module tango.io.vfs.FtpFolder; 
     1/******************************************************************************* 
     2 copyright:      Copyright (c) 2007 Tango. All rights reserved 
     3 
     4 license:        BSD style: $(LICENSE) 
     5 
     6 version:        August 2008: Initial version 
     7 
     8 author:         Lester L. Martin II 
     9 *******************************************************************************/ 
     10 
     11module tango.io.vfs.FtpFolder; 
    212 
    313private { 
     
    919} 
    1020 
    11 private FtpFileInfo[] getFiles(FTPConnection ftp, char[] path = "") { 
     21private FtpFileInfo[] getEntries(FTPConnection ftp, char[] path = "") { 
    1222    FtpFileInfo[] orig = ftp.ls(path); 
    1323    FtpFileInfo[] temp; 
     
    1828    } 
    1929    foreach(FtpFileInfo inf; temp) { 
    20         orig ~= getFiles((ftp.cd(inf.name) , ftp)); 
     30        orig ~= getEntries((ftp.cd(inf.name) , ftp)); 
    2131        ftp.cdup(); 
    2232    } 
     
    2434} 
    2535 
     36private FtpFileInfo[] getFiles(FTPConnection ftp, char[] path = "") { 
     37    FtpFileInfo[] infos = getEntries(ftp, path); 
     38    FtpFileInfo[] return_; 
     39    foreach(FtpFileInfo info; infos) { 
     40        if(info.type == FtpFileType.file || info.type == FtpFileType.other || info.type == FtpFileType.unknown) 
     41            return_ ~= info; 
     42    } 
     43    return return_; 
     44} 
     45 
     46private FtpFileInfo[] getFolders(FTPConnection ftp, char[] path = "") { 
     47    FtpFileInfo[] infos = getEntries(ftp, path); 
     48    FtpFileInfo[] return_; 
     49    foreach(FtpFileInfo info; infos) { 
     50        if(info.type == FtpFileType.dir || info.type == FtpFileType.cdir || info.type == FtpFileType.pdir) 
     51            return_ ~= info; 
     52    } 
     53    return return_; 
     54} 
     55 
    2656class FtpFolderEntry: VfsFolderEntry { 
    2757 
    28     FTPConnection ftp_; 
    2958    char[] toString_, name_, username_, password_; 
    3059    uint port_; 
    3160 
    32     public this(char[] server, char[] path, char[] username = "anonymous", 
    33                 char[] password = "anonymous@anonymous", uint port = 21) 
     61    public this(char[] server, char[] path, char[] username = "", 
     62                char[] password = "", uint port = 21) 
    3463    in { 
    35         assert(server != ""); 
     64        assert(server.length > 0); 
    3665    } 
    3766    body { 
     
    4372    } 
    4473 
     74    /*********************************************************************** 
     75     Open a folder 
     76     ***********************************************************************/ 
     77 
    4578    VfsFolder open() { 
    4679        return new FtpFolder(toString_, name_, username_, password_, port_); 
    4780    } 
    4881 
     82    /*********************************************************************** 
     83     Create a new folder 
     84     ***********************************************************************/ 
     85 
    4986    VfsFolder create() { 
    50         ftp_ = new FTPConnection(); 
    51         ftp_.connect(toString_, username_, password_, port_); 
    52         ftp_.mkdir(name_); 
    53         ftp_.close(); 
     87        FTPConnection conn; 
     88 
     89        scope(failure) { 
     90            if(conn !is null) 
     91                conn.close(); 
     92        } 
     93 
     94        scope(exit) { 
     95            if(conn !is null) 
     96                conn.close(); 
     97        } 
     98 
     99        conn = new FTPConnection(toString_, username_, password_, port_); 
     100        conn.mkdir(name_); 
     101 
    54102        return new FtpFolder(toString_, name_, username_, password_, port_); 
    55103    } 
    56104 
     105    /*********************************************************************** 
     106     Test to see if a folder exists 
     107     ***********************************************************************/ 
     108 
    57109    bool exists() { 
    58         ftp_ = new FTPConnection(); 
    59         ftp_.connect(toString_, username_, password_, port_); 
    60         switch(ftp_.exist(name_ != "" ? name_ : toString_)) { 
    61         case 0: 
    62             ftp_.close(); 
    63             return false; 
    64             break; 
    65         case 1: 
    66             ftp_.close(); 
    67             return false; 
    68             break; 
    69         case 2: 
    70             ftp_.close(); 
    71             return true; 
    72             break; 
    73         default: 
    74             ftp_.close(); 
    75         return false; 
    76         break; 
    77         } 
    78         return false; 
     110        FTPConnection conn; 
     111 
     112        scope(failure) { 
     113            if(conn !is null) 
     114                conn.close(); 
     115        } 
     116 
     117        scope(exit) { 
     118            if(conn !is null) 
     119                conn.close(); 
     120        } 
     121 
     122        bool return_; 
     123        if(name_ == "") { 
     124            try { 
     125                conn = new FTPConnection(toString_, username_, password_, port_); 
     126                return_ = true; 
     127            } catch(Exception e) { 
     128                return false; 
     129            } 
     130        } else { 
     131            try { 
     132                conn = new FTPConnection(toString_, username_, password_, port_); 
     133                try { 
     134                    conn.cd(name_); 
     135                    return_ = true; 
     136                } catch(Exception e) { 
     137                    if(conn.exist(name_) == 2) 
     138                        return_ = true; 
     139                    else 
     140                        return_ = false; 
     141                } 
     142            } catch(Exception e) { 
     143                return_ = false; 
     144            } 
     145        } 
     146 
     147        return return_; 
    79148    } 
    80149} 
     
    82151class FtpFolder: VfsFolder { 
    83152 
    84     FTPConnection ftp_; 
    85153    char[] toString_, name_, username_, password_; 
    86154    uint port_; 
    87     bool writeable_, writeableSet_, fileInfosSet_; 
    88     FtpFileInfo[] fileInfos_; 
    89  
    90     public this(char[] server, char[] path, char[] username = "anonymous", 
    91                 char[] password = "anonymous@anonymous", uint port = 21) 
     155 
     156    public this(char[] server, char[] path, char[] username = "", 
     157                char[] password = "", uint port = 21) 
    92158    in { 
    93         assert(server != ""); 
    94     } 
    95     out { 
    96         assert(server != ""); 
     159        assert(server.length > 0); 
    97160    } 
    98161    body { 
     
    104167    } 
    105168 
     169    /*********************************************************************** 
     170     Return a short name 
     171     ***********************************************************************/ 
     172 
    106173    char[] name() { 
    107174        return name_; 
    108175    } 
    109176 
     177    /*********************************************************************** 
     178     Return a long name 
     179     ***********************************************************************/ 
     180 
    110181    char[] toString() { 
    111         return toString_ ~ '/' ~ name_; 
    112     } 
     182        return toString_ ~ "/" ~ name_; 
     183    } 
     184 
     185    /*********************************************************************** 
     186     Return a contained file representation  
     187     ***********************************************************************/ 
    113188 
    114189    VfsFile file(char[] path) { 
    115         return new FtpFile(toString_ ~ name_, '/' ~ path, username_, password_, port_); 
    116     } 
     190        return new FtpFile(toString_, name_ ~ "/" ~ path, username_, password_, 
     191            port_); 
     192    } 
     193 
     194    /*********************************************************************** 
     195     Return a contained folder representation  
     196     ***********************************************************************/ 
    117197 
    118198    VfsFolderEntry folder(char[] path) { 
    119         return new FtpFolderEntry(toString_, '/' ~ path, username_, password_, port_); 
    120     } 
     199        return new FtpFolderEntry(toString_, name_ ~ "/" ~ path, username_, 
     200            password_, port_); 
     201    } 
     202 
     203    /*********************************************************************** 
     204     Returns a folder set containing only this one. Statistics  
     205     are inclusive of entries within this folder only 
     206     ***********************************************************************/ 
    121207 
    122208    VfsFolders self() { 
     209        FTPConnection conn; 
     210 
     211        scope(failure) { 
     212            if(conn !is null) 
     213                conn.close(); 
     214        } 
     215 
     216        scope(exit) { 
     217            if(conn !is null) 
     218                conn.close(); 
     219        } 
     220 
     221        char[] connect = toString_; 
     222 
     223        if(connect[$ - 1] == '/') { 
     224            connect = connect[0 .. ($ - 1)]; 
     225        } 
     226 
     227        conn = new FTPConnection(connect, username_, password_, port_); 
     228 
     229        if(name_ != "") 
     230            conn.cd(name_); 
     231 
    123232        return new FtpFolders(toString_, name_, username_, password_, port_, 
    124             true); 
    125     } 
     233            getFiles(conn), true); 
     234    } 
     235 
     236    /*********************************************************************** 
     237     Returns a subtree of folders. Statistics are inclusive of  
     238     files within this folder and all others within the tree 
     239     ***********************************************************************/ 
    126240 
    127241    VfsFolders tree() { 
     242        FTPConnection conn; 
     243 
     244        scope(failure) { 
     245            if(conn !is null) 
     246                conn.close(); 
     247        } 
     248 
     249        scope(exit) { 
     250            if(conn !is null) 
     251                conn.close(); 
     252        } 
     253 
     254        char[] connect = toString_; 
     255 
     256        if(connect[$ - 1] == '/') { 
     257            connect = connect[0 .. ($ - 1)]; 
     258        } 
     259 
     260        conn = new FTPConnection(connect, username_, password_, port_); 
     261 
     262        if(name_ != "") 
     263            conn.cd(name_); 
     264 
    128265        return new FtpFolders(toString_, name_, username_, password_, port_, 
    129             false); 
    130     } 
     266            getEntries(conn), false); 
     267    } 
     268 
     269    /*********************************************************************** 
     270     Iterate over the set of immediate child folders. This is  
     271     useful for reflecting the hierarchy 
     272     ***********************************************************************/ 
    131273 
    132274    int opApply(int delegate(inout VfsFolder) dg) { 
    133         if(!fileInfosSet_) { 
    134             ftp_ = new FTPConnection(); 
    135             ftp_.connect(toString_, username_, password_, port_); 
    136             fileInfos_ = getFiles(ftp_, name_); 
    137             ftp_.close(); 
    138             fileInfosSet_ = true; 
    139         } 
     275        FTPConnection conn; 
     276 
     277        scope(failure) { 
     278            if(conn !is null) 
     279                conn.close(); 
     280        } 
     281 
     282        scope(exit) { 
     283            if(conn !is null) 
     284                conn.close(); 
     285        } 
     286 
     287        char[] connect = toString_; 
     288 
     289        if(connect[$ - 1] == '/') { 
     290            connect = connect[0 .. ($ - 1)]; 
     291        } 
     292 
     293        conn = new FTPConnection(connect, username_, password_, port_); 
     294 
     295        if(name_ != "") 
     296            conn.cd(name_); 
     297 
     298        FtpFileInfo[] info = getFolders(conn); 
    140299 
    141300        int result; 
    142301 
    143         foreach(FtpFileInfo fi; fileInfos_) { 
    144             if(fi.type == FtpFileType.dir) { 
    145                 VfsFolder x = new FtpFolder(toString_, fi.name, username_, 
    146                     password_, port_); 
    147                 if((result = dg(x)) != 0) 
    148                     break; 
     302        foreach(FtpFileInfo fi; info) { 
     303            VfsFolder x = new FtpFolder(toString_ ~ name_, fi.name, username_, 
     304                password_, port_); 
     305            if((result = dg(x)) != 0) 
     306                break; 
     307        } 
     308 
     309        return result; 
     310    } 
     311 
     312    /*********************************************************************** 
     313     Clear all content from this folder and subordinates 
     314     ***********************************************************************/ 
     315 
     316    VfsFolder clear() { 
     317        FTPConnection conn; 
     318 
     319        scope(failure) { 
     320            if(conn !is null) 
     321                conn.close(); 
     322        } 
     323 
     324        scope(exit) { 
     325            if(conn !is null) 
     326                conn.close(); 
     327        } 
     328 
     329        char[] connect = toString_; 
     330 
     331        if(connect[$ - 1] == '/') { 
     332            connect = connect[0 .. ($ - 1)]; 
     333        } 
     334 
     335        conn = new FTPConnection(connect, username_, password_, port_); 
     336 
     337        if(name_ != "") 
     338            conn.cd(name_); 
     339 
     340        conn = new FTPConnection(connect, username_, password_, port_); 
     341 
     342        conn.cd(name_); 
     343 
     344        FtpFileInfo[] reverse(FtpFileInfo[] infos) { 
     345            FtpFileInfo[] reversed; 
     346            for(int i = infos.length - 1; i >= 0; i--) { 
     347                reversed ~= infos[i]; 
    149348            } 
    150         } 
    151  
    152         return result; 
    153     } 
    154  
    155     VfsFolder clear() { 
    156         ftp_ = new FTPConnection(); 
    157         ftp_.connect(toString_, username_, password_, port_); 
    158         ftp_.del(name_); 
    159         ftp_.mkdir(name_); 
    160         ftp_.close(); 
     349            return reversed; 
     350        } 
     351 
     352        foreach(VfsFolder f; tree.subset(null)) 
     353        conn.rm(f.name); 
     354 
     355        foreach(FtpFileInfo entries; getEntries(conn)) 
     356        conn.del(entries.name); 
     357 
     358        //foreach(VfsFolder f; tree.subset(null)) 
     359        //  conn.rm(f.name); 
     360 
    161361        return new FtpFolder(toString_, name_, username_, password_, port_); 
    162362    } 
    163363 
     364    /*********************************************************************** 
     365     Is folder writable? 
     366     ***********************************************************************/ 
     367 
    164368    bool writable() { 
    165         ftp_ = new FTPConnection(); 
    166         ftp_.connect(toString_, username_, password_, port_); 
    167         if(writeableSet_) { 
    168             return writeable_; 
    169         } else { 
    170             try { 
    171                 ftp_.mkdir("diw"); 
    172                 ftp_.rm("/diw"); 
    173                 writeable_ = true; 
    174             } catch(Exception e) { 
    175                 writeable_ = false; 
     369        try { 
     370            FTPConnection conn; 
     371 
     372            scope(failure) { 
     373                if(conn !is null) 
     374                    conn.close(); 
    176375            } 
    177         } 
    178         writeableSet_ = true; 
    179         ftp_.close(); 
    180         return writeable_; 
    181     } 
     376 
     377            scope(exit) { 
     378                if(conn !is null) 
     379                    conn.close(); 
     380            } 
     381 
     382            char[] connect = toString_; 
     383 
     384            if(connect[$ - 1] == '/') { 
     385                connect = connect[0 .. ($ - 1)]; 
     386            } 
     387 
     388            conn = new FTPConnection(connect, username_, password_, port_); 
     389 
     390            if(name_ != "") 
     391                conn.cd(name_); 
     392 
     393            conn.mkdir("ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"); 
     394            conn.rm("ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"); 
     395            return true; 
     396 
     397        } catch(Exception e) { 
     398            return false; 
     399        } 
     400        return false; 
     401    } 
     402 
     403    /*********************************************************************** 
     404     Close and/or synchronize changes made to this folder. Each 
     405     driver should take advantage of this as appropriate, perhaps 
     406     combining multiple files together, or possibly copying to a  
     407     remote location 
     408     ***********************************************************************/ 
    182409 
    183410    VfsFolder close(bool commit = true) { 
    184         ftp_.close(); 
    185411        return new FtpFolder(toString_, name_, username_, password_, port_); 
    186412    } 
    187413 
     414    /*********************************************************************** 
     415     A folder is being added or removed from the hierarchy. Use  
     416     this to test for validity (or whatever) and throw exceptions  
     417     as necessary 
     418     ***********************************************************************/ 
     419 
    188420    void verify(VfsFolder folder, bool mounting) { 
    189         return null
     421       return
    190422    } 
    191423} 
     
    193425class FtpFolders: VfsFolders { 
    194426 
    195     FTPConnection ftp_; 
    196427    char[] toString_, name_, username_, password_; 
    197     uint port_ , fileNum_, folderNum_; 
    198     ulong bytes_; 
    199     bool flat_, fileInfosSet_, fileNumSet_, folderNumSet_, bytesSet_; 
    200     FtpFileInfo[] fileInfos_; 
    201  
    202     public this(char[] server, char[] path, char[] username = "anonymous", 
    203             char[] password = "anonymous@anonymous", uint port = 21) 
     428    uint port_; 
     429    bool flat_; 
     430    FtpFileInfo[] infos_; 
     431 
     432    public this(char[] server, char[] path, char[] username = "", 
     433                char[] password = "", uint port = 21) 
    204434    in { 
    205         assert(server != ""); 
    206     } 
    207     out { 
    208         assert(server != ""); 
     435        assert(server.length > 0); 
    209436    } 
    210437    body { 
    211438        toString_ = server; 
    212         name_ = path != "" ? path : server
     439        name_ = path
    213440        username_ = username; 
    214441        password_ = password; 
    215442        port_ = port; 
    216     } 
    217  
    218     public this(char[] server, char[] path, char[] username = "anonymous", 
    219             char[] password = "anonymous@anonymous", uint port = 21, 
    220             bool flat = false) 
     443 
     444        FTPConnection conn; 
     445 
     446        scope(failure) { 
     447            if(conn !is null) 
     448                conn.close(); 
     449        } 
     450 
     451        scope(exit) { 
     452            if(conn !is null) 
     453                conn.close(); 
     454        } 
     455 
     456        char[] connect = toString_; 
     457 
     458        if(connect[$ - 1] == '/') { 
     459            connect = connect[0 .. ($ - 1)]; 
     460        } 
     461 
     462        conn = new FTPConnection(connect, username_, password_, port_); 
     463 
     464        if(name_ != "") 
     465            conn.cd(name_); 
     466 
     467        infos_ = getEntries(conn); 
     468    } 
     469 
     470    package this(char[] server, char[] path, char[] username = "", 
     471                 char[] password = "", uint port = 21, FtpFileInfo[] infos = null, 
     472                 bool flat = false) 
    221473    in { 
    222         assert(server != ""); 
    223     } 
    224     out { 
    225         assert(server != ""); 
     474        assert(server.length > 0); 
    226475    } 
    227476    body { 
    228477        toString_ = server; 
    229         name_ = path != "" ? path : server; 
     478        name_ = path; 
     479        username_ = username; 
     480        password_ = password; 
     481        port_ = port; 
     482        infos_ = infos; 
     483        flat_ = flat; 
     484    } 
     485 
     486    public this(char[] server, char[] path, char[] username = "", 
     487                char[] password = "", uint port = 21, bool flat = false) 
     488    in { 
     489        assert(server.length > 0); 
     490    } 
     491    body { 
     492        toString_ = server; 
     493        name_ = path; 
    230494        username_ = username; 
    231495        password_ = password; 
    232496        port_ = port; 
    233497        flat_ = flat; 
    234     } 
     498 
     499        FTPConnection conn; 
     500 
     501        scope(failure) { 
     502            if(conn !is null) 
     503                conn.close(); 
     504        } 
     505 
     506        scope(exit) { 
     507            if(conn !is null) 
     508                conn.close(); 
     509        } 
     510 
     511        char[] connect = toString_; 
     512 
     513        if(connect[$ - 1] == '/') { 
     514            connect = connect[0 .. ($ - 1)]; 
     515        } 
     516 
     517        conn = new FTPConnection(connect, username_, password_, port_); 
     518 
     519        if(name_ != "") 
     520            conn.cd(name_); 
     521 
     522        if(!flat_) 
     523            infos_ = getEntries(conn); 
     524        else 
     525            infos_ = getFiles(conn); 
     526    } 
     527 
     528    /*********************************************************************** 
     529     Iterate over the set of contained VfsFolder instances 
     530     ***********************************************************************/ 
    235531 
    236532    int opApply(int delegate(inout VfsFolder) dg) { 
    237         if(!fileInfosSet_) { 
    238             ftp_ = new FTPConnection(); 
    239             ftp_.connect(toString_, username_, password_, port_); 
    240             fileInfos_ = getFiles(ftp_, name_); 
    241             ftp_.close(); 
    242             fileInfosSet_ = true; 
    243         } 
     533        FTPConnection conn; 
     534 
     535        scope(failure) { 
     536            if(conn !is null) 
     537                conn.close(); 
     538        } 
     539 
     540        scope(exit) { 
     541            if(conn !is null) 
     542                conn.close(); 
     543        } 
     544 
     545        char[] connect = toString_; 
     546 
     547        if(connect[$ - 1] == '/') { 
     548            connect = connect[0 .. ($ - 1)]; 
     549        } 
     550 
     551        conn = new FTPConnection(connect, username_, password_, port_); 
     552 
     553        if(name_ != "") 
     554            conn.cd(name_); 
     555 
     556        FtpFileInfo[] info = getFolders(conn); 
    244557 
    245558        int result; 
    246559 
    247         foreach(FtpFileInfo fi; fileInfos_) { 
    248             if(fi.type == FtpFileType.dir) { 
    249                 VfsFolder x = new FtpFolder(toString_, fi.name, username_, 
    250                         password_, port_); 
    251                 if((result = dg(x)) != 0) 
    252                     break; 
     560        foreach(FtpFileInfo fi; info) { 
     561            VfsFolder x = new FtpFolder(toString_ ~ "/" ~ name_, fi.name, 
     562                username_, password_, port_); 
     563            if((result = dg(x)) != 0) 
     564                break; 
     565        } 
     566 
     567        return result; 
     568    } 
     569 
     570    /*********************************************************************** 
     571     Return the number of files  
     572     ***********************************************************************/ 
     573 
     574    uint files() { 
     575        FTPConnection conn; 
     576 
     577        scope(failure) { 
     578            if(conn !is null) 
     579                conn.close(); 
     580        } 
     581 
     582        scope(exit) { 
     583            if(conn !is null) 
     584                conn.close(); 
     585        } 
     586 
     587        char[] connect = toString_; 
     588 
     589        if(connect[$ - 1] == '/') { 
     590            connect = connect[0 .. ($ - 1)]; 
     591        } 
     592 
     593        conn = new FTPConnection(connect, username_, password_, port_); 
     594 
     595        if(name_ != "") 
     596            conn.cd(name_); 
     597 
     598        return getFiles(conn).length; 
     599    } 
     600 
     601    /*********************************************************************** 
     602     Return the number of folders  
     603     ***********************************************************************/ 
     604 
     605    uint folders() { 
     606        FTPConnection conn; 
     607 
     608        scope(failure) { 
     609            if(conn !is null) 
     610                conn.close(); 
     611        } 
     612 
     613        scope(exit) { 
     614            if(conn !is null) 
     615                conn.close(); 
     616        } 
     617 
     618        char[] connect = toString_; 
     619 
     620        if(connect[$ - 1] == '/') { 
     621            connect = connect[0 .. ($ - 1)]; 
     622        } 
     623 
     624        conn = new FTPConnection(connect, username_, password_, port_); 
     625 
     626        if(name_ != "") 
     627            conn.cd(name_); 
     628 
     629        return getFolders(conn).length; 
     630    } 
     631 
     632    /*********************************************************************** 
     633     Return the total number of entries (files + folders) 
     634     ***********************************************************************/ 
     635 
     636    uint entries() { 
     637        FTPConnection conn; 
     638 
     639        scope(failure) { 
     640            if(conn !is null) 
     641                conn.close(); 
     642        } 
     643 
     644        scope(exit) { 
     645            if(conn !is null) 
     646                conn.close(); 
     647        } 
     648 
     649        char[] connect = toString_; 
     650 
     651        if(connect[$ - 1] == '/') { 
     652            connect = connect[0 .. ($ - 1)]; 
     653        } 
     654 
     655        conn = new FTPConnection(connect, username_, password_, port_); 
     656 
     657        if(name_ != "") 
     658            conn.cd(name_); 
     659 
     660        return getEntries(conn).length; 
     661    } 
     662 
     663    /*********************************************************************** 
     664     Return the total size of contained files  
     665     ***********************************************************************/ 
     666 
     667    ulong bytes() { 
     668        FTPConnection conn; 
     669 
     670        scope(failure) { 
     671            if(conn !is null) 
     672                conn.close(); 
     673        } 
     674 
     675        scope(exit) { 
     676            if(conn !is null) 
     677                conn.close(); 
     678        } 
     679 
     680        char[] connect = toString_; 
     681 
     682        if(connect[$ - 1] == '/') { 
     683            connect = connect[0 .. ($ - 1)]; 
     684        } 
     685 
     686        conn = new FTPConnection(connect, username_, password_, port_); 
     687 
     688        if(name_ != "") 
     689            conn.cd(name_); 
     690 
     691        ulong return_; 
     692 
     693        foreach(FtpFileInfo inf; getEntries(conn)) { 
     694            return_ += inf.size; 
     695        } 
     696 
     697        return return_; 
     698    } 
     699 
     700    /*********************************************************************** 
     701     Return a subset of folders matching the given pattern 
     702     ***********************************************************************/ 
     703 
     704    VfsFolders subset(char[] pattern) { 
     705        FTPConnection conn; 
     706 
     707        scope(failure) { 
     708            if(conn !is null) 
     709                conn.close(); 
     710        } 
     711 
     712        scope(exit) { 
     713            if(conn !is null) 
     714                conn.close(); 
     715        } 
     716 
     717        char[] connect = toString_; 
     718 
     719        if(connect[$ - 1] == '/') { 
     720            connect = connect[0 .. ($ - 1)]; 
     721        } 
     722 
     723        conn = new FTPConnection(connect, username_, password_, port_); 
     724 
     725        if(name_ != "") 
     726            conn.cd(name_); 
     727 
     728        FtpFileInfo[] return__; 
     729 
     730        if(pattern !is null) 
     731            foreach(FtpFileInfo inf; getFolders(conn)) { 
     732            if(containsPattern(inf.name, pattern)) 
     733                return__ ~= inf; 
     734        } 
     735        else 
     736            return__ = getFolders(conn); 
     737 
     738        return new FtpFolders(toString_, name_, username_, password_, port_, 
     739            return__); 
     740    } 
     741 
     742    /*********************************************************************** 
     743     Return a set of files matching the given pattern 
     744     ***********************************************************************/ 
     745 
     746    VfsFiles catalog(char[] pattern) { 
     747        FTPConnection conn; 
     748 
     749        scope(failure) { 
     750            if(conn !is null) 
     751                conn.close(); 
     752        } 
     753 
     754        scope(exit) { 
     755            if(conn !is null) 
     756                conn.close(); 
     757        } 
     758 
     759        char[] connect = toString_; 
     760 
     761        if(connect[$ - 1] == '/') { 
     762            connect = connect[0 .. ($ - 1)]; 
     763        } 
     764 
     765        conn = new FTPConnection(connect, username_, password_, port_); 
     766 
     767        if(name_ != "") 
     768            conn.cd(name_); 
     769 
     770        FtpFileInfo[] return__; 
     771 
     772        if(pattern !is null) { 
     773            foreach(FtpFileInfo inf; getFiles(conn)) { 
     774                if(containsPattern(inf.name, pattern)) { 
     775                    return__ ~= inf; 
     776                } 
    253777            } 
     778        } else { 
     779            return__ = getFiles(conn); 
     780        } 
     781 
     782        return new FtpFiles(toString_, name_, username_, password_, port_, 
     783            return__); 
     784    } 
     785 
     786    /*********************************************************************** 
     787     Return a set of files matching the given filter 
     788     ***********************************************************************/ 
     789 
     790    VfsFiles catalog(VfsFilter filter = null) { 
     791        FTPConnection conn; 
     792 
     793        scope(failure) { 
     794            if(conn !is null) 
     795                conn.close(); 
     796        } 
     797 
     798        scope(exit) { 
     799            if(conn !is null) 
     800                conn.close(); 
     801        } 
     802 
     803        char[] connect = toString_; 
     804 
     805        if(connect[$ - 1] == '/') { 
     806            connect = connect[0 .. ($ - 1)]; 
     807        } 
     808 
     809        conn = new FTPConnection(connect, username_, password_, port_); 
     810 
     811        if(name_ != "") 
     812            conn.cd(name_); 
     813 
     814        FtpFileInfo[] return__; 
     815 
     816        if(filter !is null) 
     817            foreach(FtpFileInfo inf; getFiles(conn)) { 
     818            VfsFilterInfo vinf; 
     819            vinf.bytes = inf.size; 
     820            vinf.name = inf.name; 
     821            vinf.folder = false; 
     822            vinf.path = toString_ ~ "/" ~ name_ ~ "/" ~ inf.name; 
     823            if(filter(&vinf)) 
     824                return__ ~= inf; 
     825        } 
     826        else 
     827            return__ = getFiles(conn); 
     828 
     829        return new FtpFiles(toString_, name_, username_, password_, port_, 
     830            return__); 
     831    } 
     832} 
     833 
     834class FtpFile: VfsFile { 
     835 
     836    char[] toString_, name_, username_, password_; 
     837    uint port_; 
     838    bool conOpen; 
     839    FTPConnection conn; 
     840 
     841    public this(char[] server, char[] path, char[] username = "", 
     842                char[] password = "", uint port = 21) 
     843    in { 
     844        assert(server.length > 0); 
     845    } 
     846    body { 
     847        toString_ = server; 
     848        name_ = path; 
     849        username_ = username; 
     850        password_ = password; 
     851        port_ = port; 
     852    } 
     853 
     854    /*********************************************************************** 
     855     Return a short name 
     856     ***********************************************************************/ 
     857 
     858    char[] name() { 
     859        return name_; 
     860    } 
     861 
     862    /*********************************************************************** 
     863     Return a long name 
     864     ***********************************************************************/ 
     865 
     866    char[] toString() { 
     867        char[] connect = toString_; 
     868 
     869        if(connect[$ - 1] == '/') { 
     870            connect = connect[0 .. ($ - 1)]; 
     871        } 
     872 
     873        connect ~= "/" ~ name_; 
     874 
     875        if(connect[$ - 1] == '/') { 
     876            connect = connect[0 .. ($ - 1)]; 
     877        } 
     878 
     879        return connect; 
     880    } 
     881 
     882    /*********************************************************************** 
     883     Does this file exist? 
     884     ***********************************************************************/ 
     885 
     886    bool exists() { 
     887        scope(failure) { 
     888            if(!conOpen) 
     889                if(conn !is null) 
     890                    conn.close(); 
     891        } 
     892 
     893        scope(exit) { 
     894            if(!conOpen) 
     895                if(conn !is null) 
     896                    conn.close(); 
     897        } 
     898 
     899        bool return_; 
     900 
     901        char[] connect = toString_; 
     902 
     903        if(connect[$ - 1] == '/') { 
     904            connect = connect[0 .. ($ - 1)]; 
     905        } 
     906 
     907        if(!conOpen) { 
     908            conn = new FTPConnection(connect, username_, password_, port_); 
     909        } 
     910 
     911        if(conn.exist(name_) == 1) { 
     912            return_ = true; 
     913        } else { 
     914            return_ = false; 
     915        } 
     916 
     917        return return_; 
     918    } 
     919 
     920    /*********************************************************************** 
     921     Return the file size 
     922     ***********************************************************************/ 
     923 
     924    ulong size() { 
     925        scope(failure) { 
     926            if(!conOpen) 
     927                if(conn !is null) 
     928                    conn.close(); 
     929        } 
     930 
     931        scope(exit) { 
     932            if(!conOpen) 
     933                if(conn !is null) 
     934                    conn.close(); 
     935        } 
     936 
     937        char[] connect = toString_; 
     938 
     939        if(connect[$ - 1] == '/') { 
     940            connect = connect[0 .. ($ - 1)]; 
     941        } 
     942 
     943        if(!conOpen) { 
     944            conn = new FTPConnection(connect, username_, password_, port_); 
     945        } 
     946 
     947        return conn.size(name_); 
     948    } 
     949 
     950    /*********************************************************************** 
     951     Create and copy the given source 
     952     ***********************************************************************/ 
     953 
     954    VfsFile copy(VfsFile source) { 
     955        output.copy(source.input); 
     956        return new FtpFile(toString_, name_, username_, password_, port_); 
     957    } 
     958 
     959    /*********************************************************************** 
     960     Create and copy the given source, and remove the source 
     961     ***********************************************************************/ 
     962 
     963    VfsFile move(VfsFile source) { 
     964        copy(source); 
     965        source.remove; 
     966        return new FtpFile(toString_, name_, username_, password_, port_); 
     967    } 
     968 
     969    /*********************************************************************** 
     970     Create a new file instance 
     971     ***********************************************************************/ 
     972 
     973    VfsFile create() { 
     974        char[1] a = "0"; 
     975        output.write(a); 
     976        return new FtpFile(toString_, name_, username_, password_, port_); 
     977