]>
Commit | Line | Data |
---|---|---|
c1582939 JF |
1 | /* Cyrker - Remove Execution Server and Disassembler |
2 | * Copyright (C) 2009 Jay Freeman (saurik) | |
3 | */ | |
4 | ||
5 | /* | |
6 | * Redistribution and use in source and binary | |
7 | * forms, with or without modification, are permitted | |
8 | * provided that the following conditions are met: | |
9 | * | |
10 | * 1. Redistributions of source code must retain the | |
11 | * above copyright notice, this list of conditions | |
12 | * and the following disclaimer. | |
13 | * 2. Redistributions in binary form must reproduce the | |
14 | * above copyright notice, this list of conditions | |
15 | * and the following disclaimer in the documentation | |
16 | * and/or other materials provided with the | |
17 | * distribution. | |
18 | * 3. The name of the author may not be used to endorse | |
19 | * or promote products derived from this software | |
20 | * without specific prior written permission. | |
21 | * | |
22 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' | |
23 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, | |
24 | * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | |
25 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
26 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE | |
27 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |
28 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | |
29 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | |
30 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | |
31 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | |
32 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR | |
33 | * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN | |
34 | * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF | |
35 | * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
36 | */ | |
37 | ||
38 | #include <substrate.h> | |
39 | ||
40 | #include <unistd.h> | |
41 | ||
42 | #include <CoreFoundation/CoreFoundation.h> | |
43 | #include <CoreFoundation/CFLogUtilities.h> | |
44 | ||
45 | #include <CFNetwork/CFNetwork.h> | |
46 | #include <Foundation/Foundation.h> | |
47 | ||
48 | #include <JavaScriptCore/JSBase.h> | |
49 | #include <JavaScriptCore/JSValueRef.h> | |
50 | #include <JavaScriptCore/JSObjectRef.h> | |
51 | #include <JavaScriptCore/JSContextRef.h> | |
52 | #include <JavaScriptCore/JSStringRef.h> | |
53 | #include <JavaScriptCore/JSStringRefCF.h> | |
54 | ||
55 | #include <WebKit/WebScriptObject.h> | |
56 | ||
57 | #include <sys/types.h> | |
58 | #include <sys/socket.h> | |
59 | #include <netinet/in.h> | |
60 | ||
61 | /* XXX: bad _assert */ | |
62 | #define _assert(test) do { \ | |
63 | if ((test)) break; \ | |
64 | CFLog(kCFLogLevelNotice, CFSTR("_assert(%u)"), __LINE__); \ | |
65 | } while (false) | |
66 | ||
67 | #define _trace() do { \ | |
68 | CFLog(kCFLogLevelNotice, CFSTR("_trace(%u)"), __LINE__); \ | |
69 | } while (false) | |
70 | ||
71 | static JSContextRef Context_; | |
72 | static JSClassRef joc_; | |
73 | static JSObjectRef Array_; | |
74 | static JSStringRef length_; | |
75 | static Class NSCFBoolean_; | |
76 | ||
77 | struct Client { | |
78 | CFHTTPMessageRef message_; | |
79 | CFSocketRef socket_; | |
80 | }; | |
81 | ||
82 | @interface NSObject (Cyrver) | |
83 | - (NSString *) cy$toJSON; | |
84 | @end | |
85 | ||
86 | @implementation NSObject (Cyrver) | |
87 | - (NSString *) cy$toJSON { | |
88 | return [NSString stringWithFormat:@"<%@>", [self description]]; | |
89 | } @end | |
90 | ||
91 | @implementation WebUndefined (Cyrver) | |
92 | - (NSString *) cy$toJSON { | |
93 | return @"undefined"; | |
94 | } @end | |
95 | ||
96 | @implementation NSArray (Cyrver) | |
97 | - (NSString *) cy$toJSON { | |
98 | NSMutableString *json([[[NSMutableString alloc] init] autorelease]); | |
99 | [json appendString:@"["]; | |
100 | ||
101 | bool comma(false); | |
102 | for (NSObject *object in self) { | |
103 | if (comma) | |
104 | [json appendString:@","]; | |
105 | else | |
106 | comma = true; | |
107 | [json appendString:[object cy$toJSON]]; | |
108 | } | |
109 | ||
110 | [json appendString:@"]"]; | |
111 | return json; | |
112 | } @end | |
113 | ||
114 | @implementation NSNumber (Cyrver) | |
115 | - (NSString *) cy$toJSON { | |
116 | return [self class] != NSCFBoolean_ ? [self stringValue] : [self boolValue] ? @"true" : @"false"; | |
117 | } @end | |
118 | ||
119 | @implementation NSString (Cyrver) | |
120 | - (NSString *) cy$toJSON { | |
121 | CFMutableStringRef json(CFStringCreateMutableCopy(kCFAllocatorDefault, 0, (CFStringRef) self)); | |
122 | ||
123 | /* XXX: I can't believe there isn't a safe helper function for this... */ | |
124 | CFStringFindAndReplace(json, CFSTR("\\"), CFSTR("\\\\"), CFRangeMake(0, CFStringGetLength(json)), 0); | |
125 | CFStringFindAndReplace(json, CFSTR("\""), CFSTR("\\\""), CFRangeMake(0, CFStringGetLength(json)), 0); | |
126 | CFStringFindAndReplace(json, CFSTR("\t"), CFSTR("\\t"), CFRangeMake(0, CFStringGetLength(json)), 0); | |
127 | CFStringFindAndReplace(json, CFSTR("\r"), CFSTR("\\r"), CFRangeMake(0, CFStringGetLength(json)), 0); | |
128 | CFStringFindAndReplace(json, CFSTR("\n"), CFSTR("\\n"), CFRangeMake(0, CFStringGetLength(json)), 0); | |
129 | ||
130 | CFStringInsert(json, 0, CFSTR("\"")); | |
131 | CFStringAppend(json, CFSTR("\"")); | |
132 | ||
133 | return (NSString *) json; | |
134 | } @end | |
135 | ||
136 | @interface CY$JSArray : NSArray { | |
137 | JSObjectRef object_; | |
138 | JSContextRef context_; | |
139 | } | |
140 | ||
141 | - (id) initWithJSObject:(JSObjectRef)object inContext:(JSContextRef)context; | |
142 | ||
143 | - (NSUInteger) count; | |
144 | - (id) objectAtIndex:(NSUInteger)index; | |
145 | ||
146 | @end | |
147 | ||
148 | static id JSObjectToNSObject(JSContextRef ctx, JSObjectRef object) { | |
149 | if (JSValueIsObjectOfClass(ctx, object, joc_)) | |
150 | return reinterpret_cast<id>(JSObjectGetPrivate(object)); | |
151 | else if (JSValueIsInstanceOfConstructor(ctx, object, Array_, NULL)) | |
152 | return [[[CY$JSArray alloc] initWithJSObject:object inContext:ctx] autorelease]; | |
153 | else | |
154 | return @"Hello"; | |
155 | //return [[[CY$JSObject alloc] initWithJSObject:object inContext:ctx] autorelease]; | |
156 | } | |
157 | ||
158 | static CFTypeRef JSValueToCFTypeCopy(JSContextRef ctx, JSValueRef value) { | |
159 | JSType type(JSValueGetType(ctx, value)); | |
160 | ||
161 | switch (type) { | |
162 | case kJSTypeUndefined: | |
163 | return [WebUndefined undefined]; | |
164 | break; | |
165 | ||
166 | case kJSTypeNull: | |
167 | return nil; | |
168 | break; | |
169 | ||
170 | case kJSTypeBoolean: | |
171 | return CFRetain(JSValueToBoolean(ctx, value) ? kCFBooleanTrue : kCFBooleanFalse); | |
172 | break; | |
173 | ||
174 | case kJSTypeNumber: { | |
175 | double number(JSValueToNumber(ctx, value, NULL)); | |
176 | return CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &number); | |
177 | } break; | |
178 | ||
179 | case kJSTypeString: { | |
180 | JSStringRef string(JSValueToStringCopy(ctx, value, NULL)); | |
181 | CFStringRef object(JSStringCopyCFString(kCFAllocatorDefault, string)); | |
182 | JSStringRelease(string); | |
183 | return object; | |
184 | } break; | |
185 | ||
186 | case kJSTypeObject: | |
187 | return CFRetain((CFTypeRef) JSObjectToNSObject(ctx, (JSObjectRef) value)); | |
188 | break; | |
189 | ||
190 | default: | |
191 | _assert(false); | |
192 | return NULL; | |
193 | break; | |
194 | } | |
195 | } | |
196 | ||
197 | static id JSValueToNSObject(JSContextRef ctx, JSValueRef value) { | |
198 | id object((id) JSValueToCFTypeCopy(ctx, value)); | |
199 | return object == nil ? nil : [object autorelease]; | |
200 | } | |
201 | ||
202 | @implementation CY$JSArray | |
203 | ||
204 | - (id) initWithJSObject:(JSObjectRef)object inContext:(JSContextRef)context { | |
205 | if ((self = [super init]) != nil) { | |
206 | object_ = object; | |
207 | context_ = context; | |
208 | } return self; | |
209 | } | |
210 | ||
211 | - (NSUInteger) count { | |
212 | return JSValueToNumber(context_, JSObjectGetProperty(context_, object_, length_, NULL), NULL); | |
213 | } | |
214 | ||
215 | - (id) objectAtIndex:(NSUInteger)index { | |
216 | JSValueRef value(JSObjectGetPropertyAtIndex(context_, object_, index, NULL)); | |
217 | id object(JSValueToNSObject(context_, value)); | |
218 | return object == nil ? [NSNull null] : object; | |
219 | } | |
220 | ||
221 | @end | |
222 | ||
223 | static CFStringRef JSValueToJSONCopy(JSContextRef ctx, JSValueRef value) { | |
224 | id object(JSValueToNSObject(ctx, value)); | |
225 | return (CFStringRef) [(object == nil ? @"null" : [object cy$toJSON]) retain]; | |
226 | } | |
227 | ||
228 | static void OnData(CFSocketRef socket, CFSocketCallBackType type, CFDataRef address, const void *value, void *info) { | |
229 | switch (type) { | |
230 | case kCFSocketDataCallBack: | |
231 | CFDataRef data(reinterpret_cast<CFDataRef>(value)); | |
232 | Client *client(reinterpret_cast<Client *>(info)); | |
233 | ||
234 | if (client->message_ == NULL) | |
235 | client->message_ = CFHTTPMessageCreateEmpty(kCFAllocatorDefault, TRUE); | |
236 | ||
237 | if (!CFHTTPMessageAppendBytes(client->message_, CFDataGetBytePtr(data), CFDataGetLength(data))) | |
238 | CFLog(kCFLogLevelError, CFSTR("CFHTTPMessageAppendBytes()")); | |
239 | else if (CFHTTPMessageIsHeaderComplete(client->message_)) { | |
240 | CFURLRef url(CFHTTPMessageCopyRequestURL(client->message_)); | |
241 | Boolean absolute; | |
242 | CFStringRef path(CFURLCopyStrictPath(url, &absolute)); | |
243 | CFRelease(client->message_); | |
244 | ||
245 | CFStringRef code(CFURLCreateStringByReplacingPercentEscapes(kCFAllocatorDefault, path, CFSTR(""))); | |
246 | CFRelease(path); | |
247 | ||
248 | JSStringRef script(JSStringCreateWithCFString(code)); | |
249 | CFRelease(code); | |
250 | ||
251 | JSValueRef result(JSEvaluateScript(Context_, script, NULL, NULL, 0, NULL)); | |
252 | JSStringRelease(script); | |
253 | ||
254 | CFHTTPMessageRef response(CFHTTPMessageCreateResponse(kCFAllocatorDefault, 200, NULL, kCFHTTPVersion1_1)); | |
255 | CFHTTPMessageSetHeaderFieldValue(response, CFSTR("Content-Type"), CFSTR("application/json; charset=utf-8")); | |
256 | ||
257 | CFStringRef json(JSValueToJSONCopy(Context_, result)); | |
258 | CFDataRef body(CFStringCreateExternalRepresentation(kCFAllocatorDefault, json, kCFStringEncodingUTF8, NULL)); | |
259 | CFRelease(json); | |
260 | ||
261 | CFStringRef length(CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%u"), CFDataGetLength(body))); | |
262 | CFHTTPMessageSetHeaderFieldValue(response, CFSTR("Content-Length"), length); | |
263 | CFRelease(length); | |
264 | ||
265 | CFHTTPMessageSetBody(response, body); | |
266 | CFRelease(body); | |
267 | ||
268 | CFDataRef serialized(CFHTTPMessageCopySerializedMessage(response)); | |
269 | CFRelease(response); | |
270 | ||
271 | CFSocketSendData(socket, NULL, serialized, 0); | |
272 | CFRelease(serialized); | |
273 | ||
274 | CFRelease(url); | |
275 | } | |
276 | break; | |
277 | } | |
278 | } | |
279 | ||
280 | static void OnAccept(CFSocketRef socket, CFSocketCallBackType type, CFDataRef address, const void *value, void *info) { | |
281 | switch (type) { | |
282 | case kCFSocketAcceptCallBack: | |
283 | Client *client(new Client()); | |
284 | ||
285 | client->message_ = NULL; | |
286 | ||
287 | CFSocketContext context; | |
288 | context.version = 0; | |
289 | context.info = client; | |
290 | context.retain = NULL; | |
291 | context.release = NULL; | |
292 | context.copyDescription = NULL; | |
293 | ||
294 | client->socket_ = CFSocketCreateWithNative(kCFAllocatorDefault, *reinterpret_cast<const CFSocketNativeHandle *>(value), kCFSocketDataCallBack, &OnData, &context); | |
295 | ||
296 | CFRunLoopAddSource(CFRunLoopGetCurrent(), CFSocketCreateRunLoopSource(kCFAllocatorDefault, client->socket_, 0), kCFRunLoopDefaultMode); | |
297 | break; | |
298 | } | |
299 | } | |
300 | ||
301 | static JSValueRef joc_getProperty(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef *exception) { | |
302 | return NULL; | |
303 | } | |
304 | ||
305 | static JSValueRef obc_getProperty(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef *exception) { | |
306 | NSString *name([(NSString *) JSStringCopyCFString(kCFAllocatorDefault, propertyName) autorelease]); | |
307 | if (Class _class = NSClassFromString(name)) | |
308 | return JSObjectMake(ctx, joc_, _class); | |
309 | return NULL; | |
310 | } | |
311 | ||
312 | extern "C" void TweakInitialize() { | |
313 | NSAutoreleasePool *pool([[NSAutoreleasePool alloc] init]); | |
314 | ||
315 | NSCFBoolean_ = objc_getClass("NSCFBoolean"); | |
316 | ||
317 | pid_t pid(getpid()); | |
318 | ||
319 | struct sockaddr_in address; | |
320 | address.sin_len = sizeof(address); | |
321 | address.sin_family = AF_INET; | |
322 | address.sin_addr.s_addr = INADDR_ANY; | |
323 | address.sin_port = htons(10000 + pid); | |
324 | ||
325 | CFDataRef data(CFDataCreate(kCFAllocatorDefault, reinterpret_cast<UInt8 *>(&address), sizeof(address))); | |
326 | ||
327 | CFSocketSignature signature; | |
328 | signature.protocolFamily = AF_INET; | |
329 | signature.socketType = SOCK_STREAM; | |
330 | signature.protocol = IPPROTO_TCP; | |
331 | signature.address = data; | |
332 | ||
333 | CFSocketRef socket(CFSocketCreateWithSocketSignature(kCFAllocatorDefault, &signature, kCFSocketAcceptCallBack, &OnAccept, NULL)); | |
334 | CFRunLoopAddSource(CFRunLoopGetCurrent(), CFSocketCreateRunLoopSource(kCFAllocatorDefault, socket, 0), kCFRunLoopDefaultMode); | |
335 | ||
336 | JSClassDefinition definition; | |
337 | ||
338 | definition = kJSClassDefinitionEmpty; | |
339 | definition.getProperty = &obc_getProperty; | |
340 | JSClassRef obc(JSClassCreate(&definition)); | |
341 | ||
342 | definition = kJSClassDefinitionEmpty; | |
343 | definition.getProperty = &joc_getProperty; | |
344 | joc_ = JSClassCreate(&definition); | |
345 | ||
346 | Context_ = JSGlobalContextCreate(obc); | |
347 | ||
348 | JSObjectRef global(JSContextGetGlobalObject(Context_)); | |
349 | ||
350 | length_ = JSStringCreateWithUTF8CString("length"); | |
351 | ||
352 | JSStringRef name(JSStringCreateWithUTF8CString("Array")); | |
353 | Array_ = JSValueToObject(Context_, JSObjectGetProperty(Context_, global, name, NULL), NULL); | |
354 | JSStringRelease(name); | |
355 | ||
356 | [pool release]; | |
357 | } |