Browse Source

fix: wrong format on compose email addresses

main
Distopico Vegan 4 years ago
parent
commit
d4a1c8ca20
No known key found for this signature in database GPG Key ID: 98093A8072546BF3
5 changed files with 161 additions and 72 deletions
  1. +8
    -8
      app/src/main/java/org/dystopia/email/AdapterMessage.java
  2. +88
    -52
      app/src/main/java/org/dystopia/email/FragmentCompose.java
  3. +61
    -8
      app/src/main/java/org/dystopia/email/MessageHelper.java
  4. +3
    -3
      app/src/main/java/org/dystopia/email/ServiceSynchronize.java
  5. +1
    -1
      app/src/main/res/layout/fragment_account.xml

+ 8
- 8
app/src/main/java/org/dystopia/email/AdapterMessage.java View File

@ -333,9 +333,9 @@ public class AdapterMessage extends PagedListAdapter<TupleMessageEx, AdapterMess
} }
if (compact && show_expanded) { if (compact && show_expanded) {
tvFrom.setText(MessageHelper.getFormattedAddresses(addresses, false, false)); tvFrom.setText(MessageHelper.getFormattedAddresses(addresses, null));
} else { } else {
tvFrom.setText(MessageHelper.getFormattedAddresses(addresses, show_expanded, true)); tvFrom.setText(MessageHelper.getFormattedAddresses(addresses, show_expanded ? MessageHelper.ADDRESS_FULL : MessageHelper.ADDRESS_NAME));
} }
tvSize.setText( tvSize.setText(
@ -481,11 +481,11 @@ public class AdapterMessage extends PagedListAdapter<TupleMessageEx, AdapterMess
tvTimeEx.setText(df.format(new Date(message.received))); tvTimeEx.setText(df.format(new Date(message.received)));
} }
tvFromEx.setText(MessageHelper.getFormattedAddresses(message.from, true)); tvFromEx.setText(MessageHelper.getFormattedAddresses(message.from, MessageHelper.ADDRESS_FULL));
tvTo.setText(MessageHelper.getFormattedAddresses(message.to, true)); tvTo.setText(MessageHelper.getFormattedAddresses(message.to, MessageHelper.ADDRESS_FULL));
tvReplyTo.setText(MessageHelper.getFormattedAddresses(message.reply, true)); tvReplyTo.setText(MessageHelper.getFormattedAddresses(message.reply, MessageHelper.ADDRESS_FULL));
tvCc.setText(MessageHelper.getFormattedAddresses(message.cc, true)); tvCc.setText(MessageHelper.getFormattedAddresses(message.cc, MessageHelper.ADDRESS_FULL));
tvBcc.setText(MessageHelper.getFormattedAddresses(message.bcc, true)); tvBcc.setText(MessageHelper.getFormattedAddresses(message.bcc, MessageHelper.ADDRESS_FULL));
tvSubjectEx.setText(message.subject); tvSubjectEx.setText(message.subject);
tvHeaders.setText(show_headers ? message.headers : null); tvHeaders.setText(show_headers ? message.headers : null);
@ -1268,7 +1268,7 @@ public class AdapterMessage extends PagedListAdapter<TupleMessageEx, AdapterMess
lbm.sendBroadcast( lbm.sendBroadcast(
new Intent(ActivityView.ACTION_VIEW_FULL) new Intent(ActivityView.ACTION_VIEW_FULL)
.putExtra("id", data.message.id) .putExtra("id", data.message.id)
.putExtra("from", MessageHelper.getFormattedAddresses(data.message.from, true))); .putExtra("from", MessageHelper.getFormattedAddresses(data.message.from, MessageHelper.ADDRESS_FULL)));
} }
private void onDecrypt(ActionData data) { private void onDecrypt(ActionData data) {


+ 88
- 52
app/src/main/java/org/dystopia/email/FragmentCompose.java View File

@ -85,6 +85,7 @@ import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
@ -779,58 +780,93 @@ public class FragmentCompose extends FragmentEx {
} }
private void handlePickContact(int requestCode, Intent data) { private void handlePickContact(int requestCode, Intent data) {
Cursor cursor = null; Uri uri = data.getData();
try { if (uri == null)
Uri uri = data.getData(); return;
if (uri != null) { Bundle args = new Bundle();
cursor = args.putLong("id", working);
getContext() args.putInt("requestCode", requestCode);
.getContentResolver() args.putParcelable("uri", uri);
.query( new SimpleTask<EntityMessage>() {
uri, @Override
new String[] { protected EntityMessage onLoad(Context context, Bundle args) throws Throwable {
ContactsContract.CommonDataKinds.Email.ADDRESS, long id = args.getLong("id");
ContactsContract.Contacts.DISPLAY_NAME int requestCode = args.getInt("requestCode");
}, Uri uri = args.getParcelable("uri");
null, EntityMessage draft = null;
null, DB db = DB.getInstance(context);
null); try (Cursor cursor = context.getContentResolver().query(
} uri,
if (cursor != null && cursor.moveToFirst()) { new String[]{
int colEmail = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Email.ADDRESS); ContactsContract.CommonDataKinds.Email.ADDRESS,
int colName = cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME); ContactsContract.Contacts.DISPLAY_NAME
String email = cursor.getString(colEmail); },
String name = cursor.getString(colName); null, null, null)) {
if (cursor != null && cursor.moveToFirst()) {
String text = null; int colEmail = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Email.ADDRESS);
if (requestCode == ActivityCompose.REQUEST_CONTACT_TO) { int colName = cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME);
text = etTo.getText().toString(); String email = MessageHelper.sanitizeEmail(cursor.getString(colEmail));
} else if (requestCode == ActivityCompose.REQUEST_CONTACT_CC) { String name = cursor.getString(colName);
text = etCc.getText().toString(); try {
} else if (requestCode == ActivityCompose.REQUEST_CONTACT_BCC) { db.beginTransaction();
text = etBcc.getText().toString(); draft = db.message().getMessage(id);
if (draft == null)
return null;
Address[] address = null;
if (requestCode == ActivityCompose.REQUEST_CONTACT_TO) {
address = draft.to;
} else if (requestCode == ActivityCompose.REQUEST_CONTACT_CC) {
address = draft.cc;
} else if (requestCode == ActivityCompose.REQUEST_CONTACT_BCC) {
address = draft.bcc;
}
List<Address> list = new ArrayList<>();
if (address != null)
list.addAll(Arrays.asList(address));
list.add(new InternetAddress(email, name, StandardCharsets.UTF_8.name()));
if (requestCode == ActivityCompose.REQUEST_CONTACT_TO) {
draft.to = list.toArray(new Address[0]);
} else if (requestCode == ActivityCompose.REQUEST_CONTACT_CC) {
draft.cc = list.toArray(new Address[0]);
} else if (requestCode == ActivityCompose.REQUEST_CONTACT_BCC) {
draft.bcc = list.toArray(new Address[0]);
}
db.message().updateMessage(draft);
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
}
} }
InternetAddress address = new InternetAddress(email, name); return draft;
StringBuilder sb = new StringBuilder(text); }
sb.append(address.toString().replace(",", "")).append(", ");
if (requestCode == ActivityCompose.REQUEST_CONTACT_TO) { @Override
etTo.setText(sb.toString()); protected void onLoaded(Bundle args, EntityMessage draft) {
} else if (requestCode == ActivityCompose.REQUEST_CONTACT_CC) { if (draft != null) {
etCc.setText(sb.toString()); etTo.setText(MessageHelper.getAddressesCompose(draft.to));
} else if (requestCode == ActivityCompose.REQUEST_CONTACT_BCC) { etCc.setText(MessageHelper.getAddressesCompose(draft.cc));
etBcc.setText(sb.toString()); etBcc.setText(MessageHelper.getAddressesCompose(draft.bcc));
} }
} }
} catch (Throwable ex) { @Override
Log.e(Helper.TAG, ex + "\n" + Log.getStackTraceString(ex)); protected void onException(Bundle args, Throwable ex) {
Helper.unexpectedError(getContext(), ex); Helper.unexpectedError(getContext(), ex);
} finally {
if (cursor != null) {
cursor.close();
} }
} }.load(this, args);
} }
private void handleAddAttachment(Intent data, final boolean image) { private void handleAddAttachment(Intent data, final boolean image) {
@ -1121,7 +1157,7 @@ public class FragmentCompose extends FragmentEx {
String from = Helper.canonicalAddress(((InternetAddress) ref.from[0]).getAddress()); String from = Helper.canonicalAddress(((InternetAddress) ref.from[0]).getAddress());
Log.i( Log.i(
Helper.TAG, Helper.TAG,
"From=" + from + " to=" + MessageHelper.getFormattedAddresses(ref.to, false)); "From=" + from + " to=" + MessageHelper.getFormattedAddresses(ref.to, null));
for (EntityIdentity identity : identities) { for (EntityIdentity identity : identities) {
String email = Helper.canonicalAddress(identity.email); String email = Helper.canonicalAddress(identity.email);
if (from.equals(email)) { if (from.equals(email)) {
@ -1229,7 +1265,7 @@ public class FragmentCompose extends FragmentEx {
String.format( String.format(
"<p>%s %s:</p><blockquote>%s</blockquote>", "<p>%s %s:</p><blockquote>%s</blockquote>",
Html.escapeHtml(new Date(time).toString()), Html.escapeHtml(new Date(time).toString()),
Html.escapeHtml(MessageHelper.getFormattedAddresses(draft.to, true)), Html.escapeHtml(MessageHelper.getFormattedAddresses(draft.to, MessageHelper.ADDRESS_FULL)),
HtmlHelper.sanitize(ref.read(context))); HtmlHelper.sanitize(ref.read(context)));
} else if ("forward".equals(action)) { } else if ("forward".equals(action)) {
draft.subject = context.getString(R.string.title_subject_forward, ref.subject); draft.subject = context.getString(R.string.title_subject_forward, ref.subject);
@ -1237,7 +1273,7 @@ public class FragmentCompose extends FragmentEx {
String.format( String.format(
"<p>%s %s:</p><blockquote>%s</blockquote>", "<p>%s %s:</p><blockquote>%s</blockquote>",
Html.escapeHtml(new Date(time).toString()), Html.escapeHtml(new Date(time).toString()),
Html.escapeHtml(MessageHelper.getFormattedAddresses(ref.from, true)), Html.escapeHtml(MessageHelper.getFormattedAddresses(ref.from, MessageHelper.ADDRESS_FULL)),
HtmlHelper.sanitize(ref.read(context))); HtmlHelper.sanitize(ref.read(context)));
} }
@ -1328,9 +1364,9 @@ public class FragmentCompose extends FragmentEx {
setSubtitle(draft.account_name); setSubtitle(draft.account_name);
etTo.setText(MessageHelper.getFormattedAddresses(draft.to, true)); etTo.setText(MessageHelper.getFormattedAddresses(draft.to, MessageHelper.ADDRESS_FULL));
etCc.setText(MessageHelper.getFormattedAddresses(draft.cc, true)); etCc.setText(MessageHelper.getFormattedAddresses(draft.cc, MessageHelper.ADDRESS_FULL));
etBcc.setText(MessageHelper.getFormattedAddresses(draft.bcc, true)); etBcc.setText(MessageHelper.getFormattedAddresses(draft.bcc, MessageHelper.ADDRESS_FULL));
etSubject.setText(draft.subject); etSubject.setText(draft.subject);
etBody.setText(null); etBody.setText(null);


+ 61
- 8
app/src/main/java/org/dystopia/email/MessageHelper.java View File

@ -17,6 +17,7 @@ package org.dystopia.email;
along with FairEmail. If not, see <http://www.gnu.org/licenses/>. along with FairEmail. If not, see <http://www.gnu.org/licenses/>.
Copyright 2018, Marcel Bokhorst (M66B) Copyright 2018, Marcel Bokhorst (M66B)
Copyright 2018-2020, Distopico (dystopia project) <distopico@riseup.net> and contributors
*/ */
import android.content.Context; import android.content.Context;
@ -38,6 +39,8 @@ import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Properties; import java.util.Properties;
import java.util.regex.Pattern;
import javax.activation.DataHandler; import javax.activation.DataHandler;
import javax.activation.FileDataSource; import javax.activation.FileDataSource;
import javax.activation.FileTypeMap; import javax.activation.FileTypeMap;
@ -49,6 +52,7 @@ import javax.mail.MessagingException;
import javax.mail.Multipart; import javax.mail.Multipart;
import javax.mail.Part; import javax.mail.Part;
import javax.mail.Session; import javax.mail.Session;
import javax.mail.internet.AddressException;
import javax.mail.internet.ContentType; import javax.mail.internet.ContentType;
import javax.mail.internet.InternetAddress; import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart; import javax.mail.internet.MimeBodyPart;
@ -61,6 +65,10 @@ public class MessageHelper {
private MimeMessage imessage; private MimeMessage imessage;
private String raw = null; private String raw = null;
static final String ADDRESS_FULL = "full";
static final String ADDRESS_NAME = "displayName";
static final String ADDRESS_COMPOSE = "compose";
static final int NETWORK_TIMEOUT = 60 * 1000; // milliseconds static final int NETWORK_TIMEOUT = 60 * 1000; // milliseconds
static Properties getSessionProperties(int auth_type, boolean insecure) { static Properties getSessionProperties(int auth_type, boolean insecure) {
@ -423,11 +431,13 @@ public class MessageHelper {
* Get parsed email addresses. * Get parsed email addresses.
* *
* @param addresses list of email <code>Address</code> * @param addresses list of email <code>Address</code>
* @param full true render the full format * @param formatType - the name type of format that will be perform , it can be
* @param displayName true display name instead of email when is not 'full' * {@link MessageHelper#ADDRESS_FULL} to display full email address and name
* @return email addresses as string * {@link MessageHelper#ADDRESS_NAME} to only display name
* {@link MessageHelper#ADDRESS_COMPOSE} to display compose rfc822 format
* @return formatted addresses as string
*/ */
static String getFormattedAddresses(Address[] addresses, boolean full, boolean displayName) { static String getFormattedAddresses(Address[] addresses, String formatType) {
if (addresses == null || addresses.length == 0) { if (addresses == null || addresses.length == 0) {
return ""; return "";
} }
@ -442,9 +452,25 @@ public class MessageHelper {
} else { } else {
String email = a.getAddress(); String email = a.getAddress();
personal = personal.replaceAll("[\\,\\<\\>]", ""); personal = personal.replaceAll("[\\,\\<\\>]", "");
if (full) { if (ADDRESS_COMPOSE.equals(formatType)) {
boolean quote = false;
personal = personal.replace("\"", "");
for (int c = 0; c < personal.length(); c++) {
// https://tools.ietf.org/html/rfc822
if ("()<>@,;:\\\".[]".indexOf(personal.charAt(c)) >= 0) {
quote = true;
break;
}
}
if (quote) {
personal = "\"" + personal + "\"";
}
}
if (ADDRESS_FULL.equals(formatType) || ADDRESS_COMPOSE.equals(formatType)) {
formatted.add(personal + " <" + email + ">"); formatted.add(personal + " <" + email + ">");
} else if (displayName) { } else if (ADDRESS_NAME.equals(formatType)) {
formatted.add(personal); formatted.add(personal);
} else { } else {
formatted.add(email); formatted.add(email);
@ -457,8 +483,35 @@ public class MessageHelper {
return TextUtils.join(", ", formatted); return TextUtils.join(", ", formatted);
} }
static String getFormattedAddresses(Address[] addresses, boolean full) { /**
return getFormattedAddresses(addresses, full, true); * Get email addresses formatted and ready for email compose fields
* @param addresses - list of email address to perform format
* @return lists of emails addresses as string
*/
static String getAddressesCompose(Address[] addresses) {
String result = getFormattedAddresses(addresses, ADDRESS_COMPOSE);
if (!TextUtils.isEmpty(result))
result += ", ";
return result;
}
/**
* Sanitize/clean pre-format email address.
* e.g "User name <user@email.com>"
* @param email - per-format address email
* @return lists of emails addresses as string
*/
static String sanitizeEmail(String email) {
if (Pattern.matches("<|>", email)) {
try {
InternetAddress address = new InternetAddress(email);
return address.getAddress();
} catch (AddressException ignored) {
return email;
}
}
return email;
} }
String getHtml() throws MessagingException, IOException { String getHtml() throws MessagingException, IOException {


+ 3
- 3
app/src/main/java/org/dystopia/email/ServiceSynchronize.java View File

@ -506,7 +506,7 @@ public class ServiceSynchronize extends LifecycleService {
DateFormat df = SimpleDateFormat.getDateTimeInstance(SimpleDateFormat.SHORT, SimpleDateFormat.SHORT); DateFormat df = SimpleDateFormat.getDateTimeInstance(SimpleDateFormat.SHORT, SimpleDateFormat.SHORT);
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
for (EntityMessage message : messages) { for (EntityMessage message : messages) {
sb.append("<strong>").append(MessageHelper.getFormattedAddresses(message.from, false)).append("</strong>"); sb.append("<strong>").append(MessageHelper.getFormattedAddresses(message.from, null)).append("</strong>");
if (!TextUtils.isEmpty(message.subject)) { if (!TextUtils.isEmpty(message.subject)) {
sb.append(": ").append(message.subject); sb.append(": ").append(message.subject);
@ -583,7 +583,7 @@ public class ServiceSynchronize extends LifecycleService {
Notification.InboxStyle mstyle = new Notification.InboxStyle(); Notification.InboxStyle mstyle = new Notification.InboxStyle();
mbuilder.addExtras(mArgs).setSmallIcon(R.drawable.ic_mail_icon) mbuilder.addExtras(mArgs).setSmallIcon(R.drawable.ic_mail_icon)
.setContentTitle(MessageHelper.getFormattedAddresses(message.from, true)).setContentIntent(piContent) .setContentTitle(MessageHelper.getFormattedAddresses(message.from, MessageHelper.ADDRESS_FULL)).setContentIntent(piContent)
.setDeleteIntent(piDelete).setSound(uri).setColor(groupColor) .setDeleteIntent(piDelete).setSound(uri).setColor(groupColor)
.setWhen(message.sent == null ? message.received : message.sent).setPriority(Notification.PRIORITY_DEFAULT) .setWhen(message.sent == null ? message.received : message.sent).setPriority(Notification.PRIORITY_DEFAULT)
.setCategory(Notification.CATEGORY_STATUS).setVisibility(Notification.VISIBILITY_PRIVATE).setGroup(groupKey) .setCategory(Notification.CATEGORY_STATUS).setVisibility(Notification.VISIBILITY_PRIVATE).setGroup(groupKey)
@ -598,7 +598,7 @@ public class ServiceSynchronize extends LifecycleService {
mbuilder.setGroupAlertBehavior(Notification.GROUP_ALERT_CHILDREN); mbuilder.setGroupAlertBehavior(Notification.GROUP_ALERT_CHILDREN);
} }
mstyle.setBigContentTitle(MessageHelper.getFormattedAddresses(message.from, false)).setSummaryText(accountName); mstyle.setBigContentTitle(MessageHelper.getFormattedAddresses(message.from, null)).setSummaryText(accountName);
mbuilder.setStyle(mstyle); mbuilder.setStyle(mstyle);
notifications.add(mbuilder.build()); notifications.add(mbuilder.build());


+ 1
- 1
app/src/main/res/layout/fragment_account.xml View File

@ -256,7 +256,7 @@
android:id="@+id/btnColor" android:id="@+id/btnColor"
android:layout_width="@dimen/color_pick" android:layout_width="@dimen/color_pick"
android:layout_height="@dimen/color_pick" android:layout_height="@dimen/color_pick"
android:layout_gravity="center|right" android:layout_gravity="center|end"
android:layout_marginTop="@dimen/margin_lg" android:layout_marginTop="@dimen/margin_lg"
android:layout_marginEnd="@dimen/margin_sm" android:layout_marginEnd="@dimen/margin_sm"
android:layout_marginBottom="@dimen/margin_lg" android:layout_marginBottom="@dimen/margin_lg"


|||||||
|||||||
xxxxxxxxxx
 
000:0
x
 
000:0
Loading…
Cancel
Save