NSURLConnection using basic auth fails on Tiger, succeeds on Panther and Leopard - nsurlconnection

I have a program running on Panther, Tiger, and Leopard systems and using the following method for NSURLConnection authentication ("encodedUserPass" is the auth string with the word Basic followed by the base64-encoded user:pass)
[theRequest addValue:encodedUserPass forHTTPHeaderField:#"Authorization"];
NSURLConnection *theConnection=[[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
...later...in didReceiveAuthenticationChallenge
if ([challenge previousFailureCount] == 0) {
NSURLCredential *newCredential;
newCredential=[NSURLCredential credentialWithUser:login_name password:password persistence:NSURLCredentialPersistenceNone];
[[challenge sender] useCredential:newCredential forAuthenticationChallenge:challenge];
}
This works perfectly fine on Panther and Leopard systems but fails utterly on Tiger. It is odd, though, that even on Panther and Leopard "didReceiveAuthenticationChallenge" is usually called (i.e., setting the headers manually seems not to work).
On Tiger, didReceiveAuthenticationChallenge is always called, tries to respond as shown above, and is then called again with failure.
Two questions: (i) Why doesn't setting the headers manually work? and (2) Why does the method shown above fail on Tiger (10.4)?
LATER UPDATE:
After some thought, I realized that there had to be something wrong with my base64-encoding method, and there was: I didn't append equals signs to bring the base64 string up to a multiple of 4 characters. I solved it with
while ([bareString length] % 4) [bareString appendString:#"="];
And now the program works on all three platforms. So question (i) is answered: setting the headers manually didn't work because I wasn't padding with equals signs.
Question (ii) remains, though: why can't I use didReceiveAuthenticationChallenge successfuly in Tiger?

I have just stumbled into the same problem that you describe, and found that NSURLCredentialPersistenceNone simply does not work on Tiger, whereas specifying NSURLCredentialPersistenceForSession does.
Depending on your application that may or may not be an acceptable workaround.
Apple's documentation leaves a bit to be desired as it doesn't specify what the scope of a 'session' actually is - until the application quits, maybe?

Related

XCTest self measureBlock modification?

It appears that XCtest "self measureBlock" is limited to milliseconds and 10 runs of the test code. Are there any ways to modify the behavior of measureBlock for more runs and/or nano or microsecond accuracy?
TL;DR
Apple provides a way to modify the behavior of measureBlock: by providing extra string constants but they don't support any string constants other than the default.
Long explanation
measureBlock calls the following function
- (void)measureMetrics:(NSArray *)metrics automaticallyStartMeasuring:(BOOL)automaticallyStartMeasuring withBlock:(void (^)(void))block;
//The implementation looks something like this (I can't say 100% but i'm pretty sure):
- (void)measureBlock:(void (^)(void))block {
NSArray<NSString *> *metrics = [[self class] defaultPerformanceMetrics];
[self measureMetrics:metrics automaticallyStartMeasure:YES withBlock:block];
}
defaultPerformanceMetrics is a class function that returns an array of strings.
From the Xcode source
"Subclasses can override this to change the behavior of
-measureBlock:"
Lovely, that sounds promising; we have customization behavior right? Well, they don't give you any strings to return. The default is XCTPerformanceMetric_WallClockTime ("com.apple.XCTPerformanceMetric_WallClockTime")
It turns out there aren't any string constants to return besides that one.
See the slides for WWDC 2014 Session 414 Testing in Xcode 6 (link).
I quote from page 158:
Currently supports one metric: XCTPerformanceMetric_WallClockTime
Nothing else has been added in Xcode 7 so it seems you're out of luck trying to modify measureBlock, sorry.
I've never found measureBlock: very useful. Check out Tidbits.xcodeproj/TidbitsTestBase/TBTestHelpers/comparePerformance https://github.com/tipbit/tidbits if you'd like to look at an alternative.

greenline Xcode EXC_BAD_INSTRUCTION on NSUserDefaults Optional

I've been writing an app that involves using NSUserDefaults to store a few Int variables and it's been working fine. I thought I was finished and was doing some final testing and one of the first lines of code that I wrote, and that has been working consistently before, has failed me.
Apparently the green line error is supposed to occur if I try to unwrap an optional that has a value of nil, but this variable is still very much an optional
var savedTotalSeconds: Int? = userDefaults.objectForKey("totalSecondsKey") as Int?
Why would this possibly return an error? It was working fine before and I only changed things I thought were unrelated to it. In the app I have a button to remove this stored value via:
userDefaults.removeObjectForKey("totalSecondsKey")
What could possibly have gone wrong?
Try using 'as? Int' instead of 'as Int?'
The difference is that the first one tries, and might fail, at casting to Int. That failure will be captured in the optionality of the resulting variable.
The second one tries to coerce the object to 'Int?'.

Swift: using NSRunningApplication

I'm trying to activate a running application, but can't seem to get the app.activateWithOptions call correct as each of the four attempts below result in compile time errors.
import AppKit
var ws = NSWorkspace.sharedWorkspace()
var apps = ws.runningApplications
var app :NSRunningApplication
for app in apps {
if (app.activationPolicy == NSApplicationActivationPolicy.Regular) {
app.activateWithOptions(options: ActivateIgnoringOtherApps)
app.activateWithOptions(options: NSApplicationActivateIgnoringOtherApps)
app.activateWithOptions(options: NSRunningApplication.ActivateIgnoringOtherApps)
app.activateWithOptions(options: NSRunningApplication.NSApplicationActivateIgnoringOtherApps)
println(app.localizedName)
}
}
The declaration var app : NSRunningApplication is pointless, because the app in the for...in line declares a different app. You should just delete that line, as it is misleading you and is utterly pointless and confusing.
Thus, it is that app, the one in the for...in that you need to specify the type of. You won't get any further in this attempt to compile until you fix that:
for app in apps as [NSRunningApplication] {
Now you can begin to fix your errors, one by one. I'll just give you a hint for the first one; it should be:
app.activateWithOptions(.ActivateIgnoringOtherApps)
The remaining three lines are left as an exercise for the reader! Even then you will still be in some trouble, though, because you are not combining the options - instead, you are activating the same app four separate times, which is silly and not at all what you want. You should combine the options first and then active the app once.
On the whole, it looks from your code as if you do not know Swift at all. You really should stop and learn it before you try to use it. Otherwise you'll just be flailing (as you clearly are now).
Thanks for the answer Matt. I'll add that if you are looking for just your application (rather than you can add a line before activating it. The following two lines will assign your application to the constant "app" and then activates it. This is useful for when yo want to get an NSAlert on the screen from an app running in the background.
let app = NSRunningApplication.currentApplication()
app.activateWithOptions(.ActivateIgnoringOtherApps)
let user_choice = alert.runModal()

Segmentation fault on Lion creating NSData from RubyCocoa with any bytes > 127

The following RubyCocoa is fine on Max OS X 10.6, but segfaults on 10.7
require 'osx/cocoa'
include OSX
bytes = [128].pack('i1')
NSData.alloc.initWithBytes_length(bytes, bytes.length)
In all cases it works when the top bit is not set. In fact NSData.alloc seems to fail when passed a buffer where any of the bytes have the top bit set.
The version of Ruby is 1.8.7 on both OS's, so I'm at a loss to diagnose why NSData is interpreting the buffer differently. Can anyone shed any light?
You should probably go for MacRuby as it will replace RubyCocoa. It's possible that RubyCocoa does not (and will never) work properly on Lion.
I don't have any MacRuby experience with Lion yet, but chances are good that it will work.
MacRuby is still unfinished - it just doesn't work with some of the ruby code I work with.
I've run into the same NSData problem, and I was able to create a CFData instance which appears ok with the following code
gem 'RubyInline'
require 'inline'
class CFDataWrapper
begin
inline do |builder|
builder.include "<CoreFoundation/CoreFoundation.h>"
builder.c_raw_singleton "
static VALUE fromString(int argc, VALUE *args){
CFDataRef d = CFDataCreate(NULL, RSTRING_PTR(args[0]), RSTRING_LEN(args[0]));
VALUE result;
char type = '#';
ocdata_to_rbobj(Qnil,&type, (const void *)&d, &result, false) ;
return result;
}
"
end
end
end
bytes = [128].pack('i1')
data = CFDataWrapper.fromString(bytes)
puts data.inspect
The output from inspect is different from under 10.6 but I can pass it back into methods that expect NSData instances and they appear to work, call NSData methods on it etc. Beyond this toy script it works a script i use to populate core data documents, where one of the entities has a binary data attribute
I will guess that the problem is actually pack corrupting memory because it is not handling the signed overflow in a good way. Some things to try that may lead you to the answer:
try pack('C1')
Dump out bytes, compare to irb's output.
call some other function which allocates memory right after the pack (not using bytes at all).
This is fixed in RubyCocoa 1.0.2

Sporadic errSecAuthFailed (-25293) when accessing KeyChain with SecKeychainFindGenericPassword?

i'm writing an app that stores passwords on the keychain, and later obtains them with SecKeychainFindGenericPassword(). this works file 90% of the time, but every once in a while, the call to SecKeychainFindGenericPassword() will fail with errSecAuthFailed (-25293). when it does, just trying again, or restarting the app fixes it.
does anyone have an idea what could be causing this? general Google search on this error points to keychain corruption or the keychain being locked - neither of which is the case here, since subsequent calls succeed again...
This link suggests the passphrase you entered is not correct. See Here
Is it possible that sometimes you are sending a null object as a passphrase just by chance?
Alternatively, you could try out EMKeychain. I have a more up-to-date version on GitHub here: http://github.com/ctshryock/EMKeychain
You haven't shared the code around your problem, So I'll just guess your problem is not with dysfunctional keychain, but rather some coding error.
Here is a common pitfall: Since KeyChain APIs are 'C', and they only accept C-style null terminated string buffers, you'll usually need to convert your CFString/NSString objects to C buffers before handing them to the API.
Many use things like:
const char *usernameCStr = [username UTF8String];
For an NSString, or its CFString companion...
const char *CFStringGetCStringPtr(CFStringRef theString, CFStringEncoding encoding); /* May return NULL at any time; be prepared for NULL */
Dismissing the fact these APIs may return NULL. Either because the internal buffer of the CF/NSString is non-contiguous, or not in the encoding you asked for, or otherwise not-c-compatible.
Such issue can behave in runtime exactly like what you describe.
In such cases you should catch the problem and use different API to copy the CF/NS string into a C-buffer:
Boolean CFStringGetCString(CFStringRef theString, char *buffer, CFIndex bufferSize, CFStringEncoding encoding);
or
- (BOOL)getCString:(char *)buffer maxLength:(NSUInteger)maxBufferCount encoding:(NSStringEncoding)encoding;
I'm not sure this was the problem (i don't see how it could have been) but i recently changed my code to properly pass the strlen() of the cStrings, rather then the length of the NSStrings into the call. Technically this is more correct (since the string length might differ from the cString, if UTF-8 dual-byte characters are involved.
BUT, none of the usernames/passwords i tested with contained non-ASCII characters, so i don;t see how this problem could have actually affected the errors I was seeing. My new code is as follows, and i have not seen the error with it:
UInt32 length;
void *data;
const char *account = [[BC_HOST stringByAppendingFormat:#":%#", login] cStringUsingEncoding:NSUTF8StringEncoding];
NSLog(#"Getting password from keychain.");
OSStatus s = SecKeychainFindGenericPassword (nil,
strlen(BC_APPNAME), BC_APPNAME,
strlen(account), account,
&length, &data, &keychainItem);
if (s != 0) NSLog(#"Error %d obtaining password from keychain.", s);
if (s == 0)
{
password = [[NSString alloc] initWithBytes:data length:length encoding:NSUTF8StringEncoding];
}
I was having the same issue and in my case this turned out to be the cause: Cannot access keychain item after SMJobBless update
One possible reason that this issue can occur is if the executable making the call does not have access to the keychain item. In Keychain Access you can see a list of the apps that have permission to access the item under the Access Control tab for the item in question.
If your app is running from a different location, you will get this error. For example, I have a Privileged Helper Tool that on my dev machine I typically run through Xcode as root. The path to this executable is the path at which Xcode creates it which is a path in ~/Library/Developer/Xcode/DerivedData//myexecutable. When I run it as a user would, it is being run from /Library/PrivilegedHelperTools/myexecutable. So if the password was initially created by one version of the app and I try to read it using the other path, I will see the errSecAuthFailed error.
This isn't the only reason. Someone else mentioned the upgrade in place problem that SMJobBless has. That can also result in the same error code, but for sure I see it for both reasons - although I programmatically solved the upgrade in place issue by ordering the helper tool to move itself to a different location before I do the upgrade.

Resources