Back to index

lightning-sunbird  0.9+nobinonly
nsMsgHeaderParser.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; 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  *   Pierre Phaneuf <pp@ludusdesign.com>
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 "msgCore.h"    // precompiled header...
00040 #include "nsXPIDLString.h"
00041 #include "nsReadableUtils.h"
00042 #include "nsMsgHeaderParser.h"
00043 #include "nsISimpleEnumerator.h"   
00044 #include "comi18n.h"
00045 #include "prmem.h"
00046 
00047 nsresult FillResultsArray(const char * aName, const char *aAddress, PRUnichar ** aOutgoingEmailAddress, PRUnichar ** aOutgoingName, 
00048                           PRUnichar ** aOutgoingFullName, nsIMsgHeaderParser *aParser);
00049 
00050 /*
00051  * Macros used throughout the RFC-822 parsing code.
00052  */
00053 
00054 #undef FREEIF
00055 #define FREEIF(obj) do { if (obj) { PR_Free (obj); obj = 0; }} while (0)
00056 
00057 #define COPY_CHAR(_D,_S)            do { if (!_S || !*_S) { *_D++ = 0; }\
00058                                          else { int _LEN = NextChar_UTF8((char *)_S) - _S;\
00059                                                 memcpy(_D,_S,_LEN); _D += _LEN; } } while (0)
00060 //#define NEXT_CHAR(_STR)             (_STR = (* (char *) _STR < 128) ? (char *) _STR + 1 : NextChar_UTF8((char *)_STR))
00061 #define NEXT_CHAR(_STR)             (_STR = NextChar_UTF8((char *)_STR))
00062 #define TRIM_WHITESPACE(_S,_E,_T)   do { while (_E > _S && nsCRT::IsAsciiSpace(_E[-1])) _E--;\
00063                                          *_E++ = _T; } while (0)
00064 
00065 /*
00066  * The following are prototypes for the old "C" functions used to support all of the RFC-822 parsing code
00067  * We could have made these private functions of nsMsgHeaderParser if we wanted...
00068  */
00069 static int msg_parse_Header_addresses(const char *line, char **names, char **addresses,
00070                                       PRBool quote_names_p = PR_TRUE, PRBool quote_addrs_p = PR_TRUE,
00071                                       PRBool first_only_p = PR_FALSE);
00072 static int msg_quote_phrase_or_addr(char *address, PRInt32 length, PRBool addr_p);
00073 static nsresult msg_unquote_phrase_or_addr(const char *line, PRBool strict, char **lineout);
00074 static char *msg_extract_Header_address_mailboxes(const char *line);
00075 static char *msg_extract_Header_address_names(const char *line);
00076 static char *msg_extract_Header_address_name(const char *line);
00077 #if 0
00078 static char *msg_format_Header_addresses(const char *addrs, int count,
00079                                          PRBool wrap_lines_p);
00080 #endif
00081 static char *msg_reformat_Header_addresses(const char *line);
00082 
00083 static char *msg_remove_duplicate_addresses(const char *addrs, const char *other_addrs,
00084                                             PRBool removeAliasesToMe);
00085 static char *msg_make_full_address(const char* name, const char* addr);
00086 
00087 /*
00088  * nsMsgHeaderParser definitions....
00089  */
00090 
00091 nsMsgHeaderParser::nsMsgHeaderParser()
00092 {
00093 }
00094 
00095 nsMsgHeaderParser::~nsMsgHeaderParser()
00096 {
00097 }
00098 
00099 NS_IMPL_ISUPPORTS1(nsMsgHeaderParser, nsIMsgHeaderParser)
00100 
00101 // helper function called by ParseHeadersWithArray
00102 nsresult FillResultsArray(const char * aName, const char *aAddress, PRUnichar ** aOutgoingEmailAddress, PRUnichar ** aOutgoingName, 
00103                           PRUnichar ** aOutgoingFullName, nsIMsgHeaderParser *aParser)
00104 {
00105   NS_ENSURE_ARG(aParser);
00106   nsresult rv = NS_OK;
00107 
00108   char * result = nsnull;
00109   if (aAddress)
00110   {
00111     result = MIME_DecodeMimeHeader(aAddress, NULL, PR_FALSE, PR_TRUE);
00112     *aOutgoingEmailAddress = ToNewUnicode(NS_ConvertUTF8toUCS2(result ? result : aAddress));
00113     PR_FREEIF(result);
00114   }
00115   
00116   if (aName)
00117   {
00118     result = MIME_DecodeMimeHeader(aName, NULL, PR_FALSE, PR_TRUE);
00119     *aOutgoingName = ToNewUnicode(NS_ConvertUTF8toUCS2(result ? result : aName));
00120     PR_FREEIF(result);
00121   }
00122 
00123   nsXPIDLCString fullAddress;
00124   nsXPIDLCString unquotedAddress;
00125   rv = aParser->MakeFullAddress("UTF-8", aName, aAddress, getter_Copies(fullAddress));
00126   if (NS_SUCCEEDED(rv) && (const char*)fullAddress)
00127   {
00128     result = MIME_DecodeMimeHeader(fullAddress, nsnull, PR_FALSE, PR_TRUE);
00129     if (result)
00130       fullAddress.Adopt(result);
00131     aParser->UnquotePhraseOrAddr(fullAddress, PR_TRUE, getter_Copies(unquotedAddress));
00132     if (!unquotedAddress.IsEmpty())
00133       fullAddress = unquotedAddress;
00134     *aOutgoingFullName = ToNewUnicode(NS_ConvertUTF8toUCS2(fullAddress.get()));
00135   }
00136   else
00137     *aOutgoingFullName = nsnull;
00138 
00139   return rv;
00140 }
00141 
00142 NS_IMETHODIMP nsMsgHeaderParser::ParseHeadersWithArray(const PRUnichar * aLine, PRUnichar *** aEmailAddresses,
00143                                                        PRUnichar *** aNames, PRUnichar *** aFullNames, PRUint32 * aNumAddresses)
00144 {
00145   char * names = nsnull;
00146   char * addresses = nsnull;
00147   PRUint32 numAddresses = 0;
00148   nsresult rv = NS_OK;
00149 
00150   // need to convert unicode to UTF-8...
00151   nsAutoString tempString (aLine);
00152   char * utf8String = ToNewUTF8String(tempString);
00153 
00154   rv = ParseHeaderAddresses("UTF-8", utf8String, &names, &addresses, &numAddresses);
00155   nsCRT::free(utf8String);
00156   if (NS_SUCCEEDED(rv) && numAddresses)
00157   {
00158     // allocate space for our arrays....
00159     *aEmailAddresses = (PRUnichar **) PR_MALLOC(sizeof(PRUnichar *) * numAddresses);
00160     *aNames = (PRUnichar **) PR_MALLOC(sizeof(PRUnichar *) * numAddresses);
00161     *aFullNames = (PRUnichar **) PR_MALLOC(sizeof(PRUnichar *) * numAddresses);
00162 
00163     // for simplicities sake...
00164     PRUnichar ** outgoingEmailAddresses = *aEmailAddresses;
00165     PRUnichar ** outgoingNames = *aNames;
00166     PRUnichar ** outgoingFullNames = *aFullNames;
00167 
00168     // iterate over the results and fill in our arrays....
00169     PRUint32 index = 0; 
00170     const char * currentName = names;
00171     const char * currentAddress = addresses;
00172     char * unquotedName = nsnull;
00173     while (index < numAddresses)
00174     {
00175       if (NS_SUCCEEDED(UnquotePhraseOrAddr(currentName, PR_TRUE, &unquotedName)))
00176         rv = FillResultsArray(unquotedName, currentAddress, &outgoingEmailAddresses[index], &outgoingNames[index], &outgoingFullNames[index], this);
00177       else
00178         rv = FillResultsArray(currentName, currentAddress, &outgoingEmailAddresses[index], &outgoingNames[index], &outgoingFullNames[index], this);
00179       
00180       PR_FREEIF(unquotedName);
00181       currentName += strlen(currentName) + 1;
00182       currentAddress += strlen(currentAddress) + 1;
00183       index++;
00184     }
00185   }
00186 
00187   *aNumAddresses = numAddresses;
00188   PR_FREEIF(names);
00189   PR_FREEIF(addresses);
00190   return rv;
00191 }
00192 
00193 nsresult nsMsgHeaderParser::ParseHeaderAddresses (const char *charset, const char *line, char **names, char **addresses, PRUint32 *numAddresses)
00194 {
00195   *numAddresses = msg_parse_Header_addresses(line, names, addresses);
00196 
00197   return NS_OK;
00198 }
00199 
00200 nsresult nsMsgHeaderParser::ExtractHeaderAddressMailboxes (const char *charset, const char *line, char ** mailboxes)
00201 {
00202   if (mailboxes)
00203   {
00204     *mailboxes = msg_extract_Header_address_mailboxes(line);
00205     return NS_OK;
00206   }
00207   else
00208     return NS_ERROR_NULL_POINTER;
00209 }
00210 
00211 nsresult nsMsgHeaderParser::ExtractHeaderAddressNames (const char *charset, const char *line, char ** names)
00212 {
00213   if (names)
00214   {
00215     *names = msg_extract_Header_address_names(line);
00216     return NS_OK;
00217   }
00218   else
00219     return NS_ERROR_NULL_POINTER;
00220 }
00221 
00222 
00223 nsresult nsMsgHeaderParser::ExtractHeaderAddressName (const char *charset, const char *line, char ** name)
00224 {
00225   if (name)
00226   {
00227     *name = msg_extract_Header_address_name(line);
00228     return NS_OK;
00229   }
00230   else
00231     return NS_ERROR_NULL_POINTER;
00232 }
00233 
00234 nsresult nsMsgHeaderParser::ReformatHeaderAddresses (const char *charset, const char *line, char ** reformattedAddress)
00235 {
00236   if (reformattedAddress)
00237   {
00238     *reformattedAddress = msg_reformat_Header_addresses(line);
00239     return NS_OK;
00240   }
00241   else
00242     return NS_ERROR_NULL_POINTER;
00243 }
00244 
00245 nsresult nsMsgHeaderParser::RemoveDuplicateAddresses (const char *charset, const char *addrs, const char *other_addrs, PRBool removeAliasesToMe, char ** newOutput)
00246 {
00247   if (newOutput)
00248   {
00249     *newOutput = msg_remove_duplicate_addresses(addrs, other_addrs, removeAliasesToMe);
00250     return NS_OK;
00251   }
00252   else
00253     return NS_ERROR_NULL_POINTER;
00254 }
00255 
00256 nsresult nsMsgHeaderParser::MakeFullAddress (const char *charset, const char* name, const char* addr, char ** fullAddress)
00257 {
00258   NS_ENSURE_ARG_POINTER(fullAddress);
00259   
00260   *fullAddress = msg_make_full_address(name, addr);
00261   return NS_OK;
00262 }
00263 
00264 nsresult nsMsgHeaderParser::MakeFullAddressWString (const PRUnichar* name, const PRUnichar* addr, PRUnichar ** fullAddress)
00265 {
00266   nsXPIDLCString utf8Str;
00267   nsresult rv = MakeFullAddress(nsnull, NS_ConvertUCS2toUTF8(name).get(), 
00268                                 NS_ConvertUCS2toUTF8(addr).get(), getter_Copies(utf8Str));
00269   if (NS_SUCCEEDED(rv))
00270   {
00271     *fullAddress = ToNewUnicode(NS_ConvertUTF8toUCS2(utf8Str.get()));
00272     if (*fullAddress == nsnull)
00273       rv = NS_ERROR_OUT_OF_MEMORY;
00274   }
00275 
00276   return rv;
00277 }
00278 
00279 nsresult nsMsgHeaderParser::UnquotePhraseOrAddr (const char *line, PRBool preserveIntegrity, char** result)
00280 {
00281   NS_ENSURE_ARG_POINTER(result);
00282   return msg_unquote_phrase_or_addr(line, preserveIntegrity, result);
00283 }
00284 
00285 nsresult nsMsgHeaderParser::UnquotePhraseOrAddrWString (const PRUnichar *line, PRBool preserveIntegrity, PRUnichar ** result)
00286 {
00287   nsXPIDLCString utf8Str;
00288   nsresult rv = msg_unquote_phrase_or_addr(NS_ConvertUCS2toUTF8(line).get(), preserveIntegrity, getter_Copies(utf8Str));
00289   if (NS_SUCCEEDED(rv))
00290   {
00291     *result = ToNewUnicode(NS_ConvertUTF8toUCS2(utf8Str.get()));
00292     if (*result == nsnull)
00293       rv = NS_ERROR_OUT_OF_MEMORY;
00294   }
00295 
00296   return rv;
00297 }
00298 
00299 nsresult nsMsgHeaderParser::ReformatUnquotedAddresses (const PRUnichar *line, PRUnichar ** result)
00300 {
00301   NS_ENSURE_ARG_POINTER(result);
00302   *result = nsnull;
00303   PRBool badInput = PR_FALSE;
00304 
00305   NS_ConvertUCS2toUTF8 convertedLine(line);
00306 
00307   PRInt32 lineLen = convertedLine.Length();
00308   PRInt32 outputMaxLen = (lineLen * sizeof (char) * 2) + 2; //Let's presume we need to escape every char
00309   char * outputStr = (char *) PR_Malloc(outputMaxLen);
00310   if (!outputStr)
00311     return NS_ERROR_OUT_OF_MEMORY;
00312 
00313   const char *readPtr = convertedLine.get();
00314   char *writePtr = outputStr;
00315   const char *endOutputPtr = outputStr + outputMaxLen;
00316   const char *endPtr = readPtr + lineLen;
00317  
00318   const char *startRecipient = readPtr;
00319   char* reformated;
00320   PRBool openQuoteLevel1 = PR_FALSE;
00321   PRBool openQuoteLevel2 = PR_FALSE;
00322 
00323   while (readPtr <= endPtr && writePtr < endOutputPtr && !badInput)
00324   {
00325     if (*readPtr == '\\')
00326     {
00327       if (*(readPtr + 1) == '"')
00328       {
00329         openQuoteLevel2 = !openQuoteLevel2;
00330         readPtr ++;
00331       }
00332     }
00333     else if (*readPtr == '"')
00334     {
00335       openQuoteLevel1 = !openQuoteLevel1;
00336       openQuoteLevel2 &= openQuoteLevel1;
00337     }
00338     else if (*readPtr == ',')
00339     {
00340       if (!openQuoteLevel1 && !openQuoteLevel2)
00341       {
00342         /* Ok, we found an address delimiter, temporary replace the delimiter with a \0
00343            and quote the full address
00344         */
00345         char * tempEnd = NS_CONST_CAST(char *, readPtr);
00346         *tempEnd = 0;
00347         
00348         reformated = msg_reformat_Header_addresses(startRecipient);
00349         if (reformated)
00350         {
00351           if ((PRInt32) strlen(reformated) < (endOutputPtr - writePtr))
00352           {
00353             strncpy(writePtr, reformated, endOutputPtr - writePtr);
00354             writePtr += strlen(reformated);
00355           }
00356           else
00357           {
00358             badInput = PR_TRUE;
00359           }
00360           PR_Free(reformated);
00361         }
00362         else
00363         {
00364           strncpy(writePtr, startRecipient, endOutputPtr - writePtr);
00365           writePtr += strlen(startRecipient);
00366         }
00367         *writePtr = ',';
00368         writePtr ++;
00369 
00370         *tempEnd = ',';
00371         startRecipient = readPtr + 1;
00372       }
00373     }
00374 
00375     readPtr ++;
00376   }
00377 
00378   /* write off the last recipient */
00379   reformated = msg_reformat_Header_addresses(startRecipient);
00380   if (reformated)
00381   {
00382     if ((PRInt32) strlen(reformated) < (endOutputPtr - writePtr))
00383       strncpy(writePtr, reformated, endOutputPtr - writePtr);
00384     else
00385       badInput = PR_TRUE;
00386     PR_Free(reformated);
00387   }
00388   else
00389     strncpy(writePtr, startRecipient, endOutputPtr - writePtr);
00390 
00391   if (!badInput)
00392     *result = ToNewUnicode(NS_ConvertUTF8toUCS2(outputStr));
00393   PR_Free(outputStr);
00394   if (*result == nsnull)
00395     return NS_ERROR_OUT_OF_MEMORY;
00396   if (badInput)
00397     return NS_ERROR_INVALID_ARG;
00398 
00399   return NS_OK;
00400 }
00401 
00402  /* this function will be used by the factory to generate an RFC-822 Parser....*/
00403 nsresult NS_NewHeaderParser(nsIMsgHeaderParser ** aInstancePtrResult)
00404 {
00405   /* note this new macro for assertions...they can take a string describing the assertion */
00406   NS_PRECONDITION(nsnull != aInstancePtrResult, "nsnull ptr");
00407   if (nsnull != aInstancePtrResult)
00408   {
00409     nsMsgHeaderParser* parser = new nsMsgHeaderParser();
00410     if (parser)
00411       return parser->QueryInterface(NS_GET_IID(nsIMsgHeaderParser), (void **)aInstancePtrResult);
00412     else
00413       return NS_ERROR_OUT_OF_MEMORY; /* we couldn't allocate the object */
00414   }
00415   else
00416     return NS_ERROR_NULL_POINTER; /* aInstancePtrResult was NULL....*/
00417 }
00418 
00419  
00420 /*
00421  * The remainder of this file is the actual parsing code and it was extracted verbatim from addrutils.cpp 
00422  */
00423 
00424 /* msg_parse_Header_addresses
00425  *
00426  * Given a string which contains a list of Header addresses, parses it into
00427  * their component names and mailboxes.
00428  *
00429  * The returned value is the number of addresses, or a negative error code;
00430  * the names and addresses are returned into the provided pointers as
00431  * consecutive null-terminated strings.  It is up to the caller to free them.
00432  * Note that some of the strings may be zero-length.
00433  *
00434  * Either of the provided pointers may be NULL if the caller is not interested
00435  * in those components.
00436  *
00437  * quote_names_p and quote_addrs_p control whether the returned strings should
00438  * be quoted as Header entities, or returned in a more human-presentable (but
00439  * not necessarily parsable) form.
00440  *
00441  * If first_only_p is true, then only the first element of the list is
00442  * returned; we don't bother parsing the rest.
00443  */
00444 static int msg_parse_Header_addresses (const char *line, char **names, char **addresses,
00445                 PRBool quote_names_p, PRBool quote_addrs_p, PRBool first_only_p)
00446 {
00447   PRUint32 addr_count = 0;
00448   size_t line_length;
00449   const char *line_end;
00450   const char *this_start;
00451   char *name_buf = 0, *name_out, *name_start;
00452   char *addr_buf = 0, *addr_out, *addr_start;
00453 
00454   if (names)
00455     *names = 0;
00456   if (addresses)
00457     *addresses = 0;
00458   NS_ASSERTION(line, "");
00459   if (!line)
00460     return -1;
00461   line_length = strlen(line);
00462   if (line_length == 0)
00463     return 0;
00464 
00465   name_buf = (char *)PR_Malloc(line_length * 2 + 10);
00466   if (!name_buf)
00467     return NS_ERROR_OUT_OF_MEMORY;
00468 
00469   addr_buf = (char *)PR_Malloc(line_length * 2 + 10);
00470   if (!addr_buf)
00471   {
00472     FREEIF(name_buf);
00473     return NS_ERROR_OUT_OF_MEMORY;
00474   }
00475 
00476   line_end = line;
00477   addr_out = addr_buf;
00478   name_out = name_buf;
00479   name_start = name_buf;
00480   addr_start = addr_buf;
00481   this_start = line;
00482   
00483   /* Skip over extra whitespace or commas before addresses.
00484    */
00485   while (*line_end && (nsCRT::IsAsciiSpace(*line_end) || *line_end == ','))
00486     NEXT_CHAR(line_end);
00487 
00488   while (*line_end)
00489   {
00490     PRUint32 paren_depth = 0;
00491     const char *oparen = 0;
00492     const char *mailbox_start = 0;
00493     const char *mailbox_end = 0;
00494 
00495     while (   *line_end
00496            && !(   *line_end == ',' && paren_depth <= 0 /* comma is ok inside () */
00497                     && (!mailbox_start || mailbox_end)))    /* comma is ok inside <> */
00498     {
00499       if (*line_end == '\\')
00500       {
00501         line_end++;
00502         if (!*line_end) /* otherwise, we walk off end of line, right? */
00503           break;
00504       }
00505       else if (*line_end == '\"') 
00506       {
00507         int leave_quotes = 0;
00508 
00509         line_end++;  /* remove open " */
00510 
00511         /* handle '"John.Van Doe"@space.com' case */
00512         if (paren_depth == 0 && !mailbox_start)
00513         {
00514           const char *end_quote;
00515           /* search for the closing quote but ignored escaped quote \" */
00516           for (end_quote = line_end;; end_quote++)
00517           {
00518             end_quote = PL_strstr(end_quote, "\"");
00519             if (!end_quote || *(end_quote - 1) != '\\')
00520               break;
00521           }
00522           const char *mailbox = end_quote ? PL_strstr(end_quote, "<") : (char *)NULL;
00523           const char *comma = end_quote ? PL_strstr(end_quote, ",") : (char *)NULL;
00524           if (!mailbox || (comma && comma < mailbox))
00525           {
00526             leave_quotes = 1; /* no mailbox for this address */
00527             *addr_out++ = '\"';
00528           }
00529         }
00530 
00531         while (*line_end)
00532         {
00533           if (*line_end == '\\')
00534           {
00535             line_end++;
00536             if (paren_depth == 0 && (*line_end == '\\' || *line_end == '\"'))
00537               *addr_out++ = *line_end++;
00538             continue;
00539           }
00540           else if (*line_end == '\"')
00541           {
00542             line_end++;  /* remove close " */
00543             break;
00544           }
00545 
00546           if (paren_depth == 0)
00547             COPY_CHAR(addr_out, line_end);
00548 
00549           NEXT_CHAR(line_end);
00550         }
00551         if (leave_quotes) *addr_out++ = '\"';
00552         continue;
00553       }
00554 
00555       if (*line_end == '(')
00556       {
00557         if (paren_depth == 0)
00558           oparen = line_end;
00559         paren_depth++;
00560       }
00561       else if (*line_end == '<' && paren_depth == 0)
00562       {
00563         mailbox_start = line_end;
00564       }
00565       else if (*line_end == '>' && mailbox_start && paren_depth == 0)
00566       {
00567         mailbox_end = line_end;
00568       }
00569       else if (*line_end == ')' && paren_depth > 0)
00570       {
00571         paren_depth--;
00572         if (paren_depth == 0)
00573         {
00574           const char *s = oparen + 1;
00575 
00576           /* Copy the chars inside the parens onto the "name" buffer.
00577            */
00578 
00579           /* Push out some whitespace before the paren, if
00580            * there is non-whitespace there already.
00581            */
00582           if (name_out > name_start && !nsCRT::IsAsciiSpace(name_out [-1]))
00583             *name_out++ = ' ';
00584 
00585           /* Skip leading whitespace.
00586            */
00587           while (nsCRT::IsAsciiSpace(*s) && s < line_end)
00588             s++;
00589 
00590           while (s < line_end)
00591           {
00592             /* Strip out " within () unless backslashed
00593              */
00594             if (*s == '\"')
00595             {
00596               s++;
00597               continue;
00598             }
00599 
00600             if (*s == '\\') /* remove one \ */
00601               s++;
00602 
00603             if (nsCRT::IsAsciiSpace(*s) && name_out > name_start && nsCRT::IsAsciiSpace(name_out[-1]))
00604               /* collapse consecutive whitespace */;
00605             else
00606               COPY_CHAR(name_out, s);
00607 
00608             NEXT_CHAR(s);
00609           }
00610           oparen = 0;
00611         }
00612       }
00613       else
00614       {
00615         /* If we're not inside parens or a <mailbox>, tack this
00616          * on to the end of the addr_buf.
00617          */
00618         if (paren_depth == 0 && (!mailbox_start || mailbox_end))
00619         {
00620           /* Eat whitespace at the beginning of the line,
00621            * and eat consecutive whitespace within the line.
00622            */
00623           if (   nsCRT::IsAsciiSpace(*line_end)
00624               && (addr_out == addr_start || nsCRT::IsAsciiSpace(addr_out[-1])))
00625             /* skip it */;
00626           else
00627             COPY_CHAR(addr_out, line_end);
00628         }
00629       }
00630 
00631         NEXT_CHAR(line_end);
00632     }
00633 
00634     /* Now we have extracted a single address from the comma-separated
00635      * list of addresses.  The characters have been divided among the
00636      * various buffers: the parts inside parens have been placed in the
00637      * name_buf, and everything else has been placed in the addr_buf.
00638      * Quoted strings and backslashed characters have been `expanded.'
00639      *
00640      * If there was a <mailbox> spec in it, we have remembered where it was.
00641      * Copy that on to the addr_buf, replacing what was there, and copy the
00642      * characters not inside <> onto the name_buf, replacing what is there
00643      * now (which was just the parenthesized parts.)  (And we need to do the
00644      * quote and backslash hacking again, since we're coming from the
00645      * original source.)
00646      *
00647      * Otherwise, we're already done - the addr_buf and name_buf contain
00648      * the right data already (de-quoted.)
00649      */
00650     if (mailbox_end)
00651     {
00652       const char *s;
00653       NS_ASSERTION(*mailbox_start == '<', "");
00654       NS_ASSERTION(*mailbox_end == '>', "");
00655 
00656       /* First, copy the name.
00657        */
00658       name_out = name_start;
00659       s = this_start;
00660 
00661       /* Skip leading whitespace.
00662        */
00663       while (nsCRT::IsAsciiSpace(*s) && s < mailbox_start)
00664         s++;
00665 
00666       /* Copy up to (not including) the <
00667        */
00668       while (s < mailbox_start)
00669       {
00670         if (*s == '\"' && !quote_names_p)
00671         {
00672           s++;
00673           continue;
00674         }
00675         if (*s == '\\' && !quote_names_p)
00676         {
00677           s++;
00678           if (s < mailbox_start && (*s == '\\' || *s == '\"'))
00679             *name_out++ = *s++;
00680           continue;
00681         }
00682         if (nsCRT::IsAsciiSpace(*s) && name_out > name_start && nsCRT::IsAsciiSpace(name_out[-1]))
00683           /* collapse consecutive whitespace */;
00684         else
00685           COPY_CHAR(name_out, s);
00686 
00687         NEXT_CHAR(s);
00688       }
00689 
00690       /* Push out one space.
00691        */
00692       TRIM_WHITESPACE(name_start, name_out, ' ');
00693       s = mailbox_end + 1;
00694 
00695       /* Skip whitespace after >
00696        */
00697       while (nsCRT::IsAsciiSpace(*s) && s < line_end)
00698         s++;
00699 
00700       /* Copy from just after > to the end.
00701        */
00702       while (s < line_end)
00703       {
00704         if (*s == '\"' && !quote_names_p)
00705         {
00706           s++;
00707           continue;
00708         }
00709         if (*s == '\\' && !quote_names_p)
00710         {
00711           s++;
00712           if (s  < line_end && (*s == '\\' || *s == '\"'))
00713             *name_out++ = *s++;
00714           continue;
00715         }
00716         if (nsCRT::IsAsciiSpace (*s) && name_out > name_start && nsCRT::IsAsciiSpace (name_out[-1]))
00717           /* collapse consecutive whitespace */;
00718         else
00719           COPY_CHAR(name_out, s);
00720 
00721         NEXT_CHAR(s);
00722       }
00723 
00724       TRIM_WHITESPACE(name_start, name_out, 0);
00725 
00726       /* Now, copy the address.
00727        */
00728       mailbox_start++;
00729       addr_out = addr_start;
00730       s = mailbox_start;
00731 
00732       /* Skip leading whitespace.
00733        */
00734       while (nsCRT::IsAsciiSpace(*s) && s < mailbox_end)
00735         s++;
00736 
00737       /* Copy up to (not including) the >
00738        */
00739       while (s < mailbox_end)
00740       {
00741         if (*s == '\"' && !quote_addrs_p)
00742         {
00743           s++;
00744           continue;
00745         }
00746         if (*s == '\\' && !quote_addrs_p)
00747         {
00748           s++;
00749           if (s < mailbox_end && (*s == '\\' || *s == '\"'))
00750             *addr_out++ = *s++;
00751           continue;
00752         }
00753         COPY_CHAR(addr_out, s);
00754         NEXT_CHAR(s);
00755       }
00756 
00757       TRIM_WHITESPACE(addr_start, addr_out, 0);
00758     }
00759     /* No component of <mailbox> form.
00760      */
00761     else
00762     {
00763       TRIM_WHITESPACE(addr_start, addr_out, 0);
00764       TRIM_WHITESPACE(name_start, name_out, 0);
00765     }
00766 
00767     /* Now re-quote the names and addresses if necessary.
00768      */
00769 #ifdef BUG11892
00770     // **** jefft - we don't want and shouldn't to requtoe the name, this
00771     // violate the RFC822 spec
00772     if (quote_names_p && names)
00773     {
00774       int L = name_out - name_start - 1;
00775       L = msg_quote_phrase_or_addr(name_start, L, PR_FALSE);
00776       name_out = name_start + L + 1;
00777     }
00778 #endif 
00779 
00780     if (quote_addrs_p && addresses)
00781     {
00782       int L = addr_out - addr_start - 1;
00783       L = msg_quote_phrase_or_addr(addr_start, L, PR_TRUE);
00784       addr_out = addr_start + L + 1;
00785     }
00786 
00787     addr_count++;
00788 
00789     /* If we only want the first address, we can stop now.
00790      */
00791     if (first_only_p)
00792       break;
00793 
00794     if (*line_end)
00795       NEXT_CHAR(line_end);
00796 
00797     /* Skip over extra whitespace or commas between addresses. */
00798     while (*line_end && (nsCRT::IsAsciiSpace(*line_end) || *line_end == ','))
00799       line_end++;
00800 
00801     this_start = line_end;
00802     name_start = name_out;
00803     addr_start = addr_out;
00804   }
00805 
00806   /* Make one more pass through and convert all whitespace characters
00807    * to SPC.  We could do that in the first pass, but this is simpler.
00808    */
00809   {
00810     char *s;
00811     for (s = name_buf; s < name_out; NEXT_CHAR(s))
00812       if (nsCRT::IsAsciiSpace(*s) && *s != ' ')
00813         *s = ' ';
00814     for (s = addr_buf; s < addr_out; NEXT_CHAR(s))
00815       if (nsCRT::IsAsciiSpace(*s) && *s != ' ')
00816         *s = ' ';
00817   }
00818 
00819   if (names)
00820     *names = name_buf;
00821   else
00822     PR_Free(name_buf);
00823 
00824   if (addresses)
00825     *addresses = addr_buf;
00826   else
00827     PR_Free(addr_buf);
00828 
00829   return addr_count;
00830 }
00831 
00832 /* msg_quote_phrase_or_addr
00833  *
00834  * Given a single mailbox, this quotes the characters in it which need
00835  * to be quoted; it writes into `address' and returns a new length.
00836  * `address' is assumed to be long enough; worst case, its size will
00837  * be (N*2)+2.
00838  */
00839 static int
00840 msg_quote_phrase_or_addr(char *address, PRInt32 length, PRBool addr_p)
00841 {
00842   int quotable_count = 0, in_quote = 0;
00843   int unquotable_count = 0;
00844   PRInt32 new_length, full_length = length;
00845   char *in, *out, *orig_out, *atsign = NULL, *orig_address = address;
00846   PRBool user_quote = PR_FALSE;
00847   PRBool quote_all = PR_FALSE;
00848 
00849   /* If the entire address is quoted, fall out now. */
00850   if (address[0] == '\"' && address[length - 1] == '\"')
00851      return length;
00852 
00853   /* Check to see if there is a routing prefix.  If there is one, we can
00854    * skip quoting it because by definition it can't need to be quoted.
00855    */
00856   if (addr_p && *address && *address == '@')
00857   {
00858     for (in = address; *in; NEXT_CHAR(in))
00859     {
00860       if (*in == ':')
00861       {
00862         length -= ++in - address;
00863         address = in;
00864         break;
00865       }
00866       else if (!nsCRT::IsAsciiDigit(*in) && !nsCRT::IsAsciiAlpha(*in) && *in != '@' && *in != '.')
00867         break;
00868     }
00869   }
00870 
00871     for (in = address; in < address + length; NEXT_CHAR(in))
00872     {
00873         if (*in == 0)
00874             return full_length; /* #### horrible kludge... */
00875 
00876         else if (*in == '@' && addr_p && !atsign && !in_quote)
00877     {
00878             /* Exactly one unquoted at-sign is allowed in an address. */
00879       if (atsign)
00880         quotable_count++;
00881             atsign = in;
00882 
00883       /* If address is of the form '"userid"@somewhere.com' don't quote
00884        * the quotes around 'userid'.  Also reset the quotable count, since
00885        * any quotables we've seen are already inside quotes.
00886        */
00887       if (address[0] == '\"' && in > address + 2 && *(in - 1) == '\"' && *(in - 2) != '\\')
00888         unquotable_count -= 2, quotable_count = 0, user_quote = PR_TRUE;
00889     }
00890 
00891         else if (*in == '\\')
00892         {
00893             if (in + 1 < address + length && (*(in + 1) == '\\' || *(in + 1) == '\"'))
00894                 /* If the next character is a backslash or quote, this backslash */
00895                 /* is an escape backslash; ignore it and the next character.     */
00896                 in++;
00897             else
00898                 /* If the name contains backslashes or quotes, they must be escaped. */
00899                 unquotable_count++;
00900         }
00901 
00902         else if (*in == '\"')
00903             /* If the name contains quotes, they must be escaped. */
00904             unquotable_count++, in_quote = !in_quote;
00905 
00906         else if (  /* *in >= 127 || *in < 0  ducarroz: 8bits characters will be mime encoded therefore they are not a problem
00907              ||*/ (*in == ';' && !addr_p) || *in == '$' || *in == '(' || *in == ')'
00908                  || *in == '<' || *in == '>' || *in == '@' || *in == ',') 
00909             /* If the name contains control chars or Header specials, it needs to
00910              * be enclosed in quotes.  Double-quotes and backslashes will be dealt
00911              * with separately.
00912              *
00913              * The ":" character is explicitly not in this list, though Header says
00914              * it should be quoted, because that has been seen to break VMS
00915              * systems.  (Rather, it has been seen that there are Unix SMTP servers
00916              * which accept RCPT TO:<host::user> but not RCPT TO:<"host::user"> or
00917              * RCPT TO:<host\:\:user>, which is the syntax that VMS/DECNET hosts
00918              * use.
00919              *
00920              * For future reference: it is also claimed that some VMS SMTP servers
00921              * allow \ quoting but not "" quoting; and that sendmail uses self-
00922              * contradcitory quoting conventions that violate both RFCs 821 and
00923              * 822, so any address quoting on a sendmail system will lose badly.
00924              *
00925              * The ";" character in an address is a group delimiter, therefore it
00926              * should not be quoted in that case.
00927              */
00928       quotable_count++;
00929 
00930     else if (!atsign && (*in == '[' || *in == ']'))
00931       /* Braces are normally special characters, except when they're
00932        * used for domain literals (e.g. johndoe@[127.0.0.1].acme.com).
00933        */
00934       quotable_count++;
00935 
00936         else if (addr_p && *in == ' ')
00937             /* Naked spaces are allowed in names, but not addresses. */
00938             quotable_count++;
00939 
00940         else if (   !addr_p
00941              && (*in == '.' || *in == '!' || *in == '$' || *in == '%'))
00942             /* Naked dots are allowed in addresses, but not in names.
00943              * The other characters (!$%) are technically allowed in names, but
00944              * are surely going to cause someone trouble, so we quote them anyway.
00945              */
00946             quotable_count++;
00947     }
00948 
00949     if (quotable_count == 0 && unquotable_count == 0)
00950         return full_length;
00951 
00952   /* We must quote the entire string if there are quotables outside the user
00953    * quote.
00954    */
00955   if (!atsign || (user_quote && quotable_count > 0))
00956     quote_all = PR_TRUE, atsign = NULL;
00957 
00958     /* Add 2 to the length for the quotes, plus one for each character
00959      * which will need a backslash, plus one for a null terminator.
00960      */
00961     new_length = length + quotable_count + unquotable_count + 3;
00962 
00963     in = address;
00964     out = orig_out = (char *)PR_Malloc(new_length);
00965   if (!out)
00966   {
00967     *orig_address = 0;
00968     return 0;
00969   }
00970 
00971   /* Start off with a quote.
00972    */
00973   *out++ = '\"';
00974 
00975   while (*in)
00976   {
00977     if (*in == '@')
00978     {
00979       if (atsign == in)
00980         *out++ = '\"';
00981       *out++ = *in++;
00982       continue;
00983     }
00984         else if (*in == '\"')
00985     {
00986       if (!user_quote || (in != address && in != atsign - 1))
00987         *out++ = '\\';
00988       *out++ = *in++;
00989       continue;
00990     }
00991         else if (*in == '\\')
00992         {
00993         if (*(in + 1) == '\\' || *(in + 1) == '\"')
00994           *out++ = *in++;
00995       else
00996                 *out++ = '\\';
00997       *out++ = *in++;
00998       continue;
00999         }
01000     else
01001       COPY_CHAR(out, in);
01002 
01003     NEXT_CHAR(in);
01004   }
01005 
01006   /* Add a final quote if we are quoting the entire string.
01007    */
01008   if (quote_all)
01009     *out++ = '\"';
01010   *out++ = 0;
01011 
01012   NS_ASSERTION(new_length >= (out - orig_out), "");
01013   memcpy(address, orig_out, new_length);
01014   PR_FREEIF(orig_out); /* make sure we release the string we allocated */
01015 
01016   return full_length + unquotable_count + 2;
01017 }
01018 
01019 /* msg_unquote_phrase_or_addr
01020  *
01021  * Given a name or address that might have been quoted
01022  * it will take out the escape and double quotes
01023  * The caller is responsible for freeing the resulting
01024  * string.
01025  */
01026 static nsresult
01027 msg_unquote_phrase_or_addr(const char *line, PRBool preserveIntegrity, char **lineout)
01028 {
01029   if (!line || !lineout)
01030     return NS_OK;
01031 
01032   /* If the first character isn't a double quote, there is nothing to do
01033    */
01034   if (*line != '\"')
01035   {
01036     *lineout = nsCRT::strdup(line);
01037     if (!*lineout)
01038       return NS_ERROR_OUT_OF_MEMORY;
01039     else
01040       return NS_OK;
01041   }
01042 
01043   /* in preserveIntegrity mode, we must preserve the quote if the name contains a comma */
01044   if (preserveIntegrity)
01045   {
01046     const char * open_quote = nsnull;
01047     const char * comma = nsnull;;
01048     const char * at_sign = nsnull;
01049     const char * readPos = line + 1;
01050 
01051     while (*readPos)
01052     {
01053       if (*readPos == ',')
01054       {
01055         if (!open_quote)
01056           comma = readPos;
01057       }
01058       else if (*readPos == '@')
01059       {
01060         at_sign = readPos;
01061         break;
01062       }
01063       else if (*readPos == '"')
01064       {
01065         if (!open_quote)
01066           open_quote = readPos;
01067         else
01068           open_quote = nsnull;
01069       }
01070 
01071       readPos ++;
01072     }
01073 
01074     if (comma && at_sign)
01075     {
01076       *lineout = nsCRT::strdup(line);
01077       if (!*lineout)
01078         return NS_ERROR_OUT_OF_MEMORY;
01079       else
01080         return NS_OK;
01081     }
01082   }
01083 
01084   /* Don't copy the first double quote
01085    */
01086   *lineout = nsCRT::strdup(line + 1);
01087   if (!*lineout)
01088     return NS_ERROR_OUT_OF_MEMORY;
01089 
01090   const char *lineptr = line + 1; 
01091   char *outptr = *lineout;
01092   PRBool escaped = PR_FALSE;
01093 
01094   while (*lineptr)
01095   {
01096     /* If the character is an '\' then output the character that was
01097      * escaped.  If it was part of the quote then don't output it.
01098      */
01099     if (*lineptr == '\\')
01100     {
01101       escaped = PR_TRUE;
01102       lineptr++;
01103     }
01104     if (*lineptr == '\"' && !escaped)
01105       lineptr++;
01106     escaped = PR_FALSE;
01107 
01108     if (*lineptr)
01109     {
01110       COPY_CHAR(outptr, lineptr);
01111       NEXT_CHAR(lineptr);
01112     }
01113   }
01114   *outptr = '\0';
01115   
01116   return NS_OK;
01117 }
01118 
01119 /* msg_extract_Header_address_mailboxes
01120  *
01121  * Given a string which contains a list of Header addresses, returns a
01122  * comma-separated list of just the `mailbox' portions.
01123  */
01124 static char *
01125 msg_extract_Header_address_mailboxes(const char *line)
01126 {
01127   char *addrs = 0;
01128   char *result, *s, *out;
01129   PRUint32 i, size = 0;
01130   int status = msg_parse_Header_addresses(line, NULL, &addrs);
01131   if (status <= 0)
01132     return NULL;
01133 
01134   s = addrs;
01135   for (i = 0; (int) i < status; i++)
01136   {
01137     PRUint32 j = strlen(s);
01138     s += j + 1;
01139     size += j + 2;
01140   }
01141 
01142   result = (char*)PR_Malloc(size + 1);
01143   if (!result)
01144   {
01145     PR_Free(addrs);
01146     return 0;
01147   }
01148   out = result;
01149   s = addrs;
01150   for (i = 0; (int)i < status; i++)
01151   {
01152     PRUint32 j = strlen(s);
01153     memcpy(out, s, j);
01154     out += j;
01155     if ((int)(i+1) < status)
01156     {
01157       *out++ = ',';
01158       *out++ = ' ';
01159     }
01160     s += j + 1;
01161   }
01162   *out = 0;
01163 
01164   PR_Free(addrs);
01165   return result;
01166 }
01167 
01168 
01169 /* msg_extract_Header_address_names
01170  *
01171  * Given a string which contains a list of Header addresses, returns a
01172  * comma-separated list of just the `user name' portions.  If any of
01173  * the addresses doesn't have a name, then the mailbox is used instead.
01174  *
01175  * The names are *unquoted* and therefore cannot be re-parsed in any way.
01176  * They are, however, nice and human-readable.
01177  */
01178 static char *
01179 msg_extract_Header_address_names(const char *line)
01180 {
01181   char *names = 0;
01182   char *addrs = 0;
01183   char *result, *s1, *s2, *out;
01184   PRUint32 i, size = 0;
01185   int status = msg_parse_Header_addresses(line, &names, &addrs);
01186   if (status <= 0)
01187     return 0;
01188   PRUint32 len1, len2;
01189 
01190   s1 = names;
01191   s2 = addrs;
01192   for (i = 0; (int)i < status; i++)
01193   {
01194     len1 = strlen(s1);
01195     len2 = strlen(s2);
01196     s1 += len1 + 1;
01197     s2 += len2 + 1;
01198     size += (len1 ? len1 : len2) + 2;
01199   }
01200 
01201   result = (char *)PR_Malloc(size + 1);
01202   if (!result)
01203   {
01204     PR_Free(names);
01205     PR_Free(addrs);
01206     return 0;
01207   }
01208 
01209   out = result;
01210   s1 = names;
01211   s2 = addrs;
01212   for (i = 0; (int)i < status; i++)
01213   {
01214     len1 = strlen(s1);
01215     len2 = strlen(s2);
01216 
01217     if (len1)
01218     {
01219       memcpy(out, s1, len1);
01220       out += len1;
01221     }
01222     else
01223     {
01224       memcpy(out, s2, len2);
01225       out += len2;
01226     }
01227 
01228     if ((int)(i+1) < status)
01229     {
01230       *out++ = ',';
01231       *out++ = ' ';
01232     }
01233     s1 += len1 + 1;
01234     s2 += len2 + 1;
01235   }
01236   *out = 0;
01237 
01238   PR_Free(names);
01239   PR_Free(addrs);
01240   return result;
01241 }
01242 
01243 /* msg_extract_Header_address_name
01244  *
01245  * Like MSG_ExtractHeaderAddressNames(), but only returns the first name
01246  * in the list, if there is more than one. 
01247  */
01248 static char *
01249 msg_extract_Header_address_name(const char *line)
01250 {
01251   char *name = 0;
01252   char *addr = 0;
01253   int status = msg_parse_Header_addresses(line, &name, &addr, PR_FALSE, PR_FALSE, PR_TRUE);
01254   if (status <= 0)
01255     return 0;
01256 
01257   /* This can happen if there is an address like "From: foo bar" which
01258    * we parse as two addresses (that's a syntax error.)  In that case,
01259    * we'll return just the first one (the rest is after the NULL.)
01260    *
01261    * NS_ASSERTION(status == 1);
01262    */
01263   if (name && *name)
01264   {
01265     FREEIF(addr);
01266     return name;
01267   }
01268   else
01269   {
01270     FREEIF(name);
01271     return addr;
01272   }
01273 }
01274 
01275 /* msg_format_Header_addresses
01276  */
01277 static char *
01278 msg_format_Header_addresses (const char *names, const char *addrs,
01279                int count, PRBool wrap_lines_p)
01280 {
01281   char *result, *out;
01282   const char *s1, *s2;
01283   PRUint32 i, size = 0;
01284   PRUint32 column = 10;
01285   PRUint32 len1, len2;
01286   PRUint32 name_maxlen = 0;
01287   PRUint32 addr_maxlen = 0;
01288 
01289   if (count <= 0)
01290     return 0;
01291 
01292   s1 = names;
01293   s2 = addrs;
01294   for (i = 0; (int)i < count; i++)
01295   {
01296     len1 = strlen(s1);
01297     len2 = strlen(s2);
01298     s1 += len1 + 1;
01299     s2 += len2 + 1;
01300     
01301     len1 = (len1 * 2) + 2;  //(N*2) + 2 in case we need to quote it
01302     len2 = (len2 * 2) + 2;
01303     name_maxlen = PR_MAX(name_maxlen, len1);
01304     addr_maxlen = PR_MAX(addr_maxlen, len2);
01305     size += len1 + len2 + 10;
01306   }
01307 
01308   result = (char *)PR_Malloc(size + 1);
01309   char *aName = (char *)PR_Malloc(name_maxlen + 1);
01310   char *anAddr = (char *)PR_Malloc(addr_maxlen + 1);
01311   if (!result || !aName || !anAddr)
01312   {
01313     PR_FREEIF(result);
01314     PR_FREEIF(aName);
01315     PR_FREEIF(anAddr);
01316     return 0;
01317   }
01318 
01319   out = result;
01320   s1 = names;
01321   s2 = addrs;
01322 
01323   for (i = 0; (int)i < count; i++)
01324   {
01325     char *o;
01326     
01327     PL_strncpy(aName, s1, name_maxlen);
01328     PL_strncpy(anAddr, s2, addr_maxlen);
01329     len1 = msg_quote_phrase_or_addr(aName, strlen(s1), PR_FALSE);
01330     len2 = msg_quote_phrase_or_addr(anAddr, strlen(s2), PR_TRUE);
01331 
01332     if (   wrap_lines_p && i > 0
01333         && (column + len1 + len2 + 3 + (((int)(i+1) < count) ? 2 : 0) > 76))
01334     {
01335       if (out > result && out[-1] == ' ')
01336         out--;
01337       *out++ = nsCRT::CR;
01338       *out++ = nsCRT::LF;
01339       *out++ = '\t';
01340       column = 8;
01341     }
01342 
01343     o = out;
01344 
01345     if (len1)
01346     {
01347       memcpy(out, aName, len1);
01348       out += len1;
01349       *out++ = ' ';
01350       *out++ = '<';
01351     }
01352     memcpy(out, anAddr, len2);
01353     out += len2;
01354     if (len1)
01355       *out++ = '>';
01356 
01357     if ((int)(i+1) < count)
01358     {
01359       *out++ = ',';
01360       *out++ = ' ';
01361     }
01362     s1 += strlen(s1) + 1;
01363     s2 += strlen(s2) + 1;
01364 
01365     column += (out - o);
01366   }
01367   *out = 0;
01368   
01369   PR_FREEIF(aName);
01370   PR_FREEIF(anAddr);
01371   
01372   return result;
01373 }
01374 
01375 /* msg_reformat_Header_addresses
01376  *
01377  * Given a string which contains a list of Header addresses, returns a new
01378  * string with the same data, but inserts missing commas, parses and reformats
01379  * it, and wraps long lines with newline-tab.
01380  */
01381 static char *
01382 msg_reformat_Header_addresses(const char *line)
01383 {
01384   char *names = 0;
01385   char *addrs = 0;
01386   char *result;
01387   int status = msg_parse_Header_addresses(line, &names, &addrs);
01388   if (status <= 0)
01389     return 0;
01390   result = msg_format_Header_addresses(names, addrs, status, PR_TRUE);
01391   PR_Free (names);
01392   PR_Free (addrs);
01393   return result;
01394 }
01395 
01396 /* msg_remove_duplicate_addresses
01397  *
01398  * Returns a copy of ADDRS which may have had some addresses removed.
01399  * Addresses are removed if they are already in either ADDRS or OTHER_ADDRS.
01400  * (If OTHER_ADDRS contain addresses which are not in ADDRS, they are not
01401  * added.  That argument is for passing in addresses that were already
01402  * mentioned in other header fields.)
01403  *
01404  * Addresses are considered to be the same if they contain the same mailbox
01405  * part (case-insensitive.)  Real names and other comments are not compared.
01406  *
01407  * removeAliasesToMe allows the address parser to use the preference which
01408  * contains regular expressions which also mean 'me' for the purpose of
01409  * stripping the user's email address(es) out of addrs
01410  */
01411 static char *
01412 msg_remove_duplicate_addresses(const char *addrs, const char *other_addrs,
01413                                PRBool removeAliasesToMe)
01414 {
01415   if (!addrs) return 0;
01416 
01417   /* This is probably way more complicated than it should be... */
01418   char *s1 = 0, *s2 = 0;
01419   char *output = 0, *out = 0;
01420   char *result = 0;
01421   int count1 = 0, count2 = 0, count3 = 0;
01422   int size1 = 0, size2 = 0, size3 = 0;
01423   char *names1 = 0, *names2 = 0;
01424   char *addrs1 = 0, *addrs2 = 0;
01425   char **a_array1 = 0, **a_array2 = 0, **a_array3 = 0;
01426   char **n_array1 = 0,                 **n_array3 = 0;
01427   int i, j;
01428   PRUint32 addedlen = 0;
01429 
01430   count1 = msg_parse_Header_addresses(addrs, &names1, &addrs1);
01431   if (count1 < 0) goto FAIL;
01432   if (count1 == 0)
01433   {
01434     result = nsCRT::strdup("");
01435     goto FAIL;
01436   }
01437   if (other_addrs)
01438     count2 = msg_parse_Header_addresses(other_addrs, &names2, &addrs2);
01439   if (count2 < 0) goto FAIL;
01440 
01441   s1 = names1;
01442   s2 = addrs1;
01443   for (i = 0; i < count1; i++)
01444   {
01445     PRUint32 len1 = strlen(s1);
01446     PRUint32 len2 = strlen(s2);
01447     s1 += len1 + 1;
01448     s2 += len2 + 1;
01449     size1 += len1 + len2 + 10;
01450   }
01451 
01452   s1 = names2;
01453   s2 = addrs2;
01454   for (i = 0; i < count2; i++)
01455   {
01456     PRUint32 len1 = strlen(s1);
01457     PRUint32 len2 = strlen(s2);
01458     s1 += len1 + 1;
01459     s2 += len2 + 1;
01460     size2 += len1 + len2 + 10;
01461   }
01462 
01463   a_array1 = (char **)PR_Malloc(count1 * sizeof(char *));
01464   if (!a_array1) goto FAIL;
01465   n_array1 = (char **)PR_Malloc(count1 * sizeof(char *));
01466   if (!n_array1) goto FAIL;
01467 
01468   if (count2 > 0)
01469   {
01470     a_array2 = (char **)PR_Malloc(count2 * sizeof(char *));
01471     if (!a_array2) goto FAIL;
01472     /* don't need an n_array2 */
01473   }
01474 
01475   a_array3 = (char **)PR_Malloc(count1 * sizeof(char *));
01476   if (!a_array3) goto FAIL;
01477   n_array3 = (char **)PR_Malloc(count1 * sizeof(char *));
01478   if (!n_array3) goto FAIL;
01479 
01480 
01481   /* fill in the input arrays */
01482   s1 = names1;
01483   s2 = addrs1;
01484   for (i = 0; i < count1; i++)
01485   {
01486     n_array1[i] = s1;
01487     a_array1[i] = s2;
01488     s1 += strlen(s1) + 1;
01489     s2 += strlen(s2) + 1;
01490   }
01491 
01492   s2 = addrs2;
01493   for (i = 0; i < count2; i++)
01494   {
01495     a_array2[i] = s2;
01496     s2 += strlen(s2) + 1;
01497   }
01498 
01499   /* Iterate over all addrs in the "1" arrays.
01500    * If those addrs are not present in "3" or "2", add them to "3".
01501    */
01502   for (i = 0; i < count1; i++)
01503   {
01504     PRBool found = PR_FALSE;
01505     for (j = 0; j < count2; j++)
01506       if (!nsCRT::strcasecmp (a_array1[i], a_array2[j]))
01507       {
01508         found = PR_TRUE;
01509         break;
01510       }
01511 
01512     if (!found)
01513       for (j = 0; j < count3; j++)
01514         if (!nsCRT::strcasecmp(a_array1[i], a_array3[j]))
01515         {
01516           found = PR_TRUE;
01517           break;
01518         }
01519 /* HACK ALERT!!!! TEMPORARILY COMMENTING OUT UNTIL WE PORT MSG_PREFS INTO THE MOZILLA TREE!!!!!! */
01520 #if 0
01521     if (!found && removeAliasesToMe)
01522     {
01523       found = MSG_Prefs::IsEmailAddressAnAliasForMe(a_array1[i]);
01524       if (found)
01525         break;
01526     }
01527 #endif
01528     if (!found)
01529     {
01530       n_array3[count3] = n_array1[i];
01531       a_array3[count3] = a_array1[i];
01532       size3 += (strlen(n_array3[count3]) + strlen(a_array3[count3]) + 10);
01533       count3++;
01534       NS_ASSERTION (count3 <= count1, "");
01535       if (count3 > count1) break;
01536     }
01537   }
01538 
01539   PRUint32 outlen;
01540   outlen = size3 + 1;
01541   output = (char *)PR_Malloc(outlen);
01542   if (!output) goto FAIL;
01543 
01544   *output = 0;
01545   out = output;
01546   s2 = output;
01547   for (i = 0; i < count3; i++)
01548   {
01549     PL_strncpyz(out, a_array3[i], outlen);
01550     addedlen = strlen(out);
01551     outlen -= addedlen;
01552     out += addedlen;
01553     *out++ = 0;
01554   }
01555   s1 = out;
01556   for (i = 0; i < count3; i++)
01557   {
01558     PL_strncpyz(out, n_array3[i], outlen);
01559     addedlen = strlen(out);
01560     outlen -= addedlen;
01561     out += addedlen;
01562     *out++ = 0;
01563   }
01564   result = msg_format_Header_addresses(s1, s2, count3, PR_FALSE);
01565 
01566  FAIL:
01567   FREEIF(a_array1);
01568   FREEIF(a_array2);
01569   FREEIF(a_array3);
01570   FREEIF(n_array1);
01571   FREEIF(n_array3);
01572   FREEIF(names1);
01573   FREEIF(names2);
01574   FREEIF(addrs1);
01575   FREEIF(addrs2);
01576   FREEIF(output);
01577   return result;
01578 }
01579 
01580 
01581 /* msg_make_full_address
01582  *
01583  * Given an e-mail address and a person's name, cons them together into a
01584  * single string of the form "name <address>", doing all the necessary quoting.
01585  * A new string is returned, which you must free when you're done with it.
01586  */
01587 static char *
01588 msg_make_full_address(const char* name, const char* addr)
01589 {
01590   int nl = name ? strlen (name) : 0;
01591   int al = addr ? strlen (addr) : 0;
01592   char *buf, *s;
01593   PRUint32 buflen, slen;
01594   int L;
01595   if (al == 0)
01596     return 0;
01597   buflen = (nl * 2) + (al * 2) + 25;
01598   buf = (char *)PR_Malloc(buflen);
01599   if (!buf)
01600     return 0;
01601   if (nl > 0)
01602   {
01603     PL_strncpyz(buf, name, buflen);
01604     L = msg_quote_phrase_or_addr(buf, nl, PR_FALSE);
01605     s = buf + L;
01606     slen = buflen - L;
01607     if ( slen > 2 ) {
01608     *s++ = ' ';
01609     *s++ = '<';
01610        slen -= 2; // for ' ' and '<'
01611     }
01612   }
01613   else
01614   {
01615     s = buf;
01616     slen = buflen;
01617   }
01618 
01619   PL_strncpyz(s, addr, slen);
01620   L = msg_quote_phrase_or_addr(s, al, PR_TRUE);
01621   s += L;
01622   if (nl > 0)
01623     *s++ = '>';
01624   *s = 0;
01625   L = (s - buf) + 1;
01626   buf = (char *)PR_Realloc (buf, L);
01627   return buf;
01628 }