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

Changeset 3370

Show
Ignore:
Timestamp:
03/16/08 14:57:08 (9 months ago)
Author:
lmartin92
Message:

Well since Ftp servers will only let the same ip connect to it a certain number of times I ran into a problem and had to start over. I've gotten pretty far for just one day and have almost everything done that deals with folders. Haven't got to the file part.

Files:

Legend:

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

    r3369 r3370  
    1 /******************************************************************************* 
    2  
    3         copyright:      Copyright (c) 2008 (c) Lester L. Martin II. All rights reserved 
    4  
    5         license:        BSD style: $(LICENSE) 
    6  
    7         version:        Initial release: _________ 2008 
    8  
    9         author:         Lester L. Martin II 
    10  
    11         important: 
    12                 More rights and additional changes to the license can be requested 
    13             at lestermartin92@gmail.com as long as my spam manager does not drop 
    14             you in the spam box. 
    15                 All rights requested and license will be made individualy and that 
    16             license and set of rights will apply only to the individual unless  
    17             provided otherwise by liscense and copyright. 
    18  
    19 *******************************************************************************/ 
    20  
    21 module tango.io.vfs.FtpFolder; 
    22  
    23 private import tango.io.vfs.model.Vfs; 
    24  
    25 private import tango.io.FilePath; 
    26  
    27 private import tango.io.model.IConduit; 
    28  
    29 private import tango.net.ftp.FtpClient; 
    30  
    31 //deals with creating null files(what I use it for) 
    32 private import tango.io.FileConduit; 
    33  
    34 //imports things to deal with text 
    35 private import tango.text.Util; 
    36  
    37 import tango.io.Console; 
    38  
    39 /******************************************************************************* 
    40  
    41 *******************************************************************************/ 
    42  
    43  class FtpFolder : VfsFolder  
    44 
    45     char[]          _name, 
    46                     _toString, 
    47                     _server, 
    48                     _username, 
    49                     _password; 
    50     uint            _port; 
    51     package FTPConnection   _ftp; 
    52     bool            _writableSet = false; 
    53     bool            _writeable; 
    54  
    55     /*********************************************************************** 
    56     ***********************************************************************/ 
    57  
    58     package this (FtpFolder fol) 
    59     { 
    60         _name = fol._name.dup; 
    61         _toString = fol._toString.dup; 
    62         _server = fol._server.dup; 
    63         _username = fol._username.dup; 
    64         _password = fol._password.dup; 
    65         _port = fol._port; 
    66         _ftp = new FTPConnection(_toString, _username, _password, _port); 
    67         writable(); 
    68     } 
    69  
    70     package this(VfsFolder fol) 
    71     { 
    72         this(cast(FtpFolder)fol); 
    73     } 
    74      
    75     public this(char[] server, char[] path = "", char[] username = "anonymous", char[] password = "anonymous@anonymous", uint port = 21) 
    76     in 
    77     { 
    78         assert((server != null || server != "")); 
    79     } 
    80     body 
    81     { 
    82         _toString   = server ~ path; 
    83         _name       = path; 
    84         _server     = server; 
    85         _username   = username; 
    86         _password   = password; 
    87         _port       = port; 
    88         _ftp        = new FTPConnection(_server, _username, _password, _port); 
    89         if(_name != "") 
    90             _ftp.cd(_name); 
    91     } 
    92      
    93     /*********************************************************************** 
    94  
    95          Return a short name 
    96  
    97     ***********************************************************************/ 
    98  
    99     package void open() 
    100     { 
    101         _ftp.connect(_server, _username, _password, _port); 
    102     } 
    103  
    104     char[] name() 
    105     { 
    106         return _name; 
    107     } 
    108  
    109     /*********************************************************************** 
    110  
    111         Return a long name 
    112  
    113     ***********************************************************************/ 
    114  
    115     char[] toString() 
    116     { 
    117         return _toString; 
    118     } 
    119  
    120     /*********************************************************************** 
    121  
    122         Return a contained file representation  
    123  
    124     ***********************************************************************/ 
    125  
    126     VfsFile file (char[] path) 
    127     { 
    128         return new FtpFile(this, path); 
    129     } 
    130  
    131     /*********************************************************************** 
    132  
    133         Return a contained folder representation  
    134  
    135     ***********************************************************************/ 
    136  
    137     VfsFolderEntry folder (char[] path) 
    138     { 
    139         return new FtpFolderEntry(this, path); 
    140     } 
    141  
    142     /*********************************************************************** 
    143  
    144         Returns a folder set containing only this one. Statistics  
    145         are inclusive of entries within this folder only 
    146  
    147     ***********************************************************************/ 
    148  
    149     VfsFolders self () 
    150     { 
    151         return new FtpFolders(this, true); 
    152     } 
    153  
    154     /*********************************************************************** 
    155  
    156         Returns a subtree of folders. Statistics are inclusive of  
    157         files within this folder and all others within the tree 
    158  
    159     ***********************************************************************/ 
    160  
    161     VfsFolders tree () 
    162     { 
    163         return new FtpFolders(this, false); 
    164     } 
    165  
    166     /*********************************************************************** 
    167  
    168         Iterate over the set of immediate child folders. This is  
    169         useful for reflecting the hierarchy 
    170  
    171     ***********************************************************************/ 
    172  
    173     int opApply (int delegate(inout VfsFolder) dg) 
    174     { 
    175         VfsFolders fol = self; 
    176         int result = 0; 
    177         foreach(folder; fol) 
    178         { 
    179             VfsFolder x = folder;   
    180             if ((result = dg(x)) != 0) 
    181                  break; 
    182         } 
    183         return result; 
    184     } 
    185  
    186     /*********************************************************************** 
    187  
    188         Clear all content from this folder and subordinates 
    189         does nothing on failure 
    190  
    191     ***********************************************************************/ 
    192  
    193     VfsFolder clear() 
    194     { 
    195         if(_writeable) 
    196         { 
    197             _ftp.rm(name); 
    198             _ftp.mkdir(name); 
    199             return this; 
    200         } 
    201         else 
    202             return new FtpFolder(this); 
    203     } 
    204  
    205     /*********************************************************************** 
    206  
    207         Is folder writable? 
    208         You should store the variable it returns. It makes a folder on the 
    209         server if it works then it's writeable if not then returns false. 
    210         The recommendation comes from the fact it takes up 2 ftp operations :-) 
    211  
    212     ***********************************************************************/ 
    213  
    214     bool writable() 
    215     { 
    216         if(_writableSet == true) 
    217         { 
    218             return _writeable; 
    219         } 
    220         else if(_ftp.exist(_ftp.mkdir("diw"))) 
    221         { 
    222             try 
    223             { 
    224                 _ftp.rm("diw"); 
    225                 _writeable = true; 
    226                 return true; 
    227             } 
    228             catch(Exception) 
    229             { 
    230                 _writeable = false; 
    231                 return false; 
    232             } 
    233         } 
    234         else 
    235         { 
    236             return false; 
    237             _writeable = false; 
    238         } 
    239         _writeable = false; 
    240         _writableSet = true; 
    241         return false; 
    242     } 
    243  
    244     /*********************************************************************** 
    245  
    246         Close and/or synchronize changes made to this folder. Each 
    247         driver should take advantage of this as appropriate, perhaps 
    248         combining multiple files together, or possibly copying to a  
    249         remote location 
    250         This will close the ftp connection.  Do not try to use the vfs folder 
    251         it returns because operations to it no longer apply(throw exceptions  
    252         or do nothing really)(throws exceptions if it trys to access ftp which 
    253         now does not have an open connection). 
    254  
    255     ***********************************************************************/ 
    256  
    257     VfsFolder close (bool commit = true) 
    258     { 
    259         _ftp.close(); 
    260         return new FtpFolder(this); 
    261     } 
    262  
    263     /*********************************************************************** 
    264  
    265         does nothing and just returns; wastes time maybe ;-) 
    266  
    267     ***********************************************************************/ 
    268  
    269     void verify (VfsFolder folder, bool mounting) 
    270     { 
    271         return; 
    272     } 
    273 
    274  
    275 /******************************************************************************* 
    276  
    277         Operations upon a set of folders  
    278  
    279 *******************************************************************************/ 
    280   
    281  class FtpFolders : VfsFolders 
    282 
    283     FtpFolder _ftpFol; 
    284     FtpFolder[] _folders, _primaryFolders; 
    285     bool _flat, _foldersPopulated; 
    286  
    287     package this(FtpFolder[] fols) 
    288     { 
    289         _primaryFolders = fols; 
    290         foreach(folder; fols) 
    291         { 
    292             _primaryFolders ~= folder; 
    293         } 
    294         Cout("Doesn't work?").newline.flush; 
    295         populateFolders(false); 
    296     } 
    297  
    298     package this(FtpFolder fol, bool flat) 
    299     { 
    300         _primaryFolders ~= fol; 
    301         _flat = flat; 
    302         populateFolders(false); 
    303     } 
    304      
    305     public this(char[] server, char[] path = "", char[] username = "anonymous", char[] password = "anonymous@anonymous", uint port = 21) 
    306     in 
    307     { 
    308         assert((server != null || server != "")); 
    309     } 
    310     body 
    311     { 
    312         _primaryFolders ~= new FtpFolder(server, path, username, password, port); 
    313         populateFolders(false); 
    314     } 
    315     /*********************************************************************** 
    316  
    317         Iterate over the set of contained VfsFolder instances 
    318  
    319     ***********************************************************************/ 
    320  
    321     int opApply (int delegate(inout VfsFolder) dg) 
    322     { 
    323         if(_flat) 
    324             throw new Exception("This folder contains no folders."); 
    325         else 
    326         { 
    327             int result = 0; 
    328             foreach(folder; _folders) 
    329             { 
    330                 VfsFolder x = folder;   
    331                 if ((result = dg(x)) != 0) 
     1/* 
     2 * Author: Lester L. Martin II 
     3 * Copyright(c) Lester L. Martin II 2008 All rights reserved 
     4 * Liscense: $(BSD) 
     5 *  
     6 */ 
     7 
     8import tango.net.ftp.FtpClient; 
     9import tango.io.vfs.model.Vfs; 
     10import tango.io.Conduit; 
     11 
     12class FtpFolderEntry: VfsFolderEntry { 
     13 
     14    FTPConnection ftp_; 
     15    char[] toString_, name_, username_, password_; 
     16    uint port_; 
     17 
     18    public this(char[] server, char[] path, char[] username = "anonymous", 
     19                char[] password = "anonymous@anonymous", uint port = 21) 
     20    in { 
     21        assert(server != ""); 
     22    } 
     23    out { 
     24        assert(server != ""); 
     25    } 
     26    body { 
     27        toString_ = server; 
     28        name_ = path; 
     29        username_ = username; 
     30        password_ = password; 
     31        port_ = port; 
     32    } 
     33 
     34    VfsFolder open() { 
     35        if(!exists()) { 
     36            ftp_.close(); 
     37            throw new Exception("This FTP directory is not in existence."); 
     38        } else { 
     39            ftp_.close(); 
     40            return new FtpFolder(toString_, name_, username_, password_, port_); 
     41        } 
     42    } 
     43 
     44    VfsFolder create() { 
     45        if(exists()) { 
     46            ftp_.close(); 
     47            throw new Exception("This FTP Directory is already in existence."); 
     48        } else { 
     49            ftp_.close(); 
     50            ftp_ = new FTPConnection(); 
     51            ftp_.connect(toString_, username_, password_, port_); 
     52            ftp_.mkdir(name_); 
     53            ftp_.close(); 
     54        } 
     55        return new FtpFolder(toString_, name_, username_, password_, port_); 
     56    } 
     57 
     58    bool exists() { 
     59        ftp_ = new FTPConnection(); 
     60        ftp_.connect(toString_, username_, password_, port_); 
     61        return ftp_.exist(name_) == 2 ? true : false; 
     62    } 
     63
     64 
     65class FtpFolder: VfsFolder { 
     66 
     67    FTPConnection ftp_; 
     68    char[] toString_, name_, username_, password_; 
     69    uint port_; 
     70    bool writeable_, writeableSet_; 
     71 
     72    public this(char[] server, char[] path, char[] username = "anonymous", 
     73                char[] password = "anonymous@anonymous", uint port = 21) 
     74    in { 
     75        assert(server != ""); 
     76    } 
     77    out { 
     78        assert(server != ""); 
     79    } 
     80    body { 
     81        toString_ = server; 
     82        name_ = name != "" ? path : server; 
     83        username_ = username; 
     84        password_ = password; 
     85        port_ = port; 
     86    } 
     87 
     88    char[] name() { 
     89        return name_; 
     90    } 
     91 
     92    char[] toString() { 
     93        return toString_ ~ '/' ~ name_; 
     94    } 
     95 
     96    VfsFile file(char[] path) { 
     97        return new FtpFile(toString_ ~ name_, path, username_, password_, port_); 
     98    } 
     99 
     100    VfsFolderEntry folder(char[] path) { 
     101        return new FtpFolderEntry(toString_, name_, username_, password_, port_); 
     102    } 
     103 
     104    VfsFolders self() { 
     105        return new VfsFolders(toString_, name_, username_, password_, port_, 
     106            true); 
     107    } 
     108 
     109    VfsFolders tree() { 
     110        return new VfsFolders(toString_, name_, username_, password_, port_, 
     111            false); 
     112    } 
     113 
     114    int opApply(int delegate(inout VfsFolder) dg) { 
     115        ftp_ = new FTPConnection(); 
     116        ftp_.connect(toString_, username_, password_, port_); 
     117        FtpFileInfo[] fis = ftp_.ls(name_); 
     118        ftp_.close(); 
     119 
     120        int result; 
     121 
     122        foreach(FtpFileInfo fi; fis) { 
     123            if(fi.type = FtpFileType.dir) { 
     124                FtpFolder x = new FtpFolder(toString_, fi.name, username_, 
     125                    password_, port_); 
     126                if((result = dg(x)) != 0) 
    332127                    break; 
    333128            } 
    334             return result; 
    335         } 
    336     } 
    337  
    338     private void populateFolders(bool redone) 
    339     { 
    340         bool redo = false; 
    341         foreach(FtpFolder fol; redone ? _folders : _primaryFolders) 
    342         { 
    343             FtpFileInfo[] fi = fol._ftp.ls(); 
    344             foreach(FtpFileInfo finf; fi) 
    345             { 
    346                 if(finf.type == FtpFileType.cdir || finf.type == FtpFileType.dir) 
    347                 { 
    348                     _folders ~= new FtpFolder(_ftpFol.toString ~ '/' ~ finf.name, "", _ftpFol._username, _ftpFol._password, _ftpFol._port);             } 
     129        } 
     130 
     131        return result; 
     132    } 
     133 
     134    VfsFolder clear() { 
     135        ftp_ = new FTPConnection(); 
     136        ftp_.connect(toString_, username_, password_, port_); 
     137        ftp_.del(name_); 
     138        ftp_.mkdir(name_); 
     139        ftp_.close(); 
     140        return new FtpFolder(toString_, name_, username_, password_, port_); 
     141    } 
     142 
     143    bool writable() { 
     144        ftp_ = new FTPConnection(); 
     145        ftp_.connect(toString_, username_, password_, port_); 
     146        if(writeableSet_) { 
     147            return writeable_; 
     148        } else if(ftp_.exist(ftp_.mkdir("diw"))) { 
     149            try { 
     150                ftp_.rm("diw"); 
     151                writeable_ = true; 
     152            } catch(Exception e) { 
     153                writeable_ = false; 
    349154            } 
    350         } 
    351         if(redo) 
    352             populateFolders(redo); 
    353         else  
    354             _foldersPopulated = true; 
    355         for(int i = 0; i < _folders.length; i++) 
    356         { 
    357             for(int j = 0; j < _folders.length; j++) 
    358             { 
    359                 if(_folders[i] == _folders[j]) 
    360                 { 
    361                     FtpFolder[] temp = _folders.dup; 
    362                     _folders[0..j] = temp[0..j].dup; 
    363                     _folders[j..$] = temp[j..$].dup; 
    364                 } 
    365             } 
    366         } 
    367     } 
    368  
    369     /*********************************************************************** 
    370  
    371         Return the number of files  
    372  
    373     ***********************************************************************/ 
    374  
    375     uint files() 
    376     { 
    377         FtpFileInfo[] inf; 
    378         foreach(FtpFolder fol; _folders) 
    379         { 
    380             inf ~= fol._ftp.ls(); 
    381         } 
    382         uint to_return = 0; 
    383         foreach(FtpFileInfo info; inf) 
    384         { 
    385             if(info.type == FtpFileType.file || info.type ==  FtpFileType.other || info.type == FtpFileType.unknown) 
    386             { 
    387                 to_return++; 
    388             } 
    389         } 
    390         return to_return; 
    391     } 
    392  
    393     /*********************************************************************** 
    394  
    395         Return the number of folders  
    396  
    397     ***********************************************************************/ 
    398  
    399     uint folders() 
    400     { 
    401         if(_flat) 
    402             return 0; 
    403         else 
    404         { 
    405             FtpFileInfo[] inf; 
    406             foreach(FtpFolder fol; _folders) 
    407             { 
    408                 inf ~= fol._ftp.ls(); 
    409             } 
    410             uint to_return = 0; 
    411             foreach(FtpFileInfo info; inf) 
    412             { 
    413                 if(info.type == FtpFileType.cdir || info.type ==  FtpFileType.dir || info.type == FtpFileType.pdir) 
    414                 { 
    415                     to_return++; 
    416                 } 
    417             } 
    418             return to_return; 
    419         } 
    420     } 
    421  
    422     /*********************************************************************** 
    423  
    424         Return the total number of entries (files + folders) 
    425  
    426     ***********************************************************************/ 
    427  
    428     uint entries() 
    429     { 
    430         return folders + files; 
    431     } 
    432  
    433     /*********************************************************************** 
    434  
    435         Return the total size of contained files  
    436  
    437     ***********************************************************************/ 
    438  
    439     ulong bytes() 
    440     { 
    441         FtpFileInfo[] inf; 
    442         foreach(FtpFolder fol; _folders) 
    443         { 
    444             inf ~= fol._ftp.ls(); 
    445         } 
    446         ulong to_return = 0; 
    447         foreach(FtpFileInfo info; inf) 
    448         { 
    449             if(info.type == FtpFileType.file || info.type ==  FtpFileType.other || info.type == FtpFileType.unknown) 
    450             { 
    451                 to_return += info.size; 
    452             } 
    453         } 
    454         return to_return; 
    455     } 
    456  
    457     /*********************************************************************** 
    458  
    459         Return a subset of folders matching the given pattern 
    460  
    461     ***********************************************************************/ 
    462  
    463     VfsFolders subset (char[] pattern) 
    464     { 
    465         if(_flat) 
    466             return null; 
    467         else 
    468         { 
    469             FtpFolder[] folders = null; 
    470             foreach(FtpFolder folder; _folders) 
    471             { 
    472                 if(containsPattern(folder.name, pattern) || containsPattern(folder.toString, pattern)) 
    473                     folders ~= folder; 
    474             } 
    475             return new FtpFolders(folders); 
    476         } 
    477     } 
    478  
    479     /*********************************************************************** 
    480  
    481         Return a set of files matching the given pattern 
    482  
    483     ***********************************************************************/ 
    484  
    485     VfsFiles catalog (char[] pattern) 
    486     { 
    487         VfsFiles files = new FtpFiles(this); 
    488         VfsFile[] toCreateNewVfsFilesInstance; 
    489         foreach(VfsFile file; files) 
    490         { 
    491             if(containsPattern(file.name, pattern) || containsPattern(file.toString, pattern)) 
    492                 toCreateNewVfsFilesInstance ~= file; 
    493         } 
    494         return new FtpFiles(cast(FtpFile[]) toCreateNewVfsFilesInstance); 
    495     } 
    496  
    497     /*********************************************************************** 
    498  
    499         Return a set of files matching the given filter 
    500  
    501     ***********************************************************************/ 
    502  
    503     VfsFiles catalog (VfsFilter filter = null) 
    504     { 
    505         if(filter == null) 
    506         { 
    507             return catalog(""); 
    508         } 
    509         else 
    510         { 
    511             FtpFiles files = new FtpFiles(this); 
    512             VfsFile[] toCreateNewVfsFilesInstance; 
    513             foreach(VfsFile file; files) 
    514             { 
    515                 FtpFile f = cast(FtpFile) file; 
    516                 if(filter(&f._filt)) 
    517                     toCreateNewVfsFilesInstance ~= file; 
    518             } 
    519             return new FtpFiles(cast(FtpFile[])toCreateNewVfsFilesInstance); 
    520         } 
    521     } 
    522 
    523  
    524 /******************************************************************************* 
    525  
    526         A specific file representation  
    527  
    528 *******************************************************************************/ 
    529  
    530  class FtpFile : VfsFile 
    531 
    532     /*********************************************************************** 
    533  
    534         Return a short name 
    535  
    536     ***********************************************************************/ 
    537  
    538     FtpFolder _folder; 
    539     char[] _toString; 
    540     char[] _name; 
    541     VfsFilterInfo _filt; 
    542  
    543     package this(FtpFolder fol, char[] path) 
    544     { 
    545         _name ='/' ~ path; 
    546         _toString = fol.toString ~ path; 
    547         //_folder = new FtpFolder(fol); 
    548         _folder = fol; 
    549         _filt.name = _name; 
    550         _filt.path = _toString; 
    551         _filt.folder = false; 
    552         try 
    553         { 
    554             _filt.bytes = _folder._ftp.getFileInfo(path).size; 
    555         } 
    556         catch(Exception e) 
    557         { 
    558             _filt.bytes =  0; 
    559         } 
    560     } 
    561  
    562     public this(VfsFolder fol, char[] path) 
    563     { 
    564         this(cast(FtpFolder) fol, path); 
    565     } 
    566  
    567     char[] name() 
    568     { 
    569         return _name; 
    570     } 
    571  
    572     /*********************************************************************** 
    573  
    574         Return a long name 
    575  
    576     ***********************************************************************/ 
    577  
    578     char[] toString() 
    579     { 
    580         return _toString; 
    581     } 
    582  
    583     /*********************************************************************** 
    584  
    585         Does this file exist? 
    586  
    587     ***********************************************************************/ 
    588  
    589     bool exists() 
    590     { 
    591         if(((_folder._ftp.exist(_name) || _folder._ftp.exist(_toString)) == 0) || ((_folder._ftp.exist(_name) || _folder._ftp.exist(_toString)) == 2)) 
    592             return false; 
    593         else 
    594             return true; 
    595     } 
    596  
    597     /*********************************************************************** 
    598  
    599         Return the file size 
    600  
    601     ***********************************************************************/ 
    602  
    603     ulong size () 
    604     { 
    605         return _filt.bytes; 
    606     } 
    607  
    608     /*********************************************************************** 
    609  
    610         Create and copy the given source 
    611  
    612     ***********************************************************************/ 
    613  
    614     VfsFile copy (VfsFile source) 
    615     { 
    616         InputStream istream = source.input; 
    617         this.create(); 
    618         OutputStream os = this.output; 
    619         void[] dest; 
    620         istream.read(dest); 
    621         os.write(dest); 
    622         return new FtpFile(_folder, _toString); 
    623     } 
    624  
    625     /*********************************************************************** 
    626  
    627         Create and copy the given source, and remove the source 
    628  
    629     ***********************************************************************/ 
    630  
    631     VfsFile move (VfsFile source) 
    632     { 
    633         copy(source); 
    634         source.remove(); 
    635         return new FtpFile(_folder, _toString); 
    636     } 
    637  
    638     /*********************************************************************** 
    639  
    640         Create a new file instance 
    641  
    642     ***********************************************************************/ 
    643  
    644     VfsFile create () 
    645     { 
    646         _folder._ftp.del(this.toString); 
    647         FileConduit.Style style; 
    648         style.access = FileConduit.Access.ReadWrite; 
    649         style.cache = FileConduit.Cache.None; 
    650         style.open = FileConduit.Open.Create; 
    651         style.share = FileConduit.Share.None; 
    652         FilePath mfp = new FilePath(""); 
    653         mfp.set(mfp.root ~ "//NF"); 
    654         FileConduit fs = new FileConduit(mfp, style); 
    655         byte[] src; 
    656         src ~= cast(byte)1; 
    657         src ~= cast(byte)1; 
    658         fs.write(src); 
    659         return create(fs);  
    660     } 
    661  
    662     /*********************************************************************** 
    663  
    664         Create a new file instance and populate with stream 
    665  
    666         ***********************************************************************/ 
    667  
    668     VfsFile create (InputStream stream) 
    669     { 
    670         _folder._ftp.del(this.toString); 
    671         _folder._ftp.put(this.toString, stream, null, FtpFormat.image); 
    672         return new FtpFile(this._folder, this.toString); 
    673     } 
    674  
    675     /*********************************************************************** 
    676  
    677         Remove this file 
    678  
    679     ***********************************************************************/ 
    680  
    681     VfsFile remove () 
    682     { 
    683         _folder._ftp.del(this.toString); 
    684         return new FtpFile(this._folder, this.toString); 
    685     } 
    686  
    687     /*********************************************************************** 
    688  
    689         Return the input stream. Don't forget to close it 
    690  
    691     ***********************************************************************/ 
    692  
    693     InputStream input () 
    694     { 
    695         InputStream istream = _folder._ftp.input; 
    696         prepareInputStream(_folder._ftp, istream, FtpCommand.RETR, ["", _toString]); 
    697         return istream; 
    698     } 
    699  
    700     /*********************************************************************** 
    701  
    702         Return the output stream. Don't forget to close it 
    703  
    704     ***********************************************************************/ 
    705  
    706     OutputStream output () 
    707     { 
    708         OutputStream os = _folder._ftp.output; 
    709         prepareOutputStream(_folder._ftp, os, FtpCommand.STOR, ["", _toString]); 
    710         return os; 
    711     } 
    712  
    713     /*********************************************************************** 
    714  
    715         Returns a copy of this instance; 
    716         Does not copy file. 
    717  
    718     ***********************************************************************/ 
    719  
    720     VfsFile dup () 
    721     { 
    722         return new FtpFile(_folder, toString); 
    723     } 
    724 
    725  
    726 /******************************************************************************* 
    727  
    728         Operations upon a set of files 
    729  
    730 *******************************************************************************/ 
    731   
    732 class FtpFiles : VfsFiles 
    733 
    734     FtpFile[] _files; 
    735     FtpFolder[] _folders; 
    736     /*********************************************************************** 
    737  
    738         Iterate over the set of contained VfsFile instances 
    739  
    740     ***********************************************************************/ 
    741  
    742     package this(FtpFolders folders) 
    743     { 
    744         foreach(folder; folders) 
    745         { 
    746             _folders ~= cast(FtpFolder)folder; 
    747         } 
    748         populateFiles(); 
    749     } 
    750      
    751     public this(char[] server, char[] path = "", char[] username = "anonymous", char[] password = "anonymous@anonymous", uint port = 21) 
    752     in 
    753     { 
    754         assert((server != null || server != "")); 
    755     } 
    756     body 
    757     { 
    758         _folders ~= new FtpFolder(server, path, username, password, port); 
    759     } 
    760  
    761     private void populateFiles() 
    762     { 
    763         foreach(FtpFolder folder; _folders) 
    764         { 
    765             foreach(VfsFolder fol; folder) 
    766             { 
    767                 FtpFolder cfol = cast(FtpFolder) fol; 
    768                 FtpFileInfo[] fis = cfol._ftp.ls(); 
    769                 foreach(FtpFileInfo fi; fis) 
    770                 { 
    771                     if(fi.type == FtpFileType.file || fi.type == FtpFileType.other || fi.type == FtpFileType.unknown) 
    772                     { 
    773                         _files ~= new FtpFile(cfol, fi.name); 
    774                     } 
    775                 } 
    776             } 
    777         } 
    778     } 
    779  
    780     package this(FtpFile[] files) 
    781     { 
    782         _files = files; 
    783     } 
    784  
    785     public this(VfsFolders folders) 
    786     { 
    787         this(cast(FtpFolders) folders); 
    788     } 
    789  
    790     public this(VfsFile[] files) 
    791     { 
    792         this(cast(FtpFile[]) files); 
    793     } 
    794  
    795     int opApply (int delegate(inout VfsFile) dg) 
    796     { 
    797         int result; 
    798          
    799         foreach(file; _files) 
    800         { 
    801             VfsFile x = file; 
    802             if ((result = dg(x)) != 0) 
    803                     break; 
    804         } 
    805  
    806         return result; 
    807     } 
    808  
    809     /*********************************************************************** 
    810  
    811         Return the total number of entries  
    812  
    813     ***********************************************************************/ 
    814  
    815     uint files() 
    816     { 
    817         return _files.length; 
    818     } 
    819  
    820     /*********************************************************************** 
    821  
    822         Return the total size of all files  
    823  
    824     ***********************************************************************/ 
    825  
    826     ulong bytes() 
    827     { 
    828         ulong to_return; 
    829         foreach(file; this) 
    830         { 
    831             to_return += file.size; 
    832         } 
    833         return to_return; 
    834     } 
    835 
    836  
    837 /******************************************************************************* 
    838  
    839         Handler for folder operations. Needs some work ... 
    840  
    841 *******************************************************************************/ 
    842  
    843 class FtpFolderEntry : VfsFolderEntry 
    844 
    845     FtpFolder _folder; 
    846     /*********************************************************************** 
    847  
    848         Open a folder 
    849  
    850     ***********************************************************************/ 
    851  
    852     package this(FtpFolder fol, char[] path) 
    853     { 
    854         //_folder = new FtpFolder(fol); 
    855         _folder = fol; 
    856     } 
    857  
    858     public this(VfsFolder fol) 
    859     { 
    860         this(cast(FtpFolder) fol, ""); 
    861     } 
    862      
    863     public this(char[] server, char[] path = "", char[] username = "anonymous", char[] password = "anonymous@anonymous", uint port = 21) 
    864     in 
    865     { 
    866         assert((server != null || server != "")); 
    867     } 
    868     body 
    869     { 
    870         _folder = new FtpFolder(server, path, username, password, port); 
    871     } 
    872  
    873     VfsFolder open () 
    874     { 
    875         //return new FtpFolder(_folder); 
    876         _folder.open(); 
    877         return _folder; 
    878     } 
    879  
    880     /*********************************************************************** 
    881  
    882         Create a new folder 
    883  
    884     ***********************************************************************/ 
    885  
    886     VfsFolder create () 
    887     { 
    888         _folder._ftp.mkdir(_folder.toString); 
    889         return _folder; 
    890     } 
    891  
    892     /*********************************************************************** 
    893  
    894         Test to see if a folder exists 
    895  
    896     ***********************************************************************/ 
    897  
    898     bool exists () 
    899     { 
    900         if(2 == _folder._ftp.exist(_folder.toString)) 
    901             return true; 
    902         else 
    903             return false; 
    904     } 
    905 
    906  
     155        } else { 
     156            writeable_ = false; 
     157        } 
     158        writeableSet_ = true; 
     159        ftp_.close(); 
     160        return writeable_; 
     161    } 
     162 
     163    VfsFolder close(bool commit = true) { 
     164        ftp_.close(); 
     165        return new FtpFolder(toString_, name_, username_, password_, port_); 
     166    } 
     167 
     168    void verify(VfsFolder folder, bool mounting) { 
     169        return null; 
     170    } 
     171
     172 
     173class FtpFolders: VfsFolders { 
     174 
     175    FTPConnection ftp_; 
     176    char[] toString_, name_, username_, password_; 
     177    uint port_; 
     178 
     179    public this(char[] server, char[] path, char[] username = "anonymous", 
     180                char[] password = "anonymous@anonymous", uint port = 21) 
     181    in { 
     182        assert(server != ""); 
     183    } 
     184    out { 
     185        assert(server != ""); 
     186    } 
     187    body { 
     188        toString_ = server; 
     189        name_ = name != "" ? path : server; 
     190        username_ = username; 
     191        password_ = password; 
     192        port_ = port; 
     193    } 
     194 
     195    int opApply(int delegate(inout VfsFolder) dg) { 
     196        FtpFolder ftp = new FtpFolder(toString_, name_, username, password_, 
     197            port_); 
     198        ftp.opApply(dg); 
     199    } 
     200 
     201    uint files() { 
     202        ftp_ = new FTPConnection(); 
     203        ftp_.connect(toString_, username_, password_, port_); 
     204        FtpFileInfo[] fis = ftp_.ls(); 
     205        ftp_.close(); 
     206        uint size; 
     207 
     208        foreach(FtpFileInfo fi; fis) { 
     209            if(fi.type == FtpFileType.unknown || fi.type == FTPFileType.file || fi.type == FtpFileType.other) 
     210                size++; 
     211        } 
     212 
     213        return size; 
     214    } 
     215 
     216    uint folders() { 
     217        ftp_ = new FTPConnection(); 
     218        ftp_.connect(toString_, username_, password_, port_); 
     219        FtpFileInfo[] fis = ftp_.ls(); 
     220        ftp_.close(); 
     221        uint size; 
     222 
     223        foreach(FtpFileInfo fi; fis) { 
     224            if(fi.type != FtpFileType.unknown || fi.type != FTPFileType.file || fi.type != FtpFileType.other) 
     225                size++; 
     226        } 
     227 
     228        return size; 
     229    } 
     230 
     231    uint entries() { 
     232        return files + folders; 
     233    } 
     234 
     235    ulong bytes() { 
     236        ftp_ = new FTPConnection(); 
     237        ftp_.connect(toString_, username_, password_, port_); 
     238        FtpFileInfo[] fis = ftp_.ls(); 
     239        ftp_.close(); 
     240        uint size; 
     241 
     242        foreach(FtpFileInfo fi; fis) { 
     243            if(fi.type == FtpFileType.unknown || fi.type == FTPFileType.file || fi.type == FtpFileType.other) 
     244                size += fi.size; 
     245        } 
     246 
     247        return size; 
     248    } 
     249 
     250    VfsFiles catalog(char[] pattern) { 
     251        return null; 
     252    } 
     253 
     254    VfsFiles catalog(VfsFilter filter = null) { 
     255        return null; 
     256    } 
     257
     258 
     259class FtpFile: VfsFile { 
     260    char[] name() { 
     261        return null; 
     262    } 
     263 
     264    char[] toString() { 
     265        return null; 
     266    } 
     267 
     268    bool exists() { 
     269        return null; 
     270    } 
     271 
     272    ulong size() { 
     273        return null; 
     274    } 
     275 
     276    VfsFile copy(VfsFile source) { 
     277        return null; 
     278    } 
     279 
     280    VfsFile move(VfsFile source) { 
     281        return null; 
     282    } 
     283 
     284    VfsFile create() { 
     285        return null; 
     286    } 
     287 
     288    VfsFile create(InputStream stream) { 
     289        return null; 
     290    } 
     291 
     292    VfsFile remove() { 
     293        return null; 
     294    } 
     295 
     296    InputStream input() { 
     297  &nb