package org.thoughtcrime.securesms; import android.annotation.TargetApi; import android.app.Activity; import android.content.Intent; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.view.MenuItem; import android.webkit.WebResourceRequest; import android.webkit.WebResourceResponse; import android.webkit.WebSettings; import android.webkit.WebView; import android.webkit.WebViewClient; import android.widget.Toast; import androidx.appcompat.app.ActionBar; import chat.delta.rpc.Rpc; import chat.delta.rpc.RpcException; import chat.delta.rpc.types.HttpResponse; import com.b44t.messenger.DcContext; import java.io.ByteArrayInputStream; import java.util.HashMap; import org.thoughtcrime.securesms.connect.DcHelper; import org.thoughtcrime.securesms.providers.PersistentBlobProvider; import org.thoughtcrime.securesms.util.IntentUtils; import org.thoughtcrime.securesms.util.JsonUtils; import org.thoughtcrime.securesms.util.MediaUtil; import org.thoughtcrime.securesms.util.Prefs; import org.thoughtcrime.securesms.util.Util; import org.thoughtcrime.securesms.util.ViewUtil; public class WebxdcStoreActivity extends PassphraseRequiredActionBarActivity { private static final String TAG = "WebxdcStoreActivity"; private DcContext dcContext; private Rpc rpc; @Override protected void onCreate(Bundle state, boolean ready) { setContentView(R.layout.web_view_activity); rpc = DcHelper.getRpc(this); dcContext = DcHelper.getContext(this); WebView webView = findViewById(R.id.webview); // add padding to avoid content hidden behind system bars ViewUtil.applyWindowInsets(findViewById(R.id.content_container)); ActionBar actionBar = getSupportActionBar(); if (actionBar != null) { actionBar.setDisplayHomeAsUpEnabled(true); actionBar.setTitle(R.string.webxdc_apps); } webView.setWebViewClient( new WebViewClient() { @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { String ext = MediaUtil.getFileExtensionFromUrl(Uri.parse(url).getPath()); if ("xdc".equals(ext)) { Util.runOnAnyBackgroundThread( () -> { try { HttpResponse httpResponse = rpc.getHttpResponse(dcContext.getAccountId(), url); byte[] blob = JsonUtils.decodeBase64(httpResponse.blob); Uri uri = PersistentBlobProvider.getInstance() .create( WebxdcStoreActivity.this, blob, "application/octet-stream", "app.xdc"); Intent intent = new Intent(); intent.setData(uri); setResult(Activity.RESULT_OK, intent); finish(); } catch (RpcException e) { e.printStackTrace(); Util.runOnMain( () -> Toast.makeText( WebxdcStoreActivity.this, "Error: " + e.getMessage(), Toast.LENGTH_LONG) .show()); } }); } else { IntentUtils.showInBrowser(WebxdcStoreActivity.this, url); } return true; } @TargetApi(Build.VERSION_CODES.N) @Override public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) { return shouldOverrideUrlLoading(view, request.getUrl().toString()); } @Override public WebResourceResponse shouldInterceptRequest( WebView view, WebResourceRequest request) { return interceptRequest(request.getUrl().toString()); } }); WebSettings webSettings = webView.getSettings(); webSettings.setJavaScriptEnabled(true); webSettings.setAllowFileAccess(false); webSettings.setAllowContentAccess(false); webSettings.setGeolocationEnabled(false); webSettings.setAllowFileAccessFromFileURLs(false); webSettings.setAllowUniversalAccessFromFileURLs(false); webSettings.setDatabaseEnabled(true); webSettings.setDomStorageEnabled(true); webView.setNetworkAvailable( true); // this does not block network but sets `window.navigator.isOnline` in js land webView.loadUrl(Prefs.getWebxdcStoreUrl(this)); } private WebResourceResponse interceptRequest(String url) { WebResourceResponse res = null; try { if (url == null) { throw new Exception("no url specified"); } HttpResponse httpResponse = rpc.getHttpResponse(dcContext.getAccountId(), url); String mimeType = httpResponse.mimetype; if (mimeType == null) { mimeType = "application/octet-stream"; } byte[] blob = JsonUtils.decodeBase64(httpResponse.blob); ByteArrayInputStream data = new ByteArrayInputStream(blob); res = new WebResourceResponse(mimeType, httpResponse.encoding, data); } catch (Exception e) { e.printStackTrace(); ByteArrayInputStream data = new ByteArrayInputStream( ("Could not load apps. Are you online?\n\n" + e.getMessage()).getBytes()); res = new WebResourceResponse("text/plain", "UTF-8", data); } HashMap headers = new HashMap<>(); headers.put("access-control-allow-origin", "*"); res.setResponseHeaders(headers); return res; } @Override public boolean onOptionsItemSelected(MenuItem item) { super.onOptionsItemSelected(item); switch (item.getItemId()) { case android.R.id.home: finish(); return true; } return false; } }