//
//  GrowlMailMeDisplay.m
//  Growl Display Plugins
//
//  Copyright 2004 Peter Hosey. All rights reserved.
//
#import "GrowlMailMeDisplay.h"
#import "GrowlMailMePrefs.h"
#import "GrowlDefinesInternal.h"
#import "GrowlApplicationNotification.h"

#define destAddressKey @"MailMe - Recipient address"

/* for when there is no icon */
#define plainTextMessageFormat @"%@\r\n"\
	@"-- This message was automatically generated by MailMe, a Growl plug-in, --\r\n"\
	@"-- in response to a Growl notification --\r\n"\
	@"-- http://growl.info/ --\r\n"

#define MAIL_BUNDLE_IDENTIFIER @"com.apple.mail"

@interface GrowlMailMeDisplay ()

- (NSDictionary *) defaultSMTPAccountFromMail;
- (void) workspaceDidTerminateApplication:(NSNotification *)notification;

@end


@implementation GrowlMailMeDisplay

- (id) init {
	if ((self = [super init])) {
		pathToMailSenderProgram = [[[NSBundle bundleForClass:[self class]] pathForResource:@"simple-mailer" ofType:@"py"] copy];

		NSWorkspace *wksp = [NSWorkspace sharedWorkspace];
		[[wksp notificationCenter] addObserver:self
									  selector:@selector(workspaceDidTerminateApplication:)
										  name:NSWorkspaceDidTerminateApplicationNotification
										object:wksp];

		tasks = [[NSMutableArray alloc] init];
	}
	return self;
}

- (void) dealloc {
	[defaultSMTPAccount release];
	[fromAddress release];
	[tasks release];
	[pathToMailSenderProgram release];

	[preferencePane release];
	[super dealloc];
}

- (NSPreferencePane *) preferencePane {
	if (!preferencePane)
		preferencePane = [[GrowlMailMePrefs alloc] initWithBundle:[NSBundle bundleWithIdentifier:@"com.Growl.MailMe"]];
	return preferencePane;
}

- (void) displayNotification:(GrowlApplicationNotification *)notification {
	NSString *destAddress = nil;
	READ_GROWL_PREF_VALUE(destAddressKey, @"com.Growl.MailMe", NSString *, &destAddress);
	[NSMakeCollectable(destAddress) autorelease];
	NSDictionary *noteDict = [notification dictionaryRepresentation];

	if (destAddress) {
		if([destAddress length]) {
			NSString *title = [noteDict objectForKey:GROWL_NOTIFICATION_TITLE];
			NSString *desc = [noteDict objectForKey:GROWL_NOTIFICATION_DESCRIPTION];
			//hopefully something can be worked out to use the imageData.
			//One solution would be an easy way to attach at least one file in the simple-mailer program. Another would be a way to create a MIME multi-part message and to have simple-mailer handle it correctly.
			//	NSData *imageData = [noteDict objectForKey:GROWL_NOTIFICATION_ICON];

			if (!defaultSMTPAccount) {
				defaultSMTPAccount = [[self defaultSMTPAccountFromMail] retain];
				if (!defaultSMTPAccount) {
					//We can't do anything without an account.
					NSLog(@"MailMe: No suitable SMTP account found");
					return;
				}
			}

			BOOL useTLS = [[defaultSMTPAccount objectForKey:@"SSLEnabled"] boolValue];
			NSString *username = [defaultSMTPAccount objectForKey:@"Username"];
			NSString *hostname = [defaultSMTPAccount objectForKey:@"Hostname"];
			NSNumber *port = [defaultSMTPAccount objectForKey:@"PortNumber"];
			NSString *userAtHostPort = [NSString stringWithFormat:
				(port != nil) ? @"%@@%@:%@" : @"%@@%@",
				username, hostname, port];

			BOOL success = NO;

			OSStatus err;
			UInt32 passwordLength = 0U;
			void *passwordBytes = NULL;
			err = SecKeychainFindInternetPassword(/*keychainOrArray*/ NULL,
				(UInt32)[hostname length], [hostname UTF8String],
				/*securityDomainLength*/ 0U, /*securityDomain*/ NULL,
				(UInt32)[username length], [username UTF8String],
				/*pathLength*/ 0U, /*path*/ NULL,
				(UInt16)[port integerValue],
				kSecProtocolTypeSMTP, kSecAuthenticationTypeAny,
				&passwordLength, &passwordBytes,
				/*itemRef*/ NULL);
			if (err != noErr) {
				NSLog(@"MailMe: Could not get password for SMTP account %@: %i/%s", userAtHostPort, (int)err, GetMacOSStatusCommentString(err));
			} else {
				NSData *passwordData = [NSData dataWithBytesNoCopy:passwordBytes length:passwordLength freeWhenDone:NO];

				//Use only stock Python and matching modules.
				NSDictionary *environment = [NSDictionary dictionaryWithObjectsAndKeys:
					@"", @"PYTHONPATH",
					@"/bin:/usr/bin:/usr/local/bin", @"PATH",
					nil];
				NSTask *task = [[[NSTask alloc] init] autorelease];
				[task setEnvironment:environment];
				[task setLaunchPath:@"/usr/bin/python"];
				[task setArguments:[NSArray arrayWithObjects:
					pathToMailSenderProgram,
					[@"--user-agent=" stringByAppendingFormat:@"Growl/%@", [[[NSBundle mainBundle] infoDictionary] objectForKey:(NSString *)kCFBundleVersionKey]],
					useTLS ? @"--tls" : @"--no-tls",
					userAtHostPort,
					fromAddress,
					destAddress,
					/*subject*/ title,
					nil]];
				NSPipe *stdinPipe = [NSPipe pipe];
				[task setStandardInput:stdinPipe];

				[task launch];

				[[stdinPipe fileHandleForReading] closeFile];
				NSFileHandle *stdinFH = [stdinPipe fileHandleForWriting];
				[stdinFH writeData:passwordData];
				[stdinFH writeData:[@"\n" dataUsingEncoding:NSUTF8StringEncoding]];
				[stdinFH writeData:[[NSString stringWithFormat:plainTextMessageFormat, desc] dataUsingEncoding:NSUTF8StringEncoding]];
				[stdinFH closeFile];

				[task waitUntilExit];
				success = ([task terminationStatus] == 0);

				SecKeychainItemFreeContent(/*attrList*/ NULL, passwordBytes);
			}

			if (!success) {
				NSLog(@"(MailMe) WARNING: Could not send email message \"%@\" to address %@", title, destAddress);
				NSLog(@"(MailMe) description of notification:\n%@", desc);
			} else
				NSLog(@"(MailMe) Successfully sent message \"%@\" to address %@", title, destAddress);
		} else {
			NSLog(@"(MailMe) WARNING: No destination address set");
		}
	} else {
		NSLog(@"(MailMe) WARNING: No destination address set");
	}

	id clickContext = [noteDict objectForKey:GROWL_NOTIFICATION_CLICK_CONTEXT];
	if (clickContext) {
		NSDictionary *userInfo = [[NSDictionary alloc] initWithObjectsAndKeys:
			[noteDict objectForKey:@"ClickHandlerEnabled"], @"ClickHandlerEnabled",
			clickContext,                                   GROWL_KEY_CLICKED_CONTEXT,
			[noteDict objectForKey:GROWL_APP_PID],          GROWL_APP_PID,
			nil];
		[[NSNotificationCenter defaultCenter] postNotificationName:GROWL_NOTIFICATION_TIMED_OUT
															object:[notification applicationName]
														  userInfo:userInfo];
		[userInfo release];
	}
}

