Skip to content

Commit

Permalink
No commit message
Browse files Browse the repository at this point in the history
  • Loading branch information
tmtm committed Oct 9, 2006
1 parent b3cd3fa commit a7990da
Show file tree
Hide file tree
Showing 17 changed files with 527 additions and 20 deletions.
6 changes: 6 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
all:
cd rfc2822; make
cd rfc2822obs; make
cd rfc2045; make
cd rfc2183; make

clean:
cd rfc2822; make clean
cd rfc2822obs; make clean
cd rfc2045; make clean
cd rfc2183; make clean

test:
ruby -w test_rfc2822.rb
ruby -w test_rfc2822obs.rb
ruby -w test_rfc2045.rb
ruby -w test_rfc2183.rb
5 changes: 5 additions & 0 deletions mailparser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ class MailParser
"resent-message-id" => RFC2822obs,
"return-path" => RFC2822obs,
"received" => RFC2822obs,
"content-type" => RFC2045,
"content-description" => RFC2045,
"content-transfer-encoding" => RFC2045,
"content-id" => RFC2045,
"mime-version" => RFC2045,
}

class Header
Expand Down
53 changes: 53 additions & 0 deletions rfc2045.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
#
# $Id$
#
# Copyright (C) 2006 TOMITA Masahiro
# mailto:[email protected]

require "rfc2822"
require "rfc2045/parser"

module RFC2045

class ParseError < StandardError
end

HEADER_TYPE = {
"content-type" => :CONTENT_TYPE,
"content-description" => :UNSTRUCTURED,
"content-transfer-encoding" => :CONTENT_TRANSFER_ENCODING,
"content-id" => [RFC2822, :MSG_ID],
"mime-version" => :MIME_VERSION,
}

class ContentType
def initialize(type, subtype, params)
@type, @subtype, @params = type.downcase, subtype.downcase, params
end

attr_reader :type, :subtype, :params
end

class ContentTransferEncoding
def initialize(mechanism)
@mechanism = mechanism.downcase
end

attr_reader :mechanism
end

module_function
def parse(name, value)
htype = HEADER_TYPE[name.downcase] || :UNSTRUCTURED
if htype == :UNSTRUCTURED then
return value.chomp
end
if htype.is_a? Array then
parser = htype[0]::Parser.new
parser.parse(htype[1], value)
else
parser = Parser.new
parser.parse(htype, value)
end
end
end
7 changes: 7 additions & 0 deletions rfc2045/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
all: parser.rb

clean:
rm -rf parser.rb

parser.rb: parser.y
racc -v -o parser.rb parser.y
86 changes: 86 additions & 0 deletions rfc2045/parser.y
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
#
# $Id$
#
# Copyright (C) 2006 TOMITA Masahiro
# mailto:[email protected]

class RFC2045::Parser

options no_result_var

rule

all : CONTENT_TYPE content_type {val[1]}
# | CONTENT_DESCRIPTION
| CONTENT_TRANSFER_ENCODING content_transfer_encoding {val[1]}
# | CONTENT_ID content_id {val[1]}
| MIME_VERSION mime_version {val[1]}

content_type : type '/' subtype parameter_list
{
ContentType.new(val[0], val[2], val[3])
}

content_transfer_encoding: mechanism
{
ContentTransferEncoding.new(val[0])
}

#content_id : msg_id

mime_version : DIGIT '.' DIGIT
{
val.join
}

mechanism : TOKEN

type : TOKEN

subtype : TOKEN

parameter_list : /* empty */
{
{}
}
| parameter_list ';' parameter
{
pn, pv = val[2]
pv = $1 if pv =~ /\A\"(.*)\"\Z/
val[0][pn] = pv
val[0]
}
parameter : attribute '=' value
{
[val[0].downcase, val[2]]
}
attribute : TOKEN
value : TOKEN
| QUOTED_STRING
---- inner
require "rfc2045/scanner"
def parse(header_type, value)
@header_type = header_type
@value = value
@scanner = RFC2045::Scanner.new(header_type, value)
ret = yyparse(self, :parse_sub)
@comments = @scanner.comments
ret
end
def parse_sub(&block)
yield @header_type, @value
@scanner.scan(&block)
end
def on_error(t, val, vstack)
# p t, val, vstack
# p racc_token2str(t)
raise ParseError, val
end
57 changes: 57 additions & 0 deletions rfc2045/scanner.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
#
# $Id$
#
# Copyright (C) 2006 TOMITA Masahiro
# mailto:[email protected]

