How to get file's "Stationary Pad" flag information using objective c - objective-c

I need to check for "Stationary Pad" flag of a file but I am not getting any way to read this information.

You have to use the Carbon File Manager API to do this, you can't do it from Cocoa. That said, this code does still work in the Xcode 12 beta.
-(BOOL)isStationaryPad:(NSString *)path
{
static kIsStationary = 0x0800;
CFURLRef url;
FSRef fsRef;
FSCatalogInfo catInfo;
BOOL success;
url = CFURLCreateWithFileSystemPath(NULL, (CFStringRef)path, kCFURLPOSIXPathStyle, FALSE);
if (!url) return NO;
success = CFURLGetFSRef(url, &fsRef);
CFRelease(url);
// catalog info from file system reference; isStationary status from catalog info
if (success && (FSGetCatalogInfo(&fsRef, kFSCatInfoFinderInfo, &catInfo, nil, nil, nil))==noErr)
{
return ((((FileInfo*)catInfo.finderInfo)->finderFlags & kIsStationary) == kIsStationary);
}
return NO;
}
This code is from the open source version of the Bean word processor.

Related

Printing with the system dialog when using PMPrintSession

Below is the code I we are using to print on mac. Is there an easy way to allow printing using the system dialog? It looks like at one time PMSessionBeginDocument & PMSessionBeginPage were a thing, but now all I can find is the NoDialog options.
Are these calls still usable with the latest frameworks? Or is there another way to print using the system dialog?
PMPrintSession lPrintSession;
PMCreateSession(&lPrintSession);
PMPrintSettings lPrintSettings;
PMCreatePrintSettings(&lPrintSettings);
PMSessionDefaultPrintSettings(lPrintSession, lPrintSettings);
...
PMSessionSetCurrentPMPrinter(lPrintSession, lPrinter);
...
PMSetPageRange(lPrintSettings, 1, 1);
PMSetCopies(lPrintSettings, inCopies, false);
if (!inUseSystemDialog) {
PMSessionBeginCGDocumentNoDialog(lPrintSession, lPrintSettings, lPageFormat);
PMSessionBeginPageNoDialog(lPrintSession, lPageFormat, NULL);
} else {
// TODO: What do we do here? Are these calls usable?
// PMSessionBeginDocument(lPrintSession, lPrintSettings, lPageFormat);
// PMSessionBeginPage(lPrintSession, lPageFormat, NULL);
}
CGContextRef lGraphics;
PMSessionGetCGGraphicsContext(lPrintSession, &lGraphics);
...
PMSessionEndPageNoDialog(lPrintSession);
PMSessionEndDocumentNoDialog(lPrintSession);
You can run an NSPrintPanel to show the system print dialog. For that, you also need to set up an NSPrintInfo object:
NSPrintInfo* printInfo = [NSPrintInfo new];
// set printInfo.printer if you want to override the default
PMPrintSettings printSettings = printInfo.PMPrintSettings;
// configure printSettings
[printInfo updateFromPMPrintSettings];
PMPageFormat pageFormat = printInfo.PMPageFormat;
// configure pageFormat
[printInfo updateFromPMPageFormat];
Create the panel and run it with that info object:
NSPrintPanel* panel = [NSPrintPanel printPanel];
// configure panel; for example, set its options property
NSInteger result = [panel runModalWithPrintInfo:printInfo];
Use the info as the basis of your print session:
if (result == NSOKButton)
{
PMPrintSession session = printInfo.PMPrintSession;
printSettings = printInfo.PMPrintSettings;
pageFormat = printInfo.PMPageFormat;
PMSessionBeginCGDocumentNoDialog(session, printSettings, pageFormat);
PMSessionBeginPageNoDialog(session, pageFormat, NULL);
CGContextRef lGraphics;
PMSessionGetCGGraphicsContext(session, &lGraphics);
...
PMSessionEndPageNoDialog(session);
PMSessionEndDocumentNoDialog(session);
}

How to read file comment field

