shlist

share and manage lists between multiple people
Log | Files | Refs

commit 1f042b19e6449935a4cbfead730c85d1222b3ee2
parent fe522bbf57ca278902bec44dfdf23d09c1cd537c
Author: Kyle Milz <kyle@Kyles-MacBook-Pro.local>
Date:   Sat, 19 Sep 2015 16:45:49 -0600

ios: rename ShlistServer class to Network

Diffstat:
Mios-ng/shlist.xcodeproj/project.pbxproj | 12++++++------
Mios-ng/shlist/ListTableViewController.m | 4++--
Mios-ng/shlist/MainTableViewController.m | 6+++---
Aios-ng/shlist/Network.h | 20++++++++++++++++++++
Aios-ng/shlist/Network.m | 459+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dios-ng/shlist/ShlistServer.h | 20--------------------
Dios-ng/shlist/ShlistServer.m | 459-------------------------------------------------------------------------------
7 files changed, 490 insertions(+), 490 deletions(-)

diff --git a/ios-ng/shlist.xcodeproj/project.pbxproj b/ios-ng/shlist.xcodeproj/project.pbxproj @@ -11,7 +11,7 @@ 27B03A021B43B8660054B6D2 /* AddressBook.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 27B03A001B43B8660054B6D2 /* AddressBook.framework */; }; 27C70F051B32AF8000DADEB3 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 27C70F041B32AF8000DADEB3 /* main.m */; }; 27C70F081B32AF8000DADEB3 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 27C70F071B32AF8000DADEB3 /* AppDelegate.m */; }; - 27C70F0B1B32AF8000DADEB3 /* ShlistServer.m in Sources */ = {isa = PBXBuildFile; fileRef = 27C70F0A1B32AF8000DADEB3 /* ShlistServer.m */; }; + 27C70F0B1B32AF8000DADEB3 /* Network.m in Sources */ = {isa = PBXBuildFile; fileRef = 27C70F0A1B32AF8000DADEB3 /* Network.m */; }; 27C70F0E1B32AF8000DADEB3 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 27C70F0C1B32AF8000DADEB3 /* Main.storyboard */; }; 27C70F101B32AF8000DADEB3 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 27C70F0F1B32AF8000DADEB3 /* Images.xcassets */; }; 27C70F131B32AF8000DADEB3 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 27C70F111B32AF8000DADEB3 /* LaunchScreen.xib */; }; @@ -45,8 +45,8 @@ 27C70F041B32AF8000DADEB3 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; tabWidth = 8; usesTabs = 1; }; 27C70F061B32AF8000DADEB3 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; }; 27C70F071B32AF8000DADEB3 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; }; - 27C70F091B32AF8000DADEB3 /* ShlistServer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ShlistServer.h; sourceTree = "<group>"; }; - 27C70F0A1B32AF8000DADEB3 /* ShlistServer.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ShlistServer.m; sourceTree = "<group>"; }; + 27C70F091B32AF8000DADEB3 /* Network.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Network.h; sourceTree = "<group>"; }; + 27C70F0A1B32AF8000DADEB3 /* Network.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Network.m; sourceTree = "<group>"; }; 27C70F0D1B32AF8000DADEB3 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; }; 27C70F0F1B32AF8000DADEB3 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; }; 27C70F121B32AF8000DADEB3 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = "<group>"; }; @@ -127,12 +127,12 @@ 27C70F0C1B32AF8000DADEB3 /* Main.storyboard */, 27C70F2E1B33F4FA00DADEB3 /* MainTableViewController.h */, 27C70F2F1B33F4FA00DADEB3 /* MainTableViewController.m */, + 27C70F091B32AF8000DADEB3 /* Network.h */, + 27C70F0A1B32AF8000DADEB3 /* Network.m */, 27AAC22A1B50ABAF00D99171 /* NewItemTableViewController.h */, 27AAC22B1B50ABAF00D99171 /* NewItemTableViewController.m */, 27C70F2B1B33F3C300DADEB3 /* NewListViewController.h */, 27C70F2C1B33F3C300DADEB3 /* NewListViewController.m */, - 27C70F091B32AF8000DADEB3 /* ShlistServer.h */, - 27C70F0A1B32AF8000DADEB3 /* ShlistServer.m */, 27C70F021B32AF8000DADEB3 /* Supporting Files */, ); path = shlist; @@ -269,7 +269,7 @@ files = ( BF7776B91B38928D00526CB0 /* ListTableViewController.m in Sources */, 27AAC22C1B50ABAF00D99171 /* NewItemTableViewController.m in Sources */, - 27C70F0B1B32AF8000DADEB3 /* ShlistServer.m in Sources */, + 27C70F0B1B32AF8000DADEB3 /* Network.m in Sources */, 27D805731BA2649D00867494 /* ContactsTableViewController.m in Sources */, 27C70F2A1B33D1C900DADEB3 /* DataStructures.m in Sources */, 27C70F2D1B33F3C300DADEB3 /* NewListViewController.m in Sources */, diff --git a/ios-ng/shlist/ListTableViewController.m b/ios-ng/shlist/ListTableViewController.m @@ -1,11 +1,11 @@ #import "ListTableViewController.h" #import "DataStructures.h" -#import "ShlistServer.h" +#import "Network.h" @interface ListTableViewController () - (void)load_initial_data; -@property (strong, nonatomic) ShlistServer *server; +@property (strong, nonatomic) Network *server; @end diff --git a/ios-ng/shlist/MainTableViewController.m b/ios-ng/shlist/MainTableViewController.m @@ -1,13 +1,13 @@ #import "MainTableViewController.h" #import "NewListViewController.h" -#import "ShlistServer.h" +#import "Network.h" #import "ListTableViewController.h" #import <AddressBook/AddressBook.h> @interface MainTableViewController () -@property (strong, nonatomic) ShlistServer *server; +@property (strong, nonatomic) Network *server; @end @@ -43,7 +43,7 @@ self.indirect_lists = [[NSMutableArray alloc] init]; // create one and only server instance, this gets passed around - _server = [[ShlistServer alloc] init]; + _server = [[Network alloc] init]; _server->shlist_tvc = self; if ([_server prepare]) { diff --git a/ios-ng/shlist/Network.h b/ios-ng/shlist/Network.h @@ -0,0 +1,19 @@ +#import <UIKit/UIKit.h> +#import "MainTableViewController.h" +#import "ListTableViewController.h" + +@interface Network : NSObject <NSStreamDelegate> { + NSInputStream *inputShlistStream; + NSOutputStream *outputShlistStream; + int *bytesRead; + + @public + MainTableViewController *shlist_tvc; + ListTableViewController *shlist_ldvc; + +} + +- (bool) prepare; +- (void) send_message:(uint16_t)msg_type contents:(NSData *)data; + +@end +\ No newline at end of file diff --git a/ios-ng/shlist/Network.m b/ios-ng/shlist/Network.m @@ -0,0 +1,459 @@ +#import "Network.h" +#import "DataStructures.h" +#import "AddressBook.h" + +@interface Network () + +@property (strong, retain) NSMutableData *data; +@property (strong, retain) AddressBook *address_book; +@property NSMutableDictionary *phnum_to_name_map; + + +@property (strong, nonatomic) NSString *phone_number; +@property (strong, nonatomic) NSData *device_id; + +@end + +@implementation Network + +- (id) init +{ + if (self = [super init]) { + /* + CFReadStreamRef readStream; + CFWriteStreamRef writeStream; + + CFStringRef host_name = CFSTR("absentmindedproductions.ca"); + + CFStreamCreatePairWithSocketToHost(NULL, host_name, 5437, &readStream, &writeStream); + inputShlistStream = (__bridge NSInputStream *)readStream; + outputShlistStream = (__bridge NSOutputStream *)writeStream; + + [inputShlistStream setDelegate:self]; + [outputShlistStream setDelegate:self]; + + [inputShlistStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; + [outputShlistStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; + + [inputShlistStream open]; + [outputShlistStream open]; + */ + + // get instance and wait for privacy window to clear + _address_book = [AddressBook shared_address_book]; + [_address_book wait_for_ready]; + + // the capacity here assumes one phone number per person + _phnum_to_name_map = [NSMutableDictionary + dictionaryWithCapacity:_address_book.num_contacts]; + + for (Contact *contact in _address_book.contacts) { + NSString *disp_name; + // show first name and last initial if possible, otherwise + // just show the first name or the last name or the phone number + if (contact.first_name && contact.last_name) + disp_name = [NSString stringWithFormat:@"%@ %@", + contact.first_name, [contact.last_name substringToIndex:1]]; + else if (contact.first_name) + disp_name = contact.first_name; + else if (contact.last_name) + disp_name = contact.last_name; + else if ([contact.phone_numbers count]) + disp_name = [contact.phone_numbers objectAtIndex:0]; + else + disp_name = @"No Name"; + + // map the persons known phone number to their massaged name + for (NSString *phone_number in contact.phone_numbers) + [_phnum_to_name_map setObject:disp_name forKey:phone_number]; + } + } + + return self; +} + +- (bool) prepare +{ + NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); + NSString *documentsDirectory = [paths objectAtIndex:0]; + + // NSString *phone_num_file = [documentsDirectory stringByAppendingPathComponent:@"phone_num"]; + NSString *device_id_file = [documentsDirectory stringByAppendingPathComponent:@"shlist_key"]; + + // NSError *error = nil; + // [[NSFileManager defaultManager] removeItemAtPath:destinationPath error:&error]; + + // TODO: also check the length of the file + if (![[NSFileManager defaultManager] fileExistsAtPath:device_id_file]) { + // no device id file found, send a registration message + NSMutableData *msg = [NSMutableData data]; + + // message type 0 + [msg appendBytes:"\x00\x00" length:2]; + + // phone number length is 10 + uint16_t length_network = htons(10); + [msg appendBytes:&length_network length:2]; + + // actual phone number + const char *phone_number = "4037082094"; + _phone_number = @"4037082094"; + [msg appendBytes:phone_number length:10]; + + [self writeToServer:msg]; + NSLog(@"info: sent registration message"); + + // we don't have a device id so we can't do anything yet + return false; + } + + // read device id from filesystem into memory + _device_id = [NSData dataWithContentsOfFile:device_id_file]; + + return true; +} + +- (void) send_message:(uint16_t)msg_type contents:(NSData *)payload +{ + NSMutableData *msg = [NSMutableData data]; + + uint16_t msg_type_network = htons(msg_type); + [msg appendBytes:&msg_type_network length:2]; + + int payload_length = 0; + if (payload) + // include null separator in this length + payload_length = [payload length] + 1; + + uint16_t msg_len_network = htons([_device_id length] + payload_length); + [msg appendBytes:&msg_len_network length:2]; + + [msg appendData:_device_id]; + + if (payload) { + [msg appendBytes:"\0" length:1]; + [msg appendData:payload]; + } + + [self writeToServer:msg]; +} + +- (void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)eventCode +{ + switch (eventCode) { + case NSStreamEventNone: + break; + case NSStreamEventOpenCompleted: + if (stream == inputShlistStream) { + NSLog(@"info: input stream opened"); + } + else if (stream == outputShlistStream) { + NSLog(@"info: output stream opened"); + } + break; + case NSStreamEventHasBytesAvailable: + if (stream == inputShlistStream) { + /* + if (![inputShlistStream hasBytesAvailable]) { + break; + } + */ + + NSInteger len; + uint16_t msg_metadata[2]; + + len = [inputShlistStream read:(uint8_t *)&msg_metadata maxLength:4]; + if (len != 4) { + NSLog(@"warn: read: msg metadata was %li bytes, expected 4", + (long)len); + break; + } + + uint16_t msg_type = ntohs(msg_metadata[0]); + uint16_t msg_length = ntohs(msg_metadata[1]); + if (msg_type > 6) { + NSLog(@"warn: read: out of range msg type %i", msg_type); + break; + } + + if (msg_length > 1024) { + NSLog(@"warn: read: message too large: %i bytes", msg_length); + break; + } + + // guaranteed valid message type and size from here forward + NSLog(@"info: read: got msg type %i (%i bytes)", msg_type, msg_length); + + uint8_t *buffer = malloc(msg_length); + if (buffer == nil) { + NSLog(@"warn: read: couldn't allocate receiving buffer size %i", + msg_length); + break; + } + + len = [inputShlistStream read:buffer maxLength:msg_length]; + if (len != msg_length) { + NSLog(@"warn: read: main message read byte mismatch: %li vs %i", + (long)len, msg_length); + break; + } + NSString *output = [[NSString alloc] initWithBytes:buffer length:len encoding:NSASCIIStringEncoding]; + NSData *data = [[NSData alloc] initWithBytes:buffer length:msg_length]; + + if (output == nil) { + NSLog(@"warn: read: couldn't allocate output string"); + break; + } + // NSLog(@"info: read: message is %@", output); + + if (msg_type == 0) { + // write key to file + NSLog(@"info: read: writing new keyfile to disk"); + + NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); + NSString *documentsDirectory = [paths objectAtIndex:0]; + + NSString *destinationPath = [documentsDirectory stringByAppendingPathComponent:@"shlist_key"]; + // if (![[NSFileManager defaultManager] fileExistsAtPath:destinationPath]) { + [data writeToFile:destinationPath atomically:YES]; + // } + + // set this so we're ready to send other message types + _device_id = data; + + // do a bulk list update + [self send_message:3 contents:nil]; + } + + if (msg_type == 1) { + NSLog(@"info: got new list response, not doing anything with it"); + } + + if (msg_type == 3) { + [self handle_bulk_list_update:output]; + } + + if (msg_type == 4) { + NSLog(@"info: join list response '%@'", output); + + SharedList *shlist = [[SharedList alloc] init]; + shlist.id = data; + + // XXX: these need to be sent from the server + shlist.items_ready = 0; + shlist.items_total = 99; + // shlist.list_name = <network>; + // shlist.members = <network>; + + [shlist_tvc finished_join_list_request:shlist]; + } + + if (msg_type == 5) { + NSLog(@"info: leave list response '%@'", output); + + NSArray *fields = [output componentsSeparatedByString:@"\0"]; + + if ([fields count] != 2) { + NSLog(@"warn: leave list response had wrong number (%i) of fields", + [fields count]); + break; + } + + SharedList *shlist = [[SharedList alloc] init]; + shlist.id = [[fields objectAtIndex:0] dataUsingEncoding:NSUTF8StringEncoding]; + + // XXX: these need to be sent from the server + // shlist.list_name = <network>; + // shlist.members = <network>; + + [shlist_tvc finished_leave_list_request:shlist]; + } + } + break; + case NSStreamEventHasSpaceAvailable: + [self _writeData]; + break; + case NSStreamEventErrorOccurred: + NSLog(@"ShlistServer::NSStreamEventErrorOccurred"); + // I saw this case when trying to connect to a down server + break; + case NSStreamEventEndEncountered: + { + NSLog(@"ShlistServer::NSStreamEventEndEncountered"); + [inputShlistStream close]; + [outputShlistStream close]; + + [inputShlistStream removeFromRunLoop:[NSRunLoop currentRunLoop] + forMode:NSDefaultRunLoopMode]; + [outputShlistStream removeFromRunLoop:[NSRunLoop currentRunLoop] + forMode:NSDefaultRunLoopMode]; + // [inputShlistStream release]; + // [outputShlistStream release]; + + inputShlistStream = nil; // stream is ivar, so reinit it + outputShlistStream = nil; // stream is ivar, so reinit it + + break; + } + default: + break; + } +} + +- (void) handle_bulk_list_update:(NSString *)raw_data +{ + NSLog(@"info: handling bulk list update message"); + + // split over double \0 + NSArray *list_types = [raw_data componentsSeparatedByString:@"\0\0"]; + if ([list_types count] != 2) { + NSLog(@"warn: wrong number if \\0\\0 found: %i", [list_types count]); + return; + } + + NSString *my_lists = [list_types objectAtIndex:0]; + NSString *my_friends_lists = [list_types objectAtIndex:1]; + + [shlist_tvc.shared_lists removeAllObjects]; + [shlist_tvc.indirect_lists removeAllObjects]; + + if ([my_lists length] != 0) { + NSArray *my_lists_parsed = [self parse_lists:my_lists]; + [shlist_tvc.shared_lists addObjectsFromArray:my_lists_parsed]; + } + if ([my_friends_lists length] != 0) { + NSArray *indirect_lists = [self parse_lists:my_friends_lists]; + [shlist_tvc.indirect_lists addObjectsFromArray:indirect_lists]; + } + + [shlist_tvc.tableView reloadData]; +} + +- (NSArray *) parse_lists:(NSString *)raw_lists +{ + // each raw list is separated by a \0 + NSArray *lists = [raw_lists componentsSeparatedByString:@"\0"]; + NSMutableArray *output = [[NSMutableArray alloc] init]; + + for (id str in lists) { + NSArray *list_fields = [str componentsSeparatedByString:@":"]; + int field_count = [list_fields count]; + + if (field_count < 3) { + NSLog(@"warn: less than 3 fields found: %i", field_count); + + // can't do anything with this list + continue; + } + NSLog(@"info: parse_list: '%@' has %i fields", + [list_fields objectAtIndex:0], field_count); + + NSMutableArray *members = [[NSMutableArray alloc] init]; + int others = 0; + + // anything past the second field are list members + NSArray *phone_numbers = [list_fields subarrayWithRange:NSMakeRange(2, field_count - 2)]; + for (id phone_number in phone_numbers) { + + // try to find the list member in our address book + NSString *name = _phnum_to_name_map[phone_number]; + + if (name) + [members addObject:name]; + else if ([phone_number compare:_phone_number]) + [members addObject:@"You"]; + else + // didn't find it, you don't know this person + others++; + } + + NSMutableString *members_str = + [[members componentsJoinedByString:@", "] mutableCopy]; + + if (others) { + char *plural; + if (others == 1) + plural = "other"; + else + plural = "others"; + + NSString *buf = [NSString stringWithFormat:@" + %i %s", + others, plural]; + [members_str appendString:buf]; + } + + // we've got everything we need + SharedList *shared_list = [[SharedList alloc] init]; + + shared_list.name = [list_fields objectAtIndex:0]; + shared_list.id = [[list_fields objectAtIndex:1] dataUsingEncoding:NSUTF8StringEncoding]; + shared_list.members = members_str; + + // we don't currently get this information back + // XXX: lists your not in will not return this information + sranddev(); + shared_list.items_ready = rand() % 7; + shared_list.items_total = 7; + + [output addObject:shared_list]; + } + + return output; +} + +- (void) dealloc +{ + [inputShlistStream close]; + [outputShlistStream close]; + + [inputShlistStream removeFromRunLoop:[NSRunLoop currentRunLoop] + forMode:NSDefaultRunLoopMode]; + [outputShlistStream removeFromRunLoop:[NSRunLoop currentRunLoop] + forMode:NSDefaultRunLoopMode]; + + inputShlistStream = nil; // stream is ivar, so reinit it + outputShlistStream = nil; // stream is ivar, so reinit it +} + +- (void) _readData +{ +} + +- (void) _writeData +{ + NSLog(@"_writeData"); +} + +- (void) writeToServer:(NSData *)data +{ + CFReadStreamRef readStream; + CFWriteStreamRef writeStream; + + CFStringRef host_name = CFSTR("absentmindedproductions.ca"); + + CFStreamCreatePairWithSocketToHost(NULL, host_name, 5437, &readStream, &writeStream); + inputShlistStream = (__bridge NSInputStream *)readStream; + outputShlistStream = (__bridge NSOutputStream *)writeStream; + + [inputShlistStream setDelegate:self]; + [outputShlistStream setDelegate:self]; + + [inputShlistStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; + [outputShlistStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; + + [inputShlistStream open]; + [outputShlistStream open]; + + // const char bytes[] = "\x00\x00\xff\0x00"; + //string literals have implicit trailing '\0' + // size_t length = (sizeof bytes) - 1; + + // NSData *data = [NSData dataWithBytes:bytes length:length]; + NSLog(@"writeToServer()"); + [outputShlistStream write:[data bytes] maxLength:[data length]]; +} + +// - (void) readFromServer: + + +@end diff --git a/ios-ng/shlist/ShlistServer.h b/ios-ng/shlist/ShlistServer.h @@ -1,19 +0,0 @@ -#import <UIKit/UIKit.h> -#import "MainTableViewController.h" -#import "ListTableViewController.h" - -@interface ShlistServer : NSObject <NSStreamDelegate> { - NSInputStream *inputShlistStream; - NSOutputStream *outputShlistStream; - int *bytesRead; - - @public - MainTableViewController *shlist_tvc; - ListTableViewController *shlist_ldvc; - -} - -- (bool) prepare; -- (void) send_message:(uint16_t)msg_type contents:(NSData *)data; - -@end -\ No newline at end of file diff --git a/ios-ng/shlist/ShlistServer.m b/ios-ng/shlist/ShlistServer.m @@ -1,459 +0,0 @@ -#import "ShlistServer.h" -#import "DataStructures.h" -#import "AddressBook.h" - -@interface ShlistServer () - -@property (strong, retain) NSMutableData *data; -@property (strong, retain) AddressBook *address_book; -@property NSMutableDictionary *phnum_to_name_map; - - -@property (strong, nonatomic) NSString *phone_number; -@property (strong, nonatomic) NSData *device_id; - -@end - -@implementation ShlistServer - -- (id) init -{ - if (self = [super init]) { - /* - CFReadStreamRef readStream; - CFWriteStreamRef writeStream; - - CFStringRef host_name = CFSTR("absentmindedproductions.ca"); - - CFStreamCreatePairWithSocketToHost(NULL, host_name, 5437, &readStream, &writeStream); - inputShlistStream = (__bridge NSInputStream *)readStream; - outputShlistStream = (__bridge NSOutputStream *)writeStream; - - [inputShlistStream setDelegate:self]; - [outputShlistStream setDelegate:self]; - - [inputShlistStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; - [outputShlistStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; - - [inputShlistStream open]; - [outputShlistStream open]; - */ - - // get instance and wait for privacy window to clear - _address_book = [AddressBook shared_address_book]; - [_address_book wait_for_ready]; - - // the capacity here assumes one phone number per person - _phnum_to_name_map = [NSMutableDictionary - dictionaryWithCapacity:_address_book.num_contacts]; - - for (Contact *contact in _address_book.contacts) { - NSString *disp_name; - // show first name and last initial if possible, otherwise - // just show the first name or the last name or the phone number - if (contact.first_name && contact.last_name) - disp_name = [NSString stringWithFormat:@"%@ %@", - contact.first_name, [contact.last_name substringToIndex:1]]; - else if (contact.first_name) - disp_name = contact.first_name; - else if (contact.last_name) - disp_name = contact.last_name; - else if ([contact.phone_numbers count]) - disp_name = [contact.phone_numbers objectAtIndex:0]; - else - disp_name = @"No Name"; - - // map the persons known phone number to their massaged name - for (NSString *phone_number in contact.phone_numbers) - [_phnum_to_name_map setObject:disp_name forKey:phone_number]; - } - } - - return self; -} - -- (bool) prepare -{ - NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); - NSString *documentsDirectory = [paths objectAtIndex:0]; - - // NSString *phone_num_file = [documentsDirectory stringByAppendingPathComponent:@"phone_num"]; - NSString *device_id_file = [documentsDirectory stringByAppendingPathComponent:@"shlist_key"]; - - // NSError *error = nil; - // [[NSFileManager defaultManager] removeItemAtPath:destinationPath error:&error]; - - // TODO: also check the length of the file - if (![[NSFileManager defaultManager] fileExistsAtPath:device_id_file]) { - // no device id file found, send a registration message - NSMutableData *msg = [NSMutableData data]; - - // message type 0 - [msg appendBytes:"\x00\x00" length:2]; - - // phone number length is 10 - uint16_t length_network = htons(10); - [msg appendBytes:&length_network length:2]; - - // actual phone number - const char *phone_number = "4037082094"; - _phone_number = @"4037082094"; - [msg appendBytes:phone_number length:10]; - - [self writeToServer:msg]; - NSLog(@"info: sent registration message"); - - // we don't have a device id so we can't do anything yet - return false; - } - - // read device id from filesystem into memory - _device_id = [NSData dataWithContentsOfFile:device_id_file]; - - return true; -} - -- (void) send_message:(uint16_t)msg_type contents:(NSData *)payload -{ - NSMutableData *msg = [NSMutableData data]; - - uint16_t msg_type_network = htons(msg_type); - [msg appendBytes:&msg_type_network length:2]; - - int payload_length = 0; - if (payload) - // include null separator in this length - payload_length = [payload length] + 1; - - uint16_t msg_len_network = htons([_device_id length] + payload_length); - [msg appendBytes:&msg_len_network length:2]; - - [msg appendData:_device_id]; - - if (payload) { - [msg appendBytes:"\0" length:1]; - [msg appendData:payload]; - } - - [self writeToServer:msg]; -} - -- (void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)eventCode -{ - switch (eventCode) { - case NSStreamEventNone: - break; - case NSStreamEventOpenCompleted: - if (stream == inputShlistStream) { - NSLog(@"info: input stream opened"); - } - else if (stream == outputShlistStream) { - NSLog(@"info: output stream opened"); - } - break; - case NSStreamEventHasBytesAvailable: - if (stream == inputShlistStream) { - /* - if (![inputShlistStream hasBytesAvailable]) { - break; - } - */ - - NSInteger len; - uint16_t msg_metadata[2]; - - len = [inputShlistStream read:(uint8_t *)&msg_metadata maxLength:4]; - if (len != 4) { - NSLog(@"warn: read: msg metadata was %li bytes, expected 4", - (long)len); - break; - } - - uint16_t msg_type = ntohs(msg_metadata[0]); - uint16_t msg_length = ntohs(msg_metadata[1]); - if (msg_type > 6) { - NSLog(@"warn: read: out of range msg type %i", msg_type); - break; - } - - NSLog(@"info: read: received message type %i", msg_type); - - if (msg_length > 1024) { - NSLog(@"warn: read: message too large: %i bytes", msg_length); - break; - } - NSLog(@"info: read: message size is %i bytes", msg_length); - - uint8_t *buffer = malloc(msg_length); - if (buffer == nil) { - NSLog(@"warn: read: couldn't allocate receiving buffer size %i", - msg_length); - break; - } - - len = [inputShlistStream read:buffer maxLength:msg_length]; - if (len != msg_length) { - NSLog(@"warn: read: main message read byte mismatch: %li vs %i", - (long)len, msg_length); - break; - } - NSString *output = [[NSString alloc] initWithBytes:buffer length:len encoding:NSASCIIStringEncoding]; - NSData *data = [[NSData alloc] initWithBytes:buffer length:msg_length]; - - if (output == nil) { - NSLog(@"warn: read: couldn't allocate output string"); - break; - } - // NSLog(@"info: read: message is %@", output); - - if (msg_type == 0) { - // write key to file - NSLog(@"info: read: writing new keyfile to disk"); - - NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); - NSString *documentsDirectory = [paths objectAtIndex:0]; - - NSString *destinationPath = [documentsDirectory stringByAppendingPathComponent:@"shlist_key"]; - // if (![[NSFileManager defaultManager] fileExistsAtPath:destinationPath]) { - [data writeToFile:destinationPath atomically:YES]; - // } - - // set this so we're ready to send other message types - _device_id = data; - - // do a bulk list update - [self send_message:3 contents:nil]; - } - - if (msg_type == 1) { - NSLog(@"info: got new list response, not doing anything with it"); - } - - if (msg_type == 3) { - [self handle_bulk_list_update:output]; - } - - if (msg_type == 4) { - NSLog(@"info: join list response '%@'", output); - - SharedList *shlist = [[SharedList alloc] init]; - shlist.id = data; - - // XXX: these need to be sent from the server - shlist.items_ready = 0; - shlist.items_total = 99; - // shlist.list_name = <network>; - // shlist.members = <network>; - - [shlist_tvc finished_join_list_request:shlist]; - } - - if (msg_type == 5) { - NSLog(@"info: leave list response '%@'", output); - - NSArray *fields = [output componentsSeparatedByString:@"\0"]; - - if ([fields count] != 2) { - NSLog(@"warn: leave list response had wrong number (%i) of fields", - [fields count]); - break; - } - - SharedList *shlist = [[SharedList alloc] init]; - shlist.id = [[fields objectAtIndex:0] dataUsingEncoding:NSUTF8StringEncoding]; - - // XXX: these need to be sent from the server - // shlist.list_name = <network>; - // shlist.members = <network>; - - [shlist_tvc finished_leave_list_request:shlist]; - } - } - break; - case NSStreamEventHasSpaceAvailable: - [self _writeData]; - break; - case NSStreamEventErrorOccurred: - NSLog(@"ShlistServer::NSStreamEventErrorOccurred"); - // I saw this case when trying to connect to a down server - break; - case NSStreamEventEndEncountered: - { - NSLog(@"ShlistServer::NSStreamEventEndEncountered"); - [inputShlistStream close]; - [outputShlistStream close]; - - [inputShlistStream removeFromRunLoop:[NSRunLoop currentRunLoop] - forMode:NSDefaultRunLoopMode]; - [outputShlistStream removeFromRunLoop:[NSRunLoop currentRunLoop] - forMode:NSDefaultRunLoopMode]; - // [inputShlistStream release]; - // [outputShlistStream release]; - - inputShlistStream = nil; // stream is ivar, so reinit it - outputShlistStream = nil; // stream is ivar, so reinit it - - break; - } - default: - break; - } -} - -- (void) handle_bulk_list_update:(NSString *)raw_data -{ - NSLog(@"info: handling bulk list update message"); - - // split over double \0 - NSArray *list_types = [raw_data componentsSeparatedByString:@"\0\0"]; - if ([list_types count] != 2) { - NSLog(@"warn: wrong number if \\0\\0 found: %i", [list_types count]); - return; - } - - NSString *my_lists = [list_types objectAtIndex:0]; - NSString *my_friends_lists = [list_types objectAtIndex:1]; - - [shlist_tvc.shared_lists removeAllObjects]; - [shlist_tvc.indirect_lists removeAllObjects]; - - if ([my_lists length] != 0) { - NSArray *my_lists_parsed = [self parse_lists:my_lists]; - [shlist_tvc.shared_lists addObjectsFromArray:my_lists_parsed]; - } - if ([my_friends_lists length] != 0) { - NSArray *indirect_lists = [self parse_lists:my_friends_lists]; - [shlist_tvc.indirect_lists addObjectsFromArray:indirect_lists]; - } - - [shlist_tvc.tableView reloadData]; -} - -- (NSArray *) parse_lists:(NSString *)raw_lists -{ - // each raw list is separated by a \0 - NSArray *lists = [raw_lists componentsSeparatedByString:@"\0"]; - NSMutableArray *output = [[NSMutableArray alloc] init]; - - for (id str in lists) { - NSArray *list_fields = [str componentsSeparatedByString:@":"]; - int field_count = [list_fields count]; - - if (field_count < 3) { - NSLog(@"warn: less than 3 fields found: %i", field_count); - - // can't do anything with this list - continue; - } - NSLog(@"info: parse_list: '%@' has %i fields", - [list_fields objectAtIndex:0], field_count); - - NSMutableArray *members = [[NSMutableArray alloc] init]; - int others = 0; - - // anything past the second field are list members - NSArray *phone_numbers = [list_fields subarrayWithRange:NSMakeRange(2, field_count - 2)]; - for (id phone_number in phone_numbers) { - - // try to find the list member in our address book - NSString *name = _phnum_to_name_map[phone_number]; - - if (name) - [members addObject:name]; - else if ([phone_number compare:_phone_number]) - [members addObject:@"You"]; - else - // didn't find it, you don't know this person - others++; - } - - NSMutableString *members_str = - [[members componentsJoinedByString:@", "] mutableCopy]; - - if (others) { - char *plural; - if (others == 1) - plural = "other"; - else - plural = "others"; - - NSString *buf = [NSString stringWithFormat:@" + %i %s", - others, plural]; - [members_str appendString:buf]; - } - - // we've got everything we need - SharedList *shared_list = [[SharedList alloc] init]; - - shared_list.name = [list_fields objectAtIndex:0]; - shared_list.id = [[list_fields objectAtIndex:1] dataUsingEncoding:NSUTF8StringEncoding]; - shared_list.members = members_str; - - // we don't currently get this information back - // XXX: lists your not in will not return this information - sranddev(); - shared_list.items_ready = rand() % 7; - shared_list.items_total = 7; - - [output addObject:shared_list]; - } - - return output; -} - -- (void) dealloc -{ - [inputShlistStream close]; - [outputShlistStream close]; - - [inputShlistStream removeFromRunLoop:[NSRunLoop currentRunLoop] - forMode:NSDefaultRunLoopMode]; - [outputShlistStream removeFromRunLoop:[NSRunLoop currentRunLoop] - forMode:NSDefaultRunLoopMode]; - - inputShlistStream = nil; // stream is ivar, so reinit it - outputShlistStream = nil; // stream is ivar, so reinit it -} - -- (void) _readData -{ -} - -- (void) _writeData -{ - NSLog(@"_writeData"); -} - -- (void) writeToServer:(NSData *)data -{ - CFReadStreamRef readStream; - CFWriteStreamRef writeStream; - - CFStringRef host_name = CFSTR("absentmindedproductions.ca"); - - CFStreamCreatePairWithSocketToHost(NULL, host_name, 5437, &readStream, &writeStream); - inputShlistStream = (__bridge NSInputStream *)readStream; - outputShlistStream = (__bridge NSOutputStream *)writeStream; - - [inputShlistStream setDelegate:self]; - [outputShlistStream setDelegate:self]; - - [inputShlistStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; - [outputShlistStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; - - [inputShlistStream open]; - [outputShlistStream open]; - - // const char bytes[] = "\x00\x00\xff\0x00"; - //string literals have implicit trailing '\0' - // size_t length = (sizeof bytes) - 1; - - // NSData *data = [NSData dataWithBytes:bytes length:length]; - NSLog(@"writeToServer()"); - [outputShlistStream write:[data bytes] maxLength:[data length]]; -} - -// - (void) readFromServer: - - -@end