/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development * and Distribution License("CDDL") (collectively, the "License"). You * may not use this file except in compliance with the License. You can obtain * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html * or glassfish/bootstrap/legal/LICENSE.txt. See the License for the specific * language governing permissions and limitations under the License. * * When distributing the software, include this License Header Notice in each * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt. * Sun designates this particular file as subject to the "Classpath" exception * as provided by Sun in the GPL Version 2 section of the License file that * accompanied this code. If applicable, add the following below the License * Header, with the fields enclosed by brackets [] replaced by your own * identifying information: "Portions Copyrighted [year] * [name of copyright owner]" * * Contributor(s): * * If you wish your version of this file to be governed by only the CDDL or * only the GPL Version 2, indicate your decision by adding "[Contributor] * elects to include this software in this distribution under the [CDDL or GPL * Version 2] license." If you don't indicate a single choice of license, a * recipient has the option to distribute your version of this file under * either the CDDL, the GPL Version 2 or to extend the choice of license to * its licensees as provided above. However, if you add GPL Version 2 code * and therefore, elected the GPL Version 2 license, then the option applies * only if the new code is made subject to such option by the copyright * holder. */ package com.sun.mail.iap; import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; import java.io.IOException; import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.util.Vector; import com.sun.mail.util.ASCIIUtility; /** * @author John Mani */ public class Argument { protected Vector items; /** * Constructor */ public Argument() { items = new Vector(1); } /** * append the given Argument to this Argument. All items * from the source argument are copied into this destination * argument. */ public void append(Argument arg) { items.ensureCapacity(items.size() + arg.items.size()); for (int i=0; i < arg.items.size(); i++) items.addElement(arg.items.elementAt(i)); } /** * Write out given string as an ASTRING, depending on the type * of the characters inside the string. The string should * contain only ASCII characters. <p> * * XXX: Hmm .. this should really be called writeASCII() * * @param s String to write out */ public void writeString(String s) { items.addElement(new AString(ASCIIUtility.getBytes(s))); } /** * Convert the given string into bytes in the specified * charset, and write the bytes out as an ASTRING */ public void writeString(String s, String charset) throws UnsupportedEncodingException { if (charset == null) // convenience writeString(s); else items.addElement(new AString(s.getBytes(charset))); } /** * Write out given byte[] as a Literal. * @param b byte[] to write out */ public void writeBytes(byte[] b) { items.addElement(b); } /** * Write out given ByteArrayOutputStream as a Literal. * @param b ByteArrayOutputStream to be written out. */ public void writeBytes(ByteArrayOutputStream b) { items.addElement(b); } /** * Write out given data as a literal. * @param b Literal representing data to be written out. */ public void writeBytes(Literal b) { items.addElement(b); } /** * Write out given string as an Atom. Note that an Atom can contain only * certain US-ASCII characters. No validation is done on the characters * in the string. * @param s String */ public void writeAtom(String s) { items.addElement(new Atom(s)); } /** * Write out number. * @param i number */ public void writeNumber(int i) { items.addElement(new Integer(i)); } /** * Write out number. * @param i number */ public void writeNumber(long i) { items.addElement(new Long(i)); } /** * Write out as parenthesised list. * @param s statement */ public void writeArgument(Argument c) { items.addElement(c); } /* * Write out all the buffered items into the output stream. */ public void write(Protocol protocol) throws IOException, ProtocolException { int size = items != null ? items.size() : 0; DataOutputStream os = (DataOutputStream)protocol.getOutputStream(); for (int i=0; i < size; i++) { if (i > 0) // write delimiter if not the first item os.write(' '); Object o = items.elementAt(i); if (o instanceof Atom) { os.writeBytes(((Atom)o).string); } else if (o instanceof Number) { os.writeBytes(((Number)o).toString()); } else if (o instanceof AString) { astring(((AString)o).bytes, protocol); } else if (o instanceof byte[]) { literal((byte[])o, protocol); } else if (o instanceof ByteArrayOutputStream) { literal((ByteArrayOutputStream)o, protocol); } else if (o instanceof Literal) { literal((Literal)o, protocol); } else if (o instanceof Argument) { os.write('('); // open parans ((Argument)o).write(protocol); os.write(')'); // close parans } } } /** * Write out given String as either an Atom, QuotedString or Literal */ private void astring(byte[] bytes, Protocol protocol) throws IOException, ProtocolException { DataOutputStream os = (DataOutputStream)protocol.getOutputStream(); int len = bytes.length; // If length is greater than 1024 bytes, send as literal if (len > 1024) { literal(bytes, protocol); return; } // if 0 length, send as quoted-string boolean quote = len == 0 ? true: false; boolean escape = false; byte b; for (int i = 0; i < len; i++) { b = bytes[i]; if (b == '\0' || b == '\r' || b == '\n' || ((b & 0xff) > 0177)) { // NUL, CR or LF means the bytes need to be sent as literals literal(bytes, protocol); return; } if (b == '*' || b == '%' || b == '(' || b == ')' || b == '{' || b == '"' || b == '\\' || ((b & 0xff) <= ' ')) { quote = true; if (b == '"' || b == '\\') // need to escape these characters escape = true; } } if (quote) // start quote os.write('"'); if (escape) { // already quoted for (int i = 0; i < len; i++) { b = bytes[i]; if (b == '"' || b == '\\') os.write('\\'); os.write(b); } } else os.write(bytes); if (quote) // end quote os.write('"'); } /** * Write out given byte[] as a literal */ private void literal(byte[] b, Protocol protocol) throws IOException, ProtocolException { startLiteral(protocol, b.length).write(b); } /** * Write out given ByteArrayOutputStream as a literal. */ private void literal(ByteArrayOutputStream b, Protocol protocol) throws IOException, ProtocolException { b.writeTo(startLiteral(protocol, b.size())); } /** * Write out given Literal as a literal. */ private void literal(Literal b, Protocol protocol) throws IOException, ProtocolException { b.writeTo(startLiteral(protocol, b.size())); } private OutputStream startLiteral(Protocol protocol, int size) throws IOException, ProtocolException { DataOutputStream os = (DataOutputStream)protocol.getOutputStream(); boolean nonSync = protocol.supportsNonSyncLiterals(); os.write('{'); os.writeBytes(Integer.toString(size)); if (nonSync) // server supports non-sync literals os.writeBytes("+}\r\n"); else os.writeBytes("}\r\n"); os.flush(); // If we are using synchronized literals, wait for the server's // continuation signal if (!nonSync) { for (; ;) { Response r = protocol.readResponse(); if (r.isContinuation()) break; if (r.isTagged()) throw new LiteralException(r); // XXX - throw away untagged responses; // violates IMAP spec, hope no servers do this } } return os; } } class Atom { String string; Atom(String s) { string = s; } } class AString { byte[] bytes; AString(byte[] b) { bytes = b; } }