Commits

Tuukka Norri  committed 4fef67b

Split the connector classes into multiple files
- Also added the NSDictionary additions which were missing from the previous commit.

  • Participants
  • Parent commits 3c77c0e

Comments (0)

Files changed (10)

File BaseTen.xcodeproj/project.pbxproj

 		53015A850FBEF22400E52C1E /* BXArraySize.h in Headers */ = {isa = PBXBuildFile; fileRef = 53015A830FBEF18800E52C1E /* BXArraySize.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		530A69831199A5BA00ED63FD /* NSDictionary+BaseTenAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 530A69811199A5BA00ED63FD /* NSDictionary+BaseTenAdditions.h */; };
 		530A69841199A5BA00ED63FD /* NSDictionary+BaseTenAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 530A69821199A5BA00ED63FD /* NSDictionary+BaseTenAdditions.m */; };
+		530A6A561199B9CA00ED63FD /* PGTSAsynchronousConnector.h in Headers */ = {isa = PBXBuildFile; fileRef = 530A6A521199B9CA00ED63FD /* PGTSAsynchronousConnector.h */; };
+		530A6A571199B9CA00ED63FD /* PGTSAsynchronousConnector.m in Sources */ = {isa = PBXBuildFile; fileRef = 530A6A531199B9CA00ED63FD /* PGTSAsynchronousConnector.m */; };
+		530A6A581199B9CA00ED63FD /* PGTSSynchronousConnector.h in Headers */ = {isa = PBXBuildFile; fileRef = 530A6A541199B9CA00ED63FD /* PGTSSynchronousConnector.h */; };
+		530A6A591199B9CA00ED63FD /* PGTSSynchronousConnector.m in Sources */ = {isa = PBXBuildFile; fileRef = 530A6A551199B9CA00ED63FD /* PGTSSynchronousConnector.m */; };
 		531A21500DE431D1006C757A /* BXPGConnectionResetRecoveryAttempter.h in Headers */ = {isa = PBXBuildFile; fileRef = 531A214E0DE431D1006C757A /* BXPGConnectionResetRecoveryAttempter.h */; };
 		531A21510DE431D1006C757A /* BXPGConnectionResetRecoveryAttempter.m in Sources */ = {isa = PBXBuildFile; fileRef = 531A214F0DE431D1006C757A /* BXPGConnectionResetRecoveryAttempter.m */; };
 		531A21560DE4324D006C757A /* BXPGAutocommitConnectionResetRecoveryAttempter.h in Headers */ = {isa = PBXBuildFile; fileRef = 531A21540DE4324D006C757A /* BXPGAutocommitConnectionResetRecoveryAttempter.h */; };
 		5308940D0E4349A50063C5E7 /* InterfaceBuilderPlugin.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = InterfaceBuilderPlugin.xcodeproj; path = InterfaceBuilderPlugin/InterfaceBuilderPlugin.xcodeproj; sourceTree = "<group>"; };
 		530A69811199A5BA00ED63FD /* NSDictionary+BaseTenAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSDictionary+BaseTenAdditions.h"; path = "Sources/NSDictionary+BaseTenAdditions.h"; sourceTree = "<group>"; };
 		530A69821199A5BA00ED63FD /* NSDictionary+BaseTenAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSDictionary+BaseTenAdditions.m"; path = "Sources/NSDictionary+BaseTenAdditions.m"; sourceTree = "<group>"; };
+		530A6A521199B9CA00ED63FD /* PGTSAsynchronousConnector.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PGTSAsynchronousConnector.h; path = Sources/PGTSAsynchronousConnector.h; sourceTree = "<group>"; };
+		530A6A531199B9CA00ED63FD /* PGTSAsynchronousConnector.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = PGTSAsynchronousConnector.m; path = Sources/PGTSAsynchronousConnector.m; sourceTree = "<group>"; };
+		530A6A541199B9CA00ED63FD /* PGTSSynchronousConnector.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PGTSSynchronousConnector.h; path = Sources/PGTSSynchronousConnector.h; sourceTree = "<group>"; };
+		530A6A551199B9CA00ED63FD /* PGTSSynchronousConnector.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = PGTSSynchronousConnector.m; path = Sources/PGTSSynchronousConnector.m; sourceTree = "<group>"; };
 		530ADD8309ED21EF004A13C3 /* BaseTen.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; name = BaseTen.h; path = Sources/BaseTen.h; sourceTree = "<group>"; };
 		531266E40B68F9CA00637EDA /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; };
 		531A214E0DE431D1006C757A /* BXPGConnectionResetRecoveryAttempter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BXPGConnectionResetRecoveryAttempter.h; path = Sources/BXPGConnectionResetRecoveryAttempter.h; sourceTree = "<group>"; };
 				53666CC70E1CD8D20028DBEA /* PGTSNotification.m */,
 				53666CB20E1CD8D20028DBEA /* PGTSConnector.h */,
 				53666CB30E1CD8D20028DBEA /* PGTSConnector.m */,
+				530A6A541199B9CA00ED63FD /* PGTSSynchronousConnector.h */,
+				530A6A551199B9CA00ED63FD /* PGTSSynchronousConnector.m */,
+				530A6A521199B9CA00ED63FD /* PGTSAsynchronousConnector.h */,
+				530A6A531199B9CA00ED63FD /* PGTSAsynchronousConnector.m */,
 				53666CAA0E1CD8D20028DBEA /* PGTSConnection.h */,
 				53666CB10E1CD8D20028DBEA /* PGTSConnectionPrivate.h */,
 				53666CAB0E1CD8D20028DBEA /* PGTSConnection.mm */,
 				53C8CC66118F1B8800E7AF88 /* BXDatabaseObjectModelPrivate.h in Headers */,
 				53C4C3F41192EC10003FB842 /* BXCFHostCompatibility.h in Headers */,
 				530A69831199A5BA00ED63FD /* NSDictionary+BaseTenAdditions.h in Headers */,
+				530A6A561199B9CA00ED63FD /* PGTSAsynchronousConnector.h in Headers */,
+				530A6A581199B9CA00ED63FD /* PGTSSynchronousConnector.h in Headers */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
 				53617F931035B89D003CE4F5 /* variables.c in Sources */,
 				53E8985E1035B97D0062AA4D /* psqlscan.l in Sources */,
 				530A69841199A5BA00ED63FD /* NSDictionary+BaseTenAdditions.m in Sources */,
