I know that there are a few postings on this, but just want to make sure there is something that I am not missing / current.
Using sqlcipher, with an unencrypted database, want to encrypt it. Encrypting a new database is working fine.
Am trying the sqlcipher rekey with an existing database seems NOT to be working (Database remains unencrypted).
[fmdb open];
NSString *sel = #"SELECT count(*) FROM sqlite_master";
FMResultSet *fmr = [self executeQuery : fmdb : sel];
if ( [fmr next] ) // unencrypted
{
NSLog(#"Encrypting");
fmdb.key = #"";
[fmdb rekey : #"somekey"];
}
Otherwise will have to use one of the other PRAGMA methods, etc.
Does rekey only work with databases that are already encrypted?
This is using the FMDatabase Framework, but under the hood in the framework it is doing ...
- (BOOL)rekey:(NSString*)key {
#ifdef SQLITE_HAS_CODEC
if (!key) {
return NO;
}
int rc = sqlite3_rekey(db, [key UTF8String], (int)strlen([key UTF8String]));
if (rc != SQLITE_OK) {
NSLog(#"error on rekey: %d", rc);
NSLog(#"%#", [self lastErrorMessage]);
}
return (rc == SQLITE_OK);
#else
return NO;
#endif
}
It does run though the sqlite3_rekey, no errors, but database does not get encrypted.
All of the previous comments on this question are incorrect. You cannot use rekey to encrypt a plaintext database. Rekey is only to be used to change the encryption key on an encrypted database.
The correct way to encrypt a plaintext database is attach and export - see examples here http://sqlcipher.net/sqlcipher-api/#sqlcipher_export
The trick was that when the database is used to check for encryption (next time opening app) when it is already encrypted, but do not use a key to do a select, this will fail, but then the database will HAVE to be closed and reopened again with the key.
Related
I'm using NSUbiquitousKeyValueStore to store some app settings. My logic is: when I save data locally, I save it to NSUbiquitousKeyValueStore also as a backup. When I need settings, I read locally and I only use iCloud key-value store if no data is found locally (after app is reinstalled, for example). If user has several devices sharing one icloud id, he can write settings on one device and download them to another (I warn him about rewrite).
I have a strange issue. Steps:
Installed an app and save its data to NSUbiquitousKeyValueStore. Made sure data is there.
Removed the app (assuming data is still persists in iCloud).
Waited several minutes just in case, then installed and launched the app from inside Xcode.
Tried to read a settings key using [[NSUbiquitousKeyValueStore defaultStore] dataForKey: #"mykeyname"] - sometimes it's ok, but sometimes key is not found!
Waited for 15 seconds, tried again. Success. Confused.
So it seems like ios needs some time to make remote key-value storage for my app available locally for dataForKey: call.
If I'd wrote such a system (actually I did - some time ago, in another life) there obviously must be a delay before asking and receiving a key-value data. So I'd like to have some notification saying: "we finished downloading/syncing key-value storage on first start" or something similar.
As far as I understand I can work with NSUbiquitousKeyValueStore in main thread synchronously (which is convenient for me). But [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil] returns a valid url, and then I get "key isn't found". So I can't rely on it. Is there a way to be sure NSUbiquitousKeyValueStore works an is downloaded? It's important especially with slow internet.
UPDATE
Adding [[NSUbiquitousKeyValueStore defaultStore] synchronize] (as written in apple docs) to init and load was helped a little. Still there are many questions to iCloud.
Yesterday I've successfully saved data to the key-value store on phone 1 and restored on phone 2.
Today I've deleted app on phone 2 and tried to restore the data. But even [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil] returned valid URL and I called [[NSUbiquitousKeyValueStore defaultStore] synchronize] I get nil when call dataForKey: MY_DATA_KEY.
When I tried to restore data from icloud on phone 1 (app is still installed) it succeeds, but when I reinstalled on this phone the app restore doesn't succeed any more.
Temporary solution is: "turn off iCloud->Documents&Data - turn off and on network - turn on Documents&Data", but also you should wait several minutes, and then it should work.
So, questions:
do you have such problems with iCloud?
Is there any way to find out is data not available or just not downloaded yet?
Is there any known "latency" of iCloud? I've heard about 7 seconds, but it's obviously not true.
It seems that when app isn't unistalled updates of iCloud data are pretty fast (seconds), but when you reinstall the app icloud needs several minutes to actualize key-value store. Is there any way to force this process?
P.S.
Below is my CloudHelper for your reference - pretty simple c++ class to write/read binary data to/from iCloud key-value store. It is not compilable, I've adapted it for SO somewhat to make more clear - removed my engine related code. Still if you remove MySystem::... calls it works pretty well. Except that I mentioned before.
class CloudHelper
{
public:
static bool init();
static void deInit();
//save our data to iCloud with
static int saveData(unsigned char* data, int from, int count);
//get our data from iCloud
static unsigned char * loadData(int *retsize, int * retint);
//does iCloud work for us
static bool isEnabled();
//do we have our key in iCloud
static int isAvailable();
static const int RESULT_OK = 0;
static const int RESULT_NO_CONNECTION = 1;
static const int RESULT_NOT_FOUND = 2;
static const int RESULT_SYNC_ERROR = 3;
private:
static bool enabled;
static NSURL *ubiq;
};
bool CloudHelper::enabled = false;
NSURL *CloudHelper::ubiq = NULL;
#define MY_DATA_KEY #"my_data_key"
int CloudHelper::saveData(unsigned char* data, int from, int count)
{
if ([NSUbiquitousKeyValueStore defaultStore])
{
NSData *d = [[[NSData alloc] initWithBytes:(data + from) length:count] autorelease];
[[NSUbiquitousKeyValueStore defaultStore] setData:d forKey: MY_DATA_KEY)];
if ([[NSUbiquitousKeyValueStore defaultStore] synchronize] != TRUE)
return RESULT_SYNC_ERROR;
return RESULT_OK;
}
return RESULT_NO_CONNECTION;
}
unsigned char * CloudHelper::loadData(int *retsize, int * retint)
{
if ([NSUbiquitousKeyValueStore defaultStore])
{
[[NSUbiquitousKeyValueStore defaultStore] synchronize];
NSData *d = [[NSUbiquitousKeyValueStore defaultStore] dataForKey: MY_DATA_KEY];
if (d != NULL)
{
if (retsize != NULL)
*retsize = d.length;
if (retint != NULL)
*retint = RESULT_OK;
return d.bytes;
}
else
{
if (retsize != NULL)
*retsize = -1;
if (retint != NULL)
*retint = RESULT_NOT_FOUND;
}
}
else
{
if (retsize != NULL)
*retsize = -1;
if (retint != NULL)
*retint = RESULT_NO_CONNECTION;
}
return NULL;
}
int CloudHelper::isAvailable()
{
int result = RESULT_NO_CONNECTION;
if ([NSUbiquitousKeyValueStore defaultStore])
{
[[NSUbiquitousKeyValueStore defaultStore] synchronize];
NSData *d = [[NSUbiquitousKeyValueStore defaultStore] dataForKey: MY_DATA_KEY];
if (d != NULL)
result = RESULT_OK;
else
result = RESULT_NOT_FOUND;
}
else
result = RESULT_NO_CONNECTION;
return result;
}
void CloudHelper::deInit()
{
enabled = false;
[ubiq release];
}
bool CloudHelper::init()
{
enabled = false;
NSURL *ubiq_ = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil];
[[NSUbiquitousKeyValueStore defaultStore] synchronize];
if (ubiq)
{
enabled = true;
ubiq = [ubiq_ retain]; //save for further use
}
else
{
//is implemented elsewhere: this writes a local file with a counter, and if it is < REMINDER_COUNT allows us to show a warning to users
bool allow = MySystem::isAllowToShowDialog();
if (allow)
{
//determines network state with Apple's Reachability
if (!MySystem::isNetworkAvailable())
MySystem::showMessageBox(#"Network error"); //No network
else
MySystem::showMessageBox(#"You should log into your iCloud account to be able to backup your settings."); //No login
}
}
return enabled;
}
UPDATE 2
It's 2016. Android has become ios's evil twin, the humanity has discovered gravitational waves, Higgs have received his nobel, Microsoft has bought and killed Nokia. But iCloud is still as stupid as it was.
Finally I've made my own stack of network services on several VPS. I refused to use third-party services, because most of them are unstable and unpredictable. And yet I need iCloud. Because another die-born child of apple does not work. SecKeyChain. Its service dies when my game starts. So I decided to store random UUID in cloud to distinguish users (there is no device id anymore) even after reinstall. But what could go wrong? Everything! I've spend two days to make this stupid s*it to deploy without errors, and now it loses my data from time to time!
Thank you Apple, thank, thank, thank! La-la-la! Hip-hip hooray! (sounds of circus music, fading into weeping)
Conclusion
Temporary solution is:
- call synchronize before get data from key-value store
- to be sure it would work "turn off iCloud->Documents&Data - turn off and again on network - turn on Documents&Data", but also you should wait several minutes before iCloud downloads all needed data
Note: when app is installed and already worked (saved/loaded) with key-value store updates of iCloud data are pretty fast (7-15 sec), but when you reinstall the app it seems that icloud needs several minutes to actualize key-value store.
I'd be glad to hear your thoughts, because icloud looks like almost unusable feature. But I don't want to set up my own server to merely get the same functionality.
I am setting one dummy key to NSUbiquitousKeyValueStore and calling synchronize on app launch. The result is not 100% solution but somewhat better. You can try this.
Because obviously your app shouldn't hang while waiting for a slow netork. It's all in the iCloud Design Guide.
Register for NSUbiquitousKeyValueStoreDidChangeExternallyNotification, call -synchronize, and hopefully a notification should eventually arrive.
If the data is already up-to-date, I don't think you get a notification, and I don't think there's an wasy way to know how old the data is.
With the release of iOS 5 we are getting more and more errors when setting the serialized option for the sqlite database (so its save to be used for multithreading). We are getting SQLITE_MISUSE error code on sqlite3_config. Has someone noticed this odd behavior? And does someone know how I can fix this? It works perfectly fine on previous iOS versions.
here is the code:
- (sqlite3 *)getNewDBConnection {
NSLog(#"sqlite3 lib version: %s", sqlite3_libversion());
//sqlite3_config() has to be called before any sqlite3_open calls.
if (sqlite3_threadsafe() > 0) {
int retCode = sqlite3_config(SQLITE_CONFIG_SERIALIZED);
if (retCode == SQLITE_OK) {
NSLog(#"Can now use sqlite on multiple threads, using the same connection");
} else {
NSLog(#"setting sqlite thread safe mode to serialized failed!!! return code: %d", retCode);
}
} else {
NSLog(#"Your SQLite database is not compiled to be threadsafe.");
}
sqlite3 *newDBconnection;
// Open the database
if (sqlite3_open([[self getDatabaseFilePath] UTF8String], &newDBconnection) == SQLITE_OK) {
NSLog(#"Database Successfully Opened :)");
} else {
sqlite3_close(newDBconnection);
NSLog(#"Error in opening database :(");
}
return newDBconnection;
}
and this is the output:
sqlite3 lib version: 3.7.7
setting sqlite thread safe mode to serialized failed!!! return code: 21
Database Successfully Opened :)
I struggled long and hard with this as well and finally got the solution.
As #enobufs said, sqlite3_config() needs to be called before sqlite3_initialize(). However, the OS might initialize SQLite for us so I also do a sqlite3_shutdown() before the sqlite3_config().
sqlite3_shutdown()
sqlite3_config()
sqlite3_initialize().
Then its also necessary to use the same connection for every query as it is the access to the database connection that gets serialized. As described here http://www.sqlite.org/capi3ref.html#sqliteconfigserialized
So I create a connection as soon as the app starts up and the pass that connection to every class that needs it.
Is the sqlite3_config() called before sqlite3_initialize()? The function returns SQLITE_MISUSE if called after sqlite3_initialize() and before sqlite3_shutdown(). See http://www.sqlite.org/c3ref/config.html for more details.
I just installed openfire server in my Suse-Linux EC2 instance and I've configured the openfire server by creating one user named 'balaji' in addition to the admin.
I then installed the ilibjingle code for the iOS platform and I was able to build it. I ran it in the simulator and it worked perfectly for my gmail-id. It logged in, then brought the users from the roster list.
I then modified the code to point to my openfire server IP address and gave the username as 'balaji' (the one I created in openfire) and the appropriate password. I also have a self signed SSL certificate in the openfire server. When I ran this code, it was able to connect, but not able to login (I believe). The ilibjingle code is supposed to go from Connect to Login to LoggedIn to the Roster list. When I ran with my openfire server, it went from Connect to Login, but nothing beyond that.
What could have possibly gone wrong? Should I modify anything in my openfire server to make this work? Here's my iPhone code.
In the rootviewcontroller.mm, I have the following snippet.
-(void) _mainGtalkThread:(id)sender
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
//you need to setup name and passwd manuelly here
char *name = "balaji";
char *password = "mypasswd";
[self gtalk_main:(char*)name userpassword:(char*)password];
[pool release];
}
-(int) gtalk_main:(char*)un userpassword:(char*)password
{
// This app has three threads. The main thread will run the XMPP client,
// which will print to the screen in its own thread. A second thread
// will get input from the console, parse it, and pass the appropriate
// message back to the XMPP client's thread. A third thread is used
// by MediaSessionClient as its worker thread.
buzz::Jid jid;
talk_base::InsecureCryptStringImpl pass;
std::string username = un;
if (username.find('#') == std::string::npos) {
username.append("#localhost");
}
jid = buzz::Jid(username);
if (!jid.IsValid() || jid.node() == "") {
printf("Invalid JID. JIDs should be in the form user#domain\n");
return 1;
}
pass.password() = password;
buzz::XmppClientSettings xcs;
xcs.set_user(jid.node());
//xcs.set_resource("call");
xcs.set_host(jid.domain());
xcs.set_pass(talk_base::CryptString(pass));
xcs.set_use_tls(false);
xcs.set_allow_plain(true);
xcs.set_server(talk_base::SocketAddress("50.37.185.206", DEFAULT_PORT));
printf("Logging in as %s with user as %s\n", jid.Str().c_str(), jid.node().c_str());
talk_base::InitializeSSL();
talk_base::Thread athread;
talk_base::ThreadManager::SetCurrent(&athread);
talk_base::Thread* main_thread = talk_base::Thread::Current();
assert(main_thread!=NULL);
XmppPump pump;
//CallClient *client = new CallClient(pump.client());
gtalkClient_ = new gtalkClient(pump.client(), self);
pump.DoLogin(xcs, new XmppSocket(true), NULL);
main_thread->Run();
return 0;
}
and in another file 'gtalkclient.mm', I have the following:
gtalkClient::gtalkClient(buzz::XmppClient* xmpp_client, void * controller) :
xmpp_client_(xmpp_client), controller_(controller), media_engine_(NULL),
media_client_(NULL), call_(NULL), incoming_call_(false), auto_accept_(false),
pmuc_domain_("conference.localhost"), local_renderer_(NULL), remote_renderer_(NULL),
roster_(new RosterMap), portallocator_flags_(0)
{
xmpp_client_->SignalStateChange.connect(this, >alkClient::OnStateChange);
}
void gtalkClient::OnStateChange(buzz::XmppEngine::State state)
{
RootViewController * tvc = (RootViewController*)controller_;
switch (state) {
case buzz::XmppEngine::STATE_START:
printf("connecting...");
[tvc.roster_ removeAllObjects];
[tvc.roster_ addObject:#"connecting..."];
[tvc reloadTableViewData];
break;
case buzz::XmppEngine::STATE_OPENING:
printf("logging in...");
[tvc.roster_ removeAllObjects];
[tvc.roster_ addObject:#"logging in..."];
[tvc reloadTableViewData];
break;
case buzz::XmppEngine::STATE_OPEN:
printf("logged in...");
[tvc.roster_ removeAllObjects];
[tvc.roster_ addObject:#"logged in..."];
[tvc reloadTableViewData];
InitPhone();
InitPresence();
// prepare to add roster
[tvc.roster_ removeAllObjects];
break;
case buzz::XmppEngine::STATE_CLOSED:
buzz::XmppEngine::Error error = xmpp_client_->GetError(NULL);
printf("logged out...%s", strerror(error).c_str());
[tvc.roster_ removeAllObjects];
[tvc.roster_ addObject:#"logged out..."];
[tvc reloadTableViewData];
Quit();
}
}
I also had the same issue while am running libjingle sample program with my openfire server.Its because newer version of libjingle didn't support the certificates of unknown authorities.
So you need to undo the changes made on xmppsocket.cc on rev65 (when libjingle was updated to version 0.5.6):
This link will help you to see difference between two version
http://code.google.com/p/libjingle/source/diff?spec=svn95&r=65&format=side&path=/trunk/talk/examples/login/xmppsocket.cc&old_path=/trunk/talk/examples/login/xmppsocket.cc&old=30
In this commit, they removed two lines that were allowing certificates of unknown Certificate Authorities.
I got solved the problem by following the above instructions
Or you can follow the below link to get the complete idea.
http://code.google.com/p/libjingle/issues/detail?id=250.
I am using below code for inserting data in the database. and i am inserting aprox 15000 records but after 245 records it throws the error "Unable to open database"
+(void)addGeoRegions:(const char *)query geoId:(int)geoId geoFatherId:(int)geoFatherId geoName:(NSString *)geoName
geoTypeRegionId:(NSString *)geoTypeRegionId geoZone:(int)geoZone
{
sqlite3_stmt *dataRows = nil;
#try {
if(sqlite3_open([[self getDBPath] UTF8String],&PatientDatabase) == SQLITE_OK)
{
if (sqlite3_prepare_v2(PatientDatabase, query, -1, &dataRows, NULL)!=SQLITE_OK)
{
NSAssert1(0,#"error while preparing %s",sqlite3_errmsg(PatientDatabase));
}
sqlite3_bind_int(dataRows, 1, geoId);
sqlite3_bind_int(dataRows, 2, geoFatherId);
sqlite3_bind_text(dataRows, 3, [geoName UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_text(dataRows, 4, [geoTypeRegionId UTF8String], -1, SQLITE_TRANSIENT);
sqlite3_bind_int(dataRows, 5, geoZone);
if (SQLITE_DONE!=sqlite3_step(dataRows))
{
char *err;
err=(char *) sqlite3_errmsg(PatientDatabase);
if (err)
sqlite3_free(err);
NSAssert1(0,#"error while inserting geo regions. %s",sqlite3_errmsg(PatientDatabase));
}
}
}
#catch (NSException * e) {
}
#finally
{
sqlite3_close(PatientDatabase);
sqlite3_finalize(dataRows);
PatientDatabase=nil;
}
}
so please can any one suggest why this problem is occur.
Firstly, think about Mark's answer, you'll get better performance if you open the database once and close it once.
Anyway, that was a suggestion for a design improvement. What is actually wrong in your code is the finally block:
#finally
{
sqlite3_close(PatientDatabase); // will fail!
sqlite3_finalize(dataRows);
PatientDatabase=nil;
}
Here is the relevant line from the sqlite3_close() docs.
Applications must finalize all prepared statements and close all BLOB handles associated with the sqlite3 object prior to attempting to close the object. If sqlite3_close() is called on a database connection that still has outstanding prepared statements or BLOB handles, then it returns SQLITE_BUSY.
You need to run the finalize before closing the database. As things stand, the close call fails and you end up with 245 open database handles.
So, reverse the order of the two statements and check your return codes for failure.
By the way, NSAssert is not an appropriate way to report errors. Throw an exception or return an error, or just log it. NSAssert is designed to catch programming errors. It won't even be compiled into your release code.
Sqlite has much better performance "in transation" on inserts without transaction. I particularly, massive use transaction processes, or failure comes randomly at some point with error "unable to open database file"
You are opening the database on each call it would take less resource to open it once then add all the rows before closing it. In theory what you are doing should work but it is not a way I would even start using.
I'm currently building an iPhone app based on Gsoap toolkit to connect to a webservice. Everything works fine except when I try to connect to my service after disconnecting and reconnecting 3g on the device, I get :
SOAP 1.1 fault: SOAP-ENV:Client [no subcode]
"Connection refused"
Detail: connect failed in tcp_connect()
Working through the debugger shows that the error comes from connect() method of socket.h.
I don't really understand, when I launch another app like safari, the device is connected to the Internet. And after loading a web page, my app's connection works fine.
Here is the code I'm using :
//GSoap initialization
struct soap soap;
soap_init(&soap);
soap.connect_timeout = 0;
soap.send_timeout = 0;
soap.recv_timeout = 0;
// objects request & response
// struct types can be foundin soapStub.h
struct _ns1__GetAuthentification requete;
struct _ns1__GetAuthentificationResponse reponse;
// init request
requete.ConnectPass = (char *) [connectPass UTF8String];
requete.Login = (char *) [login UTF8String];
requete.Password = (char *) [password UTF8String];
requete.soap = &soap;
// request callback. returns SOAP_OK if something has been returned
if(soap_call___ns1__GetAuthentification(&soap,NULL,NULL, &requete,&reponse) == SOAP_OK){
//then we build the result
NSLog(#"Yay!");
soap_end(&soap); // remove deserialized data and clean up
soap_done(&soap); // detach the gSOAP environment
return authResult;
}
else {
//NSLog(#"Soap Error : GetAuthentification");
// We try to see if there's any problem. #catch statements are here just to keep note of the concerned
// exceptions for each request. No utility for the code.
#try {
[self processFault:&soap];
}
#catch (MMWrongId * e) {
#throw e;
}
#catch (MMConnectionFailed * e) {
#throw e;
}
#catch (MMGetAuthentificationFault * e) {
#throw e;
}
return nil;
}
Am I missing any particular flag/option?
For those who encounter the same issue, I got a solution. Michael Lasmanis has been a huge help for this one. Here is his answer :
this is one of the reasons i no longer recommend gsoap for iphone new iphone developers. gsoap uses the lower bsd sockets and bypasses the higher level iphone apis. it is the higher level api that manage the state of the internet connectivity which is why if you start safari first, then everything works. the easiest workaround is to use nsurlconnection to open a http connect to a well know site before calling gsoap.