package eu.faircode.email; /* This file is part of FairEmail. FairEmail is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. FairEmail is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with FairEmail. If not, see . Copyright 2018-2025 by Marcel Bokhorst (M66B) */ import android.app.Dialog; import android.content.Context; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.widget.Button; import android.widget.ProgressBar; import android.widget.TextView; import android.widget.Toast; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.AlertDialog; import java.net.HttpURLConnection; import java.net.URL; import java.net.URLDecoder; import java.nio.charset.StandardCharsets; import javax.net.ssl.HttpsURLConnection; public class FragmentDialogUnsubscribe extends FragmentDialogBase { private static final int UNSUBSCRIBE_TIMEOUT = 20 * 1000; @NonNull @Override public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) { final Bundle args = getArguments(); final String uri = args.getString("uri"); final String from = args.getString("from"); final Context context = getContext(); View view = LayoutInflater.from(context).inflate(R.layout.dialog_unsubscribe, null); final TextView tvSender = view.findViewById(R.id.tvSender); final TextView tvUri = view.findViewById(R.id.tvUri); final Button btnUnsubscribe = view.findViewById(R.id.btnUnsubscribe); final ProgressBar pbUnsubscribe = view.findViewById(R.id.pbUnsubscribe); tvSender.setText(from); tvUri.setText(uri); pbUnsubscribe.setVisibility(View.GONE); AlertDialog dialog = new AlertDialog.Builder(context) .setView(view) .setNeutralButton(android.R.string.cancel, null) .create(); btnUnsubscribe.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { new SimpleTask() { @Override protected void onPreExecute(Bundle args) { btnUnsubscribe.setEnabled(false); pbUnsubscribe.setVisibility(View.VISIBLE); } @Override protected void onPostExecute(Bundle args) { btnUnsubscribe.setEnabled(true); pbUnsubscribe.setVisibility(View.GONE); } @Override protected String onExecute(Context context, Bundle args) throws Throwable { final String uri = args.getString("uri"); final String request = "List-Unsubscribe=One-Click"; // https://datatracker.ietf.org/doc/html/rfc8058 int redirects = 0; URL url = new URL(uri); do { Log.i("Unsubscribe request=" + request + " uri=" + uri); HttpsURLConnection connection = (HttpsURLConnection) url.openConnection(); connection.setRequestMethod("POST"); connection.setDoInput(true); connection.setDoOutput(true); connection.setReadTimeout(UNSUBSCRIBE_TIMEOUT); connection.setConnectTimeout(UNSUBSCRIBE_TIMEOUT); ConnectionHelper.setUserAgent(context, connection); connection.setRequestProperty("Content-Length", Integer.toString(request.length())); connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); connection.connect(); try { connection.getOutputStream().write(request.getBytes()); int status = connection.getResponseCode(); if (status == HttpURLConnection.HTTP_MOVED_PERM || status == HttpURLConnection.HTTP_MOVED_TEMP || status == HttpURLConnection.HTTP_SEE_OTHER || status == 307 /* Temporary redirect */ || status == 308 /* Permanent redirect */) { String header = connection.getHeaderField("Location"); if (header != null) { String location = URLDecoder.decode(header, StandardCharsets.UTF_8.name()); Log.i("Unsubscribe redirect=" + location); url = new URL(url, location); continue; } } if (status >= 300) { String error = status + ": " + connection.getResponseMessage(); Log.i("Unsubscribe error=" + error); throw new IllegalArgumentException(error); } else Log.i("Unsubscribe status=" + status); return Helper.readStream(connection.getInputStream()); } finally { connection.disconnect(); } } while (++redirects <= ConnectionHelper.MAX_REDIRECTS); return null; } @Override protected void onExecuted(Bundle args, String output) { ToastEx.makeText(context, R.string.title_completed, Toast.LENGTH_LONG).show(); dialog.dismiss(); } @Override protected void onException(Bundle args, Throwable ex) { dialog.dismiss(); if (ex instanceof IllegalArgumentException) ToastEx.makeText(context, context.getString(R.string.title_unsubscribe_error, ex.getMessage()), Toast.LENGTH_LONG).show(); else Log.unexpectedError(getParentFragmentManager(), ex); } }.execute(FragmentDialogUnsubscribe.this, args, "unsubscribe"); } }); return dialog; } }