/*********************************************************************************
* 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 shortname, xor_key, rdir_domain, mailloop_domain FROM company_tbl WHERE company_id = :companyID", "companyID", company_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"));
}
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;
}
}