@Override protected HashMap<String, FormDetails> doInBackground(Void... values) { SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(Collect.getInstance().getBaseContext()); String downloadListUrl = settings.getString( PreferencesActivity.KEY_SERVER_URL, Collect.getInstance().getString(R.string.default_server_url)); String downloadPath = settings.getString(PreferencesActivity.KEY_FORMLIST_URL, "/formlist"); downloadListUrl += downloadPath; String auth = settings.getString(PreferencesActivity.KEY_AUTH, ""); // We populate this with available forms from the specified server. // <formname, details> HashMap<String, FormDetails> formList = new HashMap<String, FormDetails>(); // get shared HttpContext so that authentication and cookies are retained. HttpContext localContext = Collect.getInstance().getHttpContext(); HttpClient httpclient = WebUtils.createHttpClient(WebUtils.CONNECTION_TIMEOUT); DocumentFetchResult result = WebUtils.getXmlDocument(downloadListUrl, localContext, httpclient, auth); // If we can't get the document, return the error, cancel the task if (result.errorMessage != null) { if (result.responseCode == 401) { formList.put(DL_AUTH_REQUIRED, new FormDetails(result.errorMessage)); } else { formList.put(DL_ERROR_MSG, new FormDetails(result.errorMessage)); } return formList; } if (result.isOpenRosaResponse) { // Attempt OpenRosa 1.0 parsing Element xformsElement = result.doc.getRootElement(); if (!xformsElement.getName().equals("xforms")) { String error = "root element is not <xforms> : " + xformsElement.getName(); Log.e(t, "Parsing OpenRosa reply -- " + error); formList.put( DL_ERROR_MSG, new FormDetails( Collect.getInstance().getString(R.string.parse_openrosa_formlist_failed, error))); return formList; } String namespace = xformsElement.getNamespace(); if (!isXformsListNamespacedElement(xformsElement)) { String error = "root element namespace is incorrect:" + namespace; Log.e(t, "Parsing OpenRosa reply -- " + error); formList.put( DL_ERROR_MSG, new FormDetails( Collect.getInstance().getString(R.string.parse_openrosa_formlist_failed, error))); return formList; } int nElements = xformsElement.getChildCount(); for (int i = 0; i < nElements; ++i) { if (xformsElement.getType(i) != Element.ELEMENT) { // e.g., whitespace (text) continue; } Element xformElement = (Element) xformsElement.getElement(i); if (!isXformsListNamespacedElement(xformElement)) { // someone else's extension? continue; } String name = xformElement.getName(); if (!name.equalsIgnoreCase("xform")) { // someone else's extension? continue; } // this is something we know how to interpret String formId = null; String formName = null; String majorMinorVersion = null; String description = null; String downloadUrl = null; String manifestUrl = null; // don't process descriptionUrl int fieldCount = xformElement.getChildCount(); for (int j = 0; j < fieldCount; ++j) { if (xformElement.getType(j) != Element.ELEMENT) { // whitespace continue; } Element child = xformElement.getElement(j); if (!isXformsListNamespacedElement(child)) { // someone else's extension? continue; } String tag = child.getName(); if (tag.equals("formID")) { formId = XFormParser.getXMLText(child, true); if (formId != null && formId.length() == 0) { formId = null; } } else if (tag.equals("name")) { formName = XFormParser.getXMLText(child, true); if (formName != null && formName.length() == 0) { formName = null; } } else if (tag.equals("majorMinorVersion")) { majorMinorVersion = XFormParser.getXMLText(child, true); if (majorMinorVersion != null && majorMinorVersion.length() == 0) { majorMinorVersion = null; } } else if (tag.equals("descriptionText")) { description = XFormParser.getXMLText(child, true); if (description != null && description.length() == 0) { description = null; } } else if (tag.equals("downloadUrl")) { downloadUrl = XFormParser.getXMLText(child, true); if (downloadUrl != null && downloadUrl.length() == 0) { downloadUrl = null; } } else if (tag.equals("manifestUrl")) { manifestUrl = XFormParser.getXMLText(child, true); if (manifestUrl != null && manifestUrl.length() == 0) { manifestUrl = null; } } } if (formId == null || downloadUrl == null || formName == null) { String error = "Forms list entry " + Integer.toString(i) + " is missing one or more tags: formId, name, or downloadUrl"; Log.e(t, "Parsing OpenRosa reply -- " + error); formList.clear(); formList.put( DL_ERROR_MSG, new FormDetails( Collect.getInstance().getString(R.string.parse_openrosa_formlist_failed, error))); return formList; } /* * TODO: We currently don't care about major/minor version. maybe someday we will. */ // Integer modelVersion = null; // Integer uiVersion = null; // try { // if (majorMinorVersion == null || majorMinorVersion.length() == 0) { // modelVersion = null; // uiVersion = null; // } else { // int idx = majorMinorVersion.indexOf("."); // if (idx == -1) { // modelVersion = Integer.parseInt(majorMinorVersion); // uiVersion = null; // } else { // modelVersion = Integer.parseInt(majorMinorVersion.substring(0, idx)); // uiVersion = // (idx == majorMinorVersion.length() - 1) ? null : Integer // .parseInt(majorMinorVersion.substring(idx + 1)); // } // } // } catch (Exception e) { // e.printStackTrace(); // String error = "Forms list entry " + Integer.toString(i) + // " has an invalid majorMinorVersion: " + majorMinorVersion; // Log.e(t, "Parsing OpenRosa reply -- " + error); // formList.clear(); // formList.put(DL_ERROR_MSG, new FormDetails( // Collect.getInstance().getString(R.string.parse_openrosa_formlist_failed, // error))); // return formList; // } formList.put(formId, new FormDetails(formName, downloadUrl, manifestUrl, formId)); } } else { // Aggregate 0.9.x mode... // populate HashMap with form names and urls Element formsElement = result.doc.getRootElement(); int formsCount = formsElement.getChildCount(); for (int i = 0; i < formsCount; ++i) { if (formsElement.getType(i) != Element.ELEMENT) { // whitespace continue; } Element child = formsElement.getElement(i); String tag = child.getName(); String formId = null; if (tag.equals("formID")) { formId = XFormParser.getXMLText(child, true); if (formId != null && formId.length() == 0) { formId = null; } } if (tag.equalsIgnoreCase("form")) { String formName = XFormParser.getXMLText(child, true); if (formName != null && formName.length() == 0) { formName = null; } String downloadUrl = child.getAttributeValue(null, "url"); downloadUrl = downloadUrl.trim(); if (downloadUrl != null && downloadUrl.length() == 0) { downloadUrl = null; } if (downloadUrl == null || formName == null) { String error = "Forms list entry " + Integer.toString(i) + " is missing form name or url attribute"; Log.e(t, "Parsing OpenRosa reply -- " + error); formList.clear(); formList.put( DL_ERROR_MSG, new FormDetails( Collect.getInstance().getString(R.string.parse_legacy_formlist_failed, error))); return formList; } formList.put(formName, new FormDetails(formName, downloadUrl, null, formName)); } } } return formList; }
public static DecryptOutcome decryptAndValidateSubmission( Document doc, PrivateKey rsaPrivateKey, File instanceDir, File unEncryptedDir) throws ParsingException, FileSystemException, CryptoException { Element rootElement = doc.getRootElement(); String base64EncryptedSymmetricKey; String instanceIdMetadata = null; List<String> mediaNames = new ArrayList<String>(); String encryptedSubmissionFile; String base64EncryptedElementSignature; { Element base64Key = null; Element base64Signature = null; Element encryptedXml = null; for (int i = 0; i < rootElement.getChildCount(); ++i) { if (rootElement.getType(i) == Node.ELEMENT) { Element child = rootElement.getElement(i); String name = child.getName(); if (name.equals("base64EncryptedKey")) { base64Key = child; } else if (name.equals("base64EncryptedElementSignature")) { base64Signature = child; } else if (name.equals("encryptedXmlFile")) { encryptedXml = child; } else if (name.equals("media")) { Element media = child; for (int j = 0; j < media.getChildCount(); ++j) { if (media.getType(j) == Node.ELEMENT) { Element mediaChild = media.getElement(j); String mediaFileElementName = mediaChild.getName(); if (mediaFileElementName.equals("file")) { String mediaName = XFormParser.getXMLText(mediaChild, true); if (mediaName == null || mediaName.length() == 0) { mediaNames.add(null); } else { mediaNames.add(mediaName); } } } } } } } // verify base64Key if (base64Key == null) { throw new ParsingException("Missing base64EncryptedKey element in encrypted form."); } base64EncryptedSymmetricKey = XFormParser.getXMLText(base64Key, true); // get instanceID out of OpenRosa meta block instanceIdMetadata = XmlManipulationUtils.getOpenRosaInstanceId(rootElement); if (instanceIdMetadata == null) { throw new ParsingException("Missing instanceID within meta block of encrypted form."); } // get submission filename if (encryptedXml == null) { throw new ParsingException("Missing encryptedXmlFile element in encrypted form."); } encryptedSubmissionFile = XFormParser.getXMLText(encryptedXml, true); if (base64Signature == null) { throw new ParsingException( "Missing base64EncryptedElementSignature element in encrypted form."); } base64EncryptedElementSignature = XFormParser.getXMLText(base64Signature, true); } if (instanceIdMetadata == null || base64EncryptedSymmetricKey == null || base64EncryptedElementSignature == null || encryptedSubmissionFile == null) { throw new ParsingException("Missing one or more required elements of encrypted form."); } FormInstanceMetadata fim; try { fim = XmlManipulationUtils.getFormInstanceMetadata(rootElement); } catch (ParsingException e) { e.printStackTrace(); throw new ParsingException( "Unable to extract form instance medatadata from submission manifest. Cause: " + e.toString()); } if (!instanceIdMetadata.equals(fim.instanceId)) { throw new ParsingException( "InstanceID within metadata does not match that on top level element."); } boolean isValidated = FileSystemUtils.decryptSubmissionFiles( base64EncryptedSymmetricKey, fim, mediaNames, encryptedSubmissionFile, base64EncryptedElementSignature, rsaPrivateKey, instanceDir, unEncryptedDir); // and change doc to be the decrypted submission document File decryptedSubmission = new File(unEncryptedDir, "submission.xml"); doc = XmlManipulationUtils.parseXml(decryptedSubmission); if (doc == null) { return null; } // verify that the metadata matches between the manifest and the submission rootElement = doc.getRootElement(); FormInstanceMetadata sim = XmlManipulationUtils.getFormInstanceMetadata(rootElement); if (!fim.xparam.equals(sim.xparam)) { throw new ParsingException( "FormId or version in decrypted submission does not match that in manifest!"); } if (!fim.instanceId.equals(sim.instanceId)) { throw new ParsingException( "InstanceId in decrypted submission does not match that in manifest!"); } return new DecryptOutcome(doc, isValidated); }