Welcome to the "trac"-ing site of soap4r!
[soap4r] [httpclient] [openpgp4u] [pkcs1] [logger] [csv] [vtr]

root/trunk/lib/soap/mimemessage.rb

Revision 2003, 5.0 kB (checked in by nahi, 1 year ago)
  • wsdl2ruby.rb did not generate proper definitions for overloaded method in WSDL. (#446)
  • unnecessary 's' option for CRLF trimming regexp. Thanks Nobu.
  • Property svn:eol-style set to native
  • Property svn:keywords set to author date id revision
Line 
1 # SOAP4R - MIME Message implementation.
2 # Copyright (C) 2002  Jamie Herre.
3
4 # This program is copyrighted free software by NAKAMURA, Hiroshi.  You can
5 # redistribute it and/or modify it under the same terms of Ruby's license;
6 # either the dual license version in 2003, or any later version.
7
8
9 require 'soap/attachment'
10
11
12 module SOAP
13
14
15 # Classes for MIME message handling.  Should be put somewhere else!
16 # Tried using the 'tmail' module but found that I needed something
17 # lighter in weight.
18
19
20 class MIMEMessage
21   class MIMEMessageError < StandardError; end
22
23   MultipartContentType = 'multipart/\w+'
24
25   class Header
26     attr_accessor :str, :key, :root
27
28     def initialize
29       @attrs = {}
30     end
31
32     def [](key)
33       @attrs[key]
34     end
35
36     def []=(key, value)
37       @attrs[key] = value
38     end
39
40     def to_s
41       @key + ": " + @str
42     end
43   end
44
45   class Headers < Hash
46     def self.parse(str)
47       new.parse(str)
48     end
49
50     def parse(str)
51       header_cache = nil
52       str.each do |line|
53         case line
54         when /^\A[^\: \t]+:\s*.+$/
55           parse_line(header_cache) if header_cache
56           header_cache = line.sub(/\r?\n\z/, '')
57         when /^\A\s+(.*)$/
58           # a continuous line at the beginning line crashes here.
59           header_cache << line
60         else
61           raise RuntimeError.new("unexpected header: #{line.inspect}")
62         end
63       end
64       parse_line(header_cache) if header_cache
65       self
66     end
67
68     def parse_line(line)
69       if /^\A([^\: \t]+):\s*(.+)\z/ =~ line
70         header = parse_rhs($2.strip)
71         header.key = $1.strip
72         self[header.key.downcase] = header
73       else
74         raise RuntimeError.new("unexpected header line: #{line.inspect}")
75       end
76     end
77
78     def parse_rhs(str)
79       a = str.split(/;+\s+/)
80       header = Header.new
81       header.str = str
82       header.root = a.shift
83       a.each do |pair|
84         if pair =~ /(\w+)\s*=\s*"?([^"]+)"?/
85           header[$1.downcase] = $2
86         else
87           raise RuntimeError.new("unexpected header component: #{pair.inspect}")
88         end
89       end
90       header
91     end
92
93     def add(key, value)
94       if key != nil and value != nil
95         header = parse_rhs(value)
96         header.key = key
97         self[key.downcase] = header
98       end
99     end
100
101     def to_s
102       self.values.collect { |hdr|
103         hdr.to_s
104       }.join("\r\n")
105     end
106   end
107
108   class Part
109     attr_accessor :headers, :body
110
111     def initialize
112       @headers = Headers.new
113       @headers.add("Content-Transfer-Encoding", "8bit")
114       @body = nil
115       @contentid = nil
116     end
117
118     def self.parse(str)
119       new.parse(str)
120     end
121
122     def parse(str)
123       headers, body = str.split(/\r\n\r\n/, 2)
124       if headers != nil and body != nil
125         @headers = Headers.parse(headers)
126         @body = body.sub(/\r\n\z/, '')
127       else
128         raise RuntimeError.new("unexpected part: #{str.inspect}")
129       end
130       self
131     end
132
133     def contentid
134       if @contentid == nil and @headers.key?('content-id')
135         @contentid = @headers['content-id'].str
136         @contentid = $1 if @contentid =~ /^<(.+)>$/
137       end
138       @contentid
139     end
140
141     alias content body
142
143     def to_s
144       @headers.to_s + "\r\n\r\n" + @body
145     end
146   end
147
148   def initialize
149     @parts = []
150     @headers = Headers.new
151     @root = nil
152     @boundary = nil
153   end
154
155   def self.parse(head, str)
156     new.parse(head, str)
157   end
158
159   attr_reader :parts, :headers
160
161   def close
162     @headers.add(
163       "Content-Type",
164       "multipart/related; type=\"text/xml\"; boundary=\"#{boundary}\"; start=\"#{@parts[0].contentid}\""
165     )
166   end
167
168   def parse(head, str)
169     @headers = Headers.parse(head + "\r\n" + "From: jfh\r\n")
170     boundary = @headers['content-type']['boundary']
171     if boundary != nil
172       parts = str.split(/--#{Regexp.quote(boundary)}\s*(?:\r\n|--\r\n)/)
173       part = parts.shift        # preamble must be ignored.
174       @parts = parts.collect { |part| Part.parse(part) }
175     else
176       @parts = [Part.parse(str)]
177     end
178     if @parts.length < 1
179       raise MIMEMessageError.new("This message contains no valid parts!")
180     end
181     self
182   end
183
184   def root
185     if @root == nil
186       start = @headers['content-type']['start']
187       @root = (start && @parts.find { |prt| prt.contentid == start }) ||
188         @parts[0]
189     end
190     @root
191   end
192
193   def boundary
194     if @boundary == nil
195       @boundary = "----=Part_" + __id__.to_s + rand.to_s
196     end
197     @boundary
198   end
199
200   def add_part(content)
201     part = Part.new
202     part.headers.add("Content-Type",
203       "text/xml; charset=" + XSD::Charset.xml_encoding_label)
204     part.headers.add("Content-ID", Attachment.contentid(part))
205     part.body = content
206     @parts.unshift(part)
207   end
208
209   def add_attachment(attach)
210     part = Part.new
211     part.headers.add("Content-Type", attach.contenttype)
212     part.headers.add("Content-ID", attach.mime_contentid)
213     part.body = attach.content
214     @parts.unshift(part)
215   end
216
217   def has_parts?
218     (@parts.length > 0)
219   end
220
221   def headers_str
222     @headers.to_s
223   end
224
225   def content_str
226     str = ''
227     @parts.each do |prt|
228       str << "--" + boundary + "\r\n"
229       str << prt.to_s + "\r\n"
230     end
231     str << '--' + boundary + "--\r\n"
232     str
233   end
234
235   def to_s
236     str = headers_str + "\r\n\r\n" + conent_str
237   end
238 end
239
240
241 end
Note: See TracBrowser for help on using the browser.