#import "BPMailSender.h" // for applescript stuff #import #import "BPDocumentViewManager.h" #import "BPMailAppData.h" #import "BPPilesDataController.h" #import "BPEmailDocument.h" #import "BPPerson.h" #import "BPDocument.h" #import "BPDocumentGroup.h" #import "BPDocumentViewer.h" #import "BPAttachment.h" @implementation BPMailSender - (id)init { self = [super init]; if (self) { myComponent = OpenDefaultComponent(kOSAComponentType, kOSAGenericScriptingComponentSubtype); // other initialization code here } return self; } - (IBAction)writeNewEmail:(id)sender { [self writeNewEmailWithSubject:@"" body:@"" to:[NSArray array] cc:[NSArray array] attachments:[NSArray array]]; } - (void)writeNewEmailWithSubject:(NSString *)subject body:(NSString *)body to:(NSArray *)toPersons cc:(NSArray *)ccPersons attachments:(NSArray *)attachments { // make document representing this new email BPEmailDocument *newEmail = [[BPEmailDocument alloc] initWithSender:[BPPerson personThatsMe] date:[NSDate date] to:toPersons replyTo:[BPPerson personThatsMe] cc:ccPersons subject:subject body:body attachments:attachments]; [newEmail autorelease]; // set icon //NSImage *img = [NSImage imageNamed:@"email_draft"]; //[newEmail setIcon:img]; // make document editable [newEmail setEditable:YES]; // open the new email editing window [docViewManager openStandaloneViewerForDocument:newEmail]; } - (NSArray *)ccRecipientsForReplyAllForEmail:(BPEmailDocument *)email { // return empty list if there's only 1 "To" recipients and no "CC"s if ([[email toRecipients] count] == 1 && [[email ccRecipients] count] == 0) return [NSArray array]; // make list of To + CC recipients (when adding ccRecipients ensure there are no duplicate adds) NSMutableArray *allRecipients = [NSMutableArray arrayWithArray:[email toRecipients]]; BPPerson *person; int i; for (i=0; i<[[email ccRecipients] count]; i++) { person = [[email ccRecipients] objectAtIndex:i]; if (![allRecipients containsObject:person]) [allRecipients addObject:person]; } //NSLog(@"allRecipients %@", allRecipients); // remove original sender and me from the list [allRecipients removeObject:[email author]]; [allRecipients removeObject:[BPPerson personThatsMe]]; return allRecipients; } - (void)writeReplyForEmail:(BPEmailDocument *)email replyAll:(BOOL)replyAll { NSString *replyBody = [NSString stringWithFormat:@"\n\n\n--- On %@, %@ wrote: ---\n\n%@", [email date], [[email author] summary], [email body]]; NSArray *toPersons = [NSArray arrayWithObject:[email replyTo]]; NSArray *ccPersons; if (replyAll) { ccPersons = [self ccRecipientsForReplyAllForEmail:email]; } else { ccPersons = [NSArray array]; } [self writeNewEmailWithSubject:[NSString stringWithFormat:@"Re: %@", [email label]] body:replyBody to:toPersons cc:ccPersons attachments:[NSArray array]]; } - (NSString *)forwardPrefixInfoForEmail:(BPEmailDocument *)email { return [NSString stringWithFormat: @"--- Begin forwarded message: ---\n\n" @"From: %@\n" @"Date: %@\n" @"To: %@\n" @"CC: %@\n" @"Subject: %@\n", [[email author] summary], [email date], [BPPerson summaryForPersons:[email toRecipients]], [BPPerson summaryForPersons:[email ccRecipients]], [email label]]; } - (void)writeForwardForEmail:(BPEmailDocument *)email { [self writeNewEmailWithSubject:[NSString stringWithFormat:@"Fwd: %@", [email label]] body:[NSString stringWithFormat:@"\n\n\n%@\n%@", [self forwardPrefixInfoForEmail:email], [email body]] to:[NSArray array] cc:[NSArray array] attachments:[email attachments]]; } #pragma mark - #pragma mark - StoneDesign scripts for actual sending of mail // if you want check point log info, define CHECK to the next line, uncommented: #define CHECK // NSLog(@"result code = %d", ok); // This converts an AEDesc into a corresponding NSValue. static id aedesc_to_id(AEDesc *desc) { OSErr ok; if (desc->descriptorType == typeChar) { NSMutableData *outBytes; NSString *txt; outBytes = [[NSMutableData alloc] initWithLength:AEGetDescDataSize(desc)]; ok = AEGetDescData(desc, [outBytes mutableBytes], [outBytes length]); CHECK; txt = [[NSString alloc] initWithData:outBytes encoding:[NSString defaultCStringEncoding]]; [outBytes release]; [txt autorelease]; return txt; } if (desc->descriptorType == typeSInt16) { SInt16 buf; AEGetDescData(desc, &buf, sizeof(buf)); return [NSNumber numberWithShort:buf]; } return [NSString stringWithFormat:@"[unconverted AEDesc, type=\"%c%c%c%c\"]", ((char *)&(desc->descriptorType))[0], ((char *)&(desc->descriptorType))[1], ((char *)&(desc->descriptorType))[2], ((char *)&(desc->descriptorType))[3]]; } // the sweetly wrapped method is all we need to know: - (void)runScript:(NSString *)txt { NSData *scriptChars = [txt dataUsingEncoding:[NSString defaultCStringEncoding]]; AEDesc source, resultText; OSAID scriptId, resultId; OSErr ok; // Convert the source string into an AEDesc of string type. ok = AECreateDesc(typeChar, [scriptChars bytes], [scriptChars length], &source); CHECK; // Compile the source into a script. scriptId = kOSANullScript; ok = OSACompile(myComponent, &source, kOSAModeNull, &scriptId); AEDisposeDesc(&source); CHECK; // Execute the script, using defaults for everything. resultId = 0; ok = OSAExecute(myComponent, scriptId, kOSANullScript, kOSAModeNull, &resultId); CHECK; if (ok == errOSAScriptError) { AEDesc ernum, erstr; id ernumobj, erstrobj; // Extract the error number and error message from our scripting component. ok = OSAScriptError(myComponent, kOSAErrorNumber, typeShortInteger, &ernum); CHECK; ok = OSAScriptError(myComponent, kOSAErrorMessage, typeChar, &erstr); CHECK; // Convert them to ObjC types. ernumobj = aedesc_to_id(&ernum); AEDisposeDesc(&ernum); erstrobj = aedesc_to_id(&erstr); AEDisposeDesc(&erstr); txt = [NSString stringWithFormat:@"Error, number=%@, message=%@", ernumobj, erstrobj]; } else { // If no error, extract the result, and convert it to a string for display if (resultId != 0) { // apple doesn't mention that this can be 0? ok = OSADisplay(myComponent, resultId, typeChar, kOSAModeNull, &resultText); CHECK; //NSLog(@"result thingy type = \"%c%c%c%c\"", ((char *)&(resultText.descriptorType))[0], ((char *)&(resultText.descriptorType))[1], ((char *)&(resultText.descriptorType))[2], ((char *)&(resultText.descriptorType))[3]); txt = aedesc_to_id(&resultText); AEDisposeDesc(&resultText); } else { txt = @"[no value returned]"; } OSADispose(myComponent, resultId); } ok = OSADispose(myComponent, scriptId); CHECK; } #pragma mark - /* tell application "Mail" set newMessage to make new outgoing message with properties {subject:"hello", content:"body" & return & return} tell newMessage -- Default is false. Determines whether the compose window will -- show on the screen or whether it will happen in the background. set visible to true make new to recipient at end of to recipients with properties {name:"me", address:"me@me.com"} make new cc recipient at end of to recipients with properties {name:"cc guy", address:"me@me.com"} make new cc recipient at end of to recipients with properties {name:"another cc guy", address:"me@me.com"} tell content if (wantsAttachment is equal to "Yes") then -- Position must be specified for attachments make new attachment with properties {file name:theAttachment} at after the last paragraph end if end tell end tell send newMessage -- Bring the new compose window to the foreground, in all its glory activate end tell */ - (NSString *)mailScriptForEmail:(BPEmailDocument *)email { // start with a scratch string of a decent length NSMutableString *s = [NSMutableString stringWithCapacity:1000]; // start the script [s appendString:@"tell application \"Mail\"\n"]; // set subject, body [s appendString:[NSString stringWithFormat: @"set newMessage to make new outgoing message with properties {subject:\"%@\", content:\"%@\" & return & return}\n", [email label], [email body]]]; // (begin) set more email attributes [s appendString:@"tell newMessage\n"]; // set "to" recipients BPPerson *person; int i; for (i=0; i<[[email toRecipients] count]; i++) { person = [[email toRecipients] objectAtIndex:i]; [s appendString:[NSString stringWithFormat: @"make new to recipient at end of to recipients with properties {name:\"%@\", address:\"%@\"}\n", [person name], [person address]]]; } // set "cc" recipients for (i=0; i<[[email ccRecipients] count]; i++) { person = [[email ccRecipients] objectAtIndex:i]; [s appendString:[NSString stringWithFormat: @"make new to recipient at end of cc recipients with properties {name:\"%@\", address:\"%@\"}\n", [person name], [person address]]]; } // add attachments if ([[email attachments] count] > 0) { // (begin) set attachments [s appendString:@"tell content\n"]; BPAttachment *attachment; for (i=0; i<[[email attachments] count]; i++) { attachment = [[email attachments] objectAtIndex:i]; if ([[NSFileManager defaultManager] fileExistsAtPath:[attachment filePath]]) { [s appendString:[NSString stringWithFormat: @"make new attachment with properties {file name:\"%@\"} at after the last paragraph\n", [attachment filePath]]]; } } // (end) set attachments [s appendString:@"end tell\n"]; } // (end) set more email attributes [s appendString:@"end tell\n"]; [s appendString:@"send newMessage\n"]; [s appendString:@"end tell\n"]; // uncomment next line to see your AppleScript in the console: //NSLog(s); return s; } - (void)sendEmail:(BPEmailDocument *)email { [self runScript:[self mailScriptForEmail:email]]; } @end