package com.comandante.pushpackage;
import com.google.common.base.Function;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.google.common.hash.HashCode;
import com.google.common.hash.HashFunction;
import com.google.common.hash.Hashing;
import com.comandante.pushpackage.jackson.WebsiteObjectMapper;
import com.comandante.pushpackage.jackson.model.Website;
import com.comandante.pushpackage.jackson.model.WebsiteBuilder;
import com.comandante.stickypunch.api.model.PackageSigner;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.codehaus.jackson.map.ObjectMapper;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
public class PackageZipBuilder {
private PackageZipConfiguration packageZipConfiguration;
private PackageSigner signer;
private ObjectMapper objectMapper = WebsiteObjectMapper.getInstance();
public static final ImmutableSet<String> IMAGE_NAMES = ImmutableSet.of(
"icon_128x128.png",
"icon_128x128@2x.png",
"icon_16x16.png",
"icon_16x16@2x.png",
"icon_32x32.png",
"icon_32x32@2x.png");
public PackageZipBuilder(PackageZipConfiguration packageZipConfiguration, PackageSigner signer) {
this.packageZipConfiguration = packageZipConfiguration;
this.signer = signer;
}
public byte[] createPackage(String userID) throws Exception {
validatePushPackageFiles(packageZipConfiguration.pushPackageFiles);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ZipOutputStream zos = new ZipOutputStream(byteArrayOutputStream);
HashMap<String, String> sha1s = new HashMap<String, String>();
for (String imageFileName : IMAGE_NAMES) {
createImage(imageFileName, sha1s, zos);
}
createWebsiteJson(userID, sha1s, zos);
InputStream is = createManifestJson(sha1s, zos);
createSignature(is, zos);
zos.flush();
zos.close();
return byteArrayOutputStream.toByteArray();
}
private void createImage(String fileName, HashMap<String, String> sha1s, ZipOutputStream zos) throws IOException {
HashFunction hf = Hashing.sha1();
File imageFile = new File(packageZipConfiguration.pushPackageFiles + "/icon.iconset/" + fileName);
Preconditions.checkArgument(imageFile.canRead(), "File cannot be read: %s", imageFile);
HashCode hc = hf.newHasher().putBytes(FileUtils.readFileToByteArray(imageFile)).hash();
sha1s.put("icon.iconset/" + fileName, hc.toString());
addToZipFile(new FileInputStream(imageFile), "icon.iconset/" + fileName, zos);
}
private void createWebsiteJson(String userId, HashMap<String, String> sha1s, ZipOutputStream zos) throws IOException {
HashFunction hf = Hashing.sha1();
InputStream is = buildWebsiteJsonFile(userId);
HashCode hc = hf.newHasher().putBytes(IOUtils.toByteArray(is)).hash();
sha1s.put("website.json", hc.toString());
is.reset();
addToZipFile(is, "website.json", zos);
}
private InputStream createManifestJson(HashMap<String, String> sha1s, ZipOutputStream zos) throws Exception {
HashFunction hf = Hashing.sha1();
InputStream is = buildManifestJsonFile(sha1s);
addToZipFile(is, "manifest.json", zos);
return is;
}
private void createSignature(InputStream is, ZipOutputStream zos) throws Exception {
is.reset();
byte[] signedBytes = signer.sign(IOUtils.toByteArray(is));
addToZipFile(new ByteArrayInputStream(signedBytes), "signature", zos);
}
private InputStream buildWebsiteJsonFile(String userId) throws IOException {
List<String> allowedDomains = Lists.transform(packageZipConfiguration.allowedDomains, new Function<Object, String>() {
@Override
public String apply(Object arg0) {
if (arg0 != null)
return arg0.toString();
else
return "null";
}
});
Website website = new WebsiteBuilder()
.setWebsitePushId(packageZipConfiguration.websitePushID)
.setWebServiceUrl(packageZipConfiguration.webServiceUrl)
.setAllowedDomains(allowedDomains)
.setUrlFormatString(packageZipConfiguration.urlFormatString)
.setAuthenticationToken(userId)
.setWebsiteName(packageZipConfiguration.websiteName)
.build();
String json = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(website);
return new ByteArrayInputStream(json.getBytes());
}
private InputStream buildManifestJsonFile(Map<String, String> manifestSha1s) throws IOException {
String manifestJson = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(manifestSha1s);
return new ByteArrayInputStream(manifestJson.getBytes());
}
public static void addToZipFile(InputStream inputStream, String zipEntity, ZipOutputStream zos) throws IOException {
ZipEntry zipEntry = new ZipEntry(zipEntity);
zos.putNextEntry(zipEntry);
byte[] bytes = new byte[1024];
int length;
while ((length = inputStream.read(bytes)) >= 0) {
zos.write(bytes, 0, length);
}
zos.closeEntry();
inputStream.close();
}
private void isValidDirectory(File file) {
Preconditions.checkNotNull(file);
Preconditions.checkArgument(file.exists(), "Directory does not exist: %s.", file);
Preconditions.checkArgument(file.isDirectory(), "Should be a directory: %s", file);
Preconditions.checkArgument(file.canRead(), "File cannot be written: %s", file);
}
private void isValidPushPackage(File file) {
File imageDir = new File(file + "/icon.iconset");
isValidDirectory(imageDir);
for (String imageFile : IMAGE_NAMES) {
Preconditions.checkArgument(new File(imageDir + "/" + imageFile).canRead(), "");
}
}
private void validatePushPackageFiles(String fileName) {
File file = new File(fileName);
isValidDirectory(file);
isValidPushPackage(file);
}
}