Java tutorial
/********************************************************************************* * The contents of this file are subject to the Common Public Attribution * License Version 1.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.openemm.org/cpal1.html. The License is based on the Mozilla * Public License Version 1.1 but Sections 14 and 15 have been added to cover * use of software over a computer network and provide for limited attribution * for the Original Developer. In addition, Exhibit A has been modified to be * consistent with Exhibit B. * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for * the specific language governing rights and limitations under the License. * * The Original Code is OpenEMM. * The Original Developer is the Initial Developer. * The Initial Developer of the Original Code is AGNITAS AG. All portions of * the code written by AGNITAS AG are Copyright (c) 2007 AGNITAS AG. All Rights * Reserved. * * Contributor(s): AGNITAS AG. ********************************************************************************/ package org.agnitas.backend; import java.io.File; import java.io.FileInputStream; import java.sql.Date; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.sql.Time; import java.sql.Timestamp; import java.sql.Types; import java.text.SimpleDateFormat; import java.util.Map; import java.util.List; import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; import java.util.Hashtable; import java.util.Locale; import java.util.TimeZone; import java.util.Vector; import java.util.ResourceBundle; import org.springframework.jdbc.core.simple.SimpleJdbcTemplate; import org.agnitas.util.Const; import org.agnitas.beans.BindingEntry; import org.agnitas.target.TargetRepresentation; import org.agnitas.preview.Page; import org.agnitas.util.Config; import org.agnitas.util.Log; import org.agnitas.util.Title; /** Class holding most of central configuration and global database * information */ public class Data { final static long serialVersionUID = 0x055da1a; /** the file to read the configuration from */ final static String INI_FILE = "Mailgun.ini"; /** default value for domain entry */ final static String DEF_DOMAIN = "openemm.org"; /** default value for boundary entry */ final static String DEF_BOUNDARY = "AGNITAS"; /** default value for EOL coding */ final static String DEF_EOL = "\r\n"; /** default value for X-Mailer: header */ final static String DEF_MAILER = "OpenEMM 2013/Agnitas AG"; /** Constant for onepixellog: no automatic insertion */ final public static int OPL_NONE = 0; /** Constant for onepixellog: insertion on top */ final public static int OPL_TOP = 1; /** Constant for onepixellog: insertion at bottom */ final public static int OPL_BOTTOM = 2; /** Configuration from properties file */ protected Config cfg = null; /** Loglevel */ private int logLevel = Log.ERROR; /** directory to write admin-/testmails to */ public String mailDir = null; /** default encoding for all blocks */ public String defaultEncoding = "quoted-printable"; /** default character set for all blocks */ public String defaultCharset = "UTF-8"; /** database driver name */ protected String dbDriver = null; /** database login */ protected String dbLogin = null; /** database password */ protected String dbPassword = null; /** database connect expression */ protected String dbConnect = null; /** database pool size */ protected int dbPoolsize = 12; /** database growable pool */ protected boolean dbPoolgrow = true; /** used block size */ private int blockSize = 1000; /** directory to store meta files for further processing */ private String metaDir = null; /** name of program to execute meta files */ private String xmlBack = "xmlback"; /** validate each generated block */ private boolean xmlValidate = false; /** Send samples of worldmailing to dedicated address(es) */ private String sampleEmails = null; /** write a DB record after creating that number of receiver */ private int mailLogNumber = 0; /** path to accounting logfile */ private String accLogfile = "log/account.log"; /** path to bounce logfile */ private String bncLogfile = "log/extbounce.log"; /** the user_status for this query */ public long defaultUserStatus = BindingEntry.USER_STATUS_ACTIVE; /** in case of campaing mailing, send mail only to this customer */ public long campaignCustomerID = 0; /** in case of a transaction, use this transaction ID */ public long campaignTransactionID = 0; /** for campaign mailings, use this user status in the binding table */ public long campaignUserStatus = BindingEntry.USER_STATUS_ACTIVE; /** for sending to one fix address */ public String fixedEmail = null; /** for preview mailings use this for matching the customer ID */ public long previewCustomerID = 0; /** for preview mailings store output */ public Page previewOutput = null; /** for preview of external text input */ public String previewInput = null; /** for preview mailings if preview should be anon */ public boolean previewAnon = false; /** for preview mailings if only partial preview requested */ public String previewSelector = null; /** for preview mailings if preview is cachable */ public boolean previewCachable = true; /** for preview mailings to convert to entities */ public boolean previewConvertEntities = false; /** for preview mailings to use legacy UIDs */ public boolean previewLegacyUIDs = false; /** for preview mailings to create all possible parts */ public boolean previewCreateAll = false; /** alternative campaign mailing selection */ public TargetRepresentation campaignSubselect = null; /** custom generated tags */ public Vector<String> customTags = null; /** custom generated tags with values */ public Hashtable<String, String> customMap = null; /** overwtite existing database fields */ public Hashtable<String, String> overwriteMap = null; /** overwtite existing database fields for more receivers */ public Hashtable<Long, Hashtable<String, String>> overwriteMapMulti = null; /** virtual database fields */ public Hashtable<String, String> virtualMap = null; /** virtual database fields for more receivers */ public Hashtable<Long, Hashtable<String, String>> virtualMapMulti = null; /** optional infos for that company */ public Hashtable<String, String> companyInfo = null; /** optional infos for this mailing */ public Hashtable<String, String> mailingInfo = null; /** keeps track of already read EMMTags from database */ private Hashtable<String, String[]> tagCache = null; /** instance to write logs to */ private Log log = null; /** the ID to write as marker in the logfile */ private String lid = null; /** the connection to the database */ public DBase dbase = null; /** status_id from maildrop_status_tbl */ public long maildrop_status_id = -1; /** assigned company to this mailing */ public long company_id = -1; /** mailinglist assigned to this mailing */ public long mailinglist_id = -1; /** for subselect, the target expression of the mailing */ public String targetExpression = null; /** mailign_id of this mailing */ public long mailing_id = -1; /** status_field from maildrop_status_tbl */ public String status_field = null; /** when to send the mailing, date */ public Date senddate = null; /** when to send the mailing, time */ public Time sendtime = null; /** when to send the mailing, date+time */ public Timestamp sendtimestamp = null; /** current send date, calculated from sendtimstamp and stepping */ public java.util.Date currentSendDate = null; /** the currentSendDate in epoch */ public long sendSeconds = 0; /** steps in seconds between two entities */ public int step = 0; /** number of blocks per entity */ public int blocksPerStep = 1; /** start steping at this block */ public int startBlockForStep = 1; /** the subselection for the receiver of this mailing */ public String subselect = null; /** the name of this mailing */ public String mailing_name = null; /** the subject for this mailing */ public String subject = null; /** the sender address for this mailing */ public EMail fromEmail = null; /** the optional reply-to address for this mailing */ public EMail replyTo = null; /** the envelope address */ public EMail envelopeFrom = null; /** the encoding for this mailing */ public String encoding = null; /** the charachter set for this mailing */ public String charset = null; /** domain used to build message-ids */ public String domain = DEF_DOMAIN; /** boundary part to build multipart messages */ public String boundary = DEF_BOUNDARY; /** EOL coding for spoolfiles */ public String eol = DEF_EOL; /** content of the X-Mailer: header */ public String mailer = DEF_MAILER; /** the base for the profile URL */ public String profileURL = null; public String profileTag = "/p.html?"; /** the base for the unsubscribe URL */ public String unsubscribeURL = null; public String unsubscribeTag = "/uq.html?"; /** the base for the auto URL */ public String autoURL = null; public String autoTag = "/r.html?"; /** the base for the onepixellog URL */ public String onePixelURL = null; public String onePixelTag = "/g.html?"; /** the largest mailtype to generate */ public int masterMailtype = Const.Mailtype.MASK; /** default line length in text part */ public int lineLength = 72; /** where to automatically place the onepixellog */ public int onepixlog = OPL_NONE; /** Password for signatures */ public String password = null; /** the base domain to build the base URLs */ public String rdirDomain = null; /** the mailloop domain */ public String mailloopDomain = null; /** Collection of media information */ public Media media = null; /** Bitfield of available media types in mailing */ public long availableMedias = 0; /** number of all subscriber of a mailing */ public long totalSubscribers = -1; public long totalReceivers = -1; /** number of all subscriber of a mailing */ private BC bigClause = null; /** keep track of collected targets */ private Hashtable<Long, Target> targets = null; /** all URLs from rdir_url_tbl */ public Vector<URL> URLlist = null; /** number of entries in URLlist */ public int urlcount = 0; /** all title tags */ private Hashtable<Long, Title> titles = null; /** layout of the customer table */ public Vector<Column> layout = null; /** number of entries in layout */ public int lcount = 0; /** number of entries in layout used */ public int lusecount = 0; /** name of the company (for logfile display only) */ public String company_name = null; /** name of mailtracking table */ public String mailtracking_table = null; /** for housekeeping of created files */ private Vector<String> toRemove = null; /** check if database is available */ private void checkDatabase() throws Exception { if (dbase == null) throw new Exception("Database not available"); } public Object mkDBase(Object me) throws Exception { return new DBase((Data) me); } public Object mkBigClause() { return new BC(); } /** * setup database connection and retreive a list of all available * tables * @param conn an optional existing database connection */ private void setupDatabase() throws Exception { try { dbase = (DBase) mkDBase(this); dbase.setup(); } catch (Exception e) { throw new Exception("Database setup failed: " + e); } } /** close a database and free all assigned data */ private void closeDatabase() throws Exception { if (dbase != null) { try { dbase.done(); dbase = null; } catch (Exception e) { throw new Exception("Database close failed: " + e); } } } /** * find an entry from the media record for this mailing * @param m instance of media record * @param id the ID to look for * @param dflt a default value if no entry is found * @return the found entry or the default */ public String findMediadata(Media m, String id, String dflt) { String rc; Vector<String> v = m.findParameterValues(id); rc = null; if ((v != null) && (v.size() > 0)) rc = v.elementAt(0); return rc == null ? dflt : rc; } /** * find a numeric entry from the media record for this mailing * @param m instance of media record * @param id the ID to look for * @param dflt a default value if no entry is found * @return the found entry or the default */ public long ifindMediadata(Media m, String id, long dflt) { String tmp = findMediadata(m, id, null); long rc; if (tmp != null) try { rc = Integer.parseInt(tmp); } catch (Exception e) { rc = dflt; } else rc = dflt; return rc; } public int ifindMediadata(Media m, String id, int dflt) { return (int) ifindMediadata(m, id, (long) dflt); } /** * find a boolean entry from the media record for this mailing * @param m instance of media record * @param id the ID to look for * @param dflt a default value if no entry is found * @return the found entry or the default */ public boolean bfindMediadata(Media m, String id, boolean dflt) { String tmp = findMediadata(m, id, null); boolean rc = dflt; if (tmp != null) if (tmp.length() == 0) rc = true; else { String tok = tmp.substring(0, 1).toLowerCase(); if (tok.equals("t") || tok.equals("y") || tok.equals("+") || tok.equals("1")) rc = true; } else rc = false; return rc; } /** * Retreive basic mailing data */ public void retreiveMailingInformation() throws Exception { Map<String, Object> rc; rc = dbase.querys("SELECT mailinglist_id, shortname, target_expression " + "FROM mailing_tbl WHERE mailing_id = :mailingID", "mailingID", mailing_id); mailinglist_id = dbase.asInt(rc.get("mailinglist_id")); mailing_name = dbase.asString(rc.get("shortname")); targetExpression = dbase.asString(rc.get("target_expression")); if (isPreviewMailing()) { targetExpression = null; } } public Object mkMedia(int mediatype, int priority, int status, String parameter) { return new Media(mediatype, priority, status, parameter); } /** * Retreive the media data */ public void retreiveMediaInformation() throws Exception { List<Map<String, Object>> rc; rc = dbase.query("SELECT mediatype, param FROM mailing_mt_tbl " + "WHERE mailing_id = :mailingID", "mailingID", mailing_id); if (rc.size() > 0) { Map<String, Object> row = rc.get(0); media = (Media) mkMedia(dbase.asInt(row.get("mediatype")), 0, Media.STAT_ACTIVE, dbase.asString(row.get("param"))); availableMedias = (1 << Media.TYPE_EMAIL); if (media.findParameterValues("charset") == null) media.setParameter("charset", defaultCharset); if (media.findParameterValues("encoding") == null) media.setParameter("encoding", defaultEncoding); fromEmail = new EMail(findMediadata(media, "from", null)); replyTo = new EMail(findMediadata(media, "reply", null)); subject = findMediadata(media, "subject", subject); charset = findMediadata(media, "charset", charset); masterMailtype = ifindMediadata(media, "mailformat", masterMailtype) & Const.Mailtype.MASK; if (masterMailtype == 2) { masterMailtype = Const.Mailtype.HTML | Const.Mailtype.HTML_OFFLINE; } encoding = findMediadata(media, "encoding", encoding); lineLength = ifindMediadata(media, "linefeed", lineLength); String opl = findMediadata(media, "onepixlog", "none"); if (opl.equals("top")) onepixlog = OPL_TOP; else if (opl.equals("bottom")) onepixlog = OPL_BOTTOM; else onepixlog = OPL_NONE; } envelopeFrom = fromEmail; } /** * query company specific details */ public void retreiveCompanyInfo() throws Exception { Map<String, Object> rc; mailtracking_table = "mailtrack_tbl"; rc = dbase.querys( "SELECT c.shortname, c.xor_key, if(not ml.rdir_domain is null and ml.rdir_domain != '', ml.rdir_domain, c.rdir_domain) as rdir_domain, c.mailloop_domain, if(not ml.messageid_domain is null and ml.messageid_domain != '', ml.messageid_domain, c.mailloop_domain) as messageid_domain FROM company_tbl c inner join mailinglist_tbl ml on c.company_id = ml.company_id WHERE ml.mailinglist_id = :mailinglistID and ml.company_id = :companyID", "companyID", company_id, "mailinglistID", mailinglist_id); company_name = dbase.asString(rc.get("shortname")); password = dbase.asString(rc.get("xor_key")); rdirDomain = dbase.asString(rc.get("rdir_domain")); mailloopDomain = dbase.asString(rc.get("mailloop_domain")); String tmpDomain = dbase.asString(rc.get("messageid_domain")); if (tmpDomain != null && tmpDomain.length() > 0) domain = tmpDomain; } public Object mkTarget(long tid, String sql) { return new Target(tid, sql); } public Target getTarget(long tid) throws Exception { if (targets == null) { targets = new Hashtable<Long, Target>(); } Long targetID = new Long(tid); Target rc = targets.get(targetID); String reason = "invalid"; if (rc == null) { String sql = null; int deleted = 0; try { Map<String, Object> row = dbase.querys( "SELECT target_sql, deleted FROM dyn_target_tbl WHERE target_id = :targetID", "targetID", tid); deleted = dbase.asInt(row.get("deleted")); if (deleted != 0) { logging(Log.ERROR, "targets", "TargetID " + tid + " is marked as deleted"); sql = null; reason = "deleted"; } else { sql = dbase.asString(row.get("target_sql"), 3); if (sql == null) { reason = "empty"; } } } catch (Exception e) { logging(Log.ERROR, "targets", "No target with ID " + tid + " found in dyn_target_tbl: " + e.toString()); sql = null; reason = "non existing"; } rc = (Target) mkTarget(tid, sql); targets.put(targetID, rc); } if (!rc.valid()) { throw new Exception("TargetID " + tid + ": " + reason + " target found"); } return rc; } public String moreStatusColumns() { return null; } public void moreStatusColumnsParse(Map<String, Object> row) throws Exception { } public void moreStatusColumnsPostParse() throws Exception { } public void setupMailingInformations(String prefix, String status) throws Exception { if (prefix.equals("preview")) { String[] opts = status.split(","); if (opts.length > 0) { try { mailing_id = Long.parseLong(opts[0]); } catch (NumberFormatException e) { logging(Log.WARNING, "setup", "Unparseable input string for mailing_id: \"" + opts[0] + "\": " + e.toString()); mailing_id = 0; } } else { mailing_id = 0; } if (mailing_id > 0) { try { company_id = dbase.queryLong("SELECT company_id FROM mailing_tbl WHERE mailing_id = :mailingID", "mailingID", mailing_id); } catch (Exception e) { throw new Exception( "Failed to query company_id for mailing_id " + mailing_id + ": " + e.toString()); } } else { mailing_id = 0; company_id = 1; mailinglist_id = 1; try { if (opts.length > 1) { company_id = Long.parseLong(opts[1]); if (opts.length > 2) { mailinglist_id = Long.parseLong(opts[2]); } } } catch (NumberFormatException e) { logging(Log.WARNING, "setup", "Unparseable input string for preview \"" + status + "\": " + e.toString()); } } status_field = "P"; sendtimestamp = null; } else throw new Exception("Unknown status prefix \"" + prefix + "\" encountered"); } class Layout implements org.springframework.jdbc.core.ResultSetExtractor { private Vector<Column> layout; private String ref; public Layout(Vector<Column> nLayout, String nRef) { layout = nLayout; ref = nRef; } public Vector<Column> getLayout() { return layout; } public Object extractData(ResultSet rset) throws SQLException, org.springframework.dao.DataAccessException { ResultSetMetaData meta = rset.getMetaData(); int ccnt = meta.getColumnCount(); for (int n = 0; n < ccnt; ++n) { String cname = meta.getColumnName(n + 1); int ctype = meta.getColumnType(n + 1); if (ctype == -1) { String tname = meta.getColumnTypeName(n + 1); if (tname != null) { tname = tname.toLowerCase(); if (tname.equals("varchar")) { ctype = Types.VARCHAR; } } } if (Column.typeStr(ctype) != null) { Column c = new Column(cname, ctype); c.setRef(ref); if (layout == null) { layout = new Vector<Column>(); } layout.addElement(c); } } return this; } } protected void getTableLayout(String table, String ref) throws Exception { String query = "SELECT * FROM " + table + " WHERE 1 = 0"; Layout temp = new Layout(layout, ref); dbase.op().query(query, temp); layout = temp.getLayout(); if (layout != null) { lcount = layout.size(); lusecount = lcount; } } private void setGenerationStatus(int fromStatus, int toStatus) throws Exception { if (maildrop_status_id > 0) { String query; query = "UPDATE maildrop_status_tbl SET genchange = " + dbase.sysdate + ", genstatus = :tostatus " + "WHERE status_id = :statusID"; if (fromStatus > 0) { query += " AND genstatus = :fromStatus"; } try { int cnt = dbase.update(query, "tostatus", toStatus, "fromStatus", fromStatus, "statusID", maildrop_status_id); if (cnt != 1) { throw new Exception("change should affect one row, but affects " + cnt + " rows"); } } catch (Exception e) { String m = "Unable to update generation state " + (fromStatus > 0 ? "from " + fromStatus + " " : "") + "to " + toStatus; throw new Exception(m + ": " + e.toString()); } } } public String extraURLTableClause() { return ""; } public String extraURLTableColumns() { return ""; } public void extraURLTableGetColumns(URL url, Map<String, Object> row) { } public void getURLDetails() { } /** * query all basic information about this mailing * @param status_id the reference to the mailing */ private void queryMailingInformations(String status_id) throws Exception { checkDatabase(); try { String[] sdetail = status_id.split(":", 2); if (sdetail.length == 2) { setupMailingInformations(sdetail[0], sdetail[1]); } else { Map<String, Object> row; int bs, st; int genstat; String moreCols; String query; maildrop_status_id = Long.parseLong(status_id); moreCols = moreStatusColumns(); query = "SELECT company_id, mailing_id, status_field, senddate, step, blocksize, genstatus"; if (moreCols != null) { query += ", " + moreCols; } query += " FROM maildrop_status_tbl WHERE status_id = :statusID"; row = dbase.querys(query, "statusID", maildrop_status_id); company_id = dbase.asLong(row.get("company_id")); mailing_id = dbase.asLong(row.get("mailing_id")); status_field = dbase.asString(row.get("status_field")); sendtimestamp = (Timestamp) row.get("senddate"); st = dbase.asInt(row.get("step")); bs = dbase.asInt(row.get("blocksize")); genstat = dbase.asInt(row.get("genstatus")); moreStatusColumnsParse(row); moreStatusColumnsPostParse(); if (status_field.equals("C")) status_field = "E"; if (bs > 0) setBlockSize(bs); setStepping(st); if (genstat != 1) throw new Exception("Generation state is not 1, but " + genstat); if (isAdminMailing() || isTestMailing() || isWorldMailing() || isRuleMailing() || isOnDemandMailing()) { setGenerationStatus(1, 2); } } if (mailing_id > 0) { retreiveMailingInformation(); if (targetExpression != null) { StringBuffer buf = new StringBuffer(); int tlen = targetExpression.length(); for (int n = 0; n < tlen; ++n) { char ch = targetExpression.charAt(n); if ((ch == '(') || (ch == ')')) { buf.append(ch); } else if ((ch == '&') || (ch == '|')) { if (ch == '&') buf.append(" AND"); else buf.append(" OR"); while (((n + 1) < tlen) && (targetExpression.charAt(n + 1) == ch)) ++n; } else if (ch == '!') { buf.append(" NOT"); } else if ("0123456789".indexOf(ch) != -1) { int newn = n; long tid = 0; int pos; Target temp; while ((n < tlen) && ((pos = "0123456789".indexOf(ch)) != -1)) { newn = n; tid *= 10; tid += pos; ++n; if (n < tlen) ch = targetExpression.charAt(n); else ch = '\0'; } n = newn; temp = getTarget(tid); if ((temp != null) && temp.valid()) buf.append(" (" + temp.sql + ")"); } } if (buf.length() >= 3) subselect = buf.toString(); } retreiveMediaInformation(); } if (subselect == null) { if (isRuleMailing()) { try { dbase.update("DELETE FROM maildrop_status_tbl WHERE status_id = :statusID", "statusID", maildrop_status_id); } catch (SQLException e) { logging(Log.ERROR, "init", "Failed to disable rule based mailing: " + e.toString()); } throw new Exception("Missing target: Rule based mailing generation aborted and disabled"); } else if (isOnDemandMailing()) { try { setGenerationStatus(0, 4); } catch (Exception e) { logging(Log.ERROR, "init", "Failed to set genreation status: " + e.toString()); } throw new Exception( "Missing target: On Demand mailing generation aborted and left in undefined condition"); } } if ((encoding == null) || (encoding.length() == 0)) encoding = defaultEncoding; if ((charset == null) || (charset.length() == 0)) charset = defaultCharset; // // get all possible URLs that should be replaced List<Map<String, Object>> rc; URLlist = new Vector<URL>(); if (mailing_id > 0) { rc = dbase.query("SELECT url_id, full_url, " + dbase.measureType + extraURLTableColumns() + " FROM rdir_url_tbl " + "WHERE company_id = :companyID AND mailing_id = :mailingID" + extraURLTableClause(), "companyID", company_id, "mailingID", mailing_id); for (int n = 0; n < rc.size(); ++n) { Map<String, Object> row = rc.get(n); long id = dbase.asLong(row.get("url_id")); String dest = dbase.asString(row.get("full_url")); long usage = dbase.asLong(row.get(dbase.measureRepr)); if (usage != 0) { URL url = new URL(id, dest, usage); extraURLTableGetColumns(url, row); URLlist.addElement(url); } } } urlcount = URLlist.size(); getURLDetails(); // // and now try to determinate the layout of the // customer table getTableLayout("customer_" + company_id + "_tbl", null); Hashtable<String, Column> cmap = new Hashtable<String, Column>(); for (int n = 0; n < lcount; ++n) { Column c = layout.get(n); cmap.put(c.name.toLowerCase(), c); } rc = dbase.query("SELECT col_name, shortname FROM customer_field_tbl WHERE company_id = :companyID", "companyID", company_id); for (int n = 0; n < rc.size(); ++n) { Map<String, Object> row = rc.get(n); String column = dbase.asString(row.get("col_name")); if (column != null) { Column c = cmap.get(column); if (c != null) { c.setAlias(dbase.asString(row.get("shortname"))); } } } retreiveCompanyInfo(); if (rdirDomain != null) { if (profileURL == null) profileURL = rdirDomain + profileTag; if (unsubscribeURL == null) unsubscribeURL = rdirDomain + unsubscribeTag; if (autoURL == null) autoURL = rdirDomain + autoTag; if (onePixelURL == null) onePixelURL = rdirDomain + onePixelTag; } } catch (Exception e) { logging(Log.ERROR, "init", "Error in quering initial data: " + e); throw new Exception("Database error/initial query: " + e, e); } } public Locale getLocale(String language, String country) { if (language == null) { return null; } if (country == null) { return new Locale(language); } return new Locale(language, country); } public Title getTitle(Long tid) { if (titles == null) { titles = new Hashtable<Long, Title>(); try { List<Map<String, Object>> rc; rc = dbase.query("SELECT title_id, title, gender FROM title_gender_tbl " + "WHERE title_id IN (SELECT title_id FROM title_tbl WHERE company_id = :companyID OR company_id = 0 OR company_id IS null)", "companyID", company_id); for (int n = 0; n < rc.size(); ++n) { Map<String, Object> row = rc.get(n); Long id = dbase.asLong(row.get("title_id")); String title = dbase.asString(row.get("title")); int gender = dbase.asInt(row.get("gender")); Title cur = null; if ((cur = titles.get(id)) == null) { cur = new Title(id); titles.put(id, cur); } cur.setTitle(gender, title); } } catch (Exception e1) { logging(Log.ERROR, "title", "Failed to query titles: " + e1.toString()); } } return titles.get(tid); } public String[] getTag(String tagName) { String[] rc = null; if (tagCache == null) { tagCache = new Hashtable<String, String[]>(); } else { rc = tagCache.get(tagName); } if (rc == null) { String query; HashMap<String, Object> input; SimpleJdbcTemplate jdbc; rc = new String[2]; query = "SELECT selectvalue, type FROM tag_tbl WHERE tagname = :tagname AND (company_id = :companyID OR company_id = 0) ORDER BY company_id DESC"; input = new HashMap<String, Object>(2); input.put("tagname", tagName); input.put("companyID", company_id); jdbc = null; try { List<Map<String, Object>> rq; jdbc = dbase.request(query); rq = jdbc.queryForList(query, input); if (rq.size() > 0) { Map<String, Object> row = rq.get(0); rc[0] = dbase.asString(row.get("selectvalue")); rc[1] = dbase.asString(row.get("type")); } tagCache.put(tagName, rc); } catch (Exception e) { logging(Log.WARNING, "gettag", "Failed to retreive tag \"" + tagName + "\" using \"" + query + "\": " + e.toString()); } finally { if (jdbc != null) { dbase.release(jdbc); } } } return rc; } /** * Fill already sent recipient in seen hashset for * recovery prupose * @param seen the hashset to fill with seen customerIDs */ public void prefillRecipients(HashSet<Long> seen) throws Exception { if (isWorldMailing() || isRuleMailing() || isOnDemandMailing()) { File recovery = new File(metaDir, "recover-" + maildrop_status_id + ".list"); if (recovery.exists()) { logging(Log.INFO, "recover", "Found recovery file " + recovery.getAbsolutePath()); markToRemove(recovery.getAbsolutePath()); FileInputStream in = new FileInputStream(recovery); byte[] content = new byte[(int) recovery.length()]; in.read(content); in.close(); String[] data = (new String(content, "US-ASCII")).split("\n"); for (int n = 0; n < data.length; ++n) { if (data[n].length() > 0) { seen.add(Long.decode(data[n])); } } } } } /** * Set the blocksize for generation doing some sanity checks * @param newBlockSize the new block size to use */ public void setBlockSize(int newBlockSize) { blocksPerStep = 1; blockSize = newBlockSize; } /** * Set the stepping in minutes * @param stepping value */ public void setStepping(int newStep) { step = newStep; } /** increase starting block */ public void increaseStartblockForStepping() { ++startBlockForStep; } /** * Validate all set variables and make a sanity check * on the database to avoid double triggering of a * mailing */ public void checkMailingData() throws Exception { int cnt; String msg; cnt = 0; msg = ""; if (isWorldMailing()) try { List<Map<String, Object>> rq; Map<String, Object> row; long nid; checkDatabase(); rq = dbase.query( "SELECT status_id FROM maildrop_status_tbl WHERE status_field = :statusField AND mailing_id = :mailingID ORDER BY status_id", "statusField", "W", "mailingID", mailing_id); if (rq.size() == 0) { throw new Exception("no entry at all for mailingID " + mailing_id + " found"); } row = rq.get(0); nid = dbase.asLong(row.get("status_id")); if (nid != maildrop_status_id) { ++cnt; msg += "\tlowest maildrop_status_id is not mine (" + maildrop_status_id + ") but " + nid + "\n"; dbase.update("DELETE FROM maildrop_status_tbl WHERE status_id = :statusID", "statusID", maildrop_status_id); } } catch (Exception e) { ++cnt; msg += "\tunable to requery my status_id: " + e.toString() + "\n"; } if ((!isPreviewMailing()) && (maildrop_status_id < 0)) { ++cnt; msg += "\tmaildrop_status_id is less than 1 (" + maildrop_status_id + ")\n"; } if (company_id <= 0) { ++cnt; msg += "\tcompany_id is less than 1 (" + company_id + ")\n"; } if (mailinglist_id <= 0) { ++cnt; msg += "\tmailinglist_id is less than 1 (" + mailinglist_id + ")\n"; } if (mailing_id < 0) { ++cnt; msg += "\tmailing_id is less than 0 (" + mailing_id + ")\n"; } if ((!isAdminMailing()) && (!isTestMailing()) && (!isCampaignMailing()) && (!isRuleMailing()) && (!isOnDemandMailing()) && (!isWorldMailing()) && (!isPreviewMailing())) { ++cnt; msg += "\tstatus_field must be one of A, V, T, E, R, D, W or P (" + status_field + ")\n"; } long now = System.currentTimeMillis() / 1000; if (sendtimestamp != null) sendSeconds = sendtimestamp.getTime() / 1000; else if ((senddate != null) && (sendtime != null)) sendSeconds = (senddate.getTime() + sendtime.getTime()) / 1000; else sendSeconds = now; if (sendSeconds < now) currentSendDate = new java.util.Date(now * 1000); else currentSendDate = new java.util.Date(sendSeconds * 1000); if (step < 0) { ++cnt; msg += "\tstep is less than 0 (" + step + ")\n"; } if ((encoding == null) || (encoding.length() == 0)) { ++cnt; msg += "\tmissing or empty encoding\n"; } if ((charset == null) || (charset.length() == 0)) { ++cnt; msg += "\tmissing or empty charset\n"; } if ((profileURL == null) || (profileURL.length() == 0)) { ++cnt; msg += "\tmissing or empty profile_url\n"; } if ((unsubscribeURL == null) || (unsubscribeURL.length() == 0)) { ++cnt; msg += "\tmissing or empty unsubscribe_url\n"; } if ((autoURL == null) || (autoURL.length() == 0)) { ++cnt; msg += "\tmissing or empty auto_url\n"; } if ((onePixelURL == null) || (onePixelURL.length() == 0)) { // ++cnt; onePixelURL = "file://localhost/"; msg += "\tmissing or empty onepixel_url\n"; } if (lineLength < 0) { ++cnt; msg += "\tlinelength is less than zero\n"; } if (cnt > 0) { logging(Log.ERROR, "init", "Error configuration report:\n" + msg); throw new Exception(msg); } if (msg.length() > 0) logging(Log.INFO, "init", "Configuration report:\n" + msg); } /** Setup logging interface * @param program to create the logging path * @param setprinter if we should also log to stdout */ private void setupLogging(String program, boolean setprinter) { log = new Log(program, logLevel); if (setprinter) log.setPrinter(System.out); } /** * Write all settings to logfile */ public void logSettings() { logging(Log.DEBUG, "init", "Initial data valid"); logging(Log.DEBUG, "init", "All set variables:"); logging(Log.DEBUG, "init", "\tlogLevel = " + log.levelDescription() + " (" + log.level() + ")"); logging(Log.DEBUG, "init", "\tmailDir = " + mailDir); logging(Log.DEBUG, "init", "\tdefaultEncoding = " + defaultEncoding); logging(Log.DEBUG, "init", "\tdefaultCharset = " + defaultCharset); logging(Log.DEBUG, "init", "\tdbDriver = " + dbDriver); logging(Log.DEBUG, "init", "\tdbLogin = " + dbLogin); logging(Log.DEBUG, "init", "\tdbPassword = ******"); logging(Log.DEBUG, "init", "\tdbConnect = " + dbConnect); logging(Log.DEBUG, "init", "\tdbPoolsize = " + dbPoolsize); logging(Log.DEBUG, "init", "\tdbPoolgrow = " + dbPoolgrow); logging(Log.DEBUG, "init", "\tblockSize = " + blockSize); logging(Log.DEBUG, "init", "\tmetaDir = " + metaDir); logging(Log.DEBUG, "init", "\txmlBack = " + xmlBack); logging(Log.DEBUG, "init", "\txmlValidate = " + xmlValidate); logging(Log.DEBUG, "init", "\tsampleEmails = " + sampleEmails); logging(Log.DEBUG, "init", "\tmailLogNumber = " + mailLogNumber); logging(Log.DEBUG, "init", "\taccLogfile = " + accLogfile); logging(Log.DEBUG, "init", "\tbncLogfile = " + bncLogfile); logging(Log.DEBUG, "init", "\tdefaultUserStatus = " + defaultUserStatus); logging(Log.DEBUG, "init", "\tdbase = " + dbase); logging(Log.DEBUG, "init", "\tmaildrop_status_id = " + maildrop_status_id); logging(Log.DEBUG, "init", "\tcompany_id = " + company_id); if (company_name != null) logging(Log.DEBUG, "init", "\tcompany_name = " + company_name); if (mailtracking_table != null) logging(Log.DEBUG, "init", "\tmailtracking_table = " + mailtracking_table); logging(Log.DEBUG, "init", "\tmailinglist_id = " + mailinglist_id); logging(Log.DEBUG, "init", "\tmailing_id = " + mailing_id); logging(Log.DEBUG, "init", "\tstatus_field = " + status_field); logging(Log.DEBUG, "init", "\tsenddate = " + senddate); logging(Log.DEBUG, "init", "\tsendtime = " + sendtime); logging(Log.DEBUG, "init", "\tsendtimestamp = " + sendtimestamp); logging(Log.DEBUG, "init", "\tsendSeconds = " + sendSeconds); logging(Log.DEBUG, "init", "\tstep = " + step); logging(Log.DEBUG, "init", "\tblocksPerStep = " + blocksPerStep); logging(Log.DEBUG, "init", "\tsubselect = " + (subselect == null ? "*not set*" : subselect)); logging(Log.DEBUG, "init", "\tmailing_name = " + (mailing_name == null ? "*not set*" : mailing_name)); logging(Log.DEBUG, "init", "\tsubject = " + (subject == null ? "*not set*" : subject)); logging(Log.DEBUG, "init", "\tfromEmail = " + (fromEmail == null ? "*not set*" : fromEmail.toString())); logging(Log.DEBUG, "init", "\treplyTo = " + (replyTo == null ? "*not set*" : replyTo.toString())); logging(Log.DEBUG, "init", "\tenvelopeFrom = " + (envelopeFrom == null ? "*not set*" : envelopeFrom.toString())); logging(Log.DEBUG, "init", "\tencoding = " + encoding); logging(Log.DEBUG, "init", "\tcharset = " + charset); logging(Log.DEBUG, "init", "\tdomain = " + domain); logging(Log.DEBUG, "init", "\tboundary = " + boundary); if (eol.equals("\r\n")) logging(Log.DEBUG, "init", "\teol = CRLF"); else if (eol.equals("\n")) logging(Log.DEBUG, "init", "\teol = LF"); else logging(Log.DEBUG, "init", "\teol = unknown (" + eol.length() + ")"); logging(Log.DEBUG, "init", "\tmailer = " + mailer); logging(Log.DEBUG, "init", "\tprofileURL = " + profileURL); logging(Log.DEBUG, "init", "\tunsubscribeURL = " + unsubscribeURL); logging(Log.DEBUG, "init", "\tautoURL = " + autoURL); logging(Log.DEBUG, "init", "\tonePixelURL = " + onePixelURL); logging(Log.DEBUG, "init", "\tmasterMailtype = " + masterMailtype); logging(Log.DEBUG, "init", "\tlineLength = " + lineLength); logging(Log.DEBUG, "init", "\tonepixlog = " + onepixlog); logging(Log.DEBUG, "init", "\tpassword = " + password); logging(Log.DEBUG, "init", "\trdirDomain = " + rdirDomain); logging(Log.DEBUG, "init", "\tmailloopDomain = " + mailloopDomain); } /* * the main configuration */ public void configure() throws Exception { String val; if ((val = cfg.cget("LOGLEVEL")) != null) { try { logLevel = Log.matchLevel(val); if (log != null) { log.level(logLevel); } } catch (NumberFormatException e) { throw new Exception("Loglevel must be a known string or a numerical value, not " + val); } } mailDir = cfg.cget("MAILDIR", mailDir); defaultEncoding = cfg.cget("DEFAULT_ENCODING", defaultEncoding); defaultCharset = cfg.cget("DEFAULT_CHARSET", defaultCharset); dbDriver = cfg.cget("DB_DRIVER", dbDriver); dbLogin = cfg.cget("DB_LOGIN", dbLogin); dbPassword = cfg.cget("DB_PASSWORD", dbPassword); dbConnect = cfg.cget("SQL_CONNECT", dbConnect); dbPoolsize = cfg.cget("DB_POOLSIZE", dbPoolsize); dbPoolgrow = cfg.cget("DB_POOLGROW", dbPoolgrow); blockSize = cfg.cget("BLOCKSIZE", blockSize); metaDir = cfg.cget("METADIR", metaDir); xmlBack = cfg.cget("XMLBACK", xmlBack); xmlValidate = cfg.cget("XMLVALIDATE", xmlValidate); if (((sampleEmails = cfg.cget("SAMPLE_EMAILS", sampleEmails)) != null) && ((sampleEmails.length() == 0) || sampleEmails.equals("-"))) { sampleEmails = null; } domain = cfg.cget("DOMAIN", domain); boundary = cfg.cget("BOUNDARY", boundary); if ((val = cfg.cget("EOL")) != null) { if (val.equalsIgnoreCase("CRLF")) { eol = "\r\n"; } else if (val.equalsIgnoreCase("LF")) { eol = "\n"; } else { throw new Exception("EOL must be either CRLF or LF, not " + val); } } mailer = cfg.cget("MAILER", mailer); mailLogNumber = cfg.cget("MAIL_LOG_NUMBER", mailLogNumber); accLogfile = cfg.cget("ACCOUNT_LOGFILE", accLogfile); bncLogfile = cfg.cget("BOUNCE_LOGFILE", bncLogfile); } /* * Setup configuration * @param checkRsc first check for resource bundle */ private void configuration(boolean checkRsc) throws Exception { boolean done; cfg = new Config(log); done = false; if (checkRsc) { try { ResourceBundle rsc; rsc = ResourceBundle.getBundle("emm"); if (rsc != null) { done = cfg.loadConfig(rsc, "mailgun.ini"); } } catch (Exception e) { ; } } if (!done) { cfg.loadConfig(INI_FILE); } configure(); } /** * Constructor for the class * @param program the name of the program (for logging setup) * @param status_id the status_id to read the mailing information from * @param option output option * @param conn optional opened database connection */ public Data(String program, String status_id, String option) throws Exception { setupLogging(program, (option == null || !option.equals("silent"))); configuration(true); logging(Log.DEBUG, "init", "Data read from " + cfg.getSource() + " for " + status_id); setupDatabase(); logging(Log.DEBUG, "init", "Initial database connection established"); try { queryMailingInformations(status_id); } catch (Exception e) { throw new Exception("Database failure: " + e, e); } logging(Log.DEBUG, "init", "Initial data read from database"); checkMailingData(); lid = "(" + company_id + "/" + mailinglist_id + "/" + mailing_id + "/" + maildrop_status_id + ")"; if (islog(Log.DEBUG)) { logSettings(); } } /** * Constructor for non mailing based instances * @param program the program name for logging */ public Data(String program) throws Exception { setupLogging(program, true); configuration(true); logging(Log.DEBUG, "init", "Starting up"); setupDatabase(); } /** * Suspend call between setup and main execution * @param conn optional database connection */ public void suspend() throws Exception { if (isCampaignMailing() || isPreviewMailing()) closeDatabase(); } /** * Resume before main execution * @param conn optional database connection */ public void resume() throws Exception { if (isCampaignMailing() || isPreviewMailing()) if (dbase == null) { setupDatabase(); } } /** * Cleanup all open resources and write mailing status before */ public void done() throws Exception { int cnt; String msg; cnt = 0; msg = ""; if (bigClause != null) { bigClause.done(); bigClause = null; } if (dbase != null) { logging(Log.DEBUG, "deinit", "Shuting down database connection"); try { closeDatabase(); } catch (Exception e) { ++cnt; msg += "\t" + e + "\n"; } } if (toRemove != null) { int fcnt = toRemove.size(); if (fcnt > 0) { logging(Log.DEBUG, "deinit", "Remove " + fcnt + " file" + Log.exts(fcnt) + " if existing"); while (fcnt-- > 0) { String fname = toRemove.remove(0); File file = new File(fname); if (file.exists()) if (!file.delete()) msg += "\trm " + fname + "\n"; file = null; } } toRemove = null; } if (cnt > 0) throw new Exception("Unable to cleanup:\n" + msg); logging(Log.DEBUG, "deinit", "Cleanup done: " + msg); } /** * Sanity check for mismatch company_id and perhaps deleted * mailing */ public void sanityCheck() throws Exception { if (!isPreviewMailing()) { try { long cid, del; Map<String, Object> row; row = dbase.querys("SELECT company_id, deleted FROM mailing_tbl WHERE mailing_id = :mailingID", "mailingID", mailing_id); cid = dbase.asLong(row.get("company_id")); del = dbase.asLong(row.get("deleted")); if (cid != company_id) { throw new Exception("Original companyID " + company_id + " for mailing " + mailing_id + " does not match current company_id " + cid); } if (del != 0) { try { setGenerationStatus(0, 4); } catch (Exception e) { logging(Log.ERROR, "sanity", "Failed to set generation status: " + e.toString()); } throw new Exception("Mailing " + mailing_id + " marked as deleted"); } } catch (Exception e) { logging(Log.ERROR, "sanity", "Error in quering mailing_tbl: " + e); throw new Exception("Unable to find entry in mailing_tbl for " + mailing_id + ": " + e); } } } /** * Executed at start of mail generation */ public void startExecution() throws Exception { bigClause = (BC) mkBigClause(); bigClause.setData(this); if (!bigClause.prepareClause()) { throw new Exception("Failed to setup main clause"); } totalSubscribers = bigClause.subscriber(); totalReceivers = bigClause.receiver(); logging(Log.DEBUG, "start", "\ttotalSubscribers = " + totalSubscribers); logging(Log.DEBUG, "start", "\ttotalReceivers = " + totalReceivers); } /** * Executed at end of mail generation */ public void endExecution() { if (bigClause != null) { bigClause.done(); bigClause = null; } } /** * Change generation state for the current mailing */ public void updateGenerationState(int newstatus) { if (isAdminMailing() || isTestMailing() || isWorldMailing() || isRuleMailing() || isOnDemandMailing()) { try { if (newstatus == 0) { if (isRuleMailing() || isOnDemandMailing()) newstatus = 1; else newstatus = 3; } setGenerationStatus(2, newstatus); } catch (Exception e) { logging(Log.ERROR, "genstate", "Unable to update generation state: " + e.toString()); } } } public void updateGenerationState() { updateGenerationState(0); } /** * Called when main generation starts */ public Vector<String> generationClauses() { return bigClause.createClauses(); } /** * Save receivers to mailtracking table */ public void toMailtrack() { if (mailtracking_table != null) { String query = bigClause.mailtrackStatement(mailtracking_table); if (query != null) try { dbase.update(query); } catch (Exception e) { logging(Log.ERROR, "execute", "Unable to add mailtrack information using \"" + query + "\": " + e.toString()); } } } /** * Convert a given object to an integer * @param o the input object * @param what for logging purpose * @return the converted value */ private int obj2int(Object o, String what) throws Exception { int rc; if (o.getClass() == new Integer(0).getClass()) rc = ((Integer) o).intValue(); else if (o.getClass() == new Long(0L).getClass()) rc = ((Long) o).intValue(); else if (o.getClass() == new String().getClass()) rc = Integer.parseInt((String) o); else throw new Exception("Unknown data type for " + what); return rc; } /** * Convert a given object to a long * @param o the input object * @param what for logging purpose * @return the converted value */ private long obj2long(Object o, String what) throws Exception { long rc; if (o.getClass() == new Integer(0).getClass()) rc = ((Integer) o).longValue(); else if (o.getClass() == new Long(0L).getClass()) rc = ((Long) o).longValue(); else if (o.getClass() == new String().getClass()) rc = Long.parseLong((String) o); else throw new Exception("Unknown data type for " + what); return rc; } /** * Convert a given object to a boolean * @param o the input object * @param what for logging purpose * @return the converted value */ private boolean obj2bool(Object o, String what) throws Exception { boolean rc; if (o.getClass() == new Boolean(false).getClass()) rc = ((Boolean) o).booleanValue(); else throw new Exception("Unknown data type for " + what); return rc; } /** * Convert a given object to a date * @param o the input object * @param what for logging purpose * @return the converted value */ private java.util.Date obj2date(Object o, String what) throws Exception { java.util.Date rc; if (o.getClass() == new java.util.Date().getClass()) rc = (java.util.Date) o; else throw new Exception("Unknown data type for " + what); return rc; } /** * Parse options passed during runtime * @param opts the options to use * @param state if 1, the before initialization pass, 2 on execution pass */ @SuppressWarnings("unchecked") public void options(Hashtable<String, Object> opts, int state) throws Exception { Object tmp; if (opts == null) { return; } if (state == 1) { tmp = opts.get("custom-tags"); if (tmp != null) { if (customTags == null) customTags = new Vector<String>(); for (Enumeration<String> e = ((Hashtable<String, Object>) tmp).keys(); e.hasMoreElements();) { String s = e.nextElement(); if (s != null) customTags.add(s); } } previewInput = (String) opts.get("preview-input"); tmp = opts.get("preview-create-all"); if (tmp != null) previewCreateAll = obj2bool(tmp, "preview-create-all"); } else if (state == 2) { tmp = opts.get("customer-id"); if (tmp != null) campaignCustomerID = obj2long(tmp, "customer-id"); tmp = opts.get("transaction-id"); if (tmp != null) campaignTransactionID = obj2long(tmp, "transaction-id"); tmp = opts.get("user-status"); if (tmp != null) campaignUserStatus = obj2int(tmp, "user-status"); fixedEmail = (String) opts.get("fixed-email"); tmp = opts.get("preview-for"); if (tmp != null) previewCustomerID = obj2long(tmp, "preview-for"); previewOutput = (Page) opts.get("preview-output"); tmp = opts.get("preview-anon"); if (tmp != null) previewAnon = obj2bool(tmp, "preview-anon"); previewSelector = (String) opts.get("preview-selector"); tmp = opts.get("preview-cachable"); if (tmp != null) previewCachable = obj2bool(tmp, "preview-cachable"); tmp = opts.get("preview-convert-entities"); if (tmp != null) previewConvertEntities = obj2bool(tmp, "preview-convert-entities"); tmp = opts.get("preview-legacy-uids"); if (tmp != null) previewLegacyUIDs = obj2bool(tmp, "preview-legacy-uids"); tmp = opts.get("send-date"); if (tmp != null) { currentSendDate = obj2date(tmp, "send-date"); sendSeconds = currentSendDate.getTime() / 1000; long now = System.currentTimeMillis() / 1000; if (sendSeconds < now) sendSeconds = now; } tmp = opts.get("step"); if (tmp != null) setStepping(obj2int(tmp, "step")); tmp = opts.get("block-size"); if (tmp != null) setBlockSize(obj2int(tmp, "block-size")); campaignSubselect = (TargetRepresentation) opts.get("select"); customMap = (Hashtable<String, String>) opts.get("custom-tags"); overwriteMap = (Hashtable<String, String>) opts.get("overwrite"); virtualMap = (Hashtable<String, String>) opts.get("virtual"); overwriteMapMulti = (Hashtable<Long, Hashtable<String, String>>) opts.get("overwrite-multi"); virtualMapMulti = (Hashtable<Long, Hashtable<String, String>>) opts.get("virtual-multi"); } } /** * Should we use this record, according to our virtual data? * @return true if we should */ public boolean useRecord(Long cid) { return true; } /** * Optional initialization for virtual data * @param column the column to initialize */ public void initializeVirtualData(String column) { } /** * Do we have data available to overwrite columns? * @return true in this case */ public boolean overwriteData() { return (overwriteMap != null) || (overwriteMapMulti != null); } /** * Find entry in map for overwrite/virtual records * @param cid the customer id * @param multi optional available multi hash table * @param simple optional simple hash table * @param colname the name of the column * @return the found string or null */ private String findInMap(Long cid, Hashtable<Long, Hashtable<String, String>> multi, Hashtable<String, String> simple, String colname) { Hashtable<String, String> map; if ((multi != null) && multi.containsKey(cid)) map = multi.get(cid); else map = simple; if ((map != null) && map.containsKey(colname)) return map.get(colname); return null; } /** * Find an overwrite column * @param cid the customer id * @param colname the name of the column * @return the found string or null */ public String overwriteData(Long cid, String colname) { return findInMap(cid, overwriteMapMulti, overwriteMap, colname); } /** * Find a virtual column * @param cid the customer id * @param colname the name of the column * @return the found string or null */ public String virtualData(Long cid, String colname) { return findInMap(cid, virtualMapMulti, virtualMap, colname); } /** * Get envelope address * @return the punycoded envelope address */ public String getEnvelopeFrom() { return (envelopeFrom != null && envelopeFrom.pure_puny != null) ? envelopeFrom.pure_puny : (fromEmail != null ? fromEmail.pure_puny : null); } /** * If we have another subselection during runtime * return it from here * @return the extra subselect or null */ public String getCampaignSubselect() { String rc = null; String sql; if (campaignSubselect != null) { sql = campaignSubselect.generateSQL(); if ((sql != null) && (sql.length() > 0)) rc = sql; } return rc; } /** If we have further restrictions due to reference mailing * @return extra subsulect or null */ public String getReferenceSubselect() { return null; } /** If we have further restrictions due to selected media * @return extra subsulect or null */ public String getMediaSubselect() { return null; } /** Returns a default image link for a generic picture * @param name the image name * @return the created link */ public String defaultImageLink(String name) { return rdirDomain + "/image?ci=" + Long.toString(company_id) + "&mi=" + Long.toString(mailing_id) + "&name=" + name; } /** * Mark a filename to be removed during cleanup phase * @param fname the filename */ public void markToRemove(String fname) { if (toRemove == null) toRemove = new Vector<String>(); if (!toRemove.contains(fname)) toRemove.addElement(fname); } /** * Mark a file to be removed during cleanup * @param file a File instance for the file to be removed */ public void markToRemove(File file) { markToRemove(file.getAbsolutePath()); } /** * Unmark a filename to be removed, if we already removed * it by hand * @param fname the filename */ public void unmarkToRemove(String fname) { if ((toRemove != null) && toRemove.contains(fname)) toRemove.remove(fname); } /** * Unmark a file to be removed * @param file a File instance */ public void unmarkToRemove(File file) { unmarkToRemove(file.getAbsolutePath()); } /** * Check if we have to write logging for a given loglevel * @param loglvl the loglevel to check against * @return true if we should log */ public boolean islog(int loglvl) { return log.islog(loglvl); } /** * Write entry to logfile * @param loglvl the level to report * @param mid the ID of the message * @param msg the message itself */ public void logging(int loglvl, String mid, String msg) { if (lid != null) if (mid != null) mid = mid + "/" + lid; else mid = lid; if (log != null) log.out(loglvl, mid, msg); } /** * Create a path to write test-/admin mails to * @return the path to use */ public String mailDir() { return mailDir; } private void iniValidate(String value, String name) throws Exception { if (value == null) { throw new Exception("mailgun.ini." + name + " is unset"); } } public String dbDriver() throws Exception { iniValidate(dbDriver, "db_driver"); return dbDriver; } /** returns the database login * @return login string */ public String dbLogin() throws Exception { iniValidate(dbLogin, "db_login"); return dbLogin; } /** returns the database password * @return password string */ public String dbPassword() throws Exception { iniValidate(dbPassword, "db_password"); return dbPassword; } /** returns the connection string for the database * @return connection string */ public String dbConnect() throws Exception { iniValidate(dbConnect, "db_connect"); return dbConnect; } public int dbPoolsize() { return dbPoolsize; } public boolean dbPoolgrow() { return dbPoolgrow; } /** returns the block size to be used * @return block size */ public int blockSize() { return blockSize; } /** returns the directory to write meta files to * @return path to meta */ public String metaDir() throws Exception { iniValidate(metaDir, "metadir"); return metaDir; } /** returns the path to xmlback program * @return path to xmlback */ public String xmlBack() { return xmlBack; } /** returns the path to the acounting logfile * @return path to logfile */ public String accLogfile() { return accLogfile; } /** returns the path to the bounce logfile * @return path to logfile */ public String bncLogfile() { return bncLogfile; } /** returns wether we should validate generated XML files * @return true if validation should take place */ public boolean xmlValidate() { return xmlValidate; } /** returns the optional used sample receivers * @return receiver list */ public String sampleEmails() { return sampleEmails; } /** returns the number of generate mails to write log entries for * @return number of mails */ public int mailLogNumber() { return mailLogNumber; } /** returns the X-Mailer: header content * @return mailer name */ public String makeMailer() { if ((mailer != null) && (company_name != null)) return StringOps.replace(mailer, "[agnMANDANT]", company_name); return mailer; } /** if this is a admin mail * @return true, if admin mail */ public boolean isAdminMailing() { return status_field.equals("A"); } /** if this is a test mail * @return true, if test mail */ public boolean isTestMailing() { return status_field.equals("T"); } /** if this is a campaign mail * @return true, if campaign mail */ public boolean isCampaignMailing() { return status_field.equals("E"); } /** if this is a date based mailing * @return true, if its date based */ public boolean isRuleMailing() { return status_field.equals("R"); } /** if this an on demand mailing * @return true, if this is on demand */ public boolean isOnDemandMailing() { return status_field.equals("D"); } /** if this is a world mail * @return true, if world mail */ public boolean isWorldMailing() { return status_field.equals("W"); } /** if this is a preview * @return true, if preview */ public boolean isPreviewMailing() { return status_field.equals("P"); } /** * Set standard field to be retreived from database * @param predef the hashset to store field name to */ public void setStandardFields(HashSet<String> predef, Hashtable<String, EMMTag> tags) { predef.add("email"); for (Enumeration<EMMTag> e = tags.elements(); e.hasMoreElements();) { EMMTag tag = e.nextElement(); try { tag.requestFields(this, predef); } catch (Exception ex) { logging(Log.ERROR, "tag", "Failed to get required fields for tag " + tag.mTagFullname + ": " + ex.toString()); } } } /** * Set standard columns, if they are not already found in database * @param use already used column names */ public void setUsedFieldsInLayout(HashSet<String> use, Hashtable<String, EMMTag> tags) { int sanity = 0; HashSet<String> predef; if (use != null) { predef = new HashSet<String>(use); } else { predef = new HashSet<String>(); } if (targets != null) { for (Enumeration<Target> e = targets.elements(); e.hasMoreElements();) { Target t = e.nextElement(); t.requestFields(this, predef); } } setStandardFields(predef, tags); for (int n = 0; n < lcount; ++n) { Column c = layout.elementAt(n); if (predef.contains(c.qname)) { if (!c.inuse) { c.inuse = true; ++lusecount; } ++sanity; } else { if (c.inuse) { c.inuse = false; --lusecount; } } } if (sanity != lusecount) logging(Log.ERROR, "layout", "Sanity check failed in setUsedFieldsInLayout"); } /** find a column by its alias * @param alias * @return the column on success, null otherwise */ public Column columnByAlias(String alias) { for (int n = 0; n < lcount; ++n) { Column c = layout.elementAt(n); if ((c.alias != null) && c.alias.equalsIgnoreCase(alias)) return c; } return null; } /** find a column by its name * @param name * @return the column on success, null otherwise */ public Column columnByName(String name, String ref) { for (int n = 0; n < lcount; ++n) { Column c = layout.elementAt(n); if (c.name.equalsIgnoreCase(name) && (((c.ref == null) && (ref == null)) || ((c.ref != null) && (ref != null) && (c.ref.equalsIgnoreCase(ref))))) return c; } return null; } public Column columnByName(String name) { return columnByName(name, null); } public Column columnByIndex(int idx) { return (idx >= 0) && (idx < lcount) ? layout.elementAt(idx) : null; } /** return the name of the column at a given position * @param col the position in the column layout * @return the column name */ public String columnName(int col) { return layout.elementAt(col).name; } /** return the type of the column at a given position * @param col the position in the column layout * @return the column type */ public int columnType(int col) { return layout.elementAt(col).type; } /** return the type as string of the column at a given position * @param col the position in the column layout * @return the column type as string */ public String columnTypeStr(int col) { return layout.elementAt(col).typeStr(); } /** Set a column from a result set * @param col the position in the column layout * @param rset the result set * @param index position in the result set */ public void columnSet(int col, ResultSet rset, int index) { layout.elementAt(col).set(rset, index); } /** Get a value from a column * @param col the position in the column layout * @return the contents of that column */ public String columnGetStr(int col) { return layout.elementAt(col).get(); } /** Check wether a columns value is NULL * @param col the position in the column layout * @return true of column value is NULL */ public boolean columnIsNull(int col) { return layout.elementAt(col).isNull(); } /** Check wether a column is in use * @param col the position in the column layout * @return true if column is in use */ public boolean columnUse(int col) { return layout.elementAt(col).inUse(); } /** create a RFC compatible Date: line * @param ts the input time * @return the RFC representation */ public String RFCDate(java.util.Date ts) { SimpleDateFormat fmt = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss z", new Locale("en")); fmt.setTimeZone(TimeZone.getTimeZone("GMT")); if (ts == null) ts = new java.util.Date(); return fmt.format(ts); } /** Optional string to add to filename generation * @return optional string */ public String getFilenameDetail() { return ""; } public String getFilenameCompanyID() { return Long.toString(company_id); } public String getFilenameMailingID() { return Long.toString(mailing_id); } /** Should we generate URLs already here? * @return true, if we should generate them */ public boolean generateCodedURLs() { return true; } }