Subject: [sup-talk] New User Questions

From: [email-masked] (Marc Hartstein)
Date: Thu, 27 Mar 2008 20:22:09 -0400

Sorry if any of this has been discussed elsewhere.  I've read the docs
on the website, skimmed the wiki, and skimmed the mailing list archives
for the past few months, but I could easily have missed something.

First, I ran into the same problem discussed in the thread "startup
crash" started on 2008-03-09.  (I'm also running Gentoo and installed
the latest from portage)  I poked around the source a bit, and it
seems that when AccountManager.initialize is called out of
Redwood::start (sup line 113), the code to set up the default
configuration (lib/sup.rb, starts at line 166) has *not* been called, so
the $config object is empty.

Just in case the information helps; I'm not a Ruby person (yet), and I
didn't poke around enough to be clear on how it's supposed to be
working.

2. sup is pretty cool.  I'm particularly intrigued by this tag (sorry,
label) concept.

3. Is there an IRC channel?  It would be nice to be able to lurk and
drop questions if anybody happens to be around.

4. I use maildir.  Is there any way sup can mark messages as read (and,
perhaps more importantly, no longer new) once I've read them and hit
'$'?  I can deal with mutt being confused (I know sup doesn't play well
with others anyway), but it's really confusing my mail monitor, and
that's bugging me.

5. Can sup be set to automatically poll local sources for new messages
every n seconds?  Mutt does this, and it's nice not having to tell it
that I know there are new messages and it should go find them.

6. Mutt can pass text/html attachments through an external program and
display the result in its internal pager.  Is there a way to get sup to
do the equivalent?

7. How does sup choose the email address to use as "From" when replying
to an email?  It's definitely choosing the wrong thing in my setup.  My
primary address forwards to gmail, sup is configured with the primary as
my :email:, but whenever I hit reply it sets "From" to be my gmail
address.  I'd like it to be whatever alternate of mine is in To/CC if
any, and my primary address otherwise.

8. Is there any way to specify custom keybindings in a configuration
file, or would it require hacking the source?

8.1 I'd really like a one-touch reply-all like I had in mutt.

9. Is there a way to default to making all messages PGP-signed?

10. Is there (planned) a compose-hook?  I'd like to be able to switch to
sign-and-encrypt when I have a public key for every recipient.

Thanks for the cool project.  I'll probably have more "I miss this
feature from mutt" thoughts in the future, at least if I don't go
running back.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 189 bytes
Desc: not available
Url : http://rubyforge.org/pipermail/sup-talk/attachments/20080327/ca129127/attachment.bin

---

From: [email-masked] (Marcus Williams)
Date: Fri, 28 Mar 2008 10:33:45 +0000

> drop questions if anybody happens to be around.

Someone has suggested #sup on freenode but no takers so far. I'd
probably lurk on a channel if we can agree on one :)

> with others anyway), but it's really confusing my mail monitor, and

Technically not (although you could probably write a hook to do this).
However, you could change the way your monitor works if you're using
sup locally. See the before/after poll hooks [1]

> that I know there are new messages and it should go find them.

It should do already. I dont know offhand what the delay is though.

> display the result in its internal pager.  Is there a way to get sup to

Theres more than one way to do this. Via a hook for auto-demangling
(mime-decode.rb) [1] and via the pipe attachment/message keypress '|'
in thread view mode. Basically if you want to view an attachment put
your cursor line over the attachment line and hit '|'. sup will pipe
the attachment to what ever command line you type in here. html2text
works well (as does antiword for word docs). If your cursor is in the
message somewhere other than an attachment line it will pipe the raw
message to the command.

> any, and my primary address otherwise.

The :alternates: setting is probably what you want here. I think you
should just have to add your gmail address as an alternate in your
default account. At least this is how I think it should work!
Basically the alternate emails list tell sup you receive email at this
account as these addresses as well, but that you reply using the
default :email: setting.

You may also be interested in the :regexen: list - this allows you to
setup mail extensions (like marcus-sup that I use) and get sup to
reply using the extension.

