Back to index

lightning-sunbird  0.9+nobinonly
comi18n.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Pierre Phaneuf <pp@ludusdesign.com>
00024  *   John G Myers   <jgmyers@netscape.com>
00025  *   Takayuki Tei   <taka@netscape.com>
00026  *
00027  * Alternatively, the contents of this file may be used under the terms of
00028  * either of the GNU General Public License Version 2 or later (the "GPL"),
00029  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00030  * in which case the provisions of the GPL or the LGPL are applicable instead
00031  * of those above. If you wish to allow use of your version of this file only
00032  * under the terms of either the GPL or the LGPL, and not to allow others to
00033  * use your version of this file under the terms of the MPL, indicate your
00034  * decision by deleting the provisions above and replace them with the notice
00035  * and other provisions required by the GPL or the LGPL. If you do not delete
00036  * the provisions above, a recipient may use your version of this file under
00037  * the terms of any one of the MPL, the GPL or the LGPL.
00038  *
00039  * ***** END LICENSE BLOCK ***** */
00040 
00041 #include "nsICharsetConverterManager.h"
00042 #include "nsICharsetAlias.h"
00043 #include <stdio.h>
00044 #include <stdlib.h>
00045 #include <string.h>
00046 #include <assert.h>
00047 #include "prtypes.h"
00048 #include "prmem.h"
00049 #include "prlog.h"
00050 #include "prprf.h"
00051 #include "plstr.h"
00052 #include "nsCRT.h"
00053 #include "comi18n.h"
00054 #include "nsIServiceManager.h"
00055 #include "nsIStringCharsetDetector.h"
00056 #include "nsIPrefService.h"
00057 #include "nsIPrefBranch.h"
00058 #include "nsMsgUtils.h"
00059 #include "mimebuf.h"
00060 #include "nsMsgI18N.h"
00061 #include "nsMimeTypes.h"
00062 #include "nsICharsetConverterManager.h"
00063 #include "nsISaveAsCharset.h"
00064 #include "nsHankakuToZenkakuCID.h"
00065 #include "nsReadableUtils.h"
00066 #include "mimehdrs.h"
00067 #include "nsIMIMEHeaderParam.h"
00068 #include "nsNetCID.h"
00069 
00070 
00072 
00073 static char basis_64[] =
00074    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
00075 
00076 #define NO_Q_ENCODING_NEEDED(ch)  \
00077   (((ch) >= 'a' && (ch) <= 'z') || ((ch) >= 'A' && (ch) <= 'Z') || \
00078    ((ch) >= '0' && (ch) <= '9') ||  (ch) == '!' || (ch) == '*' ||  \
00079     (ch) == '+' || (ch) == '-' || (ch) == '/')
00080 
00081 static const char hexdigits[] = "0123456789ABCDEF";
00082 
00083 static PRInt32
00084 intlmime_encode_q(const unsigned char *src, PRInt32 srcsize, char *out)
00085 {
00086   const unsigned char *in = (unsigned char *) src;
00087   const unsigned char *end = in + srcsize;
00088   char *head = out;
00089 
00090   for (; in < end; in++) {
00091     if (NO_Q_ENCODING_NEEDED(*in)) {
00092       *out++ = *in;
00093     }
00094     else if (*in == ' ') {
00095       *out++ = '_';
00096     }
00097     else {
00098       *out++ = '=';
00099       *out++ = hexdigits[*in >> 4];
00100       *out++ = hexdigits[*in & 0xF];
00101     }
00102   }
00103   *out = '\0';
00104   return (out - head);
00105 }
00106 
00107 static void
00108 encodeChunk(const unsigned char* chunk, char* output)
00109 {
00110   register PRInt32 offset;
00111 
00112   offset = *chunk >> 2;
00113   *output++ = basis_64[offset];
00114 
00115   offset = ((*chunk << 4) & 0x30) + (*(chunk+1) >> 4);
00116   *output++ = basis_64[offset];
00117 
00118   if (*(chunk+1)) {
00119     offset = ((*(chunk+1) & 0x0f) << 2) + ((*(chunk+2) & 0xc0) >> 6);
00120     *output++ = basis_64[offset];
00121   }
00122   else
00123     *output++ = '=';
00124 
00125   if (*(chunk+2)) {
00126     offset = *(chunk+2) & 0x3f;
00127     *output++ = basis_64[offset];
00128   }
00129   else
00130     *output++ = '=';
00131 }
00132 
00133 static PRInt32
00134 intlmime_encode_b(const unsigned char* input, PRInt32 inlen, char* output)
00135 {
00136   unsigned char  chunk[3];
00137   PRInt32   i, len;
00138   char *head = output;
00139 
00140   for (len = 0; inlen >=3 ; inlen -= 3) {
00141     for (i = 0; i < 3; i++)
00142       chunk[i] = *input++;
00143     encodeChunk(chunk, output);
00144     output += 4;
00145     len += 4;
00146   }
00147 
00148   if (inlen > 0) {
00149     for (i = 0; i < inlen; i++)
00150       chunk[i] = *input++;
00151     for (; i < 3; i++)
00152       chunk[i] = 0;
00153     encodeChunk(chunk, output);
00154     output += 4;
00155   }
00156 
00157   *output = 0;
00158   return (output - head);
00159 }
00160 
00161 
00162 /*      some utility function used by this file */
00163 static PRBool intlmime_only_ascii_str(const char *s)
00164 {
00165   for(; *s; s++)
00166     if(*s & 0x80)
00167       return PR_FALSE;
00168   return PR_TRUE;
00169 }
00170 
00171 
00172 #define IS_UTF8_HEADER(s, h) ((*(s) & (~(~(h) >> 1))) == (h))
00173 #define IS_UTF8_SUBBYTE(s) ((*(s) & 0xC0) == 0x80)
00174 static unsigned char * utf8_nextchar(unsigned char *str)
00175 {
00176   if (*str < 0x80)
00177     return (str + 1);
00178   if (IS_UTF8_HEADER(str, 0xC0) &&
00179       IS_UTF8_SUBBYTE(str + 1))
00180     return (str + 2);
00181   if (IS_UTF8_HEADER(str, 0xE0) &&
00182       IS_UTF8_SUBBYTE(str + 1) &&
00183       IS_UTF8_SUBBYTE(str + 2) )
00184     return (str + 3);
00185   if (IS_UTF8_HEADER(str, 0xF0) &&
00186       IS_UTF8_SUBBYTE(str + 1) &&
00187       IS_UTF8_SUBBYTE(str + 2) &&
00188       IS_UTF8_SUBBYTE(str + 3) )
00189     return (str + 4);
00190   // error, return + 1 to avoid infinite loop
00191   return (str + 1); 
00192 }
00193 #undef IS_UTF8_HEADER
00194 #undef IS_UTF8_SUBBYTE
00195 
00196 /* -------------- */
00197 
00198 static
00199 PRInt32 generate_encodedwords(char *pUTF8, const char *charset, char method, char *output, PRInt32 outlen, PRInt32 output_carryoverlen, PRInt32 foldlen, PRBool foldingonly)
00200 {
00201   NS_ASSERTION(output_carryoverlen > 0, "output_carryoverlen must be > 0"); 
00202 
00203   nsCOMPtr <nsISaveAsCharset> conv;
00204   PRUnichar *_pUCS2 = nsnull, *pUCS2 = nsnull, *pUCS2Head = nsnull, cUCS2Tmp = 0;
00205   char  *ibuf, *o = output;
00206   char  encodedword_head[kMAX_CSNAME+4+1];
00207   nsCAutoString _charset;
00208   char  *pUTF8Head = nsnull, cUTF8Tmp = 0;
00209   PRInt32   olen = 0, obufsize = outlen, offset, linelen = output_carryoverlen, convlen = 0;
00210   PRInt32   encodedword_headlen = 0, encodedword_taillen = foldingonly ? 0 : 2; // "?="
00211   nsresult rv;
00212 
00213   encodedword_head[0] = 0;
00214 
00215   if (!foldingonly) {
00216 
00217     // Deal with UCS2 pointer
00218     pUCS2 = _pUCS2 = ToNewUnicode(NS_ConvertUTF8toUCS2(pUTF8));
00219     if (!pUCS2)
00220       return -1;
00221 
00222     // Resolve charset alias
00223     {
00224       nsCOMPtr <nsICharsetAlias> calias = do_GetService(NS_CHARSETALIAS_CONTRACTID, &rv);
00225       nsCOMPtr <nsIAtom> charsetAtom;
00226       charset = !nsCRT::strcasecmp(charset, "us-ascii") ? "ISO-8859-1" : charset;
00227       rv = calias->GetPreferred(nsDependentCString(charset),
00228                                 _charset);
00229       if (NS_FAILED(rv)) {
00230         if (_pUCS2)
00231           nsMemory::Free(_pUCS2);
00232         return -1;
00233       }
00234 
00235       // this is so nasty, but _charset won't be going away..
00236       charset = _charset.get();
00237     }
00238 
00239     // Prepare MIME encoded-word head with official charset name
00240     if (PR_snprintf(encodedword_head, sizeof(encodedword_head)-1, "=?%s?%c?", charset, method) < 0) {
00241       if (_pUCS2)
00242         nsMemory::Free(_pUCS2);
00243       return -1;
00244     }
00245     encodedword_headlen = strlen(encodedword_head);
00246 
00247     // Load HANKAKU-KANA prefrence and cache it.
00248     if (!nsCRT::strcasecmp("ISO-2022-JP", charset)) {
00249       static PRInt32  conv_kana = -1;
00250       if (conv_kana < 0) {
00251         nsCOMPtr<nsIPrefBranch> prefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
00252         if (NS_SUCCEEDED(rv)) {
00253           PRBool val = PR_FALSE; // no pref means need the mapping
00254           prefBranch->GetBoolPref("mailnews.send_hankaku_kana", &val);
00255           conv_kana = val ? 0 : 1;
00256         }
00257       }
00258       // Do nsITextTransform if needed
00259       if (conv_kana > 0) {
00260         nsCOMPtr <nsITextTransform> textTransform =
00261             do_CreateInstance(NS_HANKAKUTOZENKAKU_CONTRACTID, &rv);
00262         if (NS_SUCCEEDED(rv)) {
00263           nsString text(pUCS2), result;
00264           rv = textTransform->Change(pUCS2, nsCRT::strlen(pUCS2), result);
00265           if (NS_SUCCEEDED(rv)) {
00266             if (_pUCS2)
00267               nsMemory::Free(_pUCS2);
00268             pUCS2 = _pUCS2 = ToNewUnicode(result);
00269             if (!pUCS2)
00270               return -1;
00271           }
00272         }
00273       }
00274     }
00275 
00276     // Create an instance of charset converter and initialize it
00277     conv = do_CreateInstance(NS_SAVEASCHARSET_CONTRACTID, &rv);
00278     if(NS_SUCCEEDED(rv)) {
00279       rv = conv->Init(charset, 
00280                        nsISaveAsCharset::attr_FallbackQuestionMark + nsISaveAsCharset::attr_EntityAfterCharsetConv, 
00281                        nsIEntityConverter::transliterate);
00282     }
00283     if (NS_FAILED(rv)) {
00284       if (_pUCS2)
00285         nsMemory::Free(_pUCS2);
00286       return -1;
00287     }
00288   } // if (!foldingonly)
00289 
00290   /*
00291      See if folding needs to happen.
00292      If carried over length is already too long to hold
00293      encoded-word header and trailer length, it's too obvious
00294      that we need to fold.  So, don't waste time for calculation.
00295   */
00296   if (linelen + encodedword_headlen + encodedword_taillen < foldlen) {
00297     if (foldingonly) {
00298       offset = strlen(pUTF8Head = pUTF8);
00299       pUTF8 += offset;
00300     }
00301     else {
00302       rv = conv->Convert(pUCS2, &ibuf);
00303       if (NS_FAILED(rv) || ibuf == nsnull) {
00304         if (_pUCS2)
00305           nsMemory::Free(_pUCS2);
00306         return -1; //error
00307       }
00308       if (method == 'B') {
00309         /* 4 / 3 = B encoding multiplier */
00310         offset = strlen(ibuf) * 4 / 3;
00311       }
00312       else {
00313         /* 3 = Q encoding multiplier */
00314         offset = 0;
00315         for (PRInt32 i = 0; *(ibuf+i); i++)
00316           offset += NO_Q_ENCODING_NEEDED(*(ibuf+i)) ? 1 : 3;
00317       }
00318     }
00319     if (linelen + encodedword_headlen + offset + encodedword_taillen > foldlen) {
00320       /* folding has to happen */
00321       if (foldingonly) {
00322         pUTF8 = pUTF8Head;
00323         pUTF8Head = nsnull;
00324       }
00325       else
00326         PR_Free(ibuf);
00327     }
00328     else {
00329       /* no folding needed, let's fall thru */
00330       PL_strncpyz(o, encodedword_head, obufsize);
00331       olen += encodedword_headlen;
00332       linelen += encodedword_headlen;
00333       obufsize -= encodedword_headlen;
00334       o += encodedword_headlen;
00335       if (!foldingonly)
00336         *pUCS2 = 0;
00337     }
00338   }
00339   else {
00340     PL_strncpyz(o, "\r\n ", obufsize);
00341     olen += 3;
00342     o += 3;
00343     obufsize -= 3;
00344     linelen = 1;
00345   }
00346 
00347   /*
00348     Let's do the nasty RFC-2047 & folding stuff
00349   */
00350 
00351   while ((foldingonly ? *pUTF8 : *pUCS2) && (olen < outlen)) {
00352     PL_strncpyz(o, encodedword_head, obufsize);
00353     olen += encodedword_headlen;
00354     linelen += encodedword_headlen;
00355     obufsize -= encodedword_headlen;
00356     o += encodedword_headlen;
00357     olen += encodedword_taillen;
00358     if (foldingonly)
00359       pUTF8Head = pUTF8;
00360     else
00361       pUCS2Head = pUCS2;
00362 
00363     while ((foldingonly ? *pUTF8 : *pUCS2) && (olen < outlen)) {
00364       if (foldingonly) {
00365         ++pUTF8;
00366         for (;;) {
00367           if (*pUTF8 == ' ' || *pUTF8 == TAB || !*pUTF8) {
00368             offset = pUTF8 - pUTF8Head;
00369             cUTF8Tmp = *pUTF8;
00370             *pUTF8 = 0;
00371             break;
00372           }
00373           ++pUTF8;
00374         }
00375       }
00376       else {
00377         convlen = ++pUCS2 - pUCS2Head;
00378         cUCS2Tmp = *(pUCS2Head + convlen);
00379         *(pUCS2Head + convlen) = 0;
00380         rv = conv->Convert(pUCS2Head, &ibuf);
00381         *(pUCS2Head + convlen) = cUCS2Tmp;
00382         if (NS_FAILED(rv) || ibuf == nsnull) {
00383           if (_pUCS2)
00384             nsMemory::Free(_pUCS2);
00385           return -1; //error
00386         }
00387         if (method == 'B') {
00388           /* 4 / 3 = B encoding multiplier */
00389           offset = strlen(ibuf) * 4 / 3;
00390         }
00391         else {
00392           /* 3 = Q encoding multiplier */
00393           offset = 0;
00394           for (PRInt32 i = 0; *(ibuf+i); i++)
00395             offset += NO_Q_ENCODING_NEEDED(*(ibuf+i)) ? 1 : 3;
00396         }
00397       }
00398       if (linelen + offset > foldlen) {
00399 process_lastline:
00400         PRInt32 enclen;
00401         if (foldingonly) {
00402           PL_strncpyz(o, pUTF8Head, obufsize);
00403           enclen = strlen(o);
00404           obufsize -= enclen;
00405           o += enclen;
00406           *pUTF8 = cUTF8Tmp;
00407         }
00408         else {
00409           if (method == 'B')
00410             enclen = intlmime_encode_b((const unsigned char *)ibuf, strlen(ibuf), o);
00411           else
00412             enclen = intlmime_encode_q((const unsigned char *)ibuf, strlen(ibuf), o);
00413           PR_Free(ibuf);
00414           o += enclen;
00415           obufsize -= enclen;
00416           PL_strncpyz(o, "?=", obufsize);
00417         }
00418         o += encodedword_taillen;
00419         obufsize -= encodedword_taillen;
00420         olen += enclen;
00421         if (foldingonly ? *pUTF8 : *pUCS2) { /* not last line */
00422           PL_strncpyz(o, "\r\n ", obufsize);
00423           obufsize -= 3;
00424           o += 3;
00425           olen += 3;
00426           linelen = 1;
00427           if (foldingonly) {
00428             pUTF8Head = nsnull;
00429             if (*pUTF8 == ' ' || *pUTF8 == TAB) {
00430               ++pUTF8;
00431               if (!*pUTF8)
00432                 return 0;
00433             }
00434           }
00435         }
00436         else {
00437           if (_pUCS2)
00438             nsMemory::Free(_pUCS2);
00439           return linelen + enclen + encodedword_taillen;
00440         }
00441         break;
00442       }
00443       else {
00444         if (foldingonly)
00445           *pUTF8 = cUTF8Tmp;
00446         else {
00447           if (*pUCS2)
00448             PR_Free(ibuf);
00449         }
00450       }
00451     }
00452   }
00453 
00454   goto process_lastline;
00455 }
00456 
00457 typedef struct _RFC822AddressList {
00458   char        *displayname;
00459   PRBool      asciionly;
00460   char        *addrspec;
00461   struct _RFC822AddressList *next;
00462 } RFC822AddressList;
00463 
00464 static
00465 void destroy_addresslist(RFC822AddressList *p)
00466 {
00467   if (p->next)
00468     destroy_addresslist(p->next);
00469   PR_FREEIF(p->displayname);
00470   PR_FREEIF(p->addrspec);
00471   PR_Free(p);
00472   return;
00473 }
00474 
00475 static
00476 RFC822AddressList * construct_addresslist(char *s)
00477 {
00478   PRBool  quoted = PR_FALSE, angle_addr = PR_FALSE;
00479   PRInt32  comment = 0;
00480   char *displayname = nsnull, *addrspec = nsnull;
00481   static RFC822AddressList  listinit;
00482   RFC822AddressList  *listhead = (RFC822AddressList *)PR_Malloc(sizeof(RFC822AddressList));
00483   RFC822AddressList  *list = listhead;
00484 
00485   if (!list)
00486     return nsnull;
00487 
00488   while (*s == ' ' || *s == TAB)
00489     ++s;
00490 
00491   for (*list = listinit; *s; ++s) {
00492     if (*s == '\\') {
00493       if (quoted || comment) {
00494         ++s;
00495         continue;
00496       }
00497     }
00498     else if (*s == '(' || *s == ')') {
00499       if (!quoted) {
00500         if (*s == '(') {
00501           if (comment++ == 0)
00502             displayname = s + 1;
00503         }
00504         else {
00505           if (--comment == 0) {
00506             *s = '\0';
00507             PR_FREEIF(list->displayname);
00508             list->displayname = nsCRT::strdup(displayname);
00509             list->asciionly = intlmime_only_ascii_str(displayname);
00510             *s = ')';
00511           }
00512         }
00513         continue;
00514       }
00515     }
00516     else if (*s == '"') {
00517       if (!comment && !angle_addr) {
00518         quoted = !quoted;
00519         if (quoted)
00520           displayname = s;
00521         else {
00522           char tmp = *(s+1);
00523           *(s+1) = '\0';
00524           PR_FREEIF(list->displayname);
00525           list->displayname = nsCRT::strdup(displayname);
00526           list->asciionly = intlmime_only_ascii_str(displayname);
00527           *(s+1) = tmp;
00528         }
00529         continue;
00530       }
00531     }
00532     else if (*s == '<' || *s == '>') {
00533       if (!quoted && !comment) {
00534         if (*s == '<') {
00535           angle_addr = PR_TRUE;
00536           addrspec = s;
00537           if (displayname) {
00538             char *e = s - 1, tmp;
00539             while (*e == TAB || *e == ' ')
00540               --e;
00541             tmp = *++e;
00542             *e = '\0';
00543             PR_FREEIF(list->displayname);
00544             list->displayname = nsCRT::strdup(displayname);
00545             list->asciionly = intlmime_only_ascii_str(displayname);
00546             *e = tmp;
00547           }
00548         }
00549         else {
00550           char tmp;
00551           angle_addr = PR_FALSE;
00552           tmp = *(s+1);
00553           *(s+1) = '\0';
00554           PR_FREEIF(list->addrspec);
00555           list->addrspec = nsCRT::strdup(addrspec);
00556           *(s+1) = tmp;
00557         }
00558         continue;
00559       }
00560     }
00561     if (!quoted && !comment && !angle_addr) {
00562       /* address-list separator. end of this address */
00563       if (*s == ',') {
00564         /* deal with addr-spec only address */
00565         if (!addrspec && displayname) {
00566           *s = '\0';
00567           list->addrspec = nsCRT::strdup(displayname);
00568           /* and don't forget to free the display name in the list, in that case it's bogus */
00569           PR_FREEIF(list->displayname);
00570         }
00571         /* prepare for next address */
00572         addrspec = displayname = nsnull;
00573         list->next = (RFC822AddressList *)PR_Malloc(sizeof(RFC822AddressList));
00574         list = list->next;
00575         *list = listinit;
00576         /* eat spaces */
00577         ++s;
00578         while (*s == ' ' || *s == TAB)
00579           ++s;
00580         if (*s == '\r' && *(s+1) == '\n' && (*(s+2) == ' ' || *(s+2) == TAB))
00581           s += 2;
00582         else
00583           --s;
00584       }
00585       else if (!displayname && *s != ' ' && *s != TAB)
00586         displayname = s;
00587     }
00588   }
00589 
00590   /* deal with addr-spec only address comes at last */
00591   if (!addrspec && displayname)
00592   {
00593     list->addrspec = nsCRT::strdup(displayname);
00594     /* and don't forget to free the display name in the list, in that case it's bogus */
00595     PR_FREEIF(list->displayname);
00596   }
00597 
00598   return listhead;
00599 }
00600 
00601 static 
00602 char * apply_rfc2047_encoding(const char *_src, PRBool structured, const char *charset, PRInt32 cursor, PRInt32 foldlen)
00603 {
00604   RFC822AddressList  *listhead, *list;
00605   PRInt32   outputlen, usedlen;
00606   char  *src, *src_head, *output, *outputtail;
00607   char  method = nsMsgI18Nmultibyte_charset(charset) ? 'B' : 'Q';
00608 
00609   if (!_src)
00610     return nsnull;
00611 
00612   //<TAB>=?<charset>?<B/Q>?...?=<CRLF>
00613   PRInt32 perLineOverhead = strlen(charset) + 10;
00614 
00615   if (perLineOverhead > foldlen || (src = src_head = nsCRT::strdup(_src)) == nsnull)
00616     return nsnull;
00617 
00618   /* allocate enough buffer for conversion, this way it can avoid
00619      do another memory allocation which is expensive
00620    */
00621   PRInt32 charsPerLine = (foldlen - perLineOverhead) / 4;
00622   PRInt32 maxNumLines = (strlen(src) / charsPerLine) + 1;
00623   outputlen = strlen(src) * 4 + (maxNumLines * perLineOverhead) + 20 /* fudge */;
00624   if ((outputtail = output = (char *)PR_Malloc(outputlen)) == nsnull) {
00625     PR_Free(src_head);
00626     return nsnull;
00627   }
00628 
00629   if (structured) {
00630     listhead = list = construct_addresslist(src);
00631     if (!list) {
00632       PR_Free(output);
00633       output = nsnull;
00634     }
00635     else {
00636       for (; list && (outputlen > 0); list = list->next) {
00637         if (list->displayname) {
00638           if (list->asciionly && list->addrspec) {
00639             PRInt32 len = cursor + strlen(list->displayname) + strlen(list->addrspec);
00640             if (foldlen < len && len < 998) { /* see RFC 2822 for magic number 998 */
00641               PR_snprintf(outputtail, outputlen - 1, (list == listhead || cursor == 1) ? "%s %s%s" : "\r\n %s %s%s", list->displayname, list->addrspec, list->next ? ",\r\n " : "");
00642               usedlen = strlen(outputtail);
00643               outputtail += usedlen;
00644               outputlen -= usedlen;
00645               cursor = 1;
00646               continue;
00647             }
00648           }
00649           cursor = generate_encodedwords(list->displayname, charset, method, outputtail, outputlen, cursor, foldlen, list->asciionly);
00650           if (cursor < 0) {
00651             PR_Free(output);
00652             output = nsnull;
00653             break;
00654           }
00655           usedlen = strlen(outputtail);
00656           outputtail += usedlen;
00657           outputlen -= usedlen;
00658         }
00659         if (list->addrspec) {
00660           usedlen = strlen(list->addrspec);
00661           if (cursor + usedlen > foldlen) {
00662             if (PR_snprintf(outputtail, outputlen - 1, "\r\n %s", list->addrspec) < 0) {
00663               PR_Free(output);
00664               destroy_addresslist(listhead);
00665               return nsnull;
00666             }
00667             usedlen += 3;         /* FWS + addr-spec */
00668             cursor = usedlen - 2; /* \r\n */
00669           }
00670           else {
00671             if (PR_snprintf(outputtail, outputlen - 1, list->displayname ? " %s" : "%s", list->addrspec) < 0) {
00672               PR_Free(output);
00673               destroy_addresslist(listhead);
00674               return nsnull;
00675             }
00676             if (list->displayname)
00677               usedlen++;
00678             cursor += usedlen;
00679           }
00680           outputtail += usedlen;
00681           outputlen -= usedlen;
00682         }
00683         else {
00684           PR_Free(output);
00685           output = nsnull;
00686           break;
00687         }
00688         if (list->next) {
00689           PL_strncpyz(outputtail, ", ", outputlen);
00690           cursor += 2;
00691           outputtail += 2;
00692           outputlen -= 2;
00693         }
00694       }
00695       destroy_addresslist(listhead);
00696     }
00697   }
00698   else {
00699     char *spacepos = nsnull, *org_output = output;
00700     /* show some mercy to stupid ML systems which don't know 
00701        how to respect MIME encoded subject */
00702     for (char *p = src; *p && !(*p & 0x80); p++) {
00703       if (*p == 0x20 || *p == TAB)
00704         spacepos = p;
00705     }
00706     if (spacepos) {
00707       char head[kMAX_CSNAME+4+1];
00708       PRInt32  overhead, skiplen;
00709       if (PR_snprintf(head, sizeof(head) - 1, "=?%s?%c?", charset, method) < 0) {
00710         PR_Free(output);
00711         return nsnull;
00712       }
00713       overhead = strlen(head) + 2 + 4; // 2 : "?=", 4 : a B-encoded chunk
00714       skiplen = spacepos + 1 - src;
00715       if (cursor + skiplen + overhead < foldlen) {
00716         char tmp = *(spacepos + 1);
00717         *(spacepos + 1) = '\0';
00718         PL_strncpyz(output, src, outputlen);
00719         output += skiplen;
00720         outputlen -= skiplen;
00721         cursor += skiplen;
00722         src += skiplen;
00723         *src = tmp;
00724       }
00725     }
00726     PRBool asciionly = intlmime_only_ascii_str(src);
00727     if (generate_encodedwords(src, charset, method, output, outputlen, cursor, foldlen, asciionly) < 0) {
00728       PR_Free(org_output);
00729       org_output = nsnull;
00730     }
00731     output = org_output;
00732   }
00733 
00734   PR_Free(src_head);
00735 
00736   return output;
00737 }
00738 
00740 // BEGIN PUBLIC INTERFACE
00741 extern "C" {
00742 #define PUBLIC
00743 
00744 
00745 extern "C" char *MIME_DecodeMimeHeader(const char *header, 
00746                                        const char *default_charset,
00747                                        PRBool override_charset,
00748                                        PRBool eatContinuations)
00749 {
00750   nsresult rv;
00751   nsCOMPtr <nsIMIMEHeaderParam> mimehdrpar = do_GetService(NS_MIMEHEADERPARAM_CONTRACTID, &rv);
00752   if (NS_FAILED(rv))
00753     return nsnull;
00754   nsCAutoString result;
00755   rv = mimehdrpar->DecodeRFC2047Header(header, default_charset, override_charset, 
00756                                        eatContinuations, result);
00757   if (NS_SUCCEEDED(rv))
00758     return nsCRT::strdup(result.get());
00759   return nsnull;
00760 }  
00761 
00762 char *MIME_EncodeMimePartIIStr(const char* header, PRBool structured, const char* mailCharset, const PRInt32 fieldNameLen, const PRInt32 encodedWordSize)
00763 {
00764   return apply_rfc2047_encoding(header, structured, mailCharset, fieldNameLen, encodedWordSize);
00765 }
00766 
00767 // UTF-8 utility functions.
00768 
00769 char * NextChar_UTF8(char *str)
00770 {
00771   return (char *) utf8_nextchar((unsigned char *) str);
00772 }
00773 
00774 //detect charset soly based on aBuf. return in aCharset
00775 nsresult
00776 MIME_detect_charset(const char *aBuf, PRInt32 aLength, const char** aCharset)
00777 {
00778   nsresult res = NS_ERROR_UNEXPECTED;
00779   nsXPIDLString detector_name;
00780   *aCharset = nsnull;
00781 
00782   NS_GetLocalizedUnicharPreferenceWithDefault(nsnull, "intl.charset.detector", EmptyString(), detector_name);
00783 
00784   if (!detector_name.IsEmpty()) {
00785     nsCAutoString detector_contractid;
00786     detector_contractid.AssignLiteral(NS_STRCDETECTOR_CONTRACTID_BASE);
00787 
00788     AppendUTF16toUTF8(detector_name, detector_contractid);
00789     nsCOMPtr<nsIStringCharsetDetector> detector = do_CreateInstance(detector_contractid.get(), &res);
00790     if (NS_SUCCEEDED(res)) {
00791       nsDetectionConfident oConfident;
00792       res = detector->DoIt(aBuf, aLength, aCharset, oConfident);
00793       if (NS_SUCCEEDED(res) && (eBestAnswer == oConfident || eSureAnswer == oConfident)) {
00794         return NS_OK;
00795       }
00796     }
00797   }
00798   return res;
00799 }
00800 
00801 //Get unicode decoder(from inputcharset to unicode) for aInputCharset
00802 nsresult 
00803 MIME_get_unicode_decoder(const char* aInputCharset, nsIUnicodeDecoder **aDecoder)
00804 {
00805   nsresult res;
00806 
00807   // get charset converters.
00808   nsCOMPtr<nsICharsetConverterManager> ccm = 
00809            do_GetService(NS_CHARSETCONVERTERMANAGER_CONTRACTID, &res); 
00810   if (NS_SUCCEEDED(res)) {
00811 
00812     // create a decoder (conv to unicode), ok if failed if we do auto detection
00813     if (!*aInputCharset || !nsCRT::strcasecmp("us-ascii", aInputCharset))
00814       res = ccm->GetUnicodeDecoderRaw("ISO-8859-1", aDecoder);
00815     else
00816       res = ccm->GetUnicodeDecoder(aInputCharset, aDecoder);
00817   }
00818    
00819   return res;
00820 }
00821 
00822 //Get unicode encoder(from unicode to inputcharset) for aOutputCharset
00823 nsresult 
00824 MIME_get_unicode_encoder(const char* aOutputCharset, nsIUnicodeEncoder **aEncoder)
00825 {
00826   nsresult res;
00827 
00828   // get charset converters.
00829   nsCOMPtr<nsICharsetConverterManager> ccm = 
00830            do_GetService(NS_CHARSETCONVERTERMANAGER_CONTRACTID, &res); 
00831   if (NS_SUCCEEDED(res) && *aOutputCharset) {
00832       // create a encoder (conv from unicode)
00833       res = ccm->GetUnicodeEncoder(aOutputCharset, aEncoder);
00834   }
00835    
00836   return res;
00837 }
00838 
00839 } /* end of extern "C" */
00840 // END PUBLIC INTERFACE
00841