View Javadoc
1   /*
2    * ====================================================================
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *   http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing,
14   * software distributed under the License is distributed on an
15   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16   * KIND, either express or implied.  See the License for the
17   * specific language governing permissions and limitations
18   * under the License.
19   * ====================================================================
20   *
21   * This software consists of voluntary contributions made by many
22   * individuals on behalf of the Apache Software Foundation.  For more
23   * information on the Apache Software Foundation, please see
24   * <http://www.apache.org/>.
25   *
26   */
27  
28  package org.apache.hc.client5.http.entity.mime;
29  
30  import java.io.IOException;
31  import java.io.OutputStream;
32  import java.nio.charset.Charset;
33  import java.nio.charset.StandardCharsets;
34  import java.util.List;
35  
36  import org.apache.hc.core5.http.NameValuePair;
37  import org.apache.hc.core5.net.PercentCodec;
38  
39  class HttpRFC7578Multipart extends AbstractMultipartFormat {
40  
41      private final List<MultipartPart> parts;
42  
43      private final HttpMultipartMode mode;
44  
45      /**
46       * Constructs a new instance of {@code HttpRFC7578Multipart} with the given charset, boundary, parts, preamble, and epilogue.
47       *
48       * @param charset  the charset to use.
49       * @param boundary the boundary string to use.
50       * @param parts    the list of parts to include in the multipart message.
51       * @param preamble the optional preamble string to include before the first part. May be {@code null}.
52       * @param epilogue the optional epilogue string to include after the last part. May be {@code null}.
53       */
54      public HttpRFC7578Multipart(
55          final Charset charset,
56          final String boundary,
57          final List<MultipartPart> parts,
58          final String preamble,
59          final String epilogue,
60          final HttpMultipartMode mode) {
61          super(charset, boundary, preamble, epilogue);
62          this.parts = parts;
63          this.mode = mode != null ? mode : HttpMultipartMode.STRICT; // Default to STRICT
64      }
65  
66      /**
67       * Constructs a new instance of {@code HttpRFC7578Multipart} with the given charset, boundary, and parts.
68       *
69       * @param charset  the charset to use.
70       * @param boundary the boundary string to use.
71       * @param parts    the list of parts to include in the multipart message.
72       */
73      public HttpRFC7578Multipart(
74              final Charset charset,
75              final String boundary,
76              final List<MultipartPart> parts,
77              final HttpMultipartMode mode) {
78          this(charset,boundary,parts,null, null, mode);
79      }
80  
81  
82      @Override
83      public List<MultipartPart> getParts() {
84          return parts;
85      }
86  
87      @Override
88      protected void formatMultipartHeader(final MultipartPart part, final OutputStream out) throws IOException {
89          for (final MimeField field: part.getHeader()) {
90              if (MimeConsts.CONTENT_DISPOSITION.equalsIgnoreCase(field.getName())) {
91                  writeBytes(field.getName(), charset, out);
92                  writeBytes(FIELD_SEP, out);
93                  writeBytes(field.getValue(), out);
94                  final List<NameValuePair> parameters = field.getParameters();
95                  for (int i = 0; i < parameters.size(); i++) {
96                      final NameValuePair parameter = parameters.get(i);
97                      final String name = parameter.getName();
98                      final String value = parameter.getValue();
99                      writeBytes("; ", out);
100                     writeBytes(name, out);
101                     writeBytes("=\"", out);
102                     if (value != null) {
103                         if (name.equalsIgnoreCase(MimeConsts.FIELD_PARAM_FILENAME_START)) {
104                             final String encodedValue = "UTF-8''" + PercentCodec.RFC5987.encode(value);
105                             writeBytes(encodedValue, StandardCharsets.US_ASCII, out);
106                         } else if (name.equalsIgnoreCase(MimeConsts.FIELD_PARAM_FILENAME)) {
107                             if (mode == HttpMultipartMode.EXTENDED) {
108                                 final String encodedValue = PercentCodec.RFC5987.encode(value);
109                                 writeBytes(encodedValue, StandardCharsets.US_ASCII, out);
110                             } else {
111                                 // Default to ISO-8859-1 for RFC 7578 compliance in STRICT/LEGACY
112                                 writeBytes(value, StandardCharsets.ISO_8859_1, out);
113                             }
114                         } else {
115                             writeBytes(value, out);
116                         }
117                     }
118                     writeBytes("\"", out);
119                 }
120                 writeBytes(CR_LF, out);
121             } else {
122                 writeField(field, charset, out);
123             }
124         }
125     }
126 
127 }