> file, or would it require hacking the source?

Hack the source afaik

> 8.1 I'd really like a one-touch reply-all like I had in mutt.

I'm about to submit a patch for this.

> 9. Is there a way to default to making all messages PGP-signed?

I dont think so at the moment (again, it wouldnt be hard to add)

> 10. Is there (planned) a compose-hook?  I'd like to be able to
> switch to sign-and-encrypt when I have a public key for every
> recipient.

Theres certainly a before-edit hook, which you may be able to do
things like this in. I havnt played with the pgp stuff yet so someone
else probably knows more here.

HTH 

Marcus

[1] http://sup.rubyforge.org/wiki/wiki.pl?Hooks

---

From: [email-masked] (vasudeva)
Date: Fri, 28 Mar 2008 07:24:47 -0400

> probably lurk on a channel if we can agree on one :)

Actually, there are a couple of us lurking there now. Come on in.

-- 
linkswarm.com :: Collaborative Insolence
vasudeva.linkswarm.com/gallery :: For The Faint of Heart

---

From: [email-masked] (Marc Hartstein)
Date: Fri, 28 Mar 2008 13:23:29 -0400

> It should do already. I dont know offhand what the delay is though.

So it does, I think it's just less frequent than I'm accustomed to.
This should probably go on the list of things which should eventually be
configurable.

> (mime-decode.rb)

Thanks, that's exactly what I was looking for.  And should be nicely
extensible if I ever want.

> and via the pipe attachment/message keypress '|' in thread view mode.
> Basically if you want to view an attachment put your cursor line over
> the attachment line and hit '|'. sup will pipe the attachment to what
> ever command line you type in here.

Probably too much effort unless it can consult mailcap or something and
fill in an appropriate default.  I wonder how difficult that would be to
add....

> account as these addresses as well, but that you reply using the

Nope.  The gmail address is an alternate, the primary is the :email:, it
tries to reply with the gmail address.  But only sometimes.  I still
haven't figured out what's causing it.

> reply using the extension.

I'll look into this, thanks.

> > file, or would it require hacking the source?

Feh, hope that's on the planned features list.

> I dont think so at the moment (again, it wouldnt be hard to add)

Should probably be a config setting.

> else probably knows more here.

That one's definitely not on the wiki.  I'll have to track down the
canonical list of available hooks...
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 189 bytes
Desc: not available
Url : http://rubyforge.org/pipermail/sup-talk/attachments/20080328/7a51264e/attachment.bin

---

From: [email-masked] (Marcus Williams)
Date: Fri, 28 Mar 2008 19:51:18 +0000

> canonical list of available hooks...

sup --list-hooks

:)

Marcus

---

From: [email-masked] (Christopher Warrington)
Date: Sat, 29 Mar 2008 02:57:51 -0500

Each source has a poll_interval property. This property is used to
determine whether the source should be polled. The user can still for a
poll of all sources.
---
 bin/sup                    |    4 +-
 bin/sup-add                |   12 ++-
 lib/sup.rb                 |    4 +-
 lib/sup/imap.rb            |    6 +-
 lib/sup/maildir.rb         |    6 +-
 lib/sup/mbox/loader.rb     |    4 +-
 lib/sup/modes/poll-mode.rb |    4 +-
 lib/sup/poll.rb            |  211 +++++++++++++++++++++++++++++++++-----------
 lib/sup/source.rb          |    8 +-
 9 files changed, 189 insertions(+), 70 deletions(-)
 mode change 100755 => 100644 lib/sup/buffer.rb

diff --git a/bin/sup b/bin/sup
index 84fd77c..86a2d9f 100644
--- a/bin/sup
+++ b/bin/sup
@@ -197,7 +197,7 @@ begin
     end
   end unless $opts[:no_initial_poll]
   
