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

Changeset 3411

Show
Ignore:
Timestamp:
04/01/08 19:13:55 (8 months ago)
Author:
lmartin92
Message:

Now it has a data Socket Conduit getter that allows me to get a data socket(sounds very redundant doesn't it) :-)

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • branches/lmartin/ftp/tango/net/ftp/FtpClient.d

    r3395 r3411  
    1313    import tango.net.Socket; 
    1414    import tango.net.SocketConduit; 
    15     private import tango.time.Clock; 
     15    import tango.time.Clock; 
     16    import tango.time.Time; 
     17    import Text = tango.text.Util; 
     18    import tango.text.Regex: Regex; 
     19    import Ascii = tango.text.Ascii; 
     20    import Integer = tango.text.convert.Integer; 
     21    import tango.time.chrono.Gregorian; 
     22    import tango.io.GrowBuffer; 
    1623} 
    1724 
     
    184191 
    185192class FtpException: Exception { 
    186     this(char[] msg) { 
    187         super(msg); 
     193    char[3] responseCode_ = "000"; 
     194 
     195    /// Construct an FtpException based on a message and code. 
     196    /// 
     197    /// Params: 
     198    ///    message =         the exception message 
     199    ///    code =            the code (5xx for fatal errors) 
     200    this(char[] message, char[3] code = "420") { 
     201        this.responseCode_[] = code; 
     202        super(message); 
     203    } 
     204 
     205    /// Construct an FtpException based on a response. 
     206    /// 
     207    /// Params: 
     208    ///    r =               the server response 
     209    this(FtpResponse r) { 
     210        this.responseCode_[] = r.code; 
     211        super(r.message); 
     212    } 
     213 
     214    /// A string representation of the error. 
     215    char[] toString() { 
     216        char[] buffer = new char[this.msg.length + 4]; 
     217 
     218        buffer[0 .. 3] = this.responseCode_; 
     219        buffer[3] = ' '; 
     220        buffer[4 .. buffer.length] = this.msg; 
     221 
     222        return buffer; 
    188223    } 
    189224} 
     
    259294        readResponse("220"); 
    260295 
    261         if(username.length = 0) { 
     296        if(username.length == 0) { 
    262297            return; 
    263298        } 
     
    394429    } 
    395430 
    396     int exist(char[] file) { 
    397         try { 
    398             auto fi = getFileInfo(file); 
    399             if(fi.type == FtpFileType.file) 
    400                 return 1; 
    401             else if(fi.type == FtpFileType.dir || fi.type == FtpFileType.cdir || fi.type == FtpFileType.pdir) 
    402                 return 2; 
    403         } catch(FTPException o) { 
    404             if(o.response_code != "501") 
    405                 throw o; 
    406         } 
    407         return 0; 
    408     } 
    409  
    410     long size(char[] file) { 
    411         try 
    412         return getFileInfo(file).size; 
    413         catch(FTPException o) { 
    414             if(o.response_code != "501") 
    415                 throw o; 
    416         } 
    417         return -1; 
    418     } 
     431    /**     int exist(char[] file) { 
     432     *      try { 
     433     *          auto fi = getFileInfo(file); 
     434     *          if(fi.type == FtpFileType.file) 
     435     *              return 1; 
     436     *          else if(fi.type == FtpFileType.dir || fi.type == FtpFileType.cdir || fi.type == FtpFileType.pdir) 
     437     *              return 2; 
     438     *      } catch(FtpException o) { 
     439     *          if(o.responseCode_ != "501") 
     440     *              throw o; 
     441     *      } 
     442     *      return 0; 
     443     *  } 
     444     */ 
    419445 
    420446    public size_t size(char[] path, FtpFormat format = FtpFormat.image) 
     
    448474    } 
    449475 
    450     Time* modified(char[] file) { 
    451         auto ret = new Time; 
    452         try { 
    453             *ret = filemtime(file); 
    454             return ret; 
    455         } catch(FTPException o) { 
    456             if(o.response_code != "501") 
    457                 try { 
    458                     *ret = getFileInfo(file).modify; 
    459                     return ret; 
    460                 } catch(FTPException o) { 
    461                     if(o.response_code != "501") 
    462                         throw o; 
    463                 } 
    464         } 
    465         return null; 
     476    Time* modified(char[] file) 
     477    in { 
     478        assert(file.length > 0); 
     479    } 
     480    body { 
     481        this.sendCommand("MDTM", file); 
     482        auto response = this.readResponse("213"); 
     483 
     484        // The whole response should be a timeval. 
     485        return &this.parseTimeval(response.message); 
     486    } 
     487 
     488    protected Time parseTimeval(char[] timeval) { 
     489        if(timeval.length < 14) 
     490            throw new FtpException("CLIENT: Unable to parse timeval", "501"); 
     491 
     492        return Gregorian.generic.toTime(Integer.atoi(timeval[0 .. 4]), 
     493            Integer.atoi(timeval[4 .. 6]), Integer.atoi(timeval[6 .. 8]), 
     494            Integer.atoi(timeval[8 .. 10]), 
     495            Integer.atoi(timeval[10 .. 12]), 
     496            Integer.atoi(timeval[12 .. 14])); 
    466497    } 
    467498 
     
    488519        // 221 means FEAT is supported, and a list follows.  Otherwise we don't know... 
    489520        if(response.code != "211") 
    490             delete supported_features
     521            delete supportedFeatures_
    491522        else { 
    492523            char[][] lines = Text.splitLines(response.message); 
    493524 
    494525            // There are two more lines than features, but we also have FEAT. 
    495             supported_features = new FtpFeature[lines.length - 1]; 
    496             supported_features[0].command = "FEAT"; 
     526            supportedFeatures_ = new FtpFeature[lines.length - 1]; 
     527            supportedFeatures_[0].command = "FEAT"; 
    497528 
    498529            for(size_t i = 1; i < lines.length - 1; i++) { 
    499530                size_t pos = Text.locate(lines[i], ' '); 
    500531 
    501                 supported_features[i].command = lines[i][0 .. pos]; 
     532                supportedFeatures_[i].command = lines[i][0 .. pos]; 
    502533                if(pos < lines[i].length - 1) 
    503                     supported_features[i].params = lines[i][pos + 1 .. lines[i].length]; 
     534                    supportedFeatures_[i].params = lines[i][pos + 1 .. lines[i].length]; 
    504535            } 
    505536 
     
    621652    } 
    622653 
    623 
     654    /// Get a data socket from the server. 
     655    /// 
     656    /// This sends PASV/PORT as necessary. 
     657    /// 
     658    /// Returns:             the data socket or a listener 
     659    protected SocketConduit getDataSocket() { 
     660        // What type are we using? 
     661        switch(this.inf_.type) { 
     662        default: 
     663            exception("unknown connection type"); 
     664 
     665        // Passive is complicated.  Handle it in another member. 
     666        case FtpConnectionType.passive: 
     667            return this.connectPassive(); 
     668 
     669            // Active is simpler, but not as fool-proof. 
     670        case FtpConnectionType.active: 
     671            IPv4Address data_addr = cast(IPv4Address) this.inf_.address; 
     672 
     673            // Start listening. 
     674            SocketConduit listener = new SocketConduit(); 
     675            listener.bind(this.inf_.listen); 
     676            listener.socket.listen(32); 
     677 
     678            // Use EPRT if we know it's supported. 
     679            if(this.is_supported("EPRT")) { 
     680                char[64] tmp = void; 
     681 
     682                this.sendCommand("EPRT", Text.layout(tmp, "|1|%0|%1|", 
     683                    data_addr.toAddrString, data_addr.toPortString)); 
     684                // this.sendCommand("EPRT", format("|1|%s|%s|", data_addr.toAddrString(), data_addr.toPortString())); 
     685                this.readResponse("200"); 
     686            } else { 
     687                int h1, h2, h3, h4, p1, p2; 
     688                h1 = (data_addr.addr() >> 24) % 256; 
     689                h2 = (data_addr.addr() >> 16) % 256; 
     690                h3 = (data_addr.addr() >> 8_) % 256; 
     691                h4 = (data_addr.addr() >> 0_) % 256; 
     692                p1 = (data_addr.port() >> 8_) % 256; 
     693                p2 = (data_addr.port() >> 0_) % 256; 
     694 
     695                // low overhead method to format a numerical string 
     696                char[64] tmp = void; 
     697                char[20] foo = void; 
     698                auto str = Text.layout(tmp, "%0,%1,%2,%3,%4,%5", 
     699                    Integer.format(foo[0 .. 3], h1), Integer.format( 
     700                        foo[3 .. 6], h2), Integer.format( 
     701                            foo[6 .. 9], h3), Integer.format( 
     702                                foo[9 .. 12], h4), Integer.format( 
     703                                    foo[12 .. 15], p1), Integer.format( 
     704                                        foo[15 .. 18], p2)); 
     705 
     706                // This formatting is weird. 
     707                // this.sendCommand("PORT", format("%d,%d,%d,%d,%d,%d", h1, h2, h3, h4, p1, p2)); 
     708 
     709                this.sendCommand("PORT", str); 
     710                this.readResponse("200"); 
     711            } 
     712 
     713            return listener; 
     714        } 
     715        assert(false); 
     716    } 
     717 
     718    /// Send a PASV and initiate a connection. 
     719    /// 
     720    /// Returns:             a connected socket 
     721    public SocketConduit connectPassive() { 
     722        Address connect_to = null; 
     723 
     724        // SPSV, which is just a port number. 
     725        if(this.is_supported("SPSV")) { 
     726            this.sendCommand("SPSV"); 
     727            auto response = this.readResponse("227"); 
     728 
     729            // Connecting to the same host. 
     730            IPv4Address 
     731            remote = cast(IPv4Address) this.socket.socket.remoteAddress(); 
     732            assert(remote !is null); 
     733 
     734            uint address = remote.addr(); 
     735            uint port = toInt(response.message); 
     736 
     737            connect_to = new IPv4Address(address, cast(ushort) port); 
     738        } 
     739        // Extended passive mode (IP v6, etc.) 
     740        else if(this.is_supported("EPSV")) { 
     741            this.sendCommand("EPSV"); 
     742            auto response = this.readResponse("229"); 
     743 
     744            // Try to pull out the (possibly not parenthesized) address. 
     745            auto r = Regex(`\([^0-9][^0-9][^0-9](\d+)[^0-9]\)`); 
     746            if(!r.test(response.message)) 
     747                throw new FtpException("CLIENT: Unable to parse address", "501"); 
     748 
     749            IPv4Address 
     750            remote = cast(IPv4Address) this.socket.socket.remoteAddress(); 
     751            assert(remote !is null); 
     752 
     753            uint address = remote.addr(); 
     754            uint port = toInt(r.match(1)); 
     755 
     756            connect_to = new IPv4Address(address, cast(ushort) port); 
     757        } else { 
     758            this.sendCommand("PASV"); 
     759            auto response = this.readResponse("227"); 
     760 
     761            // Try to pull out the (possibly not parenthesized) address. 
     762            auto r = Regex( 
     763            `(\d+),\s*(\d+),\s*(\d+),\s*(\d+),\s*(\d+)(,\s*(\d+))?`); 
     764            if(!r.test(response.message)) 
     765                throw new FtpException("CLIENT: Unable to parse address", "501"); 
     766 
     767            // Now put it into something std.socket will understand. 
     768            char[] 
     769                 address = r.match(1) ~ "." ~ r.match(2) ~ "." ~ r.match(3) ~ "." ~ r.match( 
     770                     4); 
     771            uint 
     772            port = (toInt(r.match(5)) << 8) + (r.match(7).length > 0 ? toInt( 
     773                r.match(7)) : 0); 
     774 
     775            // Okay, we've got it! 
     776            connect_to = new IPv4Address(address, port); 
     777        } 
     778 
     779        scope(exit) 
     780        delete connect_to; 
     781 
     782        // This will throw an exception if it cannot connect. 
     783        SocketConduit sock = new SocketConduit(); 
     784        sock.connect(connect_to); 
     785        return sock; 
     786    } 
     787 
     788    public bool isSupported(char[] command) 
     789    in { 
     790        assert(command.length > 0); 
     791    } 
     792    body { 
     793        if(this.supportedFeatures_.length == 0) 
     794            return true; 
     795 
     796        // Search through the list for the feature. 
     797        foreach(FtpFeature feat; this.supportedFeatures_) { 
     798            if(Ascii.icompare(feat.command, command) == 0) 
     799                return true; 
     800        } 
     801 
     802        return false; 
     803    } 
     804 
     805    public bool is_supported(char[] command) { 
     806        if(this.supportedFeatures_.length == 0) 
     807            return false; 
     808 
     809        return this.isSupported(command); 
     810    } 
     811