Search in sources :

Example 1 with HttpContext

use of org.opendatakit.httpclientandroidlib.protocol.HttpContext in project collect by opendatakit.

the class DownloadFormListTask method downloadMediaFileList.

private List<MediaFile> downloadMediaFileList(String manifestUrl) {
    if (manifestUrl == null) {
        return null;
    }
    // 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(manifestUrl, localContext, httpclient);
    if (result.errorMessage != null) {
        return null;
    }
    String errMessage = Collect.getInstance().getString(R.string.access_error, manifestUrl);
    if (!result.isOpenRosaResponse) {
        errMessage += Collect.getInstance().getString(R.string.manifest_server_error);
        Timber.e(errMessage);
        return null;
    }
    // Attempt OpenRosa 1.0 parsing
    Element manifestElement = result.doc.getRootElement();
    if (!manifestElement.getName().equals("manifest")) {
        errMessage += Collect.getInstance().getString(R.string.root_element_error, manifestElement.getName());
        Timber.e(errMessage);
        return null;
    }
    String namespace = manifestElement.getNamespace();
    if (!DownloadFormsTask.isXformsManifestNamespacedElement(manifestElement)) {
        errMessage += Collect.getInstance().getString(R.string.root_namespace_error, namespace);
        Timber.e(errMessage);
        return null;
    }
    int elements = manifestElement.getChildCount();
    List<MediaFile> files = new ArrayList<>();
    for (int i = 0; i < elements; ++i) {
        if (manifestElement.getType(i) != Element.ELEMENT) {
            // e.g., whitespace (text)
            continue;
        }
        Element mediaFileElement = manifestElement.getElement(i);
        if (!DownloadFormsTask.isXformsManifestNamespacedElement(mediaFileElement)) {
            // someone else's extension?
            continue;
        }
        String name = mediaFileElement.getName();
        if (name.equalsIgnoreCase("mediaFile")) {
            String filename = null;
            String hash = null;
            String downloadUrl = null;
            // don't process descriptionUrl
            int childCount = mediaFileElement.getChildCount();
            for (int j = 0; j < childCount; ++j) {
                if (mediaFileElement.getType(j) != Element.ELEMENT) {
                    // e.g., whitespace (text)
                    continue;
                }
                Element child = mediaFileElement.getElement(j);
                if (!DownloadFormsTask.isXformsManifestNamespacedElement(child)) {
                    // someone else's extension?
                    continue;
                }
                String tag = child.getName();
                switch(tag) {
                    case "filename":
                        filename = XFormParser.getXMLText(child, true);
                        if (filename != null && filename.length() == 0) {
                            filename = null;
                        }
                        break;
                    case "hash":
                        hash = XFormParser.getXMLText(child, true);
                        if (hash != null && hash.length() == 0) {
                            hash = null;
                        }
                        break;
                    case "downloadUrl":
                        downloadUrl = XFormParser.getXMLText(child, true);
                        if (downloadUrl != null && downloadUrl.length() == 0) {
                            downloadUrl = null;
                        }
                        break;
                }
            }
            if (filename == null || downloadUrl == null || hash == null) {
                errMessage += Collect.getInstance().getString(R.string.manifest_tag_error, Integer.toString(i));
                Timber.e(errMessage);
                return null;
            }
            files.add(new MediaFile(filename, hash, downloadUrl));
        }
    }
    return files;
}
Also used : MediaFile(org.odk.collect.android.logic.MediaFile) DocumentFetchResult(org.odk.collect.android.utilities.DocumentFetchResult) HttpClient(org.opendatakit.httpclientandroidlib.client.HttpClient) Element(org.kxml2.kdom.Element) HttpContext(org.opendatakit.httpclientandroidlib.protocol.HttpContext) ArrayList(java.util.ArrayList)

Example 2 with HttpContext

use of org.opendatakit.httpclientandroidlib.protocol.HttpContext in project collect by opendatakit.

the class DownloadFormsTask method downloadFile.

/**
 * Common routine to download a document from the downloadUrl and save the contents in the file
 * 'file'. Shared by media file download and form file download.
 * <p>
 * SurveyCTO: The file is saved into a temp folder and is moved to the final place if everything
 * is okay, so that garbage is not left over on cancel.
 *
 * @param file        the final file
 * @param downloadUrl the url to get the contents from.
 */