-  imode.load_threads :num => ibuf.content_height, :when_done => lambda { reporting_thread("poll after loading inbox") { sleep 1; PollManager.poll } unless $opts[:no_threads] || $opts[:no_initial_poll] }
+  imode.load_threads :num => ibuf.content_height, :when_done => lambda { reporting_thread("poll after loading inbox") { sleep 1; PollManager.auto_poll } unless $opts[:no_threads] || $opts[:no_initial_poll] }
 
   if $opts[:compose]
     ComposeMode.spawn_nicely :to_default => $opts[:compose]
@@ -263,7 +263,7 @@ begin
     when :compose
       ComposeMode.spawn_nicely
     when :poll
-      reporting_thread("user-invoked poll") { PollManager.poll }
+      reporting_thread("user-invoked poll") { PollManager.forced_poll }
     when :recall_draft
       case Index.num_results_for :label => :draft
       when 0
diff --git a/bin/sup-add b/bin/sup-add
index 50bbb29..88705e1 100644
--- a/bin/sup-add
+++ b/bin/sup-add
@@ -39,6 +39,7 @@ EOS
   opt :unusual, "Do not automatically poll these sources for new messages."
   opt :labels, "A comma-separated set of labels to apply to all messages from this source", :type => String
   opt :force_new, "Create a new account for this source, even if one already exists."
+  opt :poll_interval, "The interval (in seconds) between new message polls. The default is #{Redwood::DEFAULT_POLL_INTERVAL}.", :type => :int
 end
 
 Trollop::die "require one or more sources" if ARGV.empty?
@@ -84,6 +85,9 @@ index.lock_or_die
 begin
   index.load_sources
 
+  Trollop::die "The poll interval must be a positive integer." if $opts[:poll_interval] <= 0
+  poll_interval = $opts[:poll_interval] || Redwood::DEFAULT_POLL_INTERVAL
+
   ARGV.each do |uri|
     labels = $opts[:labels] ? $opts[:labels].split(/\s*,\s*/).uniq : []
 
@@ -100,14 +104,14 @@ begin
         say "For SSH connections, if you will use public key authentication, you may leave the username and password blank."
         say ""
         username, password = get_login_info uri, index.sources
-        Redwood::MBox::SSHLoader.new uri, username, password, nil, !$opts[:unusual], $opts[:archive], nil, labels
+        Redwood::MBox::SSHLoader.new uri, username, password, nil, !$opts[:unusual], $opts[:archive], nil, poll_interval, labels
       when "imap", "imaps"
         username, password = get_login_info uri, index.sources
-        Redwood::IMAP.new uri, username, password, nil, !$opts[:unusual], $opts[:archive], nil, labels
+        Redwood::IMAP.new uri, username, password, nil, !$opts[:unusual], $opts[:archive], nil, poll_interval, labels
       when "maildir"
-        Redwood::Maildir.new uri, nil, !$opts[:unusual], $opts[:archive], nil, labels
+        Redwood::Maildir.new uri, nil, !$opts[:unusual], $opts[:archive], nil, poll_interval, labels
       when "mbox"
-        Redwood::MBox::Loader.new uri, nil, !$opts[:unusual], $opts[:archive], nil, labels
+        Redwood::MBox::Loader.new uri, nil, !$opts[:unusual], $opts[:archive], nil, poll_interval, labels
       when nil
         Trollop::die "Sources must be specified with an URI"
       else
diff --git a/lib/sup.rb b/lib/sup.rb
index 1946f3c..c27a4bc 100644
--- a/lib/sup.rb
+++ b/lib/sup.rb
@@ -50,6 +50,8 @@ module Redwood
   YAML_DOMAIN = "masanjin.net"
   YAML_DATE = "2006-10-01"
 
+  DEFAULT_POLL_INTERVAL = 300
+
 ## record exceptions thrown in threads nicely
   def reporting_thread name
     if $opts[:no_threads]
@@ -72,7 +74,7 @@ module Redwood
   def save_yaml_obj object, fn, safe=false
     if safe
       safe_fn = "#{File.dirname fn}/safe_#{File.basename fn}"
-      mode = File.stat(fn) if File.exists? fn
+      mode = File.stat(fn).mode if File.exists? fn
       File.open(safe_fn, "w", mode) { |f| f.puts object.to_yaml }
       FileUtils.mv safe_fn, fn
     else
