+class MirrorError(Exception):
+ """Exception raised when there's a problem with the mirror."""
+
+class ProxyFileStream(stream.SimpleStream):
+ """Saves a stream to a file while providing a new stream."""
+
+ def __init__(self, stream, outFile, modtime = None):
+ """Initializes the proxy.
+
+ @type stream: C{twisted.web2.stream.IByteStream}
+ @param stream: the input stream to read from
+ @type outFile: C{twisted.python.filepath.FilePath}
+ @param outFile: the file to write to
+ @type modtime: C{int}
+ @param modtime: the modification time to set for the file
+ """
+ self.stream = stream
+ self.outFile = outFile
+ self.openFile = outFile.open('w')
+ self.modtime = modtime
+ self.length = self.stream.length
+ self.start = 0
+
+ def _done(self):
+ """Close the output file."""
+ if not self.openFile.closed:
+ self.openFile.close()
+ if self.modtime:
+ os.utime(self.outFile.path, (self.modtime, self.modtime))
+
+ def read(self):
+ """Read some data from the stream."""
+ if self.openFile.closed:
+ return None
+
+ data = self.stream.read()
+ if isinstance(data, defer.Deferred):
+ data.addCallbacks(self._write, self._done)
+ return data
+
+ self._write(data)
+ return data
+
+ def _write(self, data):
+ """Write the stream data to the file and return it for others to use."""
+ if data is None:
+ self._done()
+ return data
+
+ self.openFile.write(data)
+ return data
+
+ def close(self):
+ """Clean everything up and return None to future reads."""
+ self.length = 0
+ self._done()
+ self.stream.close()
+