Back to index

lightning-sunbird  0.9+nobinonly
nsMsgCompUtils.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  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either of the GNU General Public License Version 2 or later (the "GPL"),
00026  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 #include "nsCOMPtr.h"
00038 #include "nsCRT.h"
00039 #include "nsMsgCompUtils.h"
00040 #include "nsIPrefService.h"
00041 #include "nsIPrefBranch.h"
00042 #include "prmem.h"
00043 #include "nsEscape.h"
00044 #include "nsIFileSpec.h"
00045 #include "nsMsgSend.h"
00046 #include "nsIIOService.h"
00047 #include "nsIHttpProtocolHandler.h"
00048 #include "nsMailHeaders.h"
00049 #include "nsMsgI18N.h"
00050 #include "nsIMsgHeaderParser.h"
00051 #include "nsINntpService.h"
00052 #include "nsMimeTypes.h"
00053 #include "nsMsgComposeStringBundle.h"
00054 #include "nsXPIDLString.h"
00055 #include "nsReadableUtils.h"
00056 #include "nsSpecialSystemDirectory.h"
00057 #include "nsIDocumentEncoder.h"    // for editor output flags
00058 #include "nsIURI.h"
00059 #include "nsNetCID.h"
00060 #include "nsMsgPrompts.h"
00061 #include "nsMsgUtils.h"
00062 #include "nsMsgSimulateError.h"
00063 
00064 #include "nsIMsgCompUtils.h"
00065 #include "nsIMsgMdnGenerator.h"
00066 
00067 #ifdef MOZ_THUNDERBIRD
00068 #include "nsIXULAppInfo.h"
00069 #include "nsXULAppAPI.h"
00070 #endif
00071 
00072 static NS_DEFINE_CID(kIOServiceCID, NS_IOSERVICE_CID);
00073 static NS_DEFINE_CID(kHTTPHandlerCID, NS_HTTPPROTOCOLHANDLER_CID);
00074 
00075 NS_IMPL_ISUPPORTS1(nsMsgCompUtils, nsIMsgCompUtils)
00076 
00077 nsMsgCompUtils::nsMsgCompUtils()
00078 {
00079 }
00080 
00081 nsMsgCompUtils::~nsMsgCompUtils()
00082 {
00083 }
00084 
00085 NS_IMETHODIMP nsMsgCompUtils::MimeMakeSeparator(const char *prefix,
00086                                                 char **_retval)
00087 {
00088   NS_ENSURE_ARG_POINTER(prefix);
00089   NS_ENSURE_ARG_POINTER(_retval);
00090   *_retval = mime_make_separator(prefix);
00091   return NS_OK;
00092 }
00093 
00094 NS_IMETHODIMP nsMsgCompUtils::MsgGenerateMessageId(nsIMsgIdentity *identity,
00095                                                     char **_retval)
00096 {
00097   NS_ENSURE_ARG_POINTER(identity);
00098   NS_ENSURE_ARG_POINTER(_retval);
00099   *_retval = msg_generate_message_id(identity);
00100   return NS_OK;
00101 }
00102 
00103 NS_IMETHODIMP nsMsgCompUtils::GetMsgMimeConformToStandard(PRBool *_retval)
00104 {
00105   NS_ENSURE_ARG_POINTER(_retval);
00106   *_retval = nsMsgMIMEGetConformToStandard();
00107   return NS_OK;
00108 }
00109 
00110 //
00111 // Hopefully, someone will write and XP call like this eventually!
00112 //
00113 #define     TPATH_LEN   1024
00114 
00115 //
00116 // Create a file spec for the a unique temp file
00117 // on the local machine. Caller must free memory
00118 //
00119 nsFileSpec * 
00120 nsMsgCreateTempFileSpec(const char *tFileName)
00121 {
00122   if ((!tFileName) || (!*tFileName))
00123     tFileName = "nsmail.tmp";
00124 
00125   nsFileSpec *tmpSpec = new nsFileSpec(nsSpecialSystemDirectory(nsSpecialSystemDirectory::OS_TemporaryDirectory));
00126 
00127   if (!tmpSpec)
00128     return nsnull;
00129 
00130   *tmpSpec += tFileName;
00131   tmpSpec->MakeUnique();
00132 
00133   return tmpSpec;
00134 }
00135 
00136 //
00137 // Create a file spec for the a unique temp file
00138 // on the local machine. Caller must free memory
00139 // returned
00140 //
00141 char * 
00142 nsMsgCreateTempFileName(const char *tFileName)
00143 {
00144   if ((!tFileName) || (!*tFileName))
00145     tFileName = "nsmail.tmp";
00146 
00147   nsFileSpec tmpSpec = nsSpecialSystemDirectory(nsSpecialSystemDirectory::OS_TemporaryDirectory); 
00148   tmpSpec += tFileName;
00149   tmpSpec.MakeUnique();
00150 
00151   char *tString = (char *)PL_strdup(tmpSpec.GetNativePathCString());
00152   if (!tString)
00153     return PL_strdup("mozmail.tmp");  // No need to I18N
00154   else
00155     return tString;
00156 }
00157 
00158 static PRBool mime_headers_use_quoted_printable_p = PR_FALSE;
00159 
00160 PRBool
00161 nsMsgMIMEGetConformToStandard (void)
00162 {
00163   return mime_headers_use_quoted_printable_p;
00164 }
00165 
00166 void
00167 nsMsgMIMESetConformToStandard (PRBool conform_p)
00168 {
00169   /* 
00170   * If we are conforming to mime standard no matter what we set
00171   * for the headers preference when generating mime headers we should 
00172   * also conform to the standard. Otherwise, depends the preference
00173   * we set. For now, the headers preference is not accessible from UI.
00174   */
00175   if (conform_p)
00176     mime_headers_use_quoted_printable_p = PR_TRUE;
00177   else {
00178     nsresult rv;
00179     nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv)); 
00180     if (NS_SUCCEEDED(rv)) {
00181       prefs->GetBoolPref("mail.strictly_mime_headers", &mime_headers_use_quoted_printable_p);
00182     }
00183   }
00184 }
00185 
00186 nsresult mime_sanity_check_fields (
00187           const char *from,
00188           const char *reply_to,
00189           const char *to,
00190           const char *cc,
00191           const char *bcc,
00192           const char *fcc,
00193           const char *newsgroups,
00194           const char *followup_to,
00195           const char * /*subject*/,
00196           const char * /*references*/,
00197           const char * /*organization*/,
00198           const char * /*other_random_headers*/)
00199 {
00200   if (from)
00201     while (IS_SPACE (*from))
00202       from++;
00203   if (reply_to)
00204     while (IS_SPACE (*reply_to))
00205       reply_to++;
00206   if (to)
00207     while (IS_SPACE (*to))
00208       to++;
00209   if (cc)
00210     while (IS_SPACE (*cc))
00211       cc++;
00212   if (bcc)
00213     while (IS_SPACE (*bcc))
00214       bcc++;
00215   if (fcc)
00216     while (IS_SPACE (*fcc))
00217       fcc++;
00218   if (newsgroups)
00219     while (IS_SPACE (*newsgroups))
00220       newsgroups++;
00221   if (followup_to)
00222     while (IS_SPACE (*followup_to))
00223       followup_to++;
00224 
00225   /* #### sanity check other_random_headers for newline conventions */
00226   if (!from || !*from || CHECK_SIMULATED_ERROR(SIMULATED_SEND_ERROR_6))
00227     return NS_MSG_NO_SENDER;
00228   else
00229     if (((!to || !*to) && (!cc || !*cc) &&
00230         (!bcc || !*bcc) && (!newsgroups || !*newsgroups)) ||
00231         CHECK_SIMULATED_ERROR(SIMULATED_SEND_ERROR_7))
00232       return NS_MSG_NO_RECIPIENTS;
00233   else
00234     return NS_OK;
00235 }
00236 
00237 static char *
00238 nsMsgStripLine (char * string)
00239 {
00240   char * ptr;
00241   
00242   /* remove leading blanks */
00243   while(*string=='\t' || *string==' ' || *string=='\r' || *string=='\n')
00244     string++;    
00245   
00246   for(ptr=string; *ptr; ptr++)
00247     ;   /* NULL BODY; Find end of string */
00248   
00249   /* remove trailing blanks */
00250   for(ptr--; ptr >= string; ptr--) 
00251   {
00252     if(*ptr=='\t' || *ptr==' ' || *ptr=='\r' || *ptr=='\n') 
00253       *ptr = '\0'; 
00254     else 
00255       break;
00256   }
00257   
00258   return string;
00259 }
00260 
00261 //
00262 // Generate the message headers for the new RFC822 message
00263 //
00264 #define UA_PREF_PREFIX "general.useragent."
00265 
00266 #define ENCODE_AND_PUSH(name, structured, body, charset, usemime) \
00267   { \
00268     PUSH_STRING((name)); \
00269     convbuf = nsMsgI18NEncodeMimePartIIStr((body), (structured), (charset), strlen(name), (usemime)); \
00270     if (convbuf) { \
00271       PUSH_STRING (convbuf); \
00272       PR_FREEIF(convbuf); \
00273     } \
00274     else \
00275       PUSH_STRING((body)); \
00276     PUSH_NEWLINE (); \
00277   }
00278 
00279 char * 
00280 mime_generate_headers (nsMsgCompFields *fields,
00281                        const char *charset,
00282                        nsMsgDeliverMode deliver_mode, nsIPrompt * aPrompt, PRInt32 *status)
00283 {
00284   nsresult rv;
00285   *status = 0;
00286 
00287   nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv)); 
00288   if (NS_FAILED(rv)) {
00289     *status = rv;
00290     return nsnull;
00291   }
00292 
00293   PRBool usemime = nsMsgMIMEGetConformToStandard();
00294   PRInt32 size = 0;
00295   char *buffer = 0, *buffer_tail = 0;
00296   PRBool isDraft =
00297   deliver_mode == nsIMsgSend::nsMsgSaveAsDraft ||
00298   deliver_mode == nsIMsgSend::nsMsgSaveAsTemplate ||
00299   deliver_mode == nsIMsgSend::nsMsgQueueForLater;
00300 
00301   const char* pFrom;
00302   const char* pTo;
00303   const char* pCc;
00304   const char* pMessageID;
00305   const char* pReplyTo;
00306   const char* pOrg;
00307   const char* pNewsGrp;
00308   const char* pFollow;
00309   const char* pSubject;
00310   const char* pPriority;
00311   const char* pReference;
00312   const char* pOtherHdr;
00313   char *convbuf;
00314 
00315   PRBool hasDisclosedRecipient = PR_FALSE;
00316 
00317   nsCAutoString headerBuf;    // accumulate header strings to get length
00318   headerBuf.Truncate(0);
00319 
00320   NS_ASSERTION (fields, "null fields");
00321   if (!fields)
00322     return nsnull;
00323 
00324   pFrom = fields->GetFrom(); 
00325   if (pFrom)
00326     headerBuf.Append(pFrom);
00327   pReplyTo =fields->GetReplyTo(); 
00328   if (pReplyTo)
00329     headerBuf.Append(pReplyTo);
00330   pTo = fields->GetTo();
00331   if (pTo)
00332     headerBuf.Append(pTo);
00333   pCc = fields->GetCc(); 
00334   if (pCc)
00335     headerBuf.Append(pCc);
00336   pNewsGrp = fields->GetNewsgroups(); if (pNewsGrp)     size += 3 * PL_strlen (pNewsGrp);
00337   pFollow= fields->GetFollowupTo(); if (pFollow)        size += 3 * PL_strlen (pFollow);
00338   pSubject = fields->GetSubject(); 
00339   if (pSubject)
00340     headerBuf.Append(pSubject);
00341   pReference = fields->GetReferences(); if (pReference)   size += 3 * PL_strlen (pReference);
00342   pOrg= fields->GetOrganization(); 
00343   if (pOrg)
00344     headerBuf.Append(pOrg);
00345   pOtherHdr= fields->GetOtherRandomHeaders(); if (pOtherHdr)  size += 3 * PL_strlen (pOtherHdr);
00346   pPriority = fields->GetPriority();  if (pPriority)      size += 3 * PL_strlen (pPriority);
00347   pMessageID = fields->GetMessageId(); if (pMessageID)    size += PL_strlen (pMessageID);
00348 
00349   /* Multiply by 3 here to make enough room for MimePartII conversion */
00350   size += 3 * headerBuf.Length();
00351 
00352   /* Add a bunch of slop for the static parts of the headers. */
00353   /* size += 2048; */
00354   size += 2560;
00355 
00356   buffer = (char *) PR_Malloc (size);
00357   if (!buffer)
00358     return nsnull; /* NS_ERROR_OUT_OF_MEMORY */
00359   
00360   buffer_tail = buffer;
00361 
00362   if (pMessageID && *pMessageID) {
00363     PUSH_STRING ("Message-ID: ");
00364     PUSH_STRING (pMessageID);
00365     PUSH_NEWLINE ();
00366     /* MDN request header requires to have MessageID header presented
00367     * in the message in order to
00368     * coorelate the MDN reports to the original message. Here will be
00369     * the right place
00370     */
00371 
00372     if (fields->GetReturnReceipt() && 
00373       (deliver_mode != nsIMsgSend::nsMsgSaveAsDraft &&
00374       deliver_mode != nsIMsgSend::nsMsgSaveAsTemplate))
00375     {
00376         PRInt32 receipt_header_type = nsIMsgMdnGenerator::eDntType;
00377         fields->GetReceiptHeaderType(&receipt_header_type);
00378 
00379       // nsIMsgMdnGenerator::eDntType = MDN Disposition-Notification-To: ;
00380       // nsIMsgMdnGenerator::eRrtType = Return-Receipt-To: ;
00381       // nsIMsgMdnGenerator::eDntRrtType = both MDN DNT and RRT headers .
00382       if (receipt_header_type != nsIMsgMdnGenerator::eRrtType)
00383         ENCODE_AND_PUSH(
00384          "Disposition-Notification-To: ", PR_TRUE, pFrom, charset, usemime);
00385       if (receipt_header_type != nsIMsgMdnGenerator::eDntType)
00386         ENCODE_AND_PUSH(
00387          "Return-Receipt-To: ", PR_TRUE, pFrom, charset, usemime);
00388     }
00389 
00390 #ifdef SUPPORT_X_TEMPLATE_NAME
00391     if (deliver_mode == MSG_SaveAsTemplate) {
00392       const char *pStr = fields->GetTemplateName();
00393       pStr = pStr ? pStr : "";
00394       ENCODE_AND_PUSH("X-Template: ", PR_FALSE, pStr, charset, usemime);
00395     }
00396 #endif /* SUPPORT_X_TEMPLATE_NAME */
00397   }
00398 
00399   PRExplodedTime now;
00400   PR_ExplodeTime(PR_Now(), PR_LocalTimeParameters, &now);
00401   int gmtoffset = (now.tm_params.tp_gmt_offset + now.tm_params.tp_dst_offset) / 60;
00402 
00403   /* Use PR_FormatTimeUSEnglish() to format the date in US English format,
00404      then figure out what our local GMT offset is, and append it (since
00405      PR_FormatTimeUSEnglish() can't do that.) Generate four digit years as
00406      per RFC 1123 (superceding RFC 822.)
00407    */
00408   PR_FormatTimeUSEnglish(buffer_tail, 100,
00409                "Date: %a, %d %b %Y %H:%M:%S ",
00410                &now);
00411 
00412   buffer_tail += PL_strlen (buffer_tail);
00413   PR_snprintf(buffer_tail, buffer + size - buffer_tail,
00414         "%c%02d%02d" CRLF,
00415         (gmtoffset >= 0 ? '+' : '-'),
00416         ((gmtoffset >= 0 ? gmtoffset : -gmtoffset) / 60),
00417         ((gmtoffset >= 0 ? gmtoffset : -gmtoffset) % 60));
00418   buffer_tail += PL_strlen (buffer_tail);
00419 
00420   if (pFrom && *pFrom) 
00421   {
00422     ENCODE_AND_PUSH("From: ", PR_TRUE, pFrom, charset, usemime);
00423   }
00424 
00425   if (pReplyTo && *pReplyTo)
00426   {
00427     ENCODE_AND_PUSH("Reply-To: ", PR_TRUE, pReplyTo, charset, usemime);
00428   }
00429 
00430   if (pOrg && *pOrg)
00431   {
00432     ENCODE_AND_PUSH("Organization: ", PR_FALSE, pOrg, charset, usemime);
00433   }
00434 
00435   // X-Mozilla-Draft-Info
00436   if (isDraft) 
00437   {
00438     PUSH_STRING(HEADER_X_MOZILLA_DRAFT_INFO);
00439     PUSH_STRING(": internal/draft; ");
00440     if (fields->GetAttachVCard())
00441       PUSH_STRING("vcard=1");
00442     else
00443       PUSH_STRING("vcard=0");
00444     PUSH_STRING("; ");
00445     if (fields->GetReturnReceipt()) {
00446       // slight change compared to 4.x; we used to use receipt= to tell
00447       // whether the draft/template has request for either MDN or DNS or both
00448       // return receipt; since the DNS is out of the picture we now use the
00449       // header type + 1 to tell whether user has requested the return receipt
00450       PRInt32 headerType = 0;
00451       fields->GetReceiptHeaderType(&headerType);
00452       char *type = PR_smprintf("%d", (int)headerType + 1);
00453       if (type)
00454       {
00455         PUSH_STRING("receipt=");
00456         PUSH_STRING(type);
00457         PR_FREEIF(type);
00458       }
00459     }
00460     else
00461       PUSH_STRING("receipt=0");
00462     PUSH_STRING("; ");
00463     if (fields->GetUuEncodeAttachments())
00464       PUSH_STRING("uuencode=1");
00465     else
00466       PUSH_STRING("uuencode=0");
00467 
00468     PUSH_NEWLINE ();
00469   }
00470 
00471 
00472   nsCOMPtr<nsIHttpProtocolHandler> pHTTPHandler = do_GetService(kHTTPHandlerCID, &rv); 
00473   if (NS_SUCCEEDED(rv) && pHTTPHandler)
00474   {
00475     nsCAutoString userAgentString;
00476 #ifdef MOZ_THUNDERBIRD
00477 
00478     nsXPIDLCString userAgentOverride;
00479     prefs->GetCharPref("general.useragent.override", getter_Copies(userAgentOverride));
00480 
00481     // allow a user to override the default UA
00482     if (!userAgentOverride)
00483     {
00484       nsCOMPtr<nsIXULAppInfo> xulAppInfo (do_GetService(XULAPPINFO_SERVICE_CONTRACTID, &rv));
00485       if (NS_SUCCEEDED(rv)) 
00486       {
00487         xulAppInfo->GetName(userAgentString);
00488 
00489              nsCAutoString productSub;
00490              pHTTPHandler->GetProductSub(productSub);
00491 
00492              nsCAutoString platform;
00493              pHTTPHandler->GetPlatform(platform);
00494 
00495              userAgentString += ' ';
00496              userAgentString += NS_STRINGIFY(MOZ_APP_VERSION);
00497              userAgentString += " (";
00498              userAgentString += platform;
00499              userAgentString += "/";
00500              userAgentString += productSub;
00501              userAgentString += ")";
00502       }
00503     }
00504     else
00505       userAgentString = userAgentOverride;
00506 #else
00507       pHTTPHandler->GetUserAgent(userAgentString);
00508 #endif
00509 
00510     if (!userAgentString.IsEmpty())
00511     {
00512       PUSH_STRING ("User-Agent: ");  
00513       PUSH_STRING(userAgentString.get());
00514       PUSH_NEWLINE ();
00515     }
00516   }
00517 
00518   PUSH_STRING ("MIME-Version: 1.0" CRLF);
00519 
00520   if (pNewsGrp && *pNewsGrp) {
00521     /* turn whitespace into a comma list
00522     */
00523     char *duppedNewsGrp = PL_strdup(pNewsGrp);
00524     if (!duppedNewsGrp) {
00525       PR_FREEIF(buffer);
00526       return nsnull; /* NS_ERROR_OUT_OF_MEMORY */
00527     }
00528     char *n2 = nsMsgStripLine(duppedNewsGrp);
00529 
00530     for(char *ptr = n2; *ptr != '\0'; ptr++) {
00531       /* find first non white space */
00532       while(!IS_SPACE(*ptr) && *ptr != ',' && *ptr != '\0')
00533         ptr++;
00534 
00535       if(*ptr == '\0')
00536         break;
00537 
00538       if(*ptr != ',')
00539         *ptr = ',';
00540 
00541       /* find next non white space */
00542       char *ptr2 = ptr+1;
00543       while(IS_SPACE(*ptr2))
00544         ptr2++;
00545 
00546       if(ptr2 != ptr+1)
00547         PL_strcpy(ptr+1, ptr2);
00548     }
00549 
00550     // we need to decide the Newsgroup related headers
00551     // to write to the outgoing message. In ANY case, we need to write the
00552     // "Newsgroup" header which is the "proper" header as opposed to the
00553     // HEADER_X_MOZILLA_NEWSHOST which can contain the "news:" URL's.
00554     //
00555     // Since n2 can contain data in the form of:
00556     // "news://news.mozilla.org/netscape.test,news://news.mozilla.org/netscape.junk"
00557     // we need to turn that into: "netscape.test,netscape.junk"
00558     //
00559     nsCOMPtr <nsINntpService> nntpService = do_GetService("@mozilla.org/messenger/nntpservice;1");
00560     if (NS_FAILED(rv) || !nntpService) {
00561       *status = NS_ERROR_FAILURE;
00562       return nsnull;
00563     }
00564 
00565     nsXPIDLCString newsgroupsHeaderVal;
00566     nsXPIDLCString newshostHeaderVal;
00567     rv = nntpService->GenerateNewsHeaderValsForPosting(n2, getter_Copies(newsgroupsHeaderVal), getter_Copies(newshostHeaderVal));
00568     if (NS_FAILED(rv)) {
00569       *status = rv;
00570       return nsnull;
00571     }
00572 
00573     // fixme:the newsgroups header had better be encoded as the server-side
00574     // character encoding, but this |charset| might be different from it.
00575     ENCODE_AND_PUSH("Newsgroups: ", PR_FALSE, newsgroupsHeaderVal.get(),
00576                     charset, PR_FALSE);
00577 
00578     // If we are here, we are NOT going to send this now. (i.e. it is a Draft, 
00579     // Send Later file, etc...). Because of that, we need to store what the user
00580     // typed in on the original composition window for use later when rebuilding
00581     // the headers
00582     if (deliver_mode != nsIMsgSend::nsMsgDeliverNow && deliver_mode != nsIMsgSend::nsMsgSendUnsent)
00583     {
00584       // This is going to be saved for later, that means we should just store
00585       // what the user typed into the "Newsgroup" line in the HEADER_X_MOZILLA_NEWSHOST
00586       // header for later use by "Send Unsent Messages", "Drafts" or "Templates"
00587       PUSH_STRING (HEADER_X_MOZILLA_NEWSHOST);
00588       PUSH_STRING (": ");
00589       PUSH_STRING (newshostHeaderVal.get());
00590       PUSH_NEWLINE ();
00591     }
00592 
00593     PR_FREEIF(duppedNewsGrp);
00594     hasDisclosedRecipient = PR_TRUE;
00595   }
00596 
00597   /* #### shamelessly duplicated from above */
00598   if (pFollow && *pFollow) {
00599     /* turn whitespace into a comma list
00600     */
00601     char *duppedFollowup = PL_strdup(pFollow);
00602     if (!duppedFollowup) {
00603       PR_FREEIF(buffer);
00604       return nsnull; /* NS_ERROR_OUT_OF_MEMORY */
00605     }
00606     char *n2 = nsMsgStripLine (duppedFollowup);
00607 
00608     for (char *ptr = n2; *ptr != '\0'; ptr++) {
00609       /* find first non white space */
00610       while(!IS_SPACE(*ptr) && *ptr != ',' && *ptr != '\0')
00611         ptr++;
00612 
00613       if(*ptr == '\0')
00614         break;
00615 
00616       if(*ptr != ',')
00617         *ptr = ',';
00618 
00619       /* find next non white space */
00620       char *ptr2 = ptr+1;
00621       while(IS_SPACE(*ptr2))
00622         ptr2++;
00623 
00624       if(ptr2 != ptr+1)
00625         PL_strcpy(ptr+1, ptr2);
00626     }
00627 
00628     PUSH_STRING ("Followup-To: ");
00629     PUSH_STRING (n2);
00630     PR_Free (duppedFollowup);
00631     PUSH_NEWLINE ();
00632   }
00633 
00634   if (pTo && *pTo) {
00635     ENCODE_AND_PUSH("To: ", PR_TRUE, pTo, charset, usemime);
00636     hasDisclosedRecipient = PR_TRUE;
00637   }
00638  
00639   if (pCc && *pCc) {
00640     ENCODE_AND_PUSH("CC: ", PR_TRUE, pCc, charset, usemime);
00641     hasDisclosedRecipient = PR_TRUE;
00642   }
00643 
00644   // If we don't have disclosed recipient (only Bcc), address the message to
00645   // undisclosed-recipients to prevent problem with some servers
00646 
00647   // If we are saving the message as a draft, don't bother inserting the undisclosed recipients field. We'll take care of that when we
00648   // really send the message.
00649   if (!hasDisclosedRecipient && !isDraft) {
00650     PRBool bAddUndisclosedRecipients = PR_TRUE;
00651     prefs->GetBoolPref("mail.compose.add_undisclosed_recipients", &bAddUndisclosedRecipients);
00652     if (bAddUndisclosedRecipients) {
00653       const char* pBcc = fields->GetBcc(); //Do not free me!
00654       if (pBcc && *pBcc) {
00655         nsCOMPtr<nsIStringBundleService> stringService = do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv);
00656         if (NS_SUCCEEDED(rv)) {
00657           nsCOMPtr<nsIStringBundle> composeStringBundle;
00658           rv = stringService->CreateBundle("chrome://messenger/locale/messengercompose/composeMsgs.properties", getter_AddRefs(composeStringBundle));
00659           if (NS_SUCCEEDED(rv)) {
00660             nsXPIDLString undisclosedRecipients;
00661             rv = composeStringBundle->GetStringFromID(NS_MSG_UNDISCLOSED_RECIPIENTS, getter_Copies(undisclosedRecipients));
00662             if (NS_SUCCEEDED(rv) && !undisclosedRecipients.IsEmpty()){
00663               char * cstr = ToNewCString(undisclosedRecipients);
00664               if (cstr) {
00665                 PUSH_STRING("To: ");
00666                 PUSH_STRING(cstr);
00667                 PUSH_STRING(":;");
00668                 PUSH_NEWLINE ();
00669               }
00670               PR_Free(cstr);
00671             }
00672           }
00673         }
00674       }
00675     }
00676   }
00677 
00678   if (pSubject && *pSubject) {
00679     ENCODE_AND_PUSH("Subject: ", PR_FALSE, pSubject, charset, usemime);
00680   }
00681   
00682   // Skip no or empty priority.
00683   if (pPriority && *pPriority) {
00684     nsMsgPriorityValue priorityValue;
00685 
00686     NS_MsgGetPriorityFromString(pPriority, priorityValue);
00687 
00688     // Skip default priority.
00689     if (priorityValue != nsMsgPriority::Default) {
00690       nsCAutoString priorityName;
00691       nsCAutoString priorityValueString;
00692 
00693       NS_MsgGetPriorityValueString(priorityValue, priorityValueString);
00694       NS_MsgGetUntranslatedPriorityName(priorityValue, priorityName);
00695 
00696       // Output format: [X-Priority: <pValue> (<pName>)]
00697       PUSH_STRING("X-Priority: ");
00698       PUSH_STRING(priorityValueString.get());
00699       PUSH_STRING(" (");
00700       PUSH_STRING(priorityName.get());
00701       PUSH_STRING(")");
00702       PUSH_NEWLINE();
00703     }
00704   }
00705 
00706   if (pReference && *pReference) {
00707     PUSH_STRING ("References: ");
00708     if (PL_strlen(pReference) >= 986) {
00709       char *references = PL_strdup(pReference);
00710       char *trimAt = PL_strchr(references+1, '<');
00711       char *ptr;
00712       // per sfraser, RFC 1036 - a message header line should not exceed
00713       // 998 characters including the header identifier
00714       // retiring the earliest reference one after the other
00715       // but keep the first one for proper threading
00716       while (references && PL_strlen(references) >= 986 && trimAt) {
00717         ptr = PL_strchr(trimAt+1, '<');
00718         if (ptr)
00719           memmove(trimAt, ptr, PL_strlen(ptr)+1); // including the
00720         else
00721           break;
00722       }
00723       NS_ASSERTION(references, "null references");
00724       if (references) {
00725         PUSH_STRING (references);
00726         PR_Free(references);
00727       }
00728     }
00729     else
00730       PUSH_STRING (pReference);
00731     PUSH_NEWLINE ();
00732     {
00733       const char *lastRef = PL_strrchr(pReference, '<');
00734 
00735       if (lastRef) {
00736         PUSH_STRING ("In-Reply-To: ");
00737         PUSH_STRING (lastRef);
00738         PUSH_NEWLINE ();
00739       }
00740     }
00741   }
00742 
00743   if (pOtherHdr && *pOtherHdr) {
00744     /* Assume they already have the right newlines and continuations
00745      and so on.  for these headers, the PUSH_NEWLINE() happens in addressingWidgetOverlay.js */
00746     PUSH_STRING (pOtherHdr);
00747   }
00748 
00749   if (buffer_tail > buffer + size - 1)
00750     return nsnull;
00751 
00752   /* realloc it smaller... */
00753   buffer = (char*)PR_REALLOC (buffer, buffer_tail - buffer + 1);
00754 
00755   return buffer;
00756 }
00757 
00758 static void
00759 GenerateGlobalRandomBytes(unsigned char *buf, PRInt32 len)
00760 {
00761   static PRBool    firstTime = PR_TRUE;
00762   
00763   if (firstTime)
00764   {
00765     // Seed the random-number generator with current time so that
00766     // the numbers will be different every time we run.
00767     PRInt32 aTime;
00768     LL_L2I(aTime, PR_Now());
00769     srand( (unsigned)aTime );
00770     firstTime = PR_FALSE;
00771   }
00772   
00773   for( PRInt32 i = 0; i < len; i++ )
00774     buf[i] = rand() % 10;
00775 }
00776    
00777 char 
00778 *mime_make_separator(const char *prefix)
00779 {
00780   unsigned char rand_buf[13]; 
00781   GenerateGlobalRandomBytes(rand_buf, 12);
00782 
00783   return PR_smprintf("------------%s"
00784            "%02X%02X%02X%02X"
00785            "%02X%02X%02X%02X"
00786            "%02X%02X%02X%02X",
00787            prefix,
00788            rand_buf[0], rand_buf[1], rand_buf[2], rand_buf[3],
00789            rand_buf[4], rand_buf[5], rand_buf[6], rand_buf[7],
00790            rand_buf[8], rand_buf[9], rand_buf[10], rand_buf[11]);
00791 }
00792 
00793 static char *
00794 RFC2231ParmFolding(const char *parmName, const nsAFlatCString& charset,
00795                    const char *language, const nsAFlatString& parmValue);
00796 
00797 static char *
00798 RFC2047ParmFolding(const nsAFlatCString& aCharset,
00799                    const nsAFlatCString& aFileName, PRInt32 aParmFolding);
00800 
00801 char * 
00802 mime_generate_attachment_headers (const char *type,
00803                   const char *type_param,
00804                   const char *encoding,
00805                   const char *description,
00806                   const char *x_mac_type,
00807                   const char *x_mac_creator,
00808                   const char *real_name,
00809                   const char *base_url,
00810                   PRBool /*digest_p*/,
00811                   nsMsgAttachmentHandler * /*ma*/,
00812                   const char *attachmentCharset,
00813                   const char *bodyCharset,
00814                   PRBool bodyIsAsciiOnly,
00815                   const char *content_id, 
00816                   PRBool      aBodyDocument)
00817 {
00818   NS_ASSERTION (encoding, "null encoding");
00819 
00820   PRInt32 parmFolding = 2; // RFC 2231-compliant
00821   nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID));
00822   if (prefs)
00823     prefs->GetIntPref("mail.strictly_mime.parm_folding", &parmFolding);
00824 
00825   /* Let's encode the real name */
00826   char *encodedRealName = nsnull;
00827   nsXPIDLCString charset;   // actual charset used for MIME encode
00828   nsAutoString realName;
00829   if (real_name)
00830   {
00831     // first try main body's charset to encode the file name, 
00832     // then try local file system charset if fails
00833     CopyUTF8toUTF16(real_name, realName);
00834     if (bodyCharset && *bodyCharset &&
00835         nsMsgI18Ncheck_data_in_charset_range(bodyCharset, realName.get()))
00836       charset.Assign(bodyCharset);
00837     else
00838     {
00839       charset.Assign(nsMsgI18NFileSystemCharset());
00840       if (!nsMsgI18Ncheck_data_in_charset_range(charset.get(), realName.get()))
00841         charset.Assign("UTF-8"); // set to UTF-8 if fails again
00842     }
00843 
00844     if (parmFolding == 2 || parmFolding == 3 || parmFolding == 4) {
00845       encodedRealName = RFC2231ParmFolding("filename", charset, nsnull, 
00846                                            realName);
00847       // somehow RFC2231ParamFolding failed. fall back to RFC 2047     
00848       if (!encodedRealName || !*encodedRealName) {
00849         PR_FREEIF(encodedRealName);
00850         parmFolding = 0; 
00851       }
00852     }
00853 
00854     // Not RFC 2231 style encoding (it's not standard-compliant)
00855     if (parmFolding == 0 || parmFolding == 1) {
00856       encodedRealName =
00857         RFC2047ParmFolding(charset, nsDependentCString(real_name), parmFolding);
00858     }
00859   }
00860 
00861   nsCString buf;  // very likely to be longer than 64 characters
00862   buf.Append("Content-Type: ");
00863   buf.Append(type);
00864   if (type_param && *type_param)
00865   {
00866     if (*type_param != ';')
00867       buf.Append("; ");
00868     buf.Append(type_param);
00869   }
00870 
00871   if (mime_type_needs_charset (type)) 
00872   {
00873     
00874     char charset_label[65] = "";   // Content-Type: charset
00875     if (attachmentCharset)
00876     {
00877       PL_strncpy(charset_label, attachmentCharset, sizeof(charset_label)-1);
00878       charset_label[sizeof(charset_label)-1] = '\0';
00879     }
00880     
00881     /* If the characters are all 7bit, arguably it's better to 
00882     claim the charset to be US-ASCII. However, it causes
00883     a major 'interoperability problem' with MS OE, which makes it hard
00884     to sell Mozilla/TB to people most of whose correspondents use
00885     MS OE. MS OE turns all non-ASCII characters to question marks 
00886     in replies to messages labeled as US-ASCII if users select 'send as is'
00887     with MIME turned on. (with MIME turned off, this happens without
00888     any warning.) To avoid this, we use the label 'US-ASCII' only when
00889     it's explicitly requested by setting the hidden pref.
00890     'mail.label_ascii_only_mail_as_us_ascii'. (bug 247958) */
00891     PRBool labelAsciiAsAscii = PR_FALSE;
00892     if (prefs)
00893       prefs->GetBoolPref("mail.label_ascii_only_mail_as_us_ascii",
00894                          &labelAsciiAsAscii);
00895     if (labelAsciiAsAscii && encoding &&
00896         !PL_strcasecmp (encoding, "7bit") && bodyIsAsciiOnly)
00897       PL_strcpy (charset_label, "us-ascii");
00898     
00899     // If charset is multibyte then no charset to be specified (apply base64 instead).
00900     // The list of file types match with PickEncoding() where we put base64 label.
00901     if ( ((attachmentCharset && !nsMsgI18Nmultibyte_charset(attachmentCharset)) ||
00902          ((PL_strcasecmp(type, TEXT_HTML) == 0) ||
00903          (PL_strcasecmp(type, TEXT_MDL) == 0) ||
00904          (PL_strcasecmp(type, TEXT_PLAIN) == 0) ||
00905          (PL_strcasecmp(type, TEXT_RICHTEXT) == 0) ||
00906          (PL_strcasecmp(type, TEXT_ENRICHED) == 0) ||
00907          (PL_strcasecmp(type, TEXT_VCARD) == 0) ||
00908          (PL_strcasecmp(type, APPLICATION_DIRECTORY) == 0) || /* text/x-vcard synonym */
00909          (PL_strcasecmp(type, TEXT_CSS) == 0) ||
00910          (PL_strcasecmp(type, TEXT_JSSS) == 0)) ||
00911          (PL_strcasecmp(encoding, ENCODING_BASE64) != 0)) &&
00912          (*charset_label))
00913     {
00914       buf.Append("; charset=");
00915       buf.Append(charset_label);
00916     }
00917   }
00918 
00919   // Only do this if we are in the body of a message
00920   if (aBodyDocument)
00921   {
00922     // Add format=flowed as in RFC 2646 if we are using that
00923     if(type && !PL_strcasecmp(type, "text/plain"))
00924     {
00925       if(UseFormatFlowed(bodyCharset))
00926         buf.Append("; format=flowed");
00927       // else
00928       // {
00929       // Don't add a markup. Could use 
00930       //        PUSH_STRING ("; format=fixed");
00931       // but it is equivalent to nothing at all and we do want
00932       // to save bandwidth. Don't we?
00933       // }
00934     }
00935   }    
00936 
00937   if (x_mac_type && *x_mac_type) {
00938     buf.Append("; x-mac-type=\"");
00939     buf.Append(x_mac_type);
00940     buf.Append("\"");
00941   }
00942 
00943   if (x_mac_creator && *x_mac_creator) {
00944     buf.Append("; x-mac-creator=\"");
00945     buf.Append(x_mac_creator);
00946     buf.Append("\"");
00947   }
00948 
00949 #ifdef EMIT_NAME_IN_CONTENT_TYPE
00950   if (encodedRealName && *encodedRealName) {
00951     // Note that we don't need to output the name field if the name encoding is
00952     // RFC 2231. If the MUA knows the RFC 2231, it should know the RFC 2183 too.
00953     if (parmFolding != 2) {
00954       char *nameValue = nsnull;
00955       if (parmFolding == 3 || parmFolding == 4)
00956         nameValue = RFC2047ParmFolding(charset, nsDependentCString(real_name),
00957                                        parmFolding);
00958       if (!nameValue || !*nameValue) {
00959         PR_FREEIF(nameValue);
00960         nameValue = encodedRealName;
00961       }
00962       buf.Append(";\r\n name=\"");
00963       buf.Append(nameValue);
00964       buf.Append("\"");
00965       if (nameValue != encodedRealName)
00966         PR_FREEIF(nameValue);
00967     }
00968   }
00969 #endif /* EMIT_NAME_IN_CONTENT_TYPE */
00970   buf.Append(CRLF);
00971 
00972   buf.Append("Content-Transfer-Encoding: ");
00973   buf.Append(encoding);
00974   buf.Append(CRLF);
00975 
00976   if (description && *description) {
00977     char *s = mime_fix_header (description);
00978     if (s) {
00979       buf.Append("Content-Description: ");
00980       buf.Append(s);
00981       buf.Append(CRLF);
00982       PR_Free(s);
00983     }
00984   }
00985 
00986   if ( (content_id) && (*content_id) )
00987   {
00988     buf.Append("Content-ID: <");
00989     buf.Append(content_id);
00990     buf.Append(">");  
00991     buf.Append(CRLF);
00992   }
00993 
00994   if (encodedRealName && *encodedRealName) {
00995     char *period = PL_strrchr(encodedRealName, '.');
00996     PRInt32 pref_content_disposition = 0;
00997 
00998     if (prefs) {
00999       nsresult rv = prefs->GetIntPref("mail.content_disposition_type",
01000                                       &pref_content_disposition);
01001       NS_ASSERTION(NS_SUCCEEDED(rv), "failed to get mail.content_disposition_type");
01002     }
01003 
01004     buf.Append("Content-Disposition: ");
01005 
01006     if (pref_content_disposition == 1)
01007       buf.Append("attachment");
01008     else
01009       if (pref_content_disposition == 2 && 
01010           (!PL_strcasecmp(type, TEXT_PLAIN) || 
01011           (period && !PL_strcasecmp(period, ".txt"))))
01012         buf.Append("attachment");
01013 
01014       /* If this document is an anonymous binary file or a vcard, 
01015       then always show it as an attachment, never inline. */
01016       else
01017         if (!PL_strcasecmp(type, APPLICATION_OCTET_STREAM) || 
01018             !PL_strcasecmp(type, TEXT_VCARD) ||
01019             !PL_strcasecmp(type, APPLICATION_DIRECTORY)) /* text/x-vcard synonym */
01020           buf.Append("attachment");
01021         else
01022           buf.Append("inline");
01023 
01024     if (parmFolding == 0 || parmFolding == 1) {
01025       buf.Append(";\r\n filename=\"");
01026       buf.Append(encodedRealName);
01027       buf.Append("\"" CRLF);
01028     }
01029     else // if (parmFolding == 2 || parmFolding == 3 || parmFolding == 4)
01030     {
01031       buf.Append(";\r\n ");
01032       buf.Append(encodedRealName);
01033       buf.Append(CRLF);
01034     }
01035   }
01036   else
01037     if (type &&
01038         (!PL_strcasecmp (type, MESSAGE_RFC822) ||
01039         !PL_strcasecmp (type, MESSAGE_NEWS)))
01040       buf.Append("Content-Disposition: inline" CRLF);
01041   
01042 #ifdef GENERATE_CONTENT_BASE
01043   /* If this is an HTML document, and we know the URL it originally
01044      came from, write out a Content-Base header. */
01045   if (type &&
01046       (!PL_strcasecmp (type, TEXT_HTML) ||
01047       !PL_strcasecmp (type, TEXT_MDL)) &&
01048       base_url && *base_url)
01049   {
01050     PRInt32 col = 0;
01051     const char *s = base_url;
01052     const char *colon = PL_strchr (s, ':');
01053     PRBool useContentLocation = PR_FALSE;   /* rhp - add this  */
01054 
01055     if (!colon)
01056       goto GIVE_UP_ON_CONTENT_BASE;  /* malformed URL? */
01057 
01058     /* Don't emit a content-base that points to (or into) a news or
01059        mail message. */
01060     if (!PL_strncasecmp (s, "news:", 5) ||
01061         !PL_strncasecmp (s, "snews:", 6) ||
01062         !PL_strncasecmp (s, "IMAP:", 5) ||
01063         !PL_strncasecmp (s, "file:", 5) ||    /* rhp: fix targets from included HTML files */
01064         !PL_strncasecmp (s, "mailbox:", 8))
01065       goto GIVE_UP_ON_CONTENT_BASE;
01066 
01067     /* rhp - Put in a pref for using Content-Location instead of Content-Base.
01068            This will get tweaked to default to true in 5.0
01069     */
01070     if (prefs) 
01071       prefs->GetBoolPref("mail.use_content_location_on_send", &useContentLocation);
01072 
01073     if (useContentLocation)
01074       buf.Append("Content-Location: \"");
01075     else
01076       buf.Append("Content-Base: \"");
01077     /* rhp - Pref for Content-Location usage */
01078 
01079 /* rhp: this is to work with the Content-Location stuff */
01080 CONTENT_LOC_HACK:
01081 
01082     while (*s != 0 && *s != '#')
01083     {
01084       PRUint32 ot=buf.Length();
01085       char tmp[]="\x00\x00";
01086       /* URLs must be wrapped at 40 characters or less. */
01087       if (col >= 38) {
01088         buf.Append(CRLF "\t");
01089         col = 0;
01090       }
01091 
01092       if (*s == ' ')
01093         buf.Append("%20");
01094       else if (*s == '\t')
01095         buf.Append("%09");
01096       else if (*s == '\n')
01097         buf.Append("%0A");
01098       else if (*s == '\r')
01099         buf.Append("%0D");
01100       else {
01101              tmp[0]=*s;
01102              buf.Append(tmp);
01103       }
01104       s++;
01105       col += (buf.Length() - ot);
01106     }
01107     buf.Append("\"" CRLF);
01108 
01109     /* rhp: this is to try to get around this fun problem with Content-Location */
01110     if (!useContentLocation) {
01111       buf.Append("Content-Location: \"");
01112       s = base_url;
01113       col = 0;
01114       useContentLocation = PR_TRUE;
01115       goto CONTENT_LOC_HACK;
01116     }
01117     /* rhp: this is to try to get around this fun problem with Content-Location */
01118 
01119 GIVE_UP_ON_CONTENT_BASE:
01120     ;
01121   }
01122 #endif /* GENERATE_CONTENT_BASE */
01123 
01124   /* realloc it smaller... */
01125 
01126 #ifdef DEBUG_jungshik
01127   printf ("header=%s\n", buf.get());
01128 #endif
01129   PR_Free(encodedRealName);
01130   return PL_strdup(buf.get());
01131 }
01132 
01133 static PRBool isValidHost( const char* host )
01134 {
01135   if ( host )
01136     for (const char *s = host; *s; ++s)
01137       if  (  !nsCRT::IsAsciiAlpha(*s)
01138          && !nsCRT::IsAsciiDigit(*s)
01139          && *s != '-'
01140          && *s != '_'
01141          && *s != '.'
01142          )
01143       {
01144        host = nsnull;
01145        break;
01146       }
01147 
01148   return nsnull != host;
01149 }
01150 
01151 char *
01152 msg_generate_message_id (nsIMsgIdentity *identity)
01153 {
01154   PRUint32 now;
01155   PRTime prNow = PR_Now();
01156   PRInt64 microSecondsPerSecond, intermediateResult;
01157   
01158   LL_I2L(microSecondsPerSecond, PR_USEC_PER_SEC);
01159   LL_DIV(intermediateResult, prNow, microSecondsPerSecond);
01160     LL_L2UI(now, intermediateResult);
01161 
01162   PRUint32 salt = 0;
01163   const char *host = 0;
01164   
01165   nsXPIDLCString forcedFQDN;
01166   nsXPIDLCString from;
01167   nsresult rv = NS_OK;
01168 
01169   rv = identity->GetCharAttribute("FQDN", getter_Copies(forcedFQDN));
01170 
01171   if (NS_SUCCEEDED(rv) && forcedFQDN)
01172     host = forcedFQDN.get();
01173 
01174   if (!isValidHost(host))
01175   {
01176     nsresult rv = identity->GetEmail(getter_Copies(from));
01177     if (NS_SUCCEEDED(rv) && from)
01178       host = strchr(from,'@');
01179 
01180     // No '@'? Munged address, anti-spam?
01181     // see bug #197203
01182     if (host)
01183       ++host;
01184   }
01185 
01186   if (!isValidHost(host))
01187   /* If we couldn't find a valid host name to use, we can't generate a
01188      valid message ID, so bail, and let NNTP and SMTP generate them. */
01189     return 0;
01190 
01191   GenerateGlobalRandomBytes((unsigned char *) &salt, sizeof(salt));
01192   return PR_smprintf("<%lX.%lX@%s>",
01193            (unsigned long) now, (unsigned long) salt, host);
01194 }
01195 
01196 
01197 inline static PRBool is7bitCharset(const nsAFlatCString& charset)
01198 {
01199   // charset name is canonical (no worry about case-sensitivity)
01200   return charset.EqualsLiteral("HZ-GB-2312") ||
01201          Substring(charset, 0, 8).EqualsLiteral("ISO-2022-");
01202 }
01203 
01204 #define PR_MAX_FOLDING_LEN 75     // this is to gurantee the folded line will
01205                                   // never be greater than 78 = 75 + CRLFLWSP
01206 /*static */ char *
01207 RFC2231ParmFolding(const char *parmName, const nsAFlatCString& charset,
01208                    const char *language, const nsAFlatString& parmValue)
01209 {
01210   NS_ENSURE_TRUE(parmName && *parmName && !parmValue.IsEmpty(), nsnull);
01211 
01212   PRBool needEscape;
01213   char *dupParm = nsnull; 
01214 
01215   if (!IsASCII(parmValue) || is7bitCharset(charset)) {
01216     needEscape = PR_TRUE;
01217     nsCAutoString nativeParmValue; 
01218     ConvertFromUnicode(charset.get(), parmValue, nativeParmValue);
01219     dupParm = nsEscape(nativeParmValue.get(), url_All);
01220   }
01221   else {
01222     needEscape = PR_FALSE;
01223     dupParm = 
01224       msg_make_filename_qtext(NS_LossyConvertUTF16toASCII(parmValue).get(),
01225                               PR_TRUE);
01226   }
01227 
01228   if (!dupParm)
01229     return nsnull; 
01230 
01231   PRInt32 parmNameLen = PL_strlen(parmName);
01232   PRInt32 parmValueLen = PL_strlen(dupParm);
01233 
01234   if (needEscape)
01235     parmNameLen += 5;   // *=__'__'___ or *[0]*=__'__'__ or *[1]*=___
01236   else
01237     parmNameLen += 5;   // *[0]="___";
01238 
01239   PRInt32 languageLen = language ?  PL_strlen(language) : 0;
01240   PRInt32 charsetLen = charset.Length();
01241   char *foldedParm = nsnull; 
01242 
01243   if ((parmValueLen + parmNameLen + charsetLen + languageLen) <
01244       PR_MAX_FOLDING_LEN)
01245   {
01246     foldedParm = PL_strdup(parmName);
01247     if (needEscape)
01248     {
01249       NS_MsgSACat(&foldedParm, "*=");
01250       if (charsetLen)
01251         NS_MsgSACat(&foldedParm, charset.get());
01252       NS_MsgSACat(&foldedParm, "'");
01253       if (languageLen)
01254         NS_MsgSACat(&foldedParm, language);
01255       NS_MsgSACat(&foldedParm, "'");
01256     }
01257     else
01258       NS_MsgSACat(&foldedParm, "=\"");
01259     NS_MsgSACat(&foldedParm, dupParm);
01260     if (!needEscape)
01261       NS_MsgSACat(&foldedParm, "\"");
01262     goto done;
01263   }
01264   else
01265   {
01266     int curLineLen = 0;
01267     int counter = 0;
01268     char digits[32];
01269     char *start = dupParm;
01270     char *end = NULL;
01271     char tmp = 0;
01272 
01273     while (parmValueLen > 0)
01274     {
01275       curLineLen = 0;
01276       if (counter == 0) {
01277         PR_FREEIF(foldedParm)
01278         foldedParm = PL_strdup(parmName);
01279       }
01280       else {
01281         NS_MsgSACat(&foldedParm, ";\r\n ");
01282         NS_MsgSACat(&foldedParm, parmName);
01283       }
01284       PR_snprintf(digits, sizeof(digits), "*%d", counter);
01285       NS_MsgSACat(&foldedParm, digits);
01286       curLineLen += PL_strlen(digits);
01287       if (needEscape)
01288       {
01289         NS_MsgSACat(&foldedParm, "*=");
01290         if (counter == 0)
01291         {
01292           if (charsetLen)
01293             NS_MsgSACat(&foldedParm, charset.get());
01294           NS_MsgSACat(&foldedParm, "'");
01295           if (languageLen)
01296             NS_MsgSACat(&foldedParm, language);
01297           NS_MsgSACat(&foldedParm, "'");
01298           curLineLen += charsetLen;
01299           curLineLen += languageLen;
01300         }
01301       }
01302       else
01303       {
01304         NS_MsgSACat(&foldedParm, "=\"");
01305       }
01306       counter++;
01307       curLineLen += parmNameLen;
01308       if (parmValueLen <= PR_MAX_FOLDING_LEN - curLineLen)
01309         end = start + parmValueLen;
01310       else
01311         end = start + (PR_MAX_FOLDING_LEN - curLineLen);
01312 
01313       tmp = 0;
01314       if (*end && needEscape)
01315       {
01316         // check to see if we are in the middle of escaped char
01317         if (*end == '%')
01318         {
01319           tmp = '%'; *end = nsnull;
01320         }
01321         else if (end-1 > start && *(end-1) == '%')
01322         {
01323           end -= 1; tmp = '%'; *end = nsnull;
01324         }
01325         else if (end-2 > start && *(end-2) == '%')
01326         {
01327           end -= 2; tmp = '%'; *end = nsnull;
01328         }
01329         else
01330         {
01331           tmp = *end; *end = nsnull;
01332         }
01333       }
01334       else
01335       {
01336         // XXX should check if we are in the middle of escaped char (RFC 822)
01337         tmp = *end; *end = nsnull;
01338       }
01339       NS_MsgSACat(&foldedParm, start);
01340       if (!needEscape)
01341         NS_MsgSACat(&foldedParm, "\"");
01342 
01343       parmValueLen -= (end-start);
01344       if (tmp)
01345         *end = tmp;
01346       start = end;
01347     }
01348   }
01349 
01350 done:
01351   if (needEscape) 
01352     nsMemory::Free(dupParm);
01353   else 
01354     PR_Free(dupParm);
01355   return foldedParm;
01356 }
01357 
01358 /*static */ char *
01359 RFC2047ParmFolding(const nsAFlatCString& aCharset,
01360                    const nsAFlatCString& aFileName, PRInt32 aParmFolding)
01361 {
01362   PRBool usemime = nsMsgMIMEGetConformToStandard();
01363   char *encodedRealName =
01364     nsMsgI18NEncodeMimePartIIStr(aFileName.get(), PR_FALSE, aCharset.get(),
01365                                  0, usemime);
01366 
01367   if (!encodedRealName || !*encodedRealName) {
01368     PR_FREEIF(encodedRealName);
01369     encodedRealName = (char *) PR_Malloc(aFileName.Length() + 1);
01370     if (encodedRealName)
01371       PL_strcpy(encodedRealName, aFileName.get());
01372   }
01373 
01374   // Now put backslashes before special characters per RFC 822 
01375   char *qtextName =
01376     msg_make_filename_qtext(encodedRealName,
01377       aParmFolding == 0 || aParmFolding == 3 ? PR_TRUE : PR_FALSE);
01378   if (qtextName) {
01379     PR_FREEIF(encodedRealName);
01380     encodedRealName = qtextName;
01381   }
01382   return encodedRealName;
01383 }
01384 
01385 PRBool 
01386 mime_7bit_data_p (const char *string, PRUint32 size)
01387 {
01388   if ((!string) || (!*string))
01389     return PR_TRUE;
01390 
01391   char *ptr = (char *)string;
01392   for (PRUint32 i=0; i<size; i++)
01393   {
01394     if ((unsigned char) ptr[i] > 0x7F)
01395       return PR_FALSE;
01396   }
01397   return PR_TRUE;
01398 }
01399 
01400 /* Strips whitespace, and expands newlines into newline-tab for use in
01401    mail headers.  Returns a new string or 0 (if it would have been empty.)
01402    If addr_p is true, the addresses will be parsed and reemitted as
01403    rfc822 mailboxes.
01404  */
01405 char * 
01406 mime_fix_header_1 (const char *string, PRBool addr_p, PRBool news_p)
01407 {
01408   char *new_string;
01409   const char *in;
01410   char *out;
01411   PRInt32 i, old_size, new_size;
01412 
01413   if (!string || !*string)
01414     return 0;
01415 
01416   if (addr_p) {
01417     nsresult rv = NS_OK;
01418     nsCOMPtr<nsIMsgHeaderParser> pHeader = do_GetService(NS_MAILNEWS_MIME_HEADER_PARSER_CONTRACTID, &rv);
01419     if (NS_SUCCEEDED(rv)) {
01420       char *n;
01421       pHeader->ReformatHeaderAddresses(nsnull, string, &n);
01422       if (n)        return n;
01423     }
01424   }
01425 
01426   old_size = PL_strlen (string);
01427   new_size = old_size;
01428   for (i = 0; i < old_size; i++)
01429     if (string[i] == nsCRT::CR || string[i] == nsCRT::LF)
01430       new_size += 2;
01431 
01432   new_string = (char *) PR_Malloc (new_size + 1);
01433   if (! new_string)
01434     return 0;
01435 
01436   in  = string;
01437   out = new_string;
01438 
01439   /* strip leading whitespace. */
01440   while (IS_SPACE (*in))
01441     in++;
01442 
01443   /* replace CR, LF, or CRLF with CRLF-TAB. */
01444   while (*in) {
01445     if (*in == nsCRT::CR || *in == nsCRT::LF) {
01446       if (*in == nsCRT::CR && in[1] == nsCRT::LF)
01447         in++;
01448       in++;
01449       *out++ = nsCRT::CR;
01450       *out++ = nsCRT::LF;
01451       *out++ = '\t';
01452     }
01453     else
01454       if (news_p && *in == ',') {
01455         *out++ = *in++;
01456         /* skip over all whitespace after a comma. */
01457         while (IS_SPACE (*in))
01458           in++;
01459       }
01460       else
01461         *out++ = *in++;
01462   }
01463   *out = 0;
01464 
01465   /* strip trailing whitespace. */
01466   while (out > in && IS_SPACE (out[-1]))
01467     *out-- = 0;
01468 
01469   /* If we ended up throwing it all away, use 0 instead of "". */
01470   if (!*new_string) {
01471     PR_Free (new_string);
01472     new_string = 0;
01473   }
01474 
01475   return new_string;
01476 }
01477 
01478 char * 
01479 mime_fix_header (const char *string)
01480 {
01481   return mime_fix_header_1 (string, PR_FALSE, PR_FALSE);
01482 }
01483 
01484 char * 
01485 mime_fix_addr_header (const char *string)
01486 {
01487   return mime_fix_header_1 (string, PR_TRUE, PR_FALSE);
01488 }
01489 
01490 char * 
01491 mime_fix_news_header (const char *string)
01492 {
01493   return mime_fix_header_1 (string, PR_FALSE, PR_TRUE);
01494 }
01495 
01496 PRBool
01497 mime_type_requires_b64_p (const char *type)
01498 {
01499   if (!type || !PL_strcasecmp (type, UNKNOWN_CONTENT_TYPE))
01500   /* Unknown types don't necessarily require encoding.  (Note that
01501      "unknown" and "application/octet-stream" aren't the same.) */
01502   return PR_FALSE;
01503 
01504   else if (!PL_strncasecmp (type, "image/", 6) ||
01505        !PL_strncasecmp (type, "audio/", 6) ||
01506        !PL_strncasecmp (type, "video/", 6) ||
01507        !PL_strncasecmp (type, "application/", 12))
01508   {
01509     /* The following types are application/ or image/ types that are actually
01510      known to contain textual data (meaning line-based, not binary, where
01511      CRLF conversion is desired rather than disasterous.)  So, if the type
01512      is any of these, it does not *require* base64, and if we do need to
01513      encode it for other reasons, we'll probably use quoted-printable.
01514      But, if it's not one of these types, then we assume that any subtypes
01515      of the non-"text/" types are binary data, where CRLF conversion would
01516      corrupt it, so we use base64 right off the bat.
01517 
01518      The reason it's desirable to ship these as text instead of just using
01519      base64 all the time is mainly to preserve the readability of them for
01520      non-MIME users: if I mail a /bin/sh script to someone, it might not
01521      need to be encoded at all, so we should leave it readable if we can.
01522 
01523      This list of types was derived from the comp.mail.mime FAQ, section
01524      10.2.2, "List of known unregistered MIME types" on 2-Feb-96.
01525      */
01526     static const char *app_and_image_types_which_are_really_text[] = {
01527     "application/mac-binhex40",   /* APPLICATION_BINHEX */
01528     "application/pgp",        /* APPLICATION_PGP */
01529     "application/pgp-keys",
01530     "application/x-pgp-message",  /* APPLICATION_PGP2 */
01531     "application/postscript",   /* APPLICATION_POSTSCRIPT */
01532     "application/x-uuencode",   /* APPLICATION_UUENCODE */
01533     "application/x-uue",      /* APPLICATION_UUENCODE2 */
01534     "application/uue",        /* APPLICATION_UUENCODE4 */
01535     "application/uuencode",     /* APPLICATION_UUENCODE3 */
01536     "application/sgml",
01537     "application/x-csh",
01538     "application/javascript",
01539     "application/ecmascript",
01540     "application/x-javascript",
01541     "application/x-latex",
01542     "application/x-macbinhex40",
01543     "application/x-ns-proxy-autoconfig",
01544     "application/x-www-form-urlencoded",
01545     "application/x-perl",
01546     "application/x-sh",
01547     "application/x-shar",
01548     "application/x-tcl",
01549     "application/x-tex",
01550     "application/x-texinfo",
01551     "application/x-troff",
01552     "application/x-troff-man",
01553     "application/x-troff-me",
01554     "application/x-troff-ms",
01555     "application/x-troff-ms",
01556     "application/x-wais-source",
01557     "image/x-bitmap",
01558     "image/x-pbm",
01559     "image/x-pgm",
01560     "image/x-portable-anymap",
01561     "image/x-portable-bitmap",
01562     "image/x-portable-graymap",
01563     "image/x-portable-pixmap",    /* IMAGE_PPM */
01564     "image/x-ppm",
01565     "image/x-xbitmap",        /* IMAGE_XBM */
01566     "image/x-xbm",          /* IMAGE_XBM2 */
01567     "image/xbm",          /* IMAGE_XBM3 */
01568     "image/x-xpixmap",
01569     "image/x-xpm",
01570     0 };
01571     const char **s;
01572     for (s = app_and_image_types_which_are_really_text; *s; s++)
01573     if (!PL_strcasecmp (type, *s))
01574       return PR_FALSE;
01575 
01576     /* All others must be assumed to be binary formats, and need Base64. */
01577     return PR_TRUE;
01578   }
01579 
01580   else
01581   return PR_FALSE;
01582 }
01583 
01584 //
01585 // Some types should have a "charset=" parameter, and some shouldn't.
01586 // This is what decides. 
01587 //
01588 PRBool 
01589 mime_type_needs_charset (const char *type)
01590 {
01591   /* Only text types should have charset. */
01592   if (!type || !*type)
01593     return PR_FALSE;
01594   else 
01595     if (!PL_strncasecmp (type, "text", 4))
01596       return PR_TRUE;
01597     else
01598       return PR_FALSE;
01599 }
01600 
01601 /* Given a string, convert it to 'qtext' (quoted text) for RFC822 header purposes. */
01602 char *
01603 msg_make_filename_qtext(const char *srcText, PRBool stripCRLFs)
01604 {
01605   /* newString can be at most twice the original string (every char quoted). */
01606   char *newString = (char *) PR_Malloc(PL_strlen(srcText)*2 + 1);
01607   if (!newString) return NULL;
01608 
01609   const char *s = srcText;
01610   const char *end = srcText + PL_strlen(srcText);
01611   char *d = newString;
01612 
01613   while(*s)
01614   {
01615     /*  Put backslashes in front of existing backslashes, or double quote
01616       characters.
01617       If stripCRLFs is true, don't write out CRs or LFs. Otherwise,
01618       write out a backslash followed by the CR but not
01619       linear-white-space.
01620       We might already have quoted pair of "\ " or "\\t" skip it. 
01621       */
01622     if (*s == '\\' || *s == '"' || 
01623       (!stripCRLFs && 
01624        (*s == nsCRT::CR && (*(s+1) != nsCRT::LF || 
01625                (*(s+1) == nsCRT::LF && (s+2) < end && !IS_SPACE(*(s+2)))))))
01626       *d++ = '\\';
01627 
01628     if (*s == nsCRT::CR)
01629     {
01630       if (stripCRLFs && *(s+1) == nsCRT::LF && (s+2) < end && IS_SPACE(*(s+2)))
01631         s += 2;     // skip CRLFLWSP
01632     }
01633     else
01634     {
01635       *d++ = *s;
01636     }
01637     s++;
01638   }
01639   *d = 0;
01640 
01641   return newString;
01642 }
01643 
01644 /* Rip apart the URL and extract a reasonable value for the `real_name' slot.
01645  */
01646 void
01647 msg_pick_real_name (nsMsgAttachmentHandler *attachment, const PRUnichar *proposedName, const char *charset)
01648 {
01649   const char *s, *s2;
01650   char *s3;
01651 
01652   if ( (attachment->m_real_name) && (*attachment->m_real_name))
01653     return;
01654 
01655   if (proposedName && *proposedName)
01656   {
01657     attachment->m_real_name = ToNewUTF8String(nsAutoString(proposedName));
01658   }
01659   else //Let's extract the name from the URL
01660   {
01661     nsXPIDLCString url;
01662   attachment->mURL->GetSpec(url);
01663 
01664   s = url;
01665   s2 = PL_strchr (s, ':');
01666   if (s2) s = s2 + 1;
01667   /* If we know the URL doesn't have a sensible file name in it,
01668    don't bother emitting a content-disposition. */
01669   if (!PL_strncasecmp (url, "news:", 5) ||
01670     !PL_strncasecmp (url, "snews:", 6) ||
01671     !PL_strncasecmp (url, "IMAP:", 5) ||
01672     !PL_strncasecmp (url, "mailbox:", 8))
01673     return;
01674 
01675   /* Take the part of the file name after the last / or \ */
01676   s2 = PL_strrchr (s, '/');
01677   if (s2) s = s2+1;
01678   s2 = PL_strrchr (s, '\\');
01679   
01680   if (s2) s = s2+1;
01681   /* Copy it into the attachment struct. */
01682   PR_FREEIF(attachment->m_real_name);
01683   attachment->m_real_name = PL_strdup (s);
01684   /* Now trim off any named anchors or search data. */
01685   s3 = PL_strchr (attachment->m_real_name, '?');
01686   if (s3) *s3 = 0;
01687   s3 = PL_strchr (attachment->m_real_name, '#');
01688   if (s3) *s3 = 0;
01689 
01690   /* Now lose the %XX crap. */
01691   nsUnescape (attachment->m_real_name);
01692   }
01693 
01694   PRInt32 parmFolding = 0;
01695 
01696   /* Now a special case for attaching uuencoded files...
01697 
01698    If we attach a file "foo.txt.uu", we will send it out with
01699    Content-Type: text/plain; Content-Transfer-Encoding: x-uuencode.
01700    When saving such a file, a mail reader will generally decode it first
01701    (thus removing the uuencoding.)  So, let's make life a little easier by
01702    removing the indication of uuencoding from the file name itself.  (This
01703    will presumably make the file name in the Content-Disposition header be
01704    the same as the file name in the "begin" line of the uuencoded data.)
01705 
01706    However, since there are mailers out there (including earlier versions of
01707    Mozilla) that will use "foo.txt.uu" as the file name, we still need to
01708    cope with that; the code which copes with that is in the MIME parser, in
01709    libmime/mimei.c.
01710    */
01711   if (attachment->m_already_encoded_p &&
01712     attachment->m_encoding)
01713   {
01714     char *result = attachment->m_real_name;
01715     PRInt32 L = PL_strlen(result);
01716     const char **exts = 0;
01717 
01718     /* #### TOTAL KLUDGE.
01719      I'd like to ask the mime.types file, "what extensions correspond
01720      to obj->encoding (which happens to be "x-uuencode") but doing that
01721      in a non-sphagetti way would require brain surgery.  So, since
01722      currently uuencode is the only content-transfer-encoding which we
01723      understand which traditionally has an extension, we just special-
01724      case it here!  
01725 
01726      Note that it's special-cased in a similar way in libmime/mimei.c.
01727      */
01728     if (!PL_strcasecmp(attachment->m_encoding, ENCODING_UUENCODE) ||
01729       !PL_strcasecmp(attachment->m_encoding, ENCODING_UUENCODE2) ||
01730       !PL_strcasecmp(attachment->m_encoding, ENCODING_UUENCODE3) ||
01731       !PL_strcasecmp(attachment->m_encoding, ENCODING_UUENCODE4))
01732     {
01733       static const char *uue_exts[] = { "uu", "uue", 0 };
01734       exts = uue_exts;
01735     }
01736 
01737     while (exts && *exts)
01738     {
01739       const char *ext = *exts;
01740       PRInt32 L2 = PL_strlen(ext);
01741       if (L > L2 + 1 &&             /* long enough */
01742         result[L - L2 - 1] == '.' &&      /* '.' in right place*/
01743         !PL_strcasecmp(ext, result + (L - L2))) /* ext matches */
01744       {
01745         result[L - L2 - 1] = 0;   /* truncate at '.' and stop. */
01746         break;
01747       }
01748       exts++;
01749     }
01750   }
01751 }
01752 
01753 // Utility to create a nsIURI object...
01754 nsresult 
01755 nsMsgNewURL(nsIURI** aInstancePtrResult, const char * aSpec)
01756 { 
01757   nsresult rv = NS_OK;
01758   if (nsnull == aInstancePtrResult) 
01759     return NS_ERROR_NULL_POINTER;
01760   nsCOMPtr<nsIIOService> pNetService(do_GetService(kIOServiceCID, &rv)); 
01761   if (NS_SUCCEEDED(rv) && pNetService)
01762   {
01763     if (PL_strstr(aSpec, "://") == nsnull && strncmp(aSpec, "data:", 5))
01764     {
01765       //XXXjag Temporary fix for bug 139362 until the real problem(bug 70083) get fixed
01766       nsCAutoString uri(NS_LITERAL_CSTRING("http://") + nsDependentCString(aSpec));
01767       rv = pNetService->NewURI(uri, nsnull, nsnull, aInstancePtrResult);
01768     }
01769     else
01770       rv = pNetService->NewURI(nsDependentCString(aSpec), nsnull, nsnull, aInstancePtrResult);
01771   }
01772   return rv;
01773 }
01774 
01775 PRBool
01776 nsMsgIsLocalFile(const char *url)
01777 {
01778   /*
01779     A url is considered as a local file if it's start with file://
01780     But on Window, we need to filter UNC file url because there
01781     are not really local file. Those start with file:////
01782   */
01783   if (PL_strncasecmp(url, "file://", 7) == 0)
01784   {
01785 #ifdef XP_WIN
01786     if (PL_strncasecmp(url, "file:////", 9) == 0)
01787       return PR_FALSE;
01788 #endif
01789     return PR_TRUE;
01790   }
01791   else
01792     return PR_FALSE;
01793 }
01794 
01795 char
01796 *nsMsgGetLocalFileFromURL(const char *url)
01797 {
01798 #ifdef XP_MAC
01799   nsFileURL fileURL(url);
01800   const char * nativePath = fileURL.GetFileSpec().GetNativePathCString();
01801   return nsCRT::strdup (nativePath);
01802 #else
01803   char * finalPath;
01804   NS_ASSERTION(PL_strncasecmp(url, "file://", 7) == 0, "invalid url");
01805   finalPath = (char*)PR_Malloc(strlen(url));
01806   if (finalPath == NULL)
01807     return NULL;
01808   strcpy(finalPath, url+6+1);
01809   return finalPath;
01810 #endif
01811 }
01812 
01813 char *
01814 nsMsgPlatformFileToURL (nsFileSpec  aFileSpec)
01815 {
01816   nsFileURL   tURL(aFileSpec);
01817   const char  *tPtr = nsnull;
01818 
01819   tPtr = tURL.GetURLString();
01820   if (tPtr)
01821     return PL_strdup(tPtr);
01822   else
01823     return nsnull;
01824 }
01825 
01826 char * 
01827 nsMsgParseURLHost(const char *url)
01828 {
01829   nsIURI        *workURI = nsnull;
01830   nsresult      rv;
01831 
01832   rv = nsMsgNewURL(&workURI, url);
01833   if (NS_FAILED(rv) || !workURI)
01834     return nsnull;
01835   
01836   nsCAutoString host;
01837   rv = workURI->GetHost(host);
01838   NS_IF_RELEASE(workURI);
01839   if (NS_FAILED(rv))
01840     return nsnull;
01841 
01842   return ToNewCString(host);
01843 }
01844 
01845 char *
01846 GenerateFileNameFromURI(nsIURI *aURL)
01847 {
01848   nsresult    rv; 
01849   nsXPIDLCString file;
01850   nsXPIDLCString spec;
01851   char        *returnString;
01852   char        *cp = nsnull;
01853   char        *cp1 = nsnull;
01854 
01855   rv = aURL->GetPath(file);
01856   if ( NS_SUCCEEDED(rv) && file)
01857   {
01858     char *newFile = PL_strdup(file);
01859     if (!newFile)
01860       return nsnull;
01861 
01862     // strip '/'
01863     cp = PL_strrchr(newFile, '/');
01864     if (cp)
01865       ++cp;
01866     else
01867       cp = newFile;
01868 
01869     if (*cp)
01870     {
01871       if ((cp1 = PL_strchr(cp, '/'))) *cp1 = 0;  
01872       if ((cp1 = PL_strchr(cp, '?'))) *cp1 = 0;  
01873       if ((cp1 = PL_strchr(cp, '>'))) *cp1 = 0;  
01874       if (*cp != '\0')
01875       {
01876         returnString = PL_strdup(cp);
01877         PR_FREEIF(newFile);
01878         return returnString;
01879       }
01880     }
01881     else
01882       return nsnull;
01883   }
01884 
01885   cp = nsnull;
01886   cp1 = nsnull;
01887 
01888 
01889   rv = aURL->GetSpec(spec);
01890   if ( NS_SUCCEEDED(rv) && spec)
01891   {
01892     char *newSpec = PL_strdup(spec);
01893     if (!newSpec)
01894       return nsnull;
01895 
01896     char *cp2 = NULL, *cp3=NULL ;
01897 
01898     // strip '"' 
01899     cp2 = newSpec;
01900     while (*cp2 == '"') 
01901       cp2++;
01902     if ((cp3 = PL_strchr(cp2, '"')))
01903       *cp3 = 0;
01904 
01905     char *hostStr = nsMsgParseURLHost(cp2);
01906     if (!hostStr)
01907       hostStr = PL_strdup(cp2);
01908 
01909     PRBool isHTTP = PR_FALSE;
01910     if (NS_SUCCEEDED(aURL->SchemeIs("http", &isHTTP)) && isHTTP)
01911     {
01912         returnString = PR_smprintf("%s.html", hostStr);
01913         PR_FREEIF(hostStr);
01914     }
01915     else
01916       returnString = hostStr;
01917 
01918     PR_FREEIF(newSpec);
01919     return returnString;
01920   }
01921 
01922   return nsnull;
01923 }
01924 
01925 //
01926 // This routine will generate a content id for use in a mail part.
01927 // It will take the part number passed in as well as the email 
01928 // address. If the email address is null or invalid, we will simply
01929 // use netscape.com for the interesting part. The content ID's will
01930 // look like the following:
01931 //
01932 //      Content-ID: <part1.36DF1DCE.73B5A330@netscape.com>
01933 //
01934 char *
01935 mime_gen_content_id(PRUint32 aPartNum, const char *aEmailAddress)
01936 {
01937   PRInt32           randLen = 5;
01938   unsigned char     rand_buf1[5]; 
01939   unsigned char     rand_buf2[5]; 
01940   const char        *domain = nsnull;
01941   const char        *defaultDomain = "@netscape.com";
01942 
01943   memset(rand_buf1, 0, randLen-1);
01944   memset(rand_buf2, 0, randLen-1);
01945 
01946   GenerateGlobalRandomBytes(rand_buf1, randLen);
01947   GenerateGlobalRandomBytes(rand_buf2, randLen);
01948 
01949   // Find the @domain.com string...
01950   if (aEmailAddress && *aEmailAddress)
01951     domain = NS_CONST_CAST(const char*, PL_strchr(aEmailAddress, '@'));
01952 
01953   if (!domain)
01954     domain = defaultDomain;
01955 
01956   char *retVal = PR_smprintf("part%d."
01957                               "%02X%02X%02X%02X"
01958                               "."
01959                               "%02X%02X%02X%02X"
01960                               "%s",
01961                               aPartNum,
01962                               rand_buf1[0], rand_buf1[1], rand_buf1[2], rand_buf1[3],
01963                               rand_buf2[0], rand_buf2[1], rand_buf2[2], rand_buf2[3],
01964                               domain);
01965 
01966   return retVal;
01967 }
01968 
01969 char *
01970 GetFolderURIFromUserPrefs(nsMsgDeliverMode aMode, nsIMsgIdentity* identity)
01971 {
01972   nsresult rv;
01973   char *uri = nsnull;
01974   
01975   if (aMode == nsIMsgSend::nsMsgQueueForLater)       // QueueForLater (Outbox)
01976   {
01977     nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv)); 
01978     if (NS_FAILED(rv)) 
01979       return nsnull;
01980     rv = prefs->GetCharPref("mail.default_sendlater_uri", &uri);   
01981     if (NS_FAILED(rv) || !uri) 
01982     {
01983       uri = PR_smprintf("%s", ANY_SERVER);
01984     }
01985     else
01986     {
01987       // check if uri is unescaped, and if so, escape it and reset the pef.
01988       if (PL_strchr(uri, ' ') != nsnull)
01989       {
01990         nsCAutoString uriStr(uri);
01991         uriStr.ReplaceSubstring(" ", "%20");
01992         PR_Free(uri);
01993         uri = PL_strdup(uriStr.get());
01994         prefs->SetCharPref("mail.default_sendlater_uri", uriStr.get());
01995       }
01996     }
01997     return uri;
01998   }
01999 
02000   if (!identity)
02001     return nsnull;
02002 
02003   if (aMode == nsIMsgSend::nsMsgSaveAsDraft)    // SaveAsDraft (Drafts)
02004   {
02005     rv = identity->GetDraftFolder(&uri);
02006   }
02007   else if (aMode == nsIMsgSend::nsMsgSaveAsTemplate) // SaveAsTemplate (Templates)
02008   {
02009     rv = identity->GetStationeryFolder(&uri);
02010   }
02011   else 
02012   {
02013     PRBool doFcc = PR_FALSE;
02014     rv = identity->GetDoFcc(&doFcc);
02015     if (doFcc)
02016       rv = identity->GetFccFolder(&uri);
02017     else
02018       uri = PL_strdup("");
02019   }
02020   return uri;
02021 }
02022 
02023 //
02024 // Convert an nsString buffer to plain text...
02025 //
02026 #include "nsIParser.h"
02027 #include "nsParserCIID.h"
02028 #include "nsIHTMLToTextSink.h"
02029 #include "nsIContentSink.h"
02030 #include "nsICharsetConverterManager.h"
02031 
02032 static NS_DEFINE_CID(kCParserCID, NS_PARSER_CID); 
02033 
02040 nsresult
02041 ConvertBufToPlainText(nsString &aConBuf, PRBool formatflowed /* = PR_FALSE */)
02042 {
02043   if (aConBuf.IsEmpty())
02044     return NS_OK;
02045 
02046   nsresult rv;
02047   nsCOMPtr<nsIParser> parser = do_CreateInstance(kCParserCID, &rv);
02048   if (NS_SUCCEEDED(rv) && parser)
02049   {
02050     PRUint32 converterFlags = 0;
02051     PRUint32 wrapWidth = 72;
02052     
02053     converterFlags |= nsIDocumentEncoder::OutputFormatted;
02054 
02055     /*
02056     */
02057     if(formatflowed)
02058       converterFlags |= nsIDocumentEncoder::OutputFormatFlowed;
02059     
02060     nsCOMPtr<nsIContentSink> sink;
02061 
02062     sink = do_CreateInstance(NS_PLAINTEXTSINK_CONTRACTID);
02063     NS_ENSURE_TRUE(sink, NS_ERROR_FAILURE);
02064 
02065     nsCOMPtr<nsIHTMLToTextSink> textSink(do_QueryInterface(sink));
02066     NS_ENSURE_TRUE(textSink, NS_ERROR_FAILURE);
02067 
02068     nsString convertedText;
02069     textSink->Initialize(&convertedText, converterFlags, wrapWidth);
02070 
02071     parser->SetContentSink(sink);
02072 
02073     parser->Parse(aConBuf, 0, NS_LITERAL_CSTRING("text/html"), PR_FALSE, PR_TRUE);
02074     //
02075     // Now if we get here, we need to get from ASCII text to 
02076     // UTF-8 format or there is a problem downstream...
02077     //
02078     if (NS_SUCCEEDED(rv))
02079     {
02080       aConBuf = convertedText;
02081     }
02082   }
02083 
02084   return rv;
02085 }
02086 
02087 // Simple parser to parse Subject. 
02088 // It only supports the case when the description is within one line. 
02089 char * 
02090 nsMsgParseSubjectFromFile(nsFileSpec* fileSpec) 
02091 { 
02092   nsIFileSpec   *tmpFileSpec = nsnull;
02093   char          *subject = nsnull;
02094   char          buffer[1024];
02095   char          *ptr = &buffer[0];
02096 
02097   NS_NewFileSpecWithSpec(*fileSpec, &tmpFileSpec);
02098   if (!tmpFileSpec)
02099     return nsnull;
02100 
02101   if (NS_FAILED(tmpFileSpec->OpenStreamForReading()))
02102     return nsnull;
02103 
02104   PRBool eof = PR_FALSE;
02105 
02106   while ( NS_SUCCEEDED(tmpFileSpec->Eof(&eof)) && (!eof) )
02107   {
02108     PRBool wasTruncated = PR_FALSE;
02109 
02110     if (NS_FAILED(tmpFileSpec->ReadLine(&ptr, sizeof(buffer), &wasTruncated)))
02111       break;
02112 
02113     if (wasTruncated)
02114       continue;
02115 
02116     if (*buffer == nsCRT::CR || *buffer == nsCRT::LF || *buffer == 0) 
02117       break; 
02118 
02119     if ( !PL_strncasecmp(buffer, "Subject: ", 9) )
02120     { 
02121       subject = nsCRT::strdup(buffer + 9);
02122       break;
02123     } 
02124   } 
02125 
02126   tmpFileSpec->CloseStream();
02127   return subject; 
02128 }
02129 
02137 PRBool UseFormatFlowed(const char *charset)
02138 {
02139   // Add format=flowed as in RFC 2646 unless asked to not do that.
02140   PRBool sendFlowed = PR_TRUE;
02141   PRBool disableForCertainCharsets = PR_TRUE;
02142   nsresult rv;
02143 
02144   nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
02145   if (NS_FAILED(rv))
02146     return PR_FALSE;
02147 
02148   rv = prefs->GetBoolPref("mailnews.send_plaintext_flowed", &sendFlowed);
02149   if (NS_SUCCEEDED(rv) && !sendFlowed)
02150     return PR_FALSE;
02151 
02152   // If we shouldn't care about charset, then we are finished
02153   // checking and can go on using format=flowed
02154   if(!charset)
02155     return PR_TRUE;
02156   rv = prefs->GetBoolPref("mailnews.disable_format_flowed_for_cjk",
02157                           &disableForCertainCharsets);
02158   if (NS_SUCCEEDED(rv) && !disableForCertainCharsets)
02159     return PR_TRUE;
02160 
02161   // Just the check for charset left.
02162 
02163   // This is a raw check and might include charsets which could
02164   // use format=flowed and might exclude charsets which couldn't
02165   // use format=flowed.
02166   //
02167   // The problem is the SPACE format=flowed inserts at the end of
02168   // the line. Not all charsets like that.
02169   if( nsCRT::strcasecmp(charset, "UTF-8") &&
02170       nsMsgI18Nmultibyte_charset(charset))
02171     return PR_FALSE;
02172 
02173   return PR_TRUE;
02174 }