mirror of
https://github.com/M66B/FairEmail.git
synced 2025-03-12 07:07:18 +00:00
Match identities by regex
This commit is contained in:
parent
bb4eb51d17
commit
50f48fb4c6
13 changed files with 1977 additions and 60 deletions
1860
app/schemas/eu.faircode.email.DB/101.json
Normal file
1860
app/schemas/eu.faircode.email.DB/101.json
Normal file
File diff suppressed because it is too large
Load diff
|
@ -1817,7 +1817,7 @@ public class AdapterMessage extends RecyclerView.Adapter<AdapterMessage.ViewHold
|
|||
return null;
|
||||
|
||||
for (Address sender : senders)
|
||||
if (MessageHelper.similarAddress(sender, identity.email)) {
|
||||
if (identity.similarAddress(sender)) {
|
||||
outgoing = true;
|
||||
break;
|
||||
}
|
||||
|
@ -2187,7 +2187,7 @@ public class AdapterMessage extends RecyclerView.Adapter<AdapterMessage.ViewHold
|
|||
if (amessage == null || !amessage.id.equals(message.id))
|
||||
return;
|
||||
|
||||
Address[] recipients = message.getAllRecipients(identity == null ? null : identity.email);
|
||||
Address[] recipients = message.getAllRecipients(identity);
|
||||
|
||||
View anchor = bnvActions.findViewById(R.id.action_reply);
|
||||
PopupMenuLifecycle popupMenu = new PopupMenuLifecycle(context, powner, anchor);
|
||||
|
|
|
@ -2031,16 +2031,16 @@ class Core {
|
|||
}
|
||||
|
||||
// Search for matching identity
|
||||
List<EntityIdentity> identities = db.identity().getIdentities(folder.account);
|
||||
List<EntityIdentity> identities = db.identity().getSynchronizingIdentities(folder.account);
|
||||
if (identities != null) {
|
||||
for (Address address : addresses)
|
||||
for (EntityIdentity identity : identities)
|
||||
if (MessageHelper.sameAddress(address, identity.email))
|
||||
if (identity.sameAddress(address))
|
||||
return identity;
|
||||
|
||||
for (Address address : addresses)
|
||||
for (EntityIdentity identity : identities)
|
||||
if (MessageHelper.similarAddress(address, identity.email))
|
||||
if (identity.similarAddress(address))
|
||||
return identity;
|
||||
}
|
||||
|
||||
|
@ -2092,11 +2092,11 @@ class Core {
|
|||
// Check if from self
|
||||
if (type == EntityContact.TYPE_FROM && recipients != null && recipients.length > 0) {
|
||||
boolean me = false;
|
||||
List<EntityIdentity> identities = db.identity().getIdentities(folder.account);
|
||||
List<EntityIdentity> identities = db.identity().getSynchronizingIdentities(folder.account);
|
||||
if (identities != null)
|
||||
for (Address recipient : recipients) {
|
||||
for (EntityIdentity identity : identities)
|
||||
if (MessageHelper.similarAddress(recipient, identity.email)) {
|
||||
if (identity.similarAddress(recipient)) {
|
||||
me = true;
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -58,7 +58,7 @@ import io.requery.android.database.sqlite.RequerySQLiteOpenHelperFactory;
|
|||
// https://developer.android.com/topic/libraries/architecture/room.html
|
||||
|
||||
@Database(
|
||||
version = 100,
|
||||
version = 101,
|
||||
entities = {
|
||||
EntityIdentity.class,
|
||||
EntityAccount.class,
|
||||
|
@ -1006,6 +1006,13 @@ public abstract class DB extends RoomDatabase {
|
|||
db.execSQL("ALTER TABLE `message` ADD COLUMN `unsubscribe` TEXT");
|
||||
}
|
||||
})
|
||||
.addMigrations(new Migration(100, 101) {
|
||||
@Override
|
||||
public void migrate(@NonNull SupportSQLiteDatabase db) {
|
||||
Log.i("DB migration from version " + startVersion + " to " + endVersion);
|
||||
db.execSQL("ALTER TABLE `identity` ADD COLUMN `sender_extra_regex` TEXT");
|
||||
}
|
||||
})
|
||||
.build();
|
||||
}
|
||||
|
||||
|
|
|
@ -49,6 +49,12 @@ public interface DaoIdentity {
|
|||
" ORDER BY name COLLATE NOCASE")
|
||||
List<EntityIdentity> getIdentities(long account);
|
||||
|
||||
@Query("SELECT identity.* FROM identity" +
|
||||
" JOIN account ON account.id = identity.account" +
|
||||
" WHERE identity.account = :account" +
|
||||
" AND identity.synchronize AND account.synchronize")
|
||||
List<EntityIdentity> getSynchronizingIdentities(long account);
|
||||
|
||||
@Query("SELECT * FROM identity WHERE id = :id")
|
||||
EntityIdentity getIdentity(long id);
|
||||
|
||||
|
|
|
@ -19,6 +19,8 @@ package eu.faircode.email;
|
|||
Copyright 2018-2019 by Marcel Bokhorst (M66B)
|
||||
*/
|
||||
|
||||
import android.text.TextUtils;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.room.Entity;
|
||||
import androidx.room.ForeignKey;
|
||||
|
@ -29,6 +31,10 @@ import org.json.JSONException;
|
|||
import org.json.JSONObject;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.mail.Address;
|
||||
import javax.mail.internet.InternetAddress;
|
||||
|
||||
import static androidx.room.ForeignKey.CASCADE;
|
||||
|
||||
|
@ -79,6 +85,7 @@ public class EntityIdentity {
|
|||
public Boolean primary;
|
||||
@NonNull
|
||||
public Boolean sender_extra = false;
|
||||
public String sender_extra_regex;
|
||||
public String replyto;
|
||||
public String bcc;
|
||||
@NonNull
|
||||
|
@ -102,6 +109,45 @@ public class EntityIdentity {
|
|||
return (starttls ? "smtp" : "smtps");
|
||||
}
|
||||
|
||||
boolean sameAddress(Address address) {
|
||||
String other = ((InternetAddress) address).getAddress();
|
||||
if (other == null)
|
||||
return false;
|
||||
|
||||
return other.equalsIgnoreCase(email);
|
||||
}
|
||||
|
||||
boolean similarAddress(Address address) {
|
||||
String other = ((InternetAddress) address).getAddress();
|
||||
if (other == null)
|
||||
return false;
|
||||
|
||||
if (!other.contains("@") || !email.contains("@"))
|
||||
return false;
|
||||
|
||||
String[] cother = other.split("@");
|
||||
String[] cemail = email.split("@");
|
||||
|
||||
if (cother.length != 2 || cemail.length != 2)
|
||||
return false;
|
||||
|
||||
// Domain
|
||||
if (!cother[1].equalsIgnoreCase(cemail[1]))
|
||||
return false;
|
||||
|
||||
// User
|
||||
if (TextUtils.isEmpty(sender_extra_regex)) {
|
||||
String user = (cother[0].contains("+") ? cother[0].split("\\+")[0] : cother[0]);
|
||||
if (user.equalsIgnoreCase(cemail[0]))
|
||||
return true;
|
||||
} else {
|
||||
if (Pattern.matches(sender_extra_regex, cother[0]))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public JSONObject toJSON() throws JSONException {
|
||||
JSONObject json = new JSONObject();
|
||||
json.put("id", id);
|
||||
|
|
|
@ -162,32 +162,32 @@ public class EntityMessage implements Serializable {
|
|||
return "<" + UUID.randomUUID() + "@localhost" + '>';
|
||||
}
|
||||
|
||||
boolean replySelf(String via) {
|
||||
if (via == null)
|
||||
boolean replySelf(EntityIdentity identity) {
|
||||
if (identity == null)
|
||||
return false;
|
||||
|
||||
Address[] senders = (reply == null || reply.length == 0 ? from : reply);
|
||||
if (senders != null)
|
||||
for (Address sender : senders)
|
||||
if (MessageHelper.similarAddress(sender, via))
|
||||
if (identity.similarAddress(sender))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Address[] getAllRecipients(String via) {
|
||||
Address[] getAllRecipients(EntityIdentity identity) {
|
||||
List<Address> addresses = new ArrayList<>();
|
||||
|
||||
if (to != null && !replySelf(via))
|
||||
if (to != null && !replySelf(identity))
|
||||
addresses.addAll(Arrays.asList(to));
|
||||
|
||||
if (cc != null)
|
||||
addresses.addAll(Arrays.asList(cc));
|
||||
|
||||
// Filter self
|
||||
if (via != null)
|
||||
if (identity != null)
|
||||
for (Address address : new ArrayList<>(addresses))
|
||||
if (MessageHelper.similarAddress(address, via))
|
||||
if (identity.similarAddress(address))
|
||||
addresses.remove(address);
|
||||
|
||||
return addresses.toArray(new Address[0]);
|
||||
|
|
|
@ -2096,13 +2096,11 @@ public class FragmentCompose extends FragmentBase {
|
|||
data.draft.inreplyto = ref.msgid;
|
||||
data.draft.thread = ref.thread;
|
||||
|
||||
String via = null;
|
||||
EntityIdentity identity = null;
|
||||
if (ref.identity != null) {
|
||||
EntityIdentity identity = db.identity().getIdentity(ref.identity);
|
||||
if (identity != null) {
|
||||
identity = db.identity().getIdentity(ref.identity);
|
||||
if (identity != null)
|
||||
data.draft.from = new Address[]{new InternetAddress(identity.email, identity.name)};
|
||||
via = identity.email;
|
||||
}
|
||||
}
|
||||
|
||||
if ("list".equals(action) && ref.list_post != null)
|
||||
|
@ -2111,7 +2109,7 @@ public class FragmentCompose extends FragmentBase {
|
|||
data.draft.to = ref.receipt_to;
|
||||
else {
|
||||
// Prevent replying to self
|
||||
if (ref.replySelf(via)) {
|
||||
if (ref.replySelf(identity)) {
|
||||
data.draft.to = ref.to;
|
||||
data.draft.from = ref.from;
|
||||
} else
|
||||
|
@ -2119,7 +2117,7 @@ public class FragmentCompose extends FragmentBase {
|
|||
}
|
||||
|
||||
if ("reply_all".equals(action))
|
||||
data.draft.cc = ref.getAllRecipients(via);
|
||||
data.draft.cc = ref.getAllRecipients(identity);
|
||||
else if ("receipt".equals(action))
|
||||
data.draft.receipt_request = true;
|
||||
|
||||
|
@ -2176,7 +2174,7 @@ public class FragmentCompose extends FragmentBase {
|
|||
for (Address sender : data.draft.from)
|
||||
for (EntityIdentity identity : data.identities)
|
||||
if (identity.account.equals(aid) &&
|
||||
MessageHelper.sameAddress(sender, identity.email)) {
|
||||
identity.sameAddress(sender)) {
|
||||
selected = identity;
|
||||
break;
|
||||
}
|
||||
|
@ -2185,7 +2183,7 @@ public class FragmentCompose extends FragmentBase {
|
|||
for (Address sender : data.draft.from)
|
||||
for (EntityIdentity identity : data.identities)
|
||||
if (identity.account.equals(aid) &&
|
||||
MessageHelper.similarAddress(sender, identity.email)) {
|
||||
identity.similarAddress(sender)) {
|
||||
selected = identity;
|
||||
break;
|
||||
}
|
||||
|
@ -2193,7 +2191,7 @@ public class FragmentCompose extends FragmentBase {
|
|||
if (selected == null)
|
||||
for (Address sender : data.draft.from)
|
||||
for (EntityIdentity identity : data.identities)
|
||||
if (MessageHelper.sameAddress(sender, identity.email)) {
|
||||
if (identity.sameAddress(sender)) {
|
||||
selected = identity;
|
||||
break;
|
||||
}
|
||||
|
@ -2201,7 +2199,7 @@ public class FragmentCompose extends FragmentBase {
|
|||
if (selected == null)
|
||||
for (Address sender : data.draft.from)
|
||||
for (EntityIdentity identity : data.identities)
|
||||
if (MessageHelper.similarAddress(sender, identity.email)) {
|
||||
if (identity.similarAddress(sender)) {
|
||||
selected = identity;
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -109,6 +109,7 @@ public class FragmentIdentity extends FragmentBase {
|
|||
private CheckBox cbPrimary;
|
||||
|
||||
private CheckBox cbSenderExtra;
|
||||
private TextView etSenderExtra;
|
||||
private EditText etReplyTo;
|
||||
private EditText etBcc;
|
||||
private TextView tvEncryptPro;
|
||||
|
@ -192,6 +193,7 @@ public class FragmentIdentity extends FragmentBase {
|
|||
cbPrimary = view.findViewById(R.id.cbPrimary);
|
||||
|
||||
cbSenderExtra = view.findViewById(R.id.cbSenderExtra);
|
||||
etSenderExtra = view.findViewById(R.id.etSenderExtra);
|
||||
etReplyTo = view.findViewById(R.id.etReplyTo);
|
||||
etBcc = view.findViewById(R.id.etBcc);
|
||||
tvEncryptPro = view.findViewById(R.id.tvEncryptPro);
|
||||
|
@ -528,6 +530,7 @@ public class FragmentIdentity extends FragmentBase {
|
|||
args.putString("email", etEmail.getText().toString().trim());
|
||||
args.putString("display", etDisplay.getText().toString());
|
||||
args.putBoolean("sender_extra", cbSenderExtra.isChecked());
|
||||
args.putString("sender_extra_regex", etSenderExtra.getText().toString());
|
||||
args.putString("replyto", etReplyTo.getText().toString().trim());
|
||||
args.putString("bcc", etBcc.getText().toString().trim());
|
||||
args.putBoolean("encrypt", cbEncrypt.isChecked());
|
||||
|
@ -595,6 +598,7 @@ public class FragmentIdentity extends FragmentBase {
|
|||
boolean primary = args.getBoolean("primary");
|
||||
|
||||
boolean sender_extra = args.getBoolean("sender_extra");
|
||||
String sender_extra_regex = args.getString("sender_extra_regex");
|
||||
String replyto = args.getString("replyto");
|
||||
String bcc = args.getString("bcc");
|
||||
boolean encrypt = args.getBoolean("encrypt");
|
||||
|
@ -645,6 +649,9 @@ public class FragmentIdentity extends FragmentBase {
|
|||
if (TextUtils.isEmpty(realm))
|
||||
realm = null;
|
||||
|
||||
if (TextUtils.isEmpty(sender_extra_regex))
|
||||
sender_extra_regex = null;
|
||||
|
||||
if (TextUtils.isEmpty(replyto))
|
||||
replyto = null;
|
||||
|
||||
|
@ -699,6 +706,8 @@ public class FragmentIdentity extends FragmentBase {
|
|||
return true;
|
||||
if (!Objects.equals(identity.sender_extra, sender_extra))
|
||||
return true;
|
||||
if (!Objects.equals(identity.sender_extra_regex, sender_extra_regex))
|
||||
return true;
|
||||
if (!Objects.equals(identity.replyto, replyto))
|
||||
return true;
|
||||
if (!Objects.equals(identity.bcc, bcc))
|
||||
|
@ -768,6 +777,7 @@ public class FragmentIdentity extends FragmentBase {
|
|||
identity.primary = (identity.synchronize && primary);
|
||||
|
||||
identity.sender_extra = sender_extra;
|
||||
identity.sender_extra_regex = sender_extra_regex;
|
||||
identity.replyto = replyto;
|
||||
identity.bcc = bcc;
|
||||
identity.encrypt = encrypt;
|
||||
|
@ -904,6 +914,7 @@ public class FragmentIdentity extends FragmentBase {
|
|||
cbPrimary.setChecked(identity == null ? true : identity.primary);
|
||||
|
||||
cbSenderExtra.setChecked(identity != null && identity.sender_extra);
|
||||
etSenderExtra.setText(identity == null ? null : identity.sender_extra_regex);
|
||||
etReplyTo.setText(identity == null ? null : identity.replyto);
|
||||
etBcc.setText(identity == null ? null : identity.bcc);
|
||||
cbEncrypt.setChecked(identity == null ? false : identity.encrypt);
|
||||
|
|
|
@ -456,7 +456,7 @@ public class FragmentRule extends FragmentBase {
|
|||
if (data.folders.size() > 0)
|
||||
Collections.sort(data.folders, data.folders.get(0).getComparator(null));
|
||||
|
||||
data.identities = db.identity().getIdentities(aid);
|
||||
data.identities = db.identity().getSynchronizingIdentities(aid);
|
||||
data.answers = db.answer().getAnswers(false);
|
||||
|
||||
return data;
|
||||
|
|
|
@ -713,37 +713,6 @@ public class MessageHelper {
|
|||
return TextUtils.join(", ", formatted);
|
||||
}
|
||||
|
||||
static boolean sameAddress(Address address1, String email2) {
|
||||
String email1 = ((InternetAddress) address1).getAddress();
|
||||
if (email1 == null)
|
||||
return false;
|
||||
|
||||
return email1.equalsIgnoreCase(email2);
|
||||
}
|
||||
|
||||
static boolean similarAddress(Address address1, String email2) {
|
||||
String email1 = ((InternetAddress) address1).getAddress();
|
||||
if (email1 == null)
|
||||
return false;
|
||||
|
||||
if (!email1.contains("@") || !email2.contains("@"))
|
||||
return false;
|
||||
|
||||
String[] e1 = email1.split("@");
|
||||
String[] e2 = email2.split("@");
|
||||
|
||||
if (e1.length != 2 || e2.length != 2)
|
||||
return false;
|
||||
|
||||
// Domain
|
||||
if (!e1[1].equalsIgnoreCase(e2[1]))
|
||||
return false;
|
||||
|
||||
String user1 = (e1[0].contains("+") ? e1[0].split("\\+")[0] : e1[0]);
|
||||
|
||||
return user1.equalsIgnoreCase(e2[0]);
|
||||
}
|
||||
|
||||
static String decodeMime(String text) {
|
||||
if (text == null)
|
||||
return null;
|
||||
|
|
|
@ -495,6 +495,25 @@
|
|||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/cbPrimary" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvSenderExtra"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="12dp"
|
||||
android:text="@string/title_advanced_sender_regex"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Small"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/cbSenderExtra" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/etSenderExtra"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/title_optional"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/tvSenderExtra" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvSenderExtraHint"
|
||||
android:layout_width="wrap_content"
|
||||
|
@ -503,7 +522,7 @@
|
|||
android:textAppearance="@style/TextAppearance.AppCompat.Small"
|
||||
android:textStyle="italic"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/cbSenderExtra" />
|
||||
app:layout_constraintTop_toBottomOf="@id/etSenderExtra" />
|
||||
|
||||
<!-- reply to -->
|
||||
|
||||
|
@ -699,7 +718,7 @@
|
|||
tvRealm,etRealm,
|
||||
cbUseIp,tvUseIpHint,
|
||||
cbSynchronize,cbPrimary,
|
||||
cbSenderExtra,tvSenderExtraHint,
|
||||
cbSenderExtra,tvSenderExtra,etSenderExtra,tvSenderExtraHint,
|
||||
tvReplyTo,etReplyTo,tvBcc,etBcc,
|
||||
cbEncrypt,tvEncryptPro,
|
||||
cbDeliveryReceipt,cbReadReceipt,tvReceipt" />
|
||||
|
|
|
@ -357,6 +357,7 @@
|
|||
<string name="title_identity_email">Your email address</string>
|
||||
<string name="title_identity_color_hint">Identity colors take precedence over account colors</string>
|
||||
<string name="title_advanced_sender">Allow editing sender address</string>
|
||||
<string name="title_advanced_sender_regex">Regex to match edited addresses</string>
|
||||
<string name="title_identity_reply_to">Reply to address</string>
|
||||
<string name="title_identity_encrypt">Encrypt by default</string>
|
||||
<string name="title_identity_use_ip_hint">In case of \'invalid greeting\', \'requires valid address\' or a similar error, try to change this setting</string>
|
||||
|
|
Loading…
Add table
Reference in a new issue