diff -Nru thunderbird-78.6.0+build1/browser/config/version_display.txt thunderbird-78.6.1+build1/browser/config/version_display.txt --- thunderbird-78.6.0+build1/browser/config/version_display.txt 2020-12-14 09:29:41.000000000 +0000 +++ thunderbird-78.6.1+build1/browser/config/version_display.txt 2021-01-08 18:54:29.000000000 +0000 @@ -1 +1 @@ -78.6.0esr +78.6.1esr diff -Nru thunderbird-78.6.0+build1/browser/config/version.txt thunderbird-78.6.1+build1/browser/config/version.txt --- thunderbird-78.6.0+build1/browser/config/version.txt 2020-12-14 09:29:41.000000000 +0000 +++ thunderbird-78.6.1+build1/browser/config/version.txt 2021-01-08 18:54:29.000000000 +0000 @@ -1 +1 @@ -78.6.0 +78.6.1 diff -Nru thunderbird-78.6.0+build1/build/moz.configure/warnings.configure thunderbird-78.6.1+build1/build/moz.configure/warnings.configure --- thunderbird-78.6.0+build1/build/moz.configure/warnings.configure 2020-12-14 09:29:41.000000000 +0000 +++ thunderbird-78.6.1+build1/build/moz.configure/warnings.configure 2021-01-08 18:54:06.000000000 +0000 @@ -223,6 +223,11 @@ when=depends(build_project) (lambda build_project: build_project == 'js')) +# Disable broken missing-braces warning on old clang versions +check_and_add_gcc_warning( + '-Wno-missing-braces', + when=depends(c_compiler)(lambda c: c.type == 'clang' and c.version < '6.0')) + # Please keep these last in this file add_old_configure_assignment('_WARNINGS_CFLAGS', warnings_flags.cflags) diff -Nru thunderbird-78.6.0+build1/BUILDID thunderbird-78.6.1+build1/BUILDID --- thunderbird-78.6.0+build1/BUILDID 2020-12-14 09:47:44.000000000 +0000 +++ thunderbird-78.6.1+build1/BUILDID 2021-01-08 19:06:36.000000000 +0000 @@ -1 +1 @@ -20201211152611 \ No newline at end of file +20210107201950 \ No newline at end of file diff -Nru thunderbird-78.6.0+build1/comm/calendar/base/src/CalTimezoneService.jsm thunderbird-78.6.1+build1/comm/calendar/base/src/CalTimezoneService.jsm --- thunderbird-78.6.0+build1/comm/calendar/base/src/CalTimezoneService.jsm 2020-12-14 09:35:23.000000000 +0000 +++ thunderbird-78.6.1+build1/comm/calendar/base/src/CalTimezoneService.jsm 2021-01-08 18:56:33.000000000 +0000 @@ -264,7 +264,9 @@ if (!this.mDefaultTimezone) { let prefTzid = Services.prefs.getStringPref("calendar.timezone.local", null); let tzid = prefTzid; - if (!tzid) { + // If a user already has a profile created by an earlier version + // with floating timezone, set the correctly guessed timezone. + if (!tzid || tzid == "floating") { try { tzid = guessSystemTimezone(); } catch (e) { @@ -317,8 +319,9 @@ */ function guessSystemTimezone() { // Probe JSDates for basic OS timezone offsets and names. - const dateJun = new Date(2005, 5, 20).toString(); - const dateDec = new Date(2005, 11, 20).toString(); + // Check timezone rules for current year + const dateJun = new Date(new Date().getFullYear(), 5, 20).toString(); + const dateDec = new Date(new Date().getFullYear(), 11, 20).toString(); const tzNameRegex = /[^(]* ([^ ]*) \(([^)]+)\)/; const nameDataJun = dateJun.match(tzNameRegex); const nameDataDec = dateDec.match(tzNameRegex); @@ -563,7 +566,7 @@ try { let line = {}, hasMore = true, - MAXLINES = 10; + MAXLINES = 50; for (let i = 0; hasMore && i < MAXLINES; i++) { hasMore = fileInstream.readLine(line); if (line.value && line.value.match(tzRegex)) { diff -Nru thunderbird-78.6.0+build1/comm/calendar/lightning/content/imip-bar.js thunderbird-78.6.1+build1/comm/calendar/lightning/content/imip-bar.js --- thunderbird-78.6.0+build1/comm/calendar/lightning/content/imip-bar.js 2020-12-14 09:35:23.000000000 +0000 +++ thunderbird-78.6.1+build1/comm/calendar/lightning/content/imip-bar.js 2021-01-08 18:56:33.000000000 +0000 @@ -65,11 +65,13 @@ // We need to extend the HideMessageHeaderPane function to also hide the // message header pane. Otherwise, the imip bar will still be shown when // changing folders. - ltnImipBar.tbHideMessageHeaderPane = HideMessageHeaderPane; - HideMessageHeaderPane = function(...args) { - ltnImipBar.resetBar(); - ltnImipBar.tbHideMessageHeaderPane(...args); - }; + if (!ltnImipBar.tbHideMessageHeaderPane) { + ltnImipBar.tbHideMessageHeaderPane = HideMessageHeaderPane; + HideMessageHeaderPane = function(...args) { + ltnImipBar.resetBar(); + ltnImipBar.tbHideMessageHeaderPane(...args); + }; + } // Set up our observers Services.obs.addObserver(ltnImipBar, "onItipItemCreation"); diff -Nru thunderbird-78.6.0+build1/comm/calendar/providers/caldav/modules/CalDavRequest.jsm thunderbird-78.6.1+build1/comm/calendar/providers/caldav/modules/CalDavRequest.jsm --- thunderbird-78.6.0+build1/comm/calendar/providers/caldav/modules/CalDavRequest.jsm 2020-12-14 09:35:23.000000000 +0000 +++ thunderbird-78.6.1+build1/comm/calendar/providers/caldav/modules/CalDavRequest.jsm 2021-01-08 18:56:33.000000000 +0000 @@ -253,7 +253,7 @@ }); this.completed = new Promise((resolve, reject) => { this._oncompleted = resolve; - this._oncompleteerror = reject; + this._oncompletederror = reject; }); } diff -Nru thunderbird-78.6.0+build1/comm/calendar/providers/storage/CalStorageCalendar.jsm thunderbird-78.6.1+build1/comm/calendar/providers/storage/CalStorageCalendar.jsm --- thunderbird-78.6.0+build1/comm/calendar/providers/storage/CalStorageCalendar.jsm 2020-12-14 09:35:23.000000000 +0000 +++ thunderbird-78.6.1+build1/comm/calendar/providers/storage/CalStorageCalendar.jsm 2021-01-08 18:56:33.000000000 +0000 @@ -1769,7 +1769,7 @@ } let rec = item.recurrenceInfo; - let exc = await this.getEventFromRow(row, false); + let exc = await this.getEventFromRow(row); rec.modifyException(exc, true); }); @@ -1781,7 +1781,7 @@ } let rec = item.recurrenceInfo; - let exc = await this.getTodoFromRow(row, false); + let exc = await this.getTodoFromRow(row); rec.modifyException(exc, true); }); diff -Nru thunderbird-78.6.0+build1/comm/calendar/test/unit/test_storage_get_items.js thunderbird-78.6.1+build1/comm/calendar/test/unit/test_storage_get_items.js --- thunderbird-78.6.0+build1/comm/calendar/test/unit/test_storage_get_items.js 1970-01-01 00:00:00.000000000 +0000 +++ thunderbird-78.6.1+build1/comm/calendar/test/unit/test_storage_get_items.js 2021-01-08 18:56:33.000000000 +0000 @@ -0,0 +1,215 @@ +/* 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/. */ + +/** + * Tests for the CalStorageCalendar.getItems method. + */ + +do_get_profile(); + +/** + * The bug we are interested in testing requires the calendar to clear its + * caches in order to take effect. Since we can't directly access the internals + * of the calendar here, we instead provide a custom function that lets us + * create more than one calendar with the same id. + */ +function createStorageCalendar(id) { + let db = Services.dirsvc.get("TmpD", Ci.nsIFile); + db.append("test_storage.sqlite"); + let uri = Services.io.newFileURI(db); + + // Make sure timezone service is initialized + Cc["@mozilla.org/calendar/timezone-service;1"].getService(Ci.calIStartupService).startup(null); + + let calendar = Cc["@mozilla.org/calendar/calendar;1?type=storage"].createInstance( + Ci.calISyncWriteCalendar + ); + + calendar.uri = uri; + calendar.id = id; + return cal.async.promisifyCalendar(calendar); +} + +/** + * Tests that recurring event/todo exceptions have their properties properly + * loaded. See bug 1664731. + * + * @param {number} filterType - Number indicating the filter type. + * @param {calIITemBase} originalItem - The original item to add to the calendar. + * @param {object} originalProps - The initial properites of originalItem we + * change. + * @param {object} changedProps - The changed properties of originalItem.. + */ +async function doPropertiesTest(filterType, originalItem, originalProps, changedProps) { + for (let [key, value] of Object.entries(originalProps)) { + if (key == "CATEGORIES") { + originalItem.setCategories(value); + } else { + originalItem.setProperty(key, value); + } + } + + let calId = cal.getUUID(); + let calendar = createStorageCalendar(calId); + await calendar.addItem(originalItem); + + let filter = + filterType | + Ci.calICalendar.ITEM_FILTER_COMPLETED_ALL | + Ci.calICalendar.ITEM_FILTER_CLASS_OCCURRENCES; + + let savedItems = await calendar.getItems( + filter, + 0, + cal.createDateTime("20201201T000000Z"), + cal.createDateTime("20201231T000000Z") + ); + + Assert.equal(savedItems.length, 5, `saved ${savedItems.length} items successfully`); + + // Ensure all occurrences have the correct properties initially. + for (let item of savedItems) { + for (let [key, value] of Object.entries(originalProps)) { + if (key == "CATEGORIES") { + Assert.equal( + item.getCategories().join(), + value.join(), + `item categories are set to ${value}` + ); + } else { + Assert.equal(item.getProperty(key), value, `item property "${key}" is set to "${value}"`); + } + } + } + + // Grab the occurrence whose properties we want to modify. + let targetOccurrence = savedItems[2]; + let targetException = targetOccurrence.clone(); + + let targetDate = + filterType & Ci.calICalendar.ITEM_FILTER_TYPE_TODO + ? targetOccurrence.entryDate + : targetOccurrence.startDate; + + targetDate = targetDate.clone(); + + // Make the changes to the properties. + for (let [key, value] of Object.entries(changedProps)) { + if (key == "CATEGORIES") { + targetException.setCategories(value); + } else { + targetException.setProperty(key, value); + } + } + + await calendar.modifyItem( + cal.itip.prepareSequence(targetException, targetOccurrence), + targetOccurrence + ); + + // Get a fresh copy of the items by using a new calendar with the same id. + let itemsAfterUpdate = await createStorageCalendar(calId).getItems( + filter, + 0, + cal.createDateTime("20201201T000000Z"), + cal.createDateTime("20201231T000000Z") + ); + + Assert.equal(itemsAfterUpdate.length, 5, "expected occurrence count retrieved from query"); + + // Compare each property we changed to ensure the target occurrence has + // the properties we expect. + for (let item of itemsAfterUpdate) { + let isException = targetDate.compare(item.recurrenceId) == 0; + let label = isException ? "occurrence exception" : "unmodified occurrence"; + let checkedProps = isException ? changedProps : originalProps; + + for (let [key, value] of Object.entries(checkedProps)) { + if (key == "CATEGORIES") { + Assert.equal( + item.getCategories().join(), + value.join(), + `item categories are set to ${value}` + ); + } else { + Assert.equal( + item.getProperty(key), + value, + `property "${key}" is set to "${value}" for ${label}` + ); + } + } + } +} + +/** + * Test event exceptions load their properties. + */ +add_task(async function testEventPropertiesForRecurringExceptionsLoad() { + let event = cal.createEvent(dedent` + BEGIN:VEVENT + CREATED:20201211T000000Z + LAST-MODIFIED:20201211T000000Z + DTSTAMP:20201210T080410Z + UID:c1a6cfe7-7fbb-4bfb-a00d-861e07c649a5 + SUMMARY:Original Test Event + DTSTART:20201211T000000Z + DTEND:20201211T110000Z + RRULE:FREQ=DAILY;UNTIL=20201215T140000Z + END:VEVENT + `); + + let originalProps = { + DESCRIPTION: "This is a test event.", + CATEGORIES: ["Birthday"], + LOCATION: "Castara", + }; + + let changedProps = { + DESCRIPTION: "This is an edited occurrence.", + CATEGORIES: ["Holiday"], + LOCATION: "Georgetown", + }; + + return doPropertiesTest( + Ci.calICalendar.ITEM_FILTER_TYPE_EVENT, + event, + originalProps, + changedProps + ); +}); + +/** + * Test todo exceptions load their properties. + */ +add_task(async function testTodoPropertiesForRecurringExceptionsLoad() { + let todo = cal.createTodo(dedent` + BEGIN:VTODO + CREATED:20201211T000000Z + LAST-MODIFIED:20201211T000000Z + DTSTAMP:20201210T080410Z + UID:c1a6cfe7-7fbb-4bfb-a00d-861e07c649a5 + SUMMARY:Original Test Event + DTSTART:20201211T000000Z + DTEND:20201211T110000Z + RRULE:FREQ=DAILY;UNTIL=20201215T140000Z + END:VTODO + `); + + let originalProps = { + DESCRIPTION: "This is a test todo.", + CATEGORIES: ["Birthday"], + LOCATION: "Castara", + STATUS: "NEEDS-ACTION", + }; + + let changedProps = { + DESCRIPTION: "This is an edited occurrence.", + CATEGORIES: ["Holiday"], + LOCATION: "Georgetown", + STATUS: "COMPLETE", + }; + + return doPropertiesTest(Ci.calICalendar.ITEM_FILTER_TYPE_TODO, todo, originalProps, changedProps); +}); diff -Nru thunderbird-78.6.0+build1/comm/calendar/test/unit/xpcshell-shared.ini thunderbird-78.6.1+build1/comm/calendar/test/unit/xpcshell-shared.ini --- thunderbird-78.6.0+build1/comm/calendar/test/unit/xpcshell-shared.ini 2020-12-14 09:35:23.000000000 +0000 +++ thunderbird-78.6.1+build1/comm/calendar/test/unit/xpcshell-shared.ini 2021-01-08 18:56:33.000000000 +0000 @@ -47,6 +47,7 @@ [test_search_service.js] [test_startup_service.js] [test_storage.js] +[test_storage_get_items.js] [test_timezone.js] [test_timezone_changes.js] [test_timezone_definition.js] diff -Nru thunderbird-78.6.0+build1/comm/chat/components/public/imIAccount.idl thunderbird-78.6.1+build1/comm/chat/components/public/imIAccount.idl --- thunderbird-78.6.0+build1/comm/chat/components/public/imIAccount.idl 2020-12-14 09:35:23.000000000 +0000 +++ thunderbird-78.6.1+build1/comm/chat/components/public/imIAccount.idl 2021-01-08 18:56:33.000000000 +0000 @@ -144,13 +144,17 @@ /* When a connection error occurred, this value indicates the type of error */ readonly attribute short connectionErrorReason; - /* When a certificate error occurs, the host/port that caused a + /** + * When a certificate error occurs, the host/port that caused a * SSL/certificate error when connecting to it. This is only valid when - * connectionErrorReason is one of ERROR_CERT_*. */ + * connectionErrorReason is one of ERROR_CERT_* + */ readonly attribute AUTF8String connectionTarget; - /* When a certificate error occurs, the nsITransportSecurityInfo error of - * the socket. This should only be set when connectionTarget is set. */ - readonly attribute nsITransportSecurityInfo secInfo; + /** + * When a certificate error occurs, the nsITransportSecurityInfo error of + * the socket. This should only be set when connectionTarget is set. + */ + readonly attribute nsITransportSecurityInfo securityInfo; /* Possible connection error reasons: ERROR_NETWORK_ERROR and ERROR_ENCRYPTION_ERROR are not fatal and diff -Nru thunderbird-78.6.0+build1/comm/chat/content/accounts.css thunderbird-78.6.1+build1/comm/chat/content/accounts.css --- thunderbird-78.6.0+build1/comm/chat/content/accounts.css 2020-12-14 09:35:23.000000000 +0000 +++ thunderbird-78.6.1+build1/comm/chat/content/accounts.css 2021-01-08 18:56:33.000000000 +0000 @@ -17,7 +17,6 @@ richlistitem[selected="true"]:not([state="disconnected"]) .connectButton, richlistitem[selected="true"][state="disconnected"] .disconnectButton, richlistitem[selected="true"][state="disconnecting"] .disconnectButton, -richlistitem[selected="true"]:not([certError="true"]) .addException, richlistitem:not([selected="true"]) .addException, richlistitem:not([selected="true"]) .autoSignOn, richlistitem:not([reconnectPending="true"]) description[anonid="reconnect"] diff -Nru thunderbird-78.6.0+build1/comm/chat/content/chat-account-richlistitem.js thunderbird-78.6.1+build1/comm/chat/content/chat-account-richlistitem.js --- thunderbird-78.6.0+build1/comm/chat/content/chat-account-richlistitem.js 2020-12-14 09:35:23.000000000 +0000 +++ thunderbird-78.6.1+build1/comm/chat/content/chat-account-richlistitem.js 2021-01-08 18:56:33.000000000 +0000 @@ -72,8 +72,6 @@ - ) * = onBinaryDataReceived(ArrayBuffer ) @@ -116,6 +116,12 @@ "nsIScriptableUnicodeConverter" ); +/** + * @implements {nsIStreamListener} + * @implements {nsIRequestObserver} + * @implements {nsITransportEventSink} + * @implements {nsIProtocolProxyCallback} + */ var Socket = { // Use this to use binary mode for the binaryMode: false, @@ -136,7 +142,7 @@ readWriteTimeout: 0, // A nsITransportSecurityInfo instance giving details about the certificate error. - secInfo: null, + securityInfo: null, /* ***************************************************************************** @@ -156,7 +162,7 @@ aPort = aOriginPort ) { if (Services.io.offline) { - throw Components.Exception("", Cr.NS_ERROR_FAILURE); + throw Components.Exception("Offline, can't connect", Cr.NS_ERROR_FAILURE); } // This won't work for Linux due to bug 758848. @@ -519,31 +525,13 @@ let nssErrorsService = Cc["@mozilla.org/nss_errors_service;1"].getService( Ci.nsINSSErrorsService ); - if ( - (aStatus <= - nssErrorsService.getXPCOMFromNSSError( - nssErrorsService.NSS_SEC_ERROR_BASE - ) && - aStatus >= - nssErrorsService.getXPCOMFromNSSError( - nssErrorsService.NSS_SEC_ERROR_LIMIT - 1 - )) || - (aStatus <= - nssErrorsService.getXPCOMFromNSSError( - nssErrorsService.NSS_SSL_ERROR_BASE - ) && - aStatus >= - nssErrorsService.getXPCOMFromNSSError( - nssErrorsService.NSS_SSL_ERROR_LIMIT - 1 - )) - ) { - this.onBadCertificate( - nssErrorsService.getErrorClass(aStatus) == - nssErrorsService.ERROR_CLASS_SSL_PROTOCOL, - nssErrorsService.getErrorMessage(aStatus) - ); - return; - } + this.securityInfo = this.transport.securityInfo.QueryInterface( + Ci.nsITransportSecurityInfo + ); + this.onConnectionSecurityError( + aStatus, + nssErrorsService.getErrorMessage(aStatus) + ); } this.onConnectionClosed(); }, @@ -701,7 +689,7 @@ // Called when a socket request's network is reset. onConnectionReset() {}, // Called when the certificate provided by the server didn't satisfy NSS. - onBadCertificate(aNSSErrorMessage) {}, + onConnectionSecurityError(aTLSError, aNSSErrorMessage) {}, // Called when the other end has closed the connection. onConnectionClosed() {}, diff -Nru thunderbird-78.6.0+build1/comm/chat/protocols/irc/irc.jsm thunderbird-78.6.1+build1/comm/chat/protocols/irc/irc.jsm --- thunderbird-78.6.0+build1/comm/chat/protocols/irc/irc.jsm 2020-12-14 09:35:23.000000000 +0000 +++ thunderbird-78.6.1+build1/comm/chat/protocols/irc/irc.jsm 2021-01-08 18:56:33.000000000 +0000 @@ -917,14 +917,14 @@ _("connection.error.timeOut") ); }, - onBadCertificate(aIsSslError, aNSSErrorMessage) { + onConnectionSecurityError(aTLSError, aNSSErrorMessage) { this.WARN( "Bad certificate or SSL connection for " + this._account.name + ":\n" + aNSSErrorMessage ); - let error = this._account.handleBadCertificate(this, aIsSslError); + let error = this._account.handleConnectionSecurityError(this); this._account.gotDisconnected(error, aNSSErrorMessage); }, diff -Nru thunderbird-78.6.0+build1/comm/chat/protocols/xmpp/xmpp-base.jsm thunderbird-78.6.1+build1/comm/chat/protocols/xmpp/xmpp-base.jsm --- thunderbird-78.6.0+build1/comm/chat/protocols/xmpp/xmpp-base.jsm 2020-12-14 09:35:23.000000000 +0000 +++ thunderbird-78.6.1+build1/comm/chat/protocols/xmpp/xmpp-base.jsm 2021-01-08 18:56:33.000000000 +0000 @@ -2680,7 +2680,7 @@ conv.supportChatStateNotifications = !!state; }, - /* Called when there is an error in the xmpp session */ + /** Called when there is an error in the XMPP session */ onError(aError, aException) { if (aError === null || aError === undefined) { aError = Ci.prplIAccount.ERROR_OTHER_ERROR; diff -Nru thunderbird-78.6.0+build1/comm/chat/protocols/xmpp/xmpp-session.jsm thunderbird-78.6.1+build1/comm/chat/protocols/xmpp/xmpp-session.jsm --- thunderbird-78.6.0+build1/comm/chat/protocols/xmpp/xmpp-session.jsm 2020-12-14 09:35:23.000000000 +0000 +++ thunderbird-78.6.1+build1/comm/chat/protocols/xmpp/xmpp-session.jsm 2021-01-08 18:56:33.000000000 +0000 @@ -369,8 +369,8 @@ onConnectionClosed() { this._networkError(_("connection.error.serverClosedConnection")); }, - onBadCertificate(aIsSslError, aNSSErrorMessage) { - let error = this._account.handleBadCertificate(this, aIsSslError); + onConnectionSecurityError(aTLSError, aNSSErrorMessage) { + let error = this._account.handleConnectionSecurityError(this); this.onError(error, aNSSErrorMessage); }, onConnectionReset() { diff -Nru thunderbird-78.6.0+build1/comm/.gecko_rev.yml thunderbird-78.6.1+build1/comm/.gecko_rev.yml --- thunderbird-78.6.0+build1/comm/.gecko_rev.yml 2020-12-14 09:35:23.000000000 +0000 +++ thunderbird-78.6.1+build1/comm/.gecko_rev.yml 2021-01-08 18:56:33.000000000 +0000 @@ -1,8 +1,8 @@ --- GECKO_BASE_REPOSITORY: https://hg.mozilla.org/mozilla-unified GECKO_HEAD_REPOSITORY: https://hg.mozilla.org/releases/mozilla-esr78 -GECKO_HEAD_REF: FIREFOX_78_6_0esr_BUILD1 -GECKO_HEAD_REV: 8e6813a535daf9ef261c2bbb80cb10e9a832d68e +GECKO_HEAD_REF: FIREFOX_78_6_1esr_RELEASE +GECKO_HEAD_REV: df0581e37d875577ff1f2be1d5943e0d03c9e796 ### For comm-central # GECKO_BASE_REPOSITORY: https://hg.mozilla.org/mozilla-unified diff -Nru thunderbird-78.6.0+build1/comm/ldap/xpcom/src/nsLDAPConnection.cpp thunderbird-78.6.1+build1/comm/ldap/xpcom/src/nsLDAPConnection.cpp --- thunderbird-78.6.0+build1/comm/ldap/xpcom/src/nsLDAPConnection.cpp 2020-12-14 09:35:23.000000000 +0000 +++ thunderbird-78.6.1+build1/comm/ldap/xpcom/src/nsLDAPConnection.cpp 2021-01-08 18:56:33.000000000 +0000 @@ -220,17 +220,24 @@ * and this leads to starvation. * We have to do a copy of pending operations. */ - nsTArray pending_operations; + nsTArray pending_operations; { MutexAutoLock lock(mPendingOperationsMutex); for (auto iter = mPendingOperations.Iter(); !iter.Done(); iter.Next()) { - pending_operations.AppendElement(iter.UserData()); + nsLDAPOperation* op = static_cast(iter.UserData()); + pending_operations.AppendElement(op); } } - for (uint32_t i = 0; i < pending_operations.Length(); i++) { - pending_operations[i]->AbandonExt(); + for (auto op : pending_operations) { + // Tell the operation to free any refcounts it can. + op->Clear(); + // Would be nice to tell the server that it can abort any operations + // it's currently processing, but we'd have to send the ABANDON from + // the thread, and we're shutting down our LDAP handle right now! + // SO. Future/aspirational: + // op->AbandonExt(); } - Close(); + Close(); // Byebye LDAP handle. } else { MOZ_ASSERT_UNREACHABLE("unexpected topic"); return NS_ERROR_UNEXPECTED; @@ -467,10 +474,9 @@ } nsPrintfCString location("%s:%d", mDNSHost.get(), mPort); nsCOMPtr tsi = do_QueryInterface(secInfo); - NS_DispatchToMainThread(NS_NewRunnableFunction( - "InvokeErrorCallback", [=]() { - listener->OnLDAPError(status, tsi, location); - })); + NS_DispatchToMainThread(NS_NewRunnableFunction("InvokeErrorCallback", [=]() { + listener->OnLDAPError(status, tsi, location); + })); } NS_IMETHODIMP diff -Nru thunderbird-78.6.0+build1/comm/ldap/xpcom/src/nsLDAPOperation.cpp thunderbird-78.6.1+build1/comm/ldap/xpcom/src/nsLDAPOperation.cpp --- thunderbird-78.6.0+build1/comm/ldap/xpcom/src/nsLDAPOperation.cpp 2020-12-14 09:35:23.000000000 +0000 +++ thunderbird-78.6.1+build1/comm/ldap/xpcom/src/nsLDAPOperation.cpp 2021-01-08 18:56:33.000000000 +0000 @@ -653,6 +653,9 @@ return NS_ERROR_NOT_INITIALIZED; } + MOZ_LOG(gLDAPLogModule, mozilla::LogLevel::Debug, + ("nsLDAPOperation::AbandonExt() called (msgid=%d)", mMsgID)); + // XXX handle controls here if (mServerControls || mClientControls) { return NS_ERROR_NOT_IMPLEMENTED; diff -Nru thunderbird-78.6.0+build1/comm/mail/base/content/folderDisplay.js thunderbird-78.6.1+build1/comm/mail/base/content/folderDisplay.js --- thunderbird-78.6.0+build1/comm/mail/base/content/folderDisplay.js 2020-12-14 09:35:23.000000000 +0000 +++ thunderbird-78.6.1+build1/comm/mail/base/content/folderDisplay.js 2021-01-08 18:56:33.000000000 +0000 @@ -18,6 +18,9 @@ var gFolderDisplay = null; var gMessageDisplay = null; +// An object to help collecting reading statistics of secure emails. +var gSecureMsgProbe = {}; + /** * Maintains a list of listeners for all FolderDisplayWidget instances in this * window. The assumption is that because of our multiplexed tab @@ -83,6 +86,29 @@ }; /** + * Update gSecureMsgProbe and report to telemetry if necessary. + */ +function reportMsgRead({ isNewRead = false, key = null }) { + if (isNewRead) { + gSecureMsgProbe.isNewRead = true; + } + if (key) { + gSecureMsgProbe.key = key; + } + if (gSecureMsgProbe.key && gSecureMsgProbe.isNewRead) { + Services.telemetry.keyedScalarAdd( + "tb.mails.read_secure", + gSecureMsgProbe.key, + 1 + ); + } +} + +window.addEventListener("secureMsgLoaded", event => { + reportMsgRead({ key: event.detail.key }); +}); + +/** * Abstraction for a widget that (roughly speaking) displays the contents of * folders. The widget belongs to a tab and has a lifetime as long as the tab * that contains it. This class is strictly concerned with the UI aspects of @@ -1424,6 +1450,12 @@ let msgHdr = selected.length ? selected[0] : null; this.messageDisplay.onDisplayingMessage(msgHdr); + // Reset gSecureMsgProbe. + gSecureMsgProbe = { + key: null, + isNewRead: false, + }; + // Although deletes should now be so fast that the user has no time to do // anything, treat the user explicitly choosing to display a different // message as invalidating the choice we automatically made for them when diff -Nru thunderbird-78.6.0+build1/comm/mail/base/content/mailCommands.js thunderbird-78.6.1+build1/comm/mail/base/content/mailCommands.js --- thunderbird-78.6.0+build1/comm/mail/base/content/mailCommands.js 2020-12-14 09:35:23.000000000 +0000 +++ thunderbird-78.6.1+build1/comm/mail/base/content/mailCommands.js 2021-01-08 18:56:33.000000000 +0000 @@ -514,6 +514,9 @@ ? Ci.nsMsgViewCommandType.markMessagesRead : Ci.nsMsgViewCommandType.markMessagesUnread ); + if (markRead) { + reportMsgRead({ isNewRead: true }); + } } function MarkSelectedMessagesFlagged(markFlagged) { diff -Nru thunderbird-78.6.0+build1/comm/mail/base/content/mailWidgets.js thunderbird-78.6.1+build1/comm/mail/base/content/mailWidgets.js --- thunderbird-78.6.0+build1/comm/mail/base/content/mailWidgets.js 2020-12-14 09:35:23.000000000 +0000 +++ thunderbird-78.6.1+build1/comm/mail/base/content/mailWidgets.js 2021-01-08 18:56:33.000000000 +0000 @@ -2137,6 +2137,16 @@ }); input.addEventListener("keyup", event => { + // Trigger the onRecipientsChanged method for every letter typed in + // order to properly update the "Send" button and trigger the save as + // draft prompt even before the creation of any pill. + if ( + event.key.length == 1 || + (event.key.length > 1 && /[^a-zA-Z0-9]/.test(event.key)) + ) { + onRecipientsChanged(false); + } + // Change the min size of the input field on typing only if the // current width is smaller than 80% of its container's width or none // arrow keys were pressed to prevent overflow. diff -Nru thunderbird-78.6.0+build1/comm/mail/base/content/mailWindowOverlay.js thunderbird-78.6.1+build1/comm/mail/base/content/mailWindowOverlay.js --- thunderbird-78.6.0+build1/comm/mail/base/content/mailWindowOverlay.js 2020-12-14 09:35:23.000000000 +0000 +++ thunderbird-78.6.1+build1/comm/mail/base/content/mailWindowOverlay.js 2021-01-08 18:56:33.000000000 +0000 @@ -2926,6 +2926,8 @@ function TransportErrorUrlListener() {} TransportErrorUrlListener.prototype = { + OnStartRunningUrl(url) {}, + OnStopRunningUrl(url, exitCode) { let nssErrorsService = Cc["@mozilla.org/nss_errors_service;1"].getService( Ci.nsINSSErrorsService @@ -3581,6 +3583,7 @@ var headers = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray); headers.appendElement(msgHdr); msgHdr.folder.markMessagesRead(headers, true); + reportMsgRead({ isNewRead: true }); } function ClearPendingReadTimer() { diff -Nru thunderbird-78.6.0+build1/comm/mail/base/content/mainMailToolbox.inc.xhtml thunderbird-78.6.1+build1/comm/mail/base/content/mainMailToolbox.inc.xhtml --- thunderbird-78.6.0+build1/comm/mail/base/content/mainMailToolbox.inc.xhtml 2020-12-14 09:35:23.000000000 +0000 +++ thunderbird-78.6.1+build1/comm/mail/base/content/mainMailToolbox.inc.xhtml 2021-01-08 18:56:33.000000000 +0000 @@ -326,6 +326,7 @@ align="center" flex="1" class="chromeclass-toolbar-additional"> + + 0) { - let message = gFolderDisplay.selectedMessage; - let isMessageIndexed = Gloda.isMessageIndexed(message); - openConversation.disabled = !isMessageIndexed; + // Check because this menuitem element is not present in messageWindow.xhtml. + if (openConversation) { + openConversation.disabled = !glodaEnabled; + if (glodaEnabled && gFolderDisplay.selectedCount > 0) { + let message = gFolderDisplay.selectedMessage; + let isMessageIndexed = Gloda.isMessageIndexed(message); + openConversation.disabled = !isMessageIndexed; + } } if (SelectedMessagesAreRead()) { diff -Nru thunderbird-78.6.0+build1/comm/mail/base/modules/QuickFilterManager.jsm thunderbird-78.6.1+build1/comm/mail/base/modules/QuickFilterManager.jsm --- thunderbird-78.6.0+build1/comm/mail/base/modules/QuickFilterManager.jsm 2020-12-14 09:35:23.000000000 +0000 +++ thunderbird-78.6.1+build1/comm/mail/base/modules/QuickFilterManager.jsm 2021-01-08 18:56:33.000000000 +0000 @@ -1152,7 +1152,7 @@ let panel = aDocument.getElementById("qfb-text-search-upsell"); if ( (Services.focus.activeWindow != aDocument.defaultView || - aDocument.commandDispatcher.focusedElement != aNode) && + aDocument.commandDispatcher.focusedElement != aNode.inputField) && panel.state == "open" ) { panel.hidePopup(); @@ -1198,15 +1198,16 @@ }, reflectInDOM(aNode, aFilterValue, aDocument, aMuxer, aFromPFP) { + let panel = aDocument.getElementById("qfb-text-search-upsell"); + if (aFromPFP == "nosale") { - let panel = aDocument.getElementById("qfb-text-search-upsell"); if (panel.state != "closed") { panel.hidePopup(); } return; } + if (aFromPFP == "upsell") { - let panel = aDocument.getElementById("qfb-text-search-upsell"); let line1 = aDocument.getElementById("qfb-upsell-line-one"); let line2 = aDocument.getElementById("qfb-upsell-line-two"); line1.value = line1.getAttribute("fmt").replace("#1", aFilterValue.text); @@ -1214,30 +1215,34 @@ if ( panel.state == "closed" && - aDocument.commandDispatcher.focusedElement == aNode + aDocument.commandDispatcher.focusedElement == aNode.inputField ) { - let filterBar = aDocument.getElementById("quick-filter-bar"); - // panel.sizeTo(filterBar.clientWidth - 20, filterBar.clientHeight - 20); - panel.openPopup(filterBar, "after_end", -7, 7, false, true); + panel.openPopup( + aDocument.getElementById("quick-filter-bar"), + "after_end", + -7, + 7, + false, + true + ); } return; } // Make sure we have no visible upsell on state change while our textbox - // retains focus. - let panel = aDocument.getElementById("qfb-text-search-upsell"); + // retains focus. if (panel.state != "closed") { panel.hidePopup(); } // Update the text if it has changed (linux does weird things with empty - // text if we're transitioning emptytext to emptytext) + // text if we're transitioning emptytext to emptytext). let desiredValue = aFilterValue.text || ""; if (aNode.value != desiredValue) { aNode.value = desiredValue; } - // Update our expando buttons + // Update our expanded filters buttons. let states = aFilterValue.states; for (let name in this.textFilterDefs) { let textFilter = this.textFilterDefs[name]; @@ -1245,7 +1250,7 @@ states[textFilter.name]; } - // Show the expando? + // Toggle the expanded filters visibility. aDocument.getElementById("quick-filter-bar-filter-text-bar").collapsed = aFilterValue.text == null; }, diff -Nru thunderbird-78.6.0+build1/comm/mail/components/accountcreation/content/createInBackend.js thunderbird-78.6.1+build1/comm/mail/components/accountcreation/content/createInBackend.js --- thunderbird-78.6.0+build1/comm/mail/components/accountcreation/content/createInBackend.js 2020-12-14 09:35:23.000000000 +0000 +++ thunderbird-78.6.1+build1/comm/mail/components/accountcreation/content/createInBackend.js 2021-01-08 18:56:33.000000000 +0000 @@ -94,8 +94,14 @@ // STARTTLS inServer.socketType = Ci.nsMsgSocketType.alwaysSTARTTLS; } - // inServer.prettyName = config.displayName; - inServer.prettyName = config.identity.emailAddress; + + // If we already have an account with an identical name, generate a unique + // name for the new account to avoid duplicates. + inServer.prettyName = checkAccountNameAlreadyExists( + config.identity.emailAddress + ) + ? generateUniqueAccountName(config) + : config.identity.emailAddress; inServer.doBiff = true; inServer.biffMinutes = config.incoming.checkInterval; @@ -386,6 +392,46 @@ } /** + * Check whether the user's setup already has an account with the same email + * address. This might happen if the user uses the same email for different + * protocols (eg. IMAP and POP3). + * + * @param {string} name - The name or email address of the new account. + * @returns {boolean} True if an account with the same name is found. + */ +function checkAccountNameAlreadyExists(name) { + return MailServices.accounts.accounts.some( + a => a.incomingServer.prettyName == name + ); +} + +/** + * Generate a unique account name by appending the incoming protocol type, and + * a counter if necessary. + * + * @param {AccountConfig} config - The config data of the account being created. + * @returns {string} - The unique account name. + */ +function generateUniqueAccountName(config) { + // Generate a potential unique name. e.g. "foo@bar.com (POP3)". + let name = `${ + config.identity.emailAddress + } (${config.incoming.type.toUpperCase()})`; + + // If this name already exists, append a counter until we find a unique name. + if (checkAccountNameAlreadyExists(name)) { + let counter = 2; + while (checkAccountNameAlreadyExists(`${name}_${counter}`)) { + counter++; + } + // e.g. "foo@bar.com (POP3)_1". + name = `${name}_${counter}`; + } + + return name; +} + +/** * Check if there already is a "Local Folders". If not, create it. * Copied from AccountWizard.js with minor updates. */ diff -Nru thunderbird-78.6.0+build1/comm/mail/components/accountcreation/content/exchangeAutoDiscover.js thunderbird-78.6.1+build1/comm/mail/components/accountcreation/content/exchangeAutoDiscover.js --- thunderbird-78.6.0+build1/comm/mail/components/accountcreation/content/exchangeAutoDiscover.js 2020-12-14 09:35:23.000000000 +0000 +++ thunderbird-78.6.1+build1/comm/mail/components/accountcreation/content/exchangeAutoDiscover.js 2021-01-08 18:56:33.000000000 +0000 @@ -99,6 +99,7 @@ readAutoDiscoverResponse( xml, successive, + emailAddress, username, password, config => { @@ -235,6 +236,7 @@ function readAutoDiscoverResponse( autoDiscoverXML, successive, + emailAddress, username, password, successCallback, @@ -246,12 +248,12 @@ // redirect to other email address if ( - "Action" in autoDiscoverXML.Autodiscover.Response && - "Redirect" in autoDiscoverXML.Autodiscover.Response.Action + "Account" in autoDiscoverXML.Autodiscover.Response && + "RedirectAddr" in autoDiscoverXML.Autodiscover.Response.Account ) { - // + // let redirectEmailAddress = sanitize.emailAddress( - autoDiscoverXML.Autodiscover.Response.Action.Redirect + autoDiscoverXML.Autodiscover.Response.Account.RedirectAddr ); let domain = redirectEmailAddress.split("@").pop(); if (++gLoopCounter > 2) { @@ -260,11 +262,14 @@ successive.current = fetchConfigFromExchange( domain, redirectEmailAddress, - username, + // Per spec, need to authenticate with the original email address, + // not the redirected address (if not already overridden). + username || emailAddress, password, successCallback, errorCallback ); + return; } let config = readAutoDiscoverXML(autoDiscoverXML, username); diff -Nru thunderbird-78.6.0+build1/comm/mail/components/accountcreation/content/sanitizeDatatypes.js thunderbird-78.6.1+build1/comm/mail/components/accountcreation/content/sanitizeDatatypes.js --- thunderbird-78.6.0+build1/comm/mail/components/accountcreation/content/sanitizeDatatypes.js 2020-12-14 09:35:23.000000000 +0000 +++ thunderbird-78.6.1+build1/comm/mail/components/accountcreation/content/sanitizeDatatypes.js 2021-01-08 18:56:33.000000000 +0000 @@ -113,6 +113,19 @@ return str.toLowerCase(); }, + + /** + * A value which resembles an email address. + */ + emailAddress(unchecked) { + let str = this.nonemptystring(unchecked); + if (!/^[a-z0-9\-%+_\.\*]+@[a-z0-9\-\.]+\.[a-z]+$/i.test(str)) { + throw new MalformedException("emailaddress_syntax.error", unchecked); + } + + return str.toLowerCase(); + }, + /** * A non-chrome URL that's safe to request. */ diff -Nru thunderbird-78.6.0+build1/comm/mail/components/addrbook/content/addressbook.xhtml thunderbird-78.6.1+build1/comm/mail/components/addrbook/content/addressbook.xhtml --- thunderbird-78.6.0+build1/comm/mail/components/addrbook/content/addressbook.xhtml 2020-12-14 09:35:23.000000000 +0000 +++ thunderbird-78.6.1+build1/comm/mail/components/addrbook/content/addressbook.xhtml 2021-01-08 18:56:33.000000000 +0000 @@ -381,7 +381,7 @@ #endif - @@ -2586,6 +2586,7 @@