diff --git a/lib/sup/buffer.rb b/lib/sup/buffer.rb
old mode 100755
new mode 100644
diff --git a/lib/sup/imap.rb b/lib/sup/imap.rb
index 1d36976..8b58cba 100644
--- a/lib/sup/imap.rb
+++ b/lib/sup/imap.rb
@@ -51,13 +51,13 @@ class IMAP < Source
 
   attr_accessor :username, :password
   yaml_properties :uri, :username, :password, :cur_offset, :usual,
-                  :archived, :id, :labels
+                  :archived, :id, :poll_interval, :labels
 
-  def initialize uri, username, password, last_idate=nil, usual=true, archived=false, id=nil, labels=[]
+  def initialize uri, username, password, last_idate=nil, usual=true, archived=false, id=nil, poll_interval=nil, labels=[]
     raise ArgumentError, "username and password must be specified" unless username && password
     raise ArgumentError, "not an imap uri" unless uri =~ %r!imaps?://!
 
-    super uri, last_idate, usual, archived, id
+    super uri, last_idate, usual, archived, id, [poll_interval, SCAN_INTERVAL].max
 
     @parsed_uri = URI(uri)
     @username = username
diff --git a/lib/sup/maildir.rb b/lib/sup/maildir.rb
index 584e657..ef71e0b 100644
--- a/lib/sup/maildir.rb
+++ b/lib/sup/maildir.rb
@@ -12,9 +12,9 @@ class Maildir < Source
   SCAN_INTERVAL = 30 # seconds
 
   ## remind me never to use inheritance again.
-  yaml_properties :uri, :cur_offset, :usual, :archived, :id, :labels
-  def initialize uri, last_date=nil, usual=true, archived=false, id=nil, labels=[]
-    super uri, last_date, usual, archived, id
+  yaml_properties :uri, :cur_offset, :usual, :archived, :id, :poll_interval, :labels
+  def initialize uri, last_date=nil, usual=true, archived=false, id=nil, poll_interval=nil, labels=[]
+    super uri, last_date, usual, archived, id, [poll_interval, SCAN_INTERVAL].max
     uri = URI(Source.expand_filesystem_uri(uri))
 
     raise ArgumentError, "not a maildir URI" unless uri.scheme == "maildir"
diff --git a/lib/sup/mbox/loader.rb b/lib/sup/mbox/loader.rb
index 7fe9129..44317d5 100644
--- a/lib/sup/mbox/loader.rb
+++ b/lib/sup/mbox/loader.rb
@@ -9,7 +9,7 @@ class Loader < Source
   attr_accessor :labels
 
   ## uri_or_fp is horrific. need to refactor.
-  def initialize uri_or_fp, start_offset=nil, usual=true, archived=false, id=nil, labels=[]
+  def initialize uri_or_fp, start_offset=nil, usual=true, archived=false, id=nil, poll_interval=nil, labels=[]
     @mutex = Mutex.new
     @labels = ((labels || []) - LabelManager::RESERVED_LABELS).uniq.freeze
 
@@ -26,7 +26,7 @@ class Loader < Source
       @path = uri_or_fp.path
     end
 
-    super uri_or_fp, start_offset, usual, archived, id
+    super uri_or_fp, start_offset, usual, archived, id, poll_interval
   end
 
   def file_path; @path end
diff --git a/lib/sup/modes/poll-mode.rb b/lib/sup/modes/poll-mode.rb
index 5849f3e..5521cdc 100644
--- a/lib/sup/modes/poll-mode.rb
+++ b/lib/sup/modes/poll-mode.rb
@@ -10,11 +10,11 @@ class PollMode < LogMode
     self << s + "\n"
   end
 
-  def poll
+  def poll sources
     puts unless @new
     @new = false
     puts "Poll started at #{Time.now}"
-    PollManager.do_poll { |s| puts s }
+    PollManager.do_poll_sources(sources) { |s| puts s }
   end
 end
 