- (BOOL) requiresPositioning {
	return NO;
}

- (NSDictionary *) defaultSMTPAccountFromMail {
	NSDictionary *viableAccount = nil;

	NSArray *deliveryAccounts = [NSMakeCollectable(CFPreferencesCopyAppValue(CFSTR("DeliveryAccounts"), CFSTR("com.apple.Mail"))) autorelease];
	if (!deliveryAccounts)
		return viableAccount;

	NSArray *mailAccounts = [NSMakeCollectable(CFPreferencesCopyAppValue(CFSTR("MailAccounts"), CFSTR("com.apple.Mail"))) autorelease];
	if (!mailAccounts)
		return viableAccount;

	NSMutableDictionary *deliveryAccountsBySMTPIdentifier = [NSMutableDictionary dictionaryWithCapacity:[deliveryAccounts count]];
	for (NSDictionary *account in deliveryAccounts) {
		NSString *identifier = [NSString stringWithFormat:@"%@:%@", [account objectForKey:@"Hostname"], [account objectForKey:@"Username"]];
		[deliveryAccountsBySMTPIdentifier setObject:account
											 forKey:identifier];
	}
	for (NSDictionary *account in mailAccounts) {
		NSString *identifier = [account objectForKey:@"SMTPIdentifier"];
		if (!identifier)
			continue;

		viableAccount = [deliveryAccountsBySMTPIdentifier objectForKey:identifier];
		if (viableAccount) {
			NSString *bareAddress = [[account objectForKey:@"EmailAddresses"] objectAtIndex:0UL];
			NSString *name = [account objectForKey:@"FullUserName"];
			fromAddress = [(name ? [NSString stringWithFormat:@"%@ <%@>", name, bareAddress] : bareAddress) copy];
			if (!fromAddress) {
				viableAccount = nil;
			}

			NSNumber *portNumber = [viableAccount objectForKey:@"PortNumber"];
			if (portNumber) {
				NSInteger port = [portNumber integerValue];
				if (port <= 0 || port > 0xFFFF) {
					viableAccount = nil;
				}
			}

			if (viableAccount) {
				break;
			}
		}
	}

	return viableAccount;
}

- (void) workspaceDidTerminateApplication:(NSNotification *)notification {
	NSString *bundleID = [[notification userInfo] objectForKey:@"NSApplicationBundleIdentifier"];
	if ([bundleID isEqualToString:MAIL_BUNDLE_IDENTIFIER]) {
		//Clear out our knowledge of the default Mail account, in case it's changed.
		[defaultSMTPAccount release];
		defaultSMTPAccount = nil;
	}
}

@end