+				530A6A571199B9CA00ED63FD /* PGTSAsynchronousConnector.m in Sources */,
+				530A6A591199B9CA00ED63FD /* PGTSSynchronousConnector.m in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};

File Sources/NSDictionary+BaseTenAdditions.h

+//
+// NSDictionary+BaseTenAdditions.h
+// BaseTen
+//
+// Copyright (C) 2010 Marko Karppinen & Co. LLC.
+//
+// Before using this software, please review the available licensing options
+// by visiting http://basetenframework.org/licensing/ or by contacting
+// us at sales@karppinen.fi. Without an additional license, this software
+// may be distributed only in compliance with the GNU General Public License.
+//
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License, version 2.0,
+// as published by the Free Software Foundation.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+//
+// $Id$
+//
+
+#import <Foundation/Foundation.h>
+
+
+@interface NSDictionary (BaseTenAdditions)
+- (NSDictionary *) BXDeepCopy;
+@end

File Sources/NSDictionary+BaseTenAdditions.m

+//
+// NSDictionary+BaseTenAdditions.m
+// BaseTen
+//
+// Copyright (C) 2010 Marko Karppinen & Co. LLC.
+//
+// Before using this software, please review the available licensing options
+// by visiting http://basetenframework.org/licensing/ or by contacting
+// us at sales@karppinen.fi. Without an additional license, this software
+// may be distributed only in compliance with the GNU General Public License.
+//
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License, version 2.0,
+// as published by the Free Software Foundation.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+//
+// $Id$
+//
+
+#import "NSDictionary+BaseTenAdditions.h"
+#import "BXEnumerate.h"
+
+
+@implementation NSDictionary (BaseTenAdditions)
+- (NSDictionary *) BXDeepCopy
+{
+	NSUInteger count = [self count];
+	NSMutableArray *keys = [NSMutableArray arrayWithCapacity: count];
+	NSMutableArray *vals = [NSMutableArray arrayWithCapacity: count];
+	BXEnumerate (currentKey, e, [self keyEnumerator])
+	{
+		[keys addObject: currentKey];
+		
+		id copy = [[self objectForKey: currentKey] copy];
+		[vals addObject: copy];
+		[copy release];
+	}
+	
+	NSDictionary *retval = [[NSDictionary alloc] initWithObjects: vals forKeys: keys];
+	return retval;
+}
+@end

File Sources/PGTSAsynchronousConnector.h

+//
+// PGTSAsynchronousConnector.h
+// BaseTen
+//
+// Copyright (C) 2008-2010 Marko Karppinen & Co. LLC.
+//
+// Before using this software, please review the available licensing options
+// by visiting http://www.karppinen.fi/baseten/licensing/ or by contacting
+// us at sales@karppinen.fi. Without an additional license, this software
+// may be distributed only in compliance with the GNU General Public License.
+//
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License, version 2.0,
+// as published by the Free Software Foundation.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+//
+// $Id$
+//
+
+#import <Foundation/Foundation.h>
+#import <BaseTen/PGTSConnector.h>
+
+
+@interface PGTSAsynchronousConnector : PGTSConnector
+{
+	NSDictionary* mConnectionDictionary;
+	CFRunLoopRef mRunLoop;
+	CFHostRef mHost;
+	CFSocketRef mSocket;
+	CFRunLoopSourceRef mSocketSource;
+	CFSocketCallBackType mExpectedCallBack;
+	CFStreamError mHostError;
+}
+
+- (CFRunLoopRef) CFRunLoop;
+- (void) setCFRunLoop: (CFRunLoopRef) aRef;
+
+- (void) socketReady: (CFSocketCallBackType) callBackType;
+- (void) continueFromNameResolution: (const CFStreamError *) error;
+- (BOOL) startNegotiation: (const char *) conninfo;
+- (void) negotiateConnection;
+@end
+
+
+@interface PGTSAsynchronousReconnector : PGTSAsynchronousConnector
+{
+}
+@end

File Sources/PGTSAsynchronousConnector.m

+//
+// PGTSAsynchronousConnector.m
+// BaseTen
+//
+// Copyright (C) 2008-2010 Marko Karppinen & Co. LLC.
+//
+// Before using this software, please review the available licensing options
+// by visiting http://www.karppinen.fi/baseten/licensing/ or by contacting
+// us at sales@karppinen.fi. Without an additional license, this software
+// may be distributed only in compliance with the GNU General Public License.
+//
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License, version 2.0,
+// as published by the Free Software Foundation.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+//
+// $Id$
+//
+
+
+#import "PGTSAsynchronousConnector.h"
+#import "PGTSConstants.h"
+#import "PGTSConnection.h"
+#import "BXError.h"
+#import "BXLogger.h"
+#import "BXEnumerate.h"
+#import "BXArraySize.h"
+#import <netdb.h>
+#import <arpa/inet.h>
+
+
+@implementation PGTSAsynchronousConnector
+static void
+ScheduleHost (CFHostRef theHost, CFRunLoopRef theRunLoop)
+{
+	if (theHost && theRunLoop)
+		CFHostScheduleWithRunLoop (theHost, theRunLoop, (CFStringRef) kCFRunLoopCommonModes);
+}
+
+
+static void
+UnscheduleHost (CFHostRef theHost, CFRunLoopRef theRunLoop)
+{
+	if (theHost && theRunLoop)
+		CFHostUnscheduleFromRunLoop (theHost, theRunLoop, (CFStringRef) kCFRunLoopCommonModes);
+}
+
+
+static void 
+HostReady (CFHostRef theHost, CFHostInfoType typeInfo, const CFStreamError *error, void *self)
+{
+	BXLogDebug (@"CFHost got ready.");
+	UnscheduleHost (theHost, [(id) self CFRunLoop]);
+	[(id) self continueFromNameResolution: error];
+}
+
+
+static void 
+SocketReady (CFSocketRef s, CFSocketCallBackType callBackType, CFDataRef address, const void* data, void* self)
+{
+	[(id) self socketReady: callBackType];
+}
+
+
+- (id) init
+{
+	if ((self = [super init]))
+	{
+		mExpectedCallBack = 0;
+		[self setCFRunLoop: CFRunLoopGetCurrent ()];
+	}
+	return self;
+}
+
+
+- (CFRunLoopRef) CFRunLoop
+{
+	return mRunLoop;
+}
+
+
+- (void) setCFRunLoop: (CFRunLoopRef) aRef
+{
+	if (mRunLoop != aRef)
+	{
+		if (mRunLoop) CFRelease (mRunLoop);
+		if (aRef)
+		{
+			mRunLoop = aRef;
+			CFRetain (mRunLoop);
+		}
+	}
+}
+
+
+- (void) setConnectionDictionary: (NSDictionary *) aDict
+{
+	if (mConnectionDictionary != aDict)
+	{
+		[mConnectionDictionary release];
+		mConnectionDictionary = [aDict retain];
+	}
+}
+
+
+- (void) removeHost
+{
+	if (mHost)
+	{
+		CFHostCancelInfoResolution (mHost, kCFHostReachability);
+		UnscheduleHost (mHost, mRunLoop);
+		CFRelease (mHost);
+		mHost = NULL;
+	}		
+}
+
+
+- (void) freeCFTypes
+{
+	//Don't release the connection. Delegate will handle it.
+	
+	[self removeHost];
+	
+	if (mSocketSource)
+	{
+		CFRunLoopSourceInvalidate (mSocketSource);
+		CFRelease (mSocketSource);
+		mSocketSource = NULL;
+	}
+	
+	if (mSocket)
+	{
+		CFSocketInvalidate (mSocket);
+		CFRelease (mSocket);
+		mSocket = NULL;
+	}
+	
+	if (mRunLoop)
+	{
+		CFRelease (mRunLoop);
+		mRunLoop = NULL;
+	}
+}
+
+
+- (void) cancel
+{
+	[self removeHost];
+    if (mConnection)
+    {
+        PQfinish (mConnection);
+        mConnection = NULL;
+    }
+}
+
+
+- (void) dealloc
+{
+	[self freeCFTypes];
+	[self cancel];
+	[mConnectionError release];
+	[super dealloc];
+}
+
+
+- (void) finalize
+{
+	[self freeCFTypes];
+	[self cancel];
+	[super finalize];
+}
+
+- (void) prepareForConnect
+{
+	[super prepareForConnect];
+	bzero (&mHostError, sizeof (mHostError));
+}
+
+
+#pragma mark Callbacks
+
+- (void) continueFromNameResolution: (const CFStreamError *) streamError
+{
+	//If the resolution succeeded, iterate addresses and try to connect to each. Stop when a server gets reached.
+	
+	BOOL reachedServer = NO;
+	BXLogDebug (@"Continuing from name resolution.");
+	
+	if (streamError && streamError->domain)
+	{
+		NSString* errorTitle = NSLocalizedStringWithDefaultValue (@"connectionError", nil, [NSBundle bundleForClass: [self class]],
+																  @"Connection error", @"Title for a sheet.");
+		
+		//FIXME: localization.
+		NSString* messageFormat = nil;
+		const char* reason = NULL;
+		if (streamError->domain == kCFStreamErrorDomainNetDB)
+		{
+			reason = (gai_strerror (streamError->error));
+			if (reason)
+				messageFormat = @"The server %@ wasn't found: %s.";
+			else
+				messageFormat = @"The server %@ wasn't found.";
+		}
+		else if (streamError->domain == kCFStreamErrorDomainSystemConfiguration)
+		{
+			messageFormat = @"The server %@ wasn't found. Network might be unreachable.";
+		}
+		else
+		{
+			messageFormat = @"The server %@ wasn't found.";
+		}
+		NSString* message = [NSString stringWithFormat: messageFormat, [mConnectionDictionary objectForKey: kPGTSHostKey], reason];
+		
+		NSDictionary* userInfo = [NSDictionary dictionaryWithObjectsAndKeys:
+								  errorTitle, NSLocalizedDescriptionKey,
+								  errorTitle, NSLocalizedFailureReasonErrorKey,
+								  message, NSLocalizedRecoverySuggestionErrorKey,
+								  nil];
+		//FIXME: error code
+		NSError* error = [BXError errorWithDomain: kPGTSConnectionErrorDomain code: kPGTSConnectionErrorUnknown userInfo: userInfo];
+		[self setConnectionError: error];		
+	}
+	else
+	{		
+		NSArray* addresses = (id) CFHostGetAddressing (mHost, NULL);
+		if (addresses)
+		{
+			char addressBuffer [40] = {}; // 8 x 4 (hex digits in IPv6 address) + 7 (colons) + 1 (nul character)
+			
+			NSMutableDictionary* connectionDictionary = [[mConnectionDictionary mutableCopy] autorelease];
+			[connectionDictionary removeObjectForKey: kPGTSHostKey];
+			
+			//This is safe because each address is owned by the addresses CFArray which is owned 
+			//by mHost which is CFRetained.
+			BXEnumerate (addressData, e, [addresses objectEnumerator])
+			{
+				const struct sockaddr* address = [addressData bytes];
+				sa_family_t family = address->sa_family;
+				void* addressBytes = NULL;
+				
+				switch (family)
+				{
+					case AF_INET:
+						addressBytes = &((struct sockaddr_in *) address)->sin_addr.s_addr;
+						break;
+						
+					case AF_INET6:
+						addressBytes = ((struct sockaddr_in6 *) address)->sin6_addr.s6_addr;
+						break;
+						
+					default:
+						break;
+				}
+				
+				if (addressBytes && inet_ntop (family, addressBytes, addressBuffer, BXArraySize (addressBuffer)))
+				{
+					NSString* humanReadableAddress = [NSString stringWithUTF8String: addressBuffer];
+					BXLogInfo (@"Trying '%@'", humanReadableAddress);
+					[connectionDictionary setObject: humanReadableAddress forKey: kPGTSHostAddressKey];
+					char* conninfo = PGTSCopyConnectionString (connectionDictionary);
+					
+					if ([self startNegotiation: conninfo])
+					{
+						reachedServer = YES;
+						free (conninfo);
+						break;
+					}
+					
+					free (conninfo);
+				}
+			}
+		}
+	}
+	
+	if (reachedServer)
+		[self negotiateConnection];
+	else
+		[self finishedConnecting: NO];
+}
+
+
+- (void) socketReady: (CFSocketCallBackType) callBackType
+{
+	BXLogDebug (@"Socket got ready.");
+	
+	//Sometimes the wrong callback type gets called. We cope with this
+	//by checking against an expected type and re-enabling it if needed.
+	if (callBackType != mExpectedCallBack)
+		CFSocketEnableCallBacks (mSocket, mExpectedCallBack);
+	else
+	{
+		PostgresPollingStatusType status = mPollFunction (mConnection);
+		
+		[self setUpSSL];
+		
+		switch (status)
+		{
+			case PGRES_POLLING_OK:
+				[self finishedConnecting: YES];
+				break;
+				
+			case PGRES_POLLING_FAILED:
+				[self finishedConnecting: NO];
+				break;
+				
+			case PGRES_POLLING_ACTIVE:
+				[self socketReady: mExpectedCallBack];
+				break;
+				
+			case PGRES_POLLING_READING:
+				CFSocketEnableCallBacks (mSocket, kCFSocketReadCallBack);
+				mExpectedCallBack = kCFSocketReadCallBack;
+				break;
+				
+			case PGRES_POLLING_WRITING:
+			default:
+				CFSocketEnableCallBacks (mSocket, kCFSocketWriteCallBack);
+				mExpectedCallBack = kCFSocketWriteCallBack;
+				break;
+		}
+	}
+}
+
+
+- (void) finishedConnecting: (BOOL) succeeded
+{
+	[self freeCFTypes];
+	[super finishedConnecting: succeeded];
+}
+
+
+#pragma mark Connection methods
+
+- (BOOL) connect: (NSDictionary *) connectionDictionary
+{
+	BXLogDebug (@"Beginning connecting.");
+	
+	BOOL retval = NO;
+	mExpectedCallBack = 0;
+	[self prepareForConnect];
+	[self setConnectionDictionary: connectionDictionary];
+	
+	//CFSocket etc. do some nice things for us that prevent libpq from noticing
+	//connection problems. This causes SIGPIPE to be sent to us, and we get
+	//"Broken pipe" as the error message. To cope with this, we check the socket's
+	//status after connecting but before giving it to CFSocket.
+	//For this to work, we need to resolve the host name by ourselves, if we have one.
+    //If the name begins with a slash, it is a path to socket.
+	
+	NSString* name = [connectionDictionary objectForKey: kPGTSHostKey];
+	if (0 < [name length] && '/' != [name characterAtIndex: 0])
+	{
+		Boolean status = FALSE;
+		CFHostClientContext ctx = {
+			0,
+			self,
+			NULL,
+			NULL,
+			NULL
+		};
+				
+		[self removeHost];
+		mHost = CFHostCreateWithName (NULL, (CFStringRef) name);
+		status = CFHostSetClient (mHost, &HostReady, &ctx);
+		BXLogDebug (@"Set host client: %d.", status);
+		ScheduleHost (mHost, mRunLoop);
+		
+		status = CFHostStartInfoResolution (mHost, kCFHostAddresses, &mHostError);
+		BXLogDebug (@"Started host info resolution: %d.", status);
+		if (! status)
+		{
+			UnscheduleHost (mHost, mRunLoop);
+			[self continueFromNameResolution: &mHostError];
+		}
+	}
+	else
+	{
+		char* conninfo = PGTSCopyConnectionString (mConnectionDictionary);
+		if ([self startNegotiation: conninfo])
+		{
+			retval = YES;
+			[self negotiateConnection];
+		}
+		else
+		{
+			[self finishedConnecting: NO];
+		}
+		free (conninfo);
+	}
+	
+	return retval;
+}
+
+
+- (BOOL) startNegotiation: (const char *) conninfo
+{
+	BXLogDebug (@"Beginning negotiation.");
+	
+	mNegotiationStarted = NO;
+	BOOL retval = NO;
+	if ([self start: conninfo])
+	{
+		if (CONNECTION_BAD != PQstatus (mConnection))
+		{
+			mNegotiationStarted = YES;
+			int socket = PQsocket (mConnection);
+			if (socket < 0)
+				BXLogInfo (@"Unable to get connection socket from libpq.");
+			else
+			{
+				//We mimic libpq's error message because it doesn't provide us with error codes.
+				//Also we need to select first to make sure that getsockopt returns an accurate error message.
+				
+				BOOL haveError = NO;
+				NSString* reason = nil;
+				
+				{
+					char message [256] = {};
+					int status = 0;
+					
+					struct timeval timeout = {.tv_sec = 15, .tv_usec = 0};
+					fd_set mask = {};
+					FD_ZERO (&mask);
+					FD_SET (socket, &mask);
+					status = select (socket + 1, NULL, &mask, NULL, &timeout);		
+					
+					if (status <= 0)
+					{
+						haveError = YES;
+						strerror_r (errno, message, BXArraySize (message));
+						reason = [NSString stringWithUTF8String: message];
+					}
+					else
+					{
+						int optval = 0;
+						socklen_t size = sizeof (optval);
+						status = getsockopt (socket, SOL_SOCKET, SO_ERROR, &optval, &size);
+						
+						if (0 == status)
+						{
+							if (0 == optval)
+								retval = YES;
+							else
+							{			
+								haveError = YES;
+								strerror_r (optval, message, BXArraySize (message));
+								reason = [NSString stringWithUTF8String: message];
+							}
+						}
+						else
+						{
+							haveError = YES;
+						}
+					}
+				}
+				
+				if (haveError)
+				{
+					NSString* errorTitle = NSLocalizedStringWithDefaultValue (@"connectionError", nil, [NSBundle bundleForClass: [self class]],
+																			  @"Connection error", @"Title for a sheet.");
+					NSString* messageFormat = NSLocalizedStringWithDefaultValue (@"libpqStyleConnectionErrorFormat", nil, [NSBundle bundleForClass: [self class]],
+																				 @"Could not connect to server: %@. Is the server running at \"%@\" and accepting TCP/IP connections on port %s?", 
+																				 @"Reason for error");		
+					if (! reason)
+					{
+						reason = NSLocalizedStringWithDefaultValue (@"connectionRefused", nil, [NSBundle bundleForClass: [self class]],
+																	@"Connection refused", @"Reason for error");
+					}
+					
+					NSString* address = ([mConnectionDictionary objectForKey: kPGTSHostKey] ?: [mConnectionDictionary objectForKey: kPGTSHostAddressKey]);
+					NSString* message = [NSString stringWithFormat: messageFormat, reason, address, PQport (mConnection)];
+					NSDictionary* userInfo = [NSDictionary dictionaryWithObjectsAndKeys:
+											  errorTitle, NSLocalizedDescriptionKey,
+											  errorTitle, NSLocalizedFailureReasonErrorKey,
+											  message, NSLocalizedRecoverySuggestionErrorKey,
+											  nil];		
+					NSError* error = [BXError errorWithDomain: kPGTSConnectionErrorDomain code: kPGTSConnectionErrorUnknown userInfo: userInfo];
+					[self setConnectionError: error];
+				}				
+			}
+		}
+	}
+	
+	return retval;
+}
+
+
+- (void) negotiateConnection
+{
+	BXLogDebug (@"Negotiating.");
+	
+	if (mTraceFile)
+		PQtrace (mConnection, mTraceFile);
+	
+	CFSocketContext context = {0, self, NULL, NULL, NULL};
+	CFSocketCallBackType callbacks = kCFSocketReadCallBack | kCFSocketWriteCallBack;
+	mSocket = CFSocketCreateWithNative (NULL, PQsocket (mConnection), callbacks, &SocketReady, &context);
+	CFOptionFlags flags = 
+	~kCFSocketAutomaticallyReenableReadCallBack &
+	~kCFSocketAutomaticallyReenableWriteCallBack &
+	~kCFSocketCloseOnInvalidate &
+	CFSocketGetSocketFlags (mSocket);
+	
+	CFSocketSetSocketFlags (mSocket, flags);
+	mSocketSource = CFSocketCreateRunLoopSource (NULL, mSocket, 0);
+	
+	BXAssertLog (mSocket, @"Expected source to have been created.");
+	BXAssertLog (CFSocketIsValid (mSocket), @"Expected socket to be valid.");
+	BXAssertLog (mSocketSource, @"Expected socketSource to have been created.");
+	BXAssertLog (CFRunLoopSourceIsValid (mSocketSource), @"Expected socketSource to be valid.");
+	
+	CFSocketDisableCallBacks (mSocket, kCFSocketReadCallBack);
+	CFSocketEnableCallBacks (mSocket, kCFSocketWriteCallBack);
+	mExpectedCallBack = kCFSocketWriteCallBack;
+	CFRunLoopAddSource (mRunLoop, mSocketSource, (CFStringRef) kCFRunLoopCommonModes);
+}
+@end
+
+
+
+@implementation PGTSAsynchronousReconnector
+- (id) init
+{
+    if ((self = [super init]))
+    {
+        mPollFunction = &PQresetPoll;
+    }
+    return self;
+}
+
+- (BOOL) start: (const char *) connectionString
+{
+	return (BOOL) PQresetStart (mConnection);
+}
+@end

File Sources/PGTSConnection.mm

 #import "PGTSConnection.h"
 #import "PGTSConnectionPrivate.h"
 #import "PGTSConnector.h"
+#import "PGTSSynchronousConnector.h"
+#import "PGTSAsynchronousConnector.h"
 #import "PGTSConstants.h"
 #import "PGTSQuery.h"
 #import "PGTSQueryDescription.h"

File Sources/PGTSConnector.h

 // PGTSConnector.h
 // BaseTen
 //
-// Copyright (C) 2008 Marko Karppinen & Co. LLC.
+// Copyright (C) 2008-2010 Marko Karppinen & Co. LLC.
 //
 // Before using this software, please review the available licensing options
 // by visiting http://www.karppinen.fi/baseten/licensing/ or by contacting
 
 #import <Foundation/Foundation.h>
 #import <BaseTen/BXCFHostCompatibility.h> 
+#import <BaseTen/BXExport.h>
 #import <BaseTen/libpq-fe.h>
 
 @class PGTSConnector;
 
 
+BX_INTERNAL char *PGTSCopyConnectionString (NSDictionary *);
+
+
 @protocol PGTSConnectorDelegate <NSObject>
 - (void) connector: (PGTSConnector *) connector gotConnection: (PGconn *) connection;
 - (void) connectorFailed: (PGTSConnector *) connector;
 
 - (NSError *) connectionError;
 - (void) setConnectionError: (NSError *) anError;
+
+- (void) finishedConnecting: (BOOL) status;
+- (void) setUpSSL;
+- (void) prepareForConnect;
 @end
-
-
-@interface PGTSAsynchronousConnector : PGTSConnector
-{
-	NSDictionary* mConnectionDictionary;
-	CFRunLoopRef mRunLoop;
-	CFHostRef mHost;
-	CFSocketRef mSocket;
-	CFRunLoopSourceRef mSocketSource;
-	CFSocketCallBackType mExpectedCallBack;
-	CFStreamError mHostError;
-}
-
-- (CFRunLoopRef) CFRunLoop;
-- (void) setCFRunLoop: (CFRunLoopRef) aRef;
-
-- (void) socketReady: (CFSocketCallBackType) callBackType;
-- (void) continueFromNameResolution: (const CFStreamError *) error;
-- (BOOL) startNegotiation: (const char *) conninfo;
-- (void) negotiateConnection;
-@end
-
-
-@interface PGTSSynchronousConnector : PGTSConnector
-{
-}
-@end
-
-
-@interface PGTSAsynchronousReconnector : PGTSAsynchronousConnector
-{
-}
-@end
-
-
-@interface PGTSSynchronousReconnector : PGTSSynchronousConnector
-{
-}
-@end

File Sources/PGTSConnector.m

 // PGTSConnector.m
 // BaseTen
 //
-// Copyright (C) 2008 Marko Karppinen & Co. LLC.
+// Copyright (C) 2008-2010 Marko Karppinen & Co. LLC.
 //
 // Before using this software, please review the available licensing options
 // by visiting http://www.karppinen.fi/baseten/licensing/ or by contacting
 
 
 #import "PGTSConnector.h"
+#import "PGTSConstants.h"
 #import "PGTSConnection.h"
-#import "PGTSConstants.h"
-#import "BXConstants.h"
-#import "PGTSCertificateVerificationDelegateProtocol.h"
 #import "BXLogger.h"
 #import "BXError.h"
-#import "BXEnumerate.h"
-#import "BXArraySize.h"
 #import "NSString+PGTSAdditions.h"
 #import "libpq_additions.h"
-#import <sys/select.h>
-#import <arpa/inet.h>
-#import <netdb.h>
 
 
-static char*
-CopyConnectionString (NSDictionary* connectionDict)
+char*
+PGTSCopyConnectionString (NSDictionary* connectionDict)
 {
 	NSMutableString* connectionString = [NSMutableString string];
 	NSEnumerator* e = [connectionDict keyEnumerator];
 	[self setConnectionError: nil];
 }
 @end
-
-
-
-@implementation PGTSAsynchronousConnector
-
-static void
-ScheduleHost (CFHostRef theHost, CFRunLoopRef theRunLoop)
-{
-	if (theHost && theRunLoop)
-		CFHostScheduleWithRunLoop (theHost, theRunLoop, (CFStringRef) kCFRunLoopCommonModes);
-}
-
-
-static void
-UnscheduleHost (CFHostRef theHost, CFRunLoopRef theRunLoop)
-{
-	if (theHost && theRunLoop)
-		CFHostUnscheduleFromRunLoop (theHost, theRunLoop, (CFStringRef) kCFRunLoopCommonModes);
-}
-
-
-static void 
-HostReady (CFHostRef theHost, CFHostInfoType typeInfo, const CFStreamError *error, void *self)
-{
-	BXLogDebug (@"CFHost got ready.");
-	UnscheduleHost (theHost, [(id) self CFRunLoop]);
-	[(id) self continueFromNameResolution: error];
-}
-
-
-static void 
-SocketReady (CFSocketRef s, CFSocketCallBackType callBackType, CFDataRef address, const void* data, void* self)
-{
-	[(id) self socketReady: callBackType];
-}
-
-
-- (id) init
-{
-	if ((self = [super init]))
-	{
-		mExpectedCallBack = 0;
-		[self setCFRunLoop: CFRunLoopGetCurrent ()];
-	}
-	return self;
-}
-
-
-- (CFRunLoopRef) CFRunLoop
-{
-	return mRunLoop;
-}
-
-
-- (void) setCFRunLoop: (CFRunLoopRef) aRef
-{
-	if (mRunLoop != aRef)
-	{
-		if (mRunLoop) CFRelease (mRunLoop);
-		if (aRef)
-		{
-			mRunLoop = aRef;
-			CFRetain (mRunLoop);
-		}
-	}
-}
-
-
-- (void) setConnectionDictionary: (NSDictionary *) aDict
-{
-	if (mConnectionDictionary != aDict)
-	{
-		[mConnectionDictionary release];
-		mConnectionDictionary = [aDict retain];
-	}
-}
-
-
-- (void) removeHost
-{
-	if (mHost)
-	{
-		CFHostCancelInfoResolution (mHost, kCFHostReachability);
-		UnscheduleHost (mHost, mRunLoop);
-		CFRelease (mHost);
-		mHost = NULL;
-	}		
-}
-
-
-- (void) freeCFTypes
-{
-	//Don't release the connection. Delegate will handle it.
-	
-	[self removeHost];
-	
-	if (mSocketSource)
-	{
-		CFRunLoopSourceInvalidate (mSocketSource);
-		CFRelease (mSocketSource);
-		mSocketSource = NULL;
-	}
-	
-	if (mSocket)
-	{
-		CFSocketInvalidate (mSocket);
-		CFRelease (mSocket);
-		mSocket = NULL;
-	}
-	
-	if (mRunLoop)
-	{
-		CFRelease (mRunLoop);
-		mRunLoop = NULL;
-	}
-}
-
-
-- (void) cancel
-{
-	[self removeHost];
-    if (mConnection)
-    {
-        PQfinish (mConnection);
-        mConnection = NULL;
-    }
-}
-
-
-- (void) dealloc
-{
-	[self freeCFTypes];
-	[self cancel];
-	[mConnectionError release];
-	[super dealloc];
-}
-
-
-- (void) finalize
-{
-	[self freeCFTypes];
-	[self cancel];
-	[super finalize];
-}
-
-- (void) prepareForConnect
-{
-	[super prepareForConnect];
-	bzero (&mHostError, sizeof (mHostError));
-}
-
-
-#pragma mark Callbacks
-
-- (void) continueFromNameResolution: (const CFStreamError *) streamError
-{
-	//If the resolution succeeded, iterate addresses and try to connect to each. Stop when a server gets reached.
-	
-	BOOL reachedServer = NO;
-	BXLogDebug (@"Continuing from name resolution.");
-	
-	if (streamError && streamError->domain)
-	{
-		NSString* errorTitle = NSLocalizedStringWithDefaultValue (@"connectionError", nil, [NSBundle bundleForClass: [self class]],
-																  @"Connection error", @"Title for a sheet.");
-		
-		//FIXME: localization.
-		NSString* messageFormat = nil;
-		const char* reason = NULL;
-		if (streamError->domain == kCFStreamErrorDomainNetDB)
-		{
-			reason = (gai_strerror (streamError->error));
-			if (reason)
-				messageFormat = @"The server %@ wasn't found: %s.";
-			else
-				messageFormat = @"The server %@ wasn't found.";
-		}
-		else if (streamError->domain == kCFStreamErrorDomainSystemConfiguration)
-		{
-			messageFormat = @"The server %@ wasn't found. Network might be unreachable.";
-		}
-		else
-		{
-			messageFormat = @"The server %@ wasn't found.";
-		}
-		NSString* message = [NSString stringWithFormat: messageFormat, [mConnectionDictionary objectForKey: kPGTSHostKey], reason];
-		
-		NSDictionary* userInfo = [NSDictionary dictionaryWithObjectsAndKeys:
-								  errorTitle, NSLocalizedDescriptionKey,
-								  errorTitle, NSLocalizedFailureReasonErrorKey,
-								  message, NSLocalizedRecoverySuggestionErrorKey,
-								  nil];
-		//FIXME: error code
-		NSError* error = [BXError errorWithDomain: kPGTSConnectionErrorDomain code: kPGTSConnectionErrorUnknown userInfo: userInfo];
-		[self setConnectionError: error];		
-	}
-	else
-	{		
-		NSArray* addresses = (id) CFHostGetAddressing (mHost, NULL);
-		if (addresses)
-		{
-			char addressBuffer [40] = {}; // 8 x 4 (hex digits in IPv6 address) + 7 (colons) + 1 (nul character)
-			
-			NSMutableDictionary* connectionDictionary = [[mConnectionDictionary mutableCopy] autorelease];
-			[connectionDictionary removeObjectForKey: kPGTSHostKey];
-			
-			//This is safe because each address is owned by the addresses CFArray which is owned 
-			//by mHost which is CFRetained.
-			BXEnumerate (addressData, e, [addresses objectEnumerator])
-			{
-				const struct sockaddr* address = [addressData bytes];
-				sa_family_t family = address->sa_family;
-				void* addressBytes = NULL;
-				
-				switch (family)
-				{
-					case AF_INET:
-						addressBytes = &((struct sockaddr_in *) address)->sin_addr.s_addr;
-						break;
-						
-					case AF_INET6:
-						addressBytes = ((struct sockaddr_in6 *) address)->sin6_addr.s6_addr;
-						break;
-						
-					default:
-						break;
-				}
-				
-				if (addressBytes && inet_ntop (family, addressBytes, addressBuffer, BXArraySize (addressBuffer)))
-				{
-					NSString* humanReadableAddress = [NSString stringWithUTF8String: addressBuffer];
-					BXLogInfo (@"Trying '%@'", humanReadableAddress);
-					[connectionDictionary setObject: humanReadableAddress forKey: kPGTSHostAddressKey];
-					char* conninfo = CopyConnectionString (connectionDictionary);
-					
-					if ([self startNegotiation: conninfo])
-					{
-						reachedServer = YES;
-						free (conninfo);
-						break;
-					}
-					
-					free (conninfo);
-				}
-			}
-		}
-	}
-	
-	if (reachedServer)
-		[self negotiateConnection];
-	else
-		[self finishedConnecting: NO];
-}
-
-
-- (void) socketReady: (CFSocketCallBackType) callBackType
-{
-	BXLogDebug (@"Socket got ready.");
-	
-	//Sometimes the wrong callback type gets called. We cope with this
-	//by checking against an expected type and re-enabling it if needed.
-	if (callBackType != mExpectedCallBack)
-		CFSocketEnableCallBacks (mSocket, mExpectedCallBack);
-	else
-	{
-		PostgresPollingStatusType status = mPollFunction (mConnection);
-		
-		[self setUpSSL];
-		
-		switch (status)
-		{
-			case PGRES_POLLING_OK:
-				[self finishedConnecting: YES];
-				break;
-				
-			case PGRES_POLLING_FAILED:
-				[self finishedConnecting: NO];
-				break;
-				
-			case PGRES_POLLING_ACTIVE:
-				[self socketReady: mExpectedCallBack];
-				break;
-				
-			case PGRES_POLLING_READING:
-				CFSocketEnableCallBacks (mSocket, kCFSocketReadCallBack);
-				mExpectedCallBack = kCFSocketReadCallBack;
-				break;
-				
-			case PGRES_POLLING_WRITING:
-			default:
-				CFSocketEnableCallBacks (mSocket, kCFSocketWriteCallBack);
-				mExpectedCallBack = kCFSocketWriteCallBack;
-				break;
-		}
-	}
-}
-
-
-- (void) finishedConnecting: (BOOL) succeeded
-{
-	[self freeCFTypes];
-	[super finishedConnecting: succeeded];
-}
-
-
-#pragma mark Connection methods
-
-- (BOOL) connect: (NSDictionary *) connectionDictionary
-{
-	BXLogDebug (@"Beginning connecting.");
-	
-	BOOL retval = NO;
-	mExpectedCallBack = 0;
-	[self prepareForConnect];
-	[self setConnectionDictionary: connectionDictionary];
-	
-	//CFSocket etc. do some nice things for us that prevent libpq from noticing
-	//connection problems. This causes SIGPIPE to be sent to us, and we get
-	//"Broken pipe" as the error message. To cope with this, we check the socket's
-	//status after connecting but before giving it to CFSocket.
-	//For this to work, we need to resolve the host name by ourselves, if we have one.
-    //If the name begins with a slash, it is a path to socket.
-	
-	NSString* name = [connectionDictionary objectForKey: kPGTSHostKey];
-	if (0 < [name length] && '/' != [name characterAtIndex: 0])
-	{
-		Boolean status = FALSE;
-		CFHostClientContext ctx = {
-			0,
-			self,
-			NULL,
-			NULL,
-			NULL
-		};
-				
-		[self removeHost];
-		mHost = CFHostCreateWithName (NULL, (CFStringRef) name);
-		status = CFHostSetClient (mHost, &HostReady, &ctx);
-		BXLogDebug (@"Set host client: %d.", status);
-		ScheduleHost (mHost, mRunLoop);
-		
-		status = CFHostStartInfoResolution (mHost, kCFHostAddresses, &mHostError);
-		BXLogDebug (@"Started host info resolution: %d.", status);
-		if (! status)
-		{
-			UnscheduleHost (mHost, mRunLoop);
-			[self continueFromNameResolution: &mHostError];
-		}
-	}
-	else
-	{
-		char* conninfo = CopyConnectionString (mConnectionDictionary);
-		if ([self startNegotiation: conninfo])
-		{
-			retval = YES;
-			[self negotiateConnection];
-		}
-		else
-		{
-			[self finishedConnecting: NO];
-		}
-		free (conninfo);
-	}
-	
-	return retval;
-}
-
-
-- (BOOL) startNegotiation: (const char *) conninfo
-{
-	BXLogDebug (@"Beginning negotiation.");
-	
-	mNegotiationStarted = NO;
-	BOOL retval = NO;
-	if ([self start: conninfo])
-	{
-		if (CONNECTION_BAD != PQstatus (mConnection))
-		{
-			mNegotiationStarted = YES;
-			int socket = PQsocket (mConnection);
-			if (socket < 0)
-				BXLogInfo (@"Unable to get connection socket from libpq.");
-			else
-			{
-				//We mimic libpq's error message because it doesn't provide us with error codes.
-				//Also we need to select first to make sure that getsockopt returns an accurate error message.
-				
-				BOOL haveError = NO;
-				NSString* reason = nil;
-				
-				{
-					char message [256] = {};
-					int status = 0;
-					
-					struct timeval timeout = {.tv_sec = 15, .tv_usec = 0};
-					fd_set mask = {};
-					FD_ZERO (&mask);
-					FD_SET (socket, &mask);
-					status = select (socket + 1, NULL, &mask, NULL, &timeout);		
-					
-					if (status <= 0)
-					{
-						haveError = YES;
-						strerror_r (errno, message, BXArraySize (message));
-						reason = [NSString stringWithUTF8String: message];
-					}
-					else
-					{
-						int optval = 0;
-						socklen_t size = sizeof (optval);
-						status = getsockopt (socket, SOL_SOCKET, SO_ERROR, &optval, &size);
-						
-						if (0 == status)
-						{
-							if (0 == optval)
-								retval = YES;
-							else
-							{			
-								haveError = YES;
-								strerror_r (optval, message, BXArraySize (message));
-								reason = [NSString stringWithUTF8String: message];
-							}
-						}
-						else
-						{
-							haveError = YES;
-						}
-					}
-				}
-				
-				if (haveError)
-				{
-					NSString* errorTitle = NSLocalizedStringWithDefaultValue (@"connectionError", nil, [NSBundle bundleForClass: [self class]],
-																			  @"Connection error", @"Title for a sheet.");
-					NSString* messageFormat = NSLocalizedStringWithDefaultValue (@"libpqStyleConnectionErrorFormat", nil, [NSBundle bundleForClass: [self class]],
-																				 @"Could not connect to server: %@. Is the server running at \"%@\" and accepting TCP/IP connections on port %s?", 
-																				 @"Reason for error");		
-					if (! reason)
-					{
-						reason = NSLocalizedStringWithDefaultValue (@"connectionRefused", nil, [NSBundle bundleForClass: [self class]],
-																	@"Connection refused", @"Reason for error");
-					}
-					
-					NSString* address = ([mConnectionDictionary objectForKey: kPGTSHostKey] ?: [mConnectionDictionary objectForKey: kPGTSHostAddressKey]);
-					NSString* message = [NSString stringWithFormat: messageFormat, reason, address, PQport (mConnection)];
-					NSDictionary* userInfo = [NSDictionary dictionaryWithObjectsAndKeys:
-											  errorTitle, NSLocalizedDescriptionKey,
-											  errorTitle, NSLocalizedFailureReasonErrorKey,
-											  message, NSLocalizedRecoverySuggestionErrorKey,
-											  nil];		
-					NSError* error = [BXError errorWithDomain: kPGTSConnectionErrorDomain code: kPGTSConnectionErrorUnknown userInfo: userInfo];
-					[self setConnectionError: error];
-				}				
-			}
-		}
-	}
-	
-	return retval;
-}
-
-
-- (void) negotiateConnection
-{
-	BXLogDebug (@"Negotiating.");
-	
-	if (mTraceFile)
-		PQtrace (mConnection, mTraceFile);
-	
-	CFSocketContext context = {0, self, NULL, NULL, NULL};
-	CFSocketCallBackType callbacks = kCFSocketReadCallBack | kCFSocketWriteCallBack;
-	mSocket = CFSocketCreateWithNative (NULL, PQsocket (mConnection), callbacks, &SocketReady, &context);
-	CFOptionFlags flags = 
-	~kCFSocketAutomaticallyReenableReadCallBack &
-	~kCFSocketAutomaticallyReenableWriteCallBack &
-	~kCFSocketCloseOnInvalidate &
-	CFSocketGetSocketFlags (mSocket);
-	
-	CFSocketSetSocketFlags (mSocket, flags);
-	mSocketSource = CFSocketCreateRunLoopSource (NULL, mSocket, 0);
-	
-	BXAssertLog (mSocket, @"Expected source to have been created.");
-	BXAssertLog (CFSocketIsValid (mSocket), @"Expected socket to be valid.");
-	BXAssertLog (mSocketSource, @"Expected socketSource to have been created.");
-	BXAssertLog (CFRunLoopSourceIsValid (mSocketSource), @"Expected socketSource to be valid.");
-	
-	CFSocketDisableCallBacks (mSocket, kCFSocketReadCallBack);
-	CFSocketEnableCallBacks (mSocket, kCFSocketWriteCallBack);
-	mExpectedCallBack = kCFSocketWriteCallBack;
-	CFRunLoopAddSource (mRunLoop, mSocketSource, (CFStringRef) kCFRunLoopCommonModes);
-}
-@end
-
-
-@implementation PGTSSynchronousConnector
-- (BOOL) connect: (NSDictionary *) connectionDictionary
-{
-	//Here libpq can resolve the name for us, because we don't use CFRunLoop and CFSocket.
-
-    BOOL retval = NO;
-	[self prepareForConnect];
-	char* conninfo = CopyConnectionString (connectionDictionary);
-	if ([self start: conninfo] && CONNECTION_BAD != PQstatus (mConnection))
-	{
-		mNegotiationStarted = YES;
-		fd_set mask = {};
-		struct timeval timeout = {.tv_sec = 15, .tv_usec = 0};
-		PostgresPollingStatusType pollingStatus = PGRES_POLLING_WRITING; //Start with this
-		int selectStatus = 0;
-		int bsdSocket = PQsocket (mConnection);
-		BOOL stop = NO;
-		
-		if (mTraceFile)
-			PQtrace (mConnection, mTraceFile);
-		
-		if (bsdSocket < 0)
-			BXLogInfo (@"Unable to get connection socket from libpq.");
-		else
-		{
-			//Polling loop
-			while (1)
-			{
-				struct timeval ltimeout = timeout;
-				FD_ZERO (&mask);
-				FD_SET (bsdSocket, &mask);
-				selectStatus = 0;
-				pollingStatus = mPollFunction (mConnection);
-				
-				BXLogDebug (@"Polling status: %d connection status: %d", pollingStatus, PQstatus (mConnection));
-				
-				[self setUpSSL];
-				
-				switch (pollingStatus)
-				{
-					case PGRES_POLLING_OK:
-						retval = YES;
-						//Fall through.
-					case PGRES_POLLING_FAILED:
-						stop = YES;
-						break;
-						
-					case PGRES_POLLING_ACTIVE:
-						//Select returns 0 on timeout
-						selectStatus = 1;
-						break;
-						
-					case PGRES_POLLING_READING:
-						selectStatus = select (bsdSocket + 1, &mask, NULL, NULL, &ltimeout);
-						break;
-						
-					case PGRES_POLLING_WRITING:
-					default:
-						selectStatus = select (bsdSocket + 1, NULL, &mask, NULL, &ltimeout);
-						break;
-				} //switch
-				
-				if (0 == selectStatus)
-				{
-					//Timeout.
-					break;
-				}
-				else if (selectStatus < 0 || YES == stop)
-				{
-					break;
-				}
-			}			
-		}		
-	}
-	
-	if (conninfo)
-		free (conninfo);
-	[self finishedConnecting: retval && CONNECTION_OK == PQstatus (mConnection)];
-	return retval;
-}
-@end
-
-
-@implementation PGTSSynchronousReconnector
-- (id) init
-{
-    if ((self = [super init]))
-    {
-        mPollFunction = &PQresetPoll;
-    }
-    return self;
-}
-
-- (BOOL) start: (const char *) connectionString
-{
-	return (BOOL) PQresetStart (mConnection);
-}
-@end
-
-
-@implementation PGTSAsynchronousReconnector
-- (id) init
-{
-    if ((self = [super init]))
-    {
-        mPollFunction = &PQresetPoll;
-    }
-    return self;
-}
-
-- (BOOL) start: (const char *) connectionString
-{
-	return (BOOL) PQresetStart (mConnection);
-}
-@end

File Sources/PGTSSynchronousConnector.h

+//
+// PGTSSynchronousConnector.h
+// BaseTen
+//
+// Copyright (C) 2008-2010 Marko Karppinen & Co. LLC.
+//
+// Before using this software, please review the available licensing options
+// by visiting http://www.karppinen.fi/baseten/licensing/ or by contacting
+// us at sales@karppinen.fi. Without an additional license, this software
+// may be distributed only in compliance with the GNU General Public License.
+//
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License, version 2.0,
+// as published by the Free Software Foundation.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+//
+// $Id$
+//
+
+#import <Foundation/Foundation.h>
+#import <BaseTen/PGTSConnector.h>
+
+
+@interface PGTSSynchronousConnector : PGTSConnector
+{
+}
+@end
+
+
+@interface PGTSSynchronousReconnector : PGTSSynchronousConnector
+{
+}
+@end

File Sources/PGTSSynchronousConnector.m

+//
+// PGTSSynchronousConnector.m
+// BaseTen
+//
+// Copyright (C) 2008-2010 Marko Karppinen & Co. LLC.
+//
+// Before using this software, please review the available licensing options
+// by visiting http://www.karppinen.fi/baseten/licensing/ or by contacting
+// us at sales@karppinen.fi. Without an additional license, this software
+// may be distributed only in compliance with the GNU General Public License.
+//
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License, version 2.0,
+// as published by the Free Software Foundation.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+//
+// $Id$
+//
+
+#import "PGTSSynchronousConnector.h"
+#import "BXLogger.h"
+
+
+@implementation PGTSSynchronousConnector
+- (BOOL) connect: (NSDictionary *) connectionDictionary
+{
+	//Here libpq can resolve the name for us, because we don't use CFRunLoop and CFSocket.
+
+    BOOL retval = NO;
+	[self prepareForConnect];
+	char* conninfo = PGTSCopyConnectionString (connectionDictionary);
+	if ([self start: conninfo] && CONNECTION_BAD != PQstatus (mConnection))
+	{
+		mNegotiationStarted = YES;
+		fd_set mask = {};
+		struct timeval timeout = {.tv_sec = 15, .tv_usec = 0};
+		PostgresPollingStatusType pollingStatus = PGRES_POLLING_WRITING; //Start with this
+		int selectStatus = 0;
+		int bsdSocket = PQsocket (mConnection);
+		BOOL stop = NO;
+		
+		if (mTraceFile)
+			PQtrace (mConnection, mTraceFile);
+		
+		if (bsdSocket < 0)
+			BXLogInfo (@"Unable to get connection socket from libpq.");
+		else
+		{
+			//Polling loop
+			while (1)
+			{
+				struct timeval ltimeout = timeout;
+				FD_ZERO (&mask);
+				FD_SET (bsdSocket, &mask);
+				selectStatus = 0;
+				pollingStatus = mPollFunction (mConnection);
+				
+				BXLogDebug (@"Polling status: %d connection status: %d", pollingStatus, PQstatus (mConnection));
+				
+				[self setUpSSL];
+				
+				switch (pollingStatus)
+				{
+					case PGRES_POLLING_OK:
+						retval = YES;
+						//Fall through.
+					case PGRES_POLLING_FAILED:
+						stop = YES;
+						break;
+						
+					case PGRES_POLLING_ACTIVE:
+						//Select returns 0 on timeout
+						selectStatus = 1;
+						break;
+						
+					case PGRES_POLLING_READING:
+						selectStatus = select (bsdSocket + 1, &mask, NULL, NULL, &ltimeout);
+						break;
+						
+					case PGRES_POLLING_WRITING:
+					default:
+						selectStatus = select (bsdSocket + 1, NULL, &mask, NULL, &ltimeout);
+						break;
+				} //switch
+				
+				if (0 == selectStatus)
+				{
+					//Timeout.
+					break;
+				}
+				else if (selectStatus < 0 || YES == stop)
+				{
+					break;
+				}
+			}			
+		}		
+	}
+	
+	if (conninfo)
+		free (conninfo);
+	[self finishedConnecting: retval && CONNECTION_OK == PQstatus (mConnection)];
+	return retval;
+}
+@end
+
+
+@implementation PGTSSynchronousReconnector
+- (id) init
+{
+    if ((self = [super init]))
+    {
+        mPollFunction = &PQresetPoll;
+    }
+    return self;
+}
+
+- (BOOL) start: (const char *) connectionString
+{
+	return (BOOL) PQresetStart (mConnection);
+}
+@end