pjs/grendel/mime/parser/MimeDwimText.java

257 строки
8.0 KiB
Java

/* -*- Mode: java; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.0 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
* the License for the specific language governing rights and limitations
* under the License.
*
* The Original Code is the Grendel mail/news client.
*
* The Initial Developer of the Original Code is Netscape Communications
* Corporation. Portions created by Netscape are Copyright (C) 1997
* Netscape Communications Corporation. All Rights Reserved.
*
* Created: Jamie Zawinski <jwz@netscape.com>, 21 Aug 1997.
*/
package grendel.mime.parser;
import grendel.mime.IMimeOperator;
import calypso.util.ByteBuf;
import calypso.util.ByteLineBuffer;
import javax.mail.internet.InternetHeaders;
/** This class implements a parser for untyped message contents, that is, it
is the class used for the body of a message/rfc822 object which had
<I>no</I> Content-Type header, as opposed to an unrecognized content-type.
Such a message, technically, does not contain MIME data (it follows only
RFC 822, not RFC 1521.)
<P> This is a container class, and the reason for that is that it loosely
parses the body of the message looking for ``sub-parts'' and then creates
appropriate containers for them.
<P> More specifically, it looks for uuencoded and BinHexed data. It may
do more than that some day. (DWIM stands for ``Do What I Mean.'')
@see MimeMultipart
@see MimeXSunAttachment
*/
class MimeDwimText extends MimeContainer {
ByteLineBuffer line_buffer;
ByteBuf line_bytes;
// enumeration of sub-part types
static private final int TEXT = 100;
static private final int UUE = 101;
static private final int BINHEX = 102;
int type;
String subpart_name;
String subpart_type;
String subpart_encoding;
public MimeDwimText(String content_type, InternetHeaders headers) {
super(content_type, headers);
line_buffer = new ByteLineBuffer();
line_bytes = new ByteBuf(100);
type = TEXT;
}
/** Buffers the bytes into lines, and calls process_line() on each line. */
public void pushBytes(ByteBuf bytes) {
line_buffer.pushBytes(bytes);
while (line_buffer.pullLine(line_bytes))
process_line(line_bytes);
}
/** Flushes the line buffer, and (maybe) calls process_line() one last time.
*/
public void pushEOF() {
line_buffer.pushEOF();
while (line_buffer.pullLine(line_bytes))
process_line(line_bytes);
line_buffer = null;
line_bytes = null;
super.pushEOF();
}
/** Called for each line of this part. */
void process_line(ByteBuf line_buf) {
int length = line_buf.length();
byte line[] = line_buf.toBytes();
boolean just_started = false;
if (length < 3)
;
else if (line[0] == 'b' && uue_begin_line_p(line, length)) {
// Close the old part and open a new one.
just_started = true;
// subpart_name was kludgily set by uue_begin_line_p... I suck.
subpart_type = name_to_content_type(subpart_name);
subpart_encoding = "uuencode";
open_untyped_child();
} else if (line[0] == '(' && line[1] == 'T' &&
binhex_begin_line_p(line, length)) {
// Close the old part and open a new one.
just_started = true;
subpart_type = "application/mac-binhex40";
subpart_encoding = null;
subpart_name = null;
open_untyped_child();
}
// Open a text/plain sub-part if there is no sub-part open.
if (open_child == null) {
subpart_type = "text/plain";
subpart_encoding = null;
subpart_name = null;
open_untyped_child();
}
// Hand this line to the currently-open sub-part.
open_child.pushBytes(line_buf);
// Close this sub-part if this line demands it.
if (length >= 3 &&
((type == UUE && line[0] == 'e' && uue_end_line_p(line, length)) ||
(type == BINHEX && binhex_end_line_p(line, length))))
closeChild();
}
void open_untyped_child() {
InternetHeaders hdrs = new InternetHeaders();
if (subpart_type == null)
hdrs.addHeaderLine("Content-Type: application/octet-stream\r\n");
else
hdrs.addHeaderLine("Content-Type: " + subpart_type + "\r\n");
if (subpart_encoding != null)
hdrs.addHeaderLine("Content-Transfer-Encoding: " + subpart_encoding
+ "\r\n");
if (subpart_name != null)
hdrs.addHeaderLine("Content-Disposition: inline; filename=\"" +
subpart_name + "\"\r\n");
hdrs.addHeaderLine("\r\n");
String null_str = null; // weird ambiguous prototype thing...
openChild(null_str, hdrs);
hdrs = null;
}
boolean uue_begin_line_p(byte line[], int length) {
if (length < 10 ||
line[0] != 'b' ||
line[1] != 'e' ||
line[2] != 'g' ||
line[3] != 'i' ||
line[4] != 'n' ||
line[5] != ' ')
return false;
// ...then three or four octal digits.
int i = 6;
if (line[i] < '0' || line[i] > '7') return false;
i++;
if (line[i] < '0' || line[i] > '7') return false;
i++;
if (line[i] < '0' || line[i] > '7') return false;
i++;
if (line[i] == ' ')
i++;
else {
if (line[i] < '0' || line[i] > '7') return false;
i++;
if (line[i] != ' ') return false;
}
while (i < length && line[i] <= ' ')
i++;
while (length >= i && line[length-1] <= ' ')
length--;
// so sue me.
subpart_name = new String(line, i, length-i);
return true;
}
boolean uue_end_line_p(byte line[], int length) {
// accept any line with whitespace at the beginning of the line;
// or any line beginning with the characters "end".
return (length > 1 &&
(line[0] == ' ' ||
line[0] == '\t' ||
(length > 3 &&
((line[0] == 'e' || line[0] == 'E') &&
(line[1] == 'n' || line[1] == 'N') &&
(line[2] == 'd' || line[2] == 'D')))));
}
boolean binhex_begin_line_p(byte line[], int length) {
String s = new String(line, 0, length);
return s.startsWith("(This file must be converted with BinHex 4.0)");
}
boolean binhex_end_line_p(byte line[], int length) {
if (length < 64) return false;
if (line[length-1] == '\n') length--;
if (line[length-1] == '\r') length--;
return (length == 64);
}
String name_to_content_type(String filename) {
// #### need to get at the file-extension-to-mime-type mapping...
filename = filename.toLowerCase();
if (filename.endsWith("txt") || filename.endsWith("text"))
return "text/plain";
else if (filename.endsWith("htm") || filename.endsWith("html"))
return "text/html";
else if (filename.endsWith("gif"))
return "image/gif";
else if (filename.endsWith("jpg") || filename.endsWith("jpeg"))
return "image/jpeg";
else if (filename.endsWith("pjpg") || filename.endsWith("pjpeg"))
return "image/pjpeg";
else if (filename.endsWith("xbm"))
return "image/x-xbitmap";
else if (filename.endsWith("xpm"))
return "image/x-xpixmap";
else if (filename.endsWith("xwd"))
return "image/x-xwindowdump";
else if (filename.endsWith("bmp"))
return "image/x-MS-bmp";
else if (filename.endsWith("au"))
return "audio/basic";
else if (filename.endsWith("aif") || filename.endsWith("aiff") ||
filename.endsWith("aifc"))
return "audio/x-aiff";
else if (filename.endsWith("ps"))
return "application/postscript";
else if (filename.endsWith("p7m"))
return "application/x-pkcs7-mime";
else if (filename.endsWith("p7c"))
return "application/x-pkcs7-mime";
else if (filename.endsWith("p7s"))
return "application/x-pkcs7-signature";
else
return "application/octet-stream";
}
}