2 // OAMutableURLRequest.m
5 // Created by Jon Crosby on 10/19/07.
6 // Copyright 2007 Kaboomerang LLC. All rights reserved.
7 // Modified by Cassie Doll on 02/02/09
9 // Permission is hereby granted, free of charge, to any person obtaining a copy
10 // of this software and associated documentation files (the "Software"), to deal
11 // in the Software without restriction, including without limitation the rights
12 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 // copies of the Software, and to permit persons to whom the Software is
14 // furnished to do so, subject to the following conditions:
16 // The above copyright notice and this permission notice shall be included in
17 // all copies or substantial portions of the Software.
19 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
28 #import "OAMutableURLRequest.h"
29 #import "OAConsumer.h"
31 #import "OAHMAC_SHA1SignatureProvider.h"
32 #import "OARequestParameter.h"
34 @interface OAMutableURLRequest ()
35 @property (nonatomic, retain) id<OASignatureProviding> signatureProvider;
36 - (NSString *)_generateTimestamp;
37 - (NSString *)_generateNonce;
38 - (NSString *)_signatureBaseString;
39 - (void)_putParametersInRequest;
40 - (NSString *)_getNormalizedRequestParameters;
43 @implementation OAMutableURLRequest
44 @synthesize signatureProvider;
45 @synthesize signature, nonce, parameters;
49 - (id)initWithURL:(NSURL *)aUrl consumer:(OAConsumer *)aConsumer token:(OAToken *)aToken {
50 return [self initWithURL:aUrl parameters:nil consumer:aConsumer token:aToken];
53 - (id)initWithURL:(NSURL *)aUrl parameters:(NSArray *)extraParameters
54 consumer:(OAConsumer *)aConsumer token:(OAToken *)aToken {
55 return [self initWithURL:aUrl parameters:extraParameters consumer:aConsumer token:aToken realm:nil
56 signatureProvider:nil nonce:[self _generateNonce] timestamp:[self _generateTimestamp]];
59 - (id)initWithURL:(NSURL *)aUrl consumer:(OAConsumer *)aConsumer token:(OAToken *)aToken
60 realm:(NSString *)aRealm signatureProvider:(id<OASignatureProviding, NSObject>)aProvider {
61 return [self initWithURL:aUrl parameters:nil consumer:aConsumer token:aToken realm:aRealm
62 signatureProvider:aProvider nonce:[self _generateNonce] timestamp:[self _generateTimestamp]];
65 // Setting a timestamp and nonce to known
66 // values can be helpful for testing
67 - (id)initWithURL:(NSURL *)aUrl
68 parameters:(NSArray *)extraParameters
69 consumer:(OAConsumer *)aConsumer
70 token:(OAToken *)aToken
71 realm:(NSString *)aRealm
72 signatureProvider:(id<OASignatureProviding, NSObject>)aProvider
73 nonce:(NSString *)aNonce
74 timestamp:(NSString *)aTimestamp {
75 if (self = [super initWithURL:aUrl
76 cachePolicy:NSURLRequestReloadIgnoringCacheData
77 timeoutInterval:20.0]) {
78 consumer = [aConsumer retain];
80 // empty token for Unauthorized Request Token transaction
84 token = [aToken retain];
90 realm = [aRealm retain];
93 // default to HMAC-SHA1
94 if (aProvider == nil) {
95 self.signatureProvider = [[[OAHMAC_SHA1SignatureProvider alloc] init] autorelease];
97 self.signatureProvider = aProvider;
100 timestamp = [aTimestamp retain];
101 nonce = [aNonce retain];
103 if (extraParameters) {
104 self.parameters = [NSMutableArray arrayWithArray:extraParameters];
106 self.parameters = [NSMutableArray arrayWithCapacity:7];
116 [signatureProvider release];
119 [parameters release];
127 // Add in all of the oauth parameters
128 [parameters addObject:[OARequestParameter requestParameterWithName:@"oauth_consumer_key" value:consumer.key]];
129 [parameters addObject:[OARequestParameter requestParameterWithName:@"oauth_signature_method" value:[signatureProvider name]]];
130 [parameters addObject:[OARequestParameter requestParameterWithName:@"oauth_timestamp" value:timestamp]];
131 [parameters addObject:[OARequestParameter requestParameterWithName:@"oauth_nonce" value:nonce]];
132 [parameters addObject:[OARequestParameter requestParameterWithName:@"oauth_version" value:@"1.0"]];
135 [parameters addObject:[OARequestParameter requestParameterWithName:@"oauth_token" value:token.key]];
138 // TODO: if later RSA-SHA1 support is added then a little code redesign is needed
139 NSString *baseString = [self _signatureBaseString];
140 NSString *secret = [NSString stringWithFormat:@"%@&%@",
141 [consumer.secret URLEncodedString],
142 token ? [token.secret URLEncodedString] : @""];
144 NSLog(@"Base string: %@ and secret: %@", baseString, secret);
145 signature = [signatureProvider signClearText:baseString
148 [parameters addObject:[OARequestParameter requestParameterWithName:@"oauth_signature" value:signature]];
149 [self _putParametersInRequest];
155 - (NSString *)_generateTimestamp {
156 return [NSString stringWithFormat:@"%d", time(NULL)];
159 - (NSString *)_generateNonce {
160 CFUUIDRef theUUID = CFUUIDCreate(NULL);
161 CFStringRef string = CFUUIDCreateString(NULL, theUUID);
162 NSMakeCollectable(theUUID);
163 return [(NSString *)string autorelease];
166 - (NSString *)_signatureBaseString {
167 return [NSString stringWithFormat:@"%@&%@&%@",
169 [[[self URL] absoluteString] URLEncodedString],
170 [[self _getNormalizedRequestParameters] URLEncodedString]];
173 - (void)_putParametersInRequest {
174 NSString *normalizedRequestParameters = [self _getNormalizedRequestParameters];
176 if ([[self HTTPMethod] isEqualToString:@"GET"] || [[self HTTPMethod] isEqualToString:@"DELETE"]) {
177 [self setURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@?%@", [[self URL] absoluteString], normalizedRequestParameters]]];
181 NSData *postData = [normalizedRequestParameters dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];
182 [self setHTTPBody:postData];
183 [self setValue:[NSString stringWithFormat:@"%d", [postData length]] forHTTPHeaderField:@"Content-Length"];
184 [self setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
186 NSString *oauthToken = @"";
188 oauthToken = [NSString stringWithFormat:@"oauth_token=\"%@\", ", [token.key URLEncodedString]];
191 NSString *oauthHeader = [NSString stringWithFormat:@"OAuth realm=\"%@\" oauth_consumer_key=\"%@\", %@oauth_signature_method=\"%@\", oauth_signature=\"%@\", oauth_timestamp=\"%@\", oauth_nonce=\"%@\", oauth_version=\"1.0\"",
192 [realm URLEncodedString],
193 [consumer.key URLEncodedString],
195 [[signatureProvider name] URLEncodedString],
196 [signature URLEncodedString],
200 [self setValue:oauthHeader forHTTPHeaderField:@"Authorization"];
201 [oauthToken release];
202 [oauthHeader release];
206 - (NSString *)_getNormalizedRequestParameters {
207 NSMutableArray *parameterPairs = [NSMutableArray arrayWithCapacity:([parameters count])];
209 for (OARequestParameter *param in parameters) {
210 [parameterPairs addObject:[param URLEncodedNameValuePair]];
213 NSArray* sortedPairs = [parameterPairs sortedArrayUsingSelector:@selector(compare:)];
214 return [sortedPairs componentsJoinedByString:@"&"];