javacommons/gcfprotocols/file/javasrc.s60/com/nokia/mj/impl/file/FileAccessHelper.java
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Tue, 27 Apr 2010 16:30:29 +0300
branchRCL_3
changeset 19 04becd199f91
child 23 98ccebc37403
permissions -rw-r--r--
Revision: v2.1.22 Kit: 201017

/*
* Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description:
*
*/

package com.nokia.mj.impl.file;

import java.util.Hashtable;
import java.util.Vector;
import com.nokia.mj.impl.rt.support.ApplicationInfo;

public class FileAccessHelper implements FileConstants
{
    public static int PATHS_EQUAL = 0;
    public static int PATHS_NO_MATCH = 1;
    public static int PATH_ABOVEIN_HIERARCHY = 2;
    public static int PATH_BELOWIN_HIERARCHY = 3;
    private static String PATH_PRIVATE = "private";
    private static String PATH_SYSTEM = "system";
    private static String[] iRestrictedPathList;
    private static String[] iForbiddenPathList;

    static
    {
        // Populate forbidden path list.
        Vector forbidden = FileSystemUtils.getForbiddenPaths();
        iForbiddenPathList = new String[forbidden.size()];
        for (int index = 0; index < forbidden.size(); index++)
        {
            iForbiddenPathList[index] = (String) forbidden.elementAt(index);
        }

        // Populate restricted path list.
        Vector restricted = FileSystemUtils.getRestrictedPaths();
        iRestrictedPathList = new String[restricted.size() + 1];
        for (int index = 0; index < restricted.size(); index++)
        {
            iRestrictedPathList[index] = (String) restricted.elementAt(index);
        }
        // Add midlet's private directory also to restricted path list.
        iRestrictedPathList[restricted.size()] = FileSystemUtils
                .getAppPrivateDir();
    }

    /**
     * Checks to see if the application has access to a specific path.
     *
     * @param aPath
     *            path which the application is trying to access.
     * @param aIntent
     *            mode in which the application wants to access the target.
     * @param aDomain
     *            domain of the application
     * @param aIsOpening
     *            if the operation being performed is equivalent to opening a
     *            connection.
     * @return true in case access is allowed. False otherwise
     */
    public static boolean accessAllowed(String aPath, String aIntent,
                                        String aDomain, boolean aIsOpening)
    {
        FileLogger.Log("FileAccessHelper.accessAllowed: Checking access: \n");

        if (isHomeDir(aPath))
        {
            return true;
        }

        if (isForbidden(aPath))
        {
            return false;
        }

        if (isIllegalAccessToRestrictedDir(aPath, aIntent, aIsOpening, aDomain))
        {
            return false;
        }

        if (aDomain.equals(ApplicationInfo.MANUFACTURER_DOMAIN))
        {
            return manufacturerDomainChecks(aPath, aIntent, aIsOpening);
        }
        else
        {
            return otherDomainChecks(aPath, aIntent, aIsOpening);
        }
    }

    /**
     * To be used in case of list. If list is done on a directory that is equal
     * to, or higher in path hierarchy than one of the restricted paths, then we
     * need to check for access for all files, if not, then no need.
     */
    public static boolean isDirRestricted(String aPath)
    {
        for (int index = 0; index < iRestrictedPathList.length; index++)
        {
            int matchResult = matchPaths(aPath, iRestrictedPathList[index]);
            if ((matchResult != PATHS_NO_MATCH)
                    && (matchResult != PATH_BELOWIN_HIERARCHY))
            {
                return true;
            }
        }
        return false;
    }

    /**
     * Checks to see if the path being accessed in forbidden.
     *
     * @param aPath
     *            path being accessed
     * @return true in case the path is forbidden, false otherwise
     */
    private static boolean isForbidden(String aPath)
    {
        for (int index = 0; index < iForbiddenPathList.length; index++)
        {
            int matchPathResult = matchPaths(aPath, iForbiddenPathList[index]);

            // Forbidden paths should match exactly or should be such that the
            // path must be lower in hierarchy.
            // Example: e:/system is forbidden, e:/ is not.
            // e:/system is forbidden, e:/system/dir is also forbidden
            if ((matchPathResult == PATHS_EQUAL)
                    || (matchPathResult == PATH_BELOWIN_HIERARCHY))
            {
                return true;
            }
        }
        return false;
    }

