]> git.saurik.com Git - apple/mdnsresponder.git/blobdiff - mDNSMacOSX/Applications/DNSServiceBrowser/BrowserController.m
mDNSResponder-58.tar.gz
[apple/mdnsresponder.git] / mDNSMacOSX / Applications / DNSServiceBrowser / BrowserController.m
diff --git a/mDNSMacOSX/Applications/DNSServiceBrowser/BrowserController.m b/mDNSMacOSX/Applications/DNSServiceBrowser/BrowserController.m
new file mode 100755 (executable)
index 0000000..1dbe2a4
--- /dev/null
@@ -0,0 +1,541 @@
+/*
+ * 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