001package com.hfg.util.mime;
002
003import com.hfg.exception.ProgrammingException;
004import com.hfg.util.FileUtil;
005import com.hfg.util.StringBuilderPlus;
006import com.hfg.util.StringUtil;
007import com.hfg.util.io.FileBytes;
008
009import java.io.File;
010import java.io.IOException;
011import java.io.OutputStream;
012import java.util.Base64;
013
014//------------------------------------------------------------------------------
015/**
016 MIME Entity.
017 @author J. Alex Taylor, hairyfatguy.com
018 */
019//------------------------------------------------------------------------------
020// com.hfg XML/HTML Coding Library
021//
022// This library is free software; you can redistribute it and/or
023// modify it under the terms of the GNU Lesser General Public
024// License as published by the Free Software Foundation; either
025// version 2.1 of the License, or (at your option) any later version.
026//
027// This library is distributed in the hope that it will be useful,
028// but WITHOUT ANY WARRANTY; without even the implied warranty of
029// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
030// Lesser General Public License for more details.
031//
032// You should have received a copy of the GNU Lesser General Public
033// License along with this library; if not, write to the Free Software
034// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
035//
036// J. Alex Taylor, President, Founder, CEO, COO, CFO, OOPS hairyfatguy.com
037// jataylor@hairyfatguy.com
038//------------------------------------------------------------------------------
039
040public class MimeEntity
041{
042   //##########################################################################
043   // PRIVATE FIELDS
044   //##########################################################################
045
046   private MimeContentDisposition mContentDisposition = new MimeContentDisposition().setType("form-data");
047   private MimeType  mContentType;
048   private String    mContentTransferEncoding;
049   private String    mContent;
050
051   // File data could take several forms. We won't convert them until actually generating the output.
052   private File      mFile;
053   private FileBytes mFileBytes;
054
055   private File      mCachedContentFile;
056
057   private static final int MAX_ENCODING_LINE_LENGTH = 76;
058   private static final String CRLF = "\r\n";
059
060   private static final String CONTENT_TYPE = "Content-Type";
061   private static final String CONTENT_TRANSFER_ENCODING = "Content-Transfer-Encoding";
062
063   private static final MimeType DEFAULT_FILE_CONTENT_TYPE = MimeType.APPLICATION_OCTET_STREAM;
064
065   //##########################################################################
066   // CONSTRUCTORS
067   //##########################################################################
068
069   //--------------------------------------------------------------------------
070   public MimeEntity()
071   {
072
073   }
074
075   //--------------------------------------------------------------------------
076   public MimeEntity(String inName, File inValue)
077   {
078      getContentDisposition()
079            .setName(inName)
080            .setFilename(inValue.getName());
081
082      mFile = inValue;
083   }
084
085   //--------------------------------------------------------------------------
086   public MimeEntity(String inName, FileBytes inValue)
087   {
088      getContentDisposition()
089            .setName(inName)
090            .setFilename(inValue.name());
091
092      mFileBytes = inValue;
093   }
094
095   //--------------------------------------------------------------------------
096   public MimeEntity(String inName, Object inValue)
097   {
098      getContentDisposition()
099            .setName(inName);
100
101      if (inValue != null)
102      {
103         if (inValue instanceof File)
104         {
105            mContentDisposition.setFilename(((File)inValue).getName());
106            mFile = (File) inValue;
107         }
108         else if (inValue instanceof FileBytes)
109         {
110            mContentDisposition.setFilename(((FileBytes)inValue).name());
111            mFileBytes = (FileBytes) inValue;
112         }
113         else
114         {
115            mContent = inValue.toString();
116         }
117      }
118   }
119
120   //##########################################################################
121   // PUBLIC METHODS
122   //##########################################################################
123
124   //--------------------------------------------------------------------------
125   public MimeContentDisposition getContentDisposition()
126   {
127      return mContentDisposition;
128   }
129
130   //--------------------------------------------------------------------------
131   public void setContentDisposition(MimeContentDisposition inValue)
132   {
133      mContentDisposition = inValue;
134   }
135
136
137   //--------------------------------------------------------------------------
138   public MimeType getContentType()
139   {
140      if (null == mContentType
141          && (mFile != null || mFileBytes != null))
142      {
143         setContentType(DEFAULT_FILE_CONTENT_TYPE);
144      }
145
146      return mContentType;
147   }
148
149   //--------------------------------------------------------------------------
150   public void setContentType(MimeType inValue)
151   {
152      mContentType = inValue;
153   }
154
155
156   //--------------------------------------------------------------------------
157   public String getContentTransferEncoding()
158   {
159      return mContentTransferEncoding;
160   }
161
162   //--------------------------------------------------------------------------
163   public void setContentTransferEncoding(String inValue)
164   {
165      mContentTransferEncoding = inValue;
166   }
167
168
169   //--------------------------------------------------------------------------
170   public void setContent(String inValue)
171         throws IOException
172   {
173      if (mCachedContentFile != null)
174      {
175         FileUtil.write(mCachedContentFile, inValue);
176         mContent = null;
177      }
178      else
179      {
180         mContent = inValue;
181      }
182   }
183
184   //--------------------------------------------------------------------------
185   public String getContent()
186   {
187      return mContent;
188   }
189
190   //--------------------------------------------------------------------------
191   public File getCachedContentFile()
192   {
193      return mCachedContentFile;
194   }
195
196   //--------------------------------------------------------------------------
197   /**
198    Sets the file in which the entity's content will be cached.
199    */
200   public void setCachedContentFile(File inValue)
201         throws IOException
202   {
203      mCachedContentFile = inValue;
204      if (mCachedContentFile != null)
205      {
206/*
207         if (mCachedContentFile.exists())
208         {
209            if (!mCachedContentFile.delete())
210            {
211               throw new IOException("The preexisting cache file '" + mCachedContentFile + "' couldn't be deleted!");
212            }
213         }
214*/
215         if (!mCachedContentFile.exists()
216             && !mCachedContentFile.createNewFile())
217         {
218            throw new IOException("The cache file '" + mCachedContentFile + "' couldn't be created!");
219         }
220      }
221   }
222
223   //--------------------------------------------------------------------------
224   public void write(OutputStream inStream)
225      throws IOException
226   {
227      inStream.write(getContentDisposition().toString().getBytes());
228
229
230      if (getContentType() != null)
231      {
232         inStream.write(CRLF.getBytes());
233         inStream.write(CONTENT_TYPE.getBytes());
234         inStream.write(": ".getBytes());
235         inStream.write(getContentType().toString().getBytes());
236      }
237
238      if (getContentTransferEncoding() != null)
239      {
240         inStream.write(CRLF.getBytes());
241         inStream.write(CONTENT_TRANSFER_ENCODING.getBytes());
242         inStream.write(": ".getBytes());
243         inStream.write(getContentTransferEncoding().getBytes());
244      }
245
246      inStream.write(CRLF.getBytes());
247      inStream.write(CRLF.getBytes());
248
249      byte[] content = generateContent();
250      if (content != null)
251      {
252         inStream.write(content);
253      }
254
255      inStream.write(CRLF.getBytes());
256   }
257
258   //##########################################################################
259   // PRIVATE METHODS
260   //##########################################################################
261
262   //--------------------------------------------------------------------------
263   private byte[] generateContent()
264      throws IOException
265   {
266      byte[] content = null;
267
268      if (mFile != null)
269      {
270         content = getContentFromFile(mFile);
271      }
272      else if (mFileBytes != null)
273      {
274         content = getContentFromFileBytes(mFileBytes);
275      }
276      else if (getContent() != null)
277      {
278         content = getContent().getBytes();
279      }
280
281      return content;
282   }
283
284
285   //--------------------------------------------------------------------------
286   private byte[] getContentFromFileBytes(FileBytes inValue)
287         throws IOException
288   {
289      byte[] content = null;
290      if (inValue != null)
291      {
292         content = getContentFromBytes(inValue.getBytes());
293      }
294
295      return content;
296   }
297
298   //--------------------------------------------------------------------------
299   private byte[] getContentFromFile(File inValue)
300         throws IOException
301   {
302      byte[] content = null;
303      if (inValue != null)
304      {
305         content = getContentFromBytes(FileUtil.read(inValue).toString().getBytes());
306      }
307
308      return content;
309   }
310
311   //--------------------------------------------------------------------------
312   private byte[] getContentFromBytes(byte[] inValue)
313   {
314      byte[] bytes = inValue;
315      if (getContentTransferEncoding() != null)
316      {
317         if (getContentTransferEncoding().equalsIgnoreCase("base64"))
318         {
319            String encodedString = Base64.getEncoder().encodeToString(inValue);
320            StringBuilderPlus buffer = new StringBuilderPlus().setDelimiter(CRLF);
321            int i = 0;
322            while (i < encodedString.length())
323            {
324               int limit = i + MAX_ENCODING_LINE_LENGTH;
325               if (limit > encodedString.length())
326               {
327                  limit = encodedString.length();
328               }
329
330               buffer.delimitedAppend(encodedString.substring(i, limit));
331
332               i = limit;
333            }
334
335            bytes = buffer.toString().getBytes();
336         }
337         else
338         {
339            throw new ProgrammingException(StringUtil.singleQuote(getContentTransferEncoding())
340                                           + " is not a currently supported transfer encoding!");
341         }
342      }
343
344      return bytes;
345   }
346
347}