diff --git a/lib/sup/poll.rb b/lib/sup/poll.rb
index d32c893..01db8e2 100644
--- a/lib/sup/poll.rb
+++ b/lib/sup/poll.rb
@@ -12,13 +12,22 @@ Variables:
 EOS
 
   HookManager.register "before-poll", <<EOS
+Executes immediately before any poll for new messages commences.
+Variables:
+                 accts: an array of source URIs that will be polled
+EOS
+
+  HookManager.register "before-acct-poll", <<EOS
 Executes immediately before a poll for new messages commences.
-No variables.
+Variables:
+                  acct: the URI of the account being polled
 EOS
 
-  HookManager.register "after-poll", <<EOS
-Executes immediately after a poll for new messages completes.
+  HookManager.register "after-acct-poll", <<EOS
+Executes immediately after a poll for new messages completes. The statistics are
+only for the account specified.
 Variables:
+                  acct: the URI of the account being polled
                    num: the total number of new messages added in this poll
              num_inbox: the number of new messages added in this poll which
                         appear in the inbox (i.e. were not auto-archived).
@@ -28,14 +37,25 @@ num_inbox_total_unread: the total number of unread messages in the inbox
                         only those messages appearing in the inbox
 EOS
 
-  DELAY = 300
+  HookManager.register "after-poll", <<EOS
+Executes immediately after any poll for new messages completes. The statistics
+are for all sources polled.
+Variables:
+                 accts: an array of source URIs that were polled
+                   num: the total number of new messages added in this poll
+             num_inbox: the number of new messages added in this poll which
+                        appear in the inbox (i.e. were not auto-archived).
+num_inbox_total_unread: the total number of unread messages in the inbox
+         from_and_subj: an array of (from email address, subject) pairs
+   from_and_subj_inbox: an array of (from email address, subject) pairs for
+                        only those messages appearing in the inbox
+EOS
 
   def initialize
     @mutex = Mutex.new
     @thread = nil
-    @last_poll = nil
     @polling = false
-    
+
     self.class.i_am_the_instance self
   end
 
@@ -44,47 +64,113 @@ EOS
     b
   end
 
-  def poll
+  def start
+    ## Periodically calls auto_poll, effectively automatically polling
+    ## in the background.
+    ##
+    ## auto_poll is called more frequently than the smallest poll
+    ## interval. auto_poll is not guaranteed to be called in multiples
+    ## of poll intervals. I.e., don't rely on auto_poll to be called
+    ## every x seconds: when auto_poll is called assume an
+    ## indeterminate amount of time has passed.
+    @min_poll_interval = Index.usual_sources.collect{|s| s.poll_interval}.select{|pi| !pi.nil?}.min || Redwood::DEFAULT_POLL_INTERVAL
+
+    Redwood::log "Background poll interval is #{@min_poll_interval} seconds."
+
+    @thread = Redwood::reporting_thread("periodic poll") do
+      while true
+        Redwood::log "Sleeping for #{@min_poll_interval / 2} seconds."
+
+        sleep @min_poll_interval / 2
+        auto_poll
+      end
+    end
+  end
+
+  def stop
+    @thread.kill if @thread
+    @thread = nil
+  end
+
+  def auto_poll
+    ## This method is called by the thread spawned in start. It
+    ## collects the sources that should be polled
+    ## (source.poll_interval has expired or the source has never been
+    ## polled) and polls them.
+    ##
+    ## Returns an array [# of new messages, # of new messages to
+    ## inbox, new message subject/name array pairs, subject/name array
+    ## pairs loaded to inbox]
+
+    sources_to_poll = []
+    @mutex.synchronize do # William, do we need to synchronize here?
+      begin
+        sources_to_poll = Index.usual_sources.select do |source|
+          begin
+            source.last_poll.nil? || (Time.now - source.last_poll) >= source.poll_interval
+          end
+        end
+      end
+    end
+
+    poll_sources sources_to_poll
+  end
+
+  def forced_poll
+    ## This method is called when the user explicitly requests a poll.
+    ##
+    ## Returns an array [# of new messages, # of new messages to
+    ## inbox, new message subject/name array pairs, subject/name array
+    ## pairs loaded to inbox]
+    poll_sources Index.usual_sources
+  end
+
+  def poll_sources sources
+    ## Polls the given sources. Clients of PollManager should call
+    ## this method, not do_poll_sources or poll_source. (Well, only
+    ## PollMode should call do_poll_sources)
+    ##
+    ## Returns an array [# of new messages, # of new messages to
+    ## inbox, new message subject/name array pairs, subject/name array
+    ## pairs loaded to inbox]
     return if @polling
     @polling = true