In OS X Finder there is 'Comment' file property. It can be checked in finder by adding 'Comment' column or edited/checked after right clicking on file or folder and selecting 'Get info'.
How to read this value in swift or objective-c?
I checked already NSURL and none of them seems to be the right ones
Do not use the low-level extended attributes API to read Spotlight metadata. There's a proper Spotlight API for that. (It's called the File Metadata API.) Not only is it a pain in the neck, there's no guarantee that Apple will keep using the same extended attribute to store this information.
Use MDItemCreateWithURL() to create an MDItem for the file. Use MDItemCopyAttribute() with kMDItemFinderComment to obtain the Finder comment for the item.
Putting the pieces together (Ken Thomases reading answer above and writing answer link) you can extend URL with a computed property with a getter and a setter to read/write comments to your files:
update: Xcode 8.2.1 • Swift 3.0.2
extension URL {
var finderComment: String? {
get {
guard isFileURL else { return nil }
return MDItemCopyAttribute(MDItemCreateWithURL(kCFAllocatorDefault, self as CFURL), kMDItemFinderComment) as? String
}
set {
guard isFileURL, let newValue = newValue else { return }
let script = "tell application \"Finder\"\n" +
String(format: "set filePath to \"%#\" as posix file \n", absoluteString) +
String(format: "set comment of (filePath as alias) to \"%#\" \n", newValue) +
"end tell"
guard let appleScript = NSAppleScript(source: script) else { return }
var error: NSDictionary?
appleScript.executeAndReturnError(&error)
if let error = error {
print(error[NSAppleScript.errorAppName] as! String)
print(error[NSAppleScript.errorBriefMessage] as! String)
print(error[NSAppleScript.errorMessage] as! String)
print(error[NSAppleScript.errorNumber] as! NSNumber)
print(error[NSAppleScript.errorRange] as! NSRange)
}
}
}
}
As explained in the various answers to Mac OS X : add a custom meta data field to any file,
Finder comments can be read and set programmatically with getxattr() and setxattr(). They are stored as extended attribute
"com.apple.metadata:kMDItemFinderComment", and the value is a property
list.
This works even for files not indexed by Spotlight, such as those on a network server volume.
From the Objective-C code here
and here I made this simple Swift function
to read the Finder comment (now updated for Swift 4 and later):
func finderComment(url : URL) -> String? {
let XAFinderComment = "com.apple.metadata:kMDItemFinderComment"
let data = url.withUnsafeFileSystemRepresentation { fileSystemPath -> Data? in
// Determine attribute size:
let length = getxattr(fileSystemPath, XAFinderComment, nil, 0, 0, 0)
guard length >= 0 else { return nil }
// Create buffer with required size:
var data = Data(count: length)
// Retrieve attribute:
let result = data.withUnsafeMutableBytes { [count = data.count] in
getxattr(fileSystemPath, XAFinderComment, $0.baseAddress, count, 0, 0)
}
guard result >= 0 else { return nil }
return data
}
// Deserialize to String:
guard let data = data, let comment = try? PropertyListSerialization.propertyList(from: data,
options: [], format: nil) as? String else {
return nil
}
return comment
}
Example usage:
let url = URL(fileURLWithPath: "/path/to/file")
if let comment = finderComment(url: url) {
print(comment)
}
The function returns an optional string which is nil if the file
has no Finder comment, or if anything went wrong while retrieving it.

Set Custom KeyEquivalent in Services Menu

