/ src / common / mac / HTTPMultipartUpload.m
HTTPMultipartUpload.m
  1  // Copyright 2006 Google LLC
  2  //
  3  // Redistribution and use in source and binary forms, with or without
  4  // modification, are permitted provided that the following conditions are
  5  // met:
  6  //
  7  //     * Redistributions of source code must retain the above copyright
  8  // notice, this list of conditions and the following disclaimer.
  9  //     * Redistributions in binary form must reproduce the above
 10  // copyright notice, this list of conditions and the following disclaimer
 11  // in the documentation and/or other materials provided with the
 12  // distribution.
 13  //     * Neither the name of Google LLC nor the names of its
 14  // contributors may be used to endorse or promote products derived from
 15  // this software without specific prior written permission.
 16  //
 17  // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 18  // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 19  // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 20  // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 21  // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 22  // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 23  // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 24  // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 25  // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 26  // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 27  // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 28  
 29  #import "HTTPMultipartUpload.h"
 30  
 31  #import "GTMDefines.h"
 32  #import "encoding_util.h"
 33  
 34  @interface HTTPMultipartUpload (PrivateMethods)
 35  - (NSString*)multipartBoundary;
 36  // Each of the following methods will append the starting multipart boundary,
 37  // but not the ending one.
 38  - (NSData*)formDataForKey:(NSString*)key value:(NSString*)value;
 39  - (NSData*)formDataForFileContents:(NSData*)contents name:(NSString*)name;
 40  - (NSData*)formDataForFile:(NSString*)file name:(NSString*)name;
 41  @end
 42  
 43  @implementation HTTPMultipartUpload
 44  //=============================================================================
 45  #pragma mark -
 46  #pragma mark || Private ||
 47  //=============================================================================
 48  - (NSString*)multipartBoundary {
 49    // The boundary has 27 '-' characters followed by 16 hex digits
 50    return [NSString
 51        stringWithFormat:@"---------------------------%08X%08X", rand(), rand()];
 52  }
 53  
 54  //=============================================================================
 55  - (NSData*)formDataForKey:(NSString*)key value:(NSString*)value {
 56    NSMutableData* data = [NSMutableData data];
 57    [self appendBoundaryData:data];
 58  
 59    NSString* escaped = PercentEncodeNSString(key);
 60    NSString* fmt = @"Content-Disposition: form-data; name=\"%@\"\r\n\r\n%@\r\n";
 61    NSString *form = [NSString stringWithFormat:fmt, escaped, value];
 62  
 63    [data appendData:[form dataUsingEncoding:NSUTF8StringEncoding]];
 64    return data;
 65  }
 66  
 67  //=============================================================================
 68  - (void)appendBoundaryData:(NSMutableData*)data {
 69    NSString* fmt = @"--%@\r\n";
 70    NSString* pre = [NSString stringWithFormat:fmt, boundary_];
 71  
 72    [data appendData:[pre dataUsingEncoding:NSUTF8StringEncoding]];
 73  }
 74  
 75  //=============================================================================
 76  #pragma mark -
 77  #pragma mark || Public ||
 78  //=============================================================================
 79  - (id)initWithURL:(NSURL*)url {
 80    if ((self = [super initWithURL:url])) {
 81      boundary_ = [[self multipartBoundary] retain];
 82      files_ = [[NSMutableDictionary alloc] init];
 83    }
 84  
 85    return self;
 86  }
 87  
 88  //=============================================================================
 89  - (void)dealloc {
 90    [parameters_ release];
 91    [files_ release];
 92    [boundary_ release];
 93  
 94    [super dealloc];
 95  }
 96  
 97  //=============================================================================
 98  - (void)setParameters:(NSDictionary*)parameters {
 99    if (parameters != parameters_) {
100      [parameters_ release];
101      parameters_ = [parameters copy];
102    }
103  }
104  
105  //=============================================================================
106  - (NSDictionary*)parameters {
107    return parameters_;
108  }
109  
110  //=============================================================================
111  - (void)addFileAtPath:(NSString*)path name:(NSString*)name {
112    [files_ setObject:path forKey:name];
113  }
114  
115  //=============================================================================
116  - (void)addFileContents:(NSData*)data name:(NSString*)name {
117    [files_ setObject:data forKey:name];
118  }
119  
120  //=============================================================================
121  - (NSDictionary*)files {
122    return files_;
123  }
124  
125  //=============================================================================
126  - (NSString*)HTTPMethod {
127    return @"POST";
128  }
129  
130  //=============================================================================
131  - (NSString*)contentType {
132    return [NSString
133        stringWithFormat:@"multipart/form-data; boundary=%@", boundary_];
134  }
135  
136  //=============================================================================
137  - (NSData*)bodyData {
138    NSMutableData* postBody = [NSMutableData data];
139  
140    // Add any parameters to the message
141    NSArray* parameterKeys = [parameters_ allKeys];
142    NSString* key;
143  
144    NSInteger count = [parameterKeys count];
145    for (NSInteger i = 0; i < count; ++i) {
146      key = [parameterKeys objectAtIndex:i];
147      [postBody appendData:[self formDataForKey:key
148                                          value:[parameters_ objectForKey:key]]];
149    }
150  
151    // Add any files to the message
152    NSArray* fileNames = [files_ allKeys];
153    for (NSString* name in fileNames) {
154      // First append boundary
155      [self appendBoundaryData:postBody];
156      // Then the formdata
157      id fileOrData = [files_ objectForKey:name];
158      [HTTPRequest appendFileToBodyData:postBody
159                               withName:name
160                         withFileOrData:fileOrData];
161    }
162  
163    NSString* epilogue = [NSString stringWithFormat:@"\r\n--%@--\r\n", boundary_];
164    [postBody appendData:[epilogue dataUsingEncoding:NSUTF8StringEncoding]];
165  
166    return postBody;
167  }
168  
169  @end