Browse code

[upgrade] BM-15003 Fix: move everything from 'Messages reçus' back to inbox & delete it (repair based)

Thomas Cataldo authored on 28/06/2019 10:09:37
Showing 3 changed files
... ...
@@ -48,6 +48,8 @@ public interface IMailReplicaUids {
48 48
 	 */
49 49
 	public static final String REPAIR_SUBTREE_OP = "replication.subtree";
50 50
 
51
+	public static final String REPAIR_RENAMED_INBOX_OP = "renamed.inbox";
52
+
51 53
 	/**
52 54
 	 * Repair operation id for repairing the multiple inbox syndrom
53 55
 	 */
... ...
@@ -87,8 +89,7 @@ public interface IMailReplicaUids {
87 89
 	 * Returns the uid for the subtree container for a given mailbox
88 90
 	 * 
89 91
 	 * @param domainUid
90
-	 * @param mbox
91
-	 *                      Mailbox item
92
+	 * @param mbox      Mailbox item
92 93
 	 * @return subtree container UID
93 94
 	 */
94 95
 	@GET
... ...
@@ -180,5 +180,11 @@
180 180
             factory="net.bluemind.backend.mail.replica.service.internal.repair.ReplicationDeletedMailboxRepair$ReplicationDeletedMailboxRepairFactory">
181 181
       </repairSupport>
182 182
    </extension>
183
+   <extension
184
+         point="net.bluemind.directory.repairSupport">
185
+      <repairSupport
186
+            factory="net.bluemind.backend.mail.replica.service.internal.repair.RenamedInboxRepair$RepairFactory">
187
+      </repairSupport>
188
+   </extension>
183 189
 
184 190
 </plugin>
185 191
new file mode 100644
... ...
@@ -0,0 +1,211 @@
1
+/* BEGIN LICENSE
2
+  * Copyright © Blue Mind SAS, 2012-2018
3
+  *
4
+  * This file is part of BlueMind. BlueMind is a messaging and collaborative
5
+  * solution.
6
+  *
7
+  * This program is free software; you can redistribute it and/or modify
8
+  * it under the terms of either the GNU Affero General Public License as
9
+  * published by the Free Software Foundation (version 3 of the License).
10
+  *
11
+  * This program is distributed in the hope that it will be useful,
12
+  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14
+  *
15
+  * See LICENSE.txt
16
+  * END LICENSE
17
+  */
18
+package net.bluemind.backend.mail.replica.service.internal.repair;
19
+
20
+import java.util.Collections;
21
+import java.util.Map;
22
+import java.util.Set;
23
+import java.util.concurrent.atomic.AtomicBoolean;
24
+import java.util.function.BiConsumer;
25
+
26
+import org.slf4j.Logger;
27
+import org.slf4j.LoggerFactory;
28
+
29
+import com.google.common.collect.ImmutableSet;
30
+
31
+import net.bluemind.backend.mail.replica.api.IMailReplicaUids;
32
+import net.bluemind.core.api.report.DiagnosticReport;
33
+import net.bluemind.core.container.model.ItemValue;
34
+import net.bluemind.core.rest.BmContext;
35
+import net.bluemind.core.task.service.IServerTaskMonitor;
36
+import net.bluemind.directory.api.BaseDirEntry.Kind;
37
+import net.bluemind.directory.api.DirEntry;
38
+import net.bluemind.directory.api.MaintenanceOperation;
39
+import net.bluemind.directory.service.IDirEntryRepairSupport;
40
+import net.bluemind.imap.IMAPException;
41
+import net.bluemind.imap.ListInfo;
42
+import net.bluemind.imap.ListResult;
43
+import net.bluemind.imap.StoreClient;
44
+import net.bluemind.index.mail.Sudo;
45
+import net.bluemind.mailbox.api.IMailboxes;
46
+import net.bluemind.mailbox.api.Mailbox;
47
+import net.bluemind.network.topology.Topology;
48
+import net.bluemind.server.api.Server;
49
+
50
+public class RenamedInboxRepair implements IDirEntryRepairSupport {
51
+
52
+	public static final String BROKEN_NAME = "Messages reçus";
53
+
54
+	private static final Logger logger = LoggerFactory.getLogger(RenamedInboxRepair.class);
55
+
56
+	public static final MaintenanceOperation op = MaintenanceOperation.create(IMailReplicaUids.REPAIR_RENAMED_INBOX_OP,
57
+			"Fixes mailboxes with a '" + BROKEN_NAME + "' folder");
58
+
59
+	private final BmContext context;
60
+
61
+	public RenamedInboxRepair(BmContext context) {
62
+		this.context = context;
63
+	}
64
+
65
+	@Override
66
+	public Set<MaintenanceOperation> availableOperations(Kind kind) {
67
+		if (kind == Kind.USER) {
68
+			return ImmutableSet.of(op);
69
+		}
70
+		return Collections.emptySet();
71
+	}
72
+
73
+	@Override
74
+	public Set<InternalMaintenanceOperation> ops(Kind kind) {
75
+		if (kind == Kind.USER) {
76
+			return ImmutableSet.of(new RenamedInboxMaintenance(context));
77
+		}
78
+		return Collections.emptySet();
79
+
80
+	}
81
+
82
+	public static class RepairFactory implements IDirEntryRepairSupport.Factory {
83
+		@Override
84
+		public IDirEntryRepairSupport create(BmContext context) {
85
+			return new RenamedInboxRepair(context);
86
+		}
87
+	}
88
+
89
+	private static abstract class MailboxWalk {
90
+		protected final ItemValue<Mailbox> mbox;
91
+		protected final String domainUid;
92
+		protected final BmContext context;
93
+		protected final Server srv;
94
+
95
+		private MailboxWalk(BmContext context, ItemValue<Mailbox> mbox, String domainUid, Server srv) {
96
+			this.srv = srv;
97
+			this.context = context;
98
+			this.mbox = mbox;
99
+			this.domainUid = domainUid;
100
+		}
101
+
102
+		public static MailboxWalk create(BmContext context, ItemValue<Mailbox> mbox, String domainUid, Server srv) {
103
+			return new UserMailboxWalk(context, mbox, domainUid, srv);
104
+		}
105
+
106
+		public abstract void folders(BiConsumer<StoreClient, ListResult> process);
107
+	}
108
+
109
+	public static final class UserMailboxWalk extends MailboxWalk {
110
+
111
+		public UserMailboxWalk(BmContext context, ItemValue<Mailbox> mbox, String domainUid, Server srv) {
112
+			super(context, mbox, domainUid, srv);
113
+		}
114
+
115
+		public void folders(BiConsumer<StoreClient, ListResult> process) {
116
+			String login = mbox.value.name + "@" + domainUid;
117
+
118
+			try (Sudo sudo = new Sudo(mbox.value.name, domainUid);
119
+					StoreClient sc = new StoreClient(srv.address(), 1143, login, sudo.context.getSessionId())) {
120
+				if (!sc.login()) {
121
+					logger.error("Fail to connect", mbox.value.name);
122
+					return;
123
+				}
124
+				ListResult allFolders = sc.listAll();
125
+				process.accept(sc, allFolders);
126
+			}
127
+		}
128
+	}
129
+
130
+	private static class RenamedInboxMaintenance extends InternalMaintenanceOperation {
131
+
132
+		private final BmContext context;
133
+
134
+		public RenamedInboxMaintenance(BmContext ctx) {
135
+			super(op.identifier, null, IMailReplicaUids.REPAIR_SUBTREE_OP, 1);
136
+			this.context = ctx;
137
+		}
138
+
139
+		@FunctionalInterface
140
+		private interface FolderAction {
141
+
142
+			void process(ItemValue<Mailbox> mbox, ListInfo folder, StoreClient sc, IServerTaskMonitor monitor);
143
+
144
+		}
145
+
146
+		public void runOperation(String domainUid, DirEntry entry, DiagnosticReport report, IServerTaskMonitor monitor,
147
+				FolderAction action) {
148
+
149
+			if (entry.archived) {
150
+				logger.debug("DirEntry is archived, skip it");
151
+				return;
152
+			}
153
+
154
+			IMailboxes iMailboxes = context.getServiceProvider().instance(IMailboxes.class, domainUid);
155
+			ItemValue<Mailbox> mbox = iMailboxes.getComplete(entry.entryUid);
156
+			logger.info("Checking {} {}", domainUid, mbox.value.name);
157
+
158
+			ItemValue<Server> server = Topology.get().datalocation(entry.dataLocation);
159
+
160
+			MailboxWalk moonWalk = MailboxWalk.create(context, mbox, domainUid, server.value);
161
+			AtomicBoolean completed = new AtomicBoolean();
162
+			moonWalk.folders((sc, allFolders) -> {
163
+				allFolders.stream().filter(li -> BROKEN_NAME.equals(li.getName())).findAny().ifPresent(f -> {
164
+					try {
165
+						action.process(mbox, f, sc, monitor);
166
+						completed.set(true);
167
+					} catch (Exception e) {
168
+						logger.error(e.getMessage(), e);
169
+						monitor.log(e.getMessage());
170
+					}
171
+				});
172
+			});
173
+			report.ok(op.identifier, "repair op completed (work done: " + completed.get() + ")");
174
+
175
+		}
176
+
177
+		@Override
178
+		public void check(String domainUid, DirEntry entry, DiagnosticReport report, IServerTaskMonitor monitor) {
179
+			runOperation(domainUid, entry, report, monitor, (mbox, folder, sc, mon) -> {
180
+				try {
181
+					if (sc.select(folder.getName())) {
182
+						monitor.log(mbox.value.name + "@" + domainUid + " has an extra 'Message reçus' folder");
183
+					}
184
+				} catch (IMAPException e) {
185
+					monitor.log("ERROR " + e.getMessage());
186
+					logger.warn("Fail to select {} on mailbox {}", folder.getName(), mbox.value.name);
187
+				}
188
+			});
189
+		}
190
+
191
+		@Override
192
+		public void repair(String domainUid, DirEntry entry, DiagnosticReport report, IServerTaskMonitor monitor) {
193
+			runOperation(domainUid, entry, report, monitor, (mbox, folder, sc, mon) -> {
194
+				try {
195
+					sc.select(folder.getName());
196
+					sc.expunge();
197
+					Map<Integer, Integer> copied = sc.uidCopy("1:*", "INBOX");
198
+					sc.select("INBOX");
199
+					sc.deleteMailbox(folder.getName());
200
+					monitor.log(copied.size() + " email(s) moved from '" + BROKEN_NAME + "' to INBOX");
201
+				} catch (IMAPException e) {
202
+					monitor.log("ERROR " + e.getMessage());
203
+					logger.warn("Fail to select {} on mailbox {}", folder.getName(), mbox.value.name);
204
+				}
205
+			});
206
+
207
+		}
208
+
209
+	}
210
+
211
+}