The method -writeData: of NSFileHandle class returns nothing. Is there any way for us to determine whether the operation is successful or not? Or I should use other way to save my data?
According to writeData method Reference, this method raises an exception if the file descriptor is closed or is not valid, if the receiver represents an unconnected pipe or socket endpoint, if no free space is left on the file system, or if any other writing error occurs.
You can write any standard cocoa object to a file pretty simple. This returns a BOOL value if it is sucessful or not
BOOL result = [YOUR_OBJECT writeToFile:#"absolute/file/path" atomically:YES]];
if (result)
NSLog(#"All went well");
else
NSLog(#"File was not saved");
Related
I have a SecTrustRef object from the system that I'd like to evaluate myself. Just calling SecTrustEvaluateAsync will be sufficient for this job. The problem is, I must evaluate it in a different process as only this other process has access to the keychains where the CA certificates are stored that may cause evaluation to succeed.
The two processes have an IPC link that allows me to exchange arbitrary byte data between them but I don't see any way to easily serialize a SecTrustRef into byte data and deserialize that data back to an object at the other process. There doesn't seem to be a persistent storage mechanism for SecTrustRef.
So am I overlooking something important here, or do I really have to get all the certs (SecTrustGetCertificateAtIndex) and all the policies (SecTrustCopyPolicies) and serialize these myself?
And if so, how would I serialize a policy?
For the certificate (SecCertificateRef) it's rather easy, I just call SecCertificateCopyData and later on SecCertificateCreateWithData.
But for policies I can only call SecPolicyCopyProperties on one side and later on SecPolicyCreateWithProperties, however the later one requires a 2nd parameter, a policyIdentifier and I see no way to get that value from an existing policy. What am I missing?
Reading through the source of the Security framework, I finally figured it out how to copy a SecPolicyRef:
SecPolicyCreateWithProperties wants what it calls a "policyIdentifier". It's a constant like kSecPolicyAppleIPsec.
This does not get stored directly by the function, it's comparing the value and calling dedicated internal "initializers" (like SecPolicyCreateIPsec).
These in turn call SecPolicyCreate (which is private). They end up passing the same identifier value that you passed to SecPolicyCreateWithProperties.
And this value then gets stored as-is in the _oid field!
The identifier is actually the OID. You can get it either via SecPolicyCopyProperties(policy) (stored in the dictionary with key kSecPolicyOid) or via SecPolicyGetOID (but that returns it as an inconvenient CSSM_OID). Some of those specialized initializers also use values from the properties dictionary passed to SecPolicyCreateWithProperties, those should be present in the copied properties dictionary already.
So this gives us:
Serialization:
CFDictionaryRef createSerializedPolicy(SecPolicyRef policy) {
// Already contains all the information needed.
return SecPolicyCopyProperties(policy);
}
Deserialization:
SecPolicyRef createDeserializedPolicy (CFDictionaryRef serialized) {
CFStringRef oid = CFDictionaryGetValue(serialized, kSecPolicyOid);
if (oid == NULL || CFGetTypeID(oid) != CFStringGetTypeID()) {
return NULL;
}
return SecPolicyCreateWithProperties(oid, serialized);
}
To reproduce the original SecTrustRef as closely as possible, the anchors need to be copied as well. There is an internal variable _anchorsOnly which is set to true once you set anchors. Unfortunately, there is no way to query this value and I've seen it being false in trusts passed by NSURLSession, for example. No idea yet on how to get this value in a public way.
Another problematic bit are the exceptions: if _exceptions is NULL but you query them via SecTrustCopyExceptions(trust), you do get data! And if you assign that to the deserialized trust via SecTrustSetExceptions(trust, exceptions) you suddenly end up with exceptions that were not there before and can change the evaluation result! (I've seen those suddenly appearing exceptions lead to an evaluation result of "proceed" instead of "recoverable trust failure").
When I setup an HKObserverQuery, the update handler always gets immediately called (something I didn't expect). It also gets called when I add data points through Health.app, as you would expect. I am tending to think I am not doing something right with the completion handler, but the docs are fairly sparse on what is supposed to happen here.
Question: Below is basically what I'm doing. Is this expected behavior, or am I missing something?
func listenForUpdates() {
let bodyMassType = HKQuantityType.quantityTypeForIdentifier(HKQuantityTypeIdentifierBodyMass)
let updateHandler: (HKObserverQuery!, HKObserverQueryCompletionHandler!, NSError!) -> Void = { query, completion, error in
if !error {
println("got an update")
// ... perform a sample query to get the actual data
completion() // is this the right thing to do?
} else {
println("observer query returned error: \(error)")
}
}
let query = HKObserverQuery(sampleType: bodyMassType, predicate: nil, updateHandler: updateHandler)
healthStore?.executeQuery(query)
}
Edit: discovered completion handler should only be called when there wasn't an error, so moved into the !error block. An error is present when the app is not authorized.
Yes, this is expected behavior. The update handler will always be called on first execution so that you can use it to fetch your initial data (from your sample query, anchored object query, etc) and populate your UI.
The completion handler is only necessary if you intend to use background delivery, it informs HealthKit that you have received and processed the data you need so that HealthKit knows to stop launching your app in the background. If you have not registered your app for background delivery, then the completion handler is essentially a no-op and you don't need to worry about it.
I am failing to create a file in windows using the CreateFile API, GetLastError returns the error code 80 which means the file exists, but actually the file was not existing.
hFile = CreateFile((LPCTSTR) FILEPATH, // name of the write
GENERIC_READ|GENERIC_WRITE, // open for writing
0, // do not share
NULL, // default security
CREATE_ALWAYS, // create new file only
FILE_ATTRIBUTE_NORMAL, // normal file
NULL); // no attr. template
printf("GET LAST ERROR VALUE IS: %d\n", GetLastError());
What am I doing wrong?
Your error checking is wrong. The documentation says:
Return value
If the function succeeds, the return value is an open handle to the specified file, device, named pipe, or mail slot.
If the function fails, the return value is INVALID_HANDLE_VALUE. To get extended error information, call GetLastError.
In other words, failure is determined by the return value. You cannot use GetLastError to determine failure. You must check the return value and compare against INVALID_HANDLE_VALUE. When you do so I predict that you will find that the return value is not equal to INVALID_HANDLE_VALUE.
In fact, this API uses the last error value to convey extra information even when the function succeeds.
From the documentation of CREATE_ALWAYS:
If the specified file exists and is writable, the function overwrites the file, the function succeeds, and last-error code is set to ERROR_ALREADY_EXISTS (183).
And from the documentation of CREATE_NEW:
Creates a new file, only if it does not already exist.
If the specified file exists, the function fails and the last-error code is set to ERROR_FILE_EXISTS (80).
And so on.
The golden rule, one that you must burn into your memory, is that error checking varies from function to function and that you must read the documentation from top to tail.
Note that I am rather sceptical of your (LPCTSTR) cast. That's just asking for trouble. If the path is the wrong type, the compiler will save you from yourself, unless you use that cast. That cast just tells the compiler to shut up. But here it knows better. That cast will allow you to pass ANSI text to a wide API and vice versa. You really should remove it.
GetLastError can cause trouble.
Note the docs say
"If the function fails, the return value is INVALID_HANDLE_VALUE. To
get extended error information, call GetLastError."
So, first, only call GetLastError if you get and INVALID_HANDLE_VALUE handle back from CreateFile.
Second, the last error code can be the , well, liertally, last error code - i.e. the most recent call may be OK, but something previously failed: again from the docs
"If the function is not documented to set the last-error code, the value returned by
this function is simply the most recent last-error
code to have been set; some functions set the last-error code to 0 on
success and others do not."
I am building an SNMP Agent for a Windows application using the Microsoft WinSNMP API. Currently everything is working for single-item get and set-request, and also for get-next to allow walking the defined tree (albeit with some caveats that are not relevant to this question).
I am now looking at multi-item get and also get-bulk.
My current procedure is to iterate through the list of requested items (the varbindlist within the PDU), treating each one individually, effectively causing an internal get. The result is added to the VBL, set into the PDU, and then sent back to the SNMP Manager, taking into account invalid requests, etc.
My question is how should I handle "too much" data (data that cannot fit into a single transport layer message)? Or more accurately, is there a way to test whether data is "too big" without actually attempting to transmit? The only way I can see in the API is to try sending, check the error, and try again.
In the case of a get-request this isn't a problem - if you can't return all of the requested data, you fail: so attempt sending, and if the error report is SNMPAPI_TL_PDU_TOO_BIG, send a default "error" PDU.
However, it is allowable for a response to bulk-get to return partial results.
The only way I can see to handle this is a tedious (?) loop of removing an item and trying again. Something similar to the following (some detail removed for brevity):
// Create an empty varbindlist
vbl = SnmpCreateVbl(session, NULL, NULL);
// Add all items to the list
SnmpSetVb(vbl, &oid, &value); // for each OID/Value pair
// Create the PDU
pdu = SnmpCreatePdu(session, SNMP_PDU_RESPONSE, ..., vbl);
bool retry;
do {
retry = false;
smiINT failed = SnmpSendMsg(session, ..., pdu);
if (failed && SNMPAPI_TL_PDU_TOO_BIG == SnmpGetLastError()) {
// too much data, delete the last vb
SnmpDeleteVb(vbl, SnmpCountVbl(vbl));
SnmpSetPduData(pdu, ..., vbl);
retry = true;
};
} while(retry);
This doesn't seem like an optimal approach - so is there another way that I've missed?
As a side-note, I know about libraries such as net-snmp, but my question is specific to the Microsoft API.
The RFC does require you to do what you pasted,
https://www.rfc-editor.org/rfc/rfc3416
Read page 16.
There does not seem to be a function exposed by WinSNMP API that can do this for you, so you have to write your own logic to handle it.
Looking at the Error Handling Programming Guide, I understand that in cases like the following I must never evaluate the NSError parameter theError unless the return value is NO
BOOL success = [myDoc writeToURL:[self docURL] ofType:#"html" error:&theError];
What happens in casses when the return value is not a success BOOL, and instead it is a collection object?
Take for example [NSIncrementalStore obtainPermanentIDsForObjects:error]. I must not evaluate an error using the error parameter. What should I look for? I imagine I could receive either a nil pointer or an empty array. In this case an empty array does not seem a good response, although in other functions an empty array might be a good one. I can read that if an error occurs, the error object will contain a description of what happened, but it is not so clear how can I find out that an error actually happened.
What is the normal Cocoa or Foundation way to indicate an error in cases like these?
For that method, you're expected to return an array of as many items as you had objects passed in. I'd say that if you don't do that (either the array is nil or empty), you're in an error condition, since you're violating what the documentation says will happen:
An array containing the object IDs for the objects in array.
The returned array must return the object IDs in the same order as the objects appear in array