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;
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.