mirror of
https://github.com/discourse/discourse.git
synced 2025-05-22 07:53:49 +08:00
FIX: IMAP sync email update uniqueness across groups and minor improvements (#10332)
Adds a imap_group_id column to IncomingEmail to deal with an issue where we were trying to update emails in the mailbox, calling IncomingEmail.where(imap_sync: true). However UID and UIDVALIDITY could be the same across accounts. So if group A used IMAP details for Gmail account A, and group B used IMAP details for Gmail account B, and both tried to sync changes to an email with UID of 3 (e.g. changing Labels), one account could affect the other. This even applied to Archiving! Also in this PR: * Fix error occurring if we do a uid_fetch and no emails are returned * Allow for creating labels within the target mailbox (previously we would not do this, only use existing labels) * Improve consistency for log messages * Add specs for generic IMAP provider (Gmail specs still to come) * Add custom archiving support for Gmail * Only use Message-ID for uniqueness of IncomingEmail if it was generated by us * Various refactors and improvements
This commit is contained in:
@ -4,13 +4,18 @@ module Imap
|
||||
module Providers
|
||||
class Gmail < Generic
|
||||
X_GM_LABELS = 'X-GM-LABELS'
|
||||
X_GM_THRID = 'X-GM-THRID'
|
||||
|
||||
def imap
|
||||
@imap ||= super.tap { |imap| apply_gmail_patch(imap) }
|
||||
end
|
||||
|
||||
def emails(uids, fields, opts = {})
|
||||
fields[fields.index('LABELS')] = X_GM_LABELS
|
||||
|
||||
# gmail has a special header for labels
|
||||
if fields.include?('LABELS')
|
||||
fields[fields.index('LABELS')] = X_GM_LABELS
|
||||
end
|
||||
|
||||
emails = super(uids, fields, opts)
|
||||
|
||||
@ -22,7 +27,7 @@ module Imap
|
||||
email['LABELS'].flatten!
|
||||
end
|
||||
|
||||
email['LABELS'] << '\\Inbox' if opts[:mailbox] == 'INBOX'
|
||||
email['LABELS'] << '\\Inbox' if @open_mailbox_name == 'INBOX'
|
||||
|
||||
email['LABELS'].uniq!
|
||||
end
|
||||
@ -57,6 +62,33 @@ module Imap
|
||||
super(tag)
|
||||
end
|
||||
|
||||
def archive(uid)
|
||||
# all emails in the thread must be archived in Gmail for the thread
|
||||
# to get removed from the inbox
|
||||
thread_id = thread_id_from_uid(uid)
|
||||
emails_to_archive = emails_in_thread(thread_id)
|
||||
emails_to_archive.each do |email|
|
||||
labels = email['LABELS']
|
||||
new_labels = labels.reject { |l| l == "\\Inbox" }
|
||||
store(email["UID"], "LABELS", labels, new_labels)
|
||||
end
|
||||
Imap::Sync::Logger.log("[IMAP] Thread ID #{thread_id} (UID #{uid}) archived in Gmail mailbox for #{@username}")
|
||||
end
|
||||
|
||||
def thread_id_from_uid(uid)
|
||||
fetched = imap.uid_fetch(uid, [X_GM_THRID])
|
||||
if !fetched
|
||||
raise "Thread not found for UID #{uid}!"
|
||||
end
|
||||
|
||||
fetched.last.attr[X_GM_THRID]
|
||||
end
|
||||
|
||||
def emails_in_thread(thread_id)
|
||||
uids_to_fetch = imap.uid_search("#{X_GM_THRID} #{thread_id}")
|
||||
emails(uids_to_fetch, ["UID", "LABELS"])
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def apply_gmail_patch(imap)
|
||||
|
Reference in New Issue
Block a user