]> git.notmuchmail.org Git - sup/commitdiff
Merge commit 'origin/unedited-newlines'
authorWilliam Morgan <wmorgan-sup@masanjin.net>
Sun, 25 May 2008 04:22:30 +0000 (21:22 -0700)
committerWilliam Morgan <wmorgan-sup@masanjin.net>
Sun, 25 May 2008 04:22:30 +0000 (21:22 -0700)
Conflicts:

lib/sup/modes/edit-message-mode.rb

17 files changed:
CONTRIBUTORS [new file with mode: 0644]
Manifest.txt
bin/sup
doc/TODO [deleted file]
lib/sup.rb
lib/sup/crypto.rb
lib/sup/keymap.rb
lib/sup/mbox.rb
lib/sup/modes/edit-message-mode.rb
lib/sup/modes/inbox-mode.rb
lib/sup/modes/label-list-mode.rb
lib/sup/modes/scroll-mode.rb
lib/sup/modes/thread-index-mode.rb
lib/sup/modes/thread-view-mode.rb
release-script.txt
test/test_mbox_parsing.rb [new file with mode: 0644]
www/index.html

diff --git a/CONTRIBUTORS b/CONTRIBUTORS
new file mode 100644 (file)
index 0000000..ec05a8e
--- /dev/null
@@ -0,0 +1,6 @@
+William Morgan <wmorgan-sup at the masanjin dot nets>
+Ismo Puustinen <ismo at the iki dot fis>
+Marcus Williams <marcus-sup at the bar-coded dot nets>
+Nicolas Pouillard <nicolas.pouillard at the gmail dot coms>
+Christopher Warrington <chrisw at the rice dot edus>
+Jeff Balogh <its.jeff.balogh at the gmail dot coms>
index 00f4b05e1de2d03a05577f73b906e1899a5670e3..6ecfe670d07649cf288cfadcfe78f3ce71cd4731 100644 (file)
@@ -1,3 +1,4 @@
+CONTRIBUTORS
 HACKING
 History.txt
 LICENSE
@@ -17,7 +18,6 @@ doc/FAQ.txt
 doc/Hooks.txt
 doc/NewUserGuide.txt
 doc/Philosophy.txt
-doc/TODO
 lib/sup.rb
 lib/sup/account.rb
 lib/sup/buffer.rb
diff --git a/bin/sup b/bin/sup
index 078a13405946dcd82ad9813c40ffd505ce014aab..723b1ed1e850b5eebadaf5804484793cc0a07b3f 100644 (file)
--- a/bin/sup
+++ b/bin/sup
@@ -7,7 +7,7 @@ require 'fileutils'
 require 'trollop'
 require "sup"
 
-BIN_VERSION = "0.5"
+BIN_VERSION = "git"
 
 unless Redwood::VERSION == BIN_VERSION
   $stderr.puts <<EOS
@@ -55,7 +55,8 @@ Thread.abort_on_exception = true # make debugging possible
 module Redwood
 
 global_keymap = Keymap.new do |k|
-  k.add :quit, "Quit Redwood", 'q'
+  k.add :quit_ask, "Quit Sup, but ask first", 'q'
+  k.add :quit_now, "Quit Sup immediately", 'Q'
   k.add :help, "Show help", 'H', '?'
   k.add :roll_buffers, "Switch to next buffer", 'b'
 #  k.add :roll_buffers_backwards, "Switch to previous buffer", 'B'
@@ -240,8 +241,12 @@ begin
       end
 
     case action
-    when :quit
+    when :quit_now
       break if bm.kill_all_buffers_safely
+    when :quit_ask
+      if bm.ask_yes_or_no "Really quit?"
+        break if bm.kill_all_buffers_safely
+      end
     when :help
       curmode = bm.focus_buf.mode
       bm.spawn_unless_exists("<help for #{curmode.name}>") { HelpMode.new curmode, global_keymap }
