Skip to content

Instantly share code, notes, and snippets.

@BobBurns
Created July 19, 2015 03:24
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save BobBurns/25288e5e7d1b6bac74eb to your computer and use it in GitHub Desktop.
Save BobBurns/25288e5e7d1b6bac74eb to your computer and use it in GitHub Desktop.
mac app to receive nfc data
//
// AppDelegate.m
// nfc_socket_client
//
// Created by WozniBob on 6/19/15.
// Copyright (c) 2015 Bob_Burns. All rights reserved.
//
#import "AppDelegate.h"
#import "Student.h"
#import <errno.h>
#import <fcntl.h>
#import <stdbool.h>
#import <string.h>
#import <unistd.h>
#import <arpa/inet.h> // inet_ntop()
#import <netdb.h> //gethostbyname2()
#import <netinet/in.h> // struct sockaddr_in
#import <netinet6/in6.h> //struct sockaddr_in6
#import <sys/socket.h> // socket(), AF_INET
#import <sys/types.h>
#define MAX_MESSAGE_SIZE (UINT8_MAX)
#define READ_BUFFER_SIZE (256)
static const in_port_t kPortNumber = 2342;
static const int kInvalidSocket = -1;
static int WriteMessage (int fd, const void *buffer, size_t length);
static int SocketConnectedToHostNamed (const char *hostname);
static bool GetAddressAtIndex (struct hostent *host, int addressIndex,
struct sockaddr_storage *outServerAddress);
#define debug 1
@interface AppDelegate () <NSAlertDelegate> {
CFSocketNativeHandle _sockfd;
CFSocketRef _socketRef;
}
@property (weak) IBOutlet NSWindow *window;
@property (strong) NSInputStream *inputStream;
@property (strong) NSOutputStream *outStream;
@property (strong) NSMutableData *dataToRead;
@property (strong) NSMutableData *dataToWrite;
@property NSInteger bytesRead;
@property (nonatomic, readonly, getter=isConnected) BOOL connected;
- (void) updateUI;
- (void) runErrorMessage: (NSString *)message withError:(int)err;
// Connection
- (void)connectToHost: (NSString *)hostname asUser:(NSString *)pass;
- (void)closeConnection;
// Socket
- (void)startMonitoringSocket;
- (void)stopMonitoringSocket;
// RunLoop
static void ReceiveMessage (CFSocketRef, CFSocketCallBackType, CFDataRef, const void *, void *);
- (void)handleMessageData: (NSData *)data;
- (IBAction)scanButton:(id)sender;
- (IBAction)closeButton:(id)sender;
- (IBAction)connect:(id)sender;
@property (weak) IBOutlet NSTextField *connectedLabel;
@property (weak) IBOutlet NSTextField *creditLabel;
@property (weak) IBOutlet NSTextField *nameLable;
@property (weak) IBOutlet NSTextField *scanLabel;
@property (weak) IBOutlet NSProgressIndicator *pIndicate;
@property (weak) IBOutlet NSSecureTextField *passField;
@end
@implementation AppDelegate
@synthesize persistentStoreCoordinator = _persistentStoreCoordinator;
@synthesize managedObjectModel = _managedObjectModel;
@synthesize managedObjectContext = _managedObjectContext;
#pragma mark CoreData Stack
- (NSURL *)applicationFilesDirectory {
NSFileManager *fileManager = [NSFileManager defaultManager];
NSURL *appSupportURL = [[fileManager URLsForDirectory:NSApplicationSupportDirectory inDomains:NSUserDomainMask] lastObject];
return [appSupportURL URLByAppendingPathComponent:@"com.bobburnsapps.nfc-socket-client"];
}
- (NSManagedObjectModel *)managedObjectModel {
if (_managedObjectModel) {
return _managedObjectModel;
}
NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"Person" withExtension:@"momd"];
_managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
return _managedObjectModel;
}
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
if (_persistentStoreCoordinator) {
return _persistentStoreCoordinator;
}
NSManagedObjectModel *mom = [self managedObjectModel];
if (!mom) {
NSLog(@"%@:%@ No model to generate a store from", [self class], NSStringFromSelector(_cmd));
return nil;
}
NSFileManager *fileManager = [NSFileManager defaultManager];
NSURL *applicationFilesDirectory = [self applicationFilesDirectory];
NSError *error = nil;
NSDictionary *properties = [applicationFilesDirectory resourceValuesForKeys:@[NSURLIsDirectoryKey] error:&error];
if (!properties) {
BOOL ok = FALSE;
if ([error code] == NSFileReadNoSuchFileError) {
ok = [fileManager createDirectoryAtPath:[applicationFilesDirectory path] withIntermediateDirectories:YES attributes:nil error:&error];
}
if (!ok) {
[[NSApplication sharedApplication] presentError:error];
return nil;
}
} else {
if (![properties[NSURLIsDirectoryKey] boolValue]) {
NSString *failureDescription = [NSString stringWithFormat:@"Expected a folder to store application data, found a file (%@).", [applicationFilesDirectory path]];
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
[dict setValue:failureDescription forKey:NSLocalizedDescriptionKey];
error = [NSError errorWithDomain:@"ERROR_DOMAIN" code:101 userInfo:dict];
[[NSApplication sharedApplication] presentError:error];
return nil;
}
}
NSURL *url = [applicationFilesDirectory URLByAppendingPathComponent:@"Person.storedata"];
NSPersistentStoreCoordinator *coordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:mom];
if (![coordinator addPersistentStoreWithType:NSXMLStoreType configuration:nil URL:url options:nil error:&error]) {
[[NSApplication sharedApplication] presentError:error];
return nil;
}
_persistentStoreCoordinator = coordinator;
return _persistentStoreCoordinator;
}
- (NSManagedObjectContext *)managedObjectContext {
if (_managedObjectContext) {
return _managedObjectContext;
}
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
if (!coordinator) {
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
[dict setValue:@"Failed to initialize the store" forKey:NSLocalizedDescriptionKey];
[dict setValue:@"There was an error building up the data file." forKey:NSLocalizedFailureReasonErrorKey];
NSError *error = [NSError errorWithDomain:@"NFC_APP DOMAIN" code:9999 userInfo:dict];
[[NSApplication sharedApplication] presentError:error];
return nil;
}
_managedObjectContext = [[NSManagedObjectContext alloc] init];
[_managedObjectContext setPersistentStoreCoordinator:coordinator];
return _managedObjectContext;
}
- (NSUndoManager *)windowWillReturnUndoManager:(NSWindow *)window {
return [[self managedObjectContext] undoManager];
}
- (IBAction)saveAction:(id)sender {
NSError *error = nil;
if (![[self managedObjectContext] commitEditing]) {
NSLog(@"%@:%@ unable to commit editing before saving", [self class], NSStringFromSelector(_cmd));
}
if (![[self managedObjectContext] save:&error]) {
[[NSApplication sharedApplication] presentError:error];
}
}
- (void)updateStudentTable {
NSManagedObjectContext *moc = [self managedObjectContext];
NSEntityDescription *entityDescription = [NSEntityDescription entityForName:@"Student" inManagedObjectContext:moc];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entityDescription];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"name" ascending:YES];
[request setSortDescriptors:@[sortDescriptor]];
NSError *error = nil;
NSArray *array = [moc executeFetchRequest:request error:&error];
if (array == nil) {
NSLog(@"error fetching student");
[[NSApplication sharedApplication] presentError:error];
} else {
NSLog(@"looking at students...");
BOOL found = NO;
for (Student *student in array) {
if ([student.name isEqualToString:[_nameLable stringValue]]) {
student.credits = [NSNumber numberWithInt:[_creditLabel intValue]];
NSLog(@"found match");
found = YES;
}
}
if (!found) {
NSLog(@"No one named %@ in array", [_nameLable stringValue]);
Student *newStudent = [NSEntityDescription insertNewObjectForEntityForName:@"Student" inManagedObjectContext:moc];
newStudent.name = [_nameLable stringValue];
newStudent.credits = [NSNumber numberWithInt:[_creditLabel intValue]];
}
}
if ([moc hasChanges]) {
if (![moc save:&error])
NSLog(@"Error saving changes: %@, %@", error, [error userInfo]);
}
}
- (id)init {
if ((self = [super init])) {
_sockfd = kInvalidSocket;
}
return self;
}
- (void)dealloc {
[self closeConnection];
}
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
// Insert code here to initialize your application
[_pIndicate setStyle:NSProgressIndicatorSpinningStyle];
[_scanLabel setHidden:YES];
[self updateUI];
if (![self isConnected]) {
NSAlert *alert = [[NSAlert alloc] init];
[alert setMessageText:@"Please Enter Password"];
[alert addButtonWithTitle:@"Join"];
[alert addButtonWithTitle:@"Cancel"];
[alert setDelegate:(id)self];
NSSecureTextField *input = [[NSSecureTextField alloc] initWithFrame:NSMakeRect(0, 0, 200, 24)];
[input setStringValue:@""];
[alert setAccessoryView:input];
NSInteger button = [alert runModal];
if (button == NSAlertFirstButtonReturn) {
NSString *password = [input stringValue];
[self connectToHost:@"10.0.0.6" asUser:password];
[self updateUI];
}
else if (button == NSAlertSecondButtonReturn) {
exit(0);
}
}
}
- (BOOL)isConnected {
BOOL connected = (_socketRef != NULL);
return connected;
}
- (void)startStream {
}
- (void)handleData:(NSMutableData *)data {
}
- (void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)eventCode {
}
- (void)applicationWillTerminate:(NSNotification *)aNotification {
// Insert code here to tear down your application
}
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender {
if (!_managedObjectContext) {
return NSTerminateNow;
}
if (![[self managedObjectContext] commitEditing]) {
NSLog(@"%@:%@ unable to commit editing to terminate", [self class], NSStringFromSelector(_cmd));
return NSTerminateCancel;
}
if (![[self managedObjectContext] hasChanges]) {
return NSTerminateNow;
}
NSError *error = nil;
if (![[self managedObjectContext] save:&error]) {
BOOL result = [sender presentError:error];
if (result) {
return NSTerminateCancel;
}
NSString *question = NSLocalizedString(@"Could not save changes wile quitting. Quit anyway?", @"Quit without saves error question message");
NSString *info = NSLocalizedString(@"Quitting now will lose any changes you've made since last successful save", @"Quit without saves error question info");
NSString *quitButton = @"Quit anyway";
NSString *cancelButton = @"Cancel";
NSAlert *alert = [[NSAlert alloc] init];
[alert setMessageText:question];
[alert setInformativeText:info];
[alert addButtonWithTitle:quitButton];
[alert addButtonWithTitle:cancelButton];
NSInteger answer = [alert runModal];
if (answer == NSAlertAlternateReturn) {
return NSTerminateCancel;
}
}
return NSTerminateNow;
}
- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender {
return YES;
}
- (IBAction)scanButton:(id)sender {
if (_sockfd == kInvalidSocket) return;
[_pIndicate startAnimation:self];
[_scanLabel setHidden:NO];
uint8_t cmd_buf[3];
cmd_buf[0] = 0x80; //opcode
cmd_buf[1] = 0x01; //length
cmd_buf[2] = 0x20; //cmd
int nwritten = WriteMessage(_sockfd, cmd_buf, 3);
if (nwritten == 3) printf("write scan successful!\n");
}
- (IBAction)closeButton:(id)sender {
if (_sockfd == kInvalidSocket) return;
//tell server we are closing socket
uint8_t cmd_buf[3];
cmd_buf[0] = 0x80; //opcode
cmd_buf[1] = 0x01; //length
cmd_buf[2] = 0x80; //cmd
int nwritten = WriteMessage(_sockfd, cmd_buf, 3);
if (nwritten == 3) printf("write close successful!\n");
[self closeConnection];
[self updateUI];
}
- (IBAction)connect:(id)sender {
NSString *hostname = @"71.204.163.123";
NSString *pass = [_passField stringValue];
[self connectToHost:hostname asUser:pass];
[self updateUI];
if ([self isConnected]) {
NSLog(@"Connected!");
}
}
- (void) updateUI {
const BOOL connected = [self isConnected];
[_connectedLabel setStringValue:connected ? @"Connected" : @"Not Connected"];
[_tableView reloadData];
}
- (void)runErrorMessage:(NSString *)message withError:(int)err {
NSString *errorString = @"";
if (err != 0) {
errorString = ([NSString stringWithUTF8String:strerror(err)]);
}
NSAlert *alert = [[NSAlert alloc] init];
[alert setMessageText:@"Error occured!"];
[alert setInformativeText:errorString];
[alert runModal];
}
- (void)connectToHost:(NSString *)hostname asUser:(NSString *)pass {
NSString *errorMessage = nil;
int sysError = noErr;
if (_sockfd != kInvalidSocket) [self closeConnection];
if (hostname.length < 1) {
errorMessage = @"Hostname must not be empty";
goto bailout;
}
if (pass.length == 0 || pass.length > 8) {
errorMessage = @"Password must be between 1 and 8 characters long.";
goto bailout;
}
const char *hostnameCtr = [hostname UTF8String];
_sockfd = SocketConnectedToHostNamed(hostnameCtr);
const char *passwrd = [pass UTF8String];
NSUInteger passlen = strlen(passwrd);
int nwritten = WriteMessage(_sockfd, passwrd, passlen);
if (nwritten == -1) {
errorMessage = @"Failed to send username.";
sysError = errno;
goto bailout;
}
//Make the socket non-blocking
int err = fcntl(_sockfd, F_SETFL, O_NONBLOCK);
if (err == -1) {
errorMessage = @"Couldn't put socket into non-blocking mode";
sysError = errno;
goto bailout;
}
[self startMonitoringSocket];
bailout:
if (errorMessage != nil) {
[self runErrorMessage:errorMessage withError:sysError];
[self closeConnection];
}
return;
}
- (void)closeConnection {
[self stopMonitoringSocket];
close(_sockfd);
_sockfd = kInvalidSocket;
}
- (void)startMonitoringSocket {
CFSocketContext context = {0, (__bridge void *)(self), NULL, NULL, NULL};
_socketRef = CFSocketCreateWithNative(kCFAllocatorDefault,
_sockfd,
kCFSocketDataCallBack,
ReceiveMessage,
&context);
if (_socketRef == NULL) {
[self runErrorMessage:@"Unable to create CFSocketRef." withError:noErr];
goto bailout;
}
CFRunLoopSourceRef rls = CFSocketCreateRunLoopSource(kCFAllocatorDefault, _socketRef, 0);
if (rls == NULL) {
[self runErrorMessage:@"Unable to create socket run loop source." withError:noErr];
goto bailout;
}
CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
CFRelease(rls);
bailout:
return;
}
- (void)stopMonitoringSocket {
if (_socketRef != NULL) {
CFSocketInvalidate(_socketRef);
CFRelease(_socketRef);
_socketRef = NULL;
}
}
static void ReceiveMessage (CFSocketRef socket, CFSocketCallBackType type,
CFDataRef address, const void *data, void *info) {
AppDelegate *self = (__bridge AppDelegate *)(info);
[self handleMessageData:(__bridge NSData *)(data)];
}
- (void)handleMessageData:(NSData *)data {
// Closed connection?
if (data.length == 0) {
[self closeConnection];
[self runErrorMessage:@"The server closed the connection." withError:noErr];
return;
}
uint8_t byteArray[256];
int credits;
const unsigned char* name;
[data getBytes:byteArray length:256];
NSLog(@"byte array: %s", byteArray);
if (byteArray[0] == 0x82) {
switch (byteArray[1]) {
case 0xA1:
credits = byteArray[2] + (byteArray[3] * 256);
[_creditLabel setStringValue:[NSString stringWithFormat:@"%d",credits]];
[self updateStudentTable];
break;
case 0xF0:
[_nameLable setStringValue:@"Invalid Password"];
break;
case 0xF1:
[_pIndicate stopAnimation:self];
[_scanLabel setHidden:YES];
default:
break;
}
}
if (byteArray[0] == 0x81) {
name = &byteArray[1]; // points to name string
[_nameLable setStringValue:[NSString stringWithUTF8String:(char *)name]];
[_pIndicate stopAnimation:self];
[_scanLabel setHidden:YES];
}
}
static int SocketConnectedToHostNamed(const char *hostname) {
int sockfd = -1;
sa_family_t family[] = {AF_INET6, AF_INET };
int family_count = sizeof(family) / sizeof(*family);
for (int i=0; sockfd == -1 && i < family_count; i++) {
printf("Looking at %s family:\n", family[i] == AF_INET6 ? "AF_INET6" : "AF_INET");
// Get host address.
struct hostent *host = NULL;
host = gethostbyname2(hostname, family[i]);
if (host == NULL) {
herror("gethostbyname2");
continue;
}
// Try to connect with each address.
struct sockaddr_storage server_addr;
for (int addressIndex = 0; sockfd == -1; addressIndex++) {
// grab the next address. Bail our if we've run out.
if (!GetAddressAtIndex(host, addressIndex, &server_addr)) break;
char buffer[INET6_ADDRSTRLEN];
printf("Trying %s...\n",
inet_ntop(host->h_addrtype, host->h_addr_list[addressIndex],
buffer, sizeof(buffer)));
// Get a socket
sockfd = socket(server_addr.ss_family, SOCK_STREAM, 0);
if (sockfd == -1) {
perror(" socket");
continue;
}
// connect
int err = connect(sockfd, (struct sockaddr *)&server_addr, server_addr.ss_len);
if (err == -1) {
perror(" connect");
close(sockfd);
sockfd = -1;
}
//success
}
}
return sockfd;
}
static bool GetAddressAtIndex(struct hostent *host, int addressIndex,
struct sockaddr_storage *outServerAddress) {
if (outServerAddress == NULL || host == NULL) return false;
// Out of Addresses?
if (host->h_addr_list[addressIndex] == NULL) return false;
outServerAddress->ss_family = host->h_addrtype;
if (outServerAddress->ss_family == AF_INET6) {
struct sockaddr_in6 *addr = (struct sockaddr_in6 *)outServerAddress;
addr->sin6_len = sizeof(*addr);
addr->sin6_port = htons(kPortNumber);
addr->sin6_flowinfo = 0;
addr->sin6_addr = *(struct in6_addr *)host->h_addr_list[addressIndex];
addr->sin6_scope_id = 0;
} else {
struct sockaddr_in *addr = (struct sockaddr_in *)outServerAddress;
addr->sin_len = sizeof(*addr);
addr->sin_port = htons(kPortNumber);
addr->sin_addr = *(struct in_addr *)host->h_addr_list[addressIndex];
memset(&addr->sin_zero, 0, sizeof(addr->sin_zero));
}
return true;
}
static int WriteMessage (int fd, const void*buffer, size_t length) {
// Message never longer than 256 bytes!
// Write opcode then length
uint8_t bytesLeft = (uint8_t)length;
uint8_t header[1];
header[0] = bytesLeft;
ssize_t nwritten = write(fd, header, sizeof(header));
if (nwritten <=0) goto bailout;
while (bytesLeft > 0) {
NSLog(@"%d %s",bytesLeft,buffer);
nwritten = write(fd, buffer, bytesLeft);
bytesLeft -= nwritten;
buffer += nwritten; //advance pointer
}
bailout:
if (nwritten == -1) perror("write");
return (int)nwritten;
}
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment