diff --git a/MessagesBridge.xcodeproj/project.pbxproj b/MessagesBridge.xcodeproj/project.pbxproj index dc9c6ee..5012574 100644 --- a/MessagesBridge.xcodeproj/project.pbxproj +++ b/MessagesBridge.xcodeproj/project.pbxproj @@ -7,6 +7,10 @@ objects = { /* Begin PBXBuildFile section */ + CD602056219B5DFD0024D9C5 /* MBIMBridgeOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = CD602055219B5DFD0024D9C5 /* MBIMBridgeOperation.m */; }; + CD60205C219B623F0024D9C5 /* MBIMMessagesListOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = CD60205B219B623F0024D9C5 /* MBIMMessagesListOperation.m */; }; + CD60205F219B674B0024D9C5 /* MBIMConversationListOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = CD60205E219B674B0024D9C5 /* MBIMConversationListOperation.m */; }; + CD602062219B68950024D9C5 /* MBIMSendMessageOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = CD602061219B68950024D9C5 /* MBIMSendMessageOperation.m */; }; CDF62335219A895D00690038 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = CDF62334219A895D00690038 /* main.m */; }; CDF62339219A8A5600690038 /* MBIMBridge.h in Sources */ = {isa = PBXBuildFile; fileRef = 1A0C4469219A4BC300F2AC00 /* MBIMBridge.h */; }; CDF6233A219A8A5600690038 /* MBIMBridge.m in Sources */ = {isa = PBXBuildFile; fileRef = 1A0C446A219A4BC300F2AC00 /* MBIMBridge.m */; }; @@ -102,6 +106,14 @@ 1A33B43C219A5ACD0034485A /* OSXDebugDefaults.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = OSXDebugDefaults.xcconfig; sourceTree = ""; }; 1A33B43D219A5ACD0034485A /* ReleaseDefaults.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = ReleaseDefaults.xcconfig; sourceTree = ""; }; 1A33B43E219A5BD80034485A /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; + CD602054219B5DFD0024D9C5 /* MBIMBridgeOperation.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MBIMBridgeOperation.h; sourceTree = ""; }; + CD602055219B5DFD0024D9C5 /* MBIMBridgeOperation.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MBIMBridgeOperation.m; sourceTree = ""; }; + CD60205A219B623F0024D9C5 /* MBIMMessagesListOperation.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MBIMMessagesListOperation.h; sourceTree = ""; }; + CD60205B219B623F0024D9C5 /* MBIMMessagesListOperation.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MBIMMessagesListOperation.m; sourceTree = ""; }; + CD60205D219B674B0024D9C5 /* MBIMConversationListOperation.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MBIMConversationListOperation.h; sourceTree = ""; }; + CD60205E219B674B0024D9C5 /* MBIMConversationListOperation.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MBIMConversationListOperation.m; sourceTree = ""; }; + CD602060219B68950024D9C5 /* MBIMSendMessageOperation.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MBIMSendMessageOperation.h; sourceTree = ""; }; + CD602061219B68950024D9C5 /* MBIMSendMessageOperation.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MBIMSendMessageOperation.m; sourceTree = ""; }; CDF62312219A869000690038 /* GCDWebServer.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = GCDWebServer.xcodeproj; path = GCDWebServer/GCDWebServer.xcodeproj; sourceTree = ""; }; CDF62332219A895D00690038 /* kordophoned */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = kordophoned; sourceTree = BUILT_PRODUCTS_DIR; }; CDF62334219A895D00690038 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; @@ -170,6 +182,7 @@ 1A0C446D219A4BCD00F2AC00 /* Bridge */ = { isa = PBXGroup; children = ( + CD60204C219B5D710024D9C5 /* Operations */, 1A0C4469219A4BC300F2AC00 /* MBIMBridge.h */, 1A0C446A219A4BC300F2AC00 /* MBIMBridge.m */, ); @@ -187,6 +200,21 @@ path = "Config Files"; sourceTree = ""; }; + CD60204C219B5D710024D9C5 /* Operations */ = { + isa = PBXGroup; + children = ( + CD602054219B5DFD0024D9C5 /* MBIMBridgeOperation.h */, + CD602055219B5DFD0024D9C5 /* MBIMBridgeOperation.m */, + CD60205A219B623F0024D9C5 /* MBIMMessagesListOperation.h */, + CD60205B219B623F0024D9C5 /* MBIMMessagesListOperation.m */, + CD60205D219B674B0024D9C5 /* MBIMConversationListOperation.h */, + CD60205E219B674B0024D9C5 /* MBIMConversationListOperation.m */, + CD602060219B68950024D9C5 /* MBIMSendMessageOperation.h */, + CD602061219B68950024D9C5 /* MBIMSendMessageOperation.m */, + ); + path = Operations; + sourceTree = ""; + }; CDF62313219A869000690038 /* Products */ = { isa = PBXGroup; children = ( @@ -330,6 +358,10 @@ CDF62339219A8A5600690038 /* MBIMBridge.h in Sources */, CDF6233A219A8A5600690038 /* MBIMBridge.m in Sources */, CDF62335219A895D00690038 /* main.m in Sources */, + CD60205C219B623F0024D9C5 /* MBIMMessagesListOperation.m in Sources */, + CD602062219B68950024D9C5 /* MBIMSendMessageOperation.m in Sources */, + CD602056219B5DFD0024D9C5 /* MBIMBridgeOperation.m in Sources */, + CD60205F219B674B0024D9C5 /* MBIMConversationListOperation.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/kordophone/Bridge/MBIMBridge.m b/kordophone/Bridge/MBIMBridge.m index 54621a3..5559595 100644 --- a/kordophone/Bridge/MBIMBridge.m +++ b/kordophone/Bridge/MBIMBridge.m @@ -7,6 +7,7 @@ // #import "MBIMBridge.h" +#import "MBIMBridgeOperation.h" #import @@ -18,12 +19,9 @@ static NSString *const MBIMBridgeToken = @"net.buzzert.kordophone"; -static NSString *const kAPIEndpointConversationList = @"conversations"; -static NSString *const kAPIEndpointConversationContents = @"messages"; -static NSString *const kAPIEndpointSendMessage = @"sendMessage"; - @interface MBIMBridge (/* INTERNAL */) @property (nonatomic, strong) GCDWebServer *webServer; +@property (nonatomic, strong) NSOperationQueue *operationQueue; - (instancetype)_init; @end @@ -50,6 +48,8 @@ static NSString *const kAPIEndpointSendMessage = @"sendMessage"; [sDaemonController setDelegate:self]; [sDaemonListener addHandler:self]; + + _operationQueue = [[NSOperationQueue alloc] init]; } return self; @@ -108,28 +108,21 @@ static NSString *const kAPIEndpointSendMessage = @"sendMessage"; NSLog(@"chat items changed: %@", notification); } -- (BOOL)_sendMessage:(NSString *)messageBody toChatWithGUID:(NSString *)chatGUID -{ - IMAccount *iMessageAccount = [[IMAccountController sharedInstance] bestAccountForService:[IMServiceImpl iMessageService]]; - IMHandle *handle = [[iMessageAccount arrayOfAllIMHandles] firstObject]; - - NSAttributedString *replyAttrString = [[NSAttributedString alloc] initWithString:messageBody]; - IMMessage *reply = [IMMessage fromMeIMHandle:handle withText:replyAttrString fileTransferGUIDs:@[] flags:kIMMessageFinished]; - - IMChat *chat = [sChatRegistry existingChatWithGUID:chatGUID]; - if (!chat) { - NSLog(@"Chat does not exist: %@", chatGUID); - return NO; - } - - [chat sendMessage:reply]; - - return YES; -} - #pragma mark - #pragma mark Web Server initialization +- (void)_handleAsyncServerRequest:(GCDWebServerRequest *)request completion:(GCDWebServerCompletionBlock)completion +{ + NSString *endpointName = [[request URL] lastPathComponent]; + Class operationClass = [MBIMBridgeOperation operationClassForEndpointName:endpointName]; + if (operationClass != nil) { + MBIMBridgeOperation *operation = [[operationClass alloc] initWithRequest:request completion:completion]; + [[self operationQueue] addOperation:operation]; + } else { + completion([GCDWebServerDataResponse responseWithStatusCode:404]); + } +} + - (void)startWebServer { [GCDWebServer setLogLevel:3]; @@ -138,96 +131,15 @@ static NSString *const kAPIEndpointSendMessage = @"sendMessage"; self.webServer = [[GCDWebServer alloc] init]; [self.webServer addDefaultHandlerForMethod:@"GET" requestClass:[GCDWebServerRequest class] - processBlock:^GCDWebServerResponse * _Nullable(__kindof GCDWebServerRequest * _Nonnull request) { return [weakSelf _handleWebServerRequest:request]; }]; + asyncProcessBlock:^(__kindof GCDWebServerRequest * _Nonnull request, GCDWebServerCompletionBlock _Nonnull completionBlock) { [weakSelf _handleAsyncServerRequest:request completion:completionBlock]; }]; [self.webServer addDefaultHandlerForMethod:@"POST" requestClass:[GCDWebServerURLEncodedFormRequest class] - processBlock:^GCDWebServerResponse * _Nullable(__kindof GCDWebServerRequest * _Nonnull request) { return [weakSelf _handleWebServerPOST:(GCDWebServerURLEncodedFormRequest *)request]; }]; + asyncProcessBlock:^(__kindof GCDWebServerRequest * _Nonnull request, GCDWebServerCompletionBlock _Nonnull completionBlock) { [weakSelf _handleAsyncServerRequest:request completion:completionBlock]; }]; [self.webServer startWithPort:8080 bonjourName:nil]; } -- (GCDWebServerResponse *)_handleWebServerPOST:(__kindof GCDWebServerURLEncodedFormRequest * _Nonnull)request -{ - NSURLComponents *urlComponents = [NSURLComponents componentsWithURL:[request URL] resolvingAgainstBaseURL:NO]; - NSString *endpoint = [[urlComponents path] lastPathComponent]; - - if ([endpoint isEqualToString:kAPIEndpointSendMessage]) { - NSDictionary *args = [request arguments]; - - NSString *guid = [args objectForKey:@"guid"]; - NSString *messageBody = [args objectForKey:@"body"]; - BOOL result = [self _sendMessage:messageBody toChatWithGUID:guid]; - if (result) { - return [GCDWebServerDataResponse responseWithStatusCode:200]; - } else { - return [GCDWebServerDataResponse responseWithStatusCode:500]; - } - } - - return [GCDWebServerDataResponse responseWithStatusCode:404]; -} - -- (GCDWebServerResponse *)_handleWebServerRequest:(__kindof GCDWebServerRequest * _Nonnull)request -{ - NSURLComponents *urlComponents = [NSURLComponents componentsWithURL:[request URL] resolvingAgainstBaseURL:NO]; - NSString *endpoint = [[urlComponents path] lastPathComponent]; - - if ([endpoint isEqualToString:kAPIEndpointConversationList]) { - NSArray *chats = [sChatRegistry allExistingChats]; - - NSMutableArray *conversations = [NSMutableArray array]; - for (IMChat *chat in chats) { - NSMutableDictionary *chatDict = [NSMutableDictionary dictionary]; - chatDict[@"guid"] = [chat guid]; - chatDict[@"displayName"] = [chat displayName]; - chatDict[@"date"] = GCDWebServerFormatRFC822([chat lastFinishedMessageDate]); - - [conversations addObject:chatDict]; - } - - return [GCDWebServerDataResponse responseWithJSONObject:conversations]; - } - - if ([endpoint isEqualToString:kAPIEndpointConversationContents]) { - NSString *guid = nil; - for (NSURLQueryItem *queryItem in [urlComponents queryItems]) { - if ([[queryItem name] isEqualToString:@"guid"]) { - guid = [queryItem value]; - break; - } - } - - if (!guid) { - NSLog(@"No query item provided"); - return [GCDWebServerDataResponse responseWithStatusCode:500]; - } - - IMChat *chat = [sChatRegistry existingChatWithGUID:guid]; - if (!chat) { - NSLog(@"Chat with guid: %@ not found", guid); - return [GCDWebServerDataResponse responseWithStatusCode:500]; - } - - // Load messages - [chat loadMessagesBeforeDate:[NSDate date] limit:50 loadImmediately:YES]; - - NSMutableArray *messages = [NSMutableArray array]; - for (IMMessageItem *imMessage in [[chat chatItems] messages]) { - NSMutableDictionary *messageDict = [NSMutableDictionary dictionary]; - messageDict[@"text"] = [[imMessage body] string]; - messageDict[@"date"] = GCDWebServerFormatRFC822([imMessage time]); - messageDict[@"sender"] = [imMessage sender]; - [messages addObject:messageDict]; - } - - return [GCDWebServerDataResponse responseWithJSONObject:messages]; - } - - - return [GCDWebServerDataResponse responseWithStatusCode:404]; -} - #pragma mark - #pragma mark Daemon lifecycle diff --git a/kordophone/Bridge/Operations/MBIMBridgeOperation.h b/kordophone/Bridge/Operations/MBIMBridgeOperation.h new file mode 100644 index 0000000..8b3894d --- /dev/null +++ b/kordophone/Bridge/Operations/MBIMBridgeOperation.h @@ -0,0 +1,25 @@ +// +// MBIMBridgeOperation.h +// kordophoned +// +// Created by James Magahern on 11/13/18. +// Copyright © 2018 James Magahern. All rights reserved. +// + +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface MBIMBridgeOperation : NSOperation +@property (class, nonatomic, readonly) NSString *endpointName; + +@property (nonatomic, readonly) GCDWebServerRequest *request; +@property (nonatomic, readonly) GCDWebServerCompletionBlock serverCompletionBlock; + ++ (nullable Class)operationClassForEndpointName:(NSString *)endpointName; +- (instancetype)initWithRequest:(GCDWebServerRequest *)request completion:(GCDWebServerCompletionBlock)completionBlock; + +@end + +NS_ASSUME_NONNULL_END diff --git a/kordophone/Bridge/Operations/MBIMBridgeOperation.m b/kordophone/Bridge/Operations/MBIMBridgeOperation.m new file mode 100644 index 0000000..883489e --- /dev/null +++ b/kordophone/Bridge/Operations/MBIMBridgeOperation.m @@ -0,0 +1,58 @@ +// +// MBIMBridgeOperation.m +// kordophoned +// +// Created by James Magahern on 11/13/18. +// Copyright © 2018 James Magahern. All rights reserved. +// + +#import "MBIMBridgeOperation.h" + +@interface MBIMBridgeOperation (/*INTERNAL*/) +@property (nonatomic, copy) GCDWebServerCompletionBlock serverCompletionBlock; +@property (nonatomic, strong) GCDWebServerRequest *request; +@end + +@implementation MBIMBridgeOperation + ++ (NSString *)endpointName +{ + // To be inplemented by subclasses + return @"__unimplemented__"; +} + ++ (NSMutableDictionary *)_operationClassMapping +{ + static dispatch_once_t onceToken; + static NSMutableDictionary *operationClassMapping = nil; + dispatch_once(&onceToken, ^{ + operationClassMapping = [[NSMutableDictionary alloc] init]; + }); + + return operationClassMapping; +} + ++ (void)load +{ + if ([self class] != [MBIMBridgeOperation class]) { + [[self _operationClassMapping] setObject:[self class] forKey:[self endpointName]]; + } +} + ++ (nullable Class)operationClassForEndpointName:(NSString *)endpointName +{ + return [[self _operationClassMapping] objectForKey:endpointName]; +} + +- (instancetype)initWithRequest:(GCDWebServerRequest *)request completion:(GCDWebServerCompletionBlock)completionBlock +{ + self = [super init]; + if (self) { + self.request = request; + self.serverCompletionBlock = completionBlock; + } + + return self; +} + +@end diff --git a/kordophone/Bridge/Operations/MBIMConversationListOperation.h b/kordophone/Bridge/Operations/MBIMConversationListOperation.h new file mode 100644 index 0000000..0e43311 --- /dev/null +++ b/kordophone/Bridge/Operations/MBIMConversationListOperation.h @@ -0,0 +1,17 @@ +// +// MBIMConversationListOperation.h +// kordophoned +// +// Created by James Magahern on 11/13/18. +// Copyright © 2018 James Magahern. All rights reserved. +// + +#import "MBIMBridgeOperation.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface MBIMConversationListOperation : MBIMBridgeOperation + +@end + +NS_ASSUME_NONNULL_END diff --git a/kordophone/Bridge/Operations/MBIMConversationListOperation.m b/kordophone/Bridge/Operations/MBIMConversationListOperation.m new file mode 100644 index 0000000..1962153 --- /dev/null +++ b/kordophone/Bridge/Operations/MBIMConversationListOperation.m @@ -0,0 +1,40 @@ +// +// MBIMConversationListOperation.m +// kordophoned +// +// Created by James Magahern on 11/13/18. +// Copyright © 2018 James Magahern. All rights reserved. +// + +#import "MBIMConversationListOperation.h" + +#import + +@implementation MBIMConversationListOperation + ++ (void)load { [super load]; } + ++ (NSString *)endpointName +{ + return @"conversations"; +} + +- (void)main +{ + NSArray *chats = [sChatRegistry allExistingChats]; + + NSMutableArray *conversations = [NSMutableArray array]; + for (IMChat *chat in chats) { + NSMutableDictionary *chatDict = [NSMutableDictionary dictionary]; + chatDict[@"guid"] = [chat guid]; + chatDict[@"displayName"] = [chat displayName]; + chatDict[@"date"] = GCDWebServerFormatRFC822([chat lastFinishedMessageDate]); + + [conversations addObject:chatDict]; + } + + GCDWebServerResponse *response = [GCDWebServerDataResponse responseWithJSONObject:conversations]; + self.serverCompletionBlock(response); +} + +@end diff --git a/kordophone/Bridge/Operations/MBIMMessagesListOperation.h b/kordophone/Bridge/Operations/MBIMMessagesListOperation.h new file mode 100644 index 0000000..cc0215d --- /dev/null +++ b/kordophone/Bridge/Operations/MBIMMessagesListOperation.h @@ -0,0 +1,17 @@ +// +// MBIMMessagesListOperation.h +// kordophoned +// +// Created by James Magahern on 11/13/18. +// Copyright © 2018 James Magahern. All rights reserved. +// + +#import "MBIMBridgeOperation.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface MBIMMessagesListOperation : MBIMBridgeOperation + +@end + +NS_ASSUME_NONNULL_END diff --git a/kordophone/Bridge/Operations/MBIMMessagesListOperation.m b/kordophone/Bridge/Operations/MBIMMessagesListOperation.m new file mode 100644 index 0000000..d9be1b1 --- /dev/null +++ b/kordophone/Bridge/Operations/MBIMMessagesListOperation.m @@ -0,0 +1,67 @@ +// +// MBIMMessagesListOperation.m +// kordophoned +// +// Created by James Magahern on 11/13/18. +// Copyright © 2018 James Magahern. All rights reserved. +// + +#import "MBIMMessagesListOperation.h" + +#import + +@implementation MBIMMessagesListOperation + ++ (void)load { [super load]; } + ++ (NSString *)endpointName +{ + return @"messages"; +} + +- (void)main +{ + GCDWebServerResponse *response = nil; + do { + NSURLComponents *urlComponents = [NSURLComponents componentsWithURL:[self.request URL] resolvingAgainstBaseURL:NO]; + + NSString *guid = nil; + for (NSURLQueryItem *queryItem in [urlComponents queryItems]) { + if ([[queryItem name] isEqualToString:@"guid"]) { + guid = [queryItem value]; + break; + } + } + + if (!guid) { + NSLog(@"No query item provided"); + response = [GCDWebServerDataResponse responseWithStatusCode:500]; + break; + } + + IMChat *chat = [sChatRegistry existingChatWithGUID:guid]; + if (!chat) { + NSLog(@"Chat with guid: %@ not found", guid); + response = [GCDWebServerDataResponse responseWithStatusCode:500]; + break; + } + + // Load messages + [chat loadMessagesBeforeDate:[NSDate date] limit:50 loadImmediately:YES]; + + NSMutableArray *messages = [NSMutableArray array]; + for (IMMessageItem *imMessage in [[chat chatItems] messages]) { + NSMutableDictionary *messageDict = [NSMutableDictionary dictionary]; + messageDict[@"text"] = [[imMessage body] string]; + messageDict[@"date"] = GCDWebServerFormatRFC822([imMessage time]); + messageDict[@"sender"] = [imMessage sender]; + [messages addObject:messageDict]; + } + + response = [GCDWebServerDataResponse responseWithJSONObject:messages]; + } while (0); + + self.serverCompletionBlock(response); +} + +@end diff --git a/kordophone/Bridge/Operations/MBIMSendMessageOperation.h b/kordophone/Bridge/Operations/MBIMSendMessageOperation.h new file mode 100644 index 0000000..8762af2 --- /dev/null +++ b/kordophone/Bridge/Operations/MBIMSendMessageOperation.h @@ -0,0 +1,17 @@ +// +// MBIMSendMessageOperation.h +// kordophoned +// +// Created by James Magahern on 11/13/18. +// Copyright © 2018 James Magahern. All rights reserved. +// + +#import "MBIMBridgeOperation.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface MBIMSendMessageOperation : MBIMBridgeOperation + +@end + +NS_ASSUME_NONNULL_END diff --git a/kordophone/Bridge/Operations/MBIMSendMessageOperation.m b/kordophone/Bridge/Operations/MBIMSendMessageOperation.m new file mode 100644 index 0000000..907c6c9 --- /dev/null +++ b/kordophone/Bridge/Operations/MBIMSendMessageOperation.m @@ -0,0 +1,63 @@ +// +// MBIMSendMessageOperation.m +// kordophoned +// +// Created by James Magahern on 11/13/18. +// Copyright © 2018 James Magahern. All rights reserved. +// + +#import "MBIMSendMessageOperation.h" + +#import +#import + +@implementation MBIMSendMessageOperation + ++ (void)load { [super load]; } + ++ (NSString *)endpointName +{ + return @"sendMessage"; +} + +- (BOOL)_sendMessage:(NSString *)messageBody toChatWithGUID:(NSString *)chatGUID +{ + IMAccount *iMessageAccount = [[IMAccountController sharedInstance] bestAccountForService:[IMServiceImpl iMessageService]]; + IMHandle *handle = [[iMessageAccount arrayOfAllIMHandles] firstObject]; + + NSAttributedString *replyAttrString = [[NSAttributedString alloc] initWithString:messageBody]; + IMMessage *reply = [IMMessage fromMeIMHandle:handle withText:replyAttrString fileTransferGUIDs:@[] flags:kIMMessageFinished]; + + IMChat *chat = [sChatRegistry existingChatWithGUID:chatGUID]; + if (!chat) { + NSLog(@"Chat does not exist: %@", chatGUID); + return NO; + } + + [chat sendMessage:reply]; + + return YES; +} + +- (void)main +{ + assert([self.request isKindOfClass:[GCDWebServerURLEncodedFormRequest class]]); + + GCDWebServerURLEncodedFormRequest *formRequest = (GCDWebServerURLEncodedFormRequest *)self.request; + NSDictionary *args = [formRequest arguments]; + + NSString *guid = [args objectForKey:@"guid"]; + NSString *messageBody = [args objectForKey:@"body"]; + BOOL result = [self _sendMessage:messageBody toChatWithGUID:guid]; + + GCDWebServerResponse *response = nil; + if (result) { + response = [GCDWebServerDataResponse responseWithStatusCode:200]; + } else { + response = [GCDWebServerDataResponse responseWithStatusCode:500]; + } + + self.serverCompletionBlock(response); +} + +@end