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));
tvTo.setText(MessageHelper.getFormattedAddresses(message.to, true));
tvReplyTo.setText(MessageHelper.getFormattedAddresses(message.reply, true));
tvCc.setText(MessageHelper.getFormattedAddresses(message.cc, true));
tvBcc.setText(MessageHelper.getFormattedAddresses(message.bcc, true));
tvFromEx.setText(MessageHelper.getFormattedAddresses(message.from, MessageHelper.ADDRESS_FULL));
tvTo.setText(MessageHelper.getFormattedAddresses(message.to, MessageHelper.ADDRESS_FULL));
tvReplyTo.setText(MessageHelper.getFormattedAddresses(message.reply, MessageHelper.ADDRESS_FULL));
tvCc.setText(MessageHelper.getFormattedAddresses(message.cc, MessageHelper.ADDRESS_FULL));
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;
try {
Uri uri = data.getData();
if (uri != null) {
cursor =
getContext()
.getContentResolver()
.query(
uri,
new String[] {
ContactsContract.CommonDataKinds.Email.ADDRESS,
ContactsContract.Contacts.DISPLAY_NAME
},
null,
null,
null);
}
if (cursor != null && cursor.moveToFirst()) {
int colEmail = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Email.ADDRESS);
int colName = cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME);
String email = cursor.getString(colEmail);
String name = cursor.getString(colName);
String text = null;
if (requestCode == ActivityCompose.REQUEST_CONTACT_TO) {
text = etTo.getText().toString();
} else if (requestCode == ActivityCompose.REQUEST_CONTACT_CC) {
text = etCc.getText().toString();
} else if (requestCode == ActivityCompose.REQUEST_CONTACT_BCC) {
text = etBcc.getText().toString();
Uri uri = data.getData();
if (uri == null)
return;
Bundle args = new Bundle();
args.putLong("id", working);
args.putInt("requestCode", requestCode);
args.putParcelable("uri", uri);
new SimpleTask<EntityMessage>() {
@Override
protected EntityMessage onLoad(Context context, Bundle args) throws Throwable {
long id = args.getLong("id");
int requestCode = args.getInt("requestCode");
Uri uri = args.getParcelable("uri");
EntityMessage draft = null;
DB db = DB.getInstance(context);
try (Cursor cursor = context.getContentResolver().query(
uri,
new String[]{
ContactsContract.CommonDataKinds.Email.ADDRESS,
ContactsContract.Contacts.DISPLAY_NAME
},
null, null, null)) {
if (cursor != null && cursor.moveToFirst()) {
int colEmail = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Email.ADDRESS);
int colName = cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME);
String email = MessageHelper.sanitizeEmail(cursor.getString(colEmail));
String name = cursor.getString(colName);
try {
db.beginTransaction();
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);
StringBuilder sb = new StringBuilder(text);
sb.append(address.toString().replace(",", "")).append(", ");
return draft;
}
if (requestCode == ActivityCompose.REQUEST_CONTACT_TO) {
etTo.setText(sb.toString());
} else if (requestCode == ActivityCompose.REQUEST_CONTACT_CC) {
etCc.setText(sb.toString());
} else if (requestCode == ActivityCompose.REQUEST_CONTACT_BCC) {
etBcc.setText(sb.toString());
@Override
protected void onLoaded(Bundle args, EntityMessage draft) {
if (draft != null) {
etTo.setText(MessageHelper.getAddressesCompose(draft.to));
etCc.setText(MessageHelper.getAddressesCompose(draft.cc));
etBcc.setText(MessageHelper.getAddressesCompose(draft.bcc));
} }
} }
} catch (Throwable ex) {
Log.e(Helper.TAG, ex + "\n" + Log.getStackTraceString(ex));
Helper.unexpectedError(getContext(), ex);
} finally {
if (cursor != null) {
cursor.close();
@Override
protected void onException(Bundle args, Throwable ex) {
Helper.unexpectedError(getContext(), ex);
} }
}
}.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));
etCc.setText(MessageHelper.getFormattedAddresses(draft.cc, true));
etBcc.setText(MessageHelper.getFormattedAddresses(draft.bcc, true));
etTo.setText(MessageHelper.getFormattedAddresses(draft.to, MessageHelper.ADDRESS_FULL));
etCc.setText(MessageHelper.getFormattedAddresses(draft.cc, MessageHelper.ADDRESS_FULL));
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 displayName true display name instead of email when is not 'full'
* @return email addresses as string
* @param formatType - the name type of format that will be perform , it can be
* {@link MessageHelper#ADDRESS_FULL} to display full email address and name
* {@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"


Loading…
Cancel
Save