    /**
     * Checks if the access to restricted paths is being made in correct intent
     * based on the domain.
     *
     * @param aPath
     *            path of the file/directory being accessed
     * @param aIntent
     *            intent with which it is being accessed (read or write)
     * @param aOpening
     *            set to true in case it is being used by Connector.open or
     *            setFileConnection. Both are considered as open and not as
     *            acutal read or write operations.
     * @param aDomain
     *            domain of the application.
     * @return true in case there is an access violation, false if the access is
     *         allowed.
     */
    public static boolean isIllegalAccessToRestrictedDir(String aPath,
            String aIntent, boolean aOpening, String aDomain)
    {
        for (int index = 0; index < iRestrictedPathList.length; index++)
        {
            int matchResult = matchPaths(aPath, iRestrictedPathList[index]);
            if ((matchResult != PATHS_NO_MATCH)
                    && (matchResult != PATH_BELOWIN_HIERARCHY))
            {
                if (aIntent.equals(INTENT_WRITE)
                        || aIntent.equals(INTENT_READ_WRITE))
                {
                    if (!aOpening)
                    {
                        return true;
                    }
                }
            }
        }
        return false;
    }

    /**
     * Performs manufacturer domain specific checks. Manufacturer domain apps
     * are not allowed to access any path in C:/private apart from its private
     * directory. Other checks are forbidden directories and restricted paths.
     * This is done before (accessAllowed()) So, no need to check once again.
     *
     * @param aPath
     *            path which is being accessed
     * @param aIntent
     *            intent with which access is being made. read, write
     * @param aIsOpening
     *            true in case its an open operation (open, setFileConnection)
     * @return true in case access is allowed. false otherwise.
     */
    public static boolean manufacturerDomainChecks(String aPath,
            String aIntent, boolean aIsOpening)
    {
        // Check if it is private directory.
        if (aPath.indexOf(PATH_PRIVATE) == 3)
        {
            if (matchPaths(aPath, FileSystemUtils.getAppPrivateDir()) == PATHS_NO_MATCH)
            {
                return false;
            }
        }
        return true;
    }

    /**
     * Performs domains other than manufacturer domain.
     *
     * @param aPath
     *            path which is being accessed
     * @param aIntent
     *            intent with which access is being made. read, write
     * @param aIsOpening
     *            true in case its an open operation (open, setFileConnection)
     * @return true in case access is allowed. false otherwise.
     */
    private static boolean otherDomainChecks(String aPath, String aIntent,
            boolean aIsOpening)
    {
        if (aPath.length() < 3)
        {
            // Path will be valid. This will be only in case file:///c: is given
            aPath += "/";
        }

        String rom = FileSystemUtils.getRomDrive().toLowerCase()
                     .substring(0, 2);
        String temp = FileSystemUtils.getTemporaryDrive().toLowerCase()
                      .substring(0, 2);

        if (aPath.toLowerCase().startsWith(rom)
                || aPath.toLowerCase().startsWith(temp))
        {
            return false;
        }

        // Other domains can access only below restricted paths or
        // in other drives.
        for (int index = 0; index < iRestrictedPathList.length; index++)
        {
            int matchResult = matchPaths(aPath, iRestrictedPathList[index]);

            if ((matchResult != PATH_BELOWIN_HIERARCHY)
                    && (matchResult != PATHS_NO_MATCH))
            {
                if ((!aIntent.equals(INTENT_READ)) && (!aIsOpening))
                {
                    // Anything other than read operation on par or above
                    // restricted path hierarchy is not allowed when not opening
                    return false;
                }
            }
        }

        if (partialMatchWithRestrictedPaths(aPath))
        {
            return false;
        }

        return true;
    }

