mwalker-smail 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,95 @@
1
+ class SMail
2
+ class Header #:nodoc: all
3
+ attr_reader :crlf, :head
4
+
5
+ def initialize(headers, smail)
6
+ @crlf = smail.crlf || "\r\n"
7
+
8
+ @order = Array.new
9
+ @head = Hash.new
10
+ headers.each do |header|
11
+ @order << header.first
12
+ if @head[header.first].nil?
13
+ @head[header.first] = Array.new
14
+ end
15
+ @head[header.first] << header.last
16
+ end
17
+
18
+ @header_names = @head.collect {|header,value| [header.downcase, header] }
19
+ end
20
+
21
+ def to_s #:nodoc:
22
+ header_pairs.collect {|pair| fold(pair.join(": "))}.join(@crlf) + @crlf
23
+ end
24
+
25
+ def header_names
26
+ @header_names.collect {|pair| pair.last }
27
+ end
28
+
29
+ def header_pairs
30
+ headers = []
31
+ seen = {}
32
+ seen.default = -1
33
+ @order.each do |header|
34
+ headers << [header, @head[header][seen[header] += 1]]
35
+ end
36
+
37
+ headers
38
+ end
39
+
40
+ def header(field)
41
+ return unless names = @header_names.assoc(field.downcase)
42
+ headers(field).first
43
+ end
44
+
45
+ def headers(field)
46
+ return unless names = @header_names.assoc(field.downcase)
47
+
48
+ @head[names.last]
49
+ end
50
+
51
+ def header_set(field, lines)
52
+ unless field =~ /^[\x21-\x39\x3b-\x7e]+$/
53
+ raise(ArgumentError, "Field name contains illegal characters")
54
+ end
55
+ unless field =~ /^[\w-]+$/
56
+ raise(ArgumentError, "Field name is not limited to hyphens and alphanumerics")
57
+ end
58
+
59
+ old_length = 0
60
+ if @header_names.assoc(field.downcase)
61
+ old_length = @head[@header_names.assoc(field.downcase).last].length
62
+ else
63
+ @header_names << [field.downcase, field]
64
+ # @order << field # FIXME: add new fields onto the end
65
+ end
66
+
67
+ @head[@header_names.assoc(field.downcase).last] = lines
68
+ if lines.length < old_length
69
+ # Remove the last x instances of field from @order
70
+ headers_to_remove = old_length - lines.length
71
+ headers_seen = 0
72
+ @order.collect! {|field_name|
73
+ if field_name.downcase == field.downcase
74
+ headers_seen += 1
75
+ next if headers_seen > headers_to_remove
76
+ end
77
+ field_name
78
+ }.compact!
79
+ elsif lines.length > old_length
80
+ # Add x instances of field to the end of @order
81
+ @order = @order + ([field] * (lines.length - old_length))
82
+ end
83
+
84
+ headers(field)
85
+ end
86
+
87
+ private
88
+
89
+ def fold(line)
90
+ # FIXME: this needs to actually fold the line
91
+ line.gsub( /^(.{1,78})(\s+|$)/ ){ $2.empty? ? $1 : "#{$1}#{@crlf}\t" }
92
+ end
93
+
94
+ end
95
+ end
@@ -0,0 +1,55 @@
1
+ class SMail
2
+
3
+ private
4
+
5
+ # We are liberal in what we accept.
6
+ PATTERN_CRLF = "\n\r|\r\n|\n|\r" #:nodoc:
7
+
8
+ RE_CRLF = Regexp.new(PATTERN_CRLF, Regexp::MULTILINE) #:nodoc:
9
+
10
+ def split_head_from_body(text)
11
+ # The body is a sequence of characters after the header separated by an empty line
12
+ if text =~ /(.*?(#{PATTERN_CRLF}))\2(.*)/m
13
+ return $1, $3 || '', $2
14
+ else # The body is, of course, optional.
15
+ return text, "", "\r\n"
16
+ end
17
+ end
18
+
19
+ def read_header(head)
20
+ headers = headers_to_list(head)
21
+
22
+ SMail::Header.new(headers, self)
23
+ end
24
+
25
+ # Header fields are lines composed of a field name, followed by a colon (":"),
26
+ # followed by a field body, and terminated by CRLF. A field name MUST be
27
+ # composed of printable US-ASCII characters (i.e., characters that have values
28
+ # between 33 and 126, inclusive), except colon. A field body may be composed
29
+ # of any US-ASCII characters, except for CR and LF.
30
+
31
+ # However, a field body may contain CRLF when used in header "folding" and
32
+ # "unfolding" as described in section 2.2.3.
33
+
34
+ def headers_to_list(head)
35
+ headers = Array.new
36
+
37
+ head.split(RE_CRLF).each do |line|
38
+ if line.gsub!(/^\s+/, '') or line !~ /([^:]+):\s*(.*)/
39
+ # This is a continuation line, we fold it on to the end of the previous header
40
+ next if headers.empty? # Most likely an mbox From line, skip it
41
+
42
+ #headers.last.last << (headers.last.empty? ? line : " #{line}")
43
+ if headers.last.empty?
44
+ headers.last.last << line
45
+ else
46
+ headers.last.last << " #{line}"
47
+ end
48
+ else
49
+ headers << [$1, $2]
50
+ end
51
+ end
52
+
53
+ headers
54
+ end
55
+ end
@@ -0,0 +1,61 @@
1
+ # SMail
2
+ #
3
+ # A very simple library for parsing email messages.
4
+ #
5
+ # Based on Email::Simple from CPAN
6
+ #
7
+ # No decoding of any fields or the body is attempted, see SMail::MIME.
8
+
9
+ class SMail
10
+ # The line ending found in this email.
11
+ attr_reader :crlf
12
+ # The body text of the email.
13
+ attr_accessor :body
14
+
15
+ def initialize(text = '')
16
+ head, @body, @crlf = split_head_from_body(text)
17
+
18
+ @head = read_header(head)
19
+ end
20
+
21
+ # Returns the first value for the named header
22
+ def header(field)
23
+ @head.header(field)
24
+ end
25
+
26
+ # Returns an array containing every value for the named header, for the first instance
27
+ # see header
28
+ def headers(field)
29
+ @head.headers(field)
30
+ end
31
+
32
+ # Sets the header to contain the given data, if there is more than one existing header
33
+ # the extra headers are removed.
34
+ def header_set(field, line)
35
+ headers_set(field, line).first
36
+ end
37
+
38
+ # Sets the headers to contain the given data, passing in multiple lines results in
39
+ # multiple headers and order is retained.
40
+ def headers_set(field, *lines)
41
+ @head.header_set(field, lines)
42
+ end
43
+
44
+ def header=(header) #:nodoc:
45
+ # FIXME: takes a string and splits it??
46
+ end
47
+
48
+ # Returns the list of header names currently in the message. The order is not significant.
49
+ def header_names
50
+ @head.header_names
51
+ end
52
+
53
+ # Returns a list of pairs describing the contents of the header.
54
+ def header_pairs
55
+ @head.header_pairs
56
+ end
57
+
58
+ def to_s #:nodoc:
59
+ @head.to_s + @crlf + @body
60
+ end
61
+ end
@@ -0,0 +1,9 @@
1
+ class SMail
2
+ module VERSION #:nodoc:
3
+ MAJOR = 0
4
+ MINOR = 0
5
+ TINY = 3
6
+
7
+ STRING = [MAJOR, MINOR, TINY].join('.')
8
+ end
9
+ end
data/lib/smail.rb ADDED
@@ -0,0 +1,2 @@
1
+ # $Id: email_controller.rb 856 2006-10-26 03:09:39Z pete $
2
+ Dir[File.join(File.dirname(__FILE__), 'smail/**/*.rb')].sort.each { |lib| require lib }
metadata ADDED
@@ -0,0 +1,57 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mwalker-smail
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.3
5
+ platform: ruby
6
+ authors:
7
+ - Matthew Walker
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-05-16 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description:
17
+ email: matthew@walker.wattle.id.au
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files: []
23
+
24
+ files:
25
+ - lib/smail.rb
26
+ - lib/smail/header.rb
27
+ - lib/smail/parser.rb
28
+ - lib/smail/smail.rb
29
+ - lib/smail/version.rb
30
+ has_rdoc: false
31
+ homepage: http://github.com/mwalker/smail
32
+ post_install_message:
33
+ rdoc_options: []
34
+
35
+ require_paths:
36
+ - lib
37
+ required_ruby_version: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ version: "0"
42
+ version:
43
+ required_rubygems_version: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: "0"
48
+ version:
49
+ requirements: []
50
+
51
+ rubyforge_project:
52
+ rubygems_version: 1.2.0
53
+ signing_key:
54
+ specification_version: 2
55
+ summary: A simple RFC2822 email parser
56
+ test_files: []
57
+