• Y
  • List All
  • Feedback
    • This Project
    • All Projects
Profile Account Log out
  • Favorite
  • Project
  • Recent History
Loading...
  • Log in
  • Sign up
kadrians / Testing_for_YONA star
  • Project homeH
  • CodeC
  • IssueI 1
  • Pull requestP
  • Review R
  • MilestoneM
  • BoardB 2
  • Files
  • Commit
  • Branches
Testing_for_YONAsourceappmailboxMailboxService.java
Download as .zip file
File name
Commit message
Commit date
bin
Yona 1.16.0 Upload
02-04
lib
Yona 1.16.0 Upload
02-04
share/doc/api
Yona 1.16.0 Upload
02-04
source
Source Code Upload
02-04
README.md
Yona 1.16.0 Upload
02-04
File name
Commit message
Commit date
app
Source Code Upload
02-04
conf
Source Code Upload
02-04
docs
Source Code Upload
02-04
lib
Source Code Upload
02-04
project
Source Code Upload
02-04
public
Source Code Upload
02-04
support-script
Source Code Upload
02-04
test
Source Code Upload
02-04
.gitignore
Source Code Upload
02-04
.mailmap
Source Code Upload
02-04
.travis.yml
Source Code Upload
02-04
AUTHORS
Source Code Upload
02-04
LICENSE
Source Code Upload
02-04
NOTICE
Source Code Upload
02-04
README.md
Source Code Upload
02-04
build.sbt
Source Code Upload
02-04
dev.sh
Source Code Upload
02-04
dist.sh
Source Code Upload
02-04
is-alive-bot.sh
Source Code Upload
02-04
minify-js.sh
Source Code Upload
02-04
restart.sh
Source Code Upload
02-04
File name
Commit message
Commit date
actions
Source Code Upload
02-04
actors
Source Code Upload
02-04
assets/stylesheets
Source Code Upload
02-04
controllers
Source Code Upload
02-04
data
Source Code Upload
02-04
errors
Source Code Upload
02-04
mailbox
Source Code Upload
02-04
models
Source Code Upload
02-04
notification
Source Code Upload
02-04
playRepository
Source Code Upload
02-04
service
Source Code Upload
02-04
utils
Source Code Upload
02-04
validation
Source Code Upload
02-04
views
Source Code Upload
02-04
Global.java
Source Code Upload
02-04
File name
Commit message
Commit date
exceptions
Source Code Upload
02-04
Content.java
Source Code Upload
02-04
CreationViaEmail.java
Source Code Upload
02-04
EmailAddressWithDetail.java
Source Code Upload
02-04
EmailHandler.java
Source Code Upload
02-04
IMAPMessageUtil.java
Source Code Upload
02-04
MailboxService.java
Source Code Upload
02-04
Nell 02-04 2600fe6 Source Code Upload UNIX
Raw Open in browser Change history
/** * Yona, 21st Century Project Hosting SW * <p> * Copyright Yona & Yobi Authors & NAVER Corp. & NAVER LABS Corp. * https://yona.io **/ package mailbox; import akka.actor.Cancellable; import com.sun.mail.imap.IMAPFolder; import com.sun.mail.imap.IMAPMessage; import com.sun.mail.imap.IMAPStore; import models.Property; import models.User; import play.Configuration; import play.Logger; import play.libs.Akka; import play.libs.F; import scala.concurrent.duration.Duration; import utils.Diagnostic; import utils.SimpleDiagnostic; import javax.annotation.Nonnull; import javax.annotation.concurrent.NotThreadSafe; import javax.mail.Folder; import javax.mail.FolderClosedException; import javax.mail.MessagingException; import javax.mail.Session; import javax.mail.event.MessageCountEvent; import javax.mail.event.MessageCountListener; import java.util.*; import java.util.concurrent.TimeUnit; import static javax.mail.Session.getDefaultInstance; /** * MailboxService opens a mailbox and process emails if necessary. * * MailboxService connects an IMAP server and opens the mailbox from the * server. Every configuration to be needed to do that is defined by imap.* in * conf/application.conf. * * Then MailboxService fetches and processes emails in the mailbox as follows: * * 1. Only emails whose recipients contain the address of Yobi defined by * imap.address configuration are accepted. * 2. Emails must have one or more recipients which are Yobi projects; If not * Yobi replies with an error. * 3. Emails which reference or reply to a resource assumed to comment the * resource; otherwise assumed to post an issue in the projects. * 4. Yobi does the assumed job only if the sender has proper permission to do * that; else Yobi replies with a permission denied error. * * Note: It is possible to create multiple resources if the recipients contain * multiple projects. */ @NotThreadSafe public class MailboxService { private IMAPStore store; private static IMAPFolder folder; private Thread idleThread; private Cancellable pollingSchedule; private boolean isStopping = false; private final static String IMAP_USE_KEY = "imap.use"; private final static boolean IMAP_USE_DEFAULT = true; private final static String IMAP_FOLDER_KEY = "imap.folder"; private final static String IMAP_FOLDER_DEFAULT = "inbox"; private final static String IMAP_HOST_KEY = "imap.host"; private final static String IMAP_PASSWORD_KEY = "imap.password"; private final static String IMAP_SSL_KEY = "imap.ssl"; private final static String IMAP_USER_KEY = "imap.user"; /** * Among the given {@code keys}, returns the keys that does not exist in * the given {@code config}. * * @param config * @param keys * @return the keys which don't have a matched value */ private static Set<String> getMissingKeys(Configuration config, String... keys) { Set<String> requiredKeys = new HashSet<>(Arrays.asList(keys)); requiredKeys.removeAll(config.keys()); return requiredKeys; } /** * Connects to the configured IMAP server. * * @return the store to be connected * @throws MessagingException */ private IMAPStore connect() throws MessagingException { final Configuration config = Configuration.root(); Set<String> missingKeys = getMissingKeys(config, IMAP_HOST_KEY, IMAP_USER_KEY, IMAP_PASSWORD_KEY); if (missingKeys.size() > 0) { throw new IllegalStateException( "Cannot connect to the IMAP server because these are" + " not configured: " + missingKeys); } Properties props = new Properties(); String s = config.getBoolean(IMAP_SSL_KEY, false) ? "s" : ""; props.setProperty("mail.store.protocol", "imap" + s); Session session = getDefaultInstance(props, null); store = (IMAPStore) session.getStore(); store.connect(config.getString(IMAP_HOST_KEY), config.getString(IMAP_USER_KEY), config.getString(IMAP_PASSWORD_KEY)); return store; } /** * Stop MailboxService. */ public void stop() { if (folder == null && store == null && pollingSchedule == null) { // We don't need to stop the Mailbox Service which didn't start. return; } isStopping = true; try { folder.close(true); store.close(); if (pollingSchedule != null && !pollingSchedule.isCancelled()) { pollingSchedule.cancel(); } } catch (MessagingException e) { play.Logger.error("Error occurred while stop the email receiver", e); } } /** * Start Mailbox Service. */ public void start() { if (Configuration.root().getString(IMAP_HOST_KEY) == null) { play.Logger.info("Mailbox Service doesn't start because IMAP server is not configured."); return; } Configuration config = Configuration.root(); if (!config.getBoolean(IMAP_USE_KEY, IMAP_USE_DEFAULT)) { return; } List<User> users = User.find.where() .ilike("email", config.getString(IMAP_USER_KEY) + "+%").findList(); if (users.size() == 1) { Logger.warn("There is a user whose email is danger: " + users); } if (users.size() > 1) { Logger.warn("There are some users whose email is danger: " + users); } try { store = connect(); folder = (IMAPFolder) store.getFolder( config.getString(IMAP_FOLDER_KEY, IMAP_FOLDER_DEFAULT)); folder.open(Folder.READ_ONLY); } catch (Exception e) { play.Logger.error("Failed to open IMAP folder", e); return; } handleNewMessagesAndStartListener(); Diagnostic.register(new SimpleDiagnostic() { @Override public String checkOne() { if (idleThread == null) { return "The Email Receiver is not initialized"; } else if (!idleThread.isAlive()) { return "The Email Receiver is not running"; } else { return null; } } }); } private void handleNewMessagesAndStartListener() { F.Promise<Void> promise = F.Promise.promise( new F.Function0<Void>() { public Void apply() { try { EmailHandler.handleNewMessages(folder); } catch (MessagingException e) { Logger.error("Failed to handle new messages"); } try { startEmailListener(); } catch (Exception e) { startEmailPolling(); } return null; } } ); } /** * Reopen the IMAP folder which is used by MailboxService. * * @return the open IMAP folder * @throws MessagingException */ private IMAPFolder reopenFolder() throws MessagingException { if (store == null || !store.isConnected()) { store = connect(); } IMAPFolder folder = (IMAPFolder) store.getFolder( Configuration.root().getString(IMAP_FOLDER_KEY, IMAP_FOLDER_DEFAULT)); folder.open(Folder.READ_ONLY); return folder; } /** * Start the polling of emails. * * The polling is fetching new emails from the IMAP folder and processing * them. * * This polling is a fallback of * {@link #startEmailListener()} if the IMAP server does * not support IDLE command. */ private void startEmailPolling() { Runnable polling = new Runnable() { @Override public void run() { try { if (folder == null || !folder.isOpen()) { folder = reopenFolder(); } EmailHandler.handleNewMessages(folder); } catch (MessagingException e) { play.Logger.error("Failed to poll emails", e); return; } try { folder.close(true); } catch (MessagingException e) { play.Logger.error("Failed to close the IMAP folder", e); } } }; pollingSchedule = Akka.system().scheduler().schedule( Duration.create(0, TimeUnit.MINUTES), Duration.create( Configuration.root().getMilliseconds("application.mailbox.polling.interval", 5 * 60 * 1000L), TimeUnit.MILLISECONDS), polling, Akka.system().dispatcher() ); } /** * Start the email listener by using IDLE command. * * The listener will fetch new emails from the IMAP folder and process them. * * @throws MessagingException * @throws UnsupportedOperationException */ private void startEmailListener() throws MessagingException, UnsupportedOperationException { if (!((IMAPStore)folder.getStore()).hasCapability("IDLE")) { throw new UnsupportedOperationException( "The imap server does not support IDLE command"); } MessageCountListener messageCountListener = new MessageCountListener() { @Override public void messagesAdded(@Nonnull MessageCountEvent e) { try { EmailHandler.handleMessages(folder, e.getMessages()); } catch (Exception e1) { play.Logger.error("Unexpected error occurs while handling messages", e1); } } @Override public void messagesRemoved(MessageCountEvent e) { } }; // Add the handler for messages to be added in the future. folder.addMessageCountListener(messageCountListener); idleThread = new Thread() { @Override public void run() { Logger.info("Start the Email Receiving Thread"); while (true) { if (isStopping) break; try { // Notify the message count listener if the value of EXISTS response is // larger than realTotal. folder.idle(); } catch (FolderClosedException e) { if (isStopping) break; // reconnect Logger.info("Reopen the imap folder"); try { folder = reopenFolder(); } catch (MessagingException e1) { Logger.warn("Failed to reopen the imap folder; " + "abort", e1); break; } } catch (Exception e) { Logger.warn("Failed to run IDLE command; abort", e); break; } } Logger.info("Stop the Email Receiving Thread"); } }; idleThread.start(); } /** * Update the lastSeenUID. * * lastSeenUID MUST be updated when a new email is processed so that * MailboxService fetches new emails correctly. * * @param msg * @throws MessagingException */ synchronized static void updateLastSeenUID(IMAPMessage msg) throws MessagingException { long uid = folder.getUID(msg); // Do not update lastSeenUID if it is larger than the current uid. try { long lastSeenUID = Property.getLong(Property.Name.MAILBOX_LAST_SEEN_UID); if (uid <= lastSeenUID) { return; } } catch (Exception ignored) { } Property.set(Property.Name.MAILBOX_LAST_SEEN_UID, uid); } }

          
        
    
    
Copyright Yona authors & © NAVER Corp. & NAVER LABS Supported by NAVER CLOUD PLATFORM

or
login with Google Sign in with Google
Reset password | Sign up