private void downloadFile(File file, String downloadUrl) throws IOException, TaskCancelledException, URISyntaxException, Exception {
    File tempFile = File.createTempFile(file.getName(), TEMP_DOWNLOAD_EXTENSION, new File(Collect.CACHE_PATH));
    URI uri;
    try {
        // assume the downloadUrl is escaped properly
        URL url = new URL(downloadUrl);
        uri = url.toURI();
    } catch (MalformedURLException | URISyntaxException e) {
        Timber.e(e, "Unable to get a URI for download URL : %s  due to %s : ", downloadUrl, e.getMessage());
        throw e;
    }
    // WiFi network connections can be renegotiated during a large form download sequence.
    // This will cause intermittent download failures.  Silently retry once after each
    // failure.  Only if there are two consecutive failures do we abort.
    boolean success = false;
    int attemptCount = 0;
    final int MAX_ATTEMPT_COUNT = 2;
    while (!success && ++attemptCount <= MAX_ATTEMPT_COUNT) {
        if (isCancelled()) {
            throw new TaskCancelledException(tempFile);
        }
        Timber.i("Started downloading to %s from %s", tempFile.getAbsolutePath(), downloadUrl);
        // get shared HttpContext so that authentication and cookies are retained.
        HttpContext localContext = Collect.getInstance().getHttpContext();
        HttpClient httpclient = WebUtils.createHttpClient(WebUtils.CONNECTION_TIMEOUT);
        // set up request...
        HttpGet req = WebUtils.createOpenRosaHttpGet(uri);
        req.addHeader(WebUtils.ACCEPT_ENCODING_HEADER, WebUtils.GZIP_CONTENT_ENCODING);
        HttpResponse response;
        try {
            response = httpclient.execute(req, localContext);
            int statusCode = response.getStatusLine().getStatusCode();
            if (statusCode != HttpStatus.SC_OK) {
                WebUtils.discardEntityBytes(response);
                if (statusCode == HttpStatus.SC_UNAUTHORIZED) {
                    // clear the cookies -- should not be necessary?
                    Collect.getInstance().getCookieStore().clear();
                }
                String errMsg = Collect.getInstance().getString(R.string.file_fetch_failed, downloadUrl, response.getStatusLine().getReasonPhrase(), String.valueOf(statusCode));
                Timber.e(errMsg);
                throw new Exception(errMsg);
            }
            // write connection to file
            InputStream is = null;
            OutputStream os = null;
            try {
                HttpEntity entity = response.getEntity();
                is = entity.getContent();
                Header contentEncoding = entity.getContentEncoding();
                if (contentEncoding != null && contentEncoding.getValue().equalsIgnoreCase(WebUtils.GZIP_CONTENT_ENCODING)) {
                    is = new GZIPInputStream(is);
                }
                os = new FileOutputStream(tempFile);
                byte[] buf = new byte[4096];
                int len;
                while ((len = is.read(buf)) > 0 && !isCancelled()) {
                    os.write(buf, 0, len);
                }
                os.flush();
                success = true;
            } finally {
                if (os != null) {
                    try {
                        os.close();
                    } catch (Exception e) {
                        Timber.e(e);
                    }
                }
                if (is != null) {
                    try {
                        // ensure stream is consumed...
                        final long count = 1024L;
                        while (is.skip(count) == count) {
                        // skipping to the end of the http entity
                        }
                    } catch (Exception e) {
                    // no-op
                    }
                    try {
                        is.close();
                    } catch (Exception e) {
                        Timber.e(e);
                    }
                }
            }
        } catch (Exception e) {
            Timber.e(e.toString());
            // silently retry unless this is the last attempt,
            // in which case we rethrow the exception.
            FileUtils.deleteAndReport(tempFile);
            if (attemptCount == MAX_ATTEMPT_COUNT) {
                throw e;
            }
        }
        if (isCancelled()) {
            FileUtils.deleteAndReport(tempFile);
            throw new TaskCancelledException(tempFile);
        }
    }
    Timber.d("Completed downloading of %s. It will be moved to the proper path...", tempFile.getAbsolutePath());
    FileUtils.deleteAndReport(file);
    String errorMessage = FileUtils.copyFile(tempFile, file);
    if (file.exists()) {
        Timber.w("Copied %s over %s", tempFile.getAbsolutePath(), file.getAbsolutePath());
        FileUtils.deleteAndReport(tempFile);
    } else {
        String msg = Collect.getInstance().getString(R.string.fs_file_copy_error, tempFile.getAbsolutePath(), file.getAbsolutePath(), errorMessage);
        Timber.w(msg);
        throw new RuntimeException(msg);
    }
}
Also used : MalformedURLException(java.net.MalformedURLException) HttpEntity(org.opendatakit.httpclientandroidlib.HttpEntity) GZIPInputStream(java.util.zip.GZIPInputStream) InputStream(java.io.InputStream) HttpGet(org.opendatakit.httpclientandroidlib.client.methods.HttpGet) OutputStream(java.io.OutputStream) FileOutputStream(java.io.FileOutputStream) HttpContext(org.opendatakit.httpclientandroidlib.protocol.HttpContext) HttpResponse(org.opendatakit.httpclientandroidlib.HttpResponse) URISyntaxException(java.net.URISyntaxException) URI(java.net.URI) URL(java.net.URL) URISyntaxException(java.net.URISyntaxException) MalformedURLException(java.net.MalformedURLException) IOException(java.io.IOException) GZIPInputStream(java.util.zip.GZIPInputStream) Header(org.opendatakit.httpclientandroidlib.Header) HttpClient(org.opendatakit.httpclientandroidlib.client.HttpClient) FileOutputStream(java.io.FileOutputStream) MediaFile(org.odk.collect.android.logic.MediaFile) File(java.io.File)

