set json api functions parameter names
[tine20] / tine20 / Tinebase / TempFile.php
1 <?php
2 /**
3  * Tine 2.0
4  *
5  * @package     Tinebase
6  * @subpackage  File
7  * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
8  * @author      Cornelius Weiss <c.weiss@metaways.de>
9  * @copyright   Copyright (c) 2007-2011 Metaways Infosystems GmbH (http://www.metaways.de)
10  */
11
12 /**
13  * class of persistant temp files
14  * 
15  * This class handles generation of tempfiles and registers them in a tempFile table.
16  * To access a tempFile, the session of the client must match
17  * 
18  * @package     Tinebase
19  * @subpackage  File
20  */
21 class Tinebase_TempFile extends Tinebase_Backend_Sql_Abstract implements Tinebase_Controller_Interface
22 {
23     /**
24      * Table name without prefix
25      *
26      * @var string
27      */
28     protected $_tableName = 'temp_files';
29     
30     /**
31      * Model name
32      *
33      * @var string
34      */
35     protected $_modelName = 'Tinebase_Model_TempFile';
36     
37     /**
38      * holds the instance of the singleton
39      *
40      * @var Tinebase_TempFile
41      */
42     private static $_instance = NULL;
43     
44     /**
45      * don't clone. Use the singleton.
46      *
47      */
48     private function __clone() {}
49     
50     /**
51      * the singleton pattern
52      *
53      * @return Tinebase_TempFile
54      */
55     public static function getInstance() 
56     {
57         if (self::$_instance === NULL) {
58             self::$_instance = new Tinebase_TempFile();
59         }
60         
61         return self::$_instance;
62     }
63     
64     /**
65      * get temp file description from db
66      *
67      * @param mixed $_fileId
68      * @return Tinebase_Model_TempFile
69      */
70     public function getTempFile($_fileId)
71     {
72         $fileId = is_array($_fileId) ? $_fileId['id'] : $_fileId;
73         
74         if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__
75             . " Fetching temp file with id " . print_r($fileId, true));
76         
77         $select = $this->_getSelect('*');
78         $select->where($this->_db->quoteIdentifier('id') . ' = ?', $fileId)
79                ->where($this->_db->quoteIdentifier('session_id') . ' = ?', Tinebase_Core::getSessionId(/* $generateUid */ false));
80
81         $stmt = $this->_db->query($select);
82         $queryResult = $stmt->fetch();
83         $stmt->closeCursor();
84         
85         if (!$queryResult) {
86             if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__
87                 . " Could not fetch row with id $fileId from temp_files table.");
88             return NULL;
89         }
90
91         $result = new Tinebase_Model_TempFile($queryResult);
92         return $result;
93     }
94     
95     /**
96      * uploads a file and saves it in the db
97      *
98      * @todo separate out frontend code
99      * @todo work on a file model
100      *  
101      * @return Tinebase_Model_TempFile
102      * @throws Tinebase_Exception
103      * @throws Tinebase_Exception_NotFound
104      * @throws Tinebase_Exception_UnexpectedValue
105      */
106     public function uploadTempFile()
107     {
108         $path = self::getTempPath();
109         
110         if (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest') {
111             if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->DEBUG(__METHOD__ . '::' . __LINE__ . " XMLHttpRequest style upload to path " . $path);
112             
113             $name =       base64_decode($_SERVER['HTTP_X_FILE_NAME']);
114             $size =       (double) $_SERVER['HTTP_X_FILE_SIZE'];
115             $type =       $_SERVER['HTTP_X_FILE_TYPE'];
116             $error =      0;
117             
118             if ($name === false) {
119                 throw new Tinebase_Exception('Can\'t decode base64 string, no base64 provided?');
120             }
121             
122             $success = copy("php://input", $path);
123             if (! $success) {
124                 // try again with stream_copy_to_stream
125                 $input = fopen("php://input", 'r');
126                 if (! $input) {
127                     throw new Tinebase_Exception_NotFound('No valid upload file found or some other error occurred while uploading! ');
128                 }
129                 $tempfileHandle = fopen($path, "w");
130                 if (! $tempfileHandle) {
131                     throw new Tinebase_Exception('Could not open tempfile while uploading! ');
132                 }
133                 $size = (double) stream_copy_to_stream($input, $tempfileHandle);
134                 fclose($input);
135                 fclose($tempfileHandle);
136             }
137             
138             if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->DEBUG(__METHOD__ . '::' . __LINE__ . " successfully created tempfile at {$path}");
139         } else {
140             if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->DEBUG(__METHOD__ . '::' . __LINE__ . " Plain old form style upload");
141             
142             $uploadedFile = $_FILES['file'];
143             
144             $name  = $uploadedFile['name'];
145             $size  = (double) $uploadedFile['size'];
146             $type  = $uploadedFile['type'];
147             $error = $uploadedFile['error'];
148             
149             if ($uploadedFile['error'] == UPLOAD_ERR_FORM_SIZE) {
150                 throw new Tinebase_Exception('The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form.');
151             }
152             if (! move_uploaded_file($uploadedFile['tmp_name'], $path)) {
153                 throw new Tinebase_Exception_NotFound('No valid upload file found or some other error occurred while uploading! ' . print_r($uploadedFile, true));
154             }
155         }
156         
157         return $this->createTempFile($path, $name, $type, $size, $error);
158     }
159     
160     /**
161      * creates temp filename
162      * 
163      * @throws Tinebase_Exception_UnexpectedValue
164      * @return string
165      */
166     public static function getTempPath()
167     {
168         $path = tempnam(Tinebase_Core::getTempDir(), 'tine_tempfile_');
169         if (! $path) {
170             throw new Tinebase_Exception_UnexpectedValue('Can not upload file, tempnam() could not return a valid filename!');
171         }
172         return $path;
173     }
174     
175     /**
176      * create new temp file
177      * 
178      * @param string $_path
179      * @param string $_name
180      * @param string $_type
181      * @param double $_size
182      * @param integer $_error
183      * @return Tinebase_Model_TempFile
184      */
185     public function createTempFile($_path, $_name = 'tempfile.tmp', $_type = 'unknown', $_size = 0, $_error = 0)
186     {
187         // sanitize filename (convert to utf8)
188         $filename = Tinebase_Helper::mbConvertTo($_name);
189         
190         $id = Tinebase_Model_TempFile::generateUID();
191         $tempFile = new Tinebase_Model_TempFile(array(
192            'id'          => $id,
193            'session_id'  => Tinebase_Core::getSessionId(/* $generateUid */ false),
194            'time'        => Tinebase_DateTime::now()->get(Tinebase_Record_Abstract::ISO8601LONG),
195            'path'        => $_path,
196            'name'        => $filename,
197            'type'        => ! empty($_type)  ? $_type  : 'unknown',
198            'error'       => ! empty($_error) ? $_error : 0,
199            'size'        => ! empty($_size)  ? (double) $_size  : (double) filesize($_path),
200         ));
201         
202         if (Tinebase_Core::isLogLevel(Zend_Log::TRACE)) Tinebase_Core::getLogger()->trace(__METHOD__ . '::' . __LINE__
203             . " tempfile data: " . print_r($tempFile->toArray(), TRUE));
204         
205         $this->create($tempFile);
206         
207         return $tempFile;
208     }
209     
210     /**
211      * joins all given tempfiles in given order to a single new tempFile
212      * 
213      * @param Tinebase_Record_RecordSet $_tempFiles
214      * @return Tinebase_Model_TempFile
215      */
216     public function joinTempFiles($_tempFiles)
217     {
218         $path = tempnam(Tinebase_Core::getTempDir(), 'tine_tempfile_');
219         $name = preg_replace('/\.\d+\.chunk$/', '', $_tempFiles->getFirstRecord()->name);
220         $type = $_tempFiles->getFirstRecord()->type;
221         $size = 0.0;
222         
223         $fJoin = fopen($path, 'w+b');
224         foreach ($_tempFiles as $tempFile) {
225             $fChunk = @fopen($tempFile->path, "rb");
226             if (! $fChunk) {
227                 throw new Tinebase_Exception_UnexpectedValue("Can not open chunk {$tempFile->id}");
228             }
229             
230             // NOTE: stream_copy_to_stream is about 15% slower
231             while (!feof($fChunk)) {
232                 $bytesWritten = fwrite($fJoin, fread($fChunk, 2097152 /* 2 MB */));
233                 $size += (double) $bytesWritten;
234             }
235             fclose($fChunk);
236         }
237         
238         fclose($fJoin);
239         
240         return $this->createTempFile($path, $name, $type, $size);
241     }
242     
243     /**
244      * remove all temp file records before $_date
245      * 
246      * @param Tinebase_DateTime|string $_date
247      * @return integer number of deleted records
248      */
249     public function clearTableAndTempdir($_date = NULL)
250     {
251         $date = ($_date === NULL) ? Tinebase_DateTime::now()->subDay(1) : $_date;
252         if (! $date instanceof Tinebase_DateTime) {
253             $date = new Tinebase_DateTime($date);
254         }
255         
256         if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__
257             . ' Removing all temp files prior ' . $date->toString());
258         
259         $tempfiles = $this->search(new Tinebase_Model_TempFileFilter(array(array(
260             'field'     => 'time',
261             'operator'  => 'before',
262             'value'     => $date
263         ))));
264         
265         foreach ($tempfiles as $file) {
266             if (file_exists($file->path)) {
267                 unlink($file->path);
268             } else {
269                 if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__
270                     . ' File no longer found: ' . $file->path);
271             }
272         }
273
274         $result = $this->delete($tempfiles->getArrayOfIds());
275         if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__
276             . ' Removed ' . $result . ' files.');
277         return $result;
278     }
279     
280     /**
281      * open a temp file
282      * 
283      * @throws Tinebase_Exception
284      * @return resource
285      */
286     public function openTempFile()
287     {
288         $path = self::getTempPath();
289         
290         if (Tinebase_Core::isLogLevel(Zend_Log::INFO)) Tinebase_Core::getLogger()->info(__METHOD__ . '::' . __LINE__
291             . ' Opening temp file ' . $path);
292         
293         $handle = fopen($path, 'w+');
294         if (! $handle) {
295             throw new Tinebase_Exception('Could not create temp file in ' . dirname($path));
296         }
297         
298         $this->createTempFile($path);
299         
300         return $handle;
301     }
302 }