/* * Created on 07.11.2004 * * This file is part of susimail project, see http://susi.i2p/ * * Copyright (C) 2004-2005 <susi23@mail.i2p> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * $Revision: 1.4 $ */ package i2p.susi.webmail; import i2p.susi.debug.Debug; import i2p.susi.util.ReadBuffer; import i2p.susi.webmail.encoding.DecodingException; import i2p.susi.webmail.encoding.Encoding; import i2p.susi.webmail.encoding.EncodingFactory; import java.util.ArrayList; import java.util.List; import java.util.Locale; import net.i2p.data.DataHelper; /** * @author susi23 */ class MailPart { public final String[] headerLines; public final String type, encoding, name, description, disposition, charset, version; private final int beginBody, begin, end; /** fixme never set */ public final String filename = null; public final List<MailPart> parts; public final boolean multipart, message; public final ReadBuffer buffer; public MailPart( ReadBuffer readBuffer ) throws DecodingException { this(readBuffer, readBuffer.offset, readBuffer.length); } public MailPart( ReadBuffer readBuffer, int offset, int length ) throws DecodingException { begin = offset; end = offset + length; buffer = readBuffer; parts = new ArrayList<MailPart>(); /* * parse header lines */ int bb = end; for( int i = begin; i < end - 4; i++ ) { if( buffer.content[i] == '\r' && buffer.content[i+1] == '\n' && buffer.content[i+2] == '\r' && buffer.content[i+3] == '\n' ) { bb = i + 2; break; } } beginBody = bb; ReadBuffer decodedHeaders = EncodingFactory.getEncoding( "HEADERLINE" ).decode( buffer.content, begin, beginBody - begin ); headerLines = DataHelper.split(new String(decodedHeaders.content, decodedHeaders.offset, decodedHeaders.length), "\r\n"); String boundary = null; String x_encoding = null; String x_disposition = null; String x_type = null; boolean x_multipart = false; boolean x_message = false; String x_name = null; String x_charset = null; String x_description = null; String x_version = null; for( int i = 0; i < headerLines.length; i++ ) { if( headerLines[i].toLowerCase(Locale.US).startsWith( "content-transfer-encoding: " ) ) { x_encoding = getFirstAttribute( headerLines[i] ).toLowerCase(Locale.US); } else if( headerLines[i].toLowerCase(Locale.US).startsWith( "content-disposition: " ) ) { x_disposition = getFirstAttribute( headerLines[i] ).toLowerCase(Locale.US); String str; str = getHeaderLineAttribute( headerLines[i], "filename" ); if( str != null ) x_name = str; } else if( headerLines[i].toLowerCase(Locale.US).startsWith( "content-type: " ) ) { x_type = getFirstAttribute( headerLines[i] ).toLowerCase(Locale.US); /* * extract boundary, name and charset from content type */ String str; str = getHeaderLineAttribute( headerLines[i], "boundary" ); if( str != null ) boundary = str; if (x_type.startsWith( "multipart" ) && boundary != null ) x_multipart = true; if (x_type.startsWith( "message" ) ) x_message = true; str = getHeaderLineAttribute( headerLines[i], "name" ); if( str != null ) x_name = str; str = getHeaderLineAttribute( headerLines[i], "charset" ); if( str != null ) x_charset = str.toUpperCase(Locale.US); } else if( headerLines[i].toLowerCase(Locale.US).startsWith( "content-description: " ) ) { x_description = getFirstAttribute( headerLines[i] ); } else if( headerLines[i].toLowerCase(Locale.US).startsWith( "mime-version: " ) ) { x_version = getFirstAttribute( headerLines[i] ); } } encoding = x_encoding; disposition = x_disposition; type = x_type; multipart = x_multipart; message = x_message; name = x_name; charset = x_charset; description = x_description; version = x_version; /* * parse body */ int beginLastPart = -1; if( multipart ) { byte boundaryArray[] = DataHelper.getUTF8(boundary); for( int i = beginBody; i < end - 4; i++ ) { if( buffer.content[i] == '\r' && buffer.content[i+1] == '\n' && buffer.content[i+2] == '-' && buffer.content[i+3] == '-' ) { /* * begin of possible boundary line */ int j = 0; for( ; j < boundaryArray.length && i + 4 + j < end; j++ ) if( buffer.content[ i + 4 + j ] != boundaryArray[j] ) break; if( j == boundaryArray.length ) { int k = i + 4 + j; if( k < end - 2 && buffer.content[k] == '-' && buffer.content[k+1] == '-' ) k += 2; if( k < end - 2 && buffer.content[k] == '\r' && buffer.content[k+1] == '\n' ) { k += 2; if( beginLastPart != -1 ) { int endLastPart = Math.min(i + 2, end); MailPart newPart = new MailPart( buffer, beginLastPart, endLastPart - beginLastPart ); parts.add( newPart ); } beginLastPart = k; } i = k; } } } } else if( message ) { MailPart newPart = new MailPart(buffer, beginBody, end - beginBody); parts.add( newPart ); } } /** * @param offset 2 for sendAttachment, 0 otherwise, don't know why * @since 0.9.13 */ public ReadBuffer decode(int offset) throws DecodingException { String encg = encoding; if (encg == null) { //throw new DecodingException("No encoding specified"); Debug.debug(Debug.DEBUG, "Warning: no transfer encoding found, fallback to 7bit."); encg = "7bit"; } Encoding enc = EncodingFactory.getEncoding(encg); if(enc == null) throw new DecodingException(_t("No encoder found for encoding \\''{0}\\''.", WebMail.quoteHTML(encg))); return enc.decode(buffer.content, beginBody + offset, end - beginBody - offset); } private static String getFirstAttribute( String line ) { String result = null; int i = line.indexOf( ": " ); if( i != - 1 ) { int j = line.indexOf(';', i + 2 ); if( j == -1 ) result = line.substring( i + 2 ); else result = line.substring( i + 2, j ); result = result.trim(); } return result; } private static String getHeaderLineAttribute( String line, String attributeName ) { String result = null; int h = 0; int l = attributeName.length(); while( true ) { int i = line.indexOf( attributeName, h ); // System.err.println( "i=" + i ); if( i == -1 ) break; h = i + l; int j = line.indexOf('=', i + l ); // System.err.println( "j=" + j ); if( j != -1 ) { int k = line.indexOf('"', j + 1 ); int m = line.indexOf(';', j + 1 ); // System.err.println( "k=" + k ); if( k != -1 && ( m == -1 || k < m ) ) { /* * we found a " before a possible ; * * now we look for the 2nd (not quoted) " */ m = -1; int k2 = k + 1; while( true ) { m = line.indexOf('"', k2 ); // System.err.println( "m=" + m + " '" + line.substring( m ) + "'" ); if( m == -1 ) { break; } else { /* * found one */ if( line.charAt( m - 1 ) != '\\' ) { /* * its not quoted, so it is the one we look for */ result = line.substring( k + 1, m ); break; } else { /* * this is quoted, so we extract the quote and continue the search */ line = line.substring( 0, m - 1 ) + line.substring( m ); // System.err.println( "quoting found, line='" + line + "'" ); k2 = m; } } } } else if( m != -1 ) { /* * no " found, but a ; */ result = line.substring( j + 1, m ).trim(); } else { /* * no " found and no ; */ result = line.substring( j + 1 ); } } } return result; } /** translate */ private static String _t(String s, Object o) { return Messages.getString(s, o); } }