Fixed what was likely one of the major causes of Growl-to-Growl local network forwarding failing. Previously, when forwarding was configured, the destination address and port were stored as a preference and used forever-after. That's wrong when destinations aren't on static IPs, which is by far the most common situation.
authorevands
Mon Jul 28 15:23:14 2008 +0000 (2008-07-28)
changeset 4152099dfa400709
parent 4151 66d5cd094a99
child 4153 899d3019a60a
Fixed what was likely one of the major causes of Growl-to-Growl local network forwarding failing. Previously, when forwarding was configured, the destination address and port were stored as a preference and used forever-after. That's wrong when destinations aren't on static IPs, which is by far the most common situation.

We now do a proper resolve to get the IP and port of the advertised Growl TCP service.

(Note: This doesn't update the preference code to not store this information; the address information just won't be used. The network preferences pane could use a lot of work under the hood, as it's clearly not complete.)
Core/Source/GrowlApplicationController.m
     1.1 --- a/Core/Source/GrowlApplicationController.m	Mon Jul 28 00:27:51 2008 +0000
     1.2 +++ b/Core/Source/GrowlApplicationController.m	Mon Jul 28 15:23:14 2008 +0000
     1.3 @@ -346,19 +346,65 @@
     1.4  	[pool release];
     1.5  }
     1.6  
     1.7 +/*!
     1.8 + * @brief Get address data for a Growl server
     1.9 + *
    1.10 + * @param name The name of the server
    1.11 + * @result An NSData which contains a (struct sockaddr *)'s data. This may actually be a sockaddr_in or a sockaddr_in6.
    1.12 + */
    1.13 +- (NSData *)addressDataForGrowlServerWithName:(NSString *)name
    1.14 +{
    1.15 +	NSNetService *service = [[[NSNetService alloc] initWithDomain:@"local." type:@"_growl._tcp." name:name] autorelease];
    1.16 +    if (!service) {
    1.17 +		/* No such service exists. The computer is probably offline. */
    1.18 +        return nil;
    1.19 +    }
    1.20 +
    1.21 +	/* Work for 8 seconds to resolve the net service to an IP and port. We should be running
    1.22 +	 * on a thread, so blocking is fine.
    1.23 +	 */
    1.24 +    [service scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:@"PrivateGrowlMode"];
    1.25 +    [service resolveWithTimeout:8.0];
    1.26 +    CFAbsoluteTime deadline = CFAbsoluteTimeGetCurrent() + 8.0;
    1.27 +    CFTimeInterval remaining;
    1.28 +    while ((remaining = (deadline - CFAbsoluteTimeGetCurrent())) > 0 && [[service addresses] count] == 0) {
    1.29 +        CFRunLoopRunInMode((CFStringRef)@"PrivateGrowlMode", remaining, true);
    1.30 +    }
    1.31 +    [service stop];
    1.32 +
    1.33 +    NSArray *addresses = [service addresses];
    1.34 +    if (![addresses count]) {
    1.35 +		/* Lookup failed */
    1.36 +        return nil;
    1.37 +    }
    1.38 +
    1.39 +	return [addresses objectAtIndex:0];
    1.40 +}	
    1.41 +
    1.42  - (void) forwardDictionary:(NSDictionary *)dict withSelector:(SEL)forwardMethod {
    1.43  	NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    1.44  
    1.45  	NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    1.46  	NSNumber *requestTimeout = [defaults objectForKey:@"ForwardingRequestTimeout"];
    1.47  	NSNumber *replyTimeout = [defaults objectForKey:@"ForwardingReplyTimeout"];
    1.48 -
    1.49  	NSEnumerator *enumerator = [destinations objectEnumerator];
    1.50  	NSDictionary *entry;
    1.51  	while ((entry = [enumerator nextObject])) {
    1.52  		if (getBooleanForKey(entry, @"use") && getBooleanForKey(entry, @"active")) {
    1.53 -			NSData *destAddress = [entry objectForKey:@"address"];
    1.54 +			/* Note: This assumes that all forwarding destinations are within the local network.
    1.55 +			 * When domain names and IPs can be used, this needs to change.
    1.56 +			 */
    1.57 +			NSData *destAddress = [self addressDataForGrowlServerWithName:[entry objectForKey:@"computer"]];
    1.58 +			if (!destAddress) {
    1.59 +				/* No destination address. Nothing to see here; move along. */
    1.60 +#ifdef DEBUG
    1.61 +				NSLog(@"Could not obtain destination address for %@", [entry objectForKey:@"computer"]);
    1.62 +#endif
    1.63 +				continue;
    1.64 +			}
    1.65  			NSString *password = [entry objectForKey:@"password"];
    1.66 +
    1.67 +			/* Send via DO if possible */
    1.68  			NSSocketPort *serverPort = [[NSSocketPort alloc]
    1.69  				initRemoteWithProtocolFamily:AF_INET
    1.70  								  socketType:SOCK_STREAM
    1.71 @@ -644,6 +690,7 @@
    1.72  				if (err == noErr) {
    1.73  					err = SystemSoundSetCompletionRoutine(actionID, CFRunLoopGetCurrent(), /*runLoopMode*/ NULL, soundCompletionCallback, /*refcon*/ NULL);
    1.74  					SystemSoundPlay(actionID);
    1.75 +					userInfo = nil;
    1.76  				} else {
    1.77  					userInfo = [NSDictionary dictionaryWithObjectsAndKeys:
    1.78  						[NSString stringWithFormat:NSLocalizedString(@"Could not load and play sound file named \"%@\": %s", /*comment*/ nil), soundName, GetMacOSStatusCommentString(err)], NSLocalizedDescriptionKey,