-    HookManager.run "before-poll"
+
+    source_uris = sources.map{|s| s.uri}
+
+    HookManager.run "before-acct-poll", :accts => source_uris
 
     BufferManager.flash "Polling for new messages..."
-    num, numi, from_and_subj, from_and_subj_inbox = buffer.mode.poll
+    num, numi, from_subj, from_subj_inbox = buffer.mode.poll sources
     if num > 0
       BufferManager.flash "Loaded #{num.pluralize 'new message'}, #{numi} to inbox." 
     else
       BufferManager.flash "No new messages." 
     end
 
-    HookManager.run "after-poll", :num => num, :num_inbox => numi, :from_and_subj => from_and_subj, :from_and_subj_inbox => from_and_subj_inbox, :num_inbox_total_unread => lambda { Index.num_results_for :labels => [:inbox, :unread] }
+    HookManager.run "after-poll", :accts => source_uris, :num => num, :num_inbox => numi, :from_and_subj => from_and_subj, :from_and_subj_inbox => from_and_subj_inbox, :num_inbox_total_unread => lambda { Index.num_results_for :labels => [:inbox, :unread] }
 
     @polling = false
-    [num, numi]
-  end
-
-  def start
-    @thread = Redwood::reporting_thread("periodic poll") do
-      while true
-        sleep DELAY / 2
-        poll if @last_poll.nil? || (Time.now - @last_poll) >= DELAY
-      end
-    end
+    [num, numi, from_subj, from_subj_inbox]
   end
 
-  def stop
-    @thread.kill if @thread
-    @thread = nil
-  end
+  def do_poll_sources sources, &block
+    ## Polls each source, keeping track of vital statistics (number
+    ## loaded, name/e-mail pairs, &c.) about the poll results.
+    ##
+    ## We need explicit access to the block so that we can pass it to
+    ## poll_source.
+    ##
+    ## Returns an array [total # of new messages, total # of new
+    ## messages to inbox, all new message subject/name array pairs,
+    ## all subject/name array pairs loaded to inbox]
 
-  def do_poll
     total_num = total_numi = 0
-    from_and_subj = []
-    from_and_subj_inbox = []
+    total_from_and_subj = []
+    total_from_and_subj_inbox = []
 
     @mutex.synchronize do
-      Index.usual_sources.each do |source|
-#        yield "source #{source} is done? #{source.done?} (cur_offset #{source.cur_offset} >= #{source.end_offset})"
+      sources.each do |source|
         begin
           yield "Loading from #{source}... " unless source.done? || source.has_errors?
         rescue SourceError => e
@@ -93,32 +179,57 @@ EOS
           next
         end
 
-        num = 0
-        numi = 0
-        add_messages_from source do |m, offset, entry|
-          ## always preserve the labels on disk.
-          m.labels = entry[:label].split(/\s+/).map { |x| x.intern } if entry
-          yield "Found message at #{offset} with labels {#{m.labels * ', '}}"
-          unless entry
-            num += 1
-            from_and_subj << [m.from.longname, m.subj]
-            if m.has_label?(:inbox) && ([:spam, :deleted, :killed] & m.labels).empty?
-              from_and_subj_inbox << [m.from.longname, m.subj]
-              numi += 1 
-            end
-          end
-          m
-        end
-        yield "Found #{num} messages, #{numi} to inbox." unless num == 0
+        HookManager.run "before-acct-poll", :acct => source.uri
+
+        BufferManager.flash "Polling #{source.uri} for new messages..."
+        num, numi, from_and_subj, from_and_subj_inbox = poll_source source, &block
+
         total_num += num
         total_numi += numi
