/**
 * Native method boolean removeFromSuiteList(String) for class
 * com.sun.midp.midletsuite.MIDletSuiteStorage.
 * <p>
 * Removes the suite from the list of installed suites.
 * <p>
 * Used from suitestore_midletsuitestorage_kni.c so is non-static.
 *
 * @param suiteId ID of a suite
 *
 * @return 1 if the suite was in the list, 0 if not
 * -1 if out of memory
 */
int
remove_from_suite_list_and_save(SuiteIdType suiteId) {
    int existed = 0;
    MidletSuiteData *pData, *pPrevData = NULL;

    if (suiteId == UNUSED_SUITE_ID || suiteId == INTERNAL_SUITE_ID) {
        return 0; /* suite was not in the list */
    }

    /*
     * This function is called from midp_remove_suite(),
     * so read_suites_data() was already called.
     */
    pData = g_pSuitesData;

    /* try to find a suite */
    while (pData != NULL) {
        if (pData->suiteId == suiteId) {
            int status;
            char* pszError;

            /* remove the entry we have found from the list */
            if (pPrevData) {
                /* this entry is not the first */
                pPrevData->nextEntry = pData->nextEntry;
            } else {
                /* handle the first entry */
                g_pSuitesData = pData->nextEntry;
            }

            /* free the memory allocated for the entry */
            free_suite_data_entry(pData);

            /* decrease the number of the installed suites */
            g_numberOfSuites--;

            /* save the modified list into _suites.dat */
            status = write_suites_data(&pszError);
            existed = (status == ALL_OK) ? 1 : -1;
            storageFreeError(pszError);
            break;
        }

        pPrevData = pData;
        pData = pData->nextEntry;
    }

    /* Reset the static variable for MVM-safety */
    g_lastSuiteExistsID = UNUSED_SUITE_ID;

    return existed;
}
/**
 * Change the enabled state of a suite.
 *
 * @param suiteId ID of the suite
 * @param enabled true if the suite is be enabled
 *
 * @return an error code (ALL_OK if no errors)
 */
static MIDPError
change_enabled_state(SuiteIdType suiteId, jboolean enabled) {
    char* pszError;
    MIDPError status;
    jbyte* pPermissions;
    int numberOfPermissions;
    jbyte pushInterrupt;
    jint pushOptions;
    jboolean temp;
    lockStorageList* node;
    MidletSuiteData* pData;

    /*
     * This is a public API which can be called without the VM running
     * so we need automatically init anything needed, to make the
     * caller's code less complex.
     *
     * Initialization is performed in steps so that we do use any
     * extra resources such as the VM for the operation being performed.
     */
    if (midpInit(LIST_LEVEL) != 0) {
        return OUT_OF_MEMORY;
    }

    status = midp_suite_exists(suiteId);

    if ((status != ALL_OK) && (status != SUITE_CORRUPTED_ERROR)) {
        return status;
    }

    node = find_storage_lock(suiteId);
    if (node != NULL) {
        if (node->update == KNI_TRUE) {
            /* Suite is being updated currently. */
            return SUITE_LOCKED;
        }
    }

    status = read_settings(&pszError, suiteId, &temp, &pushInterrupt,
                           &pushOptions, &pPermissions, &numberOfPermissions);
    if (status != ALL_OK) {
        storageFreeError(pszError);
        return status;
    }

    status = begin_transaction(TRANSACTION_ENABLE_SUITE, suiteId, NULL);
    if (status != ALL_OK) {
        return status;
    }

    status = write_settings(&pszError, suiteId, enabled, pushInterrupt,
                            pushOptions, pPermissions, numberOfPermissions,
                            NULL);
    pcsl_mem_free(pPermissions);
    if (status != ALL_OK) {
        storageFreeError(pszError);
        /* nothing was written, so nothing to rollback, just finish */
        (void)finish_transaction();
        return status;
    }

    /* synchronize the settings in the list of MidletSuiteData structures */
    pData = get_suite_data(suiteId);

    /*
     * We can assert that pData is not NULL because midp_suite_exists()
     * was called above to ensure that the suite with the given ID exists.
     */
    if (pData != NULL) {
        int status;
        char* pszError;
        pData->isEnabled = enabled;

        /* IMPL_NOTE: these settings must be cached and saved on AMS exit. */
        status = write_suites_data(&pszError);
        storageFreeError(pszError);
        if (status != ALL_OK) {
            (void)rollback_transaction();
            return IO_ERROR;
        }
    }

    (void)finish_transaction();

    return ALL_OK;
}
/**
 * Moves the given midlet suite to another folder.
 *
 * @param suiteId ID of the suite
 * @param newFolderId ID of the folder where the suite must be moved
 *
 * @return ALL_OK if no errors or an error code
 */