Example 3 with HttpContext

use of org.opendatakit.httpclientandroidlib.protocol.HttpContext in project collect by opendatakit.

the class DownloadFormsTask method downloadManifestAndMediaFiles.

private String downloadManifestAndMediaFiles(String tempMediaPath, String finalMediaPath, FormDetails fd, int count, int total) throws Exception {
    if (fd.getManifestUrl() == null) {
        return null;
    }
    publishProgress(Collect.getInstance().getString(R.string.fetching_manifest, fd.getFormName()), String.valueOf(count), String.valueOf(total));
    List<MediaFile> files = new ArrayList<MediaFile>();
    // 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(fd.getManifestUrl(), localContext, httpclient);
    if (result.errorMessage != null) {
        return result.errorMessage;
    }
    String errMessage = Collect.getInstance().getString(R.string.access_error, fd.getManifestUrl());
    if (!result.isOpenRosaResponse) {
        errMessage += Collect.getInstance().getString(R.string.manifest_server_error);
        Timber.e(errMessage);
        return errMessage;
    }
    // Attempt OpenRosa 1.0 parsing
    Element manifestElement = result.doc.getRootElement();
    if (!manifestElement.getName().equals("manifest")) {
        errMessage += Collect.getInstance().getString(R.string.root_element_error, manifestElement.getName());
        Timber.e(errMessage);
        return errMessage;
    }
    String namespace = manifestElement.getNamespace();
    if (!isXformsManifestNamespacedElement(manifestElement)) {
        errMessage += Collect.getInstance().getString(R.string.root_namespace_error, namespace);
        Timber.e(errMessage);
        return errMessage;
    }
    int elements = manifestElement.getChildCount();
    for (int i = 0; i < elements; ++i) {
        if (manifestElement.getType(i) != Element.ELEMENT) {
            // e.g., whitespace (text)
            continue;
        }
        Element mediaFileElement = manifestElement.getElement(i);
        if (!isXformsManifestNamespacedElement(mediaFileElement)) {
            // someone else's extension?
            continue;
        }
        String name = mediaFileElement.getName();
        if (name.equalsIgnoreCase("mediaFile")) {
            String filename = null;
            String hash = null;
            String downloadUrl = null;
            // don't process descriptionUrl
            int childCount = mediaFileElement.getChildCount();
            for (int j = 0; j < childCount; ++j) {
                if (mediaFileElement.getType(j) != Element.ELEMENT) {
                    // e.g., whitespace (text)
                    continue;
                }
                Element child = mediaFileElement.getElement(j);
                if (!isXformsManifestNamespacedElement(child)) {
                    // someone else's extension?
                    continue;
                }
                String tag = child.getName();
                switch(tag) {
                    case "filename":
                        filename = XFormParser.getXMLText(child, true);
                        if (filename != null && filename.length() == 0) {
                            filename = null;
                        }
                        break;
                    case "hash":
                        hash = XFormParser.getXMLText(child, true);
                        if (hash != null && hash.length() == 0) {
                            hash = null;
                        }
                        break;
                    case "downloadUrl":
                        downloadUrl = XFormParser.getXMLText(child, true);
                        if (downloadUrl != null && downloadUrl.length() == 0) {
                            downloadUrl = null;
                        }
                        break;
                }
            }
            if (filename == null || downloadUrl == null || hash == null) {
                errMessage += Collect.getInstance().getString(R.string.manifest_tag_error, Integer.toString(i));
                Timber.e(errMessage);
                return errMessage;
            }
            files.add(new MediaFile(filename, hash, downloadUrl));
        }
    }
    // OK we now have the full set of files to download...
    Timber.i("Downloading %d media files.", files.size());
    int mediaCount = 0;
    if (files.size() > 0) {
        File tempMediaDir = new File(tempMediaPath);
        File finalMediaDir = new File(finalMediaPath);
        FileUtils.checkMediaPath(tempMediaDir);
        FileUtils.checkMediaPath(finalMediaDir);
        for (MediaFile toDownload : files) {
            ++mediaCount;
            publishProgress(Collect.getInstance().getString(R.string.form_download_progress, fd.getFormName(), String.valueOf(mediaCount), String.valueOf(files.size())), String.valueOf(count), String.valueOf(total));
            // try {
            File finalMediaFile = new File(finalMediaDir, toDownload.getFilename());
            File tempMediaFile = new File(tempMediaDir, toDownload.getFilename());
            if (!finalMediaFile.exists()) {
                downloadFile(tempMediaFile, toDownload.getDownloadUrl());
            } else {
                String currentFileHash = FileUtils.getMd5Hash(finalMediaFile);
                String downloadFileHash = getMd5Hash(toDownload.getHash());
                if (currentFileHash != null && downloadFileHash != null && !currentFileHash.contentEquals(downloadFileHash)) {
                    // if the hashes match, it's the same file
                    // otherwise delete our current one and replace it with the new one
                    FileUtils.deleteAndReport(finalMediaFile);
                    downloadFile(tempMediaFile, toDownload.getDownloadUrl());
                } else {
                    // exists, and the hash is the same
                    // no need to download it again
                    Timber.i("Skipping media file fetch -- file hashes identical: %s", finalMediaFile.getAbsolutePath());
                }
            }
        // } catch (Exception e) {
        // return e.getLocalizedMessage();
        // }
        }
    }
    return null;
}
Also used : MediaFile(org.odk.collect.android.logic.MediaFile) DocumentFetchResult(org.odk.collect.android.utilities.DocumentFetchResult) HttpClient(org.opendatakit.httpclientandroidlib.client.HttpClient) Element(org.kxml2.kdom.Element) ArrayList(java.util.ArrayList) HttpContext(org.opendatakit.httpclientandroidlib.protocol.HttpContext) MediaFile(org.odk.collect.android.logic.MediaFile) File(java.io.File)

