Class: Redwood::Source

Inherits:
Object show all
Defined in:
lib/sup/source.rb

Direct Known Subclasses

DraftLoader, MBox, Maildir

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(uri, usual = true, archived = false, id = nil) ⇒ Source

Returns a new instance of Source.

Raises:

  • (ArgumentError)


59
60
61
62
63
64
65
66
67
68
# File 'lib/sup/source.rb', line 59

def initialize uri, usual=true, archived=false, id=nil
  raise ArgumentError, "id must be an integer: #{id.inspect}" unless id.is_a? Fixnum if id

  @uri = uri
  @usual = usual
  @archived = archived
  @id = id

  @poll_lock = Mutex.new
end

Instance Attribute Details

#idObject

Returns the value of attribute id.



57
58
59
# File 'lib/sup/source.rb', line 57

def id
  @id
end

#poll_lockObject

Returns the value of attribute poll_lock.



57
58
59
# File 'lib/sup/source.rb', line 57

def poll_lock
  @poll_lock
end

#uriObject (readonly)

Returns the value of attribute uri.



56
57
58
# File 'lib/sup/source.rb', line 56

def uri
  @uri
end

Class Method Details

.parse_raw_email_header(f) ⇒ Object

utility method to read a raw email header from an IO stream and turn it into a hash of key-value pairs. minor special semantics for certain headers.

THIS IS A SPEED-CRITICAL SECTION. Everything you do here will have a significant effect on Sup’s processing speed of email from ALL sources. Little things like string interpolation, regexp interpolation, += vs <<, all have DRAMATIC effects. BE CAREFUL WHAT YOU DO!



102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
# File 'lib/sup/source.rb', line 102

def self.parse_raw_email_header f
  header = {}
  last = nil

  while(line = f.gets)
    case line
    ## these three can occur multiple times, and we want the first one
    when /^(Delivered-To|X-Original-To|Envelope-To):\s*(.*?)\s*$/i; header[last = $1.downcase] ||= $2
    ## regular header: overwrite (not that we should see more than one)
    ## TODO: figure out whether just using the first occurrence changes
    ## anything (which would simplify the logic slightly)
    when /^([^:\s]+):\s*(.*?)\s*$/i; header[last = $1.downcase] = $2
    when /^\r*$/; break # blank line signifies end of header
    else
      if last
        header[last] << " " unless header[last].empty?
        header[last] << line.strip
      end
    end
  end

  %w(subject from to cc bcc).each do |k|
    v = header[k] or next
    next unless Rfc2047.is_encoded? v
    header[k] = begin
      Rfc2047.decode_to $encoding, v
    rescue Errno::EINVAL, Iconv::InvalidEncoding, Iconv::IllegalSequence => e
      #debug "warning: error decoding RFC 2047 header (#{e.class.name}): #{e.message}"
      v
    end
  end
  header
end

Instance Method Details

#==(o) ⇒ Object



74
# File 'lib/sup/source.rb', line 74

def == o; o.uri == uri; end

#file_pathObject

overwrite me if you have a disk incarnation (currently used only for sup-sync-back)



71
# File 'lib/sup/source.rb', line 71

def file_path; nil end

#go_idleObject

release resources that are easy to reacquire. it is called after processing a source (e.g. polling) to prevent resource leaks (esp. file descriptors).



82
# File 'lib/sup/source.rb', line 82

def go_idle; end

#is_source_for?(uri) ⇒ Boolean

Returns:

  • (Boolean)


75
# File 'lib/sup/source.rb', line 75

def is_source_for? uri; uri == @uri; end

#pollObject

Yields values of the form [Symbol, Hash] add: info, labels, progress delete: info, progress



87
88
89
# File 'lib/sup/source.rb', line 87

def poll
  unimplemented
end

#read?Boolean

Returns:

  • (Boolean)


77
# File 'lib/sup/source.rb', line 77

def read?; false; end

#to_sObject



73
# File 'lib/sup/source.rb', line 73

def to_s; @uri.to_s; end

#usualObject

Implementing a new source should be easy, because Sup only needs to be able to:

1. See how many messages it contains
2. Get an arbitrary message
3. (optional) see whether the source has marked it read or not

In particular, Sup doesn’t need to move messages, mark them as read, delete them, or anything else. (Well, it’s nice to be able to delete them, but that is optional.)

Messages are identified internally based on the message id, and stored with an unique document id. Along with the message, source information that can contain arbitrary fields (set up by the source) is stored. This information will be passed back to the source when a message in the index (Sup database) needs to be identified to its source, e.g. when re-reading or modifying a unique message.

To write a new source, subclass this class, and implement:

  • initialize

  • load_header offset

  • load_message offset

  • raw_header offset

  • raw_message offset

  • store_message (optional)

  • poll (loads new messages)

  • go_idle (optional)

All exceptions relating to accessing the source must be caught and rethrown as FatalSourceErrors or OutOfSyncSourceErrors. OutOfSyncSourceErrors should be used for problems that a call to sup-sync will fix (namely someone’s been playing with the source from another client); FatalSourceErrors can be used for anything else (e.g. the imap server is down or the maildir is missing.)

Finally, be sure the source is thread-safe, since it WILL be pummelled from multiple threads at once.

Examples for you to look at: mbox.rb and maildir.rb.



55
# File 'lib/sup/source.rb', line 55

bool_accessor :usual, :archived

#valid?(info) ⇒ Boolean

Returns:

  • (Boolean)


91
92
93
# File 'lib/sup/source.rb', line 91

def valid? info
  true
end