summary refs log tree commit diff stats
path: root/libs/cocoslive/CLScoreServerPost.m
diff options
context:
space:
mode:
Diffstat (limited to 'libs/cocoslive/CLScoreServerPost.m')
-rwxr-xr-xlibs/cocoslive/CLScoreServerPost.m335
1 files changed, 335 insertions, 0 deletions
diff --git a/libs/cocoslive/CLScoreServerPost.m b/libs/cocoslive/CLScoreServerPost.m new file mode 100755 index 0000000..43ee7b2 --- /dev/null +++ b/libs/cocoslive/CLScoreServerPost.m
@@ -0,0 +1,335 @@
1/*
2 * cocos2d for iPhone: http://www.cocos2d-iphone.org
3 *
4 * Copyright (c) 2008-2010 Ricardo Quesada
5 * Copyright (c) 2011 Zynga Inc.
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining a copy
8 * of this software and associated documentation files (the "Software"), to deal
9 * in the Software without restriction, including without limitation the rights
10 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 * copies of the Software, and to permit persons to whom the Software is
12 * furnished to do so, subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be included in
15 * all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 * THE SOFTWARE.
24 *
25 */
26
27
28#import "CLScoreServerPost.h"
29#import "ccMacros.h"
30
31// free function used to sort
32NSInteger alphabeticSort(id string1, id string2, void *reverse);
33
34NSInteger alphabeticSort(id string1, id string2, void *reverse)
35{
36 if ((NSInteger *)reverse == NO)
37 return [string2 localizedCaseInsensitiveCompare:string1];
38 return [string1 localizedCaseInsensitiveCompare:string2];
39}
40
41
42@interface CLScoreServerPost (Private)
43-(void) addValue:(NSString*)value key:(NSString*)key;
44-(void) calculateHashAndAddValue:(id)value key:(NSString*)key;
45-(NSString*) getHashForData;
46-(NSData*) getBodyValues;
47-(NSString*) encodeData:(NSString*)data;
48-(NSMutableURLRequest *) scoreServerRequestWithURLString:(NSString *)url;
49-(BOOL) submitScore:(NSDictionary*)dict forUpdate:(BOOL)isUpdate;
50@end
51
52
53@implementation CLScoreServerPost
54
55@synthesize postStatus = postStatus_;
56@synthesize ranking = ranking_;
57@synthesize scoreDidUpdate = scoreDidUpdate_;
58@synthesize connection = connection_;
59
60+(id) serverWithGameName:(NSString*) name gameKey:(NSString*) key delegate:(id) delegate
61{
62 return [[[self alloc] initWithGameName:name gameKey:key delegate:delegate] autorelease];
63}
64
65-(id) initWithGameName:(NSString*) name gameKey:(NSString*) key delegate:(id)aDelegate
66{
67 self = [super init];
68 if( self ) {
69 gameKey = [key retain];
70 gameName = [name retain];
71 bodyValues = [[NSMutableArray arrayWithCapacity:5] retain];
72 delegate = [aDelegate retain];
73 receivedData = [[NSMutableData data] retain];
74
75 ranking_ = kServerPostInvalidRanking;
76 }
77
78 return self;
79}
80
81-(void) dealloc
82{
83 CCLOGINFO(@"deallocing %@", self);
84 [delegate release];
85 [gameKey release];
86 [gameName release];
87 [bodyValues release];
88 [receivedData release];
89 [connection_ release];
90 [super dealloc];
91}
92
93
94#pragma mark ScoreServer send scores
95-(BOOL) sendScore: (NSDictionary*) dict
96{
97 return [self submitScore:dict forUpdate:NO];
98}
99
100-(BOOL) updateScore: (NSDictionary*) dict
101{
102 if (![dict objectForKey:@"cc_playername"]) {
103 // fail. cc_playername + cc_device_id are needed to update an score
104 [NSException raise:@"cocosLive:updateScore" format:@"cc_playername not found"];
105 }
106 return [self submitScore:dict forUpdate:YES];
107}
108
109-(BOOL) submitScore: (NSDictionary*)dict forUpdate:(BOOL)isUpdate
110{
111 [receivedData setLength:0];
112 [bodyValues removeAllObjects];
113
114 // reset status
115 postStatus_ = kPostStatusOK;
116
117 // create the request
118 NSMutableURLRequest *post = [self scoreServerRequestWithURLString:(isUpdate ? SCORE_SERVER_UPDATE_URL : SCORE_SERVER_SEND_URL)];
119
120 CC_MD5_Init( &md5Ctx);
121
122 // hash SHALL be calculated in certain order
123 NSArray *keys = [dict allKeys];
124 int reverseSort = NO;
125 NSArray *sortedKeys = [keys sortedArrayUsingFunction:alphabeticSort context:&reverseSort];
126 for( id key in sortedKeys )
127 [self calculateHashAndAddValue:[dict objectForKey:key] key:key];
128
129 // device id is hashed to prevent spoofing this same score from different devices
130 // one way to prevent a replay attack is to send cc_id & cc_time and use it as primary keys
131
132 [self addValue:[[UIDevice currentDevice] uniqueIdentifier] key:@"cc_device_id"];
133 [self addValue:gameName key:@"cc_gamename"];
134 [self addValue:[self getHashForData] key:@"cc_hash"];
135 [self addValue:SCORE_SERVER_PROTOCOL_VERSION key:@"cc_prot_ver"];
136
137 [post setHTTPBody: [self getBodyValues] ];
138
139 // create the connection with the request
140 // and start loading the data
141 self.connection=[NSURLConnection connectionWithRequest:post delegate:self];
142
143 if ( ! connection_)
144 return NO;
145
146 return YES;
147}
148
149-(NSMutableURLRequest *) scoreServerRequestWithURLString:(NSString *)url {
150 NSMutableURLRequest *request=[NSMutableURLRequest requestWithURL:[NSURL URLWithString: url]
151 cachePolicy:NSURLRequestUseProtocolCachePolicy
152 timeoutInterval:10.0];
153
154 [request setHTTPMethod: @"POST"];
155 [request setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
156 return request;
157}
158
159-(void) calculateHashAndAddValue:(id) value key:(NSString*) key
160{
161 NSString *val;
162 // value shall be a string or nsnumber
163 if( [value respondsToSelector:@selector(stringValue)] )
164 val = [value stringValue];
165 else if( [value isKindOfClass:[NSString class]] )
166 val = value;
167 else
168 [NSException raise:@"Invalid format for value" format:@"Invalid format for value. addValue"];
169
170 [self addValue:val key:key];
171
172 const char * data = [val UTF8String];
173 CC_MD5_Update( &md5Ctx, data, strlen(data) );
174}
175
176-(void) addValue:(NSString*)value key:(NSString*) key
177{
178
179 NSString *encodedValue = [self encodeData:value];
180 NSString *encodedKey = [self encodeData:key];
181
182 [bodyValues addObject: [NSString stringWithFormat:@"%@=%@", encodedKey, encodedValue] ];
183}
184
185-(NSData*) getBodyValues {
186 NSMutableData *data = [[NSMutableData alloc] init];
187
188 BOOL first=YES;
189 for( NSString *s in bodyValues ) {
190 if( !first)
191 [data appendBytes:"&" length:1];
192
193 [data appendBytes:[s UTF8String] length:[s length]];
194 first = NO;
195 }
196
197 return [data autorelease];
198}
199
200-(NSString*) getHashForData
201{
202 NSString *ret;
203 unsigned char pTempKey[16];
204
205 // update the hash with the secret key
206 const char *data = [gameKey UTF8String];
207 CC_MD5_Update(&md5Ctx, data, strlen(data));
208
209 // then get the hash
210 CC_MD5_Final( pTempKey, &md5Ctx);
211
212// NSData *nsdata = [NSData dataWithBytes:pTempKey length:16];
213 ret = [NSString stringWithString:@""];
214 for( int i=0;i<16;i++) {
215 ret = [NSString stringWithFormat:@"%@%02x", ret, pTempKey[i] ];
216 }
217
218 return ret;
219}
220
221-(NSString*) encodeData:(NSString*) data
222{
223 NSString *newData;
224
225 newData = [data stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
226
227 // '&' and '=' should be encoded manually
228 newData = [newData stringByReplacingOccurrencesOfString:@"&" withString:@"%26"];
229 newData = [newData stringByReplacingOccurrencesOfString:@"=" withString:@"%3D"];
230
231 return newData;
232}
233
234#pragma mark NSURLConnection Delegate
235
236- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
237{
238 // this method is called when the server has determined that it
239 // has enough information to create the NSURLResponse
240
241 // it can be called multiple times, for example in the case of a
242 // redirect, so each time we reset the data.
243 // receivedData is declared as a method instance elsewhere
244 [receivedData setLength:0];
245}
246
247- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
248{
249 // append the new data to the receivedData
250 // receivedData is declared as a method instance elsewhere
251 [receivedData appendData:data];
252
253// NSString *dataString = [NSString stringWithCString:[data bytes] length: [data length]];
254// CCLOG( @"data: %@", dataString);
255}
256
257- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
258{
259 CCLOG(@"Connection failed");
260
261 // wifi problems ?
262 postStatus_ = kPostStatusConnectionFailed;
263
264 // release the connection
265 self.connection = nil;
266
267 if( [delegate respondsToSelector:@selector(scorePostFail:) ] )
268 [delegate scorePostFail:self];
269}
270
271- (void)connectionDidFinishLoading:(NSURLConnection *)connection
272{
273 // release the connection
274 self.connection = nil;
275
276// NSString *dataString = [NSString stringWithCString:[receivedData bytes] length: [receivedData length]];
277 NSString *dataString = [NSString stringWithCString:[receivedData bytes] encoding: NSASCIIStringEncoding];
278
279 if( [dataString hasPrefix:@"OK:"] ) {
280 // parse ranking and other possible answers
281 NSArray *values = [dataString componentsSeparatedByString:@":"];
282 NSArray *answer = [ [values objectAtIndex:1] componentsSeparatedByString:@","];
283 NSEnumerator *nse = [answer objectEnumerator];
284
285 // Create a holder for each line we are going to work with
286 NSString *line;
287
288 // Loop through all the lines in the lines array processing each one
289 while( (line = [nse nextObject]) ) {
290 NSArray *keyvalue = [line componentsSeparatedByString:@"="];
291// NSLog(@"%@",keyvalue);
292 if( [[keyvalue objectAtIndex:0] isEqual:@"ranking"] ) {
293 ranking_ = [[keyvalue objectAtIndex:1] intValue];
294 } else if( [[keyvalue objectAtIndex:0] isEqual:@"score_updated"] ) {
295 scoreDidUpdate_ = [[keyvalue objectAtIndex:1] boolValue];
296 }
297
298 }
299 if( [delegate respondsToSelector:@selector(scorePostOk:) ] )
300 [delegate scorePostOk:self];
301
302 } else if( [dataString hasPrefix: @"OK"] ) {
303
304 // Ok
305 postStatus_ = kPostStatusOK;
306
307 if( [delegate respondsToSelector:@selector(scorePostOk:) ] )
308 [delegate scorePostOk:self];
309
310
311 } else {
312
313 NSLog(@"cocoslive: Post Score failed. Reason: %@", dataString);
314
315 // Error parsing answer
316 postStatus_ = kPostStatusPostFailed;
317
318 if( [delegate respondsToSelector:@selector(scorePostFail:) ] )
319 [delegate scorePostFail:self];
320 }
321}
322
323-(NSURLRequest *)connection:(NSURLConnection *)connection
324 willSendRequest:(NSURLRequest *)request
325 redirectResponse:(NSURLResponse *)redirectResponse
326{
327 NSURLRequest *newRequest=request;
328 if (redirectResponse) {
329 newRequest=nil;
330 }
331
332 return newRequest;
333}
334
335@end