From 0cf173b0753bbe7987cb220bd6230eb25fd084bb Mon Sep 17 00:00:00 2001 From: Marcus Williams Date: Tue, 29 Apr 2008 21:45:39 +0100 Subject: [PATCH] Gmail style attachment processing This patch adds: * the search terms "filename" and "filetype" * hidden/reserved attachment label to track attachments * new flag on thread index mode to denote an attachment '@' * a "has:attachment" search query. You can now search for something like "from:phil* filetype:pdf" for all messages from a person called phil with a pdf attachment. You can also specify a file name for the attachment with "filename:(this is a filename with spaces.txt)". You can use wildcards in the filename ("filename:test*.pdf"). You can search for all messages from someone with attachments using "from:someone has:attachment" It will require sup-sync -all to work against all sources, but attempts to be backwards compatable with non-synced sources. --- lib/sup/index.rb | 17 ++++++++++++++++- lib/sup/label.rb | 6 +++--- lib/sup/message.rb | 8 +++++++- lib/sup/modes/thread-index-mode.rb | 3 ++- 4 files changed, 28 insertions(+), 6 deletions(-) diff --git a/lib/sup/index.rb b/lib/sup/index.rb index f1ede55..12385d3 100644 --- a/lib/sup/index.rb +++ b/lib/sup/index.rb @@ -147,6 +147,7 @@ EOS field_infos.add_field :date, :index => :untokenized field_infos.add_field :body field_infos.add_field :label + field_infos.add_field :attachments field_infos.add_field :subject field_infos.add_field :from field_infos.add_field :to @@ -223,6 +224,7 @@ EOS :body => (entry[:body] || m.indexable_content), :snippet => snippet, # always override :label => labels.uniq.join(" "), + :attachments => (entry[:attachments] || m.attachments.uniq.join(" ")), :from => (entry[:from] || (m.from ? m.from.indexable_content : "")), :to => (entry[:to] || (m.to + m.cc + m.bcc).map { |x| x.indexable_content }.join(" ")), :subject => (entry[:subject] || wrap_subj(Message.normalize_subj(m.subj))), @@ -465,7 +467,7 @@ protected extraopts[:load_deleted] = true if subs =~ /\blabel:deleted\b/ ## gmail style "is" operator - subs = subs.gsub(/\b(is):(\S+)\b/) do + subs = subs.gsub(/\b(is|has):(\S+)\b/) do field, label = $1, $2 case label when "read" @@ -481,6 +483,19 @@ protected end end + ## gmail style attachments "filename" and "filetype" searches + subs = subs.gsub(/\b(filename|filetype):(\((.+?)\)\B|(\S+)\b)/) do + field, name = $1, ($3 || $4) + case field + when "filename" + Redwood::log "filename - translated #{field}:#{name} to attachments:(#{name.downcase})" + "attachments:(#{name.downcase})" + when "filetype" + Redwood::log "filetype - translated #{field}:#{name} to attachments:(*.#{name.downcase})" + "attachments:(*.#{name.downcase})" + end + end + if $have_chronic chronic_failure = false subs = subs.gsub(/\b(before|on|in|during|after):(\((.+?)\)\B|(\S+)\b)/) do diff --git a/lib/sup/label.rb b/lib/sup/label.rb index 4afc0f9..716ef98 100644 --- a/lib/sup/label.rb +++ b/lib/sup/label.rb @@ -5,13 +5,13 @@ class LabelManager ## labels that have special semantics. user will be unable to ## add/remove these via normal label mechanisms. - RESERVED_LABELS = [ :starred, :spam, :draft, :unread, :killed, :sent, :deleted, :inbox ] + RESERVED_LABELS = [ :starred, :spam, :draft, :unread, :killed, :sent, :deleted, :inbox, :attachment ] ## labels which it nonetheless makes sense to search for by - LISTABLE_RESERVED_LABELS = [ :starred, :spam, :draft, :sent, :killed, :deleted, :inbox ] + LISTABLE_RESERVED_LABELS = [ :starred, :spam, :draft, :sent, :killed, :deleted, :inbox, :attachment ] ## labels that will typically be hidden from the user - HIDDEN_RESERVED_LABELS = [ :starred, :unread ] + HIDDEN_RESERVED_LABELS = [ :starred, :unread, :attachment ] def initialize fn @fn = fn diff --git a/lib/sup/message.rb b/lib/sup/message.rb index 249b6c6..5fc91de 100644 --- a/lib/sup/message.rb +++ b/lib/sup/message.rb @@ -37,7 +37,7 @@ class Message DEFAULT_SENDER = "(missing sender)" attr_reader :id, :date, :from, :subj, :refs, :replytos, :to, :source, - :cc, :bcc, :labels, :list_address, :recipient_email, :replyto, + :cc, :bcc, :labels, :attachments, :list_address, :recipient_email, :replyto, :source_info, :list_subscribe, :list_unsubscribe bool_reader :dirty, :source_marked_read, :snippet_contains_encrypted_content @@ -54,6 +54,7 @@ class Message @dirty = false @encrypted = false @chunks = nil + @attachments = [] ## we need to initialize this. see comments in parse_header as to ## why. @@ -405,6 +406,11 @@ private ## if there's a filename, we'll treat it as an attachment. if filename + # add this to the attachments list if its not a generated html + # attachment (should we allow images with generated names?). + # Lowercase the filename because searches are easier that way + @attachments.push filename.downcase unless filename =~ /^sup-attachment-/ + add_label :attachment unless filename =~ /^sup-attachment-/ [Chunk::Attachment.new(m.header.content_type, filename, m, sibling_types)] ## otherwise, it's body text diff --git a/lib/sup/modes/thread-index-mode.rb b/lib/sup/modes/thread-index-mode.rb index d4aedbc..4c01c93 100644 --- a/lib/sup/modes/thread-index-mode.rb +++ b/lib/sup/modes/thread-index-mode.rb @@ -712,7 +712,8 @@ protected from + [ [subj_color, size_widget_text], - [:to_me_color, dp ? " >" : (p ? ' +' : " ")], + [:to_me_color, t.labels.member?(:attachment) ? "@" : " "], + [:to_me_color, dp ? ">" : (p ? '+' : " ")], [subj_color, t.subj + (t.subj.empty? ? "" : " ")], ] + (t.labels - @hidden_labels).map { |label| [:label_color, "+#{label} "] } + -- 2.45.2