Example #1
0
    def synchronize_times(self):
        """
        Synchronize the local times of FTP client and server. This
        is necessary to let `upload_if_newer` and `download_if_newer`
        work correctly. If `synchronize_times` isn't applicable
        (see below), the time shift can still be set explicitly with
        `set_time_shift`.

        This implementation of `synchronize_times` requires _all_ of
        the following:

        - The connection between server and client is established.
        - The client has write access to the directory that is
          current when `synchronize_times` is called.

        The common usage pattern of `synchronize_times` is to call it
        directly after the connection is established. (As can be
        concluded from the points above, this requires write access
        to the login directory.)

        If `synchronize_times` fails, it raises a `TimeShiftError`.
        """
        helper_file_name = "_ftputil_sync_"
        # Open a dummy file for writing in the current directory
        #  on the FTP host, then close it.
        try:
            # May raise `FTPIOError` if directory isn't writable
            file_ = self.file(helper_file_name, 'w')
            file_.close()
        except ftp_error.FTPIOError:
            raise ftp_error.TimeShiftError(
                  '''couldn't write helper file in directory "%s"''' %
                  self.getcwd())
        # If everything worked up to here it should be possible to stat
        #  and then remove the just-written file.
        try:
            server_time = self.path.getmtime(helper_file_name)
            self.unlink(helper_file_name)
        except ftp_error.FTPOSError:
            # If we got a `TimeShiftError` exception above, we should't
            #  come here: if we did not get a `TimeShiftError` above,
            #  deletion should be possible. The only reason for an exception
            #  I can think of here is a race condition by removing write
            #  permission from the directory or helper file after it has been
            #  written to.
            raise ftp_error.TimeShiftError(
                  "could write helper file but not unlink it")
        # Calculate the difference between server and client.
        time_shift = server_time - time.time()
        # Do some sanity checks.
        self.__assert_valid_time_shift(time_shift)
        # If tests passed, store the time difference as time shift value.
        self.set_time_shift(self.__rounded_time_shift(time_shift))
Example #2
0
 def __assert_valid_time_shift(self, time_shift):
     """
     Perform sanity checks on the time shift value (given in
     seconds). If the value is invalid, raise a `TimeShiftError`,
     else simply return `None`.
     """
     minute = 60.0
     hour = 60.0 * minute
     absolute_rounded_time_shift = abs(self.__rounded_time_shift(time_shift))
     # test 1: fail if the absolute time shift is greater than
     #  a full day (24 hours)
     if absolute_rounded_time_shift > 24 * hour:
         raise ftp_error.TimeShiftError(
               "time shift (%.2f s) > 1 day" % time_shift)
     # test 2: fail if the deviation between given time shift and
     #  full hours is greater than a certain limit (e. g. five minutes)
     maximum_deviation = 5 * minute
     if abs(time_shift - self.__rounded_time_shift(time_shift)) > \
        maximum_deviation:
         raise ftp_error.TimeShiftError(
               "time shift (%.2f s) deviates more than %d s from full hours"
               % (time_shift, maximum_deviation))
Example #3
0
    def synchronize_times(self, prepend=''):
        """
        Synchronize the local times of FTP client and server. This
        is necessary to let `upload_if_newer` and `download_if_newer`
        work correctly. If `synchronize_times` isn't applicable
        (see below), the time shift can still be set explicitly with
        `set_time_shift`.

        This implementation of `synchronize_times` requires _all_ of
        the following:

        - The connection between server and client is established.
        - The client has write access to the directory that is
          current when `synchronize_times` is called.

        The common usage pattern of `synchronize_times` is to call it
        directly after the connection is established. (As can be
        concluded from the points above, this requires write access
        to the login directory.)

        If `synchronize_times` fails, it raises a `TimeShiftError`.
        """
        helper_file_name = "%s_ftputil_sync_" % prepend
        # Open a dummy file for writing in the current directory
        #  on the FTP host, then close it.
        try:
            # May raise `FTPIOError` if directory isn't writable.
            file_ = self.file(helper_file_name, 'w')
            file_.close()
        except ftp_error.FTPIOError:
            raise ftp_error.TimeShiftError(
                '''couldn't write helper file %s in directory "%s"''' %
                (helper_file_name, self.getcwd()))
        # If everything worked up to here it should be possible to stat
        #  and then remove the just-written file.
        try:
            server_time = self.path.getmtime(helper_file_name)
            self.unlink(helper_file_name)
        except ftp_error.FTPOSError:
            # If we got a `TimeShiftError` exception above, we should't
            #  come here: if we did not get a `TimeShiftError` above,
            #  deletion should be possible. The only reason for an exception
            #  I can think of here is a race condition by removing write
            #  permission from the directory or helper file after it has been
            #  written to.
            raise ftp_error.TimeShiftError(
                "could write helper file but not unlink it")
        # Calculate the difference between server and client.
        now = time.time()
        time_shift = server_time - now
        # As the time shift for this host instance isn't set yet, the
        #  directory parser will calculate times one year in the past if
        #  the time zone of the server is east from ours. Thus the time
        #  shift will be off by a year as well (see ticket #55).
        if time_shift < -360 * 24 * 60 * 60:
            # Re-add one year and re-calculate the time shift. We don't
            #  know how many days made up that year (it might have been
            #  a leap year), so go the route via `time.localtime` and
            #  `time.mktime`.
            server_time_struct = time.localtime(server_time)
            server_time_struct = (server_time_struct.tm_year+1,) + \
                                 server_time_struct[1:]
            server_time = time.mktime(server_time_struct)
            time_shift = server_time - now
        # Do some sanity checks.
        self.__assert_valid_time_shift(time_shift)
        # If tests passed, store the time difference as time shift value.
        self.set_time_shift(self.__rounded_time_shift(time_shift))