-
Notifications
You must be signed in to change notification settings - Fork 0
/
CKPoster.m
278 lines (252 loc) · 10.6 KB
/
CKPoster.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
/*
* ChanKit - Imageboard parsing and interaction.
* Copyright 2009-2012 command-Q.org. All rights reserved.
* This framework is distributed under the terms of the Do What The Fuck You Want To Public License, Version 2.
*
* CKPoster.m - Posting data to the board.
*/
#import "CKUser.h"
#import "CKImage.h"
#import "CKPost.h"
#import "CKBoard.h"
#import "CKPoster.h"
@implementation CKPoster
- (id)initWithPostingDictionary:(NSDictionary*)dict {
if((self = [self initWithURL:[dict objectForKey:@"URL"]]) || (self = [super init])) {
user = [[CKUser alloc] initWithUserInfo:dict];
for(NSString* key in dict) {
if([key isEqualToString:@"Subject"])
subject = [[dict objectForKey:key] retain];
else if([key isEqualToString:@"Comment"])
comment = [[dict objectForKey:key] retain];
else if([key isEqualToString:@"File"])
file = [[dict objectForKey:key] retain];
else if([key isEqualToString:@"Spoiler"])
spoiler = [[dict objectForKey:key] boolValue];
else if([key isEqualToString:@"Verification"]) {
captcha.challenge = [[dict objectForKey:key] retain];
captcha.verification = @"manual_challenge";
}
}
DLog(@"Subject: %@",subject);
DLog(@"Comment: %@",comment);
DLog(@"File: %@",file);
DLog(@"Spoiler: %d",spoiler);
DLog(@"Verification: %@",captcha.verification);
}
return self;
}
+ (CKPoster*)posterWithDictionary:(NSDictionary*)dict { return [[[self alloc] initWithPostingDictionary:dict] autorelease]; }
- (id)initByReferencingURL:(NSURL*)url {
if((self = [super init])) {
URL = [url retain];
DLog(@"URL: %@",URL);
}
return self;
}
- (id)initWithURL:(NSURL*)url{
if((self = [self initByReferencingURL:url]) && ![self populate])
return self;
[self release];
return nil;
}
+ (CKPoster*)posterForURL:(NSURL*)url { return [[[self alloc] initWithURL:url] autorelease]; }
- (id)initWithXML:(NSXMLNode*)doc {
if((self = [self initByReferencingURL:[NSURL URLWithString:[doc URI]]]))
[self populate:doc];
return self;
}
+ (CKPoster*)posterForXML:(NSXMLNode*)doc { return [[[self alloc] initWithXML:doc] autorelease]; }
- (int)populate {
NSXMLDocument* doc;
int error = [CKUtil fetchXML:&doc fromURL:URL];
if(error != CK_ERR_SUCCESS)
return error;
NSURL* docURL = [[NSURL alloc] initWithString:[doc URI]];
if(!docURL) return CK_ERR_REDIRECT;
if(![[docURL absoluteURL] isEqual:[URL absoluteURL]]) {
[URL release];
URL = docURL;
}
else [docURL release];
[self populate:doc];
if(!action)
return CK_ERR_UNDEFINED;
return CK_ERR_SUCCESS;
}
- (void)populate:(NSXMLNode*)doc {
NSString* captchalink = [[CKRecipe sharedRecipe] lookup:@"Poster.Captcha.URL" inDocument:doc];
if(captchalink) {
NSURL* captchaurl = [NSURL URLWithString:captchalink relativeToURL:URL];
NSXMLDocument* captchadoc;
int error = [CKUtil fetchXML:&captchadoc fromURL:captchaurl];
if(error == CK_ERR_SUCCESS) {
captcha.challenge = [[[CKRecipe sharedRecipe] lookup:@"Poster.Captcha.Challenge" inDocument:captchadoc] retain];
NSString* imagelink = [[CKRecipe sharedRecipe] lookup:@"Poster.Captcha.Image" inDocument:captchadoc];
if(imagelink)
captcha.image = [[CKImage alloc] initWithContentsOfURL:[NSURL URLWithString:imagelink relativeToURL:captchaurl]];
DLog(@"Captcha Challenge: %@",captcha.challenge);
}
}
NSString* postinglink = [[CKRecipe sharedRecipe] lookup:@"Poster.URL" inDocument:doc];
if(postinglink)
action = [[NSURL URLWithString:postinglink relativeToURL:URL] retain];
DLog(@"Posting URL: %@",action);
int type = [[CKRecipe sharedRecipe] resourceKindForURL:URL];
NSURL* boardurl;
NSString* boardroot;
switch(type) {
case CK_RESOURCE_POST:
case CK_RESOURCE_THREAD:
if((boardroot = [[CKRecipe sharedRecipe] lookup:@"Board.Location" inDocument:doc]))
board = [[CKBoard alloc] initByReferencingURL:[[NSURL URLWithString:boardroot relativeToURL:URL] absoluteURL]];
break;
case CK_RESOURCE_BOARD:
board = [[CKBoard alloc] initByReferencingURL:URL];
break;
}
}
- (void)dealloc {
[URL release];
[action release];
[user release];
[subject release];
[comment release];
[file release];
[captcha.challenge release];
[captcha.image release];
[captcha.verification release];
// [request release]; this is causing invalid accesses, but now it's probably a memory leak
[board release];
[super dealloc];
}
@synthesize URL;
@synthesize user;
@synthesize subject;
@synthesize comment;
@synthesize file;
@synthesize spoiler;
- (NSString*)verification { return [[captcha.verification retain] autorelease]; }
- (void)setVerification:(NSString*)ver {
if(ver != captcha.verification) {
[captcha.verification release];
captcha.verification = [ver copy];
}
}
- (CKImage*)captcha { return [[captcha.image retain] autorelease]; }
- (BOOL)verify:(NSString*)captchaverification {
// Needs work
if(captchaverification) self.verification = captchaverification;
ASIFormDataRequest* crequest = [ASIFormDataRequest requestWithURL:
[NSURL URLWithString:@"http://www.google.com/recaptcha/api/noscript?k=6Ldp2bsSAAAAAAJ5uyx_lx34lJeEpTLVkP5k04qc"]];
[CKUtil setProxy:[[NSUserDefaults standardUserDefaults] URLForKey:@"CKProxySetting"] onRequest:crequest];
[crequest setPostValue:captcha.challenge forKey:@"recaptcha_challenge_field"];
[crequest setPostValue:captcha.verification forKey:@"recaptcha_response_field"];
[crequest startSynchronous];
if([CKUtil validateResponse:crequest] != CK_ERR_SUCCESS)
return NO;
NSXMLDocument* response = [[NSXMLDocument alloc] initWithData:[crequest responseData] options:NSXMLDocumentTidyHTML error:nil];
NSArray* nodes = [response nodesForXPath:@"/html/body/textarea/text()" error:nil];
[response release];
if(![nodes count])
return NO;
captcha.challenge = [[nodes objectAtIndex:0] stringValue];
captcha.verification = @"manual_challenge";
return YES;
}
- (void)prepare {
request = [[ASIFormDataRequest alloc] initWithURL:action];
request.timeOutSeconds = CK_PROXY_TIMEOUT;
[request addRequestHeader:@"Referer" value:[URL absoluteString]];
NSMutableString* namestring = [NSMutableString string];
if(user.name) [namestring appendString:user.name];
if(user.tripcode) [namestring appendFormat:@"#%@",user.tripcode];
if(user.securetrip) [namestring appendFormat:@"##%@",user.securetrip];
if([namestring length]) [request setPostValue:namestring forKey:[[CKRecipe sharedRecipe] lookup:@"Poster.Fields.Name"]];
if(user.email) [request setPostValue:user.email forKey:[[CKRecipe sharedRecipe] lookup:@"Poster.Fields.Email"]];
if(user.password) [request setPostValue:user.password forKey:[[CKRecipe sharedRecipe] lookup:@"Poster.Fields.Password"]];
if(subject) [request setPostValue:subject forKey:[[CKRecipe sharedRecipe] lookup:@"Poster.Fields.Subject"]];
if(comment) [request setPostValue:comment forKey:[[CKRecipe sharedRecipe] lookup:@"Poster.Fields.Comment"]];
if(file) {
[request setFile:[file path] forKey:[[CKRecipe sharedRecipe] lookup:@"Poster.Fields.File"]];
if(spoiler) {
NSString* spoilername,* spoilerenabled;
if((spoilername = [[CKRecipe sharedRecipe] lookup:@"Poster.Fields.Spoiler.Field"]) &&
(spoilerenabled = [[CKRecipe sharedRecipe] lookup:@"Poster.Fields.Spoiler.Enabled"]))
[request setPostValue:spoilerenabled forKey:spoilername];
}
}
if([[CKRecipe sharedRecipe] resourceKindForURL:URL] != CK_RESOURCE_BOARD)
[request setPostValue:[NSString stringWithFormat:@"%d",[CKUtil parseThreadID:URL]]
forKey:[[CKRecipe sharedRecipe] lookup:@"Poster.Fields.Thread"]];
[[[CKRecipe sharedRecipe] lookup:@"Poster.Fields.Extra"] enumerateKeysAndObjectsUsingBlock:^(id key, id object, BOOL *stop){
[request setPostValue:object forKey:key];
}];
[request setPostValue:@"regist" forKey:@"mode"];
if(captcha.verification) {
[request setPostValue:captcha.challenge forKey:@"recaptcha_challenge_field"];
[request setPostValue:captcha.verification forKey:@"recaptcha_response_field"];
}
}
- (CKPost*)post:(int*)error attempt:(BOOL (^)(int idno))test {
if(!request) [self prepare];
if(!board) {
if(error) *error = CK_ERR_UNDEFINED;
return nil;
}
int idno;
do {
NSAutoreleasePool* loop = [[NSAutoreleasePool alloc] init];
idno = [board newestPostID];
[loop drain];
} while(!test(idno));
return [self post:error];
}
- (CKPost*)post:(int*)error {
if(!request) [self prepare];
int e;
if(!error) error = &e;
NSXMLDocument* doc;
if((*error = [CKUtil fetchXML:&doc viaRequest:request allowedRedirects:0]) == CK_ERR_SUCCESS) {
if([[CKRecipe sharedRecipe] lookup:@"Poster.Response.Captcha" inDocument:doc])
*error = CK_POSTERR_VERIFICATION;
else if([[CKRecipe sharedRecipe] lookup:@"Poster.Response.Flood" inDocument:doc])
*error = CK_POSTERR_FLOOD;
else if([[CKRecipe sharedRecipe] lookup:@"Poster.Response.Disallowed" inDocument:doc])
*error = CK_POSTERR_DISALLOWED;
else if([[CKRecipe sharedRecipe] lookup:@"Poster.Response.NotFound" inDocument:doc])
*error = CK_POSTERR_NOTFOUND;
else if([[CKRecipe sharedRecipe] lookup:@"Poster.Response.Rejected" inDocument:doc])
*error = CK_POSTERR_REJECTED;
else if([[CKRecipe sharedRecipe] lookup:@"Poster.Response.Filetype" inDocument:doc])
*error = CK_POSTERR_FILETYPE;
else if([[CKRecipe sharedRecipe] lookup:@"Poster.Response.FailedUpload" inDocument:doc])
*error = CK_POSTERR_FAILEDUPLOAD;
else if([[CKRecipe sharedRecipe] lookup:@"Poster.Response.Duplicate" inDocument:doc]) {
*error = CK_POSTERR_DUPLICATE;
return [CKPost postReferencingURL:[NSURL URLWithString:[[CKRecipe sharedRecipe] lookup:@"Poster.Response.Duplicate.URL" inDocument:doc] relativeToURL:URL]];
}
else {
NSString* resboard,* redirect = [[CKRecipe sharedRecipe] lookup:@"Poster.Response.URL" inDocument:doc];
if(redirect)
resboard = [[CKUtil parseBoardRoot:[NSURL URLWithString:redirect relativeToURL:URL]] absoluteString];
else resboard = [[CKUtil parseBoardRoot:URL] absoluteString];
NSString* resthread = [[CKRecipe sharedRecipe] lookup:@"Poster.Response.Thread" inDocument:doc];
NSString* respost = [[CKRecipe sharedRecipe] lookup:@"Poster.Response.Post" inDocument:doc];
if(![resthread intValue]) resthread = respost;
DLog(@"Got Board: %@",resboard);
DLog(@"Got Thread: %@",resthread);
DLog(@"Got Post: %@",respost);
if(resthread && respost && resboard) {
NSURL* resurl = [NSURL URLWithString:[NSString stringWithFormat:[[CKRecipe sharedRecipe] lookup:@"Poster.Response.Format"],resboard,resthread,respost]];
DLog(@"URL: %@",[resurl absoluteString]);
// 4chan will happily serve us a cached page that doesn't include our post, especially on long threads, so we can't return a populated post
// Better not to make network-hitting decisions like that anyway.
return [CKPost postReferencingURL:resurl];
}
*error = CK_ERR_UNDEFINED;
}
}
return nil;
}
@end