/********************************************************************************* * 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.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.InputStream; import java.io.OutputStream; import java.io.IOException; import java.util.Enumeration; import java.util.Hashtable; import java.util.Vector; import java.util.zip.GZIPOutputStream; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.DocumentBuilder; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NodeList; import org.w3c.dom.Node; import org.w3c.dom.NamedNodeMap; import org.agnitas.util.Log; /** Implements writing of mailing information to * a XML file */ public class MailWriterMeta extends MailWriter { /** Write a log entry to the database after that number of mails */ private int logSize; /** Reference to available tagnames */ protected Hashtable tagNames; /** Base pathname without extension to write to */ private String fname; /** The pathname for the real XML file */ private String pathname; /** Output stream */ private OutputStream out; /** Output buffer */ public XMLWriter writer; /** Counter to give each block written an unique ID */ private int blockID; /** if we should keep admin/test mails for debug purpose */ public boolean keepATmails; /** after this number of records flush buffer to disk */ public int flushCount; /** Flush the created buffer to disk */ private void flushBuffer () throws Exception { if ((out == null) || (writer == null)) throw new Exception ("Try to flush buffer to not existing stream"); writer.flush (); } private Vector <String> escape (Vector <String> input) { Vector <String> rc = new Vector <String> (input.size ()); for (int n = 0; n < input.size (); ++n) { rc.add (input.get (n).replace ("\\", "\\\\")); } return rc; } /** Start the mail generating backend * @param output detailed output description * @param filename pathname to the XML file */ private void startXMLBack (Vector <String> options, String output, String filename) throws Exception { Vector <String> command = new Vector <String> (); String cmd; File efile; int rc; try { efile = File.createTempFile ("error", null); } catch (Exception e) { String javaTemp = System.getProperty ("java.io.tmpdir"); data.logging (Log.ERROR, "write/meta", "Failed to create temp.file due to: " + e.toString () + " (missing temp.directory '" + javaTemp + "'?)"); throw e; } command.add (data.xmlBack ()); if (options != null) { for (int n = 0; n < options.size (); ++n) command.add (options.elementAt (n)); } command.add ("-q"); if (data.eol.equals ("\n")) { command.add ("-l"); } command.add ("-E" + efile.getAbsolutePath ()); command.add ("-o" + output); command.add (filename); cmd = command.toString (); data.markToRemove (efile); try { data.logging (Log.DEBUG, "write/meta", "Try to execute " + cmd); ProcessBuilder bp = new ProcessBuilder (escape (command)); Process proc = bp.start (); OutputStream otemp = proc.getOutputStream (); InputStream itemp; if (otemp != null) { try { otemp.close (); } catch (IOException ie) { data.logging (Log.VERBOSE, "write/meta", "Failed to close stdin for " + cmd + ": " + ie.toString ()); } } rc = proc.waitFor (); itemp = proc.getInputStream (); if (itemp != null) { try { itemp.close (); } catch (IOException ie) { data.logging (Log.VERBOSE, "write/meta", "Failed to close stdout for " + cmd + ": " + ie.toString ()); } } itemp = proc.getErrorStream (); if (itemp != null) { try { itemp.close (); } catch (IOException ie) { data.logging (Log.VERBOSE, "write/meta", "Failed to close stderr for " + cmd + ": " + ie.toString ()); } } FileInputStream err; String msg; int size; try { err = new FileInputStream (efile); } catch (FileNotFoundException e) { err = null; } msg = null; size = 0; if (err != null) { size = err.available (); if (size > 0) { int use = size > 4096 ? 4096 : size; byte[] buf = new byte[use]; err.read (buf); msg = new String (buf); } err.close (); } if ((rc != 0) || (msg != null)) { data.logging (rc == 0 ? Log.INFO : Log.ERROR, "writer/meta", "command " + cmd + " returns " + rc + (msg != null ? ":\n" + msg : "")); if (rc != 0) throw new Exception ("command returns " + rc + (msg != null ? " (" + size + ")" : "")); } } catch (Exception e) { data.logging (Log.ERROR, "writer/meta", "command " + cmd + " failed (Missing binary? Wrong permissions?): " + e); throw new Exception ("Execution of " + cmd + " failed: " + e); } finally { if (efile.delete ()) data.unmarkToRemove (efile); } } /** Constructor * @param data Reference to configuration * @param allBlocks all content blocks * @param nTagNames all tag definitions */ public MailWriterMeta (Data data, BlockCollection allBlocks, Hashtable nTagNames) throws Exception { super (data, allBlocks); logSize = data.mailLogNumber (); tagNames = nTagNames; fname = null; pathname = null; out = null; writer = null; if (data.isAdminMailing () || data.isTestMailing () || data.isCampaignMailing () || data.isPreviewMailing ()) blockSize = 0; else blockSize = data.blockSize (); blockID = 1; keepATmails = false; flushCount = 100; } public String fileName () { return fname; } /** Create xmlback generation string * @return the newly formed string */ public String generateOutputOptions () { return "generate:temporary=true;syslog=false;account-logfile=" + data.accLogfile () + ";bounce-logfile=" + data.bncLogfile () + ";media=email;path=" + data.mailDir (); } public String previewOutputOptions (String output) { return "preview:path=" + output; } public void previewOptions (Vector <String> options) { options.add ("-r"); } /** Cleanup */ public void done () throws Exception { super.done (); if (data.isAdminMailing () || data.isTestMailing ()) { if (pathname != null) { if (! keepATmails) { data.markToRemove (pathname); } String gen = generateOutputOptions (); startXMLBack (null, gen, pathname); if (! keepATmails) { if ((new File (pathname)).delete ()) { data.unmarkToRemove (pathname); } } } } else if (data.isPreviewMailing ()) { if (pathname != null) { File output = File.createTempFile ("preview", ".xml"); String path = output.getAbsolutePath (); String opts = previewOutputOptions (path); String error = null; data.markToRemove (pathname); data.markToRemove (path); try { Vector <String> options = new Vector <String> (); previewOptions (options); startXMLBack (options, opts, pathname); } catch (Exception e) { error = e.toString (); } if (data.previewOutput != null) { try { DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance (); DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder (); Document doc = docBuilder.parse (output); Element root = doc.getDocumentElement (); NodeList nlist = root.getElementsByTagName ("content"); int ncount = nlist.getLength (); for (int n = 0; n < ncount; ++n) { Node node = nlist.item (n); NamedNodeMap attr = node.getAttributes (); Node name = attr.getNamedItem ("name"); if (name != null) { Node text = node.getFirstChild (); data.previewOutput.addContent (name.getNodeValue (), (text == null ? "" : text.getNodeValue ())); } } } catch (Exception e) { if (error != null) error += "\n" + e.toString (); else error = e.toString (); } if (error != null) data.previewOutput.setError (error); } if ((new File (path)).delete ()) { data.unmarkToRemove (path); } if ((new File (pathname)).delete ()) { data.unmarkToRemove (pathname); } } } else if (fname != null) { try { FileOutputStream temp; String msg; msg = data.company_id + "-" + data.mailing_id + "-" + blockCount + "\t" + "Start: " + startExecutionTime + "\tEnd: " + endExecutionTime + "\n"; temp = new FileOutputStream (fname + ".final"); temp.write (msg.getBytes ()); temp.close (); } catch (FileNotFoundException e) { throw new Exception ("Unable to write final stamp file " + fname + ".final: " + e); } } } /** Get encoding for block * @param b the block to examine * @return the encoding for this block */ public String getEncoding (Object ob) { BlockData b = (BlockData) ob; String encode; if (b.is_text) { if (b.media == Media.TYPE_EMAIL) encode = data.encoding; else encode = "none"; } else { encode = "base64"; } return encode; } /** Write entry part for a single block * @param b the block to write * @param encode the encoding for this block */ public void emitBlockEntry (Object ob, XMLWriter.Creator c, String encode) { BlockData b = (BlockData) ob; String cid; String flag; if (b.mime != null) { c.add ("mimetype", b.getContentMime ()); } c.add ("charset", data.charset, "encode", encode); cid = b.getContentFilename (); if (cid != null) { c.add ("cid", cid); } if (b.is_parseable) { flag = "is_parsable"; } else if (b.is_text) { flag = "is_text"; } else { flag = "is_binary"; } c.add (flag, true); if (b.is_attachment) { c.add ("is_attachment", true); } if (b.media != Media.TYPE_UNRELATED) { c.add ("media", b.mediaType ()); } if (b.condition != null) { c.add ("condition", b.condition); } } /** Write content part for a single block * @param b the block to write */ public void emitBlockContent (Object ob) { BlockData b = (BlockData) ob; String content; if (b.content != null) { content = b.content; } else if (b.binary == null) { content = ""; } else { content = null; } if ((content == null) || (content.length () > 0)) { writer.opennode ("content"); if (content == null) { writer.data (b.binary); } else { writer.data (content); } writer.close ("content"); } else { writer.openclose ("content"); } } /** Write blocks tag information * param b the block to write */ public void emitBlockTags (BlockData b, int isHeader) { if (b.is_parseable && (b.tag_position != null)) { Vector p = b.tag_position; int count = p.size (); for (int m = 0; m < count; ++m) { TagPos tp = (TagPos) p.elementAt (m); int type; XMLWriter.Creator c = writer.create ("tagposition", "name", tp.tagname, "hash", tp.tagname.hashCode ()); type = 0; if (tp.isDynamic ()) type |= 0x1; if (tp.isDynamicValue ()) type |= 0x2; if (type != 0) { c.add ("type", type); } if (tp.content != null) { writer.opennode (c); emitBlock (tp.content, (isHeader != 0 ? isHeader + 1 : 0), 0); writer.close (c); } else writer.openclose (c); } } } /** Write a single block * @param b the block to write * @param isHeader if this is the header block * @param index the unique block number */ private void emitBlock (BlockData b, int isHeader, int index) { String encode; if (isHeader != 0) { encode = "header"; } else { encode = getEncoding (b); } XMLWriter.Creator c = writer.create ("block", "id", blockID++, "nr", index); emitBlockEntry (b, c, encode); writer.opennode (c); emitBlockContent (b); emitBlockTags (b, isHeader); writer.close (c); } /** Write description entity */ public void emitDescription () { writer.open (data.companyInfo == null, "company", "id", data.company_id); if (data.companyInfo != null) { for (Enumeration e = data.companyInfo.keys (); e.hasMoreElements (); ) { String name = (String) e.nextElement (); String value = data.companyInfo.get (name); writer.single ("info", value, "name", name); } writer.close ("company"); } writer.openclose ("mailinglist", "id", data.mailinglist_id); writer.open (data.mailingInfo == null, "mailing", "id", data.mailing_id, "name", data.mailing_name); if (data.mailingInfo != null) { for (Enumeration e = data.mailingInfo.keys (); e.hasMoreElements (); ) { String name = (String) e.nextElement (); String value = data.mailingInfo.get (name); writer.single ("info", value, "name", name); } writer.close ("mailing"); } writer.openclose ("maildrop", "status_id", data.maildrop_status_id); writer.openclose ("status", "field", data.status_field); } /** Get transfer encoding * @param b the block to emit * @return the encoding */ public String getTransferEncoding (Object ob) { BlockData b = (BlockData) ob; return b.is_text ? data.encoding : "base64"; } public void getDynamicInfo (Object od, XMLWriter.Creator c) { } public void generalURLs () { writer.single ("profile_url", data.profileURL); writer.single ("unsubscribe_url", data.unsubscribeURL); writer.single ("auto_url", data.autoURL); writer.single ("onepixel_url", data.onePixelURL); } public void secrets () { writer.single ("password", data.password); } public void layout () { if (data.lusecount > 0) { writer.opennode ("layout", "count", data.lusecount); for (int n = 0; n < data.lcount; ++n) { Column c = data.columnByIndex (n); if (c.inUse ()) { XMLWriter.Creator cr = writer.create ("element", "name", c.name); if (c.ref != null) { cr.add ("ref", c.ref); } cr.add ("type", c.typeStr ()); writer.openclose (cr); } } writer.close ("layout"); } else { writer.comment ("no layout"); } } public void urls () { if (data.urlcount > 0) { writer.opennode ("urls", "count", data.urlcount); for (int n = 0; n < data.urlcount; ++n) { URL url = data.URLlist.elementAt (n); XMLWriter.Creator cr = writer.create ("url", "id", url.id, "destination", url.url, "usage", url.usage); if (url.adminLink) { cr.add ("admin_link", url.adminLink); } writer.openclose (cr); } writer.close ("urls"); } else { writer.comment ("no urls"); } } /** Start writing a new block */ public void startBlock () throws Exception { super.startBlock (); fname = data.metaDir () + dirSeparator + filenamePattern; if (data.isAdminMailing () || data.isTestMailing ()) { pathname = fname + ".xml"; out = new FileOutputStream (pathname); } else { pathname = fname + ".xml.gz"; out = new GZIPOutputStream (new FileOutputStream (pathname)); } writer = new XMLWriter (out); writer.start (); writer.opennode ("blockmail"); writer.opennode ("description"); emitDescription (); writer.close ("description"); writer.empty (); writer.opennode ("general"); writer.single ("subject", data.subject); writer.single ("from_email", data.fromEmail == null ? null : data.fromEmail.full); generalURLs (); secrets (); writer.single ("total_subscribers", data.totalSubscribers); writer.close ("general"); writer.empty (); writer.opennode ("mailcreation"); writer.single ("blocknr", blockCount); writer.single ("innerboundary", innerBoundary); writer.single ("outerboundary", outerBoundary); writer.single ("attachboundary", attachBoundary); writer.close ("mailcreation"); writer.empty (); int mediasize; Media tmp; for (tmp = data.media, mediasize = 0; tmp != null; tmp = (Media) tmp.next) ++mediasize; writer.open (mediasize == 0, "mediatypes", "count", mediasize); if (mediasize > 0) { for (tmp = data.media; tmp != null; tmp = (Media) tmp.next) { Vector vars = tmp.getParameterVariables (); boolean hasVars = ((vars != null) && (vars.size () > 0)); writer.open (! hasVars, "media", "type", tmp.typeName (), "priority", tmp.priorityName (), "status", tmp.statusName ()); if (hasVars) { for (int m = 0; m < vars.size (); ++m) { String name = (String) vars.elementAt (m); Vector vals = tmp.findParameterValues (name); boolean hasVals = ((vals != null) & (vals.size () > 0)); writer.open (! hasVals, "variable", "name", name); if (hasVals) { for (int o = 0; o < vals.size (); ++o) { writer.single ("value", (String) vals.elementAt (o)); } writer.close ("variable"); } } writer.close ("media"); } } writer.close ("mediatypes"); } writer.empty (); writer.opennode ("blocks", "count", allBlocks.totalNumber); for (int n = 0; n < allBlocks.totalNumber; ++n) { BlockData b = allBlocks.getBlock (n); emitBlock (b, (b.type == BlockData.HEADER ? 1 : 0), n); } writer.close ("blocks"); writer.empty (); writer.opennode ("types", "count", 3); for (int n = 0; n < 3; ++n) { writer.opennode ("type", "id", n); Vector <BlockData> use = new Vector <BlockData> (); int used, part, text; part = -1; text = -1; for (BlockData b : allBlocks.blocks) { boolean doit = b.isEmailHeader () || b.isEmailAttachment (); if (! doit) { switch (n) { case 0: doit = b.isEmailPlaintext (); break; case 1: doit = b.isEmailText (); break; case 2: doit = true; break; } } if (doit) { if ((part == -1) && b.isEmailAttachment ()) { part = use.size () - 1; } if (b.isEmailText ()) { text = use.size (); } use.add (b); } } used = use.size (); if (part == -1) { part = used; } if (text == -1) { text = used; } for (int m = 0; m < used; ++m) { BlockData b = use.elementAt (m); XMLWriter.Creator c = writer.create ("blockspec", "nr", b.id); if (b.isEmailPlaintext () && (data.lineLength > 0)) { c.add ("linelength", data.lineLength); } else if (b.isEmailHTML () && (data.onepixlog != Data.OPL_NONE)) { String opl; switch (data.onepixlog) { default: opl = null; break; case Data.OPL_TOP: opl = "top"; break; case Data.OPL_BOTTOM: opl = "bottom"; break; } if (opl != null) { c.add ("onepixlog", opl); } } writer.opennode (c); if (b.comptype == 0) { if (b.type == BlockData.HEADER) { writer.opennode ("postfix", "output", 0); writer.opennode ("fixdata", "valid", "simple"); if (n == 0) { // simple text mail writer.data ("HContent-Type: text/plain; charset=\"" + data.charset + "\"" + data.eol + "HContent-Transfer-Encoding: " + data.encoding + data.eol); } else if (n == 1) { // online HTML writer.data ("HContent-Type: multipart/alternative;" + data.eol + "\tboundary=\"" + outerBoundary + "\"" + data.eol); } else { // offline HTML // buf.append ("HContent-Type: multipart/related; type=\"multipart/alternative\";" + data.eol + // "\tboundary=\"" + xmlStr (outerBoundary) + "\"" + data.eol); writer.data ("HContent-Type: multipart/related;" + data.eol + "\tboundary=\"" + outerBoundary + "\"" + data.eol); } writer.data ("." + data.eol); writer.close ("fixdata"); writer.opennode ("fixdata", "valid", "attach"); writer.data ("HContent-Type: multipart/mixed; boundary=\"" + attachBoundary +"\"" + data.eol + "." + data.eol); writer.close ("fixdata"); writer.close ("postfix"); } else if (b.type == BlockData.TEXT) { writer.opennode ("prefix"); if (n > 0) { writer.opennode ("fixdata", "valid", "simple"); writer.data ("This is a multi-part message in MIME format." + data.eol + data.eol + "--" + outerBoundary + data.eol); if (n == 2) { writer.data ("Content-Type: multipart/alternative;" + data.eol + "\tboundary=\"" + innerBoundary + "\"" + data.eol + data.eol + "--" + innerBoundary + data.eol); } writer.data ("Content-Type: text/plain; charset=\"" + data.charset + "\"" + data.eol + "Content-Transfer-Encoding: " + data.encoding + data.eol + data.eol); writer.close ("fixdata"); } writer.opennode ("fixdata", "valid", "attach"); writer.data ("--" + attachBoundary + data.eol); if (n == 0) { writer.data ("Content-Type: text/plain; charset=\"" + data.charset + "\"" + data.eol + "Content-Transfer-Encoding: " + data.encoding + data.eol + data.eol); } else if (n == 1) { writer.data ("Content-Type: multipart/alternative;" + data.eol + "\tboundary=\"" + outerBoundary + "\"" + data.eol + data.eol); } else { writer.data ("Content-Type: multipart/related;" + data.eol + "\tboundary=\"" + outerBoundary + "\"" + data.eol + data.eol); } if (n > 0) { writer.data ("--" + outerBoundary + data.eol); if (n == 2) { writer.data ("Content-Type: multipart/alternative;" + data.eol + "\tboundary=\"" + innerBoundary + "\"" + data.eol + data.eol + "--" + innerBoundary + data.eol); } writer.data ("Content-Type: text/plain; charset=\"" + data.charset + "\"" + data.eol + "Content-Transfer-Encoding: " + data.encoding + data.eol + data.eol); } writer.close ("fixdata"); writer.close ("prefix"); if (n == 2) { writer.opennode ("postfix", "output", text, "pid", "inner"); writer.opennode ("fixdata", "valid", "all"); writer.data ("--" + innerBoundary + "--" + data.eol + data.eol); writer.close ("fixdata"); writer.close ("postfix"); } if (n > 0) { writer.opennode ("postfix", "output", part, "pid", "outer"); writer.opennode ("fixdata", "valid", "all"); writer.data ("--" + outerBoundary + "--" + data.eol + data.eol); writer.close ("fixdata"); writer.close ("postfix"); } writer.opennode ("postfix", "output", allBlocks.totalNumber, "pid", "attach"); writer.opennode ("fixdata", "valid","attach"); writer.data ("--" + attachBoundary + "--" + data.eol + data.eol); writer.close ("fixdata"); writer.close ("postfix"); } else { writer.opennode ("prefix"); writer.opennode ("fixdata", "valid", "all"); if (n == 1) { writer.data ("--" + outerBoundary + data.eol); } else { writer.data ("--" + innerBoundary + data.eol); } writer.data ("Content-Type: " + b.getContentMime () + "; charset=\"" + data.charset + "\"" + data.eol + "Content-Transfer-Encoding: " + getTransferEncoding (b) + data.eol + data.eol); writer.close ("fixdata"); writer.close ("prefix"); } } else { // offline + attachments writer.opennode ("prefix"); if ((b.type == BlockData.ATTACHMENT_TEXT) || (b.type == BlockData.ATTACHMENT_BINARY)) { writer.opennode ("fixdata", "valid", "attach"); writer.data ("--" + attachBoundary + data.eol + "Content-Type: " + b.getContentMime () + data.eol + "Content-Disposition: attachment; filename=\"" + b.getContentFilename () + "\"" + data.eol + "Content-Transfer-Encoding: " + getTransferEncoding (b) + data.eol + data.eol); writer.close ("fixdata"); } else { writer.opennode ("fixdata", "valid", "all"); writer.data ("--" + outerBoundary + data.eol + "Content-Type: " + b.getContentMime () + data.eol + "Content-Transfer-Encoding: " + getTransferEncoding (b) + data.eol + "Content-Location: " + b.getContentFilename () + data.eol + data.eol); writer.close ("fixdata"); } writer.close ("prefix"); if ((b.type == BlockData.ATTACHMENT_TEXT) || (b.type == BlockData.ATTACHMENT_BINARY)) { writer.opennode ("postfix", "output", allBlocks.totalNumber, "pid", "attach"); writer.opennode ("fixdata", "valid", "attach"); writer.data ("--" + attachBoundary + "--" + data.eol + data.eol); writer.close ("fixdata"); writer.close ("postfix"); } else { writer.opennode ("postfix", "output", part, "pid", "outer"); writer.opennode ("fixdata", "valid", "all"); writer.data ("--" + outerBoundary + "--" + data.eol + data.eol); writer.close ("fixdata"); writer.close ("postfix"); } } writer.close (c); } writer.close ("type"); } writer.close ("types"); writer.empty (); layout (); boolean found; found = false; for (Enumeration e = tagNames.elements (); e.hasMoreElements (); ) { EMMTag tag = (EMMTag) e.nextElement (); if (! found) { writer.opennode ("taglist", "count", tagNames.size ()); found = true; } writer.openclose ("tag", "name", tag.mTagFullname, "hash", tag.mTagFullname.hashCode ()); } if (found) { writer.close ("taglist"); } else { writer.comment ("no taglist"); } writer.empty (); found = false; for (Enumeration e = tagNames.elements (); e.hasMoreElements (); ) { EMMTag tag = (EMMTag) e.nextElement (); String ttype = tag.getType (); String value; switch (tag.tagType) { case EMMTag.TAG_INTERNAL: if ((tag.fixedValue || tag.globalValue) && ((value = tag.makeInternalValue (data, null)) != null)) { if (! found) { writer.opennode ("global_tags"); found = true; } XMLWriter.Creator c = writer.create ("tag", "name", tag.mTagFullname, "hash", tag.mTagFullname.hashCode ()); if (ttype != null) { c.add ("type", ttype); } if (value != null) { writer.opennode (c); writer.data (value); writer.close (c); } else { writer.openclose (c); } } break; } } if (found) { writer.close ("global_tags"); } else { writer.comment ("no global_tags"); } writer.empty (); if ((allBlocks.dynContent != null) && (allBlocks.dynContent.ncount > 0)) { writer.opennode ("dynamics", "count", allBlocks.dynContent.ncount); for (Enumeration e = allBlocks.dynContent.names.elements (); e.hasMoreElements (); ) { DynName dtmp = (DynName) e.nextElement (); XMLWriter.Creator c = writer.create ("dynamic", "id", dtmp.id, "name", dtmp.name); getDynamicInfo (dtmp, c); writer.opennode (c); for (int n = 0; n < dtmp.clen; ++n) { DynCont cont = dtmp.content.elementAt (n); if (cont.targetID != DynCont.MATCH_NEVER) { XMLWriter.Creator cr = writer.create ("dyncont", "id", cont.id, "order", cont.order); if ((cont.targetID != DynCont.MATCH_ALWAYS) && (cont.condition != null)) { cr.add ("condition", cont.condition); } writer.opennode (cr); if (cont.text != null) { emitBlock (cont.text, 0, 0); } if (cont.html != null) { emitBlock (cont.html, 0, 1); } writer.close (cr); } } writer.close (c); } writer.close ("dynamics"); } else { writer.comment ("no dynamics"); } writer.empty (); urls (); writer.empty (); writer.opennode ("receivers"); } /** Finalize a block */ public void endBlock () throws Exception { super.endBlock (); if (out != null) { writer.close ("receivers"); writer.close ("blockmail"); flushBuffer (); writer.end (); writer = null; out.close (); out = null; if (data.xmlValidate ()) { data.logging (Log.INFO, "writer/meta", "Validating XML output"); startXMLBack (null, "none", pathname); data.logging (Log.INFO, "writer/meta", "Validation done"); } else data.logging (Log.INFO, "writer/meta", "Skip validation of XML document"); if (! (data.isAdminMailing () || data.isTestMailing () || data.isPreviewMailing ())) try { FileOutputStream temp; String msg; msg = data.company_id + "-" + data.mailing_id + "-" + blockCount + "\t" + "Start: " + startBlockTime + "\tEnd: " + endBlockTime + "\n"; temp = new FileOutputStream (fname + ".stamp"); temp.write (msg.getBytes ()); temp.close (); } catch (FileNotFoundException e) { throw new Exception ("Unable to write stamp file " + fname + ".stamp: " + e); } } } /** Create string for media informations * @param cinfo information about this customer * @return the media string */ public void getMediaInformation (Object cinfop, XMLWriter.Creator c) { Custinfo cinfo = (Custinfo) cinfop; if (cinfo.email != null) { c.add ("to_email", cinfo.email); } } /** Write a single receiver record * @param cinfo Information about the customer * @param mcount if more than one mail is written for this receiver * @param mailtype the mailtype for this receiver * @param icustomer_id the customer ID * @param tag_names the available tags * @param urlMaker to create the URLs */ public void writeMail (Custinfo cinfo, int mcount, int mailtype, long icustomer_id, String mediatypes, Hashtable tag_names, URLMaker urlMaker) throws Exception { super.writeMail (cinfo, mcount, mailtype, icustomer_id, mediatypes, tag_names, urlMaker); if ((mailCount % flushCount) == 0) { flushBuffer (); } if ((mailCount % 100) == 0) { data.logging (Log.VERBOSE, "writer/meta", "Currently at " + mailCount + " mails (in block " + blockCount + ": " + inBlockCount + ") "); } if (billingCounter != null) if ((logSize > 0) && ((mailCount % logSize) == 0)) billingCounter.update_log (mailCount); XMLWriter.Creator c = writer.create ("receiver", "customer_id", icustomer_id, "user_type", cinfo.usertype); getMediaInformation (cinfo, c); c.add ("message_id", messageID, "mailtype", mailtype); if (mediatypes != null) { c.add ("mediatypes", mediatypes); } writer.opennode (c); writer.opennode ("tags"); for (Enumeration e = tag_names.elements (); e.hasMoreElements (); ) { EMMTag tag = (EMMTag) e.nextElement (); String value; switch (tag.tagType) { case EMMTag.TAG_DBASE: if (tag.mutableValue) value = tag.makeMutableValue (data, cinfo); else if (! (tag.fixedValue || tag.globalValue)) value = tag.mTagValue; else value = null; break; case EMMTag.TAG_URL: value = allBlocks.create_url_tag (tag, urlMaker); break; case EMMTag.TAG_INTERNAL: if (! (tag.fixedValue || tag.globalValue)) value = tag.makeInternalValue (data, cinfo); else value = null; break; default: throw new Exception ("Invalid tag type: " + tag.toString ()); } if (value != null) { writer.opennode ("tag", "name", tag.mTagFullname, "hash", tag.mTagFullname.hashCode ()); writer.data (value); writer.close ("tag"); } } writer.close ("tags"); if ((data.urlcount > 0) && (data.generateCodedURLs ())) { for (int n = 0; n < data.urlcount; ++n) { URL url = data.URLlist.elementAt (n); writer.openclose ("codedurl", "id", url.id, "destination", urlMaker.autoURL (url.id)); } } if (data.lusecount > 0) { for (int n = 0; n < data.lcount; ++n) { if (data.columnUse (n)) { if (data.columnIsNull (n)) { writer.opennode ("data", "null", true); } else { writer.opennode ("data"); } writer.data (data.columnGetStr (n)); writer.close ("data"); } } } writer.close (c); if (billingCounter != null) if (icustomer_id != 0) billingCounter.sadd (mailtype); writeMailDone (); } }