1
2
3
4 """
5 API for handling file uploads and server file management.
6
7 @package includes
8 @see <a href='http://drupy.net'>Drupy Homepage</a>
9 @see <a href='http://drupal.org'>Drupal Homepage</a>
10 @note Drupy is a port of the Drupal project.
11 @note This file was ported from Drupal's includes/file.inc
12 @author Brendon Crawford
13 @copyright 2008 Brendon Crawford
14 @contact message144 at users dot sourceforge dot net
15 @created 2008-01-10
16 @version 0.1
17 @note License:
18
19 This program is free software; you can redistribute it and/or
20 modify it under the terms of the GNU General Public License
21 as published by the Free Software Foundation; either version 2
22 of the License, or (at your option) any later version.
23
24 This program is distributed in the hope that it will be useful,
25 but WITHOUT ANY WARRANTY; without even the implied warranty of
26 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27 GNU General Public License for more details.
28
29 You should have received a copy of the GNU General Public License
30 along with this program; if not, write to:
31
32 The Free Software Foundation, Inc.,
33 51 Franklin Street, Fifth Floor,
34 Boston, MA 02110-1301,
35 USA
36 """
37
38 __version__ = "$Revision: 1 $"
39
40 from lib.drupy import DrupyPHP as php
41
42
43
44
45
46
47
48
49
50
51
52 FILE_DOWNLOADS_PUBLIC = 1
53
54
55
56
57
58
59
60 FILE_DOWNLOADS_PRIVATE = 2
61
62
63
64
65 FILE_CREATE_DIRECTORY = 1
66
67
68
69
70 FILE_MODIFY_PERMISSIONS = 2
71
72
73
74
75 FILE_EXISTS_RENAME = 0
76
77
78
79
80 FILE_EXISTS_REPLACE = 1
81
82
83
84
85 FILE_EXISTS_ERROR = 2
86
87
88
89
90
91
92
93 FILE_STATUS_TEMPORARY = 0
94
95
96
97
98
99
100
101
102 FILE_STATUS_PERMANENT = 1
103
120
121
122
124 """
125 Make sure the destination is a complete path and resides in the file system
126 directory, if it is not prepend the file system directory.
127
128 @param dest A string containing the path to verify + If this value is
129 omitted, Drupal's 'files' directory will be used.
130 @return A string containing the path to file, with file system directory
131 appended if necessary, or False if the path is invalid (i.e + outside the
132 configured 'files' or temp directories).
133 """
134 file_path = file_directory_path()
135 if (dest == 0):
136 return file_path
137
138
139 if (file_check_location(dest, file_path)):
140 return dest
141
142
143 elif (file_check_location(dest, file_directory_temp())):
144 return dest
145
146 elif (file_check_location(file_path + '/' + dest, file_path)):
147 return file_path + '/' + dest
148
149 return False
150
151
153 """
154 Check that the directory exists and is writable + Directories need to
155 have execute permissions to be considered a directory by FTP servers, etc.
156
157 @param directory A string containing the name of a directory path.
158 @param mode A Boolean value to indicate if the directory should be created
159 if it does not exist or made writable if it is read-only.
160 @param form_item An optional string containing the name of a form item that
161 any errors will be attached to + This is useful for settings forms that
162 require the user to specify a writable directory + If it can't be made to
163 work, a form error will be set preventing them from saving the settings.
164 @return False when directory not found, or True when directory exists.
165 """
166 php.Reference.check(directory);
167 directory._ = php.rtrim(directory._, '/\\')
168
169 if (not php.is_dir(directory._)):
170 if ((mode & FILE_CREATE_DIRECTORY) and mkdir(directory._) != False):
171 chmod(directory._, 0775);
172 else:
173 if (form_item):
174 form_set_error(form_item, \
175 t('The directory %directory does not exist.', \
176 {'%directory' : directory._}))
177 watchdog('file system', 'The directory %directory does not exist.', \
178 {'%directory' : directory}, WATCHDOG_ERROR);
179 return False
180
181 if (not php.is_writable(directory._)):
182 if ((mode & FILE_MODIFY_PERMISSIONS) and not php.chmod(directory, 0775)):
183 form_set_error(form_item, t('The directory %directory is not writable', \
184 {'%directory' : directory._}))
185 watchdog('file system', 'The directory %directory is not writable, ' + \
186 'because it does not have the correct permissions set.', \
187 {'%directory' : directory._}, WATCHDOG_ERROR)
188 return False
189 if ((file_directory_path() == directory._ or \
190 file_directory_temp() == directory._) and \
191 not php.is_file("directory/.htaccess")):
192 htaccess_lines = \
193 "SetHandler Drupal_Security_Do_Not_Remove_See_SA_2006_006\n" + \
194 "Options None\nOptions +FollowSymLinks"
195 fp = fopen("directory/.htaccess", 'w')
196 if (fp and fputs(fp, htaccess_lines)):
197 fclose(fp)
198 chmod(directory._ + '/.htaccess', 0664)
199 else:
200 variables = {'%directory' : directory._, \
201 '!htaccess' : '<br />' + php.nl2br(check_plain(htaccess_lines))}
202 form_set_error(form_item, t("Security warning: " + \
203 "Couldn't write + htaccess file. " + \
204 "Please create a .htaccess file in your " + \
205 "%directory directory which contains the following lines: " + \
206 "<code>!htaccess</code>", variables))
207 watchdog('security', "Security warning: Couldn't write " + \
208 ".htaccess file. Please create a .htaccess file in " + \
209 "your %directory directory which contains the " + \
210 "following lines: <code>not htaccess</code>", \
211 variables, WATCHDOG_ERROR)
212 return True
213
214
215
217 """
218 Checks path to see if it is a directory, or a dir/file.
219
220 @param path A string containing a file path + This will be set to the
221 directory's path.
222 @return If the directory is not in a Drupal writable directory, False is
223 returned + Otherwise, the base name of the path is returned.
224 """
225 php.Reference.check(path)
226
227 if (file_check_directory(path)):
228 return ''
229
230 filename = basename(path)
231 path = php.dirname(path)
232 if (file_check_directory(path)):
233 return filename
234 return False
235
236
237
239 """
240 Check if a file is really located inside directory + Should be used to make
241 sure a file specified is really located within the directory to prevent
242 exploits.
243
244 @code
245 // Returns False:
246 file_check_location('/www/example.com/files/../../../etc/passwd', \
247 '/www/example.com/files')
248 @endcode
249
250 @param source A string set to the file to check.
251 @param directory A string where the file should be located.
252 @return FALSE for invalid path or the real path of the source.
253 """
254 check = realpath(source)
255 if (check):
256 source = check
257 else:
258
259 source = realpath(php.dirname(source)) + '/' + basename(source)
260 directory = realpath(directory)
261 if (directory and php.strpos(source, directory) != 0):
262 return False
263 return source
264
265
266
268 """
269 Copies a file to a new location.
270 This is a powerful function that in many ways
271 performs like an advanced version of copy().
272 - Checks if source and dest are valid and readable/writable.
273 - Performs a file copy if source is not equal to dest.
274 - If file already exists in dest either the call will
275 error out, replace the
276 file or rename the file based on the replace parameter.
277
278 @param source A string specifying the file location of the original file.
279 This parameter will contain the resulting destination filename in case of
280 success.
281 @param dest A string containing the directory source should be copied to.
282 If this value is omitted, Drupal's 'files' directory will be used.
283 @param replace Replace behavior when the destination file already exists.
284 - FILE_EXISTS_REPLACE - Replace the existing file
285 - FILE_EXISTS_RENAME - Append _{incrementing number} until
286 the filename is unique
287 - FILE_EXISTS_ERROR - Do nothing and return False.
288 @return True for success, False for failure.
289 """
290 php.Reference.check(source)
291 dest = file_create_path(dest)
292 directory = dest
293 basename = file_check_path(directory)
294
295 if (basename == False):
296 if hasattr(source, 'filepath'):
297 source._ = source.filepath
298 drupal_set_message(t('The selected file %file could not be ' + \
299 'uploaded, because the destination %directory is not ' + \
300 'properly configured.', \
301 {'%file' : source._, '%directory' : dest}), 'error')
302 watchdog('file system', 'The selected file %file could not ' + \
303 'be uploaded, because the destination %directory could ' + \
304 'not be found, or because its permissions do ' + \
305 'not allow the file to be written.', \
306 {'%file' : source._, '%directory' : dest}, WATCHDOG_ERROR)
307 return False
308
309 if (php.is_object(source._)):
310 file = source._
311 source._ = file.filepath
312 if (not basename):
313 basename = file.filename
314 source._ = php.realpath(source._)
315 if (not php.file_exists(source._)):
316 drupal_set_message(t('The selected file %file could not be copied, ' + \
317 'because no file by that name exists. ' + \
318 'Please check that you supplied the correct filename.', \
319 {'%file' : source._}), 'error')
320 return False
321
322
323 basename = (basename if basename else basename(source._))
324 dest._ = directory + '/' + basename
325
326
327
328
329 if (source._ != php.realpath(dest._)):
330 dest._ = file_destination(dest._, replace)
331 if (not dest._):
332 drupal_set_message(t('The selected file %file could not be copied, ' + \
333 'because a file by that name already exists in the destination.', \
334 {'%file' : source._}), 'error')
335 return False
336 if (not copy(source._, dest._)):
337 drupal_set_message(t('The selected file %file could not be copied.', \
338 {'%file' : source._}), 'error')
339 return False
340
341
342
343
344 chmod(dest._, 0664)
345 if (php.isset(file) and php.is_object(file)):
346 file.filename = basename
347 file.filepath = dest._
348 source._ = file
349 else:
350 source._ = dest
351 return True
352
353
354
356 """
357 Determines the destination path for a file depending on how replacement of
358 existing files should be handled.
359
360 @param destination A string specifying the desired path.
361 @param replace Replace behavior when the destination file already exists.
362 - FILE_EXISTS_REPLACE - Replace the existing file
363 - FILE_EXISTS_RENAME - Append _{incrementing number} until the filename is
364 unique
365 - FILE_EXISTS_ERROR - Do nothing and return False.
366 @return The destination file path or False if the file already exists and
367 FILE_EXISTS_ERROR was specified.
368 """
369 if (php.file_exists(destination)):
370 if replace == FILE_EXISTS_RENAME:
371 basename = basename(destination)
372 directory = php.dirname(destination)
373 destination = file_create_filename(basename, directory)
374 elif replace == FILE_EXISTS_ERROR:
375 drupal_set_message(t('The selected file %file could not be copied, \
376 because a file by that name already exists in the destination.', \
377 {'%file' : destination}), 'error')
378 return False
379 return destination
380
381
383 """
384 Moves a file to a new location.
385 - Checks if source and dest are valid and readable/writable.
386 - Performs a file move if source is not equal to dest.
387 - If file already exists in dest either the call will error out, replace the
388 file or rename the file based on the replace parameter.
389
390 @param source A string specifying the file location of the original file.
391 This parameter will contain the resulting destination filename in case of
392 success.
393 @param dest A string containing the directory source should be copied to.
394 If this value is omitted, Drupal's 'files' directory will be used.
395 @param replace Replace behavior when the destination file already exists.
396 - FILE_EXISTS_REPLACE - Replace the existing file
397 - FILE_EXISTS_RENAME - Append _{incrementing number} until the
398 filename is unique
399 - FILE_EXISTS_ERROR - Do nothing and return False.
400 @return True for success, False for failure.
401 """
402 php.Reference.check(source)
403 if hasattr(source, 'filepath'):
404 source._ = source.filepath
405 path_original = source._
406 if (file_copy(source._, dest, replace)):
407 path_current = (source.filepath if hasattr(source, 'filepath') else \
408 source._)
409 if (path_original == path_current or file_delete(path_original)):
410 return True
411 drupal_set_message(t('The removal of the original file %file ' + \
412 'has failed.', {'%file' : path_original}), 'error')
413 return False
414
415
416
418 """
419 Munge the filename as needed for security purposes + For instance the file
420 name "exploit.php.pps" would become "exploit.php_.pps".
421
422 @param filename The name of a file to modify.
423 @param extensions A space separated list of extensions that should not
424 be altered.
425 @param alerts Whether alerts (watchdog, drupal_set_message()) should be
426 displayed.
427 @return filename The potentially modified filename.
428 """
429 original = filename
430
431 if (not variable_get('allow_insecure_uploads', 0)):
432 whitelist = array_unique(php.explode(' ', php.trim(extensions)))
433
434
435 filename_parts = php.explode('.', filename)
436 new_filename = php.array_shift(filename_parts);
437 final_extension = php.array_pop(filename_parts);
438
439
440
441 for filename_part in filename_parts:
442 new_filename += '.' + filename_part
443 if (not php.in_array(filename_part, whitelist) and \
444 php.preg_match("/^[a-zA-Z]{2,5}\d?$/", filename_part)):
445 new_filename += '_'
446 filename = new_filename + '.' + final_extension
447 if (alerts and original != filename):
448 drupal_set_message(t('For security reasons, your upload has ' + \
449 'been renamed to %filename.', {'%filename' : filename}))
450 return filename
451
452
453
455 """
456 Undo the effect of upload_munge_filename().
457
458 @param filename string filename
459 @return string
460 """
461 return php.str_replace('_.', '.', filename)
462
463
464
466 """
467 Create a full file path from a directory and filename + If a file with the
468 specified name already exists, an alternative will be used.
469
470 @param basename string filename
471 @param directory string directory
472 @return
473 """
474 dest = directory + '/' + basename
475 if (php.file_exists(dest)):
476
477 pos = strrpos(basename, '.')
478 if (pos):
479 name = php.substr(basename, 0, pos)
480 ext = php.substr(basename, pos)
481 else:
482 name = basename
483 counter = 0
484 while True:
485 dest = directory + '/' + name + '_' + counter + ext
486 counter += 1
487 if (not php.file_exists(dest)):
488 break
489 return dest
490
491
492
494 """
495 Delete a file.
496
497 @param path A string containing a file path.
498 @return True for success, False for failure.
499 """
500 if (php.is_file(path)):
501 return p.unlink(path)
502
503
505 """
506 Determine total disk space used by a single user or the whole filesystem.
507
508 @param uid
509 An optional user id + A None value returns the total space used
510 by all files.
511 """
512 if (uid != None):
513 return drupy_int(db_result(db_query(\
514 'SELECT SUM(filesize) FROM {files} WHERE uid = %d', uid)))
515 return drupy_int(db_result(db_query('SELECT SUM(filesize) FROM {files}')))
516
517
520 """
521 Saves a file upload to a new location + The source file is validated as a
522 proper upload and handled as such.
523
524 The file will be added to the files table as a temporary file.
525 Temporary files
526 are periodically cleaned + To make the file permanent file call
527 file_set_status() to change its status.
528
529 @param source
530 A string specifying the name of the upload field to save.
531 @param validators
532 An optional, associative array of callback functions used to validate the
533 file + The keys are function names and the values arrays of callback
534 parameters which will be passed in after the user and file objects + The
535 functions should return an array of error messages, an empty array
536 indicates that the file passed validation.
537 The functions will be called in
538 the order specified.
539 @param dest
540 A string containing the directory source should be copied to + If this is
541 not provided or is not writable, the temporary directory will be used.
542 @param replace
543 A boolean indicating whether an existing file of the same name in the
544 destination directory should overwritten + A False value will generate a
545 new, unique filename in the destination directory.
546 @return
547 An object containing the file information, or False
548 in the event of an error.
549 """
550 php.static(file_save_upload, 'upload_cache', {})
551
552 validators['file_validate_name_length'] = {}
553
554
555 if (php.isset(file_save_upload.uploadcache, source)):
556 return file_save_upload.uploadcache[source]
557
558 if (php.isset(p.FILES, 'files') and p.FILES['files']['name'][source] and \
559 php.is_uploaded_file(p.FILES['files']['tmp_name'][source])):
560
561
562
563 if p.FILES['files']['error'][source] == UPLOAD_ERR_OK:
564 pass
565 elif p.FILES['files']['error'][source] == UPLOAD_ERR_INI_SIZE or \
566 p.FILES['files']['error'][source] == UPLOAD_ERR_FORM_SIZE:
567 drupal_set_message(t(\
568 'The file %file could not be saved, because it exceeds %maxsize, ' + \
569 'the maximum allowed size for uploads.', \
570 {'%file' : source, '%maxsize' : \
571 format_size(file_upload_max_size())}), 'error')
572 return False
573 elif p.FILES['files']['error'][source] == UPLOAD_ERR_PARTIAL or \
574 p.FILES['files']['error'][source] == UPLOAD_ERR_NO_FILE:
575 drupal_set_message(t('The file %file could not be saved, ' + \
576 'because the upload did not complete.', {'%file' : source}), 'error')
577 return False
578
579 else:
580 drupal_set_message(t('The file %file could not be saved. ' + \
581 'An unknown error has occurred.', {'%file' : source}), 'error')
582 return False
583
584
585 extensions = ''
586 for rid,name in lib_appglobals.user.roles.items():
587 extensions += ' ' + variable_get("upload_extensions_rid",
588 variable_get('upload_extensions_default', \
589 'jpg jpeg gif png txt html doc xls pdf ppt pps odt ods odp'))
590
591 file = php.stdClass()
592 file.filename = file_munge_filename(php.trim(\
593 basename(p.FILES['files']['name'][source]), '.'), extensions)
594 file.filepath = p.FILES['files']['tmp_name'][source]
595 file.filemime = p.FILES['files']['type'][source]
596
597 if (php.preg_match('/\.(php|pl|py|cgi|asp|js)$/i', file.filename) and \
598 (php.substr(file.filename, -4) != '.txt')):
599 file.filemime = 'text/plain'
600 file.filepath += '.txt'
601 file.filename += '.txt'
602
603
604 if (php.empty(dest) or file_check_path(dest) == False):
605 dest = file_directory_temp()
606 file.source = source
607 file.destination = file_destination(file_create_path(dest + '/' + \
608 file.filename), replace)
609 file.filesize = FILES['files']['size'][source]
610
611 errors = {}
612 for function,args in validators.items():
613 array_unshift(args, file)
614 errors = php.array_merge(errors, function(*args))
615
616 if (not php.empty(errors)):
617 message = t('The selected file %name could not be uploaded.', \
618 {'%name' : file.filename})
619 if (php.count(errors) > 1):
620 message += '<ul><li>' + php.implode('</li><li>', errors) + '</li></ul>'
621 else:
622 message += ' ' + php.array_pop(errors)
623 form_set_error(source, message)
624 return False
625
626
627
628 file.filepath = file.destination
629 if (not move_uploaded_file(p.FILES['files']['tmp_name'][source], \
630 file.filepath)):
631 form_set_error(source, t('File upload error. ' + \
632 'Could not move uploaded file.'))
633 watchdog('file', 'Upload error + Could not move uploaded file ' + \
634 '%file to destination %destination.', \
635 {'%file' : file.filename, '%destination' : file.filepath})
636 return False
637
638 file.uid = lib_appglobals.user.uid
639 file.status = FILE_STATUS_TEMPORARY
640 file.timestamp = time()
641 drupal_write_record('files', file)
642
643 file_save_upload.upload_cache[source] = file
644 return file
645 return False
646
647
648
650 """
651 Check for files with names longer than we can store in the database.
652
653 @param file
654 A Drupal file object.
655 @return
656 An array + If the file name is too long, it will contain an error message.
657 """
658 errors = []
659 if (php.strlen(file.filename) > 255):
660 errors.append( t('Its name exceeds the 255 characters limit. ' + \
661 'Please rename the file and try again.') )
662 return errors
663
664
665
667 """
668 Check that the filename ends with an allowed extension + This check is not
669 enforced for the user #1.
670
671 @param file
672 A Drupal file object.
673 @param extensions
674 A string with a space separated
675 @return
676 An array + If the file extension is not allowed,
677 it will contain an error message.
678 """
679 errors = []
680
681 if (lib_appglobals.user.uid != 1):
682 regex = '/\.(' + ereg_replace(' +', '|', \
683 php.preg_quote(extensions)) + ')$/i'
684 if (not php.preg_match(regex, file.filename)):
685 errors.append( t('Only files with the following extensions ' + \
686 'are allowed: %files-allowed.', {'%files-allowed' : extensions}) )
687 return errors
688
689
690
692 """
693 Check that the file's size is below certain limits + This check is not
694 enforced for the user #1.
695
696 @param file
697 A Drupal file object.
698 @param file_limit
699 An integer specifying the maximum file size in bytes.
700 Zero indicates that
701 no limit should be enforced.
702 @param $user_limit
703 An integer specifying the maximum number of bytes the user is allowed .
704 Zero indicates that no limit should be enforced.
705 @return
706 An array + If the file size exceeds limits,
707 it will contain an error message.
708 """
709 errors = []
710
711 if (lib_appglobals.user.uid != 1):
712 if (file_limit and file.filesize > file_limit):
713 errors.append( t('The file is %filesize exceeding the ' + \
714 'maximum file size of %maxsize.', \
715 {'%filesize' : format_size(file.filesize), \
716 '%maxsize' : format_size(file_limit)}) )
717 total_size = file_space_used(lib_appglobals.uid) + file.filesize
718 if (user_limit and total_size > user_limit):
719 errors.append( t('The file is %filesize which would exceed ' + \
720 'your disk quota of %quota.', \
721 {'%filesize' : format_size(file.filesize), \
722 '%quota' : format_size(user_limit)}) )
723 return errors
724
725
726
727
729 """
730 Check that the file is recognized by image_get_info() as an image.
731
732 @param file
733 A Drupal file object.
734 @return
735 An array + If the file is not an image, it will contain an error message.
736 """
737 errors = []
738 info = image_get_info(file.filepath)
739 if (not info or not php.isset(info, 'extension') or \
740 php.empty(info['extension'])):
741 errors.append( t('Only JPEG, PNG and GIF images are allowed.') )
742 return errors
743
744
745
746
749 """
750 If the file is an image verify that its dimensions are within the specified
751 maximum and minimum dimensions + Non-image files will be ignored.
752
753 @param file
754 A Drupal file object + This function may resize the file
755 affecting its size.
756 @param maximum_dimensions
757 An optional string in the form WIDTHxHEIGHT e.g + '640x480' or '85x85'. If
758 an image toolkit is installed the image will be resized down to these
759 dimensions + A value of 0 indicates no restriction on size, so resizing
760 will be attempted.
761 @param minimum_dimensions
762 An optional string in the form WIDTHxHEIGHT.
763 This will check that the image
764 meets a minimum size + A value of 0 indicates no restriction.
765 @return
766 An array + If the file is an image and did not meet the requirements, it
767 will contain an error message.
768 """
769 php.Reference.check(file)
770 errors = []
771
772 info = image_get_info(file.filepath)
773 if (info):
774 if (maximum_dimensions):
775
776 width, height = php.explode('x', maximum_dimensions)
777 if (info['width'] > width or info['height'] > height):
778
779 if (image_get_toolkit() and image_scale(file.filepath, \
780 file.filepath, width, height)):
781 drupal_set_message(t('The image was resized to fit within ' + \
782 'the maximum allowed dimensions of %dimensions pixels.', \
783 {'%dimensions' : maximum_dimensions}))
784
785 clearstatcache()
786 info = image_get_info(file.filepath)
787 file.filesize = info['file_size']
788 else:
789 errors.append( t('The image is too large; the maximum ' + \
790 'dimensions are %dimensions pixels.', \
791 {'%dimensions' : maximum_dimensions}) )
792 if (minimum_dimensions):
793
794 width, height = php.explode('x', minimum_dimensions)
795 if (info['width'] < width or info['height'] < height):
796 errors.append( t('The image is too small; the ' + \
797 'minimum dimensions are %dimensions pixels.', \
798 {'%dimensions' : minimum_dimensions}) )
799 return errors
800
801
802
804 """
805 Save a string to the specified destination.
806
807 @param data A string containing the contents of the file.
808 @param dest A string containing the destination location.
809 @param replace Replace behavior when the destination file already exists.
810 - FILE_EXISTS_REPLACE - Replace the existing file
811 - FILE_EXISTS_RENAME - Append _{incrementing number}
812 until the filename is unique
813 - FILE_EXISTS_ERROR - Do nothing and return False.
814
815 @return A string containing the resulting filename or False on error
816 """
817 temp = file_directory_temp()
818
819 file = tempnam(realpath(temp), 'file')
820 fp = p.fopen(file, 'wb')
821 if (not fp):
822 drupal_set_message(t('The file could not be created.'), 'error')
823 return False
824 p.fwrite(fp, data)
825 p.fclose(fp)
826 if (not file_move(file, dest, replace)):
827 return False
828 return file
829
830
831
833 """
834 Set the status of a file.
835
836 @param file A Drupal file object
837 @param status A status value to set the file to.
838 @return False on failure, True on success and file.status will contain the
839 status.
840 """
841 php.Reference.check(file)
842 if (db_query('UPDATE {files} SET status = %d WHERE fid = %d', \
843 status, file.fid)):
844 file.status = status
845 return True
846 return False
847
848
850 """
851 Transfer file using http to client + Pipes a file through Drupal to the
852 client.
853
854 @param source File to transfer.
855 @param headers An array of http headers to send along with file.
856 """
857 ob_end_clean()
858 for php.header in headers:
859
860
861
862 php.header = php.preg_replace('/\r?\n(?not \t| )/', '', php.header)
863 drupal_set_header(php.header)
864 source = file_create_path(source)
865
866 fd = fopen(source, 'rb')
867 if (fd):
868 while (not feof(fd)):
869 print fread(fd, 1024)
870 fclose(fd)
871 else:
872 drupal_not_found()
873 exit()
874
875
876
878 """
879 Call plugins that implement hook_file_download() to find out if a file is
880 accessible and what headers it should be transferred with + If a plugin
881 returns -1 drupal_access_denied() will be returned + If one or more plugins
882 returned headers the download will start with the returned headers + If no
883 plugins respond drupal_not_found() will be returned.
884 """
885
886 args = func_get_args()
887 filepath = php.implode('/', args)
888
889 if (php.isset(php.GET, 'file')):
890 filepath = php.GET['file']
891 if (php.file_exists(file_create_path(filepath))):
892 headers = plugin_invoke_all('file_download', filepath)
893 if (php.in_array(-1, headers)):
894 return drupal_access_denied()
895 if (php.count(headers)):
896 file_transfer(filepath, headers)
897 return drupal_not_found()
898
899
900
901 -def scan_directory(dir, mask, nomask = ['.', '..', 'CVS'], \
902 callback = 0, recurse = True, key = 'filename', min_depth = 0, depth = 0):
903 """
904 Finds all files that match a given mask in a given directory.
905 Directories and files beginning with a period are excluded; this
906 prevents hidden files and directories (such as SVN working directories)
907 from being scanned.
908
909 @param dir
910 The base directory for the scan, without trailing slash.
911 @param mask
912 The regular expression of the files to find.
913 @param nomask
914 An array of files/directories to ignore.
915 @param callback
916 The callback function to call for each match.
917 @param recurse
918 When True, the directory scan will recurse the entire tree
919 starting at the provided directory.
920 @param key
921 The key to be used for the returned array of files + Possible
922 values are "filename", for the path starting with dir,
923 "basename", for the basename of the file, and "name" for the name
924 of the file without an extension.
925 @param min_depth
926 Minimum depth of directories to return files from.
927 @param depth
928 Current depth of recursion + This parameter is only used
929 internally and should not be passed.
930
931 @return
932 An associative array (keyed on the provided key) of objects with
933 "path", "basename", and "name" members corresponding to the
934 matching files.
935 """
936 key = (key if php.in_array(key, \
937 ('filename', 'basename', 'name')) else 'filename')
938 files = []
939 if php.is_dir(dir):
940 dir_files = php.scandir(dir)
941 for file in dir_files:
942 if (not php.in_array(file, nomask) and file[0] != '.'):
943 if (php.is_dir("%s/%s" % (dir, file)) and recurse):
944
945
946 files = php.array_merge(file_scan_directory("%s/%s" % (dir, file), \
947 mask, nomask, callback, recurse, key, min_depth, depth + 1), files)
948 elif (depth >= min_depth and ereg(mask, file)):
949
950
951 filename = "%s/%s" % (dir, file)
952 basename_ = php.basename(file)
953 name = php.substr(basename_, 0, php.strrpos(basename_, '.'))
954 files[key] = php.stdClass()
955 files[key].filename = filename
956 files[key].basename = basename_
957 files[key].name = name
958 if (callback):
959 callback(filename)
960 return files
961
962
963
965 """
966 Determine the default temporary directory.
967
968 @return A string containing a temp directory.
969 """
970 temporary_directory = variable_get('file_directory_temp', None)
971 if (is_None(temporary_directory)):
972 directories = []
973
974 if (ini_get('upload_tmp_dir')):
975 directories.append( ini_get('upload_tmp_dir') )
976
977 if (php.substr(PHP_OS, 0, 3) == 'WIN'):
978 directories.append( 'c:\\windows\\temp' )
979 directories.append( 'c:\\winnt\\temp' )
980 path_delimiter = '\\'
981 else:
982 directories.append( '/tmp' )
983 path_delimiter = '/'
984 for directory in directories:
985 if (not temporary_directory and php.is_dir(directory)):
986 temporary_directory = directory
987
988
989 temporary_directory = (temporary_directory if \
990 (temporary_directory != None) else \
991 (file_directory_path() + path_delimiter + 'tmp'))
992 variable_set('file_directory_temp', temporary_directory)
993 return temporary_directory
994
995
996
998 """
999 Determine the default 'files' directory.
1000
1001 @return A string containing the path to Drupal's 'files' directory.
1002 """
1003 return variable_get('file_directory_path', conf_path() + '/files')
1004
1005
1007 """
1008 Determine the maximum file upload size by querying the PHP settings.
1009
1010 @return
1011 A file size limit in bytes based on the PHP
1012 upload_max_filesize and post_max_size
1013 """
1014 php.static(file_upload_max_size, 'max_size', -1)
1015 if (file_upload_max_size.max_size < 0):
1016 upload_max = parse_size(ini_get('upload_max_filesize'))
1017 post_max = parse_size(ini_get('post_max_size'))
1018 file_upload_max_size.max_size = (upload_max if \
1019 (upload_max < post_max) else post_max)
1020 return max_size
1021