1 /*
   2  * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package com.sun.xml.internal.org.jvnet.mimepull;
  27 
  28 import java.io.File;
  29 import java.io.InputStream;
  30 import java.nio.ByteBuffer;
  31 import java.util.List;
  32 
  33 /**
  34  * Represents an attachment part in a MIME message. MIME message parsing is done
  35  * lazily using a pull parser, so the part may not have all the data. {@link #read}
  36  * and {@link #readOnce} may trigger the actual parsing the message. In fact,
  37  * parsing of an attachment part may be triggered by calling {@link #read} methods
  38  * on some other attachemnt parts. All this happens behind the scenes so the
  39  * application developer need not worry about these details.
  40  *
  41  * @author Jitendra Kotamraju
  42  */
  43 public class MIMEPart {
  44 
  45     private volatile InternetHeaders headers;
  46     private volatile String contentId;
  47     private String contentType;
  48     volatile boolean parsed;    // part is parsed or not
  49     final MIMEMessage msg;
  50     private final DataHead dataHead;
  51 
  52     MIMEPart(MIMEMessage msg) {
  53         this.msg = msg;
  54         this.dataHead = new DataHead(this);
  55     }
  56 
  57     MIMEPart(MIMEMessage msg, String contentId) {
  58         this(msg);
  59         this.contentId = contentId;
  60     }
  61 
  62     /**
  63      * Can get the attachment part's content multiple times. That means
  64      * the full content needs to be there in memory or on the file system.
  65      * Calling this method would trigger parsing for the part's data. So
  66      * do not call this unless it is required(otherwise, just wrap MIMEPart
  67      * into a object that returns InputStream for e.g DataHandler)
  68      *
  69      * @return data for the part's content
  70      */
  71     public InputStream read() {
  72         return dataHead.read();
  73     }
  74 
  75     /**
  76      * Cleans up any resources that are held by this part (for e.g. deletes
  77      * the temp file that is used to serve this part's content). After
  78      * calling this, one shouldn't call {@link #read()} or {@link #readOnce()}
  79      */
  80     public void close() {
  81         dataHead.close();
  82     }
  83 
  84 
  85     /**
  86      * Can get the attachment part's content only once. The content
  87      * will be lost after the method. Content data is not be stored
  88      * on the file system or is not kept in the memory for the
  89      * following case:
  90      *   - Attachement parts contents are accessed sequentially
  91      *
  92      * In general, take advantage of this when the data is used only
  93      * once.
  94      *
  95      * @return data for the part's content
  96      */
  97     public InputStream readOnce() {
  98         return dataHead.readOnce();
  99     }
 100 
 101     public void moveTo(File f) {
 102         dataHead.moveTo(f);
 103     }
 104 
 105     /**
 106      * Returns Content-ID MIME header for this attachment part
 107      *
 108      * @return Content-ID of the part
 109      */
 110     public String getContentId() {
 111         if (contentId == null) {
 112             getHeaders();
 113         }
 114         return contentId;
 115     }
 116 
 117     /**
 118      * Returns Content-Type MIME header for this attachment part
 119      *
 120      * @return Content-Type of the part
 121      */
 122     public String getContentType() {
 123         if (contentType == null) {
 124             getHeaders();
 125         }
 126         return contentType;
 127     }
 128 
 129     private void getHeaders() {
 130         // Trigger parsing for the part headers
 131         while(headers == null) {
 132             if (!msg.makeProgress()) {
 133                 if (headers == null) {
 134                     throw new IllegalStateException("Internal Error. Didn't get Headers even after complete parsing.");
 135                 }
 136             }
 137         }
 138     }
 139 
 140     /**
 141      * Return all the values for the specified header.
 142      * Returns <code>null</code> if no headers with the
 143      * specified name exist.
 144      *
 145      * @param   name header name
 146      * @return  list of header values, or null if none
 147      */
 148     public List<String> getHeader(String name) {
 149         getHeaders();
 150         assert headers != null;
 151         return headers.getHeader(name);
 152     }
 153 
 154     /**
 155      * Return all the headers
 156      *
 157      * @return list of Header objects
 158      */
 159     public List<? extends Header> getAllHeaders() {
 160         getHeaders();
 161         assert headers != null;
 162         return headers.getAllHeaders();
 163     }
 164 
 165     /**
 166      * Callback to set headers
 167      *
 168      * @param headers MIME headers for the part
 169      */
 170     void setHeaders(InternetHeaders headers) {
 171         this.headers = headers;
 172         List<String> ct = getHeader("Content-Type");
 173         this.contentType = (ct == null) ? "application/octet-stream" : ct.get(0);
 174     }
 175 
 176     /**
 177      * Callback to notify that there is a partial content for the part
 178      *
 179      * @param buf content data for the part
 180      */
 181     void addBody(ByteBuffer buf) {
 182         dataHead.addBody(buf);
 183     }
 184 
 185     /**
 186      * Callback to indicate that parsing is done for this part
 187      * (no more update events for this part)
 188      */
 189     void doneParsing() {
 190         parsed = true;
 191         dataHead.doneParsing();
 192     }
 193 
 194     /**
 195      * Callback to set Content-ID for this part
 196      * @param cid Content-ID of the part
 197      */
 198     void setContentId(String cid) {
 199         this.contentId = cid;
 200     }
 201 
 202     @Override
 203     public String toString() {
 204         return "Part="+contentId;
 205     }
 206 
 207 }