+        total_from_and_subj += from_and_subj
+        total_from_and_subj_inbox += from_and_subj_inbox
+
+        HookManager.run "after-acct-poll", :acct => source.uri, :num => num, :num_inbox => numi, :from_and_subj => from_and_subj, :from_and_subj_inbox => from_and_subj_inbox, :num_inbox_total_unread => lambda { Index.num_results_for :labels => [:inbox, :unread] }
       end
+    end
+
+    [total_num, total_numi, total_from_and_subj, total_from_and_subj_inbox]
+  end
 
-      yield "Done polling; loaded #{total_num} new messages total"
-      @last_poll = Time.now
-      @polling = false
+  def poll_source source
+    ## Polls the given source for new messages.
+    ##
+    ## @mutex must be held before calling! See do_poll_sources.
+    ##
+    ## Returns an array [# of new messages, # of new messages to
+    ## inbox, new message subject/name array pairs, subject/name array
+    ## pairs loaded to inbox]
+
+    num = 0
+    numi = 0
+    from_and_subj = []
+    from_and_subj_inbox = []
+
+    add_messages_from source do |m, offset, entry|
+      ## always preserve the labels on disk.
+      m.labels = entry[:label].split(/\s+/).map { |x| x.intern } if entry
+      yield "Found message at #{offset} with labels {#{m.labels * ', '}}"
+      unless entry
+        num += 1
+        from_and_subj << [m.from.longname, m.subj]
+        if m.has_label?(:inbox) && ([:spam, :deleted, :killed] & m.labels).empty?
+          from_and_subj_inbox << [m.from.longname, m.subj]
+          numi += 1
+        end
+      end
+      m
     end
-    [total_num, total_numi, from_and_subj, from_and_subj_inbox]
+
+    yield "For source #{source.uri}, found #{num} messages, #{numi} to inbox." unless num == 0
+
+    source.last_poll = Time.now
+
+    [num, numi, from_and_subj, from_and_subj_inbox]
   end
 
   ## this is the main mechanism for adding new messages to the
diff --git a/lib/sup/source.rb b/lib/sup/source.rb
index 6510aae..fd8d381 100644
--- a/lib/sup/source.rb
+++ b/lib/sup/source.rb
@@ -62,10 +62,10 @@ class Source
   ## dirty? means cur_offset has changed, so the source info needs to
   ## be re-saved to sources.yaml.
   bool_reader :usual, :archived, :dirty
-  attr_reader :uri, :cur_offset
-  attr_accessor :id
+  attr_reader :uri, :cur_offset, :poll_interval
+  attr_accessor :id, :last_poll
 
-  def initialize uri, initial_offset=nil, usual=true, archived=false, id=nil
+  def initialize uri, initial_offset=nil, usual=true, archived=false, id=nil, poll_interval=nil
     raise ArgumentError, "id must be an integer: #{id.inspect}" unless id.is_a? Fixnum if id
 
     @uri = uri
@@ -73,6 +73,8 @@ class Source
     @usual = usual
     @archived = archived
     @id = id
+    @poll_interval = poll_interval || Redwood::DEFAULT_POLL_INTERVAL #seconds
+    @last_poll = nil
     @dirty = false
   end
 
-- 
1.5.4

---

From: [email-masked] (Christopher Warrington)
Date: Sat, 29 Mar 2008 04:59:31 -0500

Excerpts from Christopher Warrington's message of Sat Mar 29 02:57:51 -0500 2008:
> determine whether the source should be polled. The user can still
> for a poll of all sources.

There is a bug in this patch. Watch this space for an updated patch.

-- 
Christopher Warrington <chrisw at rice.edu>

---

From: [email-masked] (Christopher Warrington)
Date: Sat, 29 Mar 2008 05:32:05 -0500