diff --git a/doc/TODO b/doc/TODO
deleted file mode 100644 (file)
index cd6251c..0000000
--- a/doc/TODO
+++ /dev/null
@@ -1,197 +0,0 @@
-for 0.5
--------
-_ mark thread as unread should remember the unread messages and mark
-  only them as unread, just like gmail
-_ bugfix: time zone parsing broken?
-_ need a better way to force an address to a particular name,
-  for things like evite addresses
-_ imap "add all folders on this server" option in sup-add
-_ for new message flashes, add new message counts until keypress
-_ bugfix: missing sources should be handled better
-_ search results: highlight relevant snippets and open to relevant
-  portion of thread
-_ have "notes" (treated as emails to oneself, never sent) as
-  first-class objects.
-_ make use of the username in URIs, and move account passwords to
-  a different file from sources.yaml. heck, allow encryption of that
-  file.
-
-for 0.4
--------
-_ bugfix in drafts: the entire thread is currently discarded, rather
-  than just the one message. (need to distinguish per-message and
-  per-thread deletion in the update messages.)
-_ bugfix in completion: capitalization for contact names
-_ imap: cache headers
-_ imap: share connection to the same server.
-_ dispatch-and-kill version of mark thread as unread in thread-view-mode
-_ forward for one or more tagged messages should attach them
-_ mbox: don't keep filehandles open, and protect all reads with dotlockfile
-_ bugfix: screwing with the headers when editing causes a crash
-
-future
-------
-_ ldbd support
-_ don't use a people.txt; store email addresses directly in the index. too many
-  problems with email addresses that occur with multiple names.
-_ infix match instead of prefix match for tab completion
-_ fix killed threads contributing to unread message count problem (prob. need
-  to maintain all killed message ids and our own unread message count for
-  inbox).
-_ emlx support (some os x thing)
-_ tab completion for mid-text cursors
-_ bugfix: not horizontal scrolling for ncurses text field entry
-_ use trac or something. this file is getting a little silly.
-_ saved searches
-_ bugfix: sometimes, when one new message comes into an imap folder,
-  we don't catch it until a reload. but we do see a message
-  indicating they're loaded to inbox (imap only? hard to reproduce.)
-_ bugfix: ferret flakiness: just added message but can't find it.
-   possibly a message id tokenization issue.
-_ bugfix: read before thread-index has finished loading then hides the
-  thread?!? wtf. (on jamie) (? still valid ?)
-_ bugfix: display field width in index-mode needs to be determined
-   per-character rather than per-byte
-_ select all, starred, to me, etc
-_ undo
-_ Net::SMTP support
-_ ruby-talk:XXXX detection (via hooks?)
-_ more control character support in buffer line editing
-_ mboxz, mboxbz
-_ swappable keymappings
-_ bugfix: when returning from a shelling out, sometime ncurses is
-   crazy and refuses to interpret any keystrokes
-_ configurable colors
-_ better batch deletion (extend to non-mbox sources)
-_ annotations on messages
-_ pop support
-_ toggleble wrapping of text
-_ maybe: de-archived messages auto-added to inbox
-_ prune old entries from people.txt so that it doesn't grow without
-   bound
-_ maildir+ssh
-
-maybe
------
-_ split out threading & message chunk parsing to a separate library
-_ rangefilter on the initial inbox to only consider the most recent 1000 messages
-
-denied
-------
-x rss feed reading: use rss2email
-x gmail support: obsoleted by imap
-
-done
-----
-x bugfix: threading broken
-x bugfix: thread ordering in thread-index-mode sometimes jumps around with 'M'
-x forwards optionally include attachments
-x flesh out gpg integration: sign & encrypt outgoing
-x pressing A in thread-view-mode should jump to next message
-x multi-thread dump upon crash
-x hook manager caches values of any proc "variables"
-x bugfix: remove delay on startup if a usual imap source exists
-x bugfix: broken source handling still needs to be improved
-x speed up querying
-x bugfix: sources sometimes aren't added by sup-add
-x more widgets: terminal title, statusbar
-x mailing list auto-subscribe/unsubscribe
-x bugfix: contacts.txt isn't parsed correctly when there are spaces in
-   aliases
-x bugfix: @ signs in names make sendmail die silently
-x bugfix: sent.mbox and >From
-x bugfix: tokenized email addresses (amazon.com, etc)
-x bugfix: trailing spaces in buffermanager.ask
-x bugfix: need to URL-unescape maildir folders
-x bugfix: downcasing tab completion
-x warnings: top-posting, missing attachment
-x hookability
-x bugfix: deadlock (on rubyforge) (? still valid ?)
-x bugfix: ferret corrupt index problem at index.c:901. see http://ferret.davebalmain.com/trac/ticket/279
-x tab completion for to: and cc: in compose-mode
-x individual labeling in thread-view-mode
-x translate aliases in queries on to: and from: fields
-x tab completion on labeling
-x bugfix: any interactive prompt after "No new messages." flash has an
-   empty line above it.
-x detect other sup instances and do something intelligent (because
-   ferret crashes violently with more than one index writer open)
-x refactor all the *-search-results-mode classes
-x decode RFC 2047 ("encoded word") headers
-  - see: http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/101949, http://dev.rubyonrails.org/ticket/6807
-x create attachments
-x add arbitrary labels to sources
-x improve sup-config
-x autoload more threads when you go down
-x add a sync-back tool that at least works for mboxes
-x thread by subject configurable in config.yaml
-x view as text command if the mime view command fails for an attachment
-x bugfix: attachment filenames sometimes not detected (filename=)
-x bugfix: rmail multipart error
-x bugfix: sup-add not prompting for old accounts, i think? possibly because
-    sources no longer respond_to? :username due to Recoverable wrapping
-x wide character support
-x i18n support
-x tab completion on labels
-x nice little startup config program
-x bugfix: triggering a pageup when cursor scrolling up jumps to the
-   bottom of the page rather than the next line
-x bugfix: final logging messages to stdout?
-x bugfix: mbox directory shouldn't generate an exception, just an error
-x bugfix: m in thread-view-mode when a person is not selected should open up a
-   blank compose-mode rather than do nothing
-x bugfix: stars on messages with blue backgrounds still have green bgs
-x ferret upgrade script (dump & restore)
-x bugfix: mark messages as read immediately when t-v-m is opened
-x compose in thread-view-mode auto-fills in person
-x bugfix: 'N' in thread-view-mode (expand only new messages) crashes
-x bugfix: detect source corruption at startup
-x maildir
-x bugfix: single-line messages come empty upon reply
-x make 'A' archive in thread-view-mode
-x remove stupid percent_done source methods (still useful; made it optional)
-x don't quit while writing thread index state to disk or with unsaved drafts/messages
-x bugfix: deleted threads are showing up (i don't see this any more)
-x bugfix: changing IMAP ids
-x bugfix: STILL new messages, drafts sometimes not showing up in inbox
-x bugfix: killed threads
-x bugfix: resuming a draft asks before discard
-x add a flag to sup-import to force the creation of a new source (see http://rubyforge.org/forum/forum.php?thread_id=10973&forum_id=10340)
-x use trollop to handle sup-devel args
-x clean up import code and share between poll.rb and sup-import
-x on startup, multi-threadedly call #connect on all sources 
-x bugfix: first time viewing a message only gets the first to:; subsequent views get them all (wtf)
-x search for other messages from author in thread-view-mode
-x resuming of arbitrary messages
-x alias authors in thread-view-mode
-x fix up contact list mode: should display while loading, and when you add an alias, should move everything else to the right
-x fix bug: envelope-to thing still not working
-x fix snippet repetitions with small snippets
-x fix next and previous in thread-view-mode with <unreceived messages>
-x move sup-import username/password prompts to highline
-x support different remote servers per user account
-x 'R' to quick-resume most recent draft
-x mbox+ssh
-x handle broken sources better
-x imap
-x word wrap
-x background indexing
-x auto-insertion of draft messages
-x drafts
-x sent messages loader
-x search: from
-x contacts
-x tagging for group operations
-x view: starred, to me, etc
-x pull in messages by subject as well in load_thread_for_
-x reply+compose+forward
-x resize
-x buffer respawns
-x readline
-x "loading" message
-x search: body, to/from, tags (requires: readline)
-x highlighting/different color stuff
-x config: your email, sendmail, etc
-x status: to/from_you, cc_you_others
-x status: new/not, important
-x bugfix: miscellaneous weirdnesses in buffer line editing
index 910b333841c28654d2abee3d95d0b83194ad7fbc..9e9026747bca29324fe44cf4b4df12c1d8e54ccc 100644 (file)
@@ -33,7 +33,7 @@ class Module
 end
 
 module Redwood
-  VERSION = "0.5"
+  VERSION = "git"
 
   BASE_DIR   = ENV["SUP_BASE"] || File.join(ENV["HOME"], ".sup")
   CONFIG_FN  = File.join(BASE_DIR, "config.yaml")
index ee0695862415939120b262d4d4cec2e1ec2f214e..0003fec7bc60314dd14d89387d25ae886a7972c6 100644 (file)
@@ -53,7 +53,7 @@ class CryptoManager
     payload_fn.write format_payload(payload)
     payload_fn.close
 
-    recipient_opts = to.map { |r| "--recipient '#{r}'" }.join(" ")
+    recipient_opts = to.map { |r| "--recipient '<#{r}>'" }.join(" ")
     sign_opts = sign ? "--sign --local-user '#{from}'" : ""
     gpg_output = run_gpg "--output - --armor --encrypt --textmode #{sign_opts} #{recipient_opts} #{payload_fn.path}"
     raise Error, (gpg_output || "gpg command failed: #{cmd}") unless $?.success?
@@ -150,6 +150,8 @@ private
     ["Can't find gpg binary in path."]
   end
 
+  ## here's where we munge rmail output into the format that signed/encrypted
+  ## PGP/GPG messages should be
   def format_payload payload
     payload.to_s.gsub(/(^|[^\r])\n/, "\\1\r\n").gsub(/^MIME-Version: .*\r\n/, "")
   end
index 3176415e84f6fd5e7b307f25ff2e801183a352a7..76c7139f45eb9478a627e49ac7c87eea81d0ee5d 100644 (file)
@@ -43,16 +43,10 @@ class Keymap
     when :home: "<home>"
     when :end: "<end>"
     when :enter, :return: "<enter>"
-    when :ctrl_l: "ctrl-l"
-    when :ctrl_g: "ctrl-g"
     when :tab: "tab"
     when " ": "<space>"
     else
-      if k.is_a?(String) && k.length == 1
-        k
-      else
-        raise ArgumentError, "unknown key name \"#{k}\""
-      end
+      Curses::keyname(keysym_to_keycode(k))
     end
   end
 
index f267b3b03aebbe846e7b3b08148cf7c72797a8a8..49489541a0b3ea9be3a46883c472951ce589191e 100644 (file)
@@ -11,6 +11,7 @@ module Redwood
 ## TODO: move functionality to somewhere better, like message.rb
 module MBox
   BREAK_RE = /^From \S+/
+  HEADER_RE = /\s*(.*?\S)\s*/
 
   def read_header f
     header = {}
@@ -20,26 +21,26 @@ module MBox
     ## when scanning over large mbox files.
     while(line = f.gets)
       case line
-      when /^(From):\s*(.*?)\s*$/i,
-        /^(To):\s*(.*?)\s*$/i,
-        /^(Cc):\s*(.*?)\s*$/i,
-        /^(Bcc):\s*(.*?)\s*$/i,
-        /^(Subject):\s*(.*?)\s*$/i,
-        /^(Date):\s*(.*?)\s*$/i,
-        /^(References):\s*(.*?)\s*$/i,
-        /^(In-Reply-To):\s*(.*?)\s*$/i,
-        /^(Reply-To):\s*(.*?)\s*$/i,
-        /^(List-Post):\s*(.*?)\s*$/i,
-        /^(List-Subscribe):\s*(.*?)\s*$/i,
-        /^(List-Unsubscribe):\s*(.*?)\s*$/i,
-        /^(Status):\s*(.*?)\s*$/i: header[last = $1] = $2
-      when /^(Message-Id):\s*(.*?)\s*$/i: header[mid_field = last = $1] = $2
+      when /^(From):#{HEADER_RE}$/i,
+        /^(To):#{HEADER_RE}$/i,
+        /^(Cc):#{HEADER_RE}$/i,
+        /^(Bcc):#{HEADER_RE}$/i,
+        /^(Subject):#{HEADER_RE}$/i,
+        /^(Date):#{HEADER_RE}$/i,
+        /^(References):#{HEADER_RE}$/i,
+        /^(In-Reply-To):#{HEADER_RE}$/i,
+        /^(Reply-To):#{HEADER_RE}$/i,
+        /^(List-Post):#{HEADER_RE}$/i,
+        /^(List-Subscribe):#{HEADER_RE}$/i,
+        /^(List-Unsubscribe):#{HEADER_RE}$/i,
+        /^(Status):#{HEADER_RE}$/i: header[last = $1] = $2
+      when /^(Message-Id):#{HEADER_RE}$/i: header[mid_field = last = $1] = $2
 
       ## these next three can occur multiple times, and we want the
       ## first one
-      when /^(Delivered-To):\s*(.*)$/i,
-        /^(X-Original-To):\s*(.*)$/i,
-        /^(Envelope-To):\s*(.*)$/i: header[last = $1] ||= $2
+      when /^(Delivered-To):#{HEADER_RE}$/i,
+        /^(X-Original-To):#{HEADER_RE}$/i,
+        /^(Envelope-To):#{HEADER_RE}$/i: header[last = $1] ||= $2
 
       when /^\r*$/: break
       when /^\S+:/: last = nil # some other header we don't care about
index 89476b32f194ccc941f15e095a05cab625cf03da..b63a00b724ae05462894ae43e3e3370431c02a86 100644 (file)
@@ -305,8 +305,9 @@ protected
     m = RMail::Message.new
     m.header["Content-Type"] = "text/plain; charset=#{$encoding}"
     m.body = @body.join("\n")
-    m.body = m.body
     m.body += sig_lines.join("\n") unless $config[:edit_signature]
+    ## body must end in a newline or GPG signatures will be WRONG!
+    m.body += "\n" unless m.body =~ /\n\Z/
 
     ## there are attachments, so wrap body in an attachment of its own
     unless @attachments.empty?
@@ -321,7 +322,7 @@ protected
     ## do whatever crypto transformation is necessary
     if @crypto_selector && @crypto_selector.val != :none
       from_email = PersonManager.person_for(@header["From"]).email
-      to_email = (@header["To"] + @header["Cc"] + @header["Bcc"]).map { |p| PersonManager.person_for(p).email }
+      to_email = [@header["To"], @header["Cc"], @header["Bcc"]].flatten.compact.map { |p| PersonManager.person_for(p).email }
 
       m = CryptoManager.send @crypto_selector.val, from_email, to_email, m
     end
@@ -377,12 +378,11 @@ protected
        if text
          @header[field] = parse_header field, text
          update
-         field
        end
     else
-      default =
-        case field
+      default = case field
         when *MULTI_HEADERS
+          @header[field] ||= []
           @header[field].join(", ")
         else
           @header[field]
@@ -393,7 +393,6 @@ protected
         text = contacts.map { |s| s.longname }.join(", ")
         @header[field] = parse_header field, text
         update
-        field
       end
     end
   end
index f156b69fa55fc774619cebcee136a5a575cb5d95..559892d5655f07b80a1186e114950156220a13c5 100644 (file)
@@ -6,6 +6,7 @@ class InboxMode < ThreadIndexMode
   register_keymap do |k|
     ## overwrite toggle_archived with archive
     k.add :archive, "Archive thread (remove from inbox)", 'a'
+    k.add :read_and_archive, "Archive thread (remove from inbox) and mark read", 'A'
   end
 
   def initialize
@@ -38,6 +39,23 @@ class InboxMode < ThreadIndexMode
     regen_text
   end
 
+  def read_and_archive
+    return unless cursor_thread
+    cursor_thread.remove_label :unread
+    cursor_thread.remove_label :inbox
+    hide_thread cursor_thread
+    regen_text
+  end
+
+  def multi_read_and_archive threads
+    threads.each do |t|
+      t.remove_label :unread
+      t.remove_label :inbox
+      hide_thread t
+    end
+    regen_text
+  end
+
   def handle_unarchived_update sender, m
     add_or_unhide m
   end
index 514e9c4d93cebb3b6a594a6fabd6440bb3ce84c6..132b654066a8f107e2e52d7d2622c722e1e65ea6 100644 (file)
@@ -29,6 +29,11 @@ class LabelListMode < LineCursorMode
       BufferManager.flash "No labels messages with unread messages."
     end
   end
+
+  def focus
+    reload # make sure unread message counts are up-to-date
+  end
+
 protected
 
   def toggle_show_unread_only
index 922bf92b46169f4d5e0dde916312c10588e0d2ec..66c098be06b43d343ac7b0e120589d71b3ea20f7 100644 (file)
@@ -15,12 +15,14 @@ class ScrollMode < Mode
   COL_JUMP = 2
 
   register_keymap do |k|
-    k.add :line_down, "Down one line", :down, 'j', 'J'
-    k.add :line_up, "Up one line", :up, 'k', 'K'
+    k.add :line_down, "Down one line", :down, 'j', 'J', "\C-e"
+    k.add :line_up, "Up one line", :up, 'k', 'K', "\C-y"
     k.add :col_left, "Left one column", :left, 'h'
     k.add :col_right, "Right one column", :right, 'l'
-    k.add :page_down, "Down one page", :page_down, ' '
-    k.add :page_up, "Up one page", :page_up, 'p', :backspace
+    k.add :page_down, "Down one page", :page_down, ' ', "\C-f"
+    k.add :page_up, "Up one page", :page_up, 'p', :backspace, "\C-b"
+    k.add :half_page_down, "Down one half page", "\C-d"
+    k.add :half_page_up, "Up one half page", "\C-u"
     k.add :jump_to_start, "Jump to top", :home, '^', '1'
     k.add :jump_to_end, "Jump to bottom", :end, '$', '0'
     k.add :jump_to_left, "Jump to the left", '['
@@ -85,15 +87,16 @@ class ScrollMode < Mode
     continue_search_in_buffer
   end
 
-  ## subclasses can override these two!
+  ## subclasses can override these three!
   def search_goto_pos line, leftcol, rightcol
-    jump_to_line line
+    search_goto_line line
 
     if rightcol > self.rightcol # if it's occluded...
       jump_to_col [rightcol - buffer.content_width + 1, 0].max # move right
     end
   end
   def search_start_line; @topline end
+  def search_goto_line line; jump_to_line line end
 
   def col_left
     return unless @leftcol > 0
@@ -130,6 +133,8 @@ class ScrollMode < Mode
   def line_up;  jump_to_line @topline - 1; end
   def page_down; jump_to_line @topline + buffer.content_height - @slip_rows; end
   def page_up; jump_to_line @topline - buffer.content_height + @slip_rows; end
+  def half_page_down; jump_to_line @topline + buffer.content_height / 2; end
+  def half_page_up; jump_to_line @topline - buffer.content_height / 2; end
   def jump_to_start; jump_to_line 0; end
   def jump_to_end; jump_to_line lines - buffer.content_height; end
 
index 7a0b8150c455cc4df19cdb581e3ae9144cacd1e0..d4aedbc4112d201d0993fea52f6bd865de2aca6a 100644 (file)
@@ -440,9 +440,8 @@ EOS
   end
 
   def multi_edit_labels threads
-    answer = BufferManager.ask :add_labels, "add labels: "
-    return unless answer
-    user_labels = answer.split(/\s+/).map { |l| l.intern }
+    user_labels = BufferManager.ask_for_labels :add_labels, "Add labels: ", [], @hidden_labels
+    return unless user_labels
     
     hl = user_labels.select { |l| @hidden_labels.member? l }
     if hl.empty?
index 559c58ecd35bbf0c27588aaaabf0f86bdfe55aa9..b96576eee58512cef3a02c1ca68db3c52738f7f8 100644 (file)
@@ -188,7 +188,7 @@ EOS
   def compose
     p = @person_lines[curpos]
     if p
-      ComposeMode.spawn_nicely :to => [p]
+      ComposeMode.spawn_nicely :to_default => p
     else
       ComposeMode.spawn_nicely
     end
index 8000535d557b4bad9e06c15b1580f70e29c0a331..b1f09dd5953839e85076a72050a4acadefb02ea8 100644 (file)
@@ -6,7 +6,10 @@ cat History.txt >> tmp.txt
 mv tmp.txt History.txt
 vi History.txt    # and cleanup
 vi ReleaseNotes   # and add whatever's necessary
-vi www/index.html  # and bump version number
+vi www/index.html # and bump version number
+git rank-contributors -o > CONTRIBUTORS
+vi CONTRIBUTORS   # and merge
+vi www/index.html # and include CONTRIBUTORS
 # ... git add, commit, etc
 git checkout -b release-<releasename>
 vi lib/sup.rb bin/sup # and bump BOTH version numbers
diff --git a/test/test_mbox_parsing.rb b/test/test_mbox_parsing.rb
new file mode 100644 (file)
index 0000000..070b152
--- /dev/null
@@ -0,0 +1,114 @@
+#!/usr/bin/ruby
+
+require 'test/unit'
+require 'sup'
+require 'stringio'
+
+include Redwood
+
+class TestMessage < Test::Unit::TestCase
+  def setup
+  end
+
+  def teardown
+  end
+
+  def test_normal_headers
+    h = MBox.read_header StringIO.new(<<EOS)
+From: Bob <bob@bob.com>
+To: Sally <sally@sally.com>
+EOS
+
+    assert_equal "Bob <bob@bob.com>", h["From"]
+    assert_equal "Sally <sally@sally.com>", h["To"]
+    assert_nil h["Message-Id"]
+  end
+
+  ## this is shitty behavior in retrospect, but it's built in now.
+  def test_message_id_stripping
+    h = MBox.read_header StringIO.new("Message-Id: <one@bob.com>\n")
+    assert_equal "one@bob.com", h["Message-Id"]
+
+    h = MBox.read_header StringIO.new("Message-Id: one@bob.com\n")
+    assert_equal "one@bob.com", h["Message-Id"]
+  end
+
+  def test_multiline
+    h = MBox.read_header StringIO.new(<<EOS)
+From: Bob <bob@bob.com>
+Subject: one two three
+  four five six
+To: Sally <sally@sally.com>
+References: seven
+  eight
+Seven: Eight
+EOS
+
+    assert_equal "one two three four five six", h["Subject"]
+    assert_equal "Sally <sally@sally.com>", h["To"]
+    assert_equal "seven eight", h["References"]
+  end
+
+  def test_ignore_spacing
+    variants = [
+      "Subject:one two  three   end\n",
+      "Subject:    one two  three   end\n",
+      "Subject:   one two  three   end    \n",
+    ]
+    variants.each do |s|
+      h = MBox.read_header StringIO.new(s)
+      assert_equal "one two  three   end", h["Subject"]
+    end
+  end
+
+  def test_message_id_ignore_spacing
+    variants = [
+      "Message-Id:     <one@bob.com>       \n",
+      "Message-Id:      one@bob.com        \n",
+      "Message-Id:<one@bob.com>       \n",
+      "Message-Id:one@bob.com       \n",
+    ]
+    variants.each do |s|
+      h = MBox.read_header StringIO.new(s)
+      assert_equal "one@bob.com", h["Message-Id"]
+    end
+  end
+
+  def test_ignore_empty_lines
+    variants = [
+      "",
+      "Message-Id:       \n",
+      "Message-Id:\n",
+    ]
+    variants.each do |s|
+      h = MBox.read_header StringIO.new(s)
+      assert_nil h["Message-Id"]
+    end
+  end
+
+  def test_detect_end_of_headers
+    h = MBox.read_header StringIO.new(<<EOS)
+From: Bob <bob@bob.com>
+
+To: a dear friend
+EOS
+  assert_equal "Bob <bob@bob.com>", h["From"]
+  assert_nil h["To"]
+
+  h = MBox.read_header StringIO.new(<<EOS)
+From: Bob <bob@bob.com>
+\r
+To: a dear friend
+EOS
+  assert_equal "Bob <bob@bob.com>", h["From"]
+  assert_nil h["To"]
+
+  h = MBox.read_header StringIO.new(<<EOS)
+From: Bob <bob@bob.com>
+\r\n\r
+To: a dear friend
+EOS
+  assert_equal "Bob <bob@bob.com>", h["From"]
+  assert_nil h["To"]
+  end
+end
index 0575f7d0359f4adde8515da7703c4cadb12c8073..2ba689bc63321c4c80e7d6885ec732e46b8a2bc3 100644 (file)
                <h2>Credit</h2>
 
                <p>
-               Sup is brought to you by <a href="http://cs.stanford.edu/~ruby/">William Morgan</a>.
-               </p>
+               Sup is brought to you by <a href="http://cs.stanford.edu/~ruby/">William Morgan</a> and the following honorable contributors:
+        <ul>
+          <li>Ismo Puustinen &lt;ismo at the iki dot fis&gt;</li>
+          <li>Marcus Williams &lt;marcus-sup at the bar-coded dot nets&gt;</li>
+          <li>Nicolas Pouillard &lt;nicolas.pouillard at the gmail dot coms&gt;</li>
+          <li>Christopher Warrington &lt;chrisw at the rice dot edus&gt;</li>
+          <li>Jeff Balogh &lt;its.jeff.balogh at the gmail dot coms&gt;</li>
+        </ul>
+        </p>
 
                <p>
                Sup is made possible by the hard work of <a