Wednesday, 19 December 2012

Extracting contacts from a .sbu file

A while Back I had my Galaxy phone stolen to which I later bought a new one. However the problem was that the backup I had from the previous phone was in a .sbu file which I could not open or restored using the Kies application. After many tries and Googling, I came across this source edited it the used it to extract the contacts(VCARD data) from the SBU file.
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.List;

public class SSVCardExtractor {

    private static final String VCARD_FILE_SUFFIX = ".vcf";

    private String outputCharset = "ascii";

    private String inputCharset = "UTF-16LE";

    private InputStream input;

    private VCardWriter output;

    private byte[] start;

    private byte[] end;

    private interface VCardWriter {

        public void write(String vcard) throws IOException;

    }

    public SSVCardExtractor(String inputPath, String outputPath,

            boolean separate, boolean suffixFix) throws IOException {

        if (!separate && suffixFix && !outputPath.endsWith(VCARD_FILE_SUFFIX)) {

            outputPath = outputPath + VCARD_FILE_SUFFIX;

        }


        final File outputFile = new File(outputPath);

        if (outputFile.exists()) {

            throw new IOException("Output path exists: " + outputFile);

        }

        if (separate) {

            outputFile.mkdirs();

            this.output = new VCardWriter() {

                private File directory = outputFile;

                private int index = 0;

                @Override

                public void write(String vcard) throws IOException {

                    File file = new File(directory, ++index + VCARD_FILE_SUFFIX);

                    FileOutputStream stream = new FileOutputStream(file);

                    try {

                        stream.write(vcard.getBytes(outputCharset));

                        stream.flush();

                    } finally {

                        stream.close();

                    }

                }

            };

        } else {

            final String outputPathCopy = new String(outputPath);

            this.output = new VCardWriter() {

                private OutputStream stream = new FileOutputStream(

                        outputPathCopy);

                @Override
                public void write(String vcard) throws IOException {

                    stream.write(vcard.getBytes(outputCharset));

                    stream.flush();

                }


                protected void finalize() throws Throwable {

                    try {

                        stream.close();

                    } catch (Exception e) {

                        // pass

                    }

                }

            };

        }

        this.input = new BufferedInputStream(new FileInputStream(inputPath));

        this.start = "BEGIN:VCARD".getBytes(inputCharset);

        this.end = "END:VCARD".getBytes(inputCharset);

    }

    public void extract() throws IOException {

        while (skipUntil(start, false) != null) {

            byte[] data = skipUntil(end, true);

            if (data == null) {

                throw new IOException("Unexpected end of stream");

            }

            String vcard = new String(start, inputCharset)

                    + new String(data, inputCharset) + "\r\n";

            output.write(vcard);

        }

    }

    private byte[] skipUntil(byte[] bytes, boolean store) throws IOException {

        int searchPosition = 0, current;

        ByteArrayOutputStream data = new ByteArrayOutputStream();

        while ((current = input.read()) >= 0) {

            if (store) {

                data.write(current);

            }

            if ((byte) current == bytes[searchPosition]) {

                if (++searchPosition == bytes.length) {

                    return data.toByteArray();

                }

            } else if (searchPosition != 0) {

                searchPosition = 0;

            }

        }

        return null;

    }


    private static void usageExit(String message) {

        PrintStream out = System.out;

        if (message != null) {

            out.println(message);

        }

        out.println("Usage: java SSVcardExtractor [OPTIONS] <SBU-FILE> <OUTPUT-PATH>");

        out.println("Options:");

        out.println(" -s, --separate       Write each vcard to separate file.");

        out.println("                      By default all vcards are written to one file.");

        out.println("     --no-suffix-fix  Do not add missing "

                + VCARD_FILE_SUFFIX + " suffix.");

        out.println("     --debug          Print debug messages.");

        out.println("     --accept-license Accept license.");

        System.exit(1);

    }

    public static void main(String[] args) {

        boolean separate = false;

        boolean debug = false;

        boolean suffixFix = true;

        boolean acceptLicense = false;

        List<String> arguments = new ArrayList<String>(2);

        for (String argument : args) {

            if (argument.equals("--separate") || argument.equals("-s")) {

                separate = true;

            } else if (argument.equals("--debug")) {

                debug = true;

            } else if (argument.equals("--no-suffix-fix")) {

                suffixFix = false;

            } else if (argument.equals("--accept-license")) {

                acceptLicense = true;

            } else if (argument.startsWith("-")) {

                usageExit("Invalid option: " + argument);

            } else {

                arguments.add(argument);

            }

        }

        if (arguments.size() != 2) {

            usageExit("Invalid argument count");

        }

        try {

            SSVCardExtractor extractor = new SSVCardExtractor(arguments.get(0),

                    arguments.get(1), separate, suffixFix);

            extractor.extract();

            System.out.println("Extract Complete...");

        } catch (IOException e) {

            System.out.println(e.getMessage());

            System.out.flush();

            if (debug) {

                e.printStackTrace();

            }

        }

    }

}






The SBU file is some kind of uncompressed archive file with UTF-16 encoding, all that this code does is to find the VCard start and End tags then writes the VCard items into a file. I then used the generated file to import the contacts to my phone.


How do you use the program?


1. Make sure you have java installed in your machine

2. Copy the code and paste it in a notepad, save the notepad as SSVCardExtractor.java

3. Copy the SBU file in the java bin folder “Program files/java/jdk1.6.0_25/bin”

4. Start the command window

5. Compile the program above as shown;
javac

6. Execute the program to extract the 20121120T112151.sbu which was my backup as follows;

java SSVCardExtractor 20121120T112151.sbu output.vcf

the extract file will be output.vcf

7. You can now use Kies to import the contacts from output.vcf file to your phone.


The Java sdk may be located on a different path on you computer, please confirm that.

5 comments:

  1. This comment has been removed by the author.

    ReplyDelete
  2. where can i find the output folder ???

    ReplyDelete
  3. You should be able to find the output file in the java installation folder. see step 5.

    ReplyDelete
  4. Does it only work with JDK? What about JRE6

    ReplyDelete

Comment