if $opts[:compose]

 

 

     @parsed_uri = URI(uri)
     @username = username

 

 
   def file_path; @path end
 
-  def poll
+  def poll sources
     puts unless @new
     @new = false
 
diff --git a/lib/sup/poll.rb b/lib/sup/poll.rb
index d32c893..1ff8014 100644
--- a/lib/sup/poll.rb
+++ b/lib/sup/poll.rb
@@ -12,13 +12,22 @@ Variables:
 EOS

 
-  DELAY = 300
 
   def initialize
     @mutex = Mutex.new
     @thread = nil
-    @last_poll = nil
     @polling = false
-    
+
 
@@ -44,47 +64,113 @@ EOS
     b
   end
 
-  def poll
+  def start

-    HookManager.run "after-poll", :num => num, :num_inbox => numi, :from_and_subj => from_and_subj, :from_and_subj_inbox => from_and_subj_inbox, :num_inbox_total_unread => lambda { Index.num_results_for :labels => [:inbox, :unread] }
+    HookManager.run "after-poll", :accts => source_uris, :num => num, :num_inbox => numi, :from_and_subj => from_subj, :from_and_subj_inbox => from_subj_inbox, :num_inbox_total_unread => lambda { Index.num_results_for :labels => [:inbox, :unread] }
 
     @polling = false
-    [num, numi]
-  end
-
-  def start
 
-  def stop
 
-  def do_poll
 
     @mutex.synchronize do
 
-        num = 0
-        numi = 0

 
 
     @uri = uri
 
-- 
1.5.4

---

From: [email-masked] (Guarded Identity)
Date: Sat, 29 Mar 2008 11:41:14 -0500

Excerpts from Marcus Williams marcus-sup-at-bar-coded.net |Sup_Talk|'s message of Fri Mar 28 05:33:45
> > file, or would it require hacking the source?

I know I'd /definitely/ be interested in this kind of thing.  For the most
part, I'm really happy with current key bindings, but there's a few that bug
me.  For instance.  I wish 'l' and 'L' were reversed.  My rationale is that I
don't like the unshifted characters to do persistent things (just in-case I hit
the wrong key).  Maybe it's just me, but I hit 'l' sometimes, briefly believe
that Sup is hung, and then realize that I'm editing labels, and then Ctrl-G'd
out before accidentally committing the junk labels.

-Sukant

---

From: [email-masked] (Christopher Warrington)
Date: Sat, 29 Mar 2008 22:15:37 -0500

> for a poll of all sources.

This is the fixed patch. I've been running it all day now with Maildir
sources and it seems to be working. I'd love some feedback, especially
from those who use mbox or IMAP.

---

From: [email-masked] (Marc Hartstein)
Date: Thu, 17 Apr 2008 16:58:29 -0400

> determine whether the source should be polled. The user can still for a

Thanks for the patch.  Seems to work great.  Only one problem.

When this patch is applied and then sup is run against an old
sources.yaml (which doesn't provide the poll_interval property), sup
will crash on startup.

(Unfortunately, running git master, say to test a patch in development,
will clobber sources.yaml and remove this property, so I keep hitting
this problem.)
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 189 bytes
Desc: not available
Url : http://rubyforge.org/pipermail/sup-talk/attachments/20080417/6519cca4/attachment.bin

---

From: [email-masked] (Christopher Warrington)
Date: Thu, 17 Apr 2008 17:24:35 -0500

> Thanks for the patch.  Seems to work great.  Only one problem.

Well, there is another. The before-poll hook doesn't get the correct
arguments and before-acct-poll is run twice. I've fixed this locally
and will post a patch to the patch (or a unified path) once I figure
out how to do that in git.
 
> sources.yaml (which doesn't provide the poll_interval property), sup

Hmmm... I don't know enough about the yaml infrastructure to fix
this. Does anyone else know if there is a way to specify a default
value when one if not found in the yaml file. This would allow for
graceful upgrading and (in theory) keep the sources.yaml file backward
compatible.