How to get locale on iPhone in C++? - iphone

Everything I see about iPhone localization is, unsurprisingly, in Objective-C. The project I'm working on is already written and working on iPhone using almost entirely C++, and we have a few complete translations already. All we need now, is a way to find out the locale/language code. On the computer, this is done using getenv, checking "LANG", or if that's not set "LC_ALL". This doesn't seem to work on the iPhone (neither is set to anything), so I need some other method.
As far as I can tell, the best way to do it with Objective-C is:
NSString* languageCode = [[NSLocale preferredLanguages] objectAtIndex:0];
But then I'd have to convert from NSString* to char*/std::string (which can be done, but it's generally annoying/messy). So I'm wondering, is there an easier way to get the locale from C++ directly?
Here's what I ended up doing:
#if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR
#include <CoreFoundation/CoreFoundation.h>
#endif
/* ... */
#if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR
CFArrayRef localeIDs = CFLocaleCopyPreferredLanguages();
if (localeIDs)
{
CFStringRef localeID = (CFStringRef)CFArrayGetValueAtIndex(localeIDs, 0);
char tmp[16];
if (CFStringGetCString(localeID, tmp, 16, kCFStringEncodingUTF8))
locale = std::string(tmp); //this is the std::string
CFRelease(localeIDs);
}
#endif

Probably you want to use CFLocaleGetValue()

Related

get IMEI on iPhone with CoreTelephony?