MIDPError midp_move_suite_to_folder(SuiteIdType suiteId,
                                    FolderIdType newFolderId) {
    MIDPError status = ALL_OK;
    char* pszError = NULL;
    lockStorageList *node = NULL;
    MidletSuiteData* pSuiteData;
    FolderIdType oldFolderId;

    /*
     * This is a public API which can be called without the VM running
     * so we need automatically init anything needed, to make the
     * caller's code less complex.
     *
     * Initialization is performed in steps so that we do use any
     * extra resources such as the VM for the operation being performed.
     */
    if (midpInit(REMOVE_LEVEL) != 0) {
        return OUT_OF_MEMORY;
    }

    /* load _suites.dat */
    status = read_suites_data(&pszError);
    storageFreeError(pszError);

    if (status != ALL_OK) {
        return status;
    }

    node = find_storage_lock(suiteId);
    if (node != NULL) {
        if (node->update != KNI_TRUE) {
            return SUITE_LOCKED;
        }
    }

    pSuiteData = get_suite_data(suiteId);
    if (pSuiteData == NULL) {
        remove_storage_lock(suiteId);
        return NOT_FOUND;
    }

    status = begin_transaction(TRANSACTION_MOVE_TO_FOLDER, suiteId, NULL);
    if (status != ALL_OK) {
        remove_storage_lock(suiteId);
        return status;
    }

    oldFolderId = pSuiteData->folderId;
    pSuiteData->folderId = newFolderId;

    status = write_suites_data(&pszError);
    storageFreeError(pszError);

    if (status != ALL_OK) {
        pSuiteData->folderId = oldFolderId;
        (void)rollback_transaction();
    } else {
        (void)finish_transaction();
    }

    remove_storage_lock(suiteId);

    return status;
}
/**
 * Gets the amount of storage on the device that this suite is using.
 * This includes the JAD, JAR, management data, and RMS.
 *
 * @param suiteId  ID of the suite
 *
 * @return number of bytes of storage the suite is using or less than
 * OUT_OF_MEM_LEN if out of memory
 */
long
midp_get_suite_storage_size(SuiteIdType suiteId) {
    long used = 0;
    long rms = 0;
    pcsl_string filename[NUM_SUITE_FILES];
    int i;
    char* pszError;
    StorageIdType storageId;
    MIDPError status;
    MidletSuiteData* pData;
    pcsl_string filenameBase;

    // get the filename base from the suite id
    status = build_suite_filename(suiteId, &PCSL_STRING_EMPTY, 
                                            &filenameBase);
    
    if (status != ALL_OK) {
        return status;
    }
    pData = get_suite_data(suiteId);
    if (pData) {
        used = (jint)pData->suiteSize;
    }

    if (used <= 0) {
        /* Suite size is not cached (should not happen!), calculate it. */
        for (i = 0; i < NUM_SUITE_FILES; i++) {
            filename[i] = PCSL_STRING_NULL;
        }

        /*
         * This is a public API which can be called without the VM running
         * so we need automatically init anything needed, to make the
         * caller's code less complex.
         *
         * Initialization is performed in steps so that we do use any
         * extra resources such as the VM for the operation being performed.
         */
        if (midpInit(LIST_LEVEL) != 0) {
            return OUT_OF_MEM_LEN;
        }

        status = midp_suite_get_suite_storage(suiteId, &storageId);
        if (status != ALL_OK) {
            return OUT_OF_MEM_LEN;
        }

        status = build_suite_filename(suiteId, &INSTALL_INFO_FILENAME, &filename[0]);
        if (status != ALL_OK) {
            return status;
        }
        status = build_suite_filename(suiteId, &SETTINGS_FILENAME, &filename[1]);
        if (status != ALL_OK) {
            return status;
        }
        midp_suite_get_class_path(suiteId, storageId, KNI_TRUE, &filename[2]);
        get_property_file(suiteId, KNI_TRUE, &filename[3]);

        for (i = 0; i < NUM_SUITE_FILES; i++) {
            long tmp;

            if (pcsl_string_is_null(&filename[i])) {
                continue;
            }

            tmp = storage_size_of_file_by_name(&pszError, &filename[i]);
            pcsl_string_free(&filename[i]);

            if (pszError != NULL) {
                storageFreeError(pszError);
                continue;
            }

            used += tmp;
        }

        if (pData) {
            /* cache the calculated size */
            pData->suiteSize = (jint)used;
            status = write_suites_data(&pszError);
            if (status != ALL_OK) {
                storageFreeError(pszError);
                return OUT_OF_MEM_LEN;
            }
        }
    }

    rms = rmsdb_get_rms_storage_size(&filenameBase, suiteId);
    if (rms == OUT_OF_MEM_LEN) {
        return OUT_OF_MEM_LEN;
    }

    return used + rms;
}
/**
 * Moves a software package with given suite ID to the specified storage.
 *
 * @param suiteId suite ID for the installed package
 * @param storageId new storage ID
 *
 * @return SUITE_LOCKED if the
 * suite is locked, NOT_FOUND if the suite cannot be found or
 * invalid storage ID specified, BAD_PARAMS if attempt is made
 * to move suite to the external storage, GENERAL_ERROR if
 * VERIFY_ONCE is not enabled and if MONET is enabled
 */
