Index: trunk/extensions/WindowsAzureStorage/includes/filerepo/backend/WindowsAzureFileBackend.php |
— | — | @@ -54,18 +54,12 @@ |
55 | 55 | * @see FileBackend::resolveContainerPath() |
56 | 56 | */ |
57 | 57 | protected function resolveContainerPath( $container, $relStoragePath ) { |
58 | | - //Azure container naming conventions; http://msdn.microsoft.com/en-us/library/dd135715.aspx |
| 58 | + //Azure blob naming conventions; http://msdn.microsoft.com/en-us/library/dd135715.aspx |
59 | 59 | |
60 | 60 | if ( strlen( urlencode( $relStoragePath ) ) > 1024 ) { |
61 | 61 | return null; |
62 | 62 | } |
63 | | - |
64 | | - // Get absolute path given the container base dir |
65 | | - if ( isset( $this->containerPaths[$container] ) ) { |
66 | | - return $this->containerPaths[$container] . "/{$relStoragePath}"; |
67 | | - } |
68 | | - // TODO: Should storagepath not be urlencoded? |
69 | | - // TODO: return null |
| 63 | + |
70 | 64 | return $relStoragePath; |
71 | 65 | } |
72 | 66 | |
— | — | @@ -75,11 +69,22 @@ |
76 | 70 | function doStoreInternal( array $params ) { |
77 | 71 | $status = Status::newGood(); |
78 | 72 | list( $dstCont, $dstRel ) = $this->resolveStoragePath( $params['dst'] ); |
| 73 | + if ( $dstRel === null ) { |
| 74 | + $status->fatal( 'backend-fail-invalidpath', $params['dst'] ); |
| 75 | + return $status; |
| 76 | + } |
| 77 | + |
| 78 | + // Check if the destination object already exists |
| 79 | + $blobExists = $this->storageClient->blobExists( $dstCont, $dstRel ); |
| 80 | + if ( $blobExists && empty( $params['overwrite'] ) ) { //Blob exists _and_ should not be overridden |
| 81 | + $status->fatal( 'backend-fail-alreadyexists', $params['dst'] ); |
| 82 | + return $status; |
| 83 | + } |
| 84 | + |
79 | 85 | try { |
80 | 86 | $result = $this->storageClient->putBlob( $dstCont, $dstRel, $params['src']); |
81 | 87 | } |
82 | 88 | catch ( Exception $e ) { |
83 | | - // TODO: Read exception. Are there different ones? |
84 | 89 | $status->fatal( 'backend-fail-store', $dstRel, $dstCont ); |
85 | 90 | } |
86 | 91 | return $status; |
— | — | @@ -92,8 +97,21 @@ |
93 | 98 | $status = Status::newGood(); |
94 | 99 | list( $srcContainer, $srcDir ) = $this->resolveStoragePath( $params['src'] ); |
95 | 100 | list( $dstContainer, $dstDir ) = $this->resolveStoragePath( $params['dst'] ); |
| 101 | + if ( $srcDir === null ) { |
| 102 | + $status->fatal( 'backend-fail-invalidpath', $params['src'] ); |
| 103 | + return $status; |
| 104 | + } |
| 105 | + if ( $dstDir === null ) { |
| 106 | + $status->fatal( 'backend-fail-invalidpath', $params['dst'] ); |
| 107 | + return $status; |
| 108 | + } |
| 109 | + |
| 110 | + $blobExists = $this->storageClient->blobExists( $dstContainer, $dstDir ); |
| 111 | + if ( $blobExists && empty( $params['overwrite'] ) ) { //Blob exists _and_ should not be overridden |
| 112 | + $status->fatal( 'backend-fail-alreadyexists', $params['dst'] ); |
| 113 | + return $status; |
| 114 | + } |
96 | 115 | |
97 | | - // TODO: check for null |
98 | 116 | try { |
99 | 117 | $result = $this->storageClient->copyBlob( $srcContainer, $srcDir, $dstContainer, $dstDir); |
100 | 118 | } |
— | — | @@ -115,8 +133,8 @@ |
116 | 134 | return $status; |
117 | 135 | } |
118 | 136 | |
119 | | - // (a) Check the source container |
120 | | - try { //TODO: Unnecessary --> remove (or better, check in resolveStoragePath?) |
| 137 | + // Check the source container |
| 138 | + try { |
121 | 139 | $container = $this->storageClient->getContainer( $srcCont ); |
122 | 140 | } |
123 | 141 | catch ( Exception $e ) { |
— | — | @@ -124,7 +142,7 @@ |
125 | 143 | return $status; |
126 | 144 | } |
127 | 145 | |
128 | | - // (b) Actually delete the object |
| 146 | + // Actually delete the object |
129 | 147 | try { |
130 | 148 | $this->storageClient->deleteBlob( $srcCont, $srcRel ); |
131 | 149 | } |
— | — | @@ -147,27 +165,25 @@ |
148 | 166 | return $status; |
149 | 167 | } |
150 | 168 | |
151 | | - // (a) Check if the destination object already exists |
| 169 | + // Check if the destination object already exists |
152 | 170 | $blobExists = $this->storageClient->blobExists( $dstCont, $dstRel ); |
153 | 171 | if ( $blobExists && empty( $params['overwrite'] ) ) { //Blob exists _and_ should not be overridden |
154 | 172 | $status->fatal( 'backend-fail-alreadyexists', $params['dst'] ); |
155 | 173 | return $status; |
156 | 174 | } |
157 | 175 | |
158 | | - // (b) Actually create the object |
| 176 | + // Actually create the object |
159 | 177 | try { |
160 | | - // TODO: how do I know the container exists? Should we call prepare? |
161 | 178 | $this->storageClient->putBlobData( $dstCont, $dstRel, $params['content'] ); |
162 | 179 | } |
163 | 180 | catch ( Exception $e ) { |
164 | 181 | $status->fatal( 'backend-fail-internal' ); |
165 | 182 | } |
166 | | - |
167 | 183 | return $status; |
168 | 184 | } |
169 | 185 | |
170 | 186 | /** |
171 | | - * @see FileBackend::prepare() |
| 187 | + * @see FileBackend::doPrepare() |
172 | 188 | */ |
173 | 189 | |
174 | 190 | function doPrepareInternal( $container, $dir, array $params ) { |
— | — | @@ -180,13 +196,7 @@ |
181 | 197 | } |
182 | 198 | try { |
183 | 199 | $this->storageClient->createContainerIfNotExists( $c ); |
184 | | - // TODO: must this be set anytime prepare is called? |
185 | 200 | $this->storageClient->setContainerAcl( $c, Microsoft_WindowsAzure_Storage_Blob::ACL_PUBLIC ); |
186 | | - |
187 | | - // TODO: check if readable and writeable |
188 | | - //$container = $this->storageClient->getContainer( $c ); |
189 | | - //$status->fatal( 'directoryreadonlyerror', $params['dir'] ); |
190 | | - //$status->fatal( 'directorynotreadableerror', $params['dir'] ); |
191 | 201 | } |
192 | 202 | catch (Exception $e ) { |
193 | 203 | $status->fatal( 'directorycreateerror', $params['dir'] ); |
— | — | @@ -203,7 +213,8 @@ |
204 | 214 | //Azure container naming conventions; http://msdn.microsoft.com/en-us/library/dd135715.aspx |
205 | 215 | $container = strtolower($container); |
206 | 216 | $container = preg_replace( '#[^a-z0-9\-]#', '', $container ); |
207 | | - // TODO: "-test" and "test-" are invalid, too |
| 217 | + $container = preg_replace( '#^-#', '', $container ); |
| 218 | + $container = preg_replace( '#-$#', '', $container ); |
208 | 219 | $container = preg_replace( '#-{2,}#', '-', $container ); |
209 | 220 | |
210 | 221 | return $container; |
— | — | @@ -212,20 +223,27 @@ |
213 | 224 | /** |
214 | 225 | * @see FileBackend::secure() |
215 | 226 | */ |
216 | | - /* |
217 | | - // TODO: check if we need to override doSecure |
218 | | - function secure( array $params ) { |
| 227 | + function doSecureInternal( $container, $dir, array $params ) { |
219 | 228 | $status = Status::newGood(); |
220 | | - return $status; // badgers? We don't need no steenking badgers! |
| 229 | + |
| 230 | + try { |
| 231 | + if ( $this->storageClient->containerExists( $container ) ) { |
| 232 | + if ( $params['noAccess'] == true ) { |
| 233 | + $this->storageClient->setContainerAcl( $container, Microsoft_WindowsAzure_Storage_Blob::ACL_PRIVATE ); |
| 234 | + } |
| 235 | + } |
| 236 | + } |
| 237 | + catch (Exception $e ) { |
| 238 | + $status->fatal( 'directorycreateerror', $container ); |
| 239 | + return $status; |
| 240 | + } |
| 241 | + return $status; |
221 | 242 | } |
222 | | - */ |
223 | 243 | |
224 | 244 | /** |
225 | 245 | * @see FileBackend::fileExists() |
226 | 246 | */ |
227 | | - function doFileExists( array $params ) {die(); |
228 | | - // TODO: Merely renamed this functino from fileExists. Check if more is neccessarey |
229 | | - // TODO: Off topic here: Prepare function might call some internal function which can be overridden. |
| 247 | + function doFileExists( array $params ) { |
230 | 248 | list( $srcCont, $srcRel ) = $this->resolveStoragePath( $params['src'] ); |
231 | 249 | if ( $srcRel === null ) { |
232 | 250 | return false; // invalid storage path |
— | — | @@ -236,45 +254,34 @@ |
237 | 255 | } |
238 | 256 | |
239 | 257 | /** |
240 | | - * @see FileBackend::getFileTimestamp() |
241 | | - */ |
242 | | - // TODO: merely renamed from getFileTimestamp. Check if there are any probs. |
243 | | - // TODO: Is this method still used? |
244 | | - function doGetFileTimestamp( array $params ) { |
245 | | - list( $srcCont, $srcRel ) = $this->resolveStoragePath( $params['src'] ); |
246 | | - if ( $srcRel === null ) { |
247 | | - return false; // invalid storage path |
248 | | - } |
249 | | - |
250 | | - $timestamp= false; |
251 | | - try { |
252 | | - //TODO Maybe use getBlobData()? |
253 | | - $blob = $this->storageClient->getBlobInstance( $srcCont, $srcRel ); |
254 | | - $timestamp = wfTimestamp( TS_MW, $blob->LastModified ); //TODO: Timezone? |
255 | | - } catch ( Exception $e ) { |
256 | | - // TODO: Log it? What about different types of exceptions? |
257 | | - } |
258 | | - return $timestamp; |
259 | | - } |
260 | | - |
261 | | - /** |
262 | 258 | * @see FileBackend::getFileList() |
263 | 259 | */ |
264 | 260 | function getFileListInternal( $container, $dir, array $params ) { |
265 | | - // TODO: merely renamed from getFileList. Check implications (i assume, the list thing is no longer needed. |
266 | | - //list( $c, $dir ) = $this->resolveStoragePath( $params['dir'] ); |
267 | 261 | $files = array(); |
| 262 | + |
268 | 263 | try { |
269 | | - if ( $dir === null ) { |
| 264 | + if ( trim($dir) == '' ) { |
270 | 265 | $blobs = $this->storageClient->listBlobs($container); |
271 | 266 | } |
272 | 267 | else { |
273 | | - //TODO: Check if $dir really is a startsequence of the blob name |
| 268 | + if ( strrpos($dir, '/') != strlen($dir)-1 ) { |
| 269 | + $dir = $dir.'/'; |
| 270 | + } |
274 | 271 | $blobs = $this->storageClient->listBlobs($container, $dir); |
275 | 272 | } |
276 | 273 | |
277 | 274 | foreach( $blobs as $blob ) { |
278 | | - $files[] = $blob->name; |
| 275 | + // Only return the actual file name without the / |
| 276 | + $tempName = $blob->name; |
| 277 | + if ( trim($dir) != '' ) { |
| 278 | + if ( strstr( $tempName, $dir ) !== false ) { |
| 279 | + $tempName = substr($tempName, strpos( $tempName, $dir ) + strlen( $dir ) ); |
| 280 | + $files[] = $tempName; |
| 281 | + } |
| 282 | + } else { |
| 283 | + $files[] = $tempName; |
| 284 | + } |
| 285 | + |
279 | 286 | } |
280 | 287 | } |
281 | 288 | catch( Exception $e ) { |
— | — | @@ -297,7 +304,6 @@ |
298 | 305 | // Get source file extension |
299 | 306 | $ext = FileBackend::extensionFromPath( $srcRel ); |
300 | 307 | // Create a new temporary file... |
301 | | - // TODO: Caution: tempfile should not write a local file. |
302 | 308 | $tmpFile = TempFSFile::factory( wfBaseName( $srcRel ) . '_', $ext ); |
303 | 309 | if ( !$tmpFile ) { |
304 | 310 | return null; |
— | — | @@ -308,17 +314,16 @@ |
309 | 315 | $this->storageClient->getBlob( $srcCont, $srcRel, $tmpPath ); |
310 | 316 | } |
311 | 317 | catch ( Exception $e ) { |
312 | | - $tmpFile = null; |
| 318 | + return null; |
313 | 319 | } |
314 | 320 | |
315 | 321 | return $tmpFile; |
316 | 322 | } |
317 | 323 | |
318 | 324 | /** |
319 | | - * @see FileBackend::doFileExists() |
| 325 | + * @see FileBackend::getFileStat() |
320 | 326 | */ |
321 | 327 | protected function doGetFileStat( array $params ) { |
322 | | - // TODO: Refactor |
323 | 328 | list( $srcCont, $srcRel ) = $this->resolveStoragePathReal( $params['src'] ); |
324 | 329 | if ( $srcRel === null ) { |
325 | 330 | return false; // invalid storage path |
— | — | @@ -326,19 +331,39 @@ |
327 | 332 | |
328 | 333 | $timestamp= false; |
329 | 334 | $size = false; |
330 | | - // TODO: need additional checks?? |
| 335 | + |
331 | 336 | try { |
332 | | - // TODO: Maybe use getBlobData()? |
333 | 337 | $blob = $this->storageClient->getBlobInstance( $srcCont, $srcRel ); |
334 | | - $timestamp = wfTimestamp( TS_MW, $blob->LastModified ); //TODO: Timezone? |
| 338 | + $timestamp = wfTimestamp( TS_MW, $blob->LastModified ); |
335 | 339 | $size = $blob->Size; |
336 | 340 | return array( |
337 | 341 | 'mtime' => $timestamp, |
338 | 342 | 'size' => $size |
339 | 343 | ); |
340 | 344 | } catch ( Exception $e ) { |
341 | | - // TODO: Log it? What about different types of exceptions? |
| 345 | + $stat = null; |
342 | 346 | } |
343 | 347 | return false; |
344 | 348 | } |
| 349 | + |
| 350 | + /** |
| 351 | + * Check if a file can be created at a given storage path. |
| 352 | + * FS backends should check if the parent directory exists and the file is writable. |
| 353 | + * Backends using key/value stores should check if the container exists. |
| 354 | + * |
| 355 | + * @param $storagePath string |
| 356 | + * @return bool |
| 357 | + */ |
| 358 | + |
| 359 | + public function isPathUsableInternal( $storagePath ) { |
| 360 | + list( $c, $dir ) = $this->resolveStoragePath( $storagePath ); |
| 361 | + if ( $dir === null ) { |
| 362 | + return false; |
| 363 | + } |
| 364 | + if ( !$this->storageClient->containerExists( $c ) ) { |
| 365 | + return false; |
| 366 | + } |
| 367 | + |
| 368 | + return true; |
| 369 | + } |
345 | 370 | } |