OmniFocus has a Cocoa Service that allows you to create tasks based upon selected items.
It has a preference that allows you to set the keyboard shortcut that triggers the Service. This is not just a global hotkey, it's a bona fide Service that shows up in the menu.
You can the keyboard shortcut to pretty much any combination, including combinations with ⌥ and ^. This functionality is not documented - the docs seem to say that KeyEquivalents must be a ⌘+[⇧]+someKey.
Once this is set, I observe three things:
The OmniFocus Info.plist file does not contain a KeyEquivalent listed. This is not surprising, as the file is read-only.
The pbs -dump_pboard utility lists NSKeyEquivalent = {}; for the service.
Using NSDebugServices lists this interesting line that does not show up with most debugging sessions (Obviously, for keyboard shortcut ⌃⌥⌘M): OmniFocus: Send to Inbox (com.omnigroup.OmniFocus) has a custom key equivalent: <NSKeyboardShortcut: 0x7fb18a0d18f0 (⌃⌥⌘M)>.
So my questions are twofold, and I suspect they are related:
How do you dynamically change a service's KeyEquivalent?
How do you set the KeyEquivalent to a combination including ⌃ and ⌥
Thank you!
Figured it out. The basic process is described here: Register NSService with Command Alt NSKeyEquivalent
The code is this:
//Bundle identifier from Info.plist
NSString* bundleIdentifier = #"com.whatever.MyApp";
//Services -> Menu -> Menu item title from Info.plist
NSString* appServiceName = #"Launch My Service";
//Services -> Instance method name from Info.plist
NSString* methodNameForService = #"myServiceMethod";
//The key equivalent
NSString* keyEquivalent = #"#~r";
CFStringRef serviceStatusName = (CFStringRef)[NSString stringWithFormat:#"%# - %# - %#", bundleIdentifier, appServiceName, methodNameForService];
CFStringRef serviceStatusRoot = CFSTR("NSServicesStatus");
CFPropertyListRef pbsAllServices = (CFPropertyListRef) CFMakeCollectable ( CFPreferencesCopyAppValue(serviceStatusRoot, CFSTR("pbs")) );
// the user did not configure any custom services
BOOL otherServicesDefined = pbsAllServices != NULL;
BOOL ourServiceDefined = NO;
if ( otherServicesDefined ) {
ourServiceDefined = NULL != CFDictionaryGetValue((CFDictionaryRef)pbsAllServices, serviceStatusName);
}
NSUpdateDynamicServices();
NSMutableDictionary *pbsAllServicesNew = nil;
if (otherServicesDefined) {
pbsAllServicesNew = [NSMutableDictionary dictionaryWithDictionary:(NSDictionary*)pbsAllServices];
} else {
pbsAllServicesNew = [NSMutableDictionary dictionaryWithCapacity:1];
}
NSDictionary *serviceStatus = [NSDictionary dictionaryWithObjectsAndKeys:
(id)kCFBooleanTrue, #"enabled_context_menu",
(id)kCFBooleanTrue, #"enabled_services_menu",
keyEquivalent, #"key_equivalent", nil];
[pbsAllServicesNew setObject:serviceStatus forKey:(NSString*)serviceStatusName];
CFPreferencesSetAppValue (
serviceStatusRoot,
(CFPropertyListRef) pbsAllServicesNew,
CFSTR("pbs"));
Boolean result = CFPreferencesAppSynchronize(CFSTR("pbs"));
if (result) {
NSUpdateDynamicServices();
NSLog(#"successfully installed our alt-command-r service");
} else {
NSLog(#"couldn't install our alt-command-r service");
}
If the code succeeds, you can view this in ~/Library/Preferences/pbs.plist
You should see something like:
NSServicesStatus = {
"com.whatever.MyApp - Launch My Service - myServiceMethod" = {
enabled_context_menu = :true;
enabled_services_menu = :true;
key_equivalent = "#~r";
};

Cocoa: Handling 407 http response cfnetwork

I am creating downloader application.
I am facing a problem with proxy authentication.
I am getting 407 response code i.e proxy authentication required. I have valid proxy authentication details.
Following is Code Flow:
1. Create Http request using CFHTTPMessageCreateRequest
2. Set necessary header field values like Cache-Control, Accept-Ranges, Range & User-Agent using CFHTTPMessageSetHeaderFieldValue
3. Create read stream using CFReadStreamCreateForHTTPRequest
4. Set proxy server URL & port properties on read stream using CFReadStreamSetProperty
5. Set kCFStreamPropertyHTTPShouldAutoredirect to kCFBooleanTrue using CFReadStreamSetProperty
6. open read stream using CFReadStreamOpen
7. In a loop wait for stream to get opened
while (1)
{
if (kCFStreamStatusOpen == CFReadStreamGetStatus)
{
if (CFReadStreamHasBytesAvailable)
{
Get Http response header using CFReadStreamCopyProperty
Get response code using CFHTTPMessageGetResponseStatusCode
if (200 || 206 is response code)
SUCCESS
else check if response code is 407.
}
}
}
I tried using following code
if (407 == nsiStatusCode)
{
CFStreamError err;
cfAuthentication = CFHTTPAuthenticationCreateFromResponse(NULL, cfHttpResponse);
if ((cfAuthentication) && (CFHTTPAuthenticationIsValid(cfAuthentication, &err)))
{
if (CFHTTPAuthenticationRequiresUserNameAndPassword(cfAuthentication))
{
CFHTTPMessageApplyCredentials(cfHttpRequest, cfAuthentication, (CFStringRef)pnsUserName, (CFStringRef)pnsPassword, &err);
}
}
}
but unable to make it work.
How do I handle 407 status code so as to communicate with authenticating HTTP server?
Thanks in advance.
Vaibhav.
Build a CFHTTPMessageRef
-(CFHTTPMessageRef)buildMessage
{
NSURL *myURL = [NSURL URLWithString:#"http://myurl.com"];
NSData *dataToPost = [[NSString stringWithString:#"POST Data It Doesn't Matter What It Is"] dataUsingEncoding:NSUTF8StringEncoding];
//Create with the default allocator (NULL), a post request,
//the URL, and pick either
//kCFHTTPVersion1_0 or kCFHTTPVersion1_1
CFHTTPMessageRef request = CFHTTPMessageCreateRequest(NULL, CSTR("POST"), (CFURLRef)myURL, kCFHTTPVersion1_1);
CFHTTPMessageSetBody(request, (CFDataRef)dataToPost);
//Unfortunately, this isn't smart enough to set reasonable headers for you
CFHTTPMessageSetHeaderFieldValue(request, CFSTR("HOST"), (CFStringRef)[myURL host]);
CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Content-Length"), (CFStringRef)[NSString stringWithFormat:"%d", [dataToPost length]);
CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Content-Type"), CFSTR("charset=utf-8"));
return [NSMakeCollectable(request) autorelease];
}
Send it to the server and read back the response
-(CFHTTPMessageRef)performHTTPRequest:(CFHTTPMessageRef)request
{
CFReadStreamRef requestStream = CFReadStreamCreateForHTTPRequest(NULL, request);
CFReadStreamOpen(requestStream);
NSMutableData *responseBytes = [NSMutableData data];
CFIndex numBytesRead = 0 ;
do
{
UInt8 buf[1024];
numBytesRead = CFReadStreamRead(requestStream, buf, sizeof(buf));
if(numBytesRead > 0)
[responseBytes appendBytes:buf length:numBytesRead];
} while(numBytesRead > 0);
CFHTTPMessageRef response = (CFHTTPMessageRef)CFReadStreamCopyProperty(requestStream, kCFStreamPropertyHTTPResponseHeader);
CFHTTPMessageSetBody(response, (CFDataRef)responseBytes);
CFReadStreamClose(requestStream);
CFRelease(requestStream);
return [NSMakeCollectable(response) autorelease];
}
Adding Authentication to an HTTP Request
-(void)addAuthenticationToRequest:(CFHTTPMessageRef)request withResponse:(CFHTTPMessageRef)response
{
CFHTTPAuthenticationRef authentication = CFHTTPAuthenticationCreateFromResponse(NULL, response);
[NSMakeCollectable(authentication) autorelease];
CFStreamError err;
Boolean success = CFHTTPMessageApplyCredentials(request, authentication, CFSTR("username"), CFSTR("password"), &err);
}
Putting It All Together
-(void)magicHappens
{
CFHTTPMessageRef request = [self buildMessage];
CFHTTPMessageRef response = [self performHTTPRequest: request];
UInt32 statusCode;
statusCode = CFHTTPMessageGetResponseStatusCode(response);
//An HTTP status code of 401 or 407 indicates that authentication is
//required I use an auth count to make sure we don't get stuck in an
//infinite loop if our credentials are bad. Sometimes, making the
//request more than once lets it go through.
//I admit I don't know why.
int authCount = 0;
while((statusCode == 401 || statusCode == 407) && authCount < 3)
{
request = [self buildMessage];
[self addAuthenticationToRequest:request withResponse:response];
response = [self performHTTPRequest: request];
statusCode = CFHTTPMessageGetResponseStatusCode;
authCount++;
}
NSData *responseBodyData = [(NSData*)CFHTTPMessageCopyBody(response) autorelease];
NSString *responseBody = [[[NSString alloc] initWithData:responseBodyData encoding:NSUTF8StringEncoding] autorelease];
NSLog(responseBody);
}
Refer this link.

SQLite iphone data lost when rebooting or closing the app

I have a question... I have an application using sqlite to save some data.Everything works perfectly, meaning I can add, delete, view data. The data are persistent when the application goes into background. Now, when I remove the application from memory or reboot the iPhone, the database is corrupted and all the data are messed up !
I have a class call dbAccess where all the database actions are define(add, delete, retreive rows). At the end I have a finalize action, that finalize all the statement used and then close the database.
+ (void)finalizeStatements{
NSLog(#"Finalizing the Delete Statements");
if(deleteStmt) {
NSLog(#"Delete Statement exist... finalization");
sqlite3_finalize(deleteStmt);
deleteStmt = nil;
}
NSLog(#"Finalizing the Add Statements");
if(addStmt) {
NSLog(#"Add Statement exist... finalization");
sqlite3_finalize(addStmt);
addStmt = nil;
}
NSLog(#"Finalizing the Store Statements");
if(storeStmt) {
NSLog(#"Store Statement exist... finalization");
sqlite3_finalize(storeStmt);
storeStmt = nil;
}
NSLog(#"Finalizing the Agent Statements");
if(agentStmt) {
NSLog(#"Agent Statement exist... finalization");
sqlite3_finalize(agentStmt);
agentStmt = nil;
}
NSLog(#"Closing the Database");
if(database) {
NSLog(#"The database exist... closing it");
sqlite3_close(database);
}
}
This method is called by the application delegate when applicationDidEnterBackground and applicationWillTerminate. An openDatabase method is call when applicationDidBecomeActive.
Any ideas why the database is corrupted ?
Thanks.
First off, check out fmdb or some other proven wrappers. You can also browse the code.
Not sure if there's enough info to know why it's corrupt. But, you should get return codes from ALL sqlite3_xxx calls and log at a minimum to understand what's going on or you may just be blasting past an issue.
Also, make sure to call sqlite_errmsg which will offer more clues if the return code is not success.
In my wrapper, I do this in close. It's expected that statements are finalized bu handled if not. I have a statement cache which on clear finalizes each statement.
:
- (void)close
{
if (_sqlite3)
{
NSLog(#"closing");
[self clearStatementCache];
int rc = sqlite3_close(_sqlite3);
NSLog(#"close rc=%d", rc);
if (rc == SQLITE_BUSY)
{
NSLog(#"SQLITE_BUSY: not all statements cleanly finalized");
sqlite3_stmt *stmt;
while ((stmt = sqlite3_next_stmt(_sqlite3, 0x00)) != 0)
{
NSLog(#"finalizing stmt");
sqlite3_finalize(stmt);
}
rc = sqlite3_close(_sqlite3);
}
if (rc != SQLITE_OK)
{
NSLog(#"close not OK. rc=%d", rc);
}
_sqlite3 = NULL;
}
}