/* * Copyright 2012 Evernote Corporation. * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package com.evernote.client.android; import com.evernote.edam.type.Resource; import java.io.IOException; import java.io.InputStream; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; public class EvernoteUtil { /** * The ENML preamble to every Evernote note. * Note content goes between <en-note> and </en-note> */ public static final String NOTE_PREFIX = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + "<!DOCTYPE en-note SYSTEM \"http://xml.evernote.com/pub/enml2.dtd\">" + "<en-note>"; /** * The ENML postamble to every Evernote note */ public static final String NOTE_SUFFIX = "</en-note>"; /** * One-way hashing function used for providing a checksum of EDAM data */ private static final String EDAM_HASH_ALGORITHM = "MD5"; /** * Create an ENML <en-media> tag for the specified Resource object. */ public static String createEnMediaTag(Resource resource) { return "<en-media hash=\"" + bytesToHex(resource.getData().getBodyHash()) + "\" type=\"" + resource.getMime() + "\"/>"; } /** * Returns an MD5 checksum of the provided array of bytes. */ public static byte[] hash(byte[] body) { try { return MessageDigest.getInstance(EDAM_HASH_ALGORITHM).digest(body); } catch (NoSuchAlgorithmException e) { throw new EvernoteUtilException(EDAM_HASH_ALGORITHM + " not supported", e); } } /** * Returns an MD5 checksum of the contents of the provided InputStream. */ public static byte[] hash(InputStream in) throws IOException { MessageDigest digest; try { digest = MessageDigest.getInstance(EDAM_HASH_ALGORITHM); } catch (NoSuchAlgorithmException e) { throw new EvernoteUtilException(EDAM_HASH_ALGORITHM + " not supported", e); } byte[] buf = new byte[1024]; int n; while ((n = in.read(buf)) != -1) { digest.update(buf, 0, n); } return digest.digest(); } /** * Converts the provided byte array into a hexadecimal string * with two characters per byte. */ public static String bytesToHex(byte[] bytes) { return bytesToHex(bytes, false); } /** * Takes the provided byte array and converts it into a hexadecimal string * with two characters per byte. * * @param withSpaces if true, include a space character between each hex-rendered * byte for readability. */ public static String bytesToHex(byte[] bytes, boolean withSpaces) { StringBuilder sb = new StringBuilder(); for (byte hashByte : bytes) { int intVal = 0xff & hashByte; if (intVal < 0x10) { sb.append('0'); } sb.append(Integer.toHexString(intVal)); if (withSpaces) { sb.append(' '); } } return sb.toString(); } /** * Takes a string in hexadecimal format and converts it to a binary byte * array. This does no checking of the format of the input, so this should * only be used after confirming the format or origin of the string. The input * string should only contain the hex data, two characters per byte. */ public static byte[] hexToBytes(String hexString) { byte[] result = new byte[hexString.length() / 2]; for (int i = 0; i < result.length; ++i) { int offset = i * 2; result[i] = (byte) Integer.parseInt(hexString.substring(offset, offset + 2), 16); } return result; } /** * A runtime exception that will be thrown when we hit an error that should * "never" occur ... e.g. if the JVM doesn't know about UTF-8 or MD5. */ @SuppressWarnings("serial") private static final class EvernoteUtilException extends RuntimeException { public EvernoteUtilException(String message, Throwable cause) { super(message, cause); } } }