/* -*- 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 , 21 Aug 1997. */ package grendel.mime.parser; import grendel.mime.IMimeOperator; import calypso.util.ByteBuf; import javax.mail.internet.InternetHeaders; import java.util.StringTokenizer; /** This class represents the X-Sun-Attachment type, which is the Content-Type assigned by that pile of garbage called MailTool. This is not a MIME type per se, but it's very similar to multipart/mixed, so it's easy to parse. Lots of people use MailTool, so we're stuck with having to parse this idiotic, obsolete format. @see MimeMultipart @see MimeUntypedText */ /* The format is this: = Content-Type is X-Sun-Attachment = parts are separated by lines of exactly ten dashes = just after the dashes comes a block of headers, including: X-Sun-Data-Type: (manditory) Values are Text, Postscript, Scribe, SGML, TeX, Troff, DVI, and Message. X-Sun-Encoding-Info: (optional) Ordered, comma-separated values, including Compress and Uuencode. X-Sun-Data-Name: (optional) File name, maybe. X-Sun-Data-Description: (optional) Longer text. X-Sun-Content-Lines: (manditory, unless Length is present) Number of lines in the body, not counting headers and the blank line that follows them. X-Sun-Content-Length: (manditory, unless Lines is present) Bytes, presumably using Unix line terminators. */ class MimeXSunAttachment extends MimeMultipart { public MimeXSunAttachment(String content_type, InternetHeaders headers) { super(content_type, headers); } protected void computeBoundary() { // handled in checkBoundary(); } protected int checkBoundary(ByteBuf line_buf) { if (line_buf.length() <= 10) return BOUNDARY_NONE; else { byte line[] = line_buf.toBytes(); if (line[0] == '-' && line[1] == '-' && line[2] == '-' && line[3] == '-' && line[4] == '-' && line[5] == '-' && line[6] == '-' && line[7] == '-' && line[8] == '-' && line[9] == '-' && (line[10] == '\r' || line[10] == '\n')) return BOUNDARY_SEPARATOR; else return BOUNDARY_NONE; } } protected void createMultipartChild() { child_headers = translateSunHeaders(child_headers); super.createMultipartChild(); } private String getHeader(InternetHeaders ih, String name) { String h[] = ih.getHeader(name); if (h == null || h.length == 0) return null; else return h[0]; } /** Translates x-sun-attachment sub-headers to legal MIME headers. */ InternetHeaders translateSunHeaders(InternetHeaders in) { InternetHeaders out = new InternetHeaders(); String dt = getHeader(in, "X-Sun-Data-Type"); String cs = getHeader(in, "X-Sun-Charset"); String ei = getHeader(in, "X-Sun-Encoding-Info"); String dn = getHeader(in, "X-Sun-Data-Name"); String dd = getHeader(in, "X-Sun-Data-Description"); if (dn != null && dd != null && dn.equalsIgnoreCase(dd)) dd = null; // you losers. if (dt != null && dd != null && dt.equalsIgnoreCase(dd)) dd = null; // you complete losers. if (dt != null && dn != null && dt.equalsIgnoreCase(dn)) dn = null; // you complete and utter losers. String mime_type = null; String mime_enc = null; /* These are the magic types used by MailTool that I can determine. The only actual written spec I've found only listed the first few. The rest were found by inspection (both of real-world messages, and by running `strings' on the MailTool binary, and on the file /usr/openwin/lib/cetables/cetables (the "Class Engine", Sun's equivalent to .mailcap and mime.types.) */ String sun_type_table[][] = { { "default", "text/plain" }, { "default-doc", "text/plain" }, { "text", "text/plain" }, { "scribe", "text/plain" }, { "sgml", "text/plain" }, { "tex", "text/plain" }, { "troff", "text/plain" }, { "c-file", "text/plain" }, { "h-file", "text/plain" }, { "readme-file", "text/plain" }, { "shell-script", "text/plain" }, { "cshell-script", "text/plain" }, { "makefile", "text/plain" }, { "hidden-docs", "text/plain" }, { "message", "message/rfc822" }, { "mail-message", "message/rfc822" }, { "mail-file", "text/plain" }, { "gif-file", "image/gif" }, { "jpeg-file", "image/jpg" }, { "ppm-file", "image/ppm" }, { "pgm-file", "image/x-portable-graymap" }, { "pbm-file", "image/x-portable-bitmap" }, { "xpm-file", "image/x-xpixmap" }, { "ilbm-file", "image/ilbm" }, { "tiff-file", "image/tiff" }, { "photocd-file", "image/x-photo-cd" }, { "sun-raster", "image/x-sun-raster" }, { "audio-file", "audio/basic" }, { "postscript", "application/postscript" }, { "postscript-file", "application/postscript" }, { "framemaker-document", "application/x-framemaker" }, { "sundraw-document", "application/x-sun-draw" }, { "sunpaint-document", "application/x-sun-paint" }, { "sunwrite-document", "application/x-sun-write" }, { "islanddraw-document", "application/x-island-draw" }, { "islandpaint-document", "application/x-island-paint" }, { "islandwrite-document", "application/x-island-write" }, { "sun-executable", "application/octet-stream" }, { "default-app", "application/octet-stream" }, { "compress", "application/x-compress" }, { "default-compress", "application/x-compress" }, { "gzip", "application/x-gzip" }, { "uuencode", "application/x-uuencode" }, }; for (int i = 0; i < sun_type_table.length; i++) { if (dt.equalsIgnoreCase(sun_type_table[i][0])) { mime_type = sun_type_table[i][1]; break; } } /* Translate x-sun-attachment sub-part encodings to legal MIME encodings. However, if the X-Sun-Encoding-Info field contains more than one encoding (that is, contains a comma) then assign it the encoding of the *rightmost* element in the list; and change its Content-Type to application/octet-stream. Examples: Sun Type: Translates To: ================== ==================== type: TEXT type: text/plain encoding: COMPRESS encoding: x-compress type: POSTSCRIPT type: application/x-compress encoding: COMPRESS,UUENCODE encoding: x-uuencode type: TEXT type: application/octet-stream encoding: UNKNOWN,UUENCODE encoding: x-uuencode */ if (ei != null) { StringTokenizer st = new StringTokenizer(ei, " ,", false); String last_enc = null; String butlast_enc = null; while (st.hasMoreTokens()) { String t = st.nextToken(); if (t.equalsIgnoreCase("adpcm-compress")) { // this "adpcm-compress" pseudo-encoding is some random junk that // MailTool adds to the encoding description of .AU files: we can // ignore it if it is the leftmost element of the encoding field. // (It looks like it's created via `audioconvert -f g721'. Why? // Who knows.) Ignore it. } else { butlast_enc = last_enc; last_enc = t; } } if (butlast_enc != null) { if (butlast_enc.equalsIgnoreCase("uuencode")) mime_type = "application/x-uuencode"; else if (butlast_enc.equalsIgnoreCase("gzip")) mime_type = "application/x-gzip"; else if (butlast_enc.equalsIgnoreCase("compress") || butlast_enc.equalsIgnoreCase("default-compress")) mime_type = "application/x-compress"; else mime_type = "application/octet-stream"; } if (last_enc != null) { if (last_enc.equalsIgnoreCase("uuencode")) { mime_enc = "x-uuencode"; } else { mime_type = "application/octet-stream"; } } } if (mime_type == null) { mime_type = "application/octet-stream"; if (mime_enc == null && dt != null && dt.equalsIgnoreCase("uuencode")) mime_enc = "x-uuencode"; } if (cs != null && mime_type.startsWith("text/")) mime_type += "; charset=\"" + cs + "\""; out.addHeaderLine("Content-Type: " + mime_type + "\r\n"); if (mime_enc != null) out.addHeaderLine("Content-Transfer-Encoding: " + mime_enc + "\r\n"); if (dn != null) out.addHeaderLine("Content-Disposition: inline; filename=\"" + dn + "\"\r\n"); if (dd != null) out.addHeaderLine("Content-Description: " + dd + "\r\n"); return out; } }