Mercurial Hosting > nabble
view src/nabble/model/MailingListImpl.java @ 0:7ecd1a4ef557
add content
author | Franklin Schmidt <fschmidt@gmail.com> |
---|---|
date | Thu, 21 Mar 2019 19:15:52 -0600 |
parents | |
children |
line wrap: on
line source
package nabble.model; import fschmidt.db.DbDatabase; import fschmidt.db.DbNull; import fschmidt.db.DbObject; import fschmidt.db.DbObjectFactory; import fschmidt.db.DbRecord; import fschmidt.db.DbTable; import fschmidt.db.DbUtils; import fschmidt.db.ListenerList; import fschmidt.db.LongKey; import fschmidt.util.java.Computable; import fschmidt.util.java.SimpleCache; import fschmidt.util.mail.Mail; import fschmidt.util.mail.MailAddress; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.File; import java.net.MalformedURLException; import java.net.URL; import java.sql.Connection; import java.sql.Statement; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.Random; import java.util.WeakHashMap; import java.util.List; import java.util.ArrayList; import java.util.regex.Matcher; import java.util.regex.Pattern; public final class MailingListImpl implements MailingList, DbObject<LongKey, MailingListImpl> { private static final Logger logger = LoggerFactory.getLogger(MailingListImpl.class); final SiteKey siteKey; private final DbRecord<LongKey, MailingListImpl> record; private String listAddress; private String listName; private String url; private String emailId; private boolean ignoreNoArchive; private boolean plainTextOnly; private ListServer listServer; private String exportOwner; private NodeImpl forum; private MailingListImpl(SiteKey siteKey,LongKey key, ResultSet rs) throws SQLException { this.siteKey = siteKey; record = table(siteKey).newRecord(this, key); listAddress = rs.getString("list_address"); emailId = rs.getString("email_id"); listName = rs.getString("list_name"); url = rs.getString("list_home_url"); ignoreNoArchive = rs.getBoolean("ignore_no_archive"); plainTextOnly = rs.getBoolean("plain_text_only"); listServer = ListServer.getServer(rs.getString("list_server")); exportOwner = rs.getString("export_owner"); } MailingListImpl(NodeImpl forum, ListServer listServer, String listAddress, String url) throws ModelException { this.siteKey = forum.siteKey; record = table(siteKey).newRecord(this); long id = forum.getId(); record.fields().put("node_id", id); this.forum = forum; setListServer(listServer); setListAddress(listAddress); setUrl(url); record.insert(); } public DbRecord<LongKey, MailingListImpl> getDbRecord() { return record; } private DbTable<LongKey,MailingListImpl> table() { return record.getDbTable(); } private DbDatabase db() { return table().getDbDatabase(); } public long getId() { return record.getPrimaryKey().value(); } NodeImpl getForumImpl() { if (DbUtils.isStale(forum)) { forum = NodeImpl.getNode(siteKey,getId()); } return forum; } public Node getForum() { return getForumImpl(); } public String getListAddress() { return listAddress; } private static MailingListImpl getMailingList(SiteKey siteKey,long id) { DbTable<LongKey,MailingListImpl> tbl = table(siteKey); return tbl==null ? null : tbl.findByPrimaryKey(new LongKey(id)); } public void delete() { if (!db().isInTransaction()) { db().beginTransaction(); try { MailingListImpl mailingList = DbUtils.getGoodCopy(this); mailingList.delete(); db().commitTransaction(); } finally { db().endTransaction(); } return; } record.delete(); } static MailingListImpl getMailingListForForum(NodeImpl forum) { MailingListImpl mailingList = table(forum.siteKey).findByPrimaryKey(new LongKey(forum.getId())); if (mailingList != null) { mailingList.forum = forum; } return mailingList; } private static class Lazy { static final String emailPrefix; static final String emailSuffix; static final Pattern EMAIL_SUFFIXPATTERN; static final Pattern pattern; static { String addrSpec = MailingLists.pop3Server.getUsername(); int ind = addrSpec.indexOf('@'); emailPrefix = addrSpec.substring(0, ind) + "+"; emailSuffix = addrSpec.substring(ind); EMAIL_SUFFIXPATTERN = Pattern.compile( Pattern.quote(emailPrefix) + "([^@]+)" + Pattern.quote(emailSuffix), Pattern.CASE_INSENSITIVE); pattern = Pattern.compile( Pattern.quote(emailPrefix) + "s(\\d+)n(\\d+)h(\\d+)" + Pattern.quote(emailSuffix), Pattern.CASE_INSENSITIVE); } } static MailingListImpl getMailingListByEnvelopeAddress(String address) { Matcher matcher = Lazy.pattern.matcher(address); if( matcher.matches() ) { long siteId = Long.valueOf(matcher.group(1)); SiteImpl site = SiteKey.getInstance(siteId).site(); if( site == null ) return null; long nodeId = Long.valueOf(matcher.group(2)); NodeImpl node = site.getNodeImpl(nodeId); if( node == null ) return null; MailingListImpl ml = node.getMailingListImpl(); if( ml==null ) return null; String hash = matcher.group(3); if( !ml.generateHash().equals(hash) ) return null; return ml; } matcher = Lazy.EMAIL_SUFFIXPATTERN.matcher(address); if( matcher.matches() ) { String emailId = matcher.group(1); return getMailingListByOldEmailId(emailId); } return null; } private static MailingListImpl getMailingListByOldEmailId(String emailId) { try { Connection con = Db.dbGlobal().getConnection(); PreparedStatement stmt = con.prepareStatement( "select * from mailing_list_lookup where email_id = ?" , ResultSet.TYPE_FORWARD_ONLY,ResultSet.CONCUR_UPDATABLE ); stmt.setString(1, emailId.trim().toLowerCase()); ResultSet rs = stmt.executeQuery(); try { if( !rs.next() ) return null; long siteId = rs.getLong("site_id"); long nodeId = rs.getLong("node_id"); SiteKey siteKey = SiteKey.getInstance(siteId); MailingListImpl ml = getMailingList(siteKey,nodeId); if( ml == null ) { logger.error("couldn't find mailing list site="+siteId+" node="+nodeId); rs.deleteRow(); return null; } return ml; } finally { rs.close(); stmt.close(); con.close(); } } catch (SQLException e) { throw new RuntimeException(e); } } public static int cleanMailingListLookup() { List<String> emailIds = new ArrayList<String>(); try { Connection con = Db.dbGlobal().getConnection(); Statement stmt = con.createStatement(); ResultSet rs = stmt.executeQuery( "select email_id from mailing_list_lookup" ); while( rs.next() ) { String emailId = rs.getString("email_id"); emailIds.add(emailId); } rs.close(); stmt.close(); con.close(); } catch (SQLException e) { throw new RuntimeException(e); } int count = 0; for( String emailId : emailIds ) { MailingList ml = getMailingListByOldEmailId(emailId); if (ml == null) count++; } return count; } public static List<MailingList> getOldMailingLists() { List<String> emailIds = new ArrayList<String>(); try { Connection con = Db.dbGlobal().getConnection(); Statement stmt = con.createStatement(); ResultSet rs = stmt.executeQuery( "select email_id from mailing_list_lookup" ); while( rs.next() ) { String emailId = rs.getString("email_id"); emailIds.add(emailId); } rs.close(); stmt.close(); con.close(); } catch (SQLException e) { throw new RuntimeException(e); } List<MailingList> list = new ArrayList<MailingList>(); for( String emailId : emailIds ) { MailingList ml = getMailingListByOldEmailId(emailId); if (ml != null) list.add(ml); } return list; } private String generateHash() { int h = 31*(int)getId(); return Integer.toString(Math.abs(h)%100); } public void setListAddress(String listAddress) throws ModelException.EmailFormat { listAddress = listAddress.trim(); UserImpl.validateEmail(listAddress); this.listAddress = listAddress; record.fields().put("list_address", listAddress); } public String getListName() { return listName; } public void setListName(String listName) { if( listName != null ) { listName = listName.trim(); if( listName.equals("") ) listName = null; } this.listName = listName; record.fields().put("list_name", DbNull.fix(listName)); } String fixSubject(String subject) { if( listName != null && subject != null ) { if( subject.startsWith(listName) ) return subject.substring(listName.length()).trim(); String lowerListName = listName.toLowerCase(); String lowerSubject = subject.toLowerCase(); if( lowerSubject.startsWith( "re: " + lowerListName + " re: " ) ) return subject.substring(listName.length()+5); if( lowerSubject.startsWith( "re: " + lowerListName ) ) return subject.substring(0,4) + subject.substring(4+listName.length()).trim(); } return subject; } public String getUrl() { return url; } public void setUrl(String url) throws ModelException.UrlFormat { url = url.trim(); validateUrl(url); this.url = url; record.fields().put("list_home_url", url); } static void validateUrl(String url) throws ModelException.UrlFormat { try { new URL(url); } catch(MalformedURLException e) { throw new ModelException.UrlFormat(url,e); } } public boolean ignoreNoArchive() { return ignoreNoArchive; } public void setIgnoreNoArchive(boolean ignoreNoArchive) { this.ignoreNoArchive = ignoreNoArchive; record.fields().put("ignore_no_archive", ignoreNoArchive); } public boolean plainTextOnly() { return plainTextOnly; } public void setPlainTextOnly(boolean plainTextOnly) { this.plainTextOnly = plainTextOnly; record.fields().put("plain_text_only", plainTextOnly); } public ListServer getListServer() { return listServer; } public void setListServer(ListServer listServer) { this.listServer = listServer; record.fields().put("list_server", listServer.getType()); } public String getEmailId() { return emailId; } public void setEmailId(String emailId) { this.emailId = emailId; record.fields().put("email_id", DbNull.fix(emailId)); } public void update() { record.update(); } public boolean equals(Object obj) { return obj instanceof MailingList && ((MailingList) obj).getId() == getId(); } public int hashCode() { return (int) getId(); } public String toString() { return "mailing_list-" + getId(); } public ImportResult importMbox(File file, String mailErrorsTo, int maxErrors) throws ModelException { ModelHome.beginImport(); try { return MailingLists.importMbox(file, this, mailErrorsTo, maxErrors); } finally { ModelHome.endImport(); } } public MailAddress getSubscriberAddress() { return new MailAddress( Lazy.emailPrefix + (emailId != null ? emailId : "s" + siteKey.getId() + 'n' + getId() + 'h' + generateHash() ) + Lazy.emailSuffix ); } public String getPassword(User user) { Random r = new Random(getId() + user.getId()); long p = Math.round(r.nextDouble() * Math.pow(Character.MAX_RADIX, 8)); return Long.toString(p, Character.MAX_RADIX); } public String getExportOwner() { return exportOwner; } public void setExportOwner(String email) throws ModelException.EmailFormat { if( email != null ) { email = email.trim(); UserImpl.validateEmail(email); } this.exportOwner = email; record.fields().put("export_owner", DbNull.fix(this.exportOwner)); } public Node getNodeFromMessageID(String messageID) { return forum.getNodeImplFromMessageID(messageID); } public void subscribe() { if (listServer.canSubscribe()) ModelHome.send(subscribeMail()); } public void unsubscribe() { if (listServer.canSubscribe()) ModelHome.send(unsubscribeMail()); } public Mail subscribeMail() { return listServer.subscribeMail(getSubscriberAddress(), getListAddress(), null, true); } public Mail subscribeMail(User user) { MailAddress userAddress = new MailAddress(user.getEmail(), user.getName()); return listServer.subscribeMail(userAddress, getListAddress(), getPassword(user), false); } public Mail unsubscribeMail() { return listServer.unsubscribeMail(getSubscriberAddress(), getListAddress(), null); } public Mail unsubscribeMail(User user) { MailAddress userAddress = new MailAddress(user.getEmail(), user.getName()); return listServer.unsubscribeMail(userAddress, getListAddress(), null); } public Mail defaultsMail(User user, String password) { MailAddress userAddress = new MailAddress(user.getEmail(), user.getName()); return listServer.defaultsMail(userAddress, getListAddress(), password); } static final ListenerList<MailingListImpl> preUpdateListeners = new ListenerList<MailingListImpl>(); static final ListenerList<MailingListImpl> postInsertListeners = new ListenerList<MailingListImpl>(); static final ListenerList<MailingListImpl> postUpdateListeners = new ListenerList<MailingListImpl>(); static final ListenerList<MailingListImpl> postDeleteListeners = new ListenerList<MailingListImpl>(); private static Computable<SiteKey,DbTable<LongKey,MailingListImpl>> tables = new SimpleCache<SiteKey,DbTable<LongKey,MailingListImpl>>(new WeakHashMap<SiteKey,DbTable<LongKey,MailingListImpl>>(), new Computable<SiteKey,DbTable<LongKey,MailingListImpl>>() { public DbTable<LongKey,MailingListImpl> get(SiteKey siteKey) { DbDatabase db; try { db = siteKey.getDb(); } catch(Db.NoSchema e) { return null; } final long siteId = siteKey.getId(); DbTable<LongKey,MailingListImpl> table = db.newTable("mailing_list",db.newAssignedLongKeySetter("node_id") , new DbObjectFactory<LongKey,MailingListImpl>() { public MailingListImpl makeDbObject(LongKey key,ResultSet rs,String tableName) throws SQLException { SiteKey siteKey = SiteKey.getInstance(siteId); return new MailingListImpl(siteKey,key,rs); } } ); table.getPreUpdateListeners().add(preUpdateListeners); table.getPostInsertListeners().add(postInsertListeners); table.getPostUpdateListeners().add(postUpdateListeners); table.getPostDeleteListeners().add(postDeleteListeners); return table; } }); private static DbTable<LongKey,MailingListImpl> table(SiteKey siteKey) { return tables.get(siteKey); } public void rethread() { try { MailingLists.rethreadForum( getForumImpl(), false ); } catch(SQLException e) { throw new RuntimeException(e); } } }