I have built a custom framework and accompanying resource bundle to be used in other projects. The resource bundle includes various .sqlite and .bin files. I am trying to open a .bin file in another project using my framework with no success.
Let's say my bundle is called CustomFramework.bundle. I have a class ResourceHelper.cpp within my framework that is attempting to open mybin.bin that is located in the CustomFramework.bundle.
Here is how I am currently trying to open it:
void ResourceHelper::openBinFromResourceFolder(FILE **file) {
std::string path;
path = "CustomFramework.bundle/";
path.append("mybin.bin");
*file = fopen(path.c_str(), "rb");
}
file is NULL after this fopen() call.
How am i suppose to open a .bin file within my .bundle?
Thanks
Turns out you have to get the main bundle string and append the resource bundle to that string. Here is how I got it working.
void ResourceHelper::openBinFromResourceFolder(const char *binName, FILE **file) {
std::string path;
// split bin name into name and file type (bin)
std::string binStr = binName;
std::size_t pos = binStr.find(".");
std::string filename = binStr.substr(0, pos);
std::string type = binStr.substr(pos+1);
// get bundle and CFStrings
CFBundleRef mainBundle = CFBundleGetMainBundle();
CFStringRef cf_resource_path = CFStringCreateWithCString(NULL, resourcePath_.c_str(), kCFStringEncodingUTF8);
CFStringRef cf_filename = CFStringCreateWithCString(NULL, filename.c_str(), kCFStringEncodingUTF8);
CFStringRef cf_file_type = CFStringCreateWithCString(NULL, type.c_str(), kCFStringEncodingUTF8);
CFURLRef url_resource = CFBundleCopyResourceURL(mainBundle, cf_filename, cf_file_type, cf_resource_path);
CFStringRef urlString = CFURLCopyFileSystemPath(url_resource, kCFURLPOSIXPathStyle);
path = CFStringGetCStringPtr(urlString, kCFStringEncodingUTF8);
*file = fopen(path.c_str(), "rb");
}
Related
In a project , I have to use an .so file , and the .so need to load a config file that I provide. The problem is that I can not change the code of .so , but the .so load the config file in default path.
When I use rosrun to launch the node , I put the config file in the catkin workspace ,and when I use ./ to launch the node, I put the config file under the same folder with node. My test program is also the result.
My test code:
int main(int argc, char **argv)
{
ros::init(argc, argv, "topic_pub");
ros::NodeHandle nh;
ros::Publisher msg_pub = nh.advertise<rostestdemo::NewMsg>("customize_msg", 20);
ros::Rate loop_rate(10);
rostestdemo::NewMsg msg;
int count = 0;
std::string filename = "hello.txt";
std::ifstream file;
file.open(filename.c_str());
char buf[500] = {0};
file.getline(buf, 500);
while(ros::ok())
{
msg.stamp = ros::Time::now();
msg.data2 = count;
msg.word = buf;
ROS_INFO("stamp.sec = %d", msg.stamp.sec);
ROS_INFO("stamp.nsec = %d", msg.stamp.nsec);
ROS_INFO("data2 = %d", msg.data2);
ROS_INFO("word = %s", msg.word.c_str());
msg_pub.publish(msg);
loop_rate.sleep();
++count;
}
return 0;
}
In addition, I can use absolute path in code or param of launch file to load the file when I use roslaunch to launch the config file. But, when I use default path to load the file, where shuold I put the file when I use roslaunch ?
You are able to search for package paths, so maybe using ros::package::getPath('PKG_NAME') for example, and then you concatenate the config/your_configuration.yaml
I use Cocos2dx 3.9; and use rapidjson to convert CCDictionary to string; I find it will crash in value->Accept(wirter) in Real machine; but work will in Simulator.
rapidjson::StringBuffer buffer;
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
value->Accept(writer);
It's my error, I use:
rapidjson::Value *value = KSCCJsonRapid::jsonValueFromDictionary(dic);
rapidjson::StringBuffer buffer;
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
value->Accept(writer);
std::string jsonString = buffer.GetString();
but in
static rapidjson::Value* jsonValueFromDictionary(cocos2d::CCDictionary *dic, rapidjson::Document *document = NULL);
, I create a new document, and delete it; in Real machine, the memory is limit; so the value is invalid.
it can be corrected by :
rapidjson::Document *document = new Document();
rapidjson::Value *value = KSCCJsonRapid::jsonValueFromDictionary(dic, document);
rapidjson::StringBuffer buffer;
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
value->Accept(writer);
std::string jsonString = buffer.GetString();
delete value;
delete document;
i have a private key. Text File that begins like "--- begin private key..."
i want to use that key to encrypt an NSString. since its the private key, better call it sign an NSString.
can this be done without any external frameworks?
the result should be equivalent to the php openssl_sign function.
The iOS SDK framework you will need to use is called CommonCrypto. Here's a very good article that describes the right way to go about it.
Edit: I missed the part about compatibility with the PHP function openssl_sign. The solution below resolves that.
The way to do this so that it's compatible with the PHP function openssl_sign is to use the OpenSSL library. The openssl_sign function uses OpenSSL's EVP API internally to encrypt the input string using the private key and compute the SHA-1 hash digest of that encrypted string. It's common then to convert this hash digest into a Base64-encoded string.
Unfortunately, the iOS SDK does not include OpenSSL, but it's easy to build it. The following instructions for building OpenSSL for iOS are taken from this blog post and are reproduced here to provide a complete solution to the question.
In Terminal, follow those steps to build the OpenSSL library for iOS:
# Make a directory in which to run the build
mkdir ~/openssl-ios
cd ~/openssl-ios
# Download the openssl source (verify the file before using it in production!)
curl -O http://www.openssl.org/source/openssl-1.0.1e.tar.gz
# Download the openssl iOS build script
curl -O https://raw.github.com/Raphaelios/raphaelios-scripts/master/openssl/build-openssl.sh
# Make the build script executable
chmod +x build-openssl.sh
# Run the script (takes about 3min on an Intel Core i5)
./build-openssl.sh
This will take a few minutes but once it's complete you can verify that the build library is a universal library that you can use on iOS devices and in the iOS Simulator using the following command:
lipo -info ~/openssl-ios/lib/*.a
Now that the OpenSSL library has been built, let's got on with writing the code to sign a string.
First, we need to setup the Xcode project to link against the OpenSSL library. Drag & drop both libcrypto.a and libssl.a to the Frameworks group in the Project Navigator of your iOS project. In your project's Build Settings, add the following to the Header Search Paths setting:
~/openssl-ios/include/include
Next, create a new Objective-C Category file called openssl_sign on the NSString class. In NSString+openssl_sign.h, define the following interface:
#interface NSString (openssl_sign)
- (NSString *)signStringWithPrivateKey:(NSData *)privateKey;
#end
In NSString+openssl_sign.m, add the following header imports:
#import <openssl/evp.h>
#import <openssl/pem.h>
And add the following implementation of signStringWithPrivateKey::
#implementation NSString (openssl_sign)
- (NSString *)signStringWithPrivateKey:(NSData *)privateKeyData
{
BIO *publicBIO = NULL;
EVP_PKEY *privateKey = NULL;
if ((publicBIO = BIO_new_mem_buf((unsigned char *)[privateKeyData bytes], [privateKeyData length])) == NO) {
NSLog(#"BIO_new_mem_buf() failed!");
return nil;
}
if (PEM_read_bio_PrivateKey(publicBIO, &privateKey, NULL, NULL) == NO) {
NSLog(#"PEM_read_bio_PrivateKey() failed!");
return nil;
}
const char * cString = [self cStringUsingEncoding:NSUTF8StringEncoding];
unsigned int stringLength = [self length];
unsigned char * signatureBuffer[EVP_MAX_MD_SIZE];
int signatureLength;
EVP_MD_CTX msgDigestContext;
const EVP_MD * msgDigest = EVP_sha1();
EVP_MD_CTX_init(&msgDigestContext);
EVP_SignInit(&msgDigestContext, msgDigest);
EVP_SignUpdate(&msgDigestContext, cString, stringLength);
if (EVP_SignFinal(&msgDigestContext, (unsigned char *)signatureBuffer, (unsigned int *)&signatureLength, privateKey) == NO) {
NSLog(#"Failed to sign string.");
return nil;
}
EVP_MD_CTX_cleanup(&msgDigestContext);
EVP_PKEY_free(privateKey);
NSData *signatureData = [NSData dataWithBytes:signatureBuffer length:signatureLength];
NSString *signature = [signatureData base64EncodedStringWithOptions:0];
return signature;
}
#end
In the class that will be signing the string, you can now import NSString+openssl_sign.h and sign the string like so:
NSData *privateKey = ...; // Read the .pem file into a NSData variable
NSString *helloSignature = [#"hello" signStringWithPrivateKey:privateKey];
You can verify that the signatures are the same using the following command in Terminal:
echo -n "hello" | openssl dgst -sha1 -sign priv.pem | openssl enc -base64 | tr -d '\n'
You can solve this much easier with no external sources or components.
I found out how and wanted to share it so i may help others.
You need to load the key file a SecKeyRef and safe the maxPlainLen as well
NSString *resourcePath = [[NSBundle mainBundle] pathForResource:privateKeyResourceName ofType:#"p12"];
NSData *p12Data = [NSData dataWithContentsOfFile:resourcePath];
NSMutableDictionary * options = [[NSMutableDictionary alloc] init];
SecKeyRef privateKeyRef = NULL;
//change to the actual password you used here
[options setObject:#"_YOURPASSWORDHERE__" forKey:(__bridge id)kSecImportExportPassphrase];
CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL);
OSStatus securityError = SecPKCS12Import((__bridge CFDataRef) p12Data,
(__bridge CFDictionaryRef)options, &items);
if (securityError == noErr && CFArrayGetCount(items) > 0) {
CFDictionaryRef identityDict = CFArrayGetValueAtIndex(items, 0);
SecIdentityRef identityApp =
(SecIdentityRef)CFDictionaryGetValue(identityDict,
kSecImportItemIdentity);
securityError = SecIdentityCopyPrivateKey(identityApp, &privateKeyRef);
if (securityError != noErr) {
privateKeyRef = NULL;
}
}
CFRelease(items);
privateKey = privateKeyRef;
maxPlainLen = SecKeyGetBlockSize(privateKey) - 12;
You can convert NSString with a category method to SHA1
- (NSData*)toSha1AsData {
// PHP uses ASCII encoding, not UTF
const char *s = [self cStringUsingEncoding:NSASCIIStringEncoding];
NSData *keyData = [NSData dataWithBytes:s length:strlen(s)];
// This is the destination
uint8_t digest[CC_SHA1_DIGEST_LENGTH] = {0};
// This one function does an unkeyed SHA1 hash of your hash data
CC_SHA1(keyData.bytes, keyData.length, digest);
// Now convert to NSData structure to make it usable again
NSData *out = [NSData dataWithBytes:digest length:CC_SHA1_DIGEST_LENGTH]
return out;
}
Now you can sign your SHA1 with this method
(NSData *)signSha1Data:(NSData *)data {
size_t plainLen = [data length];
if (plainLen > maxPlainLen)
{
NSLog(#"content(%ld) is too long, must < %ld", plainLen, maxPlainLen);
return nil;
}
void *plain = malloc(plainLen);
[data getBytes:plain
length:plainLen];
size_t cipherLen = 128; // currently RSA key length is set to 128 bytes
void *cipher = malloc(cipherLen);
OSStatus returnCode = SecKeyRawSign(privateKey, kSecPaddingPKCS1SHA1,
plain, plainLen, cipher, &cipherLen);
NSData *result = nil;
if (returnCode != 0) {
NSLog(#"SecKeyEncrypt fail. Error Code: %ld", returnCode);
}
else {
result = [NSData dataWithBytes:cipher
length:cipherLen];
}
free(plain);
free(cipher);
return result;
}
It works very well and without any external libs. There is no need to compile some wierd openssl stuff.
I need to determine if a file in my app's documents directory is a zip file. The file name cannot be used in making this determination. So I will need to be able read the MIME type or find some other property that only applies to zips.
NOTE: A solution that requires putting the entire file into memory is not ideal as files could potentially be pretty large.
According to http://www.pkware.com/documents/casestudies/APPNOTE.TXT,
a ZIP file starts with the "local file header signature"
0x50, 0x4b, 0x03, 0x04
so it is sufficient to read the first 4 bytes to check if the file is possibly a ZIP file.
A definite decision can only be made if you actually try to extract the file.
There are many methods to read the first 4 bytes of a file. You can use NSFileHandle,
NSInputStream, open/read/close, ... . So this should only be taken as one possible example:
NSFileHandle *fh = [NSFileHandle fileHandleForReadingAtPath:#"/path/to/file"];
NSData *data = [fh readDataOfLength:4];
if ([data length] == 4) {
const char *bytes = [data bytes];
if (bytes[0] == 'P' && bytes[1] == 'K' && bytes[2] == 3 && bytes[3] == 4) {
// File starts with ZIP magic ...
}
}
Swift 4 version:
if let fh = FileHandle(forReadingAtPath: "/path/to/file") {
let data = fh.readData(ofLength: 4)
if data.starts(with: [0x50, 0x4b, 0x03, 0x04]) {
// File starts with ZIP magic ...
}
fh.closeFile()
}
Try this
NSWorkspace *ws = [NSWorkspace sharedWorkspace];
NSString *description = [ws localizedDescriptionForType:[ws typeOfFile:#"/full/path/to/file" error:nil]];
Or for mime this
+ (NSString*) mimeTypeForFileAtPath: (NSString *) path {
if (![[NSFileManager defaultManager] fileExistsAtPath:path]) {
return nil;
}
CFStringRef UTI = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (CFStringRef)[path pathExtension], NULL);
CFStringRef mimeType = UTTypeCopyPreferredTagWithClass (UTI, kUTTagClassMIMEType);
CFRelease(UTI);
if (!mimeType) {
return #"application/octet-stream";
}
return [NSMakeCollectable((NSString *)mimeType) autorelease];
}
I'd just use file, then grep if it has the text "zip" or "Zip Archive" to be safe.
if file -q $FILENAME | grep "Zip archive"; then
echo "zip";
else
echo "not zip";
fi
Since both .zip and .xlsx having the same Magic number, I couldn't find the valid zip file (if renamed).
So, I have used Apache Tika to find the exact document type.
Even if renamed the file type as zip, it finds the exact type.
Reference Apache tika use cases
My cocoa application uses one library written in 'C' which is tryings write file at '/tmp' path. This creates sandbox violations. In Cocoa we can use 'NSTemporaryDirectory' API. To fix sandbox violation Is it safe to use 'tmpfile' API in sandboxed environment? Are there in any other solutions?
EDITED After actually testing it
No, tmpnam() won't work and I think the only way to get a temporary filename is to provide a .m file with your library specifically for use with iOS and OSX, which can be used return the temporary directory as a C-String:
apple.h:
#pragma once
extern size_t getTemporaryDirectory(char *buffer, size_t len);
apple.m:
size_t getTemporaryDirectory(char *buffer, size_t len)
{
#autoreleasepool
{
NSString *tempDir = NSTemporaryDirectory();
if (tempDir != nil)
{
const char *utf = [tempDir UTF8String];
strncpy(buffer, utf, len);
return strlen(utf);
}
}
return 0;
}