Sending an E-Mail with attachments in Cocoa - cocoa

I have an NSTextView with text & images in it, which is supposed to send both in an e-mail.I know that the message.framework is deprecated,so I came up with the idea to send it via NSTask, since mail is integrated.I came up with the code below, however in the log I get this:
*** -[NSCFDictionary setObject:forKey:]: attempt to insert
nil value (key:
_NSTaskInputFileHandle)
This is the code I am using:
NSError *error;
if([textView writeRTFDToFile:#"/Library/Application Support/log.rtfd" atomically:NO])
{
NSArray *args = [NSArray arrayWithObjects:#"-s", [subject stringValue], [sendto stringValue], nil];
NSTask *task = [[[NSTask alloc] init] autorelease];
[task setLaunchPath:#"/usr/bin/mailx"];
[task setArguments:args];
[task setStandardInput:[NSFileHandle fileHandleForReadingAtPath:#"/Library/Application Support/log.rtfd"]];
[task launch];
[task waitUntilExit];
Can someone tell me what I am doing wrong?

You can also try the Scripting Bridge. See Apple's SBSendEmail example.

Related

Using NSPipe with NSTask to simulate command line input

I'm writing a very simple GUI tool for a command line app. It only has 2 buttons. Connect and Quit.
In applicationDidFinishLaunching I run the following
NSPipe *pipe = [[NSPipe alloc] init];
writer = [pipe fileHandleForWriting];
NSTask *runTask = [[[NSTask alloc] init] autorelease];
NSString *exefile = [[[NSBundle mainBundle] resourcePath]
stringByAppendingPathComponent:#"vpngui"];
[runTask setLaunchPath: exefile];
NSString *exeDir = [[NSBundle mainBundle] resourcePath];
NSArray *pargs;
pargs = [NSArray arrayWithObjects: exeDir, nil];
[runTask setArguments: pargs];
[runTask setStandardInput:pipe];
[runTask launch];
Then when the Connect button is clicked the following line of code is run
writer writeData:[#"Connect" dataUsingEncoding:NSUTF8StringEncoding]];
and for the Quit button
writer writeData:[#"Quit" dataUsingEncoding:NSUTF8StringEncoding]];
Somehow the command line app never gets the Connect and Quit commands
The data is probably buffered.
Try forcing to flush the file handle by calling the synchronizeFile: method:
synchronizeFile
Causes all in-memory data and attributes of the file represented by the receiver to be written to permanent storage.
I meet the same problem and
closeFile: works for me.
closeFile: to indicate that you have finished writing.
But next time you want to write you'd have to create a new fileHandle instance because the old one has been closed.

Strange output from LESS when using with NSTask

I'm trying to use LESS with NSTask. It basically works but the output is sometimes quite strange. Example:
[31mParseError: Unrecognised input[39m[31m in [39m/Volumes/Macintosh HD/Users/x/Sites/y/css/style.less[90m on line 4, column 2:[39m
[90m3 background-color: purple;[39m
4 [7m[31m[1md[22md[39m[27m
[90m5 }[39m[0m[0m
Where does this [31m etc. come from?
My relevant code is:
NSTask *task = [[NSTask alloc] init];
task.launchPath = [bundle.resourcePath stringByAppendingPathComponent:#"less.js/bin/lessc"];
task.arguments = #[path,cssPath];
NSPipe *outputPipe = [NSPipe pipe];
[task setStandardError:outputPipe];
[task setTerminationHandler:^(NSTask *task) {
NSString *s = [[NSString alloc] initWithData: [[outputPipe fileHandleForReading] readDataToEndOfFile] encoding:NSUTF8StringEncoding];
if(s.length > 0){
[[NSAlert alertWithMessageText:#"LESS Compilation" defaultButton:#"OK" alternateButton:nil otherButton:nil informativeTextWithFormat:#"%#",s] runModal];
}
}];
[task launch];
Edit: I think now this has something to do with the coloring from the command line, hasn't it? So my question is: What's the best way to avoid them?
Those are ANSI color codes. You might want to look at whether or not there are arguments for the task that will not return color coded text.

Mac OS X: Execute scripts with hooks from an application

Im building an cocoa app that monitors something™ and I am planning to have some hooks for users. So I want to enable the user to put a script (Bash, Ruby, Python you name it) with a specified name (let's say after_event) into the Application Support directory and that script gets executed after a certain event in my code. Ideally I could pass some variables to the script so the script knows what happened.
Any ideas on this?
So problem one is: How do I get the path of the Application Support "the SDK way"? problem two is: How do I execute script with variables like THAT_APPEND="foo"?
Thanks,
Philip
Because sharing is caring here is the method that executes the scripts:
-(void) runScript:(NSString*)scriptName withVariables:(NSDictionary *)variables
{
NSString *appSupportPath = [NSFileManager defaultManager] applicationSupportDirectory];
NSArray *arguments;
NSString* newpath = [NSString stringWithFormat:#"%#/%#",appSupportPath, scriptName];
if ([[NSFileManager defaultManager]fileExistsAtPath:newpath]){
NSTask *task = [[NSTask alloc] init];
[task setLaunchPath: newpath];
NSLog(#"Executing hook: %#",newpath);
arguments = [NSArray arrayWithObjects:newpath, nil];
[task setArguments: arguments];
[task setEnvironment:variables];
NSPipe *pipe;
pipe = [NSPipe pipe];
[task setStandardOutput: pipe];
NSFileHandle *file;
file = [pipe fileHandleForReading];
[task launch];
NSData *data;
data = [file readDataToEndOfFile];
NSString *string;
string = [[NSString alloc] initWithData: data encoding: NSUTF8StringEncoding];
NSLog (#"script returned:\n%#", string);
}
}
}
UPDATE: I updated the code to be more generic. Now NSTask will tell the kernel to execute the script directly so your user can not online use Bash scripts but also python, perl, php whatever she likes. The only thing she needs to use is a Shebang in that file.
The NSFileManager Category can be found here.
Look for NSTask documentation. There's an environment member you can manipulate. Also adding command line parameters in a form -name = value should be trivial.

create zip archive using NSTask containing a first level folder with the files

my method works for zipping files from a temporary directory previously created and populated:
NSURL *destURL = self.archiveDestURL;
NSTask *task = [[NSTask alloc] init];
[task setCurrentDirectoryPath:[srcURL path]];
[task setLaunchPath:#"/usr/bin/zip"];
NSArray *argsArray = [NSArray arrayWithObjects:#"-r", #"-q", [destURL path], #".", #"-i", #"*", nil];
[task setArguments:argsArray];
[task launch];
[task waitUntilExit];
but what i'd like to have when unzipped, is a folder with the files.
sure i can make a folder in the tempDir and write my files there, but what is the zip argument for having a folder be the top level in the created archive?
i didn't see this in man zip .
This will help you.
NSTask *unzip = [[NSTask alloc] init];
[unzip setLaunchPath:#"/usr/bin/unzip"];
[unzip setArguments:[NSArray arrayWithObjects:#"-u", #"-d",
destination, zipFile, nil]];
NSPipe *aPipe = [[NSPipe alloc] init];
[unzip setStandardOutput:aPipe];
[unzip launch];
[unzip waitUntilExit];
instead of using NSTask, you could incorporate compress functionality into your code. there are several options.
ZipBrowser from apple.com
adding a category to NSData, as in here
a similar question. How can I create a zip file by using Objective C?

NSTask Launch causing crash

I have an application that can import an XML file through this terminal command :
open /path/to/main\ app.app --args myXML.xml
This works great with no issues. And i have used Applescript to launch this command through shell and it works just as well. Yet when try using Cocoa's NSTask Launcher using this code :
NSTask *task = [[NSTask alloc] init];
[task setLaunchPath:#"/usr/bin/open"];
[task setCurrentDirectoryPath:#"/Applications/MainApp/InstallData/App/"];
[task setArguments:[NSArray arrayWithObjects:[(NSURL *)foundApplicationURL path], #"--args", #"ImportP.xml", nil]];
[task launch];
the applications will start up to the initial screen and then crash when either the next button is clicked or when trying to close the window. Ive tried using NSAppleScript with this :
NSAppleScript *script = [[NSAppleScript alloc] initWithSource:#"tell application \"Terminal\" do script \"open /Applications/MainApp/InstallData/App/Main\\\\ App.app\" end tell"];
NSDictionary *errorInfo;
[script executeAndReturnError:&errorInfo];
This will launch the program and it will crash as well and i get this error in my Xcode debug window :
12011-01-04 17:41:28.296 LaunchAppFile[4453:a0f]
Error loading /Library/ScriptingAdditions/Adobe Unit Types.osax/Contents/MacOS/Adobe Unit Types: dlopen(/Library/ScriptingAdditions/Adobe Unit Types.osax/Contents/MacOS/Adobe Unit Types, 262): no suitable image found.
Did find: /Library/ScriptingAdditions/Adobe Unit Types.osax/Contents/MacOS/Adobe Unit Types: no matching architecture in universal wrapper
LaunchAppFile: OpenScripting.framework - scripting addition "/Library/ScriptingAdditions/Adobe Unit Types.osax" declares no loadable handlers.
So with research i came up with this :
NSAppleScript *script = [[NSAppleScript alloc] initWithSource:#"do shell script \"arch -i386 osascript /Applications/MainApp/InstallData/App/test.scpt\""];
NSDictionary *errorInfo;
[script executeAndReturnError:&errorInfo];
But this causes the same results as the last command.
Any ideas on what causes this crash?
Check here to fix the adobe errors. I'm not sure that's the problem though. Actually I couldn't get the open command to pass arguments to anything so I couldn't look into your problem.
Give NSTask another try with full paths only:
NSTask *task = [[NSTask alloc] init];
[task setLaunchPath:#"/usr/bin/open"];
[task setArguments:[NSArray arrayWithObjects:[ #"'/path/to/main app.app'", #"--args", #"/path/to/ImportP.xml", nil]];
[task launch];
Another approach would be to give NSTask the following command line:
sh -c '/usr/bin/open "/path/to/main app.app" --args /path/to/myXML.xml'
...
NSTask *task = [[NSTask alloc] init];
[task setLaunchPath:#"/bin/sh"];
NSString *cmd = [NSString stringWithFormat:
#"%# %# %# %#",
#"/usr/bin/open",
#"'/path/to/main app.app'",
#"--args",
#"/path/to/myXML.xml"
];
[task setArguments:[NSArray arrayWithObjects:[ #"-c", cmd, nil]];
[task launch];

Resources