Java tutorial
/** * InboxPager, an android email client. * Copyright (C) 2016 ITPROJECTS * <p/> * This program 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. * <p/> * This program 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 this program. If not, see <http://www.gnu.org/licenses/>. **/ package net.inbox; import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.os.Bundle; import android.os.Environment; import android.preference.PreferenceManager; import android.support.v4.content.ContextCompat; import android.support.v7.app.AlertDialog; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.Toolbar; import android.text.Html; import android.text.Spanned; import android.util.Base64; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.ImageView; import android.widget.ListView; import android.widget.TextView; import net.inbox.db.Attachment; import net.inbox.db.DBAccess; import net.inbox.db.Message; import net.inbox.dialogs.DialogsCerts; import net.inbox.dialogs.Dialogs; import net.inbox.dialogs.SpinningStatus; import net.inbox.server.Handler; import net.inbox.server.IMAP; import net.inbox.server.POP; import net.inbox.server.Utils; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.UUID; import java.util.regex.Matcher; import java.util.regex.Pattern; public class InboxMessage extends AppCompatActivity { private DBAccess db; private Handler handler; private TextView tv_contents; private TextView tv_texts; private ImageView iv_ssl_auth; private ImageView iv_gpg_crypto; private int current_inbox = -2; // GPG variables private boolean crypto_locked = false; private boolean msg_encrypted; private boolean msg_signed; private String msg_contents; private String msg_signature; private boolean imap_or_pop; private boolean no_send; private boolean good_incoming_server = false; private boolean btn_texts_ready = true; private int btn_texts_state = 0; private ArrayList<String[]> btn_texts_states; private Message current; private Attachment chosen_att; private List<Attachment> attachments; // Folder picker variables private boolean save_in_db; private AlertDialog dialog_folder_picker; private ArrayAdapter lv_adapter; private File chosen_folder; private File current_path = new File("/"); private List<File> f_folders = new ArrayList<>(); private List<String> s_folders = new ArrayList<>(); private TextView tv_page_attachments; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.message); // Get the database db = Pager.get_db(); current = db.get_message(getIntent().getExtras().getInt("db_id")); current_inbox = getIntent().getExtras().getInt("db_inbox"); imap_or_pop = getIntent().getExtras().getBoolean("imap_or_pop"); no_send = getIntent().getExtras().getBoolean("no_send"); // Crypto information if (current.get_contents_crypto() != null) { // GPG Encrypted msg_encrypted = current.get_content_type().toLowerCase().contains("application/pgp-encrypted"); // GPG Signed msg_signed = current.get_content_type().toLowerCase().contains("application/pgp-signature"); } Toolbar tb = (Toolbar) findViewById(R.id.message_toolbar); setSupportActionBar(tb); // Find the title TextView tv_t; for (int i = 0; i < tb.getChildCount(); ++i) { int idd = tb.getChildAt(i).getId(); if (idd == -1) { tv_t = (TextView) tb.getChildAt(i); tv_t.setTextColor(ContextCompat.getColor(this, R.color.color_title)); tv_t.setTypeface(Pager.tf); break; } } if (getSupportActionBar() != null) { String s_title = getIntent().getExtras().getString("title"); if (s_title != null) getSupportActionBar().setTitle(s_title.toUpperCase()); } // Message Attachments Counter tv_page_attachments = (TextView) findViewById(R.id.message_attachments); tv_page_attachments.setTypeface(Pager.tf); tv_page_attachments.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { dialog_list_attachments(); } }); // Setting the number of attachments if (current.get_attachments() > 0) { tv_page_attachments.setVisibility(View.VISIBLE); } else { tv_page_attachments.setVisibility(View.GONE); } TextView tv_from = (TextView) findViewById(R.id.message_from); TextView tv_cc = (TextView) findViewById(R.id.message_cc); TextView tv_bcc = (TextView) findViewById(R.id.message_bcc); TextView tv_subject = (TextView) findViewById(R.id.message_subject); TextView tv_date = (TextView) findViewById(R.id.message_date); tv_contents = (TextView) findViewById(R.id.message_contents); tv_texts = (TextView) findViewById(R.id.message_loop); // Setting the correct size SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); float sz = Float.parseFloat(prefs.getString("sett_msg_font_size", "100")); tv_from.setTextSize(sz); tv_cc.setTextSize(sz); tv_bcc.setTextSize(sz); tv_subject.setTextSize(sz); tv_date.setTextSize(sz); tv_contents.setTextSize(sz); tv_texts.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (btn_texts_states.size() > 1 && btn_texts_ready) { btn_texts_ready = false; if (btn_texts_state == (btn_texts_states.size() - 1)) { btn_texts_state = 0; } else { ++btn_texts_state; } populate_contents(); } } }); TextView tv_from_title = (TextView) findViewById(R.id.message_from_title); tv_from_title.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { dialog_originating(); } }); // Insert the data tv_from.setText(current.get_from()); if (current.get_cc() == null) { tv_cc.setVisibility(View.GONE); findViewById(R.id.message_cc_title).setVisibility(View.GONE); } else { tv_cc.setText(current.get_cc()); } if (current.get_bcc() == null) { tv_bcc.setVisibility(View.GONE); findViewById(R.id.message_bcc_title).setVisibility(View.GONE); } else { tv_bcc.setText(current.get_bcc()); } tv_subject.setText(current.get_subject()); tv_date.setText(current.get_date()); // Counting the states of the texts' button set_btn_texts(); // Setting up the SSL authentication application iv_ssl_auth = (ImageView) findViewById(R.id.ssl_auth_img_vw); iv_ssl_auth.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { dialog_servers(); } }); // Set unseen -> seen if (!(current.get_seen())) { db.seen_unseen_message(current.get_account(), current.get_uid(), true); } // GPG crypto activity iv_gpg_crypto = (ImageView) findViewById(R.id.iv_gpg_crypto); iv_gpg_crypto.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (!crypto_locked && crypto_package()) { gpg_crypto_start(); } } }); if (current.get_contents_crypto() != null) { iv_gpg_crypto.setVisibility(View.VISIBLE); } else { iv_gpg_crypto.setVisibility(View.GONE); } } @Override public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.message_action_btns, menu); if (no_send) { menu.findItem(R.id.write_reply_menu).setVisible(false); } else { menu.findItem(R.id.write_reply_menu).setVisible(true); } return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.log_menu: Dialogs.dialog_view_log(this); break; case R.id.delete_from_server_menu: delete_message(); break; case R.id.delete_from_local_menu: db.delete_full_message(current.get_id()); current.set_full_msg(null); Dialogs.toaster(false, getString(R.string.message_no_full_message), this); break; case R.id.download_message_menu: // Starting a spinning animation dialog start_saving_full_message(true); break; case R.id.save_message_menu: // Save a message to device if (current.get_full_msg() == null || current.get_full_msg().isEmpty()) { dialog_download_prompt(); } else { save_in_db = false; dialog_folder_picker(); } break; case R.id.write_reply_menu: if (no_send) { Dialogs.toaster(false, getString(R.string.err_server_not_configured), this); } else { write_reply(); } break; case R.id.details_menu: // Display the full message if (current.get_full_msg() == null || current.get_full_msg().isEmpty()) { Dialogs.toaster(false, getString(R.string.err_no_full_msg), this); } else { Dialogs.dialog_view_message(current.get_full_msg(), this); } break; } return true; } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (resultCode == 19091) { crypto_locked = true; iv_gpg_crypto.setImageResource(R.drawable.padlock_open_inverse); msg_signature = data.getStringExtra("msg-signature"); if (data.getIntExtra("ret-code", 0) == 92) { msg_contents = data.getStringExtra("message-crypto"); // MIME parsing gpg_mime_parsing(); } if (msg_signature != null && msg_signature.length() > 0) { final AppCompatActivity ct = this; ImageView iv_gpg_signature = (ImageView) findViewById(R.id.iv_gpg_signature); iv_gpg_signature.setVisibility(View.VISIBLE); iv_gpg_signature.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Dialogs.dialog_server_ext(getString(R.string.open_pgp_message_signature), msg_signature, ct); } }); } } } @Override public void finish() { super.finish(); overridePendingTransition(R.anim.right_in, R.anim.right_out); } /** * Creates/refreshes the list of readable message texts. **/ private void set_btn_texts() { btn_texts_states = new ArrayList<>(); if (current.get_contents_plain() != null && !current.get_contents_plain().isEmpty()) { btn_texts_states.add(new String[] { getString(R.string.message_contents_loop_plain), "1" }); } if (current.get_contents_html() != null && !current.get_contents_html().isEmpty()) { btn_texts_states.add(new String[] { getString(R.string.message_contents_loop_html), "2" }); } if (current.get_contents_other() != null && !current.get_contents_other().isEmpty()) { btn_texts_states.add(new String[] { getString(R.string.message_contents_loop_other), "3" }); } if (btn_texts_states.size() < 1) { tv_texts.setVisibility(View.GONE); } else if (btn_texts_states.size() == 1) { btn_texts_state = 0; populate_contents(); } else { for (int i = 0; i < btn_texts_states.size(); ++i) { btn_texts_states.get(i)[0] += " " + (i + 1) + "/" + btn_texts_states.size(); } btn_texts_state = 0; populate_contents(); } } /** * Sets refreshed message texts. **/ private void populate_contents() { tv_contents.setText(""); switch (btn_texts_states.get(btn_texts_state)[1]) { case "0": break; case "1": if (current.get_contents_plain() != null) { if (!current.get_charset_plain().isEmpty() && !current.get_charset_plain().equalsIgnoreCase("UTF-8") && !current.get_charset_plain().equalsIgnoreCase("NIL")) { try { current.set_contents_plain( new String(current.get_contents_plain().getBytes(current.get_charset_plain()))); } catch (UnsupportedEncodingException e) { System.out.println("Exception: " + e.getMessage()); Pager.log += getString(R.string.ex_field) + e.getMessage() + "\n\n"; } } tv_contents.setText(current.get_contents_plain()); } else { tv_contents.setText(""); } tv_texts.setText(btn_texts_states.get(btn_texts_state)[0]); break; case "2": if (current.get_contents_html() != null) { if (!current.get_charset_html().isEmpty() && !current.get_charset_html().equalsIgnoreCase("UTF-8") && !current.get_charset_html().equalsIgnoreCase("NIL")) { try { html_text(new String(current.get_contents_html().getBytes(current.get_charset_html()))); } catch (UnsupportedEncodingException e) { System.out.println("Exception: " + e.getMessage()); Pager.log += getString(R.string.ex_field) + e.getMessage() + "\n\n"; } } html_text(current.get_contents_html()); } else { tv_contents.setText(""); } tv_texts.setText(btn_texts_states.get(btn_texts_state)[0]); break; case "3": if (current.get_contents_other() != null) { tv_contents.setText(current.get_contents_other()); } else { tv_contents.setText(""); } tv_texts.setText(btn_texts_states.get(btn_texts_state)[0]); break; } btn_texts_ready = true; } private void html_text(String html_txt) { Spanned spn; if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) { spn = Html.fromHtml(html_txt, Html.FROM_HTML_MODE_LEGACY); } else { spn = Html.fromHtml(html_txt); } tv_contents.setText(spn); } /** * Finishes activity to start a new message. **/ private void write_reply() { String ret; Pattern pat = Pattern.compile(".*<(.*)>.*", Pattern.CASE_INSENSITIVE); Matcher mat = pat.matcher(current.get_from()); if (mat.matches()) { ret = mat.group(1); } else { ret = current.get_from().trim(); } // Declaring the result of the activity Intent ret_intent = new Intent(); // Request ListView re-flow ret_intent = ret_intent.putExtra("reply-to", ret); ret_intent = ret_intent.putExtra("subject", current.get_subject()); setResult(10101, ret_intent); // End activity finish(); } /** * Dialog showing the IP of the message sender. **/ private void dialog_originating() { TextView tv_msg = new TextView(this); tv_msg.setTextIsSelectable(true); tv_msg.setPadding(20, 20, 20, 0); tv_msg.setText(current.get_received()); AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle(getString(R.string.message_dialog)); builder.setView(tv_msg); builder.setPositiveButton(getString(android.R.string.ok), null); builder.show(); } private void dialog_list_attachments() { AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle(getString(R.string.attch_title)); if (!crypto_locked) attachments = db.get_all_attachments_of_msg(current.get_id()); String[] str_temp = new String[attachments.size()]; if (str_temp.length > 0) { for (int i = 0; i < str_temp.length; ++i) { int bytes = attachments.get(i).get_size(); if (attachments.get(i).get_name().trim().isEmpty()) { attachments.get(i).set_name(getString(R.string.attch_no_name) + UUID.randomUUID().toString()); str_temp[i] = "[ " + attachments.get(i).get_name() + " ]\n"; } else { str_temp[i] = attachments.get(i).get_name() + "\n"; } str_temp[i] += attachments.get(i).get_mime_type().toLowerCase() + ", "; if (bytes < 0) { str_temp[i] += "? " + getString(R.string.attch_bytes); } else { if (bytes < 1024) { str_temp[i] += bytes + " " + getString(R.string.attch_bytes); } else if (bytes >= 1024 && bytes < 1048576) { str_temp[i] += (bytes / 1024) + " " + getString(R.string.attch_kilobytes); } else { str_temp[i] += (bytes / 1048576) + " " + getString(R.string.attch_megabytes); } } } builder.setItems(str_temp, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int choice) { dialog.dismiss(); chosen_att = attachments.get(choice); dialog_folder_picker(); } }); builder.show(); } } private void dialog_download_prompt() { AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle(getString(R.string.progress_downloading)); builder.setMessage(getString(R.string.message_empty_go_download)); builder.setCancelable(true); builder.setPositiveButton(getString(android.R.string.ok), new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { if (msg_encrypted) { save_in_db = false; dialog_folder_picker(); } else { dialog_download_and_keep(); } } }); builder.show(); } /** * Dialog choose to keep a DB copy of the full message. **/ private void dialog_download_and_keep() { AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle(getString(R.string.progress_keep_msg)); builder.setMessage(getString(R.string.message_keep_in_database)); builder.setCancelable(false); builder.setPositiveButton(getString(R.string.btn_yes), new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { save_in_db = true; dialog_folder_picker(); } }); builder.setNegativeButton(getString(R.string.btn_no), new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { save_in_db = false; dialog_folder_picker(); } }); builder.show(); } /** * Dialog picking the folder for the attachment download. **/ private void dialog_folder_picker() { chosen_folder = null; refresh_folders(1); AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle(getString(R.string.folder_title)); builder.setPositiveButton(getString(R.string.btn_up_one_level), null); // Populating folders ListView lv_folders = new ListView(this); lv_folders.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View v, int position, long id) { // Obtain the index of the chosen folder String str = (String) parent.getItemAtPosition(position); for (File f : f_folders) { if (f.getName().equals(str)) { current_path = f; break; } } refresh_folders(2); lv_adapter.notifyDataSetChanged(); } }); // Continuing download lv_folders.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() { @Override public boolean onItemLongClick(AdapterView<?> parent, View v, int position, long id) { // Obtain the index of the chosen folder String str = (String) parent.getItemAtPosition(position); for (File f : f_folders) { if (f.getName().equals(str)) { chosen_folder = f; break; } } dialog_folder_picker.dismiss(); if (chosen_att == null) { full_message_tests(); } else { attachment_tests(); } return true; } }); lv_adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, s_folders); lv_folders.setAdapter(lv_adapter); builder.setView(lv_folders); dialog_folder_picker = builder.show(); // Reassigning button to prevent early dialog ending dialog_folder_picker.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { if (current_path.getParent() != null) { refresh_folders(3); lv_adapter.notifyDataSetChanged(); } } }); } /** * Reconfigures the folder picker with new items. **/ private void refresh_folders(int level_up) { f_folders.clear(); s_folders.clear(); switch (level_up) { case 1: // Add SDCARD directory if (Environment.getExternalStorageDirectory().exists()) { current_path = Environment.getExternalStorageDirectory(); } break; case 2: break; case 3: // Listing folder, one level up current_path = new File(current_path.getParent()); break; } if (current_path.exists() && current_path.listFiles() != null) { // Add all path [array] to folder_list f_folders.addAll(Arrays.asList(current_path.listFiles())); } // Remove files, keep directories for (int i = f_folders.size() - 1; i >= 0; i--) { if (!f_folders.get(i).isDirectory()) { f_folders.remove(f_folders.get(i)); } else { s_folders.add(f_folders.get(i).getName()); } } // Alphabetic sort; Collections.sort(s_folders, new Comparator<String>() { public int compare(String s1, String s2) { return s1.compareTo(s2); } }); } /** * Checks for a series of conditions that may prevent file saving. **/ private void attachment_tests() { // Cannot write to this folder, android permissions not set to enabled if (!check_writable()) { AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle(getString(R.string.err_title_android_permission)); builder.setMessage(getString(R.string.err_msg_android_permission)); builder.setPositiveButton(getString(android.R.string.ok), null); builder.show(); return; } // Cannot write to this folder, permissions on the device if (!chosen_folder.canWrite()) { AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle(getString(R.string.err_title_write_perms)); builder.setMessage(getString(R.string.err_msg_write_perms)); builder.setPositiveButton(getString(android.R.string.ok), null); builder.show(); return; } // Not enough space, and also not an exact calculation int file_size; if (chosen_att != null && chosen_att.get_size() != -1) { file_size = chosen_att.get_size(); } else { file_size = current.get_size(); } if (chosen_folder.getUsableSpace() < file_size) { AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle(getString(R.string.err_title_no_space)); builder.setMessage(getString(R.string.err_msg_no_space)); builder.setPositiveButton(getString(android.R.string.ok), null); builder.show(); return; } // Quoted-printable warning if (chosen_att.get_transfer_encoding().equalsIgnoreCase("quoted-printable")) { Dialogs.toaster(false, getString(R.string.err_msg_qp_warn), this); } // File already exists, overwrite? String full_url = chosen_folder.getPath() + "/"; if (chosen_att != null) { full_url += chosen_att.get_name(); } else { full_url += current.get_subject(); } boolean already_exists = (new File(full_url)).exists(); if (already_exists) { AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle(getString(R.string.err_title_already_exists)); builder.setMessage(getString(R.string.err_msg_already_exists)); builder.setPositiveButton(getString(android.R.string.yes), new AlertDialog.OnClickListener() { public void onClick(DialogInterface dialog, int which) { if (msg_encrypted && crypto_locked) { write_attachment_from_db(); } else if (current.get_full_msg() == null || current.get_full_msg().isEmpty()) { start_saving_attachment(); } else { write_attachment_from_db(); } } }); builder.setNegativeButton(getString(android.R.string.no), null); builder.show(); } else { if (msg_encrypted && crypto_locked) { write_attachment_from_db(); } else if (current.get_full_msg() == null || current.get_full_msg().isEmpty()) { start_saving_attachment(); } else { write_attachment_from_db(); } } } /** * Checks for a series of conditions that may prevent file saving. **/ private void full_message_tests() { // Cannot write to this folder, android permissions not set to enabled if (!check_writable()) { AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle(getString(R.string.err_title_android_permission)); builder.setMessage(getString(R.string.err_msg_android_permission)); builder.setPositiveButton(getString(android.R.string.ok), null); builder.show(); return; } // Cannot write to this folder, permissions on the device if (!chosen_folder.canWrite()) { AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle(getString(R.string.err_title_write_perms)); builder.setMessage(getString(R.string.err_msg_write_perms)); builder.setPositiveButton(getString(android.R.string.ok), null); builder.show(); return; } // Not enough space, and also not an exact calculation int file_size = current.get_size(); if (chosen_folder.getUsableSpace() < file_size) { AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle(getString(R.string.err_title_no_space)); builder.setMessage(getString(R.string.err_msg_no_space)); builder.setPositiveButton(getString(android.R.string.ok), null); builder.show(); return; } // File already exists, overwrite? String full_url = chosen_folder.getPath() + "/"; if (chosen_att != null) { full_url += chosen_att.get_name(); } else { full_url += current.get_subject(); } boolean already_exists = (new File(full_url)).exists(); if (already_exists) { AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle(getString(R.string.err_title_already_exists)); builder.setMessage(getString(R.string.err_msg_already_exists)); builder.setPositiveButton(getString(android.R.string.yes), new AlertDialog.OnClickListener() { public void onClick(DialogInterface dialog, int which) { if (current.get_full_msg() == null || current.get_full_msg().isEmpty()) { start_saving_full_message(false); } else { write_full_message(); } } }); builder.setNegativeButton(getString(android.R.string.no), null); builder.show(); } else { if (current.get_full_msg() == null || current.get_full_msg().isEmpty()) { start_saving_full_message(false); } else { write_full_message(); } } } private boolean check_writable() { String permission = "android.permission.WRITE_EXTERNAL_STORAGE"; int res = checkCallingOrSelfPermission(permission); return (res == PackageManager.PERMISSION_GRANTED); } private void start_saving_attachment() { // Starting a spinning animation dialog SpinningStatus spt = new SpinningStatus(false, this, handler); spt.execute(); spt.onProgressUpdate(getString(R.string.progress_downloading), getString(R.string.progress_fetch_attachment)); // Starting Handler if (imap_or_pop) { handler = new IMAP(this); } else { handler = new POP(this); } handler.sp = spt; handler.start(); handler.attachment_action(current.get_account(), chosen_att, chosen_folder.getPath(), this); } /** * Continues the file saving process. * Convert attachments from BASE64 to normal. **/ public void write_attachment_from_db() { // Offline IMAP attachment file can give errors if (imap_or_pop) { Pattern pat = Pattern.compile(".*boundary=\"(.*)\".*", Pattern.CASE_INSENSITIVE); Matcher mat = pat.matcher(current.get_content_type()); if (mat.matches()) { chosen_att.set_boundary("--" + mat.group(1)); chosen_att.set_pop_indx(chosen_att.get_imap_uid()); } else { Pager.log += getString(R.string.err_imap_attachment_saving) + "\n\n"; Dialogs.dialog_error_line(getString(R.string.err_imap_attachment_saving), this); return; } } String att; if (msg_encrypted && crypto_locked) { att = Utils.mime_part_section(current.get_contents_crypto(), chosen_att.get_pop_indx(), chosen_att.get_boundary()); } else { att = Utils.mime_part_section(current.get_full_msg(), chosen_att.get_pop_indx(), chosen_att.get_boundary()); } try { FileOutputStream f_stream = new FileOutputStream( new File(chosen_folder.getAbsoluteFile() + "/" + chosen_att.get_name())); // Converting transfer encoding if (chosen_att != null) { // Parsing file download if (chosen_att.get_transfer_encoding().equalsIgnoreCase("BASE64")) { boolean CR = false; StringBuilder sb_tmp = new StringBuilder(0); for (int i = 0; i < att.length(); ++i) { if (att.charAt(i) == '\n') { if (CR) { f_stream.write(Base64.decode(sb_tmp.toString().getBytes(), Base64.DEFAULT)); sb_tmp.setLength(0); CR = false; } } else if (att.charAt(i) == '\r') { CR = true; } else if (att.charAt(i) != '=') { sb_tmp.append(att.charAt(i)); CR = false; } } if (sb_tmp.length() > 0) { f_stream.write(Base64.decode(sb_tmp.toString().getBytes(), Base64.DEFAULT)); } f_stream.close(); } else { // 7BIT, 8BIT, BINARY, QUOTED-PRINTABLE f_stream.write(att.getBytes()); f_stream.close(); } } } catch (IOException e) { Pager.log += e.getMessage() + "\n\n"; Dialogs.dialog_exception(e, this); } } private void start_saving_full_message(boolean in_db_only) { // Starting a spinning animation dialog SpinningStatus sp = new SpinningStatus(true, this, handler); sp.execute(); sp.onProgressUpdate(getString(R.string.progress_downloading), getString(R.string.progress_fetch_msg) + "."); // Starting refresh INBOX if (imap_or_pop) { handler = new IMAP(this); } else { handler = new POP(this); } handler.sp = sp; handler.start(); if (in_db_only) { handler.msg_action(current.get_account(), current, null, save_in_db, this); } else { handler.msg_action(current.get_account(), current, chosen_folder.getPath(), save_in_db, this); } } /** * Available full messages are directly written to file. **/ private void write_full_message() { // Prepare for direct file write try { FileOutputStream f_stream = new FileOutputStream( new File(chosen_folder.getAbsoluteFile() + "/" + current.get_subject() + ".eml")); f_stream.write(current.get_full_msg().getBytes()); f_stream.close(); Dialogs.toaster(true, getString(R.string.progress_download_complete), this); } catch (IOException ioe) { Pager.log += ioe.getMessage() + "\n\n"; Dialogs.dialog_exception(ioe, this); } } /** * Deletes the current message from server and local database. **/ private void delete_message() { // Starting a spinning animation dialog SpinningStatus spt = new SpinningStatus(false, this, handler); spt.execute(); spt.onProgressUpdate(getString(R.string.progress_deleting), getString(R.string.progress_deleting_msg) + " " + current.get_subject()); // Starting Handler if (imap_or_pop) { handler = new IMAP(this); } else { handler = new POP(this); } handler.sp = spt; handler.start(); handler.move_action(current.get_account(), current, this); } /** * Deletes the current message from UI. **/ public void delete_message_ui() { // Request ListView re-flow setResult(1010101); // End activity finish(); } public void connection_security() { good_incoming_server = handler.get_hostname_verify(); if (good_incoming_server) { if (handler != null && handler.get_last_connection_data() != null && (handler.get_last_connection_data_id() == current_inbox)) { good_incoming_server = handler.get_last_connection_data().size() > 0; iv_ssl_auth.setVisibility(View.VISIBLE); if (good_incoming_server) { good_incoming_server = true; iv_ssl_auth.setImageResource(R.drawable.padlock_normal); } else { good_incoming_server = false; iv_ssl_auth.setImageResource(R.drawable.padlock_error); } } else { good_incoming_server = false; iv_ssl_auth.setVisibility(View.GONE); } } else { iv_ssl_auth.setVisibility(View.VISIBLE); iv_ssl_auth.setImageResource(R.drawable.padlock_error); Dialogs.toaster(true, getString(R.string.err_action_failed), this); } } /** * Intermediaries' SSL certificates of the last live connection. **/ private void dialog_servers() { if (good_incoming_server) { DialogsCerts.dialog_certs(this, handler.get_last_connection_data()); } else { AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle(getString(R.string.ssl_auth_popup_title)); builder.setMessage(getString(R.string.ssl_auth_popup_bad_connection)); builder.setPositiveButton(getString(android.R.string.ok), null); builder.show(); } } /** * Looks for available and supported encryption packages. * OpenKeychain for GPG. **/ private boolean crypto_package() { PackageManager pack_man = getPackageManager(); try { pack_man.getPackageInfo(Pager.open_key_chain, PackageManager.GET_ACTIVITIES); return pack_man.getApplicationInfo(Pager.open_key_chain, 0).enabled; } catch (PackageManager.NameNotFoundException e) { Dialogs.toaster(false, getString(R.string.open_pgp_none_found), this); return false; } } /** * Starts GPG message work. **/ private void gpg_crypto_start() { // Starting decryption and verification intent Intent gpg = new Intent(this, InboxGPG.class); Bundle b = new Bundle(); int request_code; if (msg_encrypted) { request_code = 92; // Extracting the encrypted part attachments = db.get_all_attachments_of_msg(current.get_id()); boolean pgp_content = false; for (Attachment a : attachments) { if (a.get_mime_type().toLowerCase().contains("pgp-encrypted")) { pgp_content = true; } else if (pgp_content) { msg_contents = current.get_contents_crypto(); if (msg_contents == null) { Pager.log += getString(R.string.err_missing_crypto_mime) + "\n\n"; Dialogs.toaster(false, getString(R.string.err_missing_crypto_mime), this); return; } int start_index = -1; int end_index = -1; Pattern p = Pattern.compile(".*(-----BEGIN PGP MESSAGE-----.*)", Pattern.CASE_INSENSITIVE | Pattern.DOTALL); Matcher m = p.matcher(msg_contents); if (m.matches()) start_index = m.start(1); p = Pattern.compile(".*-----END PGP MESSAGE-----(.*)", Pattern.CASE_INSENSITIVE | Pattern.DOTALL); m = p.matcher(msg_contents); if (m.matches()) end_index = m.start(1); if ((start_index > -1 && end_index > -1) && start_index < end_index) { msg_contents = msg_contents.substring(start_index, end_index); } else { Pager.log += getString(R.string.err_missing_crypto_mime) + "\n\n"; Dialogs.toaster(false, getString(R.string.err_missing_crypto_mime), this); return; } break; } } } else if (msg_signed) { request_code = 93; // Extracting the signed part String[] ct_bonds = Utils.content_type_boundary(current.get_content_type()); msg_contents = current.get_contents_crypto(); int split_index = msg_contents.indexOf(ct_bonds[1]); if (split_index != -1) { msg_contents = msg_contents.substring(split_index + ct_bonds[1].length()); // Twice to remove \r\n if (msg_contents.charAt(0) == '\r' || msg_contents.charAt(0) == '\n') { msg_contents = msg_contents.substring(1); } if (msg_contents.charAt(0) == '\r' || msg_contents.charAt(0) == '\n') { msg_contents = msg_contents.substring(1); } } split_index = msg_contents.indexOf(ct_bonds[1]); if (split_index != -1) { msg_contents = msg_contents.substring(0, split_index - 1).trim() + "\r\n"; } msg_signature = Utils.mime_part_section(current.get_contents_crypto(), "2", ct_bonds[1]); b.putString("signature", msg_signature);//get from pgp-mime part } else return; b.putString("message-data", msg_contents); b.putInt("request-code", request_code); gpg = gpg.putExtras(b); startActivityForResult(gpg, request_code, null); overridePendingTransition(R.anim.left_in, R.anim.left_out); } /** * MIME to message conversion after decryption. **/ private void gpg_mime_parsing() { String c_type; if (msg_contents.length() > 500) { c_type = msg_contents.substring(0, 500); } else { c_type = msg_contents; } // Decrypted inner MIME Content-type & boundary String[] ct_bound = Utils.content_type_boundary(c_type); try { Pattern p = Pattern.compile(".*(" + ct_bound[1] + "(.*)" + ct_bound[1] + "--).*", Pattern.DOTALL); Matcher m = p.matcher(msg_contents); if (m.matches()) { msg_contents = m.group(1); current.set_contents_crypto(msg_contents); String m_type = ct_bound[0].trim(); // Structural parsing ArrayList<String[]> msg_structure = Utils.mime_bodystructure(msg_contents, ct_bound[1], m_type); ArrayList<String[]> msg_texts = new ArrayList<>(); Utils.mime_parse_full_msg_into_texts(current.get_contents_crypto(), msg_structure, msg_texts, current); msg_contents = ""; set_btn_texts(); // Inner attachments assignment attachments = new ArrayList<>(); if (msg_structure != null && msg_structure.size() > 0) { for (int ii = 0; ii < msg_structure.size(); ++ii) { // Mime part, i.e. - BODY[1], BODY[1.1], ... Attachment att = new Attachment(); att.set_pop_indx(msg_structure.get(ii)[0]); att.set_mime_type(msg_structure.get(ii)[1]); att.set_boundary(msg_structure.get(ii)[2]); att.set_name(msg_structure.get(ii)[3]); att.set_transfer_encoding(msg_structure.get(ii)[4]); att.set_size(-1); attachments.add(att); } } else { tv_page_attachments.setVisibility(View.GONE); } } else { Pager.log += getString(R.string.err_missing_crypto_mime) + "\n\n"; Dialogs.dialog_error_line(getString(R.string.err_missing_crypto_mime), this); } } catch (Exception e) { Pager.log += e.getMessage() + "\n\n"; Dialogs.dialog_exception(e, this); } } }