I have tried the accepted answer of
How to get IMEI on iPhone?
but I got an empty string.
I saw somebody suggested to use CoreTelephony framework,
but I am not sure how to use it to obtain the IMEI.
Any suggestion on how to use this private API?
NOTE: this does not work anymore!
Haven't tested on any new iOS.
You have to add CoreTelephony.h to your project.
Make sure the header has
int * _CTServerConnectionCopyMobileEquipmentInfo (
struct CTResult * Status,
struct __CTServerConnection * Connection,
CFMutableDictionaryRef * Dictionary
);
Then you can try this code:
#import "CoreTelephony.h"
void getImei() {
struct CTResult it;
CFMutableDictionaryRef kCTDict;
conn = _CTServerConnectionCreate(kCFAllocatorDefault, ConnectionCallback,NULL);
_CTServerConnectionCopyMobileEquipmentInfo(&it, conn, &kCTDict);
NSLog (# "kCTDict is %#", kCTDict);
CFStringRef meid = CFDictionaryGetValue(kCTDict, CFSTR("kCTMobileEquipmentInfoMEID"));
NSLog (# "kCTMobileEquipmentInfoMEID is %#", meid);
CFStringRef mobileId = CFDictionaryGetValue(kCTDict, CFSTR("kCTMobileEquipmentInfoCurrentMobileId"));
NSLog (# "kCTMobileEquipmentInfoCurrentMobileId is %#", mobileId);
}
Here's the CoreTelephony.h
You can check my example project.
Note: I don't think the code works on the simulator and your app might get rejected.

CCKeyDerivationPBKDF on iOS5

I'm trying to write a password encryption function into my app, following this article.
I wrote a function that runs the CCCalibratePBKDF function and outputs the number of rounds.
const uint32_t oneSecond = 1000;
uint rounds = CCCalibratePBKDF(kCCPBKDF2,
predictedPasswordLength,
predictedSaltLength,
kCCPRFHmacAlgSHA256,
kCCKeySizeAES128,
oneSecond);
This works perfectly, but when I try to implement the next part it all goes wrong.
I can start writing the CCKeyDerivationPBKDF function call and it auto-completes the function and all the parameters. As I go through filling it in all the parameters are also auto-completed.
- (NSData *)authenticationDataForPassword: (NSString *)password salt: (NSData *)salt rounds: (uint) rounds
{
const NSString *plainData = #"Fuzzy Aliens";
uint8_t key[kCCKeySizeAES128] = {0};
int keyDerivationResult = CCKeyDerivationPBKDF(kCCPBKDF2,
[password UTF8String],
[password lengthOfBytesUsingEncoding: NSUTF8StringEncoding],
[salt bytes],
[salt length],
kCCPRFHmacAlgSHA256,
rounds,
key,
kCCKeySizeAES128);
if (keyDerivationResult == kCCParamError) {
//you shouldn't get here with the parameters as above
return nil;
}
uint8_t hmac[CC_SHA256_DIGEST_LENGTH] = {0};
CCHmac(kCCHmacAlgSHA256,
key,
kCCKeySizeAES128,
[plainData UTF8String],
[plainData lengthOfBytesUsingEncoding: NSUTF8StringEncoding],
hmac);
NSData *hmacData = [NSData dataWithBytes: hmac length: CC_SHA256_DIGEST_LENGTH];
return hmacData;
}
But as soon as I hit ; it marks an error saying "No matching function for call to 'CCKeyDerivationPBKDF'" and it won't build or anything.
I've imported CommonCrypto/CommonKeyDerivation.h and CommonCrypto/CommonCryptor.h as both of these were necessary for the enum names.
First, make sure that you haven't done anything funny with your include path (in particular, I do not recommend #HachiEthan's solution, which just confuses things). In general, leave this alone, and specifically don't add things like /usr/include to it. Make sure you've added Security.framework to your link step. This is the usual cause of problems.
The biggest thing you want to be sure of is that you're getting the iOS 5 Security.framework (rather than some other version like the OS X 10.6 or iOS 4 versions). But my suspicion is that you have a problem with your build settings.
If you want to see a framework that does all of this for reference, take a look at RNCryptor.
Right, I've found the problem (and solution).
Because I was using ZXing I had to rename the .m file to .mm so it could run the C++ stuff in the ZXing library.
I don't know why but renaming the file this way broke the CCKeyDerivationPBKDF function.
I've now moved the crypto code into it's own class and left it as .m and all I need now is to include the two imports as I did in the original post.
I didn't have to include any frameworks or anything.

Write stderr on iPhone to both file and console

I'm following the suggestion in the answer here for redirecting NSLog output on an iOS device to a file, which works great. The problem is that it no longer shows up in the console on the device. What I'd really like is a way to tee the stderr stream to both the console and the file. Does anyone have an idea how to do that?
I found an acceptable answer on another thread (NSLog() to both console and file).
The solution provided there is to only redirect to a file if a debugger is not detected, like this:
if (!isatty(STDERR_FILENO))
{
// Redirection code
}
Thanks to Sailesh for that answer.
Once you freopen() the file descriptor, you can read from it and do as you please with the data. Some ideas from this will be useful to you.
You could either write it back out to stdout, or try to write directly to /dev/console. I've never tried to open /dev/console on an iPhone, but I'm guessing it's possible despite being outside of the sandbox. I'm not sure how the app review process will treat it.
Or you can redirect to a TCP socket and view on a remote telnet client. No need for XCode this way!
Basically:
Create a standard C function which calls an Obj-C static method:
void tcpLogg_log(NSString* fmt, ...)
{
va_list args;
va_start(args, fmt);
[TCPLogger tcpLog:fmt :args];
va_end(args);
}
The static Obj-C method:
(void)tcpLog:(NSString*)fmt :(va_list)args
{
NSLogv(fmt, args);
if(sharedSingleton != nil && sharedSingleton.socket != nil)
{
NSString *time = [sharedSingleton.dateFormat stringFromDate:[NSDate date]];
NSString *msg = [[NSString alloc] initWithFormat:fmt arguments:args];
mach_port_t tid = pthread_mach_thread_np(pthread_self());
NSString *str = [NSString stringWithFormat:#"%#[%X]: %#\r\n", time, tid, msg];
NSData *data = [str dataUsingEncoding:NSUTF8StringEncoding];
[sharedSingleton.socket writeData:data
withTimeout:NETWORK_CLIENT_TIMEOUT_PERIOD
tag:0];
}
}
Then in your .pch file, add the following lines to override NSLog()
define NSLog(...) tcpLogg_log(__VA_ARGS__);
void tcpLogg_log(NSString* fmt, ...);
Of course more details are required to handle the TCP Socket. Working source code is available here:
https://github.com/driedler/iOS-TCP-Logger/wiki/About

Executable encryption check anti piracy measure

I read a very interesting blog about implementing some anti-piracy protection into your apps. Some of them dont work anymore, some of them do. The 2 ones that still are effective to an extent are the 2 last ones listed.
http://shmoopi.wordpress.com/2011/06/19/27/
The one I'm interested in is the very last one. Code below. I've implemented this in my AppDelegate.m
Anti piracy via the encryption check.
Required Headers
#import <dlfcn.h>
#import <mach-o/dyld.h>
#import <TargetConditionals.h>
Encryption Struct
#if TARGET_IPHONE_SIMULATOR && !defined(LC_ENCRYPTION_INFO)
#define LC_ENCRYPTION_INFO 0x21
struct encryption_info_command
{
uint32_t cmd;
uint32_t cmdsize;
uint32_t cryptoff;
uint32_t cryptsize;
uint32_t cryptid;
};
#endif
Needed Methods
int main (int argc, char *argv[]);
static BOOL is_encrypted ()
{
const struct mach_header *header;
Dl_info dlinfo;
/* Fetch the dlinfo for main() */
if (dladdr(main, &dlinfo) == 0 || dlinfo.dli_fbase == NULL)
{
NSLog(#"Could not find main() symbol (very odd)");
return NO;
}
header = dlinfo.dli_fbase;
/* Compute the image size and search for a UUID */
struct load_command *cmd = (struct load_command *) (header+1);
for (uint32_t i = 0; cmd != NULL && i < header->ncmds; i++)
{
/* Encryption info segment */
if (cmd->cmd == LC_ENCRYPTION_INFO)
{
struct encryption_info_command *crypt_cmd = (struct encryption_info_command *) cmd;
/* Check if binary encryption is enabled */
if (crypt_cmd->cryptid < 1)
{
return NO;
}
return YES;
}
cmd = (struct load_command *) ((uint8_t *) cmd + cmd->cmdsize);
}
return NO;
}
This method checks to see if the binary is still encrypted.
When I run this on the device attached to x-code it gives me a false positive on this line
if (crypt_cmd->cryptid < 1)
{
NSLog(#"Pirated from (crypt_cmd->cryptid < 1) ");
return NO;
}
I was wondering is it possible that the builds xcode puts onto the device for debugging purposes not encrypted? And its only encrypted when the build is submitted to Apple for use on iTunes. Hence why I am getting this false positive when check the code.
Many Thanks,
-Code
This code won't work successfully on a 64-bit device like the iPhone 5s. The header has been changed from mach_header to mach_header_64 and the command ID is now LC_ENCRYPTION_INFO_64.
What I did was to read the header and then see what the magic number was. If it's MH_MAGIC_64 then you're on a 64-bit device and you need to use the mach_header_64 struct and look for LC_ENCRYPTION_INFO_64 (defined as 0x2C) instead of LC_ENCRYPTION_INFO.
A better otool command to see whether a file is encrypted or not is:
otool -arch armv7 -l YourAppName | grep crypt
I have been looking into this recently as well and tested with the same results. It turns out this code is telling you YES or NO based on whether the binary is encrypted with Apple's FairPlay DRM. Any debug or ad-hoc builds you do will say NO.
You can see the same information on your binary or any iPhone apps you have purchased using the otool command-line tool.
For your own binaries, find the binary in your project under e.g. build/Debug-iphoneos/MyApp.app and run (from Terminal)
otool -l MyApp | more
Scan through for cryptid in the LC_ENCRYPTION_INFO section. Since this is a debug build it will be 0. If you have synched your phone to your computer, check under ~/Music/iTunes/Mobile Applications and pick an .ipa file. Unzip it and try otool against the binary from the .ipa and it should have 1 for the cryptid.
It looks like this is looking for the signature block in the dyload header. This means that you're only going to see this on code which is signed. Chances are that your code isn't being automatically signed for debugging (unnecessary), although it will be signed when it goes to the device.
You might want to make this entire check conditional on the project running on an iOS device instead of in the simulator. Any binary sent to an iOS device must be signed.
#if !(TARGET_IPHONE_SIMULATOR)
your check
#endif //

How to reference an environment variable inside Obj-C code

I define a path variable in Xcode source tree called "MY_SRC_DIR". I would like to get the value of this environment variable and put it in a NSString in the obj-c code. For example,
-(NSString*) getSourceDir
{
return #"${MY_SRC_DIR}"; // not the right solution and this is the question
}
From http://rosettacode.org/wiki/Environment_variables#Objective-C:
[[NSProcessInfo processInfo] environment] returns an NSDictionary of the current environment.
For example:
[[[NSProcessInfo processInfo] environment] objectForKey:#"MY_SRC_DIR"]
Just expose the desired var into the Environment Variables list of your current Xcode's deployment Scheme and you'll be able to retrieve it at runtime like this:
NSString *buildConfiguration = [[NSProcessInfo processInfo] environment][#"BUILD_CONFIGURATION"];
It also applies to swift based projects.
Hope it helps!! :]
Here is another way to do it:
.xcconfig file:
FIRST_PRESIDENT = '#"Washington, George"'
GCC_PREPROCESSOR_DEFINITIONS = MACRO_FIRST_PRESIDENT=$(FIRST_PRESIDENT)
objective C code:
#ifdef FIRST_PRESIDENT
NSLog(#"FIRST_PRESIDENT is defined");
#else
NSLog(#"FIRST_PRESIDENT is NOT defined");
#endif
#ifdef MACRO_FIRST_PRESIDENT
NSLog(#"MACRO_FIRST_PRESIDENT is %#", MACRO_FIRST_PRESIDENT);
#else
NSLog(#"MACRO_FIRST_PRESIDENT is undefined, sorry!");
#endif
Console output -- I've stripped out the garbage from NSLog:
FIRST_PRESIDENT is NOT defined
MACRO_FIRST_PRESIDENT is Washington, George
The only way I've found to get a build time environment variable as a string is to put it in an dictionary element like this:
<key>Product Name</key>
<string>$PRODUCT_NAME</string>
and then retrieve it like this:
NSDictionary* infoDict = [[NSBundle mainBundle] infoDictionary];
NSString* productName = infoDict[#"Product Name"];
NSLog(#"Product Name: %#", productName);
The best answer to this question is the accepted answer on this question.
Constants in Objective-C
You'll get the most mileage, and won't need any special methods to get the value you're searching for as long as you import the file into whatever .h/.m file is going to consume said value.