package org.commcare.xml; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; import java.util.Hashtable; import javax.crypto.Cipher; import javax.crypto.CipherOutputStream; import javax.crypto.NoSuchPaddingException; import javax.crypto.SecretKey; import org.commcare.android.database.SqlStorage; import org.commcare.android.database.user.models.FormRecord; import org.commcare.android.logic.GlobalConstants; import org.commcare.android.util.FileUtil; import org.commcare.android.util.SessionUnavailableException; import org.commcare.dalvik.application.CommCareApplication; import org.commcare.dalvik.odk.provider.InstanceProviderAPI; import org.commcare.dalvik.odk.provider.InstanceProviderAPI.InstanceColumns; import org.commcare.data.xml.TransactionParser; import org.commcare.xml.util.InvalidStructureException; import org.javarosa.core.services.storage.IStorageUtilityIndexed; import org.javarosa.core.services.storage.StorageFullException; import org.kxml2.io.KXmlParser; import org.kxml2.io.KXmlSerializer; import org.kxml2.kdom.Document; import org.kxml2.kdom.Element; import org.kxml2.kdom.Node; import org.xmlpull.v1.XmlPullParserException; import android.content.ContentValues; import android.content.Context; import android.net.Uri; /** * @author ctsims * */ public class FormInstanceXmlParser extends TransactionParser<FormRecord> { Context c; IStorageUtilityIndexed<FormRecord> storage; Hashtable<String, String> namespaces; int counter = 0; Cipher encrypter; private String destination; public FormInstanceXmlParser(KXmlParser parser, Context c, Hashtable<String, String> namespaces, String destination) { super(parser, null, null); this.c = c; this.namespaces = namespaces; this.destination = destination; } /* * (non-Javadoc) * @see org.commcare.data.xml.TransactionParser#parses(java.lang.String, java.lang.String) */ @Override public boolean parses(String name, String namespace) { if(namespaces.containsKey(namespace)) { return true; } return false; } public FormRecord parse() throws InvalidStructureException, IOException, XmlPullParserException, SessionUnavailableException { String xmlns = parser.getNamespace(); //Parse this subdocument into a dom Element element = new Element(); element.setName(parser.getName()); element.setNamespace(parser.getNamespace()); element.parse(this.parser); //Consume the end tag. //this.parser.next(); //create an actual document out of it. Document document = new Document(); document.addChild(Node.ELEMENT, element); KXmlSerializer serializer = new KXmlSerializer(); SecretKey key = CommCareApplication._().createNewSymetricKey(); String filePath = getFileDestination(namespaces.get(xmlns), destination); //Register this instance for inspection ContentValues values = new ContentValues(); values.put(InstanceColumns.DISPLAY_NAME, "Historical Form"); values.put(InstanceColumns.SUBMISSION_URI, ""); values.put(InstanceColumns.INSTANCE_FILE_PATH, filePath); values.put(InstanceColumns.JR_FORM_ID, xmlns); values.put(InstanceColumns.STATUS, InstanceProviderAPI.STATUS_COMPLETE); values.put(InstanceColumns.CAN_EDIT_WHEN_COMPLETE, false); Uri instanceRecord = c.getContentResolver().insert(InstanceColumns.CONTENT_URI,values); FormRecord r = new FormRecord(instanceRecord.toString(), FormRecord.STATUS_UNINDEXED, xmlns, key.getEncoded(),null, new Date(0)); IStorageUtilityIndexed<FormRecord> storage = storage(); OutputStream o = new FileOutputStream(filePath); CipherOutputStream cos = null; BufferedOutputStream bos = null; try { if(encrypter == null) { encrypter = Cipher.getInstance(key.getAlgorithm()); } encrypter.init(Cipher.ENCRYPT_MODE, key); cos = new CipherOutputStream(o, encrypter); bos = new BufferedOutputStream(cos,1024*256); serializer.setOutput(bos, "UTF-8"); document.write(serializer); storage.write(r); } catch (StorageFullException e) { throw new IOException(e.getMessage()); } //There's nothing we can do about any of these in code, failfast. catch (NoSuchAlgorithmException e) { throw new RuntimeException(e.getMessage()); } catch (NoSuchPaddingException e) { throw new RuntimeException(e.getMessage()); } catch (InvalidKeyException e) { throw new RuntimeException(e.getMessage()); } finally { //since bos might not have even been created. if(bos != null) { bos.close(); } else { o.close(); } } return r; } public IStorageUtilityIndexed<FormRecord> storage() throws SessionUnavailableException{ if(storage == null) { storage = CommCareApplication._().getUserStorage(FormRecord.class); } return storage; } private String getFileDestination(String formPath, String instancePath) { // Create new answer folder. String time = new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss").format(Calendar.getInstance().getTime()) + counter; String file = formPath.substring(formPath.lastIndexOf('/') + 1, formPath.lastIndexOf('.')); counter++; String path = instancePath + file + "_" + time; if (FileUtil.createFolder(path)) { return new File(path + "/" + file + "_" + time + ".xml").getAbsolutePath(); } throw new RuntimeException("Couldn't create folder needed to save form instance"); } /* * (non-Javadoc) * @see org.commcare.data.xml.TransactionParser#commit(java.lang.Object) */ @Override public void commit(FormRecord parsed) throws IOException { //This is unused. } }