/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- * * The contents of this file are subject to the Netscape Public License * Version 1.0 (the "NPL"); you may not use this file except in * compliance with the NPL. You may obtain a copy of the NPL at * http://www.mozilla.org/NPL/ * * Software distributed under the NPL is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL * for the specific language governing rights and limitations under the * NPL. * * The Initial Developer of this code under the NPL is Netscape * Communications Corporation. Portions created by Netscape are * Copyright (C) 1998 Netscape Communications Corporation. All Rights * Reserved. */ #include "msg.h" #include "errcode.h" #include "rosetta.h" #include "msgprefs.h" #include "msgprnot.h" #include "prefapi.h" #include "msgmast.h" #include "proto.h" // for XP_FindSomeContext extern "C" { #include "mkreg.h" } #include "ptrarray.h" #ifdef XP_MAC #include "Memory.h" #include "Files.h" #include "ufilemgr.h" #include "uprefd.h" #else extern "C" { const char *FE_GetFolderDirectory(MWContext *c); void NET_SetMailRelayHost(char *); void NET_SetNewsHost(const char *); } #endif extern "C" { extern int MK_MSG_MAIL_DIRECTORY_CHANGED; extern int MK_MSG_SENT_L10N_NAME; extern void MIME_ConformToStandard(XP_Bool conform_p); extern int MK_MSG_UNABLE_TO_SAVE_DRAFT; extern int MK_MSG_UNABLE_TO_SAVE_TEMPLATE; } // Notify listeners that something got changed. updateCode is an int16 instead of // a MSG_PrefsNotify::NotifyCode because it was deemed a Bad Thing to include all of // msgprnot.h just to pick up that definition. void MSG_Prefs::Notify(int16 updateCode) { int i; // if we are changing mail servers, trash cached path to imap databases // m_IMAPdirectory will be reset by next reload call if ((updateCode == MSG_PrefsNotify::MailServerType) || (updateCode == MSG_PrefsNotify::PopHost)) FREEIF(m_IMAPdirectory); // Do the notification in such a way that deleted listeners don't // foul up the notification for everyone else. (m_notifying affects // the behavior of RemoveNotify so that it only NULLs deleted listeners.) m_notifying = TRUE; for (i = 0; i < m_numnotify ; i++) { if (m_notify[i]) m_notify[i]->NotifyPrefsChange((MSG_PrefsNotify::NotifyCode) updateCode); } m_notifying = FALSE; // Clean up after nulled pointers. for(i=(m_numnotify-1);i >= 0; i--) { if (! m_notify[i]) { if (i == (m_numnotify-1)) // last element is null, keep decrementing the count m_numnotify--; else // replace this null element with the last in the list m_notify[i] = m_notify[--m_numnotify]; } } } char *headerPrefNames[] = { "mail.identity.reply_to", "mail.default_cc", "mail.default_fcc", "mail.imap_sentmail_path", "news.default_cc", "news.default_fcc", "news.imap_sentmail_path", }; enum headerPrefIndices { mail_identity_reply_to=0, mail_default_cc, mail_default_fcc, // this is a mess don't change order mail_imap_sentmail_path, news_default_cc, news_default_fcc, // this is a mess don't change order news_imap_sentmail_path, num_of_header_prefs }; void MSG_Prefs::PlatformFileToURLPath(const char *platformFile, char **result) { *result = NULL; char *tmp = XP_PlatformFileToURL(platformFile); XP_ASSERT(tmp && !strncmp(tmp, "file://", 7)); if (tmp && !strncmp(tmp, "file://", 7)) *result = XP_STRDUP(&(tmp[7])); #ifdef XP_UNIX if ( *result && **result == '~' ) { char buf[1024]; char* home_dir = getenv("HOME"); char* tmp = (*result) + 1; while ( *tmp == '/' ) tmp++; /* trim trailing slashes in home_dir */ while ( (tmp = strrchr(home_dir, '/')) && tmp[1] == '\0' ) *tmp = '\0'; PR_snprintf(buf, sizeof(buf), "%s/%s", home_dir ? home_dir : "", tmp); XP_FREE(*result); *result = XP_STRDUP(buf); } #endif FREEIF(tmp); } static XP_Bool ShouldSavePrefAsBinaryAlias(const char* prefname) { #ifdef XP_MAC // Return TRUE if path names are saved as binary alias prefs (the default). // Return FALSE for the URL cases, now saved as a string (new in Nova). These are IMAP // fccs, and all drafts and all templates. if (XP_STRCMP(prefname, "mail.imap_sentmail_path") == 0) return FALSE; // URL else if (XP_STRCMP(prefname, "news.imap_sentmail_path") == 0) return FALSE; // URL else if (XP_STRCMP(prefname, "mail.default_drafts") == 0) return FALSE; // URL else if (XP_STRCMP(prefname, "mail.default_templates") == 0) return FALSE; // URL return TRUE; #else return FALSE; #endif // XP_MAC } int MSG_Prefs::GetXPDirPathPref(const char *prefName, XP_Bool /*expectFile*/, char **result) { int returnVal = PREF_NOERROR; char *tmp = NULL; *result = NULL; // in case we fail if (ShouldSavePrefAsBinaryAlias(prefName)) { returnVal = PREF_CopyPathPref(prefName, &tmp); if (returnVal == PREF_NOERROR) *result = tmp; return returnVal; } returnVal = PREF_CopyCharPref(prefName, &tmp); // paths are strings elsewhere if (returnVal == PREF_NOERROR) { // Convert pathname to an xp path. if (XP_STRLEN(tmp) > 0 && NET_URL_Type(tmp) != IMAP_TYPE_URL && NET_URL_Type(tmp) != MAILBOX_TYPE_URL) { PlatformFileToURLPath(tmp,result); XP_FREEIF(tmp); } else *result = tmp; } return returnVal; } int MSG_Prefs::SetXPMailFilePref(const char* /*prefName*/, char *xpPath) { int returnVal = PREF_NOERROR; if (NET_URL_Type(xpPath) != IMAP_TYPE_URL) { // ## mwelch 4.0b2 hack! Create the mail file if it doesn't already exist. // Mail parent folder was created at app startup, so it is // safe to assume that the parent directory exists. char *platformPath = WH_FileName(xpPath, xpMailFolder); XP_File fp = XP_FileOpen(xpPath, xpMailFolder, XP_FILE_APPEND_BIN); if (fp) XP_FileClose(fp); XP_FREEIF(platformPath); } return returnVal; } // MSG_Prefs::SetXPMailFilePref void MSG_Prefs::SetMailNewsProfileAgeFlag(int32 flag, XP_Bool set /* = TRUE */) { // each 'trick' uses this function to register that it is done, but we do not // want one trick to erase the completion of another. int32 currentAge = 0; PREF_GetIntPref("mailnews.profile_age",¤tAge); if (set) { if (!(currentAge & flag)) PREF_SetIntPref("mailnews.profile_age",(currentAge | flag)); } else { if (currentAge & flag) PREF_SetIntPref("mailnews.profile_age",(currentAge | ~flag)); } m_dirty = TRUE; } int32 MSG_Prefs::GetStartingMailNewsProfileAge() { Reload(); return m_startingMailNewsProfileAge; } void MSG_Prefs::Reload() { if (m_dirty) { // Temp vars we need to convey pref values. int32 intPref; char *strPref; int prefError = PREF_NOERROR; // Load in boolean prefs. PREF_GetBoolPref("mail.fixed_width_messages", &m_plainText); PREF_GetBoolPref("mail.auto_quote", &m_autoQuote); PREF_GetBoolPref("news.show_pretty_names", &m_showPrettyNames); PREF_GetBoolPref("news.notify.on", &m_newsNotifyOn); PREF_GetBoolPref("mail.cc_self", &m_mailBccSelf); PREF_GetBoolPref("news.cc_self", &m_newsBccSelf); PREF_GetBoolPref("mail.wrap_long_lines", &m_wraplonglines); HG63256 PREF_GetBoolPref("mail.inline_attachments", &m_noinline); PREF_GetBoolPref("mail.prompt_purge_threshhold", &m_purgeThreshholdEnabled); //Ask about compacting folders m_noinline = !m_noinline; // Load in int/enum prefs. intPref = (int) MSG_ItalicFont; // make italic if pref call fails PREF_GetIntPref("mail.quoted_style", &intPref); m_citationFont = (MSG_FONT) intPref; intPref = (int) MSG_NormalSize; // make normal size if pref call fails PREF_GetIntPref("mail.quoted_size", &intPref); m_citationFontSize = (MSG_CITATION_SIZE) intPref; intPref = 1; PREF_GetIntPref("mailnews.nav_crosses_folders", &intPref); m_navCrossesFolders = intPref; PREF_GetIntPref("mailnews.profile_age",&m_startingMailNewsProfileAge); intPref = 1; // default in case we fail prefError = PREF_GetIntPref("mail.show_headers", &intPref); switch (intPref) { case 0: m_headerstyle = MSG_ShowMicroHeaders; break; case 1: m_headerstyle = MSG_ShowSomeHeaders; break; case 2: m_headerstyle = MSG_ShowAllHeaders; break; default: XP_ASSERT(FALSE); break; } intPref = 0; // if no pref is set, return 0 - let the backend set the default port HG87635 // Server preference. // ### mwelch This used to use mail.use_imap, but we're switching // to mail.server_type as of 4.0b2. intPref = 0; // pop by default prefError = PREF_GetIntPref("mail.server_type", &intPref); m_mailInputType = intPref; m_mailServerIsIMAP = (m_mailInputType == 1); PREF_GetIntPref("mail.purge_threshhold", &m_purgeThreshhold); // Get string prefs. if (!m_freezeMailDirectory) { // m_localMailDirectory strPref = m_localMailDirectory; m_localMailDirectory = NULL; // Get the m_localMailDirectory pref. Passing in TRUE (expectFile) since the flag // tells the Mac-specific code to make use of the name (usually "\pMail") in the FSSpec // as well as the parent directory ID. GetXPDirPathPref("mail.directory", TRUE, &m_localMailDirectory); #if defined (XP_MAC) if (!m_localMailDirectory || !*m_localMailDirectory) { Assert_(FALSE); // can this happen? If not, remove this. char *newDirURL = NULL; // ### mwelch This is a hack, because the MacFE // doesn't set the mail root directory by default. FSSpec mailFolder = CPrefs::GetFolderSpec(CPrefs::MailFolder); newDirURL = CFileMgr::EncodedPathNameFromFSSpec(mailFolder, true); m_localMailDirectory = newDirURL; } #endif // It is still possible to have a NULL directory at this // point in the code. This is because the WinFE sets the default // mail directory preference after creating the prefs object. if (m_localMailDirectory) { // by arbitrary convention, the directory shouldn't have // a trailing slash int len = XP_STRLEN(m_localMailDirectory); if (len && m_localMailDirectory[len-1] == '/') m_localMailDirectory[len-1] = '\0'; #if !defined(XP_MAC) && !defined(XP_WIN) && !defined(XP_OS2) if (!strPref || strcmp(m_localMailDirectory, strPref)) { // Create the directory if it doesn't exist (Unix only) XP_StatStruct dirStat; if (-1 == XP_Stat(m_localMailDirectory, &dirStat, xpMailFolder)) XP_MakeDirectory (m_localMailDirectory, xpMailFolder); } #endif } if (strPref) XP_FREE(strPref); } HG93653 char onlineDir[256]; onlineDir[0] = '\0'; int stringSize = 256; PREF_GetCharPref("mail.imap.server_sub_directory", onlineDir, &stringSize); if ( *onlineDir && (*(onlineDir + XP_STRLEN(onlineDir) - 1) != '/') ) XP_STRCAT(onlineDir, "/"); if ((!m_OnlineImapSubDir) || ((m_OnlineImapSubDir) && XP_STRCMP(onlineDir, m_OnlineImapSubDir))) { FREEIF(m_OnlineImapSubDir); m_OnlineImapSubDir = XP_STRDUP(onlineDir); //if (XP_STRCMP(m_OnlineImapSubDir,"")) // only set it if it is not empty // IMAP_SetNamespacesFromPrefs(GetPopHost(), m_OnlineImapSubDir, "", ""); } PREF_GetBoolPref("mailnews.searchServer", &m_searchServer); PREF_GetBoolPref("mailnews.searchSubFolders", &m_searchSubFolders); PREF_GetBoolPref("mailnews.confirm.moveFoldersToTrash", &m_confirmMoveFoldersToTrash); FREEIF(m_customHeaders); PREF_CopyCharPref("mailnews.customHeaders",&m_customHeaders); FREEIF(m_citationColor); PREF_CopyCharPref("mail.citation_color", &m_citationColor); FREEIF(m_popHost); PREF_CopyCharPref("network.hosts.pop_server", &m_popHost); PREF_GetBoolPref("mail.imap.delete_is_move_to_trash", &m_ImapDeleteMoveToTrash); // Set the smtp and news (nntp) hosts. strPref = NULL; prefError = PREF_CopyCharPref("network.hosts.smtp_server", &strPref); if (prefError == PREF_NOERROR) NET_SetMailRelayHost(strPref); XP_FREEIF(strPref); for(int i=0;i<(int) num_of_header_prefs ;i++) { strPref = NULL; // Only look at the path if the "use it" bool flag is set! // Bug #45449 jrm XP_Bool doingFccPath = TRUE, wantsFccPath = FALSE; if (i == mail_default_fcc || i == mail_imap_sentmail_path) #ifdef XP_MAC PREF_GetBoolPref("mail.use_fcc", &wantsFccPath); #else wantsFccPath = TRUE; #endif else if (i == news_default_fcc || i == news_imap_sentmail_path) #ifdef XP_MAC PREF_GetBoolPref("news.use_fcc", &wantsFccPath); #else wantsFccPath = TRUE; #endif else doingFccPath = FALSE; if (doingFccPath && wantsFccPath) { prefError = GetXPDirPathPref(headerPrefNames[i], TRUE, &strPref); if ((prefError != PREF_NOERROR) || (!strPref) || (!*strPref) || (m_localMailDirectory && !XP_STRCMP(strPref, m_localMailDirectory))) { // Take the directory preference and add "Sent" to it. char *sent = XP_GetString(MK_MSG_SENT_L10N_NAME); XP_FREEIF(strPref); if (m_localMailDirectory && *m_localMailDirectory) strPref = PR_smprintf("%s/%s", m_localMailDirectory, sent); #ifdef XP_MAC // Still may need to ensure the file exists. SetXPMailFilePref(headerPrefNames[i], strPref); #endif } } else if (!doingFccPath) PREF_CopyCharPref(headerPrefNames[i], &strPref); FREEIF(m_defaultHeaders[i]); m_defaultHeaders[i] = strPref; } // Collect all the email addresses which specify the user. We'll need to know them // when checking the Reply recipients, or doing MDN receipts if (PREF_NOERROR == PREF_CopyCharPref ("mail.identity.useremail.aliases", &strPref)) { if (*strPref) // default is empty string. don't create an array for that { if (!m_emailAliases) m_emailAliases = new msg_StringArray (TRUE /*ownsMemory*/); if (m_emailAliases) { m_emailAliases->RemoveAll(); m_emailAliases->ImportTokenList (strPref); } } XP_FREE (strPref); } // Collect the email addresses which can't be considered aliases for this user. // This is intended to keep the email aliases feature from defeating the reply-to // header in messages char *replyTo = m_defaultHeaders[mail_identity_reply_to]; if (replyTo && *replyTo) { if (!m_emailAliasesNot) m_emailAliasesNot = new msg_StringArray (TRUE); if (m_emailAliasesNot) { char *addresses = NULL; int num = 0; if (0 != (num = MSG_ParseRFC822Addresses (replyTo, NULL, &addresses))) { // We're ignoring the name of the reply-to header, since the // actual address is all we care about for the alias calculation m_emailAliasesNot->RemoveAll(); for (int i = 0; i < num; i++) { m_emailAliasesNot->Add (addresses); addresses += XP_STRLEN (addresses) + 1; } } } } } m_dirty = FALSE; } int PR_CALLBACK MSG_PrefsChangeCallback(const char * prefName, void *data) { MSG_Prefs *prefs = (MSG_Prefs *) data; if (prefs) prefs->m_dirty = TRUE; // Depending on the preference being changed, // notify listeners as to the change. // Default headers first, since we have easy access to them. for (int i=0;i<(int) num_of_header_prefs;i++) { if (!XP_STRCMP(prefName, headerPrefNames[i])) { prefs->Notify(MSG_PrefsNotify::DefaultHeader); return PREF_NOERROR; } } if (!XP_STRNCMP(prefName, "netw", 4)) { // cause smtp and news host to be set to new values immediately if (prefs) prefs->Reload(); } else if (!XP_STRNCMP(prefName, "mail", 4)) { if (0) ; } return PREF_NOERROR; } MSG_Prefs::MSG_Prefs() { m_citationFont = MSG_PlainFont; m_citationFontSize = MSG_Bigger; m_plainText = TRUE; m_mailServerIsIMAP = FALSE; HG98298 m_ImapDeleteMoveToTrash = TRUE; m_IMAPdirectory = NULL; m_headerstyle = MSG_ShowSomeHeaders; m_masterForBiff = NULL; m_notifying = FALSE; // Set up dirty flag. m_dirty = TRUE; m_freezeMailDirectory = FALSE; m_emailAliases = NULL; m_emailAliasesNot = NULL; // Set up prefs callback. PREF_RegisterCallback("mail.", &MSG_PrefsChangeCallback, this); PREF_RegisterCallback("news.", &MSG_PrefsChangeCallback, this); PREF_RegisterCallback("mailnews.", &MSG_PrefsChangeCallback, this); PREF_RegisterCallback("network.hosts.", &MSG_PrefsChangeCallback, this); // Load pref values from the prefs api. // (We can rely on the string members being NULL initially // since we derive from MSG_Zap.) Reload(); // we are only interested in what this prefs value was at startup so initialize it // here rather than in Reload() m_startingMailNewsProfileAge = 0; PREF_GetIntPref("mailnews.profile_age",&m_startingMailNewsProfileAge); HG92734 } MSG_Prefs::~MSG_Prefs() { XP_ASSERT(m_numnotify == 0); FREEIF(m_localMailDirectory); FREEIF(m_citationColor); FREEIF(m_popHost); for (int i=0 ; iGetSize(); i++) if (!strcasecomp (addr, m_emailAliasesNot->GetAt(i))) return FALSE; } if (m_emailAliases) // master/prefs may not have been initialized yet { for (int i = 0; i < m_emailAliases->GetSize(); i++) { char *alias = (char*) m_emailAliases->GetAt(i); //Hacky cast: regexp API isn't const if (VALID_SXP == NET_RegExpValid (alias)) { // The alias is a regular expression, so send it into the regexp evaluator if (!NET_RegExpMatch ((char*) addr, alias, FALSE /*case sensitive*/)) return TRUE; } else { // The alias is not a regular expression, so just use a string compare if (!strcasecomp (addr, alias)) return TRUE; } } } return FALSE; }