Tag Archives: import

But my dog does.

Migrating Prosody from text store and sqlite3 to PostgreSQL

Assumptions

  • You started off from the basic storage config:
    • Regular data is in filesystem.
    • mam (xep0313) message archive is in SQLite3.
  • You want to transition to using just PostgreSQL.
  • PostgreSQL version is 9.4.

Installation

# # largest version of pgsql you have
~# apt install postgresql-9.4-client

# # get lua-dbi module for pgsql
~# apt install lua-dbi-postgresql

Creating PostgreSQL user

# # PostgreSQL trusts users connecting over unix domain socket to be the
# # same as their local account.
# # Therefore, become postgres -- the admin account.
user:~$ sudo su postgres

# # run user creation
postgres:~$ createuser --interactive
# # name: prosody
# # no other administrative options

# # run postgresql client
postgres:~$ psql
-- create database
CREATE DATABASE prosody;

-- give the prosody user all rights on it
GRANT ALL ON DATABASE prosody TO prosody;

-- in case of connecting over network and using md5 trust,
-- set prosody account password:
ALTER ROLE prosody WITH ENCRYPTED PASSWORD 'here_some_password';

Over network?

Assuming you want to connect over the network, edit /etc/postgresql/9.4/main/pg_hba.conf. Append:

# type, database, user, address, auth method
host prosody prosody 172.16.0.0/16 md5
  • Try to minimize your permitted netmask.
  • Can you configure a more secure auth method than md5? Do so.

Migrate data from filesystem

# # as prosody local user

prosody:$ cd prosody-hg/tools/migrator

# # overwrite the config.
prosody:$ cat > migrator.cfg.lua << _EOF
local data_path = "../../data";

input {
        type = "prosody_files";
        path = data_path;
}
output {
        type = "prosody_sql";
        driver = "PostgreSQL";
        database = "prosody";
        username = "prosody";
        password = "here_some_password";
        host = "database.host.here"; -- this assumes network connection; migration with local user credentials was not attempted.
}
_EOF

# # run the migrator in ~/prosody-hg/tools/migrator
prosody:$ lua prosody-migrator.lua input output

Migrate mam archive from SQLite3

# # as prosody user

prosody:~$ cd prosody-hg/data

# # Having first verified there is nothing in Prosody table...
prosody:$ sqlite3 prosody.sqlite 'SELECT COUNT(*) FROM prosody;'
# # ...drop prosody table. All its data (roster etc) was until now stored on the filesystem.
prosody:$ sqlite3 prosody.sqlite 'DROP TABLE prosody;'

# # dump and massage the sqlite3 output, piping it into psql.
# # psql authenticates as the 'prosody' user and does not require the password.
prosody:$ sqlite3 prosody.sqlite .dump | \
    grep -v 'BEGIN TRANSACTION;' | \
    sed 's/PRAGMA foreign_keys=OFF;/BEGIN TRANSACTION; SET CONSTRAINTS ALL DEFERRED;/' | \
    grep -v sqlite_sequence | \
    awk '/CREATE TABLE/{gsub("`","\"");} 1' | \
    awk '/CREATE UNIQUE INDEX/{gsub("`","\"");} 1' | \
    sed 's/INTEGER PRIMARY KEY AUTOINCREMENT/SERIAL PRIMARY KEY/' | \
    psql

# # manual step :(
# # fix the autoincrement.
prosody:$ sqlite3 prosody.sqlite 'SELECT COUNT(*) FROM prosodyarchive;'
# # use this number + 1 in:
prosody:$ echo 'ALTER SEQUENCE prosodyarchive_sort_id_seq RESTART WITH 123456;' | psql

Update Prosody config

In prosody.cfg.lua:

storage = {
        archive2 = "sql";
}
sql = { driver = "PostgreSQL", database = "prosody", username = "prosody", password = "here_some_password", host = "database.host.here" }

Overriding HTTP user agent for calls to -initWithContentsOfURL:

Perhaps you need to override the HTTP user agent whenever you call -initWithContentsOfURL: from classes such as NSString, NSDictionary or NSArray, or one of this method’s convenience wrappers such as +stringWithContentsOfURL:, +dictionaryWithContentsOfURL: or +arrayWithContentsOfURL:. So let’s consider how this can be accomplished under iOS.

From what I can see, there is no easy and “clean” way apart from adding a category on the classes where you need to support this and writing your own implementation of -initWithContentsOfURL: and convenience functions (with a slightly different name, of course). These implementations would use NSURLConnection‘s +sendSynchronousRequest:returningResponse:error:. Of course, as with -initWithContentsOfURL: you’d use this replacement method in a background thread to maintain UI responsiveness.

