blob: 54971c407cd68131a98dbc83e3d3372249413137 [file] [log] [blame]
// Copyright (C) 2016 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.gerrit.server.mail.receive;
import com.google.common.flogger.FluentLogger;
import com.google.common.primitives.Ints;
import com.google.gerrit.mail.MailMessage;
import com.google.gerrit.mail.MailParsingException;
import com.google.gerrit.mail.RawMailParser;
import com.google.gerrit.server.git.WorkQueue;
import com.google.gerrit.server.mail.EmailSettings;
import com.google.gerrit.server.mail.Encryption;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.io.BufferedReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.net.pop3.POP3Client;
import org.apache.commons.net.pop3.POP3MessageInfo;
import org.apache.commons.net.pop3.POP3SClient;
/** An implementation of {@link MailReceiver} for POP3. */
@Singleton
public class Pop3MailReceiver extends MailReceiver {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
@Inject
Pop3MailReceiver(EmailSettings mailSettings, MailProcessor mailProcessor, WorkQueue workQueue) {
super(mailSettings, mailProcessor, workQueue);
}
/**
* Opens a connection to the mail server, removes emails where deletion is pending, reads new
* email and closes the connection.
*
* @param async determines if processing messages should happen asynchronously
* @throws MailTransferException in case of a known transport failure
* @throws IOException in case of a low-level transport failure
*/
@Override
public synchronized void handleEmails(boolean async) throws MailTransferException, IOException {
POP3Client pop3;
if (mailSettings.encryption != Encryption.NONE) {
pop3 = new POP3SClient(mailSettings.encryption.name(), true);
} else {
pop3 = new POP3Client();
}
if (mailSettings.port > 0) {
pop3.setDefaultPort(mailSettings.port);
}
pop3.connect(mailSettings.host);
try {
if (!pop3.login(mailSettings.username, mailSettings.password)) {
throw new MailTransferException(
"Could not login to POP3 email server. Check username and password");
}
try {
POP3MessageInfo[] messages = pop3.listMessages();
if (messages == null) {
throw new MailTransferException("Could not retrieve message list via POP3");
}
logger.atInfo().log("Received %d messages via POP3", messages.length);
// Fetch messages
List<MailMessage> mailMessages = new ArrayList<>();
for (POP3MessageInfo msginfo : messages) {
if (msginfo == null) {
// Message was deleted
continue;
}
try (BufferedReader reader = (BufferedReader) pop3.retrieveMessage(msginfo.number)) {
if (reader == null) {
throw new MailTransferException(
"Could not retrieve POP3 message header for message " + msginfo.identifier);
}
int[] message = fetchMessage(reader);
MailMessage mailMessage = RawMailParser.parse(message);
// Delete messages where deletion is pending. This requires
// knowing the integer message ID of the email. We therefore parse
// the message first and extract the Message-ID specified in RFC
// 822 and delete the message if deletion is pending.
if (pendingDeletion.contains(mailMessage.id())) {
if (pop3.deleteMessage(msginfo.number)) {
pendingDeletion.remove(mailMessage.id());
} else {
logger.atSevere().log("Could not delete message %d", msginfo.number);
}
} else {
// Process message further
mailMessages.add(mailMessage);
}
} catch (MailParsingException e) {
logger.atSevere().log("Could not parse message %d", msginfo.number);
}
}
dispatchMailProcessor(mailMessages, async);
} finally {
pop3.logout();
}
} finally {
pop3.disconnect();
}
}
public final int[] fetchMessage(BufferedReader reader) throws IOException {
List<Integer> character = new ArrayList<>();
int ch;
while ((ch = reader.read()) != -1) {
character.add(ch);
}
return Ints.toArray(character);
}
}