PageRenderTime 32ms CodeModel.GetById 20ms RepoModel.GetById 1ms app.codeStats 0ms

/iPhoneTrackingAppDelegate.m

http://github.com/petewarden/iPhoneTracker
Objective C | 271 lines | 183 code | 60 blank | 28 comment | 17 complexity | ea4e48e644e3d9e1ba8e9edf0fbaa936 MD5 | raw file
  1. //
  2. // iPhoneTrackingAppDelegate.m
  3. // iPhoneTracking
  4. //
  5. // Created by Pete Warden on 4/15/11.
  6. //
  7. /***********************************************************************************
  8. *
  9. * All code (C) Pete Warden, 2011
  10. *
  11. * This program is free software: you can redistribute it and/or modify
  12. * it under the terms of the GNU General Public License as published by
  13. * the Free Software Foundation, either version 3 of the License, or
  14. * (at your option) any later version.
  15. *
  16. * This program is distributed in the hope that it will be useful,
  17. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  18. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  19. *
  20. * GNU General Public License for more details.
  21. *
  22. * You should have received a copy of the GNU General Public License
  23. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  24. *
  25. ************************************************************************************/
  26. #import "iPhoneTrackingAppDelegate.h"
  27. #import "fmdb/FMDatabase.h"
  28. #import "parsembdb.h"
  29. @implementation iPhoneTrackingAppDelegate
  30. @synthesize window;
  31. @synthesize webView;
  32. - (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
  33. }
  34. - displayErrorAndQuit:(NSString *)error
  35. {
  36. [[NSAlert alertWithMessageText: @"Error"
  37. defaultButton:@"OK" alternateButton:nil otherButton:nil
  38. informativeTextWithFormat: error] runModal];
  39. exit(1);
  40. }
  41. - (void)awakeFromNib
  42. {
  43. NSString* htmlString = [NSString stringWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"index" ofType:@"html"]
  44. encoding:NSUTF8StringEncoding error:NULL];
  45. [[webView mainFrame] loadHTMLString:htmlString baseURL:NULL];
  46. [webView setUIDelegate:self];
  47. [webView setFrameLoadDelegate:self];
  48. [webView setResourceLoadDelegate:self];
  49. }
  50. - (void)debugLog:(NSString *) message
  51. {
  52. NSLog(@"%@", message);
  53. }
  54. + (BOOL)isSelectorExcludedFromWebScript:(SEL)aSelector { return NO; }
  55. - (void)webView:(WebView *)sender windowScriptObjectAvailable: (WebScriptObject *)windowScriptObject
  56. {
  57. scriptObject = windowScriptObject;
  58. }
  59. - (void)webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame
  60. {
  61. [self loadLocationDB];
  62. }
  63. - (void)loadLocationDB
  64. {
  65. NSString* backupPath = [NSHomeDirectory() stringByAppendingPathComponent:@"Library/Application Support/MobileSync/Backup/"];
  66. NSFileManager *fm = [NSFileManager defaultManager];
  67. NSArray* backupContents = [[NSFileManager defaultManager] directoryContentsAtPath:backupPath];
  68. NSMutableArray* fileInfoList = [NSMutableArray array];
  69. for (NSString *childName in backupContents) {
  70. NSString* childPath = [backupPath stringByAppendingPathComponent:childName];
  71. NSString *plistFile = [childPath stringByAppendingPathComponent:@"Info.plist"];
  72. NSError* error;
  73. NSDictionary *childInfo = [fm attributesOfItemAtPath:childPath error:&error];
  74. NSDate* modificationDate = [childInfo objectForKey:@"NSFileModificationDate"];
  75. NSDictionary* fileInfo = [NSDictionary dictionaryWithObjectsAndKeys:
  76. childPath, @"fileName",
  77. modificationDate, @"modificationDate",
  78. plistFile, @"plistFile",
  79. nil];
  80. [fileInfoList addObject: fileInfo];
  81. }
  82. NSSortDescriptor* sortDescriptor = [[[NSSortDescriptor alloc] initWithKey:@"modificationDate" ascending:NO] autorelease];
  83. [fileInfoList sortUsingDescriptors:[NSArray arrayWithObject:sortDescriptor]];
  84. BOOL loadWorked = NO;
  85. for (NSDictionary* fileInfo in fileInfoList) {
  86. @try {
  87. NSString* newestFolder = [fileInfo objectForKey:@"fileName"];
  88. NSString* plistFile = [fileInfo objectForKey:@"plistFile"];
  89. NSDictionary *plist = [NSDictionary dictionaryWithContentsOfFile:plistFile];
  90. if (plist==nil) {
  91. NSLog(@"No plist file found at '%@'", plistFile);
  92. continue;
  93. }
  94. NSString* deviceName = [plist objectForKey:@"Device Name"];
  95. NSLog(@"file = %@, device = %@", plistFile, deviceName);
  96. NSDictionary* mbdb = [ParseMBDB getFileListForPath: newestFolder];
  97. if (mbdb==nil) {
  98. NSLog(@"No MBDB file found at '%@'", newestFolder);
  99. continue;
  100. }
  101. NSString* wantedFileName = @"Library/Caches/locationd/consolidated.db";
  102. NSString* dbFileName = nil;
  103. for (NSNumber* offset in mbdb) {
  104. NSDictionary* fileInfo = [mbdb objectForKey:offset];
  105. NSString* fileName = [fileInfo objectForKey:@"filename"];
  106. if ([wantedFileName compare:fileName]==NSOrderedSame) {
  107. dbFileName = [fileInfo objectForKey:@"fileID"];
  108. }
  109. }
  110. if (dbFileName==nil) {
  111. NSLog(@"No consolidated.db file found in '%@'", newestFolder);
  112. continue;
  113. }
  114. NSString* dbFilePath = [newestFolder stringByAppendingPathComponent:dbFileName];
  115. loadWorked = [self tryToLoadLocationDB: dbFilePath forDevice:deviceName];
  116. if (loadWorked) {
  117. break;
  118. }
  119. }
  120. @catch (NSException *exception) {
  121. NSLog(@"Exception: %@", [exception reason]);
  122. }
  123. }
  124. if (!loadWorked) {
  125. [self displayErrorAndQuit: [NSString stringWithFormat: @"Couldn't load consolidated.db file from '%@'", backupPath]];
  126. }
  127. }
  128. - (BOOL)tryToLoadLocationDB:(NSString*) locationDBPath forDevice:(NSString*) deviceName
  129. {
  130. [scriptObject setValue:self forKey:@"cocoaApp"];
  131. FMDatabase* database = [FMDatabase databaseWithPath: locationDBPath];
  132. [database setLogsErrors: YES];
  133. BOOL openWorked = [database open];
  134. if (!openWorked) {
  135. return NO;
  136. }
  137. const float precision = 100;
  138. NSMutableDictionary* buckets = [NSMutableDictionary dictionary];
  139. NSString* queries[] = {@"SELECT * FROM CellLocation;", @"SELECT * FROM WifiLocation;"};
  140. // Temporarily disabled WiFi location pulling, since it's so dodgy. Change to
  141. for (int pass=0; pass<1; /*pass<2;*/ pass+=1) {
  142. FMResultSet* results = [database executeQuery:queries[pass]];
  143. while ([results next]) {
  144. NSDictionary* row = [results resultDict];
  145. NSNumber* latitude_number = [row objectForKey:@"latitude"];
  146. NSNumber* longitude_number = [row objectForKey:@"longitude"];
  147. NSNumber* timestamp_number = [row objectForKey:@"timestamp"];
  148. const float latitude = [latitude_number floatValue];
  149. const float longitude = [longitude_number floatValue];
  150. const float timestamp = [timestamp_number floatValue];
  151. // The timestamps seem to be based off 2001-01-01 strangely, so convert to the
  152. // standard Unix form using this offset
  153. const float iOSToUnixOffset = (31*365.25*24*60*60);
  154. const float unixTimestamp = (timestamp+iOSToUnixOffset);
  155. if ((latitude==0.0)&&(longitude==0.0)) {
  156. continue;
  157. }
  158. const float weekInSeconds = (7*24*60*60);
  159. const float timeBucket = (floor(unixTimestamp/weekInSeconds)*weekInSeconds);
  160. NSDate* timeBucketDate = [NSDate dateWithTimeIntervalSince1970:timeBucket];
  161. NSString* timeBucketString = [timeBucketDate descriptionWithCalendarFormat:@"%Y-%m-%d" timeZone:nil locale:nil];
  162. const float latitude_index = (floor(latitude*precision)/precision);
  163. const float longitude_index = (floor(longitude*precision)/precision);
  164. NSString* allKey = [NSString stringWithFormat:@"%f,%f,All Time", latitude_index, longitude_index];
  165. NSString* timeKey = [NSString stringWithFormat:@"%f,%f,%@", latitude_index, longitude_index, timeBucketString];
  166. [self incrementBuckets: buckets forKey: allKey];
  167. [self incrementBuckets: buckets forKey: timeKey];
  168. }
  169. }
  170. NSMutableArray* csvArray = [[[NSMutableArray alloc] init] autorelease];
  171. [csvArray addObject: @"lat,lon,value,time\n"];
  172. for (NSString* key in buckets) {
  173. NSNumber* count = [buckets objectForKey:key];
  174. NSArray* parts = [key componentsSeparatedByString:@","];
  175. NSString* latitude_string = [parts objectAtIndex:0];
  176. NSString* longitude_string = [parts objectAtIndex:1];
  177. NSString* time_string = [parts objectAtIndex:2];
  178. NSString* rowString = [NSString stringWithFormat:@"%@,%@,%@,%@\n", latitude_string, longitude_string, count, time_string];
  179. [csvArray addObject: rowString];
  180. }
  181. if ([csvArray count]<10) {
  182. return NO;
  183. }
  184. NSString* csvText = [csvArray componentsJoinedByString:@"\n"];
  185. id scriptResult = [scriptObject callWebScriptMethod: @"storeLocationData" withArguments:[NSArray arrayWithObjects:csvText,deviceName,nil]];
  186. if(![scriptResult isMemberOfClass:[WebUndefined class]]) {
  187. NSLog(@"scriptResult='%@'", scriptResult);
  188. }
  189. return YES;
  190. }
  191. - (void) incrementBuckets:(NSMutableDictionary*)buckets forKey:(NSString*)key
  192. {
  193. NSNumber* existingValue = [buckets objectForKey:key];
  194. if (existingValue==nil) {
  195. existingValue = [NSNumber numberWithInteger:0];
  196. }
  197. NSNumber* newValue = [NSNumber numberWithInteger:([existingValue integerValue]+1)];
  198. [buckets setObject: newValue forKey: key];
  199. }
  200. - (IBAction)openAboutPanel:(id)sender {
  201. NSImage *img = [NSImage imageNamed: @"Icon"];
  202. NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
  203. @"1.0", @"Version",
  204. @"iPhone Tracking", @"ApplicationName",
  205. img, @"ApplicationIcon",
  206. @"Copyright 2011, Pete Warden and Alasdair Allan", @"Copyright",
  207. @"iPhone Tracking v1.0", @"ApplicationVersion",
  208. nil];
  209. [[NSApplication sharedApplication] orderFrontStandardAboutPanelWithOptions:options];
  210. }
  211. @end