You’d have to write a reimplementation of -initWithContentsOfURL: because the first place you can change this is NSURLRequest, or more specifically, its mutable variant NSMutableURLRequest, using the -setValue:forHTTPHeaderField:. But, if you have tons of code, you probably can’t easily change it to use the new method.

So I dug in and, with a few smart tricks (such as feeding a broken non-NSURL as a NSURL to figure out which methods get called, then implementing them as necessary), I figured out which of several ways for fetching web content is actually used in NSString‘s implementation of -initWithContentsOfURL:. These could have been NSURLConnection or some low level messing with CFNetwork.

It turned out not to matter since NSURLRequest is generated out of the NSURL passed to the method. Customizing the user agent turned out to be just a matter of taking all NSURLRequests, forcing them to become mutable copies in form of instances of NSMutableURLRequest during the initializer and setting the user agent at that time. Specific initializer appearing in iOS implementation used in iOS 5 Simulator that ships with Xcode 4.2.1 appears to be -initWithURL:cachePolicy:timeoutInterval:.

It’s an enormous hack, but I decided to simply swizzle this method out. Swizzling NSURLConnection‘s class method +sendSynchronousRequest:returningResponse:error: did not appear to work – the original method still got called despite my best efforts to figure out what went wrong with swizzling, so I gave up on it. If you can see a mistake in my class swizzling code, please tell me about it in the comments section below.

I definitely have no idea whether or not your app will be rejected for this, but from what I know, method swizzling is not illegal.

//  NSURLRequest+UserAgentFix.m

#define YOUR_USER_AGENT @"Your User Agent"
#import "NSURLRequest+UserAgentFix.h"
#import "NSObject+ISSwizzling.h"
@implementation NSURLRequest (UserAgentFix)
+(void)load
{
    [self swizzleMethod:@selector(initWithURL:cachePolicy:timeoutInterval:)
             withMethod:@selector(initWithURL2:cachePolicy:timeoutInterval:)];
}
-(id)initWithURL2:(NSURL *)URL cachePolicy:(NSURLRequestCachePolicy)cachePolicy timeoutInterval:(NSTimeInterval)timeoutInterval
{
    self = [self initWithURL2:URL cachePolicy:cachePolicy timeoutInterval:timeoutInterval];
    
    if(!self)
        return nil;
    
    if([self class] == [NSURLRequest class])
        self = [self mutableCopy];
    
    if([self class] == [NSMutableURLRequest class])
    {
        NSMutableURLRequest * req = self;
        [req setValue:YOUR_USER_AGENT forHTTPHeaderField:@"User-Agent"];
    }
    
    return self;
}
@end

// NSURLRequest+UserAgentFix.h
#import <Foundation/Foundation.h>

@interface NSURLRequest (UserAgentFix)

@end
// NSObject+ISSwizzling.h
#import <Foundation/Foundation.h>

@interface NSObject (ISSwizzling)
+ (BOOL)swizzleMethod:(SEL)origSelector withMethod:(SEL)newSelector;
+ (BOOL)swizzleClassMethod:(SEL)origSelector withMethod:(SEL)newSelector;

@end
// NSObject+ISSwizzling.m
#import <objc/runtime.h>
#import "NSObject+ISSwizzling.h"

@implementation NSObject (ISSwizzling)
+ (BOOL)swizzleMethod:(SEL)origSelector withMethod:(SEL)newSelector
{
    Method origMethod = class_getInstanceMethod(self, origSelector);
    Method newMethod = class_getInstanceMethod(self, newSelector);
    
    if (origMethod && newMethod) {
        if (class_addMethod(self, origSelector, method_getImplementation(newMethod), method_getTypeEncoding(newMethod))) {
            class_replaceMethod(self, newSelector, method_getImplementation(origMethod), method_getTypeEncoding(origMethod));
        } else {
            method_exchangeImplementations(origMethod, newMethod);
        }
        return YES;
    }
    return NO;
}
+ (BOOL)swizzleClassMethod:(SEL)origSelector withMethod:(SEL)newSelector
{
    Method origMethod = class_getClassMethod(self, origSelector);
    Method newMethod = class_getClassMethod(self, newSelector);
    
    Class class = object_getClass((id)self);

    if (origMethod && newMethod) {
        if (class_addMethod(class, origSelector, method_getImplementation(newMethod), method_getTypeEncoding(newMethod))) {
            class_replaceMethod(class, newSelector, method_getImplementation(origMethod), method_getTypeEncoding(origMethod));
        } else {
            method_exchangeImplementations(origMethod, newMethod);
        }
        return YES;
    }
    return NO;
}

@end

Tested on iOS 5 Simulator with NSString‘s +stringWithContentsOfURL:.