File: composite_media.rb

package info (click to toggle)
ruby-mime 0.4.4-2
  • links: PTS
  • area: main
  • in suites: bookworm, bullseye, buster, forky, sid, trixie
  • size: 284 kB
  • sloc: ruby: 1,187; xml: 17; makefile: 2
file content (249 lines) | stat: -rw-r--r-- 7,576 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
module MIME

  #
  # Composite media types allow encapsulating, mixing, and hierarchical
  # structuring of entities of different types within a single message.
  # Therefore, a CompositeMedia body is composed of one or more CompositeMedia
  # and/or DiscreteMedia objects.
  #
  # CompositeMedia implements Content-Disposition for dictating presentation
  # style of body entities via #add, #attach, and #inline. For more information
  # on disposition parameters, such as filename, size, and modification-date,
  # see https://tools.ietf.org/html/rfc2183.
  #
  # This class is abstract.
  #
  class CompositeMedia < Media

    class Body
      #
      # Create new composite body.
      #
      def initialize boundary
        @boundary = boundary
        @body = Array.new
      end

      #
      # Format the CompositeMedia object as a MIME message.
      #
      def to_s
        all_entities = @body.join("\r\n--#{@boundary}\r\n")
        "--#{@boundary}\r\n#{all_entities}\r\n--#{@boundary}--\r\n"
      end

      #
      # Add +entity+ to the composite body.
      #
      def add entity
        @body.push(entity)
      end
    end


    attr_reader :boundary

    def initialize content_type
      AbstractClassError.no_instantiation(self, CompositeMedia)
      @boundary = "Boundary_#{ID.generate_id}"  # delimits body entities
      super(Body.new(boundary), content_type, 'boundary' => boundary)
    end

    #
    # Add a Media +entity+ to the message.
    #
    # The entity will be added to the main body of the message with no
    # disposition specified. Presentation of the entity will be dictated by
    # the display user agent.
    #
    # === Text and HTML Multipart/Alternative message
    #
    # A display user agent may only be capable of displaying plain text. If so,
    # it will choose to display the Text/Plain entity. However, if it is capable
    # of displaying HTML, it may choose to display the Text/HTML version.
    #
    #   msg = MIME::Multipart::Alternative.new
    #   msg.add(MIME::Text.new('plain text'))
    #   msg.add(MIME::Text.new('<html>html text</html>', 'html'))
    #
    # The order in which the entities are added is significant. Add the simplest
    # representations first or in increasing order of complexity.
    #
    def add entity
      raise Error.new('can only add Media objects') unless entity.is_a? Media
      @body.add(entity)
    end

    #
    # Attach a Media +entity+ to the message.
    #
    # The entity will be presented as separate from the main body of the
    # message. Thus, display of the entity will not be automatic, but contingent
    # upon some further action of the user. For example, the display user agent
    # may present an icon representation of the entity, which the user can
    # select to view or save the entity.
    #
    # === Attachment with filename and size parameters:
    #
    #   f = File.open('file.txt')
    #   file = MIME::Text.new(f.read)
    #   text = MIME::Text.new('See the attached file.')
    #
    #   msg = MIME::Multipart::Mixed.new
    #   msg.inline(text)
    #   msg.attach(file, 'filename' => f.path, 'size' => f.size)
    #
    def attach entity, params = {}
      entity.set_disposition('attachment', params)
      add(entity)
    end

    #
    # Inline a Media +entity+ in the message.
    #
    # The entity will be embedded within the main body of the message. Thus,
    # display of the entity will be automatic upon display of the message.
    # Inline entities should be added in the order in which they occur within
    # the message.
    #
    # === Message with two embedded images:
    #
    #  msg = MIME::Multipart::Mixed.new
    #  msg.inline(MIME::Image.new(File.read('screenshot1.png'), 'png'))
    #  msg.inline(MIME::Image.new(File.read('screenshot2.png'), 'png'))
    #  msg.description = 'My screenshots'
    #
    def inline entity, params = {}
      entity.set_disposition('inline', params)
      add(entity)
    end

  end

  #
  # Message is intended to encapsulate another message. In particular, the
  # <em>message/rfc822</em> content type is used to encapsulate RFC 822
  # messages.
  #
  # TODO Implement
  #
  class Message < CompositeMedia
  end

  #
  # The abstract base class for all multipart message subtypes. The entities of
  # a multipart message are delimited by a unique boundary.
  #
  class Multipart < CompositeMedia
    def initialize media_subtype
      AbstractClassError.no_instantiation(self, Multipart)
      super("multipart/#{media_subtype}")
    end
  end

  #
  # The Alternative subtype indicates that each contained entity is an
  # alternatively formatted version of the same content.
  #
  # In general, when composing multipart/alternative entities, add the body
  # parts in increasing order of preference; that is, with the preferred format
  # last. For example, to prioritize for fancy text, add the plainest format
  # first and the richest format last. Typically, receiving user agents select
  # the last format they are capable of displaying or interpreting.
  #
  class Multipart::Alternative < Multipart

    #
    # Returns a Multipart::Alternative object with a content type of
    # multipart/alternative.
    #
    def initialize
      super('alternative')
    end

  end

  #
  # The FormData subtype expresses values for HTML form data submissions.
  # ---
  # RFCs consulted during implementation:
  #
  # * RFC-1867  Form-based File Upload in HTML
  # * RFC-2388  Returning Values from Forms: multipart/form-data
  #
  class Multipart::FormData < Multipart

    #
    # Returns a Multipart::FormData object with a content type of
    # multipart/form-data.
    #
    def initialize
      super('form-data')
    end

    #
    # Add the Media object, +entity+, to the FormData object. +name+ is
    # typically an HTML input tag variable name. If the input tag is of type
    # _file_, then +filename+ must be specified to indicate a file upload.
    #
    def add entity, name, filename = nil
      entity.set_disposition('form-data', 'name' => name, 'filename' => filename)
      super(entity)
    end

  end

  #
  # The Mixed subtype aggregates contextually independent entities.
  #
  class Multipart::Mixed < Multipart

    #
    # Returns a Multipart::Mixed object with a content type of
    # multipart/mixed.
    #
    def initialize
      super('mixed')
    end

  end

  #
  # The Related subtype aggregates multiple related entities. The message
  # consists of a root (the first entity) which references subsequent inline
  # entities. Message entities should be referenced by their Content-ID header.
  # The syntax of a reference is unspecified and is instead dictated by the
  # encoding or protocol used in the entity.
  # ---
  # RFC consulted during implementation:
  # 
  # * RFC-2387  The MIME Multipart/Related Content-type
  #
  class Multipart::Related < Multipart

    #
    # Returns a Multipart::Related object with a content type of
    # multipart/related.
    #
    def initialize
      super('related')
    end

    #
    # The first entity added becomes the root object. The related message
    # type is set to the value of the root object media type.
    #--
    # The "type" parameter is required and should equal the root media type.
    # https://tools.ietf.org/html/rfc2387#section-3.1
    #
    def add entity
      unless type.include? '; type='
        root_type = entity.type.partition(';').first  # omit parameters
        self.type = append_field_params(type, 'type' => root_type)
      end
      super
    end

  end

end