    private static boolean partialMatchWithRestrictedPaths(String aPath)
    {
        String path1 = aPath;
        boolean initialNoMatch = true;

        // Partial match is only when path is not a substring initially,
        // but when stripped, becomes a substring of one of the restricted paths
        for (int index = 0; index < iRestrictedPathList.length; index++)
        {
            int matchResult = matchPaths(aPath, iRestrictedPathList[index]);
            if (matchResult == PATH_BELOWIN_HIERARCHY
                    || matchResult == PATH_ABOVEIN_HIERARCHY
                    || matchResult == PATHS_EQUAL)
            {
                return false;
            }
        }

        if (path1.length() > 3)
        {
            path1 = path1.substring(0, path1.lastIndexOf('/'));
        }

        // path1 is stripped to know in case the file is being created inside
        // root.
        while (path1.length() > 3)
        {
            for (int index = 0; index < iRestrictedPathList.length; index++)
            {
                if (iRestrictedPathList[index].toLowerCase().startsWith(
                            path1.toLowerCase()))
                {
                    return true;
                }
            }
            path1 = path1.substring(0, path1.lastIndexOf('/'));
        }

        // C:/data/somefile should be matched with c:/data/images,c:/data/videos
        // and must return true but c:/data or c:/ must not return as true
        return false;
    }

    /**
     * Resolves a path to a one of the following categories:
     *
     * <pre>
     * PUBLIC_DIRS - C:/Data/Images
     *               C:/Data/Videos
     *               C:/Data/Graphics
     *               C:/Data/Sounds
     *               C:/Data/Music
     *               C:/Data/Recordings and all files therein
     * HOME_DIR - App's private directory
     * PRIVATE_USER_FILES - All files and directories higher in path hierarchy
     *                      of PUBLIC_DIRS
     * SYSTEM_FILES - Z drive
     * </pre>
     *
     * @param aPath
     *            path that has to be mapped to a particular category.
     * @return category of the path specified.<br/> One of the following:
     *         SYSTEM_FILES, PRIVATE_USER_FILES, PUBLIC_DIRS, HOME_DIR
     */
    public static String getCategory(String aPath)
    {
        FileLogger.Log("+ FileAccessHelper: getCategory: " + aPath);
        // SYSTEM_FILES, PRIVATE_USER_FILES, PUBLIC_DIRS, HOME_DIR
        if (aPath.equals(SYSTEM_FILES) || aPath.equals(PRIVATE_USER_FILES)
                || aPath.equals(PUBLIC_DIRS) || aPath.equals(HOME_DIR)
                || aPath.equals(RESTRICTED_PUBLIC_FILES))
        {
            // if it is already mapped
            FileLogger.Log("- FileAccessHelper: getCategory: returning: "
                           + aPath);
            return aPath;
        }

        if (aPath.equals(""))
        {
            // Used in case of FileSystemRegistry
            return PUBLIC_DIRS;
        }

        // First check for Home directory. Restricted paths list contains
        // app's private directory too.
        if (isHomeDir(aPath))
        {
            return HOME_DIR;
        }

        int matchResult = PATHS_NO_MATCH;
        // Paths below restricted paths in hierarchy are part of Public files
        // Paths above in hierarchy are part of private used files.
        for (int index = 0; index < iRestrictedPathList.length; index++)
        {
            matchResult = matchPaths(aPath, iRestrictedPathList[index]);
            if (PATH_BELOWIN_HIERARCHY == matchResult)
            {
                FileLogger.Log("- FileAccessHelper: getCategory: returning: "
                               + PUBLIC_DIRS);
                return PUBLIC_DIRS;
            }

            if (PATHS_EQUAL == matchResult)
            {
                return PUBLIC_DIRS;
            }

            // Do we need this at all? Restricted PUBLIC Files can be removed
            if (PATH_ABOVEIN_HIERARCHY == matchResult)
            {
                FileLogger.Log("- FileAccessHelper: getCategory: returning: "
                               + PUBLIC_DIRS);
                return PUBLIC_DIRS;
            }
        }

        String rom = FileSystemUtils.getRomDrive().toLowerCase()
                     .substring(0, 2);
        String temp = FileSystemUtils.getTemporaryDrive().toLowerCase()
                      .substring(0, 2);

        if (aPath.toLowerCase().startsWith(rom)
                || aPath.toLowerCase().startsWith(temp))
        {
            FileLogger.Log("- FileAccessHelper: getCategory: returning: "
                           + SYSTEM_FILES);
            return SYSTEM_FILES;

        }
        else if (aPath.toLowerCase().startsWith(
                     FileSystemUtils.getDefaultRoot().toLowerCase()))
        {
            FileLogger.Log("- FileAccessHelper: getCategory: returning: "
                           + RESTRICTED_PUBLIC_FILES);
            // It is however known that the default root of the device can
            // change.
            return RESTRICTED_PUBLIC_FILES;
        }
        else if ((aPath.toLowerCase().indexOf(PATH_PRIVATE) == 3)
                 || (aPath.toLowerCase().indexOf(PATH_SYSTEM) == 3))
        {
            FileLogger.Log("- FileAccessHelper: getCategory: returning: "
                           + SYSTEM_FILES);
            return SYSTEM_FILES;
        }

        FileLogger.Log("- FileAccessHelper: getCategory: returning: "
                       + PUBLIC_DIRS);
        return PUBLIC_DIRS;
    }