Example 4 with HttpContext

use of org.opendatakit.httpclientandroidlib.protocol.HttpContext in project collect by opendatakit.

the class Collect method getHttpContext.

/**
 * Construct and return a session context with shared cookieStore and credsProvider so a user
 * does not have to re-enter login information.
 */
public synchronized HttpContext getHttpContext() {
    // context holds authentication state machine, so it cannot be
    // shared across independent activities.
    HttpContext localContext = new BasicHttpContext();
    localContext.setAttribute(HttpClientContext.COOKIE_STORE, cookieStore);
    localContext.setAttribute(HttpClientContext.CREDS_PROVIDER, credsProvider);
    return localContext;
}
Also used : BasicHttpContext(org.opendatakit.httpclientandroidlib.protocol.BasicHttpContext) BasicHttpContext(org.opendatakit.httpclientandroidlib.protocol.BasicHttpContext) HttpContext(org.opendatakit.httpclientandroidlib.protocol.HttpContext)

Example 5 with HttpContext

use of org.opendatakit.httpclientandroidlib.protocol.HttpContext in project collect by opendatakit.

the class InstanceServerUploader method processChunk.

private boolean processChunk(int low, int high, Outcome outcome, Long... values) {
    if (values == null) {
        // don't try anything if values is null
        return false;
    }
    StringBuilder selectionBuf = new StringBuilder(InstanceColumns._ID + " IN (");
    String[] selectionArgs = new String[high - low];
    for (int i = 0; i < (high - low); i++) {
        if (i > 0) {
            selectionBuf.append(",");
        }
        selectionBuf.append("?");
        selectionArgs[i] = values[i + low].toString();
    }
    selectionBuf.append(")");
    String selection = selectionBuf.toString();
    String deviceId = new PropertyManager(Collect.getInstance().getApplicationContext()).getSingularProperty(PropertyManager.withUri(PropertyManager.PROPMGR_DEVICE_ID));
    // get shared HttpContext so that authentication and cookies are retained.
    HttpContext localContext = Collect.getInstance().getHttpContext();
    Map<Uri, Uri> uriRemap = new HashMap<Uri, Uri>();
    Cursor c = null;
    try {
        c = new InstancesDao().getInstancesCursor(selection, selectionArgs);
        if (c != null && c.getCount() > 0) {
            c.moveToPosition(-1);
            while (c.moveToNext()) {
                if (isCancelled()) {
                    return false;
                }
                publishProgress(c.getPosition() + 1 + low, values.length);
                String instance = c.getString(c.getColumnIndex(InstanceColumns.INSTANCE_FILE_PATH));
                String id = c.getString(c.getColumnIndex(InstanceColumns._ID));
                Uri toUpdate = Uri.withAppendedPath(InstanceColumns.CONTENT_URI, id);
                // Use the app's configured URL unless the form included a submission URL
                int subIdx = c.getColumnIndex(InstanceColumns.SUBMISSION_URI);
                String urlString = c.isNull(subIdx) ? getServerSubmissionURL() : c.getString(subIdx).trim();
                // add the deviceID to the request...
                try {
                    urlString += "?deviceID=" + URLEncoder.encode(deviceId, "UTF-8");
                } catch (UnsupportedEncodingException e) {
                    // unreachable...
                    Timber.i(e, "Error encoding URL for device id : %s", deviceId);
                }
                if (!uploadOneSubmission(urlString, id, instance, toUpdate, localContext, uriRemap, outcome)) {
                    // get credentials...
                    return false;
                }
            }
        }
    } finally {
        if (c != null) {
            c.close();
        }
    }
    return true;
}
Also used : InstancesDao(org.odk.collect.android.dao.InstancesDao) HashMap(java.util.HashMap) PropertyManager(org.odk.collect.android.logic.PropertyManager) HttpContext(org.opendatakit.httpclientandroidlib.protocol.HttpContext) UnsupportedEncodingException(java.io.UnsupportedEncodingException) Cursor(android.database.Cursor) Uri(android.net.Uri)

Aggregations

HttpContext (org.opendatakit.httpclientandroidlib.protocol.HttpContext)5 MediaFile (org.odk.collect.android.logic.MediaFile)3 HttpClient (org.opendatakit.httpclientandroidlib.client.HttpClient)3 File (java.io.File)2 ArrayList (java.util.ArrayList)2 Element (org.kxml2.kdom.Element)2 DocumentFetchResult (org.odk.collect.android.utilities.DocumentFetchResult)2 Cursor (android.database.Cursor)1 Uri (android.net.Uri)1 FileOutputStream (java.io.FileOutputStream)1 IOException (java.io.IOException)1 InputStream (java.io.InputStream)1 OutputStream (java.io.OutputStream)1 UnsupportedEncodingException (java.io.UnsupportedEncodingException)1 MalformedURLException (java.net.MalformedURLException)1 URI (java.net.URI)1 URISyntaxException (java.net.URISyntaxException)1 URL (java.net.URL)1 HashMap (java.util.HashMap)1 GZIPInputStream (java.util.zip.GZIPInputStream)1