Back to index

lightning-sunbird  0.9+nobinonly
mimepbuf.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either of the GNU General Public License Version 2 or later (the "GPL"),
00026  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 #include "nsCOMPtr.h"
00038 #include "mimepbuf.h"
00039 #include "mimemoz2.h"
00040 #include "prmem.h"
00041 #include "prio.h"
00042 #include "plstr.h"
00043 #include "nsCRT.h"
00044 #include "nsMimeStringResources.h"
00045 #include "nsFileSpec.h"
00046 #include "nsFileStream.h"
00047 
00048 //
00049 // External Defines...
00050 //
00051 extern nsFileSpec *nsMsgCreateTempFileSpec(const char *tFileName);
00052 
00053 /* See mimepbuf.h for a description of the mission of this file.
00054 
00055    Implementation:
00056 
00057      When asked to buffer an object, we first try to malloc() a buffer to
00058         hold the upcoming part.  First we try to allocate a 50k buffer, and
00059         then back off by 5k until we are able to complete the allocation,
00060         or are unable to allocate anything.
00061 
00062         As data is handed to us, we store it in the memory buffer, until the
00063         size of the memory buffer is exceeded (including the case where no
00064         memory buffer was able to be allocated at all.)
00065 
00066         Once we've filled the memory buffer, we open a temp file on disk.
00067         Anything that is currently in the memory buffer is then flushed out
00068         to the disk file (and the memory buffer is discarded.)  Subsequent
00069         data that is passed in is appended to the file.
00070 
00071         Thus only one of the memory buffer or the disk buffer ever exist at
00072         the same time; and small parts tend to live completely in memory
00073         while large parts tend to live on disk.
00074 
00075         When we are asked to read the data back out of the buffer, we call
00076         the provided read-function with either: the contents of the memory
00077         buffer; or blocks read from the disk file.
00078  */
00079 
00080 #define TARGET_MEMORY_BUFFER_SIZE    (1024 * 50)  /* try for 50k mem buffer */
00081 #define TARGET_MEMORY_BUFFER_QUANTUM (1024 * 5)   /* decrease in steps of 5k */
00082 #define DISK_BUFFER_SIZE                   (1024 * 10)  /* read disk in 10k chunks */
00083 
00084 
00085 struct MimePartBufferData
00086 {
00087   char        *part_buffer;                        /* Buffer used for part-lookahead. */
00088   PRInt32     part_buffer_fp;                           /* Active length. */
00089   PRInt32     part_buffer_size;                  /* How big it is. */
00090 
00091        nsFileSpec          *file_buffer_spec;           /* The nsFileSpec of a temp file used when we
00092                                                                                        run out of room in the head_buffer. */
00093        nsInputFileStream   *input_file_stream;          /* A stream to it. */
00094        nsOutputFileStream  *output_file_stream;  /* A stream to it. */
00095 };
00096 
00097 MimePartBufferData *
00098 MimePartBufferCreate (void)
00099 {
00100   MimePartBufferData *data = PR_NEW(MimePartBufferData);
00101   if (!data) return 0;
00102   memset(data, 0, sizeof(*data));
00103   return data;
00104 }
00105 
00106 
00107 void
00108 MimePartBufferClose (MimePartBufferData *data)
00109 {
00110   NS_ASSERTION(data, "MimePartBufferClose: no data");
00111   if (!data) return;
00112 
00113        if (data->input_file_stream) 
00114   {
00115               data->input_file_stream->close();
00116     delete data->input_file_stream;
00117     data->input_file_stream = nsnull;
00118        }
00119 
00120   if (data->output_file_stream) 
00121   {
00122               data->output_file_stream->close();
00123     delete data->output_file_stream;
00124     data->output_file_stream = nsnull;
00125        }
00126 }
00127 
00128 
00129 void
00130 MimePartBufferReset (MimePartBufferData *data)
00131 {
00132   NS_ASSERTION(data, "MimePartBufferReset: no data");
00133   if (!data) return;
00134 
00135   PR_FREEIF(data->part_buffer);
00136   data->part_buffer_fp = 0;
00137 
00138        if (data->input_file_stream) 
00139   {
00140               data->input_file_stream->close();
00141     delete data->input_file_stream;
00142     data->input_file_stream = nsnull;
00143        }
00144 
00145   if (data->output_file_stream) 
00146   {
00147               data->output_file_stream->close();
00148     delete data->output_file_stream;
00149     data->output_file_stream = nsnull;
00150        }
00151 
00152        if (data->file_buffer_spec) 
00153   {
00154     data->file_buffer_spec->Delete(PR_FALSE);
00155     delete data->file_buffer_spec;
00156     data->file_buffer_spec = nsnull;
00157        }
00158 }
00159 
00160 
00161 void
00162 MimePartBufferDestroy (MimePartBufferData *data)
00163 {
00164   NS_ASSERTION(data, "MimePartBufferDestroy: no data");
00165   if (!data) return;
00166   MimePartBufferReset (data);
00167   PR_Free(data);
00168 }
00169 
00170 
00171 int
00172 MimePartBufferWrite (MimePartBufferData *data,
00173                                     const char *buf, PRInt32 size)
00174 {
00175   int status = 0;
00176 
00177   NS_ASSERTION(data && buf && size > 0, "MimePartBufferWrite: Bad param");
00178   if (!data || !buf || size <= 0)
00179        return -1;
00180 
00181   /* If we don't yet have a buffer (either memory or file) try and make a
00182         memory buffer.
00183    */
00184   if (!data->part_buffer &&
00185            !data->file_buffer_spec)
00186        {
00187          int target_size = TARGET_MEMORY_BUFFER_SIZE;
00188          while (target_size > 0)
00189               {
00190                 data->part_buffer = (char *) PR_MALLOC(target_size);
00191                 if (data->part_buffer) break;                                /* got it! */
00192                 target_size -= TARGET_MEMORY_BUFFER_QUANTUM;   /* decrease it and try
00193                                                                                                      again */
00194               }
00195 
00196          if (data->part_buffer)
00197               data->part_buffer_size = target_size;
00198          else
00199               data->part_buffer_size = 0;
00200 
00201          data->part_buffer_fp = 0;
00202        }
00203 
00204   /* Ok, if at this point we still don't have either kind of buffer, try and
00205         make a file buffer. */
00206   if (!data->part_buffer && !data->file_buffer_spec)
00207        {
00208     data->file_buffer_spec = nsMsgCreateTempFileSpec("nsma");
00209               if (!data->file_buffer_spec) 
00210       return MIME_OUT_OF_MEMORY;
00211 
00212     data->output_file_stream = new nsOutputFileStream(*(data->file_buffer_spec), PR_WRONLY | PR_CREATE_FILE, 00600);
00213               if (!data->output_file_stream) 
00214     {
00215                      return MIME_UNABLE_TO_OPEN_TMP_FILE;
00216               }
00217   }
00218 
00219   NS_ASSERTION(data->part_buffer || data->output_file_stream, "no part_buffer or file_stream");
00220 
00221   /* If this buf will fit in the memory buffer, put it there.
00222    */
00223   if (data->part_buffer &&
00224          data->part_buffer_fp + size < data->part_buffer_size)
00225        {
00226          memcpy(data->part_buffer + data->part_buffer_fp,
00227                 buf, size);
00228          data->part_buffer_fp += size;
00229        }
00230 
00231   /* Otherwise it won't fit; write it to the file instead. */
00232   else
00233        {
00234          /* If the file isn't open yet, open it, and dump the memory buffer
00235                to it. */
00236          if (!data->output_file_stream)
00237               {
00238                 if (!data->file_buffer_spec)
00239                      data->file_buffer_spec = nsMsgCreateTempFileSpec("nsma");
00240                 if (!data->file_buffer_spec) 
00241         return MIME_OUT_OF_MEMORY;
00242 
00243       data->output_file_stream = new nsOutputFileStream(*(data->file_buffer_spec), PR_WRONLY | PR_CREATE_FILE, 00600);
00244                 if (!data->output_file_stream) 
00245       {
00246                        return MIME_UNABLE_TO_OPEN_TMP_FILE;
00247                 }
00248 
00249                 if (data->part_buffer && data->part_buffer_fp)
00250                      {
00251                        status = data->output_file_stream->write(data->part_buffer,
00252                                                                        data->part_buffer_fp);
00253                        if (status < data->part_buffer_fp) 
00254           return MIME_OUT_OF_MEMORY;
00255                      }
00256 
00257                 PR_FREEIF(data->part_buffer);
00258                 data->part_buffer_fp = 0;
00259                 data->part_buffer_size = 0;
00260               }
00261 
00262          /* Dump this buf to the file. */
00263          status = data->output_file_stream->write (buf, size);
00264          if (status < size) 
00265       return MIME_OUT_OF_MEMORY;
00266        }
00267 
00268   return 0;
00269 }
00270 
00271 
00272 int
00273 MimePartBufferRead (MimePartBufferData *data,
00274                                    nsresult (*read_fn) (const char *buf, PRInt32 size, void *closure),
00275                                    void *closure)
00276 {
00277   int status = 0;
00278   NS_ASSERTION(data, "no data");
00279   if (!data) return -1;
00280 
00281   if (data->part_buffer)
00282        {
00283          // Read it out of memory.
00284          status = read_fn(data->part_buffer, data->part_buffer_fp, closure);
00285        }
00286   else if (data->file_buffer_spec)
00287        {
00288          /* Read it off disk.
00289           */
00290          char *buf;
00291          PRInt32 buf_size = DISK_BUFFER_SIZE;
00292 
00293          NS_ASSERTION(data->part_buffer_size == 0 && data->part_buffer_fp == 0, "buffer size is not null");
00294          NS_ASSERTION(data->file_buffer_spec, "no file buffer name");
00295          if (!data->file_buffer_spec) 
00296       return -1;
00297 
00298          buf = (char *) PR_MALLOC(buf_size);
00299          if (!buf) 
00300       return MIME_OUT_OF_MEMORY;
00301 
00302     // First, close the output file to open the input file!
00303     if (data->output_file_stream)
00304               data->output_file_stream->close();
00305 
00306               data->input_file_stream = new nsInputFileStream(*(data->file_buffer_spec));
00307               if (!data->input_file_stream) 
00308     {
00309                      PR_Free(buf);
00310                      return MIME_UNABLE_TO_OPEN_TMP_FILE;
00311               }
00312 
00313     while(1)
00314               {
00315                 PRInt32 rstatus = data->input_file_stream->read(buf, buf_size - 1);
00316                 if (rstatus <= 0)
00317                      {
00318                        status = rstatus;
00319                        break;
00320                      }
00321                 else
00322                      {
00323                        /* It would be really nice to be able to yield here, and let
00324                              some user events and other input sources get processed.
00325                              Oh well. */
00326 
00327                        status = read_fn (buf, rstatus, closure);
00328                        if (status < 0) break;
00329                      }
00330               }
00331          PR_Free(buf);
00332        }
00333 
00334   return 0;
00335 }