require "rfc2822"

class RFC2045::Scanner < RFC2822::Scanner
TOKEN_RE = '\x21\x23-\x27\x2a\x2b\x2d\x2e\x30-\x39\x41-\x5a\x5e-\x7f'

def scan(&block)
case @header_type
when :MIME_VERSION
scan_mime_version(&block)
else
scan_structured(&block)
end
end

def scan_structured()
until @ss.eos?
case
when s = @ss.scan(/\s*\(/nmo)
s << cfws(@ss)
next
when s = @ss.scan(/\s+/nmo)
next
when s = @ss.scan(/\"(\s*(\\[#{TEXT_RE}]|[#{QTEXT_RE}]))*\s*\"/nmo)
yield :QUOTED_STRING, s
when s = @ss.scan(/[#{TOKEN_RE}]+/no)
yield :TOKEN, s
when s = @ss.scan(/./no)
yield s, s
end
end
yield nil
end

def scan_mime_version()
until @ss.eos?
case
when s = @ss.scan(/\s*\(/nmo)
s << cfws(@ss)
next
when s = @ss.scan(/\s+/nmo)
next
when s = @ss.scan(/\d+/no)
yield :DIGIT, s
when s = @ss.scan(/./no)
yield s, s
end
end
yield nil
end

end
40 changes: 40 additions & 0 deletions rfc2183.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#
# $Id$
#
# Copyright (C) 2006 TOMITA Masahiro
# mailto:[email protected]

require "rfc2183/parser"

module RFC2183

class ParseError < StandardError
end

HEADER_TYPE = {
"content-disposition" => :CONTENT_DISPOSITION,
}

class ContentDisposition
def initialize(type, params)
@type, @params = type.downcase, params
end

attr_reader :type, :params
end

module_function
def parse(name, value)
htype = HEADER_TYPE[name.downcase] || :UNSTRUCTURED
if htype == :UNSTRUCTURED then
return value.chomp
end
if htype.is_a? Array then
parser = htype[0]::Parser.new
parser.parse(htype[1], value)
else
parser = Parser.new
parser.parse(htype, value)
end
end
end
7 changes: 7 additions & 0 deletions rfc2183/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
all: parser.rb

clean:
rm -rf parser.rb

parser.rb: parser.y
racc -v -o parser.rb parser.y
66 changes: 66 additions & 0 deletions rfc2183/parser.y
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
#
# $Id$
#
# Copyright (C) 2006 TOMITA Masahiro
# mailto:[email protected]

class RFC2183::Parser

options no_result_var

rule

all : CONTENT_DISPOSITION content_disposition {val[1]}

content_disposition : type parameter_list
{
ContentDisposition.new(val[0], val[1])
}

type : TOKEN

parameter_list : /* empty */
{
{}
}
| parameter_list ';' parameter
{
pn, pv = val[2]
pv = $1 if pv =~ /\A\"(.*)\"\Z/
val[0][pn] = pv
val[0]
}
parameter : attribute '=' value
{
[val[0].downcase, val[2]]
}
attribute : TOKEN
value : TOKEN
| QUOTED_STRING
---- inner
require "rfc2183/scanner"
def parse(header_type, value)
@header_type = header_type
@value = value
@scanner = RFC2183::Scanner.new(header_type, value)
ret = yyparse(self, :parse_sub)
@comments = @scanner.comments
ret
end
def parse_sub(&block)
yield @header_type, @value
@scanner.scan(&block)
end
def on_error(t, val, vstack)
# p t, val, vstack
# p racc_token2str(t)
raise ParseError, val
end
10 changes: 10 additions & 0 deletions rfc2183/scanner.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#
# $Id$
#
# Copyright (C) 2006 TOMITA Masahiro
# mailto:[email protected]

require "rfc2045"

class RFC2183::Scanner < RFC2045::Scanner
end
5 changes: 4 additions & 1 deletion rfc2822.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
#
# $Id$
#
# Copyright (C) 2006 TOMITA Masahiro
# mailto:[email protected]

require "rfc2822/parser"

Expand Down Expand Up @@ -136,7 +140,6 @@ def time()
end
end


module_function
def parse(name, value)
htype = HEADER_TYPE[name.downcase] || :UNSTRUCTURED
Expand Down
Loading

0 comments on commit a7990da

Please sign in to comment.