    /**
     * Checks to see if the specified path is same as application's private
     * directory.
     */
    private static boolean isHomeDir(String aPath)
    {
        String appPrivateDir = FileSystemUtils.getAppPrivateDir();
        if (aPath.equalsIgnoreCase(appPrivateDir))
        {
            return true;
        }
        if (aPath.startsWith(appPrivateDir))
        {
            return true;
        }

        return false;
    }

    /**
     * Checks to see if a file/directory can be created within the specidied
     * path.
     *
     * @param aPath
     *            directory within which the application intends to create a
     *            file.
     * @param aDomain
     *            domain of the application
     * @return true in case access is allowed, false otherwise.
     */
    public static boolean isCreateAllowedWithinDir(String aPath, String aDomain)
    {
        if (aDomain.equals(ApplicationInfo.MANUFACTURER_DOMAIN))
        {
            return true;
        }

        boolean allowed = false;

        if (!aPath.startsWith(FileSystemUtils.getDefaultRoot()))
        {
            return true;
        }

        for (int index = 0; index < iRestrictedPathList.length; index++)
        {
            String path = iRestrictedPathList[index];
            int matchResult = matchPaths(aPath, path);
            // Domains other than manufacturer are allowed to create content
            // only within restricted directories.
            if ((PATHS_EQUAL == matchResult)
                    || (PATH_BELOWIN_HIERARCHY == matchResult))
            {
                allowed = true;
                break;
            }
        }
        return allowed;
    }

    /**
     * Tries to match paths. Returns how "path2" is related to "path1". Checks if
     * the path is above or below in path hierarchy. Also checks to see if paths
     * are same or are totally different.
     */
    private static int matchPaths(String aPath1, String aPath2)
    {
        // Strip trailing slash in case its present.
        String path1 = aPath1.endsWith("/") ? aPath1.substring(0, aPath1
                       .length() - 1) : aPath1;

        String path2 = aPath2.endsWith("/") ? aPath2.substring(0, aPath2
                       .length() - 1) : aPath2;

        // In case both paths are the same.
        if (path1.equalsIgnoreCase(path2))
        {
            return PATHS_EQUAL;
        }

        // Check if path1 is higher in path hierarchy
        if (path2.toLowerCase().startsWith(path1.toLowerCase()))
        {
            return PATH_ABOVEIN_HIERARCHY;
        }

        if (path1.toLowerCase().startsWith(path2.toLowerCase()))
        {
            return PATH_BELOWIN_HIERARCHY;
        }

        return PATHS_NO_MATCH;
    }
}