]> git.donarmstrong.com Git - dsa-puppet.git/blob - 3rdparty/modules/aviator/feature/parts.rb
c06cbd95d1f2d9d0a7a3da6cf404cb89f6d1fb1e
[dsa-puppet.git] / 3rdparty / modules / aviator / feature / parts.rb
1 #--
2 # Copyright (c) 2007-2013 Nick Sieger.
3 # See the file README.txt included with the distribution for
4 # software license details.
5 #++
6
7 module Parts
8   module Part #:nodoc:
9     def self.new(boundary, name, value, headers = {})
10       headers ||= {} # avoid nil values
11       if file?(value)
12         FilePart.new(boundary, name, value, headers)
13       else
14         ParamPart.new(boundary, name, value, headers)
15       end
16     end
17
18     def self.file?(value)
19       value.respond_to?(:content_type) && value.respond_to?(:original_filename)
20     end
21
22     def length
23       @part.length
24     end
25
26     def to_io
27       @io
28     end
29   end
30
31   class ParamPart
32     include Part
33     def initialize(boundary, name, value, headers = {})
34       @part = build_part(boundary, name, value, headers)
35       @io = StringIO.new(@part)
36     end
37
38     def length
39      @part.bytesize
40     end
41
42     def build_part(boundary, name, value, headers = {})
43       part = ''
44       part << "--#{boundary}\r\n"
45       part << "Content-Disposition: form-data; name=\"#{name.to_s}\"\r\n"
46       part << "Content-Type: #{headers["Content-Type"]}\r\n" if headers["Content-Type"]
47       part << "\r\n"
48       part << "#{value}\r\n"
49     end
50   end
51
52   # Represents a part to be filled from file IO.
53   class FilePart
54     include Part
55     attr_reader :length
56     def initialize(boundary, name, io, headers = {})
57       file_length = io.respond_to?(:length) ?  io.length : File.size(io.local_path)
58       @head = build_head(boundary, name, io.original_filename, io.content_type, file_length,
59                          io.respond_to?(:opts) ? io.opts.merge(headers) : headers)
60       @foot = "\r\n"
61       @length = @head.bytesize + file_length + @foot.length
62       @io = CompositeReadIO.new(StringIO.new(@head), io, StringIO.new(@foot))
63     end
64
65     def build_head(boundary, name, filename, type, content_len, opts = {}, headers = {})
66       trans_encoding = opts["Content-Transfer-Encoding"] || "binary"
67       content_disposition = opts["Content-Disposition"] || "form-data"
68
69       part = ''
70       part << "--#{boundary}\r\n"
71       part << "Content-Disposition: #{content_disposition}; name=\"#{name.to_s}\"; filename=\"#{filename}\"\r\n"
72       part << "Content-Length: #{content_len}\r\n"
73       if content_id = opts["Content-ID"]
74         part << "Content-ID: #{content_id}\r\n"
75       end
76
77       if headers["Content-Type"] != nil
78         part <<  "Content-Type: " + headers["Content-Type"] + "\r\n"
79       else
80         part << "Content-Type: #{type}\r\n"
81       end
82
83       part << "Content-Transfer-Encoding: #{trans_encoding}\r\n"
84       part << "\r\n"
85     end
86   end
87
88   # Represents the epilogue or closing boundary.
89   class EpiloguePart
90     include Part
91     def initialize(boundary)
92       @part = "--#{boundary}--\r\n\r\n"
93       @io = StringIO.new(@part)
94     end
95   end
96 end