--- /dev/null
+/*
+ * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+
+ Change History (most recent first):
+
+$Log: BrowserController.m,v $
+Revision 1.18 2003/08/12 19:55:07 cheshire
+Update to APSL 2.0
+
+ */
+
+#import "BrowserController.h"
+
+#include "arpa/inet.h"
+
+void
+MyHandleMachMessage ( CFMachPortRef port, void * msg, CFIndex size, void * info )
+{
+ DNSServiceDiscovery_handleReply(msg);
+}
+
+void browse_reply (
+ DNSServiceBrowserReplyResultType resultType, // One of DNSServiceBrowserReplyResultType
+ const char *replyName,
+ const char *replyType,
+ const char *replyDomain,
+ DNSServiceDiscoveryReplyFlags flags, // DNS Service Discovery reply flags information
+ void *context
+ )
+{
+ [[NSApp delegate] updateBrowseWithResult:resultType name:[NSString stringWithUTF8String:replyName] type:[NSString stringWithUTF8String:replyType] domain:[NSString stringWithUTF8String:replyDomain] flags:flags];
+ return;
+}
+
+void enum_reply (
+ DNSServiceDomainEnumerationReplyResultType resultType,
+ const char *replyDomain,
+ DNSServiceDiscoveryReplyFlags flags,
+ void *context
+ )
+{
+ [[NSApp delegate] updateEnumWithResult:resultType domain:[NSString stringWithUTF8String:replyDomain] flags:flags];
+
+ return;
+}
+
+void resolve_reply (
+ struct sockaddr *interface,
+ struct sockaddr *address,
+ const char *txtRecord,
+ DNSServiceDiscoveryReplyFlags flags,
+ void *context
+ )
+{
+ [[NSApp delegate] resolveClientWithInterface:interface address:address txtRecord:[NSString stringWithUTF8String:txtRecord]];
+
+ return;
+}
+
+@implementation BrowserController //Begin implementation of BrowserController methods
+
+- (void)registerDefaults
+{
+ NSMutableDictionary *regDict = [NSMutableDictionary dictionary];
+
+ NSArray *typeArray = [NSArray arrayWithObjects:@"_ftp._tcp.", @"_tftp._tcp.",
+ @"_ssh._tcp.", @"_telnet._tcp.",
+ @"_http._tcp.",
+ @"_printer._tcp.", @"_ipp._tcp.",
+ @"_ichat._tcp.", @"_eppc._tcp.",
+ @"_afpovertcp._tcp.", @"_afpovertcp._tcp.", @"_MacOSXDupSuppress._tcp.", nil];
+ NSArray *nameArray = [NSArray arrayWithObjects:@"File Transfer (ftp)", @"Trivial File Transfer (tftp)",
+ @"Secure Shell (ssh)", @"Telnet",
+ @"Web Server (http)",
+ @"LPR Printer", @"IPP Printer",
+ @"iChat", @"Remote AppleEvents",
+ @"AppleShare Server", @"SMB File Server", @"Mystery Service", nil];
+
+ [regDict setObject:typeArray forKey:@"SrvTypeKeys"];
+ [regDict setObject:nameArray forKey:@"SrvNameKeys"];
+
+ [[NSUserDefaults standardUserDefaults] registerDefaults:regDict];
+}
+
+
+- (id)init
+{
+ [self registerDefaults];
+
+ browse_client = nil;
+
+ return [super init];
+}
+
+- (void)awakeFromNib //BrowserController startup procedure
+{
+ SrvType=NULL;
+ Domain=NULL;
+ srvtypeKeys = [NSMutableArray array]; //Define arrays for Type, Domain, and Name
+ srvnameKeys = [NSMutableArray array];
+
+ domainKeys = [NSMutableArray array];
+ [domainKeys retain];
+
+ nameKeys = [NSMutableArray array];
+ [nameKeys retain];
+
+ [srvtypeKeys retain]; //Keep arrays in memory until BrowserController closes
+ [srvnameKeys retain]; //Keep arrays in memory until BrowserController closes
+ [typeField setDataSource:self]; //Set application fields' data source to BrowserController
+ [typeField sizeLastColumnToFit]; //and set column sizes to use their whole table's width.
+ [nameField setDataSource:self];
+ [nameField sizeLastColumnToFit];
+ [domainField setDataSource:self];
+ [domainField sizeLastColumnToFit];
+
+ [nameField setDoubleAction:@selector(connect:)];
+
+ //[srvtypeKeys addObject:@"_ftp._tcp."]; //Add supported protocols and domains to their
+ //[srvnameKeys addObject:@"File Transfer (ftp)"];
+ //[srvtypeKeys addObject:@"_printer._tcp."]; //respective arrays
+ //[srvnameKeys addObject:@"Printer (lpr)"];
+ //[srvtypeKeys addObject:@"_http._tcp."]; //respective arrays
+ //[srvnameKeys addObject:@"Web Server (http)"];
+ //[srvtypeKeys addObject:@"_afp._tcp."]; //respective arrays
+ //[srvnameKeys addObject:@"AppleShare Server (afp)"];
+
+ [ipAddressField setStringValue:@""];
+ [portField setStringValue:@""];
+ [textField setStringValue:@""];
+
+ [srvtypeKeys addObjectsFromArray:[[NSUserDefaults standardUserDefaults] arrayForKey:@"SrvTypeKeys"]];
+ [srvnameKeys addObjectsFromArray:[[NSUserDefaults standardUserDefaults] arrayForKey:@"SrvNameKeys"]];
+
+
+ [typeField reloadData]; //Reload (redraw) data in fields
+ [domainField reloadData];
+
+ [self loadDomains:self];
+
+}
+
+- (void)dealloc //Deallocation method
+{
+ [srvtypeKeys release];
+ [srvnameKeys release];
+ [nameKeys release];
+ [domainKeys release];
+}
+
+-(void)tableView:(NSTableView *)theTableView setObjectValue:(id)object forTableColumn:(NSTableColumn *)tableColumn row:(int)row
+{
+ if (row<0) return;
+}
+
+- (int)numberOfRowsInTableView:(NSTableView *)theTableView //Begin mandatory TableView methods
+{
+ if (theTableView == typeField)
+ {
+ return [srvnameKeys count];
+ }
+ if (theTableView == domainField)
+ {
+ return [domainKeys count];
+ }
+ if (theTableView == nameField)
+ {
+ return [nameKeys count];
+ }
+ if (theTableView == serviceDisplayTable)
+ {
+ return [srvnameKeys count];
+ }
+ return 0;
+}
+
+- (id)tableView:(NSTableView *)theTableView objectValueForTableColumn:(NSTableColumn *)theColumn row:(int)rowIndex
+{
+ if (theTableView == typeField)
+ {
+ return [srvnameKeys objectAtIndex:rowIndex];
+ }
+ if (theTableView == domainField)
+ {
+ return [domainKeys objectAtIndex:rowIndex];
+ }
+ if (theTableView == nameField)
+ {
+ return [[nameKeys sortedArrayUsingSelector:@selector(compare:)] objectAtIndex:rowIndex];
+ }
+ if (theTableView == serviceDisplayTable)
+ {
+ if (theColumn == typeColumn) {
+ return [srvtypeKeys objectAtIndex:rowIndex];
+ }
+ if (theColumn == nameColumn) {
+ return [srvnameKeys objectAtIndex:rowIndex];
+ }
+ return 0;
+ }
+ else
+ return(0);
+} //End of mandatory TableView methods
+
+- (IBAction)handleTypeClick:(id)sender //Handle clicks for Type
+{
+ int index=[sender selectedRow]; //Find index of selected row
+ if (index==-1) return; //Error checking
+ SrvType = [srvtypeKeys objectAtIndex:index]; //Save desired Type
+ SrvName = [srvnameKeys objectAtIndex:index]; //Save desired Type
+
+ [ipAddressField setStringValue:@""];
+ [portField setStringValue:@""];
+ [textField setStringValue:@""];
+
+ [self update:SrvType Domain:Domain]; //If Type and Domain are set, update records
+}
+
+- (IBAction)handleDomainClick:(id)sender //Handle clicks for Domain
+{
+ int index=[sender selectedRow]; //Find index of selected row
+ if (index==-1) return; //Error checking
+ Domain = [domainKeys objectAtIndex:index]; //Save desired Domain
+
+ [ipAddressField setStringValue:@""];
+ [portField setStringValue:@""];
+ [textField setStringValue:@""];
+
+ if (SrvType!=NULL) [self update:SrvType Domain:Domain]; //If Type and Domain are set, update records
+}
+
+- (IBAction)handleNameClick:(id)sender //Handle clicks for Name
+{
+ int index=[sender selectedRow]; //Find index of selected row
+ if (index==-1) return; //Error checking
+ Name=[[nameKeys sortedArrayUsingSelector:@selector(compare:)] objectAtIndex:index]; //Save desired name
+
+ {
+ CFMachPortRef cfMachPort;
+ CFMachPortContext context;
+ Boolean shouldFreeInfo;
+ dns_service_discovery_ref dns_client;
+ mach_port_t port;
+ CFRunLoopSourceRef rls;
+
+ context.version = 1;
+ context.info = 0;
+ context.retain = NULL;
+ context.release = NULL;
+ context.copyDescription = NULL;
+
+ [ipAddressField setStringValue:@"?"];
+ [portField setStringValue:@"?"];
+ [textField setStringValue:@"?"];
+ // start an enumerator on the local server
+ dns_client = DNSServiceResolverResolve
+ (
+ (char *)[Name UTF8String],
+ (char *)[SrvType UTF8String],
+ (char *)(Domain?[Domain UTF8String]:""),
+ resolve_reply,
+ nil
+ );
+
+ port = DNSServiceDiscoveryMachPort(dns_client);
+
+ if (port) {
+ cfMachPort = CFMachPortCreateWithPort ( kCFAllocatorDefault, port, ( CFMachPortCallBack ) MyHandleMachMessage,&context,&shouldFreeInfo );
+
+ /* Create and add a run loop source for the port */
+ rls = CFMachPortCreateRunLoopSource(NULL, cfMachPort, 0);
+ CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
+ CFRelease(rls);
+ } else {
+ printf("Could not obtain client port\n");
+ return;
+ }
+ }
+}
+
+- (IBAction)loadDomains:(id)sender
+{
+ CFMachPortRef cfMachPort;
+ CFMachPortContext context;
+ Boolean shouldFreeInfo;
+ dns_service_discovery_ref dns_client;
+ mach_port_t port;
+ CFRunLoopSourceRef rls;
+
+ context.version = 1;
+ context.info = 0;
+ context.retain = NULL;
+ context.release = NULL;
+ context.copyDescription = NULL;
+
+ // start an enumerator on the local server
+ dns_client = DNSServiceDomainEnumerationCreate
+ (
+ 0,
+ enum_reply,
+ nil
+ );
+
+ port = DNSServiceDiscoveryMachPort(dns_client);
+
+ if (port) {
+ cfMachPort = CFMachPortCreateWithPort ( kCFAllocatorDefault, port, ( CFMachPortCallBack ) MyHandleMachMessage,&context,&shouldFreeInfo );
+
+ /* Create and add a run loop source for the port */
+ rls = CFMachPortCreateRunLoopSource(NULL, cfMachPort, 0);
+ CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
+ CFRelease(rls);
+ } else {
+ printf("Could not obtain client port\n");
+ return;
+ }
+}
+
+- (IBAction)update:theType Domain:theDomain; //The Big Kahuna: Fetch PTR records and update application
+{
+ const char * DomainC;
+ const char * TypeC=[theType UTF8String]; //Type in C string format
+
+ if (theDomain) {
+ DomainC = [theDomain UTF8String]; //Domain in C string format
+ } else {
+ DomainC = "";
+ }
+
+ [nameKeys removeAllObjects]; //Get rid of displayed records if we're going to go get new ones
+ [nameField reloadData]; //Reload (redraw) names to show the old data is gone
+
+ // get rid of the previous browser if one exists
+ if (browse_client) {
+ DNSServiceDiscoveryDeallocate(browse_client);
+ browse_client = nil;
+ }
+
+ // now create a browser to return the values for the nameField ...
+ {
+ CFMachPortRef cfMachPort;
+ CFMachPortContext context;
+ Boolean shouldFreeInfo;
+ mach_port_t port;
+ CFRunLoopSourceRef rls;
+
+ context.version = 1;
+ context.info = 0;
+ context.retain = NULL;
+ context.release = NULL;
+ context.copyDescription = NULL;
+
+ // start an enumerator on the local server
+ browse_client = DNSServiceBrowserCreate
+ (
+ (char *)TypeC,
+ (char *)DomainC,
+ browse_reply,
+ nil
+ );
+
+ port = DNSServiceDiscoveryMachPort(browse_client);
+
+ if (port) {
+ cfMachPort = CFMachPortCreateWithPort ( kCFAllocatorDefault, port, ( CFMachPortCallBack ) MyHandleMachMessage,&context,&shouldFreeInfo );
+
+ /* Create and add a run loop source for the port */
+ rls = CFMachPortCreateRunLoopSource(NULL, cfMachPort, 0);
+ CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
+ CFRelease(rls);
+ } else {
+ printf("Could not obtain client port\n");
+ return;
+ }
+ }
+
+}
+
+
+- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)theApplication //Quit when main window is closed
+{
+ return YES;
+}
+
+- (BOOL)windowShouldClose:(NSWindow *)sender //Save domains to our domain file when quitting
+{
+ [domainField reloadData];
+ return YES;
+}
+
+- (void)updateEnumWithResult:(int)resultType domain:(NSString *)domain flags:(int)flags
+{
+ // new domain received
+ if (DNSServiceDomainEnumerationReplyAddDomain == resultType || DNSServiceDomainEnumerationReplyAddDomainDefault == resultType) {
+ // add the domain to the list
+ [domainKeys addObject:domain];
+ } else {
+ // remove the domain from the list
+ NSEnumerator *dmnEnum = [domainKeys objectEnumerator];
+ NSString *aDomain = nil;
+
+ while (aDomain = [dmnEnum nextObject]) {
+ if ([aDomain isEqualToString:domain]) {
+ [domainKeys removeObject:domain];
+ break;
+ }
+ }
+ }
+ // update the domain table
+ [domainField reloadData];
+ return;
+}
+
+
+
+- (void)updateBrowseWithResult:(int)type name:(NSString *)name type:(NSString *)resulttype domain:(NSString *)domain flags:(int)flags
+{
+
+ //NSLog(@"Received result %@ %@ %@ %d", name, resulttype, domain, type);
+
+ if (([domain isEqualToString:Domain] || [domain isEqualToString:@"local."]) && [resulttype isEqualToString:SrvType]) {
+
+ if (type == DNSServiceBrowserReplyRemoveInstance) {
+ if ([nameKeys containsObject:name]) {
+ [nameKeys removeObject:name];
+ }
+ }
+ if (type == DNSServiceBrowserReplyAddInstance) {
+ if (![nameKeys containsObject:name]) {
+ [nameKeys addObject:name];
+ }
+ }
+
+ // If not expecting any more data, then reload (redraw) Name TableView with newly found data
+ if ((flags & kDNSServiceDiscoveryMoreRepliesImmediately) == 0)
+ [nameField reloadData];
+ }
+ return;
+}
+
+- (void)resolveClientWithInterface:(struct sockaddr *)interface address:(struct sockaddr *)address txtRecord:(NSString *)txtRecord
+{
+ if (address->sa_family != AF_INET) return; // For now we only handle IPv4
+ //printf("interface length = %d, port = %d, family = %d, address = %s\n", ((struct sockaddr_in *)interface)->sin_len, ((struct sockaddr_in *)interface)->sin_port, ((struct sockaddr_in *)interface)->sin_family, inet_ntoa(((struct in_addr)((struct sockaddr_in *)interface)->sin_addr)));
+ //printf("address length = %d, port = %d, family = %d, address = %s\n", ((struct sockaddr_in *)address)->sin_len, ((struct sockaddr_in *)address)->sin_port, ((struct sockaddr_in *)address)->sin_family, inet_ntoa(((struct in_addr)((struct sockaddr_in *)address)->sin_addr)));
+ NSString *ipAddr = [NSString stringWithCString:inet_ntoa(((struct in_addr)((struct sockaddr_in *)address)->sin_addr))];
+ int port = ((struct sockaddr_in *)address)->sin_port;
+
+ [ipAddressField setStringValue:ipAddr];
+ [portField setIntValue:port];
+ [textField setStringValue:txtRecord];
+
+ return;
+}
+
+- (void)connect:(id)sender
+{
+ NSString *ipAddr = [ipAddressField stringValue];
+ int port = [portField intValue];
+ NSString *txtRecord = [textField stringValue];
+
+ if (!txtRecord) txtRecord = @"";
+
+ if (!ipAddr || !port) return;
+
+ if ([SrvType isEqualToString:@"_ftp._tcp."]) [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"ftp://%@:%d/", ipAddr, port]]];
+ else if ([SrvType isEqualToString:@"_tftp._tcp."]) [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"tftp://%@:%d/", ipAddr, port]]];
+ else if ([SrvType isEqualToString:@"_ssh._tcp."]) [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"ssh://%@:%d/", ipAddr, port]]];
+ else if ([SrvType isEqualToString:@"_telnet._tcp."]) [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"telnet://%@:%d/", ipAddr, port]]];
+ else if ([SrvType isEqualToString:@"_http._tcp."]) [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"http://%@:%d", ipAddr, port]]];
+ else if ([SrvType isEqualToString:@"_printer._tcp."]) [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"lpr://%@:%d/", ipAddr, port]]];
+ else if ([SrvType isEqualToString:@"_ipp._tcp."]) [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"ipp://%@:%d/", ipAddr, port]]];
+ else if ([SrvType isEqualToString:@"_afpovertcp._tcp."]) [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"afp://%@:%d/", ipAddr, port]]];
+ else if ([SrvType isEqualToString:@"_smb._tcp."]) [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"smb://%@:%d/", ipAddr, port]]];
+
+ return;
+}
+
+- (IBAction)handleTableClick:(id)sender
+{
+ //populate the text fields
+}
+
+- (IBAction)removeSelected:(id)sender
+{
+ // remove the selected row and force a refresh
+
+ int selectedRow = [serviceDisplayTable selectedRow];
+
+ if (selectedRow) {
+
+ [srvtypeKeys removeObjectAtIndex:selectedRow];
+ [srvnameKeys removeObjectAtIndex:selectedRow];
+
+ [[NSUserDefaults standardUserDefaults] setObject:srvtypeKeys forKey:@"SrvTypeKeys"];
+ [[NSUserDefaults standardUserDefaults] setObject:srvnameKeys forKey:@"SrvNameKeys"];
+
+ [typeField reloadData];
+ [serviceDisplayTable reloadData];
+ }
+}
+
+- (IBAction)addNewService:(id)sender
+{
+ // add new entries from the edit fields to the arrays for the defaults
+
+ if ([[serviceTypeField stringValue] length] && [[serviceNameField stringValue] length]) {
+ [srvtypeKeys addObject:[serviceTypeField stringValue]];
+ [srvnameKeys addObject:[serviceNameField stringValue]];
+
+ [[NSUserDefaults standardUserDefaults] setObject:srvtypeKeys forKey:@"SrvTypeKeys"];
+ [[NSUserDefaults standardUserDefaults] setObject:srvnameKeys forKey:@"SrvNameKeys"];
+
+ [typeField reloadData];
+ [serviceDisplayTable reloadData];
+ }
+
+}
+
+
+
+@end
\ No newline at end of file