diff --git a/Demo-Mac/TouchServ.m b/Demo-Mac/TouchServ.m index 597b2c0..5bcb802 100644 --- a/Demo-Mac/TouchServ.m +++ b/Demo-Mac/TouchServ.m @@ -10,6 +10,7 @@ #import #import #import +#import "CollectionUtils.h" #if DEBUG #import "Logging.h" @@ -61,15 +62,28 @@ int main (int argc, const char * argv[]) // Start a listener socket: TDListener* listener = [[TDListener alloc] initWithTDServer: server port: kPortNumber]; - if (argc >= 2 && strcmp(argv[1], "--readonly") == 0) - listener.readOnly = YES; + [listener setBonjourName: @"TouchServ" type: @"_touchdb._tcp."]; + listener.TXTRecordDictionary = $dict({@"Key", + [@"value" dataUsingEncoding: NSUTF8StringEncoding]}); + + for (int i = 1; i < argc; ++i) { + if (strcmp(argv[i], "--readonly") == 0) { + listener.readOnly = YES; + } else if (strcmp(argv[i], "--auth") == 0) { + srandomdev(); + NSString* password = $sprintf(@"%x", random()); + listener.passwords = [NSDictionary dictionaryWithObject: password + forKey: @"touchdb"]; + Log(@"Auth required: user='touchdb', password='%@'", password); + } + } [listener start]; Log(@"TouchServ %@ is listening%@ on port %d ... relax!", [TDRouter versionString], (listener.readOnly ? @" in read-only mode" : @""), - kPortNumber); + listener.port); [[NSRunLoop currentRunLoop] run]; diff --git a/Listener/TDHTTPConnection.m b/Listener/TDHTTPConnection.m index 7b7b22e..d384c5a 100644 --- a/Listener/TDHTTPConnection.m +++ b/Listener/TDHTTPConnection.m @@ -36,6 +36,20 @@ - (TDListener*) listener { } +- (BOOL)isPasswordProtected:(NSString *)path { + return self.listener.requiresAuth; +} + +- (NSString*) realm { + return self.listener.realm; +} + +- (NSString*) passwordForUser: (NSString*)username { + LogTo(TDListener, @"Login attempted for user '%@'", username); + return [self.listener passwordForUser: username]; +} + + - (BOOL)supportsMethod:(NSString *)method atPath:(NSString *)path { return $equal(method, @"POST") || $equal(method, @"PUT") || $equal(method, @"DELETE") || [super supportsMethod: method atPath: path]; diff --git a/Listener/TDListener.h b/Listener/TDListener.h index 3fc21b3..97a6cf3 100644 --- a/Listener/TDListener.h +++ b/Listener/TDListener.h @@ -15,14 +15,54 @@ { TDHTTPServer* _httpServer; TDServer* _tdServer; + NSString* _realm; BOOL _readOnly; + BOOL _requiresAuth; + NSDictionary* _passwords; } +/** Initializes a TDListener. + @param server The TDServer whose databases to serve. + @param port The TCP port number to listen on. Use 0 to automatically pick an available port (you can get the port number after the server starts by getting the .port property.) */ - (id) initWithTDServer: (TDServer*)server port: (UInt16)port; +/** The TCP port number that the listener is listening on. + If the listener has not yet started, this will return 0. */ +@property (readonly) UInt16 port; + + +/** The Bonjour service name and type to advertise as. + @param name The service name; this can be arbitrary but is generally the device user's name. An empty string will be mapped to the device's name. + @param type The service type; the type of a generic HTTP server is "_http._tcp." but you should use something more specific. */ +- (void) setBonjourName: (NSString*)name type: (NSString*)type; + +/** Bonjour metadata associated with the service. Changes will be visible almost immediately. + The keys are NSStrings and values are NSData. Total size should be kept small (under 1kbyte if possible) as this data is multicast over UDP. */ +@property (copy) NSDictionary* TXTRecordDictionary; + + +/** If set to YES, remote requests will not be allowed to make any changes to the server or its databases. */ @property BOOL readOnly; +/** If set to YES, all requests will be required to authenticate. + Setting a .passwords dictionary automatically enables this.*/ +@property BOOL requiresAuth; + +/** Security realm string to return in authentication challenges. */ +@property (copy) NSString* realm; + +/** Sets user names and passwords for authentication. + @param passwords A dictionary mapping user names to passwords. */ +- (void) setPasswords: (NSDictionary*)passwords; + +/** Returns the password assigned to a user name, or nil if the name is not recognized. */ +- (NSString*) passwordForUser: (NSString*)username; + + +/** Starts the listener. */ - (BOOL) start; + +/** Stops the listener. */ - (void) stop; @end diff --git a/Listener/TDListener.m b/Listener/TDListener.m index 9d761cb..b71e228 100644 --- a/Listener/TDListener.m +++ b/Listener/TDListener.m @@ -24,7 +24,7 @@ @implementation TDListener -@synthesize readOnly=_readOnly; +@synthesize readOnly=_readOnly, requiresAuth=_requiresAuth, realm=_realm; - (id) initWithTDServer: (TDServer*)server port: (UInt16)port { @@ -36,6 +36,7 @@ - (id) initWithTDServer: (TDServer*)server port: (UInt16)port { _httpServer.tdServer = _tdServer; _httpServer.port = port; _httpServer.connectionClass = [TDHTTPConnection class]; + self.realm = @"TouchDB"; } return self; } @@ -46,10 +47,22 @@ - (void)dealloc [self stop]; [_tdServer release]; [_httpServer release]; + [_realm release]; + [_passwords release]; [super dealloc]; } +- (void) setBonjourName: (NSString*)name type: (NSString*)type { + _httpServer.name = name; + _httpServer.type = type; +} + +- (NSDictionary *)TXTRecordDictionary {return _httpServer.TXTRecordDictionary;} +- (void)setTXTRecordDictionary:(NSDictionary *)dict {_httpServer.TXTRecordDictionary = dict;} + + + - (BOOL) start { NSError* error; return [_httpServer start: &error]; @@ -60,6 +73,22 @@ - (void) stop { } +- (UInt16) port { + return _httpServer.listeningPort; +} + + +- (void) setPasswords: (NSDictionary*)passwords { + [_passwords autorelease]; + _passwords = [passwords copy]; + _requiresAuth = (_passwords != nil); +} + +- (NSString*) passwordForUser:(NSString *)username { + return [_passwords objectForKey: username]; +} + + @end