Back to index

lightning-sunbird  0.9+nobinonly
nsTextAddress.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  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either of the GNU General Public License Version 2 or later (the "GPL"),
00027  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038 
00039 #include "nsTextAddress.h"
00040 #include "nsIAddrDatabase.h"
00041 #include "nsNativeCharsetUtils.h"
00042 
00043 #include "TextDebugLog.h"
00044 
00045 #define kWhitespace    " \t\b\r\n"
00046 
00047 // If we get a line longer than 32K it's just toooooo bad!
00048 #define kTextAddressBufferSz    (64 * 1024)
00049 
00050 nsTextAddress::nsTextAddress()
00051 {
00052     m_database = nsnull;
00053     m_fieldMap = nsnull;
00054     m_LFCount = 0;
00055     m_CRCount = 0;
00056 }
00057 
00058 nsTextAddress::~nsTextAddress()
00059 {
00060     NS_IF_RELEASE( m_database);
00061     NS_IF_RELEASE( m_fieldMap);
00062 }
00063 
00064 nsresult nsTextAddress::ImportAddresses( PRBool *pAbort, const PRUnichar *pName, nsIFileSpec *pSrc, nsIAddrDatabase *pDb, nsIImportFieldMap *fieldMap, nsString& errors, PRUint32 *pProgress)
00065 {
00066     // Open the source file for reading, read each line and process it!
00067     NS_IF_RELEASE( m_database);
00068     NS_IF_RELEASE( m_fieldMap);
00069     m_database = pDb;
00070     m_fieldMap = fieldMap;
00071     NS_ADDREF( m_fieldMap);
00072     NS_ADDREF( m_database);
00073 
00074     nsresult rv = pSrc->OpenStreamForReading();
00075     if (NS_FAILED( rv)) {
00076         IMPORT_LOG0( "*** Error opening address file for reading\n");
00077         return( rv);
00078     }
00079     
00080     char *pLine = new char[kTextAddressBufferSz];
00081     PRBool    eof = PR_FALSE;
00082     rv = pSrc->Eof( &eof);
00083     if (NS_FAILED( rv)) {
00084         IMPORT_LOG0( "*** Error checking address file for eof\n");
00085         pSrc->CloseStream();
00086         return( rv);
00087     }
00088     
00089     PRInt32    loc;
00090     PRInt32    lineLen = 0;
00091     PRBool     skipRecord = PR_FALSE;
00092 
00093     rv = m_fieldMap->GetSkipFirstRecord(&skipRecord);
00094     if (NS_FAILED(rv)) {
00095       IMPORT_LOG0("*** Error checking to see if we should skip the first record\n");
00096       return rv;
00097     }
00098 
00099     // Skip the first record if the user has requested it.
00100     if (skipRecord)
00101       rv = ReadRecord( pSrc, pLine, kTextAddressBufferSz, m_delim, &lineLen);
00102 
00103     while (!(*pAbort) && !eof && NS_SUCCEEDED( rv)) {
00104         rv = pSrc->Tell( &loc);
00105         if (NS_SUCCEEDED( rv) && pProgress)
00106             *pProgress = (PRUint32)loc;
00107         rv = ReadRecord( pSrc, pLine, kTextAddressBufferSz, m_delim, &lineLen);
00108         if (NS_SUCCEEDED( rv)) {
00109             rv = ProcessLine( pLine, strlen( pLine), errors);
00110             if (NS_FAILED( rv)) {
00111                 IMPORT_LOG0( "*** Error processing text record.\n");
00112             }
00113             else
00114                 rv = pSrc->Eof( &eof);
00115         }
00116     }
00117     
00118     rv = pSrc->CloseStream();
00119     
00120     delete [] pLine;
00121     
00122     if (!eof) {
00123         IMPORT_LOG0( "*** Error reading the address book, didn't reach the end\n");
00124         return( NS_ERROR_FAILURE);
00125     }
00126     
00127     rv = pDb->Commit(nsAddrDBCommitType::kLargeCommit);
00128     return rv;
00129 }
00130 
00131 
00132 
00133 nsresult nsTextAddress::ReadRecord( nsIFileSpec *pSrc, char *pLine, PRInt32 bufferSz, char delim, PRInt32 *pLineLen)
00134 {
00135     PRBool        wasTruncated;
00136     PRBool        isEof;
00137     char *        pRead;
00138     PRInt32        lineLen = 0;
00139     nsresult    rv;
00140     do {
00141         if (lineLen) {
00142             if ((lineLen + 2) < bufferSz) {
00143                 memcpy( pLine + lineLen, "\x0D\x0A", 2);
00144                 lineLen += 2;
00145                 pLine[lineLen] = 0;
00146             }
00147         }
00148         wasTruncated = PR_FALSE;
00149         pRead = pLine;
00150         pRead += lineLen;
00151         pSrc->Eof(&isEof);
00152         if (isEof)
00153           // If we get an EOF here, then the line isn't complete
00154           // so we must have an incorrect file.
00155           rv = NS_ERROR_FAILURE;
00156         else
00157         {
00158           rv = pSrc->ReadLine( &pRead, bufferSz - lineLen, &wasTruncated);
00159           if (wasTruncated) {
00160             pLine[bufferSz - 1] = 0;
00161             IMPORT_LOG0( "Unable to read line from file, buffer too small\n");
00162             rv = NS_ERROR_FAILURE;
00163           }
00164           else if (NS_SUCCEEDED( rv)) {
00165             lineLen = strlen( pLine);
00166           }
00167         }
00168     } while (NS_SUCCEEDED( rv) && !IsLineComplete( pLine, lineLen));
00169 
00170     *pLineLen = lineLen;
00171     return( rv);
00172 }
00173 
00174 
00175 nsresult nsTextAddress::ReadRecordNumber( nsIFileSpec *pSrc, char *pLine, PRInt32 bufferSz, char delim, PRInt32 *pLineLen, PRInt32 rNum)
00176 {
00177     PRInt32    rIndex = 0;
00178     nsresult rv = pSrc->Seek( 0);
00179     if (NS_FAILED( rv))
00180         return( rv);
00181     
00182     PRBool    eof = PR_FALSE;
00183 
00184     while (!eof && (rIndex <= rNum)) {
00185         if (NS_FAILED( rv = ReadRecord( pSrc, pLine, bufferSz, delim, pLineLen)))
00186             return( rv);
00187         if (rIndex == rNum)
00188             return( NS_OK);
00189         rIndex++;
00190         rv = pSrc->Eof( &eof);
00191         if (NS_FAILED( rv))
00192             return( rv);
00193     }
00194 
00195     return( NS_ERROR_FAILURE);
00196 }
00197 
00198 
00199 
00200 
00201 /*
00202     Find out if the given line appears to be a complete record or
00203     if it needs more data because the line ends with a quoted value non-terminated
00204 */
00205 PRBool nsTextAddress::IsLineComplete( const char *pLine, PRInt32 len)
00206 {
00207     PRBool    quoted = PR_FALSE;
00208 
00209     while (len) {
00210       if ((*pLine == '"')) {
00211         quoted = !quoted;
00212       }
00213       pLine++;
00214       len--;
00215     }
00216 
00217     return !quoted;
00218 }
00219 
00220 PRInt32 nsTextAddress::CountFields( const char *pLine, PRInt32 maxLen, char delim)
00221 {
00222     const char *pChar = pLine;
00223     PRInt32        len = 0;
00224     PRInt32        count = 0;
00225     char        tab = 9;
00226 
00227     if (delim == tab)
00228         tab = 0;
00229 
00230     while (len < maxLen) {
00231         while (((*pChar == ' ') || (*pChar == tab)) && (len < maxLen)) {
00232             pChar++;
00233             len++;
00234         }
00235         if ((len < maxLen) && (*pChar == '"')) {
00236             pChar++;
00237             len++;
00238             while ((len < maxLen) && (*pChar != '"')) {
00239                 len++;
00240                 pChar++;
00241                 if (((len + 1) < maxLen) && (*pChar == '"') && (*(pChar + 1) == '"')) {
00242                     len += 2;
00243                     pChar += 2;
00244                 }
00245             }
00246             if (len < maxLen) {
00247                 pChar++;
00248                 len++;
00249             }
00250         }        
00251         while ((len < maxLen) && (*pChar != delim)) {
00252             len++;
00253             pChar++;
00254         }
00255         
00256         count++;
00257         pChar++;
00258         len++;
00259     }
00260 
00261     return( count);
00262 }
00263 
00264 PRBool nsTextAddress::GetField( const char *pLine, PRInt32 maxLen, PRInt32 index, nsCString& field, char delim)
00265 {
00266     PRBool result = PR_FALSE;
00267     const char *pChar = pLine;
00268     PRInt32        len = 0;
00269     char        tab = 9;
00270 
00271     field.Truncate();
00272 
00273     if (delim == tab)
00274         tab = 0;
00275 
00276     while (index && (len < maxLen)) {
00277         while (((*pChar == ' ') || (*pChar == tab)) && (len < maxLen)) {
00278             pChar++;
00279             len++;
00280         }
00281         if (len >= maxLen)
00282             break;
00283         if (*pChar == '"') {
00284             len = -1;
00285             do {
00286                 len++;
00287                 pChar++;
00288                 if (((len + 1) < maxLen) && (*pChar == '"') && (*(pChar + 1) == '"')) {
00289                     len += 2;
00290                     pChar += 2;
00291                 }
00292             } while ((len < maxLen) && (*pChar != '"'));
00293             if (len < maxLen) {
00294                 pChar++;
00295                 len++;
00296             }
00297         }
00298         if (len >= maxLen)
00299             break;
00300         
00301         while ((len < maxLen) && (*pChar != delim)) {
00302             len++;
00303             pChar++;
00304         }
00305         
00306         if (len >= maxLen)
00307             break;
00308 
00309         index--;
00310         pChar++;
00311         len++;
00312     }
00313 
00314     if (len >= maxLen) {
00315         return( result);
00316     }
00317 
00318     result = PR_TRUE;
00319 
00320     while ((len < maxLen) && ((*pChar == ' ') || (*pChar == tab))) {
00321         len++;
00322         pChar++;
00323     }
00324 
00325     const char *pStart = pChar;
00326     PRInt32        fLen = 0;
00327     PRBool        quoted = PR_FALSE;
00328     if (*pChar == '"') {
00329         pStart++;
00330         fLen = -1;
00331         do {
00332             pChar++;
00333             len++;
00334             fLen++;
00335             if (((len + 1) < maxLen) && (*pChar == '"') && (*(pChar + 1) == '"')) {
00336                 quoted = PR_TRUE;
00337                 len += 2;
00338                 pChar += 2;
00339                 fLen += 2;
00340             }
00341         } while ((len < maxLen) && (*pChar != '"'));
00342     }
00343     else {
00344         while ((len < maxLen) && (*pChar != delim)) {
00345             pChar++;
00346             len++;
00347             fLen++;
00348         }
00349     }
00350 
00351     if (!fLen) {
00352         return( result);
00353     }
00354 
00355     field.Append( pStart, fLen);
00356     field.Trim( kWhitespace);
00357 
00358     if (quoted) {
00359         field.ReplaceSubstring( "\"\"", "\"");
00360     }
00361 
00362     return( result);
00363 }
00364 
00365 
00366 void nsTextAddress::SanitizeSingleLine( nsCString& val)
00367 {
00368     val.ReplaceSubstring( "\x0D\x0A", ", ");
00369     val.ReplaceChar( 13, ' ');
00370     val.ReplaceChar( 10, ' ');
00371 }
00372 
00373 
00374 nsresult nsTextAddress::DetermineDelim( nsIFileSpec *pSrc)
00375 {
00376     nsresult rv = pSrc->OpenStreamForReading();
00377     if (NS_FAILED( rv)) {
00378         IMPORT_LOG0( "*** Error opening address file for reading\n");
00379         return( rv);
00380     }
00381     
00382     char *pLine = new char[kTextAddressBufferSz];
00383     PRBool    eof = PR_FALSE;
00384     rv = pSrc->Eof( &eof);
00385     if (NS_FAILED( rv)) {
00386         IMPORT_LOG0( "*** Error checking address file for eof\n");
00387         pSrc->CloseStream();
00388         return( rv);
00389     }
00390     
00391     PRBool    wasTruncated = PR_FALSE;
00392     PRInt32    lineLen = 0;
00393     PRInt32    lineCount = 0;
00394     PRInt32    tabCount = 0;
00395     PRInt32    commaCount = 0;
00396     PRInt32    tabLines = 0;
00397     PRInt32    commaLines = 0;
00398 
00399     while (!eof && NS_SUCCEEDED( rv) && (lineCount < 100)) {
00400         wasTruncated = PR_FALSE;
00401         rv = pSrc->ReadLine( &pLine, kTextAddressBufferSz, &wasTruncated);
00402         if (wasTruncated)
00403             pLine[kTextAddressBufferSz - 1] = 0;
00404         if (NS_SUCCEEDED( rv)) {
00405             lineLen = strlen( pLine);
00406             tabCount = CountFields( pLine, lineLen, 9);
00407             commaCount = CountFields( pLine, lineLen, ',');
00408             if (tabCount > commaCount)
00409                 tabLines++;
00410             else if (commaCount)
00411                 commaLines++;
00412             rv = pSrc->Eof( &eof);
00413         }
00414         lineCount++;
00415     }
00416     
00417     rv = pSrc->CloseStream();
00418     
00419     delete [] pLine;
00420     
00421     if (tabLines > commaLines)
00422         m_delim = 9;
00423     else
00424         m_delim = ',';
00425 
00426     return( NS_OK);
00427 }
00428 
00429 
00430 /*
00431     This is where the real work happens!
00432     Go through the field map and set the data in a new database row
00433 */
00434 nsresult nsTextAddress::ProcessLine( const char *pLine, PRInt32 len, nsString& errors)
00435 {
00436     if (!m_fieldMap) {
00437         IMPORT_LOG0( "*** Error, text import needs a field map\n");
00438         return( NS_ERROR_FAILURE);
00439     }
00440 
00441     nsresult rv;
00442     
00443     // Wait until we get our first non-empty field, then create a new row,
00444     // fill in the data, then add the row to the database.
00445         
00446 
00447     nsIMdbRow *    newRow = nsnull;
00448     nsString    uVal;
00449     nsCString    fieldVal;
00450     PRInt32        fieldNum;
00451     PRInt32        numFields = 0;
00452     PRBool        active;
00453     rv = m_fieldMap->GetMapSize( &numFields);
00454     for (PRInt32 i = 0; (i < numFields) && NS_SUCCEEDED( rv); i++) {
00455         active = PR_FALSE;
00456         rv = m_fieldMap->GetFieldMap( i, &fieldNum);
00457         if (NS_SUCCEEDED( rv))
00458             rv = m_fieldMap->GetFieldActive( i, &active);
00459         if (NS_SUCCEEDED( rv) && active) {
00460             if (GetField( pLine, len, i, fieldVal, m_delim)) {
00461                 if (!fieldVal.IsEmpty()) {
00462                     if (!newRow) {
00463                         rv = m_database->GetNewRow( &newRow);
00464                         if (NS_FAILED( rv)) {
00465                             IMPORT_LOG0( "*** Error getting new address database row\n");
00466                         }
00467                     }
00468                     if (newRow) {
00469                         NS_CopyNativeToUnicode( fieldVal, uVal);
00470                         rv = m_fieldMap->SetFieldValue( m_database, newRow, fieldNum, uVal.get());
00471                     }
00472                 }
00473             }
00474             else
00475                 break;
00476             
00477         }
00478         else {
00479             if (active) {
00480                 IMPORT_LOG1( "*** Error getting field map for index %ld\n", (long) i);
00481             }
00482         }
00483 
00484     }
00485     
00486     if (NS_SUCCEEDED( rv)) {
00487         if (newRow) {
00488             rv = m_database->AddCardRowToDB( newRow);
00489             // Release newRow????
00490         }
00491     }
00492     else {
00493         // Release newRow??
00494     }
00495 
00496     return( rv);
00497 }
00498