/**
  * Constructor
  *
  * @param   peer.ftp.FtpFile file
  */
 public function __construct(FtpFile $file)
 {
     with($conn = $file->getConnection(), $cmd = $this->getCommand());
     // Always use binary mode
     // Check for "200 Type set to X"
     $conn->expect($conn->sendCommand('TYPE I'), array(200));
     // Always use passive mode, just to be sure
     // Check for "227 Entering Passive Mode (h1,h2,h3,h4,p1,p2)."
     $this->socket = $conn->transferSocket();
     // Begin transfer depending on the direction returned by getCommand()
     // Check for "150 Opening XXX mode data connection for ..."
     $conn->expect($conn->sendCommand($cmd . ' ' . $file->getName()), array(150));
     $this->file = $file;
 }
 /**
  * Parse raw listing entry.
  *
  * @param   string raw a single line
  * @param   peer.ftp.FtpConnection connection
  * @param   string base default "/"
  * @param   util.Date ref default NULL
  * @return  peer.ftp.FtpEntry
  */
 public function entryFrom($raw, FtpConnection $conn = NULL, $base = '/', Date $ref = NULL)
 {
     preg_match('/([0-9]{2})-([0-9]{2})-([0-9]{2}) +([0-9]{2}):([0-9]{2})(AM|PM) +(<DIR>)?([0-9]+)? +(.+)/', $raw, $result);
     if ($result[7]) {
         $e = new FtpDir($base . $result[9], $conn);
     } else {
         $e = new FtpFile($base . $result[9], $conn);
     }
     $e->setPermissions(0);
     $e->setNumlinks(0);
     $e->setUser(NULL);
     $e->setGroup(NULL);
     $e->setSize(intval($result[8]));
     $e->setDate(new Date(sprintf('%02d/%02d/%02d %02d:%02d%02s', $result[1], $result[2], $result[3], $result[4], $result[5], $result[6])));
     return $e;
 }
 /**
  * Parse raw listing entry.
  *
  * @param   string raw a single line
  * @param   peer.ftp.FtpConnection connection
  * @param   string base default "/"
  * @param   util.Date ref default NULL
  * @return  peer.ftp.FtpEntry
  */
 public function entryFrom($raw, FtpConnection $conn = NULL, $base = '/', Date $ref = NULL)
 {
     sscanf($raw, '%s %d %s %s %d %s %d %[^ ] %[^$]', $permissions, $numlinks, $user, $group, $size, $month, $day, $date, $filename);
     // Only qualify filenames if they appear unqualified in the listing
     if ('/' !== $filename[0]) {
         $filename = $base . $filename;
     }
     // Create a directory or an entry
     if ('d' === $permissions[0]) {
         $e = new FtpDir($filename, $conn);
     } else {
         $e = new FtpFile($filename, $conn);
     }
     // If the entry contains a timestamp, the year is omitted, "Apr 4 20:16"
     // instead of "Apr 4 2009". This compact format is used when the file
     // time is within six months* from the current date, in either direction!
     //
     // *] #define SIXMONTHS       ((365 / 2) * 86400) := 15724800
     //    See http://svn.freebsd.org/base/projects/releng_7_xen/bin/ls/print.c
     if (strstr($date, ':')) {
         $ref || ($ref = Date::now());
         $d = new Date($month . ' ' . $day . ' ' . $ref->getYear() . ' ' . $date);
         if ($d->getTime() - $ref->getTime() > 15724800) {
             $d = DateUtil::addMonths($d, -12);
         }
     } else {
         $d = new Date($month . ' ' . $day . ' ' . $date);
     }
     try {
         $e->setPermissions(substr($permissions, 1));
         $e->setNumlinks($numlinks);
         $e->setUser($user);
         $e->setGroup($group);
         $e->setSize($size);
         $e->setDate($d);
     } catch (IllegalArgumentException $e) {
         throw new FormatException('Cannot parse "' . $raw . '": ' . $e->getMessage());
     }
     return $e;
 }