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.)
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,