For some reason I got an empty dictionary when calling SecItemCopyMatching on OSX 10.8.4. The corresponding item is in the keychain and contains username and password. SecItemCopyMatching founds it (errSecSuccess) but the result dictionary just contains 0 entries. I would expect it to have at least username and password data, so what's wrong with my request?
OSStatus status;
NSMutableDictionary *query = [NSMutableDictionary dictionary];
[query setObject:(id)kSecClassGenericPassword forKey:(id)kSecClass];
[query setObject:(id)kSecReturnAttributes forKey:(id)kCFBooleanTrue];
[query setObject:#"MyService" forKey:(id)kSecAttrService];
CFDictionaryRef dictRef = NULL;
status = SecItemCopyMatching((__bridge CFDictionaryRef)query, (CFTypeRef *)&dictRef);
if (status != errSecSuccess) {
CFStringRef errorRef = SecCopyErrorMessageString(status, NULL);
NSLog(#"%s: %#", __FUNCTION__, (__bridge NSString *)errorRef);
CFRelease(errorRef);
return nil;
}
// --> dictRef empty
if (dictRef != NULL) CFRelease(dictRef);
There is a mistake in the request. I've mixed up key and object.
[query setObject:(id)kSecReturnAttributes forKey:(id)kCFBooleanTrue];
must be
[query setObject:(id)kCFBooleanTrue forKey:(id)kSecReturnAttributes];
however as bdash pointed out, it will return only non-encrypted attributes like the username. To get the password there is another request necessary with passing
[query setObject:(id)kCFBooleanTrue forKey:(id)kSecReturnData];
So here what I have now:
OSStatus status;
NSMutableDictionary *query = [NSMutableDictionary dictionary];
[query setObject:(id)kSecClassGenericPassword forKey:(id)kSecClass];
[query setObject:(id)kCFBooleanTrue forKey:(id)kSecReturnAttributes];
[query setObject:(id)kSecMatchLimitOne forKey:(id)kSecMatchLimit];
[query setObject:#"MyService" forKey:(id)kSecAttrService];
// get username
CFDictionaryRef dictRef = NULL;
status = SecItemCopyMatching((__bridge CFDictionaryRef)query, (CFTypeRef *)&dictRef);
if (status != errSecSuccess) {
CFStringRef errorRef = SecCopyErrorMessageString(status, NULL);
NSLog(#"%s: %#", __FUNCTION__, (__bridge NSString *)errorRef);
CFRelease(errorRef);
return nil;
}
NSString *username = (__bridge NSString *)CFDictionaryGetValue(dictRef, kSecAttrAccount);
CFRelease(dictRef);
// get password
[query removeObjectForKey:(id)kSecReturnAttributes];
[query setObject:(id)kCFBooleanTrue forKey:(id)kSecReturnData];
CFDataRef dataRef = NULL;
status = SecItemCopyMatching((__bridge CFDictionaryRef)query, (CFTypeRef *)&dataRef);
if (status != errSecSuccess) {
CFStringRef errorRef = SecCopyErrorMessageString(status, NULL);
NSLog(#"%s: %#", __FUNCTION__, (__bridge NSString *)errorRef);
CFRelease(errorRef);
return nil;
}
NSString *password = [[NSString alloc] initWithData:(__bridge NSData *)(dataRef) encoding:NSUTF8StringEncoding];
CFRelease(dataRef);
Related
im working on an that search in the content of allot of files, i planed to use SearchKit but i can't figure out to make Apple's sample code to work, and i can't find any other ressources (NSHipster code didn't work either), here's my code:
#define kSearchMax 1000
#interface ViewController()
#property(nonatomic) SKIndexRef mySKIndex;
#end
#implementation ViewController
#synthesize mySKIndex;
- (void)viewDidLoad {
[super viewDidLoad];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
[self openIndex];
[self addDoc];
SKIndexFlush(self.mySKIndex);
// i thought that the indexation may need some time ..
sleep(2);
dispatch_async(dispatch_get_main_queue(), ^{
[self searchterm:#"var"];
});
});
}
- (void) openIndex {
NSString *path = [[NSHomeDirectory() stringByAppendingPathComponent:#"index"] stringByAppendingPathExtension:#"txt"]; // 1
NSURL *url = [NSURL fileURLWithPath:path];
NSString *name = #"extension_index";
if ([name length] == 0) name = nil;
SKIndexType type = kSKIndexInverted;
if ([[NSFileManager defaultManager] fileExistsAtPath:path]) {
mySKIndex = SKIndexOpenWithURL ((__bridge CFURLRef) url,
(__bridge CFStringRef) name,
true
);
}else{
self.mySKIndex = SKIndexCreateWithURL((__bridge CFURLRef) url,
(__bridge CFStringRef) name,
(SKIndexType) type,
(CFDictionaryRef) NULL);
}
}
- (void) addDoc {
SKLoadDefaultExtractorPlugIns ();
NSString *path = [NSBundle.mainBundle pathForResource:#"Products" ofType:#"rtf"]; // 1
NSURL *url = [NSURL fileURLWithPath: path]; // 2
SKDocumentRef doc = SKDocumentCreateWithURL ((__bridge CFURLRef) url);
NSString *mimeTypeHint = #"text/rtf";
BOOL added = SKIndexAddDocument ((SKIndexRef) mySKIndex,
(SKDocumentRef) doc,
(__bridge CFStringRef)mimeTypeHint,
(Boolean) true
);
NSLog(added ? #"added" : #"not added");
}
- (void) searchterm:(NSString*)query{
SKSearchOptions options = kSKSearchOptionDefault;
BOOL more = YES;
UInt32 totalCount = 0;
SKSearchRef search = SKSearchCreate (mySKIndex,
(__bridge CFStringRef) query,
options);
while (more) {
SKDocumentID foundDocIDs [kSearchMax];
float foundScores [kSearchMax];
float *scores;
Boolean unranked =
options & kSKSearchOptionNoRelevanceScores;
if (unranked) {
scores = NULL;
} else {
scores = foundScores;
}
CFIndex foundCount = 0;
more = SKSearchFindMatches (
search,
kSearchMax,
foundDocIDs,
scores,
100,
&foundCount
);
NSLog(#"%#", [NSString stringWithFormat:#"current count = %i", totalCount]);
totalCount += foundCount;
}
}
#end
it always print "current count = 0" and the loop is executed only one time.
I need to send a cookie in the request. But seems the cookie is not send. What is wrong I am doing here,
// set the cookie for getdoc request url
if (request.cookie)
{
NSDictionary *cookieProperties = #{NSHTTPCookieName : #"GetDoc",
NSHTTPCookieValue : request.cookie,
NSHTTPCookieDomain : [NSURL URLWithString:request.url].host,
// NSHTTPCookieOriginURL: request.url,
NSHTTPCookiePath : #"/",
// NSHTTPCookieVersion : #"1",
NSHTTPCookieExpires : [[NSDate date] dateByAddingTimeInterval:60*60*24*30]
};
NSHTTPCookie *cookie = [NSHTTPCookie cookieWithProperties:cookieProperties];
[[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookie:cookie];
}
NSURLSessionConfiguration *sessionConfig = [NSURLSessionConfiguration defaultSessionConfiguration];
// NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfig];
NSURLSession *session = [NSURLSession sharedSession];
__block NSData *documentData = nil;
__block NSString *mimeType = nil;
DLog(#"COOKIES: %#", [NSHTTPCookieStorage sharedHTTPCookieStorage].cookies);
// retrieve the doc from url
// block the call to wait for response for kMaxBlockingTimeHttpSeconds
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
NSURLSessionDownloadTask *getDocTask = [session downloadTaskWithURL:[NSURL URLWithString:request.url]
completionHandler:^(NSURL *location,
NSURLResponse *response,
NSError *error)
{
if (error) {
NSLog(#"Error retrieving document from %#, Error: %#",
request.url, error);
res.errorCode = SCPErrorDocumentGetError;
res.errorMessage = [error localizedDescription];
} else {
mimeType = response.MIMEType;
documentData = [NSData dataWithContentsOfURL:location];
}
dispatch_semaphore_signal(sema);
}];
[getDocTask resume];
dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(kMaxBlockingTimeHttpSeconds * NSEC_PER_SEC));
dispatch_semaphore_wait(sema, timeout);
if (!documentData) {
DLog(#"Response: %#", [res jsonDict]);
self.data = [NSJSONSerialization dataWithJSONObject:[res jsonDict] options:NSJSONWritingPrettyPrinted error:nil];
return;
}
NSString *str = [[NSString alloc] initWithData:documentData encoding:NSISOLatin1StringEncoding];
DLog(#"%#", str);
The NSLog printout of the cookie is,
"",
But this is not sent in the request header.
Adding the cookie header directly in the sessionConfig worked for me. But why did not the shareCookie work. This is supposed to work according the the documentation.
NSURLSessionConfiguration *sessionConfig = [NSURLSessionConfiguration defaultSessionConfiguration];
sessionConfig.HTTPAdditionalHeaders = #{#"Cookie" : request.cookie};
saveInBackgroundWithBlock stopped working after I upgraded parse library. Please check the code below, this used to work before but not anymore. Am I doing something wrong here?
PFQuery* query = [PFQuery queryWithClassName: #"FoodDaily"];
[query whereKey: #"user_id" equalTo: [[PFUser currentUser] objectId]];
[query whereKey: #"date" equalTo: [dicItem valueForKey: #"date"]];
m_loadingView.hidden = NO;
[query findObjectsInBackgroundWithBlock:^(NSArray *result, NSError *error)
{
if(error)
{
NSLog(#"error");
m_loadingView.hidden = YES;
[[AppDelegate getDelegate] showMessage: NETWORK_DISCONNECT_ERROR];
return;
}
if (!error) {
NSLog(#"result: %#", result);
m_loadingView.hidden = YES;
PFObject* objRecord = [PFObject objectWithClassName:#"FoodDaily"];
if ([result count]>0) {
objRecord = [result objectAtIndex: 0];
}
[objRecord setObject: [dicItem valueForKey: #"breakfast_food"] forKey: #"breakfast_food"];
m_loadingView.hidden = NO;
[objRecord saveInBackgroundWithBlock: ^(BOOL succeeded, NSError* error)
{
if(succeeded)
{
m_loadingView.hidden = YES;
NSLog(#"Success Updating New Food Daily Item");
[self.navigationController popToViewController: [AppDelegate getDelegate].m_viewFoodDaily animated: YES];
}
else
{
m_loadingView.hidden = YES;
[[AppDelegate getDelegate] showMessage: NETWORK_DISCONNECT_ERROR];
NSLog(#"Failed Saving New Food Item");
}
}];
}
}];
In log I am only getting
result: (
)
which is by NSLog(#"result: %#", result); but nothing from saveInBackgroundWithBlock
Your problem is not with saveInBackgroundWithBlock but your query in findObjectsInBackgroundWithBlock is not fetching any result. Please run the same query on prase UI and check if your getting any result over there.
In my current xcode project, I need to send parameter values to a url and need to retrieve an xml file based on the parameter values sent.
I tried the below code but it's not working:
(IBAction)myButtonClick:(id)sender
{
NSURL *oRequestURL =
[NSURL URLWithString:#"http://xyz .com/air/1.0/search?from=MAA&to=CJB&depart-date=2012-06-30&adults=2&children=2&infants=1&way=one&cabin-type=Economy&sort=asc"];
NSMutableURLRequest *oRequest = [[[NSMutableURLRequest alloc]init]autorelease];
[oRequest setHTTPMethod:#"POST"];
[oRequest setURL: oRequestURL];
NSMutableData *oHttpBody = [NSMutableData data];
[oHttpBody appendData:[#"This is HTTP Request body" dataUsingEncoding:NSUTF8StringEncoding]];
[oRequest setValue:[oHttpBody length] forHTTPHeaderField:#"Content-Length"];
NSError *oError = [[NSError alloc]init];
NSHTTPURLResponse *oResponseCode = nil;
NSData *oResponseData = [NSURLConnection sendSynchronousRequest:oRequest returningResponse:oResponseCode error:oError];
if ([oResponseCode statusCode]> 200) {
NSLog(#"Status code is greater than 200");
}
NSString *strResult=[[NSString alloc]initWithData:oResponseData encoding:NSUTF8StringEncoding];
NSLog(#"The result is %s",strResult);
}
I have searched many sites and books but could not find a solution.
Would be of great help if a link to a tutorial or some other useful resource can be provided. Appreciate your great help.
Thank You.
Hi,
I have found the solution. The code is as below. Hope it helps someone else:)
- (IBAction)myButtonPressed:(id)sender
{
NSString *urlAsString = #"http://api.abc.com/air/1.0/search?from=MAA&to=CJB&depart-date=2012-09-30&adults=2&children=2&infants=1&way=one&cabin-type=Economy&sort=asc";
NSURL *url = [NSURL URLWithString:urlAsString];
NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:url];
[urlRequest setValue:#"2193141de2g7e2a34bb19bc3aa52b3b5" forHTTPHeaderField:#"X-XX-API-KEY"];
[urlRequest setTimeoutInterval:30.0f];
[urlRequest setHTTPMethod:#"GET"];
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
[NSURLConnection
sendAsynchronousRequest:urlRequest queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
{
if ([data length]>0 &&
error == nil)
{
NSString *html = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
NSLog(#"HTML = %#", html);
}
else if ([data length]== 0 && error==nil) {
NSLog(#"Nothing was downloaded");
}
else if (error!= nil) {
NSLog(#"Error occured = %#", error);
}
}];
}
I have been playing around with Cocoa for the last couple of days and I was wondering how I would go about listing all Name/Account pairs of a keychain that I created? the little key chain access app that comes with Mac OS X does that, so it must be possible I presume? Is SecItemCopyMatching what I'm looking for? How do I specify the keychain I want to search, though? And what's a service name in this context?
...am I the only one who thinks the Keychain API in Cocoa is absolutely horrible? I have been reading the documentation up and down for the last couple of hours or so and I'm still getting nowhere :-/
you iterate over the items in your keychain with SecItemCopyMatching and access the password with SecKeychainFindInternetPassword or SecKeychainFindGenericPassword.
Iterate over Keychain:
// iterates over keychain and pass every item found by the query to PrintAccount.
static void IterateOverKeychain() {
// create query
CFMutableDictionaryRef query = CFDictionaryCreateMutable(kCFAllocatorDefault, 3, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFDictionaryAddValue(query, kSecReturnAttributes, kCFBooleanTrue);
CFDictionaryAddValue(query, kSecMatchLimit, kSecMatchLimitAll);
CFDictionaryAddValue(query, kSecClass, kSecClassInternetPassword);
// get search results
CFArrayRef result = nil;
OSStatus status = SecItemCopyMatching(query, (CFTypeRef*)&result);
assert(status == 0);
// do something with the result
CFRange range = CFRangeMake(0, CFArrayGetCount(result));
CFArrayApplyFunction(result, range, PrintAccount, nil);
}
// prints the password for a item from the keychain.
static void PrintAccount(const void *value, void *context) {
CFDictionaryRef dict = value;
CFStringRef acct = CFDictionaryGetValue(dict, kSecAttrAccount);
NSLog(#"%#", acct);
}
Print Password:
static void PrintPassword() {
const char *acct = "foo.bar#googlemail.com";
UInt32 acctLen = (UInt32)strlen(acct);
const char *srvr = "calendar.google.com";
UInt32 srvrLen = (UInt32)strlen(srvr);
UInt32 pwLen = 0;
void *pw = 0;
SecKeychainFindInternetPassword(nil, srvrLen, srvr, 0, nil, acctLen, acct, 0, nil, 0, kSecProtocolTypeAny, kSecAuthenticationTypeAny, &pwLen, &pw, nil);
CFStringRef pwString = CFStringCreateWithBytes(kCFAllocatorDefault, pw, pwLen, kCFStringEncodingUTF8, NO);
NSLog(#"%s %#", acct, pwString);
}
Well, I managed to enumerate over the keychain entries, however the password field is empty. I thought if authorization was required, the program would ask for the keychain password automatically like it usually does?
NSDictionary *query = [NSDictionary dictionaryWithObjectsAndKeys:
(id)kSecClassInternetPassword, kSecClass,
(id)kCFBooleanTrue, kSecReturnData,
(id)kCFBooleanTrue, kSecReturnAttributes,
kSecMatchLimitAll, kSecMatchLimit,
nil];
NSArray *itemDicts = nil;
OSStatus status = SecItemCopyMatching((CFDictionaryRef)query, (CFTypeRef *)&itemDicts);
if (status)
[MessageBox Show:(NSString*)SecCopyErrorMessageString(status, NULL)];
NSMutableArray *arr = [[NSMutableArray alloc] init];
for (NSDictionary *itemDict in itemDicts) {
NSData *data = [itemDict objectForKey:(id)kSecValueData];
NSString *pwd = [[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease];
NSString *acc = [itemDict objectForKey:(id)kSecAttrAccount];
NSString *name = [itemDict objectForKey:(id)kSecAttrLabel];
if(acc != nil) {
NSArray *values = [NSArray arrayWithObjects: (id)name, (id)acc, (id)pwd, nil];
[arr addObject:(id)values];
}
}
[itemDicts release];
NSInteger c = arr.count;
NSString *cnt = [NSString stringWithFormat:#"%d", c];
[MessageBox Show: [arr objectAtIndex:10]];