Objective-C: Check Firewall status in OSX? - objective-c

My objective-c app needs to be aware if the firewall in OSX is running, so it can tell the user to turn it off or create a new rule.
Also, is it possible to create rules directly from my app so users never need to handle networking issues?
John

I am writing a function that will provide you the status of OSX firewall :)
-(BOOL)getFirewallStatus{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSSystemDomainMask, YES);
NSString *path = [paths objectAtIndex:0];
path = [NSString stringWithFormat:#"%#/%#",path,#"Preferences/com.apple.alf.plist"];
path = [path stringByReplacingOccurrencesOfString:#"/System"
withString:#""];
NSDictionary* _dictionary = [[NSMutableDictionary alloc] initWithContentsOfFile:path];
// firewall status
int status = [[_dictionary valueForKey:#"globalstate"] integerValue];
if (status == 0)
{
return NO;
}
return YES;
}

If your application is being run by the user (i.e., double-clicked in the Finder), any attempt by your application to create a socket listener will prompt the user to allow/deny that listener - and subsequently adjust the firewall settings accordingly - without any programmatic intervention on the part of your application.
If the firewall in question is your router (a problem I recently had to deal with), you have a few options. The best supported option is Bonjour/mDNSResponder (as long as you don't want to support a double-nat'ed situation). Apple provides an Objective-C wrapper application around the rather obtuse dns_sd.h:
http://developer.apple.com/library/mac/#samplecode/PortMapper/Introduction/Intro.html#//apple_ref/doc/uid/DTS40007879-Intro-DontLinkElementID_2
Going the 3rd party route, take a look at TCM Port Mapper. It uses some deprecated features and it'll take a bit of effort to get it running with ARC support (if that's important to you).
http://code.google.com/p/tcmportmapper/
Both support UPnP and NAT-PMP.
Finally, if your application is running as a daemon (without a user interface), you're going to have to become acquainted with ipfw. Brace yourself. Google for "ipfw os x". StackOverflow is preventing me from posting more than two links. Brilliant.
Hope this helps....

Related

Standard temporary directory in OSX

I'm trying to figure what is the best place where to store temporary files in the OSX version of my application.
The obvious answer /tmp is not good since it is cleaned up at boot and my application may need to continue an interrupted job also after a restart.
I tried also to use the path pointed by the environment variable TMPDIR, that is the same returned by NSTemporaryDirectory(), that changes every boot and is something like:
/var/folders/wx/p4rqqs8d1ws0wlpx9dkwsh_80000gn/T/
.. but also the contents of this path are removed at boot.
There is a standard path where I can place some temporary files, resilient to restarts, or I have to invent my own solution (ie ~/Library/myapplication/temp)?
In Windows I'm using GetTempPath() and it works the way it should.
I've found my answer googling harder that I did before asking here, in this excellent article:
https://www.cocoawithlove.com/2009/07/temporary-files-and-folders-in-cocoa.html
Reading the articles and the various options I found that the Caches directory (NSCachesDirectory) is the correct place where to store my files. Placing them in "Application Support" will cause them to be backed up by time machine.
So here is what I did:
const char *get_temporary_dir()
{
NSString *path = nil;
NSString *bundleName = [[[NSBundle mainBundle] infoDictionary] objectForKey:#"CFBundleIdentifier"];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
if ([paths count]) {
path = [[paths objectAtIndex:0] stringByAppendingPathComponent:bundleName];
} else {
path = NSTemporaryDirectory();
path = [path stringByAppendingPathComponent:bundleName];
}
return [path UTF8String];
}
... I'm not sure if the fallback to the standard, deletable, directory is needed but it doesn't hurt!
You should use the "application support directory" - this is typically ~/Library/Application Support or for a sandboxed application and equivalent within its container.
To obtain the URL for this directory you use URLForDirectory:inDomain:appropriateForURL:create:error: passing as first argument NSApplicationSupportDirectory.
Within this directory you need to create a directory just for your application, using your app's bundle ID is a common strategy for naming this directory.
This directory is intended to store files needed by your application, but not your user's files.

cocoa get list of installed applications

Is there a way to get all installed applications for the current user in cocoa?
NSArray *runningApps = [[NSWorkspace sharedWorkspace] launchedApplications];
The above gives me currently running applications but for my app I need to list all installed applications. I need the application key (e.g. com.apple.appname) so system_profiler will does not work.
For OSX, the key library for gathering information about launchable applications is Launch Services (see Apple's Launch Services Programming Guide), which will give you the information about an application such as bundle id, file types that it accepts, etc.
For actually locating all executables on the machine, you're going to want to use Spotlight in one form or the other (either the API or by calling out to mdfind).
Example of using the command line version:
mdfind "kMDItemContentType == 'com.apple.application-bundle'"
will return a list of all application paths.
Using a similar term in the spotlight API will result in an appropriate list, from which you can then either open the main bundle using NSBundle or use Launch Services to retrieve information about the app.
I don't have time to do a thorough test of this, but the basic code would be:
NSMetadataQuery *query = [[NSMetadataQuery alloc] init];
[query setSearchScopes: #[#"/Applications"]]; // if you want to isolate to Applications
NSPredicate *pred = [NSPredicate predicateWithFormat:#"kMDItemContentType == 'com.apple.application-bundle'"];
// Register for NSMetadataQueryDidFinishGatheringNotification here because you need that to
// know when the query has completed
[query setPredicate:pred];
[query startQuery];
(Revised to use #John's localization-independent query instead of my original)

NSString's stringWithContentsOfURL uses browser cookies?

I'm working on an objective-c project that downloads webpages from a community website and parses the results. The download code looks like this:
NSError* error = nil;
NSString* text = [NSString stringWithContentsOfURL:fileUrl encoding:NSASCIIStringEncoding error:&error];
if(text) {
return text;
}
else {
NSLog(#"Error = %#", error);
return nil;
}
The odd thing is that when I download from the site I see resulting content that I would only see if logged into the site (which, in my browser, I am).
Does that method (NSString stringWithContentsOfURL:encoding:error) use browser cookies when executing the request? If so, is it Safari specifically that it's integrated with? The default browser? I can't seem to find documentation describing the behavior that I'm seeing. I'm ok with the behavior (in fact, it's preferable), but I only want to depend on it if I fully understand what's going on.
Thanks for your time.
Cookies are automatically handled and stored in an app's NSHTTPCookieStorage shared instance. Call the cookies method and check to see if your cookie is there. If it is, then that confirms your suspicion.
EDIT: I highly suspect you are using a UIWebView in your app and logging in from there. In that case, then yes, cookies are stored in your app's NSHTTPCookieStorage shared instance and will be used with further URL requests.

Problem writing plist in local vs. admin mode

In my application, I am storing user preferences (which are applicable for all users) into a plist file,
which is attached as a form of bundle.
The problem is, it runs fine in admin mode, but when I run the application, it's not writing the file. Do I need to set some attribute to write to the plist in local mode? Or is it not possible at all?
My code for writing the file is below:
-(void)SavePrefrence:(NSString *)fileName PrefrenceOption:(NSMutableDictionary *)pDict{
NSString *filePath = [[[NSBundle mainBundle] pathForResource:fileName ofType:#"plist"] retain];
NSDictionary *pTemp = [[NSDictionary alloc]initWithDictionary:pDict];
bool bRet = [pTemp writeToFile:filePath atomically:YES];
if(bRet==YES){
NSLog(#"File Saved ");
}
else {
NSLog(#"File not saved ");
}
}
This is the code which calls it:
-(void)SaveListSettings:(NSMutableDictionary *)pListSettings{
[ self SavePrefrence:#“MyList" PrefrenceOption:pListSettings];
if(pListInfo)
[pListInfo release];
[self LoadListProfile];
}
The application bundle will, by default, only be writable by the owner (if it's installed by dragging into the Applications folder, this'll be whoever installed it; if it's installed by a .pkg, it should probably be root). The way you're doing this, if I follow it properly, requires a user to have write access to the app's Contents/Resources folder, which is a really bad idea (not that it's unheard of -- see the University of Utah's documentation about "Poorly-Made Applications" for examples). Actually, saving preferences inside the application is a bad idea anyway; that's what the various Library/Preferences folders are for (~/Library/Preferences for personal settings, /Library/Preferences for system-wide settings. But, of course, /Library/Preferences is only writable by admins (for very good reasons). I don't really know of a good way to handle this, as system-wide settings modifiable by non-admins is not exactly normal.
You could use an installer which asks for an admin password and then create "/Library/Application Support/MyApp" and then either make this world writable, or make a sub-folder inside it which is world-writeable. Now MyApp running under a non-admin account can still write to this shared folder.
If you don't want to make the folder world-writeable then include a helper app to the bundle to do the writing and make the helper setuid root by using an installer which asks for an admin password...
BTW: Both of those options will fail Mac App Store rules. Maybe you can use '/Users/Shared', but I don't know if it is allowed by MAS, and anyway it is far from standard. Which would leave you with storing it on a web server...

How to hardcode CFBundleIdentifier?

what else can I say, how do I hardcode the CFBundleIdentifier in the app ?
Thanks
If you want to change CFBundleIdentifier during runtime, you can’t unless you write to the application bundle Info.plist. That’s bad practice because the application bundle might have been moved to a read-only volume, or had its write permissions limited by the system administrator, or digitally signed to avoid tampering. I guess Launch Services wouldn’t recognise this change instantly and the application would have to be restarted. Furthermore, it is also a reason for rejection by the Mac Apple Store.
On the other hand, if you want to detect tampering of CFBundleIdentifier, you can always read its value upon application startup, e.g. in applicationDidFinishLaunching:
- (void)applicationWillFinishLaunching:(NSNotification *)aNotification
{
NSString *bundleId = [[[NSBundle mainBundle] infoDictionary] objectForKey:#"CFBundleIdentifier"];
if (! [bundleId isEqualToString:#"com.yourcompany.yourapp"])
{
// Ooops, CFBundleIdentifier doesn’t match
}
}
Depending on your requirements, you might want to obfuscate the code above, including the literal strings. However, in general, you won’t be able to stop a determined adversary.