mirror of
https://github.com/moparisthebest/k-9
synced 2024-12-25 00:58:50 -05:00
Upgrade to mime4j. "0.7" branch as of
http://svn.apache.org/repos/asf/james/mime4j/trunk@1058339
This commit is contained in:
parent
f5bb836087
commit
8329a0287b
@ -13,11 +13,12 @@ import com.fsck.k9.K9;
|
||||
import com.fsck.k9.helper.Contacts;
|
||||
import com.fsck.k9.helper.Utility;
|
||||
import org.apache.james.mime4j.codec.EncoderUtil;
|
||||
import org.apache.james.mime4j.field.address.AddressList;
|
||||
import org.apache.james.mime4j.field.address.Mailbox;
|
||||
import org.apache.james.mime4j.field.address.MailboxList;
|
||||
import org.apache.james.mime4j.field.address.NamedMailbox;
|
||||
import org.apache.james.mime4j.field.address.parser.ParseException;
|
||||
import org.apache.james.mime4j.dom.address.AddressList;
|
||||
import org.apache.james.mime4j.dom.address.Mailbox;
|
||||
import org.apache.james.mime4j.dom.address.MailboxList;
|
||||
import org.apache.james.mime4j.field.DefaultFieldParser;
|
||||
import org.apache.james.mime4j.field.address.parser.AddressBuilder;
|
||||
import org.apache.james.mime4j.MimeException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@ -137,21 +138,21 @@ public class Address
|
||||
}
|
||||
try
|
||||
{
|
||||
MailboxList parsedList = AddressList.parse(addressList).flatten();
|
||||
MailboxList parsedList = (MailboxList) AddressBuilder.parseAddressList(addressList).flatten();
|
||||
for (int i = 0, count = parsedList.size(); i < count; i++)
|
||||
{
|
||||
org.apache.james.mime4j.field.address.Address address = parsedList.get(i);
|
||||
if (address instanceof NamedMailbox)
|
||||
{
|
||||
NamedMailbox namedMailbox = (NamedMailbox)address;
|
||||
addresses.add(new Address(namedMailbox.getLocalPart() + "@"
|
||||
+ namedMailbox.getDomain(), namedMailbox.getName()));
|
||||
}
|
||||
else if (address instanceof Mailbox)
|
||||
org.apache.james.mime4j.dom.address.Address address = parsedList.get(i);
|
||||
if (address instanceof Mailbox)
|
||||
{
|
||||
Mailbox mailbox = (Mailbox)address;
|
||||
if (mailbox.getName() != null )
|
||||
{
|
||||
addresses.add(new Address(mailbox.getLocalPart() + "@" + mailbox.getDomain(), mailbox.getName()));
|
||||
} else
|
||||
{
|
||||
addresses.add(new Address(mailbox.getLocalPart() + "@" + mailbox.getDomain()));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.e(K9.LOG_TAG, "Unknown address type from Mime4J: "
|
||||
@ -160,7 +161,7 @@ public class Address
|
||||
|
||||
}
|
||||
}
|
||||
catch (ParseException pe)
|
||||
catch (MimeException pe)
|
||||
{
|
||||
}
|
||||
return addresses.toArray(EMPTY_ADDRESS_ARRAY);
|
||||
|
@ -8,8 +8,8 @@ import com.fsck.k9.mail.MessagingException;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import org.apache.james.mime4j.decoder.Base64InputStream;
|
||||
import org.apache.james.mime4j.decoder.QuotedPrintableInputStream;
|
||||
import org.apache.james.mime4j.codec.Base64InputStream;
|
||||
import org.apache.james.mime4j.codec.QuotedPrintableInputStream;
|
||||
import org.apache.james.mime4j.util.CharsetUtil;
|
||||
|
||||
|
||||
|
@ -4,12 +4,16 @@ package com.fsck.k9.mail.internet;
|
||||
import com.fsck.k9.mail.*;
|
||||
import com.fsck.k9.mail.store.UnavailableStorageException;
|
||||
|
||||
import org.apache.james.mime4j.BodyDescriptor;
|
||||
import org.apache.james.mime4j.ContentHandler;
|
||||
import org.apache.james.mime4j.EOLConvertingInputStream;
|
||||
import org.apache.james.mime4j.MimeStreamParser;
|
||||
import org.apache.james.mime4j.field.DateTimeField;
|
||||
import org.apache.james.mime4j.field.Field;
|
||||
import org.apache.james.mime4j.stream.BodyDescriptor;
|
||||
import org.apache.james.mime4j.stream.RawField;
|
||||
import org.apache.james.mime4j.parser.ContentHandler;
|
||||
import org.apache.james.mime4j.io.EOLConvertingInputStream;
|
||||
import org.apache.james.mime4j.parser.MimeStreamParser;
|
||||
import org.apache.james.mime4j.dom.field.DateTimeField;
|
||||
import org.apache.james.mime4j.dom.field.Field;
|
||||
import org.apache.james.mime4j.field.DefaultFieldParser;
|
||||
|
||||
import org.apache.james.mime4j.MimeException;
|
||||
|
||||
import java.io.*;
|
||||
import java.text.SimpleDateFormat;
|
||||
@ -74,7 +78,12 @@ public class MimeMessage extends Message
|
||||
|
||||
MimeStreamParser parser = new MimeStreamParser();
|
||||
parser.setContentHandler(new MimeMessageBuilder());
|
||||
parser.parse(new EOLConvertingInputStream(in));
|
||||
try {
|
||||
parser.parse(new EOLConvertingInputStream(in));
|
||||
} catch (MimeException me) {
|
||||
throw new Error(me);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -84,7 +93,7 @@ public class MimeMessage extends Message
|
||||
{
|
||||
try
|
||||
{
|
||||
DateTimeField field = (DateTimeField)Field.parse("Date: "
|
||||
DateTimeField field = (DateTimeField)DefaultFieldParser.parse("Date: "
|
||||
+ MimeUtility.unfoldAndDecode(getFirstHeader("Date")));
|
||||
mSentDate = field.getDate();
|
||||
}
|
||||
@ -565,6 +574,18 @@ public class MimeMessage extends Message
|
||||
expect(Part.class);
|
||||
}
|
||||
|
||||
public void field(RawField field)
|
||||
{
|
||||
try {
|
||||
Field parsedField = DefaultFieldParser.parse(field.getRaw(), null);
|
||||
((Part)stack.peek()).addHeader(parsedField.getName(), field.getBody().trim());
|
||||
} catch (MessagingException me) {
|
||||
throw new Error(me);
|
||||
} catch (MimeException me) {
|
||||
throw new Error(me);
|
||||
}
|
||||
}
|
||||
|
||||
public void field(String fieldData)
|
||||
{
|
||||
expect(Part.class);
|
||||
|
@ -5,8 +5,8 @@ import android.util.Log;
|
||||
import com.fsck.k9.K9;
|
||||
import com.fsck.k9.mail.*;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.james.mime4j.decoder.Base64InputStream;
|
||||
import org.apache.james.mime4j.decoder.QuotedPrintableInputStream;
|
||||
import org.apache.james.mime4j.codec.Base64InputStream;
|
||||
import org.apache.james.mime4j.codec.QuotedPrintableInputStream;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
@ -1,113 +0,0 @@
|
||||
/****************************************************************
|
||||
* Licensed to the Apache Software Foundation (ASF) under one *
|
||||
* or more contributor license agreements. See the NOTICE file *
|
||||
* distributed with this work for additional information *
|
||||
* regarding copyright ownership. The ASF licenses this file *
|
||||
* to you 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. *
|
||||
****************************************************************/
|
||||
|
||||
package org.apache.james.mime4j;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* Abstract <code>ContentHandler</code> with default implementations of all
|
||||
* the methods of the <code>ContentHandler</code> interface.
|
||||
*
|
||||
* The default is to todo nothing.
|
||||
*
|
||||
*
|
||||
* @version $Id: AbstractContentHandler.java,v 1.3 2004/10/02 12:41:10 ntherning Exp $
|
||||
*/
|
||||
public abstract class AbstractContentHandler implements ContentHandler {
|
||||
|
||||
/**
|
||||
* @see org.apache.james.mime4j.ContentHandler#endMultipart()
|
||||
*/
|
||||
public void endMultipart() {
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.apache.james.mime4j.ContentHandler#startMultipart(org.apache.james.mime4j.BodyDescriptor)
|
||||
*/
|
||||
public void startMultipart(BodyDescriptor bd) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.apache.james.mime4j.ContentHandler#body(org.apache.james.mime4j.BodyDescriptor, java.io.InputStream)
|
||||
*/
|
||||
public void body(BodyDescriptor bd, InputStream is) throws IOException {
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.apache.james.mime4j.ContentHandler#endBodyPart()
|
||||
*/
|
||||
public void endBodyPart() {
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.apache.james.mime4j.ContentHandler#endHeader()
|
||||
*/
|
||||
public void endHeader() {
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.apache.james.mime4j.ContentHandler#endMessage()
|
||||
*/
|
||||
public void endMessage() {
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.apache.james.mime4j.ContentHandler#epilogue(java.io.InputStream)
|
||||
*/
|
||||
public void epilogue(InputStream is) throws IOException {
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.apache.james.mime4j.ContentHandler#field(java.lang.String)
|
||||
*/
|
||||
public void field(String fieldData) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.apache.james.mime4j.ContentHandler#preamble(java.io.InputStream)
|
||||
*/
|
||||
public void preamble(InputStream is) throws IOException {
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.apache.james.mime4j.ContentHandler#startBodyPart()
|
||||
*/
|
||||
public void startBodyPart() {
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.apache.james.mime4j.ContentHandler#startHeader()
|
||||
*/
|
||||
public void startHeader() {
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.apache.james.mime4j.ContentHandler#startMessage()
|
||||
*/
|
||||
public void startMessage() {
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.apache.james.mime4j.ContentHandler#raw(java.io.InputStream)
|
||||
*/
|
||||
public void raw(InputStream is) throws IOException {
|
||||
}
|
||||
}
|
@ -1,129 +0,0 @@
|
||||
/****************************************************************
|
||||
* Licensed to the Apache Software Foundation (ASF) under one *
|
||||
* or more contributor license agreements. See the NOTICE file *
|
||||
* distributed with this work for additional information *
|
||||
* regarding copyright ownership. The ASF licenses this file *
|
||||
* to you 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. *
|
||||
****************************************************************/
|
||||
|
||||
package org.apache.james.mime4j;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* InputStream that shields its underlying input stream from
|
||||
* being closed.
|
||||
*
|
||||
*
|
||||
* @version $Id: CloseShieldInputStream.java,v 1.2 2004/10/02 12:41:10 ntherning Exp $
|
||||
*/
|
||||
public class CloseShieldInputStream extends InputStream {
|
||||
|
||||
/**
|
||||
* Underlying InputStream
|
||||
*/
|
||||
private InputStream is;
|
||||
|
||||
public CloseShieldInputStream(InputStream is) {
|
||||
this.is = is;
|
||||
}
|
||||
|
||||
public InputStream getUnderlyingStream() {
|
||||
return is;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.io.InputStream#read()
|
||||
*/
|
||||
public int read() throws IOException {
|
||||
checkIfClosed();
|
||||
return is.read();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.io.InputStream#available()
|
||||
*/
|
||||
public int available() throws IOException {
|
||||
checkIfClosed();
|
||||
return is.available();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the underlying InputStream to null
|
||||
*/
|
||||
public void close() throws IOException {
|
||||
is = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.io.FilterInputStream#reset()
|
||||
*/
|
||||
public synchronized void reset() throws IOException {
|
||||
checkIfClosed();
|
||||
is.reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.io.FilterInputStream#markSupported()
|
||||
*/
|
||||
public boolean markSupported() {
|
||||
if (is == null)
|
||||
return false;
|
||||
return is.markSupported();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.io.FilterInputStream#mark(int)
|
||||
*/
|
||||
public synchronized void mark(int readlimit) {
|
||||
if (is != null)
|
||||
is.mark(readlimit);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.io.FilterInputStream#skip(long)
|
||||
*/
|
||||
public long skip(long n) throws IOException {
|
||||
checkIfClosed();
|
||||
return is.skip(n);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.io.FilterInputStream#read(byte[])
|
||||
*/
|
||||
public int read(byte b[]) throws IOException {
|
||||
checkIfClosed();
|
||||
return is.read(b);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.io.FilterInputStream#read(byte[], int, int)
|
||||
*/
|
||||
public int read(byte b[], int off, int len) throws IOException {
|
||||
checkIfClosed();
|
||||
return is.read(b, off, len);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the underlying InputStream is null. If so throw an Exception
|
||||
*
|
||||
* @throws IOException if the underlying InputStream is null
|
||||
*/
|
||||
private void checkIfClosed() throws IOException {
|
||||
if (is == null)
|
||||
throw new IOException("Stream is closed");
|
||||
}
|
||||
}
|
@ -1,184 +0,0 @@
|
||||
/****************************************************************
|
||||
* Licensed to the Apache Software Foundation (ASF) under one *
|
||||
* or more contributor license agreements. See the NOTICE file *
|
||||
* distributed with this work for additional information *
|
||||
* regarding copyright ownership. The ASF licenses this file *
|
||||
* to you 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. *
|
||||
****************************************************************/
|
||||
|
||||
package org.apache.james.mime4j;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.PushbackInputStream;
|
||||
|
||||
/**
|
||||
* Stream that constrains itself to a single MIME body part.
|
||||
* After the stream ends (i.e. read() returns -1) {@link #hasMoreParts()}
|
||||
* can be used to determine if a final boundary has been seen or not.
|
||||
* If {@link #parentEOF()} is <code>true</code> an unexpected end of stream
|
||||
* has been detected in the parent stream.
|
||||
*
|
||||
*
|
||||
*
|
||||
* @version $Id: MimeBoundaryInputStream.java,v 1.2 2004/11/29 13:15:42 ntherning Exp $
|
||||
*/
|
||||
public class MimeBoundaryInputStream extends InputStream {
|
||||
|
||||
private PushbackInputStream s = null;
|
||||
private byte[] boundary = null;
|
||||
private boolean first = true;
|
||||
private boolean eof = false;
|
||||
private boolean parenteof = false;
|
||||
private boolean moreParts = true;
|
||||
|
||||
/**
|
||||
* Creates a new MimeBoundaryInputStream.
|
||||
* @param s The underlying stream.
|
||||
* @param boundary Boundary string (not including leading hyphens).
|
||||
*/
|
||||
public MimeBoundaryInputStream(InputStream s, String boundary)
|
||||
throws IOException {
|
||||
|
||||
this.s = new PushbackInputStream(s, boundary.length() + 4);
|
||||
|
||||
boundary = "--" + boundary;
|
||||
this.boundary = new byte[boundary.length()];
|
||||
for (int i = 0; i < this.boundary.length; i++) {
|
||||
this.boundary[i] = (byte) boundary.charAt(i);
|
||||
}
|
||||
|
||||
/*
|
||||
* By reading one byte we will update moreParts to be as expected
|
||||
* before any bytes have been read.
|
||||
*/
|
||||
int b = read();
|
||||
if (b != -1) {
|
||||
this.s.unread(b);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the underlying stream.
|
||||
*
|
||||
* @throws IOException on I/O errors.
|
||||
*/
|
||||
public void close() throws IOException {
|
||||
s.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the underlying stream has more parts (this stream has
|
||||
* not seen an end boundary).
|
||||
*
|
||||
* @return <code>true</code> if there are more parts in the underlying
|
||||
* stream, <code>false</code> otherwise.
|
||||
*/
|
||||
public boolean hasMoreParts() {
|
||||
return moreParts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the parent stream has reached EOF
|
||||
*
|
||||
* @return <code>true</code> if EOF has been reached for the parent stream,
|
||||
* <code>false</code> otherwise.
|
||||
*/
|
||||
public boolean parentEOF() {
|
||||
return parenteof;
|
||||
}
|
||||
|
||||
/**
|
||||
* Consumes all unread bytes of this stream. After a call to this method
|
||||
* this stream will have reached EOF.
|
||||
*
|
||||
* @throws IOException on I/O errors.
|
||||
*/
|
||||
public void consume() throws IOException {
|
||||
while (read() != -1) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.io.InputStream#read()
|
||||
*/
|
||||
public int read() throws IOException {
|
||||
if (eof) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (first) {
|
||||
first = false;
|
||||
if (matchBoundary()) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int b1 = s.read();
|
||||
int b2 = s.read();
|
||||
|
||||
if (b1 == '\r' && b2 == '\n') {
|
||||
if (matchBoundary()) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (b2 != -1) {
|
||||
s.unread(b2);
|
||||
}
|
||||
|
||||
parenteof = b1 == -1;
|
||||
eof = parenteof;
|
||||
|
||||
return b1;
|
||||
}
|
||||
|
||||
private boolean matchBoundary() throws IOException {
|
||||
|
||||
for (int i = 0; i < boundary.length; i++) {
|
||||
int b = s.read();
|
||||
if (b != boundary[i]) {
|
||||
if (b != -1) {
|
||||
s.unread(b);
|
||||
}
|
||||
for (int j = i - 1; j >= 0; j--) {
|
||||
s.unread(boundary[j]);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* We have a match. Is it an end boundary?
|
||||
*/
|
||||
int prev = s.read();
|
||||
int curr = s.read();
|
||||
moreParts = !(prev == '-' && curr == '-');
|
||||
do {
|
||||
if (curr == '\n' && prev == '\r') {
|
||||
break;
|
||||
}
|
||||
prev = curr;
|
||||
} while ((curr = s.read()) != -1);
|
||||
|
||||
if (curr == -1) {
|
||||
moreParts = false;
|
||||
parenteof = true;
|
||||
}
|
||||
|
||||
eof = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
65
src/org/apache/james/mime4j/MimeException.java
Normal file
65
src/org/apache/james/mime4j/MimeException.java
Normal file
@ -0,0 +1,65 @@
|
||||
/****************************************************************
|
||||
* Licensed to the Apache Software Foundation (ASF) under one *
|
||||
* or more contributor license agreements. See the NOTICE file *
|
||||
* distributed with this work for additional information *
|
||||
* regarding copyright ownership. The ASF licenses this file *
|
||||
* to you 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. *
|
||||
****************************************************************/
|
||||
|
||||
package org.apache.james.mime4j;
|
||||
|
||||
/**
|
||||
* MIME processing exception.
|
||||
* <p>
|
||||
* A <code>MimeException</code> may be thrown by a {@link org.apache.james.mime4j.parser.ContentHandler} to
|
||||
* indicate that it has failed to process a message event and that no further
|
||||
* events should be generated.
|
||||
* <p>
|
||||
* <code>MimeException</code> also gets thrown by the parser to indicate MIME
|
||||
* protocol errors, e.g. if a message boundary is too long or a header field
|
||||
* cannot be parsed.
|
||||
*/
|
||||
public class MimeException extends Exception {
|
||||
|
||||
private static final long serialVersionUID = 8352821278714188542L;
|
||||
|
||||
/**
|
||||
* Constructs a new MIME exception with the specified detail message.
|
||||
*
|
||||
* @param message detail message
|
||||
*/
|
||||
public MimeException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a MIME exception with the specified cause.
|
||||
*
|
||||
* @param cause cause of the exception
|
||||
*/
|
||||
public MimeException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a MIME exception with the specified detail message and cause.
|
||||
*
|
||||
* @param message detail message
|
||||
* @param cause cause of the exception
|
||||
*/
|
||||
public MimeException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
}
|
58
src/org/apache/james/mime4j/MimeIOException.java
Normal file
58
src/org/apache/james/mime4j/MimeIOException.java
Normal file
@ -0,0 +1,58 @@
|
||||
/****************************************************************
|
||||
* Licensed to the Apache Software Foundation (ASF) under one *
|
||||
* or more contributor license agreements. See the NOTICE file *
|
||||
* distributed with this work for additional information *
|
||||
* regarding copyright ownership. The ASF licenses this file *
|
||||
* to you 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. *
|
||||
****************************************************************/
|
||||
|
||||
package org.apache.james.mime4j;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* A wrapper class based on {@link IOException} for MIME protocol exceptions.
|
||||
* <p>
|
||||
* This exception is used to signal a <code>MimeException</code> in methods
|
||||
* that only permit <code>IOException</code> to be thrown.
|
||||
* <p>
|
||||
* The cause of a <code>MimeIOException</code> is always a
|
||||
* <code>MimeException</code> therefore.
|
||||
*/
|
||||
public class MimeIOException extends IOException {
|
||||
|
||||
private static final long serialVersionUID = 5393613459533735409L;
|
||||
|
||||
/**
|
||||
* Constructs an IO exception based on {@link MimeException}.
|
||||
*
|
||||
* @param cause the cause.
|
||||
*/
|
||||
public MimeIOException(MimeException cause) {
|
||||
super(cause == null ? null : cause.getMessage());
|
||||
initCause(cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the <code>MimeException</code> that caused this
|
||||
* <code>MimeIOException</code>.
|
||||
*
|
||||
* @return the cause of this <code>MimeIOException</code>.
|
||||
*/
|
||||
@Override
|
||||
public MimeException getCause() {
|
||||
return (MimeException) super.getCause();
|
||||
}
|
||||
|
||||
}
|
@ -1,320 +0,0 @@
|
||||
/****************************************************************
|
||||
* Licensed to the Apache Software Foundation (ASF) under one *
|
||||
* or more contributor license agreements. See the NOTICE file *
|
||||
* distributed with this work for additional information *
|
||||
* regarding copyright ownership. The ASF licenses this file *
|
||||
* to you 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. *
|
||||
****************************************************************/
|
||||
|
||||
package org.apache.james.mime4j;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.BitSet;
|
||||
import java.util.LinkedList;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.apache.james.mime4j.decoder.Base64InputStream;
|
||||
import org.apache.james.mime4j.decoder.QuotedPrintableInputStream;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Parses MIME (or RFC822) message streams of bytes or characters and reports
|
||||
* parsing events to a <code>ContentHandler</code> instance.
|
||||
* </p>
|
||||
* <p>
|
||||
* Typical usage:<br/>
|
||||
* <pre>
|
||||
* ContentHandler handler = new MyHandler();
|
||||
* MimeStreamParser parser = new MimeStreamParser();
|
||||
* parser.setContentHandler(handler);
|
||||
* parser.parse(new BufferedInputStream(new FileInputStream("mime.msg")));
|
||||
* </pre>
|
||||
* <strong>NOTE:</strong> All lines must end with CRLF
|
||||
* (<code>\r\n</code>). If you are unsure of the line endings in your stream
|
||||
* you should wrap it in a {@link org.apache.james.mime4j.EOLConvertingInputStream} instance.
|
||||
*
|
||||
*
|
||||
* @version $Id: MimeStreamParser.java,v 1.8 2005/02/11 10:12:02 ntherning Exp $
|
||||
*/
|
||||
public class MimeStreamParser {
|
||||
private static final Log log = LogFactory.getLog(MimeStreamParser.class);
|
||||
|
||||
private static BitSet fieldChars = null;
|
||||
|
||||
private RootInputStream rootStream = null;
|
||||
private LinkedList bodyDescriptors = new LinkedList();
|
||||
private ContentHandler handler = null;
|
||||
private boolean raw = false;
|
||||
|
||||
static {
|
||||
fieldChars = new BitSet();
|
||||
for (int i = 0x21; i <= 0x39; i++) {
|
||||
fieldChars.set(i);
|
||||
}
|
||||
for (int i = 0x3b; i <= 0x7e; i++) {
|
||||
fieldChars.set(i);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new <code>MimeStreamParser</code> instance.
|
||||
*/
|
||||
public MimeStreamParser() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a stream of bytes containing a MIME message.
|
||||
*
|
||||
* @param is the stream to parse.
|
||||
* @throws IOException on I/O errors.
|
||||
*/
|
||||
public void parse(InputStream is) throws IOException {
|
||||
rootStream = new RootInputStream(is);
|
||||
parseMessage(rootStream);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if this parser is currently in raw mode.
|
||||
*
|
||||
* @return <code>true</code> if in raw mode, <code>false</code>
|
||||
* otherwise.
|
||||
* @see #setRaw(boolean)
|
||||
*/
|
||||
public boolean isRaw() {
|
||||
return raw;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables or disables raw mode. In raw mode all future entities
|
||||
* (messages or body parts) in the stream will be reported to the
|
||||
* {@link ContentHandler#raw(InputStream)} handler method only.
|
||||
* The stream will contain the entire unparsed entity contents
|
||||
* including header fields and whatever is in the body.
|
||||
*
|
||||
* @param raw <code>true</code> enables raw mode, <code>false</code>
|
||||
* disables it.
|
||||
*/
|
||||
public void setRaw(boolean raw) {
|
||||
this.raw = raw;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finishes the parsing and stops reading lines.
|
||||
* NOTE: No more lines will be parsed but the parser
|
||||
* will still call
|
||||
* {@link ContentHandler#endMultipart()},
|
||||
* {@link ContentHandler#endBodyPart()},
|
||||
* {@link ContentHandler#endMessage()}, etc to match previous calls
|
||||
* to
|
||||
* {@link ContentHandler#startMultipart(BodyDescriptor)},
|
||||
* {@link ContentHandler#startBodyPart()},
|
||||
* {@link ContentHandler#startMessage()}, etc.
|
||||
*/
|
||||
public void stop() {
|
||||
rootStream.truncate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses an entity which consists of a header followed by a body containing
|
||||
* arbitrary data, body parts or an embedded message.
|
||||
*
|
||||
* @param is the stream to parse.
|
||||
* @throws IOException on I/O errors.
|
||||
*/
|
||||
private void parseEntity(InputStream is) throws IOException {
|
||||
BodyDescriptor bd = parseHeader(is);
|
||||
|
||||
if (bd.isMultipart()) {
|
||||
bodyDescriptors.addFirst(bd);
|
||||
|
||||
handler.startMultipart(bd);
|
||||
|
||||
MimeBoundaryInputStream tempIs =
|
||||
new MimeBoundaryInputStream(is, bd.getBoundary());
|
||||
handler.preamble(new CloseShieldInputStream(tempIs));
|
||||
tempIs.consume();
|
||||
|
||||
while (tempIs.hasMoreParts()) {
|
||||
tempIs = new MimeBoundaryInputStream(is, bd.getBoundary());
|
||||
parseBodyPart(tempIs);
|
||||
tempIs.consume();
|
||||
if (tempIs.parentEOF()) {
|
||||
if (log.isWarnEnabled()) {
|
||||
log.warn("Line " + rootStream.getLineNumber()
|
||||
+ ": Body part ended prematurely. "
|
||||
+ "Higher level boundary detected or "
|
||||
+ "EOF reached.");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
handler.epilogue(new CloseShieldInputStream(is));
|
||||
|
||||
handler.endMultipart();
|
||||
|
||||
bodyDescriptors.removeFirst();
|
||||
|
||||
} else if (bd.isMessage()) {
|
||||
if (bd.isBase64Encoded()) {
|
||||
log.warn("base64 encoded message/rfc822 detected");
|
||||
is = new EOLConvertingInputStream(
|
||||
new Base64InputStream(is));
|
||||
} else if (bd.isQuotedPrintableEncoded()) {
|
||||
log.warn("quoted-printable encoded message/rfc822 detected");
|
||||
is = new EOLConvertingInputStream(
|
||||
new QuotedPrintableInputStream(is));
|
||||
}
|
||||
bodyDescriptors.addFirst(bd);
|
||||
parseMessage(is);
|
||||
bodyDescriptors.removeFirst();
|
||||
} else {
|
||||
handler.body(bd, new CloseShieldInputStream(is));
|
||||
}
|
||||
|
||||
/*
|
||||
* Make sure the stream has been consumed.
|
||||
*/
|
||||
while (is.read() != -1) {
|
||||
}
|
||||
}
|
||||
|
||||
private void parseMessage(InputStream is) throws IOException {
|
||||
if (raw) {
|
||||
handler.raw(new CloseShieldInputStream(is));
|
||||
} else {
|
||||
handler.startMessage();
|
||||
parseEntity(is);
|
||||
handler.endMessage();
|
||||
}
|
||||
}
|
||||
|
||||
private void parseBodyPart(InputStream is) throws IOException {
|
||||
if (raw) {
|
||||
handler.raw(new CloseShieldInputStream(is));
|
||||
} else {
|
||||
handler.startBodyPart();
|
||||
parseEntity(is);
|
||||
handler.endBodyPart();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a header.
|
||||
*
|
||||
* @param is the stream to parse.
|
||||
* @return a <code>BodyDescriptor</code> describing the body following
|
||||
* the header.
|
||||
*/
|
||||
private BodyDescriptor parseHeader(InputStream is) throws IOException {
|
||||
BodyDescriptor bd = new BodyDescriptor(bodyDescriptors.isEmpty()
|
||||
? null : (BodyDescriptor) bodyDescriptors.getFirst());
|
||||
|
||||
handler.startHeader();
|
||||
|
||||
int lineNumber = rootStream.getLineNumber();
|
||||
|
||||
StringBuffer sb = new StringBuffer();
|
||||
int curr = 0;
|
||||
int prev = 0;
|
||||
while ((curr = is.read()) != -1) {
|
||||
if (curr == '\n' && (prev == '\n' || prev == 0)) {
|
||||
/*
|
||||
* [\r]\n[\r]\n or an immediate \r\n have been seen.
|
||||
*/
|
||||
sb.deleteCharAt(sb.length() - 1);
|
||||
break;
|
||||
}
|
||||
sb.append((char) curr);
|
||||
prev = curr == '\r' ? prev : curr;
|
||||
}
|
||||
|
||||
if (curr == -1 && log.isWarnEnabled()) {
|
||||
log.warn("Line " + rootStream.getLineNumber()
|
||||
+ ": Unexpected end of headers detected. "
|
||||
+ "Boundary detected in header or EOF reached.");
|
||||
}
|
||||
|
||||
int start = 0;
|
||||
int pos = 0;
|
||||
int startLineNumber = lineNumber;
|
||||
while (pos < sb.length()) {
|
||||
while (pos < sb.length() && sb.charAt(pos) != '\r') {
|
||||
pos++;
|
||||
}
|
||||
if (pos < sb.length() - 1 && sb.charAt(pos + 1) != '\n') {
|
||||
pos++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (pos >= sb.length() - 2 || fieldChars.get(sb.charAt(pos + 2))) {
|
||||
|
||||
/*
|
||||
* field should be the complete field data excluding the
|
||||
* trailing \r\n.
|
||||
*/
|
||||
String field = sb.substring(start, pos);
|
||||
start = pos + 2;
|
||||
|
||||
/*
|
||||
* Check for a valid field.
|
||||
*/
|
||||
int index = field.indexOf(':');
|
||||
boolean valid = false;
|
||||
if (index != -1 && fieldChars.get(field.charAt(0))) {
|
||||
valid = true;
|
||||
String fieldName = field.substring(0, index).trim();
|
||||
for (int i = 0; i < fieldName.length(); i++) {
|
||||
if (!fieldChars.get(fieldName.charAt(i))) {
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (valid) {
|
||||
handler.field(field);
|
||||
bd.addField(fieldName, field.substring(index + 1));
|
||||
}
|
||||
}
|
||||
|
||||
if (!valid && log.isWarnEnabled()) {
|
||||
log.warn("Line " + startLineNumber
|
||||
+ ": Ignoring invalid field: '" + field.trim() + "'");
|
||||
}
|
||||
|
||||
startLineNumber = lineNumber;
|
||||
}
|
||||
|
||||
pos += 2;
|
||||
lineNumber++;
|
||||
}
|
||||
|
||||
handler.endHeader();
|
||||
|
||||
return bd;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the <code>ContentHandler</code> to use when reporting
|
||||
* parsing events.
|
||||
*
|
||||
* @param h the <code>ContentHandler</code>.
|
||||
*/
|
||||
public void setContentHandler(ContentHandler h) {
|
||||
this.handler = h;
|
||||
}
|
||||
|
||||
}
|
@ -1,111 +0,0 @@
|
||||
/****************************************************************
|
||||
* Licensed to the Apache Software Foundation (ASF) under one *
|
||||
* or more contributor license agreements. See the NOTICE file *
|
||||
* distributed with this work for additional information *
|
||||
* regarding copyright ownership. The ASF licenses this file *
|
||||
* to you 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. *
|
||||
****************************************************************/
|
||||
|
||||
package org.apache.james.mime4j;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* <code>InputStream</code> used by the parser to wrap the original user
|
||||
* supplied stream. This stream keeps track of the current line number and
|
||||
* can also be truncated. When truncated the stream will appear to have
|
||||
* reached end of file. This is used by the parser's
|
||||
* {@link org.apache.james.mime4j.MimeStreamParser#stop()} method.
|
||||
*
|
||||
*
|
||||
* @version $Id: RootInputStream.java,v 1.2 2004/10/02 12:41:10 ntherning Exp $
|
||||
*/
|
||||
class RootInputStream extends InputStream {
|
||||
private InputStream is = null;
|
||||
private int lineNumber = 1;
|
||||
private int prev = -1;
|
||||
private boolean truncated = false;
|
||||
|
||||
/**
|
||||
* Creates a new <code>RootInputStream</code>.
|
||||
*
|
||||
* @param in the stream to read from.
|
||||
*/
|
||||
public RootInputStream(InputStream is) {
|
||||
this.is = is;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current line number starting at 1
|
||||
* (the number of <code>\r\n</code> read so far plus 1).
|
||||
*
|
||||
* @return the current line number.
|
||||
*/
|
||||
public int getLineNumber() {
|
||||
return lineNumber;
|
||||
}
|
||||
|
||||
/**
|
||||
* Truncates this <code>InputStream</code>. After this call any
|
||||
* call to {@link #read()}, {@link #read(byte[]) or
|
||||
* {@link #read(byte[], int, int)} will return
|
||||
* -1 as if end-of-file had been reached.
|
||||
*/
|
||||
public void truncate() {
|
||||
this.truncated = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.io.InputStream#read()
|
||||
*/
|
||||
public int read() throws IOException {
|
||||
if (truncated) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int b = is.read();
|
||||
if (prev == '\r' && b == '\n') {
|
||||
lineNumber++;
|
||||
}
|
||||
prev = b;
|
||||
return b;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @see java.io.InputStream#read(byte[], int, int)
|
||||
*/
|
||||
public int read(byte[] b, int off, int len) throws IOException {
|
||||
if (truncated) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int n = is.read(b, off, len);
|
||||
for (int i = off; i < off + n; i++) {
|
||||
if (prev == '\r' && b[i] == '\n') {
|
||||
lineNumber++;
|
||||
}
|
||||
prev = b[i];
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.io.InputStream#read(byte[])
|
||||
*/
|
||||
public int read(byte[] b) throws IOException {
|
||||
return read(b, 0, b.length);
|
||||
}
|
||||
}
|
@ -1,100 +0,0 @@
|
||||
/****************************************************************
|
||||
* Licensed to the Apache Software Foundation (ASF) under one *
|
||||
* or more contributor license agreements. See the NOTICE file *
|
||||
* distributed with this work for additional information *
|
||||
* regarding copyright ownership. The ASF licenses this file *
|
||||
* to you 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. *
|
||||
****************************************************************/
|
||||
|
||||
package org.apache.james.mime4j;
|
||||
|
||||
import org.apache.james.mime4j.decoder.Base64InputStream;
|
||||
import org.apache.james.mime4j.decoder.QuotedPrintableInputStream;
|
||||
import org.apache.james.mime4j.field.Field;
|
||||
import org.apache.james.mime4j.message.Header;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Abstract implementation of ContentHandler that automates common
|
||||
* tasks. Currently performs header parsing and applies content-transfer
|
||||
* decoding to body parts.
|
||||
*
|
||||
*
|
||||
*/
|
||||
public abstract class SimpleContentHandler extends AbstractContentHandler {
|
||||
|
||||
/**
|
||||
* Called after headers are parsed.
|
||||
*/
|
||||
public abstract void headers(Header header);
|
||||
|
||||
/**
|
||||
* Called when the body of a discrete (non-multipart) entity is encountered.
|
||||
|
||||
* @param bd encapsulates the values (either read from the
|
||||
* message stream or, if not present, determined implictly
|
||||
* as described in the
|
||||
* MIME rfc:s) of the <code>Content-Type</code> and
|
||||
* <code>Content-Transfer-Encoding</code> header fields.
|
||||
* @param is the contents of the body. Base64 or quoted-printable
|
||||
* decoding will be applied transparently.
|
||||
* @throws IOException should be thrown on I/O errors.
|
||||
*/
|
||||
public abstract void bodyDecoded(BodyDescriptor bd, InputStream is) throws IOException;
|
||||
|
||||
|
||||
/* Implement introduced callbacks. */
|
||||
|
||||
private Header currHeader;
|
||||
|
||||
/**
|
||||
* @see org.apache.james.mime4j.AbstractContentHandler#startHeader()
|
||||
*/
|
||||
public final void startHeader() {
|
||||
currHeader = new Header();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.apache.james.mime4j.AbstractContentHandler#field(java.lang.String)
|
||||
*/
|
||||
public final void field(String fieldData) {
|
||||
currHeader.addField(Field.parse(fieldData));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.apache.james.mime4j.AbstractContentHandler#endHeader()
|
||||
*/
|
||||
public final void endHeader() {
|
||||
Header tmp = currHeader;
|
||||
currHeader = null;
|
||||
headers(tmp);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.apache.james.mime4j.AbstractContentHandler#body(org.apache.james.mime4j.BodyDescriptor, java.io.InputStream)
|
||||
*/
|
||||
public final void body(BodyDescriptor bd, InputStream is) throws IOException {
|
||||
if (bd.isBase64Encoded()) {
|
||||
bodyDecoded(bd, new Base64InputStream(is));
|
||||
}
|
||||
else if (bd.isQuotedPrintableEncoded()) {
|
||||
bodyDecoded(bd, new QuotedPrintableInputStream(is));
|
||||
}
|
||||
else {
|
||||
bodyDecoded(bd, is);
|
||||
}
|
||||
}
|
||||
}
|
286
src/org/apache/james/mime4j/codec/Base64InputStream.java
Normal file
286
src/org/apache/james/mime4j/codec/Base64InputStream.java
Normal file
@ -0,0 +1,286 @@
|
||||
/****************************************************************
|
||||
* Licensed to the Apache Software Foundation (ASF) under one *
|
||||
* or more contributor license agreements. See the NOTICE file *
|
||||
* distributed with this work for additional information *
|
||||
* regarding copyright ownership. The ASF licenses this file *
|
||||
* to you 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. *
|
||||
****************************************************************/
|
||||
|
||||
package org.apache.james.mime4j.codec;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import org.apache.james.mime4j.util.ByteArrayBuffer;
|
||||
|
||||
/**
|
||||
* Performs Base-64 decoding on an underlying stream.
|
||||
*/
|
||||
public class Base64InputStream extends InputStream {
|
||||
private static final int ENCODED_BUFFER_SIZE = 1536;
|
||||
|
||||
private static final int[] BASE64_DECODE = new int[256];
|
||||
|
||||
static {
|
||||
for (int i = 0; i < 256; i++)
|
||||
BASE64_DECODE[i] = -1;
|
||||
for (int i = 0; i < Base64OutputStream.BASE64_TABLE.length; i++)
|
||||
BASE64_DECODE[Base64OutputStream.BASE64_TABLE[i] & 0xff] = i;
|
||||
}
|
||||
|
||||
private static final byte BASE64_PAD = '=';
|
||||
|
||||
private static final int EOF = -1;
|
||||
|
||||
private final byte[] singleByte = new byte[1];
|
||||
|
||||
private final InputStream in;
|
||||
private final byte[] encoded;
|
||||
private final ByteArrayBuffer decodedBuf;
|
||||
|
||||
private int position = 0; // current index into encoded buffer
|
||||
private int size = 0; // current size of encoded buffer
|
||||
|
||||
private boolean closed = false;
|
||||
private boolean eof; // end of file or pad character reached
|
||||
|
||||
private final DecodeMonitor monitor;
|
||||
|
||||
public Base64InputStream(InputStream in, DecodeMonitor monitor) {
|
||||
this(ENCODED_BUFFER_SIZE, in, monitor);
|
||||
}
|
||||
|
||||
protected Base64InputStream(int bufsize, InputStream in, DecodeMonitor monitor) {
|
||||
if (in == null)
|
||||
throw new IllegalArgumentException();
|
||||
this.encoded = new byte[bufsize];
|
||||
this.decodedBuf = new ByteArrayBuffer(512);
|
||||
this.in = in;
|
||||
this.monitor = monitor;
|
||||
}
|
||||
|
||||
public Base64InputStream(InputStream in) {
|
||||
this(in, false);
|
||||
}
|
||||
|
||||
public Base64InputStream(InputStream in, boolean strict) {
|
||||
this(ENCODED_BUFFER_SIZE, in, strict ? DecodeMonitor.STRICT : DecodeMonitor.SILENT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
if (closed)
|
||||
throw new IOException("Stream has been closed");
|
||||
|
||||
while (true) {
|
||||
int bytes = read0(singleByte, 0, 1);
|
||||
if (bytes == EOF)
|
||||
return EOF;
|
||||
|
||||
if (bytes == 1)
|
||||
return singleByte[0] & 0xff;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(byte[] buffer) throws IOException {
|
||||
if (closed)
|
||||
throw new IOException("Stream has been closed");
|
||||
|
||||
if (buffer == null)
|
||||
throw new NullPointerException();
|
||||
|
||||
if (buffer.length == 0)
|
||||
return 0;
|
||||
|
||||
return read0(buffer, 0, buffer.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(byte[] buffer, int offset, int length) throws IOException {
|
||||
if (closed)
|
||||
throw new IOException("Stream has been closed");
|
||||
|
||||
if (buffer == null)
|
||||
throw new NullPointerException();
|
||||
|
||||
if (offset < 0 || length < 0 || offset + length > buffer.length)
|
||||
throw new IndexOutOfBoundsException();
|
||||
|
||||
if (length == 0)
|
||||
return 0;
|
||||
|
||||
return read0(buffer, offset, length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
if (closed)
|
||||
return;
|
||||
|
||||
closed = true;
|
||||
}
|
||||
|
||||
private int read0(final byte[] buffer, final int off, final int len) throws IOException {
|
||||
int from = off;
|
||||
int to = off + len;
|
||||
int index = off;
|
||||
|
||||
// check if a previous invocation left decoded content
|
||||
if (decodedBuf.length() > 0) {
|
||||
int chunk = Math.min(decodedBuf.length(), len);
|
||||
System.arraycopy(decodedBuf.buffer(), 0, buffer, index, chunk);
|
||||
decodedBuf.remove(0, chunk);
|
||||
index += chunk;
|
||||
}
|
||||
|
||||
// eof or pad reached?
|
||||
|
||||
if (eof)
|
||||
return index == from ? EOF : index - from;
|
||||
|
||||
// decode into given buffer
|
||||
|
||||
int data = 0; // holds decoded data; up to four sextets
|
||||
int sextets = 0; // number of sextets
|
||||
|
||||
while (index < to) {
|
||||
// make sure buffer not empty
|
||||
|
||||
while (position == size) {
|
||||
int n = in.read(encoded, 0, encoded.length);
|
||||
if (n == EOF) {
|
||||
eof = true;
|
||||
|
||||
if (sextets != 0) {
|
||||
// error in encoded data
|
||||
handleUnexpectedEof(sextets);
|
||||
}
|
||||
|
||||
return index == from ? EOF : index - from;
|
||||
} else if (n > 0) {
|
||||
position = 0;
|
||||
size = n;
|
||||
} else {
|
||||
assert n == 0;
|
||||
}
|
||||
}
|
||||
|
||||
// decode buffer
|
||||
|
||||
while (position < size && index < to) {
|
||||
int value = encoded[position++] & 0xff;
|
||||
|
||||
if (value == BASE64_PAD) {
|
||||
index = decodePad(data, sextets, buffer, index, to);
|
||||
return index - from;
|
||||
}
|
||||
|
||||
int decoded = BASE64_DECODE[value];
|
||||
if (decoded < 0) { // -1: not a base64 char
|
||||
if (value != 0x0D && value != 0x0A && value != 0x20) {
|
||||
if (monitor.warn("Unexpected base64 byte: "+(byte) value, "ignoring."))
|
||||
throw new IOException("Unexpected base64 byte");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
data = (data << 6) | decoded;
|
||||
sextets++;
|
||||
|
||||
if (sextets == 4) {
|
||||
sextets = 0;
|
||||
|
||||
byte b1 = (byte) (data >>> 16);
|
||||
byte b2 = (byte) (data >>> 8);
|
||||
byte b3 = (byte) data;
|
||||
|
||||
if (index < to - 2) {
|
||||
buffer[index++] = b1;
|
||||
buffer[index++] = b2;
|
||||
buffer[index++] = b3;
|
||||
} else {
|
||||
if (index < to - 1) {
|
||||
buffer[index++] = b1;
|
||||
buffer[index++] = b2;
|
||||
decodedBuf.append(b3);
|
||||
} else if (index < to) {
|
||||
buffer[index++] = b1;
|
||||
decodedBuf.append(b2);
|
||||
decodedBuf.append(b3);
|
||||
} else {
|
||||
decodedBuf.append(b1);
|
||||
decodedBuf.append(b2);
|
||||
decodedBuf.append(b3);
|
||||
}
|
||||
|
||||
assert index == to;
|
||||
return to - from;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assert sextets == 0;
|
||||
assert index == to;
|
||||
return to - from;
|
||||
}
|
||||
|
||||
private int decodePad(int data, int sextets, final byte[] buffer,
|
||||
int index, final int end) throws IOException {
|
||||
eof = true;
|
||||
|
||||
if (sextets == 2) {
|
||||
// one byte encoded as "XY=="
|
||||
|
||||
byte b = (byte) (data >>> 4);
|
||||
if (index < end) {
|
||||
buffer[index++] = b;
|
||||
} else {
|
||||
decodedBuf.append(b);
|
||||
}
|
||||
} else if (sextets == 3) {
|
||||
// two bytes encoded as "XYZ="
|
||||
|
||||
byte b1 = (byte) (data >>> 10);
|
||||
byte b2 = (byte) ((data >>> 2) & 0xFF);
|
||||
|
||||
if (index < end - 1) {
|
||||
buffer[index++] = b1;
|
||||
buffer[index++] = b2;
|
||||
} else if (index < end) {
|
||||
buffer[index++] = b1;
|
||||
decodedBuf.append(b2);
|
||||
} else {
|
||||
decodedBuf.append(b1);
|
||||
decodedBuf.append(b2);
|
||||
}
|
||||
} else {
|
||||
// error in encoded data
|
||||
handleUnexpecedPad(sextets);
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
private void handleUnexpectedEof(int sextets) throws IOException {
|
||||
if (monitor.warn("Unexpected end of BASE64 stream", "dropping " + sextets + " sextet(s)"))
|
||||
throw new IOException("Unexpected end of BASE64 stream");
|
||||
}
|
||||
|
||||
private void handleUnexpecedPad(int sextets) throws IOException {
|
||||
if (monitor.warn("Unexpected padding character", "dropping " + sextets + " sextet(s)"))
|
||||
throw new IOException("Unexpected padding character");
|
||||
}
|
||||
}
|
321
src/org/apache/james/mime4j/codec/Base64OutputStream.java
Normal file
321
src/org/apache/james/mime4j/codec/Base64OutputStream.java
Normal file
@ -0,0 +1,321 @@
|
||||
/****************************************************************
|
||||
* Licensed to the Apache Software Foundation (ASF) under one *
|
||||
* or more contributor license agreements. See the NOTICE file *
|
||||
* distributed with this work for additional information *
|
||||
* regarding copyright ownership. The ASF licenses this file *
|
||||
* to you 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. *
|
||||
****************************************************************/
|
||||
|
||||
package org.apache.james.mime4j.codec;
|
||||
|
||||
import java.io.FilterOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* This class implements section <cite>6.8. Base64 Content-Transfer-Encoding</cite>
|
||||
* from RFC 2045 <cite>Multipurpose Internet Mail Extensions (MIME) Part One:
|
||||
* Format of Internet Message Bodies</cite> by Freed and Borenstein.
|
||||
* <p>
|
||||
* Code is based on Base64 and Base64OutputStream code from Commons-Codec 1.4.
|
||||
*
|
||||
* @see <a href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045</a>
|
||||
*/
|
||||
public class Base64OutputStream extends FilterOutputStream {
|
||||
|
||||
// Default line length per RFC 2045 section 6.8.
|
||||
private static final int DEFAULT_LINE_LENGTH = 76;
|
||||
|
||||
// CRLF line separator per RFC 2045 section 2.1.
|
||||
private static final byte[] CRLF_SEPARATOR = { '\r', '\n' };
|
||||
|
||||
// This array is a lookup table that translates 6-bit positive integer index
|
||||
// values into their "Base64 Alphabet" equivalents as specified in Table 1
|
||||
// of RFC 2045.
|
||||
static final byte[] BASE64_TABLE = { 'A', 'B', 'C', 'D', 'E', 'F',
|
||||
'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S',
|
||||
'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
|
||||
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's',
|
||||
't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5',
|
||||
'6', '7', '8', '9', '+', '/' };
|
||||
|
||||
// Byte used to pad output.
|
||||
private static final byte BASE64_PAD = '=';
|
||||
|
||||
// This set contains all base64 characters including the pad character. Used
|
||||
// solely to check if a line separator contains any of these characters.
|
||||
private static final Set<Byte> BASE64_CHARS = new HashSet<Byte>();
|
||||
|
||||
static {
|
||||
for (byte b : BASE64_TABLE) {
|
||||
BASE64_CHARS.add(b);
|
||||
}
|
||||
BASE64_CHARS.add(BASE64_PAD);
|
||||
}
|
||||
|
||||
// Mask used to extract 6 bits
|
||||
private static final int MASK_6BITS = 0x3f;
|
||||
|
||||
private static final int ENCODED_BUFFER_SIZE = 2048;
|
||||
|
||||
private final byte[] singleByte = new byte[1];
|
||||
|
||||
private final int lineLength;
|
||||
private final byte[] lineSeparator;
|
||||
|
||||
private boolean closed = false;
|
||||
|
||||
private final byte[] encoded;
|
||||
private int position = 0;
|
||||
|
||||
private int data = 0;
|
||||
private int modulus = 0;
|
||||
|
||||
private int linePosition = 0;
|
||||
|
||||
/**
|
||||
* Creates a <code>Base64OutputStream</code> that writes the encoded data
|
||||
* to the given output stream using the default line length (76) and line
|
||||
* separator (CRLF).
|
||||
*
|
||||
* @param out
|
||||
* underlying output stream.
|
||||
*/
|
||||
public Base64OutputStream(OutputStream out) {
|
||||
this(out, DEFAULT_LINE_LENGTH, CRLF_SEPARATOR);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a <code>Base64OutputStream</code> that writes the encoded data
|
||||
* to the given output stream using the given line length and the default
|
||||
* line separator (CRLF).
|
||||
* <p>
|
||||
* The given line length will be rounded up to the nearest multiple of 4. If
|
||||
* the line length is zero then the output will not be split into lines.
|
||||
*
|
||||
* @param out
|
||||
* underlying output stream.
|
||||
* @param lineLength
|
||||
* desired line length.
|
||||
*/
|
||||
public Base64OutputStream(OutputStream out, int lineLength) {
|
||||
this(out, lineLength, CRLF_SEPARATOR);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a <code>Base64OutputStream</code> that writes the encoded data
|
||||
* to the given output stream using the given line length and line
|
||||
* separator.
|
||||
* <p>
|
||||
* The given line length will be rounded up to the nearest multiple of 4. If
|
||||
* the line length is zero then the output will not be split into lines and
|
||||
* the line separator is ignored.
|
||||
* <p>
|
||||
* The line separator must not include characters from the BASE64 alphabet
|
||||
* (including the padding character <code>=</code>).
|
||||
*
|
||||
* @param out
|
||||
* underlying output stream.
|
||||
* @param lineLength
|
||||
* desired line length.
|
||||
* @param lineSeparator
|
||||
* line separator to use.
|
||||
*/
|
||||
public Base64OutputStream(OutputStream out, int lineLength,
|
||||
byte[] lineSeparator) {
|
||||
super(out);
|
||||
|
||||
if (out == null)
|
||||
throw new IllegalArgumentException();
|
||||
if (lineLength < 0)
|
||||
throw new IllegalArgumentException();
|
||||
checkLineSeparator(lineSeparator);
|
||||
|
||||
this.lineLength = lineLength;
|
||||
this.lineSeparator = new byte[lineSeparator.length];
|
||||
System.arraycopy(lineSeparator, 0, this.lineSeparator, 0,
|
||||
lineSeparator.length);
|
||||
|
||||
this.encoded = new byte[ENCODED_BUFFER_SIZE];
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void write(final int b) throws IOException {
|
||||
if (closed)
|
||||
throw new IOException("Base64OutputStream has been closed");
|
||||
|
||||
singleByte[0] = (byte) b;
|
||||
write0(singleByte, 0, 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void write(final byte[] buffer) throws IOException {
|
||||
if (closed)
|
||||
throw new IOException("Base64OutputStream has been closed");
|
||||
|
||||
if (buffer == null)
|
||||
throw new NullPointerException();
|
||||
|
||||
if (buffer.length == 0)
|
||||
return;
|
||||
|
||||
write0(buffer, 0, buffer.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void write(final byte[] buffer, final int offset,
|
||||
final int length) throws IOException {
|
||||
if (closed)
|
||||
throw new IOException("Base64OutputStream has been closed");
|
||||
|
||||
if (buffer == null)
|
||||
throw new NullPointerException();
|
||||
|
||||
if (offset < 0 || length < 0 || offset + length > buffer.length)
|
||||
throw new IndexOutOfBoundsException();
|
||||
|
||||
if (length == 0)
|
||||
return;
|
||||
|
||||
write0(buffer, offset, offset + length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flush() throws IOException {
|
||||
if (closed)
|
||||
throw new IOException("Base64OutputStream has been closed");
|
||||
|
||||
flush0();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
if (closed)
|
||||
return;
|
||||
|
||||
closed = true;
|
||||
close0();
|
||||
}
|
||||
|
||||
private void write0(final byte[] buffer, final int from, final int to)
|
||||
throws IOException {
|
||||
for (int i = from; i < to; i++) {
|
||||
data = (data << 8) | (buffer[i] & 0xff);
|
||||
|
||||
if (++modulus == 3) {
|
||||
modulus = 0;
|
||||
|
||||
// write line separator if necessary
|
||||
|
||||
if (lineLength > 0 && linePosition >= lineLength) {
|
||||
// writeLineSeparator() inlined for performance reasons
|
||||
|
||||
linePosition = 0;
|
||||
|
||||
if (encoded.length - position < lineSeparator.length)
|
||||
flush0();
|
||||
|
||||
for (byte ls : lineSeparator)
|
||||
encoded[position++] = ls;
|
||||
}
|
||||
|
||||
// encode data into 4 bytes
|
||||
|
||||
if (encoded.length - position < 4)
|
||||
flush0();
|
||||
|
||||
encoded[position++] = BASE64_TABLE[(data >> 18) & MASK_6BITS];
|
||||
encoded[position++] = BASE64_TABLE[(data >> 12) & MASK_6BITS];
|
||||
encoded[position++] = BASE64_TABLE[(data >> 6) & MASK_6BITS];
|
||||
encoded[position++] = BASE64_TABLE[data & MASK_6BITS];
|
||||
|
||||
linePosition += 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void flush0() throws IOException {
|
||||
if (position > 0) {
|
||||
out.write(encoded, 0, position);
|
||||
position = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private void close0() throws IOException {
|
||||
if (modulus != 0)
|
||||
writePad();
|
||||
|
||||
// write line separator at the end of the encoded data
|
||||
|
||||
if (lineLength > 0 && linePosition > 0) {
|
||||
writeLineSeparator();
|
||||
}
|
||||
|
||||
flush0();
|
||||
}
|
||||
|
||||
private void writePad() throws IOException {
|
||||
// write line separator if necessary
|
||||
|
||||
if (lineLength > 0 && linePosition >= lineLength) {
|
||||
writeLineSeparator();
|
||||
}
|
||||
|
||||
// encode data into 4 bytes
|
||||
|
||||
if (encoded.length - position < 4)
|
||||
flush0();
|
||||
|
||||
if (modulus == 1) {
|
||||
encoded[position++] = BASE64_TABLE[(data >> 2) & MASK_6BITS];
|
||||
encoded[position++] = BASE64_TABLE[(data << 4) & MASK_6BITS];
|
||||
encoded[position++] = BASE64_PAD;
|
||||
encoded[position++] = BASE64_PAD;
|
||||
} else {
|
||||
assert modulus == 2;
|
||||
encoded[position++] = BASE64_TABLE[(data >> 10) & MASK_6BITS];
|
||||
encoded[position++] = BASE64_TABLE[(data >> 4) & MASK_6BITS];
|
||||
encoded[position++] = BASE64_TABLE[(data << 2) & MASK_6BITS];
|
||||
encoded[position++] = BASE64_PAD;
|
||||
}
|
||||
|
||||
linePosition += 4;
|
||||
}
|
||||
|
||||
private void writeLineSeparator() throws IOException {
|
||||
linePosition = 0;
|
||||
|
||||
if (encoded.length - position < lineSeparator.length)
|
||||
flush0();
|
||||
|
||||
for (byte ls : lineSeparator)
|
||||
encoded[position++] = ls;
|
||||
}
|
||||
|
||||
private void checkLineSeparator(byte[] lineSeparator) {
|
||||
if (lineSeparator.length > ENCODED_BUFFER_SIZE)
|
||||
throw new IllegalArgumentException("line separator length exceeds "
|
||||
+ ENCODED_BUFFER_SIZE);
|
||||
|
||||
for (byte b : lineSeparator) {
|
||||
if (BASE64_CHARS.contains(b)) {
|
||||
throw new IllegalArgumentException(
|
||||
"line separator must not contain base64 character '"
|
||||
+ (char) (b & 0xff) + "'");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
108
src/org/apache/james/mime4j/codec/CodecUtil.java
Normal file
108
src/org/apache/james/mime4j/codec/CodecUtil.java
Normal file
@ -0,0 +1,108 @@
|
||||
/****************************************************************
|
||||
* Licensed to the Apache Software Foundation (ASF) under one *
|
||||
* or more contributor license agreements. See the NOTICE file *
|
||||
* distributed with this work for additional information *
|
||||
* regarding copyright ownership. The ASF licenses this file *
|
||||
* to you 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. *
|
||||
****************************************************************/
|
||||
|
||||
package org.apache.james.mime4j.codec;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
/**
|
||||
* Utility methods related to codecs.
|
||||
*/
|
||||
public class CodecUtil {
|
||||
|
||||
static final int DEFAULT_ENCODING_BUFFER_SIZE = 1024;
|
||||
|
||||
/**
|
||||
* Copies the contents of one stream to the other.
|
||||
* @param in not null
|
||||
* @param out not null
|
||||
* @throws IOException
|
||||
*/
|
||||
public static void copy(final InputStream in, final OutputStream out) throws IOException {
|
||||
final byte[] buffer = new byte[DEFAULT_ENCODING_BUFFER_SIZE];
|
||||
int inputLength;
|
||||
while (-1 != (inputLength = in.read(buffer))) {
|
||||
out.write(buffer, 0, inputLength);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes the given stream using Quoted-Printable.
|
||||
* This assumes that stream is binary and therefore escapes
|
||||
* all line endings.
|
||||
* @param in not null
|
||||
* @param out not null
|
||||
* @throws IOException
|
||||
*/
|
||||
public static void encodeQuotedPrintableBinary(final InputStream in, final OutputStream out) throws IOException {
|
||||
QuotedPrintableOutputStream qpOut = new QuotedPrintableOutputStream(out, true);
|
||||
copy(in, qpOut);
|
||||
qpOut.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes the given stream using Quoted-Printable.
|
||||
* This assumes that stream is text and therefore does not escape
|
||||
* all line endings.
|
||||
* @param in not null
|
||||
* @param out not null
|
||||
* @throws IOException
|
||||
*/
|
||||
public static void encodeQuotedPrintable(final InputStream in, final OutputStream out) throws IOException {
|
||||
QuotedPrintableOutputStream qpOut = new QuotedPrintableOutputStream(out, false);
|
||||
copy(in, qpOut);
|
||||
qpOut.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes the given stream using base64.
|
||||
*
|
||||
* @param in not null
|
||||
* @param out not null
|
||||
* @throws IOException if an I/O error occurs
|
||||
*/
|
||||
public static void encodeBase64(final InputStream in, final OutputStream out) throws IOException {
|
||||
Base64OutputStream b64Out = new Base64OutputStream(out);
|
||||
copy(in, b64Out);
|
||||
b64Out.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps the given stream in a Quoted-Printable encoder.
|
||||
* @param out not null
|
||||
* @return encoding outputstream
|
||||
* @throws IOException
|
||||
*/
|
||||
public static OutputStream wrapQuotedPrintable(final OutputStream out, boolean binary) throws IOException {
|
||||
return new QuotedPrintableOutputStream(out, binary);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps the given stream in a Base64 encoder.
|
||||
* @param out not null
|
||||
* @return encoding outputstream
|
||||
* @throws IOException
|
||||
*/
|
||||
public static OutputStream wrapBase64(final OutputStream out) throws IOException {
|
||||
return new Base64OutputStream(out);
|
||||
}
|
||||
|
||||
}
|
65
src/org/apache/james/mime4j/codec/DecodeMonitor.java
Normal file
65
src/org/apache/james/mime4j/codec/DecodeMonitor.java
Normal file
@ -0,0 +1,65 @@
|
||||
/****************************************************************
|
||||
* Licensed to the Apache Software Foundation (ASF) under one *
|
||||
* or more contributor license agreements. See the NOTICE file *
|
||||
* distributed with this work for additional information *
|
||||
* regarding copyright ownership. The ASF licenses this file *
|
||||
* to you 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. *
|
||||
****************************************************************/
|
||||
|
||||
package org.apache.james.mime4j.codec;
|
||||
|
||||
|
||||
/**
|
||||
* This class is used to drive how decoder/parser should deal with malformed
|
||||
* and unexpected data.
|
||||
*
|
||||
* 2 basic implementations are provided:
|
||||
* STRICT return "true" on any occourence.
|
||||
* SILENT ignores any problem.
|
||||
*
|
||||
* @see org.apache.james.mime4j.field.LoggingMonitor for an example
|
||||
* about logging malformations via Commons-logging.
|
||||
*/
|
||||
public class DecodeMonitor {
|
||||
|
||||
/**
|
||||
* The STRICT monitor throws an exception on every event.
|
||||
*/
|
||||
public static final DecodeMonitor STRICT = new DecodeMonitor() {
|
||||
|
||||
@Override
|
||||
public boolean warn(String error, String dropDesc) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isListening() {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* The SILENT monitor ignore requests.
|
||||
*/
|
||||
public static final DecodeMonitor SILENT = new DecodeMonitor();
|
||||
|
||||
public boolean warn(String error, String dropDesc) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isListening() {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
260
src/org/apache/james/mime4j/codec/DecoderUtil.java
Normal file
260
src/org/apache/james/mime4j/codec/DecoderUtil.java
Normal file
@ -0,0 +1,260 @@
|
||||
/****************************************************************
|
||||
* Licensed to the Apache Software Foundation (ASF) under one *
|
||||
* or more contributor license agreements. See the NOTICE file *
|
||||
* distributed with this work for additional information *
|
||||
* regarding copyright ownership. The ASF licenses this file *
|
||||
* to you 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. *
|
||||
****************************************************************/
|
||||
|
||||
package org.apache.james.mime4j.codec;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.apache.james.mime4j.util.CharsetUtil;
|
||||
|
||||
/**
|
||||
* Static methods for decoding strings, byte arrays and encoded words.
|
||||
*/
|
||||
public class DecoderUtil {
|
||||
|
||||
private static final Pattern PATTERN_ENCODED_WORD = Pattern.compile(
|
||||
"(.*?)=\\?([^\\?]+?)\\?(\\w)\\?([^\\?]+?)\\?=", Pattern.DOTALL);
|
||||
|
||||
/**
|
||||
* Decodes a string containing quoted-printable encoded data.
|
||||
*
|
||||
* @param s the string to decode.
|
||||
* @return the decoded bytes.
|
||||
*/
|
||||
private static byte[] decodeQuotedPrintable(String s, DecodeMonitor monitor) {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
|
||||
try {
|
||||
byte[] bytes = s.getBytes("US-ASCII");
|
||||
|
||||
QuotedPrintableInputStream is = new QuotedPrintableInputStream(
|
||||
new ByteArrayInputStream(bytes), monitor);
|
||||
|
||||
int b = 0;
|
||||
while ((b = is.read()) != -1) {
|
||||
baos.write(b);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
// This should never happen!
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
|
||||
return baos.toByteArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes a string containing base64 encoded data.
|
||||
*
|
||||
* @param s the string to decode.
|
||||
* @param monitor
|
||||
* @return the decoded bytes.
|
||||
*/
|
||||
private static byte[] decodeBase64(String s, DecodeMonitor monitor) {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
|
||||
try {
|
||||
byte[] bytes = s.getBytes("US-ASCII");
|
||||
|
||||
Base64InputStream is = new Base64InputStream(
|
||||
new ByteArrayInputStream(bytes), monitor);
|
||||
|
||||
int b = 0;
|
||||
while ((b = is.read()) != -1) {
|
||||
baos.write(b);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
// This should never happen!
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
|
||||
return baos.toByteArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes an encoded text encoded with the 'B' encoding (described in
|
||||
* RFC 2047) found in a header field body.
|
||||
*
|
||||
* @param encodedText the encoded text to decode.
|
||||
* @param charset the Java charset to use.
|
||||
* @param monitor
|
||||
* @return the decoded string.
|
||||
* @throws UnsupportedEncodingException if the given Java charset isn't
|
||||
* supported.
|
||||
*/
|
||||
static String decodeB(String encodedText, String charset, DecodeMonitor monitor)
|
||||
throws UnsupportedEncodingException {
|
||||
byte[] decodedBytes = decodeBase64(encodedText, monitor);
|
||||
return new String(decodedBytes, charset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes an encoded text encoded with the 'Q' encoding (described in
|
||||
* RFC 2047) found in a header field body.
|
||||
*
|
||||
* @param encodedText the encoded text to decode.
|
||||
* @param charset the Java charset to use.
|
||||
* @return the decoded string.
|
||||
* @throws UnsupportedEncodingException if the given Java charset isn't
|
||||
* supported.
|
||||
*/
|
||||
static String decodeQ(String encodedText, String charset, DecodeMonitor monitor)
|
||||
throws UnsupportedEncodingException {
|
||||
encodedText = replaceUnderscores(encodedText);
|
||||
|
||||
byte[] decodedBytes = decodeQuotedPrintable(encodedText, monitor);
|
||||
return new String(decodedBytes, charset);
|
||||
}
|
||||
|
||||
static String decodeEncodedWords(String body) {
|
||||
return decodeEncodedWords(body, DecodeMonitor.SILENT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes a string containing encoded words as defined by RFC 2047. Encoded
|
||||
* words have the form =?charset?enc?encoded-text?= where enc is either 'Q'
|
||||
* or 'q' for quoted-printable and 'B' or 'b' for base64.
|
||||
*
|
||||
* @param body the string to decode
|
||||
* @param monitor the DecodeMonitor to be used.
|
||||
* @return the decoded string.
|
||||
* @throws IllegalArgumentException only if the DecodeMonitor strategy throws it (Strict parsing)
|
||||
*/
|
||||
public static String decodeEncodedWords(String body, DecodeMonitor monitor) throws IllegalArgumentException {
|
||||
int tailIndex = 0;
|
||||
boolean lastMatchValid = false;
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
for (Matcher matcher = PATTERN_ENCODED_WORD.matcher(body); matcher.find();) {
|
||||
String separator = matcher.group(1);
|
||||
String mimeCharset = matcher.group(2);
|
||||
String encoding = matcher.group(3);
|
||||
String encodedText = matcher.group(4);
|
||||
|
||||
String decoded = null;
|
||||
decoded = tryDecodeEncodedWord(mimeCharset, encoding, encodedText, monitor);
|
||||
if (decoded == null) {
|
||||
sb.append(matcher.group(0));
|
||||
} else {
|
||||
if (!lastMatchValid || !CharsetUtil.isWhitespace(separator)) {
|
||||
sb.append(separator);
|
||||
}
|
||||
sb.append(decoded);
|
||||
}
|
||||
|
||||
tailIndex = matcher.end();
|
||||
lastMatchValid = decoded != null;
|
||||
}
|
||||
|
||||
if (tailIndex == 0) {
|
||||
return body;
|
||||
} else {
|
||||
sb.append(body.substring(tailIndex));
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
||||
// return null on error
|
||||
private static String tryDecodeEncodedWord(final String mimeCharset,
|
||||
final String encoding, final String encodedText, DecodeMonitor monitor) throws IllegalArgumentException {
|
||||
String charset = CharsetUtil.toJavaCharset(mimeCharset);
|
||||
if (charset == null) {
|
||||
monitor(monitor, mimeCharset, encoding, encodedText, "leaving word encoded",
|
||||
"Mime charser '", mimeCharset, "' doesn't have a corresponding Java charset");
|
||||
return null;
|
||||
} else if (!CharsetUtil.isDecodingSupported(charset)) {
|
||||
monitor(monitor, mimeCharset, encoding, encodedText, "leaving word encoded",
|
||||
"Current JDK doesn't support decoding of charset '", charset,
|
||||
"' - MIME charset '", mimeCharset, "' in encoded word");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (encodedText.length() == 0) {
|
||||
monitor(monitor, mimeCharset, encoding, encodedText, "leaving word encoded",
|
||||
"Missing encoded text in encoded word");
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
if (encoding.equalsIgnoreCase("Q")) {
|
||||
return DecoderUtil.decodeQ(encodedText, charset, monitor);
|
||||
} else if (encoding.equalsIgnoreCase("B")) {
|
||||
return DecoderUtil.decodeB(encodedText, charset, monitor);
|
||||
} else {
|
||||
monitor(monitor, mimeCharset, encoding, encodedText, "leaving word encoded",
|
||||
"Warning: Unknown encoding in encoded word");
|
||||
return null;
|
||||
}
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
// should not happen because of isDecodingSupported check above
|
||||
monitor(monitor, mimeCharset, encoding, encodedText, "leaving word encoded",
|
||||
"Unsupported encoding (", e.getMessage(), ") in encoded word");
|
||||
return null;
|
||||
} catch (RuntimeException e) {
|
||||
monitor(monitor, mimeCharset, encoding, encodedText, "leaving word encoded",
|
||||
"Could not decode (", e.getMessage(), ") encoded word");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static void monitor(DecodeMonitor monitor, String mimeCharset, String encoding,
|
||||
String encodedText, String dropDesc, String... strings) throws IllegalArgumentException {
|
||||
if (monitor.isListening()) {
|
||||
String encodedWord = recombine(mimeCharset, encoding, encodedText);
|
||||
StringBuilder text = new StringBuilder();
|
||||
for (String str : strings) {
|
||||
text.append(str);
|
||||
}
|
||||
text.append(" (");
|
||||
text.append(encodedWord);
|
||||
text.append(")");
|
||||
String exceptionDesc = text.toString();
|
||||
if (monitor.warn(exceptionDesc, dropDesc))
|
||||
throw new IllegalArgumentException(text.toString());
|
||||
}
|
||||
}
|
||||
|
||||
private static String recombine(final String mimeCharset,
|
||||
final String encoding, final String encodedText) {
|
||||
return "=?" + mimeCharset + "?" + encoding + "?" + encodedText + "?=";
|
||||
}
|
||||
|
||||
// Replace _ with =20
|
||||
private static String replaceUnderscores(String str) {
|
||||
// probably faster than String#replace(CharSequence, CharSequence)
|
||||
|
||||
StringBuilder sb = new StringBuilder(128);
|
||||
|
||||
for (int i = 0; i < str.length(); i++) {
|
||||
char c = str.charAt(i);
|
||||
if (c == '_') {
|
||||
sb.append("=20");
|
||||
} else {
|
||||
sb.append(c);
|
||||
}
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
@ -26,31 +26,14 @@ import java.util.Locale;
|
||||
|
||||
import org.apache.james.mime4j.util.CharsetUtil;
|
||||
|
||||
/**
|
||||
* ANDROID: THIS CLASS IS COPIED FROM A NEWER VERSION OF MIME4J
|
||||
*/
|
||||
|
||||
/**
|
||||
* Static methods for encoding header field values. This includes encoded-words
|
||||
* as defined in <a href='http://www.faqs.org/rfcs/rfc2047.html'>RFC 2047</a>
|
||||
* or display-names of an e-mail address, for example.
|
||||
*
|
||||
*/
|
||||
public class EncoderUtil {
|
||||
|
||||
// This array is a lookup table that translates 6-bit positive integer index
|
||||
// values into their "Base64 Alphabet" equivalents as specified in Table 1
|
||||
// of RFC 2045.
|
||||
// ANDROID: THIS TABLE IS COPIED FROM BASE64OUTPUTSTREAM
|
||||
static final byte[] BASE64_TABLE = { 'A', 'B', 'C', 'D', 'E', 'F',
|
||||
'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S',
|
||||
'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
|
||||
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's',
|
||||
't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5',
|
||||
'6', '7', '8', '9', '+', '/' };
|
||||
|
||||
// Byte used to pad output.
|
||||
private static final byte BASE64_PAD = '=';
|
||||
private static final byte[] BASE64_TABLE = Base64OutputStream.BASE64_TABLE;
|
||||
private static final char BASE64_PAD = '=';
|
||||
|
||||
private static final BitSet Q_REGULAR_CHARS = initChars("=_?");
|
||||
|
||||
@ -374,14 +357,14 @@ public class EncoderUtil {
|
||||
sb.append((char) BASE64_TABLE[data >> 18 & 0x3f]);
|
||||
sb.append((char) BASE64_TABLE[data >> 12 & 0x3f]);
|
||||
sb.append((char) BASE64_TABLE[data >> 6 & 0x3f]);
|
||||
sb.append((char) BASE64_PAD);
|
||||
sb.append(BASE64_PAD);
|
||||
|
||||
} else if (idx == end - 1) {
|
||||
int data = (bytes[idx] & 0xff) << 16;
|
||||
sb.append((char) BASE64_TABLE[data >> 18 & 0x3f]);
|
||||
sb.append((char) BASE64_TABLE[data >> 12 & 0x3f]);
|
||||
sb.append((char) BASE64_PAD);
|
||||
sb.append((char) BASE64_PAD);
|
||||
sb.append(BASE64_PAD);
|
||||
sb.append(BASE64_PAD);
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
@ -518,14 +501,12 @@ public class EncoderUtil {
|
||||
if (totalLength <= ENCODED_WORD_MAX_LENGTH - usedCharacters) {
|
||||
return prefix + encodeB(bytes) + ENC_WORD_SUFFIX;
|
||||
} else {
|
||||
int splitOffset = text.offsetByCodePoints(text.length() / 2, -1);
|
||||
|
||||
String part1 = text.substring(0, splitOffset);
|
||||
String part1 = text.substring(0, text.length() / 2);
|
||||
byte[] bytes1 = encode(part1, charset);
|
||||
String word1 = encodeB(prefix, part1, usedCharacters, charset,
|
||||
bytes1);
|
||||
|
||||
String part2 = text.substring(splitOffset);
|
||||
String part2 = text.substring(text.length() / 2);
|
||||
byte[] bytes2 = encode(part2, charset);
|
||||
String word2 = encodeB(prefix, part2, 0, charset, bytes2);
|
||||
|
||||
@ -546,14 +527,12 @@ public class EncoderUtil {
|
||||
if (totalLength <= ENCODED_WORD_MAX_LENGTH - usedCharacters) {
|
||||
return prefix + encodeQ(bytes, usage) + ENC_WORD_SUFFIX;
|
||||
} else {
|
||||
int splitOffset = text.offsetByCodePoints(text.length() / 2, -1);
|
||||
|
||||
String part1 = text.substring(0, splitOffset);
|
||||
String part1 = text.substring(0, text.length() / 2);
|
||||
byte[] bytes1 = encode(part1, charset);
|
||||
String word1 = encodeQ(prefix, part1, usage, usedCharacters,
|
||||
charset, bytes1);
|
||||
|
||||
String part2 = text.substring(splitOffset);
|
||||
String part2 = text.substring(text.length() / 2);
|
||||
byte[] bytes2 = encode(part2, charset);
|
||||
String word2 = encodeQ(prefix, part2, usage, 0, charset, bytes2);
|
||||
|
||||
|
@ -1,271 +0,0 @@
|
||||
/****************************************************************
|
||||
* Licensed to the Apache Software Foundation (ASF) under one *
|
||||
* or more contributor license agreements. See the NOTICE file *
|
||||
* distributed with this work for additional information *
|
||||
* regarding copyright ownership. The ASF licenses this file *
|
||||
* to you 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. *
|
||||
****************************************************************/
|
||||
|
||||
package org.apache.james.mime4j.codec;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
// Taken from Apache Mime4j 0.6
|
||||
|
||||
final class QuotedPrintableEncoder
|
||||
{
|
||||
private static final byte TAB = 0x09;
|
||||
private static final byte SPACE = 0x20;
|
||||
private static final byte EQUALS = 0x3D;
|
||||
private static final byte CR = 0x0D;
|
||||
private static final byte LF = 0x0A;
|
||||
private static final byte QUOTED_PRINTABLE_LAST_PLAIN = 0x7E;
|
||||
private static final int QUOTED_PRINTABLE_MAX_LINE_LENGTH = 76;
|
||||
private static final int QUOTED_PRINTABLE_OCTETS_PER_ESCAPE = 3;
|
||||
private static final byte[] HEX_DIGITS = { '0', '1', '2', '3', '4', '5',
|
||||
'6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
|
||||
|
||||
private final byte[] inBuffer;
|
||||
private final byte[] outBuffer;
|
||||
private final boolean binary;
|
||||
|
||||
private boolean pendingSpace;
|
||||
private boolean pendingTab;
|
||||
private boolean pendingCR;
|
||||
private int nextSoftBreak;
|
||||
private int outputIndex;
|
||||
private OutputStream out;
|
||||
|
||||
public QuotedPrintableEncoder(int bufferSize, boolean binary)
|
||||
{
|
||||
inBuffer = new byte[bufferSize];
|
||||
outBuffer = new byte[3 * bufferSize];
|
||||
outputIndex = 0;
|
||||
nextSoftBreak = QUOTED_PRINTABLE_MAX_LINE_LENGTH + 1;
|
||||
out = null;
|
||||
this.binary = binary;
|
||||
pendingSpace = false;
|
||||
pendingTab = false;
|
||||
pendingCR = false;
|
||||
}
|
||||
|
||||
void initEncoding(final OutputStream out)
|
||||
{
|
||||
this.out = out;
|
||||
pendingSpace = false;
|
||||
pendingTab = false;
|
||||
pendingCR = false;
|
||||
nextSoftBreak = QUOTED_PRINTABLE_MAX_LINE_LENGTH + 1;
|
||||
}
|
||||
|
||||
void encodeChunk(byte[] buffer, int off, int len) throws IOException
|
||||
{
|
||||
for (int inputIndex = off; inputIndex < len + off; inputIndex++)
|
||||
{
|
||||
encode(buffer[inputIndex]);
|
||||
}
|
||||
}
|
||||
|
||||
void completeEncoding() throws IOException
|
||||
{
|
||||
writePending();
|
||||
flushOutput();
|
||||
}
|
||||
|
||||
public void encode(final InputStream in, final OutputStream out)
|
||||
throws IOException
|
||||
{
|
||||
initEncoding(out);
|
||||
int inputLength;
|
||||
while ((inputLength = in.read(inBuffer)) > -1)
|
||||
{
|
||||
encodeChunk(inBuffer, 0, inputLength);
|
||||
}
|
||||
completeEncoding();
|
||||
}
|
||||
|
||||
private void writePending() throws IOException
|
||||
{
|
||||
if (pendingSpace)
|
||||
{
|
||||
plain(SPACE);
|
||||
}
|
||||
else if (pendingTab)
|
||||
{
|
||||
plain(TAB);
|
||||
}
|
||||
else if (pendingCR)
|
||||
{
|
||||
plain(CR);
|
||||
}
|
||||
clearPending();
|
||||
}
|
||||
|
||||
private void clearPending() throws IOException
|
||||
{
|
||||
pendingSpace = false;
|
||||
pendingTab = false;
|
||||
pendingCR = false;
|
||||
}
|
||||
|
||||
private void encode(byte next) throws IOException
|
||||
{
|
||||
if (next == LF)
|
||||
{
|
||||
if (binary)
|
||||
{
|
||||
writePending();
|
||||
escape(next);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (pendingCR)
|
||||
{
|
||||
// Expect either space or tab pending
|
||||
// but not both
|
||||
if (pendingSpace)
|
||||
{
|
||||
escape(SPACE);
|
||||
}
|
||||
else if (pendingTab)
|
||||
{
|
||||
escape(TAB);
|
||||
}
|
||||
lineBreak();
|
||||
clearPending();
|
||||
}
|
||||
else
|
||||
{
|
||||
writePending();
|
||||
plain(next);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (next == CR)
|
||||
{
|
||||
if (binary)
|
||||
{
|
||||
escape(next);
|
||||
}
|
||||
else
|
||||
{
|
||||
pendingCR = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
writePending();
|
||||
if (next == SPACE)
|
||||
{
|
||||
if (binary)
|
||||
{
|
||||
escape(next);
|
||||
}
|
||||
else
|
||||
{
|
||||
pendingSpace = true;
|
||||
}
|
||||
}
|
||||
else if (next == TAB)
|
||||
{
|
||||
if (binary)
|
||||
{
|
||||
escape(next);
|
||||
}
|
||||
else
|
||||
{
|
||||
pendingTab = true;
|
||||
}
|
||||
}
|
||||
else if (next < SPACE)
|
||||
{
|
||||
escape(next);
|
||||
}
|
||||
else if (next > QUOTED_PRINTABLE_LAST_PLAIN)
|
||||
{
|
||||
escape(next);
|
||||
}
|
||||
else if (next == EQUALS)
|
||||
{
|
||||
escape(next);
|
||||
}
|
||||
else
|
||||
{
|
||||
plain(next);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void plain(byte next) throws IOException
|
||||
{
|
||||
if (--nextSoftBreak <= 1)
|
||||
{
|
||||
softBreak();
|
||||
}
|
||||
write(next);
|
||||
}
|
||||
|
||||
private void escape(byte next) throws IOException
|
||||
{
|
||||
if (--nextSoftBreak <= QUOTED_PRINTABLE_OCTETS_PER_ESCAPE)
|
||||
{
|
||||
softBreak();
|
||||
}
|
||||
|
||||
int nextUnsigned = next & 0xff;
|
||||
|
||||
write(EQUALS);
|
||||
--nextSoftBreak;
|
||||
write(HEX_DIGITS[nextUnsigned >> 4]);
|
||||
--nextSoftBreak;
|
||||
write(HEX_DIGITS[nextUnsigned % 0x10]);
|
||||
}
|
||||
|
||||
private void write(byte next) throws IOException
|
||||
{
|
||||
outBuffer[outputIndex++] = next;
|
||||
if (outputIndex >= outBuffer.length)
|
||||
{
|
||||
flushOutput();
|
||||
}
|
||||
}
|
||||
|
||||
private void softBreak() throws IOException
|
||||
{
|
||||
write(EQUALS);
|
||||
lineBreak();
|
||||
}
|
||||
|
||||
private void lineBreak() throws IOException
|
||||
{
|
||||
write(CR);
|
||||
write(LF);
|
||||
nextSoftBreak = QUOTED_PRINTABLE_MAX_LINE_LENGTH;
|
||||
}
|
||||
|
||||
void flushOutput() throws IOException
|
||||
{
|
||||
if (outputIndex < outBuffer.length)
|
||||
{
|
||||
out.write(outBuffer, 0, outputIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
out.write(outBuffer);
|
||||
}
|
||||
outputIndex = 0;
|
||||
}
|
||||
}
|
@ -0,0 +1,309 @@
|
||||
/****************************************************************
|
||||
* Licensed to the Apache Software Foundation (ASF) under one *
|
||||
* or more contributor license agreements. See the NOTICE file *
|
||||
* distributed with this work for additional information *
|
||||
* regarding copyright ownership. The ASF licenses this file *
|
||||
* to you 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. *
|
||||
****************************************************************/
|
||||
|
||||
package org.apache.james.mime4j.codec;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import org.apache.james.mime4j.util.ByteArrayBuffer;
|
||||
|
||||
/**
|
||||
* Performs Quoted-Printable decoding on an underlying stream.
|
||||
*/
|
||||
public class QuotedPrintableInputStream extends InputStream {
|
||||
|
||||
private static final int DEFAULT_BUFFER_SIZE = 1024 * 2;
|
||||
|
||||
private static final byte EQ = 0x3D;
|
||||
private static final byte CR = 0x0D;
|
||||
private static final byte LF = 0x0A;
|
||||
|
||||
private final byte[] singleByte = new byte[1];
|
||||
|
||||
private final InputStream in;
|
||||
private final ByteArrayBuffer decodedBuf;
|
||||
private final ByteArrayBuffer blanks;
|
||||
|
||||
private final byte[] encoded;
|
||||
private int pos = 0; // current index into encoded buffer
|
||||
private int limit = 0; // current size of encoded buffer
|
||||
|
||||
private boolean closed;
|
||||
|
||||
private final DecodeMonitor monitor;
|
||||
|
||||
public QuotedPrintableInputStream(final InputStream in, DecodeMonitor monitor) {
|
||||
this(DEFAULT_BUFFER_SIZE, in, monitor);
|
||||
}
|
||||
|
||||
protected QuotedPrintableInputStream(final int bufsize, final InputStream in, DecodeMonitor monitor) {
|
||||
super();
|
||||
this.in = in;
|
||||
this.encoded = new byte[bufsize];
|
||||
this.decodedBuf = new ByteArrayBuffer(512);
|
||||
this.blanks = new ByteArrayBuffer(512);
|
||||
this.closed = false;
|
||||
this.monitor = monitor;
|
||||
}
|
||||
|
||||
protected QuotedPrintableInputStream(final int bufsize, final InputStream in, boolean strict) {
|
||||
this(bufsize, in, strict ? DecodeMonitor.STRICT : DecodeMonitor.SILENT);
|
||||
}
|
||||
|
||||
public QuotedPrintableInputStream(final InputStream in, boolean strict) {
|
||||
this(DEFAULT_BUFFER_SIZE, in, strict);
|
||||
}
|
||||
|
||||
public QuotedPrintableInputStream(final InputStream in) {
|
||||
this(in, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Terminates Quoted-Printable coded content. This method does NOT close
|
||||
* the underlying input stream.
|
||||
*
|
||||
* @throws IOException on I/O errors.
|
||||
*/
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
closed = true;
|
||||
}
|
||||
|
||||
private int fillBuffer() throws IOException {
|
||||
// Compact buffer if needed
|
||||
if (pos < limit) {
|
||||
System.arraycopy(encoded, pos, encoded, 0, limit - pos);
|
||||
limit -= pos;
|
||||
pos = 0;
|
||||
} else {
|
||||
limit = 0;
|
||||
pos = 0;
|
||||
}
|
||||
|
||||
int capacity = encoded.length - limit;
|
||||
if (capacity > 0) {
|
||||
int bytesRead = in.read(encoded, limit, capacity);
|
||||
if (bytesRead > 0) {
|
||||
limit += bytesRead;
|
||||
}
|
||||
return bytesRead;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
private int getnext() {
|
||||
if (pos < limit) {
|
||||
byte b = encoded[pos];
|
||||
pos++;
|
||||
return b & 0xFF;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
private int peek(int i) {
|
||||
if (pos + i < limit) {
|
||||
return encoded[pos + i] & 0xFF;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
private int transfer(
|
||||
final int b, final byte[] buffer, final int from, final int to, boolean keepblanks) throws IOException {
|
||||
int index = from;
|
||||
if (keepblanks && blanks.length() > 0) {
|
||||
int chunk = Math.min(blanks.length(), to - index);
|
||||
System.arraycopy(blanks.buffer(), 0, buffer, index, chunk);
|
||||
index += chunk;
|
||||
int remaining = blanks.length() - chunk;
|
||||
if (remaining > 0) {
|
||||
decodedBuf.append(blanks.buffer(), chunk, remaining);
|
||||
}
|
||||
blanks.clear();
|
||||
} else if (blanks.length() > 0 && !keepblanks) {
|
||||
StringBuilder sb = new StringBuilder(blanks.length() * 3);
|
||||
for (int i = 0; i < blanks.length(); i++) sb.append(" "+blanks.byteAt(i));
|
||||
if (monitor.warn("ignored blanks", sb.toString()))
|
||||
throw new IOException("ignored blanks");
|
||||
}
|
||||
if (b != -1) {
|
||||
if (index < to) {
|
||||
buffer[index++] = (byte) b;
|
||||
} else {
|
||||
decodedBuf.append(b);
|
||||
}
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
private int read0(final byte[] buffer, final int off, final int len) throws IOException {
|
||||
boolean eof = false;
|
||||
int from = off;
|
||||
int to = off + len;
|
||||
int index = off;
|
||||
|
||||
// check if a previous invocation left decoded content
|
||||
if (decodedBuf.length() > 0) {
|
||||
int chunk = Math.min(decodedBuf.length(), to - index);
|
||||
System.arraycopy(decodedBuf.buffer(), 0, buffer, index, chunk);
|
||||
decodedBuf.remove(0, chunk);
|
||||
index += chunk;
|
||||
}
|
||||
|
||||
while (index < to) {
|
||||
|
||||
if (limit - pos < 3) {
|
||||
int bytesRead = fillBuffer();
|
||||
eof = bytesRead == -1;
|
||||
}
|
||||
|
||||
// end of stream?
|
||||
if (limit - pos == 0 && eof) {
|
||||
return index == from ? -1 : index - from;
|
||||
}
|
||||
|
||||
boolean lastWasCR = false;
|
||||
while (pos < limit && index < to) {
|
||||
int b = encoded[pos++] & 0xFF;
|
||||
|
||||
if (lastWasCR && b != LF) {
|
||||
if (monitor.warn("Found CR without LF", "Leaving it as is"))
|
||||
throw new IOException("Found CR without LF");
|
||||
index = transfer(CR, buffer, index, to, false);
|
||||
} else if (!lastWasCR && b == LF) {
|
||||
if (monitor.warn("Found LF without CR", "Translating to CRLF"))
|
||||
throw new IOException("Found LF without CR");
|
||||
}
|
||||
|
||||
if (b == CR) {
|
||||
lastWasCR = true;
|
||||
continue;
|
||||
} else {
|
||||
lastWasCR = false;
|
||||
}
|
||||
|
||||
if (b == LF) {
|
||||
// at end of line
|
||||
if (blanks.length() == 0) {
|
||||
index = transfer(CR, buffer, index, to, false);
|
||||
index = transfer(LF, buffer, index, to, false);
|
||||
} else {
|
||||
if (blanks.byteAt(0) != EQ) {
|
||||
// hard line break
|
||||
index = transfer(CR, buffer, index, to, false);
|
||||
index = transfer(LF, buffer, index, to, false);
|
||||
}
|
||||
}
|
||||
blanks.clear();
|
||||
} else if (b == EQ) {
|
||||
if (limit - pos < 2 && !eof) {
|
||||
// not enough buffered data
|
||||
pos--;
|
||||
break;
|
||||
}
|
||||
|
||||
// found special char '='
|
||||
int b2 = getnext();
|
||||
if (b2 == EQ) {
|
||||
index = transfer(b2, buffer, index, to, true);
|
||||
// deal with '==\r\n' brokenness
|
||||
int bb1 = peek(0);
|
||||
int bb2 = peek(1);
|
||||
if (bb1 == LF || (bb1 == CR && bb2 == LF)) {
|
||||
monitor.warn("Unexpected ==EOL encountered", "== 0x"+bb1+" 0x"+bb2);
|
||||
blanks.append(b2);
|
||||
} else {
|
||||
monitor.warn("Unexpected == encountered", "==");
|
||||
}
|
||||
} else if (Character.isWhitespace((char) b2)) {
|
||||
// soft line break
|
||||
index = transfer(-1, buffer, index, to, true);
|
||||
if (b2 != LF) {
|
||||
blanks.append(b);
|
||||
blanks.append(b2);
|
||||
}
|
||||
} else {
|
||||
int b3 = getnext();
|
||||
int upper = convert(b2);
|
||||
int lower = convert(b3);
|
||||
if (upper < 0 || lower < 0) {
|
||||
monitor.warn("Malformed encoded value encountered", "leaving "+((char) EQ)+((char) b2)+((char) b3)+" as is");
|
||||
// TODO see MIME4J-160
|
||||
index = transfer(EQ, buffer, index, to, true);
|
||||
index = transfer(b2, buffer, index, to, false);
|
||||
index = transfer(b3, buffer, index, to, false);
|
||||
} else {
|
||||
index = transfer((upper << 4) | lower, buffer, index, to, true);
|
||||
}
|
||||
}
|
||||
} else if (Character.isWhitespace(b)) {
|
||||
blanks.append(b);
|
||||
} else {
|
||||
index = transfer((int) b & 0xFF, buffer, index, to, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
return to - from;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts '0' => 0, 'A' => 10, etc.
|
||||
* @param c ASCII character value.
|
||||
* @return Numeric value of hexadecimal character.
|
||||
*/
|
||||
private int convert(int c) {
|
||||
if (c >= '0' && c <= '9') {
|
||||
return (c - '0');
|
||||
} else if (c >= 'A' && c <= 'F') {
|
||||
return (0xA + (c - 'A'));
|
||||
} else if (c >= 'a' && c <= 'f') {
|
||||
return (0xA + (c - 'a'));
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
if (closed) {
|
||||
throw new IOException("Stream has been closed");
|
||||
}
|
||||
for (;;) {
|
||||
int bytes = read(singleByte, 0, 1);
|
||||
if (bytes == -1) {
|
||||
return -1;
|
||||
}
|
||||
if (bytes == 1) {
|
||||
return singleByte[0] & 0xff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(byte[] b, int off, int len) throws IOException {
|
||||
if (closed) {
|
||||
throw new IOException("Stream has been closed");
|
||||
}
|
||||
return read0(b, off, len);
|
||||
}
|
||||
|
||||
}
|
@ -23,66 +23,211 @@ import java.io.FilterOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
// Taken from Apache Mime4j 0.6
|
||||
|
||||
/**
|
||||
* Performs Quoted-Printable encoding on an underlying stream.
|
||||
*/
|
||||
public class QuotedPrintableOutputStream extends FilterOutputStream
|
||||
{
|
||||
public class QuotedPrintableOutputStream extends FilterOutputStream {
|
||||
|
||||
private static final int DEFAULT_ENCODING_BUFFER_SIZE = 1024;
|
||||
private static final int DEFAULT_BUFFER_SIZE = 1024 * 3;
|
||||
|
||||
private static final byte TB = 0x09;
|
||||
private static final byte SP = 0x20;
|
||||
private static final byte EQ = 0x3D;
|
||||
private static final byte CR = 0x0D;
|
||||
private static final byte LF = 0x0A;
|
||||
private static final byte QUOTED_PRINTABLE_LAST_PLAIN = 0x7E;
|
||||
private static final int QUOTED_PRINTABLE_MAX_LINE_LENGTH = 76;
|
||||
private static final int QUOTED_PRINTABLE_OCTETS_PER_ESCAPE = 3;
|
||||
private static final byte[] HEX_DIGITS = {
|
||||
'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
|
||||
|
||||
private final byte[] outBuffer;
|
||||
private final boolean binary;
|
||||
|
||||
private boolean pendingSpace;
|
||||
private boolean pendingTab;
|
||||
private boolean pendingCR;
|
||||
private int nextSoftBreak;
|
||||
private int outputIndex;
|
||||
|
||||
private QuotedPrintableEncoder encoder;
|
||||
private boolean closed = false;
|
||||
|
||||
public QuotedPrintableOutputStream(OutputStream out, boolean binary)
|
||||
{
|
||||
private byte[] singleByte = new byte[1];
|
||||
|
||||
public QuotedPrintableOutputStream(int bufsize, OutputStream out, boolean binary) {
|
||||
super(out);
|
||||
encoder = new QuotedPrintableEncoder(DEFAULT_ENCODING_BUFFER_SIZE, binary);
|
||||
encoder.initEncoding(out);
|
||||
this.outBuffer = new byte[bufsize];
|
||||
this.binary = binary;
|
||||
this.pendingSpace = false;
|
||||
this.pendingTab = false;
|
||||
this.pendingCR = false;
|
||||
this.outputIndex = 0;
|
||||
this.nextSoftBreak = QUOTED_PRINTABLE_MAX_LINE_LENGTH + 1;
|
||||
}
|
||||
|
||||
public QuotedPrintableOutputStream(OutputStream out, boolean binary) {
|
||||
this(DEFAULT_BUFFER_SIZE, out, binary);
|
||||
}
|
||||
|
||||
private void encodeChunk(byte[] buffer, int off, int len) throws IOException {
|
||||
for (int inputIndex = off; inputIndex < len + off; inputIndex++) {
|
||||
encode(buffer[inputIndex]);
|
||||
}
|
||||
}
|
||||
|
||||
private void completeEncoding() throws IOException {
|
||||
writePending();
|
||||
flushOutput();
|
||||
}
|
||||
|
||||
private void writePending() throws IOException {
|
||||
if (pendingSpace) {
|
||||
plain(SP);
|
||||
} else if (pendingTab) {
|
||||
plain(TB);
|
||||
} else if (pendingCR) {
|
||||
plain(CR);
|
||||
}
|
||||
clearPending();
|
||||
}
|
||||
|
||||
private void clearPending() throws IOException {
|
||||
pendingSpace = false;
|
||||
pendingTab = false;
|
||||
pendingCR = false;
|
||||
}
|
||||
|
||||
private void encode(byte next) throws IOException {
|
||||
if (next == LF) {
|
||||
if (binary) {
|
||||
writePending();
|
||||
escape(next);
|
||||
} else {
|
||||
if (pendingCR) {
|
||||
// Expect either space or tab pending
|
||||
// but not both
|
||||
if (pendingSpace) {
|
||||
escape(SP);
|
||||
} else if (pendingTab) {
|
||||
escape(TB);
|
||||
}
|
||||
lineBreak();
|
||||
clearPending();
|
||||
} else {
|
||||
writePending();
|
||||
plain(next);
|
||||
}
|
||||
}
|
||||
} else if (next == CR) {
|
||||
if (binary) {
|
||||
escape(next);
|
||||
} else {
|
||||
pendingCR = true;
|
||||
}
|
||||
} else {
|
||||
writePending();
|
||||
if (next == SP) {
|
||||
if (binary) {
|
||||
escape(next);
|
||||
} else {
|
||||
pendingSpace = true;
|
||||
}
|
||||
} else if (next == TB) {
|
||||
if (binary) {
|
||||
escape(next);
|
||||
} else {
|
||||
pendingTab = true;
|
||||
}
|
||||
} else if (next < SP) {
|
||||
escape(next);
|
||||
} else if (next > QUOTED_PRINTABLE_LAST_PLAIN) {
|
||||
escape(next);
|
||||
} else if (next == EQ) {
|
||||
escape(next);
|
||||
} else {
|
||||
plain(next);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void plain(byte next) throws IOException {
|
||||
if (--nextSoftBreak <= 1) {
|
||||
softBreak();
|
||||
}
|
||||
write(next);
|
||||
}
|
||||
|
||||
private void escape(byte next) throws IOException {
|
||||
if (--nextSoftBreak <= QUOTED_PRINTABLE_OCTETS_PER_ESCAPE) {
|
||||
softBreak();
|
||||
}
|
||||
|
||||
int nextUnsigned = next & 0xff;
|
||||
|
||||
write(EQ);
|
||||
--nextSoftBreak;
|
||||
write(HEX_DIGITS[nextUnsigned >> 4]);
|
||||
--nextSoftBreak;
|
||||
write(HEX_DIGITS[nextUnsigned % 0x10]);
|
||||
}
|
||||
|
||||
private void write(byte next) throws IOException {
|
||||
outBuffer[outputIndex++] = next;
|
||||
if (outputIndex >= outBuffer.length) {
|
||||
flushOutput();
|
||||
}
|
||||
}
|
||||
|
||||
private void softBreak() throws IOException {
|
||||
write(EQ);
|
||||
lineBreak();
|
||||
}
|
||||
|
||||
private void lineBreak() throws IOException {
|
||||
write(CR);
|
||||
write(LF);
|
||||
nextSoftBreak = QUOTED_PRINTABLE_MAX_LINE_LENGTH;
|
||||
}
|
||||
|
||||
void flushOutput() throws IOException {
|
||||
if (outputIndex < outBuffer.length) {
|
||||
out.write(outBuffer, 0, outputIndex);
|
||||
} else {
|
||||
out.write(outBuffer);
|
||||
}
|
||||
outputIndex = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException
|
||||
{
|
||||
public void close() throws IOException {
|
||||
if (closed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
encoder.completeEncoding();
|
||||
try {
|
||||
completeEncoding();
|
||||
// do not close the wrapped stream
|
||||
}
|
||||
finally
|
||||
{
|
||||
} finally {
|
||||
closed = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flush() throws IOException
|
||||
{
|
||||
encoder.flushOutput();
|
||||
public void flush() throws IOException {
|
||||
flushOutput();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(int b) throws IOException
|
||||
{
|
||||
this.write(new byte[] { (byte) b }, 0, 1);
|
||||
public void write(int b) throws IOException {
|
||||
singleByte[0] = (byte) b;
|
||||
this.write(singleByte, 0, 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(byte[] b, int off, int len) throws IOException
|
||||
{
|
||||
if (closed)
|
||||
{
|
||||
throw new IOException("QuotedPrintableOutputStream has been closed");
|
||||
public void write(byte[] b, int off, int len) throws IOException {
|
||||
if (closed) {
|
||||
throw new IOException("Stream has been closed");
|
||||
}
|
||||
|
||||
encoder.encodeChunk(b, off, len);
|
||||
encodeChunk(b, off, len);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -1,146 +0,0 @@
|
||||
/****************************************************************
|
||||
* Licensed to the Apache Software Foundation (ASF) under one *
|
||||
* or more contributor license agreements. See the NOTICE file *
|
||||
* distributed with this work for additional information *
|
||||
* regarding copyright ownership. The ASF licenses this file *
|
||||
* to you 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. *
|
||||
****************************************************************/
|
||||
|
||||
package org.apache.james.mime4j.decoder;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
/**
|
||||
* Performs Base-64 decoding on an underlying stream.
|
||||
*
|
||||
*
|
||||
* @version $Id: Base64InputStream.java,v 1.3 2004/11/29 13:15:47 ntherning Exp $
|
||||
*/
|
||||
public class Base64InputStream extends InputStream {
|
||||
private static Log log = LogFactory.getLog(Base64InputStream.class);
|
||||
|
||||
private final InputStream s;
|
||||
private final ByteQueue byteq = new ByteQueue(3);
|
||||
private boolean done = false;
|
||||
|
||||
public Base64InputStream(InputStream s) {
|
||||
this.s = s;
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the underlying stream.
|
||||
*
|
||||
* @throws IOException on I/O errors.
|
||||
*/
|
||||
public void close() throws IOException {
|
||||
s.close();
|
||||
}
|
||||
|
||||
public int read() throws IOException {
|
||||
if (byteq.count() == 0) {
|
||||
fillBuffer();
|
||||
if (byteq.count() == 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
byte val = byteq.dequeue();
|
||||
if (val >= 0)
|
||||
return val;
|
||||
else
|
||||
return val & 0xFF;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve data from the underlying stream, decode it,
|
||||
* and put the results in the byteq.
|
||||
* @throws IOException
|
||||
*/
|
||||
private void fillBuffer() throws IOException {
|
||||
byte[] data = new byte[4];
|
||||
int pos = 0;
|
||||
|
||||
int i;
|
||||
while (!done) {
|
||||
switch (i = s.read()) {
|
||||
case -1:
|
||||
if (pos > 0) {
|
||||
log.warn("Unexpected EOF in MIME parser, dropping "
|
||||
+ pos + " sextets");
|
||||
}
|
||||
return;
|
||||
case '=':
|
||||
decodeAndEnqueue(data, pos);
|
||||
done = true;
|
||||
break;
|
||||
default:
|
||||
byte sX = TRANSLATION[i];
|
||||
if (sX < 0)
|
||||
continue;
|
||||
data[pos++] = sX;
|
||||
if (pos == data.length) {
|
||||
decodeAndEnqueue(data, pos);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void decodeAndEnqueue(byte[] data, int len) {
|
||||
int accum = 0;
|
||||
accum |= data[0] << 18;
|
||||
accum |= data[1] << 12;
|
||||
accum |= data[2] << 6;
|
||||
accum |= data[3];
|
||||
|
||||
byte b1 = (byte)(accum >>> 16);
|
||||
byteq.enqueue(b1);
|
||||
|
||||
if (len > 2) {
|
||||
byte b2 = (byte)((accum >>> 8) & 0xFF);
|
||||
byteq.enqueue(b2);
|
||||
|
||||
if (len > 3) {
|
||||
byte b3 = (byte)(accum & 0xFF);
|
||||
byteq.enqueue(b3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static byte[] TRANSLATION = {
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x00 */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x10 */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, /* 0x20 */
|
||||
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, /* 0x30 */
|
||||
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, /* 0x40 */
|
||||
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, /* 0x50 */
|
||||
-1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, /* 0x60 */
|
||||
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, /* 0x70 */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x80 */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x90 */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xA0 */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xB0 */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xC0 */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xD0 */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xE0 */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 /* 0xF0 */
|
||||
};
|
||||
|
||||
|
||||
}
|
@ -1,277 +0,0 @@
|
||||
/****************************************************************
|
||||
* Licensed to the Apache Software Foundation (ASF) under one *
|
||||
* or more contributor license agreements. See the NOTICE file *
|
||||
* distributed with this work for additional information *
|
||||
* regarding copyright ownership. The ASF licenses this file *
|
||||
* to you 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. *
|
||||
****************************************************************/
|
||||
|
||||
package org.apache.james.mime4j.decoder;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.apache.james.mime4j.util.CharsetUtil;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
|
||||
/**
|
||||
* Static methods for decoding strings, byte arrays and encoded words.
|
||||
*
|
||||
*
|
||||
* @version $Id: DecoderUtil.java,v 1.3 2005/02/07 15:33:59 ntherning Exp $
|
||||
*/
|
||||
public class DecoderUtil {
|
||||
private static Log log = LogFactory.getLog(DecoderUtil.class);
|
||||
|
||||
/**
|
||||
* Decodes a string containing quoted-printable encoded data.
|
||||
*
|
||||
* @param s the string to decode.
|
||||
* @return the decoded bytes.
|
||||
*/
|
||||
public static byte[] decodeBaseQuotedPrintable(String s) {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
|
||||
try {
|
||||
byte[] bytes = s.getBytes("US-ASCII");
|
||||
|
||||
QuotedPrintableInputStream is = new QuotedPrintableInputStream(
|
||||
new ByteArrayInputStream(bytes));
|
||||
|
||||
int b = 0;
|
||||
while ((b = is.read()) != -1) {
|
||||
baos.write(b);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
/*
|
||||
* This should never happen!
|
||||
*/
|
||||
log.error(e);
|
||||
}
|
||||
|
||||
return baos.toByteArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes a string containing base64 encoded data.
|
||||
*
|
||||
* @param s the string to decode.
|
||||
* @return the decoded bytes.
|
||||
*/
|
||||
public static byte[] decodeBase64(String s) {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
|
||||
try {
|
||||
byte[] bytes = s.getBytes("US-ASCII");
|
||||
|
||||
Base64InputStream is = new Base64InputStream(
|
||||
new ByteArrayInputStream(bytes));
|
||||
|
||||
int b = 0;
|
||||
while ((b = is.read()) != -1) {
|
||||
baos.write(b);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
/*
|
||||
* This should never happen!
|
||||
*/
|
||||
log.error(e);
|
||||
}
|
||||
|
||||
return baos.toByteArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes an encoded word encoded with the 'B' encoding (described in
|
||||
* RFC 2047) found in a header field body.
|
||||
*
|
||||
* @param encodedWord the encoded word to decode.
|
||||
* @param charset the Java charset to use.
|
||||
* @return the decoded string.
|
||||
* @throws UnsupportedEncodingException if the given Java charset isn't
|
||||
* supported.
|
||||
*/
|
||||
public static String decodeB(String encodedWord, String charset)
|
||||
throws UnsupportedEncodingException {
|
||||
|
||||
return new String(decodeBase64(encodedWord), charset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes an encoded word encoded with the 'Q' encoding (described in
|
||||
* RFC 2047) found in a header field body.
|
||||
*
|
||||
* @param encodedWord the encoded word to decode.
|
||||
* @param charset the Java charset to use.
|
||||
* @return the decoded string.
|
||||
* @throws UnsupportedEncodingException if the given Java charset isn't
|
||||
* supported.
|
||||
*/
|
||||
public static String decodeQ(String encodedWord, String charset)
|
||||
throws UnsupportedEncodingException {
|
||||
|
||||
/*
|
||||
* Replace _ with =20
|
||||
*/
|
||||
StringBuffer sb = new StringBuffer();
|
||||
for (int i = 0; i < encodedWord.length(); i++) {
|
||||
char c = encodedWord.charAt(i);
|
||||
if (c == '_') {
|
||||
sb.append("=20");
|
||||
} else {
|
||||
sb.append(c);
|
||||
}
|
||||
}
|
||||
|
||||
return new String(decodeBaseQuotedPrintable(sb.toString()), charset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes a string containing encoded words as defined by RFC 2047.
|
||||
* Encoded words in have the form
|
||||
* =?charset?enc?Encoded word?= where enc is either 'Q' or 'q' for
|
||||
* quoted-printable and 'B' or 'b' for Base64.
|
||||
*
|
||||
* ANDROID: COPIED FROM A NEWER VERSION OF MIME4J
|
||||
*
|
||||
* @param body the string to decode.
|
||||
* @return the decoded string.
|
||||
*/
|
||||
public static String decodeEncodedWords(String body) {
|
||||
|
||||
// ANDROID: Most strings will not include "=?" so a quick test can prevent unneeded
|
||||
// object creation. This could also be handled via lazy creation of the StringBuilder.
|
||||
if (body.indexOf("=?") == -1) {
|
||||
return body;
|
||||
}
|
||||
|
||||
int previousEnd = 0;
|
||||
boolean previousWasEncoded = false;
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
while (true) {
|
||||
int begin = body.indexOf("=?", previousEnd);
|
||||
|
||||
// ANDROID: The mime4j original version has an error here. It gets confused if
|
||||
// the encoded string begins with an '=' (just after "?Q?"). This patch seeks forward
|
||||
// to find the two '?' in the "header", before looking for the final "?=".
|
||||
int endScan = begin + 2;
|
||||
if (begin != -1) {
|
||||
int qm1 = body.indexOf('?', endScan + 2);
|
||||
int qm2 = body.indexOf('?', qm1 + 1);
|
||||
if (qm2 != -1) {
|
||||
endScan = qm2 + 1;
|
||||
}
|
||||
}
|
||||
|
||||
int end = begin == -1 ? -1 : body.indexOf("?=", endScan);
|
||||
if (end == -1) {
|
||||
if (previousEnd == 0)
|
||||
return body;
|
||||
|
||||
sb.append(body.substring(previousEnd));
|
||||
return sb.toString();
|
||||
}
|
||||
end += 2;
|
||||
|
||||
String sep = body.substring(previousEnd, begin);
|
||||
|
||||
String decoded = decodeEncodedWord(body, begin, end);
|
||||
if (decoded == null) {
|
||||
sb.append(sep);
|
||||
sb.append(body.substring(begin, end));
|
||||
} else {
|
||||
if (!previousWasEncoded || !CharsetUtil.isWhitespace(sep)) {
|
||||
sb.append(sep);
|
||||
}
|
||||
sb.append(decoded);
|
||||
}
|
||||
|
||||
previousEnd = end;
|
||||
previousWasEncoded = decoded != null;
|
||||
}
|
||||
}
|
||||
|
||||
// return null on error
|
||||
private static String decodeEncodedWord(String body, int begin, int end) {
|
||||
int qm1 = body.indexOf('?', begin + 2);
|
||||
if (qm1 == end - 2)
|
||||
return null;
|
||||
|
||||
int qm2 = body.indexOf('?', qm1 + 1);
|
||||
if (qm2 == end - 2)
|
||||
return null;
|
||||
|
||||
String mimeCharset = body.substring(begin + 2, qm1);
|
||||
String encoding = body.substring(qm1 + 1, qm2);
|
||||
String encodedText = body.substring(qm2 + 1, end - 2);
|
||||
|
||||
String charset = CharsetUtil.toJavaCharset(mimeCharset);
|
||||
if (charset == null) {
|
||||
if (log.isWarnEnabled()) {
|
||||
log.warn("MIME charset '" + mimeCharset + "' in encoded word '"
|
||||
+ body.substring(begin, end) + "' doesn't have a "
|
||||
+ "corresponding Java charset");
|
||||
}
|
||||
return null;
|
||||
} else if (!CharsetUtil.isDecodingSupported(charset)) {
|
||||
if (log.isWarnEnabled()) {
|
||||
log.warn("Current JDK doesn't support decoding of charset '"
|
||||
+ charset + "' (MIME charset '" + mimeCharset
|
||||
+ "' in encoded word '" + body.substring(begin, end)
|
||||
+ "')");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
if (encodedText.length() == 0) {
|
||||
if (log.isWarnEnabled()) {
|
||||
log.warn("Missing encoded text in encoded word: '"
|
||||
+ body.substring(begin, end) + "'");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
if (encoding.equalsIgnoreCase("Q")) {
|
||||
return DecoderUtil.decodeQ(encodedText, charset);
|
||||
} else if (encoding.equalsIgnoreCase("B")) {
|
||||
return DecoderUtil.decodeB(encodedText, charset);
|
||||
} else {
|
||||
if (log.isWarnEnabled()) {
|
||||
log.warn("Warning: Unknown encoding in encoded word '"
|
||||
+ body.substring(begin, end) + "'");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
// should not happen because of isDecodingSupported check above
|
||||
if (log.isWarnEnabled()) {
|
||||
log.warn("Unsupported encoding in encoded word '"
|
||||
+ body.substring(begin, end) + "'", e);
|
||||
}
|
||||
return null;
|
||||
} catch (RuntimeException e) {
|
||||
if (log.isWarnEnabled()) {
|
||||
log.warn("Could not decode encoded word '"
|
||||
+ body.substring(begin, end) + "'", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,227 +0,0 @@
|
||||
/****************************************************************
|
||||
* Licensed to the Apache Software Foundation (ASF) under one *
|
||||
* or more contributor license agreements. See the NOTICE file *
|
||||
* distributed with this work for additional information *
|
||||
* regarding copyright ownership. The ASF licenses this file *
|
||||
* to you 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. *
|
||||
****************************************************************/
|
||||
|
||||
package org.apache.james.mime4j.decoder;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
/**
|
||||
* Performs Quoted-Printable decoding on an underlying stream.
|
||||
*
|
||||
*
|
||||
*
|
||||
* @version $Id: QuotedPrintableInputStream.java,v 1.3 2004/11/29 13:15:47 ntherning Exp $
|
||||
*/
|
||||
public class QuotedPrintableInputStream extends InputStream {
|
||||
private static Log log = LogFactory.getLog(QuotedPrintableInputStream.class);
|
||||
|
||||
private InputStream stream;
|
||||
ByteQueue byteq = new ByteQueue();
|
||||
ByteQueue pushbackq = new ByteQueue();
|
||||
private byte state = 0;
|
||||
|
||||
public QuotedPrintableInputStream(InputStream stream) {
|
||||
this.stream = stream;
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the underlying stream.
|
||||
*
|
||||
* @throws IOException on I/O errors.
|
||||
*/
|
||||
public void close() throws IOException {
|
||||
stream.close();
|
||||
}
|
||||
|
||||
public int read() throws IOException {
|
||||
fillBuffer();
|
||||
if (byteq.count() == 0)
|
||||
return -1;
|
||||
else {
|
||||
byte val = byteq.dequeue();
|
||||
if (val >= 0)
|
||||
return val;
|
||||
else
|
||||
return val & 0xFF;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Pulls bytes out of the underlying stream and places them in the
|
||||
* pushback queue. This is necessary (vs. reading from the
|
||||
* underlying stream directly) to detect and filter out "transport
|
||||
* padding" whitespace, i.e., all whitespace that appears immediately
|
||||
* before a CRLF.
|
||||
*
|
||||
* @throws IOException Underlying stream threw IOException.
|
||||
*/
|
||||
private void populatePushbackQueue() throws IOException {
|
||||
//Debug.verify(pushbackq.count() == 0, "PopulatePushbackQueue called when pushback queue was not empty!");
|
||||
|
||||
if (pushbackq.count() != 0)
|
||||
return;
|
||||
|
||||
while (true) {
|
||||
int i = stream.read();
|
||||
switch (i) {
|
||||
case -1:
|
||||
// stream is done
|
||||
pushbackq.clear(); // discard any whitespace preceding EOF
|
||||
return;
|
||||
case ' ':
|
||||
case '\t':
|
||||
pushbackq.enqueue((byte)i);
|
||||
break;
|
||||
case '\r':
|
||||
case '\n':
|
||||
pushbackq.clear(); // discard any whitespace preceding EOL
|
||||
pushbackq.enqueue((byte)i);
|
||||
return;
|
||||
default:
|
||||
pushbackq.enqueue((byte)i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Causes the pushback queue to get populated if it is empty, then
|
||||
* consumes and decodes bytes out of it until one or more bytes are
|
||||
* in the byte queue. This decoding step performs the actual QP
|
||||
* decoding.
|
||||
*
|
||||
* @throws IOException Underlying stream threw IOException.
|
||||
*/
|
||||
private void fillBuffer() throws IOException {
|
||||
byte msdChar = 0; // first digit of escaped num
|
||||
while (byteq.count() == 0) {
|
||||
if (pushbackq.count() == 0) {
|
||||
populatePushbackQueue();
|
||||
if (pushbackq.count() == 0)
|
||||
return;
|
||||
}
|
||||
|
||||
byte b = (byte)pushbackq.dequeue();
|
||||
|
||||
switch (state) {
|
||||
case 0: // start state, no bytes pending
|
||||
if (b != '=') {
|
||||
byteq.enqueue(b);
|
||||
break; // state remains 0
|
||||
} else {
|
||||
state = 1;
|
||||
break;
|
||||
}
|
||||
case 1: // encountered "=" so far
|
||||
if (b == '\r') {
|
||||
state = 2;
|
||||
break;
|
||||
} else if ((b >= '0' && b <= '9') || (b >= 'A' && b <= 'F') || (b >= 'a' && b <= 'f')) {
|
||||
state = 3;
|
||||
msdChar = b; // save until next digit encountered
|
||||
break;
|
||||
} else if (b == '=') {
|
||||
/*
|
||||
* Special case when == is encountered.
|
||||
* Emit one = and stay in this state.
|
||||
*/
|
||||
if (log.isWarnEnabled()) {
|
||||
log.warn("Malformed MIME; got ==");
|
||||
}
|
||||
byteq.enqueue((byte)'=');
|
||||
break;
|
||||
} else {
|
||||
if (log.isWarnEnabled()) {
|
||||
log.warn("Malformed MIME; expected \\r or "
|
||||
+ "[0-9A-Z], got " + b);
|
||||
}
|
||||
state = 0;
|
||||
byteq.enqueue((byte)'=');
|
||||
byteq.enqueue(b);
|
||||
break;
|
||||
}
|
||||
case 2: // encountered "=\r" so far
|
||||
if (b == '\n') {
|
||||
state = 0;
|
||||
break;
|
||||
} else {
|
||||
if (log.isWarnEnabled()) {
|
||||
log.warn("Malformed MIME; expected "
|
||||
+ (int)'\n' + ", got " + b);
|
||||
}
|
||||
state = 0;
|
||||
byteq.enqueue((byte)'=');
|
||||
byteq.enqueue((byte)'\r');
|
||||
byteq.enqueue(b);
|
||||
break;
|
||||
}
|
||||
case 3: // encountered =<digit> so far; expecting another <digit> to complete the octet
|
||||
if ((b >= '0' && b <= '9') || (b >= 'A' && b <= 'F') || (b >= 'a' && b <= 'f')) {
|
||||
byte msd = asciiCharToNumericValue(msdChar);
|
||||
byte low = asciiCharToNumericValue(b);
|
||||
state = 0;
|
||||
byteq.enqueue((byte)((msd << 4) | low));
|
||||
break;
|
||||
} else {
|
||||
if (log.isWarnEnabled()) {
|
||||
log.warn("Malformed MIME; expected "
|
||||
+ "[0-9A-Z], got " + b);
|
||||
}
|
||||
state = 0;
|
||||
byteq.enqueue((byte)'=');
|
||||
byteq.enqueue(msdChar);
|
||||
byteq.enqueue(b);
|
||||
break;
|
||||
}
|
||||
default: // should never happen
|
||||
log.error("Illegal state: " + state);
|
||||
state = 0;
|
||||
byteq.enqueue(b);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts '0' => 0, 'A' => 10, etc.
|
||||
* @param c ASCII character value.
|
||||
* @return Numeric value of hexadecimal character.
|
||||
*/
|
||||
private byte asciiCharToNumericValue(byte c) {
|
||||
if (c >= '0' && c <= '9') {
|
||||
return (byte)(c - '0');
|
||||
} else if (c >= 'A' && c <= 'Z') {
|
||||
return (byte)(0xA + (c - 'A'));
|
||||
} else if (c >= 'a' && c <= 'z') {
|
||||
return (byte)(0xA + (c - 'a'));
|
||||
} else {
|
||||
/*
|
||||
* This should never happen since all calls to this method
|
||||
* are preceded by a check that c is in [0-9A-Za-z]
|
||||
*/
|
||||
throw new IllegalArgumentException((char) c
|
||||
+ " is not a hexadecimal digit");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,272 +0,0 @@
|
||||
/****************************************************************
|
||||
* Licensed to the Apache Software Foundation (ASF) under one *
|
||||
* or more contributor license agreements. See the NOTICE file *
|
||||
* distributed with this work for additional information *
|
||||
* regarding copyright ownership. The ASF licenses this file *
|
||||
* to you 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. *
|
||||
****************************************************************/
|
||||
|
||||
package org.apache.james.mime4j.decoder;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
/**
|
||||
* UnboundedFifoByteBuffer is a very efficient buffer implementation.
|
||||
* According to performance testing, it exhibits a constant access time, but it
|
||||
* also outperforms ArrayList when used for the same purpose.
|
||||
* <p>
|
||||
* The removal order of an <code>UnboundedFifoByteBuffer</code> is based on the insertion
|
||||
* order; elements are removed in the same order in which they were added.
|
||||
* The iteration order is the same as the removal order.
|
||||
* <p>
|
||||
* The {@link #remove()} and {@link #get()} operations perform in constant time.
|
||||
* The {@link #add(Object)} operation performs in amortized constant time. All
|
||||
* other operations perform in linear time or worse.
|
||||
* <p>
|
||||
* Note that this implementation is not synchronized. The following can be
|
||||
* used to provide synchronized access to your <code>UnboundedFifoByteBuffer</code>:
|
||||
* <pre>
|
||||
* Buffer fifo = BufferUtils.synchronizedBuffer(new UnboundedFifoByteBuffer());
|
||||
* </pre>
|
||||
* <p>
|
||||
* This buffer prevents null objects from being added.
|
||||
*
|
||||
* @since Commons Collections 3.0 (previously in main package v2.1)
|
||||
* @version $Revision: 1.1 $ $Date: 2004/08/24 06:52:02 $
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
class UnboundedFifoByteBuffer {
|
||||
|
||||
protected byte[] buffer;
|
||||
protected int head;
|
||||
protected int tail;
|
||||
|
||||
/**
|
||||
* Constructs an UnboundedFifoByteBuffer with the default number of elements.
|
||||
* It is exactly the same as performing the following:
|
||||
*
|
||||
* <pre>
|
||||
* new UnboundedFifoByteBuffer(32);
|
||||
* </pre>
|
||||
*/
|
||||
public UnboundedFifoByteBuffer() {
|
||||
this(32);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an UnboundedFifoByteBuffer with the specified number of elements.
|
||||
* The integer must be a positive integer.
|
||||
*
|
||||
* @param initialSize the initial size of the buffer
|
||||
* @throws IllegalArgumentException if the size is less than 1
|
||||
*/
|
||||
public UnboundedFifoByteBuffer(int initialSize) {
|
||||
if (initialSize <= 0) {
|
||||
throw new IllegalArgumentException("The size must be greater than 0");
|
||||
}
|
||||
buffer = new byte[initialSize + 1];
|
||||
head = 0;
|
||||
tail = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of elements stored in the buffer.
|
||||
*
|
||||
* @return this buffer's size
|
||||
*/
|
||||
public int size() {
|
||||
int size = 0;
|
||||
|
||||
if (tail < head) {
|
||||
size = buffer.length - head + tail;
|
||||
} else {
|
||||
size = tail - head;
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this buffer is empty; false otherwise.
|
||||
*
|
||||
* @return true if this buffer is empty
|
||||
*/
|
||||
public boolean isEmpty() {
|
||||
return (size() == 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the given element to this buffer.
|
||||
*
|
||||
* @param b the byte to add
|
||||
* @return true, always
|
||||
*/
|
||||
public boolean add(final byte b) {
|
||||
|
||||
if (size() + 1 >= buffer.length) {
|
||||
byte[] tmp = new byte[((buffer.length - 1) * 2) + 1];
|
||||
|
||||
int j = 0;
|
||||
for (int i = head; i != tail;) {
|
||||
tmp[j] = buffer[i];
|
||||
buffer[i] = 0;
|
||||
|
||||
j++;
|
||||
i++;
|
||||
if (i == buffer.length) {
|
||||
i = 0;
|
||||
}
|
||||
}
|
||||
|
||||
buffer = tmp;
|
||||
head = 0;
|
||||
tail = j;
|
||||
}
|
||||
|
||||
buffer[tail] = b;
|
||||
tail++;
|
||||
if (tail >= buffer.length) {
|
||||
tail = 0;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the next object in the buffer.
|
||||
*
|
||||
* @return the next object in the buffer
|
||||
* @throws BufferUnderflowException if this buffer is empty
|
||||
*/
|
||||
public byte get() {
|
||||
if (isEmpty()) {
|
||||
throw new IllegalStateException("The buffer is already empty");
|
||||
}
|
||||
|
||||
return buffer[head];
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the next object from the buffer
|
||||
*
|
||||
* @return the removed object
|
||||
* @throws BufferUnderflowException if this buffer is empty
|
||||
*/
|
||||
public byte remove() {
|
||||
if (isEmpty()) {
|
||||
throw new IllegalStateException("The buffer is already empty");
|
||||
}
|
||||
|
||||
byte element = buffer[head];
|
||||
|
||||
head++;
|
||||
if (head >= buffer.length) {
|
||||
head = 0;
|
||||
}
|
||||
|
||||
return element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Increments the internal index.
|
||||
*
|
||||
* @param index the index to increment
|
||||
* @return the updated index
|
||||
*/
|
||||
private int increment(int index) {
|
||||
index++;
|
||||
if (index >= buffer.length) {
|
||||
index = 0;
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrements the internal index.
|
||||
*
|
||||
* @param index the index to decrement
|
||||
* @return the updated index
|
||||
*/
|
||||
private int decrement(int index) {
|
||||
index--;
|
||||
if (index < 0) {
|
||||
index = buffer.length - 1;
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an iterator over this buffer's elements.
|
||||
*
|
||||
* @return an iterator over this buffer's elements
|
||||
*/
|
||||
public Iterator iterator() {
|
||||
return new Iterator() {
|
||||
|
||||
private int index = head;
|
||||
private int lastReturnedIndex = -1;
|
||||
|
||||
public boolean hasNext() {
|
||||
return index != tail;
|
||||
|
||||
}
|
||||
|
||||
public Object next() {
|
||||
if (!hasNext()) {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
lastReturnedIndex = index;
|
||||
index = increment(index);
|
||||
return new Byte(buffer[lastReturnedIndex]);
|
||||
}
|
||||
|
||||
public void remove() {
|
||||
if (lastReturnedIndex == -1) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
// First element can be removed quickly
|
||||
if (lastReturnedIndex == head) {
|
||||
UnboundedFifoByteBuffer.this.remove();
|
||||
lastReturnedIndex = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
// Other elements require us to shift the subsequent elements
|
||||
int i = lastReturnedIndex + 1;
|
||||
while (i != tail) {
|
||||
if (i >= buffer.length) {
|
||||
buffer[i - 1] = buffer[0];
|
||||
i = 0;
|
||||
} else {
|
||||
buffer[i - 1] = buffer[i];
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
lastReturnedIndex = -1;
|
||||
tail = decrement(tail);
|
||||
buffer[tail] = 0;
|
||||
index = decrement(index);
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
}
|
33
src/org/apache/james/mime4j/dom/BinaryBody.java
Normal file
33
src/org/apache/james/mime4j/dom/BinaryBody.java
Normal file
@ -0,0 +1,33 @@
|
||||
/****************************************************************
|
||||
* Licensed to the Apache Software Foundation (ASF) under one *
|
||||
* or more contributor license agreements. See the NOTICE file *
|
||||
* distributed with this work for additional information *
|
||||
* regarding copyright ownership. The ASF licenses this file *
|
||||
* to you 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. *
|
||||
****************************************************************/
|
||||
|
||||
package org.apache.james.mime4j.dom;
|
||||
|
||||
/**
|
||||
* A body containing binary data.
|
||||
*/
|
||||
public abstract class BinaryBody extends SingleBody {
|
||||
|
||||
/**
|
||||
* Sole constructor.
|
||||
*/
|
||||
protected BinaryBody() {
|
||||
}
|
||||
|
||||
}
|
@ -17,38 +17,30 @@
|
||||
* under the License. *
|
||||
****************************************************************/
|
||||
|
||||
package org.apache.james.mime4j.message;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
package org.apache.james.mime4j.dom;
|
||||
|
||||
/**
|
||||
* Encapsulates the body of an entity (see RFC 2045).
|
||||
*
|
||||
*
|
||||
* @version $Id: Body.java,v 1.4 2004/10/04 15:36:43 ntherning Exp $
|
||||
* <p>
|
||||
* A body can be a {@link Message}, a {@link Multipart} or a {@link SingleBody}.
|
||||
* This interface should not be implemented directly by classes other than
|
||||
* those.
|
||||
*/
|
||||
public interface Body {
|
||||
public interface Body extends Disposable {
|
||||
|
||||
/**
|
||||
* Gets the parent of this body.
|
||||
*
|
||||
*
|
||||
* @return the parent.
|
||||
*/
|
||||
Entity getParent();
|
||||
|
||||
|
||||
/**
|
||||
* Sets the parent of this body.
|
||||
*
|
||||
* @param parent the parent.
|
||||
*
|
||||
* @param parent
|
||||
* the parent.
|
||||
*/
|
||||
void setParent(Entity parent);
|
||||
|
||||
/**
|
||||
* Writes this body to the given stream in MIME message format.
|
||||
*
|
||||
* @param out the stream to write to.
|
||||
* @throws IOException on I/O errors.
|
||||
*/
|
||||
void writeTo(OutputStream out) throws IOException;
|
||||
|
||||
}
|
36
src/org/apache/james/mime4j/dom/Disposable.java
Normal file
36
src/org/apache/james/mime4j/dom/Disposable.java
Normal file
@ -0,0 +1,36 @@
|
||||
/****************************************************************
|
||||
* Licensed to the Apache Software Foundation (ASF) under one *
|
||||
* or more contributor license agreements. See the NOTICE file *
|
||||
* distributed with this work for additional information *
|
||||
* regarding copyright ownership. The ASF licenses this file *
|
||||
* to you 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. *
|
||||
****************************************************************/
|
||||
|
||||
package org.apache.james.mime4j.dom;
|
||||
|
||||
/**
|
||||
* A <tt>Disposable</tt> is an object that should be disposed of explicitly
|
||||
* when it is no longer needed.
|
||||
*
|
||||
* The dispose method is invoked to release resources that the object is
|
||||
* holding (such as open files).
|
||||
*/
|
||||
public interface Disposable {
|
||||
/**
|
||||
* Free any resources this object is holding and prepares this object
|
||||
* for garbage collection. Once an object has been disposed of it can no
|
||||
* longer be used.
|
||||
*/
|
||||
void dispose();
|
||||
}
|
550
src/org/apache/james/mime4j/dom/Entity.java
Normal file
550
src/org/apache/james/mime4j/dom/Entity.java
Normal file
@ -0,0 +1,550 @@
|
||||
/****************************************************************
|
||||
* Licensed to the Apache Software Foundation (ASF) under one *
|
||||
* or more contributor license agreements. See the NOTICE file *
|
||||
* distributed with this work for additional information *
|
||||
* regarding copyright ownership. The ASF licenses this file *
|
||||
* to you 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. *
|
||||
****************************************************************/
|
||||
|
||||
package org.apache.james.mime4j.dom;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.james.mime4j.dom.field.ContentDispositionField;
|
||||
import org.apache.james.mime4j.dom.field.ContentTransferEncodingField;
|
||||
import org.apache.james.mime4j.dom.field.ContentTypeField;
|
||||
import org.apache.james.mime4j.dom.field.Field;
|
||||
import org.apache.james.mime4j.dom.field.FieldName;
|
||||
|
||||
/**
|
||||
* MIME entity. An entity has a header and a body (see RFC 2045).
|
||||
*/
|
||||
public abstract class Entity implements Disposable {
|
||||
private Header header = null;
|
||||
private Body body = null;
|
||||
private Entity parent = null;
|
||||
|
||||
/**
|
||||
* Creates a new <code>Entity</code>. Typically invoked implicitly by a
|
||||
* subclass constructor.
|
||||
*/
|
||||
protected Entity() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the parent entity of this entity.
|
||||
* Returns <code>null</code> if this is the root entity.
|
||||
*
|
||||
* @return the parent or <code>null</code>.
|
||||
*/
|
||||
public Entity getParent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the parent entity of this entity.
|
||||
*
|
||||
* @param parent the parent entity or <code>null</code> if
|
||||
* this will be the root entity.
|
||||
*/
|
||||
public void setParent(Entity parent) {
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the entity header.
|
||||
*
|
||||
* @return the header.
|
||||
*/
|
||||
public Header getHeader() {
|
||||
return header;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the entity header.
|
||||
*
|
||||
* @param header the header.
|
||||
*/
|
||||
public void setHeader(Header header) {
|
||||
this.header = header;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the body of this entity.
|
||||
*
|
||||
* @return the body,
|
||||
*/
|
||||
public Body getBody() {
|
||||
return body;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the body of this entity.
|
||||
*
|
||||
* @param body the body.
|
||||
* @throws IllegalStateException if the body has already been set.
|
||||
*/
|
||||
public void setBody(Body body) {
|
||||
if (this.body != null)
|
||||
throw new IllegalStateException("body already set");
|
||||
|
||||
this.body = body;
|
||||
body.setParent(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes and returns the body of this entity. The removed body may be
|
||||
* attached to another entity. If it is no longer needed it should be
|
||||
* {@link Disposable#dispose() disposed} of.
|
||||
*
|
||||
* @return the removed body or <code>null</code> if no body was set.
|
||||
*/
|
||||
public Body removeBody() {
|
||||
if (body == null)
|
||||
return null;
|
||||
|
||||
Body body = this.body;
|
||||
this.body = null;
|
||||
body.setParent(null);
|
||||
|
||||
return body;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the specified message as body of this entity and the content type to
|
||||
* "message/rfc822". A <code>Header</code> is created if this
|
||||
* entity does not already have one.
|
||||
*
|
||||
* @param message
|
||||
* the message to set as body.
|
||||
*/
|
||||
public void setMessage(Message message) {
|
||||
setBody(message, "message/rfc822", null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the specified multipart as body of this entity. Also sets the
|
||||
* content type accordingly and creates a message boundary string. A
|
||||
* <code>Header</code> is created if this entity does not already have
|
||||
* one.
|
||||
*
|
||||
* @param multipart
|
||||
* the multipart to set as body.
|
||||
*/
|
||||
public void setMultipart(Multipart multipart) {
|
||||
String mimeType = "multipart/" + multipart.getSubType();
|
||||
Map<String, String> parameters = Collections.singletonMap("boundary",
|
||||
newUniqueBoundary());
|
||||
|
||||
setBody(multipart, mimeType, parameters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the specified multipart as body of this entity. Also sets the
|
||||
* content type accordingly and creates a message boundary string. A
|
||||
* <code>Header</code> is created if this entity does not already have
|
||||
* one.
|
||||
*
|
||||
* @param multipart
|
||||
* the multipart to set as body.
|
||||
* @param parameters
|
||||
* additional parameters for the Content-Type header field.
|
||||
*/
|
||||
public void setMultipart(Multipart multipart, Map<String, String> parameters) {
|
||||
String mimeType = "multipart/" + multipart.getSubType();
|
||||
if (!parameters.containsKey("boundary")) {
|
||||
parameters = new HashMap<String, String>(parameters);
|
||||
parameters.put("boundary", newUniqueBoundary());
|
||||
}
|
||||
|
||||
setBody(multipart, mimeType, parameters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the specified <code>TextBody</code> as body of this entity and the
|
||||
* content type to "text/plain". A <code>Header</code> is
|
||||
* created if this entity does not already have one.
|
||||
*
|
||||
* @param textBody
|
||||
* the <code>TextBody</code> to set as body.
|
||||
* @see org.apache.james.mime4j.message.BodyFactory#textBody(String)
|
||||
*/
|
||||
public void setText(TextBody textBody) {
|
||||
setText(textBody, "plain");
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the specified <code>TextBody</code> as body of this entity. Also
|
||||
* sets the content type according to the specified sub-type. A
|
||||
* <code>Header</code> is created if this entity does not already have
|
||||
* one.
|
||||
*
|
||||
* @param textBody
|
||||
* the <code>TextBody</code> to set as body.
|
||||
* @param subtype
|
||||
* the text subtype (e.g. "plain", "html" or
|
||||
* "xml").
|
||||
* @see org.apache.james.mime4j.message.BodyFactory#textBody(String)
|
||||
*/
|
||||
public void setText(TextBody textBody, String subtype) {
|
||||
String mimeType = "text/" + subtype;
|
||||
|
||||
Map<String, String> parameters = null;
|
||||
String mimeCharset = textBody.getMimeCharset();
|
||||
if (mimeCharset != null && !mimeCharset.equalsIgnoreCase("us-ascii")) {
|
||||
parameters = Collections.singletonMap("charset", mimeCharset);
|
||||
}
|
||||
|
||||
setBody(textBody, mimeType, parameters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the body of this entity and sets the content-type to the specified
|
||||
* value. A <code>Header</code> is created if this entity does not already
|
||||
* have one.
|
||||
*
|
||||
* @param body
|
||||
* the body.
|
||||
* @param mimeType
|
||||
* the MIME media type of the specified body
|
||||
* ("type/subtype").
|
||||
*/
|
||||
public void setBody(Body body, String mimeType) {
|
||||
setBody(body, mimeType, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the body of this entity and sets the content-type to the specified
|
||||
* value. A <code>Header</code> is created if this entity does not already
|
||||
* have one.
|
||||
*
|
||||
* @param body
|
||||
* the body.
|
||||
* @param mimeType
|
||||
* the MIME media type of the specified body
|
||||
* ("type/subtype").
|
||||
* @param parameters
|
||||
* additional parameters for the Content-Type header field.
|
||||
*/
|
||||
public void setBody(Body body, String mimeType,
|
||||
Map<String, String> parameters) {
|
||||
setBody(body);
|
||||
|
||||
Header header = obtainHeader();
|
||||
header.setField(newContentType(mimeType, parameters));
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the MIME type of this <code>Entity</code>. The MIME type
|
||||
* is derived by looking at the parent's Content-Type field if no
|
||||
* Content-Type field is set for this <code>Entity</code>.
|
||||
*
|
||||
* @return the MIME type.
|
||||
*/
|
||||
public String getMimeType() {
|
||||
ContentTypeField child =
|
||||
getContentTypeField();
|
||||
ContentTypeField parent = getParent() != null
|
||||
? (ContentTypeField) getParent().getHeader().
|
||||
getField(FieldName.CONTENT_TYPE)
|
||||
: null;
|
||||
|
||||
return calcMimeType(child, parent);
|
||||
}
|
||||
|
||||
private ContentTypeField getContentTypeField() {
|
||||
return (ContentTypeField) getHeader().getField(FieldName.CONTENT_TYPE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the MIME character set encoding of this <code>Entity</code>.
|
||||
*
|
||||
* @return the MIME character set encoding.
|
||||
*/
|
||||
public String getCharset() {
|
||||
return calcCharset((ContentTypeField) getHeader().getField(FieldName.CONTENT_TYPE));
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the transfer encoding of this <code>Entity</code>.
|
||||
*
|
||||
* @return the transfer encoding.
|
||||
*/
|
||||
public String getContentTransferEncoding() {
|
||||
ContentTransferEncodingField f = (ContentTransferEncodingField)
|
||||
getHeader().getField(FieldName.CONTENT_TRANSFER_ENCODING);
|
||||
|
||||
return calcTransferEncoding(f);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the transfer encoding of this <code>Entity</code> to the specified
|
||||
* value.
|
||||
*
|
||||
* @param contentTransferEncoding
|
||||
* transfer encoding to use.
|
||||
*/
|
||||
public void setContentTransferEncoding(String contentTransferEncoding) {
|
||||
Header header = obtainHeader();
|
||||
header.setField(newContentTransferEncoding(contentTransferEncoding));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the disposition type of the content disposition of this
|
||||
* <code>Entity</code>.
|
||||
*
|
||||
* @return the disposition type or <code>null</code> if no disposition
|
||||
* type has been set.
|
||||
*/
|
||||
public String getDispositionType() {
|
||||
ContentDispositionField field = obtainField(FieldName.CONTENT_DISPOSITION);
|
||||
if (field == null)
|
||||
return null;
|
||||
|
||||
return field.getDispositionType();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the content disposition of this <code>Entity</code> to the
|
||||
* specified disposition type. No filename, size or date parameters
|
||||
* are included in the content disposition.
|
||||
*
|
||||
* @param dispositionType
|
||||
* disposition type value (usually <code>inline</code> or
|
||||
* <code>attachment</code>).
|
||||
*/
|
||||
public void setContentDisposition(String dispositionType) {
|
||||
Header header = obtainHeader();
|
||||
header.setField(newContentDisposition(dispositionType, null, -1, null,
|
||||
null, null));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the content disposition of this <code>Entity</code> to the
|
||||
* specified disposition type and filename. No size or date parameters are
|
||||
* included in the content disposition.
|
||||
*
|
||||
* @param dispositionType
|
||||
* disposition type value (usually <code>inline</code> or
|
||||
* <code>attachment</code>).
|
||||
* @param filename
|
||||
* filename parameter value or <code>null</code> if the
|
||||
* parameter should not be included.
|
||||
*/
|
||||
public void setContentDisposition(String dispositionType, String filename) {
|
||||
Header header = obtainHeader();
|
||||
header.setField(newContentDisposition(dispositionType, filename, -1,
|
||||
null, null, null));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the content disposition of this <code>Entity</code> to the
|
||||
* specified values. No date parameters are included in the content
|
||||
* disposition.
|
||||
*
|
||||
* @param dispositionType
|
||||
* disposition type value (usually <code>inline</code> or
|
||||
* <code>attachment</code>).
|
||||
* @param filename
|
||||
* filename parameter value or <code>null</code> if the
|
||||
* parameter should not be included.
|
||||
* @param size
|
||||
* size parameter value or <code>-1</code> if the parameter
|
||||
* should not be included.
|
||||
*/
|
||||
public void setContentDisposition(String dispositionType, String filename,
|
||||
long size) {
|
||||
Header header = obtainHeader();
|
||||
header.setField(newContentDisposition(dispositionType, filename, size,
|
||||
null, null, null));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the content disposition of this <code>Entity</code> to the
|
||||
* specified values.
|
||||
*
|
||||
* @param dispositionType
|
||||
* disposition type value (usually <code>inline</code> or
|
||||
* <code>attachment</code>).
|
||||
* @param filename
|
||||
* filename parameter value or <code>null</code> if the
|
||||
* parameter should not be included.
|
||||
* @param size
|
||||
* size parameter value or <code>-1</code> if the parameter
|
||||
* should not be included.
|
||||
* @param creationDate
|
||||
* creation-date parameter value or <code>null</code> if the
|
||||
* parameter should not be included.
|
||||
* @param modificationDate
|
||||
* modification-date parameter value or <code>null</code> if
|
||||
* the parameter should not be included.
|
||||
* @param readDate
|
||||
* read-date parameter value or <code>null</code> if the
|
||||
* parameter should not be included.
|
||||
*/
|
||||
public void setContentDisposition(String dispositionType, String filename,
|
||||
long size, Date creationDate, Date modificationDate, Date readDate) {
|
||||
Header header = obtainHeader();
|
||||
header.setField(newContentDisposition(dispositionType, filename, size,
|
||||
creationDate, modificationDate, readDate));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the filename parameter of the content disposition of this
|
||||
* <code>Entity</code>.
|
||||
*
|
||||
* @return the filename parameter of the content disposition or
|
||||
* <code>null</code> if the filename has not been set.
|
||||
*/
|
||||
public String getFilename() {
|
||||
ContentDispositionField field = obtainField(FieldName.CONTENT_DISPOSITION);
|
||||
if (field == null)
|
||||
return null;
|
||||
|
||||
return field.getFilename();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the filename parameter of the content disposition of this
|
||||
* <code>Entity</code> to the specified value. If this entity does not
|
||||
* have a content disposition header field a new one with disposition type
|
||||
* <code>attachment</code> is created.
|
||||
*
|
||||
* @param filename
|
||||
* filename parameter value or <code>null</code> if the
|
||||
* parameter should be removed.
|
||||
*/
|
||||
public void setFilename(String filename) {
|
||||
Header header = obtainHeader();
|
||||
ContentDispositionField field = (ContentDispositionField) header
|
||||
.getField(FieldName.CONTENT_DISPOSITION);
|
||||
if (field == null) {
|
||||
if (filename != null) {
|
||||
header.setField(newContentDisposition(
|
||||
ContentDispositionField.DISPOSITION_TYPE_ATTACHMENT,
|
||||
filename, -1, null, null, null));
|
||||
}
|
||||
} else {
|
||||
String dispositionType = field.getDispositionType();
|
||||
Map<String, String> parameters = new HashMap<String, String>(field
|
||||
.getParameters());
|
||||
if (filename == null) {
|
||||
parameters.remove(ContentDispositionField.PARAM_FILENAME);
|
||||
} else {
|
||||
parameters
|
||||
.put(ContentDispositionField.PARAM_FILENAME, filename);
|
||||
}
|
||||
header.setField(newContentDisposition(dispositionType, parameters));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the MIME type of this <code>Entity</code> matches the
|
||||
* given one. MIME types are case-insensitive.
|
||||
*
|
||||
* @param type the MIME type to match against.
|
||||
* @return <code>true</code> on match, <code>false</code> otherwise.
|
||||
*/
|
||||
public boolean isMimeType(String type) {
|
||||
return getMimeType().equalsIgnoreCase(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the MIME type of this <code>Entity</code> is
|
||||
* <code>multipart/*</code>. Since multipart-entities must have
|
||||
* a boundary parameter in the <code>Content-Type</code> field this
|
||||
* method returns <code>false</code> if no boundary exists.
|
||||
*
|
||||
* @return <code>true</code> on match, <code>false</code> otherwise.
|
||||
*/
|
||||
public boolean isMultipart() {
|
||||
ContentTypeField f = getContentTypeField();
|
||||
return f != null
|
||||
&& f.getBoundary() != null
|
||||
&& getMimeType().startsWith(
|
||||
ContentTypeField.TYPE_MULTIPART_PREFIX);
|
||||
}
|
||||
|
||||
/**
|
||||
* Disposes of the body of this entity. Note that the dispose call does not
|
||||
* get forwarded to the parent entity of this Entity.
|
||||
*
|
||||
* Subclasses that need to free resources should override this method and
|
||||
* invoke super.dispose().
|
||||
*
|
||||
* @see org.apache.james.mime4j.dom.Disposable#dispose()
|
||||
*/
|
||||
public void dispose() {
|
||||
if (body != null) {
|
||||
body.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains the header of this entity. Creates and sets a new header if this
|
||||
* entity's header is currently <code>null</code>.
|
||||
*
|
||||
* @return the header of this entity; never <code>null</code>.
|
||||
*/
|
||||
Header obtainHeader() {
|
||||
if (header == null) {
|
||||
header = new Header();
|
||||
}
|
||||
return header;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains the header field with the specified name.
|
||||
*
|
||||
* @param <F>
|
||||
* concrete field type.
|
||||
* @param fieldName
|
||||
* name of the field to retrieve.
|
||||
* @return the header field or <code>null</code> if this entity has no
|
||||
* header or the header contains no such field.
|
||||
*/
|
||||
<F extends Field> F obtainField(String fieldName) {
|
||||
Header header = getHeader();
|
||||
if (header == null)
|
||||
return null;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
F field = (F) header.getField(fieldName);
|
||||
return field;
|
||||
}
|
||||
|
||||
protected abstract String newUniqueBoundary();
|
||||
|
||||
protected abstract ContentDispositionField newContentDisposition(
|
||||
String dispositionType, String filename, long size,
|
||||
Date creationDate, Date modificationDate, Date readDate);
|
||||
|
||||
protected abstract ContentDispositionField newContentDisposition(
|
||||
String dispositionType, Map<String, String> parameters);
|
||||
|
||||
protected abstract ContentTypeField newContentType(String mimeType,
|
||||
Map<String, String> parameters);
|
||||
|
||||
protected abstract ContentTransferEncodingField newContentTransferEncoding(
|
||||
String contentTransferEncoding);
|
||||
|
||||
protected abstract String calcMimeType(ContentTypeField child, ContentTypeField parent);
|
||||
|
||||
protected abstract String calcTransferEncoding(ContentTransferEncodingField f);
|
||||
|
||||
protected abstract String calcCharset(ContentTypeField contentType);
|
||||
}
|
204
src/org/apache/james/mime4j/dom/Header.java
Normal file
204
src/org/apache/james/mime4j/dom/Header.java
Normal file
@ -0,0 +1,204 @@
|
||||
/****************************************************************
|
||||
* Licensed to the Apache Software Foundation (ASF) under one *
|
||||
* or more contributor license agreements. See the NOTICE file *
|
||||
* distributed with this work for additional information *
|
||||
* regarding copyright ownership. The ASF licenses this file *
|
||||
* to you 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. *
|
||||
****************************************************************/
|
||||
|
||||
package org.apache.james.mime4j.dom;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.james.mime4j.dom.field.Field;
|
||||
|
||||
/**
|
||||
* The header of an entity (see RFC 2045).
|
||||
*/
|
||||
public class Header implements Iterable<Field> {
|
||||
|
||||
private List<Field> fields = new LinkedList<Field>();
|
||||
private Map<String, List<Field>> fieldMap = new HashMap<String, List<Field>>();
|
||||
|
||||
/**
|
||||
* Creates a new empty <code>Header</code>.
|
||||
*/
|
||||
public Header() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new <code>Header</code> from the specified
|
||||
* <code>Header</code>. The <code>Header</code> instance is initialized
|
||||
* with a copy of the list of {@link Field}s of the specified
|
||||
* <code>Header</code>. The <code>Field</code> objects are not copied
|
||||
* because they are immutable and can safely be shared between headers.
|
||||
*
|
||||
* @param other
|
||||
* header to copy.
|
||||
*/
|
||||
public Header(Header other) {
|
||||
for (Field otherField : other.fields) {
|
||||
addField(otherField);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a field to the end of the list of fields.
|
||||
*
|
||||
* @param field the field to add.
|
||||
*/
|
||||
public void addField(Field field) {
|
||||
List<Field> values = fieldMap.get(field.getName().toLowerCase());
|
||||
if (values == null) {
|
||||
values = new LinkedList<Field>();
|
||||
fieldMap.put(field.getName().toLowerCase(), values);
|
||||
}
|
||||
values.add(field);
|
||||
fields.add(field);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the fields of this header. The returned list will not be
|
||||
* modifiable.
|
||||
*
|
||||
* @return the list of <code>Field</code> objects.
|
||||
*/
|
||||
public List<Field> getFields() {
|
||||
return Collections.unmodifiableList(fields);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a <code>Field</code> given a field name. If there are multiple
|
||||
* such fields defined in this header the first one will be returned.
|
||||
*
|
||||
* @param name the field name (e.g. From, Subject).
|
||||
* @return the field or <code>null</code> if none found.
|
||||
*/
|
||||
public Field getField(String name) {
|
||||
List<Field> l = fieldMap.get(name.toLowerCase());
|
||||
if (l != null && !l.isEmpty()) {
|
||||
return l.get(0);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all <code>Field</code>s having the specified field name.
|
||||
*
|
||||
* @param name the field name (e.g. From, Subject).
|
||||
* @return the list of fields.
|
||||
*/
|
||||
public List<Field> getFields(final String name) {
|
||||
final String lowerCaseName = name.toLowerCase();
|
||||
final List<Field> l = fieldMap.get(lowerCaseName);
|
||||
final List<Field> results;
|
||||
if (l == null || l.isEmpty()) {
|
||||
results = Collections.emptyList();
|
||||
} else {
|
||||
results = Collections.unmodifiableList(l);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an iterator over the list of fields of this header.
|
||||
*
|
||||
* @return an iterator.
|
||||
*/
|
||||
public Iterator<Field> iterator() {
|
||||
return Collections.unmodifiableList(fields).iterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all <code>Field</code>s having the specified field name.
|
||||
*
|
||||
* @param name
|
||||
* the field name (e.g. From, Subject).
|
||||
* @return number of fields removed.
|
||||
*/
|
||||
public int removeFields(String name) {
|
||||
final String lowerCaseName = name.toLowerCase();
|
||||
List<Field> removed = fieldMap.remove(lowerCaseName);
|
||||
if (removed == null || removed.isEmpty())
|
||||
return 0;
|
||||
|
||||
for (Iterator<Field> iterator = fields.iterator(); iterator.hasNext();) {
|
||||
Field field = iterator.next();
|
||||
if (field.getName().equalsIgnoreCase(name))
|
||||
iterator.remove();
|
||||
}
|
||||
|
||||
return removed.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets or replaces a field. This method is useful for header fields such as
|
||||
* Subject or Message-ID that should not occur more than once in a message.
|
||||
*
|
||||
* If this <code>Header</code> does not already contain a header field of
|
||||
* the same name as the given field then it is added to the end of the list
|
||||
* of fields (same behavior as {@link #addField(Field)}). Otherwise the
|
||||
* first occurrence of a field with the same name is replaced by the given
|
||||
* field and all further occurrences are removed.
|
||||
*
|
||||
* @param field the field to set.
|
||||
*/
|
||||
public void setField(Field field) {
|
||||
final String lowerCaseName = field.getName().toLowerCase();
|
||||
List<Field> l = fieldMap.get(lowerCaseName);
|
||||
if (l == null || l.isEmpty()) {
|
||||
addField(field);
|
||||
return;
|
||||
}
|
||||
|
||||
l.clear();
|
||||
l.add(field);
|
||||
|
||||
int firstOccurrence = -1;
|
||||
int index = 0;
|
||||
for (Iterator<Field> iterator = fields.iterator(); iterator.hasNext(); index++) {
|
||||
Field f = iterator.next();
|
||||
if (f.getName().equalsIgnoreCase(field.getName())) {
|
||||
iterator.remove();
|
||||
|
||||
if (firstOccurrence == -1)
|
||||
firstOccurrence = index;
|
||||
}
|
||||
}
|
||||
|
||||
fields.add(firstOccurrence, field);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return Header Object as String representation. Each headerline is
|
||||
* seperated by "\r\n"
|
||||
*
|
||||
* @return headers
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder str = new StringBuilder(128);
|
||||
for (Field field : fields) {
|
||||
str.append(field.toString());
|
||||
str.append("\r\n");
|
||||
}
|
||||
return str.toString();
|
||||
}
|
||||
|
||||
}
|
512
src/org/apache/james/mime4j/dom/Message.java
Normal file
512
src/org/apache/james/mime4j/dom/Message.java
Normal file
@ -0,0 +1,512 @@
|
||||
/****************************************************************
|
||||
* Licensed to the Apache Software Foundation (ASF) under one *
|
||||
* or more contributor license agreements. See the NOTICE file *
|
||||
* distributed with this work for additional information *
|
||||
* regarding copyright ownership. The ASF licenses this file *
|
||||
* to you 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. *
|
||||
****************************************************************/
|
||||
|
||||
package org.apache.james.mime4j.dom;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import org.apache.james.mime4j.dom.address.Address;
|
||||
import org.apache.james.mime4j.dom.address.AddressList;
|
||||
import org.apache.james.mime4j.dom.address.Mailbox;
|
||||
import org.apache.james.mime4j.dom.address.MailboxList;
|
||||
import org.apache.james.mime4j.dom.field.AddressListField;
|
||||
import org.apache.james.mime4j.dom.field.DateTimeField;
|
||||
import org.apache.james.mime4j.dom.field.Field;
|
||||
import org.apache.james.mime4j.dom.field.FieldName;
|
||||
import org.apache.james.mime4j.dom.field.MailboxField;
|
||||
import org.apache.james.mime4j.dom.field.MailboxListField;
|
||||
import org.apache.james.mime4j.dom.field.UnstructuredField;
|
||||
|
||||
public abstract class Message extends Entity implements Body {
|
||||
|
||||
/**
|
||||
* Write the content to the given output stream using the
|
||||
* {@link org.apache.james.mime4j.message.MessageWriter#DEFAULT default} message writer.
|
||||
*
|
||||
* @param out
|
||||
* the output stream to write to.
|
||||
* @throws IOException
|
||||
* in case of an I/O error
|
||||
* @see org.apache.james.mime4j.message.MessageWriter
|
||||
*/
|
||||
public abstract void writeTo(OutputStream out) throws IOException;
|
||||
|
||||
/**
|
||||
* Returns the value of the <i>Message-ID</i> header field of this message
|
||||
* or <code>null</code> if it is not present.
|
||||
*
|
||||
* @return the identifier of this message.
|
||||
*/
|
||||
public String getMessageId() {
|
||||
Field field = obtainField(FieldName.MESSAGE_ID);
|
||||
if (field == null)
|
||||
return null;
|
||||
|
||||
return field.getBody();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and sets a new <i>Message-ID</i> header field for this message.
|
||||
* A <code>Header</code> is created if this message does not already have
|
||||
* one.
|
||||
*
|
||||
* @param hostname
|
||||
* host name to be included in the identifier or
|
||||
* <code>null</code> if no host name should be included.
|
||||
*/
|
||||
public void createMessageId(String hostname) {
|
||||
Header header = obtainHeader();
|
||||
|
||||
header.setField(newMessageId(hostname));
|
||||
}
|
||||
|
||||
protected abstract Field newMessageId(String hostname);
|
||||
|
||||
/**
|
||||
* Returns the (decoded) value of the <i>Subject</i> header field of this
|
||||
* message or <code>null</code> if it is not present.
|
||||
*
|
||||
* @return the subject of this message.
|
||||
*/
|
||||
public String getSubject() {
|
||||
UnstructuredField field = obtainField(FieldName.SUBJECT);
|
||||
if (field == null)
|
||||
return null;
|
||||
|
||||
return field.getValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the <i>Subject</i> header field for this message. The specified
|
||||
* string may contain non-ASCII characters, in which case it gets encoded as
|
||||
* an 'encoded-word' automatically. A <code>Header</code> is created if
|
||||
* this message does not already have one.
|
||||
*
|
||||
* @param subject
|
||||
* subject to set or <code>null</code> to remove the subject
|
||||
* header field.
|
||||
*/
|
||||
public void setSubject(String subject) {
|
||||
Header header = obtainHeader();
|
||||
|
||||
if (subject == null) {
|
||||
header.removeFields(FieldName.SUBJECT);
|
||||
} else {
|
||||
header.setField(newSubject(subject));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of the <i>Date</i> header field of this message as
|
||||
* <code>Date</code> object or <code>null</code> if it is not present.
|
||||
*
|
||||
* @return the date of this message.
|
||||
*/
|
||||
public Date getDate() {
|
||||
DateTimeField dateField = obtainField(FieldName.DATE);
|
||||
if (dateField == null)
|
||||
return null;
|
||||
|
||||
return dateField.getDate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the <i>Date</i> header field for this message. This method uses the
|
||||
* default <code>TimeZone</code> of this host to encode the specified
|
||||
* <code>Date</code> object into a string.
|
||||
*
|
||||
* @param date
|
||||
* date to set or <code>null</code> to remove the date header
|
||||
* field.
|
||||
*/
|
||||
public void setDate(Date date) {
|
||||
setDate(date, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the <i>Date</i> header field for this message. The specified
|
||||
* <code>TimeZone</code> is used to encode the specified <code>Date</code>
|
||||
* object into a string.
|
||||
*
|
||||
* @param date
|
||||
* date to set or <code>null</code> to remove the date header
|
||||
* field.
|
||||
* @param zone
|
||||
* a time zone.
|
||||
*/
|
||||
public void setDate(Date date, TimeZone zone) {
|
||||
Header header = obtainHeader();
|
||||
|
||||
if (date == null) {
|
||||
header.removeFields(FieldName.DATE);
|
||||
} else {
|
||||
header.setField(newDate(date, zone));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of the <i>Sender</i> header field of this message as
|
||||
* <code>Mailbox</code> object or <code>null</code> if it is not
|
||||
* present.
|
||||
*
|
||||
* @return the sender of this message.
|
||||
*/
|
||||
public Mailbox getSender() {
|
||||
return getMailbox(FieldName.SENDER);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the <i>Sender</i> header field of this message to the specified
|
||||
* mailbox address.
|
||||
*
|
||||
* @param sender
|
||||
* address to set or <code>null</code> to remove the header
|
||||
* field.
|
||||
*/
|
||||
public void setSender(Mailbox sender) {
|
||||
setMailbox(FieldName.SENDER, sender);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of the <i>From</i> header field of this message as
|
||||
* <code>MailboxList</code> object or <code>null</code> if it is not
|
||||
* present.
|
||||
*
|
||||
* @return value of the from field of this message.
|
||||
*/
|
||||
public MailboxList getFrom() {
|
||||
return getMailboxList(FieldName.FROM);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the <i>From</i> header field of this message to the specified
|
||||
* mailbox address.
|
||||
*
|
||||
* @param from
|
||||
* address to set or <code>null</code> to remove the header
|
||||
* field.
|
||||
*/
|
||||
public void setFrom(Mailbox from) {
|
||||
setMailboxList(FieldName.FROM, from);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the <i>From</i> header field of this message to the specified
|
||||
* mailbox addresses.
|
||||
*
|
||||
* @param from
|
||||
* addresses to set or <code>null</code> or no arguments to
|
||||
* remove the header field.
|
||||
*/
|
||||
public void setFrom(Mailbox... from) {
|
||||
setMailboxList(FieldName.FROM, from);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the <i>From</i> header field of this message to the specified
|
||||
* mailbox addresses.
|
||||
*
|
||||
* @param from
|
||||
* addresses to set or <code>null</code> or an empty collection
|
||||
* to remove the header field.
|
||||
*/
|
||||
public void setFrom(Collection<Mailbox> from) {
|
||||
setMailboxList(FieldName.FROM, from);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of the <i>To</i> header field of this message as
|
||||
* <code>AddressList</code> object or <code>null</code> if it is not
|
||||
* present.
|
||||
*
|
||||
* @return value of the to field of this message.
|
||||
*/
|
||||
public AddressList getTo() {
|
||||
return getAddressList(FieldName.TO);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the <i>To</i> header field of this message to the specified
|
||||
* address.
|
||||
*
|
||||
* @param to
|
||||
* address to set or <code>null</code> to remove the header
|
||||
* field.
|
||||
*/
|
||||
public void setTo(Address to) {
|
||||
setAddressList(FieldName.TO, to);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the <i>To</i> header field of this message to the specified
|
||||
* addresses.
|
||||
*
|
||||
* @param to
|
||||
* addresses to set or <code>null</code> or no arguments to
|
||||
* remove the header field.
|
||||
*/
|
||||
public void setTo(Address... to) {
|
||||
setAddressList(FieldName.TO, to);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the <i>To</i> header field of this message to the specified
|
||||
* addresses.
|
||||
*
|
||||
* @param to
|
||||
* addresses to set or <code>null</code> or an empty collection
|
||||
* to remove the header field.
|
||||
*/
|
||||
public void setTo(Collection<Address> to) {
|
||||
setAddressList(FieldName.TO, to);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of the <i>Cc</i> header field of this message as
|
||||
* <code>AddressList</code> object or <code>null</code> if it is not
|
||||
* present.
|
||||
*
|
||||
* @return value of the cc field of this message.
|
||||
*/
|
||||
public AddressList getCc() {
|
||||
return getAddressList(FieldName.CC);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the <i>Cc</i> header field of this message to the specified
|
||||
* address.
|
||||
*
|
||||
* @param cc
|
||||
* address to set or <code>null</code> to remove the header
|
||||
* field.
|
||||
*/
|
||||
public void setCc(Address cc) {
|
||||
setAddressList(FieldName.CC, cc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the <i>Cc</i> header field of this message to the specified
|
||||
* addresses.
|
||||
*
|
||||
* @param cc
|
||||
* addresses to set or <code>null</code> or no arguments to
|
||||
* remove the header field.
|
||||
*/
|
||||
public void setCc(Address... cc) {
|
||||
setAddressList(FieldName.CC, cc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the <i>Cc</i> header field of this message to the specified
|
||||
* addresses.
|
||||
*
|
||||
* @param cc
|
||||
* addresses to set or <code>null</code> or an empty collection
|
||||
* to remove the header field.
|
||||
*/
|
||||
public void setCc(Collection<Address> cc) {
|
||||
setAddressList(FieldName.CC, cc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of the <i>Bcc</i> header field of this message as
|
||||
* <code>AddressList</code> object or <code>null</code> if it is not
|
||||
* present.
|
||||
*
|
||||
* @return value of the bcc field of this message.
|
||||
*/
|
||||
public AddressList getBcc() {
|
||||
return getAddressList(FieldName.BCC);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the <i>Bcc</i> header field of this message to the specified
|
||||
* address.
|
||||
*
|
||||
* @param bcc
|
||||
* address to set or <code>null</code> to remove the header
|
||||
* field.
|
||||
*/
|
||||
public void setBcc(Address bcc) {
|
||||
setAddressList(FieldName.BCC, bcc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the <i>Bcc</i> header field of this message to the specified
|
||||
* addresses.
|
||||
*
|
||||
* @param bcc
|
||||
* addresses to set or <code>null</code> or no arguments to
|
||||
* remove the header field.
|
||||
*/
|
||||
public void setBcc(Address... bcc) {
|
||||
setAddressList(FieldName.BCC, bcc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the <i>Bcc</i> header field of this message to the specified
|
||||
* addresses.
|
||||
*
|
||||
* @param bcc
|
||||
* addresses to set or <code>null</code> or an empty collection
|
||||
* to remove the header field.
|
||||
*/
|
||||
public void setBcc(Collection<Address> bcc) {
|
||||
setAddressList(FieldName.BCC, bcc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of the <i>Reply-To</i> header field of this message as
|
||||
* <code>AddressList</code> object or <code>null</code> if it is not
|
||||
* present.
|
||||
*
|
||||
* @return value of the reply to field of this message.
|
||||
*/
|
||||
public AddressList getReplyTo() {
|
||||
return getAddressList(FieldName.REPLY_TO);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the <i>Reply-To</i> header field of this message to the specified
|
||||
* address.
|
||||
*
|
||||
* @param replyTo
|
||||
* address to set or <code>null</code> to remove the header
|
||||
* field.
|
||||
*/
|
||||
public void setReplyTo(Address replyTo) {
|
||||
setAddressList(FieldName.REPLY_TO, replyTo);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the <i>Reply-To</i> header field of this message to the specified
|
||||
* addresses.
|
||||
*
|
||||
* @param replyTo
|
||||
* addresses to set or <code>null</code> or no arguments to
|
||||
* remove the header field.
|
||||
*/
|
||||
public void setReplyTo(Address... replyTo) {
|
||||
setAddressList(FieldName.REPLY_TO, replyTo);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the <i>Reply-To</i> header field of this message to the specified
|
||||
* addresses.
|
||||
*
|
||||
* @param replyTo
|
||||
* addresses to set or <code>null</code> or an empty collection
|
||||
* to remove the header field.
|
||||
*/
|
||||
public void setReplyTo(Collection<Address> replyTo) {
|
||||
setAddressList(FieldName.REPLY_TO, replyTo);
|
||||
}
|
||||
|
||||
private Mailbox getMailbox(String fieldName) {
|
||||
MailboxField field = obtainField(fieldName);
|
||||
if (field == null)
|
||||
return null;
|
||||
|
||||
return field.getMailbox();
|
||||
}
|
||||
|
||||
private void setMailbox(String fieldName, Mailbox mailbox) {
|
||||
Header header = obtainHeader();
|
||||
|
||||
if (mailbox == null) {
|
||||
header.removeFields(fieldName);
|
||||
} else {
|
||||
header.setField(newMailbox(fieldName, mailbox));
|
||||
}
|
||||
}
|
||||
|
||||
private MailboxList getMailboxList(String fieldName) {
|
||||
MailboxListField field = obtainField(fieldName);
|
||||
if (field == null)
|
||||
return null;
|
||||
|
||||
return field.getMailboxList();
|
||||
}
|
||||
|
||||
private void setMailboxList(String fieldName, Mailbox mailbox) {
|
||||
setMailboxList(fieldName, mailbox == null ? null : Collections
|
||||
.singleton(mailbox));
|
||||
}
|
||||
|
||||
private void setMailboxList(String fieldName, Mailbox... mailboxes) {
|
||||
setMailboxList(fieldName, mailboxes == null ? null : Arrays
|
||||
.asList(mailboxes));
|
||||
}
|
||||
|
||||
private void setMailboxList(String fieldName, Collection<Mailbox> mailboxes) {
|
||||
Header header = obtainHeader();
|
||||
|
||||
if (mailboxes == null || mailboxes.isEmpty()) {
|
||||
header.removeFields(fieldName);
|
||||
} else {
|
||||
header.setField(newMailboxList(fieldName, mailboxes));
|
||||
}
|
||||
}
|
||||
|
||||
private AddressList getAddressList(String fieldName) {
|
||||
AddressListField field = obtainField(fieldName);
|
||||
if (field == null)
|
||||
return null;
|
||||
|
||||
return field.getAddressList();
|
||||
}
|
||||
|
||||
private void setAddressList(String fieldName, Address address) {
|
||||
setAddressList(fieldName, address == null ? null : Collections
|
||||
.singleton(address));
|
||||
}
|
||||
|
||||
private void setAddressList(String fieldName, Address... addresses) {
|
||||
setAddressList(fieldName, addresses == null ? null : Arrays
|
||||
.asList(addresses));
|
||||
}
|
||||
|
||||
private void setAddressList(String fieldName, Collection<Address> addresses) {
|
||||
Header header = obtainHeader();
|
||||
|
||||
if (addresses == null || addresses.isEmpty()) {
|
||||
header.removeFields(fieldName);
|
||||
} else {
|
||||
header.setField(newAddressList(fieldName, addresses));
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract AddressListField newAddressList(String fieldName, Collection<Address> addresses);
|
||||
|
||||
protected abstract UnstructuredField newSubject(String subject);
|
||||
|
||||
protected abstract DateTimeField newDate(Date date, TimeZone zone);
|
||||
|
||||
protected abstract MailboxField newMailbox(String fieldName, Mailbox mailbox);
|
||||
|
||||
protected abstract MailboxListField newMailboxList(String fieldName, Collection<Mailbox> mailboxes);
|
||||
|
||||
|
||||
}
|
46
src/org/apache/james/mime4j/dom/MessageBuilder.java
Normal file
46
src/org/apache/james/mime4j/dom/MessageBuilder.java
Normal file
@ -0,0 +1,46 @@
|
||||
/****************************************************************
|
||||
* Licensed to the Apache Software Foundation (ASF) under one *
|
||||
* or more contributor license agreements. See the NOTICE file *
|
||||
* distributed with this work for additional information *
|
||||
* regarding copyright ownership. The ASF licenses this file *
|
||||
* to you 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. *
|
||||
****************************************************************/
|
||||
|
||||
package org.apache.james.mime4j.dom;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import org.apache.james.mime4j.MimeException;
|
||||
import org.apache.james.mime4j.codec.DecodeMonitor;
|
||||
|
||||
/**
|
||||
* Defines the API to obtain Message instances from a mime stream.
|
||||
*/
|
||||
public abstract class MessageBuilder {
|
||||
|
||||
public abstract Message newMessage();
|
||||
|
||||
public abstract Message newMessage(Message source);
|
||||
|
||||
public abstract Message parse(InputStream source) throws MimeException, IOException;
|
||||
|
||||
public abstract void setDecodeMonitor(
|
||||
DecodeMonitor decodeMonitor);
|
||||
|
||||
public abstract void setContentDecoding(boolean contentDecoding);
|
||||
|
||||
public abstract void setFlatMode();
|
||||
|
||||
}
|
41
src/org/apache/james/mime4j/dom/MessageBuilderFactory.java
Normal file
41
src/org/apache/james/mime4j/dom/MessageBuilderFactory.java
Normal file
@ -0,0 +1,41 @@
|
||||
/****************************************************************
|
||||
* Licensed to the Apache Software Foundation (ASF) under one *
|
||||
* or more contributor license agreements. See the NOTICE file *
|
||||
* distributed with this work for additional information *
|
||||
* regarding copyright ownership. The ASF licenses this file *
|
||||
* to you 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. *
|
||||
****************************************************************/
|
||||
|
||||
package org.apache.james.mime4j.dom;
|
||||
|
||||
import org.apache.james.mime4j.MimeException;
|
||||
|
||||
/**
|
||||
* A MessageBuilderFactory is used to create EntityBuilder instances.
|
||||
*
|
||||
* MessageBuilderFactory.newInstance() is used to get access to an implementation
|
||||
* of MessageBuilderFactory.
|
||||
* Then the method newMessageBuilder is used to create a new EntityBuilder object.
|
||||
*/
|
||||
public abstract class MessageBuilderFactory {
|
||||
|
||||
public abstract MessageBuilder newMessageBuilder() throws MimeException;
|
||||
|
||||
public static MessageBuilderFactory newInstance() throws MimeException {
|
||||
return ServiceLoader.load(MessageBuilderFactory.class);
|
||||
}
|
||||
|
||||
public abstract void setAttribute(String name, Object value) throws IllegalArgumentException;
|
||||
|
||||
}
|
238
src/org/apache/james/mime4j/dom/Multipart.java
Normal file
238
src/org/apache/james/mime4j/dom/Multipart.java
Normal file
@ -0,0 +1,238 @@
|
||||
/****************************************************************
|
||||
* Licensed to the Apache Software Foundation (ASF) under one *
|
||||
* or more contributor license agreements. See the NOTICE file *
|
||||
* distributed with this work for additional information *
|
||||
* regarding copyright ownership. The ASF licenses this file *
|
||||
* to you 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. *
|
||||
****************************************************************/
|
||||
|
||||
package org.apache.james.mime4j.dom;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Represents a MIME multipart body (see RFC 2045).A multipart body has a
|
||||
* ordered list of body parts. The multipart body also has a preamble and
|
||||
* epilogue. The preamble consists of whatever characters appear before the
|
||||
* first body part while the epilogue consists of whatever characters come after
|
||||
* the last body part.
|
||||
*/
|
||||
public abstract class Multipart implements Body {
|
||||
|
||||
protected List<Entity> bodyParts = new LinkedList<Entity>();
|
||||
private Entity parent = null;
|
||||
|
||||
private String subType;
|
||||
|
||||
/**
|
||||
* Creates a new empty <code>Multipart</code> instance.
|
||||
*/
|
||||
public Multipart(String subType) {
|
||||
this.subType = subType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the multipart sub-type. E.g. <code>alternative</code> (the
|
||||
* default) or <code>parallel</code>. See RFC 2045 for common sub-types
|
||||
* and their meaning.
|
||||
*
|
||||
* @return the multipart sub-type.
|
||||
*/
|
||||
public String getSubType() {
|
||||
return subType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the multipart sub-type. E.g. <code>alternative</code> or
|
||||
* <code>parallel</code>. See RFC 2045 for common sub-types and their
|
||||
* meaning.
|
||||
*
|
||||
* @param subType
|
||||
* the sub-type.
|
||||
*/
|
||||
public void setSubType(String subType) {
|
||||
this.subType = subType;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.apache.james.mime4j.dom.Body#getParent()
|
||||
*/
|
||||
public Entity getParent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.apache.james.mime4j.dom.Body#setParent(org.apache.james.mime4j.dom.Entity)
|
||||
*/
|
||||
public void setParent(Entity parent) {
|
||||
this.parent = parent;
|
||||
for (Entity bodyPart : bodyParts) {
|
||||
bodyPart.setParent(parent);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of body parts.
|
||||
*
|
||||
* @return number of <code>Entity</code> objects.
|
||||
*/
|
||||
public int getCount() {
|
||||
return bodyParts.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the list of body parts. The list is immutable.
|
||||
*
|
||||
* @return the list of <code>Entity</code> objects.
|
||||
*/
|
||||
public List<Entity> getBodyParts() {
|
||||
return Collections.unmodifiableList(bodyParts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the list of body parts.
|
||||
*
|
||||
* @param bodyParts
|
||||
* the new list of <code>Entity</code> objects.
|
||||
*/
|
||||
public void setBodyParts(List<Entity> bodyParts) {
|
||||
this.bodyParts = bodyParts;
|
||||
for (Entity bodyPart : bodyParts) {
|
||||
bodyPart.setParent(parent);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a body part to the end of the list of body parts.
|
||||
*
|
||||
* @param bodyPart
|
||||
* the body part.
|
||||
*/
|
||||
public void addBodyPart(Entity bodyPart) {
|
||||
if (bodyPart == null)
|
||||
throw new IllegalArgumentException();
|
||||
|
||||
bodyParts.add(bodyPart);
|
||||
bodyPart.setParent(parent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts a body part at the specified position in the list of body parts.
|
||||
*
|
||||
* @param bodyPart
|
||||
* the body part.
|
||||
* @param index
|
||||
* index at which the specified body part is to be inserted.
|
||||
* @throws IndexOutOfBoundsException
|
||||
* if the index is out of range (index < 0 || index >
|
||||
* getCount()).
|
||||
*/
|
||||
public void addBodyPart(Entity bodyPart, int index) {
|
||||
if (bodyPart == null)
|
||||
throw new IllegalArgumentException();
|
||||
|
||||
bodyParts.add(index, bodyPart);
|
||||
bodyPart.setParent(parent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the body part at the specified position in the list of body
|
||||
* parts.
|
||||
*
|
||||
* @param index
|
||||
* index of the body part to be removed.
|
||||
* @return the removed body part.
|
||||
* @throws IndexOutOfBoundsException
|
||||
* if the index is out of range (index < 0 || index >=
|
||||
* getCount()).
|
||||
*/
|
||||
public Entity removeBodyPart(int index) {
|
||||
Entity bodyPart = bodyParts.remove(index);
|
||||
bodyPart.setParent(null);
|
||||
return bodyPart;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces the body part at the specified position in the list of body
|
||||
* parts with the specified body part.
|
||||
*
|
||||
* @param bodyPart
|
||||
* body part to be stored at the specified position.
|
||||
* @param index
|
||||
* index of body part to replace.
|
||||
* @return the replaced body part.
|
||||
* @throws IndexOutOfBoundsException
|
||||
* if the index is out of range (index < 0 || index >=
|
||||
* getCount()).
|
||||
*/
|
||||
public Entity replaceBodyPart(Entity bodyPart, int index) {
|
||||
if (bodyPart == null)
|
||||
throw new IllegalArgumentException();
|
||||
|
||||
Entity replacedEntity = bodyParts.set(index, bodyPart);
|
||||
if (bodyPart == replacedEntity)
|
||||
throw new IllegalArgumentException(
|
||||
"Cannot replace body part with itself");
|
||||
|
||||
bodyPart.setParent(parent);
|
||||
replacedEntity.setParent(null);
|
||||
|
||||
return replacedEntity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the preamble or null if the message has no preamble.
|
||||
*
|
||||
* @return the preamble.
|
||||
*/
|
||||
public abstract String getPreamble();
|
||||
|
||||
/**
|
||||
* Sets the preamble with a value or null to remove the preamble.
|
||||
*
|
||||
* @param preamble
|
||||
* the preamble.
|
||||
*/
|
||||
public abstract void setPreamble(String preamble);
|
||||
|
||||
/**
|
||||
* Gets the epilogue or null if the message has no epilogue
|
||||
*
|
||||
* @return the epilogue.
|
||||
*/
|
||||
public abstract String getEpilogue();
|
||||
|
||||
/**
|
||||
* Sets the epilogue value, or remove it if the value passed is null.
|
||||
*
|
||||
* @param epilogue
|
||||
* the epilogue.
|
||||
*/
|
||||
public abstract void setEpilogue(String epilogue);
|
||||
|
||||
/**
|
||||
* Disposes of the BodyParts of this Multipart. Note that the dispose call
|
||||
* does not get forwarded to the parent entity of this Multipart.
|
||||
*
|
||||
* @see org.apache.james.mime4j.dom.Disposable#dispose()
|
||||
*/
|
||||
public void dispose() {
|
||||
for (Entity bodyPart : bodyParts) {
|
||||
bodyPart.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
95
src/org/apache/james/mime4j/dom/ServiceLoader.java
Normal file
95
src/org/apache/james/mime4j/dom/ServiceLoader.java
Normal file
@ -0,0 +1,95 @@
|
||||
/****************************************************************
|
||||
* Licensed to the Apache Software Foundation (ASF) under one *
|
||||
* or more contributor license agreements. See the NOTICE file *
|
||||
* distributed with this work for additional information *
|
||||
* regarding copyright ownership. The ASF licenses this file *
|
||||
* to you 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. *
|
||||
****************************************************************/
|
||||
package org.apache.james.mime4j.dom;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.URL;
|
||||
import java.util.Enumeration;
|
||||
|
||||
/**
|
||||
* Utility class to load Service Providers (SPI).
|
||||
* This will deprecated as soon as mime4j will be upgraded to Java6
|
||||
* as Java6 has javax.util.ServiceLoader as a core class.
|
||||
*/
|
||||
class ServiceLoader {
|
||||
|
||||
private ServiceLoader() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a Service Provider for the given interface/class (SPI).
|
||||
*/
|
||||
static <T> T load(Class<T> spiClass) {
|
||||
String spiResURI = "META-INF/services/" + spiClass.getName();
|
||||
ClassLoader classLoader = spiClass.getClassLoader();
|
||||
Enumeration<URL> resources;
|
||||
try {
|
||||
resources = classLoader.getResources(spiResURI);
|
||||
} catch (IOException e) {
|
||||
return null;
|
||||
}
|
||||
|
||||
while (resources.hasMoreElements()) {
|
||||
URL resource = resources.nextElement();
|
||||
BufferedReader reader = null;
|
||||
try {
|
||||
reader = new BufferedReader(new InputStreamReader(resource
|
||||
.openStream()));
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
line = line.trim();
|
||||
int cmtIdx = line.indexOf('#');
|
||||
if (cmtIdx != -1) {
|
||||
line = line.substring(0, cmtIdx);
|
||||
line = line.trim();
|
||||
}
|
||||
|
||||
if (line.length() == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Class<?> implClass;
|
||||
try {
|
||||
implClass = classLoader.loadClass(line);
|
||||
|
||||
if (spiClass.isAssignableFrom(implClass)) {
|
||||
Object impl = implClass.newInstance();
|
||||
return spiClass.cast(impl);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
if (reader != null) {
|
||||
try {
|
||||
reader.close();
|
||||
} catch (IOException e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
139
src/org/apache/james/mime4j/dom/SingleBody.java
Normal file
139
src/org/apache/james/mime4j/dom/SingleBody.java
Normal file
@ -0,0 +1,139 @@
|
||||
/****************************************************************
|
||||
* Licensed to the Apache Software Foundation (ASF) under one *
|
||||
* or more contributor license agreements. See the NOTICE file *
|
||||
* distributed with this work for additional information *
|
||||
* regarding copyright ownership. The ASF licenses this file *
|
||||
* to you 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. *
|
||||
****************************************************************/
|
||||
|
||||
package org.apache.james.mime4j.dom;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
/**
|
||||
* Abstract implementation of a single message body; that is, a body that does
|
||||
* not contain (directly or indirectly) any other child bodies. It also provides
|
||||
* the parent functionality required by bodies.
|
||||
*/
|
||||
public abstract class SingleBody implements Body {
|
||||
|
||||
private Entity parent = null;
|
||||
|
||||
/**
|
||||
* Sole constructor.
|
||||
*/
|
||||
protected SingleBody() {
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.apache.james.mime4j.dom.Body#getParent()
|
||||
*/
|
||||
public Entity getParent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.apache.james.mime4j.dom.Body#setParent(org.apache.james.mime4j.dom.Entity)
|
||||
*/
|
||||
public void setParent(Entity parent) {
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a <code>InputStream</code> which reads the bytes of the body.
|
||||
*
|
||||
* @return the stream, transfer decoded
|
||||
* @throws IOException
|
||||
* on I/O errors.
|
||||
*/
|
||||
public abstract InputStream getInputStream() throws IOException;
|
||||
|
||||
/**
|
||||
* Writes this single body to the given stream. The default implementation copies
|
||||
* the input stream obtained by {@link #getInputStream()} to the specified output
|
||||
* stream. May be overwritten by a subclass to improve performance.
|
||||
*
|
||||
* @param out
|
||||
* the stream to write to.
|
||||
* @throws IOException
|
||||
* in case of an I/O error
|
||||
*/
|
||||
public void writeTo(OutputStream out) throws IOException {
|
||||
if (out == null)
|
||||
throw new IllegalArgumentException();
|
||||
|
||||
InputStream in = getInputStream();
|
||||
SingleBody.copy(in, out);
|
||||
in.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a copy of this <code>SingleBody</code> (optional operation).
|
||||
* <p>
|
||||
* The general contract of this method is as follows:
|
||||
* <ul>
|
||||
* <li>Invoking {@link #getParent()} on the copy returns <code>null</code>.
|
||||
* That means that the copy is detached from the parent entity of this
|
||||
* <code>SingleBody</code>. The copy may get attached to a different
|
||||
* entity later on.</li>
|
||||
* <li>The underlying content does not have to be copied. Instead it may be
|
||||
* shared between multiple copies of a <code>SingleBody</code>.</li>
|
||||
* <li>If the underlying content is shared by multiple copies the
|
||||
* implementation has to make sure that the content gets deleted when the
|
||||
* last copy gets disposed of (and not before that).</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* This implementation always throws an
|
||||
* <code>UnsupportedOperationException</code>.
|
||||
*
|
||||
* @return a copy of this <code>SingleBody</code>.
|
||||
* @throws UnsupportedOperationException
|
||||
* if the <code>copy</code> operation is not supported by this
|
||||
* single body.
|
||||
*/
|
||||
public SingleBody copy() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses should override this method if they have allocated resources
|
||||
* that need to be freed explicitly (e.g. cannot be simply reclaimed by the
|
||||
* garbage collector).
|
||||
*
|
||||
* The default implementation of this method does nothing.
|
||||
*
|
||||
* @see org.apache.james.mime4j.dom.Disposable#dispose()
|
||||
*/
|
||||
public void dispose() {
|
||||
}
|
||||
|
||||
static final int DEFAULT_ENCODING_BUFFER_SIZE = 1024;
|
||||
|
||||
/**
|
||||
* Copies the contents of one stream to the other.
|
||||
* @param in not null
|
||||
* @param out not null
|
||||
* @throws IOException
|
||||
*/
|
||||
private static void copy(final InputStream in, final OutputStream out) throws IOException {
|
||||
final byte[] buffer = new byte[DEFAULT_ENCODING_BUFFER_SIZE];
|
||||
int inputLength;
|
||||
while (-1 != (inputLength = in.read(buffer))) {
|
||||
out.write(buffer, 0, inputLength);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -17,26 +17,37 @@
|
||||
* under the License. *
|
||||
****************************************************************/
|
||||
|
||||
package org.apache.james.mime4j.message;
|
||||
package org.apache.james.mime4j.dom;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
|
||||
|
||||
/**
|
||||
* Encapsulates the contents of a <code>text/*</code> entity body.
|
||||
*
|
||||
*
|
||||
* @version $Id: TextBody.java,v 1.3 2004/10/02 12:41:11 ntherning Exp $
|
||||
*/
|
||||
public interface TextBody extends Body {
|
||||
|
||||
public abstract class TextBody extends SingleBody {
|
||||
|
||||
/**
|
||||
* Sole constructor.
|
||||
*/
|
||||
protected TextBody() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the MIME charset of this text body.
|
||||
*
|
||||
* @return the MIME charset.
|
||||
*/
|
||||
public abstract String getMimeCharset();
|
||||
|
||||
/**
|
||||
* Gets a <code>Reader</code> which may be used to read out the contents
|
||||
* of this body.
|
||||
*
|
||||
*
|
||||
* @return the <code>Reader</code>.
|
||||
* @throws IOException on I/O errors.
|
||||
* @throws IOException
|
||||
* on I/O errors.
|
||||
*/
|
||||
Reader getReader() throws IOException;
|
||||
public abstract Reader getReader() throws IOException;
|
||||
|
||||
}
|
@ -17,36 +17,32 @@
|
||||
* under the License. *
|
||||
****************************************************************/
|
||||
|
||||
package org.apache.james.mime4j.field.address;
|
||||
package org.apache.james.mime4j.dom.address;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* The abstract base for classes that represent RFC2822 addresses.
|
||||
* This includes groups and mailboxes.
|
||||
*
|
||||
* Currently, no public methods are introduced on this class.
|
||||
*
|
||||
*
|
||||
* The abstract base for classes that represent RFC2822 addresses. This includes
|
||||
* groups and mailboxes.
|
||||
*/
|
||||
public abstract class Address {
|
||||
public abstract class Address implements Serializable {
|
||||
|
||||
/**
|
||||
* Adds any mailboxes represented by this address
|
||||
* into the given ArrayList. Note that this method
|
||||
* has default (package) access, so a doAddMailboxesTo
|
||||
* method is needed to allow the behavior to be
|
||||
* overridden by subclasses.
|
||||
*/
|
||||
final void addMailboxesTo(ArrayList results) {
|
||||
doAddMailboxesTo(results);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds any mailboxes represented by this address
|
||||
* into the given ArrayList. Must be overridden by
|
||||
* concrete subclasses.
|
||||
*/
|
||||
protected abstract void doAddMailboxesTo(ArrayList results);
|
||||
private static final long serialVersionUID = 634090661990433426L;
|
||||
|
||||
}
|
||||
/**
|
||||
* Adds any mailboxes represented by this address into the given List. Note
|
||||
* that this method has default (package) access, so a doAddMailboxesTo
|
||||
* method is needed to allow the behavior to be overridden by subclasses.
|
||||
*/
|
||||
final void addMailboxesTo(List<Mailbox> results) {
|
||||
doAddMailboxesTo(results);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds any mailboxes represented by this address into the given List. Must
|
||||
* be overridden by concrete subclasses.
|
||||
*/
|
||||
protected abstract void doAddMailboxesTo(List<Mailbox> results);
|
||||
|
||||
}
|
98
src/org/apache/james/mime4j/dom/address/AddressList.java
Normal file
98
src/org/apache/james/mime4j/dom/address/AddressList.java
Normal file
@ -0,0 +1,98 @@
|
||||
/****************************************************************
|
||||
* Licensed to the Apache Software Foundation (ASF) under one *
|
||||
* or more contributor license agreements. See the NOTICE file *
|
||||
* distributed with this work for additional information *
|
||||
* regarding copyright ownership. The ASF licenses this file *
|
||||
* to you 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. *
|
||||
****************************************************************/
|
||||
|
||||
package org.apache.james.mime4j.dom.address;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.AbstractList;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* An immutable, random-access list of Address objects.
|
||||
*/
|
||||
public class AddressList extends AbstractList<Address> implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final List<? extends Address> addresses;
|
||||
|
||||
/**
|
||||
* @param addresses
|
||||
* A List that contains only Address objects.
|
||||
* @param dontCopy
|
||||
* true iff it is not possible for the addresses list to be
|
||||
* modified by someone else.
|
||||
*/
|
||||
public AddressList(List<? extends Address> addresses, boolean dontCopy) {
|
||||
if (addresses != null)
|
||||
this.addresses = dontCopy ? addresses : new ArrayList<Address>(
|
||||
addresses);
|
||||
else
|
||||
this.addresses = Collections.emptyList();
|
||||
}
|
||||
|
||||
/**
|
||||
* The number of elements in this list.
|
||||
*/
|
||||
@Override
|
||||
public int size() {
|
||||
return addresses.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an address.
|
||||
*/
|
||||
@Override
|
||||
public Address get(int index) {
|
||||
return addresses.get(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a flat list of all mailboxes represented in this address list.
|
||||
* Use this if you don't care about grouping.
|
||||
*/
|
||||
public MailboxList flatten() {
|
||||
// in the common case, all addresses are mailboxes
|
||||
boolean groupDetected = false;
|
||||
for (Address addr : addresses) {
|
||||
if (!(addr instanceof Mailbox)) {
|
||||
groupDetected = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!groupDetected) {
|
||||
@SuppressWarnings("unchecked")
|
||||
final List<Mailbox> mailboxes = (List<Mailbox>) addresses;
|
||||
return new MailboxList(mailboxes, true);
|
||||
}
|
||||
|
||||
List<Mailbox> results = new ArrayList<Mailbox>();
|
||||
for (Address addr : addresses) {
|
||||
addr.addMailboxesTo(results);
|
||||
}
|
||||
|
||||
// copy-on-construct this time, because subclasses
|
||||
// could have held onto a reference to the results
|
||||
return new MailboxList(results, false);
|
||||
}
|
||||
|
||||
}
|
95
src/org/apache/james/mime4j/dom/address/DomainList.java
Normal file
95
src/org/apache/james/mime4j/dom/address/DomainList.java
Normal file
@ -0,0 +1,95 @@
|
||||
/****************************************************************
|
||||
* Licensed to the Apache Software Foundation (ASF) under one *
|
||||
* or more contributor license agreements. See the NOTICE file *
|
||||
* distributed with this work for additional information *
|
||||
* regarding copyright ownership. The ASF licenses this file *
|
||||
* to you 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. *
|
||||
****************************************************************/
|
||||
|
||||
package org.apache.james.mime4j.dom.address;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.AbstractList;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* An immutable, random-access list of Strings (that are supposedly domain names
|
||||
* or domain literals).
|
||||
*/
|
||||
public class DomainList extends AbstractList<String> implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final List<String> domains;
|
||||
|
||||
/**
|
||||
* @param domains
|
||||
* A List that contains only String objects.
|
||||
* @param dontCopy
|
||||
* true iff it is not possible for the domains list to be
|
||||
* modified by someone else.
|
||||
*/
|
||||
public DomainList(List<String> domains, boolean dontCopy) {
|
||||
if (domains != null)
|
||||
this.domains = dontCopy ? domains : new ArrayList<String>(domains);
|
||||
else
|
||||
this.domains = Collections.emptyList();
|
||||
}
|
||||
|
||||
/**
|
||||
* The number of elements in this list.
|
||||
*/
|
||||
@Override
|
||||
public int size() {
|
||||
return domains.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the domain name or domain literal at the specified index.
|
||||
*
|
||||
* @throws IndexOutOfBoundsException
|
||||
* If index is < 0 or >= size().
|
||||
*/
|
||||
@Override
|
||||
public String get(int index) {
|
||||
return domains.get(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of domains formatted as a route string (not including
|
||||
* the trailing ':').
|
||||
*/
|
||||
public String toRouteString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
for (String domain : domains) {
|
||||
if (sb.length() > 0) {
|
||||
sb.append(',');
|
||||
}
|
||||
|
||||
sb.append("@");
|
||||
sb.append(domain);
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return toRouteString();
|
||||
}
|
||||
|
||||
}
|
113
src/org/apache/james/mime4j/dom/address/Group.java
Normal file
113
src/org/apache/james/mime4j/dom/address/Group.java
Normal file
@ -0,0 +1,113 @@
|
||||
/****************************************************************
|
||||
* Licensed to the Apache Software Foundation (ASF) under one *
|
||||
* or more contributor license agreements. See the NOTICE file *
|
||||
* distributed with this work for additional information *
|
||||
* regarding copyright ownership. The ASF licenses this file *
|
||||
* to you 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. *
|
||||
****************************************************************/
|
||||
|
||||
package org.apache.james.mime4j.dom.address;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A named group of zero or more mailboxes.
|
||||
*/
|
||||
public class Group extends Address {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final String name;
|
||||
private final MailboxList mailboxList;
|
||||
|
||||
/**
|
||||
* @param name
|
||||
* The group name.
|
||||
* @param mailboxes
|
||||
* The mailboxes in this group.
|
||||
*/
|
||||
public Group(String name, MailboxList mailboxes) {
|
||||
if (name == null)
|
||||
throw new IllegalArgumentException();
|
||||
if (mailboxes == null)
|
||||
throw new IllegalArgumentException();
|
||||
|
||||
this.name = name;
|
||||
this.mailboxList = mailboxes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param name
|
||||
* The group name.
|
||||
* @param mailboxes
|
||||
* The mailboxes in this group.
|
||||
*/
|
||||
public Group(String name, Mailbox... mailboxes) {
|
||||
this(name, new MailboxList(Arrays.asList(mailboxes), true));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param name
|
||||
* The group name.
|
||||
* @param mailboxes
|
||||
* The mailboxes in this group.
|
||||
*/
|
||||
public Group(String name, Collection<Mailbox> mailboxes) {
|
||||
this(name, new MailboxList(new ArrayList<Mailbox>(mailboxes), true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the group name.
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the mailboxes in this group.
|
||||
*/
|
||||
public MailboxList getMailboxes() {
|
||||
return mailboxList;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doAddMailboxesTo(List<Mailbox> results) {
|
||||
for (Mailbox mailbox : mailboxList) {
|
||||
results.add(mailbox);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(name);
|
||||
sb.append(':');
|
||||
boolean first = true;
|
||||
for (Mailbox mailbox : mailboxList) {
|
||||
if (first) {
|
||||
first = false;
|
||||
} else {
|
||||
sb.append(',');
|
||||
}
|
||||
sb.append(' ');
|
||||
sb.append(mailbox);
|
||||
}
|
||||
sb.append(";");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
}
|
204
src/org/apache/james/mime4j/dom/address/Mailbox.java
Normal file
204
src/org/apache/james/mime4j/dom/address/Mailbox.java
Normal file
@ -0,0 +1,204 @@
|
||||
/****************************************************************
|
||||
* Licensed to the Apache Software Foundation (ASF) under one *
|
||||
* or more contributor license agreements. See the NOTICE file *
|
||||
* distributed with this work for additional information *
|
||||
* regarding copyright ownership. The ASF licenses this file *
|
||||
* to you 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. *
|
||||
****************************************************************/
|
||||
|
||||
package org.apache.james.mime4j.dom.address;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import org.apache.james.mime4j.util.LangUtils;
|
||||
|
||||
/**
|
||||
* Represents a single e-mail address.
|
||||
*/
|
||||
public class Mailbox extends Address {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private static final DomainList EMPTY_ROUTE_LIST = new DomainList(
|
||||
Collections.<String> emptyList(), true);
|
||||
|
||||
private final String name;
|
||||
private final DomainList route;
|
||||
private final String localPart;
|
||||
private final String domain;
|
||||
|
||||
/**
|
||||
* Creates a named mailbox with a route. Routes are obsolete.
|
||||
*
|
||||
* @param name
|
||||
* the name of the e-mail address. May be <code>null</code>.
|
||||
* @param route
|
||||
* The zero or more domains that make up the route. May be
|
||||
* <code>null</code>.
|
||||
* @param localPart
|
||||
* The part of the e-mail address to the left of the "@".
|
||||
* @param domain
|
||||
* The part of the e-mail address to the right of the "@".
|
||||
*/
|
||||
public Mailbox(String name, DomainList route, String localPart,
|
||||
String domain) {
|
||||
if (localPart == null || localPart.length() == 0)
|
||||
throw new IllegalArgumentException();
|
||||
|
||||
this.name = name == null || name.length() == 0 ? null : name;
|
||||
this.route = route == null ? EMPTY_ROUTE_LIST : route;
|
||||
this.localPart = localPart;
|
||||
this.domain = domain == null || domain.length() == 0 ? null : domain;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a named mailbox based on an unnamed mailbox. Package private;
|
||||
* internally used by Builder.
|
||||
*/
|
||||
Mailbox(String name, Mailbox baseMailbox) {
|
||||
this(name, baseMailbox.getRoute(), baseMailbox.getLocalPart(),
|
||||
baseMailbox.getDomain());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an unnamed mailbox without a route. Routes are obsolete.
|
||||
*
|
||||
* @param localPart
|
||||
* The part of the e-mail address to the left of the "@".
|
||||
* @param domain
|
||||
* The part of the e-mail address to the right of the "@".
|
||||
*/
|
||||
public Mailbox(String localPart, String domain) {
|
||||
this(null, null, localPart, domain);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an unnamed mailbox with a route. Routes are obsolete.
|
||||
*
|
||||
* @param route
|
||||
* The zero or more domains that make up the route. May be
|
||||
* <code>null</code>.
|
||||
* @param localPart
|
||||
* The part of the e-mail address to the left of the "@".
|
||||
* @param domain
|
||||
* The part of the e-mail address to the right of the "@".
|
||||
*/
|
||||
public Mailbox(DomainList route, String localPart, String domain) {
|
||||
this(null, route, localPart, domain);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a named mailbox without a route. Routes are obsolete.
|
||||
*
|
||||
* @param name
|
||||
* the name of the e-mail address. May be <code>null</code>.
|
||||
* @param localPart
|
||||
* The part of the e-mail address to the left of the "@".
|
||||
* @param domain
|
||||
* The part of the e-mail address to the right of the "@".
|
||||
*/
|
||||
public Mailbox(String name, String localPart, String domain) {
|
||||
this(name, null, localPart, domain);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the mailbox or <code>null</code> if it does not
|
||||
* have a name.
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the route list. If the mailbox does not have a route an empty
|
||||
* domain list is returned.
|
||||
*/
|
||||
public DomainList getRoute() {
|
||||
return route;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the left part of the e-mail address (before "@").
|
||||
*/
|
||||
public String getLocalPart() {
|
||||
return localPart;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the right part of the e-mail address (after "@").
|
||||
*/
|
||||
public String getDomain() {
|
||||
return domain;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the address in the form <i>localPart@domain</i>.
|
||||
*
|
||||
* @return the address part of this mailbox.
|
||||
*/
|
||||
public String getAddress() {
|
||||
if (domain == null) {
|
||||
return localPart;
|
||||
} else {
|
||||
return localPart + '@' + domain;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final void doAddMailboxesTo(List<Mailbox> results) {
|
||||
results.add(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = LangUtils.HASH_SEED;
|
||||
hash = LangUtils.hashCode(hash, this.localPart);
|
||||
hash = LangUtils.hashCode(hash, this.domain != null ?
|
||||
this.domain.toLowerCase(Locale.US) : null);
|
||||
return hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether some other object is "equal to" this mailbox.
|
||||
* <p>
|
||||
* An object is considered to be equal to this mailbox if it is an instance
|
||||
* of class <code>Mailbox</code> that holds the same address as this one.
|
||||
* The domain is considered to be case-insensitive but the local-part is not
|
||||
* (because of RFC 5321: <cite>the local-part of a mailbox MUST BE treated
|
||||
* as case sensitive</cite>).
|
||||
*
|
||||
* @param obj
|
||||
* the object to test for equality.
|
||||
* @return <code>true</code> if the specified object is a
|
||||
* <code>Mailbox</code> that holds the same address as this one.
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == this)
|
||||
return true;
|
||||
if (!(obj instanceof Mailbox))
|
||||
return false;
|
||||
Mailbox that = (Mailbox) obj;
|
||||
return LangUtils.equals(this.localPart, that.localPart) &&
|
||||
LangUtils.equalsIgnoreCase(this.domain, that.domain);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getAddress();
|
||||
}
|
||||
|
||||
}
|
@ -17,55 +17,52 @@
|
||||
* under the License. *
|
||||
****************************************************************/
|
||||
|
||||
package org.apache.james.mime4j.field.address;
|
||||
package org.apache.james.mime4j.dom.address;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.AbstractList;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* An immutable, random-access list of Mailbox objects.
|
||||
*
|
||||
*
|
||||
*/
|
||||
public class MailboxList {
|
||||
public class MailboxList extends AbstractList<Mailbox> implements Serializable {
|
||||
|
||||
private ArrayList mailboxes;
|
||||
|
||||
/**
|
||||
* @param mailboxes An ArrayList that contains only Mailbox objects.
|
||||
* @param dontCopy true iff it is not possible for the mailboxes ArrayList to be modified by someone else.
|
||||
*/
|
||||
public MailboxList(ArrayList mailboxes, boolean dontCopy) {
|
||||
if (mailboxes != null)
|
||||
this.mailboxes = (dontCopy ? mailboxes : (ArrayList) mailboxes.clone());
|
||||
else
|
||||
this.mailboxes = new ArrayList(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* The number of elements in this list.
|
||||
*/
|
||||
public int size() {
|
||||
return mailboxes.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an address.
|
||||
*/
|
||||
public Mailbox get(int index) {
|
||||
if (0 > index || size() <= index)
|
||||
throw new IndexOutOfBoundsException();
|
||||
return (Mailbox) mailboxes.get(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dumps a representation of this mailbox list to
|
||||
* stdout, for debugging purposes.
|
||||
*/
|
||||
public void print() {
|
||||
for (int i = 0; i < size(); i++) {
|
||||
Mailbox mailbox = get(i);
|
||||
System.out.println(mailbox.toString());
|
||||
}
|
||||
}
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final List<Mailbox> mailboxes;
|
||||
|
||||
/**
|
||||
* @param mailboxes
|
||||
* A List that contains only Mailbox objects.
|
||||
* @param dontCopy
|
||||
* true iff it is not possible for the mailboxes list to be
|
||||
* modified by someone else.
|
||||
*/
|
||||
public MailboxList(List<Mailbox> mailboxes, boolean dontCopy) {
|
||||
if (mailboxes != null)
|
||||
this.mailboxes = dontCopy ? mailboxes : new ArrayList<Mailbox>(
|
||||
mailboxes);
|
||||
else
|
||||
this.mailboxes = Collections.emptyList();
|
||||
}
|
||||
|
||||
/**
|
||||
* The number of elements in this list.
|
||||
*/
|
||||
@Override
|
||||
public int size() {
|
||||
return mailboxes.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an address.
|
||||
*/
|
||||
@Override
|
||||
public Mailbox get(int index) {
|
||||
return mailboxes.get(index);
|
||||
}
|
||||
|
||||
}
|
@ -17,17 +17,12 @@
|
||||
* under the License. *
|
||||
****************************************************************/
|
||||
|
||||
package org.apache.james.mime4j.field.datetime;
|
||||
package org.apache.james.mime4j.dom.datetime;
|
||||
|
||||
import org.apache.james.mime4j.field.datetime.parser.DateTimeParser;
|
||||
import org.apache.james.mime4j.field.datetime.parser.ParseException;
|
||||
import org.apache.james.mime4j.field.datetime.parser.TokenMgrError;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.Calendar;
|
||||
import java.util.TimeZone;
|
||||
import java.util.Date;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.io.StringReader;
|
||||
import java.util.TimeZone;
|
||||
|
||||
public class DateTime {
|
||||
private final Date date;
|
||||
@ -112,16 +107,59 @@ public class DateTime {
|
||||
}
|
||||
|
||||
public void print() {
|
||||
System.out.println(getYear() + " " + getMonth() + " " + getDay() + "; " + getHour() + " " + getMinute() + " " + getSecond() + " " + getTimeZone());
|
||||
System.out.println(toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getYear() + " " + getMonth() + " " + getDay() + "; " + getHour() + " " + getMinute() + " " + getSecond() + " " + getTimeZone();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int PRIME = 31;
|
||||
int result = 1;
|
||||
result = PRIME * result + ((date == null) ? 0 : date.hashCode());
|
||||
result = PRIME * result + day;
|
||||
result = PRIME * result + hour;
|
||||
result = PRIME * result + minute;
|
||||
result = PRIME * result + month;
|
||||
result = PRIME * result + second;
|
||||
result = PRIME * result + timeZone;
|
||||
result = PRIME * result + year;
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
final DateTime other = (DateTime) obj;
|
||||
if (date == null) {
|
||||
if (other.date != null)
|
||||
return false;
|
||||
} else if (!date.equals(other.date))
|
||||
return false;
|
||||
if (day != other.day)
|
||||
return false;
|
||||
if (hour != other.hour)
|
||||
return false;
|
||||
if (minute != other.minute)
|
||||
return false;
|
||||
if (month != other.month)
|
||||
return false;
|
||||
if (second != other.second)
|
||||
return false;
|
||||
if (timeZone != other.timeZone)
|
||||
return false;
|
||||
if (year != other.year)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public static DateTime parse(String dateString) throws ParseException {
|
||||
try {
|
||||
return new DateTimeParser(new StringReader(dateString)).parseAll();
|
||||
}
|
||||
catch (TokenMgrError err) {
|
||||
throw new ParseException(err.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
28
src/org/apache/james/mime4j/dom/field/AddressListField.java
Normal file
28
src/org/apache/james/mime4j/dom/field/AddressListField.java
Normal file
@ -0,0 +1,28 @@
|
||||
/****************************************************************
|
||||
* Licensed to the Apache Software Foundation (ASF) under one *
|
||||
* or more contributor license agreements. See the NOTICE file *
|
||||
* distributed with this work for additional information *
|
||||
* regarding copyright ownership. The ASF licenses this file *
|
||||
* to you 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. *
|
||||
****************************************************************/
|
||||
|
||||
package org.apache.james.mime4j.dom.field;
|
||||
|
||||
import org.apache.james.mime4j.dom.address.AddressList;
|
||||
|
||||
public interface AddressListField extends ParsedField {
|
||||
|
||||
AddressList getAddressList();
|
||||
|
||||
}
|
@ -0,0 +1,117 @@
|
||||
package org.apache.james.mime4j.dom.field;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
|
||||
public interface ContentDispositionField extends ParsedField {
|
||||
|
||||
/** The <code>inline</code> disposition type. */
|
||||
public static final String DISPOSITION_TYPE_INLINE = "inline";
|
||||
/** The <code>attachment</code> disposition type. */
|
||||
public static final String DISPOSITION_TYPE_ATTACHMENT = "attachment";
|
||||
/** The name of the <code>filename</code> parameter. */
|
||||
public static final String PARAM_FILENAME = "filename";
|
||||
/** The name of the <code>creation-date</code> parameter. */
|
||||
public static final String PARAM_CREATION_DATE = "creation-date";
|
||||
/** The name of the <code>modification-date</code> parameter. */
|
||||
public static final String PARAM_MODIFICATION_DATE = "modification-date";
|
||||
/** The name of the <code>read-date</code> parameter. */
|
||||
public static final String PARAM_READ_DATE = "read-date";
|
||||
/** The name of the <code>size</code> parameter. */
|
||||
public static final String PARAM_SIZE = "size";
|
||||
|
||||
/**
|
||||
* Gets the disposition type defined in this Content-Disposition field.
|
||||
*
|
||||
* @return the disposition type or an empty string if not set.
|
||||
*/
|
||||
String getDispositionType();
|
||||
|
||||
/**
|
||||
* Gets the value of a parameter. Parameter names are case-insensitive.
|
||||
*
|
||||
* @param name
|
||||
* the name of the parameter to get.
|
||||
* @return the parameter value or <code>null</code> if not set.
|
||||
*/
|
||||
String getParameter(String name);
|
||||
|
||||
/**
|
||||
* Gets all parameters.
|
||||
*
|
||||
* @return the parameters.
|
||||
*/
|
||||
Map<String, String> getParameters();
|
||||
|
||||
/**
|
||||
* Determines if the disposition type of this field matches the given one.
|
||||
*
|
||||
* @param dispositionType
|
||||
* the disposition type to match against.
|
||||
* @return <code>true</code> if the disposition type of this field
|
||||
* matches, <code>false</code> otherwise.
|
||||
*/
|
||||
boolean isDispositionType(String dispositionType);
|
||||
|
||||
/**
|
||||
* Return <code>true</code> if the disposition type of this field is
|
||||
* <i>inline</i>, <code>false</code> otherwise.
|
||||
*
|
||||
* @return <code>true</code> if the disposition type of this field is
|
||||
* <i>inline</i>, <code>false</code> otherwise.
|
||||
*/
|
||||
boolean isInline();
|
||||
|
||||
/**
|
||||
* Return <code>true</code> if the disposition type of this field is
|
||||
* <i>attachment</i>, <code>false</code> otherwise.
|
||||
*
|
||||
* @return <code>true</code> if the disposition type of this field is
|
||||
* <i>attachment</i>, <code>false</code> otherwise.
|
||||
*/
|
||||
public abstract boolean isAttachment();
|
||||
|
||||
/**
|
||||
* Gets the value of the <code>filename</code> parameter if set.
|
||||
*
|
||||
* @return the <code>filename</code> parameter value or <code>null</code>
|
||||
* if not set.
|
||||
*/
|
||||
String getFilename();
|
||||
|
||||
/**
|
||||
* Gets the value of the <code>creation-date</code> parameter if set and
|
||||
* valid.
|
||||
*
|
||||
* @return the <code>creation-date</code> parameter value or
|
||||
* <code>null</code> if not set or invalid.
|
||||
*/
|
||||
Date getCreationDate();
|
||||
|
||||
/**
|
||||
* Gets the value of the <code>modification-date</code> parameter if set
|
||||
* and valid.
|
||||
*
|
||||
* @return the <code>modification-date</code> parameter value or
|
||||
* <code>null</code> if not set or invalid.
|
||||
*/
|
||||
Date getModificationDate();
|
||||
|
||||
/**
|
||||
* Gets the value of the <code>read-date</code> parameter if set and
|
||||
* valid.
|
||||
*
|
||||
* @return the <code>read-date</code> parameter value or <code>null</code>
|
||||
* if not set or invalid.
|
||||
*/
|
||||
Date getReadDate();
|
||||
|
||||
/**
|
||||
* Gets the value of the <code>size</code> parameter if set and valid.
|
||||
*
|
||||
* @return the <code>size</code> parameter value or <code>-1</code> if
|
||||
* not set or invalid.
|
||||
*/
|
||||
long getSize();
|
||||
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
/****************************************************************
|
||||
* Licensed to the Apache Software Foundation (ASF) under one *
|
||||
* or more contributor license agreements. See the NOTICE file *
|
||||
* distributed with this work for additional information *
|
||||
* regarding copyright ownership. The ASF licenses this file *
|
||||
* to you 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. *
|
||||
****************************************************************/
|
||||
|
||||
package org.apache.james.mime4j.dom.field;
|
||||
|
||||
public interface ContentTransferEncodingField extends ParsedField {
|
||||
|
||||
/**
|
||||
* Gets the encoding defined in this field.
|
||||
*
|
||||
* @return the encoding or an empty string if not set.
|
||||
*/
|
||||
String getEncoding();
|
||||
|
||||
}
|
97
src/org/apache/james/mime4j/dom/field/ContentTypeField.java
Normal file
97
src/org/apache/james/mime4j/dom/field/ContentTypeField.java
Normal file
@ -0,0 +1,97 @@
|
||||
/****************************************************************
|
||||
* Licensed to the Apache Software Foundation (ASF) under one *
|
||||
* or more contributor license agreements. See the NOTICE file *
|
||||
* distributed with this work for additional information *
|
||||
* regarding copyright ownership. The ASF licenses this file *
|
||||
* to you 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. *
|
||||
****************************************************************/
|
||||
|
||||
package org.apache.james.mime4j.dom.field;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public interface ContentTypeField extends ParsedField {
|
||||
|
||||
/** The prefix of all <code>multipart</code> MIME types. */
|
||||
public static final String TYPE_MULTIPART_PREFIX = "multipart/";
|
||||
/** The <code>multipart/digest</code> MIME type. */
|
||||
public static final String TYPE_MULTIPART_DIGEST = "multipart/digest";
|
||||
/** The <code>text/plain</code> MIME type. */
|
||||
public static final String TYPE_TEXT_PLAIN = "text/plain";
|
||||
/** The <code>message/rfc822</code> MIME type. */
|
||||
public static final String TYPE_MESSAGE_RFC822 = "message/rfc822";
|
||||
/** The name of the <code>boundary</code> parameter. */
|
||||
public static final String PARAM_BOUNDARY = "boundary";
|
||||
/** The name of the <code>charset</code> parameter. */
|
||||
public static final String PARAM_CHARSET = "charset";
|
||||
|
||||
/**
|
||||
* Gets the MIME type defined in this Content-Type field.
|
||||
*
|
||||
* @return the MIME type or an empty string if not set.
|
||||
*/
|
||||
String getMimeType();
|
||||
|
||||
/**
|
||||
* Gets the value of a parameter. Parameter names are case-insensitive.
|
||||
*
|
||||
* @param name
|
||||
* the name of the parameter to get.
|
||||
* @return the parameter value or <code>null</code> if not set.
|
||||
*/
|
||||
String getParameter(String name);
|
||||
|
||||
/**
|
||||
* Gets all parameters.
|
||||
*
|
||||
* @return the parameters.
|
||||
*/
|
||||
Map<String, String> getParameters();
|
||||
|
||||
/**
|
||||
* Determines if the MIME type of this field matches the given one.
|
||||
*
|
||||
* @param mimeType
|
||||
* the MIME type to match against.
|
||||
* @return <code>true</code> if the MIME type of this field matches,
|
||||
* <code>false</code> otherwise.
|
||||
*/
|
||||
boolean isMimeType(String mimeType);
|
||||
|
||||
/**
|
||||
* Determines if the MIME type of this field is <code>multipart/*</code>.
|
||||
*
|
||||
* @return <code>true</code> if this field is has a
|
||||
* <code>multipart/*</code> MIME type, <code>false</code>
|
||||
* otherwise.
|
||||
*/
|
||||
boolean isMultipart();
|
||||
|
||||
/**
|
||||
* Gets the value of the <code>boundary</code> parameter if set.
|
||||
*
|
||||
* @return the <code>boundary</code> parameter value or <code>null</code>
|
||||
* if not set.
|
||||
*/
|
||||
String getBoundary();
|
||||
|
||||
/**
|
||||
* Gets the value of the <code>charset</code> parameter if set.
|
||||
*
|
||||
* @return the <code>charset</code> parameter value or <code>null</code>
|
||||
* if not set.
|
||||
*/
|
||||
String getCharset();
|
||||
|
||||
}
|
9
src/org/apache/james/mime4j/dom/field/DateTimeField.java
Normal file
9
src/org/apache/james/mime4j/dom/field/DateTimeField.java
Normal file
@ -0,0 +1,9 @@
|
||||
package org.apache.james.mime4j.dom.field;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
public interface DateTimeField extends ParsedField {
|
||||
|
||||
Date getDate();
|
||||
|
||||
}
|
51
src/org/apache/james/mime4j/dom/field/Field.java
Normal file
51
src/org/apache/james/mime4j/dom/field/Field.java
Normal file
@ -0,0 +1,51 @@
|
||||
/****************************************************************
|
||||
* Licensed to the Apache Software Foundation (ASF) under one *
|
||||
* or more contributor license agreements. See the NOTICE file *
|
||||
* distributed with this work for additional information *
|
||||
* regarding copyright ownership. The ASF licenses this file *
|
||||
* to you 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. *
|
||||
****************************************************************/
|
||||
|
||||
package org.apache.james.mime4j.dom.field;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
/**
|
||||
* Abstract MIME field.
|
||||
*/
|
||||
public interface Field {
|
||||
|
||||
/**
|
||||
* Gets the name of the field (<code>Subject</code>, <code>From</code>, etc).
|
||||
*
|
||||
* @return the field name.
|
||||
*/
|
||||
String getName();
|
||||
|
||||
/**
|
||||
* Gets the unparsed and possibly encoded (see RFC 2047) field body string.
|
||||
*
|
||||
* @return the unparsed field body string.
|
||||
*/
|
||||
String getBody();
|
||||
|
||||
/**
|
||||
* Writes the original raw field bytes to an output stream.
|
||||
* The output is folded, the last CRLF is not included.
|
||||
* @throws IOException
|
||||
*/
|
||||
void writeTo(OutputStream out) throws IOException;
|
||||
|
||||
}
|
@ -17,46 +17,37 @@
|
||||
* under the License. *
|
||||
****************************************************************/
|
||||
|
||||
package org.apache.james.mime4j.decoder;
|
||||
package org.apache.james.mime4j.dom.field;
|
||||
|
||||
import java.util.Iterator;
|
||||
/**
|
||||
* Constants for common header field names.
|
||||
*/
|
||||
public class FieldName {
|
||||
|
||||
public class ByteQueue {
|
||||
public static final String CONTENT_DISPOSITION = "Content-Disposition";
|
||||
public static final String CONTENT_TRANSFER_ENCODING = "Content-Transfer-Encoding";
|
||||
public static final String CONTENT_TYPE = "Content-Type";
|
||||
|
||||
private UnboundedFifoByteBuffer buf;
|
||||
private int initialCapacity = -1;
|
||||
public static final String DATE = "Date";
|
||||
public static final String MESSAGE_ID = "Message-ID";
|
||||
public static final String SUBJECT = "Subject";
|
||||
|
||||
public ByteQueue() {
|
||||
buf = new UnboundedFifoByteBuffer();
|
||||
public static final String FROM = "From";
|
||||
public static final String SENDER = "Sender";
|
||||
public static final String TO = "To";
|
||||
public static final String CC = "Cc";
|
||||
public static final String BCC = "Bcc";
|
||||
public static final String REPLY_TO = "Reply-To";
|
||||
|
||||
public static final String RESENT_DATE = "Resent-Date";
|
||||
|
||||
public static final String RESENT_FROM = "Resent-From";
|
||||
public static final String RESENT_SENDER = "Resent-Sender";
|
||||
public static final String RESENT_TO = "Resent-To";
|
||||
public static final String RESENT_CC = "Resent-Cc";
|
||||
public static final String RESENT_BCC = "Resent-Bcc";
|
||||
|
||||
private FieldName() {
|
||||
}
|
||||
|
||||
public ByteQueue(int initialCapacity) {
|
||||
buf = new UnboundedFifoByteBuffer(initialCapacity);
|
||||
this.initialCapacity = initialCapacity;
|
||||
}
|
||||
|
||||
public void enqueue(byte b) {
|
||||
buf.add(b);
|
||||
}
|
||||
|
||||
public byte dequeue() {
|
||||
return buf.remove();
|
||||
}
|
||||
|
||||
public int count() {
|
||||
return buf.size();
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
if (initialCapacity != -1)
|
||||
buf = new UnboundedFifoByteBuffer(initialCapacity);
|
||||
else
|
||||
buf = new UnboundedFifoByteBuffer();
|
||||
}
|
||||
|
||||
public Iterator iterator() {
|
||||
return buf.iterator();
|
||||
}
|
||||
|
||||
|
||||
}
|
28
src/org/apache/james/mime4j/dom/field/MailboxField.java
Normal file
28
src/org/apache/james/mime4j/dom/field/MailboxField.java
Normal file
@ -0,0 +1,28 @@
|
||||
/****************************************************************
|
||||
* Licensed to the Apache Software Foundation (ASF) under one *
|
||||
* or more contributor license agreements. See the NOTICE file *
|
||||
* distributed with this work for additional information *
|
||||
* regarding copyright ownership. The ASF licenses this file *
|
||||
* to you 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. *
|
||||
****************************************************************/
|
||||
|
||||
package org.apache.james.mime4j.dom.field;
|
||||
|
||||
import org.apache.james.mime4j.dom.address.Mailbox;
|
||||
|
||||
public interface MailboxField extends ParsedField {
|
||||
|
||||
Mailbox getMailbox();
|
||||
|
||||
}
|
28
src/org/apache/james/mime4j/dom/field/MailboxListField.java
Normal file
28
src/org/apache/james/mime4j/dom/field/MailboxListField.java
Normal file
@ -0,0 +1,28 @@
|
||||
/****************************************************************
|
||||
* Licensed to the Apache Software Foundation (ASF) under one *
|
||||
* or more contributor license agreements. See the NOTICE file *
|
||||
* distributed with this work for additional information *
|
||||
* regarding copyright ownership. The ASF licenses this file *
|
||||
* to you 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. *
|
||||
****************************************************************/
|
||||
|
||||
package org.apache.james.mime4j.dom.field;
|
||||
|
||||
import org.apache.james.mime4j.dom.address.MailboxList;
|
||||
|
||||
public interface MailboxListField extends ParsedField {
|
||||
|
||||
MailboxList getMailboxList();
|
||||
|
||||
}
|
64
src/org/apache/james/mime4j/dom/field/ParseException.java
Normal file
64
src/org/apache/james/mime4j/dom/field/ParseException.java
Normal file
@ -0,0 +1,64 @@
|
||||
/****************************************************************
|
||||
* Licensed to the Apache Software Foundation (ASF) under one *
|
||||
* or more contributor license agreements. See the NOTICE file *
|
||||
* distributed with this work for additional information *
|
||||
* regarding copyright ownership. The ASF licenses this file *
|
||||
* to you 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. *
|
||||
****************************************************************/
|
||||
|
||||
package org.apache.james.mime4j.dom.field;
|
||||
|
||||
import org.apache.james.mime4j.MimeException;
|
||||
|
||||
/**
|
||||
* This exception is thrown when parse errors are encountered.
|
||||
*/
|
||||
public class ParseException extends MimeException {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* Constructs a new parse exception with the specified detail message.
|
||||
*
|
||||
* @param message
|
||||
* detail message
|
||||
*/
|
||||
protected ParseException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new parse exception with the specified cause.
|
||||
*
|
||||
* @param cause
|
||||
* the cause
|
||||
*/
|
||||
protected ParseException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new parse exception with the specified detail message and
|
||||
* cause.
|
||||
*
|
||||
* @param message
|
||||
* detail message
|
||||
* @param cause
|
||||
* the cause
|
||||
*/
|
||||
protected ParseException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
}
|
45
src/org/apache/james/mime4j/dom/field/ParsedField.java
Normal file
45
src/org/apache/james/mime4j/dom/field/ParsedField.java
Normal file
@ -0,0 +1,45 @@
|
||||
/****************************************************************
|
||||
* Licensed to the Apache Software Foundation (ASF) under one *
|
||||
* or more contributor license agreements. See the NOTICE file *
|
||||
* distributed with this work for additional information *
|
||||
* regarding copyright ownership. The ASF licenses this file *
|
||||
* to you 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. *
|
||||
****************************************************************/
|
||||
|
||||
package org.apache.james.mime4j.dom.field;
|
||||
|
||||
|
||||
public interface ParsedField extends Field {
|
||||
|
||||
/**
|
||||
* Returns <code>true</code> if this field is valid, i.e. no errors were
|
||||
* encountered while parsing the field value.
|
||||
*
|
||||
* @return <code>true</code> if this field is valid, <code>false</code>
|
||||
* otherwise.
|
||||
* @see #getParseException()
|
||||
*/
|
||||
boolean isValidField();
|
||||
|
||||
/**
|
||||
* Returns the exception that was thrown by the field parser while parsing
|
||||
* the field value. The result is <code>null</code> if the field is valid
|
||||
* and no errors were encountered.
|
||||
*
|
||||
* @return the exception that was thrown by the field parser or
|
||||
* <code>null</code> if the field is valid.
|
||||
*/
|
||||
ParseException getParseException();
|
||||
|
||||
}
|
26
src/org/apache/james/mime4j/dom/field/UnstructuredField.java
Normal file
26
src/org/apache/james/mime4j/dom/field/UnstructuredField.java
Normal file
@ -0,0 +1,26 @@
|
||||
/****************************************************************
|
||||
* Licensed to the Apache Software Foundation (ASF) under one *
|
||||
* or more contributor license agreements. See the NOTICE file *
|
||||
* distributed with this work for additional information *
|
||||
* regarding copyright ownership. The ASF licenses this file *
|
||||
* to you 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. *
|
||||
****************************************************************/
|
||||
|
||||
package org.apache.james.mime4j.dom.field;
|
||||
|
||||
public interface UnstructuredField extends ParsedField {
|
||||
|
||||
String getValue();
|
||||
|
||||
}
|
97
src/org/apache/james/mime4j/field/AbstractField.java
Normal file
97
src/org/apache/james/mime4j/field/AbstractField.java
Normal file
@ -0,0 +1,97 @@
|
||||
/****************************************************************
|
||||
* Licensed to the Apache Software Foundation (ASF) under one *
|
||||
* or more contributor license agreements. See the NOTICE file *
|
||||
* distributed with this work for additional information *
|
||||
* regarding copyright ownership. The ASF licenses this file *
|
||||
* to you 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. *
|
||||
****************************************************************/
|
||||
|
||||
package org.apache.james.mime4j.field;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import org.apache.james.mime4j.codec.DecodeMonitor;
|
||||
import org.apache.james.mime4j.dom.field.ParseException;
|
||||
import org.apache.james.mime4j.dom.field.ParsedField;
|
||||
import org.apache.james.mime4j.util.ByteSequence;
|
||||
|
||||
/**
|
||||
* The base class of all field classes.
|
||||
*/
|
||||
public abstract class AbstractField implements ParsedField {
|
||||
|
||||
private final String name;
|
||||
private final String body;
|
||||
private final ByteSequence raw;
|
||||
protected DecodeMonitor monitor;
|
||||
|
||||
protected AbstractField(
|
||||
final String name,
|
||||
final String body,
|
||||
final ByteSequence raw,
|
||||
final DecodeMonitor monitor) {
|
||||
this.name = name;
|
||||
this.body = body;
|
||||
this.raw = raw;
|
||||
this.monitor = monitor != null ? monitor : DecodeMonitor.SILENT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name of the field (<code>Subject</code>,
|
||||
* <code>From</code>, etc).
|
||||
*
|
||||
* @return the field name.
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.apache.james.mime4j.dom.field.Field#writeTo(java.io.OutputStream)
|
||||
*/
|
||||
public void writeTo(OutputStream out) throws IOException {
|
||||
out.write(raw.toByteArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the unfolded, unparsed and possibly encoded (see RFC 2047) field
|
||||
* body string.
|
||||
*
|
||||
* @return the unfolded unparsed field body string.
|
||||
*/
|
||||
public String getBody() {
|
||||
return body;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ParsedField#isValidField()
|
||||
*/
|
||||
public boolean isValidField() {
|
||||
return getParseException() == null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ParsedField#getParseException()
|
||||
*/
|
||||
public ParseException getParseException() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name + ": " + body;
|
||||
}
|
||||
|
||||
}
|
@ -19,45 +19,63 @@
|
||||
|
||||
package org.apache.james.mime4j.field;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.apache.james.mime4j.field.address.AddressList;
|
||||
import org.apache.james.mime4j.codec.DecodeMonitor;
|
||||
import org.apache.james.mime4j.dom.address.AddressList;
|
||||
import org.apache.james.mime4j.field.address.parser.AddressBuilder;
|
||||
import org.apache.james.mime4j.field.address.parser.ParseException;
|
||||
import org.apache.james.mime4j.util.ByteSequence;
|
||||
|
||||
/**
|
||||
* Address list field such as <code>To</code> or <code>Reply-To</code>.
|
||||
*/
|
||||
public class AddressListFieldImpl extends AbstractField implements org.apache.james.mime4j.dom.field.AddressListField {
|
||||
|
||||
private boolean parsed = false;
|
||||
|
||||
public class AddressListField extends Field {
|
||||
private AddressList addressList;
|
||||
private ParseException parseException;
|
||||
|
||||
protected AddressListField(String name, String body, String raw, AddressList addressList, ParseException parseException) {
|
||||
super(name, body, raw);
|
||||
this.addressList = addressList;
|
||||
this.parseException = parseException;
|
||||
AddressListFieldImpl(String name, String body, ByteSequence raw, DecodeMonitor monitor) {
|
||||
super(name, body, raw, monitor);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.apache.james.mime4j.dom.field.AddressListField#getAddressList()
|
||||
*/
|
||||
public AddressList getAddressList() {
|
||||
if (!parsed)
|
||||
parse();
|
||||
|
||||
return addressList;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.apache.james.mime4j.dom.field.AddressListField#getParseException()
|
||||
*/
|
||||
@Override
|
||||
public ParseException getParseException() {
|
||||
if (!parsed)
|
||||
parse();
|
||||
|
||||
return parseException;
|
||||
}
|
||||
|
||||
public static class Parser implements FieldParser {
|
||||
private static Log log = LogFactory.getLog(Parser.class);
|
||||
private void parse() {
|
||||
String body = getBody();
|
||||
|
||||
public Field parse(final String name, final String body, final String raw) {
|
||||
AddressList addressList = null;
|
||||
ParseException parseException = null;
|
||||
try {
|
||||
addressList = AddressList.parse(body);
|
||||
}
|
||||
catch (ParseException e) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Parsing value '" + body + "': "+ e.getMessage());
|
||||
}
|
||||
parseException = e;
|
||||
}
|
||||
return new AddressListField(name, body, raw, addressList, parseException);
|
||||
try {
|
||||
addressList = AddressBuilder.parseAddressList(body, monitor);
|
||||
} catch (ParseException e) {
|
||||
parseException = e;
|
||||
}
|
||||
|
||||
parsed = true;
|
||||
}
|
||||
|
||||
static final FieldParser<AddressListFieldImpl> PARSER = new FieldParser<AddressListFieldImpl>() {
|
||||
public AddressListFieldImpl parse(final String name, final String body,
|
||||
final ByteSequence raw, DecodeMonitor monitor) {
|
||||
return new AddressListFieldImpl(name, body, raw, monitor);
|
||||
}
|
||||
};
|
||||
}
|
@ -0,0 +1,253 @@
|
||||
/****************************************************************
|
||||
* Licensed to the Apache Software Foundation (ASF) under one *
|
||||
* or more contributor license agreements. See the NOTICE file *
|
||||
* distributed with this work for additional information *
|
||||
* regarding copyright ownership. The ASF licenses this file *
|
||||
* to you 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. *
|
||||
****************************************************************/
|
||||
|
||||
package org.apache.james.mime4j.field;
|
||||
|
||||
import java.io.StringReader;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.james.mime4j.codec.DecodeMonitor;
|
||||
import org.apache.james.mime4j.field.contentdisposition.parser.ContentDispositionParser;
|
||||
import org.apache.james.mime4j.field.contentdisposition.parser.ParseException;
|
||||
import org.apache.james.mime4j.field.contentdisposition.parser.TokenMgrError;
|
||||
import org.apache.james.mime4j.field.datetime.parser.DateTimeParser;
|
||||
import org.apache.james.mime4j.util.ByteSequence;
|
||||
|
||||
/**
|
||||
* Represents a <code>Content-Disposition</code> field.
|
||||
*/
|
||||
public class ContentDispositionFieldImpl extends AbstractField implements org.apache.james.mime4j.dom.field.ContentDispositionField {
|
||||
|
||||
private boolean parsed = false;
|
||||
|
||||
private String dispositionType = "";
|
||||
private Map<String, String> parameters = new HashMap<String, String>();
|
||||
private ParseException parseException;
|
||||
|
||||
private boolean creationDateParsed;
|
||||
private Date creationDate;
|
||||
|
||||
private boolean modificationDateParsed;
|
||||
private Date modificationDate;
|
||||
|
||||
private boolean readDateParsed;
|
||||
private Date readDate;
|
||||
|
||||
ContentDispositionFieldImpl(String name, String body, ByteSequence raw, DecodeMonitor monitor) {
|
||||
super(name, body, raw, monitor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the exception that was raised during parsing of the field value, if
|
||||
* any; otherwise, null.
|
||||
*/
|
||||
@Override
|
||||
public ParseException getParseException() {
|
||||
if (!parsed)
|
||||
parse();
|
||||
|
||||
return parseException;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.apache.james.mime4j.dom.field.ContentDispositionField#getDispositionType()
|
||||
*/
|
||||
public String getDispositionType() {
|
||||
if (!parsed)
|
||||
parse();
|
||||
|
||||
return dispositionType;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.apache.james.mime4j.dom.field.ContentDispositionField#getParameter(java.lang.String)
|
||||
*/
|
||||
public String getParameter(String name) {
|
||||
if (!parsed)
|
||||
parse();
|
||||
|
||||
return parameters.get(name.toLowerCase());
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.apache.james.mime4j.dom.field.ContentDispositionField#getParameters()
|
||||
*/
|
||||
public Map<String, String> getParameters() {
|
||||
if (!parsed)
|
||||
parse();
|
||||
|
||||
return Collections.unmodifiableMap(parameters);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.apache.james.mime4j.dom.field.ContentDispositionField#isDispositionType(java.lang.String)
|
||||
*/
|
||||
public boolean isDispositionType(String dispositionType) {
|
||||
if (!parsed)
|
||||
parse();
|
||||
|
||||
return this.dispositionType.equalsIgnoreCase(dispositionType);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.apache.james.mime4j.dom.field.ContentDispositionField#isInline()
|
||||
*/
|
||||
public boolean isInline() {
|
||||
if (!parsed)
|
||||
parse();
|
||||
|
||||
return dispositionType.equals(DISPOSITION_TYPE_INLINE);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.apache.james.mime4j.dom.field.ContentDispositionField#isAttachment()
|
||||
*/
|
||||
public boolean isAttachment() {
|
||||
if (!parsed)
|
||||
parse();
|
||||
|
||||
return dispositionType.equals(DISPOSITION_TYPE_ATTACHMENT);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.apache.james.mime4j.dom.field.ContentDispositionField#getFilename()
|
||||
*/
|
||||
public String getFilename() {
|
||||
return getParameter(PARAM_FILENAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.apache.james.mime4j.dom.field.ContentDispositionField#getCreationDate()
|
||||
*/
|
||||
public Date getCreationDate() {
|
||||
if (!creationDateParsed) {
|
||||
creationDate = parseDate(PARAM_CREATION_DATE);
|
||||
creationDateParsed = true;
|
||||
}
|
||||
|
||||
return creationDate;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.apache.james.mime4j.dom.field.ContentDispositionField#getModificationDate()
|
||||
*/
|
||||
public Date getModificationDate() {
|
||||
if (!modificationDateParsed) {
|
||||
modificationDate = parseDate(PARAM_MODIFICATION_DATE);
|
||||
modificationDateParsed = true;
|
||||
}
|
||||
|
||||
return modificationDate;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.apache.james.mime4j.dom.field.ContentDispositionField#getReadDate()
|
||||
*/
|
||||
public Date getReadDate() {
|
||||
if (!readDateParsed) {
|
||||
readDate = parseDate(PARAM_READ_DATE);
|
||||
readDateParsed = true;
|
||||
}
|
||||
|
||||
return readDate;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.apache.james.mime4j.dom.field.ContentDispositionField#getSize()
|
||||
*/
|
||||
public long getSize() {
|
||||
String value = getParameter(PARAM_SIZE);
|
||||
if (value == null)
|
||||
return -1;
|
||||
|
||||
try {
|
||||
long size = Long.parseLong(value);
|
||||
return size < 0 ? -1 : size;
|
||||
} catch (NumberFormatException e) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
private Date parseDate(String paramName) {
|
||||
String value = getParameter(paramName);
|
||||
if (value == null) {
|
||||
monitor.warn("Parsing " + paramName + " null", "returning null");
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
return new DateTimeParser(new StringReader(value)).parseAll()
|
||||
.getDate();
|
||||
} catch (org.apache.james.mime4j.field.datetime.parser.ParseException e) {
|
||||
monitor.warn("Parsing " + paramName + " '" + value + "': "
|
||||
+ e.getMessage(), "returning null");
|
||||
return null;
|
||||
} catch (org.apache.james.mime4j.field.datetime.parser.TokenMgrError e) {
|
||||
monitor.warn("Parsing " + paramName + " '" + value + "': "
|
||||
+ e.getMessage(), "returning null");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private void parse() {
|
||||
String body = getBody();
|
||||
|
||||
ContentDispositionParser parser = new ContentDispositionParser(
|
||||
new StringReader(body));
|
||||
try {
|
||||
parser.parseAll();
|
||||
} catch (ParseException e) {
|
||||
parseException = e;
|
||||
} catch (TokenMgrError e) {
|
||||
parseException = new ParseException(e.getMessage());
|
||||
}
|
||||
|
||||
final String dispositionType = parser.getDispositionType();
|
||||
|
||||
if (dispositionType != null) {
|
||||
this.dispositionType = dispositionType.toLowerCase(Locale.US);
|
||||
|
||||
List<String> paramNames = parser.getParamNames();
|
||||
List<String> paramValues = parser.getParamValues();
|
||||
|
||||
if (paramNames != null && paramValues != null) {
|
||||
final int len = Math.min(paramNames.size(), paramValues.size());
|
||||
for (int i = 0; i < len; i++) {
|
||||
String paramName = paramNames.get(i).toLowerCase(Locale.US);
|
||||
String paramValue = paramValues.get(i);
|
||||
parameters.put(paramName, paramValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
parsed = true;
|
||||
}
|
||||
|
||||
static final FieldParser<ContentDispositionFieldImpl> PARSER = new FieldParser<ContentDispositionFieldImpl>() {
|
||||
public ContentDispositionFieldImpl parse(final String name, final String body,
|
||||
final ByteSequence raw, DecodeMonitor monitor) {
|
||||
return new ContentDispositionFieldImpl(name, body, raw, monitor);
|
||||
}
|
||||
};
|
||||
}
|
@ -19,70 +19,47 @@
|
||||
|
||||
package org.apache.james.mime4j.field;
|
||||
|
||||
|
||||
import org.apache.james.mime4j.codec.DecodeMonitor;
|
||||
import org.apache.james.mime4j.dom.field.ContentTransferEncodingField;
|
||||
import org.apache.james.mime4j.util.ByteSequence;
|
||||
import org.apache.james.mime4j.util.MimeUtil;
|
||||
|
||||
/**
|
||||
* Represents a <code>Content-Transfer-Encoding</code> field.
|
||||
*
|
||||
*
|
||||
* @version $Id: ContentTransferEncodingField.java,v 1.2 2004/10/02 12:41:11 ntherning Exp $
|
||||
*/
|
||||
public class ContentTransferEncodingField extends Field {
|
||||
/**
|
||||
* The <code>7bit</code> encoding.
|
||||
*/
|
||||
public static final String ENC_7BIT = "7bit";
|
||||
/**
|
||||
* The <code>8bit</code> encoding.
|
||||
*/
|
||||
public static final String ENC_8BIT = "8bit";
|
||||
/**
|
||||
* The <code>binary</code> encoding.
|
||||
*/
|
||||
public static final String ENC_BINARY = "binary";
|
||||
/**
|
||||
* The <code>quoted-printable</code> encoding.
|
||||
*/
|
||||
public static final String ENC_QUOTED_PRINTABLE = "quoted-printable";
|
||||
/**
|
||||
* The <code>base64</code> encoding.
|
||||
*/
|
||||
public static final String ENC_BASE64 = "base64";
|
||||
|
||||
public class ContentTransferEncodingFieldImpl extends AbstractField implements ContentTransferEncodingField {
|
||||
private String encoding;
|
||||
|
||||
protected ContentTransferEncodingField(String name, String body, String raw, String encoding) {
|
||||
super(name, body, raw);
|
||||
this.encoding = encoding;
|
||||
|
||||
ContentTransferEncodingFieldImpl(String name, String body, ByteSequence raw, DecodeMonitor monitor) {
|
||||
super(name, body, raw, monitor);
|
||||
encoding = body.trim().toLowerCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the encoding defined in this field.
|
||||
*
|
||||
* @return the encoding or an empty string if not set.
|
||||
* @see org.apache.james.mime4j.dom.field.ContentTransferEncodingField#getEncoding()
|
||||
*/
|
||||
public String getEncoding() {
|
||||
return encoding;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the encoding of the given field if. Returns the default
|
||||
* <code>7bit</code> if not set or if
|
||||
* <code>f</code> is <code>null</code>.
|
||||
*
|
||||
* Gets the encoding of the given field if. Returns the default
|
||||
* <code>7bit</code> if not set or if <code>f</code> is
|
||||
* <code>null</code>.
|
||||
*
|
||||
* @return the encoding.
|
||||
*/
|
||||
public static String getEncoding(ContentTransferEncodingField f) {
|
||||
if (f != null && f.getEncoding().length() != 0) {
|
||||
return f.getEncoding();
|
||||
}
|
||||
return ENC_7BIT;
|
||||
return MimeUtil.ENC_7BIT;
|
||||
}
|
||||
|
||||
public static class Parser implements FieldParser {
|
||||
public Field parse(final String name, final String body, final String raw) {
|
||||
final String encoding = body.trim().toLowerCase();
|
||||
return new ContentTransferEncodingField(name, body, raw, encoding);
|
||||
|
||||
static final FieldParser<ContentTransferEncodingFieldImpl> PARSER = new FieldParser<ContentTransferEncodingFieldImpl>() {
|
||||
public ContentTransferEncodingFieldImpl parse(final String name, final String body,
|
||||
final ByteSequence raw, DecodeMonitor monitor) {
|
||||
return new ContentTransferEncodingFieldImpl(name, body, raw, monitor);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
@ -1,256 +0,0 @@
|
||||
/****************************************************************
|
||||
* Licensed to the Apache Software Foundation (ASF) under one *
|
||||
* or more contributor license agreements. See the NOTICE file *
|
||||
* distributed with this work for additional information *
|
||||
* regarding copyright ownership. The ASF licenses this file *
|
||||
* to you 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. *
|
||||
****************************************************************/
|
||||
|
||||
package org.apache.james.mime4j.field;
|
||||
|
||||
import java.io.StringReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.apache.james.mime4j.field.contenttype.parser.ContentTypeParser;
|
||||
import org.apache.james.mime4j.field.contenttype.parser.ParseException;
|
||||
import org.apache.james.mime4j.field.contenttype.parser.TokenMgrError;
|
||||
|
||||
/**
|
||||
* Represents a <code>Content-Type</code> field.
|
||||
*
|
||||
* <p>TODO: Remove dependency on Java 1.4 regexps</p>
|
||||
*
|
||||
*
|
||||
* @version $Id: ContentTypeField.java,v 1.6 2005/01/27 14:16:31 ntherning Exp $
|
||||
*/
|
||||
public class ContentTypeField extends Field {
|
||||
|
||||
/**
|
||||
* The prefix of all <code>multipart</code> MIME types.
|
||||
*/
|
||||
public static final String TYPE_MULTIPART_PREFIX = "multipart/";
|
||||
/**
|
||||
* The <code>multipart/digest</code> MIME type.
|
||||
*/
|
||||
public static final String TYPE_MULTIPART_DIGEST = "multipart/digest";
|
||||
/**
|
||||
* The <code>text/plain</code> MIME type.
|
||||
*/
|
||||
public static final String TYPE_TEXT_PLAIN = "text/plain";
|
||||
/**
|
||||
* The <code>message/rfc822</code> MIME type.
|
||||
*/
|
||||
public static final String TYPE_MESSAGE_RFC822 = "message/rfc822";
|
||||
/**
|
||||
* The name of the <code>boundary</code> parameter.
|
||||
*/
|
||||
public static final String PARAM_BOUNDARY = "boundary";
|
||||
/**
|
||||
* The name of the <code>charset</code> parameter.
|
||||
*/
|
||||
public static final String PARAM_CHARSET = "charset";
|
||||
|
||||
private String mimeType = "";
|
||||
private Map parameters = null;
|
||||
private ParseException parseException;
|
||||
|
||||
protected ContentTypeField(String name, String body, String raw, String mimeType, Map parameters, ParseException parseException) {
|
||||
super(name, body, raw);
|
||||
this.mimeType = mimeType;
|
||||
this.parameters = parameters;
|
||||
this.parseException = parseException;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the exception that was raised during parsing of
|
||||
* the field value, if any; otherwise, null.
|
||||
*/
|
||||
public ParseException getParseException() {
|
||||
return parseException;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the MIME type defined in this Content-Type field.
|
||||
*
|
||||
* @return the MIME type or an empty string if not set.
|
||||
*/
|
||||
public String getMimeType() {
|
||||
return mimeType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the MIME type defined in the child's
|
||||
* Content-Type field or derives a MIME type from the parent
|
||||
* if child is <code>null</code> or hasn't got a MIME type value set.
|
||||
* If child's MIME type is multipart but no boundary
|
||||
* has been set the MIME type of child will be derived from
|
||||
* the parent.
|
||||
*
|
||||
* @param child the child.
|
||||
* @param parent the parent.
|
||||
* @return the MIME type.
|
||||
*/
|
||||
public static String getMimeType(ContentTypeField child,
|
||||
ContentTypeField parent) {
|
||||
|
||||
if (child == null || child.getMimeType().length() == 0
|
||||
|| child.isMultipart() && child.getBoundary() == null) {
|
||||
|
||||
if (parent != null && parent.isMimeType(TYPE_MULTIPART_DIGEST)) {
|
||||
return TYPE_MESSAGE_RFC822;
|
||||
} else {
|
||||
return TYPE_TEXT_PLAIN;
|
||||
}
|
||||
}
|
||||
|
||||
return child.getMimeType();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of a parameter. Parameter names are case-insensitive.
|
||||
*
|
||||
* @param name the name of the parameter to get.
|
||||
* @return the parameter value or <code>null</code> if not set.
|
||||
*/
|
||||
public String getParameter(String name) {
|
||||
return parameters != null
|
||||
? (String) parameters.get(name.toLowerCase())
|
||||
: null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all parameters.
|
||||
*
|
||||
* @return the parameters.
|
||||
*/
|
||||
public Map getParameters() {
|
||||
return parameters != null
|
||||
? Collections.unmodifiableMap(parameters)
|
||||
: Collections.EMPTY_MAP;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of the <code>boundary</code> parameter if set.
|
||||
*
|
||||
* @return the <code>boundary</code> parameter value or <code>null</code>
|
||||
* if not set.
|
||||
*/
|
||||
public String getBoundary() {
|
||||
return getParameter(PARAM_BOUNDARY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of the <code>charset</code> parameter if set.
|
||||
*
|
||||
* @return the <code>charset</code> parameter value or <code>null</code>
|
||||
* if not set.
|
||||
*/
|
||||
public String getCharset() {
|
||||
return getParameter(PARAM_CHARSET);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of the <code>charset</code> parameter if set for the
|
||||
* given field. Returns the default <code>us-ascii</code> if not set or if
|
||||
* <code>f</code> is <code>null</code>.
|
||||
*
|
||||
* @return the <code>charset</code> parameter value.
|
||||
*/
|
||||
public static String getCharset(ContentTypeField f) {
|
||||
if (f != null) {
|
||||
if (f.getCharset() != null && f.getCharset().length() > 0) {
|
||||
return f.getCharset();
|
||||
}
|
||||
}
|
||||
return "us-ascii";
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the MIME type of this field matches the given one.
|
||||
*
|
||||
* @param mimeType the MIME type to match against.
|
||||
* @return <code>true</code> if the MIME type of this field matches,
|
||||
* <code>false</code> otherwise.
|
||||
*/
|
||||
public boolean isMimeType(String mimeType) {
|
||||
return this.mimeType.equalsIgnoreCase(mimeType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the MIME type of this field is <code>multipart/*</code>.
|
||||
*
|
||||
* @return <code>true</code> if this field is has a <code>multipart/*</code>
|
||||
* MIME type, <code>false</code> otherwise.
|
||||
*/
|
||||
public boolean isMultipart() {
|
||||
return mimeType.startsWith(TYPE_MULTIPART_PREFIX);
|
||||
}
|
||||
|
||||
public static class Parser implements FieldParser {
|
||||
private static Log log = LogFactory.getLog(Parser.class);
|
||||
|
||||
public Field parse(final String name, final String body, final String raw) {
|
||||
ParseException parseException = null;
|
||||
String mimeType = "";
|
||||
Map parameters = null;
|
||||
|
||||
ContentTypeParser parser = new ContentTypeParser(new StringReader(body));
|
||||
try {
|
||||
parser.parseAll();
|
||||
}
|
||||
catch (ParseException e) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Parsing value '" + body + "': "+ e.getMessage());
|
||||
}
|
||||
parseException = e;
|
||||
}
|
||||
catch (TokenMgrError e) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Parsing value '" + body + "': "+ e.getMessage());
|
||||
}
|
||||
parseException = new ParseException(e.getMessage());
|
||||
}
|
||||
|
||||
try {
|
||||
final String type = parser.getType();
|
||||
final String subType = parser.getSubType();
|
||||
|
||||
if (type != null && subType != null) {
|
||||
mimeType = (type + "/" + parser.getSubType()).toLowerCase();
|
||||
|
||||
ArrayList paramNames = parser.getParamNames();
|
||||
ArrayList paramValues = parser.getParamValues();
|
||||
|
||||
if (paramNames != null && paramValues != null) {
|
||||
for (int i = 0; i < paramNames.size() && i < paramValues.size(); i++) {
|
||||
if (parameters == null)
|
||||
parameters = new HashMap((int)(paramNames.size() * 1.3 + 1));
|
||||
String paramName = ((String)paramNames.get(i)).toLowerCase();
|
||||
String paramValue = ((String)paramValues.get(i));
|
||||
parameters.put(paramName, paramValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (NullPointerException npe) {
|
||||
}
|
||||
return new ContentTypeField(name, body, raw, mimeType, parameters, parseException);
|
||||
}
|
||||
}
|
||||
}
|
208
src/org/apache/james/mime4j/field/ContentTypeFieldImpl.java
Normal file
208
src/org/apache/james/mime4j/field/ContentTypeFieldImpl.java
Normal file
@ -0,0 +1,208 @@
|
||||
/****************************************************************
|
||||
* Licensed to the Apache Software Foundation (ASF) under one *
|
||||
* or more contributor license agreements. See the NOTICE file *
|
||||
* distributed with this work for additional information *
|
||||
* regarding copyright ownership. The ASF licenses this file *
|
||||
* to you 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. *
|
||||
****************************************************************/
|
||||
|
||||
package org.apache.james.mime4j.field;
|
||||
|
||||
import java.io.StringReader;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.james.mime4j.codec.DecodeMonitor;
|
||||
import org.apache.james.mime4j.dom.field.ContentTypeField;
|
||||
import org.apache.james.mime4j.field.contenttype.parser.ContentTypeParser;
|
||||
import org.apache.james.mime4j.field.contenttype.parser.ParseException;
|
||||
import org.apache.james.mime4j.field.contenttype.parser.TokenMgrError;
|
||||
import org.apache.james.mime4j.util.ByteSequence;
|
||||
|
||||
/**
|
||||
* Represents a <code>Content-Type</code> field.
|
||||
*/
|
||||
public class ContentTypeFieldImpl extends AbstractField implements ContentTypeField {
|
||||
private boolean parsed = false;
|
||||
|
||||
private String mimeType = "";
|
||||
private Map<String, String> parameters = new HashMap<String, String>();
|
||||
private ParseException parseException;
|
||||
|
||||
ContentTypeFieldImpl(String name, String body, ByteSequence raw, DecodeMonitor monitor) {
|
||||
super(name, body, raw, monitor);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.apache.james.mime4j.dom.field.ContentTypeField#getParseException()
|
||||
*/
|
||||
@Override
|
||||
public ParseException getParseException() {
|
||||
if (!parsed)
|
||||
parse();
|
||||
|
||||
return parseException;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.apache.james.mime4j.dom.field.ContentTypeField#getMimeType()
|
||||
*/
|
||||
public String getMimeType() {
|
||||
if (!parsed)
|
||||
parse();
|
||||
|
||||
return mimeType;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.apache.james.mime4j.dom.field.ContentTypeField#getParameter(java.lang.String)
|
||||
*/
|
||||
public String getParameter(String name) {
|
||||
if (!parsed)
|
||||
parse();
|
||||
|
||||
return parameters.get(name.toLowerCase());
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.apache.james.mime4j.dom.field.ContentTypeField#getParameters()
|
||||
*/
|
||||
public Map<String, String> getParameters() {
|
||||
if (!parsed)
|
||||
parse();
|
||||
|
||||
return Collections.unmodifiableMap(parameters);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.apache.james.mime4j.dom.field.ContentTypeField#isMimeType(java.lang.String)
|
||||
*/
|
||||
public boolean isMimeType(String mimeType) {
|
||||
if (!parsed)
|
||||
parse();
|
||||
|
||||
return this.mimeType.equalsIgnoreCase(mimeType);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.apache.james.mime4j.dom.field.ContentTypeField#isMultipart()
|
||||
*/
|
||||
public boolean isMultipart() {
|
||||
if (!parsed)
|
||||
parse();
|
||||
|
||||
return mimeType.startsWith(TYPE_MULTIPART_PREFIX);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.apache.james.mime4j.dom.field.ContentTypeField#getBoundary()
|
||||
*/
|
||||
public String getBoundary() {
|
||||
return getParameter(PARAM_BOUNDARY);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.apache.james.mime4j.dom.field.ContentTypeField#getCharset()
|
||||
*/
|
||||
public String getCharset() {
|
||||
return getParameter(PARAM_CHARSET);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the MIME type defined in the child's Content-Type field or derives a
|
||||
* MIME type from the parent if child is <code>null</code> or hasn't got a
|
||||
* MIME type value set. If child's MIME type is multipart but no boundary
|
||||
* has been set the MIME type of child will be derived from the parent.
|
||||
*
|
||||
* @param child
|
||||
* the child.
|
||||
* @param parent
|
||||
* the parent.
|
||||
* @return the MIME type.
|
||||
*/
|
||||
public static String getMimeType(ContentTypeField child,
|
||||
ContentTypeField parent) {
|
||||
if (child == null || child.getMimeType().length() == 0
|
||||
|| child.isMultipart() && child.getBoundary() == null) {
|
||||
|
||||
if (parent != null && parent.isMimeType(TYPE_MULTIPART_DIGEST)) {
|
||||
return TYPE_MESSAGE_RFC822;
|
||||
} else {
|
||||
return TYPE_TEXT_PLAIN;
|
||||
}
|
||||
}
|
||||
|
||||
return child.getMimeType();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of the <code>charset</code> parameter if set for the
|
||||
* given field. Returns the default <code>us-ascii</code> if not set or if
|
||||
* <code>f</code> is <code>null</code>.
|
||||
*
|
||||
* @return the <code>charset</code> parameter value.
|
||||
*/
|
||||
public static String getCharset(ContentTypeField f) {
|
||||
if (f != null) {
|
||||
String charset = f.getCharset();
|
||||
if (charset != null && charset.length() > 0) {
|
||||
return charset;
|
||||
}
|
||||
}
|
||||
return "us-ascii";
|
||||
}
|
||||
|
||||
private void parse() {
|
||||
String body = getBody();
|
||||
|
||||
ContentTypeParser parser = new ContentTypeParser(new StringReader(body));
|
||||
try {
|
||||
parser.parseAll();
|
||||
} catch (ParseException e) {
|
||||
parseException = e;
|
||||
} catch (TokenMgrError e) {
|
||||
parseException = new ParseException(e.getMessage());
|
||||
}
|
||||
|
||||
final String type = parser.getType();
|
||||
final String subType = parser.getSubType();
|
||||
|
||||
if (type != null && subType != null) {
|
||||
mimeType = (type + "/" + subType).toLowerCase();
|
||||
|
||||
List<String> paramNames = parser.getParamNames();
|
||||
List<String> paramValues = parser.getParamValues();
|
||||
|
||||
if (paramNames != null && paramValues != null) {
|
||||
final int len = Math.min(paramNames.size(), paramValues.size());
|
||||
for (int i = 0; i < len; i++) {
|
||||
String paramName = paramNames.get(i).toLowerCase();
|
||||
String paramValue = paramValues.get(i);
|
||||
parameters.put(paramName, paramValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
parsed = true;
|
||||
}
|
||||
|
||||
static final FieldParser<ContentTypeFieldImpl> PARSER = new FieldParser<ContentTypeFieldImpl>() {
|
||||
public ContentTypeFieldImpl parse(final String name, final String body,
|
||||
final ByteSequence raw, DecodeMonitor monitor) {
|
||||
return new ContentTypeFieldImpl(name, body, raw, monitor);
|
||||
}
|
||||
};
|
||||
}
|
@ -1,65 +0,0 @@
|
||||
/****************************************************************
|
||||
* Licensed to the Apache Software Foundation (ASF) under one *
|
||||
* or more contributor license agreements. See the NOTICE file *
|
||||
* distributed with this work for additional information *
|
||||
* regarding copyright ownership. The ASF licenses this file *
|
||||
* to you 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. *
|
||||
****************************************************************/
|
||||
|
||||
package org.apache.james.mime4j.field;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.apache.james.mime4j.field.datetime.DateTime;
|
||||
import org.apache.james.mime4j.field.datetime.parser.ParseException;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
public class DateTimeField extends Field {
|
||||
private Date date;
|
||||
private ParseException parseException;
|
||||
|
||||
protected DateTimeField(String name, String body, String raw, Date date, ParseException parseException) {
|
||||
super(name, body, raw);
|
||||
this.date = date;
|
||||
this.parseException = parseException;
|
||||
}
|
||||
|
||||
public Date getDate() {
|
||||
return date;
|
||||
}
|
||||
|
||||
public ParseException getParseException() {
|
||||
return parseException;
|
||||
}
|
||||
|
||||
public static class Parser implements FieldParser {
|
||||
private static Log log = LogFactory.getLog(Parser.class);
|
||||
|
||||
public Field parse(final String name, final String body, final String raw) {
|
||||
Date date = null;
|
||||
ParseException parseException = null;
|
||||
try {
|
||||
date = DateTime.parse(body).getDate();
|
||||
}
|
||||
catch (ParseException e) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Parsing value '" + body + "': "+ e.getMessage());
|
||||
}
|
||||
parseException = e;
|
||||
}
|
||||
return new DateTimeField(name, body, raw, date, parseException);
|
||||
}
|
||||
}
|
||||
}
|
86
src/org/apache/james/mime4j/field/DateTimeFieldImpl.java
Normal file
86
src/org/apache/james/mime4j/field/DateTimeFieldImpl.java
Normal file
@ -0,0 +1,86 @@
|
||||
/****************************************************************
|
||||
* Licensed to the Apache Software Foundation (ASF) under one *
|
||||
* or more contributor license agreements. See the NOTICE file *
|
||||
* distributed with this work for additional information *
|
||||
* regarding copyright ownership. The ASF licenses this file *
|
||||
* to you 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. *
|
||||
****************************************************************/
|
||||
|
||||
package org.apache.james.mime4j.field;
|
||||
|
||||
import java.io.StringReader;
|
||||
import java.util.Date;
|
||||
|
||||
import org.apache.james.mime4j.codec.DecodeMonitor;
|
||||
import org.apache.james.mime4j.field.datetime.parser.DateTimeParser;
|
||||
import org.apache.james.mime4j.field.datetime.parser.ParseException;
|
||||
import org.apache.james.mime4j.field.datetime.parser.TokenMgrError;
|
||||
import org.apache.james.mime4j.util.ByteSequence;
|
||||
|
||||
/**
|
||||
* Date-time field such as <code>Date</code> or <code>Resent-Date</code>.
|
||||
*/
|
||||
public class DateTimeFieldImpl extends AbstractField implements org.apache.james.mime4j.dom.field.DateTimeField {
|
||||
private boolean parsed = false;
|
||||
|
||||
private Date date;
|
||||
private ParseException parseException;
|
||||
|
||||
DateTimeFieldImpl(String name, String body, ByteSequence raw, DecodeMonitor monitor) {
|
||||
super(name, body, raw, monitor);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.apache.james.mime4j.dom.field.DateTimeField#getDate()
|
||||
*/
|
||||
public Date getDate() {
|
||||
if (!parsed)
|
||||
parse();
|
||||
|
||||
return date;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.apache.james.mime4j.dom.field.DateTimeField#getParseException()
|
||||
*/
|
||||
@Override
|
||||
public ParseException getParseException() {
|
||||
if (!parsed)
|
||||
parse();
|
||||
|
||||
return parseException;
|
||||
}
|
||||
|
||||
private void parse() {
|
||||
String body = getBody();
|
||||
|
||||
try {
|
||||
date = new DateTimeParser(new StringReader(body)).parseAll()
|
||||
.getDate();
|
||||
} catch (ParseException e) {
|
||||
parseException = e;
|
||||
} catch (TokenMgrError e) {
|
||||
parseException = new ParseException(e.getMessage());
|
||||
}
|
||||
|
||||
parsed = true;
|
||||
}
|
||||
|
||||
static final FieldParser<DateTimeFieldImpl> PARSER = new FieldParser<DateTimeFieldImpl>() {
|
||||
public DateTimeFieldImpl parse(final String name, final String body,
|
||||
final ByteSequence raw, DecodeMonitor monitor) {
|
||||
return new DateTimeFieldImpl(name, body, raw, monitor);
|
||||
}
|
||||
};
|
||||
}
|
@ -1,45 +1,133 @@
|
||||
/*
|
||||
* Copyright 2006 the mime4j project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
/****************************************************************
|
||||
* Licensed to the Apache Software Foundation (ASF) under one *
|
||||
* or more contributor license agreements. See the NOTICE file *
|
||||
* distributed with this work for additional information *
|
||||
* regarding copyright ownership. The ASF licenses this file *
|
||||
* to you 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. *
|
||||
****************************************************************/
|
||||
|
||||
package org.apache.james.mime4j.field;
|
||||
|
||||
import org.apache.james.mime4j.MimeException;
|
||||
import org.apache.james.mime4j.codec.DecodeMonitor;
|
||||
import org.apache.james.mime4j.dom.field.FieldName;
|
||||
import org.apache.james.mime4j.dom.field.ParsedField;
|
||||
import org.apache.james.mime4j.field.AddressListFieldImpl;
|
||||
import org.apache.james.mime4j.field.ContentDispositionFieldImpl;
|
||||
import org.apache.james.mime4j.field.ContentTransferEncodingFieldImpl;
|
||||
import org.apache.james.mime4j.field.ContentTypeFieldImpl;
|
||||
import org.apache.james.mime4j.field.DateTimeFieldImpl;
|
||||
import org.apache.james.mime4j.field.MailboxFieldImpl;
|
||||
import org.apache.james.mime4j.field.MailboxListFieldImpl;
|
||||
import org.apache.james.mime4j.field.UnstructuredFieldImpl;
|
||||
import org.apache.james.mime4j.stream.RawField;
|
||||
import org.apache.james.mime4j.util.ByteSequence;
|
||||
import org.apache.james.mime4j.util.ContentUtil;
|
||||
|
||||
public class DefaultFieldParser extends DelegatingFieldParser {
|
||||
|
||||
private static final DefaultFieldParser PARSER = new DefaultFieldParser();
|
||||
|
||||
public DefaultFieldParser() {
|
||||
setFieldParser(Field.CONTENT_TRANSFER_ENCODING, new ContentTransferEncodingField.Parser());
|
||||
setFieldParser(Field.CONTENT_TYPE, new ContentTypeField.Parser());
|
||||
|
||||
final DateTimeField.Parser dateTimeParser = new DateTimeField.Parser();
|
||||
setFieldParser(Field.DATE, dateTimeParser);
|
||||
setFieldParser(Field.RESENT_DATE, dateTimeParser);
|
||||
|
||||
final MailboxListField.Parser mailboxListParser = new MailboxListField.Parser();
|
||||
setFieldParser(Field.FROM, mailboxListParser);
|
||||
setFieldParser(Field.RESENT_FROM, mailboxListParser);
|
||||
|
||||
final MailboxField.Parser mailboxParser = new MailboxField.Parser();
|
||||
setFieldParser(Field.SENDER, mailboxParser);
|
||||
setFieldParser(Field.RESENT_SENDER, mailboxParser);
|
||||
|
||||
final AddressListField.Parser addressListParser = new AddressListField.Parser();
|
||||
setFieldParser(Field.TO, addressListParser);
|
||||
setFieldParser(Field.RESENT_TO, addressListParser);
|
||||
setFieldParser(Field.CC, addressListParser);
|
||||
setFieldParser(Field.RESENT_CC, addressListParser);
|
||||
setFieldParser(Field.BCC, addressListParser);
|
||||
setFieldParser(Field.RESENT_BCC, addressListParser);
|
||||
setFieldParser(Field.REPLY_TO, addressListParser);
|
||||
|
||||
/**
|
||||
* Gets the default parser used to parse fields.
|
||||
*
|
||||
* @return the default field parser
|
||||
*/
|
||||
public static DefaultFieldParser getParser() {
|
||||
return PARSER;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parses the given byte sequence and returns an instance of the
|
||||
* <code>Field</code> class. The type of the class returned depends on the
|
||||
* field name; see {@link #parse(String)} for a table of field names and
|
||||
* their corresponding classes.
|
||||
*
|
||||
* @param raw the bytes to parse.
|
||||
* @param monitor a DecodeMonitor object used while parsing/decoding.
|
||||
* @return a <code>ParsedField</code> instance.
|
||||
* @throws MimeException if the raw string cannot be split into field name and body.
|
||||
*/
|
||||
public static ParsedField parse(
|
||||
final ByteSequence raw,
|
||||
final DecodeMonitor monitor) throws MimeException {
|
||||
RawField rawField = new RawField(raw);
|
||||
return PARSER.parse(rawField.getName(), rawField.getBody(), raw, monitor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the given string and returns an instance of the
|
||||
* <code>Field</code> class. The type of the class returned depends on
|
||||
* the field name:
|
||||
* <p>
|
||||
* <table>
|
||||
* <tr><th>Class returned</th><th>Field names</th></tr>
|
||||
* <tr><td>{@link ContentTypeFieldImpl}</td><td>Content-Type</td></tr>
|
||||
* <tr><td>{@link ContentTransferEncodingFieldImpl}</td><td>Content-Transfer-Encoding</td></tr>
|
||||
* <tr><td>{@link ContentDispositionFieldImpl}</td><td>Content-Disposition</td></tr>
|
||||
* <tr><td>{@link DateTimeFieldImpl}</td><td>Date, Resent-Date</td></tr>
|
||||
* <tr><td>{@link MailboxFieldImpl}</td><td>Sender, Resent-Sender</td></tr>
|
||||
* <tr><td>{@link MailboxListFieldImpl}</td><td>From, Resent-From</td></tr>
|
||||
* <tr><td>{@link AddressListFieldImpl}</td><td>To, Cc, Bcc, Reply-To, Resent-To, Resent-Cc, Resent-Bcc</td></tr>
|
||||
* <tr><td>{@link UnstructuredFieldImpl}</td><td>Subject and others</td></tr>
|
||||
* </table>
|
||||
*
|
||||
* @param rawStr the string to parse.
|
||||
* @return a <code>ParsedField</code> instance.
|
||||
* @throws MimeException if the raw string cannot be split into field name and body.
|
||||
*/
|
||||
public static ParsedField parse(
|
||||
final String rawStr,
|
||||
final DecodeMonitor monitor) throws MimeException {
|
||||
ByteSequence raw = ContentUtil.encode(rawStr);
|
||||
return parse(raw, monitor);
|
||||
}
|
||||
|
||||
public static ParsedField parse(final String rawStr) throws MimeException {
|
||||
ByteSequence raw = ContentUtil.encode(rawStr);
|
||||
return parse(raw, DecodeMonitor.SILENT);
|
||||
}
|
||||
|
||||
public DefaultFieldParser() {
|
||||
setFieldParser(FieldName.CONTENT_TRANSFER_ENCODING,
|
||||
ContentTransferEncodingFieldImpl.PARSER);
|
||||
setFieldParser(FieldName.CONTENT_TYPE, ContentTypeFieldImpl.PARSER);
|
||||
setFieldParser(FieldName.CONTENT_DISPOSITION,
|
||||
ContentDispositionFieldImpl.PARSER);
|
||||
|
||||
final FieldParser<DateTimeFieldImpl> dateTimeParser = DateTimeFieldImpl.PARSER;
|
||||
setFieldParser(FieldName.DATE, dateTimeParser);
|
||||
setFieldParser(FieldName.RESENT_DATE, dateTimeParser);
|
||||
|
||||
final FieldParser<MailboxListFieldImpl> mailboxListParser = MailboxListFieldImpl.PARSER;
|
||||
setFieldParser(FieldName.FROM, mailboxListParser);
|
||||
setFieldParser(FieldName.RESENT_FROM, mailboxListParser);
|
||||
|
||||
final FieldParser<MailboxFieldImpl> mailboxParser = MailboxFieldImpl.PARSER;
|
||||
setFieldParser(FieldName.SENDER, mailboxParser);
|
||||
setFieldParser(FieldName.RESENT_SENDER, mailboxParser);
|
||||
|
||||
final FieldParser<AddressListFieldImpl> addressListParser = AddressListFieldImpl.PARSER;
|
||||
setFieldParser(FieldName.TO, addressListParser);
|
||||
setFieldParser(FieldName.RESENT_TO, addressListParser);
|
||||
setFieldParser(FieldName.CC, addressListParser);
|
||||
setFieldParser(FieldName.RESENT_CC, addressListParser);
|
||||
setFieldParser(FieldName.BCC, addressListParser);
|
||||
setFieldParser(FieldName.RESENT_BCC, addressListParser);
|
||||
setFieldParser(FieldName.REPLY_TO, addressListParser);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,47 +1,56 @@
|
||||
/*
|
||||
* Copyright 2006 the mime4j project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
/****************************************************************
|
||||
* Licensed to the Apache Software Foundation (ASF) under one *
|
||||
* or more contributor license agreements. See the NOTICE file *
|
||||
* distributed with this work for additional information *
|
||||
* regarding copyright ownership. The ASF licenses this file *
|
||||
* to you 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. *
|
||||
****************************************************************/
|
||||
|
||||
package org.apache.james.mime4j.field;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class DelegatingFieldParser implements FieldParser {
|
||||
|
||||
private Map parsers = new HashMap();
|
||||
private FieldParser defaultParser = new UnstructuredField.Parser();
|
||||
|
||||
import org.apache.james.mime4j.codec.DecodeMonitor;
|
||||
import org.apache.james.mime4j.dom.field.ParsedField;
|
||||
import org.apache.james.mime4j.field.UnstructuredFieldImpl;
|
||||
import org.apache.james.mime4j.util.ByteSequence;
|
||||
|
||||
public class DelegatingFieldParser implements FieldParser<ParsedField> {
|
||||
private static final FieldParser<UnstructuredFieldImpl> DEFAULT_PARSER = UnstructuredFieldImpl.PARSER;
|
||||
|
||||
private Map<String, FieldParser<? extends ParsedField>> parsers = new HashMap<String, FieldParser<? extends ParsedField>>();
|
||||
|
||||
/**
|
||||
* Sets the parser used for the field named <code>name</code>.
|
||||
* @param name the name of the field
|
||||
* @param parser the parser for fields named <code>name</code>
|
||||
*/
|
||||
public void setFieldParser(final String name, final FieldParser parser) {
|
||||
public void setFieldParser(final String name, final FieldParser<? extends ParsedField> parser) {
|
||||
parsers.put(name.toLowerCase(), parser);
|
||||
}
|
||||
|
||||
public FieldParser getParser(final String name) {
|
||||
final FieldParser field = (FieldParser) parsers.get(name.toLowerCase());
|
||||
if(field==null) {
|
||||
return defaultParser;
|
||||
public FieldParser<? extends ParsedField> getParser(final String name) {
|
||||
final FieldParser<? extends ParsedField> field = parsers.get(name.toLowerCase());
|
||||
if (field == null) {
|
||||
return DEFAULT_PARSER;
|
||||
}
|
||||
return field;
|
||||
}
|
||||
|
||||
public Field parse(final String name, final String body, final String raw) {
|
||||
final FieldParser parser = getParser(name);
|
||||
return parser.parse(name, body, raw);
|
||||
public ParsedField parse(final String name, final String body, final ByteSequence raw, DecodeMonitor monitor) {
|
||||
final FieldParser<? extends ParsedField> parser = getParser(name);
|
||||
return parser.parse(name, body, raw, monitor);
|
||||
}
|
||||
}
|
||||
|
@ -1,192 +0,0 @@
|
||||
/****************************************************************
|
||||
* Licensed to the Apache Software Foundation (ASF) under one *
|
||||
* or more contributor license agreements. See the NOTICE file *
|
||||
* distributed with this work for additional information *
|
||||
* regarding copyright ownership. The ASF licenses this file *
|
||||
* to you 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. *
|
||||
****************************************************************/
|
||||
|
||||
package org.apache.james.mime4j.field;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* The base class of all field classes.
|
||||
*
|
||||
*
|
||||
* @version $Id: Field.java,v 1.6 2004/10/25 07:26:46 ntherning Exp $
|
||||
*/
|
||||
public abstract class Field {
|
||||
public static final String SENDER = "Sender";
|
||||
public static final String FROM = "From";
|
||||
public static final String TO = "To";
|
||||
public static final String CC = "Cc";
|
||||
public static final String BCC = "Bcc";
|
||||
public static final String REPLY_TO = "Reply-To";
|
||||
public static final String RESENT_SENDER = "Resent-Sender";
|
||||
public static final String RESENT_FROM = "Resent-From";
|
||||
public static final String RESENT_TO = "Resent-To";
|
||||
public static final String RESENT_CC = "Resent-Cc";
|
||||
public static final String RESENT_BCC = "Resent-Bcc";
|
||||
|
||||
public static final String DATE = "Date";
|
||||
public static final String RESENT_DATE = "Resent-Date";
|
||||
|
||||
public static final String SUBJECT = "Subject";
|
||||
public static final String CONTENT_TYPE = "Content-Type";
|
||||
public static final String CONTENT_TRANSFER_ENCODING =
|
||||
"Content-Transfer-Encoding";
|
||||
|
||||
private static final String FIELD_NAME_PATTERN =
|
||||
"^([\\x21-\\x39\\x3b-\\x7e]+)[ \t]*:";
|
||||
private static final Pattern fieldNamePattern =
|
||||
Pattern.compile(FIELD_NAME_PATTERN);
|
||||
|
||||
private static final DefaultFieldParser parser = new DefaultFieldParser();
|
||||
|
||||
private final String name;
|
||||
private final String body;
|
||||
private final String raw;
|
||||
|
||||
protected Field(final String name, final String body, final String raw) {
|
||||
this.name = name;
|
||||
this.body = body;
|
||||
this.raw = raw;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the given string and returns an instance of the
|
||||
* <code>Field</code> class. The type of the class returned depends on
|
||||
* the field name:
|
||||
* <table>
|
||||
* <tr>
|
||||
* <td><em>Field name</em></td><td><em>Class returned</em></td>
|
||||
* <td>Content-Type</td><td>org.apache.james.mime4j.field.ContentTypeField</td>
|
||||
* <td>other</td><td>org.apache.james.mime4j.field.UnstructuredField</td>
|
||||
* </tr>
|
||||
* </table>
|
||||
*
|
||||
* @param s the string to parse.
|
||||
* @return a <code>Field</code> instance.
|
||||
* @throws IllegalArgumentException on parse errors.
|
||||
*/
|
||||
public static Field parse(final String raw) {
|
||||
|
||||
/*
|
||||
* Unfold the field.
|
||||
*/
|
||||
final String unfolded = raw.replaceAll("\r|\n", "");
|
||||
|
||||
/*
|
||||
* Split into name and value.
|
||||
*/
|
||||
final Matcher fieldMatcher = fieldNamePattern.matcher(unfolded);
|
||||
if (!fieldMatcher.find()) {
|
||||
throw new IllegalArgumentException("Invalid field in string");
|
||||
}
|
||||
final String name = fieldMatcher.group(1);
|
||||
|
||||
String body = unfolded.substring(fieldMatcher.end());
|
||||
if (body.length() > 0 && body.charAt(0) == ' ') {
|
||||
body = body.substring(1);
|
||||
}
|
||||
|
||||
return parser.parse(name, body, raw);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the default parser used to parse fields.
|
||||
* @return the default field parser
|
||||
*/
|
||||
public static DefaultFieldParser getParser() {
|
||||
return parser;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name of the field (<code>Subject</code>,
|
||||
* <code>From</code>, etc).
|
||||
*
|
||||
* @return the field name.
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the original raw field string.
|
||||
*
|
||||
* @return the original raw field string.
|
||||
*/
|
||||
public String getRaw() {
|
||||
return raw;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the unfolded, unparsed and possibly encoded (see RFC 2047) field
|
||||
* body string.
|
||||
*
|
||||
* @return the unfolded unparsed field body string.
|
||||
*/
|
||||
public String getBody() {
|
||||
return body;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if this is a <code>Content-Type</code> field.
|
||||
*
|
||||
* @return <code>true</code> if this is a <code>Content-Type</code> field,
|
||||
* <code>false</code> otherwise.
|
||||
*/
|
||||
public boolean isContentType() {
|
||||
return CONTENT_TYPE.equalsIgnoreCase(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if this is a <code>Subject</code> field.
|
||||
*
|
||||
* @return <code>true</code> if this is a <code>Subject</code> field,
|
||||
* <code>false</code> otherwise.
|
||||
*/
|
||||
public boolean isSubject() {
|
||||
return SUBJECT.equalsIgnoreCase(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if this is a <code>From</code> field.
|
||||
*
|
||||
* @return <code>true</code> if this is a <code>From</code> field,
|
||||
* <code>false</code> otherwise.
|
||||
*/
|
||||
public boolean isFrom() {
|
||||
return FROM.equalsIgnoreCase(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if this is a <code>To</code> field.
|
||||
*
|
||||
* @return <code>true</code> if this is a <code>To</code> field,
|
||||
* <code>false</code> otherwise.
|
||||
*/
|
||||
public boolean isTo() {
|
||||
return TO.equalsIgnoreCase(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see #getRaw()
|
||||
*/
|
||||
public String toString() {
|
||||
return raw;
|
||||
}
|
||||
}
|
@ -1,21 +1,30 @@
|
||||
/*
|
||||
* Copyright 2006 the mime4j project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
/****************************************************************
|
||||
* Licensed to the Apache Software Foundation (ASF) under one *
|
||||
* or more contributor license agreements. See the NOTICE file *
|
||||
* distributed with this work for additional information *
|
||||
* regarding copyright ownership. The ASF licenses this file *
|
||||
* to you 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. *
|
||||
****************************************************************/
|
||||
|
||||
package org.apache.james.mime4j.field;
|
||||
|
||||
public interface FieldParser {
|
||||
import org.apache.james.mime4j.codec.DecodeMonitor;
|
||||
import org.apache.james.mime4j.dom.field.ParsedField;
|
||||
import org.apache.james.mime4j.util.ByteSequence;
|
||||
|
||||
public interface FieldParser<T extends ParsedField> {
|
||||
|
||||
T parse(final String name, final String body, final ByteSequence raw, DecodeMonitor monitor);
|
||||
|
||||
Field parse(final String name, final String body, final String raw);
|
||||
}
|
||||
|
643
src/org/apache/james/mime4j/field/Fields.java
Normal file
643
src/org/apache/james/mime4j/field/Fields.java
Normal file
@ -0,0 +1,643 @@
|
||||
/****************************************************************
|
||||
* Licensed to the Apache Software Foundation (ASF) under one *
|
||||
* or more contributor license agreements. See the NOTICE file *
|
||||
* distributed with this work for additional information *
|
||||
* regarding copyright ownership. The ASF licenses this file *
|
||||
* to you 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. *
|
||||
****************************************************************/
|
||||
|
||||
package org.apache.james.mime4j.field;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.TimeZone;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.apache.james.mime4j.codec.DecodeMonitor;
|
||||
import org.apache.james.mime4j.codec.EncoderUtil;
|
||||
import org.apache.james.mime4j.dom.address.Address;
|
||||
import org.apache.james.mime4j.dom.address.Mailbox;
|
||||
import org.apache.james.mime4j.dom.field.AddressListField;
|
||||
import org.apache.james.mime4j.dom.field.ContentDispositionField;
|
||||
import org.apache.james.mime4j.dom.field.ContentTransferEncodingField;
|
||||
import org.apache.james.mime4j.dom.field.ContentTypeField;
|
||||
import org.apache.james.mime4j.dom.field.DateTimeField;
|
||||
import org.apache.james.mime4j.dom.field.Field;
|
||||
import org.apache.james.mime4j.dom.field.FieldName;
|
||||
import org.apache.james.mime4j.dom.field.MailboxField;
|
||||
import org.apache.james.mime4j.dom.field.MailboxListField;
|
||||
import org.apache.james.mime4j.dom.field.ParsedField;
|
||||
import org.apache.james.mime4j.dom.field.UnstructuredField;
|
||||
import org.apache.james.mime4j.field.AddressListFieldImpl;
|
||||
import org.apache.james.mime4j.field.ContentDispositionFieldImpl;
|
||||
import org.apache.james.mime4j.field.ContentTransferEncodingFieldImpl;
|
||||
import org.apache.james.mime4j.field.ContentTypeFieldImpl;
|
||||
import org.apache.james.mime4j.field.DateTimeFieldImpl;
|
||||
import org.apache.james.mime4j.field.MailboxFieldImpl;
|
||||
import org.apache.james.mime4j.field.MailboxListFieldImpl;
|
||||
import org.apache.james.mime4j.field.UnstructuredFieldImpl;
|
||||
import org.apache.james.mime4j.field.address.formatter.AddressFormatter;
|
||||
import org.apache.james.mime4j.stream.RawField;
|
||||
import org.apache.james.mime4j.util.MimeUtil;
|
||||
|
||||
/**
|
||||
* Factory for concrete {@link Field} instances.
|
||||
*/
|
||||
public class Fields {
|
||||
|
||||
private static final Pattern FIELD_NAME_PATTERN = Pattern
|
||||
.compile("[\\x21-\\x39\\x3b-\\x7e]+");
|
||||
|
||||
private Fields() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a <i>Content-Type</i> field from the specified raw field value.
|
||||
* The specified string gets folded into a multiple-line representation if
|
||||
* necessary but is otherwise taken as is.
|
||||
*
|
||||
* @param contentType
|
||||
* raw content type containing a MIME type and optional
|
||||
* parameters.
|
||||
* @return the newly created <i>Content-Type</i> field.
|
||||
*/
|
||||
public static ContentTypeField contentType(String contentType) {
|
||||
return parse(ContentTypeFieldImpl.PARSER, FieldName.CONTENT_TYPE,
|
||||
contentType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a <i>Content-Type</i> field from the specified MIME type and
|
||||
* parameters.
|
||||
*
|
||||
* @param mimeType
|
||||
* a MIME type (such as <code>"text/plain"</code> or
|
||||
* <code>"application/octet-stream"</code>).
|
||||
* @param parameters
|
||||
* map containing content-type parameters such as
|
||||
* <code>"boundary"</code>.
|
||||
* @return the newly created <i>Content-Type</i> field.
|
||||
*/
|
||||
public static ContentTypeField contentType(String mimeType,
|
||||
Map<String, String> parameters) {
|
||||
if (!isValidMimeType(mimeType))
|
||||
throw new IllegalArgumentException();
|
||||
|
||||
if (parameters == null || parameters.isEmpty()) {
|
||||
return parse(ContentTypeFieldImpl.PARSER, FieldName.CONTENT_TYPE,
|
||||
mimeType);
|
||||
} else {
|
||||
StringBuilder sb = new StringBuilder(mimeType);
|
||||
for (Map.Entry<String, String> entry : parameters.entrySet()) {
|
||||
sb.append("; ");
|
||||
sb.append(EncoderUtil.encodeHeaderParameter(entry.getKey(),
|
||||
entry.getValue()));
|
||||
}
|
||||
String contentType = sb.toString();
|
||||
return contentType(contentType);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a <i>Content-Transfer-Encoding</i> field from the specified raw
|
||||
* field value.
|
||||
*
|
||||
* @param contentTransferEncoding
|
||||
* an encoding mechanism such as <code>"7-bit"</code>
|
||||
* or <code>"quoted-printable"</code>.
|
||||
* @return the newly created <i>Content-Transfer-Encoding</i> field.
|
||||
*/
|
||||
public static ContentTransferEncodingField contentTransferEncoding(
|
||||
String contentTransferEncoding) {
|
||||
return parse(ContentTransferEncodingFieldImpl.PARSER,
|
||||
FieldName.CONTENT_TRANSFER_ENCODING, contentTransferEncoding);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a <i>Content-Disposition</i> field from the specified raw field
|
||||
* value. The specified string gets folded into a multiple-line
|
||||
* representation if necessary but is otherwise taken as is.
|
||||
*
|
||||
* @param contentDisposition
|
||||
* raw content disposition containing a disposition type and
|
||||
* optional parameters.
|
||||
* @return the newly created <i>Content-Disposition</i> field.
|
||||
*/
|
||||
public static ContentDispositionField contentDisposition(
|
||||
String contentDisposition) {
|
||||
return parse(ContentDispositionFieldImpl.PARSER,
|
||||
FieldName.CONTENT_DISPOSITION, contentDisposition);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a <i>Content-Disposition</i> field from the specified
|
||||
* disposition type and parameters.
|
||||
*
|
||||
* @param dispositionType
|
||||
* a disposition type (usually <code>"inline"</code>
|
||||
* or <code>"attachment"</code>).
|
||||
* @param parameters
|
||||
* map containing disposition parameters such as
|
||||
* <code>"filename"</code>.
|
||||
* @return the newly created <i>Content-Disposition</i> field.
|
||||
*/
|
||||
public static ContentDispositionField contentDisposition(
|
||||
String dispositionType, Map<String, String> parameters) {
|
||||
if (!isValidDispositionType(dispositionType))
|
||||
throw new IllegalArgumentException();
|
||||
|
||||
if (parameters == null || parameters.isEmpty()) {
|
||||
return parse(ContentDispositionFieldImpl.PARSER,
|
||||
FieldName.CONTENT_DISPOSITION, dispositionType);
|
||||
} else {
|
||||
StringBuilder sb = new StringBuilder(dispositionType);
|
||||
for (Map.Entry<String, String> entry : parameters.entrySet()) {
|
||||
sb.append("; ");
|
||||
sb.append(EncoderUtil.encodeHeaderParameter(entry.getKey(),
|
||||
entry.getValue()));
|
||||
}
|
||||
String contentDisposition = sb.toString();
|
||||
return contentDisposition(contentDisposition);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a <i>Content-Disposition</i> field from the specified
|
||||
* disposition type and filename.
|
||||
*
|
||||
* @param dispositionType
|
||||
* a disposition type (usually <code>"inline"</code>
|
||||
* or <code>"attachment"</code>).
|
||||
* @param filename
|
||||
* filename parameter value or <code>null</code> if the
|
||||
* parameter should not be included.
|
||||
* @return the newly created <i>Content-Disposition</i> field.
|
||||
*/
|
||||
public static ContentDispositionField contentDisposition(
|
||||
String dispositionType, String filename) {
|
||||
return contentDisposition(dispositionType, filename, -1, null, null,
|
||||
null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a <i>Content-Disposition</i> field from the specified values.
|
||||
*
|
||||
* @param dispositionType
|
||||
* a disposition type (usually <code>"inline"</code>
|
||||
* or <code>"attachment"</code>).
|
||||
* @param filename
|
||||
* filename parameter value or <code>null</code> if the
|
||||
* parameter should not be included.
|
||||
* @param size
|
||||
* size parameter value or <code>-1</code> if the parameter
|
||||
* should not be included.
|
||||
* @return the newly created <i>Content-Disposition</i> field.
|
||||
*/
|
||||
public static ContentDispositionField contentDisposition(
|
||||
String dispositionType, String filename, long size) {
|
||||
return contentDisposition(dispositionType, filename, size, null, null,
|
||||
null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a <i>Content-Disposition</i> field from the specified values.
|
||||
*
|
||||
* @param dispositionType
|
||||
* a disposition type (usually <code>"inline"</code>
|
||||
* or <code>"attachment"</code>).
|
||||
* @param filename
|
||||
* filename parameter value or <code>null</code> if the
|
||||
* parameter should not be included.
|
||||
* @param size
|
||||
* size parameter value or <code>-1</code> if the parameter
|
||||
* should not be included.
|
||||
* @param creationDate
|
||||
* creation-date parameter value or <code>null</code> if the
|
||||
* parameter should not be included.
|
||||
* @param modificationDate
|
||||
* modification-date parameter value or <code>null</code> if
|
||||
* the parameter should not be included.
|
||||
* @param readDate
|
||||
* read-date parameter value or <code>null</code> if the
|
||||
* parameter should not be included.
|
||||
* @return the newly created <i>Content-Disposition</i> field.
|
||||
*/
|
||||
public static ContentDispositionField contentDisposition(
|
||||
String dispositionType, String filename, long size,
|
||||
Date creationDate, Date modificationDate, Date readDate) {
|
||||
Map<String, String> parameters = new HashMap<String, String>();
|
||||
if (filename != null) {
|
||||
parameters.put(ContentDispositionFieldImpl.PARAM_FILENAME, filename);
|
||||
}
|
||||
if (size >= 0) {
|
||||
parameters.put(ContentDispositionFieldImpl.PARAM_SIZE, Long
|
||||
.toString(size));
|
||||
}
|
||||
if (creationDate != null) {
|
||||
parameters.put(ContentDispositionFieldImpl.PARAM_CREATION_DATE,
|
||||
MimeUtil.formatDate(creationDate, null));
|
||||
}
|
||||
if (modificationDate != null) {
|
||||
parameters.put(ContentDispositionFieldImpl.PARAM_MODIFICATION_DATE,
|
||||
MimeUtil.formatDate(modificationDate, null));
|
||||
}
|
||||
if (readDate != null) {
|
||||
parameters.put(ContentDispositionFieldImpl.PARAM_READ_DATE, MimeUtil
|
||||
.formatDate(readDate, null));
|
||||
}
|
||||
return contentDisposition(dispositionType, parameters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a <i>Date</i> field from the specified <code>Date</code>
|
||||
* value. The default time zone of the host is used to format the date.
|
||||
*
|
||||
* @param date
|
||||
* date value for the header field.
|
||||
* @return the newly created <i>Date</i> field.
|
||||
*/
|
||||
public static DateTimeField date(Date date) {
|
||||
return date0(FieldName.DATE, date, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a date field from the specified field name and <code>Date</code>
|
||||
* value. The default time zone of the host is used to format the date.
|
||||
*
|
||||
* @param fieldName
|
||||
* a field name such as <code>Date</code> or
|
||||
* <code>Resent-Date</code>.
|
||||
* @param date
|
||||
* date value for the header field.
|
||||
* @return the newly created date field.
|
||||
*/
|
||||
public static DateTimeField date(String fieldName, Date date) {
|
||||
checkValidFieldName(fieldName);
|
||||
return date0(fieldName, date, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a date field from the specified field name, <code>Date</code>
|
||||
* and <code>TimeZone</code> values.
|
||||
*
|
||||
* @param fieldName
|
||||
* a field name such as <code>Date</code> or
|
||||
* <code>Resent-Date</code>.
|
||||
* @param date
|
||||
* date value for the header field.
|
||||
* @param zone
|
||||
* the time zone to be used for formatting the date.
|
||||
* @return the newly created date field.
|
||||
*/
|
||||
public static DateTimeField date(String fieldName, Date date, TimeZone zone) {
|
||||
checkValidFieldName(fieldName);
|
||||
return date0(fieldName, date, zone);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a <i>Message-ID</i> field for the specified host name.
|
||||
*
|
||||
* @param hostname
|
||||
* host name to be included in the message ID or
|
||||
* <code>null</code> if no host name should be included.
|
||||
* @return the newly created <i>Message-ID</i> field.
|
||||
*/
|
||||
public static UnstructuredField messageId(String hostname) {
|
||||
String fieldValue = MimeUtil.createUniqueMessageId(hostname);
|
||||
return parse(UnstructuredFieldImpl.PARSER, FieldName.MESSAGE_ID, fieldValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a <i>Subject</i> field from the specified string value. The
|
||||
* specified string may contain non-ASCII characters.
|
||||
*
|
||||
* @param subject
|
||||
* the subject string.
|
||||
* @return the newly created <i>Subject</i> field.
|
||||
*/
|
||||
public static UnstructuredField subject(String subject) {
|
||||
int usedCharacters = FieldName.SUBJECT.length() + 2;
|
||||
String fieldValue = EncoderUtil.encodeIfNecessary(subject,
|
||||
EncoderUtil.Usage.TEXT_TOKEN, usedCharacters);
|
||||
|
||||
return parse(UnstructuredFieldImpl.PARSER, FieldName.SUBJECT, fieldValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a <i>Sender</i> field for the specified mailbox address.
|
||||
*
|
||||
* @param mailbox
|
||||
* address to be included in the field.
|
||||
* @return the newly created <i>Sender</i> field.
|
||||
*/
|
||||
public static MailboxField sender(Mailbox mailbox) {
|
||||
return mailbox0(FieldName.SENDER, mailbox);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a <i>From</i> field for the specified mailbox address.
|
||||
*
|
||||
* @param mailbox
|
||||
* address to be included in the field.
|
||||
* @return the newly created <i>From</i> field.
|
||||
*/
|
||||
public static MailboxListField from(Mailbox mailbox) {
|
||||
return mailboxList0(FieldName.FROM, Collections.singleton(mailbox));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a <i>From</i> field for the specified mailbox addresses.
|
||||
*
|
||||
* @param mailboxes
|
||||
* addresses to be included in the field.
|
||||
* @return the newly created <i>From</i> field.
|
||||
*/
|
||||
public static MailboxListField from(Mailbox... mailboxes) {
|
||||
return mailboxList0(FieldName.FROM, Arrays.asList(mailboxes));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a <i>From</i> field for the specified mailbox addresses.
|
||||
*
|
||||
* @param mailboxes
|
||||
* addresses to be included in the field.
|
||||
* @return the newly created <i>From</i> field.
|
||||
*/
|
||||
public static MailboxListField from(Iterable<Mailbox> mailboxes) {
|
||||
return mailboxList0(FieldName.FROM, mailboxes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a <i>To</i> field for the specified mailbox or group address.
|
||||
*
|
||||
* @param address
|
||||
* mailbox or group address to be included in the field.
|
||||
* @return the newly created <i>To</i> field.
|
||||
*/
|
||||
public static AddressListField to(Address address) {
|
||||
return addressList0(FieldName.TO, Collections.singleton(address));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a <i>To</i> field for the specified mailbox or group addresses.
|
||||
*
|
||||
* @param addresses
|
||||
* mailbox or group addresses to be included in the field.
|
||||
* @return the newly created <i>To</i> field.
|
||||
*/
|
||||
public static AddressListField to(Address... addresses) {
|
||||
return addressList0(FieldName.TO, Arrays.asList(addresses));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a <i>To</i> field for the specified mailbox or group addresses.
|
||||
*
|
||||
* @param addresses
|
||||
* mailbox or group addresses to be included in the field.
|
||||
* @return the newly created <i>To</i> field.
|
||||
*/
|
||||
public static AddressListField to(Iterable<Address> addresses) {
|
||||
return addressList0(FieldName.TO, addresses);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a <i>Cc</i> field for the specified mailbox or group address.
|
||||
*
|
||||
* @param address
|
||||
* mailbox or group address to be included in the field.
|
||||
* @return the newly created <i>Cc</i> field.
|
||||
*/
|
||||
public static AddressListField cc(Address address) {
|
||||
return addressList0(FieldName.CC, Collections.singleton(address));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a <i>Cc</i> field for the specified mailbox or group addresses.
|
||||
*
|
||||
* @param addresses
|
||||
* mailbox or group addresses to be included in the field.
|
||||
* @return the newly created <i>Cc</i> field.
|
||||
*/
|
||||
public static AddressListField cc(Address... addresses) {
|
||||
return addressList0(FieldName.CC, Arrays.asList(addresses));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a <i>Cc</i> field for the specified mailbox or group addresses.
|
||||
*
|
||||
* @param addresses
|
||||
* mailbox or group addresses to be included in the field.
|
||||
* @return the newly created <i>Cc</i> field.
|
||||
*/
|
||||
public static AddressListField cc(Iterable<Address> addresses) {
|
||||
return addressList0(FieldName.CC, addresses);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a <i>Bcc</i> field for the specified mailbox or group address.
|
||||
*
|
||||
* @param address
|
||||
* mailbox or group address to be included in the field.
|
||||
* @return the newly created <i>Bcc</i> field.
|
||||
*/
|
||||
public static AddressListField bcc(Address address) {
|
||||
return addressList0(FieldName.BCC, Collections.singleton(address));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a <i>Bcc</i> field for the specified mailbox or group addresses.
|
||||
*
|
||||
* @param addresses
|
||||
* mailbox or group addresses to be included in the field.
|
||||
* @return the newly created <i>Bcc</i> field.
|
||||
*/
|
||||
public static AddressListField bcc(Address... addresses) {
|
||||
return addressList0(FieldName.BCC, Arrays.asList(addresses));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a <i>Bcc</i> field for the specified mailbox or group addresses.
|
||||
*
|
||||
* @param addresses
|
||||
* mailbox or group addresses to be included in the field.
|
||||
* @return the newly created <i>Bcc</i> field.
|
||||
*/
|
||||
public static AddressListField bcc(Iterable<Address> addresses) {
|
||||
return addressList0(FieldName.BCC, addresses);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a <i>Reply-To</i> field for the specified mailbox or group
|
||||
* address.
|
||||
*
|
||||
* @param address
|
||||
* mailbox or group address to be included in the field.
|
||||
* @return the newly created <i>Reply-To</i> field.
|
||||
*/
|
||||
public static AddressListField replyTo(Address address) {
|
||||
return addressList0(FieldName.REPLY_TO, Collections.singleton(address));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a <i>Reply-To</i> field for the specified mailbox or group
|
||||
* addresses.
|
||||
*
|
||||
* @param addresses
|
||||
* mailbox or group addresses to be included in the field.
|
||||
* @return the newly created <i>Reply-To</i> field.
|
||||
*/
|
||||
public static AddressListField replyTo(Address... addresses) {
|
||||
return addressList0(FieldName.REPLY_TO, Arrays.asList(addresses));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a <i>Reply-To</i> field for the specified mailbox or group
|
||||
* addresses.
|
||||
*
|
||||
* @param addresses
|
||||
* mailbox or group addresses to be included in the field.
|
||||
* @return the newly created <i>Reply-To</i> field.
|
||||
*/
|
||||
public static AddressListField replyTo(Iterable<Address> addresses) {
|
||||
return addressList0(FieldName.REPLY_TO, addresses);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a mailbox field from the specified field name and mailbox
|
||||
* address. Valid field names are <code>Sender</code> and
|
||||
* <code>Resent-Sender</code>.
|
||||
*
|
||||
* @param fieldName
|
||||
* the name of the mailbox field (<code>Sender</code> or
|
||||
* <code>Resent-Sender</code>).
|
||||
* @param mailbox
|
||||
* mailbox address for the field value.
|
||||
* @return the newly created mailbox field.
|
||||
*/
|
||||
public static MailboxField mailbox(String fieldName, Mailbox mailbox) {
|
||||
checkValidFieldName(fieldName);
|
||||
return mailbox0(fieldName, mailbox);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a mailbox-list field from the specified field name and mailbox
|
||||
* addresses. Valid field names are <code>From</code> and
|
||||
* <code>Resent-From</code>.
|
||||
*
|
||||
* @param fieldName
|
||||
* the name of the mailbox field (<code>From</code> or
|
||||
* <code>Resent-From</code>).
|
||||
* @param mailboxes
|
||||
* mailbox addresses for the field value.
|
||||
* @return the newly created mailbox-list field.
|
||||
*/
|
||||
public static MailboxListField mailboxList(String fieldName,
|
||||
Iterable<Mailbox> mailboxes) {
|
||||
checkValidFieldName(fieldName);
|
||||
return mailboxList0(fieldName, mailboxes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an address-list field from the specified field name and mailbox
|
||||
* or group addresses. Valid field names are <code>To</code>,
|
||||
* <code>Cc</code>, <code>Bcc</code>, <code>Reply-To</code>,
|
||||
* <code>Resent-To</code>, <code>Resent-Cc</code> and
|
||||
* <code>Resent-Bcc</code>.
|
||||
*
|
||||
* @param fieldName
|
||||
* the name of the mailbox field (<code>To</code>,
|
||||
* <code>Cc</code>, <code>Bcc</code>, <code>Reply-To</code>,
|
||||
* <code>Resent-To</code>, <code>Resent-Cc</code> or
|
||||
* <code>Resent-Bcc</code>).
|
||||
* @param addresses
|
||||
* mailbox or group addresses for the field value.
|
||||
* @return the newly created address-list field.
|
||||
*/
|
||||
public static AddressListField addressList(String fieldName,
|
||||
Iterable<Address> addresses) {
|
||||
checkValidFieldName(fieldName);
|
||||
return addressList0(fieldName, addresses);
|
||||
}
|
||||
|
||||
private static DateTimeField date0(String fieldName, Date date,
|
||||
TimeZone zone) {
|
||||
final String formattedDate = MimeUtil.formatDate(date, zone);
|
||||
return parse(DateTimeFieldImpl.PARSER, fieldName, formattedDate);
|
||||
}
|
||||
|
||||
private static MailboxField mailbox0(String fieldName, Mailbox mailbox) {
|
||||
String fieldValue = encodeAddresses(Collections.singleton(mailbox));
|
||||
return parse(MailboxFieldImpl.PARSER, fieldName, fieldValue);
|
||||
}
|
||||
|
||||
private static MailboxListField mailboxList0(String fieldName,
|
||||
Iterable<Mailbox> mailboxes) {
|
||||
String fieldValue = encodeAddresses(mailboxes);
|
||||
return parse(MailboxListFieldImpl.PARSER, fieldName, fieldValue);
|
||||
}
|
||||
|
||||
private static AddressListField addressList0(String fieldName,
|
||||
Iterable<Address> addresses) {
|
||||
String fieldValue = encodeAddresses(addresses);
|
||||
return parse(AddressListFieldImpl.PARSER, fieldName, fieldValue);
|
||||
}
|
||||
|
||||
private static void checkValidFieldName(String fieldName) {
|
||||
if (!FIELD_NAME_PATTERN.matcher(fieldName).matches())
|
||||
throw new IllegalArgumentException("Invalid field name");
|
||||
}
|
||||
|
||||
private static boolean isValidMimeType(String mimeType) {
|
||||
if (mimeType == null)
|
||||
return false;
|
||||
|
||||
int idx = mimeType.indexOf('/');
|
||||
if (idx == -1)
|
||||
return false;
|
||||
|
||||
String type = mimeType.substring(0, idx);
|
||||
String subType = mimeType.substring(idx + 1);
|
||||
return EncoderUtil.isToken(type) && EncoderUtil.isToken(subType);
|
||||
}
|
||||
|
||||
private static boolean isValidDispositionType(String dispositionType) {
|
||||
if (dispositionType == null)
|
||||
return false;
|
||||
|
||||
return EncoderUtil.isToken(dispositionType);
|
||||
}
|
||||
|
||||
private static <F extends ParsedField> F parse(FieldParser<F> parser,
|
||||
String fieldName, String fieldBody) {
|
||||
RawField rawField = new RawField(fieldName, fieldBody);
|
||||
return parser.parse(rawField.getName(), rawField.getBody(), rawField.getRaw(),
|
||||
DecodeMonitor.SILENT);
|
||||
}
|
||||
|
||||
private static String encodeAddresses(Iterable<? extends Address> addresses) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
for (Address address : addresses) {
|
||||
if (sb.length() > 0) {
|
||||
sb.append(", ");
|
||||
}
|
||||
AddressFormatter.encode(sb, address);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
}
|
@ -1,68 +0,0 @@
|
||||
/****************************************************************
|
||||
* Licensed to the Apache Software Foundation (ASF) under one *
|
||||
* or more contributor license agreements. See the NOTICE file *
|
||||
* distributed with this work for additional information *
|
||||
* regarding copyright ownership. The ASF licenses this file *
|
||||
* to you 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. *
|
||||
****************************************************************/
|
||||
|
||||
package org.apache.james.mime4j.field;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.apache.james.mime4j.field.address.AddressList;
|
||||
import org.apache.james.mime4j.field.address.Mailbox;
|
||||
import org.apache.james.mime4j.field.address.MailboxList;
|
||||
import org.apache.james.mime4j.field.address.parser.ParseException;
|
||||
|
||||
public class MailboxField extends Field {
|
||||
private final Mailbox mailbox;
|
||||
private final ParseException parseException;
|
||||
|
||||
protected MailboxField(final String name, final String body, final String raw, final Mailbox mailbox, final ParseException parseException) {
|
||||
super(name, body, raw);
|
||||
this.mailbox = mailbox;
|
||||
this.parseException = parseException;
|
||||
}
|
||||
|
||||
public Mailbox getMailbox() {
|
||||
return mailbox;
|
||||
}
|
||||
|
||||
public ParseException getParseException() {
|
||||
return parseException;
|
||||
}
|
||||
|
||||
public static class Parser implements FieldParser {
|
||||
private static Log log = LogFactory.getLog(Parser.class);
|
||||
|
||||
public Field parse(final String name, final String body, final String raw) {
|
||||
Mailbox mailbox = null;
|
||||
ParseException parseException = null;
|
||||
try {
|
||||
MailboxList mailboxList = AddressList.parse(body).flatten();
|
||||
if (mailboxList.size() > 0) {
|
||||
mailbox = mailboxList.get(0);
|
||||
}
|
||||
}
|
||||
catch (ParseException e) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Parsing value '" + body + "': "+ e.getMessage());
|
||||
}
|
||||
parseException = e;
|
||||
}
|
||||
return new MailboxField(name, body, raw, mailbox, parseException);
|
||||
}
|
||||
}
|
||||
}
|
84
src/org/apache/james/mime4j/field/MailboxFieldImpl.java
Normal file
84
src/org/apache/james/mime4j/field/MailboxFieldImpl.java
Normal file
@ -0,0 +1,84 @@
|
||||
/****************************************************************
|
||||
* Licensed to the Apache Software Foundation (ASF) under one *
|
||||
* or more contributor license agreements. See the NOTICE file *
|
||||
* distributed with this work for additional information *
|
||||
* regarding copyright ownership. The ASF licenses this file *
|
||||
* to you 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. *
|
||||
****************************************************************/
|
||||
|
||||
package org.apache.james.mime4j.field;
|
||||
|
||||
import org.apache.james.mime4j.codec.DecodeMonitor;
|
||||
import org.apache.james.mime4j.dom.address.Mailbox;
|
||||
import org.apache.james.mime4j.dom.address.MailboxList;
|
||||
import org.apache.james.mime4j.field.address.parser.AddressBuilder;
|
||||
import org.apache.james.mime4j.field.address.parser.ParseException;
|
||||
import org.apache.james.mime4j.util.ByteSequence;
|
||||
|
||||
/**
|
||||
* Mailbox field such as <code>Sender</code> or <code>Resent-Sender</code>.
|
||||
*/
|
||||
public class MailboxFieldImpl extends AbstractField implements org.apache.james.mime4j.dom.field.MailboxField {
|
||||
private boolean parsed = false;
|
||||
|
||||
private Mailbox mailbox;
|
||||
private ParseException parseException;
|
||||
|
||||
MailboxFieldImpl(final String name, final String body, final ByteSequence raw, DecodeMonitor monitor) {
|
||||
super(name, body, raw, monitor);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.apache.james.mime4j.dom.field.MailboxField#getMailbox()
|
||||
*/
|
||||
public Mailbox getMailbox() {
|
||||
if (!parsed)
|
||||
parse();
|
||||
|
||||
return mailbox;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.apache.james.mime4j.dom.field.MailboxField#getParseException()
|
||||
*/
|
||||
@Override
|
||||
public ParseException getParseException() {
|
||||
if (!parsed)
|
||||
parse();
|
||||
|
||||
return parseException;
|
||||
}
|
||||
|
||||
private void parse() {
|
||||
String body = getBody();
|
||||
|
||||
try {
|
||||
MailboxList mailboxList = AddressBuilder.parseAddressList(body, monitor).flatten();
|
||||
if (mailboxList.size() > 0) {
|
||||
mailbox = mailboxList.get(0);
|
||||
}
|
||||
} catch (ParseException e) {
|
||||
parseException = e;
|
||||
}
|
||||
|
||||
parsed = true;
|
||||
}
|
||||
|
||||
static final FieldParser<MailboxFieldImpl> PARSER = new FieldParser<MailboxFieldImpl>() {
|
||||
public MailboxFieldImpl parse(final String name, final String body,
|
||||
final ByteSequence raw, DecodeMonitor monitor) {
|
||||
return new MailboxFieldImpl(name, body, raw, monitor);
|
||||
}
|
||||
};
|
||||
}
|
@ -19,47 +19,62 @@
|
||||
|
||||
package org.apache.james.mime4j.field;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.apache.james.mime4j.field.address.AddressList;
|
||||
import org.apache.james.mime4j.field.address.MailboxList;
|
||||
import org.apache.james.mime4j.codec.DecodeMonitor;
|
||||
import org.apache.james.mime4j.dom.address.MailboxList;
|
||||
import org.apache.james.mime4j.field.address.parser.AddressBuilder;
|
||||
import org.apache.james.mime4j.field.address.parser.ParseException;
|
||||
import org.apache.james.mime4j.util.ByteSequence;
|
||||
|
||||
/**
|
||||
* Mailbox-list field such as <code>From</code> or <code>Resent-From</code>.
|
||||
*/
|
||||
public class MailboxListFieldImpl extends AbstractField implements org.apache.james.mime4j.dom.field.MailboxListField {
|
||||
private boolean parsed = false;
|
||||
|
||||
public class MailboxListField extends Field {
|
||||
|
||||
private MailboxList mailboxList;
|
||||
private ParseException parseException;
|
||||
|
||||
protected MailboxListField(final String name, final String body, final String raw, final MailboxList mailboxList, final ParseException parseException) {
|
||||
super(name, body, raw);
|
||||
this.mailboxList = mailboxList;
|
||||
this.parseException = parseException;
|
||||
MailboxListFieldImpl(final String name, final String body, final ByteSequence raw, DecodeMonitor monitor) {
|
||||
super(name, body, raw, monitor);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.apache.james.mime4j.dom.field.MailboxListField#getMailboxList()
|
||||
*/
|
||||
public MailboxList getMailboxList() {
|
||||
if (!parsed)
|
||||
parse();
|
||||
|
||||
return mailboxList;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.apache.james.mime4j.dom.field.MailboxListField#getParseException()
|
||||
*/
|
||||
@Override
|
||||
public ParseException getParseException() {
|
||||
if (!parsed)
|
||||
parse();
|
||||
|
||||
return parseException;
|
||||
}
|
||||
|
||||
public static class Parser implements FieldParser {
|
||||
private static Log log = LogFactory.getLog(Parser.class);
|
||||
|
||||
public Field parse(final String name, final String body, final String raw) {
|
||||
MailboxList mailboxList = null;
|
||||
ParseException parseException = null;
|
||||
try {
|
||||
mailboxList = AddressList.parse(body).flatten();
|
||||
}
|
||||
catch (ParseException e) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Parsing value '" + body + "': "+ e.getMessage());
|
||||
}
|
||||
parseException = e;
|
||||
}
|
||||
return new MailboxListField(name, body, raw, mailboxList, parseException);
|
||||
private void parse() {
|
||||
String body = getBody();
|
||||
|
||||
try {
|
||||
mailboxList = AddressBuilder.parseAddressList(body, monitor).flatten();
|
||||
} catch (ParseException e) {
|
||||
parseException = e;
|
||||
}
|
||||
|
||||
parsed = true;
|
||||
}
|
||||
|
||||
static final FieldParser<MailboxListFieldImpl> PARSER = new FieldParser<MailboxListFieldImpl>() {
|
||||
public MailboxListFieldImpl parse(final String name, final String body,
|
||||
final ByteSequence raw, DecodeMonitor monitor) {
|
||||
return new MailboxListFieldImpl(name, body, raw, monitor);
|
||||
}
|
||||
};
|
||||
}
|
62
src/org/apache/james/mime4j/field/UnstructuredFieldImpl.java
Normal file
62
src/org/apache/james/mime4j/field/UnstructuredFieldImpl.java
Normal file
@ -0,0 +1,62 @@
|
||||
/****************************************************************
|
||||
* Licensed to the Apache Software Foundation (ASF) under one *
|
||||
* or more contributor license agreements. See the NOTICE file *
|
||||
* distributed with this work for additional information *
|
||||
* regarding copyright ownership. The ASF licenses this file *
|
||||
* to you 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. *
|
||||
****************************************************************/
|
||||
|
||||
package org.apache.james.mime4j.field;
|
||||
|
||||
import org.apache.james.mime4j.codec.DecodeMonitor;
|
||||
import org.apache.james.mime4j.codec.DecoderUtil;
|
||||
import org.apache.james.mime4j.util.ByteSequence;
|
||||
|
||||
/**
|
||||
* Simple unstructured field such as <code>Subject</code>.
|
||||
*/
|
||||
public class UnstructuredFieldImpl extends AbstractField implements org.apache.james.mime4j.dom.field.UnstructuredField {
|
||||
private boolean parsed = false;
|
||||
|
||||
private String value;
|
||||
|
||||
UnstructuredFieldImpl(String name, String body, ByteSequence raw, DecodeMonitor monitor) {
|
||||
super(name, body, raw, monitor);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.apache.james.mime4j.dom.field.UnstructuredField#getValue()
|
||||
*/
|
||||
public String getValue() {
|
||||
if (!parsed)
|
||||
parse();
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
private void parse() {
|
||||
String body = getBody();
|
||||
|
||||
value = DecoderUtil.decodeEncodedWords(body, monitor);
|
||||
|
||||
parsed = true;
|
||||
}
|
||||
|
||||
static final FieldParser<UnstructuredFieldImpl> PARSER = new FieldParser<UnstructuredFieldImpl>() {
|
||||
public UnstructuredFieldImpl parse(final String name, final String body,
|
||||
final ByteSequence raw, DecodeMonitor monitor) {
|
||||
return new UnstructuredFieldImpl(name, body, raw, monitor);
|
||||
}
|
||||
};
|
||||
}
|
@ -1,140 +0,0 @@
|
||||
/****************************************************************
|
||||
* Licensed to the Apache Software Foundation (ASF) under one *
|
||||
* or more contributor license agreements. See the NOTICE file *
|
||||
* distributed with this work for additional information *
|
||||
* regarding copyright ownership. The ASF licenses this file *
|
||||
* to you 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. *
|
||||
****************************************************************/
|
||||
|
||||
package org.apache.james.mime4j.field.address;
|
||||
|
||||
import org.apache.james.mime4j.field.address.parser.AddressListParser;
|
||||
import org.apache.james.mime4j.field.address.parser.ParseException;
|
||||
|
||||
import java.io.StringReader;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* An immutable, random-access list of Address objects.
|
||||
*
|
||||
*
|
||||
*/
|
||||
public class AddressList {
|
||||
|
||||
private ArrayList addresses;
|
||||
|
||||
/**
|
||||
* @param addresses An ArrayList that contains only Address objects.
|
||||
* @param dontCopy true iff it is not possible for the addresses ArrayList to be modified by someone else.
|
||||
*/
|
||||
public AddressList(ArrayList addresses, boolean dontCopy) {
|
||||
if (addresses != null)
|
||||
this.addresses = (dontCopy ? addresses : (ArrayList) addresses.clone());
|
||||
else
|
||||
this.addresses = new ArrayList(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* The number of elements in this list.
|
||||
*/
|
||||
public int size() {
|
||||
return addresses.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an address.
|
||||
*/
|
||||
public Address get(int index) {
|
||||
if (0 > index || size() <= index)
|
||||
throw new IndexOutOfBoundsException();
|
||||
return (Address) addresses.get(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a flat list of all mailboxes represented
|
||||
* in this address list. Use this if you don't care
|
||||
* about grouping.
|
||||
*/
|
||||
public MailboxList flatten() {
|
||||
// in the common case, all addresses are mailboxes
|
||||
boolean groupDetected = false;
|
||||
for (int i = 0; i < size(); i++) {
|
||||
if (!(get(i) instanceof Mailbox)) {
|
||||
groupDetected = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!groupDetected)
|
||||
return new MailboxList(addresses, true);
|
||||
|
||||
ArrayList results = new ArrayList();
|
||||
for (int i = 0; i < size(); i++) {
|
||||
Address addr = get(i);
|
||||
addr.addMailboxesTo(results);
|
||||
}
|
||||
|
||||
// copy-on-construct this time, because subclasses
|
||||
// could have held onto a reference to the results
|
||||
return new MailboxList(results, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dumps a representation of this address list to
|
||||
* stdout, for debugging purposes.
|
||||
*/
|
||||
public void print() {
|
||||
for (int i = 0; i < size(); i++) {
|
||||
Address addr = get(i);
|
||||
System.out.println(addr.toString());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Parse the address list string, such as the value
|
||||
* of a From, To, Cc, Bcc, Sender, or Reply-To
|
||||
* header.
|
||||
*
|
||||
* The string MUST be unfolded already.
|
||||
*/
|
||||
public static AddressList parse(String rawAddressList) throws ParseException {
|
||||
AddressListParser parser = new AddressListParser(new StringReader(rawAddressList));
|
||||
return Builder.getInstance().buildAddressList(parser.parse());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test console.
|
||||
*/
|
||||
public static void main(String[] args) throws Exception {
|
||||
java.io.BufferedReader reader = new java.io.BufferedReader(new java.io.InputStreamReader(System.in));
|
||||
while (true) {
|
||||
try {
|
||||
System.out.print("> ");
|
||||
String line = reader.readLine();
|
||||
if (line.length() == 0 || line.toLowerCase().equals("exit") || line.toLowerCase().equals("quit")) {
|
||||
System.out.println("Goodbye.");
|
||||
return;
|
||||
}
|
||||
AddressList list = parse(line);
|
||||
list.print();
|
||||
}
|
||||
catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
Thread.sleep(300);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,244 +0,0 @@
|
||||
/****************************************************************
|
||||
* Licensed to the Apache Software Foundation (ASF) under one *
|
||||
* or more contributor license agreements. See the NOTICE file *
|
||||
* distributed with this work for additional information *
|
||||
* regarding copyright ownership. The ASF licenses this file *
|
||||
* to you 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. *
|
||||
****************************************************************/
|
||||
|
||||
package org.apache.james.mime4j.field.address;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
|
||||
import org.apache.james.mime4j.decoder.DecoderUtil;
|
||||
import org.apache.james.mime4j.field.address.parser.*;
|
||||
import org.apache.james.mime4j.field.address.parser.ASTaddr_spec;
|
||||
import org.apache.james.mime4j.field.address.parser.ASTaddress;
|
||||
import org.apache.james.mime4j.field.address.parser.ASTaddress_list;
|
||||
import org.apache.james.mime4j.field.address.parser.ASTangle_addr;
|
||||
import org.apache.james.mime4j.field.address.parser.ASTdomain;
|
||||
import org.apache.james.mime4j.field.address.parser.ASTgroup_body;
|
||||
import org.apache.james.mime4j.field.address.parser.ASTlocal_part;
|
||||
import org.apache.james.mime4j.field.address.parser.ASTmailbox;
|
||||
import org.apache.james.mime4j.field.address.parser.ASTname_addr;
|
||||
import org.apache.james.mime4j.field.address.parser.ASTphrase;
|
||||
import org.apache.james.mime4j.field.address.parser.ASTroute;
|
||||
import org.apache.james.mime4j.field.address.parser.Node;
|
||||
import org.apache.james.mime4j.field.address.parser.SimpleNode;
|
||||
import org.apache.james.mime4j.field.address.parser.Token;
|
||||
|
||||
/**
|
||||
* Transforms the JJTree-generated abstract syntax tree
|
||||
* into a graph of org.apache.james.mime4j.field.address objects.
|
||||
*
|
||||
*
|
||||
*/
|
||||
class Builder {
|
||||
|
||||
private static Builder singleton = new Builder();
|
||||
|
||||
public static Builder getInstance() {
|
||||
return singleton;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public AddressList buildAddressList(ASTaddress_list node) {
|
||||
ArrayList list = new ArrayList();
|
||||
for (int i = 0; i < node.jjtGetNumChildren(); i++) {
|
||||
ASTaddress childNode = (ASTaddress) node.jjtGetChild(i);
|
||||
Address address = buildAddress(childNode);
|
||||
list.add(address);
|
||||
}
|
||||
return new AddressList(list, true);
|
||||
}
|
||||
|
||||
private Address buildAddress(ASTaddress node) {
|
||||
ChildNodeIterator it = new ChildNodeIterator(node);
|
||||
Node n = it.nextNode();
|
||||
if (n instanceof ASTaddr_spec) {
|
||||
return buildAddrSpec((ASTaddr_spec)n);
|
||||
}
|
||||
else if (n instanceof ASTangle_addr) {
|
||||
return buildAngleAddr((ASTangle_addr)n);
|
||||
}
|
||||
else if (n instanceof ASTphrase) {
|
||||
String name = buildString((ASTphrase)n, false);
|
||||
Node n2 = it.nextNode();
|
||||
if (n2 instanceof ASTgroup_body) {
|
||||
return new Group(name, buildGroupBody((ASTgroup_body)n2));
|
||||
}
|
||||
else if (n2 instanceof ASTangle_addr) {
|
||||
name = DecoderUtil.decodeEncodedWords(name);
|
||||
return new NamedMailbox(name, buildAngleAddr((ASTangle_addr)n2));
|
||||
}
|
||||
else {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private MailboxList buildGroupBody(ASTgroup_body node) {
|
||||
ArrayList results = new ArrayList();
|
||||
ChildNodeIterator it = new ChildNodeIterator(node);
|
||||
while (it.hasNext()) {
|
||||
Node n = it.nextNode();
|
||||
if (n instanceof ASTmailbox)
|
||||
results.add(buildMailbox((ASTmailbox)n));
|
||||
else
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
return new MailboxList(results, true);
|
||||
}
|
||||
|
||||
private Mailbox buildMailbox(ASTmailbox node) {
|
||||
ChildNodeIterator it = new ChildNodeIterator(node);
|
||||
Node n = it.nextNode();
|
||||
if (n instanceof ASTaddr_spec) {
|
||||
return buildAddrSpec((ASTaddr_spec)n);
|
||||
}
|
||||
else if (n instanceof ASTangle_addr) {
|
||||
return buildAngleAddr((ASTangle_addr)n);
|
||||
}
|
||||
else if (n instanceof ASTname_addr) {
|
||||
return buildNameAddr((ASTname_addr)n);
|
||||
}
|
||||
else {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
|
||||
private NamedMailbox buildNameAddr(ASTname_addr node) {
|
||||
ChildNodeIterator it = new ChildNodeIterator(node);
|
||||
Node n = it.nextNode();
|
||||
String name;
|
||||
if (n instanceof ASTphrase) {
|
||||
name = buildString((ASTphrase)n, false);
|
||||
}
|
||||
else {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
n = it.nextNode();
|
||||
if (n instanceof ASTangle_addr) {
|
||||
name = DecoderUtil.decodeEncodedWords(name);
|
||||
return new NamedMailbox(name, buildAngleAddr((ASTangle_addr) n));
|
||||
}
|
||||
else {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
|
||||
private Mailbox buildAngleAddr(ASTangle_addr node) {
|
||||
ChildNodeIterator it = new ChildNodeIterator(node);
|
||||
DomainList route = null;
|
||||
Node n = it.nextNode();
|
||||
if (n instanceof ASTroute) {
|
||||
route = buildRoute((ASTroute)n);
|
||||
n = it.nextNode();
|
||||
}
|
||||
else if (n instanceof ASTaddr_spec)
|
||||
; // do nothing
|
||||
else
|
||||
throw new IllegalStateException();
|
||||
|
||||
if (n instanceof ASTaddr_spec)
|
||||
return buildAddrSpec(route, (ASTaddr_spec)n);
|
||||
else
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
private DomainList buildRoute(ASTroute node) {
|
||||
ArrayList results = new ArrayList(node.jjtGetNumChildren());
|
||||
ChildNodeIterator it = new ChildNodeIterator(node);
|
||||
while (it.hasNext()) {
|
||||
Node n = it.nextNode();
|
||||
if (n instanceof ASTdomain)
|
||||
results.add(buildString((ASTdomain)n, true));
|
||||
else
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
return new DomainList(results, true);
|
||||
}
|
||||
|
||||
private Mailbox buildAddrSpec(ASTaddr_spec node) {
|
||||
return buildAddrSpec(null, node);
|
||||
}
|
||||
private Mailbox buildAddrSpec(DomainList route, ASTaddr_spec node) {
|
||||
ChildNodeIterator it = new ChildNodeIterator(node);
|
||||
String localPart = buildString((ASTlocal_part)it.nextNode(), true);
|
||||
String domain = buildString((ASTdomain)it.nextNode(), true);
|
||||
return new Mailbox(route, localPart, domain);
|
||||
}
|
||||
|
||||
|
||||
private String buildString(SimpleNode node, boolean stripSpaces) {
|
||||
Token head = node.firstToken;
|
||||
Token tail = node.lastToken;
|
||||
StringBuffer out = new StringBuffer();
|
||||
|
||||
while (head != tail) {
|
||||
out.append(head.image);
|
||||
head = head.next;
|
||||
if (!stripSpaces)
|
||||
addSpecials(out, head.specialToken);
|
||||
}
|
||||
out.append(tail.image);
|
||||
|
||||
return out.toString();
|
||||
}
|
||||
|
||||
private void addSpecials(StringBuffer out, Token specialToken) {
|
||||
if (specialToken != null) {
|
||||
addSpecials(out, specialToken.specialToken);
|
||||
out.append(specialToken.image);
|
||||
}
|
||||
}
|
||||
|
||||
private static class ChildNodeIterator implements Iterator {
|
||||
|
||||
private SimpleNode simpleNode;
|
||||
private int index;
|
||||
private int len;
|
||||
|
||||
public ChildNodeIterator(SimpleNode simpleNode) {
|
||||
this.simpleNode = simpleNode;
|
||||
this.len = simpleNode.jjtGetNumChildren();
|
||||
this.index = 0;
|
||||
}
|
||||
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public boolean hasNext() {
|
||||
return index < len;
|
||||
}
|
||||
|
||||
public Object next() {
|
||||
return nextNode();
|
||||
}
|
||||
|
||||
public Node nextNode() {
|
||||
return simpleNode.jjtGetChild(index++);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -1,76 +0,0 @@
|
||||
/****************************************************************
|
||||
* Licensed to the Apache Software Foundation (ASF) under one *
|
||||
* or more contributor license agreements. See the NOTICE file *
|
||||
* distributed with this work for additional information *
|
||||
* regarding copyright ownership. The ASF licenses this file *
|
||||
* to you 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. *
|
||||
****************************************************************/
|
||||
|
||||
package org.apache.james.mime4j.field.address;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* An immutable, random-access list of Strings (that
|
||||
* are supposedly domain names or domain literals).
|
||||
*
|
||||
*
|
||||
*/
|
||||
public class DomainList {
|
||||
private ArrayList domains;
|
||||
|
||||
/**
|
||||
* @param domains An ArrayList that contains only String objects.
|
||||
* @param dontCopy true iff it is not possible for the domains ArrayList to be modified by someone else.
|
||||
*/
|
||||
public DomainList(ArrayList domains, boolean dontCopy) {
|
||||
if (domains != null)
|
||||
this.domains = (dontCopy ? domains : (ArrayList) domains.clone());
|
||||
else
|
||||
this.domains = new ArrayList(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* The number of elements in this list.
|
||||
*/
|
||||
public int size() {
|
||||
return domains.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the domain name or domain literal at the
|
||||
* specified index.
|
||||
* @throws IndexOutOfBoundsException If index is < 0 or >= size().
|
||||
*/
|
||||
public String get(int index) {
|
||||
if (0 > index || size() <= index)
|
||||
throw new IndexOutOfBoundsException();
|
||||
return (String) domains.get(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of domains formatted as a route
|
||||
* string (not including the trailing ':').
|
||||
*/
|
||||
public String toRouteString() {
|
||||
StringBuffer out = new StringBuffer();
|
||||
for (int i = 0; i < domains.size(); i++) {
|
||||
out.append("@");
|
||||
out.append(get(i));
|
||||
if (i + 1 < domains.size())
|
||||
out.append(",");
|
||||
}
|
||||
return out.toString();
|
||||
}
|
||||
}
|
@ -1,73 +0,0 @@
|
||||
/****************************************************************
|
||||
* Licensed to the Apache Software Foundation (ASF) under one *
|
||||
* or more contributor license agreements. See the NOTICE file *
|
||||
* distributed with this work for additional information *
|
||||
* regarding copyright ownership. The ASF licenses this file *
|
||||
* to you 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. *
|
||||
****************************************************************/
|
||||
|
||||
package org.apache.james.mime4j.field.address;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* A named group of zero or more mailboxes.
|
||||
*
|
||||
*
|
||||
*/
|
||||
public class Group extends Address {
|
||||
private String name;
|
||||
private MailboxList mailboxList;
|
||||
|
||||
/**
|
||||
* @param name The group name.
|
||||
* @param mailboxes The mailboxes in this group.
|
||||
*/
|
||||
public Group(String name, MailboxList mailboxes) {
|
||||
this.name = name;
|
||||
this.mailboxList = mailboxes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the group name.
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the mailboxes in this group.
|
||||
*/
|
||||
public MailboxList getMailboxes() {
|
||||
return mailboxList;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
StringBuffer buf = new StringBuffer();
|
||||
buf.append(name);
|
||||
buf.append(":");
|
||||
for (int i = 0; i < mailboxList.size(); i++) {
|
||||
buf.append(mailboxList.get(i).toString());
|
||||
if (i + 1 < mailboxList.size())
|
||||
buf.append(",");
|
||||
}
|
||||
buf.append(";");
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
protected void doAddMailboxesTo(ArrayList results) {
|
||||
for (int i = 0; i < mailboxList.size(); i++)
|
||||
results.add(mailboxList.get(i));
|
||||
}
|
||||
}
|
@ -1,119 +0,0 @@
|
||||
/****************************************************************
|
||||
* Licensed to the Apache Software Foundation (ASF) under one *
|
||||
* or more contributor license agreements. See the NOTICE file *
|
||||
* distributed with this work for additional information *
|
||||
* regarding copyright ownership. The ASF licenses this file *
|
||||
* to you 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. *
|
||||
****************************************************************/
|
||||
|
||||
package org.apache.james.mime4j.field.address;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Represents a single e-mail address.
|
||||
*
|
||||
*
|
||||
*/
|
||||
public class Mailbox extends Address {
|
||||
private DomainList route;
|
||||
private String localPart;
|
||||
private String domain;
|
||||
|
||||
/**
|
||||
* Creates a mailbox without a route. Routes are obsolete.
|
||||
* @param localPart The part of the e-mail address to the left of the "@".
|
||||
* @param domain The part of the e-mail address to the right of the "@".
|
||||
*/
|
||||
public Mailbox(String localPart, String domain) {
|
||||
this(null, localPart, domain);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a mailbox with a route. Routes are obsolete.
|
||||
* @param route The zero or more domains that make up the route. Can be null.
|
||||
* @param localPart The part of the e-mail address to the left of the "@".
|
||||
* @param domain The part of the e-mail address to the right of the "@".
|
||||
*/
|
||||
public Mailbox(DomainList route, String localPart, String domain) {
|
||||
this.route = route;
|
||||
this.localPart = localPart;
|
||||
this.domain = domain;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the route list.
|
||||
*/
|
||||
public DomainList getRoute() {
|
||||
return route;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the left part of the e-mail address
|
||||
* (before "@").
|
||||
*/
|
||||
public String getLocalPart() {
|
||||
return localPart;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the right part of the e-mail address
|
||||
* (after "@").
|
||||
*/
|
||||
public String getDomain() {
|
||||
return domain;
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats the address as a string, not including
|
||||
* the route.
|
||||
*
|
||||
* @see #getAddressString(boolean)
|
||||
*/
|
||||
public String getAddressString() {
|
||||
return getAddressString(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Note that this value may not be usable
|
||||
* for transport purposes, only display purposes.
|
||||
*
|
||||
* For example, if the unparsed address was
|
||||
*
|
||||
* <"Joe Cheng"@joecheng.com>
|
||||
*
|
||||
* this method would return
|
||||
*
|
||||
* <Joe Cheng@joecheng.com>
|
||||
*
|
||||
* which is not valid for transport; the local part
|
||||
* would need to be re-quoted.
|
||||
*
|
||||
* @param includeRoute true if the route should be included if it exists.
|
||||
*/
|
||||
public String getAddressString(boolean includeRoute) {
|
||||
return "<" + (!includeRoute || route == null ? "" : route.toRouteString() + ":")
|
||||
+ localPart
|
||||
+ (domain == null ? "" : "@")
|
||||
+ domain + ">";
|
||||
}
|
||||
|
||||
protected final void doAddMailboxesTo(ArrayList results) {
|
||||
results.add(this);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return getAddressString();
|
||||
}
|
||||
}
|
@ -1,70 +0,0 @@
|
||||
/****************************************************************
|
||||
* Licensed to the Apache Software Foundation (ASF) under one *
|
||||
* or more contributor license agreements. See the NOTICE file *
|
||||
* distributed with this work for additional information *
|
||||
* regarding copyright ownership. The ASF licenses this file *
|
||||
* to you 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. *
|
||||
****************************************************************/
|
||||
|
||||
package org.apache.james.mime4j.field.address;
|
||||
|
||||
/**
|
||||
* A Mailbox that has a name/description.
|
||||
*
|
||||
*
|
||||
*/
|
||||
public class NamedMailbox extends Mailbox {
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* @see Mailbox#Mailbox(String, String)
|
||||
*/
|
||||
public NamedMailbox(String name, String localPart, String domain) {
|
||||
super(localPart, domain);
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Mailbox#Mailbox(DomainList, String, String)
|
||||
*/
|
||||
public NamedMailbox(String name, DomainList route, String localPart, String domain) {
|
||||
super(route, localPart, domain);
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a named mailbox based on an unnamed mailbox.
|
||||
*/
|
||||
public NamedMailbox(String name, Mailbox baseMailbox) {
|
||||
super(baseMailbox.getRoute(), baseMailbox.getLocalPart(), baseMailbox.getDomain());
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the mailbox.
|
||||
*/
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Same features (or problems) as Mailbox.getAddressString(boolean),
|
||||
* only more so.
|
||||
*
|
||||
* @see Mailbox#getAddressString(boolean)
|
||||
*/
|
||||
public String getAddressString(boolean includeRoute) {
|
||||
return (name == null ? "" : name + " ") + super.getAddressString(includeRoute);
|
||||
}
|
||||
}
|
@ -0,0 +1,209 @@
|
||||
/****************************************************************
|
||||
* Licensed to the Apache Software Foundation (ASF) under one *
|
||||
* or more contributor license agreements. See the NOTICE file *
|
||||
* distributed with this work for additional information *
|
||||
* regarding copyright ownership. The ASF licenses this file *
|
||||
* to you 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. *
|
||||
****************************************************************/
|
||||
|
||||
package org.apache.james.mime4j.field.address.formatter;
|
||||
|
||||
import org.apache.james.mime4j.codec.EncoderUtil;
|
||||
import org.apache.james.mime4j.dom.address.Address;
|
||||
import org.apache.james.mime4j.dom.address.Group;
|
||||
import org.apache.james.mime4j.dom.address.Mailbox;
|
||||
|
||||
public class AddressFormatter {
|
||||
|
||||
/**
|
||||
* Formats the address as a human readable string, not including the route.
|
||||
* The resulting string is intended for display purposes only and cannot be
|
||||
* used for transport purposes.
|
||||
*
|
||||
* For example, if the unparsed address was
|
||||
*
|
||||
* <"Joe Cheng"@joecheng.com>
|
||||
*
|
||||
* this method would return
|
||||
*
|
||||
* <Joe Cheng@joecheng.com>
|
||||
*
|
||||
* which is not valid for transport; the local part would need to be
|
||||
* re-quoted.
|
||||
*
|
||||
* @param includeRoute
|
||||
* <code>true</code> if the route should be included if it
|
||||
* exists, <code>false</code> otherwise.
|
||||
* @return a string representation of this address intended to be displayed.
|
||||
*/
|
||||
public static void format(final StringBuilder sb, final Address address, boolean includeRoute) {
|
||||
if (address == null) {
|
||||
return;
|
||||
}
|
||||
if (address instanceof Mailbox) {
|
||||
format(sb, (Mailbox) address, includeRoute);
|
||||
} else if (address instanceof Group) {
|
||||
format(sb, (Group) address, includeRoute);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unsuppported Address class: " + address.getClass());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string representation of this address that can be used for
|
||||
* transport purposes. The route is never included in this representation
|
||||
* because routes are obsolete and RFC 5322 states that obsolete syntactic
|
||||
* forms MUST NOT be generated.
|
||||
*
|
||||
* @return a string representation of this address intended for transport
|
||||
* purposes.
|
||||
*/
|
||||
public static void encode(final StringBuilder sb, final Address address) {
|
||||
if (address == null) {
|
||||
return;
|
||||
}
|
||||
if (address instanceof Mailbox) {
|
||||
encode(sb, (Mailbox) address);
|
||||
} else if (address instanceof Group) {
|
||||
encode(sb, (Group) address);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unsuppported Address class: " + address.getClass());
|
||||
}
|
||||
}
|
||||
|
||||
public static void format(final StringBuilder sb, final Mailbox mailbox, boolean includeRoute) {
|
||||
if (sb == null) {
|
||||
throw new IllegalArgumentException("StringBuilder may not be null");
|
||||
}
|
||||
if (mailbox == null) {
|
||||
throw new IllegalArgumentException("Mailbox may not be null");
|
||||
}
|
||||
includeRoute &= mailbox.getRoute() != null;
|
||||
boolean includeAngleBrackets = mailbox.getName() != null || includeRoute;
|
||||
if (mailbox.getName() != null) {
|
||||
sb.append(mailbox.getName());
|
||||
sb.append(' ');
|
||||
}
|
||||
if (includeAngleBrackets) {
|
||||
sb.append('<');
|
||||
}
|
||||
if (includeRoute) {
|
||||
sb.append(mailbox.getRoute().toRouteString());
|
||||
sb.append(':');
|
||||
}
|
||||
sb.append(mailbox.getLocalPart());
|
||||
if (mailbox.getDomain() != null) {
|
||||
sb.append('@');
|
||||
sb.append(mailbox.getDomain());
|
||||
}
|
||||
if (includeAngleBrackets) {
|
||||
sb.append('>');
|
||||
}
|
||||
}
|
||||
|
||||
public static String format(final Mailbox mailbox, boolean includeRoute) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
format(sb, mailbox, includeRoute);
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static void encode(final StringBuilder sb, final Mailbox mailbox) {
|
||||
if (sb == null) {
|
||||
throw new IllegalArgumentException("StringBuilder may not be null");
|
||||
}
|
||||
if (mailbox == null) {
|
||||
throw new IllegalArgumentException("Mailbox may not be null");
|
||||
}
|
||||
if (mailbox.getName() != null) {
|
||||
sb.append(EncoderUtil.encodeAddressDisplayName(mailbox.getName()));
|
||||
sb.append(" <");
|
||||
}
|
||||
sb.append(EncoderUtil.encodeAddressLocalPart(mailbox.getLocalPart()));
|
||||
// domain = dot-atom / domain-literal
|
||||
// domain-literal = [CFWS] "[" *([FWS] dtext) [FWS] "]" [CFWS]
|
||||
// dtext = %d33-90 / %d94-126
|
||||
if (mailbox.getDomain() != null) {
|
||||
sb.append('@');
|
||||
sb.append(mailbox.getDomain());
|
||||
}
|
||||
if (mailbox.getName() != null) {
|
||||
sb.append('>');
|
||||
}
|
||||
}
|
||||
|
||||
public static String encode(final Mailbox mailbox) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
encode(sb, mailbox);
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static void format(final StringBuilder sb, final Group group, boolean includeRoute) {
|
||||
if (sb == null) {
|
||||
throw new IllegalArgumentException("StringBuilder may not be null");
|
||||
}
|
||||
if (group == null) {
|
||||
throw new IllegalArgumentException("Group may not be null");
|
||||
}
|
||||
sb.append(group.getName());
|
||||
sb.append(':');
|
||||
|
||||
boolean first = true;
|
||||
for (Mailbox mailbox : group.getMailboxes()) {
|
||||
if (first) {
|
||||
first = false;
|
||||
} else {
|
||||
sb.append(',');
|
||||
}
|
||||
sb.append(' ');
|
||||
format(sb, mailbox, includeRoute);
|
||||
}
|
||||
sb.append(";");
|
||||
}
|
||||
|
||||
public static String format(final Group group, boolean includeRoute) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
format(sb, group, includeRoute);
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static void encode(final StringBuilder sb, final Group group) {
|
||||
if (sb == null) {
|
||||
throw new IllegalArgumentException("StringBuilder may not be null");
|
||||
}
|
||||
if (group == null) {
|
||||
throw new IllegalArgumentException("Group may not be null");
|
||||
}
|
||||
sb.append(EncoderUtil.encodeAddressDisplayName(group.getName()));
|
||||
sb.append(':');
|
||||
boolean first = true;
|
||||
for (Mailbox mailbox : group.getMailboxes()) {
|
||||
if (first) {
|
||||
first = false;
|
||||
} else {
|
||||
sb.append(',');
|
||||
}
|
||||
|
||||
sb.append(' ');
|
||||
encode(sb, mailbox);
|
||||
}
|
||||
sb.append(';');
|
||||
}
|
||||
|
||||
public static String encode(final Group group) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
encode(sb, group);
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
}
|
@ -1,8 +1,9 @@
|
||||
/* Generated By:JJTree: Do not edit this line. ASTaddr_spec.java */
|
||||
|
||||
/* Generated By:JJTree: Do not edit this line. ASTaddr_spec.java Version 4.3 */
|
||||
/* JavaCCOptions:MULTI=true,NODE_USES_PARSER=false,VISITOR=true,TRACK_TOKENS=false,NODE_PREFIX=AST,NODE_EXTENDS=org.apache.james.mime4j.field.address.parser.BaseNode,NODE_FACTORY=,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */
|
||||
package org.apache.james.mime4j.field.address.parser;
|
||||
|
||||
public class ASTaddr_spec extends SimpleNode {
|
||||
public
|
||||
class ASTaddr_spec extends SimpleNode {
|
||||
public ASTaddr_spec(int id) {
|
||||
super(id);
|
||||
}
|
||||
@ -17,3 +18,4 @@ public class ASTaddr_spec extends SimpleNode {
|
||||
return visitor.visit(this, data);
|
||||
}
|
||||
}
|
||||
/* JavaCC - OriginalChecksum=750ab0a2f6a942d3f4a7a7e076d12a4d (do not edit this line) */
|
||||
|
@ -1,8 +1,9 @@
|
||||
/* Generated By:JJTree: Do not edit this line. ASTaddress.java */
|
||||
|
||||
/* Generated By:JJTree: Do not edit this line. ASTaddress.java Version 4.3 */
|
||||
/* JavaCCOptions:MULTI=true,NODE_USES_PARSER=false,VISITOR=true,TRACK_TOKENS=false,NODE_PREFIX=AST,NODE_EXTENDS=org.apache.james.mime4j.field.address.parser.BaseNode,NODE_FACTORY=,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */
|
||||
package org.apache.james.mime4j.field.address.parser;
|
||||
|
||||
public class ASTaddress extends SimpleNode {
|
||||
public
|
||||
class ASTaddress extends SimpleNode {
|
||||
public ASTaddress(int id) {
|
||||
super(id);
|
||||
}
|
||||
@ -17,3 +18,4 @@ public class ASTaddress extends SimpleNode {
|
||||
return visitor.visit(this, data);
|
||||
}
|
||||
}
|
||||
/* JavaCC - OriginalChecksum=73be0a52ecfe4cf5d5a926be94fbb411 (do not edit this line) */
|
||||
|
@ -1,8 +1,9 @@
|
||||
/* Generated By:JJTree: Do not edit this line. ASTaddress_list.java */
|
||||
|
||||
/* Generated By:JJTree: Do not edit this line. ASTaddress_list.java Version 4.3 */
|
||||
/* JavaCCOptions:MULTI=true,NODE_USES_PARSER=false,VISITOR=true,TRACK_TOKENS=false,NODE_PREFIX=AST,NODE_EXTENDS=org.apache.james.mime4j.field.address.parser.BaseNode,NODE_FACTORY=,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */
|
||||
package org.apache.james.mime4j.field.address.parser;
|
||||
|
||||
public class ASTaddress_list extends SimpleNode {
|
||||
public
|
||||
class ASTaddress_list extends SimpleNode {
|
||||
public ASTaddress_list(int id) {
|
||||
super(id);
|
||||
}
|
||||
@ -17,3 +18,4 @@ public class ASTaddress_list extends SimpleNode {
|
||||
return visitor.visit(this, data);
|
||||
}
|
||||
}
|
||||
/* JavaCC - OriginalChecksum=6615a805f4abebfcf7252d9aad462299 (do not edit this line) */
|
||||
|
@ -1,8 +1,9 @@
|
||||
/* Generated By:JJTree: Do not edit this line. ASTangle_addr.java */
|
||||
|
||||
/* Generated By:JJTree: Do not edit this line. ASTangle_addr.java Version 4.3 */
|
||||
/* JavaCCOptions:MULTI=true,NODE_USES_PARSER=false,VISITOR=true,TRACK_TOKENS=false,NODE_PREFIX=AST,NODE_EXTENDS=org.apache.james.mime4j.field.address.parser.BaseNode,NODE_FACTORY=,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */
|
||||
package org.apache.james.mime4j.field.address.parser;
|
||||
|
||||
public class ASTangle_addr extends SimpleNode {
|
||||
public
|
||||
class ASTangle_addr extends SimpleNode {
|
||||
public ASTangle_addr(int id) {
|
||||
super(id);
|
||||
}
|
||||
@ -17,3 +18,4 @@ public class ASTangle_addr extends SimpleNode {
|
||||
return visitor.visit(this, data);
|
||||
}
|
||||
}
|
||||
/* JavaCC - OriginalChecksum=2201bedb23ef9d1b75dd88b2a5571384 (do not edit this line) */
|
||||
|
@ -1,8 +1,9 @@
|
||||
/* Generated By:JJTree: Do not edit this line. ASTdomain.java */
|
||||
|
||||
/* Generated By:JJTree: Do not edit this line. ASTdomain.java Version 4.3 */
|
||||
/* JavaCCOptions:MULTI=true,NODE_USES_PARSER=false,VISITOR=true,TRACK_TOKENS=false,NODE_PREFIX=AST,NODE_EXTENDS=org.apache.james.mime4j.field.address.parser.BaseNode,NODE_FACTORY=,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */
|
||||
package org.apache.james.mime4j.field.address.parser;
|
||||
|
||||
public class ASTdomain extends SimpleNode {
|
||||
public
|
||||
class ASTdomain extends SimpleNode {
|
||||
public ASTdomain(int id) {
|
||||
super(id);
|
||||
}
|
||||
@ -17,3 +18,4 @@ public class ASTdomain extends SimpleNode {
|
||||
return visitor.visit(this, data);
|
||||
}
|
||||
}
|
||||
/* JavaCC - OriginalChecksum=0105eb2d00d34d34b0665fd5ced14d52 (do not edit this line) */
|
||||
|
@ -1,8 +1,9 @@
|
||||
/* Generated By:JJTree: Do not edit this line. ASTgroup_body.java */
|
||||
|
||||
/* Generated By:JJTree: Do not edit this line. ASTgroup_body.java Version 4.3 */
|
||||
/* JavaCCOptions:MULTI=true,NODE_USES_PARSER=false,VISITOR=true,TRACK_TOKENS=false,NODE_PREFIX=AST,NODE_EXTENDS=org.apache.james.mime4j.field.address.parser.BaseNode,NODE_FACTORY=,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */
|
||||
package org.apache.james.mime4j.field.address.parser;
|
||||
|
||||
public class ASTgroup_body extends SimpleNode {
|
||||
public
|
||||
class ASTgroup_body extends SimpleNode {
|
||||
public ASTgroup_body(int id) {
|
||||
super(id);
|
||||
}
|
||||
@ -17,3 +18,4 @@ public class ASTgroup_body extends SimpleNode {
|
||||
return visitor.visit(this, data);
|
||||
}
|
||||
}
|
||||
/* JavaCC - OriginalChecksum=29b09d0a20de5b5f3d7e08c7e325d23f (do not edit this line) */
|
||||
|
@ -1,8 +1,9 @@
|
||||
/* Generated By:JJTree: Do not edit this line. ASTlocal_part.java */
|
||||
|
||||
/* Generated By:JJTree: Do not edit this line. ASTlocal_part.java Version 4.3 */
|
||||
/* JavaCCOptions:MULTI=true,NODE_USES_PARSER=false,VISITOR=true,TRACK_TOKENS=false,NODE_PREFIX=AST,NODE_EXTENDS=org.apache.james.mime4j.field.address.parser.BaseNode,NODE_FACTORY=,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */
|
||||
package org.apache.james.mime4j.field.address.parser;
|
||||
|
||||
public class ASTlocal_part extends SimpleNode {
|
||||
public
|
||||
class ASTlocal_part extends SimpleNode {
|
||||
public ASTlocal_part(int id) {
|
||||
super(id);
|
||||
}
|
||||
@ -17,3 +18,4 @@ public class ASTlocal_part extends SimpleNode {
|
||||
return visitor.visit(this, data);
|
||||
}
|
||||
}
|
||||
/* JavaCC - OriginalChecksum=42e77dd54203428772aecd61a95fc01c (do not edit this line) */
|
||||
|
@ -1,8 +1,9 @@
|
||||
/* Generated By:JJTree: Do not edit this line. ASTmailbox.java */
|
||||
|
||||
/* Generated By:JJTree: Do not edit this line. ASTmailbox.java Version 4.3 */
|
||||
/* JavaCCOptions:MULTI=true,NODE_USES_PARSER=false,VISITOR=true,TRACK_TOKENS=false,NODE_PREFIX=AST,NODE_EXTENDS=org.apache.james.mime4j.field.address.parser.BaseNode,NODE_FACTORY=,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */
|
||||
package org.apache.james.mime4j.field.address.parser;
|
||||
|
||||
public class ASTmailbox extends SimpleNode {
|
||||
public
|
||||
class ASTmailbox extends SimpleNode {
|
||||
public ASTmailbox(int id) {
|
||||
super(id);
|
||||
}
|
||||
@ -17,3 +18,4 @@ public class ASTmailbox extends SimpleNode {
|
||||
return visitor.visit(this, data);
|
||||
}
|
||||
}
|
||||
/* JavaCC - OriginalChecksum=246fe7d146969407e2c7977748e2fc99 (do not edit this line) */
|
||||
|
@ -1,8 +1,9 @@
|
||||
/* Generated By:JJTree: Do not edit this line. ASTname_addr.java */
|
||||
|
||||
/* Generated By:JJTree: Do not edit this line. ASTname_addr.java Version 4.3 */
|
||||
/* JavaCCOptions:MULTI=true,NODE_USES_PARSER=false,VISITOR=true,TRACK_TOKENS=false,NODE_PREFIX=AST,NODE_EXTENDS=org.apache.james.mime4j.field.address.parser.BaseNode,NODE_FACTORY=,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */
|
||||
package org.apache.james.mime4j.field.address.parser;
|
||||
|
||||
public class ASTname_addr extends SimpleNode {
|
||||
public
|
||||
class ASTname_addr extends SimpleNode {
|
||||
public ASTname_addr(int id) {
|
||||
super(id);
|
||||
}
|
||||
@ -17,3 +18,4 @@ public class ASTname_addr extends SimpleNode {
|
||||
return visitor.visit(this, data);
|
||||
}
|
||||
}
|
||||
/* JavaCC - OriginalChecksum=37e69dc07dfc157b3fb2449483ff82b6 (do not edit this line) */
|
||||
|
@ -1,8 +1,9 @@
|
||||
/* Generated By:JJTree: Do not edit this line. ASTphrase.java */
|
||||
|
||||
/* Generated By:JJTree: Do not edit this line. ASTphrase.java Version 4.3 */
|
||||
/* JavaCCOptions:MULTI=true,NODE_USES_PARSER=false,VISITOR=true,TRACK_TOKENS=false,NODE_PREFIX=AST,NODE_EXTENDS=org.apache.james.mime4j.field.address.parser.BaseNode,NODE_FACTORY=,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */
|
||||
package org.apache.james.mime4j.field.address.parser;
|
||||
|
||||
public class ASTphrase extends SimpleNode {
|
||||
public
|
||||
class ASTphrase extends SimpleNode {
|
||||
public ASTphrase(int id) {
|
||||
super(id);
|
||||
}
|
||||
@ -17,3 +18,4 @@ public class ASTphrase extends SimpleNode {
|
||||
return visitor.visit(this, data);
|
||||
}
|
||||
}
|
||||
/* JavaCC - OriginalChecksum=f1c7166e3c5b192d1f6db62b8239b0be (do not edit this line) */
|
||||
|
@ -1,8 +1,9 @@
|
||||
/* Generated By:JJTree: Do not edit this line. ASTroute.java */
|
||||
|
||||
/* Generated By:JJTree: Do not edit this line. ASTroute.java Version 4.3 */
|
||||
/* JavaCCOptions:MULTI=true,NODE_USES_PARSER=false,VISITOR=true,TRACK_TOKENS=false,NODE_PREFIX=AST,NODE_EXTENDS=org.apache.james.mime4j.field.address.parser.BaseNode,NODE_FACTORY=,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */
|
||||
package org.apache.james.mime4j.field.address.parser;
|
||||
|
||||
public class ASTroute extends SimpleNode {
|
||||
public
|
||||
class ASTroute extends SimpleNode {
|
||||
public ASTroute(int id) {
|
||||
super(id);
|
||||
}
|
||||
@ -17,3 +18,4 @@ public class ASTroute extends SimpleNode {
|
||||
return visitor.visit(this, data);
|
||||
}
|
||||
}
|
||||
/* JavaCC - OriginalChecksum=bcec06c89402cfcb3700aefe8d5f14f9 (do not edit this line) */
|
||||
|
@ -0,0 +1,115 @@
|
||||
/****************************************************************
|
||||
* Licensed to the Apache Software Foundation (ASF) under one *
|
||||
* or more contributor license agreements. See the NOTICE file *
|
||||
* distributed with this work for additional information *
|
||||
* regarding copyright ownership. The ASF licenses this file *
|
||||
* to you 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. *
|
||||
****************************************************************/
|
||||
|
||||
package org.apache.james.mime4j.field.address.parser;
|
||||
|
||||
import java.io.StringReader;
|
||||
|
||||
import org.apache.james.mime4j.codec.DecodeMonitor;
|
||||
import org.apache.james.mime4j.dom.address.Address;
|
||||
import org.apache.james.mime4j.dom.address.AddressList;
|
||||
import org.apache.james.mime4j.dom.address.Group;
|
||||
import org.apache.james.mime4j.dom.address.Mailbox;
|
||||
|
||||
public class AddressBuilder {
|
||||
|
||||
/**
|
||||
* Parses the specified raw string into an address.
|
||||
*
|
||||
* @param rawAddressString
|
||||
* string to parse.
|
||||
* @param monitor the DecodeMonitor to be used while parsing/decoding
|
||||
* @return an <code>Address</code> object for the specified string.
|
||||
* @throws ParseException if the raw string does not represent a single address.
|
||||
*/
|
||||
public static Address parseAddress(String rawAddressString, DecodeMonitor monitor) throws ParseException {
|
||||
AddressListParser parser = new AddressListParser(new StringReader(
|
||||
rawAddressString));
|
||||
return Builder.getInstance().buildAddress(parser.parseAddress(), monitor);
|
||||
}
|
||||
|
||||
public static Address parseAddress(String rawAddressString) throws ParseException {
|
||||
return parseAddress(rawAddressString, DecodeMonitor.STRICT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the address list string, such as the value of a From, To, Cc, Bcc,
|
||||
* Sender, or Reply-To header.
|
||||
*
|
||||
* The string MUST be unfolded already.
|
||||
* @param monitor the DecodeMonitor to be used while parsing/decoding
|
||||
*/
|
||||
public static AddressList parseAddressList(String rawAddressList, DecodeMonitor monitor)
|
||||
throws ParseException {
|
||||
AddressListParser parser = new AddressListParser(new StringReader(
|
||||
rawAddressList));
|
||||
try {
|
||||
return Builder.getInstance().buildAddressList(parser.parseAddressList(), monitor);
|
||||
} catch (RuntimeException e) {
|
||||
throw new ParseException(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public static AddressList parseAddressList(String rawAddressList) throws ParseException {
|
||||
return parseAddressList(rawAddressList, DecodeMonitor.STRICT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the specified raw string into a mailbox address.
|
||||
*
|
||||
* @param rawMailboxString
|
||||
* string to parse.
|
||||
* @param monitor the DecodeMonitor to be used while parsing/decoding.
|
||||
* @return a <code>Mailbox</code> object for the specified string.
|
||||
* @throws ParseException
|
||||
* if the raw string does not represent a single mailbox
|
||||
* address.
|
||||
*/
|
||||
public static Mailbox parseMailbox(String rawMailboxString, DecodeMonitor monitor) throws ParseException {
|
||||
AddressListParser parser = new AddressListParser(new StringReader(
|
||||
rawMailboxString));
|
||||
return Builder.getInstance().buildMailbox(parser.parseMailbox(), monitor);
|
||||
}
|
||||
|
||||
public static Mailbox parseMailbox(String rawMailboxString) throws ParseException {
|
||||
return parseMailbox(rawMailboxString, DecodeMonitor.STRICT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the specified raw string into a group address.
|
||||
*
|
||||
* @param rawGroupString
|
||||
* string to parse.
|
||||
* @return a <code>Group</code> object for the specified string.
|
||||
* @throws ParseException
|
||||
* if the raw string does not represent a single group address.
|
||||
*/
|
||||
public static Group parseGroup(String rawGroupString, DecodeMonitor monitor) throws ParseException {
|
||||
Address address = parseAddress(rawGroupString, monitor);
|
||||
if (!(address instanceof Group))
|
||||
throw new ParseException("Not a group address");
|
||||
|
||||
return (Group) address;
|
||||
}
|
||||
|
||||
public static Group parseGroup(String rawGroupString) throws ParseException {
|
||||
return parseGroup(rawGroupString, DecodeMonitor.STRICT);
|
||||
}
|
||||
|
||||
}
|
@ -1,55 +1,71 @@
|
||||
/* Generated By:JJTree&JavaCC: Do not edit this line. AddressListParser.java */
|
||||
/*
|
||||
* Copyright 2004 the mime4j project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
/****************************************************************
|
||||
* Licensed to the Apache Software Foundation (ASF) under one *
|
||||
* or more contributor license agreements. See the NOTICE file *
|
||||
* distributed with this work for additional information *
|
||||
* regarding copyright ownership. The ASF licenses this file *
|
||||
* to you 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. *
|
||||
****************************************************************/
|
||||
package org.apache.james.mime4j.field.address.parser;
|
||||
|
||||
public class AddressListParser/*@bgen(jjtree)*/implements AddressListParserTreeConstants, AddressListParserConstants {/*@bgen(jjtree)*/
|
||||
protected JJTAddressListParserState jjtree = new JJTAddressListParserState();public static void main(String args[]) throws ParseException {
|
||||
while (true) {
|
||||
try {
|
||||
AddressListParser parser = new AddressListParser(System.in);
|
||||
parser.parseLine();
|
||||
((SimpleNode)parser.jjtree.rootNode()).dump("> ");
|
||||
} catch (Exception x) {
|
||||
x.printStackTrace();
|
||||
return;
|
||||
}
|
||||
}
|
||||
while (true) {
|
||||
try {
|
||||
AddressListParser parser = new AddressListParser(System.in);
|
||||
parser.parseLine();
|
||||
((SimpleNode) parser.jjtree.rootNode()).dump("> ");
|
||||
} catch (Exception x) {
|
||||
x.printStackTrace();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void log(String msg) {
|
||||
System.out.print(msg);
|
||||
}
|
||||
|
||||
public ASTaddress_list parse() throws ParseException {
|
||||
public ASTaddress_list parseAddressList() throws ParseException {
|
||||
try {
|
||||
parseAll();
|
||||
return (ASTaddress_list)jjtree.rootNode();
|
||||
parseAddressList0();
|
||||
return (ASTaddress_list) jjtree.rootNode();
|
||||
} catch (TokenMgrError tme) {
|
||||
throw new ParseException(tme.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public ASTaddress parseAddress() throws ParseException {
|
||||
try {
|
||||
parseAddress0();
|
||||
return (ASTaddress) jjtree.rootNode();
|
||||
} catch (TokenMgrError tme) {
|
||||
throw new ParseException(tme.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public ASTmailbox parseMailbox() throws ParseException {
|
||||
try {
|
||||
parseMailbox0();
|
||||
return (ASTmailbox) jjtree.rootNode();
|
||||
} catch (TokenMgrError tme) {
|
||||
throw new ParseException(tme.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
void jjtreeOpenNodeScope(Node n) {
|
||||
((SimpleNode)n).firstToken = getToken(1);
|
||||
((SimpleNode) n).firstToken = getToken(1);
|
||||
}
|
||||
|
||||
void jjtreeCloseNodeScope(Node n) {
|
||||
((SimpleNode)n).lastToken = getToken(0);
|
||||
((SimpleNode) n).lastToken = getToken(0);
|
||||
}
|
||||
|
||||
final public void parseLine() throws ParseException {
|
||||
@ -65,11 +81,21 @@ public class AddressListParser/*@bgen(jjtree)*/implements AddressListParserTreeC
|
||||
jj_consume_token(2);
|
||||
}
|
||||
|
||||
final public void parseAll() throws ParseException {
|
||||
final public void parseAddressList0() throws ParseException {
|
||||
address_list();
|
||||
jj_consume_token(0);
|
||||
}
|
||||
|
||||
final public void parseAddress0() throws ParseException {
|
||||
address();
|
||||
jj_consume_token(0);
|
||||
}
|
||||
|
||||
final public void parseMailbox0() throws ParseException {
|
||||
mailbox();
|
||||
jj_consume_token(0);
|
||||
}
|
||||
|
||||
final public void address_list() throws ParseException {
|
||||
/*@bgen(jjtree) address_list */
|
||||
ASTaddress_list jjtn000 = new ASTaddress_list(JJTADDRESS_LIST);
|
||||
@ -537,7 +563,7 @@ public class AddressListParser/*@bgen(jjtree)*/implements AddressListParserTreeC
|
||||
jj_la1[17] = jj_gen;
|
||||
;
|
||||
}
|
||||
if (t.image.charAt(t.image.length() - 1) != '.' || t.kind == AddressListParserConstants.QUOTEDSTRING)
|
||||
if ( t.kind == AddressListParserConstants.QUOTEDSTRING || t.image.charAt(t.image.length() - 1) != '.')
|
||||
{if (true) throw new ParseException("Words in local part must be separated by '.'");}
|
||||
switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
|
||||
case DOTATOM:
|
||||
@ -610,21 +636,21 @@ public class AddressListParser/*@bgen(jjtree)*/implements AddressListParserTreeC
|
||||
}
|
||||
}
|
||||
|
||||
final private boolean jj_2_1(int xla) {
|
||||
private boolean jj_2_1(int xla) {
|
||||
jj_la = xla; jj_lastpos = jj_scanpos = token;
|
||||
try { return !jj_3_1(); }
|
||||
catch(LookaheadSuccess ls) { return true; }
|
||||
finally { jj_save(0, xla); }
|
||||
}
|
||||
|
||||
final private boolean jj_2_2(int xla) {
|
||||
private boolean jj_2_2(int xla) {
|
||||
jj_la = xla; jj_lastpos = jj_scanpos = token;
|
||||
try { return !jj_3_2(); }
|
||||
catch(LookaheadSuccess ls) { return true; }
|
||||
finally { jj_save(1, xla); }
|
||||
}
|
||||
|
||||
final private boolean jj_3R_11() {
|
||||
private boolean jj_3R_11() {
|
||||
Token xsp;
|
||||
xsp = jj_scanpos;
|
||||
if (jj_scan_token(9)) jj_scanpos = xsp;
|
||||
@ -636,7 +662,7 @@ public class AddressListParser/*@bgen(jjtree)*/implements AddressListParserTreeC
|
||||
return false;
|
||||
}
|
||||
|
||||
final private boolean jj_3R_13() {
|
||||
private boolean jj_3R_13() {
|
||||
Token xsp;
|
||||
xsp = jj_scanpos;
|
||||
if (jj_scan_token(9)) jj_scanpos = xsp;
|
||||
@ -644,19 +670,19 @@ public class AddressListParser/*@bgen(jjtree)*/implements AddressListParserTreeC
|
||||
return false;
|
||||
}
|
||||
|
||||
final private boolean jj_3R_8() {
|
||||
private boolean jj_3R_8() {
|
||||
if (jj_3R_9()) return true;
|
||||
if (jj_scan_token(8)) return true;
|
||||
if (jj_3R_10()) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
final private boolean jj_3_1() {
|
||||
private boolean jj_3_1() {
|
||||
if (jj_3R_8()) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
final private boolean jj_3R_12() {
|
||||
private boolean jj_3R_12() {
|
||||
if (jj_scan_token(DOTATOM)) return true;
|
||||
Token xsp;
|
||||
while (true) {
|
||||
@ -666,7 +692,7 @@ public class AddressListParser/*@bgen(jjtree)*/implements AddressListParserTreeC
|
||||
return false;
|
||||
}
|
||||
|
||||
final private boolean jj_3R_10() {
|
||||
private boolean jj_3R_10() {
|
||||
Token xsp;
|
||||
xsp = jj_scanpos;
|
||||
if (jj_3R_12()) {
|
||||
@ -676,12 +702,12 @@ public class AddressListParser/*@bgen(jjtree)*/implements AddressListParserTreeC
|
||||
return false;
|
||||
}
|
||||
|
||||
final private boolean jj_3_2() {
|
||||
private boolean jj_3_2() {
|
||||
if (jj_3R_8()) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
final private boolean jj_3R_9() {
|
||||
private boolean jj_3R_9() {
|
||||
Token xsp;
|
||||
xsp = jj_scanpos;
|
||||
if (jj_scan_token(14)) {
|
||||
@ -695,35 +721,39 @@ public class AddressListParser/*@bgen(jjtree)*/implements AddressListParserTreeC
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Generated Token Manager. */
|
||||
public AddressListParserTokenManager token_source;
|
||||
SimpleCharStream jj_input_stream;
|
||||
public Token token, jj_nt;
|
||||
/** Current token. */
|
||||
public Token token;
|
||||
/** Next token. */
|
||||
public Token jj_nt;
|
||||
private int jj_ntk;
|
||||
private Token jj_scanpos, jj_lastpos;
|
||||
private int jj_la;
|
||||
public boolean lookingAhead = false;
|
||||
private boolean jj_semLA;
|
||||
private int jj_gen;
|
||||
final private int[] jj_la1 = new int[22];
|
||||
static private int[] jj_la1_0;
|
||||
static private int[] jj_la1_1;
|
||||
static {
|
||||
jj_la1_0();
|
||||
jj_la1_1();
|
||||
jj_la1_init_0();
|
||||
jj_la1_init_1();
|
||||
}
|
||||
private static void jj_la1_0() {
|
||||
private static void jj_la1_init_0() {
|
||||
jj_la1_0 = new int[] {0x2,0x80004040,0x8,0x80004040,0x50,0x80004040,0x80004040,0x80004040,0x8,0x80004040,0x100,0x108,0x8,0x80004000,0x80004000,0x80004000,0x80004200,0x200,0x80004000,0x4200,0x200,0x44000,};
|
||||
}
|
||||
private static void jj_la1_1() {
|
||||
private static void jj_la1_init_1() {
|
||||
jj_la1_1 = new int[] {0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,};
|
||||
}
|
||||
final private JJCalls[] jj_2_rtns = new JJCalls[2];
|
||||
private boolean jj_rescan = false;
|
||||
private int jj_gc = 0;
|
||||
|
||||
/** Constructor with InputStream. */
|
||||
public AddressListParser(java.io.InputStream stream) {
|
||||
this(stream, null);
|
||||
}
|
||||
/** Constructor with InputStream and supplied encoding */
|
||||
public AddressListParser(java.io.InputStream stream, String encoding) {
|
||||
try { jj_input_stream = new SimpleCharStream(stream, encoding, 1, 1); } catch(java.io.UnsupportedEncodingException e) { throw new RuntimeException(e); }
|
||||
token_source = new AddressListParserTokenManager(jj_input_stream);
|
||||
@ -734,9 +764,11 @@ public class AddressListParser/*@bgen(jjtree)*/implements AddressListParserTreeC
|
||||
for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls();
|
||||
}
|
||||
|
||||
/** Reinitialise. */
|
||||
public void ReInit(java.io.InputStream stream) {
|
||||
ReInit(stream, null);
|
||||
}
|
||||
/** Reinitialise. */
|
||||
public void ReInit(java.io.InputStream stream, String encoding) {
|
||||
try { jj_input_stream.ReInit(stream, encoding, 1, 1); } catch(java.io.UnsupportedEncodingException e) { throw new RuntimeException(e); }
|
||||
token_source.ReInit(jj_input_stream);
|
||||
@ -748,6 +780,7 @@ public class AddressListParser/*@bgen(jjtree)*/implements AddressListParserTreeC
|
||||
for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls();
|
||||
}
|
||||
|
||||
/** Constructor. */
|
||||
public AddressListParser(java.io.Reader stream) {
|
||||
jj_input_stream = new SimpleCharStream(stream, 1, 1);
|
||||
token_source = new AddressListParserTokenManager(jj_input_stream);
|
||||
@ -758,6 +791,7 @@ public class AddressListParser/*@bgen(jjtree)*/implements AddressListParserTreeC
|
||||
for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls();
|
||||
}
|
||||
|
||||
/** Reinitialise. */
|
||||
public void ReInit(java.io.Reader stream) {
|
||||
jj_input_stream.ReInit(stream, 1, 1);
|
||||
token_source.ReInit(jj_input_stream);
|
||||
@ -769,6 +803,7 @@ public class AddressListParser/*@bgen(jjtree)*/implements AddressListParserTreeC
|
||||
for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls();
|
||||
}
|
||||
|
||||
/** Constructor with generated Token Manager. */
|
||||
public AddressListParser(AddressListParserTokenManager tm) {
|
||||
token_source = tm;
|
||||
token = new Token();
|
||||
@ -778,6 +813,7 @@ public class AddressListParser/*@bgen(jjtree)*/implements AddressListParserTreeC
|
||||
for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls();
|
||||
}
|
||||
|
||||
/** Reinitialise. */
|
||||
public void ReInit(AddressListParserTokenManager tm) {
|
||||
token_source = tm;
|
||||
token = new Token();
|
||||
@ -788,7 +824,7 @@ public class AddressListParser/*@bgen(jjtree)*/implements AddressListParserTreeC
|
||||
for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls();
|
||||
}
|
||||
|
||||
final private Token jj_consume_token(int kind) throws ParseException {
|
||||
private Token jj_consume_token(int kind) throws ParseException {
|
||||
Token oldToken;
|
||||
if ((oldToken = token).next != null) token = token.next;
|
||||
else token = token.next = token_source.getNextToken();
|
||||
@ -814,7 +850,7 @@ public class AddressListParser/*@bgen(jjtree)*/implements AddressListParserTreeC
|
||||
|
||||
static private final class LookaheadSuccess extends java.lang.Error { }
|
||||
final private LookaheadSuccess jj_ls = new LookaheadSuccess();
|
||||
final private boolean jj_scan_token(int kind) {
|
||||
private boolean jj_scan_token(int kind) {
|
||||
if (jj_scanpos == jj_lastpos) {
|
||||
jj_la--;
|
||||
if (jj_scanpos.next == null) {
|
||||
@ -835,6 +871,8 @@ public class AddressListParser/*@bgen(jjtree)*/implements AddressListParserTreeC
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/** Get the next Token. */
|
||||
final public Token getNextToken() {
|
||||
if (token.next != null) token = token.next;
|
||||
else token = token.next = token_source.getNextToken();
|
||||
@ -843,8 +881,9 @@ public class AddressListParser/*@bgen(jjtree)*/implements AddressListParserTreeC
|
||||
return token;
|
||||
}
|
||||
|
||||
/** Get the specific Token. */
|
||||
final public Token getToken(int index) {
|
||||
Token t = lookingAhead ? jj_scanpos : token;
|
||||
Token t = token;
|
||||
for (int i = 0; i < index; i++) {
|
||||
if (t.next != null) t = t.next;
|
||||
else t = t.next = token_source.getNextToken();
|
||||
@ -852,14 +891,14 @@ public class AddressListParser/*@bgen(jjtree)*/implements AddressListParserTreeC
|
||||
return t;
|
||||
}
|
||||
|
||||
final private int jj_ntk() {
|
||||
private int jj_ntk() {
|
||||
if ((jj_nt=token.next) == null)
|
||||
return (jj_ntk = (token.next=token_source.getNextToken()).kind);
|
||||
else
|
||||
return (jj_ntk = jj_nt.kind);
|
||||
}
|
||||
|
||||
private java.util.Vector jj_expentries = new java.util.Vector();
|
||||
private java.util.List<int[]> jj_expentries = new java.util.ArrayList<int[]>();
|
||||
private int[] jj_expentry;
|
||||
private int jj_kind = -1;
|
||||
private int[] jj_lasttokens = new int[100];
|
||||
@ -874,31 +913,26 @@ public class AddressListParser/*@bgen(jjtree)*/implements AddressListParserTreeC
|
||||
for (int i = 0; i < jj_endpos; i++) {
|
||||
jj_expentry[i] = jj_lasttokens[i];
|
||||
}
|
||||
boolean exists = false;
|
||||
for (java.util.Enumeration e = jj_expentries.elements(); e.hasMoreElements();) {
|
||||
int[] oldentry = (int[])(e.nextElement());
|
||||
jj_entries_loop: for (java.util.Iterator<?> it = jj_expentries.iterator(); it.hasNext();) {
|
||||
int[] oldentry = (int[])(it.next());
|
||||
if (oldentry.length == jj_expentry.length) {
|
||||
exists = true;
|
||||
for (int i = 0; i < jj_expentry.length; i++) {
|
||||
if (oldentry[i] != jj_expentry[i]) {
|
||||
exists = false;
|
||||
break;
|
||||
continue jj_entries_loop;
|
||||
}
|
||||
}
|
||||
if (exists) break;
|
||||
jj_expentries.add(jj_expentry);
|
||||
break jj_entries_loop;
|
||||
}
|
||||
}
|
||||
if (!exists) jj_expentries.addElement(jj_expentry);
|
||||
if (pos != 0) jj_lasttokens[(jj_endpos = pos) - 1] = kind;
|
||||
}
|
||||
}
|
||||
|
||||
/** Generate ParseException. */
|
||||
public ParseException generateParseException() {
|
||||
jj_expentries.removeAllElements();
|
||||
jj_expentries.clear();
|
||||
boolean[] la1tokens = new boolean[34];
|
||||
for (int i = 0; i < 34; i++) {
|
||||
la1tokens[i] = false;
|
||||
}
|
||||
if (jj_kind >= 0) {
|
||||
la1tokens[jj_kind] = true;
|
||||
jj_kind = -1;
|
||||
@ -919,7 +953,7 @@ public class AddressListParser/*@bgen(jjtree)*/implements AddressListParserTreeC
|
||||
if (la1tokens[i]) {
|
||||
jj_expentry = new int[1];
|
||||
jj_expentry[0] = i;
|
||||
jj_expentries.addElement(jj_expentry);
|
||||
jj_expentries.add(jj_expentry);
|
||||
}
|
||||
}
|
||||
jj_endpos = 0;
|
||||
@ -927,18 +961,20 @@ public class AddressListParser/*@bgen(jjtree)*/implements AddressListParserTreeC
|
||||
jj_add_error_token(0, 0);
|
||||
int[][] exptokseq = new int[jj_expentries.size()][];
|
||||
for (int i = 0; i < jj_expentries.size(); i++) {
|
||||
exptokseq[i] = (int[])jj_expentries.elementAt(i);
|
||||
exptokseq[i] = jj_expentries.get(i);
|
||||
}
|
||||
return new ParseException(token, exptokseq, tokenImage);
|
||||
}
|
||||
|
||||
/** Enable tracing. */
|
||||
final public void enable_tracing() {
|
||||
}
|
||||
|
||||
/** Disable tracing. */
|
||||
final public void disable_tracing() {
|
||||
}
|
||||
|
||||
final private void jj_rescan_token() {
|
||||
private void jj_rescan_token() {
|
||||
jj_rescan = true;
|
||||
for (int i = 0; i < 2; i++) {
|
||||
try {
|
||||
@ -958,7 +994,7 @@ public class AddressListParser/*@bgen(jjtree)*/implements AddressListParserTreeC
|
||||
jj_rescan = false;
|
||||
}
|
||||
|
||||
final private void jj_save(int index, int xla) {
|
||||
private void jj_save(int index, int xla) {
|
||||
JJCalls p = jj_2_rtns[index];
|
||||
while (p.gen > jj_gen) {
|
||||
if (p.next == null) { p = p.next = new JJCalls(); break; }
|
||||
|
@ -1,595 +0,0 @@
|
||||
/*@bgen(jjtree) Generated By:JJTree: Do not edit this line. /Users/jason/Projects/apache-mime4j-0.3/target/generated-sources/jjtree/org/apache/james/mime4j/field/address/parser/AddressListParser.jj */
|
||||
/*@egen*//****************************************************************
|
||||
* Licensed to the Apache Software Foundation (ASF) under one *
|
||||
* or more contributor license agreements. See the NOTICE file *
|
||||
* distributed with this work for additional information *
|
||||
* regarding copyright ownership. The ASF licenses this file *
|
||||
* to you 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. *
|
||||
****************************************************************/
|
||||
|
||||
|
||||
/**
|
||||
* RFC2822 address list parser.
|
||||
*
|
||||
* Created 9/17/2004
|
||||
* by Joe Cheng <code@joecheng.com>
|
||||
*/
|
||||
|
||||
options {
|
||||
STATIC=false;
|
||||
LOOKAHEAD=1;
|
||||
//DEBUG_PARSER=true;
|
||||
//DEBUG_TOKEN_MANAGER=true;
|
||||
}
|
||||
|
||||
PARSER_BEGIN(AddressListParser)
|
||||
/*
|
||||
* Copyright 2004 the mime4j project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package org.apache.james.mime4j.field.address.parser;
|
||||
|
||||
public class AddressListParser/*@bgen(jjtree)*/implements AddressListParserTreeConstants/*@egen*/ {/*@bgen(jjtree)*/
|
||||
protected JJTAddressListParserState jjtree = new JJTAddressListParserState();
|
||||
|
||||
/*@egen*/
|
||||
public static void main(String args[]) throws ParseException {
|
||||
while (true) {
|
||||
try {
|
||||
AddressListParser parser = new AddressListParser(System.in);
|
||||
parser.parseLine();
|
||||
((SimpleNode)parser.jjtree.rootNode()).dump("> ");
|
||||
} catch (Exception x) {
|
||||
x.printStackTrace();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void log(String msg) {
|
||||
System.out.print(msg);
|
||||
}
|
||||
|
||||
public ASTaddress_list parse() throws ParseException {
|
||||
try {
|
||||
parseAll();
|
||||
return (ASTaddress_list)jjtree.rootNode();
|
||||
} catch (TokenMgrError tme) {
|
||||
throw new ParseException(tme.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void jjtreeOpenNodeScope(Node n) {
|
||||
((SimpleNode)n).firstToken = getToken(1);
|
||||
}
|
||||
|
||||
void jjtreeCloseNodeScope(Node n) {
|
||||
((SimpleNode)n).lastToken = getToken(0);
|
||||
}
|
||||
}
|
||||
|
||||
PARSER_END(AddressListParser)
|
||||
|
||||
void parseLine() :
|
||||
{}
|
||||
{
|
||||
address_list() ["\r"] "\n"
|
||||
}
|
||||
|
||||
void parseAll() :
|
||||
{}
|
||||
{
|
||||
address_list() <EOF>
|
||||
}
|
||||
|
||||
void address_list() :
|
||||
{/*@bgen(jjtree) address_list */
|
||||
ASTaddress_list jjtn000 = new ASTaddress_list(JJTADDRESS_LIST);
|
||||
boolean jjtc000 = true;
|
||||
jjtree.openNodeScope(jjtn000);
|
||||
jjtreeOpenNodeScope(jjtn000);
|
||||
/*@egen*/}
|
||||
{/*@bgen(jjtree) address_list */
|
||||
try {
|
||||
/*@egen*/
|
||||
[ address() ]
|
||||
(
|
||||
","
|
||||
[ address() ]
|
||||
)*/*@bgen(jjtree)*/
|
||||
} catch (Throwable jjte000) {
|
||||
if (jjtc000) {
|
||||
jjtree.clearNodeScope(jjtn000);
|
||||
jjtc000 = false;
|
||||
} else {
|
||||
jjtree.popNode();
|
||||
}
|
||||
if (jjte000 instanceof RuntimeException) {
|
||||
throw (RuntimeException)jjte000;
|
||||
}
|
||||
if (jjte000 instanceof ParseException) {
|
||||
throw (ParseException)jjte000;
|
||||
}
|
||||
throw (Error)jjte000;
|
||||
} finally {
|
||||
if (jjtc000) {
|
||||
jjtree.closeNodeScope(jjtn000, true);
|
||||
jjtreeCloseNodeScope(jjtn000);
|
||||
}
|
||||
}
|
||||
/*@egen*/
|
||||
}
|
||||
|
||||
void address() :
|
||||
{/*@bgen(jjtree) address */
|
||||
ASTaddress jjtn000 = new ASTaddress(JJTADDRESS);
|
||||
boolean jjtc000 = true;
|
||||
jjtree.openNodeScope(jjtn000);
|
||||
jjtreeOpenNodeScope(jjtn000);
|
||||
/*@egen*/}
|
||||
{/*@bgen(jjtree) address */
|
||||
try {
|
||||
/*@egen*/
|
||||
LOOKAHEAD(2147483647)
|
||||
addr_spec()
|
||||
| angle_addr()
|
||||
| ( phrase() (group_body() | angle_addr()) )/*@bgen(jjtree)*/
|
||||
} catch (Throwable jjte000) {
|
||||
if (jjtc000) {
|
||||
jjtree.clearNodeScope(jjtn000);
|
||||
jjtc000 = false;
|
||||
} else {
|
||||
jjtree.popNode();
|
||||
}
|
||||
if (jjte000 instanceof RuntimeException) {
|
||||
throw (RuntimeException)jjte000;
|
||||
}
|
||||
if (jjte000 instanceof ParseException) {
|
||||
throw (ParseException)jjte000;
|
||||
}
|
||||
throw (Error)jjte000;
|
||||
} finally {
|
||||
if (jjtc000) {
|
||||
jjtree.closeNodeScope(jjtn000, true);
|
||||
jjtreeCloseNodeScope(jjtn000);
|
||||
}
|
||||
}
|
||||
/*@egen*/
|
||||
}
|
||||
|
||||
void mailbox() :
|
||||
{/*@bgen(jjtree) mailbox */
|
||||
ASTmailbox jjtn000 = new ASTmailbox(JJTMAILBOX);
|
||||
boolean jjtc000 = true;
|
||||
jjtree.openNodeScope(jjtn000);
|
||||
jjtreeOpenNodeScope(jjtn000);
|
||||
/*@egen*/}
|
||||
{/*@bgen(jjtree) mailbox */
|
||||
try {
|
||||
/*@egen*/
|
||||
LOOKAHEAD(2147483647)
|
||||
addr_spec()
|
||||
| angle_addr()
|
||||
| name_addr()/*@bgen(jjtree)*/
|
||||
} catch (Throwable jjte000) {
|
||||
if (jjtc000) {
|
||||
jjtree.clearNodeScope(jjtn000);
|
||||
jjtc000 = false;
|
||||
} else {
|
||||
jjtree.popNode();
|
||||
}
|
||||
if (jjte000 instanceof RuntimeException) {
|
||||
throw (RuntimeException)jjte000;
|
||||
}
|
||||
if (jjte000 instanceof ParseException) {
|
||||
throw (ParseException)jjte000;
|
||||
}
|
||||
throw (Error)jjte000;
|
||||
} finally {
|
||||
if (jjtc000) {
|
||||
jjtree.closeNodeScope(jjtn000, true);
|
||||
jjtreeCloseNodeScope(jjtn000);
|
||||
}
|
||||
}
|
||||
/*@egen*/
|
||||
}
|
||||
|
||||
void name_addr() :
|
||||
{/*@bgen(jjtree) name_addr */
|
||||
ASTname_addr jjtn000 = new ASTname_addr(JJTNAME_ADDR);
|
||||
boolean jjtc000 = true;
|
||||
jjtree.openNodeScope(jjtn000);
|
||||
jjtreeOpenNodeScope(jjtn000);
|
||||
/*@egen*/}
|
||||
{/*@bgen(jjtree) name_addr */
|
||||
try {
|
||||
/*@egen*/
|
||||
phrase() angle_addr()/*@bgen(jjtree)*/
|
||||
} catch (Throwable jjte000) {
|
||||
if (jjtc000) {
|
||||
jjtree.clearNodeScope(jjtn000);
|
||||
jjtc000 = false;
|
||||
} else {
|
||||
jjtree.popNode();
|
||||
}
|
||||
if (jjte000 instanceof RuntimeException) {
|
||||
throw (RuntimeException)jjte000;
|
||||
}
|
||||
if (jjte000 instanceof ParseException) {
|
||||
throw (ParseException)jjte000;
|
||||
}
|
||||
throw (Error)jjte000;
|
||||
} finally {
|
||||
if (jjtc000) {
|
||||
jjtree.closeNodeScope(jjtn000, true);
|
||||
jjtreeCloseNodeScope(jjtn000);
|
||||
}
|
||||
}
|
||||
/*@egen*/
|
||||
}
|
||||
|
||||
void group_body() :
|
||||
{/*@bgen(jjtree) group_body */
|
||||
ASTgroup_body jjtn000 = new ASTgroup_body(JJTGROUP_BODY);
|
||||
boolean jjtc000 = true;
|
||||
jjtree.openNodeScope(jjtn000);
|
||||
jjtreeOpenNodeScope(jjtn000);
|
||||
/*@egen*/}
|
||||
{/*@bgen(jjtree) group_body */
|
||||
try {
|
||||
/*@egen*/
|
||||
":"
|
||||
[ mailbox() ]
|
||||
(
|
||||
","
|
||||
[ mailbox() ]
|
||||
)*
|
||||
";"/*@bgen(jjtree)*/
|
||||
} catch (Throwable jjte000) {
|
||||
if (jjtc000) {
|
||||
jjtree.clearNodeScope(jjtn000);
|
||||
jjtc000 = false;
|
||||
} else {
|
||||
jjtree.popNode();
|
||||
}
|
||||
if (jjte000 instanceof RuntimeException) {
|
||||
throw (RuntimeException)jjte000;
|
||||
}
|
||||
if (jjte000 instanceof ParseException) {
|
||||
throw (ParseException)jjte000;
|
||||
}
|
||||
throw (Error)jjte000;
|
||||
} finally {
|
||||
if (jjtc000) {
|
||||
jjtree.closeNodeScope(jjtn000, true);
|
||||
jjtreeCloseNodeScope(jjtn000);
|
||||
}
|
||||
}
|
||||
/*@egen*/
|
||||
}
|
||||
|
||||
void angle_addr() :
|
||||
{/*@bgen(jjtree) angle_addr */
|
||||
ASTangle_addr jjtn000 = new ASTangle_addr(JJTANGLE_ADDR);
|
||||
boolean jjtc000 = true;
|
||||
jjtree.openNodeScope(jjtn000);
|
||||
jjtreeOpenNodeScope(jjtn000);
|
||||
/*@egen*/}
|
||||
{/*@bgen(jjtree) angle_addr */
|
||||
try {
|
||||
/*@egen*/
|
||||
"<" [ route() ] addr_spec() ">"/*@bgen(jjtree)*/
|
||||
} catch (Throwable jjte000) {
|
||||
if (jjtc000) {
|
||||
jjtree.clearNodeScope(jjtn000);
|
||||
jjtc000 = false;
|
||||
} else {
|
||||
jjtree.popNode();
|
||||
}
|
||||
if (jjte000 instanceof RuntimeException) {
|
||||
throw (RuntimeException)jjte000;
|
||||
}
|
||||
if (jjte000 instanceof ParseException) {
|
||||
throw (ParseException)jjte000;
|
||||
}
|
||||
throw (Error)jjte000;
|
||||
} finally {
|
||||
if (jjtc000) {
|
||||
jjtree.closeNodeScope(jjtn000, true);
|
||||
jjtreeCloseNodeScope(jjtn000);
|
||||
}
|
||||
}
|
||||
/*@egen*/
|
||||
}
|
||||
|
||||
void route() :
|
||||
{/*@bgen(jjtree) route */
|
||||
ASTroute jjtn000 = new ASTroute(JJTROUTE);
|
||||
boolean jjtc000 = true;
|
||||
jjtree.openNodeScope(jjtn000);
|
||||
jjtreeOpenNodeScope(jjtn000);
|
||||
/*@egen*/}
|
||||
{/*@bgen(jjtree) route */
|
||||
try {
|
||||
/*@egen*/
|
||||
"@" domain() ( (",")* "@" domain() )* ":"/*@bgen(jjtree)*/
|
||||
} catch (Throwable jjte000) {
|
||||
if (jjtc000) {
|
||||
jjtree.clearNodeScope(jjtn000);
|
||||
jjtc000 = false;
|
||||
} else {
|
||||
jjtree.popNode();
|
||||
}
|
||||
if (jjte000 instanceof RuntimeException) {
|
||||
throw (RuntimeException)jjte000;
|
||||
}
|
||||
if (jjte000 instanceof ParseException) {
|
||||
throw (ParseException)jjte000;
|
||||
}
|
||||
throw (Error)jjte000;
|
||||
} finally {
|
||||
if (jjtc000) {
|
||||
jjtree.closeNodeScope(jjtn000, true);
|
||||
jjtreeCloseNodeScope(jjtn000);
|
||||
}
|
||||
}
|
||||
/*@egen*/
|
||||
}
|
||||
|
||||
void phrase() :
|
||||
{/*@bgen(jjtree) phrase */
|
||||
ASTphrase jjtn000 = new ASTphrase(JJTPHRASE);
|
||||
boolean jjtc000 = true;
|
||||
jjtree.openNodeScope(jjtn000);
|
||||
jjtreeOpenNodeScope(jjtn000);
|
||||
/*@egen*/}
|
||||
{/*@bgen(jjtree) phrase */
|
||||
try {
|
||||
/*@egen*/
|
||||
( <DOTATOM>
|
||||
| <QUOTEDSTRING>
|
||||
)+/*@bgen(jjtree)*/
|
||||
} finally {
|
||||
if (jjtc000) {
|
||||
jjtree.closeNodeScope(jjtn000, true);
|
||||
jjtreeCloseNodeScope(jjtn000);
|
||||
}
|
||||
}
|
||||
/*@egen*/
|
||||
}
|
||||
|
||||
void addr_spec() :
|
||||
{/*@bgen(jjtree) addr_spec */
|
||||
ASTaddr_spec jjtn000 = new ASTaddr_spec(JJTADDR_SPEC);
|
||||
boolean jjtc000 = true;
|
||||
jjtree.openNodeScope(jjtn000);
|
||||
jjtreeOpenNodeScope(jjtn000);
|
||||
/*@egen*/}
|
||||
{/*@bgen(jjtree) addr_spec */
|
||||
try {
|
||||
/*@egen*/
|
||||
( local_part() "@" domain() )/*@bgen(jjtree)*/
|
||||
} catch (Throwable jjte000) {
|
||||
if (jjtc000) {
|
||||
jjtree.clearNodeScope(jjtn000);
|
||||
jjtc000 = false;
|
||||
} else {
|
||||
jjtree.popNode();
|
||||
}
|
||||
if (jjte000 instanceof RuntimeException) {
|
||||
throw (RuntimeException)jjte000;
|
||||
}
|
||||
if (jjte000 instanceof ParseException) {
|
||||
throw (ParseException)jjte000;
|
||||
}
|
||||
throw (Error)jjte000;
|
||||
} finally {
|
||||
if (jjtc000) {
|
||||
jjtree.closeNodeScope(jjtn000, true);
|
||||
jjtreeCloseNodeScope(jjtn000);
|
||||
}
|
||||
}
|
||||
/*@egen*/
|
||||
}
|
||||
|
||||
void local_part() :
|
||||
{/*@bgen(jjtree) local_part */
|
||||
ASTlocal_part jjtn000 = new ASTlocal_part(JJTLOCAL_PART);
|
||||
boolean jjtc000 = true;
|
||||
jjtree.openNodeScope(jjtn000);
|
||||
jjtreeOpenNodeScope(jjtn000);
|
||||
/*@egen*/ Token t; }
|
||||
{/*@bgen(jjtree) local_part */
|
||||
try {
|
||||
/*@egen*/
|
||||
( t=<DOTATOM> | t=<QUOTEDSTRING> )
|
||||
( [t="."]
|
||||
{
|
||||
if (t.image.charAt(t.image.length() - 1) != '.' || t.kind == AddressListParserConstants.QUOTEDSTRING)
|
||||
throw new ParseException("Words in local part must be separated by '.'");
|
||||
}
|
||||
( t=<DOTATOM> | t=<QUOTEDSTRING> )
|
||||
)*/*@bgen(jjtree)*/
|
||||
} finally {
|
||||
if (jjtc000) {
|
||||
jjtree.closeNodeScope(jjtn000, true);
|
||||
jjtreeCloseNodeScope(jjtn000);
|
||||
}
|
||||
}
|
||||
/*@egen*/
|
||||
}
|
||||
|
||||
void domain() :
|
||||
{/*@bgen(jjtree) domain */
|
||||
ASTdomain jjtn000 = new ASTdomain(JJTDOMAIN);
|
||||
boolean jjtc000 = true;
|
||||
jjtree.openNodeScope(jjtn000);
|
||||
jjtreeOpenNodeScope(jjtn000);
|
||||
/*@egen*/ Token t; }
|
||||
{/*@bgen(jjtree) domain */
|
||||
try {
|
||||
/*@egen*/
|
||||
( t=<DOTATOM>
|
||||
( [t="."]
|
||||
{
|
||||
if (t.image.charAt(t.image.length() - 1) != '.')
|
||||
throw new ParseException("Atoms in domain names must be separated by '.'");
|
||||
}
|
||||
t=<DOTATOM>
|
||||
)*
|
||||
)
|
||||
| <DOMAINLITERAL>/*@bgen(jjtree)*/
|
||||
} finally {
|
||||
if (jjtc000) {
|
||||
jjtree.closeNodeScope(jjtn000, true);
|
||||
jjtreeCloseNodeScope(jjtn000);
|
||||
}
|
||||
}
|
||||
/*@egen*/
|
||||
}
|
||||
|
||||
SPECIAL_TOKEN :
|
||||
{
|
||||
< WS: ( [" ", "\t"] )+ >
|
||||
}
|
||||
|
||||
TOKEN :
|
||||
{
|
||||
< #ALPHA: ["a" - "z", "A" - "Z"] >
|
||||
| < #DIGIT: ["0" - "9"] >
|
||||
| < #ATEXT: ( <ALPHA> | <DIGIT>
|
||||
| "!" | "#" | "$" | "%"
|
||||
| "&" | "'" | "*" | "+"
|
||||
| "-" | "/" | "=" | "?"
|
||||
| "^" | "_" | "`" | "{"
|
||||
| "|" | "}" | "~"
|
||||
)>
|
||||
| < DOTATOM: <ATEXT> ( <ATEXT> | "." )* >
|
||||
}
|
||||
|
||||
TOKEN_MGR_DECLS :
|
||||
{
|
||||
// Keeps track of how many levels of comment nesting
|
||||
// we've encountered. This is only used when the 2nd
|
||||
// level is reached, for example ((this)), not (this).
|
||||
// This is because the outermost level must be treated
|
||||
// specially anyway, because the outermost ")" has a
|
||||
// different token type than inner ")" instances.
|
||||
static int commentNest;
|
||||
}
|
||||
|
||||
MORE :
|
||||
{
|
||||
// domain literal
|
||||
"[" : INDOMAINLITERAL
|
||||
}
|
||||
|
||||
<INDOMAINLITERAL>
|
||||
MORE :
|
||||
{
|
||||
< <QUOTEDPAIR>> { image.deleteCharAt(image.length() - 2); }
|
||||
| < ~["[", "]", "\\"] >
|
||||
}
|
||||
|
||||
<INDOMAINLITERAL>
|
||||
TOKEN :
|
||||
{
|
||||
< DOMAINLITERAL: "]" > { matchedToken.image = image.toString(); }: DEFAULT
|
||||
}
|
||||
|
||||
MORE :
|
||||
{
|
||||
// starts a comment
|
||||
"(" : INCOMMENT
|
||||
}
|
||||
|
||||
<INCOMMENT>
|
||||
SKIP :
|
||||
{
|
||||
// ends a comment
|
||||
< COMMENT: ")" > : DEFAULT
|
||||
// if this is ever changed to not be a SKIP, need
|
||||
// to make sure matchedToken.token = token.toString()
|
||||
// is called.
|
||||
}
|
||||
|
||||
<INCOMMENT>
|
||||
MORE :
|
||||
{
|
||||
< <QUOTEDPAIR>> { image.deleteCharAt(image.length() - 2); }
|
||||
| "(" { commentNest = 1; } : NESTED_COMMENT
|
||||
| < <ANY>>
|
||||
}
|
||||
|
||||
<NESTED_COMMENT>
|
||||
MORE :
|
||||
{
|
||||
< <QUOTEDPAIR>> { image.deleteCharAt(image.length() - 2); }
|
||||
| "(" { ++commentNest; }
|
||||
| ")" { --commentNest; if (commentNest == 0) SwitchTo(INCOMMENT); }
|
||||
| < <ANY>>
|
||||
}
|
||||
|
||||
|
||||
// QUOTED STRINGS
|
||||
|
||||
MORE :
|
||||
{
|
||||
"\"" { image.deleteCharAt(image.length() - 1); } : INQUOTEDSTRING
|
||||
}
|
||||
|
||||
<INQUOTEDSTRING>
|
||||
MORE :
|
||||
{
|
||||
< <QUOTEDPAIR>> { image.deleteCharAt(image.length() - 2); }
|
||||
| < (~["\"", "\\"])+ >
|
||||
}
|
||||
|
||||
<INQUOTEDSTRING>
|
||||
TOKEN :
|
||||
{
|
||||
< QUOTEDSTRING: "\"" > { matchedToken.image = image.substring(0, image.length() - 1); } : DEFAULT
|
||||
}
|
||||
|
||||
// GLOBALS
|
||||
|
||||
<*>
|
||||
TOKEN :
|
||||
{
|
||||
< #QUOTEDPAIR: "\\" <ANY> >
|
||||
| < #ANY: ~[] >
|
||||
}
|
||||
|
||||
// ERROR!
|
||||
/*
|
||||
|
||||
<*>
|
||||
TOKEN :
|
||||
{
|
||||
< UNEXPECTED_CHAR: <ANY> >
|
||||
}
|
||||
|
||||
*/
|
@ -1,41 +1,66 @@
|
||||
/* Generated By:JJTree&JavaCC: Do not edit this line. AddressListParserConstants.java */
|
||||
/*
|
||||
* Copyright 2004 the mime4j project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
/****************************************************************
|
||||
* Licensed to the Apache Software Foundation (ASF) under one *
|
||||
* or more contributor license agreements. See the NOTICE file *
|
||||
* distributed with this work for additional information *
|
||||
* regarding copyright ownership. The ASF licenses this file *
|
||||
* to you 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. *
|
||||
****************************************************************/
|
||||
package org.apache.james.mime4j.field.address.parser;
|
||||
|
||||
|
||||
/**
|
||||
* Token literal values and constants.
|
||||
* Generated by org.javacc.parser.OtherFilesGen#start()
|
||||
*/
|
||||
public interface AddressListParserConstants {
|
||||
|
||||
/** End of File. */
|
||||
int EOF = 0;
|
||||
/** RegularExpression Id. */
|
||||
int WS = 10;
|
||||
/** RegularExpression Id. */
|
||||
int ALPHA = 11;
|
||||
/** RegularExpression Id. */
|
||||
int DIGIT = 12;
|
||||
/** RegularExpression Id. */
|
||||
int ATEXT = 13;
|
||||
/** RegularExpression Id. */
|
||||
int DOTATOM = 14;
|
||||
/** RegularExpression Id. */
|
||||
int DOMAINLITERAL = 18;
|
||||
/** RegularExpression Id. */
|
||||
int COMMENT = 20;
|
||||
/** RegularExpression Id. */
|
||||
int QUOTEDSTRING = 31;
|
||||
/** RegularExpression Id. */
|
||||
int QUOTEDPAIR = 32;
|
||||
/** RegularExpression Id. */
|
||||
int ANY = 33;
|
||||
|
||||
/** Lexical state. */
|
||||
int DEFAULT = 0;
|
||||
/** Lexical state. */
|
||||
int INDOMAINLITERAL = 1;
|
||||
/** Lexical state. */
|
||||
int INCOMMENT = 2;
|
||||
/** Lexical state. */
|
||||
int NESTED_COMMENT = 3;
|
||||
/** Lexical state. */
|
||||
int INQUOTEDSTRING = 4;
|
||||
|
||||
/** Literal token values. */
|
||||
String[] tokenImage = {
|
||||
"<EOF>",
|
||||
"\"\\r\"",
|
||||
|
@ -1,21 +1,25 @@
|
||||
/* Generated By:JJTree&JavaCC: Do not edit this line. AddressListParserTokenManager.java */
|
||||
/*
|
||||
* Copyright 2004 the mime4j project
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
/****************************************************************
|
||||
* Licensed to the Apache Software Foundation (ASF) under one *
|
||||
* or more contributor license agreements. See the NOTICE file *
|
||||
* distributed with this work for additional information *
|
||||
* regarding copyright ownership. The ASF licenses this file *
|
||||
* to you 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. *
|
||||
****************************************************************/
|
||||
package org.apache.james.mime4j.field.address.parser;
|
||||
|
||||
/** Token Manager. */
|
||||
public class AddressListParserTokenManager implements AddressListParserConstants
|
||||
{
|
||||
// Keeps track of how many levels of comment nesting
|
||||
@ -25,7 +29,10 @@ public class AddressListParserTokenManager implements AddressListParserConstants
|
||||
// specially anyway, because the outermost ")" has a
|
||||
// different token type than inner ")" instances.
|
||||
static int commentNest;
|
||||
|
||||
/** Debug output. */
|
||||
public java.io.PrintStream debugStream = System.out;
|
||||
/** Set debug output. */
|
||||
public void setDebugStream(java.io.PrintStream ds) { debugStream = ds; }
|
||||
private final int jjStopStringLiteralDfa_0(int pos, long active0)
|
||||
{
|
||||
@ -39,21 +46,13 @@ private final int jjStartNfa_0(int pos, long active0)
|
||||
{
|
||||
return jjMoveNfa_0(jjStopStringLiteralDfa_0(pos, active0), pos + 1);
|
||||
}
|
||||
private final int jjStopAtPos(int pos, int kind)
|
||||
private int jjStopAtPos(int pos, int kind)
|
||||
{
|
||||
jjmatchedKind = kind;
|
||||
jjmatchedPos = pos;
|
||||
return pos + 1;
|
||||
}
|
||||
private final int jjStartNfaWithStates_0(int pos, int kind, int state)
|
||||
{
|
||||
jjmatchedKind = kind;
|
||||
jjmatchedPos = pos;
|
||||
try { curChar = input_stream.readChar(); }
|
||||
catch(java.io.IOException e) { return pos + 1; }
|
||||
return jjMoveNfa_0(state, pos + 1);
|
||||
}
|
||||
private final int jjMoveStringLiteralDfa0_0()
|
||||
private int jjMoveStringLiteralDfa0_0()
|
||||
{
|
||||
switch(curChar)
|
||||
{
|
||||
@ -85,44 +84,13 @@ private final int jjMoveStringLiteralDfa0_0()
|
||||
return jjMoveNfa_0(1, 0);
|
||||
}
|
||||
}
|
||||
private final void jjCheckNAdd(int state)
|
||||
private int jjMoveNfa_0(int startState, int curPos)
|
||||
{
|
||||
if (jjrounds[state] != jjround)
|
||||
{
|
||||
jjstateSet[jjnewStateCnt++] = state;
|
||||
jjrounds[state] = jjround;
|
||||
}
|
||||
}
|
||||
private final void jjAddStates(int start, int end)
|
||||
{
|
||||
do {
|
||||
jjstateSet[jjnewStateCnt++] = jjnextStates[start];
|
||||
} while (start++ != end);
|
||||
}
|
||||
private final void jjCheckNAddTwoStates(int state1, int state2)
|
||||
{
|
||||
jjCheckNAdd(state1);
|
||||
jjCheckNAdd(state2);
|
||||
}
|
||||
private final void jjCheckNAddStates(int start, int end)
|
||||
{
|
||||
do {
|
||||
jjCheckNAdd(jjnextStates[start]);
|
||||
} while (start++ != end);
|
||||
}
|
||||
private final void jjCheckNAddStates(int start)
|
||||
{
|
||||
jjCheckNAdd(jjnextStates[start]);
|
||||
jjCheckNAdd(jjnextStates[start + 1]);
|
||||
}
|
||||
private final int jjMoveNfa_0(int startState, int curPos)
|
||||
{
|
||||
int[] nextStates;
|
||||
int startsAt = 0;
|
||||
jjnewStateCnt = 3;
|
||||
int i = 1;
|
||||
jjstateSet[0] = startState;
|
||||
int j, kind = 0x7fffffff;
|
||||
int kind = 0x7fffffff;
|
||||
for (;;)
|
||||
{
|
||||
if (++jjround == 0x7fffffff)
|
||||
@ -130,7 +98,7 @@ private final int jjMoveNfa_0(int startState, int curPos)
|
||||
if (curChar < 64)
|
||||
{
|
||||
long l = 1L << curChar;
|
||||
MatchLoop: do
|
||||
do
|
||||
{
|
||||
switch(jjstateSet[--i])
|
||||
{
|
||||
@ -168,7 +136,7 @@ private final int jjMoveNfa_0(int startState, int curPos)
|
||||
else if (curChar < 128)
|
||||
{
|
||||
long l = 1L << (curChar & 077);
|
||||
MatchLoop: do
|
||||
do
|
||||
{
|
||||
switch(jjstateSet[--i])
|
||||
{
|
||||
@ -188,7 +156,7 @@ private final int jjMoveNfa_0(int startState, int curPos)
|
||||
{
|
||||
int i2 = (curChar & 0xff) >> 6;
|
||||
long l2 = 1L << (curChar & 077);
|
||||
MatchLoop: do
|
||||
do
|
||||
{
|
||||
switch(jjstateSet[--i])
|
||||
{
|
||||
@ -221,15 +189,7 @@ private final int jjStartNfa_2(int pos, long active0)
|
||||
{
|
||||
return jjMoveNfa_2(jjStopStringLiteralDfa_2(pos, active0), pos + 1);
|
||||
}
|
||||
private final int jjStartNfaWithStates_2(int pos, int kind, int state)
|
||||
{
|
||||
jjmatchedKind = kind;
|
||||
jjmatchedPos = pos;
|
||||
try { curChar = input_stream.readChar(); }
|
||||
catch(java.io.IOException e) { return pos + 1; }
|
||||
return jjMoveNfa_2(state, pos + 1);
|
||||
}
|
||||
private final int jjMoveStringLiteralDfa0_2()
|
||||
private int jjMoveStringLiteralDfa0_2()
|
||||
{
|
||||
switch(curChar)
|
||||
{
|
||||
@ -244,14 +204,13 @@ private final int jjMoveStringLiteralDfa0_2()
|
||||
static final long[] jjbitVec0 = {
|
||||
0x0L, 0x0L, 0xffffffffffffffffL, 0xffffffffffffffffL
|
||||
};
|
||||
private final int jjMoveNfa_2(int startState, int curPos)
|
||||
private int jjMoveNfa_2(int startState, int curPos)
|
||||
{
|
||||
int[] nextStates;
|
||||
int startsAt = 0;
|
||||
jjnewStateCnt = 3;
|
||||
int i = 1;
|
||||
jjstateSet[0] = startState;
|
||||
int j, kind = 0x7fffffff;
|
||||
int kind = 0x7fffffff;
|
||||
for (;;)
|
||||
{
|
||||
if (++jjround == 0x7fffffff)
|
||||
@ -259,7 +218,7 @@ private final int jjMoveNfa_2(int startState, int curPos)
|
||||
if (curChar < 64)
|
||||
{
|
||||
long l = 1L << curChar;
|
||||
MatchLoop: do
|
||||
do
|
||||
{
|
||||
switch(jjstateSet[--i])
|
||||
{
|
||||
@ -278,7 +237,7 @@ private final int jjMoveNfa_2(int startState, int curPos)
|
||||
else if (curChar < 128)
|
||||
{
|
||||
long l = 1L << (curChar & 077);
|
||||
MatchLoop: do
|
||||
do
|
||||
{
|
||||
switch(jjstateSet[--i])
|
||||
{
|
||||
@ -304,7 +263,7 @@ private final int jjMoveNfa_2(int startState, int curPos)
|
||||
{
|
||||
int i2 = (curChar & 0xff) >> 6;
|
||||
long l2 = 1L << (curChar & 077);
|
||||
MatchLoop: do
|
||||
do
|
||||
{
|
||||
switch(jjstateSet[--i])
|
||||
{
|
||||
@ -345,15 +304,7 @@ private final int jjStartNfa_4(int pos, long active0)
|
||||
{
|
||||
return jjMoveNfa_4(jjStopStringLiteralDfa_4(pos, active0), pos + 1);
|
||||
}
|
||||
private final int jjStartNfaWithStates_4(int pos, int kind, int state)
|
||||
{
|
||||
jjmatchedKind = kind;
|
||||
jjmatchedPos = pos;
|
||||
try { curChar = input_stream.readChar(); }
|
||||
catch(java.io.IOException e) { return pos + 1; }
|
||||
return jjMoveNfa_4(state, pos + 1);
|
||||
}
|
||||
private final int jjMoveStringLiteralDfa0_4()
|
||||
private int jjMoveStringLiteralDfa0_4()
|
||||
{
|
||||
switch(curChar)
|
||||
{
|
||||
@ -363,14 +314,13 @@ private final int jjMoveStringLiteralDfa0_4()
|
||||
return jjMoveNfa_4(0, 0);
|
||||
}
|
||||
}
|
||||
private final int jjMoveNfa_4(int startState, int curPos)
|
||||
private int jjMoveNfa_4(int startState, int curPos)
|
||||
{
|
||||
int[] nextStates;
|
||||
int startsAt = 0;
|
||||
jjnewStateCnt = 3;
|
||||
int i = 1;
|
||||
jjstateSet[0] = startState;
|
||||
int j, kind = 0x7fffffff;
|
||||
int kind = 0x7fffffff;
|
||||
for (;;)
|
||||
{
|
||||
if (++jjround == 0x7fffffff)
|
||||
@ -378,7 +328,7 @@ private final int jjMoveNfa_4(int startState, int curPos)
|
||||
if (curChar < 64)
|
||||
{
|
||||
long l = 1L << curChar;
|
||||
MatchLoop: do
|
||||
do
|
||||
{
|
||||
switch(jjstateSet[--i])
|
||||
{
|
||||
@ -401,7 +351,7 @@ private final int jjMoveNfa_4(int startState, int curPos)
|
||||
else if (curChar < 128)
|
||||
{
|
||||
long l = 1L << (curChar & 077);
|
||||
MatchLoop: do
|
||||
do
|
||||
{
|
||||
switch(jjstateSet[--i])
|
||||
{
|
||||
@ -434,7 +384,7 @@ private final int jjMoveNfa_4(int startState, int curPos)
|
||||
{
|
||||
int i2 = (curChar & 0xff) >> 6;
|
||||
long l2 = 1L << (curChar & 077);
|
||||
MatchLoop: do
|
||||
do
|
||||
{
|
||||
switch(jjstateSet[--i])
|
||||
{
|
||||
@ -479,15 +429,7 @@ private final int jjStartNfa_3(int pos, long active0)
|
||||
{
|
||||
return jjMoveNfa_3(jjStopStringLiteralDfa_3(pos, active0), pos + 1);
|
||||
}
|
||||
private final int jjStartNfaWithStates_3(int pos, int kind, int state)
|
||||
{
|
||||
jjmatchedKind = kind;
|
||||
jjmatchedPos = pos;
|
||||
try { curChar = input_stream.readChar(); }
|
||||
catch(java.io.IOException e) { return pos + 1; }
|
||||
return jjMoveNfa_3(state, pos + 1);
|
||||
}
|
||||
private final int jjMoveStringLiteralDfa0_3()
|
||||
private int jjMoveStringLiteralDfa0_3()
|
||||
{
|
||||
switch(curChar)
|
||||
{
|
||||
@ -499,14 +441,13 @@ private final int jjMoveStringLiteralDfa0_3()
|
||||
return jjMoveNfa_3(0, 0);
|
||||
}
|
||||
}
|
||||
private final int jjMoveNfa_3(int startState, int curPos)
|
||||
private int jjMoveNfa_3(int startState, int curPos)
|
||||
{
|
||||
int[] nextStates;
|
||||
int startsAt = 0;
|
||||
jjnewStateCnt = 3;
|
||||
int i = 1;
|
||||
jjstateSet[0] = startState;
|
||||
int j, kind = 0x7fffffff;
|
||||
int kind = 0x7fffffff;
|
||||
for (;;)
|
||||
{
|
||||
if (++jjround == 0x7fffffff)
|
||||
@ -514,7 +455,7 @@ private final int jjMoveNfa_3(int startState, int curPos)
|
||||
if (curChar < 64)
|
||||
{
|
||||
long l = 1L << curChar;
|
||||
MatchLoop: do
|
||||
do
|
||||
{
|
||||
switch(jjstateSet[--i])
|
||||
{
|
||||
@ -533,7 +474,7 @@ private final int jjMoveNfa_3(int startState, int curPos)
|
||||
else if (curChar < 128)
|
||||
{
|
||||
long l = 1L << (curChar & 077);
|
||||
MatchLoop: do
|
||||
do
|
||||
{
|
||||
switch(jjstateSet[--i])
|
||||
{
|
||||
@ -559,7 +500,7 @@ private final int jjMoveNfa_3(int startState, int curPos)
|
||||
{
|
||||
int i2 = (curChar & 0xff) >> 6;
|
||||
long l2 = 1L << (curChar & 077);
|
||||
MatchLoop: do
|
||||
do
|
||||
{
|
||||
switch(jjstateSet[--i])
|
||||
{
|
||||
@ -600,15 +541,7 @@ private final int jjStartNfa_1(int pos, long active0)
|
||||
{
|
||||
return jjMoveNfa_1(jjStopStringLiteralDfa_1(pos, active0), pos + 1);
|
||||
}
|
||||
private final int jjStartNfaWithStates_1(int pos, int kind, int state)
|
||||
{
|
||||
jjmatchedKind = kind;
|
||||
jjmatchedPos = pos;
|
||||
try { curChar = input_stream.readChar(); }
|
||||
catch(java.io.IOException e) { return pos + 1; }
|
||||
return jjMoveNfa_1(state, pos + 1);
|
||||
}
|
||||
private final int jjMoveStringLiteralDfa0_1()
|
||||
private int jjMoveStringLiteralDfa0_1()
|
||||
{
|
||||
switch(curChar)
|
||||
{
|
||||
@ -618,14 +551,13 @@ private final int jjMoveStringLiteralDfa0_1()
|
||||
return jjMoveNfa_1(0, 0);
|
||||
}
|
||||
}
|
||||
private final int jjMoveNfa_1(int startState, int curPos)
|
||||
private int jjMoveNfa_1(int startState, int curPos)
|
||||
{
|
||||
int[] nextStates;
|
||||
int startsAt = 0;
|
||||
jjnewStateCnt = 3;
|
||||
int i = 1;
|
||||
jjstateSet[0] = startState;
|
||||
int j, kind = 0x7fffffff;
|
||||
int kind = 0x7fffffff;
|
||||
for (;;)
|
||||
{
|
||||
if (++jjround == 0x7fffffff)
|
||||
@ -633,7 +565,7 @@ private final int jjMoveNfa_1(int startState, int curPos)
|
||||
if (curChar < 64)
|
||||
{
|
||||
long l = 1L << curChar;
|
||||
MatchLoop: do
|
||||
do
|
||||
{
|
||||
switch(jjstateSet[--i])
|
||||
{
|
||||
@ -652,7 +584,7 @@ private final int jjMoveNfa_1(int startState, int curPos)
|
||||
else if (curChar < 128)
|
||||
{
|
||||
long l = 1L << (curChar & 077);
|
||||
MatchLoop: do
|
||||
do
|
||||
{
|
||||
switch(jjstateSet[--i])
|
||||
{
|
||||
@ -681,7 +613,7 @@ private final int jjMoveNfa_1(int startState, int curPos)
|
||||
{
|
||||
int i2 = (curChar & 0xff) >> 6;
|
||||
long l2 = 1L << (curChar & 077);
|
||||
MatchLoop: do
|
||||
do
|
||||
{
|
||||
switch(jjstateSet[--i])
|
||||
{
|
||||
@ -712,17 +644,23 @@ private final int jjMoveNfa_1(int startState, int curPos)
|
||||
}
|
||||
static final int[] jjnextStates = {
|
||||
};
|
||||
|
||||
/** Token literal values. */
|
||||
public static final String[] jjstrLiteralImages = {
|
||||
"", "\15", "\12", "\54", "\72", "\73", "\74", "\76", "\100", "\56", null, null,
|
||||
null, null, null, null, null, null, null, null, null, null, null, null, null, null,
|
||||
null, null, null, null, null, null, null, null, };
|
||||
|
||||
/** Lexer state names. */
|
||||
public static final String[] lexStateNames = {
|
||||
"DEFAULT",
|
||||
"INDOMAINLITERAL",
|
||||
"INCOMMENT",
|
||||
"NESTED_COMMENT",
|
||||
"INQUOTEDSTRING",
|
||||
"DEFAULT",
|
||||
"INDOMAINLITERAL",
|
||||
"INCOMMENT",
|
||||
"NESTED_COMMENT",
|
||||
"INQUOTEDSTRING",
|
||||
};
|
||||
|
||||
/** Lex State array. */
|
||||
public static final int[] jjnewLexState = {
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, -1, -1, 0, 2, 0, -1, 3, -1, -1,
|
||||
-1, -1, -1, 4, -1, -1, 0, -1, -1,
|
||||
@ -742,19 +680,25 @@ static final long[] jjtoMore = {
|
||||
protected SimpleCharStream input_stream;
|
||||
private final int[] jjrounds = new int[3];
|
||||
private final int[] jjstateSet = new int[6];
|
||||
StringBuffer image;
|
||||
int jjimageLen;
|
||||
int lengthOfMatch;
|
||||
private final StringBuilder jjimage = new StringBuilder();
|
||||
private StringBuilder image = jjimage;
|
||||
private int jjimageLen;
|
||||
private int lengthOfMatch;
|
||||
protected char curChar;
|
||||
/** Constructor. */
|
||||
public AddressListParserTokenManager(SimpleCharStream stream){
|
||||
if (SimpleCharStream.staticFlag)
|
||||
throw new Error("ERROR: Cannot use a static CharStream class with a non-static lexical analyzer.");
|
||||
input_stream = stream;
|
||||
}
|
||||
|
||||
/** Constructor. */
|
||||
public AddressListParserTokenManager(SimpleCharStream stream, int lexState){
|
||||
this(stream);
|
||||
SwitchTo(lexState);
|
||||
}
|
||||
|
||||
/** Reinitialise parser. */
|
||||
public void ReInit(SimpleCharStream stream)
|
||||
{
|
||||
jjmatchedPos = jjnewStateCnt = 0;
|
||||
@ -762,18 +706,22 @@ public void ReInit(SimpleCharStream stream)
|
||||
input_stream = stream;
|
||||
ReInitRounds();
|
||||
}
|
||||
private final void ReInitRounds()
|
||||
private void ReInitRounds()
|
||||
{
|
||||
int i;
|
||||
jjround = 0x80000001;
|
||||
for (i = 3; i-- > 0;)
|
||||
jjrounds[i] = 0x80000000;
|
||||
}
|
||||
|
||||
/** Reinitialise parser. */
|
||||
public void ReInit(SimpleCharStream stream, int lexState)
|
||||
{
|
||||
ReInit(stream);
|
||||
SwitchTo(lexState);
|
||||
}
|
||||
|
||||
/** Switch to specified lex state. */
|
||||
public void SwitchTo(int lexState)
|
||||
{
|
||||
if (lexState >= 5 || lexState < 0)
|
||||
@ -784,14 +732,25 @@ public void SwitchTo(int lexState)
|
||||
|
||||
protected Token jjFillToken()
|
||||
{
|
||||
Token t = Token.newToken(jjmatchedKind);
|
||||
t.kind = jjmatchedKind;
|
||||
final Token t;
|
||||
final String curTokenImage;
|
||||
final int beginLine;
|
||||
final int endLine;
|
||||
final int beginColumn;
|
||||
final int endColumn;
|
||||
String im = jjstrLiteralImages[jjmatchedKind];
|
||||
t.image = (im == null) ? input_stream.GetImage() : im;
|
||||
t.beginLine = input_stream.getBeginLine();
|
||||
t.beginColumn = input_stream.getBeginColumn();
|
||||
t.endLine = input_stream.getEndLine();
|
||||
t.endColumn = input_stream.getEndColumn();
|
||||
curTokenImage = (im == null) ? input_stream.GetImage() : im;
|
||||
beginLine = input_stream.getBeginLine();
|
||||
beginColumn = input_stream.getBeginColumn();
|
||||
endLine = input_stream.getEndLine();
|
||||
endColumn = input_stream.getEndColumn();
|
||||
t = Token.newToken(jjmatchedKind, curTokenImage);
|
||||
|
||||
t.beginLine = beginLine;
|
||||
t.endLine = endLine;
|
||||
t.beginColumn = beginColumn;
|
||||
t.endColumn = endColumn;
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
@ -802,28 +761,29 @@ int jjround;
|
||||
int jjmatchedPos;
|
||||
int jjmatchedKind;
|
||||
|
||||
/** Get the next Token. */
|
||||
public Token getNextToken()
|
||||
{
|
||||
int kind;
|
||||
Token specialToken = null;
|
||||
Token matchedToken;
|
||||
int curPos = 0;
|
||||
|
||||
EOFLoop :
|
||||
for (;;)
|
||||
{
|
||||
try
|
||||
{
|
||||
{
|
||||
try
|
||||
{
|
||||
curChar = input_stream.BeginToken();
|
||||
}
|
||||
}
|
||||
catch(java.io.IOException e)
|
||||
{
|
||||
{
|
||||
jjmatchedKind = 0;
|
||||
matchedToken = jjFillToken();
|
||||
matchedToken.specialToken = specialToken;
|
||||
return matchedToken;
|
||||
}
|
||||
image = null;
|
||||
image = jjimage;
|
||||
image.setLength(0);
|
||||
jjimageLen = 0;
|
||||
|
||||
for (;;)
|
||||
@ -927,62 +887,46 @@ void MoreLexicalActions()
|
||||
switch(jjmatchedKind)
|
||||
{
|
||||
case 16 :
|
||||
if (image == null)
|
||||
image = new StringBuffer();
|
||||
image.append(input_stream.GetSuffix(jjimageLen));
|
||||
jjimageLen = 0;
|
||||
image.deleteCharAt(image.length() - 2);
|
||||
break;
|
||||
case 21 :
|
||||
if (image == null)
|
||||
image = new StringBuffer();
|
||||
image.append(input_stream.GetSuffix(jjimageLen));
|
||||
jjimageLen = 0;
|
||||
image.deleteCharAt(image.length() - 2);
|
||||
break;
|
||||
case 22 :
|
||||
if (image == null)
|
||||
image = new StringBuffer();
|
||||
image.append(input_stream.GetSuffix(jjimageLen));
|
||||
jjimageLen = 0;
|
||||
commentNest = 1;
|
||||
break;
|
||||
case 24 :
|
||||
if (image == null)
|
||||
image = new StringBuffer();
|
||||
image.append(input_stream.GetSuffix(jjimageLen));
|
||||
jjimageLen = 0;
|
||||
image.deleteCharAt(image.length() - 2);
|
||||
break;
|
||||
case 25 :
|
||||
if (image == null)
|
||||
image = new StringBuffer();
|
||||
image.append(input_stream.GetSuffix(jjimageLen));
|
||||
jjimageLen = 0;
|
||||
++commentNest;
|
||||
break;
|
||||
case 26 :
|
||||
if (image == null)
|
||||
image = new StringBuffer();
|
||||
image.append(input_stream.GetSuffix(jjimageLen));
|
||||
jjimageLen = 0;
|
||||
--commentNest; if (commentNest == 0) SwitchTo(INCOMMENT);
|
||||
break;
|
||||
case 28 :
|
||||
if (image == null)
|
||||
image = new StringBuffer();
|
||||
image.append(input_stream.GetSuffix(jjimageLen));
|
||||
jjimageLen = 0;
|
||||
image.deleteCharAt(image.length() - 1);
|
||||
break;
|
||||
case 29 :
|
||||
if (image == null)
|
||||
image = new StringBuffer();
|
||||
image.append(input_stream.GetSuffix(jjimageLen));
|
||||
jjimageLen = 0;
|
||||
image.deleteCharAt(image.length() - 2);
|
||||
break;
|
||||
default :
|
||||
default :
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -991,19 +935,35 @@ void TokenLexicalActions(Token matchedToken)
|
||||
switch(jjmatchedKind)
|
||||
{
|
||||
case 18 :
|
||||
if (image == null)
|
||||
image = new StringBuffer();
|
||||
image.append(input_stream.GetSuffix(jjimageLen + (lengthOfMatch = jjmatchedPos + 1)));
|
||||
image.append(input_stream.GetSuffix(jjimageLen + (lengthOfMatch = jjmatchedPos + 1)));
|
||||
matchedToken.image = image.toString();
|
||||
break;
|
||||
case 31 :
|
||||
if (image == null)
|
||||
image = new StringBuffer();
|
||||
image.append(input_stream.GetSuffix(jjimageLen + (lengthOfMatch = jjmatchedPos + 1)));
|
||||
image.append(input_stream.GetSuffix(jjimageLen + (lengthOfMatch = jjmatchedPos + 1)));
|
||||
matchedToken.image = image.substring(0, image.length() - 1);
|
||||
break;
|
||||
default :
|
||||
default :
|
||||
break;
|
||||
}
|
||||
}
|
||||
private void jjCheckNAdd(int state)
|
||||
{
|
||||
if (jjrounds[state] != jjround)
|
||||
{
|
||||
jjstateSet[jjnewStateCnt++] = state;
|
||||
jjrounds[state] = jjround;
|
||||
}
|
||||
}
|
||||
private void jjAddStates(int start, int end)
|
||||
{
|
||||
do {
|
||||
jjstateSet[jjnewStateCnt++] = jjnextStates[start];
|
||||
} while (start++ != end);
|
||||
}
|
||||
private void jjCheckNAddTwoStates(int state1, int state2)
|
||||
{
|
||||
jjCheckNAdd(state1);
|
||||
jjCheckNAdd(state2);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,5 +1,4 @@
|
||||
/* Generated By:JJTree: Do not edit this line. /Users/jason/Projects/apache-mime4j-0.3/target/generated-sources/jjtree/org/apache/james/mime4j/field/address/parser/AddressListParserTreeConstants.java */
|
||||
|
||||
/* Generated By:JavaCC: Do not edit this line. AddressListParserTreeConstants.java Version 5.0 */
|
||||
package org.apache.james.mime4j.field.address.parser;
|
||||
|
||||
public interface AddressListParserTreeConstants
|
||||
@ -33,3 +32,4 @@ public interface AddressListParserTreeConstants
|
||||
"domain",
|
||||
};
|
||||
}
|
||||
/* JavaCC - OriginalChecksum=e7d2b24000a70a573955cf10036f0056 (do not edit this line) */
|
||||
|
@ -1,5 +1,4 @@
|
||||
/* Generated By:JJTree: Do not edit this line. /Users/jason/Projects/apache-mime4j-0.3/target/generated-sources/jjtree/org/apache/james/mime4j/field/address/parser/AddressListParserVisitor.java */
|
||||
|
||||
/* Generated By:JavaCC: Do not edit this line. AddressListParserVisitor.java Version 5.0 */
|
||||
package org.apache.james.mime4j.field.address.parser;
|
||||
|
||||
public interface AddressListParserVisitor
|
||||
@ -17,3 +16,4 @@ public interface AddressListParserVisitor
|
||||
public Object visit(ASTlocal_part node, Object data);
|
||||
public Object visit(ASTdomain node, Object data);
|
||||
}
|
||||
/* JavaCC - OriginalChecksum=f57edd9a1eb17afa5907a87b3c51e284 (do not edit this line) */
|
||||
|
@ -19,8 +19,6 @@
|
||||
|
||||
package org.apache.james.mime4j.field.address.parser;
|
||||
|
||||
import org.apache.james.mime4j.field.address.parser.Node;
|
||||
import org.apache.james.mime4j.field.address.parser.Token;
|
||||
|
||||
public abstract class BaseNode implements Node {
|
||||
|
||||
|
229
src/org/apache/james/mime4j/field/address/parser/Builder.java
Normal file
229
src/org/apache/james/mime4j/field/address/parser/Builder.java
Normal file
@ -0,0 +1,229 @@
|
||||
/****************************************************************
|
||||
* Licensed to the Apache Software Foundation (ASF) under one *
|
||||
* or more contributor license agreements. See the NOTICE file *
|
||||
* distributed with this work for additional information *
|
||||
* regarding copyright ownership. The ASF licenses this file *
|
||||
* to you 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. *
|
||||
****************************************************************/
|
||||
|
||||
package org.apache.james.mime4j.field.address.parser;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.james.mime4j.codec.DecodeMonitor;
|
||||
import org.apache.james.mime4j.codec.DecoderUtil;
|
||||
import org.apache.james.mime4j.dom.address.Address;
|
||||
import org.apache.james.mime4j.dom.address.AddressList;
|
||||
import org.apache.james.mime4j.dom.address.DomainList;
|
||||
import org.apache.james.mime4j.dom.address.Group;
|
||||
import org.apache.james.mime4j.dom.address.Mailbox;
|
||||
import org.apache.james.mime4j.dom.address.MailboxList;
|
||||
|
||||
/**
|
||||
* Transforms the JJTree-generated abstract syntax tree into a graph of
|
||||
* org.apache.james.mime4j.dom.address objects.
|
||||
*/
|
||||
class Builder {
|
||||
|
||||
private static Builder singleton = new Builder();
|
||||
|
||||
public static Builder getInstance() {
|
||||
return singleton;
|
||||
}
|
||||
|
||||
public AddressList buildAddressList(ASTaddress_list node, DecodeMonitor monitor) throws ParseException {
|
||||
List<Address> list = new ArrayList<Address>();
|
||||
for (int i = 0; i < node.jjtGetNumChildren(); i++) {
|
||||
ASTaddress childNode = (ASTaddress) node.jjtGetChild(i);
|
||||
Address address = buildAddress(childNode, monitor);
|
||||
list.add(address);
|
||||
}
|
||||
return new AddressList(list, true);
|
||||
}
|
||||
|
||||
public Address buildAddress(ASTaddress node, DecodeMonitor monitor) throws ParseException {
|
||||
ChildNodeIterator it = new ChildNodeIterator(node);
|
||||
Node n = it.next();
|
||||
if (n instanceof ASTaddr_spec) {
|
||||
return buildAddrSpec((ASTaddr_spec) n);
|
||||
} else if (n instanceof ASTangle_addr) {
|
||||
return buildAngleAddr((ASTangle_addr) n);
|
||||
} else if (n instanceof ASTphrase) {
|
||||
String name = buildString((ASTphrase) n, false);
|
||||
Node n2 = it.next();
|
||||
if (n2 instanceof ASTgroup_body) {
|
||||
return new Group(name, buildGroupBody((ASTgroup_body) n2, monitor));
|
||||
} else if (n2 instanceof ASTangle_addr) {
|
||||
try {
|
||||
name = DecoderUtil.decodeEncodedWords(name, monitor);
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new ParseException(e.getMessage());
|
||||
}
|
||||
Mailbox mb = buildAngleAddr((ASTangle_addr) n2);
|
||||
return new Mailbox(name, mb.getRoute(), mb.getLocalPart(),
|
||||
mb.getDomain());
|
||||
} else {
|
||||
throw new ParseException();
|
||||
}
|
||||
} else {
|
||||
throw new ParseException();
|
||||
}
|
||||
}
|
||||
|
||||
private MailboxList buildGroupBody(ASTgroup_body node, DecodeMonitor monitor) throws ParseException {
|
||||
List<Mailbox> results = new ArrayList<Mailbox>();
|
||||
ChildNodeIterator it = new ChildNodeIterator(node);
|
||||
while (it.hasNext()) {
|
||||
Node n = it.next();
|
||||
if (n instanceof ASTmailbox)
|
||||
results.add(buildMailbox((ASTmailbox) n, monitor));
|
||||
else
|
||||
throw new ParseException();
|
||||
}
|
||||
return new MailboxList(results, true);
|
||||
}
|
||||
|
||||
public Mailbox buildMailbox(ASTmailbox node, DecodeMonitor monitor) throws ParseException {
|
||||
ChildNodeIterator it = new ChildNodeIterator(node);
|
||||
Node n = it.next();
|
||||
if (n instanceof ASTaddr_spec) {
|
||||
return buildAddrSpec((ASTaddr_spec) n);
|
||||
} else if (n instanceof ASTangle_addr) {
|
||||
return buildAngleAddr((ASTangle_addr) n);
|
||||
} else if (n instanceof ASTname_addr) {
|
||||
return buildNameAddr((ASTname_addr) n, monitor);
|
||||
} else {
|
||||
throw new ParseException();
|
||||
}
|
||||
}
|
||||
|
||||
private Mailbox buildNameAddr(ASTname_addr node, DecodeMonitor monitor) throws ParseException {
|
||||
ChildNodeIterator it = new ChildNodeIterator(node);
|
||||
Node n = it.next();
|
||||
String name;
|
||||
if (n instanceof ASTphrase) {
|
||||
name = buildString((ASTphrase) n, false);
|
||||
} else {
|
||||
throw new ParseException();
|
||||
}
|
||||
|
||||
n = it.next();
|
||||
if (n instanceof ASTangle_addr) {
|
||||
try {
|
||||
name = DecoderUtil.decodeEncodedWords(name, monitor);
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new ParseException(e.getMessage());
|
||||
}
|
||||
Mailbox mb = buildAngleAddr((ASTangle_addr) n);
|
||||
return new Mailbox(name, mb.getRoute(), mb.getLocalPart(),
|
||||
mb.getDomain());
|
||||
} else {
|
||||
throw new ParseException();
|
||||
}
|
||||
}
|
||||
|
||||
private Mailbox buildAngleAddr(ASTangle_addr node) throws ParseException {
|
||||
ChildNodeIterator it = new ChildNodeIterator(node);
|
||||
DomainList route = null;
|
||||
Node n = it.next();
|
||||
if (n instanceof ASTroute) {
|
||||
route = buildRoute((ASTroute) n);
|
||||
n = it.next();
|
||||
} else if (n instanceof ASTaddr_spec) {
|
||||
// do nothing
|
||||
}
|
||||
else
|
||||
throw new ParseException();
|
||||
|
||||
if (n instanceof ASTaddr_spec)
|
||||
return buildAddrSpec(route, (ASTaddr_spec) n);
|
||||
else
|
||||
throw new ParseException();
|
||||
}
|
||||
|
||||
private DomainList buildRoute(ASTroute node) throws ParseException {
|
||||
List<String> results = new ArrayList<String>(node.jjtGetNumChildren());
|
||||
ChildNodeIterator it = new ChildNodeIterator(node);
|
||||
while (it.hasNext()) {
|
||||
Node n = it.next();
|
||||
if (n instanceof ASTdomain)
|
||||
results.add(buildString((ASTdomain) n, true));
|
||||
else
|
||||
throw new ParseException();
|
||||
}
|
||||
return new DomainList(results, true);
|
||||
}
|
||||
|
||||
private Mailbox buildAddrSpec(ASTaddr_spec node) {
|
||||
return buildAddrSpec(null, node);
|
||||
}
|
||||
|
||||
private Mailbox buildAddrSpec(DomainList route, ASTaddr_spec node) {
|
||||
ChildNodeIterator it = new ChildNodeIterator(node);
|
||||
String localPart = buildString((ASTlocal_part) it.next(), true);
|
||||
String domain = buildString((ASTdomain) it.next(), true);
|
||||
return new Mailbox(route, localPart, domain);
|
||||
}
|
||||
|
||||
private String buildString(SimpleNode node, boolean stripSpaces) {
|
||||
Token head = node.firstToken;
|
||||
Token tail = node.lastToken;
|
||||
StringBuilder out = new StringBuilder();
|
||||
|
||||
while (head != tail) {
|
||||
out.append(head.image);
|
||||
head = head.next;
|
||||
if (!stripSpaces)
|
||||
addSpecials(out, head.specialToken);
|
||||
}
|
||||
out.append(tail.image);
|
||||
|
||||
return out.toString();
|
||||
}
|
||||
|
||||
private void addSpecials(StringBuilder out, Token specialToken) {
|
||||
if (specialToken != null) {
|
||||
addSpecials(out, specialToken.specialToken);
|
||||
out.append(specialToken.image);
|
||||
}
|
||||
}
|
||||
|
||||
private static class ChildNodeIterator implements Iterator<Node> {
|
||||
|
||||
private SimpleNode simpleNode;
|
||||
private int index;
|
||||
private int len;
|
||||
|
||||
public ChildNodeIterator(SimpleNode simpleNode) {
|
||||
this.simpleNode = simpleNode;
|
||||
this.len = simpleNode.jjtGetNumChildren();
|
||||
this.index = 0;
|
||||
}
|
||||
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public boolean hasNext() {
|
||||
return index < len;
|
||||
}
|
||||
|
||||
public Node next() {
|
||||
return simpleNode.jjtGetChild(index++);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user