From: <haw...@us...> - 2007-10-18 15:31:51
|
Revision: 191 http://pgsqlformac.svn.sourceforge.net/pgsqlformac/?rev=191&view=rev Author: hawkmoon Date: 2007-10-18 08:31:50 -0700 (Thu, 18 Oct 2007) Log Message: ----------- Initial Commit of the PGSQLKit Framework Added Paths: ----------- trunk/PGSQLKit/ trunk/PGSQLKit/English.lproj/ trunk/PGSQLKit/English.lproj/InfoPlist.strings trunk/PGSQLKit/English.lproj/PGSQL Login.nib/ trunk/PGSQLKit/English.lproj/PGSQL Login.nib/classes.nib trunk/PGSQLKit/English.lproj/PGSQL Login.nib/icon_query_tool.tiff trunk/PGSQLKit/English.lproj/PGSQL Login.nib/info.nib trunk/PGSQLKit/English.lproj/PGSQL Login.nib/keyedobjects.nib trunk/PGSQLKit/Info.plist trunk/PGSQLKit/PGSQLColumn.h trunk/PGSQLKit/PGSQLColumn.m trunk/PGSQLKit/PGSQLConnection.h trunk/PGSQLKit/PGSQLConnection.m trunk/PGSQLKit/PGSQLField.h trunk/PGSQLKit/PGSQLField.m trunk/PGSQLKit/PGSQLKit.h trunk/PGSQLKit/PGSQLKit.xcodeproj/ trunk/PGSQLKit/PGSQLKit.xcodeproj/project.pbxproj trunk/PGSQLKit/PGSQLKit_Prefix.pch trunk/PGSQLKit/PGSQLLogin.h trunk/PGSQLKit/PGSQLLogin.m trunk/PGSQLKit/PGSQLRecord.h trunk/PGSQLKit/PGSQLRecord.m trunk/PGSQLKit/PGSQLRecordset.h trunk/PGSQLKit/PGSQLRecordset.m Added: trunk/PGSQLKit/English.lproj/InfoPlist.strings =================================================================== (Binary files differ) Property changes on: trunk/PGSQLKit/English.lproj/InfoPlist.strings ___________________________________________________________________ Name: svn:mime-type + application/octet-stream Added: trunk/PGSQLKit/English.lproj/PGSQL Login.nib/classes.nib =================================================================== --- trunk/PGSQLKit/English.lproj/PGSQL Login.nib/classes.nib (rev 0) +++ trunk/PGSQLKit/English.lproj/PGSQL Login.nib/classes.nib 2007-10-18 15:31:50 UTC (rev 191) @@ -0,0 +1,25 @@ +{ + IBClasses = ( + {CLASS = FirstResponder; LANGUAGE = ObjC; SUPERCLASS = NSObject; }, + {CLASS = NSObject; LANGUAGE = ObjC; }, + { + ACTIONS = {onCancel = id; onConnectionDetails = id; onHelp = id; onLogin = id; }; + CLASS = PGSQLLogin; + LANGUAGE = ObjC; + OUTLETS = { + detailsView = NSView; + displayConnectionDetails = NSButton; + loginPanel = NSWindow; + loginPassword = NSSecureTextField; + loginUserName = NSTextField; + rememberConnection = NSButton; + savedConnections = NSComboBox; + serverDatabase = NSTextField; + serverName = NSTextField; + serverPort = NSTextField; + }; + SUPERCLASS = NSObject; + } + ); + IBVersion = 1; +} \ No newline at end of file Added: trunk/PGSQLKit/English.lproj/PGSQL Login.nib/icon_query_tool.tiff =================================================================== (Binary files differ) Property changes on: trunk/PGSQLKit/English.lproj/PGSQL Login.nib/icon_query_tool.tiff ___________________________________________________________________ Name: svn:mime-type + application/octet-stream Added: trunk/PGSQLKit/English.lproj/PGSQL Login.nib/info.nib =================================================================== --- trunk/PGSQLKit/English.lproj/PGSQL Login.nib/info.nib (rev 0) +++ trunk/PGSQLKit/English.lproj/PGSQL Login.nib/info.nib 2007-10-18 15:31:50 UTC (rev 191) @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>IBDocumentLocation</key> + <string>31 507 473 512 0 0 1680 1028 </string> + <key>IBFramework Version</key> + <string>446.1</string> + <key>IBOpenObjects</key> + <array> + <integer>5</integer> + </array> + <key>IBSystem Version</key> + <string>8R2218</string> +</dict> +</plist> Added: trunk/PGSQLKit/English.lproj/PGSQL Login.nib/keyedobjects.nib =================================================================== (Binary files differ) Property changes on: trunk/PGSQLKit/English.lproj/PGSQL Login.nib/keyedobjects.nib ___________________________________________________________________ Name: svn:mime-type + application/octet-stream Added: trunk/PGSQLKit/Info.plist =================================================================== --- trunk/PGSQLKit/Info.plist (rev 0) +++ trunk/PGSQLKit/Info.plist 2007-10-18 15:31:50 UTC (rev 191) @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>CFBundleDevelopmentRegion</key> + <string>English</string> + <key>CFBundleExecutable</key> + <string>${EXECUTABLE_NAME}</string> + <key>CFBundleIconFile</key> + <string></string> + <key>CFBundleIdentifier</key> + <string>com.druwaresoftwaredesigns.pgsqlkit</string> + <key>CFBundleInfoDictionaryVersion</key> + <string>6.0</string> + <key>CFBundleName</key> + <string>${PRODUCT_NAME}</string> + <key>CFBundlePackageType</key> + <string>FMWK</string> + <key>CFBundleSignature</key> + <string>????</string> + <key>CFBundleVersion</key> + <string>1.0</string> + <key>NSPrincipalClass</key> + <string></string> +</dict> +</plist> Added: trunk/PGSQLKit/PGSQLColumn.h =================================================================== --- trunk/PGSQLKit/PGSQLColumn.h (rev 0) +++ trunk/PGSQLKit/PGSQLColumn.h 2007-10-18 15:31:50 UTC (rev 191) @@ -0,0 +1,27 @@ +// +// PGSQLColumn.h +// PGSQLKit +// +// Created by Andy Satori on 6/7/07. +// Copyright 2007 Druware Software Designs. All rights reserved. +// + +#import <Cocoa/Cocoa.h> + +@interface PGSQLColumn : NSObject { + NSString *name; + int index; + int type; + int size; + int offset; +} + +-(id)initWithResult:(void *)result atIndex:(int)columnIndex; + +-(NSString *)name; +-(int)index; +-(int)type; +-(int)size; +-(int)offset; + +@end Added: trunk/PGSQLKit/PGSQLColumn.m =================================================================== --- trunk/PGSQLKit/PGSQLColumn.m (rev 0) +++ trunk/PGSQLKit/PGSQLColumn.m 2007-10-18 15:31:50 UTC (rev 191) @@ -0,0 +1,52 @@ +// +// PGSQLColumn.m +// PGSQLKit +// +// Created by Andy Satori on 6/7/07. +// Copyright 2007 Druware Software Designs. All rights reserved. +// + +#import "PGSQLColumn.h" +#include "libpq-fe.h" + +@implementation PGSQLColumn + +-(id)initWithResult:(void *)result atIndex:(int)columnIndex +{ + [super init]; + + index = columnIndex; + name = [[[[NSString alloc] initWithCString:PQfname(result, columnIndex)] retain] autorelease]; + type = PQftype(result, columnIndex); + size = PQfsize(result, columnIndex); + offset = PQfmod(result, columnIndex); + + return self; +} + +- (NSString *)name { + return [[name retain] autorelease]; +} + +-(int)index +{ + return index; +} + +-(int)type; +{ + return type; +} + +-(int)size +{ + return size; +} + +-(int)offset +{ + return offset; +} + +@end + Added: trunk/PGSQLKit/PGSQLConnection.h =================================================================== --- trunk/PGSQLKit/PGSQLConnection.h (rev 0) +++ trunk/PGSQLKit/PGSQLConnection.h 2007-10-18 15:31:50 UTC (rev 191) @@ -0,0 +1,81 @@ +// +// PGSQLConnection.h +// PGSQLKit +// +// Created by Andy Satori on 5/8/07. +// Copyright 2007 Druware Software Designs. All rights reserved. +// + +#import <Cocoa/Cocoa.h> +#import "PGSQLRecordset.h" + +@interface PGSQLConnection : NSObject { + BOOL isConnected; + + NSString *connectionString; + + NSString *errorDescription; + NSMutableString *sqlLog; + + /* platform specific definitions */ + + BOOL logInfo; + BOOL logSQL; + + void *pgconn; + + NSString *host; + NSString *port; + NSString *options; + NSString *tty; // ignored now + NSString *dbName; + NSString *userName; + NSString *password; + NSString *sslMode; // allow, prefer, require + NSString *service; // service name + NSString *krbsrvName; +} + ++(id)defaultConnection; + +-(BOOL)close; +-(BOOL)connect; +-(void)connectAsync; +-(BOOL)execCommand:(NSString *)sql; +-(void)execCommandAsync:(NSString *)sql; +-(PGSQLRecordset *)open:(NSString *)sql; +-(void)openAsync:(NSString *)sql; +-(NSMutableString *)makeConnectionString; + +-(BOOL)isConnected; + +-(NSString *)connectionString; +-(void)setConnectionString:(NSString *)value; + +-(NSString *)userName; +-(void)setUserName:(NSString *)value; + +-(NSString *)password; +-(void)setPassword:(NSString *)value; + +-(NSString *)server; +-(void)setServer:(NSString *)value; + +-(NSString *)port; +-(void)setPort:(NSString *)value; + +-(NSString *)databaseName; +-(void)setDatabaseName:(NSString *)value; + +-(NSString *)lastError; + +-(NSMutableString *)sqlLog; +-(void)appendSQLLog:(NSString *)value; + + +FOUNDATION_EXPORT NSString * const PGSQLConnectionDidCompleteNotification; +FOUNDATION_EXPORT NSString * const PGSQLCommandDidCompleteNotification; + +@end + +static PGSQLConnection *globalConnection; Added: trunk/PGSQLKit/PGSQLConnection.m =================================================================== --- trunk/PGSQLKit/PGSQLConnection.m (rev 0) +++ trunk/PGSQLKit/PGSQLConnection.m 2007-10-18 15:31:50 UTC (rev 191) @@ -0,0 +1,510 @@ +// +// PGSQLConnection.m +// PGSQLKit +// +// Created by Andy Satori on 5/8/07. +// Copyright 2007 Druware Software Designs. All rights reserved. +// + +#import "PGSQLConnection.h" +#include "libpq-fe.h" +#import <sys/time.h> +#import <Security/Security.h> +#import <CoreFoundation/CoreFoundation.h> +#import <stdlib.h> + +// When a pqlib notice is raised this function gets called +void +handle_pq_notice(void *arg, const char *message) +{ + PGSQLConnection *theConn = (PGSQLConnection *) arg; + //NSLog(@"%s", message); + [theConn appendSQLLog:[NSString stringWithFormat: @"%s\n", message]]; +} + +@implementation PGSQLConnection + +NSString *const PGSQLConnectionDidCompleteNotification = @"PGSQLConnectionDidCompleteNotification"; +NSString *const PGSQLCommandDidCompleteNotification = @"PGSQLCommandDidCompleteNotification"; + +#pragma mark Class Methods + ++(id)defaultConnection +{ + if (globalConnection == nil) + { + return nil; + } + + return globalConnection; +} + +#pragma mark Instance Methods + +-(id)init +{ + self = [super init]; + + if (self != nil) { + isConnected = NO; + errorDescription = nil; + sqlLog = [[NSMutableString alloc] init]; + + pgconn = nil; + host = [[NSString alloc] initWithString:@"localhost"]; + port = [[NSString alloc] initWithString:@"5432"]; + options = nil; + tty = nil; + dbName = [[NSString alloc] initWithString:@"template1"]; + userName = nil; + password = nil; + sslMode = nil; + service = nil; + krbsrvName = nil; + connectionString = nil; + + if (globalConnection == nil) + { + [self retain]; + globalConnection = self; + } + } + + return self; +} + +-(void)dealloc +{ + [self close]; + + [host release]; + [port release]; + [options release]; + [tty release]; + [dbName release]; + [userName release]; + [password release]; + [sslMode release]; + [service release]; + [krbsrvName release]; + [connectionString release]; + [errorDescription release]; + [sqlLog release]; + + [super dealloc]; +} + + +- (void)connectAsync +{ + // perform the connection on a thread + [NSThread detachNewThreadSelector:@selector(performConnectThread) toTarget:self withObject:nil]; +} + +- (void)performConnectThread +{ + // allocate the thread, begin the connection and send the notification when done. + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + NSMutableDictionary *info = [[NSMutableDictionary alloc] init]; + + if ([self connect]) + { + [info setValue:nil forKey:@"Error"]; + } else { + [info setValue:[self lastError] forKey:@"Error"]; + } + + [[NSNotificationCenter defaultCenter] postNotificationName:PGSQLConnectionDidCompleteNotification + object:nil + userInfo:info]; + [pool release]; +} + + +- (BOOL)connect { + + // replace with postgres connect code + [self close]; + + if (connectionString == nil) + { + connectionString = [self makeConnectionString]; + [connectionString retain]; + } + NSAssert( (connectionString != nil), @"Attempted to connect to PostgreSQL with empty connectionString."); + pgconn = (PGconn *)PQconnectdb([connectionString cString]); + if (PQoptions(pgconn)) + { + NSLog(@"Options: %s", PQoptions(pgconn)); + } + + if (PQstatus(pgconn) == CONNECTION_BAD) + { + NSLog(@"Connection to database '%@' failed.", dbName); + NSLog(@"\t%s", PQerrorMessage(pgconn)); + errorDescription = [NSString stringWithFormat:@"%s", PQerrorMessage(pgconn)]; + [self appendSQLLog:[NSMutableString stringWithFormat:@"Connection to database %@ Failed.\n", dbName]]; + [self appendSQLLog:[NSMutableString stringWithFormat:@"Connection string: %@\n\n", connectionString]]; + + PQfinish(pgconn); + pgconn = nil; + isConnected = NO; + return NO; + } + + // TODO if good connection should we remove password from memory + // or should it be encrypted? + + // TODO password should be asked for in dialog used and then erased? + + if (errorDescription) + { + [errorDescription release]; + errorDescription = nil; + } + // set up notification + PQsetNoticeProcessor(pgconn, handle_pq_notice, self); + + if (sqlLog != nil) { + [sqlLog release]; + } + sqlLog = [[NSMutableString alloc] init]; + [self appendSQLLog:[NSMutableString stringWithFormat:@"Connected to database %@ on %@.\n", dbName, [[NSCalendarDate calendarDate] description]]]; + isConnected = YES; + return YES; +} + +- (BOOL)close +{ + if (pgconn == nil) { return NO; } + if (isConnected == NO) { return NO; } + + [self appendSQLLog:[NSMutableString stringWithString:@"Disconnected from database.\n"]]; + PQfinish(pgconn); + pgconn = nil; + isConnected = NO; + return YES; +} + +- (void)execCommandAsync:(NSString *)sql +{ + // perform the connection on a thread + [NSThread detachNewThreadSelector:@selector(performExecCommand:) toTarget:self withObject:sql]; +} + +- (void)performExecCommand:(id)sqlCommand +{ + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + NSString *sql = (NSString *)sqlCommand; + + NSMutableDictionary *info = [[NSMutableDictionary alloc] init]; + + NSNumber *recordCount = [[NSNumber alloc] initWithInt:[self execCommand:sql]]; + [info setValue:recordCount forKey:@"RecordCount"]; + [info setValue:[self lastError] forKey:@"Error"]; + + [[NSNotificationCenter defaultCenter] postNotificationName:PGSQLCommandDidCompleteNotification + object:nil + userInfo:info]; + [pool release]; +} + +- (BOOL)execCommand:(NSString *)sql +{ + PGresult* res; + + if (pgconn == nil) + { + errorDescription = [NSString stringWithString:@"Object is not Connected."]; + return nil; + } + + res = PQexec(pgconn, [sql cString]); + if (PQresultStatus(res) != PGRES_COMMAND_OK) + { + errorDescription = [NSString stringWithString:@"Command failed."]; + PQclear(res); + return NO; + } + if (strlen(PQcmdStatus(res))) + { + [self appendSQLLog:[NSString stringWithFormat:@"%s\n", PQcmdStatus(res)]]; + } +// results = [[[NSString alloc] initWithCString:PQcmdTuples(res)] autorelease]; + + PQclear(res); + return YES; +} + +- (void)openAsync:(NSString *)sql +{ + // perform the connection on a thread + [NSThread detachNewThreadSelector:@selector(performOpen:) toTarget:self withObject:sql]; +} + +- (void)performOpen:(id)sqlCommand +{ + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + NSString *sql = (NSString *)sqlCommand; + + NSMutableDictionary *info = [[NSMutableDictionary alloc] init]; + + PGSQLRecordset *rs = [self open:sql]; + [info setValue:rs forKey:@"Recordset"]; + [info setValue:[self lastError] forKey:@"Error"]; + + [[NSNotificationCenter defaultCenter] postNotificationName:PGSQLCommandDidCompleteNotification + object:nil + userInfo:info]; + [pool release]; +} + +- (PGSQLRecordset *)open:(NSString *)sql +{ + struct timeval start, finished; + double elapsed_time; + long seconds, usecs; + PGresult* res; + + [errorDescription release]; + errorDescription = nil; + + if (pgconn == nil) + { + errorDescription = @"Object is not Connected."; + [self appendSQLLog:@"Object is not Connected.\n"]; + return nil; + } + + if (logSQL) + { + [self appendSQLLog: [NSString stringWithFormat:@"%@\n", [sql stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]]]; + } + + + + + gettimeofday(&start, 0); + res = PQexec(pgconn, [sql cString]); + if (logInfo) + { + gettimeofday(&finished, 0); + seconds = finished.tv_sec - start.tv_sec; + usecs = finished.tv_usec - start.tv_usec; + if (usecs < 0) + { + seconds--; + usecs = usecs + 1000000; + } + elapsed_time = (double) seconds *1000.0 + (double) usecs *0.001; + [self appendSQLLog: [NSString stringWithFormat: @"Completed in %d milliseconds.\n", (long) elapsed_time]]; + } + switch (PQresultStatus(res)) + { + case PGRES_TUPLES_OK: + { + // build the recordset + PGSQLRecordset *rs = [[[PGSQLRecordset alloc] initWithResult:res] autorelease]; + + if (logInfo) + { + long nRecords = PQntuples(res); + [self appendSQLLog:[NSString stringWithFormat: @"%d rows affected.\n\n", nRecords]]; + } + + return rs; + break; + } + + case PGRES_COMMAND_OK: + { + if (logInfo) + { + [self appendSQLLog:@"Query ran successfully.\n"]; + } + PQclear(res); + return nil; + break; + } + + case PGRES_EMPTY_QUERY: + { + [self appendSQLLog:@"Postgres reported Empty Query\n"]; + PQclear(res); + return nil; + break; + } + + case PGRES_COPY_OUT: + case PGRES_COPY_IN: + case PGRES_BAD_RESPONSE: + case PGRES_NONFATAL_ERROR: + case PGRES_FATAL_ERROR: + default: + { + errorDescription = [NSString stringWithFormat:@"PostgreSQL Error: %s", PQresultErrorMessage(res)]; + [self appendSQLLog:[NSString stringWithFormat:@"PostgreSQL Error: %s\n", PQresultErrorMessage(res)]]; + PQclear(res); + return nil; + } + } +} + +-(NSMutableString *)makeConnectionString +{ + NSMutableString *connStr = [[[NSMutableString alloc] init] autorelease]; + + if (connectionString) + { + [connStr appendString:connectionString]; + return connStr; + } + if (host) + { + [connStr appendFormat:@" host='%@' ", host]; + } + if (port) + { + [connStr appendFormat:@" port='%@' ", port]; + } + if (options) + { + [connStr appendFormat:@" options='%@' ", options]; + } + if (dbName) + { + [connStr appendFormat:@" dbname='%@' ", dbName]; + } + if (userName) + { + [connStr appendFormat:@" user='%@' ", userName]; + } + if (password) + { + [connStr appendFormat:@" password='%@' ", password]; + } + if (sslMode) + { + [connStr appendFormat:@" sslmode='%@' ", sslMode]; + } + if (service) + { + [connStr appendFormat:@" service='%@' ", service]; + } + if (krbsrvName) + { + [connStr appendFormat:@" krbsrvname='%@' ", krbsrvName]; + } + return connStr; +} + + + +#pragma mark Dictionary Tools + +- (BOOL)insertIntoTable:(NSString *)table fromDictionary:(NSDictionary *)dict +{ + return NO; +} + +- (BOOL)updateTable:(NSString *)table fromDictionary:(NSDictionary *)dict +{ + return NO; +} + +#pragma mark Property Accessors + +- (BOOL)isConnected { + return isConnected; +} + +- (NSString *)connectionString { + return [[connectionString retain] autorelease]; +} + +- (void)setConnectionString:(NSString *)value { + if (connectionString != value) { + [connectionString release]; + connectionString = [value copy]; + } +} + +- (NSString *)userName { + return [[userName retain] autorelease]; +} + +- (void)setUserName:(NSString *)value { + if (userName != value) { + [userName release]; + userName = [value copy]; + } +} + +- (NSString *)password { + return [[password retain] autorelease]; +} + +- (void)setPassword:(NSString *)value { + if (password != value) { + [password release]; + password = [value copy]; + } +} + +- (NSString *)server { + return [[host retain] autorelease]; +} + +- (void)setServer:(NSString *)value { + if (host != value) { + [host release]; + host = [value copy]; + } +} + +-(NSString *)port { + return [[port retain] autorelease]; +} + +-(void)setPort:(NSString *)value { + if (port != value) { + [port release]; + port = [value copy]; + } +} + +-(NSString *)databaseName { + return [[dbName retain] autorelease]; +} + +-(void)setDatabaseName:(NSString *)value { + if (dbName != value) { + [dbName release]; + dbName = [value copy]; + } +} + + +- (NSString *)lastError { + return errorDescription; +} + +- (NSMutableString *)sqlLog { + return sqlLog; +} + +- (void)appendSQLLog:(NSString *)value { + if (sqlLog == nil) + { + sqlLog = [[NSMutableString alloc] initWithString:value]; + } + else + { + [sqlLog appendString:value]; + } +} + +@end Added: trunk/PGSQLKit/PGSQLField.h =================================================================== --- trunk/PGSQLKit/PGSQLField.h (rev 0) +++ trunk/PGSQLKit/PGSQLField.h 2007-10-18 15:31:50 UTC (rev 191) @@ -0,0 +1,30 @@ +// +// PGSQLField.h +// PGSQLKit +// +// Created by Andy Satori on 6/7/07. +// Copyright 2007 Druware Software Designs. All rights reserved. +// + +#import <Cocoa/Cocoa.h> +#import "PGSQLColumn.h" + + +@interface PGSQLField : NSObject { + NSData *data; + + PGSQLColumn *column; +} + +-(id)initWithResult:(void *)result forColumn:(PGSQLColumn *)forColumn + atRow:(int)atRow; +-(NSString *)asString; +-(NSNumber *)asNumber; +-(long)asLong; +-(NSDate *)asDate; +-(NSData *)asData; +-(BOOL)asBoolean; + +-(BOOL)isNull; + +@end Added: trunk/PGSQLKit/PGSQLField.m =================================================================== --- trunk/PGSQLKit/PGSQLField.m (rev 0) +++ trunk/PGSQLKit/PGSQLField.m 2007-10-18 15:31:50 UTC (rev 191) @@ -0,0 +1,134 @@ +// +// PGSQLField.m +// PGSQLKit +// +// Created by Andy Satori on 6/7/07. +// Copyright 2007 Druware Software Designs. All rights reserved. +// + +#import "PGSQLField.h" +#include "libpq-fe.h" + +@implementation PGSQLField + +-(id)initWithResult:(void *)result forColumn:(PGSQLColumn *)forColumn + atRow:(int)atRow +{ + [super init]; + + data = nil; + + if (PQgetisnull(result, atRow, [column index]) == 1) { + return self; + } + + int iLen; + char *szBuf; + + column = forColumn; + iLen = PQgetlength(result, atRow, [column index]) + 1; + + // this may have to be adjust if the column type is not 0 (eg, it's binary) + szBuf = PQgetvalue(result, atRow, [column index]); + data = nil; + if (iLen > 0) { + data = [[NSData alloc] initWithBytes:szBuf length:iLen]; + } + + return self; +} + +-(NSString *)asString +{ + if (data != nil) { + if ([data length] <= 0) + { + return nil; + } + return [[NSString alloc] initWithBytes:[data bytes] length:[data length] encoding:NSMacOSRomanStringEncoding]; + } + return @""; +} + + +-(NSNumber *)asNumber +{ + if (data != nil) { + if ([data length] <= 0) + { + return nil; + } + + NSNumber *value = [[NSNumber alloc] initWithFloat: + [[[NSString alloc] initWithBytes:[data bytes] length:[data length] encoding:NSMacOSRomanStringEncoding] floatValue]]; + return value; + } + return nil; +} + +-(long)asLong +{ + if (data != nil) { + if ([data length] <= 0) + { + return nil; + } + + NSString *value = [[NSString alloc] initWithBytes:[data bytes] length:[data length] encoding:NSMacOSRomanStringEncoding]; + + return (long)[[NSNumber numberWithFloat:[value floatValue]] longValue]; + } + return 0; +} + +-(NSDate *)asDate +{ + if (data != nil) { + if ([data length] <= 0) + { + return nil; + } + + NSString *value = [NSString stringWithCString:(char *)[data bytes] + encoding:NSMacOSRomanStringEncoding]; + if ([value rangeOfString:@"."].location != NSNotFound) + { + value = [NSString stringWithFormat:@"%@ +0000", [value substringToIndex:[value rangeOfString:@"."].location]]; + } else { + + value = [NSString stringWithFormat:@"%@ +0000", value]; + } + NSDate *newDate = [[NSDate alloc] initWithString:value]; + + return newDate; + } + return nil; +} + +-(NSData *)asData +{ + if (data != nil) { + if ([data length] <= 0) + { + return nil; + } + + return [[[[NSData alloc] initWithData:data] autorelease] retain]; + } + return nil; +} + +-(BOOL)asBoolean +{ + if (data != nil) { + return ([data bytes] == 't'); + } +} + + +-(BOOL)isNull +{ + return (data == nil); +} + +@end; Added: trunk/PGSQLKit/PGSQLKit.h =================================================================== --- trunk/PGSQLKit/PGSQLKit.h (rev 0) +++ trunk/PGSQLKit/PGSQLKit.h 2007-10-18 15:31:50 UTC (rev 191) @@ -0,0 +1,15 @@ +/* + * PGSQLKit.h + * PGSQLKit + * + * Created by Andy Satori on 5/2/07. + * Copyright 2007 Druware Software Designs. All rights reserved. + * + */ + +#import "PGSQLLogin.h" +#import "PGSQLConnection.h" + + + + Added: trunk/PGSQLKit/PGSQLKit.xcodeproj/project.pbxproj =================================================================== --- trunk/PGSQLKit/PGSQLKit.xcodeproj/project.pbxproj (rev 0) +++ trunk/PGSQLKit/PGSQLKit.xcodeproj/project.pbxproj 2007-10-18 15:31:50 UTC (rev 191) @@ -0,0 +1,397 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 42; + objects = { + +/* Begin PBXBuildFile section */ + 4313978C0C31BAF600E596BA /* PGSQLColumn.h in Headers */ = {isa = PBXBuildFile; fileRef = 439F7B670C1894AA001CA5A4 /* PGSQLColumn.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 4313978D0C31BAF600E596BA /* PGSQLColumn.m in Sources */ = {isa = PBXBuildFile; fileRef = 439F7B680C1894AA001CA5A4 /* PGSQLColumn.m */; }; + 431397B60C31BC1000E596BA /* PGSQLField.m in Sources */ = {isa = PBXBuildFile; fileRef = 439F7B600C18947C001CA5A4 /* PGSQLField.m */; }; + 431397B70C31BC1000E596BA /* PGSQLField.h in Headers */ = {isa = PBXBuildFile; fileRef = 439F7B5F0C18947C001CA5A4 /* PGSQLField.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 4324B38A0BF36227001F6E5E /* PGSQLKit.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 8DC2EF5B0486A6940098B216 /* PGSQLKit.framework */; }; + 432A8C110C47074F00CA8F33 /* PGSQLKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 439656720BE8C779003EDC3D /* PGSQLKit.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 43318B370BF1227A008C2560 /* PGSQL Login.nib in Resources */ = {isa = PBXBuildFile; fileRef = 43318B350BF1227A008C2560 /* PGSQL Login.nib */; }; + 4349A2B20C31D47100DACE65 /* PGSQLRecord.h in Headers */ = {isa = PBXBuildFile; fileRef = 439F7B630C189495001CA5A4 /* PGSQLRecord.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 4349A2B30C31D47100DACE65 /* PGSQLRecord.m in Sources */ = {isa = PBXBuildFile; fileRef = 439F7B640C189495001CA5A4 /* PGSQLRecord.m */; }; + 4349A2BB0C31D57700DACE65 /* PGSQLRecordset.h in Headers */ = {isa = PBXBuildFile; fileRef = 4325688D0C0D122F007E8DE8 /* PGSQLRecordset.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 4349A2BC0C31D57800DACE65 /* PGSQLRecordset.m in Sources */ = {isa = PBXBuildFile; fileRef = 4325688E0C0D122F007E8DE8 /* PGSQLRecordset.m */; }; + 4349A2C90C31DD5700DACE65 /* PGSQLConnection.h in Headers */ = {isa = PBXBuildFile; fileRef = 43318B520BF16497008C2560 /* PGSQLConnection.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 4349A2CA0C31DD5800DACE65 /* PGSQLConnection.m in Sources */ = {isa = PBXBuildFile; fileRef = 43318B530BF16497008C2560 /* PGSQLConnection.m */; }; + 4349A2E60C31EAA700DACE65 /* PGSQLLogin.h in Headers */ = {isa = PBXBuildFile; fileRef = 43CBC57C0BF11AC300A5E5AF /* PGSQLLogin.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 4349A2E70C31EAA800DACE65 /* PGSQLLogin.m in Sources */ = {isa = PBXBuildFile; fileRef = 43CBC57D0BF11AC300A5E5AF /* PGSQLLogin.m */; }; + 439774040C7D29140009F0B4 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 439774030C7D29140009F0B4 /* Security.framework */; }; + 8DC2EF530486A6940098B216 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 089C1666FE841158C02AAC07 /* InfoPlist.strings */; }; + 8DC2EF570486A6940098B216 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7B1FEA5585E11CA2CBB /* Cocoa.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 4324B36A0BF36104001F6E5E /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /Library/Frameworks; + dstSubfolderSpec = 0; + files = ( + 4324B38A0BF36227001F6E5E /* PGSQLKit.framework in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 0867D69BFE84028FC02AAC07 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = "<absolute>"; }; + 0867D6A5FE840307C02AAC07 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = "<absolute>"; }; + 089C1667FE841158C02AAC07 /* English */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = English; path = English.lproj/InfoPlist.strings; sourceTree = "<group>"; }; + 1058C7B1FEA5585E11CA2CBB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = "<absolute>"; }; + 32DBCF5E0370ADEE00C91783 /* PGSQLKit_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PGSQLKit_Prefix.pch; sourceTree = "<group>"; }; + 4325688D0C0D122F007E8DE8 /* PGSQLRecordset.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PGSQLRecordset.h; sourceTree = "<group>"; }; + 4325688E0C0D122F007E8DE8 /* PGSQLRecordset.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PGSQLRecordset.m; sourceTree = "<group>"; }; + 43318B360BF1227A008C2560 /* English */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = English; path = "English.lproj/PGSQL Login.nib"; sourceTree = "<group>"; }; + 43318B520BF16497008C2560 /* PGSQLConnection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PGSQLConnection.h; sourceTree = "<group>"; }; + 43318B530BF16497008C2560 /* PGSQLConnection.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PGSQLConnection.m; sourceTree = "<group>"; }; + 439656720BE8C779003EDC3D /* PGSQLKit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PGSQLKit.h; sourceTree = "<group>"; }; + 439774030C7D29140009F0B4 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = /Developer/SDKs/MacOSX10.4u.sdk/System/Library/Frameworks/Security.framework; sourceTree = "<absolute>"; }; + 439F7B5F0C18947C001CA5A4 /* PGSQLField.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PGSQLField.h; sourceTree = "<group>"; }; + 439F7B600C18947C001CA5A4 /* PGSQLField.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PGSQLField.m; sourceTree = "<group>"; }; + 439F7B630C189495001CA5A4 /* PGSQLRecord.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PGSQLRecord.h; sourceTree = "<group>"; }; + 439F7B640C189495001CA5A4 /* PGSQLRecord.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PGSQLRecord.m; sourceTree = "<group>"; }; + 439F7B670C1894AA001CA5A4 /* PGSQLColumn.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PGSQLColumn.h; sourceTree = "<group>"; }; + 439F7B680C1894AA001CA5A4 /* PGSQLColumn.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PGSQLColumn.m; sourceTree = "<group>"; }; + 43CBC57C0BF11AC300A5E5AF /* PGSQLLogin.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PGSQLLogin.h; sourceTree = "<group>"; }; + 43CBC57D0BF11AC300A5E5AF /* PGSQLLogin.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PGSQLLogin.m; sourceTree = "<group>"; }; + 8DC2EF5A0486A6940098B216 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; }; + 8DC2EF5B0486A6940098B216 /* PGSQLKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = PGSQLKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + D2F7E79907B2D74100F64583 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = /System/Library/Frameworks/CoreData.framework; sourceTree = "<absolute>"; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 8DC2EF560486A6940098B216 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 8DC2EF570486A6940098B216 /* Cocoa.framework in Frameworks */, + 439774040C7D29140009F0B4 /* Security.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 034768DFFF38A50411DB9C8B /* Products */ = { + isa = PBXGroup; + children = ( + 8DC2EF5B0486A6940098B216 /* PGSQLKit.framework */, + ); + name = Products; + sourceTree = "<group>"; + }; + 0867D691FE84028FC02AAC07 /* PGSQLKit */ = { + isa = PBXGroup; + children = ( + 08FB77AEFE84172EC02AAC07 /* Classes */, + 32C88DFF0371C24200C91783 /* Other Sources */, + 089C1665FE841158C02AAC07 /* Resources */, + 0867D69AFE84028FC02AAC07 /* External Frameworks and Libraries */, + 034768DFFF38A50411DB9C8B /* Products */, + ); + name = PGSQLKit; + sourceTree = "<group>"; + }; + 0867D69AFE84028FC02AAC07 /* External Frameworks and Libraries */ = { + isa = PBXGroup; + children = ( + 1058C7B0FEA5585E11CA2CBB /* Linked Frameworks */, + 1058C7B2FEA5585E11CA2CBB /* Other Frameworks */, + ); + name = "External Frameworks and Libraries"; + sourceTree = "<group>"; + }; + 089C1665FE841158C02AAC07 /* Resources */ = { + isa = PBXGroup; + children = ( + 43318B350BF1227A008C2560 /* PGSQL Login.nib */, + 8DC2EF5A0486A6940098B216 /* Info.plist */, + 089C1666FE841158C02AAC07 /* InfoPlist.strings */, + ); + name = Resources; + sourceTree = "<group>"; + }; + 08FB77AEFE84172EC02AAC07 /* Classes */ = { + isa = PBXGroup; + children = ( + 439F7B670C1894AA001CA5A4 /* PGSQLColumn.h */, + 439F7B680C1894AA001CA5A4 /* PGSQLColumn.m */, + 439F7B5F0C18947C001CA5A4 /* PGSQLField.h */, + 439F7B600C18947C001CA5A4 /* PGSQLField.m */, + 439F7B630C189495001CA5A4 /* PGSQLRecord.h */, + 439F7B640C189495001CA5A4 /* PGSQLRecord.m */, + 4325688D0C0D122F007E8DE8 /* PGSQLRecordset.h */, + 4325688E0C0D122F007E8DE8 /* PGSQLRecordset.m */, + 43318B520BF16497008C2560 /* PGSQLConnection.h */, + 43318B530BF16497008C2560 /* PGSQLConnection.m */, + 43CBC57C0BF11AC300A5E5AF /* PGSQLLogin.h */, + 43CBC57D0BF11AC300A5E5AF /* PGSQLLogin.m */, + 43AEC8BD0C306B38009B1479 /* Incomplete */, + ); + name = Classes; + sourceTree = "<group>"; + }; + 1058C7B0FEA5585E11CA2CBB /* Linked Frameworks */ = { + isa = PBXGroup; + children = ( + 439774030C7D29140009F0B4 /* Security.framework */, + 1058C7B1FEA5585E11CA2CBB /* Cocoa.framework */, + ); + name = "Linked Frameworks"; + sourceTree = "<group>"; + }; + 1058C7B2FEA5585E11CA2CBB /* Other Frameworks */ = { + isa = PBXGroup; + children = ( + 0867D6A5FE840307C02AAC07 /* AppKit.framework */, + D2F7E79907B2D74100F64583 /* CoreData.framework */, + 0867D69BFE84028FC02AAC07 /* Foundation.framework */, + ); + name = "Other Frameworks"; + sourceTree = "<group>"; + }; + 32C88DFF0371C24200C91783 /* Other Sources */ = { + isa = PBXGroup; + children = ( + 32DBCF5E0370ADEE00C91783 /* PGSQLKit_Prefix.pch */, + ); + name = "Other Sources"; + sourceTree = "<group>"; + }; + 43AEC8BD0C306B38009B1479 /* Incomplete */ = { + isa = PBXGroup; + children = ( + 439656720BE8C779003EDC3D /* PGSQLKit.h */, + ); + name = Incomplete; + sourceTree = "<group>"; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 8DC2EF500486A6940098B216 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 4313978C0C31BAF600E596BA /* PGSQLColumn.h in Headers */, + 431397B70C31BC1000E596BA /* PGSQLField.h in Headers */, + 4349A2B20C31D47100DACE65 /* PGSQLRecord.h in Headers */, + 4349A2BB0C31D57700DACE65 /* PGSQLRecordset.h in Headers */, + 4349A2C90C31DD5700DACE65 /* PGSQLConnection.h in Headers */, + 4349A2E60C31EAA700DACE65 /* PGSQLLogin.h in Headers */, + 432A8C110C47074F00CA8F33 /* PGSQLKit.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 8DC2EF4F0486A6940098B216 /* PGSQLKit */ = { + isa = PBXNativeTarget; + buildConfigurationList = 1DEB91AD08733DA50010E9CD /* Build configuration list for PBXNativeTarget "PGSQLKit" */; + buildPhases = ( + 8DC2EF500486A6940098B216 /* Headers */, + 8DC2EF520486A6940098B216 /* Resources */, + 8DC2EF540486A6940098B216 /* Sources */, + 8DC2EF560486A6940098B216 /* Frameworks */, + 4324B36A0BF36104001F6E5E /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = PGSQLKit; + productInstallPath = "$(HOME)/Library/Frameworks"; + productName = PGSQLKit; + productReference = 8DC2EF5B0486A6940098B216 /* PGSQLKit.framework */; + productType = "com.apple.product-type.framework"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 0867D690FE84028FC02AAC07 /* Project object */ = { + isa = PBXProject; + buildConfigurationList = 1DEB91B108733DA50010E9CD /* Build configuration list for PBXProject "PGSQLKit" */; + hasScannedForEncodings = 1; + mainGroup = 0867D691FE84028FC02AAC07 /* PGSQLKit */; + productRefGroup = 034768DFFF38A50411DB9C8B /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 8DC2EF4F0486A6940098B216 /* PGSQLKit */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 8DC2EF520486A6940098B216 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 8DC2EF530486A6940098B216 /* InfoPlist.strings in Resources */, + 43318B370BF1227A008C2560 /* PGSQL Login.nib in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 8DC2EF540486A6940098B216 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 4313978D0C31BAF600E596BA /* PGSQLColumn.m in Sources */, + 431397B60C31BC1000E596BA /* PGSQLField.m in Sources */, + 4349A2B30C31D47100DACE65 /* PGSQLRecord.m in Sources */, + 4349A2BC0C31D57800DACE65 /* PGSQLRecordset.m in Sources */, + 4349A2CA0C31DD5800DACE65 /* PGSQLConnection.m in Sources */, + 4349A2E70C31EAA800DACE65 /* PGSQLLogin.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 089C1666FE841158C02AAC07 /* InfoPlist.strings */ = { + isa = PBXVariantGroup; + children = ( + 089C1667FE841158C02AAC07 /* English */, + ); + name = InfoPlist.strings; + sourceTree = "<group>"; + }; + 43318B350BF1227A008C2560 /* PGSQL Login.nib */ = { + isa = PBXVariantGroup; + children = ( + 43318B360BF1227A008C2560 /* English */, + ); + name = "PGSQL Login.nib"; + sourceTree = "<group>"; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 1DEB91AE08733DA50010E9CD /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = ( + ppc, + i386, + ); + COPY_PHASE_STRIP = NO; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(FRAMEWORK_SEARCH_PATHS_QUOTED_1)", + ); + FRAMEWORK_SEARCH_PATHS_QUOTED_1 = "\"$(SYSTEM_DEVELOPER_DIR)/SDKs/MacOSX10.4u.sdk/System/Library/Frameworks\""; + FRAMEWORK_VERSION = A; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = PGSQLKit_Prefix.pch; + HEADER_SEARCH_PATHS = /opt/local/include; + INFOPLIST_FILE = Info.plist; + INSTALL_PATH = "$(HOME)/Library/Frameworks"; + LIBRARY_SEARCH_PATHS = /opt/local/lib; + OTHER_LDFLAGS = ( + "-lssl", + "-lkrb5", + "-lcrypto", + "-lpq", + ); + PRODUCT_NAME = PGSQLKit; + WRAPPER_EXTENSION = framework; + ZERO_LINK = YES; + }; + name = Debug; + }; + 1DEB91AF08733DA50010E9CD /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = ( + ppc, + i386, + ); + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(FRAMEWORK_SEARCH_PATHS_QUOTED_1)", + ); + FRAMEWORK_SEARCH_PATHS_QUOTED_1 = "\"$(SYSTEM_DEVELOPER_DIR)/SDKs/MacOSX10.4u.sdk/System/Library/Frameworks\""; + FRAMEWORK_VERSION = A; + GCC_GENERATE_DEBUGGING_SYMBOLS = NO; + GCC_MODEL_TUNING = G5; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = PGSQLKit_Prefix.pch; + HEADER_SEARCH_PATHS = /opt/local/include; + INFOPLIST_FILE = Info.plist; + INSTALL_PATH = "@loader_path/../Frameworks"; + LIBRARY_SEARCH_PATHS = /opt/local/lib; + OTHER_LDFLAGS = ( + "-lssl", + "-lkrb5", + "-lcrypto", + "-lpq", + ); + PRODUCT_NAME = PGSQLKit; + WRAPPER_EXTENSION = framework; + }; + name = Release; + }; + 1DEB91B208733DA50010E9CD /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + PREBINDING = NO; + SDKROOT = /Developer/SDKs/MacOSX10.4u.sdk; + }; + name = Debug; + }; + 1DEB91B308733DA50010E9CD /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + PREBINDING = NO; + SDKROOT = /Developer/SDKs/MacOSX10.4u.sdk; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 1DEB91AD08733DA50010E9CD /* Build configuration list for PBXNativeTarget "PGSQLKit" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1DEB91AE08733DA50010E9CD /* Debug */, + 1DEB91AF08733DA50010E9CD /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 1DEB91B108733DA50010E9CD /* Build configuration list for PBXProject "PGSQLKit" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1DEB91B208733DA50010E9CD /* Debug */, + 1DEB91B308733DA50010E9CD /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 0867D690FE84028FC02AAC07 /* Project object */; +} Added: trunk/PGSQLKit/PGSQLKit_Prefix.pch =================================================================== --- trunk/PGSQLKit/PGSQLKit_Prefix.pch (rev 0) +++ trunk/PGSQLKit/PGSQLKit_Prefix.pch 2007-10-18 15:31:50 UTC (rev 191) @@ -0,0 +1,7 @@ +// +// Prefix header for all source files of the 'PGSQLKit' target in the 'PGSQLKit' project. +// + +#ifdef __OBJC__ + #import <Cocoa/Cocoa.h> +#endif Added: trunk/PGSQLKit/PGSQLLogin.h =================================================================== --- trunk/PGSQLKit/PGSQLLogin.h (rev 0) +++ trunk/PGSQLKit/PGSQLLogin.h 2007-10-18 15:31:50 UTC (rev 191) @@ -0,0 +1,64 @@ +// +// PGSQLLogin.h +// PGSQLKit +// +// Created by Andy Satori on 5/8/07. +// Copyright 2007 Druware Software Designs. All rights reserved. +// + +#import <Cocoa/Cocoa.h> +#import "PGSQLConnection.h" + +@interface PGSQLLogin : NSObject { + IBOutlet NSWindow *loginPanel; + IBOutlet NSComboBox *savedConnections; + IBOutlet NSTextField *loginUserName; + IBOutlet NSSecureTextField *loginPassword; + IBOutlet NSTextField *serverName; + IBOutlet NSTextField *serverPort; + IBOutlet NSTextField *serverDatabase; + IBOutlet NSButton *rememberConnection; + IBOutlet NSButton *displayConnectionDetails; + IBOutlet NSView *detailsView; + + NSWindow *parentWindow; + + PGSQLConnection *resultConnection; + + NSMutableArray *savedConnectionList; + + NSString *defaultDSN; + NSString *defaultUser; + NSString *defaultPassword; + + BOOL isShowingDetails; +} + +- (void)beginModalLoginForWindow:(NSWindow *)parent; + +- (IBAction)onCancel:(id)sender; +- (IBAction)onLogin:(id)sender; +- (IBAction)onHelp:(id)sender; +- (IBAction)onConnectionDetails:(id)sender; + +- (BOOL)isConnected; + +- (PGSQLConnection *)connection; + +- (NSString *)defaultConnectionString; +- (void)setDefaultConnectionString:(NSString *)value; + +- (void)setDefaultUser:(NSString *)value; +- (NSString *)defaultUser; + +- (void)setDefaultPassword:(NSString *)value; + +- (void)onConnectionCompleted:(NSNotification *)aNotification; + +@end + +@interface NSObject (PGSQLKitDelegateMethods) + +- (id)loginCompleted:(PGSQLConnection *)connection; + +@end Added: trunk/PGSQLKit/PGSQLLogin.m =================================================================== --- trunk/PGSQLKit/PGSQLLogin.m (rev 0) +++ trunk/PGSQLKit/PGSQLLogin.m 2007-10-18 15:31:50 UTC (rev 191) @@ -0,0 +1,641 @@ +// +// PGSQLLogin.m +// PGSQLKit +// +// Created by Andy Satori on 5/8/07. +// Copyright 2007 Druware Software Designs. All rights reserved. +// + +#import "PGSQLLogin.h" +#import <Security/Security.h> +#import <CoreFoundation/CoreFoundation.h> +#import <stdlib.h> + + +@implementation PGSQLLogin + +#pragma mark Keychain Helper Functions + +- (NSString *)connectionNameForKeychainItem:(SecKeychainItemRef)item +{ + OSStatus status; + SecKeychainAttribute attributes[2]; + SecKeychainAttributeList list; + NSString *result = nil; + + attributes[0].tag = kSecLabelItemAttr; + + list.count = 1; + list.attr = attributes; + + status = SecKeychainItemCopyContent(item, NULL, &list, NULL, NULL); + + if (status == noErr) + { + char buffer[1024]; + SecKeychainAttribute attr; + int i; + + for (i = 0; i < list.count; i++) + { + attr = list.attr[i]; + if (attr.length < 1024) + { + strncpy(buffer, attr.data, attr.length); + buffer[attr.length] = '\0'; + result = [[NSString alloc] initWithFormat:@"%s", buffer]; + } + } + } + + SecKeychainItemFreeContent(&list, NULL); + return result; +} + +- (NSString *)setConnectionDetailsForKeychainItem:(SecKeychainItemRef)item +{ + OSStatus status; + SecKeychainAttribute attributes[4]; + SecKeychainAttributeList list; + NSString *where = nil; + char *password; + UInt32 passwordLen; + passwordLen = 1024; + password = malloc(passwordLen); + + attributes[0].tag = kSecServiceItemAttr; + attributes[1].tag = kSecAccountItemAttr; + + list.count = 2; + list.attr = attributes; + + + // alter this to read the password (last two nulls) + status = SecKeychainItemCopyContent(item, NULL, &list, &passwordLen, + (void *)&password); + + if (status == noErr) + { + char buffer[1024]; + SecKeychainAttribute attr; + int i; + + for (i = 0; i < list.count; i++) + { + attr = list.attr[i]; + switch (attr.tag) + { + case kSecServiceItemAttr: + if (attr.length < 1024) + { + strncpy(buffer, attr.data, attr.length); + buffer[attr.length] = '\0'; + where = [[NSString alloc] initWithFormat:@"%s", buffer]; + + // split the where into the location elements + [serverName setStringValue:where]; + + NSRange range1 = [where rangeOfString:@"@"]; + NSRange range2 = [where rangeOfString:@":"]; + NSRange range3 = NSMakeRange( + (range1.location + range1.length), + (range2.location - (range1.location + range1.length))); + + [serverName setStringValue: + [where substringWithRange:range3]]; + [serverPort setStringValue: + [where substringFromIndex:range2.location + range2.length]]; + [serverDatabase setStringValue: + [where substringToIndex:range1.location]]; + } + break; + case kSecAccountItemAttr: + if (attr.length < 1024) + { + strncpy(buffer, attr.data, attr.length); + buffer[attr.length] = '\0'; + NSString *who = [[NSString alloc] initWithFormat:@"%s", buffer]; + [loginUserName setStringValue:who]; + } + break; + default: + break; + } + } + + strncpy(buffer, password, passwordLen); + buffer[passwordLen] = '\0'; + // set the password + [loginPassword setStringValue:[NSString stringWithFormat:@"%s", buffer]]; + } + + free(password); + + + return where; +} + +- (BOOL)editExistingKeychainItem +{ + OSStatus status; + SecKeychainSearchRef search; + SecKeychainAttribute searchAttributes[2]; + SecKeychainAttributeList searchList; + SecKeychainItemRef item; + + NSString *selectedValue = [savedConnections stringValue]; + + searchAttributes[0].tag = kSecCreatorItemAttr; + searchAttributes[0].data = "pgds"; + searchAttributes[0].length = 4; + + searchAttributes[1].tag = kSecLabelItemAttr; + searchAttributes[1].data = (char *)[selectedValue cString]; + searchAttributes[1].length = [selectedValue length]; + + searchList.count = 2; + searchList.attr = searchAttributes; + + status = SecKeychainSearchCreateFromAttributes(NULL, + kSecGenericPasswordItemClass, + &searchList, &search); + if (status != noErr) + { + NSLog(@"Error reading the keychain: %d", status); + } + + + int i = 0; + while (SecKeychainSearchCopyNext(search, &item) == noErr) + { + SecKeychainAttribute attributes[12]; + SecKeychainAttributeList list; + char *password; + UInt32 passwordLen; + passwordLen = 1024; + password = malloc(passwordLen); + + attributes[0].tag = kSecServiceItemAttr; + attributes[1].tag = kSecAccountItemAttr; + attributes[2].tag = kSecDescriptionItemAttr; + attributes[3].tag = kSecLabelItemAttr; + attributes[4].tag = kSecCommentItemAttr; + attributes[5].tag = kSecCreatorItemAttr; + + list.count = 6; + list.attr = attributes; + + // alter this to read the password (last two nulls) + status = SecKeychainItemCopyContent(item, NULL, &list, NULL, NULL); + + if (status == noErr) + { + SecKeychainAttribute attr; + int i; + + NSString *where = [NSString stringWithFormat:@"%@@%@:%@", + [resultConnection databaseName], [resultConnection server], + [resultConnection port]]; + NSString *description = [NSString stringWithFormat:@"%@@%@:%@", + [resultConnection databaseName], [resultConnection server], + [resultConnection port]]; + + for (i = 0; i < list.count; i++) + { + attr = list.attr[i]; + switch (attr.tag) + { + case kSecServiceItemAttr: + attr.data = (char *)[where cString]; + attr.length = [where length]; + break; + case kSecAccountItemAttr: + attr.data = (char *)[[loginUserName stringValue] cString]; + attr.length = [[loginUserName stringValue] length]; + break; + case kSecDescriptionItemAttr: + attr.data = "PostgreSQL Login"; + attr.length = 16; + break; + case kSecLabelItemAttr: + attr.data = (char *)[[savedConnections stringValue] cString]; + attr.length = [[savedConnections stringValue] length]; + break; + case kSecCommentItemAttr: + attr.data = (char*)[description cString]; + attr.length = [description length]; + break; + case kSecCreatorItemAttr: + attr.data = "pgds"; + attr.length = 4; + break; + default: + break; + } + } + // do the save of the edited item + status = SecKeychainItemModifyContent(item, &list, + [[loginPassword stringValue] length], + [[loginPassword stringValue] cString]); + + } + + free(password); + CFRelease(item); + i++; + } + CFRelease(search); + + return (i > 0); +} + +- (BOOL)createKeychainItem +{ + SecKeychainAttribute attributes[6]; + SecKeychainAttributeList list; + SecKeychainItemRef item; + OSStatus status; + + NSString *where = [NSString stringWithFormat:@"%@@%@:%@", + [resultConnection databaseName], [resultConnection server], + [resultConnection port]]; + NSString *description = [NSString stringWithFormat:@"%@@%@:%@", + [resultConnection databaseName], [resultConnection server], + [resultConnection port]]; + + attributes[0].tag = kSecAccountItemAttr; + attributes[0].data = (char *)[[loginUserName stringValue] cString]; + attributes[0].length = [[loginUserName stringValue] length]; + + attributes[1].tag = kSecDescriptionItemAttr; + attributes[1].data = "PostgreSQL Login"; + attributes[1].length = 16; + + attributes[2].tag = kSecLabelItemAttr; + attributes[2].data = (char *)[[savedConnections stringValue] cString]; + attributes[2].length = [[savedConnections stringValue] length]; + + attributes[3].tag = kSecServiceItemAttr; + attributes[3].data = (char *)[where cString]; + attributes[3].length = [where length]; + + attributes[4].tag = kSecCommentItemAttr; + attributes[4].data = (char*)[description cString]; + attributes[4].length = [description length]; + + attributes[5].tag = kSecCreatorItemAttr; + attributes[5].data = "pgds"; + attributes[5].length = 4; + + list.count = 6; + list.attr = attributes; + + status = SecKeychainItemCreateFromContent(kSecGenericPasswordItemClass, + &list, + [[loginPassword stringValue] length], + [[loginPassword stringValue] cString], + NULL, NULL, &item); + CFRelease(item); + return (status == noErr); + +} + + +#pragma mark - +#pragma mark Class implementation + +-(id)init +{ + self = [super init]; + + resultConnection = [[[[PGSQLConnection alloc] init] retain] autorelease]; + defaultDSN = nil; + defaultUser = nil; + defaultPassword = nil; + + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onConnectionCompleted:) name:PGSQLConnectionDidCompleteNotification object:nil]; + + return self; +} + + +- (void)beginModalLoginForWindow:(NSWindow *)parent +{ + parentWindow = parent; + + // load the nib + + if (![NSBundle loadNibNamed:@"PGSQL Login" owner:self]) + { + NSLog(@"Error loading nib for login"); + return; + } + + [rememberConnection setState:NSOnState]; + + [savedConnections removeAllItems]; + + // populate the list from the keychain + OSStatus status; + SecKeychainSearchRef search; + SecKeychainAttribute attributes[1]; + SecKeychainAttributeList list; + SecKeychainItemRef item; + + attributes[0].tag = kSecCreatorItemAttr; + attributes[0].data = "pgds"; + attributes[0].length = 4; + + list.count = 1; + list.attr = attributes; + + status = SecKeychainSearchCreateFromAttributes(NULL, + kSecGenericPasswordItemClass, + &list, &search); + if (status != noErr) + { + NSLog(@"Error reading the keychain: %d", status); + } + + while (SecKeychainSearchCopyNext(search, &item) == noErr) + { + [savedConnections addItemWithObjectValue:[self connectionNameForKeychainItem:item]]; + CFRelease(item); + } + CFRelease(search); + + // default the host fields + isShowingDetails = YES; + [serverName setStringValue:@"localhost"]; + [serverPort setStringValue:@"5432"]; + [serverDatabase setStringValue:@"template1"]; + + if (defaultDSN != nil) + { + isShowingDetails = YES; + + // [savedConnections selectItemWithTitle:defaultDSN]; + } + + [loginUserName setStringValue:@"postgres"]; + if (defaultUser != nil) + { + [loginUserName setStringValue:defaultUser]; + } + + [loginPassword setStringValue:@""]; + if (defaultPassword != nil) + { + [loginPassword setStringValue:defaultPassword]; + } + + // make sure the UI delegates are in place + [savedConnections setDelegate:self]; + + [NSApp beginSheet:loginPanel + modalForWindow:parentWindow + modalDelegate:nil + didEndSelector:nil + contextInfo:nil]; + + [NSApp runModalForWindow:loginPanel]; + + return; +} + +- (void)onConnectionCompleted:(NSNotification *)aNotification +{ + NSDictionary *info = [aNotification userInfo]; + + if (nil == [info valueForKey:@"Error"]) { + NSLog(@"Connected to PostgreSQL Database"); + } else { + // show an alert + NSLog(@"Unable to connect to PostgreSQL Database"); + } + + NSObject* windowDelegate = [parentWindow delegate]; + if ([windowDelegate respondsToSelector:@selector(loginCompleted:)] == YES) + { + [windowDelegate loginCompleted:resultConnection]; + } + +} + +- (IBAction)onLogin:(id)sender +{ + [NSApp stopModal]; + + [resultConnection setUserName:[loginUserName stringValue]]; + [resultConnection setPassword:[loginPassword stringValue]]; + [resultConnection setServer:[serverName stringValue]]; + [resultConnection setPort:[serverPort stringValue]]; + [resultConnection setDatabaseName:[serverDatabase stringValue]]; + + // if 'remember connection' is enabled, add/update the connection in the + // keychain using the keychain api. + if ([rememberConnection state] == NSOnState) + { + if ([[savedConnections stringValue] length] > 0) + { + if ([self editExistingKeychainItem] == NO) + { + if ([self createKeychainItem] == NO) + { + NSLog(@"Failed to set keychain item"); + } + } + } + } + + + // attempt the connection. + [resultConnection connectAsync]; + + [NSApp endSheet:loginPanel]; + [loginPanel orderOut:self]; +} + +- (void)resizeWindowToSize:(NSSize)newSize +{ + NSRect aFrame; + + float newHeight = newSize.height; + float newWidth = newSize.width; + + aFrame = [NSWindow contentRectForFrameRect:[loginPanel frame] + styleMask:[loginPanel styleMask]]; + + aFrame.origin.y += aFrame.size.height; + aFrame.origin.y -= newHeight; + aFrame.size.height = newHeight; + aFrame.size.width = newWidth; + + aFrame = [NSWindow frameRectForContentRect:aFrame + styleMask:[loginPanel styleMask]]; + + [loginPanel setFrame:aFrame display:YES animate:YES]; +} + + + +- (IBAction)onConnectionDetails:(id)sender +{ + NSSize newSize; // box is 144 + NSRect currentSize; + + NSView *contentView = [loginPanel contentView]; + + currentSize = [contentView frame]; + // show/hide the details section of the dialog + if ([displayConnectionDetails state] == NSOnState) + { + // flip the flag + newSize.width = currentSize.size.width; + newSize.height = currentSize.size.height + 144; + [detailsView setHidden:NO]; + [self resizeWindowToSize:newSize]; + } else { + // + newSize.width = currentSize.size.width; + newSize.height = currentSize.size.height - 144; + [self resizeWindowToSize:newSize]; + [detailsView setHidden:YES]; + + } + isShowingDetails = !(isShowingDetails); +} + +- (IBAction)onHelp:(id)sender +{ + // display the login dialog help window + +} + +- (IBAction)onCancel:(id)sender +{ + [NSApp stopModal]; + + // exit the application + NSObject* windowDelegate = [parentWindow delegate]; + if ([windowDelegate respondsToSelector:@selector(loginCompleted:)] == YES) + { + [windowDelegate loginCompleted:nil]; + } + + [NSApp endSheet:loginPanel]; + [loginPanel orderOut:self]; +} + +- (BOOL)isConnected +{ + if (resultConnection != nil) + { + return [resultConnection isConnected]; + } + + return NO; +} + +- (PGSQLConnection *)connection ... [truncated message content] |