MIDPError
midp_change_suite_storage(SuiteIdType suiteId, StorageIdType newStorageId) {

#ifndef VERIFY_ONCE
    (void)suiteId;
    (void)newStorageId;
    return GENERAL_ERROR;
#else

   /*
    * if VERIFY_ONCE is enabled then MONET is disabled
    * so we don't have to check ENABLE_MONET.
    */

    {
        pcsl_string suiteRoot;
        MidletSuiteData* pData = NULL;
        int status;
        void* fileIteratorHandle = NULL;
        lockStorageList *node = NULL;

        if ((UNUSED_STORAGE_ID == newStorageId) ||
            (newStorageId >= MAX_STORAGE_NUM)) {
            return BAD_PARAMS;
        }

        /*
         * IMPL_NOTE: for security reasons we allow to move suite
         * only to the internal storage.
         */
        if (newStorageId != INTERNAL_STORAGE_ID) {
            return BAD_PARAMS;
        }

        node = find_storage_lock(suiteId);
        if (node != NULL) {
            if (node->update != KNI_TRUE) {
                return SUITE_LOCKED;
            }
        }

        /*
         * This is a public API which can be called without the VM running
         * so we need automatically init anything needed, to make the
         * caller's code less complex.
         *
         * Initialization is performed in steps so that we do use any
         * extra resources such as the VM for the operation being performed.
         */
        if (midpInit(REMOVE_LEVEL) != 0) {
            remove_storage_lock(suiteId);
            return OUT_OF_MEMORY;
        }

        /* check that the suite exists and it is not a preloaded one */
        pData = get_suite_data(suiteId);

        if (pData == NULL) {
            remove_storage_lock(suiteId);
            return NOT_FOUND;
        }

        if (pData->storageId == newStorageId) {
            remove_storage_lock(suiteId);
            return BAD_PARAMS;
        }

        if (pData->type == COMPONENT_PREINSTALLED_SUITE) {
            remove_storage_lock(suiteId);
            return BAD_PARAMS;
        }

        do {
            jsize oldRootLength;
            jsize newRootLength;
            const pcsl_string* newRoot;
            const pcsl_string* oldRoot;
            char* pszError = NULL;
            pcsl_string filePath;
            pcsl_string newFilePath;


            status = begin_transaction(TRANSACTION_CHANGE_STORAGE, suiteId, NULL);
            if (status != ALL_OK) {
                break;
            }

            if ((status = get_suite_storage_root(suiteId, &suiteRoot)) != ALL_OK) {
                break;
            }

            fileIteratorHandle = storage_open_file_iterator(&suiteRoot);
            if (!fileIteratorHandle) {
                status = IO_ERROR;
                break;
            }

            newRoot = storage_get_root(newStorageId);
            oldRoot = storage_get_root(pData->storageId);
            newRootLength = pcsl_string_length(newRoot);
            oldRootLength = pcsl_string_length(oldRoot);

            status = ALL_OK;

            if ((status = midp_suite_get_class_path(suiteId,
                pData->storageId, KNI_FALSE, &filePath)) != ALL_OK) {
                break;
            }

            if ((status = midp_suite_get_class_path(suiteId,
                newStorageId, KNI_FALSE, &newFilePath)) != ALL_OK) {
                break;
            }

            storage_rename_file(&pszError, &filePath, &newFilePath);
            if (pszError != NULL) {
                status = IO_ERROR;
                storageFreeError(pszError);
                pcsl_string_free(&filePath);
                pcsl_string_free(&newFilePath);
                break;
            }

            pcsl_string_free(&filePath);
            pcsl_string_free(&newFilePath);

#if ENABLE_IMAGE_CACHE
            moveImageCache(suiteId, pData->storageId, newStorageId);
#endif

            pData->storageId = newStorageId;

            status = write_suites_data(&pszError);
            storageFreeError(pszError);

        } while (0);

        pcsl_string_free(&suiteRoot);
        storageCloseFileIterator(fileIteratorHandle);

        if (status != ALL_OK) {
            (void)rollback_transaction();
        } else {
            (void)finish_transaction();
        }

        remove_storage_lock(suiteId);

        return status;
    }

#endif /* VERIFY_ONCE */
}