Back to index

lightning-sunbird  0.9+nobinonly
nsEudoraMailbox.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
00002  *
00003  * ***** BEGIN LICENSE BLOCK *****
00004  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00005  *
00006  * The contents of this file are subject to the Mozilla Public License Version
00007  * 1.1 (the "License"); you may not use this file except in compliance with
00008  * the License. You may obtain a copy of the License at
00009  * http://www.mozilla.org/MPL/
00010  *
00011  * Software distributed under the License is distributed on an "AS IS" basis,
00012  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00013  * for the specific language governing rights and limitations under the
00014  * License.
00015  *
00016  * The Original Code is mozilla.org Code.
00017  *
00018  * The Initial Developer of the Original Code is
00019  * Netscape Communications Corporation.
00020  * Portions created by the Initial Developer are Copyright (C) 1998
00021  * the Initial Developer. All Rights Reserved.
00022  *
00023  * Contributor(s):
00024  *   Pierre Phaneuf <pp@ludusdesign.com>
00025  *
00026  * Alternatively, the contents of this file may be used under the terms of
00027  * either of the GNU General Public License Version 2 or later (the "GPL"),
00028  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00029  * in which case the provisions of the GPL or the LGPL are applicable instead
00030  * of those above. If you wish to allow use of your version of this file only
00031  * under the terms of either the GPL or the LGPL, and not to allow others to
00032  * use your version of this file under the terms of the MPL, indicate your
00033  * decision by deleting the provisions above and replace them with the notice
00034  * and other provisions required by the GPL or the LGPL. If you do not delete
00035  * the provisions above, a recipient may use your version of this file under
00036  * the terms of any one of the MPL, the GPL or the LGPL.
00037  *
00038  * ***** END LICENSE BLOCK ***** */
00039 
00040 #include "nsCOMPtr.h"
00041 #include "nsReadableUtils.h"
00042 #include "nsEudoraMailbox.h"
00043 #include "nsSpecialSystemDirectory.h"
00044 #include "nsEudoraCompose.h"
00045 #include "nspr.h"
00046 
00047 #include "EudoraDebugLog.h"
00048 
00049 #define       kCopyBufferSize             8192
00050 #define       kMailReadBufferSize  16384
00051 #define DATE_STR_LEN      64      // 64 bytes is plenty to hold the date header.
00052 
00053 #define       kWhitespace   " \t\b\r\n"
00054 
00055 const char *eudoraFromLine = "From - Mon Jan 1 00:00:00 1965\x0D\x0A";
00056 
00057 #ifdef IMPORT_DEBUG
00058 void DUMP_FILENAME( nsIFileSpec *pSpec, PRBool endLine);
00059 
00060 void DUMP_FILENAME( nsIFileSpec *pSpec, PRBool endLine)
00061 {
00062        char *pPath = nsnull;
00063        if (pSpec)
00064               pSpec->GetNativePath( &pPath);
00065        if (pPath) {
00066               IMPORT_LOG1( "%s", pPath);
00067               nsCRT::free( pPath);
00068        }
00069        else {
00070               IMPORT_LOG0( "Unknown");
00071        }
00072        if (endLine) {
00073               IMPORT_LOG0( "\n");
00074        }
00075 }
00076 
00077 // #define    DONT_DELETE_EUDORA_TEMP_FILES             1
00078 
00079 #else
00080 #define DUMP_FILENAME( x, y)
00081 #endif
00082 
00083 static char *eudoraWeekDays[7] = {
00084        "Mon",
00085        "Tue",
00086        "Wed",
00087        "Thu",
00088        "Fri",
00089        "Sat",
00090        "Sun"
00091 };
00092 
00093 static char *eudoraMonths[12] = {
00094        "Jan",
00095        "Feb",
00096        "Mar",
00097        "Apr",
00098        "May",
00099        "Jun",
00100        "Jul",
00101        "Aug",
00102        "Sep",
00103        "Oct",
00104        "Nov",
00105        "Dec"
00106 };
00107 
00108 
00109 nsEudoraMailbox::nsEudoraMailbox()
00110 {
00111        m_fromLen = 0;
00112 }
00113 
00114 nsEudoraMailbox::~nsEudoraMailbox()
00115 {
00116        EmptyAttachments();
00117 }
00118 
00119 nsresult nsEudoraMailbox::CreateTempFile( nsIFileSpec **ppSpec)
00120 {
00121        *ppSpec = nsnull;
00122        
00123        nsSpecialSystemDirectory temp(nsSpecialSystemDirectory::OS_TemporaryDirectory);
00124        
00125        // nsSpecialSystemDirectory temp(nsSpecialSystemDirectory::Mac_DesktopDirectory);
00126        
00127     temp += "impmail.txt";
00128        temp.MakeUnique();
00129        nsresult rv = NS_NewFileSpecWithSpec( temp, ppSpec);
00130     if (NS_SUCCEEDED(rv)) {
00131               if (*ppSpec)
00132                      return( NS_OK);
00133               else
00134                      return( NS_ERROR_FAILURE);
00135        }
00136 
00137        return( rv);
00138 }
00139 
00140 nsresult nsEudoraMailbox::DeleteFile( nsIFileSpec *pSpec)
00141 {
00142        PRBool        result;
00143        nsresult      rv = NS_OK;
00144 
00145        result = PR_FALSE;
00146        pSpec->IsStreamOpen( &result);
00147        if (result)
00148               pSpec->CloseStream();
00149        result = PR_FALSE;
00150        pSpec->Exists( &result);
00151        if (result) {
00152               result = PR_FALSE;
00153               pSpec->IsFile( &result);
00154               if (result) {
00155                      nsFileSpec    spec;
00156                      rv = pSpec->GetFileSpec( &spec);
00157 #ifndef DONT_DELETE_EUDORA_TEMP_FILES
00158                      if (NS_SUCCEEDED( rv))
00159                             spec.Delete( PR_FALSE);
00160 #endif
00161               }
00162        }
00163 
00164        return( rv);
00165 }
00166 
00167 
00168 #define kComposeErrorStr    "X-Eudora-Compose-Error: *****" "\x0D\x0A"
00169 #define kHTMLTag "<html>"
00170 
00171 nsresult nsEudoraMailbox::ImportMailbox( PRUint32 *pBytes, PRBool *pAbort, const PRUnichar *pName, nsIFileSpec *pSrc, nsIFileSpec *pDst, PRInt32 *pMsgCount)
00172 {
00173        nsCOMPtr<nsIFileSpec>       tocFile;
00174        PRBool                             deleteToc = PR_FALSE;
00175        nsresult                           rv;
00176        nsCOMPtr<nsIFileSpec>       mailFile;
00177        PRBool                             deleteMailFile = PR_FALSE;
00178        PRUint32                           div = 1;
00179        PRUint32                           mul = 1;
00180 
00181        if (pMsgCount)
00182               *pMsgCount = 0;
00183 
00184        rv = pSrc->GetFileSize( &m_mailSize);
00185 
00186        rv = pSrc->OpenStreamForReading();
00187        if (NS_FAILED( rv))
00188               return( rv);
00189 
00190        NS_ADDREF( pSrc);
00191 
00192        // First, get the index file for this mailbox
00193        rv = FindTOCFile( pSrc, getter_AddRefs( tocFile), &deleteToc);
00194        if (NS_SUCCEEDED( rv) && tocFile) {
00195               IMPORT_LOG0( "Reading euroda toc file: ");
00196               DUMP_FILENAME( tocFile, PR_TRUE);
00197 
00198               rv = CreateTempFile( getter_AddRefs( mailFile));
00199               deleteMailFile = PR_TRUE;
00200               if (NS_SUCCEEDED( rv)) {
00201                      // Read the TOC and compact the mailbox file into a temp file
00202                      rv = tocFile->OpenStreamForReading();
00203                      if (NS_SUCCEEDED( rv)) {
00204                             rv = mailFile->OpenStreamForWriting();
00205                             if (NS_SUCCEEDED( rv)) {
00206                                    // Read the toc and compact the mailbox into mailFile
00207                                    rv = CompactMailbox( pBytes, pAbort, pSrc, tocFile, mailFile);
00208                                    div = 4;
00209                                    mul = 3;
00210                             }
00211                      }
00212               }
00213               
00214               pSrc->CloseStream();
00215 
00216               // clean up
00217               if (deleteToc) {
00218                      DeleteFile( tocFile);
00219               }
00220               if (NS_SUCCEEDED( rv))
00221                      rv = mailFile->CloseStream();
00222               if (NS_FAILED( rv)) {
00223                      DeleteFile( mailFile);
00224                      mailFile->FromFileSpec( pSrc);
00225                      deleteMailFile = PR_FALSE;
00226                      IMPORT_LOG0( "*** Error compacting mailbox.\n");
00227               }
00228               
00229               IMPORT_LOG0( "Compacted mailbox: "); DUMP_FILENAME( mailFile, PR_TRUE);
00230 
00231               rv = mailFile->OpenStreamForReading();
00232               if (NS_SUCCEEDED( rv)) {
00233                      pSrc->Release();
00234                      mailFile->QueryInterface( NS_GET_IID(nsIFileSpec), (void **)&pSrc);
00235 
00236                      IMPORT_LOG0( "Compacting mailbox was successful\n");
00237               }
00238               else {
00239                      DeleteFile( mailFile);
00240                      deleteMailFile = PR_FALSE;
00241                      IMPORT_LOG0( "*** Error accessing compacted mailbox\n");
00242               }
00243        }
00244        
00245        // So, where we are now, is we have the mail file to import in pSrc
00246        // It may need deleting if deleteMailFile is PR_TRUE.
00247        // pSrc must be Released before returning
00248        
00249        // The source file contains partially constructed mail messages,
00250        // and attachments.  We should first investigate if we can use the mailnews msgCompose
00251        // stuff to do the work for us.  If not we have to scan the mailboxes and do TONS
00252        // of work to properly reconstruct the message - Eudora is so nice that it strips things
00253        // like MIME headers, character encoding, and attachments - beautiful!
00254        
00255        rv = pSrc->GetFileSize( &m_mailSize);
00256 
00257        nsCOMPtr<nsIFileSpec>       compositionFile;
00258        SimpleBufferTonyRCopiedOnce               readBuffer;
00259        SimpleBufferTonyRCopiedOnce               headers;
00260        SimpleBufferTonyRCopiedOnce               body;
00261        SimpleBufferTonyRCopiedOnce               copy;
00262        PRInt32                                   written;
00263        nsCString                          fromLine(eudoraFromLine);
00264        
00265        headers.m_convertCRs = PR_TRUE;
00266        body.m_convertCRs = PR_TRUE;
00267        
00268        copy.Allocate( kCopyBufferSize);
00269        readBuffer.Allocate( kMailReadBufferSize);
00270        ReadFileState               state;
00271        state.offset = 0;
00272        state.size = m_mailSize;
00273        state.pFile = pSrc;
00274 
00275        IMPORT_LOG0( "Reading mailbox\n");
00276 
00277        if (NS_SUCCEEDED( rv = NS_NewFileSpec( getter_AddRefs( compositionFile)))) {
00278               nsEudoraCompose             compose;
00279     nsCString defaultDate;
00280     nsCAutoString bodyType;
00281               
00282               /*
00283               IMPORT_LOG0( "Calling compose.SendMessage\n");
00284               rv = compose.SendMessage( compositionFile);
00285               if (NS_SUCCEEDED( rv)) {
00286                      IMPORT_LOG0( "Composed message in file: "); DUMP_FILENAME( compositionFile, PR_TRUE);
00287               }
00288               else {
00289                      IMPORT_LOG0( "*** Error composing message\n");
00290               }
00291               */
00292 
00293               IMPORT_LOG0( "Reading first message\n");
00294 
00295     while (!*pAbort && NS_SUCCEEDED( rv = ReadNextMessage( &state, readBuffer, headers, body, defaultDate, bodyType))) {
00296                      
00297                      if (pBytes) {
00298                             *pBytes += (((body.m_writeOffset - 1 + headers.m_writeOffset - 1) / div) * mul);
00299                      }
00300 
00301       // Unfortunately Eudora stores HTML messages in the sent folder
00302       // without any content type header at all. If the first line of the message body is <html>
00303       // then mark the message as html internally...See Bug #258489
00304       if (body.m_pBuffer && (body.m_writeOffset > strlen(kHTMLTag)) && (strncmp(body.m_pBuffer, kHTMLTag, strlen(kHTMLTag)) == 0 ))
00305         bodyType = "text/html"; // ignore whatever body type we were given...force html
00306 
00307       compose.SetBody( body.m_pBuffer, body.m_writeOffset - 1, bodyType);
00308                      compose.SetHeaders( headers.m_pBuffer, headers.m_writeOffset - 1);
00309                      compose.SetAttachments( &m_attachments);
00310       compose.SetDefaultDate(defaultDate);
00311 
00312                      rv = compose.SendTheMessage( compositionFile);
00313                      if (NS_SUCCEEDED( rv)) {
00314                             /* IMPORT_LOG0( "Composed message in file: "); DUMP_FILENAME( compositionFile, PR_TRUE); */
00315                             // copy the resulting file into the destination file!
00316                             rv = compose.CopyComposedMessage( fromLine, compositionFile, pDst, copy);
00317                             DeleteFile( compositionFile);
00318                             if (NS_FAILED( rv)) {
00319                                    IMPORT_LOG0( "*** Error copying composed message to destination mailbox\n");
00320                                    break;
00321                             }
00322                             if (pMsgCount)
00323                                    (*pMsgCount)++;
00324                      }
00325                      else {
00326                             IMPORT_LOG0( "*** Error composing message, writing raw message\n");
00327                             rv = WriteFromSep( pDst);
00328                             
00329                             rv = pDst->Write( kComposeErrorStr,
00330                                                                strlen( kComposeErrorStr),
00331                                                                &written );
00332                             
00333                             if (NS_SUCCEEDED( rv))
00334                                    rv = pDst->Write( headers.m_pBuffer, headers.m_writeOffset - 1, &written);
00335                             if (NS_SUCCEEDED( rv) && (written == (headers.m_writeOffset - 1)))
00336                                    rv = pDst->Write( "\x0D\x0A" "\x0D\x0A", 4, &written);
00337                             if (NS_SUCCEEDED( rv) && (written == 4))
00338                                    rv = pDst->Write( body.m_pBuffer, body.m_writeOffset - 1, &written);
00339                             if (NS_SUCCEEDED( rv) && (written == (body.m_writeOffset - 1))) {
00340                                    rv = pDst->Write( "\x0D\x0A", 2, &written);
00341                                    if (written != 2)
00342                                           rv = NS_ERROR_FAILURE;
00343                             }
00344                             
00345                             if (NS_FAILED( rv)) {
00346                                    IMPORT_LOG0( "*** Error writing to destination mailbox\n");
00347                                    break;
00348                             }
00349                      }
00350 
00351                      if (!readBuffer.m_bytesInBuf && (state.offset >= state.size))
00352                             break;
00353               }
00354               
00355        }
00356        else {
00357               IMPORT_LOG0( "*** Error creating file spec for composition\n");
00358        }
00359 
00360        if (deleteMailFile)
00361               DeleteFile( pSrc);
00362        
00363        pSrc->Release();
00364 
00365        return( rv);
00366 }
00367 
00368 #if defined(XP_MAC) || defined(XP_MACOSX)
00369 #define kMsgHeaderSize             220
00370 #define       kMsgFirstOffset             278
00371 #else
00372 #define       kMsgHeaderSize              218
00373 #define kMsgFirstOffset            104
00374 #endif
00375 
00376 nsresult nsEudoraMailbox::CompactMailbox( PRUint32 *pBytes, PRBool *pAbort, nsIFileSpec *pMail, nsIFileSpec *pToc, nsIFileSpec *pDst)
00377 {
00378        PRUint32      mailSize = m_mailSize;
00379        PRUint32      tocSize = 0;
00380   PRUint32    msgCount = 0;
00381        nsresult      rv;
00382 
00383        rv = pToc->GetFileSize( &tocSize);
00384 
00385               // if the index or the mail file is empty then just
00386               // use the original mail file.
00387        if (!mailSize || !tocSize)
00388               return( NS_ERROR_FAILURE);
00389 
00390        SimpleBufferTonyRCopiedOnce copy;
00391        PRBool               done = PR_FALSE;
00392        PRInt32                     tocOffset = kMsgFirstOffset;
00393        PRInt32                     data[2];
00394        PRInt32                     count;
00395        PRInt32                     read;
00396        PRInt32                     written;
00397        char *               pBuffer;
00398        char                 lastChar;
00399 
00400        copy.Allocate( kCopyBufferSize);
00401 
00402        IMPORT_LOG0( "Compacting mailbox: ");
00403 
00404        while (!*pAbort && (tocOffset < (PRInt32)tocSize)) {
00405               if (NS_FAILED( rv = pToc->Seek( tocOffset))) return( rv);
00406               pBuffer = (char *)data;
00407               if (NS_FAILED( rv = pToc->Read( &pBuffer, 8, &read)) || (read != 8)) 
00408                      return( NS_ERROR_FAILURE);
00409               // data[0] is the message offset, data[1] is the message size
00410               if (NS_FAILED( rv = pMail->Seek( data[0]))) return( rv);
00411               count = kCopyBufferSize;
00412               if (count > data[1])
00413                      count = data[1];
00414               pBuffer = copy.m_pBuffer;
00415               if (NS_FAILED( rv = pMail->Read( &pBuffer, count, &read)) || (read != count))
00416                      return( NS_ERROR_FAILURE);
00417               if (count < 6)
00418                      return( NS_ERROR_FAILURE);
00419               if ((copy.m_pBuffer[0] != 'F') || (copy.m_pBuffer[1] != 'r') ||
00420                      (copy.m_pBuffer[2] != 'o') || (copy.m_pBuffer[3] != 'm') ||
00421                      (copy.m_pBuffer[4] != ' '))
00422                      return( NS_ERROR_FAILURE);
00423               
00424               if (pBytes)
00425                      *pBytes += (data[1] / 4);
00426               // Looks like everything is cool, we have a message that appears to start with a separator
00427               while (data[1]) {
00428                      lastChar = copy.m_pBuffer[count - 1];
00429                      if (NS_FAILED( rv = pDst->Write( copy.m_pBuffer, count, &written)) || (written != count))
00430                             return( NS_ERROR_FAILURE);
00431                      data[1] -= count;
00432                      if (data[1]) {
00433                             pBuffer = copy.m_pBuffer;
00434                             count = kCopyBufferSize;
00435                             if (count > data[1])
00436                                    count = data[1];
00437                             if (NS_FAILED( rv = pMail->Read( &pBuffer, count, &read)) || (read != count))
00438                                    return( NS_ERROR_FAILURE);
00439                      }
00440               }
00441               if ((lastChar != 0x0D) && (lastChar != 0x0A)) {
00442                      if (NS_FAILED( rv = pDst->Write( "\x0D\x0A", 2, &written)) || (written != 2))
00443                             return( rv);
00444               }
00445               
00446               IMPORT_LOG1( "  Message #%d processed.", ++msgCount);
00447 
00448               tocOffset += kMsgHeaderSize;
00449        }
00450 
00451        IMPORT_LOG0( " finished\n");
00452 
00453        return( NS_OK);
00454 }
00455 
00456 
00457 
00458 
00459 nsresult nsEudoraMailbox::ReadNextMessage( ReadFileState *pState, SimpleBufferTonyRCopiedOnce& copy, SimpleBufferTonyRCopiedOnce& header, SimpleBufferTonyRCopiedOnce& body, nsCString& defaultDate, nsCString& bodyType)
00460 {
00461        header.m_writeOffset = 0;
00462        body.m_writeOffset = 0;
00463        
00464        nsresult             rv;
00465        PRInt32                     lineLen;
00466        char                 endBuffer = 0;
00467 
00468        lineLen = -1;
00469        // Find the from separator - we should actually be positioned at the
00470        // from separator, but for now, we'll verify this.
00471        while (lineLen == -1) {
00472               if (NS_FAILED( rv = FillMailBuffer( pState, copy))) {
00473                      IMPORT_LOG0( "*** Error, FillMailBuffer FAILED in ReadNextMessage\n");
00474                      return( rv);
00475               }
00476               lineLen = IsEudoraFromSeparator( copy.m_pBuffer + copy.m_writeOffset, copy.m_bytesInBuf - copy.m_writeOffset, defaultDate);
00477        
00478               if (lineLen == -1) {
00479                      while ((lineLen = FindStartLine( copy)) == -1) {
00480                             copy.m_writeOffset = copy.m_bytesInBuf;
00481                             if (NS_FAILED( rv = FillMailBuffer( pState, copy))) {
00482                                    IMPORT_LOG0( "*** Error, FillMailBuffer FAILED in ReadNextMessage, looking for next start line\n");
00483                                    return( rv);
00484                             }
00485                             if (!copy.m_bytesInBuf) {
00486                                    IMPORT_LOG0( "*** Error, ReadNextMessage, looking for start of next line, got end of file.\n");
00487                                    return( NS_ERROR_FAILURE);
00488                             }
00489                      }
00490                      copy.m_writeOffset += lineLen;
00491                      lineLen = -1;
00492               }
00493        }
00494 
00495        // Skip past the from line separator
00496        while ((lineLen = FindStartLine( copy)) == -1) {
00497               copy.m_writeOffset = copy.m_bytesInBuf;
00498               if (NS_FAILED( rv = FillMailBuffer( pState, copy))) {
00499                      IMPORT_LOG0( "*** Error, ReadNextMessage, FillMailBuffer failed looking for from sep\n");
00500                      return( rv);
00501               }
00502               if (!copy.m_bytesInBuf) {
00503                      IMPORT_LOG0( "*** Error, ReadNextMessage, end of file looking for from sep\n");
00504                      return( NS_ERROR_FAILURE);
00505               }
00506        }
00507        copy.m_writeOffset += lineLen;
00508        if (NS_FAILED( rv = FillMailBuffer( pState, copy))) {
00509               IMPORT_LOG0( "*** Error, Unable to fill mail buffer after from sep.\n");
00510               return( rv);
00511        }
00512 
00513        // This should be the headers...
00514        PRInt32 endLen = -1;
00515        while ((endLen = IsEndHeaders( copy)) == -1) {
00516               while ((lineLen = FindNextEndLine( copy)) == -1) {
00517                      copy.m_writeOffset = copy.m_bytesInBuf;
00518                      if (!header.Write( copy.m_pBuffer, copy.m_writeOffset)) {
00519                             IMPORT_LOG0( "*** ERROR, writing headers\n");
00520                             return( NS_ERROR_FAILURE);
00521                      }
00522                      if (NS_FAILED( rv = FillMailBuffer( pState, copy))) {
00523                             IMPORT_LOG0( "*** Error reading message headers\n");
00524                             return( rv);
00525                      }
00526                      if (!copy.m_bytesInBuf) {
00527                             IMPORT_LOG0( "*** Error, end of file while reading headers\n");
00528                             return( NS_ERROR_FAILURE);
00529                      }
00530               }
00531               copy.m_writeOffset += lineLen;
00532               if ((copy.m_writeOffset + 4) >= copy.m_bytesInBuf) {
00533                      if (!header.Write( copy.m_pBuffer, copy.m_writeOffset)) {
00534                             IMPORT_LOG0( "*** ERROR, writing headers 2\n");
00535                             return( NS_ERROR_FAILURE);
00536                      }
00537                      if (NS_FAILED( rv = FillMailBuffer( pState, copy))) {
00538                             IMPORT_LOG0( "*** Error reading message headers 2\n");
00539                             return( rv);
00540                      }
00541               }
00542        }
00543 
00544        if (!header.Write( copy.m_pBuffer, copy.m_writeOffset)) {
00545               IMPORT_LOG0( "*** Error writing final headers\n");
00546               return( NS_ERROR_FAILURE);
00547        }
00548        if (!header.Write( &endBuffer, 1)) {
00549               IMPORT_LOG0( "*** Error writing header trailing null\n");
00550               return( NS_ERROR_FAILURE);
00551        }
00552 
00553 
00554        copy.m_writeOffset += endLen;
00555        if (NS_FAILED( rv = FillMailBuffer( pState, copy))) {
00556               IMPORT_LOG0( "*** Error reading beginning of message body\n");
00557               return( rv);
00558        }
00559        
00560        EmptyAttachments();
00561                             
00562        // Get the body!
00563        // Read one line at a time here and look for the next separator
00564   nsCString tmp;
00565   PRBool insideEudoraTags = PR_FALSE;
00566   // by default we consider the body text to be plain text
00567   bodyType = "text/plain";
00568 
00569        while ((lineLen = IsEudoraFromSeparator( copy.m_pBuffer + copy.m_writeOffset, copy.m_bytesInBuf - copy.m_writeOffset, tmp)) == -1) {
00570     PRInt32 tagLength = 0; 
00571               if (IsEudoraTag ( copy.m_pBuffer + copy.m_writeOffset, copy.m_bytesInBuf - copy.m_writeOffset, insideEudoraTags, bodyType, tagLength)) {
00572                      // We don't want to keep eudora tags so skip over them.
00573                      
00574                      // let's write the previous text
00575                      if (!body.Write( copy.m_pBuffer, copy.m_writeOffset)) {
00576                             IMPORT_LOG0( "*** Error writing to message body\n");
00577                             return( NS_ERROR_FAILURE);
00578                      }
00579 
00580       // we want to skip over the tag...for now we are assuming the tag is always at the start of line.
00581       copy.m_writeOffset += tagLength;
00582                             if (NS_FAILED( rv = FillMailBuffer( pState, copy))) {
00583                                    IMPORT_LOG0( "*** Error reading message body\n");
00584                                    return( rv);
00585                             }
00586                      
00587                      if (!copy.m_bytesInBuf)
00588                             break;
00589                                    
00590                      continue;
00591               }
00592 
00593               // Eudora Attachment lines are always outside Eudora Tags
00594               // so we shouldn't try to find one here
00595               if (!insideEudoraTags) {
00596               // Debatable is whether or not to exclude these lines from the
00597               // text of the message, I prefer not to in case the original
00598               // attachment is actually missing.
00599               rv = ExamineAttachment( copy);
00600               if (NS_FAILED( rv)) {
00601                      IMPORT_LOG0( "*** Error examining attachment line\n");
00602                      return( rv);
00603               }
00604               }
00605                      
00606                                    
00607               while (((lineLen = FindStartLine( copy)) == -1) && copy.m_bytesInBuf) {
00608                      copy.m_writeOffset = copy.m_bytesInBuf;
00609                      if (!body.Write( copy.m_pBuffer, copy.m_writeOffset)) {
00610                             IMPORT_LOG0( "*** Error writing to message body\n");
00611                             return( NS_ERROR_FAILURE);
00612                      }
00613                      if (NS_FAILED( rv = FillMailBuffer( pState, copy))) {
00614                             IMPORT_LOG0( "*** Error reading message body\n");
00615                             return( rv);
00616                      }
00617               }
00618               if (!copy.m_bytesInBuf)
00619                      break;
00620               
00621               copy.m_writeOffset += lineLen;
00622 
00623               // found the start of the next line
00624               // make sure it's long enough to check for the from line
00625               if ((copy.m_writeOffset + 2048) >= copy.m_bytesInBuf) {
00626                      if (!body.Write( copy.m_pBuffer, copy.m_writeOffset)) {
00627                             IMPORT_LOG0( "*** Error writing to message body 2\n");
00628                             return( NS_ERROR_FAILURE);
00629                      }
00630                      if (NS_FAILED( rv = FillMailBuffer( pState, copy))) {
00631                             IMPORT_LOG0( "*** Error reading message body 2\n");
00632                             return( rv);
00633                      }
00634               }
00635        }
00636        
00637        // the start of the current line is a from, we-re done
00638        if (!body.Write( copy.m_pBuffer, copy.m_writeOffset)) {
00639               IMPORT_LOG0( "*** Error writing final message body\n");
00640               return( NS_ERROR_FAILURE);
00641        }
00642        if (!body.Write( &endBuffer, 1)) {
00643               IMPORT_LOG0( "*** Error writing body trailing null\n");
00644               IMPORT_LOG2( "\tbody.m_size: %ld, body.m_writeOffset: %ld\n", body.m_size, body.m_writeOffset);
00645               return( NS_ERROR_FAILURE);
00646        }
00647        if (NS_FAILED( rv = FillMailBuffer( pState, copy))) {
00648               IMPORT_LOG0( "*** Error filling mail buffer for next read message\n");
00649               return( rv);
00650        }
00651        
00652        return( NS_OK);
00653 }
00654 
00655 
00656 
00657 PRInt32       nsEudoraMailbox::FindStartLine( SimpleBufferTonyRCopiedOnce& data)
00658 {
00659        PRInt32 len = data.m_bytesInBuf - data.m_writeOffset;
00660        if (!len)
00661               return( -1);
00662        PRInt32       count = 0;
00663        const char *pData = data.m_pBuffer + data.m_writeOffset;
00664        while ((count < len) && (*pData != 0x0D) && (*pData != 0x0A)) {
00665               pData++;
00666               count++;
00667        }
00668        if (count == len)
00669               return( -1);
00670 
00671        while ((count < len) && ((*pData == 0x0D) || (*pData == 0x0A))) {
00672               pData++;
00673               count++;
00674        }
00675        
00676        if (count < len)
00677               return( count);
00678 
00679        return( -1);
00680 }
00681 
00682 PRInt32 nsEudoraMailbox::FindNextEndLine( SimpleBufferTonyRCopiedOnce& data)
00683 {
00684        PRInt32 len = data.m_bytesInBuf - data.m_writeOffset;
00685        if (!len)
00686               return( -1);
00687        PRInt32       count = 0;
00688        const char *pData = data.m_pBuffer + data.m_writeOffset;
00689        while ((count < len) && ((*pData == 0x0D) || (*pData == 0x0A))) {
00690               pData++;
00691               count++;
00692        }
00693        while ((count < len) && (*pData != 0x0D) && (*pData != 0x0A)) {
00694               pData++;
00695               count++;
00696        }
00697        
00698        if (count < len)
00699               return( count);
00700 
00701        return( -1);
00702 }
00703 
00704 
00705 PRInt32 nsEudoraMailbox::IsEndHeaders( SimpleBufferTonyRCopiedOnce& data)
00706 {
00707        PRInt32 len = data.m_bytesInBuf - data.m_writeOffset;
00708        if (len < 2)
00709               return( -1);
00710        const char *pChar = data.m_pBuffer + data.m_writeOffset;
00711        if ((*pChar == 0x0D) && (*(pChar + 1) == 0x0D))
00712               return( 2);
00713 
00714        if (len < 4)
00715               return( -1);
00716        if ((*pChar == 0x0D) && (*(pChar + 1) == 0x0A) &&
00717               (*(pChar + 2) == 0x0D) && (*(pChar + 3) == 0x0A))
00718               return( 4);
00719 
00720        return( -1);
00721 }
00722 
00723 
00724 
00725 
00726 static char *eudoraTag[] = {
00727   "<x-html>",
00728   "</x-html>",
00729   "<x-rich>",
00730   "</x-rich>",
00731   "<x-flowed>",
00732   "</x-flowed>"
00733 };
00734 
00735 static PRInt32 eudoraTagLen[] = {
00736        8,
00737        9,
00738        8,
00739        9,
00740        10,
00741        11,
00742        0
00743 };
00744 
00745 
00746 static const char *TagContentType[] = {
00747        "text/html",
00748        "text/html",
00749        "text/enriched",
00750        "text/enriched",
00751        "text/plain",
00752        "text/plain",
00753 };
00754 
00755 
00756        // Determine if this line contains an eudora special tag
00757 PRBool nsEudoraMailbox::IsEudoraTag( const char *pChar, PRInt32 maxLen, PRBool &insideEudoraTags, nsCString &bodyType, PRInt32 &tagLength)
00758 {
00759        PRInt32       idx = 0;
00760        while ((tagLength = eudoraTagLen[idx]) != 0) {
00761               if (maxLen >= tagLength && !nsCRT::strncmp( eudoraTag[idx], pChar, tagLength)) {
00762                      insideEudoraTags = (pChar[1] != '/');
00763                      bodyType = TagContentType[idx];
00764                      return PR_TRUE;
00765               }
00766               idx++;
00767        }
00768 
00769        return PR_FALSE;
00770 }
00771 
00772        // Determine if this line meets Eudora standards for a separator line
00773        // This logic is based on Eudora 1.3.1's strict requirements for what
00774        // makes a valid separator line.  This may need to be relaxed for newer
00775        // versions of Eudora.
00776        // A sample from line: 
00777        // From john@uxc.cso.uiuc.edu Wed Jan 14 12:36:18 1989
00778 PRInt32       nsEudoraMailbox::IsEudoraFromSeparator( const char *pChar, PRInt32 maxLen, nsCString& defaultDate)
00779 {
00780        if (maxLen < 12)
00781               return( -1);
00782 
00783        PRInt32              len = 0;
00784        if ((*pChar != 'F') || (*(pChar + 1) != 'r') || (*(pChar + 2) != 'o') || (*(pChar + 3) != 'm'))
00785               return( -1);
00786        pChar += 4;
00787        len += 4;
00788 
00789        // According to Eudora the next char MUST be a space, and there can only be 1 space 
00790        // before the return mail address.
00791        // I'll be nicer and allow any amount of whitespace
00792        while (((*pChar == ' ') || (*pChar == '\t')) && (len < maxLen)) {
00793               pChar++;
00794               len++;
00795        }
00796        if (len == maxLen)
00797               return( -1);
00798        
00799        // Determine the length of the line
00800        PRInt32                     lineLen = len;
00801        const char *  pTok = pChar;
00802        while ((lineLen < maxLen) && (*pTok != 0x0D) && (*pTok != 0x0A)) {
00803               lineLen++;
00804               pTok++;
00805        }
00806 
00807        if (len >= lineLen)
00808               return( -1);
00809 
00810        // Eudora allows the return address to be double quoted or not at all..
00811        // I'll allow single or double quote, but other than that, just skip
00812        // the return address until you hit a space char (I allow tab as well)
00813        char   quote = *pChar;
00814        if ((quote == '"') || (quote == '\'')) {
00815               pChar++;
00816               len++;
00817               while ((len < lineLen) && (*pChar != quote)) {
00818                      pChar++;
00819                      len++;
00820               }
00821               if (len == lineLen)
00822                      return( -1);
00823               len++;
00824               pChar++;
00825        }
00826        else {
00827               while ((len < lineLen) && (*pChar != ' ') && (*pChar != '\t')) {
00828                      pChar++;
00829                      len++;
00830               }
00831        }
00832        while (((*pChar == ' ') || (*pChar == '\t')) && (len < lineLen)) {
00833               pChar++;
00834               len++;
00835        }
00836        if (len == lineLen)
00837               return( -1);
00838        
00839        // we've passed the address, now check for the remaining data  
00840        // Now it gets really funky!
00841        // In no particular order, with token separators space, tab, comma, newline
00842        // a - the phrase "remote from", remote must be first, from is optional.  2 froms or 2 remotes fails
00843        // b - one and only one time value xx:xx or xx:xx:xx
00844        // c - one and only one day, 1 to 31
00845        // d - one and only one year, 2 digit anything or 4 digit > 1900
00846        // e - one and only one weekday, 3 letter abreviation
00847        // f - one and only one month, 3 letter abreviation
00848        // 2 allowable "other" tokens
00849        // to be valid, day, year, month, & tym must exist and other must be less than 3
00850        
00851        int           day = 0;
00852        int           month = 0;
00853        int           year = 0;
00854        int           weekDay = 0;
00855        int           other = 0;
00856        int           result;
00857        char   tymStr[9];  // Make it a null terminated string (used in PR_snprintf() call()).
00858        PRBool tym = PR_FALSE;
00859        PRBool remote = PR_FALSE;
00860        PRBool from = PR_FALSE;
00861        PRInt32       tokLen;
00862        PRInt32       tokStart;
00863        PRInt32       num;
00864 
00865        while ((len < lineLen) && (other < 3)) {
00866               pTok = pChar;
00867               tokStart = len;
00868               while ((len < lineLen) && (*pChar != ' ') && (*pChar != '\t') && (*pChar != ',')) {
00869                      pChar++;
00870                      len++;
00871               }
00872               tokLen = len - tokStart;
00873               if (tokLen) {
00874                      num = AsciiToLong( pTok, tokLen);
00875                      if ((tokLen == 3) && ((result = IsWeekDayStr( pTok)) != 0)) {
00876                             if (weekDay)
00877                                    return( -1);
00878                             weekDay = result;
00879                      }             
00880                      else if ((tokLen == 3) && ((result = IsMonthStr( pTok)) != 0)) {
00881                             if (month)
00882                                    return( -1);
00883                             month = result;
00884                      }
00885                      else if ((tokLen == 6) && !nsCRT::strncasecmp( pTok, "remote", 6)) {
00886                             if (remote || from)
00887                                    return( -1);
00888                             remote = PR_TRUE;
00889                      } 
00890                      else if ((tokLen == 4) && !nsCRT::strncasecmp( pTok, "from", 4)) {
00891                             if (!remote || from)
00892                                    return( -1);
00893                             from = PR_TRUE;
00894                      }
00895                      else if ((tokLen == 4) && ((num > 1900) || !nsCRT::strncmp( pTok, "0000", 4))) {
00896                             if (year)
00897                                    return( -1);
00898                             year = (int)num;
00899                             if (!year)
00900                                    year = 1900;
00901                      }
00902                      else if (!year && day && (tokLen == 2) && (*(pTok + 1) >= '0') && (*(pTok + 1) <= '9')) {
00903                             if (num < 65)
00904                                    num += 1900;
00905                             else
00906                                    num += 2000;
00907                              year = (int) num;
00908                      }
00909                      else if ((tokLen <= 2) && (*pTok >= '0') && (*pTok <= '9')) {
00910                             day = (int) num;
00911                             if ((day < 1) || (day > 31))
00912                                    day = 1;
00913                      }
00914                      else if ((tokLen >= 5) && (pTok[2] == ':') && ((tokLen == 5) || ((tokLen == 8) && (pTok[5] == ':')))) {
00915                             // looks like the tym...
00916                             for (result = 0; result < (int)tokLen; result++) {
00917                                    if ((result != 2) && (result != 5)) {
00918                                           if ((pTok[result] < '0') || (pTok[result] > '9')) {
00919                                                  break;
00920                                           }
00921                                    }
00922                             }
00923                             if (result == tokLen) {
00924                                    if (tym)
00925                                           return( -1);
00926                                    tym = PR_TRUE;
00927                                    // for future use, get the time value
00928                                    memcpy( tymStr, pTok, tokLen);
00929                                    if (tokLen == 5) {
00930                                           tymStr[5] = ':';
00931                                           tymStr[6] = '0';
00932                                           tymStr[7] = '0';
00933                                    }
00934           tymStr[8] = 0;
00935                             }
00936                             else {
00937                                    other++;
00938                             }
00939                      }
00940                      else
00941                             other++;
00942               }      
00943               // Skip the space chars...
00944               while ((len < lineLen) && ((*pChar == ' ') || (*pChar == '\t') || (*pChar == ','))) {
00945                      pChar++;
00946                      len++;
00947               }
00948        } // end while (len < lineLen) token loop
00949        
00950        // Now let's see what we found on the line
00951        if (day && year && month && tym && (other < 3)) {
00952               // Now we need to make sure the next line
00953               // isn't blank!
00954               while (len < lineLen) {
00955                      len++;
00956                      pChar++;
00957               }
00958               if (len == maxLen)
00959                      return( -1);
00960               
00961               if (*pChar == 0x0D) {
00962                      len++;
00963                      pChar++;
00964                      if (*pChar == 0x0A) {
00965                             len++;
00966                             pChar++;
00967                      }
00968               }
00969               else if (*pChar == 0x0A) {
00970                      len++;
00971                      pChar++;
00972               }
00973               else
00974                      return( -1);
00975               if (len >= maxLen)
00976                      return( -1);
00977 
00978               while (len < maxLen) {
00979                      if ((*pChar == 0x0D) || (*pChar == 0x0A))
00980                             return( -1);
00981                      if ((*pChar != ' ') && (*pChar != '\t'))
00982                             break;
00983                      pChar++;
00984                      len++;
00985               }
00986 
00987               // Whew!, the next line isn't blank.
00988     // Generate the default date header in case the date header is missing when we
00989     // write out headers later. The header looks like "Date: Tue, 5 Feb 2002 23:05:04"
00990     char date_header_str[DATE_STR_LEN];
00991     PR_snprintf(date_header_str, DATE_STR_LEN, "Date: %s, %2d %s %4d %s", eudoraWeekDays[weekDay-1], day, eudoraMonths[month-1], year, tymStr);
00992     defaultDate.Assign(date_header_str);
00993 
00994               return( lineLen);
00995        }
00996        
00997        return( -1);
00998 
00999 }
01000 
01001 PRInt32 nsEudoraMailbox::AsciiToLong( const char *pChar, PRInt32 len)
01002 {
01003        PRInt32 num = 0;
01004        while (len) {
01005               if ((*pChar < '0') || (*pChar > '9'))
01006                      return( num);
01007               num *= 10;
01008               num += (*pChar - '0');
01009               len--;
01010               pChar++;
01011        }
01012        return( num);
01013 }
01014 
01015 
01016 int nsEudoraMailbox::IsWeekDayStr( const char *pStr)
01017 {
01018        for (int i = 0; i < 7; i++) {
01019               if (!nsCRT::strncasecmp( pStr, eudoraWeekDays[i], 3))
01020                      return( i + 1);
01021        }
01022        return( 0);
01023 }
01024 
01025 int nsEudoraMailbox::IsMonthStr( const char *pStr)
01026 {
01027        for (int i = 0; i < 12; i++) {
01028               if (!nsCRT::strncasecmp( pStr, eudoraMonths[i], 3))
01029                      return( i + 1);
01030        }
01031        return( 0);
01032 }
01033 
01034 nsresult nsEudoraMailbox::WriteFromSep( nsIFileSpec *pDst)
01035 {
01036        if (!m_fromLen)
01037               m_fromLen = strlen( eudoraFromLine);
01038        PRInt32       written = 0;
01039        nsresult rv = pDst->Write( eudoraFromLine, m_fromLen, &written);
01040        if (NS_SUCCEEDED( rv) && (written != m_fromLen))
01041               return( NS_ERROR_FAILURE);
01042        return( rv);
01043 }
01044 
01045 void nsEudoraMailbox::EmptyAttachments( void)
01046 {
01047        PRInt32 max = m_attachments.Count();
01048        ImportAttachment *   pAttach;
01049        for (PRInt32 i = 0; i < max; i++) {
01050               pAttach = (ImportAttachment *) m_attachments.ElementAt( i);
01051               if (pAttach) {
01052                      NS_IF_RELEASE( pAttach->pAttachment);
01053                      nsCRT::free( pAttach->description);
01054                      nsCRT::free( pAttach->mimeType);
01055                      delete pAttach;
01056               }
01057        }
01058 
01059        m_attachments.Clear();
01060 }
01061 
01062 static char *eudoraAttachLines[] = {
01063        "Attachment Converted:",
01064        "Attachment converted:",
01065        "Pièce jointe convertie :",
01066        // Japanese text encoded with Shift-JIS.
01067        // The meaning is "Restored attached file".
01068        "\x95\x9c\x8c\xb3\x82\xb3\x82\xea\x82\xbd\x93\x59\x95\x74\x83\x74\x83\x40\x83\x43\x83\x8b\x81\x46"
01069 };
01070 
01071 static PRInt32 eudoraAttachLen[] = {
01072        21,
01073        21,
01074        24,
01075        24,
01076        0
01077 };
01078 
01079 nsresult nsEudoraMailbox::ExamineAttachment( SimpleBufferTonyRCopiedOnce& data)
01080 {
01081        // get the file, then get the mime type, and add it to the array
01082        // of attachments.
01083        PRInt32              len = data.m_bytesInBuf - data.m_writeOffset;
01084        const char *pChar = data.m_pBuffer + data.m_writeOffset;
01085        const char *pData;
01086        const char *pStart;
01087        PRInt32       nameLen;
01088        char   quote;
01089        PRInt32       cnt;
01090        PRInt32       idx = 0;
01091        while ((cnt = eudoraAttachLen[idx]) != 0) {
01092               if (!nsCRT::strncmp( eudoraAttachLines[idx], pChar, cnt)) {
01093                      pData = pChar + cnt;
01094                      while (((*pData == ' ') || (*pData == '\t')) && (cnt < len)) {
01095                             cnt++;
01096                             pData++;
01097                      }
01098                      if (pData != pChar) {
01099                             quote = *pData;
01100                             nameLen = 0;
01101                             if ((quote == '"') || (quote == '\'')) {
01102                                    pData++;
01103                                    cnt++;
01104                                    pStart = pData;
01105                                    while ((*pData != quote) && (cnt < len)) {
01106                                           cnt++;
01107                                           pData++;
01108                                           nameLen++;
01109                                    }
01110                             }
01111                             else {
01112                                    pStart = pData;
01113                                    while ((*pData != 0x0D) && (*pData != 0x0A) && (cnt < len)) {
01114                                           pData++;
01115                                           cnt++;
01116                                           nameLen++;
01117                                    }
01118                             }
01119                             nsCString     fileName;
01120                             fileName.Append( pStart, nameLen);
01121                             fileName.Trim( kWhitespace);
01122                             if (fileName.Length()) {
01123 #ifdef XP_MACOSX
01124                                    return NS_OK;
01125 #else
01126                                    if( AddAttachment( fileName))
01127                                           return( NS_OK);
01128 #endif
01129                             }
01130                      }
01131               }
01132               idx++;
01133        }
01134 
01135        return( NS_OK);
01136 }
01137 
01138 PRBool nsEudoraMailbox::AddAttachment( nsCString& fileName)
01139 {
01140        IMPORT_LOG1( "Found attachment: %s\n", fileName.get());
01141 
01142        nsIFileSpec * pSpec;
01143        nsresult      rv  = NS_NewFileSpec( &pSpec);
01144        if (NS_FAILED( rv))
01145               return( PR_FALSE);
01146 
01147        nsCString     mimeType;
01148   nsCString attachmentName;
01149        if (NS_FAILED( GetAttachmentInfo( fileName.get(), pSpec, mimeType, attachmentName))) {
01150               NS_RELEASE( pSpec);
01151               return( PR_FALSE);
01152        }
01153 
01154        ImportAttachment *a = new ImportAttachment;
01155        a->mimeType = ToNewCString(mimeType);
01156   a->description = !attachmentName.IsEmpty() ? ToNewCString(attachmentName) : nsCRT::strdup( "Attached File");
01157        a->pAttachment = pSpec;
01158 
01159        m_attachments.AppendElement( a);
01160 
01161        return( PR_TRUE);
01162 }
01163 
01164 nsresult nsEudoraMailbox::FillMailBuffer( ReadFileState *pState, SimpleBufferTonyRCopiedOnce& read)
01165 {
01166        if (read.m_writeOffset >= read.m_bytesInBuf) {
01167               read.m_writeOffset = 0;
01168               read.m_bytesInBuf = 0;
01169        }
01170        else if (read.m_writeOffset) {
01171               memcpy( read.m_pBuffer, read.m_pBuffer + read.m_writeOffset, read.m_bytesInBuf - read.m_writeOffset);
01172               read.m_bytesInBuf -= read.m_writeOffset;
01173               read.m_writeOffset = 0;
01174        }
01175 
01176        PRInt32       count = read.m_size - read.m_bytesInBuf;
01177        if (((PRUint32)count + pState->offset) > pState->size)
01178               count = pState->size - pState->offset;
01179        if (count) {
01180               PRInt32              bytesRead = 0;
01181               char *        pBuffer = read.m_pBuffer + read.m_bytesInBuf;
01182               nsresult      rv = pState->pFile->Read( &pBuffer, count, &bytesRead);
01183               if (NS_FAILED( rv)) return( rv);
01184               if (bytesRead != count) return( NS_ERROR_FAILURE);
01185               read.m_bytesInBuf += bytesRead;
01186               pState->offset += bytesRead;
01187        }
01188 
01189        return( NS_OK);
01190 }
01191