// // RKParams.m // RestKit // // Created by Blake Watters on 8/3/09. // Copyright 2009 Two Toasters. All rights reserved. // #import "RKParams.h" /** * The boundary used used for multi-part headers */ NSString* const kRKStringBoundary = @"0xKhTmLbOuNdArY"; @implementation RKParams + (RKParams*)params { RKParams* params = [[[RKParams alloc] init] autorelease]; return params; } + (RKParams*)paramsWithDictionary:(NSDictionary*)dictionary { RKParams* params = [[[RKParams alloc] initWithDictionary:dictionary] autorelease]; return params; } - (id)init { self = [super init]; if (self) { _attachments = [NSMutableArray new]; _footer = [[[NSString stringWithFormat:@"--%@--\r\n", kRKStringBoundary] dataUsingEncoding:NSUTF8StringEncoding] retain]; _footerLength = [_footer length]; } return self; } - (void)dealloc { [_attachments release]; [super dealloc]; } - (RKParams*)initWithDictionary:(NSDictionary*)dictionary { self = [self init]; if (self) { for (NSString* key in dictionary) { id value = [dictionary objectForKey:key]; [self setValue:value forParam:key]; } } return self; } - (RKParamsAttachment*)setValue:(id )value forParam:(NSString*)param { RKParamsAttachment* attachment = [[RKParamsAttachment alloc] initWithName:param value:value]; [_attachments addObject:attachment]; [attachment release]; return attachment; } - (RKParamsAttachment*)setFile:(NSString*)filePath forParam:(NSString*)param { RKParamsAttachment* attachment = [[RKParamsAttachment alloc] initWithName:param file:filePath]; [_attachments addObject:attachment]; [attachment release]; return attachment; } - (RKParamsAttachment*)setData:(NSData*)data forParam:(NSString*)param { RKParamsAttachment* attachment = [[RKParamsAttachment alloc] initWithName:param data:data]; [_attachments addObject:attachment]; [attachment release]; return attachment; } - (RKParamsAttachment*)setData:(NSData*)data MIMEType:(NSString*)MIMEType forParam:(NSString*)param { RKParamsAttachment* attachment = [self setData:data forParam:param]; if (MIMEType != nil) { attachment.MIMEType = MIMEType; } return attachment; } - (RKParamsAttachment*)setData:(NSData*)data MIMEType:(NSString*)MIMEType fileName:(NSString*)fileName forParam:(NSString*)param { RKParamsAttachment* attachment = [self setData:data forParam:param]; if (MIMEType != nil) { attachment.MIMEType = MIMEType; } if (fileName != nil) { attachment.fileName = fileName; } return attachment; } - (RKParamsAttachment*)setFile:(NSString*)filePath MIMEType:(NSString*)MIMEType fileName:(NSString*)fileName forParam:(NSString*)param { RKParamsAttachment* attachment = [self setFile:filePath forParam:param]; if (MIMEType != nil) { attachment.MIMEType = MIMEType; } if (fileName != nil) { attachment.fileName = fileName; } return attachment; } #pragma mark RKRequestSerializable methods - (NSString*)HTTPHeaderValueForContentType { return [NSString stringWithFormat:@"multipart/form-data; boundary=%@", kRKStringBoundary]; } - (NSUInteger)HTTPHeaderValueForContentLength { return _length; } - (NSInputStream*)HTTPBodyStream { // Open each of our attachments [_attachments makeObjectsPerformSelector:@selector(open)]; // Calculate the length of the stream _length = _footerLength; for (RKParamsAttachment* attachment in _attachments) { _length += [attachment length]; } return (NSInputStream*)self; } #pragma mark NSInputStream methods - (NSInteger)read:(uint8_t *)buffer maxLength:(NSUInteger)maxLength { NSUInteger bytesSentInThisRead = 0, bytesRead; NSUInteger lengthOfAttachments = (_length - _footerLength); // Proxy the read through to our attachments _streamStatus = NSStreamStatusReading; while (_bytesDelivered < _length && bytesSentInThisRead < maxLength && _currentPart < [_attachments count]) { if ((bytesRead = [[_attachments objectAtIndex:_currentPart] read:buffer + bytesSentInThisRead maxLength:maxLength - bytesSentInThisRead]) == 0) { _currentPart ++; continue; } bytesSentInThisRead += bytesRead; _bytesDelivered += bytesRead; } // If we have sent all the attachments data, begin emitting the boundary footer if ((_bytesDelivered >= lengthOfAttachments) && (bytesSentInThisRead < maxLength)) { NSUInteger footerBytesSent, footerBytesRemaining, bytesRemainingInBuffer; // Calculate our position in the stream & buffer footerBytesSent = _bytesDelivered - lengthOfAttachments; footerBytesRemaining = _footerLength - footerBytesSent; bytesRemainingInBuffer = maxLength - bytesSentInThisRead; // Send the entire footer back if there is room bytesRead = (footerBytesRemaining < bytesRemainingInBuffer) ? footerBytesRemaining : bytesRemainingInBuffer; [_footer getBytes:buffer + bytesSentInThisRead range:NSMakeRange(footerBytesSent, bytesRead)]; bytesSentInThisRead += bytesRead; _bytesDelivered += bytesRead; } return bytesSentInThisRead; } - (BOOL)getBuffer:(uint8_t **)buffer length:(NSUInteger *)len { return NO; } - (BOOL)hasBytesAvailable { return _bytesDelivered < _length; } - (void)open { _streamStatus = NSStreamStatusOpen; } - (void)close { _streamStatus = NSStreamStatusClosed; } - (NSStreamStatus)streamStatus { if (_streamStatus != NSStreamStatusClosed && _bytesDelivered >= _length) { _streamStatus = NSStreamStatusAtEnd; } return _streamStatus; } #pragma mark Core Foundation stream methods - (void)_scheduleInCFRunLoop:(NSRunLoop *)runLoop forMode:(id)mode { } - (void)_setCFClientFlags:(CFOptionFlags)flags callback:(CFReadStreamClientCallBack)callback context:(CFStreamClientContext)context { } @end