blob: 281ee26ba81b5bd8af5f11b8965c6848be317283 [file] [log] [blame]
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
#import "CMISBroswerFormDataWriter.h"
#import "CMISConstants.h"
#import "CMISBrowserConstants.h"
#import "CMISEnums.h"
#import "CMISLog.h"
#import "CMISMimeHelper.h"
#import "CMISURLUtil.h"
NSString * const kCMISFormDataContentTypeUrlEncoded = @"application/x-www-form-urlencoded;charset=utf-8";
NSString * const kCMISFormDataContentTypeFormData = @"multipart/form-data; boundary=";
@interface CMISBroswerFormDataWriter ()
@property (nonatomic, strong) NSInputStream *contentStream;
@property (nonatomic, strong) NSMutableDictionary *parameters;
@property (nonatomic, strong) NSString *boundary;
@property (nonatomic, strong) NSString *mediaType;
@property (nonatomic, strong) NSString *fileName;
@end
@implementation CMISBroswerFormDataWriter
- (id)initWithAction:(NSString *)action
{
return [self initWithAction:action contentStream:nil mediaType:nil];
}
- (id)initWithAction:(NSString *)action contentStream:(NSInputStream *)contentStream mediaType:(NSString *)mediaType
{
self = [super init];
if (self) {
self.parameters = [[NSMutableDictionary alloc] init];
[self addParameter:kCMISBrowserJSONControlCmisAction value:action];
self.contentStream = contentStream;
self.mediaType = mediaType;
self.boundary = [NSString stringWithFormat:@"aPacHeCheMIStryoBjECtivEcmiS%x%a%x", (unsigned int) action.hash, CFAbsoluteTimeGetCurrent(), (unsigned int) self.hash];
}
return self;
}
- (void)addParameter:(NSString *)name value:(id)value
{
if(!name || !value) {
return;
}
[self.parameters setValue:[value description] forKey:name];
}
- (void)addParameter:(NSString *)name boolValue:(BOOL)value
{
[self addParameter:name value:(value? kCMISParameterValueTrue : kCMISParameterValueFalse)];
}
- (void)addSuccinctFlag:(BOOL)succinct
{
if (succinct) {
[self addParameter:kCMISBrowserJSONParameterSuccinct value:kCMISParameterValueTrue];
}
}
- (void)addPropertiesParameters:(CMISProperties *)properties
{
if (!properties) {
return;
}
if(!self.fileName){
self.fileName = [properties propertyValueForId:kCMISPropertyName];
}
int idx = 0;
for (CMISPropertyData *prop in properties.propertyList) {
NSString *idxStr = [NSString stringWithFormat:@"[%d]", idx];
[self addParameter:[NSString stringWithFormat:@"%@%@", kCMISBrowserJSONControlPropertyId, idxStr] value:prop.identifier];
if (prop.values && prop.values.count > 0) {
if (prop.values.count == 1) {
NSString *value = [self convertPropertyValue:prop.firstValue forPropertyType:prop.type];
[self addParameter:[NSString stringWithFormat:@"%@%@", kCMISBrowserJSONControlPropertyValue, idxStr] value:value];
} else {
int vidx = 0;
for (id obj in prop.values) {
NSString *vidxStr = [NSString stringWithFormat:@"[%d]", vidx];
NSString *value = [self convertPropertyValue:obj forPropertyType:prop.type];
[self addParameter:[NSString stringWithFormat:@"%@%@%@", kCMISBrowserJSONControlPropertyValue, idxStr, vidxStr] value:value];
vidx++;
}
}
}
idx++;
}
}
// TODO should this method be part of CMISPropertyData class (as class method?)
- (NSString *)convertPropertyValue:(id)value forPropertyType:(CMISPropertyType)type
{
if (!value) {
return nil;
}
if (type == CMISPropertyTypeBoolean) {
return [value boolValue] ? kCMISParameterValueTrue : kCMISParameterValueFalse;
} else if (type == CMISPropertyTypeDateTime) {
if ([value isKindOfClass:NSDate.class]) {
return [NSNumber numberWithUnsignedLongLong:[(NSDate *)value timeIntervalSince1970] * 1000].description; //seconds to milliseconds
} else {
CMISLogWarning(@"value is not a date!");
}
}
return value;
}
- (NSDictionary *)headers
{
NSString *contentType = self.contentStream == nil ? kCMISFormDataContentTypeUrlEncoded : [NSString stringWithFormat:@"%@%@", kCMISFormDataContentTypeFormData, self.boundary];
return @{@"Content-Type" : contentType};
}
- (NSData *)body
{
if (self.contentStream == nil) {
BOOL first = YES;
NSData *amp = [@"&" dataUsingEncoding:NSUTF8StringEncoding];
NSMutableData *data = [[NSMutableData alloc] init];
for (NSString *parameterKey in self.parameters) {
if (first) {
first = NO;
} else {
[data appendData:amp];
}
NSString *encodedParameterValue = [CMISURLUtil encodeUrlParameterValue:self.parameters[parameterKey]];
NSString *parameter = [NSString stringWithFormat:@"%@=%@", parameterKey, encodedParameterValue];
[data appendData:[parameter dataUsingEncoding:NSUTF8StringEncoding]];
}
return data;
} else {
CMISLogError(@"this method should not be called when content stream is set. Use startData and endData method to retrieve the data.");
return nil;
}
}
- (NSData *)startData
{
if (self.contentStream) {
NSMutableData *data = [[NSMutableData alloc] init];
[self appendLine:data];
// parameters
for (NSString *paramKey in self.parameters) {
[self appendLine:data string:[NSString stringWithFormat:@"--%@", self.boundary]];
[self appendLine:data string:[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"", paramKey]];
[self appendLine:data string:@"Content-Type: text/plain; charset=utf-8"];
[self appendLine:data];
[self appendLine:data string:self.parameters[paramKey]];
}
// content
if (self.fileName == nil || self.fileName.length == 0) {
self.fileName = @"content";
}
if (self.mediaType == nil ||
[self.mediaType rangeOfString:@"/"].location < 1 ||
[self.mediaType rangeOfString:@"\n"].location > -1 ||
[self.mediaType rangeOfString:@"\r"].location > -1) {
self.mediaType = kCMISMediaTypeOctetStream;
}
[self appendLine:data string:[NSString stringWithFormat:@"--%@", self.boundary]];
[self appendLine:data string:[NSString stringWithFormat:@"Content-Disposition: %@",
[CMISMimeHelper encodeContentDisposition:kCMISMimeHelperDispositionFormDataContent fileName:self.fileName]]];
[self appendLine:data string:[NSString stringWithFormat:@"Content-Type: %@", self.mediaType]];
[self appendLine:data string:@"Content-Transfer-Encoding: binary"];
[self appendLine:data];
return data;
} else {
CMISLogError(@"this method should not be called when content stream is nil. Use body method to retrieve the data.");
return nil;
}
}
- (NSData *)endData
{
if (self.contentStream) {
NSMutableData *data = [[NSMutableData alloc] init];
[self appendLine:data];
[self appendLine:data string:[NSString stringWithFormat:@"--%@--", self.boundary]];
return data;
} else {
CMISLogError(@"this method should not be called when content stream is nil. Use body method to retrieve the data.");
return nil;
}
}
- (void)appendLine:(NSMutableData *)data
{
[self appendLine:data string:nil];
}
- (void)appendLine:(NSMutableData *)data string:(NSString *)s
{
s = s ? [NSString stringWithFormat:@"%@\r\n", s] : @"\r\n";
[data appendData:[s dataUsingEncoding:NSUTF8StringEncoding]];
}
@end