OSDN Git Service

CMFileManager: when cancel coping, the dest file also exists and is incomplete.
[android-x86/packages-apps-CMFileManager.git] / src / com / cyanogenmod / filemanager / util / FileHelper.java
1 /*
2  * Copyright (C) 2012 The CyanogenMod Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package com.cyanogenmod.filemanager.util;
18
19 import android.content.Context;
20 import android.content.SharedPreferences;
21 import android.content.res.Resources;
22 import android.system.ErrnoException;
23 import android.system.OsConstants;
24 import android.util.Log;
25
26 import com.cyanogenmod.filemanager.FileManagerApplication;
27 import com.cyanogenmod.filemanager.R;
28 import com.cyanogenmod.filemanager.commands.SyncResultExecutable;
29 import com.cyanogenmod.filemanager.commands.java.Program;
30 import com.cyanogenmod.filemanager.commands.shell.ResolveLinkCommand;
31 import com.cyanogenmod.filemanager.console.CancelledOperationException;
32 import com.cyanogenmod.filemanager.console.Console;
33 import com.cyanogenmod.filemanager.console.ExecutionException;
34 import com.cyanogenmod.filemanager.console.InsufficientPermissionsException;
35 import com.cyanogenmod.filemanager.console.java.JavaConsole;
36 import com.cyanogenmod.filemanager.model.AID;
37 import com.cyanogenmod.filemanager.model.BlockDevice;
38 import com.cyanogenmod.filemanager.model.CharacterDevice;
39 import com.cyanogenmod.filemanager.model.Directory;
40 import com.cyanogenmod.filemanager.model.DomainSocket;
41 import com.cyanogenmod.filemanager.model.FileSystemObject;
42 import com.cyanogenmod.filemanager.model.Group;
43 import com.cyanogenmod.filemanager.model.Identity;
44 import com.cyanogenmod.filemanager.model.NamedPipe;
45 import com.cyanogenmod.filemanager.model.ParentDirectory;
46 import com.cyanogenmod.filemanager.model.Permissions;
47 import com.cyanogenmod.filemanager.model.RegularFile;
48 import com.cyanogenmod.filemanager.model.Symlink;
49 import com.cyanogenmod.filemanager.model.SystemFile;
50 import com.cyanogenmod.filemanager.model.User;
51 import com.cyanogenmod.filemanager.preferences.DisplayRestrictions;
52 import com.cyanogenmod.filemanager.preferences.FileManagerSettings;
53 import com.cyanogenmod.filemanager.preferences.FileTimeFormatMode;
54 import com.cyanogenmod.filemanager.preferences.NavigationSortMode;
55 import com.cyanogenmod.filemanager.preferences.ObjectIdentifier;
56 import com.cyanogenmod.filemanager.preferences.ObjectStringIdentifier;
57 import com.cyanogenmod.filemanager.preferences.Preferences;
58 import com.cyanogenmod.filemanager.util.MimeTypeHelper.MimeTypeCategory;
59
60 import java.io.BufferedInputStream;
61 import java.io.BufferedOutputStream;
62 import java.io.File;
63 import java.io.FileInputStream;
64 import java.io.FileOutputStream;
65 import java.io.IOException;
66 import java.text.DateFormat;
67 import java.text.SimpleDateFormat;
68 import java.util.Collections;
69 import java.util.Comparator;
70 import java.util.Date;
71 import java.util.Iterator;
72 import java.util.List;
73 import java.util.Map;
74 import java.util.UUID;
75
76 /**
77  * A helper class with useful methods for deal with files.
78  */
79 public final class FileHelper {
80
81     private static final String TAG = "FileHelper"; //$NON-NLS-1$
82
83     // Scheme for file and directory picking
84     public static final String FILE_URI_SCHEME = "file"; //$NON-NLS-1$
85     public static final String FOLDER_URI_SCHEME = "folder"; //$NON-NLS-1$
86     public static final String DIRECTORY_URI_SCHEME = "directory"; //$NON-NLS-1$
87
88     /**
89      * Special extension for compressed tar files
90      */
91     private static final String[] COMPRESSED_TAR =
92         {
93          "tar.gz", "tar.bz2", "tar.lzma" //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
94         };
95
96     /**
97      * The root directory.
98      * @hide
99      */
100     public static final String ROOT_DIRECTORY = "/";  //$NON-NLS-1$
101
102     /**
103      * The parent directory string.
104      * @hide
105      */
106     public static final String PARENT_DIRECTORY = "..";  //$NON-NLS-1$
107
108     /**
109      * The current directory string.
110      * @hide
111      */
112     public static final String CURRENT_DIRECTORY = ".";  //$NON-NLS-1$
113
114     /**
115      * The administrator user.
116      * @hide
117      */
118     public static final String USER_ROOT = "root";  //$NON-NLS-1$
119
120     /**
121      * The newline string.
122      * @hide
123      */
124     public static final String NEWLINE = System.getProperty("line.separator"); //$NON-NLS-1$
125
126     // The date/time formats objects
127     /**
128      * @hide
129      */
130     public final static Object DATETIME_SYNC = new Object();
131     /**
132      * @hide
133      */
134     public static boolean sReloadDateTimeFormats = true;
135     private static String sDateTimeFormatOrder = null;
136     private static FileTimeFormatMode sFiletimeFormatMode = null;
137     private static DateFormat sDateFormat = null;
138     private static DateFormat sTimeFormat = null;
139
140     /**
141      * Constructor of <code>FileHelper</code>.
142      */
143     private FileHelper() {
144         super();
145     }
146
147     /**
148      * Method that check if a file is a symbolic link.
149      *
150      * @param file File to check
151      * @return boolean If file is a symbolic link
152      * @throws IOException If real file couldn't be checked
153      */
154     public static boolean isSymlink(File file) throws IOException {
155         return file.getAbsolutePath().compareTo(file.getCanonicalPath()) != 0;
156     }
157
158     /**
159      * Method that resolves a symbolic link to the real file or directory.
160      *
161      * @param file File to check
162      * @return File The real file or directory
163      * @throws IOException If real file couldn't be resolved
164      */
165     public static File resolveSymlink(File file) throws IOException {
166         return file.getCanonicalFile();
167     }
168
169     /**
170      * Method that returns a more human readable of the size
171      * of a file system object.
172      *
173      * @param fso File system object
174      * @return String The human readable size (void if fso don't supports size)
175      */
176     public static String getHumanReadableSize(FileSystemObject fso) {
177         //Only if has size
178         if (fso instanceof Directory) {
179             return ""; //$NON-NLS-1$
180         }
181         if (hasSymlinkRef(fso)) {
182             if (isSymlinkRefDirectory(fso)) {
183                 return ""; //$NON-NLS-1$
184             }
185             return getHumanReadableSize(((Symlink)fso).getLinkRef().getSize());
186         }
187         return getHumanReadableSize(fso.getSize());
188     }
189
190     /**
191      * Method that returns a more human readable of a size in bytes.
192      *
193      * @param size The size in bytes
194      * @return String The human readable size
195      */
196     public static String getHumanReadableSize(long size) {
197         Resources res = FileManagerApplication.getInstance().getResources();
198         final int[] magnitude = {
199                                  R.string.size_bytes,
200                                  R.string.size_kilobytes,
201                                  R.string.size_megabytes,
202                                  R.string.size_gigabytes
203                                 };
204
205         long aux = size;
206         int cc = magnitude.length;
207         for (int i = 0; i < cc; i++) {
208             long s = aux / 1024;
209             if (aux < 1024) {
210                 return Long.toString(aux) + " " + res.getString(magnitude[i]); //$NON-NLS-1$
211             }
212             aux = s;
213         }
214         return Long.toString(aux) + " " + res.getString(magnitude[cc - 1]); //$NON-NLS-1$
215     }
216
217     /**
218      * Method that returns if the file system object if the root directory.
219      *
220      * @param fso The file system object to check
221      * @return boolean if the file system object if the root directory
222      */
223     public static boolean isRootDirectory(FileSystemObject fso) {
224         if (fso.getName() == null) return true;
225         return fso.getName().compareTo(FileHelper.ROOT_DIRECTORY) == 0;
226     }
227
228     /**
229      * Method that returns if the folder if the root directory.
230      *
231      * @param folder The folder
232      * @return boolean if the folder if the root directory
233      */
234     public static boolean isRootDirectory(String folder) {
235         if (folder == null) return true;
236         return isRootDirectory(new File(folder));
237     }
238
239     /**
240      * Method that returns if the folder if the root directory.
241      *
242      * @param folder The folder
243      * @return boolean if the folder if the root directory
244      */
245     public static boolean isRootDirectory(File folder) {
246         if (folder.getPath() == null) return true;
247         return folder.getPath().compareTo(FileHelper.ROOT_DIRECTORY) == 0;
248     }
249
250     /**
251      * Method that returns if the parent file system object if the root directory.
252      *
253      * @param fso The parent file system object to check
254      * @return boolean if the parent file system object if the root directory
255      */
256     public static boolean isParentRootDirectory(FileSystemObject fso) {
257         if (fso.getParent() == null) return true;
258         return fso.getParent().compareTo(FileHelper.ROOT_DIRECTORY) == 0;
259     }
260
261     /**
262      * Method that returns the name without the extension of a file system object.
263      *
264      * @param fso The file system object
265      * @return The name without the extension of the file system object.
266      */
267     public static String getName(FileSystemObject fso) {
268         return getName(fso.getName());
269     }
270
271     /**
272      * Method that returns the name without the extension of a file system object.
273      *
274      * @param name The name of file system object
275      * @return The name without the extension of the file system object.
276      */
277     public static String getName(String name) {
278        String ext = getExtension(name);
279        if (ext == null) return name;
280        return name.substring(0, name.length() - ext.length() - 1);
281     }
282
283     /**
284      * Method that returns the extension of a file system object.
285      *
286      * @param fso The file system object
287      * @return The extension of the file system object, or <code>null</code>
288      * if <code>fso</code> has no extension.
289      */
290     public static String getExtension(FileSystemObject fso) {
291         return getExtension(fso.getName());
292     }
293
294     /**
295      * Method that returns the extension of a file system object.
296      *
297      * @param name The name of file system object
298      * @return The extension of the file system object, or <code>null</code>
299      * if <code>fso</code> has no extension.
300      */
301     public static String getExtension(String name) {
302         final char dot = '.';
303         int pos = name.lastIndexOf(dot);
304         if (pos == -1 || pos == 0) { // Hidden files doesn't have extensions
305             return null;
306         }
307
308         // Exceptions to the general extraction method
309         int cc = COMPRESSED_TAR.length;
310         for (int i = 0; i < cc; i++) {
311             if (name.endsWith("." + COMPRESSED_TAR[i])) { //$NON-NLS-1$
312                 return COMPRESSED_TAR[i];
313             }
314         }
315
316         // General extraction method
317         return name.substring(pos + 1);
318     }
319
320     /**
321      * Method that returns the parent directory of a file/folder
322      *
323      * @param path The file/folder
324      * @return String The parent directory
325      */
326     public static String getParentDir(String path) {
327         return getParentDir(new File(path));
328     }
329
330     /**
331      * Method that returns the parent directory of a file/folder
332      *
333      * @param path The file/folder
334      * @return String The parent directory
335      */
336     public static String getParentDir(File path) {
337         String parent = path.getParent();
338         if (parent == null && path.getAbsolutePath().compareTo(FileHelper.ROOT_DIRECTORY) != 0) {
339             parent = FileHelper.ROOT_DIRECTORY;
340         }
341         return parent;
342     }
343
344     /**
345      * Method that evaluates if a path is relative.
346      *
347      * @param src The path to check
348      * @return boolean If a path is relative
349      */
350     public static boolean isRelativePath(String src) {
351         if (src.startsWith(CURRENT_DIRECTORY + File.separator)) {
352             return true;
353         }
354         if (src.startsWith(PARENT_DIRECTORY + File.separator)) {
355             return true;
356         }
357         if (src.indexOf(File.separator + CURRENT_DIRECTORY + File.separator) != -1) {
358             return true;
359         }
360         if (src.indexOf(File.separator + PARENT_DIRECTORY + File.separator) != -1) {
361             return true;
362         }
363         if (!src.startsWith(ROOT_DIRECTORY)) {
364             return true;
365         }
366         return false;
367     }
368
369     /**
370      * Method that check if the file system object is a {@link Symlink} and
371      * has a link reference.
372      *
373      * @param fso The file system object to check
374      * @return boolean If file system object the has a link reference
375      */
376     public static boolean hasSymlinkRef(FileSystemObject fso) {
377         if (fso instanceof Symlink) {
378             return ((Symlink)fso).getLinkRef() != null;
379         }
380         return false;
381     }
382
383     /**
384      * Method that check if the file system object is a {@link Symlink} and
385      * the link reference is a directory.
386      *
387      * @param fso The file system object to check
388      * @return boolean If file system object the link reference is a directory
389      */
390     public static boolean isSymlinkRefDirectory(FileSystemObject fso) {
391         if (!hasSymlinkRef(fso)) {
392             return false;
393         }
394         return ((Symlink)fso).getLinkRef() instanceof Directory;
395     }
396
397     /**
398      * Method that check if the file system object is a {@link Symlink} and
399      * the link reference is a system file.
400      *
401      * @param fso The file system object to check
402      * @return boolean If file system object the link reference is a system file
403      */
404     public static boolean isSymlinkRefSystemFile(FileSystemObject fso) {
405         if (!hasSymlinkRef(fso)) {
406             return false;
407         }
408         return ((Symlink)fso).getLinkRef() instanceof SystemFile;
409     }
410
411     /**
412      * Method that check if the file system object is a {@link Symlink} and
413      * the link reference is a block device.
414      *
415      * @param fso The file system object to check
416      * @return boolean If file system object the link reference is a block device
417      */
418     public static boolean isSymlinkRefBlockDevice(FileSystemObject fso) {
419         if (!hasSymlinkRef(fso)) {
420             return false;
421         }
422         return ((Symlink)fso).getLinkRef() instanceof BlockDevice;
423     }
424
425     /**
426      * Method that check if the file system object is a {@link Symlink} and
427      * the link reference is a character device.
428      *
429      * @param fso The file system object to check
430      * @return boolean If file system object the link reference is a character device
431      */
432     public static boolean isSymlinkRefCharacterDevice(FileSystemObject fso) {
433         if (!hasSymlinkRef(fso)) {
434             return false;
435         }
436         return ((Symlink)fso).getLinkRef() instanceof CharacterDevice;
437     }
438
439     /**
440      * Method that check if the file system object is a {@link Symlink} and
441      * the link reference is a named pipe.
442      *
443      * @param fso The file system object to check
444      * @return boolean If file system object the link reference is a named pipe
445      */
446     public static boolean isSymlinkRefNamedPipe(FileSystemObject fso) {
447         if (!hasSymlinkRef(fso)) {
448             return false;
449         }
450         return ((Symlink)fso).getLinkRef() instanceof NamedPipe;
451     }
452
453     /**
454      * Method that check if the file system object is a {@link Symlink} and
455      * the link reference is a domain socket.
456      *
457      * @param fso The file system object to check
458      * @return boolean If file system object the link reference is a domain socket
459      */
460     public static boolean isSymlinkRefDomainSocket(FileSystemObject fso) {
461         if (!hasSymlinkRef(fso)) {
462             return false;
463         }
464         return ((Symlink)fso).getLinkRef() instanceof DomainSocket;
465     }
466
467     /**
468      * Method that checks if a file system object is a directory (real o symlink).
469      *
470      * @param fso The file system object to check
471      * @return boolean If file system object is a directory
472      */
473     public static boolean isDirectory(FileSystemObject fso) {
474         if (fso instanceof Directory) {
475             return true;
476         }
477         if (isSymlinkRefDirectory(fso)) {
478             return true;
479         }
480         return false;
481     }
482
483     /**
484      * Method that checks if a file system object is a system file (real o symlink).
485      *
486      * @param fso The file system object to check
487      * @return boolean If file system object is a system file
488      */
489     public static boolean isSystemFile(FileSystemObject fso) {
490         if (fso instanceof SystemFile) {
491             return true;
492         }
493         if (isSymlinkRefSystemFile(fso)) {
494             return true;
495         }
496         return false;
497     }
498
499     /**
500      * Method that returns the real reference of a file system object
501      * (the reference file system object if the file system object is a symlink.
502      * Otherwise the same reference).
503      *
504      * @param fso The file system object to check
505      * @return FileSystemObject The real file system object reference
506      */
507     public static FileSystemObject getReference(FileSystemObject fso) {
508         if (hasSymlinkRef(fso)) {
509             return ((Symlink)fso).getLinkRef();
510         }
511         return fso;
512     }
513
514     /**
515      * Method that applies the configuration modes to the listed files
516      * (sort mode, hidden files, ...).
517      *
518      * @param files The listed files
519      * @param restrictions The restrictions to apply when displaying files
520      * @param chRooted If app run with no privileges
521      * @return List<FileSystemObject> The applied mode listed files
522      */
523     public static List<FileSystemObject> applyUserPreferences(
524                     List<FileSystemObject> files, Map<DisplayRestrictions,
525                     Object> restrictions, boolean chRooted) {
526         return applyUserPreferences(files, restrictions, false, chRooted);
527     }
528
529     /**
530      * Method that applies the configuration modes to the listed files
531      * (sort mode, hidden files, ...).
532      *
533      * @param files The listed files
534      * @param restrictions The restrictions to apply when displaying files
535      * @param noSort If sort must be applied
536      * @param chRooted If app run with no privileges
537      * @return List<FileSystemObject> The applied mode listed files
538      */
539     public static List<FileSystemObject> applyUserPreferences(
540             List<FileSystemObject> files, Map<DisplayRestrictions, Object> restrictions,
541             boolean noSort, boolean chRooted) {
542         //Retrieve user preferences
543         SharedPreferences prefs = Preferences.getSharedPreferences();
544         FileManagerSettings sortModePref = FileManagerSettings.SETTINGS_SORT_MODE;
545         FileManagerSettings showDirsFirstPref = FileManagerSettings.SETTINGS_SHOW_DIRS_FIRST;
546         FileManagerSettings showHiddenPref = FileManagerSettings.SETTINGS_SHOW_HIDDEN;
547         FileManagerSettings showSystemPref = FileManagerSettings.SETTINGS_SHOW_SYSTEM;
548         FileManagerSettings showSymlinksPref = FileManagerSettings.SETTINGS_SHOW_SYMLINKS;
549
550         //Remove all unnecessary files (no required by the user)
551         int cc = files.size();
552         for (int i = cc - 1; i >= 0; i--) {
553             FileSystemObject file = files.get(i);
554
555             //Hidden files
556             if (!prefs.getBoolean(
557                     showHiddenPref.getId(),
558                     ((Boolean)showHiddenPref.getDefaultValue()).booleanValue()) || chRooted) {
559                 if (file.isHidden()) {
560                     files.remove(i);
561                     continue;
562                 }
563             }
564
565             //System files
566             if (!prefs.getBoolean(
567                     showSystemPref.getId(),
568                     ((Boolean)showSystemPref.getDefaultValue()).booleanValue()) || chRooted) {
569                 if (file instanceof SystemFile) {
570                     files.remove(i);
571                     continue;
572                 }
573             }
574
575             //Symlinks files
576             if (!prefs.getBoolean(
577                     showSymlinksPref.getId(),
578                     ((Boolean)showSymlinksPref.getDefaultValue()).booleanValue()) || chRooted) {
579                 if (file instanceof Symlink) {
580                     files.remove(i);
581                     continue;
582                 }
583             }
584
585             // Restrictions (only apply to files)
586             if (restrictions != null) {
587                 if (!isDirectory(file)) {
588                     if (!isDisplayAllowed(file, restrictions)) {
589                         files.remove(i);
590                         continue;
591                     }
592                 }
593             }
594         }
595
596         //Apply sort mode
597         if (!noSort) {
598             final boolean showDirsFirst =
599                     prefs.getBoolean(
600                             showDirsFirstPref.getId(),
601                         ((Boolean)showDirsFirstPref.getDefaultValue()).booleanValue());
602             final NavigationSortMode sortMode =
603                     NavigationSortMode.fromId(
604                             prefs.getInt(sortModePref.getId(),
605                             ((ObjectIdentifier)sortModePref.getDefaultValue()).getId()));
606             Collections.sort(files, new Comparator<FileSystemObject>() {
607                 @Override
608                 public int compare(FileSystemObject lhs, FileSystemObject rhs) {
609                     //Parent directory always goes first
610                     boolean isLhsParentDirectory = lhs instanceof ParentDirectory;
611                     boolean isRhsParentDirectory = rhs instanceof ParentDirectory;
612                     if (isLhsParentDirectory || isRhsParentDirectory) {
613                         if (isLhsParentDirectory && isRhsParentDirectory) {
614                             return 0;
615                         }
616                         return (isLhsParentDirectory) ? -1 : 1;
617                     }
618
619                     //Need to sort directory first?
620                     if (showDirsFirst) {
621                         boolean isLhsDirectory = FileHelper.isDirectory(lhs);
622                         boolean isRhsDirectory = FileHelper.isDirectory(rhs);
623                         if (isLhsDirectory || isRhsDirectory) {
624                             if (isLhsDirectory && isRhsDirectory) {
625                                 //Apply sort mode
626                                 return FileHelper.doCompare(lhs, rhs, sortMode);
627                             }
628                             return (isLhsDirectory) ? -1 : 1;
629                         }
630                     }
631
632                     //Apply sort mode
633                     return FileHelper.doCompare(lhs, rhs, sortMode);
634                 }
635
636             });
637         }
638
639         //Return the files
640         return files;
641     }
642
643     /**
644      * Determines if a file system object complies w/ a user's display preferences implying that
645      * the user is interested in this file
646      * (sort mode, hidden files, ...).
647      *
648      * @param fso The file
649      * @param restrictions The restrictions to apply when displaying files
650      * @param chRooted If app run with no privileges
651      * @return boolean indicating user's interest
652      */
653     public static boolean compliesWithDisplayPreferences(
654             FileSystemObject fso, Map<DisplayRestrictions, Object> restrictions, boolean chRooted) {
655         //Retrieve user preferences
656         SharedPreferences prefs = Preferences.getSharedPreferences();
657         FileManagerSettings showHiddenPref = FileManagerSettings.SETTINGS_SHOW_HIDDEN;
658         FileManagerSettings showSystemPref = FileManagerSettings.SETTINGS_SHOW_SYSTEM;
659         FileManagerSettings showSymlinksPref = FileManagerSettings.SETTINGS_SHOW_SYMLINKS;
660
661         //Hidden files
662         if (!prefs.getBoolean(
663                 showHiddenPref.getId(),
664                 ((Boolean)showHiddenPref.getDefaultValue()).booleanValue()) || chRooted) {
665             if (fso.isHidden()) {
666                 return false;
667             }
668         }
669
670         //System files
671         if (!prefs.getBoolean(
672                 showSystemPref.getId(),
673                 ((Boolean)showSystemPref.getDefaultValue()).booleanValue()) || chRooted) {
674             if (fso instanceof SystemFile) {
675                 return false;
676             }
677         }
678
679         //Symlinks files
680         if (!prefs.getBoolean(
681                 showSymlinksPref.getId(),
682                 ((Boolean)showSymlinksPref.getDefaultValue()).booleanValue()) || chRooted) {
683             if (fso instanceof Symlink) {
684                 return false;
685             }
686         }
687
688         // Restrictions (only apply to files)
689         if (restrictions != null) {
690             if (!isDirectory(fso)) {
691                 if (!isDisplayAllowed(fso, restrictions)) {
692                     return false;
693                 }
694             }
695         }
696
697         // all checks passed
698         return true;
699     }
700
701     /**
702      * Method that check if a file should be displayed according to the restrictions
703      *
704      * @param fso The file system object to check
705      * @param restrictions The restrictions map
706      * @return boolean If the file should be displayed
707      */
708     private static boolean isDisplayAllowed(
709             FileSystemObject fso, Map<DisplayRestrictions, Object> restrictions) {
710         Iterator<DisplayRestrictions> it = restrictions.keySet().iterator();
711         while (it.hasNext()) {
712             DisplayRestrictions restriction = it.next();
713             Object value = restrictions.get(restriction);
714             if (value == null) {
715                 continue;
716             }
717             switch (restriction) {
718                 case CATEGORY_TYPE_RESTRICTION:
719                     if (value instanceof MimeTypeCategory) {
720                         MimeTypeCategory cat1 = (MimeTypeCategory)value;
721                         // NOTE: We don't need the context here, because mime-type
722                         // database should be loaded prior to this call
723                         MimeTypeCategory cat2 = MimeTypeHelper.getCategory(null, fso);
724                         if (cat1.compareTo(cat2) != 0) {
725                             return false;
726                         }
727                     }
728                     break;
729
730                 case MIME_TYPE_RESTRICTION:
731                     String[] mimeTypes = null;
732                     if (value instanceof String) {
733                         mimeTypes = new String[] {(String) value};
734                     } else if (value instanceof String[]) {
735                         mimeTypes = (String[]) value;
736                     }
737                     if (mimeTypes != null) {
738                         boolean matches = false;
739                         for (String mimeType : mimeTypes) {
740                             if (mimeType.compareTo(MimeTypeHelper.ALL_MIME_TYPES) == 0) {
741                                 matches = true;
742                                 break;
743                             }
744                             // NOTE: We don't need the context here, because mime-type
745                             // database should be loaded prior to this call
746                             if (MimeTypeHelper.matchesMimeType(null, fso, mimeType)) {
747                                 matches = true;
748                                 break;
749                             }
750                         }
751                         if (!matches) {
752                             return false;
753                         }
754                     }
755                     break;
756
757                 case SIZE_RESTRICTION:
758                     if (value instanceof Long) {
759                         Long maxSize = (Long)value;
760                         if (fso.getSize() > maxSize.longValue()) {
761                             return false;
762                         }
763                     }
764                     break;
765
766                 case DIRECTORY_ONLY_RESTRICTION:
767                     if (value instanceof Boolean) {
768                         Boolean directoryOnly = (Boolean) value;
769                         if (directoryOnly.booleanValue() && !FileHelper.isDirectory(fso)) {
770                             return false;
771                         }
772                     }
773                     break;
774
775                 case LOCAL_FILESYSTEM_ONLY_RESTRICTION:
776                     if (value instanceof Boolean) {
777                         Boolean localOnly = (Boolean)value;
778                         if (localOnly.booleanValue()) {
779                             /** TODO Needed when CMFM gets networking **/
780                         }
781                     }
782                     break;
783
784                 default:
785                     break;
786             }
787         }
788         return true;
789     }
790
791     /**
792      * Method that resolve the symbolic links of the list of files passed as argument.<br />
793      * This method invokes the {@link ResolveLinkCommand} in those files that have a valid
794      * symlink reference
795      *
796      * @param context The current context
797      * @param files The listed files
798      */
799     public static void resolveSymlinks(Context context, List<FileSystemObject> files) {
800         int cc = files.size();
801         for (int i = 0; i < cc; i++) {
802             FileSystemObject fso = files.get(i);
803             resolveSymlink(context, fso);
804         }
805     }
806
807     /**
808      * Method that resolves the symbolic link of a file passed in as argument.<br />
809      * This method invokes the {@link ResolveLinkCommand} on the file that has a valid
810      * symlink reference
811      *
812      * @param context The current context
813      * @param fso FileSystemObject to resolve symlink
814      */
815     public static void resolveSymlink(Context context, FileSystemObject fso) {
816         if (fso instanceof Symlink && ((Symlink)fso).getLinkRef() == null) {
817             try {
818                 FileSystemObject symlink =
819                         CommandHelper.resolveSymlink(context, fso.getFullPath(), null);
820                 ((Symlink)fso).setLinkRef(symlink);
821             } catch (Throwable ex) {/**NON BLOCK**/}
822         }
823     }
824
825     /**
826      * Method that do a comparison between 2 file system objects.
827      *
828      * @param fso1 The first file system objects
829      * @param fso2 The second file system objects
830      * @param mode The sort mode
831      * @return int a negative integer if {@code fso1} is less than {@code fso2};
832      *         a positive integer if {@code fso1} is greater than {@code fso2};
833      *         0 if {@code fso1} has the same order as {@code fso2}.
834      */
835     public static int doCompare(
836             final FileSystemObject fso1,
837             final FileSystemObject fso2,
838             final NavigationSortMode mode) {
839
840         // Retrieve the user preference for case sensitive sort
841         boolean caseSensitive =
842                 Preferences.getSharedPreferences().
843                     getBoolean(
844                         FileManagerSettings.SETTINGS_CASE_SENSITIVE_SORT.getId(),
845                         ((Boolean)FileManagerSettings.SETTINGS_CASE_SENSITIVE_SORT.
846                                 getDefaultValue()).booleanValue());
847
848         //Name (ascending)
849         if (mode.getId() == NavigationSortMode.NAME_ASC.getId()) {
850             if (!caseSensitive) {
851                 return fso1.getName().compareToIgnoreCase(fso2.getName());
852             }
853             return fso1.getName().compareTo(fso2.getName());
854         }
855         //Name (descending)
856         if (mode.getId() == NavigationSortMode.NAME_DESC.getId()) {
857             if (!caseSensitive) {
858                 return fso1.getName().compareToIgnoreCase(fso2.getName()) * -1;
859             }
860             return fso1.getName().compareTo(fso2.getName()) * -1;
861         }
862
863         //Date (ascending)
864         if (mode.getId() == NavigationSortMode.DATE_ASC.getId()) {
865             return fso1.getLastModifiedTime().compareTo(fso2.getLastModifiedTime());
866         }
867         //Date (descending)
868         if (mode.getId() == NavigationSortMode.DATE_DESC.getId()) {
869             return fso1.getLastModifiedTime().compareTo(fso2.getLastModifiedTime()) * -1;
870         }
871
872         //Size (ascending)
873         if (mode.getId() == NavigationSortMode.SIZE_ASC.getId()) {
874             return Long.compare(fso1.getSize(), fso2.getSize());
875         }
876         //Size (descending)
877         if (mode.getId() == NavigationSortMode.SIZE_DESC.getId()) {
878             return Long.compare(fso1.getSize(), fso2.getSize()) * -1;
879         }
880
881         //Type (ascending)
882         if (mode.getId() == NavigationSortMode.TYPE_ASC.getId()) {
883             // Shouldn't need context here, mimetypes should be loaded
884             return MimeTypeHelper.compareFSO(null, fso1, fso2);
885         }
886         //Type (descending)
887         if (mode.getId() == NavigationSortMode.TYPE_DESC.getId()) {
888             // Shouldn't need context here, mimetypes should be loaded
889             return MimeTypeHelper.compareFSO(null, fso1, fso2) * -1;
890         }
891
892         //Comparison between files directly
893         return fso1.compareTo(fso2);
894     }
895
896     /**
897      * Method that add to the path the trailing slash
898      *
899      * @param path The path
900      * @return String The path with the trailing slash
901      */
902     public static String addTrailingSlash(String path) {
903         if (path == null) return null;
904         return path.endsWith(File.separator) ? path : path + File.separator;
905     }
906
907     /**
908      * Method that cleans the path and removes the trailing slash
909      *
910      * @param path The path to clean
911      * @return String The path without the trailing slash
912      */
913     public static String removeTrailingSlash(String path) {
914         if (path == null) return null;
915         if (path.trim().compareTo(ROOT_DIRECTORY) == 0) return path;
916         if (path.endsWith(File.separator)) {
917             return path.substring(0, path.length()-1);
918         }
919         return path;
920     }
921
922     /**
923      * Method that creates a new name based on the name of the {@link FileSystemObject}
924      * that is not current used by the filesystem.
925      *
926      * @param ctx The current context
927      * @param files The list of files of the current directory
928      * @param attemptedName The attempted name
929      * @param regexp The resource of the regular expression to create the new name
930      * @return String The new non-existing name
931      */
932     public static String createNonExistingName(
933             final Context ctx, final List<FileSystemObject> files,
934             final String attemptedName, int regexp) {
935         // Find a non-exiting name
936         String newName = attemptedName;
937         if (!isNameExists(files, newName)) return newName;
938         do {
939             String name  = FileHelper.getName(newName);
940             String ext  = FileHelper.getExtension(newName);
941             if (ext == null) {
942                 ext = ""; //$NON-NLS-1$
943             } else {
944                 ext = "." + ext; //$NON-NLS-1$
945             }
946             newName = ctx.getString(regexp, name, ext);
947         } while (isNameExists(files, newName));
948         return newName;
949     }
950
951     /**
952      * Method that checks if a name exists in the current directory.
953      *
954      * @param files The list of files of the current directory
955      * @param name The name to check
956      * @return boolean Indicate if the name exists in the current directory
957      */
958     public static boolean isNameExists(List<FileSystemObject> files, String name) {
959         //Verify if the name exists in the current file list
960         int cc = files.size();
961         for (int i = 0; i < cc; i++) {
962             FileSystemObject fso = files.get(i);
963             if (fso.getName().compareTo(name) == 0) {
964                 return true;
965             }
966         }
967         return false;
968     }
969
970     /**
971      * Method that returns is a {@link FileSystemObject} can be handled by this application
972      * allowing the uncompression of the file
973      *
974      * @param fso The file system object to verify
975      * @return boolean If the file is supported
976      */
977     @SuppressWarnings("nls")
978     public static boolean isSupportedUncompressedFile(FileSystemObject fso) {
979         // Valid uncompressed formats are:
980         final String[] VALID =
981                 {
982                     "tar", "tgz", "tar.gz", "tar.bz2", "tar.lzma",
983                     "zip", "gz", "bz2", "lzma", "xz", "Z", "rar"
984                 };
985         // Null values for required commands
986         final String[] OPT_KEYS =
987                 {
988                     null, null, null, null, null,
989                     "unzip", null, null, "unlzma", "unxz", "uncompress", "unrar"
990                 };
991
992         // Check that have a valid file
993         if (fso == null) return false;
994
995         // Only regular files
996         if (isDirectory(fso) || fso instanceof Symlink) {
997             return false;
998         }
999         // No in virtual filesystems
1000         if (fso.isSecure() || fso.isRemote()) {
1001             return false;
1002         }
1003         String ext = getExtension(fso);
1004         if (ext != null) {
1005             int cc = VALID.length;
1006             for (int i = 0; i < cc; i++) {
1007                 if (VALID[i].compareToIgnoreCase(ext) == 0) {
1008                     // Is the command present
1009                     if (OPT_KEYS[i] != null &&
1010                         FileManagerApplication.hasOptionalCommand(OPT_KEYS[i])) {
1011                         return true;
1012                     }
1013                     return false;
1014                 }
1015             }
1016         }
1017         return false;
1018     }
1019
1020     /**
1021      * Method that converts an absolute path to a relative path
1022      *
1023      * @param path The absolute path to convert
1024      * @param relativeTo The absolute path from which make path relative to (a folder)
1025      * @return String The relative path
1026      */
1027     public static String toRelativePath(String path, String relativeTo) {
1028         // Normalize the paths
1029         File f1 = new File(path);
1030         File f2 = new File(relativeTo);
1031         String s1 = f1.getAbsolutePath();
1032         String s2 = f2.getAbsolutePath();
1033         if (!s2.endsWith(File.separator)) {
1034             s2 = s2 + File.separator;
1035         }
1036
1037         // If s2 contains s1 then the relative is replace of the start of the path
1038         if (s1.startsWith(s2)) {
1039             return s1.substring(s2.length());
1040         }
1041
1042         StringBuffer relative = new StringBuffer();
1043         do {
1044             File f3 = new File(s2);
1045             relative.append(String.format("..%s", File.separator)); //$NON-NLS-1$
1046             s2 = f3.getParent() + File.separator;
1047         } while (!s1.startsWith(s2) && !s1.startsWith(new File(s2).getAbsolutePath()));
1048         s2 = new File(s2).getAbsolutePath();
1049         return relative.toString() + s1.substring(s2.length());
1050     }
1051
1052     /**
1053      * Method that creates a {@link FileSystemObject} from a {@link File}
1054      *
1055      * @param file The file or folder reference
1056      * @return FileSystemObject The file system object reference
1057      */
1058     public static FileSystemObject createFileSystemObject(File file) {
1059         try {
1060             // The user and group name of the files. Use the defaults one for sdcards
1061             final String USER = "root"; //$NON-NLS-1$
1062             final String GROUP = "sdcard_r"; //$NON-NLS-1$
1063
1064             // The user and group name of the files. In ChRoot, aosp give restrict access to
1065             // this user and group. This applies for permission also. This has no really much
1066             // interest if we not allow to change the permissions
1067             AID userAID = AIDHelper.getAIDFromName(USER);
1068             AID groupAID = AIDHelper.getAIDFromName(GROUP);
1069             User user = new User(userAID.getId(), userAID.getName());
1070             Group group = new Group(groupAID.getId(), groupAID.getName());
1071             Permissions perm = file.isDirectory()
1072                     ? Permissions.createDefaultFolderPermissions()
1073                     : Permissions.createDefaultFilePermissions();
1074
1075             // Build a directory?
1076             Date lastModified = new Date(file.lastModified());
1077             if (file.isDirectory()) {
1078                 return
1079                     new Directory(
1080                             file.getName(),
1081                             file.getParent(),
1082                             user, group, perm,
1083                             lastModified, lastModified, lastModified); // The only date we have
1084             }
1085
1086             // Build a regular file
1087             return
1088                 new RegularFile(
1089                         file.getName(),
1090                         file.getParent(),
1091                         user, group, perm,
1092                         file.length(),
1093                         lastModified, lastModified, lastModified); // The only date we have
1094         } catch (Exception e) {
1095             Log.e(TAG, "Exception retrieving the fso", e); //$NON-NLS-1$
1096         }
1097         return null;
1098     }
1099
1100     /**
1101      * Method that copies recursively to the destination
1102      *
1103      * @param src The source file or folder
1104      * @param dst The destination file or folder
1105      * @param bufferSize The buffer size for the operation
1106      * @return boolean If the operation complete successfully
1107      * @throws ExecutionException If a problem was detected in the operation
1108      */
1109     public static boolean copyRecursive(
1110             final File src, final File dst, int bufferSize, Program program)
1111                 throws ExecutionException, CancelledOperationException {
1112         if (src.isDirectory()) {
1113             // Create the directory
1114             if (dst.exists() && !dst.isDirectory()) {
1115                 Log.e(TAG,
1116                         String.format("Failed to check destionation dir: %s", dst)); //$NON-NLS-1$
1117                 throw new ExecutionException("the path exists but is not a folder"); //$NON-NLS-1$
1118             }
1119             if (!dst.exists()) {
1120                 if (!dst.mkdir()) {
1121                     Log.e(TAG, String.format("Failed to create directory: %s", dst)); //$NON-NLS-1$
1122                     return false;
1123                 }
1124             }
1125             File[] files = src.listFiles();
1126             if (files != null) {
1127                 for (int i = 0; i < files.length; i++) {
1128                     // Short circuit if we've been cancelled. Show's over :(
1129                     if (program.isCancelled()) {
1130                         throw new CancelledOperationException();
1131                     }
1132
1133                     if (!copyRecursive(files[i], new File(dst, files[i].getName()), bufferSize,
1134                                        program)) {
1135                         return false;
1136                     }
1137                 }
1138             }
1139         } else {
1140             // Copy the directory
1141             if (!bufferedCopy(src, dst,bufferSize, program)) {
1142                 return false;
1143             }
1144         }
1145         return true;
1146     }
1147
1148     /**
1149      * Method that copies a file
1150      *
1151      * @param src The source file
1152      * @param dst The destination file
1153      * @param bufferSize The buffer size for the operation
1154      * @return boolean If the operation complete successfully
1155      */
1156     public static boolean bufferedCopy(final File src, final File dst,
1157         int bufferSize, Program program)
1158             throws ExecutionException, CancelledOperationException {
1159         BufferedInputStream bis = null;
1160         BufferedOutputStream bos = null;
1161         try {
1162             bis = new BufferedInputStream(new FileInputStream(src), bufferSize);
1163             bos = new BufferedOutputStream(new FileOutputStream(dst), bufferSize);
1164             int read = 0;
1165             byte[] data = new byte[bufferSize];
1166             while ((read = bis.read(data, 0, bufferSize)) != -1) {
1167                 // Short circuit if we've been cancelled. Show's over :(
1168                 if (program.isCancelled()) {
1169                     throw new CancelledOperationException();
1170                 }
1171                 bos.write(data, 0, read);
1172             }
1173             return true;
1174
1175         } catch (Throwable e) {
1176             Log.e(TAG,
1177                     String.format(TAG, "Failed to copy from %s to %d", src, dst), e); //$NON-NLS-1$
1178
1179             // Check if this error is an out of space exception and throw that specifically.
1180             // ENOSPC -> Error No Space
1181             if (e.getCause() instanceof ErrnoException
1182                         && ((ErrnoException)e.getCause()).errno == OsConstants.ENOSPC) {
1183                 throw new ExecutionException(R.string.msgs_no_disk_space);
1184             } if (e instanceof CancelledOperationException) {
1185                 // If the user cancelled this operation, let it through.
1186                 throw (CancelledOperationException)e;
1187             }
1188
1189             return false;
1190         } finally {
1191             try {
1192                 if (bis != null) {
1193                     bis.close();
1194                 }
1195             } catch (Throwable e) {/**NON BLOCK**/}
1196             try {
1197                 if (bos != null) {
1198                     bos.close();
1199                 }
1200             } catch (Throwable e) {/**NON BLOCK**/}
1201             if (program.isCancelled()) {
1202                 if (!dst.delete()) {
1203                     Log.e(TAG, "Failed to delete the dest file: " + dst);
1204                 }
1205             }
1206         }
1207     }
1208
1209     /**
1210      * Method that deletes a folder recursively
1211      *
1212      * @param folder The folder to delete
1213      * @return boolean If the folder was deleted
1214      */
1215     public static boolean deleteFolder(File folder) {
1216         File[] files = folder.listFiles();
1217         if (files != null) {
1218             for (int i = 0; i < files.length; i++) {
1219                 if (files[i].isDirectory()) {
1220                     if (!deleteFolder(files[i])) {
1221                         return false;
1222                     }
1223                 } else {
1224                     if (!files[i].delete()) {
1225                         return false;
1226                     }
1227                 }
1228             }
1229         }
1230         return folder.delete();
1231     }
1232
1233     /**
1234      * Method that returns the canonical/absolute path of the path.<br/>
1235      * This method performs path resolution
1236      *
1237      * @param path The path to convert
1238      * @return String The canonical/absolute path
1239      */
1240     public static String getAbsPath(String path) {
1241         try {
1242             return new File(path).getCanonicalPath();
1243         } catch (Exception e) {
1244             return new File(path).getAbsolutePath();
1245         }
1246     }
1247
1248     /**
1249      * Method that returns the .nomedia file
1250      *
1251      * @param fso The folder that contains the .nomedia file
1252      * @return File The .nomedia file
1253      */
1254     public static File getNoMediaFile(FileSystemObject fso) {
1255         File file = null;
1256         try {
1257             file = new File(fso.getFullPath()).getCanonicalFile();
1258         } catch (Exception e) {
1259             file = new File(fso.getFullPath()).getAbsoluteFile();
1260         }
1261         return new File(file, ".nomedia").getAbsoluteFile(); //$NON-NLS-1$
1262     }
1263
1264     /**
1265      * Method that ensures that the actual console has access to read the
1266      * {@link FileSystemObject} passed.
1267      *
1268      * @param console The console
1269      * @param fso The {@link FileSystemObject} to check
1270      * @param executable The executable to associate to the {@link InsufficientPermissionsException}
1271      * @throws InsufficientPermissionsException If the console doesn't have enough rights
1272      */
1273     public static void ensureReadAccess(
1274             Console console, FileSystemObject fso, SyncResultExecutable executable)
1275             throws InsufficientPermissionsException {
1276         try {
1277             if (console.isPrivileged()) {
1278                 // Should have access
1279                 return;
1280             }
1281             if (console instanceof JavaConsole &&
1282                     StorageHelper.isPathInStorageVolume(fso.getFullPath())) {
1283                 // Java console runs in chrooted environment, and sdcard are always readable
1284                 return;
1285             }
1286             Identity identity = console.getIdentity();
1287             if (identity == null) {
1288                 throw new InsufficientPermissionsException(executable);
1289             }
1290             Permissions permissions = fso.getPermissions();
1291             User user = fso.getUser();
1292             Group group = fso.getGroup();
1293             List<Group> groups = identity.getGroups();
1294             if ( permissions == null || user == null || group == null) {
1295                 throw new InsufficientPermissionsException(executable);
1296             }
1297             // Check others
1298             if (permissions.getOthers().isRead()) {
1299                 return;
1300             }
1301             // Check user
1302             if (user.getId() == identity.getUser().getId() && permissions.getUser().isRead()) {
1303                 return;
1304             }
1305             // Check group
1306             if (group.getId() == identity.getGroup().getId() && permissions.getGroup().isRead()) {
1307                 return;
1308             }
1309             // Check groups
1310             int cc = groups.size();
1311             for (int i = 0; i < cc; i++) {
1312                 Group g = groups.get(i);
1313                 if (group.getId() == g.getId() && permissions.getGroup().isRead()) {
1314                     return;
1315                 }
1316             }
1317
1318         } catch (Exception e) {
1319             Log.e(TAG, "Failed to check fso read permission,", e); //$NON-NLS-1$
1320         }
1321         throw new InsufficientPermissionsException(executable);
1322     }
1323
1324     /**
1325      * Method that ensures that the actual console has access to write the
1326      * {@link FileSystemObject} passed.
1327      *
1328      * @param console The console
1329      * @param fso The {@link FileSystemObject} to check
1330      * @param executable The executable to associate to the {@link InsufficientPermissionsException}
1331      * @throws InsufficientPermissionsException If the console doesn't have enough rights
1332      */
1333     public static void ensureWriteAccess(
1334             Console console, FileSystemObject fso, SyncResultExecutable executable)
1335             throws InsufficientPermissionsException {
1336         try {
1337             if (console.isPrivileged()) {
1338                 // Should have access
1339                 return;
1340             }
1341             if (console instanceof JavaConsole &&
1342                     StorageHelper.isPathInStorageVolume(fso.getFullPath())) {
1343                 // Java console runs in chrooted environment, and sdcard are always writeable
1344                 return;
1345             }
1346             Identity identity = console.getIdentity();
1347             if (identity == null) {
1348                 throw new InsufficientPermissionsException(executable);
1349             }
1350             Permissions permissions = fso.getPermissions();
1351             User user = fso.getUser();
1352             Group group = fso.getGroup();
1353             List<Group> groups = identity.getGroups();
1354             if ( permissions == null || user == null || group == null) {
1355                 throw new InsufficientPermissionsException(executable);
1356             }
1357             // Check others
1358             if (permissions.getOthers().isWrite()) {
1359                 return;
1360             }
1361             // Check user
1362             if (user.getId() == identity.getUser().getId() && permissions.getUser().isWrite()) {
1363                 return;
1364             }
1365             // Check group
1366             if (group.getId() == identity.getGroup().getId() && permissions.getGroup().isWrite()) {
1367                 return;
1368             }
1369             // Check groups
1370             int cc = groups.size();
1371             for (int i = 0; i < cc; i++) {
1372                 Group g = groups.get(i);
1373                 if (group.getId() == g.getId() && permissions.getGroup().isWrite()) {
1374                     return;
1375                 }
1376             }
1377
1378         } catch (Exception e) {
1379             Log.e(TAG, "Failed to check fso write permission,", e); //$NON-NLS-1$
1380         }
1381         throw new InsufficientPermissionsException(executable);
1382     }
1383
1384     /**
1385      * Method that ensures that the actual console has access to execute the
1386      * {@link FileSystemObject} passed.
1387      *
1388      * @param console The console
1389      * @param fso The {@link FileSystemObject} to check
1390      * @param executable The executable to associate to the {@link InsufficientPermissionsException}
1391      * @throws InsufficientPermissionsException If the console doesn't have enough rights
1392      */
1393     public static void ensureExecuteAccess(
1394             Console console, FileSystemObject fso, SyncResultExecutable executable)
1395             throws InsufficientPermissionsException {
1396         try {
1397             if (console.isPrivileged()) {
1398                 // Should have access
1399                 return;
1400             }
1401             if (console instanceof JavaConsole &&
1402                     StorageHelper.isPathInStorageVolume(fso.getFullPath())) {
1403                 // Java console runs in chrooted environment, and sdcard are never executable
1404                 throw new InsufficientPermissionsException(executable);
1405             }
1406             Identity identity = console.getIdentity();
1407             if (identity == null) {
1408                 throw new InsufficientPermissionsException(executable);
1409             }
1410             Permissions permissions = fso.getPermissions();
1411             User user = fso.getUser();
1412             Group group = fso.getGroup();
1413             List<Group> groups = identity.getGroups();
1414             if ( permissions == null || user == null || group == null) {
1415                 throw new InsufficientPermissionsException(executable);
1416             }
1417             // Check others
1418             if (permissions.getOthers().isExecute()) {
1419                 return;
1420             }
1421             // Check user
1422             if (user.getId() == identity.getUser().getId() && permissions.getUser().isExecute()) {
1423                 return;
1424             }
1425             // Check group
1426             if (group.getId() == identity.getGroup().getId() && permissions.getGroup().isExecute()) {
1427                 return;
1428             }
1429             // Check groups
1430             int cc = groups.size();
1431             for (int i = 0; i < cc; i++) {
1432                 Group g = groups.get(i);
1433                 if (group.getId() == g.getId() && permissions.getGroup().isExecute()) {
1434                     return;
1435                 }
1436             }
1437
1438         } catch (Exception e) {
1439             Log.e(TAG, "Failed to check fso execute permission,", e); //$NON-NLS-1$
1440         }
1441         throw new InsufficientPermissionsException(executable);
1442     }
1443
1444     /**
1445      * Method that formats a filetime date with the specific system settings
1446      *
1447      * @param ctx The current context
1448      * @param filetime The filetime date
1449      * @return String The filetime date formatted
1450      */
1451     public static String formatFileTime(Context ctx, Date filetime) {
1452         synchronized (DATETIME_SYNC) {
1453             if (sReloadDateTimeFormats) {
1454                 String defaultValue =
1455                         ((ObjectStringIdentifier)FileManagerSettings.
1456                                     SETTINGS_FILETIME_FORMAT_MODE.getDefaultValue()).getId();
1457                 String id = FileManagerSettings.SETTINGS_FILETIME_FORMAT_MODE.getId();
1458                 sFiletimeFormatMode =
1459                         FileTimeFormatMode.fromId(
1460                                 Preferences.getSharedPreferences().getString(id, defaultValue));
1461                 if (sFiletimeFormatMode.compareTo(FileTimeFormatMode.SYSTEM) == 0) {
1462                     sDateTimeFormatOrder = ctx.getString(R.string.datetime_format_order);
1463                     sDateFormat = android.text.format.DateFormat.getDateFormat(ctx);
1464                     sTimeFormat = android.text.format.DateFormat.getTimeFormat(ctx);
1465                 } else if (sFiletimeFormatMode.compareTo(FileTimeFormatMode.LOCALE) == 0) {
1466                     sDateFormat =
1467                             DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT);
1468                 } else {
1469                     sDateFormat = new SimpleDateFormat(sFiletimeFormatMode.getFormat());
1470                 }
1471                 sReloadDateTimeFormats = false;
1472             }
1473         }
1474
1475         // Apply the user settings
1476         if (sFiletimeFormatMode.compareTo(FileTimeFormatMode.SYSTEM) == 0) {
1477             String date = sDateFormat.format(filetime);
1478             String time = sTimeFormat.format(filetime);
1479             return String.format(sDateTimeFormatOrder, date, time);
1480         } else {
1481             return sDateFormat.format(filetime);
1482         }
1483     }
1484
1485     /**
1486      * Method that create a new temporary filename
1487      *
1488      * @param external If the file should be created in the external or the internal cache dir
1489      */
1490     public static synchronized File createTempFilename(Context context, boolean external) {
1491         File tempDirectory = external ? context.getExternalCacheDir() : context.getCacheDir();
1492         File tempFile;
1493         do {
1494             UUID uuid = UUID.randomUUID();
1495             tempFile = new File(tempDirectory, uuid.toString());
1496         } while (tempFile.exists());
1497         return tempFile;
1498     }
1499
1500     /**
1501      * Method that delete a file or a folder
1502      *
1503      * @param src The file or folder to delete
1504      * @return boolean If the operation was successfully
1505      */
1506     public static boolean deleteFileOrFolder(File src) {
1507         if (src.isDirectory()) {
1508             return FileHelper.deleteFolder(src);
1509         }
1510         return src.delete();
1511     }
1512
1513     /**
1514      * Method that checks if the source file passed belongs to (is under) the directory passed
1515      *
1516      * @param src The file to check
1517      * @param dir The parent file to check
1518      * @return boolean If the source belongs to the directory
1519      */
1520     public static boolean belongsToDirectory(File src, File dir) {
1521         if (dir.isFile()) {
1522             return false;
1523         }
1524         return src.getAbsolutePath().startsWith(dir.getAbsolutePath());
1525     }
1526
1527     /**
1528      * Method that checks if both path are the same (by checking sensitive cases).
1529      *
1530      * @param src The source path
1531      * @param dst The destination path
1532      * @return boolean If both are the same path
1533      */
1534     public static boolean isSamePath(String src, String dst) {
1535         // This is only true if both are exactly the same path or the same file in insensitive
1536         // file systems
1537         File o1 = new File(src);
1538         File o2 = new File(dst);
1539         return o1.equals(o2);
1540     }
1541 }