/* * Universal Password Manager * Copyright (C) 2005-2013 Adrian Smith * * This file is part of Universal Password Manager. * * Universal Password Manager is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Universal Password Manager is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Universal Password Manager; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ package com._17od.upm.database; import java.io.InputStream; import java.io.IOException; import java.io.EOFException; import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.nio.charset.Charset; import com._17od.upm.util.Util; /** * This class represents an object that can be serialised * into a structured ASCII string. It's purpose is to * provide a means of serialising/deserialising an object * without having to go through all the hassle of using XML. */ public abstract class FlatPackObject { private static int LENGTH_FIELD_NUM_CHARS = 4; /** * Write the given string to the given OutputStream * @param s * @param os * @throws UnsupportedEncodingException */ protected byte[] flatPack(String s) throws UnsupportedEncodingException { return flatPack(s.getBytes("UTF-8")); } protected byte[] flatPack(byte[] bytesToFlatPack) throws UnsupportedEncodingException { //Create a byte array populated with the field length String l = Util.lpad(bytesToFlatPack.length, LENGTH_FIELD_NUM_CHARS, '0'); byte[] fieldLengthBytes = l.getBytes("UTF-8"); //Declare the buffer we're going to return byte[] returnBuffer = new byte[fieldLengthBytes.length + bytesToFlatPack.length]; //Populate the return buffer with the 'field length' bytes and 'field contents' bytes System.arraycopy(fieldLengthBytes, 0, returnBuffer, 0, fieldLengthBytes.length); System.arraycopy(bytesToFlatPack, 0, returnBuffer, fieldLengthBytes.length, bytesToFlatPack.length); return returnBuffer; } public byte[] getBytes(InputStream is) throws IOException, ProblemReadingDatabaseFile { byte[] fieldContents = null; //Get the length of the next field byte[] fieldLength = new byte[LENGTH_FIELD_NUM_CHARS]; int bytesRead = is.read(fieldLength); if (bytesRead == -1 || bytesRead != LENGTH_FIELD_NUM_CHARS) { throw new EOFException(); } String s = new String(fieldLength); try { int i = Integer.parseInt(s); //Read the field fieldContents = new byte[i]; //Had to do it this way because the next section (commented out) //didn't read in the correct number of bytes for (int j=0; j<i; j++) { fieldContents[j] = (byte) is.read(); if (fieldContents[j] == -1) { throw new EOFException(); } } /*if (i > 0) { bytesRead = is.read(fieldContents); //I had to comment this next line out because the CipherInputStream reads one to few bytes for //the last field in the file, don't know why??? Problem now is I'm not checking that the number //of bytes read is correct //if (bytesRead == -1 || bytesRead != i) { if (bytesRead == -1) { throw new EOFException(); } }*/ } catch (NumberFormatException e) { throw new ProblemReadingDatabaseFile("A field length had invalid characters", e); } return fieldContents; } public int getInt(InputStream is) throws IOException, ProblemReadingDatabaseFile { return Integer.parseInt(getString(is)); } public String getString(InputStream is) throws IOException, ProblemReadingDatabaseFile { return new String(getBytes(is), "UTF-8"); } public String getString(InputStream is, Charset charset) throws IOException, ProblemReadingDatabaseFile { return new String(getBytes(is), charset.name()); } public abstract void flatPack(OutputStream os) throws IOException; }