Guest User

Untitled

a guest
Apr 27th, 2018
137
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
D 25.55 KB | None | 0 0
  1. module std.net.ftp;
  2.  
  3. import std.net.uri;
  4. import std.socket, std.stream, std.conv;
  5. import std.traits;
  6. import std.typecons, std.array, std.string;
  7. import core.thread;
  8.  
  9. import std.stdio : writeln, writef, readln, writefln;
  10. import std.datetime: Clock;
  11.  
  12.  
  13. class FtpClient
  14. {
  15.     enum defaultUser = "anonymous";
  16.     enum defaultPass = "anonymous@anonymous";
  17.     enum defaultPort = 21;
  18.     enum bufferSize = 1024 * 4;
  19.    
  20.     private
  21.     {
  22.         alias Tuple!(ushort, "code", string, "msg") Response;
  23.         alias Tuple!(string, "ip", ushort, "port") dataSocketInfo;
  24.    
  25.         bool _connected = false;
  26.         Uri _uri;
  27.         TcpSocket _socket;
  28.         FtpStream _stream;
  29.         Format _format;
  30.         Mode _mode;
  31.     }
  32.    
  33.     enum Format
  34.     {
  35.         Binary,
  36.         Ascii,
  37.     }
  38.    
  39.     enum Mode
  40.     {
  41.         Active,
  42.         Passive,
  43.     }
  44.    
  45.    
  46.    
  47.     void delegate(size_t current, size_t total) Progress;
  48.    
  49.     /*this(Uri uri)
  50.     {
  51.         if ( uri.user is null )
  52.         {
  53.             uri.user = defaultUser;
  54.             uri.password = defaultPass;
  55.         }
  56.        
  57.         if ( uri.port == 0 )
  58.         {
  59.             uri.port = defaultPort;
  60.         }
  61.        
  62.         _uri = uri;
  63.         this();
  64.     }*/
  65.    
  66.     this(string host, ushort port)
  67.     {
  68.         this();
  69.         _uri.parse(host, port);
  70.     }
  71.    
  72.     this()
  73.     {
  74.         _socket = new TcpSocket();
  75.         _stream = FtpStream(_socket, 30);
  76.         _mode = Mode.Passive;
  77.         _format = Format.Binary;
  78.     }
  79.    
  80.     ~this()
  81.     {
  82.         //close();
  83.         // need to implement somekind of lock in order to wait for
  84.         // Async downloads to finish their work
  85.     }
  86.    
  87.     void auth(string username, string password)
  88.     {
  89.         _uri.user     = username;
  90.         _uri.password = password;
  91.     }
  92.    
  93.     void open()
  94.     {
  95.         if ( _connected == true ) return;
  96.    
  97.         _socket.connect(new InternetAddress(_uri.host, _uri.port));
  98.    
  99.         Response response = readResponse();
  100.    
  101.         if ( response.code != 220 )
  102.         {
  103.             throw new FtpException(response);
  104.         }
  105.    
  106.         if ( _uri.user != "" )
  107.         {
  108.             response = exec("USER", _uri.user);
  109.        
  110.             if ( response.code != 331 )
  111.             {
  112.                 throw new FtpException(response);
  113.             }
  114.         }
  115.    
  116.         if ( _uri.password != "" && _uri.password !is null )
  117.         {
  118.             response = exec("PASS", _uri.password);
  119.        
  120.             if ( response.code != 230 )
  121.             {
  122.                 throw new FtpException(response);
  123.             }
  124.         }
  125.        
  126.         _connected = true;
  127.    
  128.         if (_format == Format.Binary)
  129.         {
  130.             exec("TYPE I");
  131.         }
  132.         else
  133.         {
  134.             exec("TYPE A");
  135.         }
  136.     }
  137.    
  138.     void close(ulong line = __LINE__)
  139.     {
  140.         if (_connected == false){
  141.             return;
  142.         }
  143.  
  144.         exec("QUIT");
  145.         _socket.close();
  146.        
  147.         _connected = false;
  148.     }
  149.    
  150.     FtpFile[] list(string path = "/")
  151.     {
  152.         if (_format == Format.Binary)
  153.         {
  154.             exec("TYPE I");
  155.         }
  156.         else
  157.         {
  158.             exec("TYPE A");
  159.         }
  160.        
  161.         auto info = requestDataSocket();
  162.         auto sock = createDataSocket(info);
  163.        
  164.         auto response = exec("LIST", path);
  165.        
  166.         if (response.code == 550)
  167.         {
  168.             throw new FtpException(response);
  169.         }
  170.        
  171.         auto stream = FtpStream(sock, 30);
  172.         char[10240] buffer;
  173.         auto len = stream.read(buffer);
  174.        
  175.         sock.close();
  176.         readResponse();
  177.        
  178.         auto data = buffer[0..len];
  179.         FtpFile[] files;
  180.         FtpFile file;
  181.        
  182.         foreach (line; data.splitLines())
  183.         {
  184.             file = FtpFile(line);
  185.            
  186.             if (file.valid)
  187.             {
  188.                 files ~= file;
  189.             }
  190.         }
  191.        
  192.         return files;
  193.     }
  194.    
  195.     @property Format format() const
  196.     {
  197.         return _format;
  198.     }
  199.    
  200.     @property void format(Format format_)
  201.     {
  202.         _format = format_;
  203.     }
  204.    
  205.     @property Mode mode() const
  206.     {
  207.         return _mode;
  208.     }
  209.    
  210.     void mode(Mode mode_)
  211.     {
  212.         _mode = mode_;
  213.     }
  214.    
  215.    
  216.     size_t size(string file)
  217.     {
  218.         if (_format == Format.Binary)
  219.         {
  220.             auto response = exec("SIZE", file);
  221.             if (response.code != 213)
  222.             {
  223.                 throw new FtpException(response);
  224.             }
  225.            
  226.             return to!(size_t)(chomp(response.msg));
  227.         }
  228.         else
  229.         {
  230.             auto _files = list(file);
  231.             return _files[0].size;
  232.         }
  233.     }
  234.    
  235.     void createDir(string name)
  236.     {
  237.         auto response = exec("MKD", name);
  238.        
  239.         if ( response.code != 257 )
  240.         {
  241.             throw new FtpException(response);
  242.         }
  243.     }
  244.    
  245.     void deleteDir(string name)
  246.     {
  247.         auto response = exec("RMD", name);
  248.        
  249.         if ( response.code != 250 )
  250.         {
  251.             throw new FtpException(response);
  252.         }
  253.     }
  254.    
  255.     void rename(string oldName, string newName)
  256.     {
  257.         auto response = exec("RNFR", oldName);
  258.         if ( response.code != 350 )
  259.         {
  260.             throw new FtpException(response);
  261.         }
  262.        
  263.         response = exec("RNTO", newName);
  264.         if ( response.code != 250 )
  265.         {
  266.             throw new FtpException(response);
  267.         }
  268.     }
  269.    
  270.     void deleteFile(string filename)
  271.     {
  272.         auto response = exec("DELE", filename);
  273.        
  274.         if ( response.code != 250 )
  275.         {
  276.             throw new FtpException(response);
  277.         }
  278.     }
  279.    
  280.     string currentDir()
  281.     {
  282.         auto response = exec("PWD");
  283.        
  284.         if ( response.code != 257 )
  285.         {
  286.             throw new FtpException(response);
  287.         }
  288.        
  289.         string resp = response.msg.idup;
  290.         sizediff_t startPos = resp.indexOf(`"`);
  291.         sizediff_t endPos = resp.lastIndexOf(`"`);
  292.        
  293.         if ( startPos == -1 || startPos == endPos || endPos == -1 )
  294.         {
  295.             return "";
  296.         }
  297.        
  298.         return resp[startPos + 1..endPos];
  299.     }
  300.    
  301.     void changeWorkingDir(string path)
  302.     {
  303.         auto response = exec("CWD", path);
  304.         if ( response.code != 550 )
  305.         {
  306.             throw new FtpException(response);
  307.         }
  308.     }
  309.    
  310.     void download()(string remoteFile, string localFile, bool resume = true)
  311.     {
  312.         download(remoteFile, new BufferedFile(localFile, FileMode.Out, bufferSize), resume);
  313.     }
  314.    
  315.     void download()(string remoteFile, Stream localFile, bool resume = true)
  316.     {
  317.         if (_format == Format.Binary)
  318.         {
  319.             exec("TYPE I");
  320.         }
  321.         else
  322.         {
  323.             exec("TYPE A");
  324.         }
  325.    
  326.         size_t totalSize;
  327.         if (Progress !is null)
  328.         {
  329.             totalSize = size(remoteFile);
  330.         }
  331.    
  332.         auto info = requestDataSocket();
  333.         auto sock = createDataSocket(info);
  334.        
  335.         if ( resume == true )
  336.         {
  337.             ulong size = localFile.size();
  338.             localFile.position(size);
  339.             exec("REST", size);
  340.         }
  341.         exec("RETR", remoteFile);
  342.    
  343.    
  344.         ubyte[bufferSize] buffer;
  345.         ptrdiff_t len = 0;
  346.         size_t totalLen;
  347.        
  348.         enum convtime = convert!("seconds", "hnsecs")(60);
  349.         ulong timeOut = Clock.currStdTime() + convtime;
  350.    
  351.         while (Clock.currStdTime() < timeOut)
  352.         {
  353.             len = sock.receive(buffer);
  354.             if (len < 1) break;
  355.            
  356.             localFile.write(buffer[0..len]);
  357.             totalLen += len;
  358.            
  359.             if (Progress !is null)
  360.             {
  361.                 Progress(totalLen, totalSize);
  362.             }
  363.            
  364.             timeOut = Clock.currStdTime() + convtime;
  365.         }
  366.  
  367.    
  368.         sock.close();
  369.         localFile.close();
  370.        
  371.         readResponse();
  372.     }
  373.    
  374.     T[] get(T = immutable(char))(string filename, int offset = -1)
  375.     {
  376.         T[] buffer;
  377.         size_t totalSize;
  378.         if (Progress !is null)
  379.         {
  380.             totalSize = size(filename);
  381.         }
  382.    
  383.         if (_format == Format.Binary)
  384.         {
  385.             exec("TYPE I");
  386.         }
  387.         else
  388.         {
  389.             exec("TYPE A");
  390.         }
  391.  
  392.         auto info = requestDataSocket();
  393.         auto sock = createDataSocket(info);
  394.        
  395.         if ( offset > 0 )
  396.         {
  397.             exec("REST", offset);
  398.         }
  399.         exec("RETR", filename);
  400.        
  401.         void[bufferSize] buff = void;
  402.         size_t totalLen;
  403.        
  404.         enum convtime = convert!("seconds", "hnsecs")(60);
  405.         ulong timeOut = Clock.currStdTime() + convtime;
  406.        
  407.         while (Clock.currStdTime() < timeOut)
  408.         {
  409.             auto len = sock.receive(buff);
  410.            
  411.             if (len < 1)
  412.             {
  413.                 break;
  414.             }
  415.        
  416.             buffer ~= cast(T[])buff[0..len];
  417.             totalLen += len;
  418.            
  419.             if (Progress !is null)
  420.             {
  421.                 Progress(totalLen, totalSize);
  422.             }
  423.        
  424.             if (len < bufferSize)
  425.             {
  426.             //break;
  427.             }
  428.             timeOut = Clock.currStdTime() + convtime;
  429.         }
  430.        
  431.         sock.close();
  432.         readResponse();
  433.        
  434.         return buffer;
  435.     }
  436.    
  437.     size_t get()(string remoteFile, scope void delegate(void[] data, size_t received) func, int offset = -1)
  438.     {
  439.         size_t totalSize;
  440.         if (Progress !is null)
  441.         {
  442.             totalSize = size(remoteFile);
  443.         }
  444.    
  445.         if (_format == Format.Binary)
  446.         {
  447.             exec("TYPE I");
  448.         }
  449.         else
  450.         {
  451.             exec("TYPE A");
  452.         }
  453.  
  454.         auto info = requestDataSocket();
  455.         auto sock = createDataSocket(info);
  456.        
  457.         if ( offset > 0 )
  458.         {
  459.             exec("REST", offset);
  460.         }
  461.         exec("RETR", remoteFile);
  462.        
  463.         void[bufferSize] buff = void;
  464.         size_t totalLen;
  465.        
  466.         enum convtime = convert!("seconds", "hnsecs")(60);
  467.         ulong timeOut = Clock.currStdTime() + convtime;
  468.        
  469.         while (Clock.currStdTime() < timeOut)
  470.         {
  471.             auto len = sock.receive(buff);
  472.            
  473.             if (len < 1)
  474.             {
  475.                 break;
  476.             }
  477.        
  478.             func(buff[0..len], len);
  479.             totalLen += len;
  480.            
  481.             if (Progress !is null)
  482.             {
  483.                 Progress(totalLen, totalSize);
  484.             }
  485.        
  486.             timeOut = Clock.currStdTime() + convtime;
  487.         }
  488.        
  489.         sock.close();
  490.         readResponse();
  491.        
  492.         return totalLen;
  493.     }
  494.    
  495.     size_t get(T)(string remoteFile, ref T buffer, int offset = -1)
  496.     if (isMutable!(T) && !isDelegate!(T))
  497.     {
  498.         size_t totalSize;
  499.    
  500.         if (Progress !is null)
  501.         {
  502.             totalSize = size(remoteFile);
  503.         }
  504.    
  505.         if (_format == Format.Binary)
  506.         {
  507.             exec("TYPE I");
  508.         }
  509.         else
  510.         {
  511.             exec("TYPE A");
  512.         }
  513.  
  514.         auto info = requestDataSocket();
  515.         auto sock = createDataSocket(info);
  516.        
  517.         if ( offset > 0 )
  518.         {
  519.             exec("REST", offset);
  520.         }
  521.         exec("RETR", remoteFile);
  522.        
  523.         auto stream = FtpStream(sock, 60);
  524.         if (Progress !is null)
  525.         {
  526.             stream.Progress = (size_t current) { Progress(current, totalSize); };
  527.         }
  528.        
  529.         auto len = stream.read(buffer);
  530.         sock.close();
  531.         readResponse();
  532.        
  533.         return len;
  534.     }
  535.    
  536.     private:
  537.    
  538.     void writeRequest(T...)(string cmd, T args)
  539.     {
  540.         foreach ( arg; args )
  541.         {
  542.             cmd ~= " " ~ to!(string)(arg);
  543.         }
  544.    
  545.         cmd ~= "\r\n";
  546.  
  547.         _socket.send(cmd);
  548.        
  549.         debug(Ftp)
  550.         writeln("<", cmd);
  551.     }
  552.    
  553.     Response exec(T...)(string cmd, T args)
  554.     {
  555.         writeRequest(cmd, args);
  556.        
  557.         return readResponse();
  558.     }
  559.    
  560.     Response readResponse()
  561.     {
  562.         char[4096] resp;
  563.         size_t len = _stream.readResponse(resp);
  564.        
  565.         Response response;
  566.        
  567.         if ( len < 5 ) {
  568.             response.code = 500;
  569.             response.msg = "Syntax error, command unrecognized. "
  570.             "This may include errors such as command line too long";
  571.         }
  572.         else
  573.         {
  574.             try
  575.             {
  576.                 response.code = to!(ushort)(resp[0..3]);
  577.             }
  578.             catch ( Exception e )
  579.             {
  580.                 response.code = 500;
  581.             }
  582.        
  583.             response.msg = resp[4..len].idup;
  584.         }
  585.        
  586.         writeln(">", response.msg);
  587.        
  588.         return response;
  589.     }
  590.    
  591.     dataSocketInfo requestDataSocket()
  592.     {
  593.         dataSocketInfo tuple;
  594.        
  595.         if ( _mode == Mode.Passive )
  596.         {
  597.             auto response = exec("PASV");
  598.             if ( response.code != 227 )
  599.             {
  600.                 throw new FtpException(response);
  601.             }
  602.        
  603.             sizediff_t begin = response.msg.indexOf("(");
  604.             sizediff_t end   = response.msg.indexOf(")");
  605.            
  606.             string ipData = response.msg[begin+1 .. end];
  607.             string[6] parts = ipData.split(",");
  608.            
  609.             string ip   = parts[0] ~ "." ~ parts[1] ~ "." ~ parts[2] ~ "." ~ parts[3];
  610.             int port1 = to!(int)(parts[4]) << 8;
  611.             int port2 = to!(int)(parts[5]);
  612.             ushort port = to!ushort(port1 + port2);
  613.            
  614.             debug(Ftp)
  615.             {
  616.                 writeln("origin: ", ip, " : ", parts[4], " - ", parts[5]);
  617.                 writeln("dataSocket adress: ", ip, " : ", port);
  618.             }
  619.            
  620.             tuple.ip = ip;
  621.             tuple.port = port;
  622.         }
  623.         else
  624.         {
  625.             tuple.ip = "localhost";
  626.             tuple.port = 21;
  627.             exec("PORT", tuple.ip, tuple.port);
  628.         }
  629.        
  630.         return tuple;
  631.     }
  632.        
  633.     Socket createDataSocket(dataSocketInfo info)
  634.     {
  635.         Socket dataSock = new TcpSocket();
  636.         dataSock.connect(new InternetAddress(info.ip, info.port));
  637.         //dataSock.blocking(false);
  638.  
  639.         return dataSock;
  640.     }
  641.    
  642. }
  643.  
  644. struct FtpFile
  645. {
  646.     enum Type
  647.     {
  648.         File,
  649.         Directory,
  650.         Link,
  651.     }
  652.  
  653.     struct Time
  654.     {
  655.         ushort day;
  656.         ushort month;
  657.         string monthString;
  658.         uint year;
  659.         ushort hour;
  660.         ushort min;
  661.     }
  662.  
  663.     private
  664.     {
  665.         char[]      _stream;
  666.         bool        _valid = false;
  667.        
  668.         Type        _type;
  669.         string      _chmodString;
  670.         ubyte[3]    _chmod;
  671.         uint        _childs;
  672.         string      _user;
  673.         string      _group;
  674.         Time        _date;
  675.         ulong       _size;
  676.         string      _filename;
  677.     }
  678.  
  679.     this(char[] stream)
  680.     {
  681.         _stream = stream;
  682.         parse();
  683.     }
  684.  
  685.     @property Type type() const
  686.     {
  687.         return _type;
  688.     }
  689.  
  690.     @property string chmodString()
  691.     {
  692.         return _chmodString;
  693.     }
  694.  
  695.     @property ubyte[3] chmod() const
  696.     {
  697.         return _chmod;
  698.     }
  699.  
  700.     @property uint child() const
  701.     {
  702.         return _childs;
  703.     }
  704.    
  705.     @property string user()
  706.     {
  707.         return _user;
  708.     }
  709.    
  710.     @property string group()
  711.     {
  712.         return _group;
  713.     }
  714.    
  715.     @property Time time() const
  716.     {
  717.         return _date;
  718.     }
  719.    
  720.     @property ulong size() const
  721.     {
  722.         return _size;
  723.     }
  724.    
  725.     @property string name()
  726.     {
  727.         return _filename;
  728.     }
  729.    
  730.     @property bool valid() const
  731.     {
  732.         return _valid;
  733.     }
  734.  
  735.     void parse()
  736.     {
  737.         switch (_stream[0])
  738.         {
  739.             case 'b':
  740.             case 'c':
  741.             case 'd':
  742.             case 'l':
  743.             case 'p':
  744.             case 's':
  745.             case '-':
  746.                 parseDefault();
  747.                 return;
  748.             break;
  749.             case '+':
  750.                 parseEplf();
  751.                 return;
  752.             break;
  753.             default:
  754.                 parseDos();
  755.             break;
  756.         }
  757.     }
  758.  
  759.     void parseDefault()
  760.     {
  761.         if (_stream.length < 3) return;
  762.        
  763.         switch (_stream[0])
  764.         {
  765.             case 'd':
  766.                 _type = Type.Directory;
  767.             break;
  768.             case '-':
  769.                 _type = Type.File;
  770.             break;
  771.             case 'l':
  772.                 _type = Type.Link;
  773.             break;
  774.             default:
  775.                 _type = Type.Directory;
  776.             break;
  777.         }
  778.  
  779.         char[][] split = _stream[1..$].split(" ");
  780.         char[][] truncate = new char[][split.length];
  781.        
  782.         size_t iter;
  783.         foreach (elem; split)
  784.         {
  785.             if (elem != "")
  786.             {
  787.             truncate[iter++] = elem;
  788.             }
  789.         }
  790.         truncate = truncate[0..iter];
  791.        
  792.         if (truncate.length < 4)
  793.         {
  794.             _valid = false;
  795.             return;
  796.         }
  797.  
  798.         _chmodString = to!(string)(truncate[0]);
  799.         _chmod = parseChmod(truncate[0]);
  800.         _childs = to!(uint)(truncate[1]);
  801.         _user = to!(string)(truncate[2]);
  802.         _group = to!(string)(truncate[3]);
  803.         _size = to!(ulong)(truncate[4]);
  804.         _date = parseDate(truncate);
  805.         _filename = parseFilename(truncate);
  806.        
  807.         if (_filename != "") {
  808.             _valid = true;
  809.         }
  810.     }
  811.  
  812.     Time parseDate(const(char)[][] input)
  813.     {
  814.         Time time;
  815.        
  816.         if (_type == Type.Link)
  817.         {
  818.             input = input[5..$-3];
  819.         }
  820.         else
  821.         {
  822.             input = input[5..$-1];
  823.         }
  824.    
  825.         foreach (elem; input)
  826.         {
  827.             if (!std.string.isNumeric(elem)) // either month or hour
  828.             {
  829.                 if (elem[0] > '9') // month
  830.                 {
  831.                     time.monthString = to!(string)(elem);
  832.                    
  833.                     final switch(toLower(elem))
  834.                     {
  835.                         case "jan":
  836.                             time.month = 1;
  837.                         break;
  838.                         case "feb":
  839.                             time.month = 2;
  840.                         break;
  841.                         case "mar":
  842.                             time.month = 3;
  843.                         break;
  844.                         case "apr":
  845.                             time.month = 4;
  846.                         break;
  847.                         case "may":
  848.                             time.month = 5;
  849.                         break;
  850.                         case "jun":
  851.                             time.month = 6;
  852.                         break;
  853.                         case "jul":
  854.                             time.month = 7;
  855.                         break;
  856.                         case "aug":
  857.                             time.month = 8;
  858.                         break;
  859.                         case "sep":
  860.                             time.month = 9;
  861.                         break;
  862.                         case "oct":
  863.                             time.month = 10;
  864.                         break;
  865.                         case "nov":
  866.                             time.month = 11;
  867.                         break;
  868.                         case "dec":
  869.                             time.month = 12;
  870.                         break;
  871.                    
  872.                     }
  873.                 }
  874.                 else // hour:minute
  875.                 {
  876.                     if (elem[2] == ':')
  877.                     {
  878.                         time.hour = to!(ushort)(elem[0..1]);
  879.                         time.min  = to!(ushort)(elem[3..4]);
  880.                     }
  881.                 }
  882.             }
  883.             else
  884.             {
  885.                 if (elem.length == 4) // year
  886.                 {
  887.                     time.year = to!(uint)(elem);
  888.                 }
  889.                 else if (elem.length == 2) // day
  890.                 {
  891.                     time.day = to!(ushort)(elem);
  892.                 }
  893.             }
  894.         }
  895.    
  896.         if (time.year == 0)
  897.         {
  898.         time.year = Clock.currTime().year();
  899.         }
  900.    
  901.         return time;
  902.     }
  903.    
  904.     string parseFilename(const(char)[][] input)
  905.     {
  906.         if (_type == Type.Link)
  907.         {
  908.             // check last 3 entries
  909.             if (input[$-2] == "->")
  910.             {
  911.                 return to!(string)(input[$-3]);
  912.             }
  913.         }
  914.    
  915.         return to!(string)(input[$-1]);
  916.     }
  917.  
  918.     void parseEplf(){}
  919.     void parseDos(){}
  920.    
  921.     ubyte[3] parseChmod(const(char)[] chmod)
  922.     {
  923.         ubyte[3] octalChmod = 0;
  924.        
  925.         auto owner = chmod[0..3];
  926.         if (owner[0] == 'r')
  927.         {
  928.             octalChmod[0] += 4;
  929.         }
  930.         else if (owner[1] == 'w')
  931.         {
  932.             octalChmod[0] += 2;
  933.         }
  934.         else if (owner[2] == 'x')
  935.         {
  936.             octalChmod[0] += 1;
  937.         }
  938.  
  939.         auto group = chmod[3..6];
  940.         if (group[0] == 'r')
  941.         {
  942.             octalChmod[1] += 4;        
  943.         }
  944.         else if(group[1] == 'w')
  945.         {
  946.             octalChmod[1] += 2;
  947.         }
  948.         else if(group[2] == 'x')
  949.         {
  950.             octalChmod[1] += 1;
  951.         }
  952.      
  953.         auto other = chmod[6..9];
  954.         if (other[0] == 'r')
  955.         {
  956.             octalChmod[2] += 4;
  957.         }
  958.         else if(other[1] == 'w')
  959.         {
  960.             octalChmod[2] += 2;
  961.         }
  962.         else if(other[2] == 'x')
  963.         {
  964.             octalChmod[2] += 1;
  965.         }
  966.      
  967.         writeln("owner: ", owner, "group: ", group, "other: ", other);
  968.        
  969.         return octalChmod;
  970.     }
  971. }
  972.  
  973. struct FtpStream
  974. {
  975.     Socket _socket;
  976.     const ulong _timeOut;
  977.     void delegate(size_t current) Progress;
  978.    
  979.     this (Socket socket, int timeOut)
  980.     {
  981.         _socket = socket;
  982.         _timeOut = convert!("seconds", "hnsecs")(timeOut);
  983.     }
  984.    
  985.     size_t readResponse(T)(ref T resp)
  986.     if (isMutable!(T) &&
  987.         (is(Unqual!(typeof(T[0])) : char) ||
  988.         is(Unqual!(typeof(T[0])) : ubyte)))
  989.     {
  990.         typeof(T[0])[4096] buff;
  991.         size_t totalLen;
  992.         ulong timeOut = Clock.currStdTime() + _timeOut;
  993.        
  994.         ptrdiff_t len;
  995.        
  996.         if (buff.length > resp.length && resp.length > 0)
  997.         buff = buff[0..resp.length];
  998.        
  999.         while (Clock.currStdTime() < timeOut && totalLen < resp.length)
  1000.         {
  1001.             len = _socket.receive(buff);
  1002.            
  1003.             resp[totalLen..totalLen+len] = buff[0..len];
  1004.             totalLen += len;
  1005.            
  1006.             if (Progress !is null)
  1007.             {
  1008.                 Progress(totalLen);
  1009.             }
  1010.            
  1011.             if (len < buff.length)
  1012.             {
  1013.                 break;
  1014.             }
  1015.            
  1016.             timeOut = Clock.currStdTime() + _timeOut;
  1017.         }
  1018.        
  1019.         return totalLen;
  1020.     }
  1021.    
  1022.     size_t read(T)(ref T resp)
  1023.     if ( isMutable!(T) &&
  1024.         (is(Unqual!(typeof(T[0])) : char) ||
  1025.         is(Unqual!(typeof(T[0])) : ubyte)) )
  1026.     {
  1027.         typeof(T[0])[4096] buff;
  1028.         size_t totalLen;
  1029.         ulong timeOut = Clock.currStdTime() + _timeOut;
  1030.        
  1031.         ptrdiff_t len;
  1032.        
  1033.         while (Clock.currStdTime() < timeOut && totalLen < resp.length)
  1034.         {
  1035.             len = _socket.receive(buff);
  1036.    
  1037.             if (len < 1)
  1038.                 break;
  1039.    
  1040.             if (totalLen + len > resp.length)
  1041.             {
  1042.                 auto fill = resp.length - totalLen;
  1043.                 resp[totalLen..totalLen+fill] = buff[0..fill];
  1044.                 return resp.length;
  1045.             }
  1046.    
  1047.             resp[totalLen..totalLen+len] = buff[0..len];
  1048.             totalLen += len;
  1049.    
  1050.             if (Progress !is null)
  1051.             {
  1052.                 Progress(totalLen);
  1053.             }
  1054.    
  1055.             timeOut = Clock.currStdTime() + _timeOut;
  1056.         }
  1057.        
  1058.         return totalLen;
  1059.     }
  1060. }
  1061.  
  1062. class FtpException : Exception
  1063. {
  1064.     ushort code;
  1065.     string msg;
  1066.    
  1067.     this(Tuple!(ushort, "code", string, "msg") response,
  1068.     string file = __FILE__, size_t line = __LINE__)
  1069.     {
  1070.         code = response.code;
  1071.         msg = response.msg;
  1072.        
  1073.         super("\n"~to!(string)(response.code) ~ ": " ~ response.msg ~
  1074.              "\n" ~ file ~ "(" ~ to!(string)(line) ~ ")\t" ~ "\t"
  1075.          );
  1076.     }
  1077. }
  1078.  
  1079. debug(Ftp)
  1080. {
  1081.     import std.string, std.datetime;
  1082.    
  1083.     void main()
  1084.     {
  1085.         //auto ftp = new FtpClient(new Uri("ftp://ftp.digitalmars.com"));
  1086.         auto ftp = new FtpClient("***", 21);
  1087.         ftp.auth("***", "***");
  1088.        
  1089.         ftp.open();
  1090.         auto files = ftp.list();
  1091.        
  1092.         foreach(file; files) {
  1093.             writeln(file.name, ":\t", file.chmod);
  1094.         }
  1095.         char[] content = ftp.get!(char)("index.php");
  1096.         writeln("1st one:", content);
  1097.        
  1098.         char[4096 * 4] content2;
  1099.         ftp.get("index.php", content2);
  1100.         writeln("2nd one:", content2);
  1101.        
  1102.         writeln("3rd one:");
  1103.         ftp.get("index.php", (void[] data, size_t len){ writeln(len, ">", cast(char[]) data); });
  1104.         ftp.close();
  1105.         readln();
  1106.     }
  1107. }
Add Comment
Please, Sign In to add comment