diff options
author | Brian Smith <brian@dbsoft.org> | 2022-04-26 09:34:34 -0500 |
---|---|---|
committer | Brian Smith <brian@dbsoft.org> | 2022-04-26 10:19:00 -0500 |
commit | 378738aaa9924d0b95e2c57f27cbad2b2e644282 (patch) | |
tree | 34ce9c4ce3995576604fb4bc47d9405e661daf39 /mailnews | |
parent | 82f11ad8aaeff395629c3a3f72ece43712fd8e72 (diff) | |
download | uxp-378738aaa9924d0b95e2c57f27cbad2b2e644282.tar.gz |
Issue #1829 - Revert “Issue #1751 - Remove Mac code behind MOZ_WIDGET_TOOLKIT == 'cocoa’”
This reverts commit 1fe9c19305dadf2d5bcaa0e589fcd250389dfa8a.
Diffstat (limited to 'mailnews')
-rw-r--r-- | mailnews/base/ispdata/moz.build | 4 | ||||
-rw-r--r-- | mailnews/base/src/moz.build | 2 | ||||
-rw-r--r-- | mailnews/base/src/nsMessengerOSXIntegration.h | 63 | ||||
-rw-r--r-- | mailnews/base/src/nsMessengerOSXIntegration.mm | 700 | ||||
-rw-r--r-- | mailnews/build/moz.build | 4 | ||||
-rw-r--r-- | mailnews/compose/src/moz.build | 10 | ||||
-rw-r--r-- | mailnews/compose/src/nsMsgAppleCodes.h | 106 | ||||
-rw-r--r-- | mailnews/compose/src/nsMsgAppleDouble.h | 207 | ||||
-rw-r--r-- | mailnews/compose/src/nsMsgAppleDoubleEncode.cpp | 266 | ||||
-rw-r--r-- | mailnews/compose/src/nsMsgAppleEncode.cpp | 703 | ||||
-rw-r--r-- | mailnews/import/build/moz.build | 7 |
11 files changed, 2071 insertions, 1 deletions
diff --git a/mailnews/base/ispdata/moz.build b/mailnews/base/ispdata/moz.build index 363c2ba0c2..7de5dc2e32 100644 --- a/mailnews/base/ispdata/moz.build +++ b/mailnews/base/ispdata/moz.build @@ -2,4 +2,6 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. -# Stub. This was configuring special snowflake OSX stuff.
\ No newline at end of file +# Disable movemail for Thunderbird on OSX +if CONFIG['MOZ_MOVEMAIL'] and not (CONFIG['MOZ_THUNDERBIRD'] and CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa'): + FINAL_TARGET_FILES.isp += ['movemail.rdf'] diff --git a/mailnews/base/src/moz.build b/mailnews/base/src/moz.build index 65853daf96..91bf235c34 100644 --- a/mailnews/base/src/moz.build +++ b/mailnews/base/src/moz.build @@ -61,6 +61,8 @@ if CONFIG['OS_ARCH'] == 'WINNT': if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('gtk2', 'gtk3', 'qt'): SOURCES += ['nsMessengerUnixIntegration.cpp'] +elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa': + SOURCES += ['nsMessengerOSXIntegration.mm'] EXTRA_COMPONENTS += [ 'folderLookupService.js', diff --git a/mailnews/base/src/nsMessengerOSXIntegration.h b/mailnews/base/src/nsMessengerOSXIntegration.h new file mode 100644 index 0000000000..91f42f2a2a --- /dev/null +++ b/mailnews/base/src/nsMessengerOSXIntegration.h @@ -0,0 +1,63 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __nsMessengerOSXIntegration_h +#define __nsMessengerOSXIntegration_h + +#include "nsIMessengerOSIntegration.h" +#include "nsIFolderListener.h" +#include "nsIAtom.h" +#include "nsITimer.h" +#include "nsCOMPtr.h" +#include "nsStringGlue.h" +#include "nsIObserver.h" +#include "nsIAlertsService.h" +#include "mozINewMailListener.h" + +#define NS_MESSENGEROSXINTEGRATION_CID \ + {0xaa83266, 0x4225, 0x4c4b, \ + {0x93, 0xf8, 0x94, 0xb1, 0x82, 0x58, 0x6f, 0x93}} + +class nsIStringBundle; + +class nsMessengerOSXIntegration : public nsIMessengerOSIntegration, + public nsIFolderListener, + public nsIObserver, + public mozINewMailListener +{ +public: + nsMessengerOSXIntegration(); + virtual nsresult Init(); + + NS_DECL_ISUPPORTS + NS_DECL_NSIMESSENGEROSINTEGRATION + NS_DECL_NSIFOLDERLISTENER + NS_DECL_NSIOBSERVER + NS_DECL_MOZINEWMAILLISTENER + +private: + virtual ~nsMessengerOSXIntegration(); + + nsCOMPtr<nsIAtom> mBiffStateAtom; + nsCOMPtr<nsIAtom> mNewMailReceivedAtom; + nsresult ShowAlertMessage(const nsAString& aAlertTitle, const nsAString& aAlertText, const nsACString& aFolderURI); + nsresult OnAlertFinished(); + nsresult OnAlertClicked(const char16_t * aAlertCookie); +#ifdef MOZ_SUITE + nsresult OnAlertClickedSimple(); +#endif + nsresult GetStringBundle(nsIStringBundle **aBundle); + void FillToolTipInfo(nsIMsgFolder *aFolder, int32_t aNewCount); + nsresult GetFirstFolderWithNewMail(nsIMsgFolder* aFolder, nsCString& aFolderURI); + nsresult BadgeDockIcon(); + nsresult RestoreDockIcon(); + nsresult BounceDockIcon(); + nsresult GetNewMailAuthors(nsIMsgFolder* aFolder, nsString& aAuthors, int32_t aNewCount, int32_t* aNotDisplayed); + + int32_t mUnreadTotal; + int32_t mUnreadChat; +}; + +#endif // __nsMessengerOSXIntegration_h diff --git a/mailnews/base/src/nsMessengerOSXIntegration.mm b/mailnews/base/src/nsMessengerOSXIntegration.mm new file mode 100644 index 0000000000..a38716e179 --- /dev/null +++ b/mailnews/base/src/nsMessengerOSXIntegration.mm @@ -0,0 +1,700 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nscore.h" +#include "nsMsgUtils.h" +#include "nsArrayUtils.h" +#include "nsMessengerOSXIntegration.h" +#include "nsIMsgMailSession.h" +#include "nsIMsgIncomingServer.h" +#include "nsIMsgIdentity.h" +#include "nsIMsgAccount.h" +#include "nsIMsgFolder.h" +#include "nsCOMPtr.h" +#include "nsMsgBaseCID.h" +#include "nsMsgFolderFlags.h" +#include "nsDirectoryServiceDefs.h" +#include "nsIDirectoryService.h" +#include "MailNewsTypes.h" +#include "nsIWindowMediator.h" +#include "nsIDOMChromeWindow.h" +#include "mozIDOMWindow.h" +#include "nsPIDOMWindow.h" +#include "nsIDocShell.h" +#include "nsIBaseWindow.h" +#include "nsIWidget.h" +#include "nsIObserverService.h" +#include "nsIPrefService.h" +#include "nsIPrefBranch.h" +#include "nsIMessengerWindowService.h" +#include "prprf.h" +#include "nsIAlertsService.h" +#include "nsIStringBundle.h" +#include "nsToolkitCompsCID.h" +#include "nsIMsgDatabase.h" +#include "nsIMsgHdr.h" +#include "nsISupportsPrimitives.h" +#include "nsIWindowWatcher.h" +#include "nsMsgLocalCID.h" +#include "nsIMsgMailNewsUrl.h" +#include "nsIMsgWindow.h" +#include "nsIMsgAccountManager.h" +#include "nsIMessenger.h" +#include "nsObjCExceptions.h" +#include "nsComponentManagerUtils.h" +#include "nsServiceManagerUtils.h" +#include "mozINewMailNotificationService.h" +#include "mozilla/mailnews/MimeHeaderParser.h" + +#include <Carbon/Carbon.h> +#import <Cocoa/Cocoa.h> + +#define kBiffAnimateDockIconPref "mail.biff.animate_dock_icon" +#define kMaxDisplayCount 10 + +using namespace mozilla::mailnews; + +// HACK: Limitations in Focus/SetFocus on Mac (see bug 465446) +nsresult FocusAppNative() +{ + ProcessSerialNumber psn; + + if (::GetCurrentProcess(&psn) != 0) + return NS_ERROR_FAILURE; + + if (::SetFrontProcess(&psn) != 0) + return NS_ERROR_FAILURE; + + return NS_OK; +} + +static void openMailWindow(const nsCString& aUri) +{ + nsresult rv; + nsCOMPtr<nsIMsgMailSession> mailSession ( do_GetService(NS_MSGMAILSESSION_CONTRACTID, &rv)); + if (NS_FAILED(rv)) + return; + + nsCOMPtr<nsIMsgWindow> topMostMsgWindow; + rv = mailSession->GetTopmostMsgWindow(getter_AddRefs(topMostMsgWindow)); + if (topMostMsgWindow) + { + if (!aUri.IsEmpty()) + { + nsCOMPtr<nsIMsgMailNewsUrl> msgUri(do_CreateInstance(NS_MAILBOXURL_CONTRACTID, &rv)); + if (NS_FAILED(rv)) + return; + + rv = msgUri->SetSpec(aUri); + if (NS_FAILED(rv)) + return; + + bool isMessageUri = false; + msgUri->GetIsMessageUri(&isMessageUri); + if (isMessageUri) + { + nsCOMPtr<nsIWindowWatcher> wwatch(do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv)); + if (NS_FAILED(rv)) + return; + + // SeaMonkey only supports message uris, whereas Thunderbird only + // supports message headers. This should be simplified/removed when + // bug 507593 is implemented. +#ifdef MOZ_SUITE + nsCOMPtr<mozIDOMWindowProxy> newWindow; + wwatch->OpenWindow(0, "chrome://messenger/content/messageWindow.xul", + "_blank", "all,chrome,dialog=no,status,toolbar", msgUri, + getter_AddRefs(newWindow)); +#else + nsCOMPtr<nsIMessenger> messenger(do_CreateInstance(NS_MESSENGER_CONTRACTID, &rv)); + if (NS_FAILED(rv)) + return; + + nsCOMPtr<nsIMsgDBHdr> msgHdr; + messenger->MsgHdrFromURI(aUri, getter_AddRefs(msgHdr)); + if (msgHdr) + { + nsCOMPtr<mozIDOMWindowProxy> newWindow; + wwatch->OpenWindow(0, "chrome://messenger/content/messageWindow.xul", + "_blank", "all,chrome,dialog=no,status,toolbar", msgHdr, + getter_AddRefs(newWindow)); + } +#endif + } + else + { + nsCOMPtr<nsIMsgWindowCommands> windowCommands; + topMostMsgWindow->GetWindowCommands(getter_AddRefs(windowCommands)); + if (windowCommands) + windowCommands->SelectFolder(aUri); + } + } + + FocusAppNative(); + nsCOMPtr<mozIDOMWindowProxy> domWindow; + topMostMsgWindow->GetDomWindow(getter_AddRefs(domWindow)); + if (domWindow) { + nsCOMPtr<nsPIDOMWindowOuter> privateWindow = nsPIDOMWindowOuter::From(domWindow); + privateWindow->Focus(); + } + } + else + { + // the user doesn't have a mail window open already so open one for them... + nsCOMPtr<nsIMessengerWindowService> messengerWindowService = + do_GetService(NS_MESSENGERWINDOWSERVICE_CONTRACTID); + // if we want to preselect the first account with new mail, + // here is where we would try to generate a uri to pass in + // (and add code to the messenger window service to make that work) + if (messengerWindowService) + messengerWindowService->OpenMessengerWindowWithUri( + "mail:3pane", aUri.get(), nsMsgKey_None); + } +} + +nsMessengerOSXIntegration::nsMessengerOSXIntegration() +{ + mBiffStateAtom = MsgGetAtom("BiffState"); + mNewMailReceivedAtom = MsgGetAtom("NewMailReceived"); + mUnreadTotal = 0; +} + +nsMessengerOSXIntegration::~nsMessengerOSXIntegration() +{ + RestoreDockIcon(); +} + +NS_IMPL_ADDREF(nsMessengerOSXIntegration) +NS_IMPL_RELEASE(nsMessengerOSXIntegration) + +NS_INTERFACE_MAP_BEGIN(nsMessengerOSXIntegration) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIMessengerOSIntegration) + NS_INTERFACE_MAP_ENTRY(nsIMessengerOSIntegration) + NS_INTERFACE_MAP_ENTRY(nsIFolderListener) + NS_INTERFACE_MAP_ENTRY(nsIObserver) + NS_INTERFACE_MAP_ENTRY(mozINewMailListener) +NS_INTERFACE_MAP_END + + +nsresult +nsMessengerOSXIntegration::Init() +{ + nsresult rv; + nsCOMPtr<nsIObserverService> observerService = do_GetService("@mozilla.org/observer-service;1", &rv); + NS_ENSURE_SUCCESS(rv, rv); + return observerService->AddObserver(this, "mail-startup-done", false); +} + +NS_IMETHODIMP +nsMessengerOSXIntegration::OnItemPropertyChanged(nsIMsgFolder *, nsIAtom *, char const *, char const *) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsMessengerOSXIntegration::OnItemUnicharPropertyChanged(nsIMsgFolder *, nsIAtom *, const char16_t *, const char16_t *) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsMessengerOSXIntegration::OnItemRemoved(nsIMsgFolder *, nsISupports *) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsMessengerOSXIntegration::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aData) +{ + if (!strcmp(aTopic, "alertfinished")) + return OnAlertFinished(); + + if (!strcmp(aTopic, "alertclickcallback")) + return OnAlertClicked(aData); + +#ifdef MOZ_SUITE + // SeaMonkey does most of the GUI work in JS code when clicking on a mail + // notification, so it needs an extra function here + if (!strcmp(aTopic, "alertclicksimplecallback")) + return OnAlertClickedSimple(); +#endif + + if (!strcmp(aTopic, "mail-startup-done")) { + nsresult rv; + nsCOMPtr<nsIObserverService> observerService = do_GetService("@mozilla.org/observer-service;1", &rv); + if (NS_SUCCEEDED(rv)) { + observerService->RemoveObserver(this, "mail-startup-done"); + + nsCOMPtr<nsIPrefBranch> pref(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv)); + } + + // Register with the new mail service for changes to the unread message count + nsCOMPtr<mozINewMailNotificationService> newmail + = do_GetService(MOZ_NEWMAILNOTIFICATIONSERVICE_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv, rv); // This should really be an assert with a helpful message + rv = newmail->AddListener(this, mozINewMailNotificationService::count); + NS_ENSURE_SUCCESS(rv, rv); // This should really be an assert with a helpful message + + // Get the initial unread count. Ignore return value; if code above didn't fail, this won't + rv = newmail->GetMessageCount(&mUnreadTotal); + BadgeDockIcon(); + + // register with the mail sesson for folder events + // we care about new count, biff status + nsCOMPtr<nsIMsgMailSession> mailSession = do_GetService(NS_MSGMAILSESSION_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + return mailSession->AddFolderListener(this, nsIFolderListener::boolPropertyChanged | nsIFolderListener::intPropertyChanged); + } + + return NS_OK; +} + +nsresult +nsMessengerOSXIntegration::GetStringBundle(nsIStringBundle **aBundle) +{ + NS_ENSURE_ARG_POINTER(aBundle); + nsresult rv; + nsCOMPtr<nsIStringBundleService> bundleService = do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv); + nsCOMPtr<nsIStringBundle> bundle; + if (bundleService && NS_SUCCEEDED(rv)) + bundleService->CreateBundle("chrome://messenger/locale/messenger.properties", getter_AddRefs(bundle)); + bundle.swap(*aBundle); + return rv; +} + +void +nsMessengerOSXIntegration::FillToolTipInfo(nsIMsgFolder *aFolder, int32_t aNewCount) +{ + if (aFolder) + { + nsString authors; + int32_t numNotDisplayed; + nsresult rv = GetNewMailAuthors(aFolder, authors, aNewCount, &numNotDisplayed); + + // If all senders are vetoed, the authors string will be empty. + if (NS_FAILED(rv) || authors.IsEmpty()) + return; + + // If this isn't the root folder, get it so we can report for it. + // GetRootFolder always returns the server's root, so calling on the root itself is fine. + nsCOMPtr<nsIMsgFolder> rootFolder; + aFolder->GetRootFolder(getter_AddRefs(rootFolder)); + if (!rootFolder) + return; + + nsString accountName; + rootFolder->GetPrettiestName(accountName); + + nsCOMPtr<nsIStringBundle> bundle; + GetStringBundle(getter_AddRefs(bundle)); + if (bundle) + { + nsAutoString numNewMsgsText; + numNewMsgsText.AppendInt(aNewCount); + nsString finalText; + nsCString uri; + aFolder->GetURI(uri); + + if (numNotDisplayed > 0) + { + nsAutoString numNotDisplayedText; + numNotDisplayedText.AppendInt(numNotDisplayed); + const char16_t *formatStrings[3] = { numNewMsgsText.get(), authors.get(), numNotDisplayedText.get() }; + bundle->FormatStringFromName(u"macBiffNotification_messages_extra", + formatStrings, + 3, + getter_Copies(finalText)); + } + else + { + const char16_t *formatStrings[2] = { numNewMsgsText.get(), authors.get() }; + + if (aNewCount == 1) + { + bundle->FormatStringFromName(u"macBiffNotification_message", + formatStrings, + 2, + getter_Copies(finalText)); + // Since there is only 1 message, use the most recent mail's URI instead of the folder's + nsCOMPtr<nsIMsgDatabase> db; + rv = aFolder->GetMsgDatabase(getter_AddRefs(db)); + if (NS_SUCCEEDED(rv) && db) + { + uint32_t numNewKeys; + uint32_t *newMessageKeys; + rv = db->GetNewList(&numNewKeys, &newMessageKeys); + if (NS_SUCCEEDED(rv)) + { + nsCOMPtr<nsIMsgDBHdr> hdr; + rv = db->GetMsgHdrForKey(newMessageKeys[numNewKeys - 1], + getter_AddRefs(hdr)); + if (NS_SUCCEEDED(rv) && hdr) + aFolder->GetUriForMsg(hdr, uri); + } + NS_Free(newMessageKeys); + } + } + else + bundle->FormatStringFromName(u"macBiffNotification_messages", + formatStrings, + 2, + getter_Copies(finalText)); + } + ShowAlertMessage(accountName, finalText, uri); + } // if we got a bundle + } // if we got a folder +} + +nsresult +nsMessengerOSXIntegration::ShowAlertMessage(const nsAString& aAlertTitle, + const nsAString& aAlertText, + const nsACString& aFolderURI) +{ + nsresult rv; + + nsCOMPtr<nsIAlertsService> alertsService (do_GetService(NS_ALERTSERVICE_CONTRACTID, &rv)); + // If we have an nsIAlertsService implementation, use it: + if (NS_SUCCEEDED(rv)) + { + alertsService->ShowAlertNotification(EmptyString(), + aAlertTitle, aAlertText, true, + NS_ConvertASCIItoUTF16(aFolderURI), + this, EmptyString(), + NS_LITERAL_STRING("auto"), + EmptyString(), EmptyString(), + nullptr, + false, + false); + } + + BounceDockIcon(); + + if (NS_FAILED(rv)) + OnAlertFinished(); + + return rv; +} + +NS_IMETHODIMP +nsMessengerOSXIntegration::OnItemIntPropertyChanged(nsIMsgFolder *aFolder, + nsIAtom *aProperty, + int64_t aOldValue, + int64_t aNewValue) +{ + // if we got new mail show an alert + if (aNewValue == nsIMsgFolder::nsMsgBiffState_NewMail) + { + bool performingBiff = false; + nsCOMPtr<nsIMsgIncomingServer> server; + aFolder->GetServer(getter_AddRefs(server)); + if (server) + server->GetPerformingBiff(&performingBiff); + if (!performingBiff) + return NS_OK; // kick out right now... + + // Biff happens for the root folder, but we want info for the child with new mail + nsCString folderUri; + GetFirstFolderWithNewMail(aFolder, folderUri); + nsCOMPtr<nsIMsgFolder> childFolder; + nsresult rv = aFolder->GetChildWithURI(folderUri, true, true, + getter_AddRefs(childFolder)); + if (NS_FAILED(rv) || !childFolder) + return NS_ERROR_FAILURE; + + int32_t numNewMessages = 0; + childFolder->GetNumNewMessages(true, &numNewMessages); + FillToolTipInfo(childFolder, numNewMessages); + } + else if (mNewMailReceivedAtom == aProperty) + { + FillToolTipInfo(aFolder, aNewValue); + } + return NS_OK; +} + +nsresult +nsMessengerOSXIntegration::OnAlertClicked(const char16_t* aAlertCookie) +{ + openMailWindow(NS_ConvertUTF16toUTF8(aAlertCookie)); + return NS_OK; +} + +#ifdef MOZ_SUITE +nsresult +nsMessengerOSXIntegration::OnAlertClickedSimple() +{ + // SeaMonkey only function; only focus the app here, rest of the work will + // be done in suite/mailnews/mailWidgets.xml + FocusAppNative(); + return NS_OK; +} +#endif + +nsresult +nsMessengerOSXIntegration::OnAlertFinished() +{ + return NS_OK; +} + +nsresult +nsMessengerOSXIntegration::BounceDockIcon() +{ + nsresult rv; + nsCOMPtr<nsIPrefBranch> prefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv)); + NS_ENSURE_SUCCESS(rv, rv); + + bool bounceDockIcon = false; + rv = prefBranch->GetBoolPref(kBiffAnimateDockIconPref, &bounceDockIcon); + NS_ENSURE_SUCCESS(rv, rv); + + if (!bounceDockIcon) + return NS_OK; + + nsCOMPtr<nsIWindowMediator> mediator(do_GetService(NS_WINDOWMEDIATOR_CONTRACTID)); + if (mediator) + { + nsCOMPtr<mozIDOMWindowProxy> domWindow; + mediator->GetMostRecentWindow(u"mail:3pane", getter_AddRefs(domWindow)); + if (domWindow) + { + nsCOMPtr<nsIDOMChromeWindow> chromeWindow(do_QueryInterface(domWindow)); + chromeWindow->GetAttention(); + } + } + return NS_OK; +} + +nsresult +nsMessengerOSXIntegration::RestoreDockIcon() +{ + NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; + + id tile = [[NSApplication sharedApplication] dockTile]; + [tile setBadgeLabel: nil]; + + return NS_OK; + + NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; +} + +nsresult +nsMessengerOSXIntegration::BadgeDockIcon() +{ + NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; + + int32_t unreadCount = mUnreadTotal; + // If count is less than one, we should restore the original dock icon. + if (unreadCount < 1) + { + RestoreDockIcon(); + return NS_OK; + } + + // Draw the number, first giving extensions a chance to modify. + // Extensions might wish to transform "1000" into "100+" or some + // other short string. Getting back the empty string will cause + // nothing to be drawn and us to return early. + nsresult rv; + nsCOMPtr<nsIObserverService> os + (do_GetService("@mozilla.org/observer-service;1", &rv)); + if (NS_FAILED(rv)) + { + RestoreDockIcon(); + return rv; + } + + nsCOMPtr<nsISupportsString> str + (do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv)); + if (NS_FAILED(rv)) + { + RestoreDockIcon(); + return rv; + } + + nsAutoString total; + total.AppendInt(unreadCount); + str->SetData(total); + os->NotifyObservers(str, "before-unread-count-display", + total.get()); + nsAutoString badgeString; + str->GetData(badgeString); + if (badgeString.IsEmpty()) + { + RestoreDockIcon(); + return NS_OK; + } + + id tile = [[NSApplication sharedApplication] dockTile]; + [tile setBadgeLabel:[NSString stringWithFormat:@"%S", (const unichar*)badgeString.get()]]; + return NS_OK; + + NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; +} + +NS_IMETHODIMP +nsMessengerOSXIntegration::OnItemPropertyFlagChanged(nsIMsgDBHdr *item, nsIAtom *property, uint32_t oldFlag, uint32_t newFlag) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsMessengerOSXIntegration::OnItemAdded(nsIMsgFolder *, nsISupports *) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsMessengerOSXIntegration::OnItemBoolPropertyChanged(nsIMsgFolder *aItem, + nsIAtom *aProperty, + bool aOldValue, + bool aNewValue) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsMessengerOSXIntegration::OnItemEvent(nsIMsgFolder *, nsIAtom *) +{ + return NS_OK; +} + +nsresult +nsMessengerOSXIntegration::GetNewMailAuthors(nsIMsgFolder* aFolder, + nsString& aAuthors, + int32_t aNewCount, + int32_t* aNotDisplayed) +{ + // Get a list of names or email addresses for the folder's authors + // with new mail. Note that we only process the most recent "new" + // mail (aNewCount), working from most recently added. Duplicates + // are removed, and names are displayed to a set limit + // (kMaxDisplayCount) with the remaining count being returned in + // aNotDisplayed. Extension developers can listen for + // "newmail-notification-requested" and then make a decision about + // including a given author or not. As a result, it is possible that + // the resulting length of aAuthors will be 0. + nsCOMPtr<nsIMsgDatabase> db; + nsresult rv = aFolder->GetMsgDatabase(getter_AddRefs(db)); + uint32_t numNewKeys = 0; + if (NS_SUCCEEDED(rv) && db) + { + nsCOMPtr<nsIObserverService> os = + do_GetService("@mozilla.org/observer-service;1", &rv); + NS_ENSURE_SUCCESS(rv, rv); + + // Get proper l10n list separator -- ", " in English + nsCOMPtr<nsIStringBundle> bundle; + GetStringBundle(getter_AddRefs(bundle)); + if (!bundle) + return NS_ERROR_FAILURE; + + uint32_t *newMessageKeys; + rv = db->GetNewList(&numNewKeys, &newMessageKeys); + if (NS_SUCCEEDED(rv)) + { + nsString listSeparator; + bundle->GetStringFromName(u"macBiffNotification_separator", getter_Copies(listSeparator)); + + int32_t displayed = 0; + for (int32_t i = numNewKeys - 1; i >= 0; i--, aNewCount--) + { + if (0 == aNewCount || displayed == kMaxDisplayCount) + break; + + nsCOMPtr<nsIMsgDBHdr> hdr; + rv = db->GetMsgHdrForKey(newMessageKeys[i], + getter_AddRefs(hdr)); + if (NS_SUCCEEDED(rv) && hdr) + { + nsString author; + rv = hdr->GetMime2DecodedAuthor(author); + if (NS_FAILED(rv)) + continue; + + nsString name; + ExtractName(DecodedHeader(author), name); + + // Give extensions a chance to suppress notifications for this author + nsCOMPtr<nsISupportsPRBool> notify = + do_CreateInstance(NS_SUPPORTS_PRBOOL_CONTRACTID); + + notify->SetData(true); + os->NotifyObservers(notify, "newmail-notification-requested", + author.get()); + + bool includeSender; + notify->GetData(&includeSender); + + // Don't add unwanted or duplicate names + if (includeSender && aAuthors.Find(name, true) == -1) + { + if (displayed > 0) + aAuthors.Append(listSeparator); + aAuthors.Append(name); + displayed++; + } + } + } + } + NS_Free(newMessageKeys); + } + *aNotDisplayed = aNewCount; + return rv; +} + +nsresult +nsMessengerOSXIntegration::GetFirstFolderWithNewMail(nsIMsgFolder* aFolder, nsCString& aFolderURI) +{ + // Find the subfolder in aFolder with new mail and return the folderURI + if (aFolder) + { + nsCOMPtr<nsIMsgFolder> msgFolder; + // enumerate over the folders under this root folder till we find one with new mail.... + nsCOMPtr<nsIArray> allFolders; + nsresult rv = aFolder->GetDescendants(getter_AddRefs(allFolders)); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr<nsISimpleEnumerator> enumerator; + rv = allFolders->Enumerate(getter_AddRefs(enumerator)); + if (NS_SUCCEEDED(rv) && enumerator) + { + nsCOMPtr<nsISupports> supports; + int32_t numNewMessages = 0; + bool hasMore = false; + while (NS_SUCCEEDED(enumerator->HasMoreElements(&hasMore)) && hasMore) + { + rv = enumerator->GetNext(getter_AddRefs(supports)); + if (NS_SUCCEEDED(rv) && supports) + { + msgFolder = do_QueryInterface(supports, &rv); + if (msgFolder) + { + numNewMessages = 0; + msgFolder->GetNumNewMessages(false, &numNewMessages); + if (numNewMessages) + break; // kick out of the while loop + } + } // if we have a folder + } // if we have more potential folders to enumerate + } // if enumerator + + if (msgFolder) + msgFolder->GetURI(aFolderURI); + } + + return NS_OK; +} + +/* + * Method implementations for mozINewMailListener + */ +NS_IMETHODIMP +nsMessengerOSXIntegration::OnCountChanged(uint32_t count) +{ + mUnreadTotal = count; + BadgeDockIcon(); + return NS_OK; +} diff --git a/mailnews/build/moz.build b/mailnews/build/moz.build index 9561fd33d8..9620e8f3d4 100644 --- a/mailnews/build/moz.build +++ b/mailnews/build/moz.build @@ -35,6 +35,10 @@ if CONFIG['OS_ARCH'] == 'WINNT': else: OS_LIBS += CONFIG['MOZ_ZLIB_LIBS'] +if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa': + OS_LIBS += CONFIG['TK_LIBS'] + OS_LIBS += ['-framework Cocoa'] + LOCAL_INCLUDES += [ '/mailnews/addrbook/src', '/mailnews/base/search/src', diff --git a/mailnews/compose/src/moz.build b/mailnews/compose/src/moz.build index 831a0340f4..dcb9960a66 100644 --- a/mailnews/compose/src/moz.build +++ b/mailnews/compose/src/moz.build @@ -35,6 +35,16 @@ SOURCES += [ 'nsURLFetcher.cpp', ] +if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa': + SOURCES += [ + 'nsMsgAppleDoubleEncode.cpp', + 'nsMsgAppleEncode.cpp', + ] + EXPORTS += [ + 'nsMsgAppleCodes.h', + 'nsMsgAppleDouble.h', + ] + EXTRA_COMPONENTS += [ 'nsSMTPProtocolHandler.js', 'nsSMTPProtocolHandler.manifest', diff --git a/mailnews/compose/src/nsMsgAppleCodes.h b/mailnews/compose/src/nsMsgAppleCodes.h new file mode 100644 index 0000000000..d8ca2f327c --- /dev/null +++ b/mailnews/compose/src/nsMsgAppleCodes.h @@ -0,0 +1,106 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* +** AD_Codes.h +** +** --------------- +** +** Head file for Apple Decode/Encode essential codes. +** +** +*/ + +#ifndef ad_codes_h +#define ad_codes_h + +/* +** applefile definitions used +*/ +#if PRAGMA_STRUCT_ALIGN + #pragma options align=mac68k +#endif + +#define APPLESINGLE_MAGIC 0x00051600L +#define APPLEDOUBLE_MAGIC 0x00051607L +#define VERSION 0x00020000 + +#define NUM_ENTRIES 6 + +#define ENT_DFORK 1L +#define ENT_RFORK 2L +#define ENT_NAME 3L +#define ENT_COMMENT 4L +#define ENT_DATES 8L +#define ENT_FINFO 9L +#define CONVERT_TIME 1265437696L + +/* +** data type used in the encoder/decoder. +*/ +typedef struct ap_header +{ + int32_t magic; + int32_t version; + char fill[16]; + int16_t entries; + +} ap_header; + +typedef struct ap_entry +{ + int32_t id; + int32_t offset; + int32_t length; + +} ap_entry; + +typedef struct ap_dates +{ + int32_t create, modify, backup, access; + +} ap_dates; + +typedef struct myFInfo /* the mac FInfo structure for the cross platform. */ +{ + int32_t fdType, fdCreator; + int16_t fdFlags; + int32_t fdLocation; /* it really should be a pointer, but just a place-holder */ + int16_t fdFldr; + +} myFInfo; + +PR_BEGIN_EXTERN_C +/* +** string utils. +*/ +int write_stream(appledouble_encode_object *p_ap_encode_obj, const char *s,int len); + +int fill_apple_mime_header(appledouble_encode_object *p_ap_encode_obj); +int ap_encode_file_infor(appledouble_encode_object *p_ap_encode_obj); +int ap_encode_header(appledouble_encode_object* p_ap_encode_obj, bool firstTime); +int ap_encode_data( appledouble_encode_object* p_ap_encode_obj, bool firstTime); + +/* +** the prototypes for the ap_decoder. +*/ +int fetch_a_line(appledouble_decode_object* p_ap_decode_obj, char *buff); +int ParseFileHeader(appledouble_decode_object* p_ap_decode_obj); +int ap_seek_part_start(appledouble_decode_object* p_ap_decode_obj); +void parse_param(char *p, char **param, char**define, char **np); +int ap_seek_to_boundary(appledouble_decode_object* p_ap_decode_obj, bool firstime); +int ap_parse_header(appledouble_decode_object* p_ap_decode_obj,bool firstime); +int ap_decode_file_infor(appledouble_decode_object* p_ap_decode_obj); +int ap_decode_process_header(appledouble_decode_object* p_ap_decode_obj, bool firstime); +int ap_decode_process_data( appledouble_decode_object* p_ap_decode_obj, bool firstime); + +PR_END_EXTERN_C + +#if PRAGMA_STRUCT_ALIGN + #pragma options align=reset +#endif + +#endif /* ad_codes_h */ diff --git a/mailnews/compose/src/nsMsgAppleDouble.h b/mailnews/compose/src/nsMsgAppleDouble.h new file mode 100644 index 0000000000..f4ae934add --- /dev/null +++ b/mailnews/compose/src/nsMsgAppleDouble.h @@ -0,0 +1,207 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* +* AppleDouble.h +* ------------- +* +* The header file for a stream based apple single/double encodor/decodor. +* +* 2aug95 mym +* +*/ + + +#ifndef AppleDouble_h +#define AppleDouble_h + +#include "msgCore.h" +#include "nsComposeStrings.h" +#include "nsIOutputStream.h" +#include "nsCOMPtr.h" + +#include <CoreServices/CoreServices.h> + +#define NOERR 0 +#define errDone 1 + /* Done with current operation. */ +#define errEOB 2 + /* End of a buffer. */ +#define errEOP 3 + /* End of a Part. */ + + +#define errFileOpen NS_ERROR_GET_CODE(NS_MSG_UNABLE_TO_OPEN_TMP_FILE) +#define errFileWrite -202 /*Error writing temporary file.*/ +#define errUsrCancel -2 /*MK_INTERRUPTED */ +#define errDecoding -1 + +/* +** The envirment block data type. +*/ +enum +{ + kInit, + kDoingHeaderPortion, + kDoneHeaderPortion, + kDoingDataPortion, + kDoneDataPortion +}; + +typedef struct _appledouble_encode_object +{ + char fname[256]; + FSIORefNum fileId; /* the id for the open file (data/resource fork) */ + + int state; + int text_file_type; /* if the file has a text file type with it. */ + char *boundary; /* the boundary string. */ + + int status; /* the error code if anyerror happens. */ + char b_overflow[200]; + int s_overflow; + + int state64; /* the left over state of base64 enocding */ + int ct; /* the character count of base64 encoding */ + int c1, c2; /* the left of the last base64 encoding */ + + char *outbuff; /* the outbuff by the caller. */ + int s_outbuff; /* the size of the buffer. */ + int pos_outbuff; /* the offset in the current buffer. */ + +} appledouble_encode_object; + +/* The possible content transfer encodings */ + +enum +{ + kEncodeNone, + kEncodeQP, + kEncodeBase64, + kEncodeUU +}; + +enum +{ + kGeneralMine, + kAppleDouble, + kAppleSingle +}; + +enum +{ + kInline, + kDontCare +}; + +enum +{ + kHeaderPortion, + kDataPortion +}; + +/* the decode states. */ +enum +{ + kBeginParseHeader = 3, + kParsingHeader, + kBeginSeekBoundary, + kSeekingBoundary, + kBeginHeaderPortion, + kProcessingHeaderPortion, + kBeginDataPortion, + kProcessingDataPortion, + kFinishing +}; + +/* uuencode states */ +enum +{ + kWaitingForBegin = (int) 0, + kBegin, + kMainBody, + kEnd +}; + +typedef struct _appledouble_decode_object +{ + int is_binary; + int is_apple_single; /* if the object encoded is in apple single */ + int write_as_binhex; + + int messagetype; + char* boundary0; /* the boundary for the enclosure. */ + int deposition; /* the deposition. */ + int encoding; /* the encoding method. */ + int which_part; + + char fname[256]; + // nsIOFileStream *fileSpec; /* the stream for data fork work. */ + + int state; + + int rksize; /* the resource fork size count. */ + int dksize; /* the data fork size count. */ + + int status; /* the error code if anyerror happens. */ + char b_leftover[256]; + int s_leftover; + + int encode; /* the encode type of the message. */ + int state64; /* the left over state of base64 enocding */ + int left; /* the character count of base64 encoding */ + int c[4]; /* the left of the last base64 encoding */ + int uu_starts_line; /* is decoder at the start of a line? (uuencode) */ + int uu_state; /* state w/r/t the uuencode body */ + int uu_bytes_written; /* bytes written from the current tuple (uuencode) */ + int uu_line_bytes; /* encoded bytes remaining in the current line (uuencode) */ + + char *inbuff; /* the outbuff by the caller. */ + int s_inbuff; /* the size of the buffer. */ + int pos_inbuff; /* the offset in the current buffer. */ + + + nsCOMPtr <nsIFile> tmpFile; /* the temp file to hold the decode data fork */ + /* when doing the binhex exporting. */ + nsCOMPtr <nsIOutputStream> tmpFileStream; /* The output File Stream */ + int32_t data_size; /* the size of the data in the tmp file. */ + +} appledouble_decode_object; + + +/* +** The protypes. +*/ + +PR_BEGIN_EXTERN_C + +int ap_encode_init(appledouble_encode_object *p_ap_encode_obj, + const char* fname, + char* separator); + +int ap_encode_next(appledouble_encode_object* p_ap_encode_obj, + char *to_buff, + int32_t buff_size, + int32_t* real_size); + +int ap_encode_end(appledouble_encode_object* p_ap_encode_obj, + bool is_aborting); + +int ap_decode_init(appledouble_decode_object* p_ap_decode_obj, + bool is_apple_single, + bool write_as_bin_hex, + void *closure); + +int ap_decode_next(appledouble_decode_object* p_ap_decode_obj, + char *in_buff, + int32_t buff_size); + +int ap_decode_end(appledouble_decode_object* p_ap_decode_obj, + bool is_aborting); + +PR_END_EXTERN_C + +#endif diff --git a/mailnews/compose/src/nsMsgAppleDoubleEncode.cpp b/mailnews/compose/src/nsMsgAppleDoubleEncode.cpp new file mode 100644 index 0000000000..4d71781233 --- /dev/null +++ b/mailnews/compose/src/nsMsgAppleDoubleEncode.cpp @@ -0,0 +1,266 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* +* +* apple-double.c +* -------------- +* +* The codes to do apple double encoding/decoding. +* +* 02aug95 mym created. +* +*/ +#include "nsID.h" +#include "nscore.h" +#include "nsStringGlue.h" +#include "nsMsgAppleDouble.h" +#include "nsMsgAppleCodes.h" +#include "nsMsgCompUtils.h" +#include "nsCExternalHandlerService.h" +#include "nsIMIMEService.h" +#include "nsMimeTypes.h" +#include "prmem.h" +#include "nsNetUtil.h" + + +void +MacGetFileType(nsIFile *fs, + bool *useDefault, + char **fileType, + char **encoding) +{ + if ((fs == NULL) || (fileType == NULL) || (encoding == NULL)) + return; + + bool exists = false; + fs->Exists(&exists); + if (!exists) + return; + + *useDefault = TRUE; + *fileType = NULL; + *encoding = NULL; + + nsCOMPtr<nsILocalFileMac> macFile = do_QueryInterface(fs); + FSRef fsRef; + FSCatalogInfo catalogInfo; + OSErr err = errFileOpen; + if (NS_SUCCEEDED(macFile->GetFSRef(&fsRef))) + err = ::FSGetCatalogInfo(&fsRef, kFSCatInfoFinderInfo, &catalogInfo, nullptr, nullptr, nullptr); + + if ( (err != noErr) || (((FileInfo*)(&catalogInfo.finderInfo))->fileType == 'TEXT') ) + *fileType = strdup(APPLICATION_OCTET_STREAM); + else + { + // At this point, we should call the mime service and + // see what we can find out? + nsresult rv; + nsCOMPtr <nsIURI> tURI; + if (NS_SUCCEEDED(NS_NewFileURI(getter_AddRefs(tURI), fs)) && tURI) + { + nsCOMPtr<nsIMIMEService> mimeFinder (do_GetService(NS_MIMESERVICE_CONTRACTID, &rv)); + if (NS_SUCCEEDED(rv) && mimeFinder) + { + nsAutoCString mimeType; + rv = mimeFinder->GetTypeFromURI(tURI, mimeType); + if (NS_SUCCEEDED(rv)) + { + *fileType = ToNewCString(mimeType); + return; + } + } + } + + // If we hit here, return something...default to this... + *fileType = strdup(APPLICATION_OCTET_STREAM); + } +} + +//#pragma cplusplus reset + +/* +* ap_encode_init +* -------------- +* +* Setup the encode envirment +*/ + +int ap_encode_init( appledouble_encode_object *p_ap_encode_obj, + const char *fname, + char *separator) +{ + nsCOMPtr <nsIFile> myFile; + NS_NewNativeLocalFile(nsDependentCString(fname), true, getter_AddRefs(myFile)); + bool exists; + if (myFile && NS_SUCCEEDED(myFile->Exists(&exists)) && !exists) + return -1; + + nsCOMPtr<nsILocalFileMac> macFile = do_QueryInterface(myFile); + nsAutoCString path; + macFile->GetNativePath(path); + + memset(p_ap_encode_obj, 0, sizeof(appledouble_encode_object)); + + /* + ** Fill out the source file inforamtion. + */ + memcpy(p_ap_encode_obj->fname, path.get(), path.Length()); + p_ap_encode_obj->fname[path.Length()] = '\0'; + + p_ap_encode_obj->boundary = strdup(separator); + return noErr; +} + +/* +** ap_encode_next +** -------------- +** +** return : +** noErr : everything is ok +** errDone : when encoding is done. +** errors : otherwise. +*/ +int ap_encode_next( + appledouble_encode_object* p_ap_encode_obj, + char *to_buff, + int32_t buff_size, + int32_t* real_size) +{ + int status; + + /* + ** install the out buff now. + */ + p_ap_encode_obj->outbuff = to_buff; + p_ap_encode_obj->s_outbuff = buff_size; + p_ap_encode_obj->pos_outbuff = 0; + + /* + ** first copy the outstandind data in the overflow buff to the out buffer. + */ + if (p_ap_encode_obj->s_overflow) + { + status = write_stream(p_ap_encode_obj, + (const char*)(p_ap_encode_obj->b_overflow), + p_ap_encode_obj->s_overflow); + if (status != noErr) + return status; + + p_ap_encode_obj->s_overflow = 0; + } + + /* + ** go the next processing stage based on the current state. + */ + switch (p_ap_encode_obj->state) + { + case kInit: + /* + ** We are in the starting position, fill out the header. + */ + status = fill_apple_mime_header(p_ap_encode_obj); + if (status != noErr) + break; /* some error happens */ + + p_ap_encode_obj->state = kDoingHeaderPortion; + status = ap_encode_header(p_ap_encode_obj, true); + /* it is the first time to calling */ + if (status == errDone) + { + p_ap_encode_obj->state = kDoneHeaderPortion; + } + else + { + break; /* we need more work on header portion. */ + } + + /* + ** we are done with the header, so let's go to the data port. + */ + p_ap_encode_obj->state = kDoingDataPortion; + status = ap_encode_data(p_ap_encode_obj, true); + /* it is first time call do data portion */ + + if (status == errDone) + { + p_ap_encode_obj->state = kDoneDataPortion; + status = noErr; + } + break; + + case kDoingHeaderPortion: + + status = ap_encode_header(p_ap_encode_obj, false); + /* continue with the header portion. */ + if (status == errDone) + { + p_ap_encode_obj->state = kDoneHeaderPortion; + } + else + { + break; /* we need more work on header portion. */ + } + + /* + ** start the data portion. + */ + p_ap_encode_obj->state = kDoingDataPortion; + status = ap_encode_data(p_ap_encode_obj, true); + /* it is the first time calling */ + if (status == errDone) + { + p_ap_encode_obj->state = kDoneDataPortion; + status = noErr; + } + break; + + case kDoingDataPortion: + + status = ap_encode_data(p_ap_encode_obj, false); + /* it is not the first time */ + + if (status == errDone) + { + p_ap_encode_obj->state = kDoneDataPortion; + status = noErr; + } + break; + + case kDoneDataPortion: + status = errDone; /* we are really done. */ + + break; + } + + *real_size = p_ap_encode_obj->pos_outbuff; + return status; +} + +/* +** ap_encode_end +** ------------- +** +** clear the apple encoding. +*/ + +int ap_encode_end( + appledouble_encode_object *p_ap_encode_obj, + bool is_aborting) +{ + /* + ** clear up the apple doubler. + */ + if (p_ap_encode_obj == NULL) + return noErr; + + if (p_ap_encode_obj->fileId) /* close the file if it is open. */ + ::FSCloseFork(p_ap_encode_obj->fileId); + + PR_FREEIF(p_ap_encode_obj->boundary); /* the boundary string. */ + + return noErr; +} diff --git a/mailnews/compose/src/nsMsgAppleEncode.cpp b/mailnews/compose/src/nsMsgAppleEncode.cpp new file mode 100644 index 0000000000..27e39a8cda --- /dev/null +++ b/mailnews/compose/src/nsMsgAppleEncode.cpp @@ -0,0 +1,703 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * + * apple_double_encode.c + * --------------------- + * + * The routines doing the Apple Double Encoding. + * + * 2aug95 mym Created. + * + */ + +#include "nscore.h" +#include "nsStringGlue.h" +#include "nsMimeTypes.h" +#include "prprf.h" +#include "nsServiceManagerUtils.h" +#include "nsMsgAppleDouble.h" +#include "nsMsgAppleCodes.h" +#include "nsILocalFileMac.h" + +/* +** Local Functions prototypes. +*/ +static int output64chunk( appledouble_encode_object* p_ap_encode_obj, + int c1, int c2, int c3, int pads); + +static int to64(appledouble_encode_object* p_ap_encode_obj, + char *p, + int in_size); + +static int finish64(appledouble_encode_object* p_ap_encode_obj); + + +#define BUFF_LEFT(p) ((p)->s_outbuff - (p)->pos_outbuff) + +/* +** write_stream. +*/ +int write_stream( + appledouble_encode_object *p_ap_encode_obj, + const char *out_string, + int len) +{ + if (p_ap_encode_obj->pos_outbuff + len < p_ap_encode_obj->s_outbuff) + { + memcpy(p_ap_encode_obj->outbuff + p_ap_encode_obj->pos_outbuff, + out_string, + len); + p_ap_encode_obj->pos_outbuff += len; + return noErr; + } + else + { + /* + ** If the buff doesn't have enough space, use the overflow buffer then. + */ + int s_len = p_ap_encode_obj->s_outbuff - p_ap_encode_obj->pos_outbuff; + + memcpy(p_ap_encode_obj->outbuff + p_ap_encode_obj->pos_outbuff, + out_string, + s_len); + memcpy(p_ap_encode_obj->b_overflow + p_ap_encode_obj->s_overflow, + out_string + s_len, + p_ap_encode_obj->s_overflow += (len - s_len)); + p_ap_encode_obj->pos_outbuff += s_len; + return errEOB; + } +} + +int fill_apple_mime_header( + appledouble_encode_object *p_ap_encode_obj) +{ + int status; + + char tmpstr[266]; + +#if 0 +// strcpy(tmpstr, "Content-Type: multipart/mixed; boundary=\"-\"\n\n---\n"); +// status = write_stream(p_ap_encode_env, +// tmpstr, +// strlen(tmpstr)); +// if (status != noErr) +// return status; + + PR_snprintf(tmpstr, sizeof(tmpstr), + "Content-Type: multipart/appledouble; boundary=\"=\"; name=\""); + status = write_stream(p_ap_encode_obj, (const char*)tmpstr, strlen(tmpstr)); + if (status != noErr) + return status; + + status = write_stream(p_ap_encode_obj, + p_ap_encode_obj->fname, + strlen(p_ap_encode_obj->fname)); + if (status != noErr) + return status; + + PR_snprintf(tmpstr, sizeof(tmpstr), + "\"\r\nContent-Disposition: inline; filename=\"%s\"\r\n\r\n\r\n--=\r\n", + p_ap_encode_obj->fname); +#endif /* 0 */ + PR_snprintf(tmpstr, sizeof(tmpstr), "--%s" CRLF, p_ap_encode_obj->boundary); + status = write_stream(p_ap_encode_obj, (const char*)tmpstr, strlen(tmpstr)); + return status; +} + +int ap_encode_file_infor( + appledouble_encode_object *p_ap_encode_obj) +{ + ap_header head; + ap_entry entries[NUM_ENTRIES]; + ap_dates dates; + short i; + long comlen; + char comment[256]; + int status; + + nsCOMPtr <nsIFile> resFile; + NS_NewNativeLocalFile(nsDependentCString(p_ap_encode_obj->fname), true, + getter_AddRefs(resFile)); + if (!resFile) + return errFileOpen; + + FSRef ref; + nsCOMPtr <nsILocalFileMac> macFile = do_QueryInterface(resFile); + if (NS_FAILED(macFile->GetFSRef(&ref))) + return errFileOpen; + + FSCatalogInfo catalogInfo; + if (::FSGetCatalogInfo(&ref, kFSCatInfoFinderInfo, &catalogInfo, nullptr, nullptr, nullptr) != noErr) + { + return errFileOpen; + } + + /* get a file comment, if possible */ +#if 1 + // Carbon doesn't support GetWDInfo(). (Bug 555684) + + // not sure why working directories are needed here... + comlen = 0; +#else + long procID; + procID = 0; + GetWDInfo(p_ap_encode_obj->vRefNum, &fpb->ioVRefNum, &fpb->ioDirID, &procID); + IOParam vinfo; + memset((void *) &vinfo, '\0', sizeof (vinfo)); + GetVolParmsInfoBuffer vp; + vinfo.ioCompletion = nil; + vinfo.ioVRefNum = fpb->ioVRefNum; + vinfo.ioBuffer = (Ptr) &vp; + vinfo.ioReqCount = sizeof (vp); + comlen = 0; + if (PBHGetVolParmsSync((HParmBlkPtr) &vinfo) == noErr && + ((vp.vMAttrib >> bHasDesktopMgr) & 1)) + { + DTPBRec dtp; + memset((void *) &dtp, '\0', sizeof (dtp)); + dtp.ioVRefNum = fpb->ioVRefNum; + if (PBDTGetPath(&dtp) == noErr) + { + dtp.ioCompletion = nil; + dtp.ioDTBuffer = (Ptr) comment; + dtp.ioNamePtr = fpb->ioNamePtr; + dtp.ioDirID = fpb->ioFlParID; + if (PBDTGetCommentSync(&dtp) == noErr) + comlen = dtp.ioDTActCount; + } + } +#endif /* ! 1 */ + + /* write header */ +// head.magic = dfork ? APPLESINGLE_MAGIC : APPLEDOUBLE_MAGIC; + head.magic = APPLEDOUBLE_MAGIC; /* always do apple double */ + head.version = VERSION; + memset(head.fill, '\0', sizeof (head.fill)); + head.entries = NUM_ENTRIES - 1; + status = to64(p_ap_encode_obj, + (char *) &head, + sizeof (head)); + if (status != noErr) + return status; + + /* write entry descriptors */ + nsAutoCString leafname; + macFile->GetNativeLeafName(leafname); + entries[0].offset = sizeof (head) + sizeof (ap_entry) * head.entries; + entries[0].id = ENT_NAME; + entries[0].length = leafname.Length(); + entries[1].id = ENT_FINFO; + entries[1].length = sizeof (FInfo) + sizeof (FXInfo); + entries[2].id = ENT_DATES; + entries[2].length = sizeof (ap_dates); + entries[3].id = ENT_COMMENT; + entries[3].length = comlen; + entries[4].id = ENT_RFORK; + entries[4].length = catalogInfo.rsrcLogicalSize; + entries[5].id = ENT_DFORK; + entries[5].length = catalogInfo.dataLogicalSize; + + /* correct the link in the entries. */ + for (i = 1; i < NUM_ENTRIES; ++i) + { + entries[i].offset = entries[i-1].offset + entries[i-1].length; + } + status = to64(p_ap_encode_obj, + (char *) entries, + sizeof (ap_entry) * head.entries); + if (status != noErr) + return status; + + /* write name */ + status = to64(p_ap_encode_obj, + (char *) leafname.get(), + leafname.Length()); + if (status != noErr) + return status; + + /* write finder info */ + status = to64(p_ap_encode_obj, + (char *) &catalogInfo.finderInfo, + sizeof (FInfo)); + if (status != noErr) + return status; + + status = to64(p_ap_encode_obj, + (char *) &catalogInfo.extFinderInfo, + sizeof (FXInfo)); + if (status != noErr) + return status; + + /* write dates */ + dates.create = catalogInfo.createDate.lowSeconds + CONVERT_TIME; + dates.modify = catalogInfo.contentModDate.lowSeconds + CONVERT_TIME; + dates.backup = catalogInfo.backupDate.lowSeconds + CONVERT_TIME; + dates.access = catalogInfo.accessDate.lowSeconds + CONVERT_TIME; + status = to64(p_ap_encode_obj, + (char *) &dates, + sizeof (ap_dates)); + if (status != noErr) + return status; + + /* write comment */ + if (comlen) + { + status = to64(p_ap_encode_obj, + comment, + comlen * sizeof(char)); + } + /* + ** Get some help information on deciding the file type. + */ + if (((FileInfo*)(&catalogInfo.finderInfo))->fileType == 'TEXT' || + ((FileInfo*)(&catalogInfo.finderInfo))->fileType == 'text') + { + p_ap_encode_obj->text_file_type = true; + } + + return status; +} +/* +** ap_encode_header +** +** encode the file header and the resource fork. +** +*/ +int ap_encode_header( + appledouble_encode_object* p_ap_encode_obj, + bool firstime) +{ + char rd_buff[256]; + FSIORefNum fileId; + OSErr retval = noErr; + int status; + ByteCount inCount; + + if (firstime) + { + PL_strcpy(rd_buff, + "Content-Type: application/applefile\r\nContent-Transfer-Encoding: base64\r\n\r\n"); + status = write_stream(p_ap_encode_obj, (const char*)rd_buff, strlen(rd_buff)); + if (status != noErr) + return status; + + status = ap_encode_file_infor(p_ap_encode_obj); + if (status != noErr) + return status; + + /* + ** preparing to encode the resource fork. + */ + nsCOMPtr <nsIFile> myFile; + NS_NewNativeLocalFile(nsDependentCString(p_ap_encode_obj->fname), true, getter_AddRefs(myFile)); + if (!myFile) + return errFileOpen; + + FSRef ref; + nsCOMPtr <nsILocalFileMac> macFile = do_QueryInterface(myFile); + if (NS_FAILED(macFile->GetFSRef(&ref))) + return errFileOpen; + + HFSUniStr255 forkName; + ::FSGetResourceForkName(&forkName); + retval = ::FSOpenFork(&ref, forkName.length, forkName.unicode, fsRdPerm, &p_ap_encode_obj->fileId); + if (retval != noErr) + return retval; + } + + fileId = p_ap_encode_obj->fileId; + while (retval == noErr) + { + if (BUFF_LEFT(p_ap_encode_obj) < 400) + break; + + inCount = 0; + retval = ::FSReadFork(fileId, fsAtMark, 0, 256, rd_buff, &inCount); + if (inCount) + { + status = to64(p_ap_encode_obj, + rd_buff, + inCount); + if (status != noErr) + return status; + } + } + + if (retval == eofErr) + { + ::FSCloseFork(fileId); + p_ap_encode_obj->fileId = 0; + + status = finish64(p_ap_encode_obj); + if (status != noErr) + return status; + + /* + ** write out the boundary + */ + PR_snprintf(rd_buff, sizeof(rd_buff), + CRLF "--%s" CRLF, + p_ap_encode_obj->boundary); + + status = write_stream(p_ap_encode_obj, (const char*)rd_buff, strlen(rd_buff)); + if (status == noErr) + status = errDone; + } + return status; +} + +#if 0 +// This is unused for now and Clang complains about that is it is ifdefed out +static void replace(char *p, int len, char frm, char to) +{ + for (; len > 0; len--, p++) + if (*p == frm) *p = to; +} +#endif + +/* Description of the various file formats and their magic numbers */ +struct magic +{ + const char *name; /* Name of the file format */ + const char *num; /* The magic number */ + int len; /* Length (0 means strlen(magicnum)) */ +}; + +/* The magic numbers of the file formats we know about */ +static struct magic magic[] = +{ + { "image/gif", "GIF", 0 }, + { "image/jpeg", "\377\330\377", 0 }, + { "video/mpeg", "\0\0\001\263", 4 }, + { "application/postscript", "%!", 0 }, +}; +static int num_magic = MOZ_ARRAY_LENGTH(magic); + +static const char *text_type = TEXT_PLAIN; /* the text file type. */ +static const char *default_type = APPLICATION_OCTET_STREAM; + + +/* + * Determins the format of the file "inputf". The name + * of the file format (or NULL on error) is returned. + */ +static const char *magic_look(char *inbuff, int numread) +{ + int i, j; + + for (i=0; i<num_magic; i++) + { + if (magic[i].len == 0) + magic[i].len = strlen(magic[i].num); + } + + for (i=0; i<num_magic; i++) + { + if (numread >= magic[i].len) + { + for (j=0; j<magic[i].len; j++) + { + if (inbuff[j] != magic[i].num[j]) break; + } + + if (j == magic[i].len) + return magic[i].name; + } + } + + return default_type; +} +/* +** ap_encode_data +** +** --------------- +** +** encode on the data fork. +** +*/ +int ap_encode_data( + appledouble_encode_object* p_ap_encode_obj, + bool firstime) +{ + char rd_buff[256]; + FSIORefNum fileId; + OSErr retval = noErr; + ByteCount in_count; + int status; + + if (firstime) + { + const char* magic_type; + + /* + ** preparing to encode the data fork. + */ + nsCOMPtr <nsIFile> resFile; + NS_NewNativeLocalFile(nsDependentCString(p_ap_encode_obj->fname), true, + getter_AddRefs(resFile)); + if (!resFile) + return errFileOpen; + + FSRef ref; + nsCOMPtr <nsILocalFileMac> macFile = do_QueryInterface(resFile); + if (NS_FAILED(macFile->GetFSRef(&ref))) + return errFileOpen; + + HFSUniStr255 forkName; + ::FSGetDataForkName(&forkName); + retval = ::FSOpenFork(&ref, forkName.length, forkName.unicode, fsRdPerm, &fileId); + if (retval != noErr) + return retval; + + p_ap_encode_obj->fileId = fileId; + + + if (!p_ap_encode_obj->text_file_type) + { + /* + ** do a smart check for the file type. + */ + in_count = 0; + retval = ::FSReadFork(fileId, fsFromStart, 0, 256, rd_buff, &in_count); + magic_type = magic_look(rd_buff, in_count); + + /* don't forget to rewind the index to start point. */ + ::FSSetForkPosition(fileId, fsFromStart, 0); + /* and reset retVal just in case... */ + if (retval == eofErr) + retval = noErr; + } + else + { + magic_type = text_type; /* we already know it is a text type. */ + } + + /* + ** the data portion header information. + */ + nsAutoCString leafName; + resFile->GetNativeLeafName(leafName); + PR_snprintf(rd_buff, sizeof(rd_buff), + "Content-Type: %s; name=\"%s\"" CRLF "Content-Transfer-Encoding: base64" CRLF "Content-Disposition: inline; filename=\"%s\"" CRLF CRLF, + magic_type, + leafName.get(), + leafName.get()); + + status = write_stream(p_ap_encode_obj, (const char*)rd_buff, strlen(rd_buff)); + if (status != noErr) + return status; + } + + while (retval == noErr) + { + if (BUFF_LEFT(p_ap_encode_obj) < 400) + break; + + in_count = 0; + retval = ::FSReadFork(p_ap_encode_obj->fileId, fsAtMark, 0, 256, rd_buff, &in_count); + if (in_count) + { +#if 0 +/* replace(rd_buff, in_count, '\r', '\n'); */ +#endif +/* ** may be need to do character set conversion here for localization. ** */ + status = to64(p_ap_encode_obj, + rd_buff, + in_count); + if (status != noErr) + return status; + } + } + + if (retval == eofErr) + { + ::FSCloseFork(p_ap_encode_obj->fileId); + p_ap_encode_obj->fileId = 0; + + status = finish64(p_ap_encode_obj); + if (status != noErr) + return status; + + /* write out the boundary */ + + PR_snprintf(rd_buff, sizeof(rd_buff), + CRLF "--%s--" CRLF CRLF, + p_ap_encode_obj->boundary); + + status = write_stream(p_ap_encode_obj, (const char*)rd_buff, strlen(rd_buff)); + + if (status == noErr) + status = errDone; + } + return status; +} + +static char basis_64[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +/* +** convert the stream in the inbuff to 64 format and put it in the out buff. +** To make the life easier, the caller will responcable of the cheking of the outbuff's bundary. +*/ +static int +to64(appledouble_encode_object* p_ap_encode_obj, + char *p, + int in_size) +{ + int status; + int c1, c2, c3, ct; + unsigned char *inbuff = (unsigned char*)p; + + ct = p_ap_encode_obj->ct; /* the char count left last time. */ + + /* + ** resume the left state of the last conversion. + */ + switch (p_ap_encode_obj->state64) + { + case 0: + p_ap_encode_obj->c1 = c1 = *inbuff ++; + if (--in_size <= 0) + { + p_ap_encode_obj->state64 = 1; + return noErr; + } + p_ap_encode_obj->c2 = c2 = *inbuff ++; + if (--in_size <= 0) + { + p_ap_encode_obj->state64 = 2; + return noErr; + } + c3 = *inbuff ++; --in_size; + break; + case 1: + c1 = p_ap_encode_obj->c1; + p_ap_encode_obj->c2 = c2 = *inbuff ++; + if (--in_size <= 0) + { + p_ap_encode_obj->state64 = 2; + return noErr; + } + c3 = *inbuff ++; --in_size; + break; + case 2: + c1 = p_ap_encode_obj->c1; + c2 = p_ap_encode_obj->c2; + c3 = *inbuff ++; --in_size; + break; + } + + while (in_size >= 0) + { + status = output64chunk(p_ap_encode_obj, + c1, + c2, + c3, + 0); + if (status != noErr) + return status; + + ct += 4; + if (ct > 71) + { + status = write_stream(p_ap_encode_obj, + CRLF, + 2); + if (status != noErr) + return status; + + ct = 0; + } + + if (in_size <= 0) + { + p_ap_encode_obj->state64 = 0; + break; + } + + c1 = (int)*inbuff++; + if (--in_size <= 0) + { + p_ap_encode_obj->c1 = c1; + p_ap_encode_obj->state64 = 1; + break; + } + c2 = *inbuff++; + if (--in_size <= 0) + { + p_ap_encode_obj->c1 = c1; + p_ap_encode_obj->c2 = c2; + p_ap_encode_obj->state64 = 2; + break; + } + c3 = *inbuff++; + in_size--; + } + p_ap_encode_obj->ct = ct; + return status; +} + +/* +** clear the left base64 encodes. +*/ +static int +finish64(appledouble_encode_object* p_ap_encode_obj) +{ + int status; + + switch (p_ap_encode_obj->state64) + { + case 0: + break; + case 1: + status = output64chunk(p_ap_encode_obj, + p_ap_encode_obj->c1, + 0, + 0, + 2); + break; + case 2: + status = output64chunk(p_ap_encode_obj, + p_ap_encode_obj->c1, + p_ap_encode_obj->c2, + 0, + 1); + break; + } + status = write_stream(p_ap_encode_obj, CRLF, 2); + p_ap_encode_obj->state64 = 0; + p_ap_encode_obj->ct = 0; + return status; +} + +static int output64chunk( + appledouble_encode_object* p_ap_encode_obj, + int c1, int c2, int c3, int pads) +{ + char tmpstr[32]; + char *p = tmpstr; + + *p++ = basis_64[c1>>2]; + *p++ = basis_64[((c1 & 0x3)<< 4) | ((c2 & 0xF0) >> 4)]; + if (pads == 2) + { + *p++ = '='; + *p++ = '='; + } + else if (pads) + { + *p++ = basis_64[((c2 & 0xF) << 2) | ((c3 & 0xC0) >>6)]; + *p++ = '='; + } + else + { + *p++ = basis_64[((c2 & 0xF) << 2) | ((c3 & 0xC0) >>6)]; + *p++ = basis_64[c3 & 0x3F]; + } + return write_stream(p_ap_encode_obj, (const char*) tmpstr, p-tmpstr); +} diff --git a/mailnews/import/build/moz.build b/mailnews/import/build/moz.build index 58814a6940..e8ac0751f5 100644 --- a/mailnews/import/build/moz.build +++ b/mailnews/import/build/moz.build @@ -35,6 +35,13 @@ LOCAL_INCLUDES += [ '../vcard/src', ] +if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa': + LOCAL_INCLUDES += [ + '../applemail/src', + ] + OS_LIBS += CONFIG['TK_LIBS'] + OS_LIBS += ['-framework Cocoa'] + if CONFIG['OS_ARCH'] == 'WINNT': LOCAL_INCLUDES += [ ] |