package com.cloudhopper.sxmp; /* * #%L * ch-sxmp * %% * Copyright (C) 2012 - 2013 Cloudhopper by Twitter * %% * Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * #L% */ import com.cloudhopper.commons.util.HexUtil; import com.cloudhopper.commons.util.StringUtil; import com.cloudhopper.sxmp.util.XmlEscapeUtil; import java.io.IOException; import java.io.StringWriter; import java.io.Writer; import org.apache.commons.lang.StringUtils; import org.json.JSONException; import org.json.JSONObject; /** * * @author joelauer */ public class SxmpWriter { static private void writeXmlHeader(Writer out, Operation operation) throws IOException { // v1.1 needs to be UTF-8; v1.0 was unspecified if (operation.getVersion().equals(SxmpParser.VERSION_1_0)) out.write("<?xml version=\"1.0\"?>\n"); else out.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"); } static private void writeOperationStartTag(Writer out, Operation.Type type) throws IOException { if (type == null) { throw new NullPointerException("Operation type cannot be null"); } out.write("<operation type=\"" + type.getValue() + "\">\n"); } static private void writeOperationEndTag(Writer out) throws IOException { out.write("</operation>\n"); } static private void writeRequestResponseTag(Writer out, Operation operation) throws IOException { out.write(operation.getType().getValue()); if (operation.isRequest()) { out.write("Request"); } else { out.write("Response"); } } static private void writeRequestResponseStartTag(Writer out, Operation operation) throws IOException { out.write(" <"); writeRequestResponseTag(out, operation); if (operation.getReferenceId() != null) { out.write(" referenceId=\""); out.write(operation.getReferenceId()); out.write("\""); } out.write(">\n"); } static private void writeRequestResponseEndTag(Writer out, Operation operation) throws IOException { out.write(" </"); writeRequestResponseTag(out, operation); out.write(">\n"); } static private void writeErrorElement(Writer out, Response response) throws IOException { out.write(" <error code=\""); out.write(response.getErrorCode().toString()); out.write("\" message=\""); out.write(StringUtil.escapeXml(response.getErrorMessage())); out.write("\"/>\n"); } /** static public byte[] createByteArray(Operation operation) throws SxmpErrorException, IOException { // most requests will be ~1000 bytes //FastByteArrayOutputStream baos = new FastByteArrayOutputStream(1000); ByteArrayOutputStream baos = new ByteArrayOutputStream(1000); OutputStreamWriter out = new OutputStreamWriter(baos, "ISO-8859-1"); SxmpWriter.write(out, operation); byte[] data = baos.toByteArray(); //out.close(); //baos.close(); return data; } */ static public String createString(Operation operation) throws SxmpErrorException, IOException { StringWriter sw = new StringWriter(1000); SxmpWriter.write(sw, operation); return sw.toString(); } static public void write(Writer out, Operation operation) throws SxmpErrorException, IOException { write(out, operation, true); } static public void write(Writer out, Operation operation, boolean withXmlHeader) throws SxmpErrorException, IOException { if (withXmlHeader) writeXmlHeader(out, operation); writeOperationStartTag(out, operation.getType()); // let's validate this operation is valid operation.validate(); if (operation instanceof ErrorResponse) { // write out just an ErrorResponse writeErrorElement(out, (ErrorResponse)operation); } else if (operation instanceof Request) { // any request requires an account to be printed out Request request = (Request)operation; if (request.getAccount() != null) { out.write(" <account username=\""); out.write(StringUtil.escapeXml(request.getAccount().getUsername())); out.write("\" password=\""); out.write(StringUtil.escapeXml(request.getAccount().getPassword())); out.write("\"/>\n"); } if (request.getApplication() != null) { out.write(" <application>"); out.write(StringUtil.escapeXml(request.getApplication().getName())); out.write("</application>\n"); } writeRequestResponseStartTag(out, operation); // tickets are now supported any any request if (request.getTicketId() != null) { out.write(" <ticketId>"); out.write(StringUtil.escapeXml(request.getTicketId())); out.write("</ticketId>\n"); } if (operation instanceof DeliveryReportRequest) { DeliveryReportRequest deliveryRequest = (DeliveryReportRequest)operation; if (deliveryRequest.getStatus() != null) { out.write(" <status code=\""); out.write(deliveryRequest.getStatus().getCode().toString()); out.write("\" message=\""); out.write(StringUtil.escapeXml(deliveryRequest.getStatus().getMessage())); out.write("\"/>\n"); } if (deliveryRequest.getMessageErrorCode() != null) { out.write(" <messageError code=\""); out.write(deliveryRequest.getMessageErrorCode().toString()); out.write("\"/>\n"); } if (deliveryRequest.getCreateDate() != null) { out.write(" <createDate>"); out.write(SxmpParser.dateTimeFormat.print(deliveryRequest.getCreateDate())); out.write("</createDate>\n"); } if (deliveryRequest.getFinalDate() != null) { out.write(" <finalDate>"); out.write(SxmpParser.dateTimeFormat.print(deliveryRequest.getFinalDate())); out.write("</finalDate>\n"); } } else if (operation instanceof MessageRequest) { MessageRequest messageRequest = (MessageRequest)operation; if (messageRequest.getOperatorId() != null) { out.write(" <operatorId>"); out.write(messageRequest.getOperatorId().toString()); out.write("</operatorId>\n"); } if (messageRequest.getPriority() != null) { out.write(" <priority>"); out.write(messageRequest.getPriority().getPriorityFlag().toString()); out.write("</priority>\n"); } if (operation instanceof SubmitRequest) { SubmitRequest submitRequest = (SubmitRequest)operation; if (submitRequest.getDeliveryReport() != null) { out.write(" <deliveryReport>"); out.write(submitRequest.getDeliveryReport().toString().toLowerCase()); out.write("</deliveryReport>\n"); } } if (messageRequest.getSourceAddress() != null) { out.write(" <sourceAddress type=\""); out.write(messageRequest.getSourceAddress().getType().toString().toLowerCase()); out.write("\">"); out.write(messageRequest.getSourceAddress().getAddress()); out.write("</sourceAddress>\n"); } if (messageRequest.getDestinationAddress() != null) { out.write(" <destinationAddress type=\""); out.write(messageRequest.getDestinationAddress().getType().toString().toLowerCase()); out.write("\">"); // push dest address is not guaranteed to be XML-safe // do minimum-necessary escaping? or better to stay standardized & escape everything? if (messageRequest.getDestinationAddress().getType() == MobileAddress.Type.PUSH_DESTINATION) { out.write(XmlEscapeUtil.escapeTextXml(messageRequest.getDestinationAddress().getAddress())); } else { out.write(messageRequest.getDestinationAddress().getAddress()); } out.write("</destinationAddress>\n"); } if (messageRequest.getText() != null) { String charset = messageRequest.getTextEncoding().getCharset(); out.write(" <text encoding=\""); out.write(charset); out.write("\">"); out.write(HexUtil.toHexString(messageRequest.getText().getBytes(charset))); out.write("</text>\n"); } // this is a v1.1 option; only write if version == 1.1 if (messageRequest.getOptionalParams() != null && messageRequest.getVersion().equals(SxmpParser.VERSION_1_1)) { //String charset = messageRequest.getTextEncoding().getCharset(); out.write(" <optionalParams>"); JSONObject jsonObj = new JSONObject(messageRequest.getOptionalParams()); // JSON encoding is not XML-safe // do minimum-necessary escaping? or better to stay standardized & escape everything? // tradeoff: all " and ' become " and ' and all json strings are quoted. out.write(XmlEscapeUtil.escapeTextXml(jsonObj.toString())); out.write("</optionalParams>\n"); } } writeRequestResponseEndTag(out, operation); } else { writeRequestResponseStartTag(out, operation); writeErrorElement(out, (Response)operation); // tickets on responses are now supported for submits or delivers if (operation instanceof SubmitResponse || operation instanceof DeliverResponse) { if (operation.getTicketId() != null) { out.write(" <ticketId>"); out.write(StringUtil.escapeXml(operation.getTicketId())); out.write("</ticketId>\n"); } } writeRequestResponseEndTag(out, operation); } writeOperationEndTag(out); } }