Repository: nst/SpyPhone Branch: master Commit: 7c386b11c738 Files: 120 Total size: 683.5 KB Directory structure: gitextract_56wmzv_r/ ├── Classes/ │ ├── NSNumber+SP.h │ ├── NSNumber+SP.m │ ├── SPAllSourcesTVC.h │ ├── SPAllSourcesTVC.m │ ├── SPCell.h │ ├── SPCell.m │ ├── SPEmailASAccount.h │ ├── SPEmailASAccount.m │ ├── SPEmailAccount.h │ ├── SPEmailAccount.m │ ├── SPEmailGmailAccount.h │ ├── SPEmailGmailAccount.m │ ├── SPEmailIMAPAccount.h │ ├── SPEmailIMAPAccount.m │ ├── SPEmailIToolsAccount.h │ ├── SPEmailIToolsAccount.m │ ├── SPEmailMobileMeAccount.h │ ├── SPEmailMobileMeAccount.m │ ├── SPEmailPOPAccount.h │ ├── SPEmailPOPAccount.m │ ├── SPEmailReportVC.h │ ├── SPEmailReportVC.m │ ├── SPImageAnnotation.h │ ├── SPImageAnnotation.m │ ├── SPImageMapVC.h │ ├── SPImageMapVC.m │ ├── SPImageVC.h │ ├── SPImageVC.m │ ├── SPSourceAddressBookTVC.h │ ├── SPSourceAddressBookTVC.m │ ├── SPSourceEmailTVC.h │ ├── SPSourceEmailTVC.m │ ├── SPSourceKeyboardTVC.h │ ├── SPSourceKeyboardTVC.m │ ├── SPSourceLocationTVC.h │ ├── SPSourceLocationTVC.m │ ├── SPSourcePhoneTVC.h │ ├── SPSourcePhoneTVC.m │ ├── SPSourcePhotosTVC.h │ ├── SPSourcePhotosTVC.m │ ├── SPSourceTVC.h │ ├── SPSourceTVC.m │ ├── SPSourceWifiTVC.h │ ├── SPSourceWifiTVC.m │ ├── SPWebViewVC.h │ ├── SPWebViewVC.m │ ├── SPWifiAnnotation.h │ ├── SPWifiAnnotation.m │ ├── SPWifiMapVC.h │ ├── SPWifiMapVC.m │ ├── SPWifiMapVC.xib │ ├── SpyPhoneAppDelegate.h │ ├── SpyPhoneAppDelegate.m │ ├── TVOutManager.h │ ├── TVOutManager.m │ ├── TVOutManager_.m │ ├── UIImage+GPS.h │ └── UIImage+GPS.m ├── EXIF/ │ ├── EXF.h │ ├── EXFConstants.h │ ├── EXFGPS.h │ ├── EXFGPS.m │ ├── EXFHandlers.h │ ├── EXFHandlers.m │ ├── EXFJFIF.h │ ├── EXFJFIF.m │ ├── EXFJpeg.h │ ├── EXFJpeg.m │ ├── EXFLogging.h │ ├── EXFMetaData.h │ ├── EXFMetaData.m │ ├── EXFMutableMetaData.h │ ├── EXFTagDefinitionHolder.h │ ├── EXFTagDefinitionHolder.m │ ├── EXFUtils.h │ └── EXFUtils.m ├── FMDB/ │ ├── FMDatabase.h │ ├── FMDatabase.m │ ├── FMDatabaseAdditions.h │ ├── FMDatabaseAdditions.m │ ├── FMResultSet.h │ └── FMResultSet.m ├── JSON/ │ ├── JSON.h │ ├── LICENSE │ ├── NSObject+SBJSON.h │ ├── NSObject+SBJSON.m │ ├── NSString+SBJSON.h │ ├── NSString+SBJSON.m │ ├── Readme.markdown │ ├── SBJsonBase.h │ ├── SBJsonBase.m │ ├── SBJsonParser.h │ ├── SBJsonParser.m │ ├── SBJsonStreamWriter.h │ ├── SBJsonStreamWriter.m │ ├── SBJsonWriter.h │ ├── SBJsonWriter.m │ └── SBProxyForJson.h ├── MainWindow.xib ├── OUILookupTool/ │ ├── OUILookupTool.h │ └── OUILookupTool.m ├── README.markdown ├── SPCell.xib ├── SPEmailReportVC.xib ├── SPImageMapVC.xib ├── SPImageVC.xib ├── SPSourceTVC.xib ├── SPWebViewVC.xib ├── Settings.bundle/ │ ├── Root.plist │ └── en.lproj/ │ └── Root.strings ├── Sources.xib ├── SpyPhone-Info.plist ├── SpyPhone.xcodeproj/ │ ├── nst.pbxuser │ ├── nst.perspectivev3 │ ├── project.pbxproj │ └── project.xcworkspace/ │ ├── contents.xcworkspacedata │ └── xcuserdata/ │ └── nst.xcuserdatad/ │ └── WorkspaceSettings.xcsettings ├── SpyPhone_Prefix.pch ├── gpl-2.0.txt └── main.m ================================================ FILE CONTENTS ================================================ ================================================ FILE: Classes/NSNumber+SP.h ================================================ // // NSNumber+SL.h // SpotLook // // Created by Nicolas Seriot on 31.03.08. // Copyright 2008 __MyCompanyName__. All rights reserved. // #import @interface NSNumber (SP) - (NSString *)prettyBytes; @end ================================================ FILE: Classes/NSNumber+SP.m ================================================ // // NSNumber+SL.m // SpotLook // // Created by Nicolas Seriot on 31.03.08. // Copyright 2008 __MyCompanyName__. All rights reserved. // #import "NSNumber+SP.h" @implementation NSNumber (SP) - (NSString *)prettyBytes { float bytes = [self longValue]; NSUInteger unit = 0; if(bytes < 1) { return @"-"; } while(bytes > 1024) { bytes = bytes / 1024.0; unit++; } if(unit > 4) { return @"HUGE"; } NSString *unitString = [[NSArray arrayWithObjects:/* @"Bytes", */ @"KB", @"MB", @"GB", @"TB", @"PB", nil] objectAtIndex:unit]; if(unit == 0) { return [NSString stringWithFormat:@"%d %@", (int)bytes, unitString]; } else { return [NSString stringWithFormat:@"%.2f %@", (float)bytes, unitString]; } } @end ================================================ FILE: Classes/SPAllSourcesTVC.h ================================================ // // SourcesTVController.h // SpyPhone // // Created by Nicolas Seriot on 11/15/09. // Copyright 2009. // Licensed under GPL 2.0 http://www.gnu.org/licenses/gpl-2.0.txt // #import @class SPSourceEmailTVC; @class SPSourceWifiTVC; @class SPSourcePhoneTVC; @class SPSourceLocationTVC; @class SPSourcePhotosTVC; @class SPSourceAddressBookTVC; @class SPSourceKeyboardTVC; @interface SPAllSourcesTVC : UITableViewController { NSArray *sources; IBOutlet SPSourceEmailTVC *sourceEmailTVC; IBOutlet SPSourceWifiTVC *sourceWifiTVC; IBOutlet SPSourcePhoneTVC *sourcePhoneTVC; IBOutlet SPSourceLocationTVC *sourceLocationTVC; IBOutlet SPSourcePhotosTVC *sourcePhotosTVC; IBOutlet SPSourceAddressBookTVC *sourceAddressBookTVC; IBOutlet SPSourceKeyboardTVC *sourceKeyboardTVC; } @property (nonatomic, retain) NSArray *sources; - (NSString *)emailForReport; - (NSString *)report; @end ================================================ FILE: Classes/SPAllSourcesTVC.m ================================================ // // SourcesTVController.m // SpyPhone // // Created by Nicolas Seriot on 11/15/09. // Copyright 2009. // Licensed under GPL 2.0 http://www.gnu.org/licenses/gpl-2.0.txt // #import "SPAllSourcesTVC.h" #import "SPSourceTVC.h" #import "SPCell.h" @implementation SPAllSourcesTVC @synthesize sources; - (void)loadSources { if(sources) return; if(!self.isViewLoaded) [self loadView]; self.sources = [NSArray arrayWithObjects: sourceEmailTVC, sourceWifiTVC, sourcePhoneTVC, sourceLocationTVC, sourcePhotosTVC, sourceAddressBookTVC, sourceKeyboardTVC, nil]; } - (NSString *)emailForReport { if(!self.isViewLoaded) [self loadView]; return [sourceEmailTVC emailForReport]; } // Implement viewDidLoad to do additional setup after loading the view, typically from a nib. - (void)viewDidLoad { [super viewDidLoad]; if(!sources) [self loadSources]; } - (NSString *)report { [self loadSources]; NSMutableString *s = [NSMutableString string]; for(SPSourceTVC *source in sources) { [s appendString:[NSString stringWithFormat:@"----- %@ -----\n\n", [source.title uppercaseString]]]; [source loadData]; NSArray *a = source.contentsDictionaries; for(NSDictionary *d in a) { [s appendString:[NSString stringWithFormat:@"[[ %@ ]]\n", [[d allKeys] lastObject]]]; [s appendString:[[[d allValues] lastObject] componentsJoinedByString:@"\n"]]; [s appendString:@"\n\n"]; } //[s appendString:@"\n"]; } return s; } - (void)dealloc { [sources release]; [super dealloc]; } #pragma mark UITableViewDataSource - (NSInteger)tableView:(UITableView *)table numberOfRowsInSection:(NSInteger)section { return [sources count]; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *SourceCellIdentifier = @"SPCell"; SPCell *cell = (SPCell *)[tableView dequeueReusableCellWithIdentifier:SourceCellIdentifier]; if (cell == nil) { cell = (SPCell *)[[[NSBundle mainBundle] loadNibNamed:@"SPCell" owner:self options:nil] lastObject]; } SPSourceTVC *sourceTVC = [sources objectAtIndex:indexPath.row]; cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; cell.textLabel.text = [sourceTVC title]; cell.imageView.image = [sourceTVC image]; return cell; } #pragma mark UITableViewDelegate - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { UIViewController *sourceVC = [sources objectAtIndex:indexPath.row]; [self.navigationController pushViewController:sourceVC animated:YES]; } @end ================================================ FILE: Classes/SPCell.h ================================================ // // SPCell.h // SpyPhone // // Created by Nicolas Seriot on 11/15/09. // Copyright 2009. // Licensed under GPL 2.0 http://www.gnu.org/licenses/gpl-2.0.txt // #import @interface SPCell : UITableViewCell { } @end ================================================ FILE: Classes/SPCell.m ================================================ // // SPCell.m // SpyPhone // // Created by Nicolas Seriot on 11/15/09. // Copyright 2009. // Licensed under GPL 2.0 http://www.gnu.org/licenses/gpl-2.0.txt // #import "SPCell.h" @implementation SPCell @end ================================================ FILE: Classes/SPEmailASAccount.h ================================================ // // SPEmailASAccount.h // SpyPhone // // Created by Nicolas Seriot on 11/20/09. // Copyright 2009. // Licensed under GPL 2.0 http://www.gnu.org/licenses/gpl-2.0.txt // #import "SPEmailAccount.h" @interface SPEmailASAccount : SPEmailAccount { } @end ================================================ FILE: Classes/SPEmailASAccount.m ================================================ // // SPEmailASAccount.m // SpyPhone // // Created by Nicolas Seriot on 11/20/09. // Copyright 2009. // Licensed under GPL 2.0 http://www.gnu.org/licenses/gpl-2.0.txt // #import "SPEmailASAccount.h" @implementation SPEmailASAccount + (SPEmailAccount *)accountWithDictionary:(NSDictionary *)d { SPEmailASAccount *account = [[SPEmailASAccount alloc] init]; account.type = [d valueForKey:@"Short Type String"]; //account.fullname = nil; account.emails = [NSArray arrayWithObject:[d valueForKey:@"ASAccountEmailAddress"]]; account.hostname = [d valueForKey:@"ASAccountHost"]; account.username = [d valueForKey:@"ASAccountUsername"]; account.displayName = [d valueForKey:@"DisplayName"]; return [account autorelease]; } @end ================================================ FILE: Classes/SPEmailAccount.h ================================================ // // SPEmailAccount.h // SpyPhone // // Created by Nicolas Seriot on 11/20/09. // Copyright 2009. // Licensed under GPL 2.0 http://www.gnu.org/licenses/gpl-2.0.txt // #import // TODO: subclass for AOL accounts @interface SPEmailAccount : NSObject { NSString *fullname; NSArray *emails; NSString *type; NSString *hostname; NSString *username; NSString *displayName; NSMutableArray *calendars; } @property (nonatomic, retain) NSString *fullname; @property (nonatomic, retain) NSArray *emails; @property (nonatomic, retain) NSString *type; @property (nonatomic, retain) NSString *hostname; @property (nonatomic, retain) NSString *username; @property (nonatomic, retain) NSString *displayName; @property (nonatomic, retain) NSArray *calendars; + (SPEmailAccount *)accountWithDictionary:(NSDictionary *)d; - (NSArray *)infoArray; @end ================================================ FILE: Classes/SPEmailAccount.m ================================================ // // SPEmailAccount.m // SpyPhone // // Created by Nicolas Seriot on 11/20/09. // Copyright 2009. // Licensed under GPL 2.0 http://www.gnu.org/licenses/gpl-2.0.txt // #import "SPEmailAccount.h" @implementation SPEmailAccount @synthesize fullname; @synthesize emails; @synthesize type; @synthesize hostname; @synthesize username; @synthesize displayName; @synthesize calendars; - (void)dealloc { [fullname release]; [emails release]; [type release]; [hostname release]; [username release]; [displayName release]; [super dealloc]; } + (SPEmailAccount *)accountWithDictionary:(NSDictionary *)d { return nil; // for subclasses } - (NSArray *)infoArray { NSMutableArray *a = [NSMutableArray array]; if(fullname) [a addObject:[NSString stringWithFormat:@"Name: %@", fullname]]; if(type) [a addObject:[NSString stringWithFormat:@"Type: %@", type]]; if(hostname) [a addObject:[NSString stringWithFormat:@"Host: %@", hostname]]; if(username) [a addObject:[NSString stringWithFormat:@"User: %@", username]]; if(emails) { for (id emailAddress in emails) [a addObject:[NSString stringWithFormat:@"Email: %@", emailAddress]]; } if (calendars) { for (id calendar in calendars) [a addObject:[NSString stringWithFormat:@"Calendar URL: %@", calendar]]; } return a; } @end ================================================ FILE: Classes/SPEmailGmailAccount.h ================================================ // // SPEmailGmailAccount.h // SpyPhone // // Created by Nicolas Seriot on 11/20/09. // Copyright 2009. // Licensed under GPL 2.0 http://www.gnu.org/licenses/gpl-2.0.txt // #import "SPEmailAccount.h" @interface SPEmailGmailAccount : SPEmailAccount { } @end ================================================ FILE: Classes/SPEmailGmailAccount.m ================================================ // // SPEmailGmailAccount.m // SpyPhone // // Created by Nicolas Seriot on 11/20/09. // Copyright 2009. // Licensed under GPL 2.0 http://www.gnu.org/licenses/gpl-2.0.txt // #import "SPEmailGmailAccount.h" @implementation SPEmailGmailAccount + (SPEmailAccount *)accountWithDictionary:(NSDictionary *)d { SPEmailGmailAccount *account = [[SPEmailGmailAccount alloc] init]; account.type = [d valueForKey:@"Short Type String"]; account.fullname = [d valueForKey:@"FullUserName"]; account.hostname = [d valueForKey:@"Hostname"]; account.username = [d valueForKey:@"Username"]; account.displayName = [d valueForKey:@"DisplayName"]; NSString *theEmail = [d valueForKey:@"Username"]; if(![[theEmail lowercaseString] hasSuffix:@"@gmail.com"]) { theEmail = [theEmail stringByAppendingString:@"@gmail.com"]; } account.emails = [NSArray arrayWithObject:theEmail]; return [account autorelease]; } @end ================================================ FILE: Classes/SPEmailIMAPAccount.h ================================================ // // SPEmailIMAPAccount.h // SpyPhone // // Created by Nicolas Seriot on 11/20/09. // Copyright 2009. // Licensed under GPL 2.0 http://www.gnu.org/licenses/gpl-2.0.txt // #import "SPEmailAccount.h" @interface SPEmailIMAPAccount : SPEmailAccount { } @end ================================================ FILE: Classes/SPEmailIMAPAccount.m ================================================ // // SPEmailIMAPAccount.m // SpyPhone // // Created by Nicolas Seriot on 11/20/09. // Copyright 2009. // Licensed under GPL 2.0 http://www.gnu.org/licenses/gpl-2.0.txt // #import "SPEmailIMAPAccount.h" @implementation SPEmailIMAPAccount + (SPEmailAccount *)accountWithDictionary:(NSDictionary *)d { SPEmailIMAPAccount *account = [[SPEmailIMAPAccount alloc] init]; account.type = [d valueForKey:@"Short Type String"]; account.fullname = [d valueForKey:@"FullUserName"]; account.emails = [d valueForKey:@"EmailAddresses"]; account.hostname = [d valueForKey:@"Hostname"]; account.username = [d valueForKey:@"Username"]; account.displayName = [d valueForKey:@"DisplayName"]; return [account autorelease]; } @end ================================================ FILE: Classes/SPEmailIToolsAccount.h ================================================ // // SPEmailIToolsAccount.h // SpyPhone // // Created by Nicolas Seriot on 11/20/09. // Copyright 2009. // Licensed under GPL 2.0 http://www.gnu.org/licenses/gpl-2.0.txt // #import "SPEmailAccount.h" @interface SPEmailIToolsAccount : SPEmailAccount { } @end ================================================ FILE: Classes/SPEmailIToolsAccount.m ================================================ // // SPEmailIToolsAccount.m // SpyPhone // // Created by Nicolas Seriot on 11/20/09. // Copyright 2009. // Licensed under GPL 2.0 http://www.gnu.org/licenses/gpl-2.0.txt // #import "SPEmailIToolsAccount.h" @implementation SPEmailIToolsAccount + (SPEmailAccount *)accountWithDictionary:(NSDictionary *)d { SPEmailIToolsAccount *account = [[SPEmailIToolsAccount alloc] init]; account.type = [d valueForKey:@"Short Type String"]; account.fullname = [d valueForKey:@"FullUserName"]; NSArray *theEmailAddresses = [d valueForKey:@"EmailAddresses"]; NSMutableArray *theEmails = [NSMutableArray array]; for (id emailAddress in theEmailAddresses) { [theEmails addObject:[NSString stringWithFormat:@"%@@me.com", emailAddress]]; } account.emails = theEmails; //account.hostname = nil; account.username = [d valueForKey:@"Username"]; account.displayName = [d valueForKey:@"DisplayName"]; return [account autorelease]; } @end ================================================ FILE: Classes/SPEmailMobileMeAccount.h ================================================ // // SPEmailMobileMeAccount.h // SpyPhone // // Created by Nicolas Seriot on 11/20/09. // Copyright 2009. // Licensed under GPL 2.0 http://www.gnu.org/licenses/gpl-2.0.txt // #import "SPEmailAccount.h" @interface SPEmailMobileMeAccount : SPEmailAccount { } @end ================================================ FILE: Classes/SPEmailMobileMeAccount.m ================================================ // // SPEmailMobileMeAccount.m // SpyPhone // // Created by Nicolas Seriot on 11/20/09. // Copyright 2009. // Licensed under GPL 2.0 http://www.gnu.org/licenses/gpl-2.0.txt // #import "SPEmailMobileMeAccount.h" @implementation SPEmailMobileMeAccount + (SPEmailAccount *)accountWithDictionary:(NSDictionary *)d { SPEmailMobileMeAccount *account = [[SPEmailMobileMeAccount alloc] init]; account.type = [d valueForKey:@"Short Type String"]; account.fullname = [d valueForKey:@"FullUserName"]; account.emails = [d valueForKey:@"EmailAddresses"]; account.username = [d valueForKey:@"Username"]; account.displayName = [d valueForKey:@"DisplayName"]; NSMutableArray *theCalendars = [NSMutableArray array]; NSDictionary *calendars = [d valueForKey:@"Subscribed Calendars"]; if (calendars) { for(id calendarItem in [calendars allValues]) { [theCalendars addObject:[calendarItem valueForKey:@"com.apple.ical.urlsubscribe.url"]]; } account.calendars = theCalendars; } return [account autorelease]; } @end ================================================ FILE: Classes/SPEmailPOPAccount.h ================================================ // // SPEMailPOPAccount.h // SpyPhone // // Created by Nicolas Seriot on 11/20/09. // Copyright 2009. // Licensed under GPL 2.0 http://www.gnu.org/licenses/gpl-2.0.txt // #import "SPEmailAccount.h" @interface SPEmailPOPAccount : SPEmailAccount { } @end ================================================ FILE: Classes/SPEmailPOPAccount.m ================================================ // // SPEMailPOPAccount.m // SpyPhone // // Created by Nicolas Seriot on 11/20/09. // Copyright 2009. // Licensed under GPL 2.0 http://www.gnu.org/licenses/gpl-2.0.txt // #import "SPEmailPOPAccount.h" @implementation SPEmailPOPAccount + (SPEmailAccount *)accountWithDictionary:(NSDictionary *)d { SPEmailPOPAccount *account = [[SPEmailPOPAccount alloc] init]; account.type = [d valueForKey:@"Short Type String"]; account.fullname = [d valueForKey:@"FullUserName"]; account.emails = [d valueForKey:@"EmailAddresses"]; account.hostname = [d valueForKey:@"Hostname"]; account.username = [d valueForKey:@"Username"]; account.displayName = [d valueForKey:@"DisplayName"]; return [account autorelease]; } @end ================================================ FILE: Classes/SPEmailReportVC.h ================================================ // // SPEmailReportVC.h // SpyPhone // // Created by Nicolas Seriot on 11/22/09. // Copyright 2009. // Licensed under GPL 2.0 http://www.gnu.org/licenses/gpl-2.0.txt // #import #import #import "SPAllSourcesTVC.h" @interface SPEmailReportVC : UIViewController { IBOutlet UILabel *message; IBOutlet SPAllSourcesTVC *allSources; } @property (nonatomic, retain) IBOutlet UILabel *message; @property (nonatomic, retain) IBOutlet SPAllSourcesTVC *allSources; - (IBAction)sendReport:(id)sender; @end ================================================ FILE: Classes/SPEmailReportVC.m ================================================ // // SPEmailReportVC.m // SpyPhone // // Created by Nicolas Seriot on 11/22/09. // Copyright 2009. // Licensed under GPL 2.0 http://www.gnu.org/licenses/gpl-2.0.txt // #import "SPEmailReportVC.h" @implementation SPEmailReportVC @synthesize message; @synthesize allSources; - (IBAction)sendReport:(id)sender { if([MFMailComposeViewController canSendMail] == NO) { message.text = @"Error: this device can't send emails."; return; } MFMailComposeViewController *picker = [[MFMailComposeViewController alloc] init]; picker.mailComposeDelegate = self; [picker setSubject:@"SpyPhone Report"]; // Set up recipients NSString *email = [allSources emailForReport]; if(email) [picker setToRecipients:[NSArray arrayWithObject:email]]; // Fill out the email body text NSString *emailBody = [allSources report]; [picker setMessageBody:emailBody isHTML:NO]; [self presentModalViewController:picker animated:YES]; [picker release]; } // Dismisses the email composition interface when users tap Cancel or Send. Proceeds to update the message field with the result of the operation. - (void)mailComposeController:(MFMailComposeViewController*)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError*)error { // message.hidden = NO; // Notifies users about errors associated with the interface switch (result) { case MFMailComposeResultCancelled: message.text = @"Result: canceled"; break; case MFMailComposeResultSaved: message.text = @"Result: saved"; break; case MFMailComposeResultSent: message.text = @"Result: sent"; break; case MFMailComposeResultFailed: message.text = @"Result: failed"; break; default: message.text = @"Result: not sent"; break; } [self dismissModalViewControllerAnimated:YES]; } - (void)didReceiveMemoryWarning { // Releases the view if it doesn't have a superview. [super didReceiveMemoryWarning]; // Release any cached data, images, etc that aren't in use. } - (void)viewDidUnload { // Release any retained subviews of the main view. // e.g. self.myOutlet = nil; } - (void)dealloc { [allSources release]; [message release]; [super dealloc]; } @end ================================================ FILE: Classes/SPImageAnnotation.h ================================================ // // SPImageAnnotation.h // SpyPhone // // Created by Nicolas Seriot on 11/21/09. // Copyright 2009. // Licensed under GPL 2.0 http://www.gnu.org/licenses/gpl-2.0.txt // #import @protocol MKAnnotation; @interface SPImageAnnotation : NSObject { NSString *title; NSString *path; CLLocationCoordinate2D coordinate; } @property (nonatomic, retain) NSString *title; @property (nonatomic, retain) NSString *path; @property (nonatomic, readwrite) CLLocationCoordinate2D coordinate; + (SPImageAnnotation *) annotationWithCoordinate:(CLLocationCoordinate2D)coord date:(NSDate *)date path:(NSString *)path; - (NSString *)annotationViewIdentifier; - (BOOL)hasValidCoordinates; @end ================================================ FILE: Classes/SPImageAnnotation.m ================================================ // // SPImageAnnotation.m // SpyPhone // // Created by Nicolas Seriot on 11/21/09. // Copyright 2009. // Licensed under GPL 2.0 http://www.gnu.org/licenses/gpl-2.0.txt // #import "SPImageAnnotation.h" @implementation SPImageAnnotation @synthesize title; @synthesize path; @synthesize coordinate; - (BOOL)hasValidCoordinates { return coordinate.longitude != 0.0 && coordinate.latitude != 0.0; } + (SPImageAnnotation *)annotationWithCoordinate:(CLLocationCoordinate2D)coord date:(NSDate *)date path:(NSString *)path { SPImageAnnotation *annotation = [[SPImageAnnotation alloc] init]; annotation.coordinate = coord; annotation.path = path; NSDateFormatter *df = [[NSDateFormatter alloc] init]; [df setDateFormat:@"yyyy-MM-dd HH:mm"]; annotation.title = [df stringFromDate:date]; [df release]; return [annotation autorelease]; } - (void)dealloc { [path release]; [title release]; [super dealloc]; } - (NSString *)annotationViewIdentifier { return title; } @end ================================================ FILE: Classes/SPImageMapVC.h ================================================ // // SPMapVC.h // SpyPhone // // Created by Nicolas Seriot on 11/21/09. // Copyright 2009. // Licensed under GPL 2.0 http://www.gnu.org/licenses/gpl-2.0.txt // #import #import @class SPImageVC; @interface SPImageMapVC : UIViewController { NSArray *annotations; IBOutlet MKMapView *mapView; IBOutlet SPImageVC *imageVC; } @property (nonatomic, retain) NSArray *annotations; - (void)addAnnotation:(id )annotation; - (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id )annotation; @end ================================================ FILE: Classes/SPImageMapVC.m ================================================ // // SPMapVC.m // SpyPhone // // Created by Nicolas Seriot on 11/21/09. // Copyright 2009. // Licensed under GPL 2.0 http://www.gnu.org/licenses/gpl-2.0.txt // #import "SPImageMapVC.h" #import "SPImageVC.h" @implementation SPImageMapVC @synthesize annotations; - (void)addAnnotation:(id )annotation { [mapView addAnnotation:annotation]; } - (MKAnnotationView *)mapView:(MKMapView *)aMapView viewForAnnotation:(id )annotation { if([annotation isKindOfClass:[MKUserLocation class]]) return nil; NSString *annID = @"SPImageAnnotation"; MKAnnotationView *av = [aMapView dequeueReusableAnnotationViewWithIdentifier:annID]; if(av == nil) { av = [[[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:annID] autorelease]; [av setRightCalloutAccessoryView:[UIButton buttonWithType:UIButtonTypeDetailDisclosure]]; av.canShowCallout = YES; } return av; } - (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control { NSString *path = [(id)view.annotation path]; NSError *error = nil; NSDictionary *d = [[NSFileManager defaultManager] attributesOfItemAtPath:path error:&error]; if(!d) { NSLog(@"Error: can't read file attributes at path %@, %@ %@", path, [error description], [error userInfo]); } NSDate *date = [d fileModificationDate]; NSDateFormatter *df = [[NSDateFormatter alloc] init]; [df setDateFormat:@"yyyy-MM-dd HH:mm"]; imageVC.title = error ? @"Photo" : [df stringFromDate:date]; [df release]; imageVC.path = path; [self.navigationController pushViewController:imageVC animated:YES]; } - (void)viewDidLoad { [super viewDidLoad]; //mapView.showsUserLocation = YES; if([annotations count] == 0) return; MKCoordinateRegion region; MKCoordinateSpan span = MKCoordinateSpanMake(0.03, 0.03); for(id annotation in annotations) { region = [mapView regionThatFits:MKCoordinateRegionMake(annotation.coordinate, span)]; } [mapView setRegion:region]; [mapView addAnnotations:annotations]; } /* // Override to allow orientations other than the default portrait orientation. - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation { // Return YES for supported orientations return (interfaceOrientation == UIInterfaceOrientationPortrait); } */ - (void)didReceiveMemoryWarning { // Releases the view if it doesn't have a superview. [super didReceiveMemoryWarning]; // Release any cached data, images, etc that aren't in use. } - (void)viewDidUnload { // Release any retained subviews of the main view. // e.g. self.myOutlet = nil; self.annotations = nil; } - (void)dealloc { [annotations release]; [super dealloc]; } @end ================================================ FILE: Classes/SPImageVC.h ================================================ // // SPImageVC.h // SpyPhone // // Created by Nicolas Seriot on 11/21/09. // Copyright 2009. All rights reserved. // #import @interface SPImageVC : UIViewController { NSString *path; IBOutlet UIImageView *imageView; } @property (nonatomic, retain) UIImageView *imageView; @property (nonatomic, retain) NSString *path; @end ================================================ FILE: Classes/SPImageVC.m ================================================ // // SPImageVC.m // SpyPhone // // Created by Nicolas Seriot on 11/21/09. // Copyright 2009. All rights reserved. // #import "SPImageVC.h" @implementation SPImageVC @synthesize imageView; @synthesize path; - (void)viewDidAppear:(BOOL)animated { imageView.image = [UIImage imageWithContentsOfFile:path]; } - (void)viewDidDisappear:(BOOL)animated { imageView.image = nil; } - (void)didReceiveMemoryWarning { // Releases the view if it doesn't have a superview. [super didReceiveMemoryWarning]; // Release any cached data, images, etc that aren't in use. } - (void)viewDidUnload { // Release any retained subviews of the main view. // e.g. self.myOutlet = nil; } - (void)dealloc { [path release]; [imageView release]; [super dealloc]; } @end ================================================ FILE: Classes/SPSourceAddressBookTVC.h ================================================ // // SPSourceAddressBookTVC.h // SpyPhone // // Created by Nicolas Seriot on 11/16/09. // Copyright 2009. // Licensed under GPL 2.0 http://www.gnu.org/licenses/gpl-2.0.txt // #import #import "SPSourceTVC.h" @interface SPSourceAddressBookTVC : SPSourceTVC { } @end ================================================ FILE: Classes/SPSourceAddressBookTVC.m ================================================ // // SPSourceAddressBookTVC.m // SpyPhone // // Created by Nicolas Seriot on 11/16/09. // Copyright 2009. // Licensed under GPL 2.0 http://www.gnu.org/licenses/gpl-2.0.txt // #import "SPSourceAddressBookTVC.h" #import @implementation SPSourceAddressBookTVC - (void)loadData { if(contentsDictionaries) return; ABAddressBookRef addressBook = ABAddressBookCreate(); NSArray *people = (NSArray *)ABAddressBookCopyArrayOfAllPeople(addressBook); NSMutableArray *allEmails = [NSMutableArray array]; for(id record in people) { ABMultiValueRef emailsContainer = ABRecordCopyValue(record, kABPersonEmailProperty); CFArrayRef emails = ABMultiValueCopyArrayOfAllValues(emailsContainer); CFRelease(emailsContainer); if(emails) { [allEmails addObjectsFromArray:(NSArray *)emails]; CFRelease(emails); } } [people release]; CFRelease(addressBook); NSString *keyName = [NSString stringWithFormat:@"%d Email Addresses", [allEmails count]]; self.contentsDictionaries = [NSArray arrayWithObject:[NSDictionary dictionaryWithObject:allEmails forKey:keyName]]; } /* // Override to allow orientations other than the default portrait orientation. - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation { // Return YES for supported orientations return (interfaceOrientation == UIInterfaceOrientationPortrait); } */ - (void)didReceiveMemoryWarning { // Releases the view if it doesn't have a superview. [super didReceiveMemoryWarning]; // Release any cached data, images, etc that aren't in use. } - (void)viewDidUnload { // Release any retained subviews of the main view. // e.g. self.myOutlet = nil; } - (void)dealloc { [super dealloc]; } @end ================================================ FILE: Classes/SPSourceEmailTVC.h ================================================ // // SPSourceEmailTVC.h // SpyPhone // // Created by Nicolas Seriot on 11/15/09. // Copyright 2009. // Licensed under GPL 2.0 http://www.gnu.org/licenses/gpl-2.0.txt // #import #import "SPSourceTVC.h" @interface SPSourceEmailTVC : SPSourceTVC { NSMutableArray *emails; } @property (nonatomic, retain) NSMutableArray *emails; - (NSString *)emailForReport; @end ================================================ FILE: Classes/SPSourceEmailTVC.m ================================================ // // SPSourceEmailTVC.m // SpyPhone // // Created by Nicolas Seriot on 11/15/09. // Copyright 2009. // Licensed under GPL 2.0 http://www.gnu.org/licenses/gpl-2.0.txt // #import "SPSourceEmailTVC.h" #import "SPCell.h" #import "SPEmailASAccount.h" #import "SPEmailPOPAccount.h" #import "SPEmailIToolsAccount.h" #import "SPEmailGmailAccount.h" #import "SPEmailIMAPAccount.h" #import "SPEmailMobileMeAccount.h" @implementation SPSourceEmailTVC @synthesize emails; - (void)dealloc { [emails release]; [super dealloc]; } - (NSString *)emailForReport { [self loadData]; if([emails count] < 1) return nil; return [emails objectAtIndex:0]; } - (void)loadData { if(contentsDictionaries) return; self.emails = [NSMutableArray array]; self.contentsDictionaries = [NSMutableArray array]; NSMutableArray *accountsFound = [NSMutableArray array]; NSString *path = @"/var/mobile/Library/Preferences/com.apple.accountsettings.plist"; NSDictionary *d = [NSDictionary dictionaryWithContentsOfFile:path]; NSArray *accounts = [d valueForKey:@"Accounts"]; for(NSDictionary *account in accounts) { NSString *classValue = [account valueForKey:@"Class"]; if([classValue isEqualToString:@"ASAccount"]) [accountsFound addObject:[SPEmailASAccount accountWithDictionary:account]]; if([classValue isEqualToString:@"POPAccount"]) [accountsFound addObject:[SPEmailPOPAccount accountWithDictionary:account]]; if([classValue isEqualToString:@"iToolsAccount"]) [accountsFound addObject:[SPEmailIToolsAccount accountWithDictionary:account]]; if([classValue isEqualToString:@"GmailAccount"]) [accountsFound addObject:[SPEmailGmailAccount accountWithDictionary:account]]; if([classValue isEqualToString:@"IMAPAccount"]) [accountsFound addObject:[SPEmailIMAPAccount accountWithDictionary:account]]; if([classValue isEqualToString:@"MobileMeAccount"]) [accountsFound addObject:[SPEmailMobileMeAccount accountWithDictionary:account]]; } for(SPEmailAccount *account in accountsFound) { if(!account.emails) continue; [emails addObjectsFromArray:account.emails]; [contentsDictionaries addObject:[NSDictionary dictionaryWithObject:[account infoArray] forKey:account.displayName]]; } } @end ================================================ FILE: Classes/SPSourceKeyboardTVC.h ================================================ // // SPSourceKeyboardTVC.h // SpyPhone // // Created by Nicolas Seriot on 11/16/09. // Copyright 2009. // Licensed under GPL 2.0 http://www.gnu.org/licenses/gpl-2.0.txt // #import #import "SPSourceTVC.h" @interface SPSourceKeyboardTVC : SPSourceTVC { } @end ================================================ FILE: Classes/SPSourceKeyboardTVC.m ================================================ // // SPSourceKeyboardTVC.m // SpyPhone // // Created by Nicolas Seriot on 11/16/09. // Copyright 2009. // Licensed under GPL 2.0 http://www.gnu.org/licenses/gpl-2.0.txt // #import "SPSourceKeyboardTVC.h" @implementation SPSourceKeyboardTVC - (BOOL)caseInsensitiveString:(NSString *)s startsWithUnichar:(unichar)c { if(![s length]) return NO; unichar c1 = [[s lowercaseString] characterAtIndex:0]; unichar c2 = [[[NSString stringWithFormat:@"%c", c] lowercaseString] characterAtIndex:0]; return c1 == c2; } - (NSString *)sanitizeString:(NSString *)s { NSString *s2 = [s stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; s2 = [s2 stringByTrimmingCharactersInSet:[NSCharacterSet controlCharacterSet]]; return [s2 stringByTrimmingCharactersInSet:[NSCharacterSet illegalCharacterSet]]; } - (NSArray *)wordsInDictionaryCacheFileAtPath:(NSString *)path { NSData *data = [NSData dataWithContentsOfFile:path]; if(!data) return nil; static const int BUFSIZE = 256; int length = [data length]; int len = 0; int pos = 0; NSRange range; char buf[BUFSIZE]; NSString *w = nil; NSMutableArray *words = [NSMutableArray array]; while(pos < length) { range = NSMakeRange(pos, MIN(BUFSIZE, length-pos)); [data getBytes:buf range:range]; len = strlen(buf); w = [[NSString alloc] initWithBytes:buf length:len encoding:NSUTF8StringEncoding]; if([w length]) { [words addObject:[self sanitizeString:w]]; } [w release]; pos += (len + 1); } return words; } - (void)loadData { if(contentsDictionaries) return; NSMutableSet *set = [NSMutableSet set]; NSString *dir = @"/var/mobile/Library/Keyboard/"; NSError *error = nil; NSArray *dirContents = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:dir error:&error]; if(dirContents == nil) { NSLog(@"-- error: %@", error); return; } for(NSString *filePath in dirContents) { if(![filePath hasSuffix:@".dat"]) continue; NSArray *a = [self wordsInDictionaryCacheFileAtPath:[dir stringByAppendingPathComponent:filePath]]; if(!a) continue; [set addObjectsFromArray:a]; } NSArray *words = [[set allObjects] sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)]; NSMutableArray *letters = [NSMutableArray array]; NSMutableArray *letterWords = [[NSMutableArray alloc] init]; NSString *current = nil; for(NSString *w in words) { if([w length] == 0) continue; NSString *s = [[w substringToIndex:1] lowercaseString]; if([s isEqualToString:current]) { // w starts with current letter [letterWords addObject:w]; } else { // w start with new letter // add previous letterWords to letters if(current) { [letters addObject:[NSDictionary dictionaryWithObjectsAndKeys:letterWords, current, nil]]; } // update current current = s; // create new letterWords [letterWords release]; letterWords = [[NSMutableArray alloc] init]; [letterWords addObject:w]; } } if(!current) current = @""; [letters addObject:[NSDictionary dictionaryWithObjectsAndKeys:letterWords, current, nil]]; [letterWords release]; self.contentsDictionaries = letters; } #pragma mark UITableViewDataSource - (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView { NSMutableArray *a = [NSMutableArray array]; for(NSDictionary *d in contentsDictionaries) { [a addObject:[[d allKeys] lastObject]]; } return a; } @end ================================================ FILE: Classes/SPSourceLocationTVC.h ================================================ // // SPSourceLocationTVC.h // SpyPhone // // Created by Nicolas Seriot on 11/15/09. // Copyright 2009. // Licensed under GPL 2.0 http://www.gnu.org/licenses/gpl-2.0.txt // #import #import #import "SPSourceTVC.h" @class CLLocation; @interface SPSourceLocationTVC : SPSourceTVC /* */ { NSArray *items; // MKReverseGeocoder *geo; NSString *geoString; NSString *locString; NSString *locDateString; NSString *timezone; NSArray *cities; CLLocation *cachedLocationFromMaps; } //@property (nonatomic, retain) MKReverseGeocoder *geo; @property (nonatomic, retain) NSString *geoString; @property (nonatomic, retain) CLLocation *cachedLocationFromMaps; @property (nonatomic, retain) NSArray *cities; @property (nonatomic, retain) NSString *locString; @property (nonatomic, retain) NSString *locDateString; @property (nonatomic, retain) NSString *timezone; @end ================================================ FILE: Classes/SPSourceLocationTVC.m ================================================ // // SPSourceLocationTVC.m // SpyPhone // // Created by Nicolas Seriot on 11/15/09. // Copyright 2009. // Licensed under GPL 2.0 http://www.gnu.org/licenses/gpl-2.0.txt // #import "SPSourceLocationTVC.h" #import #import "SPImageMapVC.h" #import "SPImageAnnotation.h" @implementation SPSourceLocationTVC @synthesize cities; //@synthesize geo; @synthesize geoString; @synthesize locString; @synthesize locDateString; @synthesize timezone; @synthesize cachedLocationFromMaps; - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { if(indexPath.section == 0 && indexPath.row == 0 && cachedLocationFromMaps) { SPImageMapVC *mapVC = [[SPImageMapVC alloc] initWithNibName:@"SPImageMapVC" bundle:[NSBundle mainBundle]]; [self.navigationController pushViewController:mapVC animated:YES]; SPImageAnnotation *annotation = [SPImageAnnotation annotationWithCoordinate:cachedLocationFromMaps.coordinate date:nil path:nil]; if(annotation) [mapVC addAnnotation:annotation]; } } - (void)loadData { if(contentsDictionaries) return; NSString *path = @"/var/mobile/Library/Preferences/com.apple.Maps.plist"; NSDictionary *d = [NSDictionary dictionaryWithContentsOfFile:path]; NSData *data = [d valueForKey:@"UserLocation"]; CLLocation *loc = data ? [NSKeyedUnarchiver unarchiveObjectWithData:data] : nil; self.cachedLocationFromMaps = loc; self.locString = @""; self.locDateString = @""; if(loc) { self.locString = [NSString stringWithFormat:@"%f, %f", loc.coordinate.latitude, loc.coordinate.longitude]; self.locDateString = [NSString stringWithFormat:@"%@", loc.timestamp]; } path = @"/var/mobile/Library/Preferences/com.apple.preferences.datetime.plist"; d = [NSDictionary dictionaryWithContentsOfFile:path]; self.timezone = [NSString stringWithFormat:@"%@", [d valueForKey:@"timezone"]]; path = @"/var/mobile/Library/Preferences/com.apple.weather.plist"; d = [NSDictionary dictionaryWithContentsOfFile:path]; NSMutableArray *citiesNames = [NSMutableArray array]; for(NSDictionary *dict in [d valueForKey:@"Cities"]) { [citiesNames addObject:[dict objectForKey:@"Name"]]; } self.cities = citiesNames; self.contentsDictionaries = [NSArray arrayWithObjects: [NSDictionary dictionaryWithObjectsAndKeys:[NSArray arrayWithObject:locString], @"Location", nil], [NSDictionary dictionaryWithObjectsAndKeys:[NSArray arrayWithObject:locDateString], @"Location Date", nil], [NSDictionary dictionaryWithObjectsAndKeys:[NSArray arrayWithObject:timezone], @"Timezone", nil], [NSDictionary dictionaryWithObjectsAndKeys:cities, @"Weather Cities", nil], nil]; /* self.geo = [[[MKReverseGeocoder alloc] initWithCoordinate:loc.coordinate] autorelease]; geo.delegate = self; [geo start]; */ } - (void)dealloc { [items release]; // [geo release]; [geoString release]; [cachedLocationFromMaps release]; [super dealloc]; } /* #pragma mark MKReverseGeocoderDelegate - (void)reverseGeocoder:(MKReverseGeocoder *)geocoder didFindPlacemark:(MKPlacemark *)placemark { self.geoString = [NSString stringWithFormat:@"%@ %@", placemark.locality, placemark.country]; self.contentsDictionaries = [NSArray arrayWithObjects: [NSDictionary dictionaryWithObjectsAndKeys:[NSArray arrayWithObject:[NSString stringWithFormat:@"%@ %@", locString, geoString]], @"Location", nil], [NSDictionary dictionaryWithObjectsAndKeys:[NSArray arrayWithObject:locDateString], @"Location Date", nil], [NSDictionary dictionaryWithObjectsAndKeys:[NSArray arrayWithObject:timezone], @"Timezone", nil], [NSDictionary dictionaryWithObjectsAndKeys:cities, @"Weather Cities", nil], nil]; [self.tableView reloadData]; } - (void)reverseGeocoder:(MKReverseGeocoder *)geocoder didFailWithError:(NSError *)error { NSLog(@"-- Error: %@", [error description]); } */ @end ================================================ FILE: Classes/SPSourcePhoneTVC.h ================================================ // // SPSourcePhoneTVC.h // SpyPhone // // Created by Nicolas Seriot on 11/15/09. // Copyright 2009. // Licensed under GPL 2.0 http://www.gnu.org/licenses/gpl-2.0.txt // #import #import "SPSourceTVC.h" @interface SPSourcePhoneTVC : SPSourceTVC { NSString *ICCID; // NSString *IMEI; NSString *IMSI; NSString *phone; NSString *UUID; NSString *lastDialed; NSString *lastContact; NSString *lastForwardNumber; NSMutableArray *callHistories; NSString *prettyBytesSent; NSString *prettyBytesReceived; } @property (nonatomic, retain) NSString *ICCID; //@property (nonatomic, retain) NSString *IMEI; @property (nonatomic, retain) NSString *IMSI; @property (nonatomic, retain) NSString *phone; @property (nonatomic, retain) NSString *UUID; @property (nonatomic, retain) NSString *lastDialed; @property (nonatomic, retain) NSString *lastContact; @property (nonatomic, retain) NSString *lastForwardNumber; @property (nonatomic, retain) NSString *prettyBytesSent; @property (nonatomic, retain) NSString *prettyBytesReceived; @property (nonatomic, retain) NSMutableArray *callHistories; @end ================================================ FILE: Classes/SPSourcePhoneTVC.m ================================================ // // SPSourcePhoneTVC.m // SpyPhone // // Created by Nicolas Seriot on 11/15/09. // Copyright 2009. // Licensed under GPL 2.0 http://www.gnu.org/licenses/gpl-2.0.txt // #import "SPSourcePhoneTVC.h" #import "SPCell.h" #import #import "FMDatabase.h" #import "NSNumber+SP.h" #import #import @implementation SPSourcePhoneTVC @synthesize ICCID; //@synthesize IMEI; @synthesize IMSI; @synthesize phone; @synthesize UUID; @synthesize lastDialed; @synthesize lastContact; @synthesize lastForwardNumber; @synthesize callHistories; @synthesize prettyBytesSent; @synthesize prettyBytesReceived; - (NSString *)nameOfABPersonWithID:(NSUInteger)recordID { ABAddressBookRef addressBook = ABAddressBookCreate(); ABRecordRef person = ABAddressBookGetPersonWithRecordID(addressBook, recordID); if(!person) { CFRelease(addressBook); return nil; } NSString *firstName = (NSString *)ABRecordCopyValue(person, kABPersonFirstNameProperty); NSString *lastName = (NSString *)ABRecordCopyValue(person, kABPersonLastNameProperty); NSString *fullName = nil; if(firstName && lastName) { fullName = [NSString stringWithFormat:@"%@ %@", firstName, lastName]; } else if (firstName) { fullName = [NSString stringWithString:firstName]; } else if (lastName) { fullName = [NSString stringWithString:lastName]; } [firstName release]; [lastName release]; CFRelease(addressBook); return fullName; } - (void)loadData { if(contentsDictionaries) return; NSString *path = @"/private/var/wireless/Library/Preferences/com.apple.commcenter.plist"; NSDictionary *d = [NSDictionary dictionaryWithContentsOfFile:path]; self.ICCID = [d valueForKey:@"ICCID"]; self.IMSI = [d valueForKey:@"IMSI"]; self.phone = [[NSUserDefaults standardUserDefaults] valueForKey:@"SBFormattedPhoneNumber"]; self.UUID = [[UIDevice currentDevice] uniqueIdentifier]; /* NSBundle *b = [NSBundle bundleWithPath:@"/System/Library/PrivateFrameworks/Message.framework"]; BOOL success = [b load]; if(success) { Class NetworkController = NSClassFromString(@"NetworkController"); id nc = [NetworkController sharedInstance]; if([nc respondsToSelector:@selector(IMEI)]) { self.IMEI = [nc IMEI]; } } if(!self.IMEI) self.IMEI = @""; */ path = @"/var/mobile/Library/Preferences/com.apple.mobilephone.settings.plist"; d = [NSDictionary dictionaryWithContentsOfFile:path]; NSString *callForwardingNumber = [d valueForKey:@"call-forwarding-number"]; self.lastForwardNumber = callForwardingNumber ? [NSString stringWithFormat:@"%@", callForwardingNumber] : nil; path = @"/var/mobile/Library/Preferences/com.apple.mobilephone.plist"; d = [NSDictionary dictionaryWithContentsOfFile:path]; NSString *s = [NSString stringWithFormat:@"%@", [d valueForKey:@"DialerSavedNumber"]]; self.lastDialed = [s length] == 0 ? nil : s; self.contentsDictionaries = [NSMutableArray array]; NSUInteger abId = [[d valueForKey:@"AddressBookLastDialedUid"] intValue]; NSString *fullName = [self nameOfABPersonWithID:abId]; self.lastContact = fullName; /**/ self.callHistories = [NSMutableArray array]; FMDatabase *db = [FMDatabase databaseWithPath:@"/private/var/wireless/Library/CallHistory/call_history.db"]; // NSLocale *usLocale = [[[NSLocale alloc] initWithLocaleIdentifier:@"en_US"] autorelease]; if([db open]) { FMResultSet *rs = [db executeQuery:@"select address, date, flags, duration from call order by date"]; while ([rs next]) { int dateInt = [rs intForColumn:@"date"]; NSDate *date = [NSDate dateWithTimeIntervalSince1970:dateInt]; NSDateFormatter *df = [[NSDateFormatter alloc] init]; [df setDateFormat:@"YYYY-MM-dd HH:mm"]; NSString *dateString = [df stringFromDate:date]; int flagsInt = [rs intForColumn:@"flags"]; NSString *flags = @"?"; switch (flagsInt) { case 4: flags = @"<-"; break; case 5: flags = @"->"; break; default: break; } int durationInt = [rs intForColumn:@"duration"]; NSString *duration = [NSString stringWithFormat:@"%d:%02d", durationInt / 60, durationInt % 60]; NSString *logLine = [NSString stringWithFormat:@"%@ %@ %@ (%@)", dateString, flags, [rs stringForColumn:@"address"], duration]; [callHistories addObject:logLine]; } [rs close]; rs = [db executeQuery:@"select bytes_rcvd, bytes_sent from data where pdp_ip = 0"]; while ([rs next]) { double bytes_sent = [rs doubleForColumn:@"bytes_sent"]; double bytes_rcvd = [rs doubleForColumn:@"bytes_rcvd"]; self.prettyBytesSent = [[NSNumber numberWithDouble:bytes_sent] prettyBytes]; self.prettyBytesReceived = [[NSNumber numberWithDouble:bytes_rcvd] prettyBytes]; } [rs close]; [db close]; } /**/ CTTelephonyNetworkInfo *networkInfo = [[CTTelephonyNetworkInfo alloc] init]; CTCarrier *carrier = networkInfo.subscriberCellularProvider; [networkInfo release]; NSString *s1 = [NSString stringWithFormat:@"%@ %@", [carrier isoCountryCode], [carrier carrierName]]; NSString *s2 = [NSString stringWithFormat:@"country %@ network %@", [carrier mobileCountryCode], [carrier mobileNetworkCode]]; NSArray *carrierInfoArray = [NSArray arrayWithObjects:s1, s2, nil]; NSDictionary *carrierInfo = [NSDictionary dictionaryWithObjectsAndKeys:carrierInfoArray, @"Carrier Info", nil]; /**/ if(carrierInfo) { [self.contentsDictionaries addObject:carrierInfo]; } if(self.lastForwardNumber) { NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:[NSArray arrayWithObject:self.lastForwardNumber], @"Call forwarding number", nil]; [self.contentsDictionaries addObject:dict]; } if(self.phone) { NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:[NSArray arrayWithObject:self.phone], @"Phone number", nil]; [self.contentsDictionaries addObject:dict]; } if(self.lastContact) { NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:[NSArray arrayWithObject:self.lastContact], @"Last contact called from list", nil]; [self.contentsDictionaries addObject:dict]; } if(self.lastDialed) { NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:[NSArray arrayWithObject:self.lastDialed], @"Last dialed", nil]; [self.contentsDictionaries addObject:dict]; } if(self.ICCID) { NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:[NSArray arrayWithObject:self.ICCID], @"ICCID (SIM card serial number)", nil]; [self.contentsDictionaries addObject:dict]; } if(self.IMSI) { NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:[NSArray arrayWithObject:self.IMSI], @"IMSI (International Mobile Subscriber Identity)", nil]; [self.contentsDictionaries addObject:dict]; } if(self.UUID) { NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:[NSArray arrayWithObject:self.UUID], @"Device UUID", nil]; [self.contentsDictionaries addObject:dict]; } if(prettyBytesSent) { NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:[NSArray arrayWithObject:prettyBytesSent], @"Cellular Network - Bytes Sent", nil]; [self.contentsDictionaries addObject:dict]; } if(prettyBytesReceived) { NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:[NSArray arrayWithObject:prettyBytesReceived], @"Cellular Network - Bytes Received", nil]; [self.contentsDictionaries addObject:dict]; } if(callHistories) { NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:callHistories, @"Call History", nil]; [self.contentsDictionaries addObject:dict]; } } - (void)didReceiveMemoryWarning { // Releases the view if it doesn't have a superview. [super didReceiveMemoryWarning]; // Release any cached data, images, etc that aren't in use. } - (void)dealloc { [ICCID release]; // [IMEI release]; [IMSI release]; [phone release]; [UUID release]; [lastForwardNumber release]; [lastDialed release]; [lastContact release]; [callHistories release]; [prettyBytesSent release]; [prettyBytesReceived release]; [super dealloc]; } @end ================================================ FILE: Classes/SPSourcePhotosTVC.h ================================================ // // SPSourcePhotosTVC.h // SpyPhone // // Created by Nicolas Seriot on 11/15/09. // Copyright 2009. // Licensed under GPL 2.0 http://www.gnu.org/licenses/gpl-2.0.txt // #import "SPSourceTVC.h" @class SPImageMapVC; @class SPImageVC; @interface SPSourcePhotosTVC : SPSourceTVC { NSMutableArray *coordinates; NSMutableArray *annotations; IBOutlet SPImageMapVC *mapVC; IBOutlet SPImageVC *imageVC; } @property (nonatomic, retain) NSMutableArray *annotations; @property (nonatomic, retain) NSMutableArray *coordinates; @property (nonatomic, retain) SPImageMapVC *mapVC; @property (nonatomic, retain) SPImageVC *imageVC; @end ================================================ FILE: Classes/SPSourcePhotosTVC.m ================================================ // // SPSourcePhotosTVC.m // SpyPhone // // Created by Nicolas Seriot on 11/15/09. // Copyright 2009. // Licensed under GPL 2.0 http://www.gnu.org/licenses/gpl-2.0.txt // #import #import "SPSourcePhotosTVC.h" #import "UIImage+GPS.h" #import "SPImageMapVC.h" #import "SPImageVC.h" #import "SPImageAnnotation.h" @implementation SPSourcePhotosTVC @synthesize annotations; @synthesize coordinates; @synthesize mapVC; @synthesize imageVC; - (void)mapButtonClicked:(id)sender { NSArray *annotationsWithValidCoordinates = [annotations filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) { SPImageAnnotation *annotation = (SPImageAnnotation *)evaluatedObject; return [annotation hasValidCoordinates]; }] ]; mapVC.annotations = annotationsWithValidCoordinates; [self.navigationController pushViewController:mapVC animated:YES]; } - (NSArray *)jpgPngPaths { NSMutableArray *a = [NSMutableArray array]; NSString *dirPath = @"/private/var/mobile/Media/PhotoStreamsData/"; NSFileManager *fm = [[NSFileManager alloc] init]; NSDirectoryEnumerator *dirEnum = [fm enumeratorAtPath:dirPath]; NSString *path = nil; while (path = [dirEnum nextObject]) { if([[path pathComponents] containsObject:@".MISC"]) continue; NSString *fullPath = [dirPath stringByAppendingPathComponent:path]; if([fm isReadableFileAtPath:fullPath] == NO) continue; NSString *ext = [fullPath pathExtension]; if([ext isEqualToString:@"JPG"] || [ext isEqualToString:@"PNG"]) { [a addObject:fullPath]; } } return a; } - (void)readPhotosInNewThread { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; NSArray *jpgPngPaths = [self jpgPngPaths]; NSEnumerator *e = [jpgPngPaths reverseObjectEnumerator]; NSAutoreleasePool *subpool = [[NSAutoreleasePool alloc] init]; NSString *s = nil; while(s = [e nextObject]) { [subpool release]; subpool = [[NSAutoreleasePool alloc] init]; CLLocationCoordinate2D coord = [UIImage coordinatesOfImageAtPath:s]; //if(coord.latitude == 0.0 && coord.longitude == 0.0) continue; NSNumber *lat = [NSNumber numberWithDouble:coord.latitude]; NSNumber *lon = [NSNumber numberWithDouble:coord.longitude]; [coordinates addObject:[NSArray arrayWithObjects:lat, lon, nil]]; NSString *coordString = (lat && lon) ? [NSString stringWithFormat:@"%@, %@", lat, lon] : nil; NSError *error = nil; NSDictionary *d = [[NSFileManager defaultManager] attributesOfItemAtPath:s error:&error]; if(!d) { NSLog(@"Error, can't read attributes of file at path %@, %@ %@", s, [error description], [error userInfo]); continue; } NSDate *date = [d fileModificationDate]; NSString *dateString = date ? [date description] : @""; SPImageAnnotation *annotation = [SPImageAnnotation annotationWithCoordinate:coord date:date path:s]; [annotations performSelectorOnMainThread:@selector(addObject:) withObject:annotation waitUntilDone:YES]; NSDictionary *cd = [NSDictionary dictionaryWithObject:[NSArray arrayWithObject:coordString] forKey:dateString]; [contentsDictionaries performSelectorOnMainThread:@selector(addObject:) withObject:cd waitUntilDone:YES]; [self.tableView performSelectorOnMainThread:@selector(reloadData) withObject:nil waitUntilDone:YES]; if([annotation hasValidCoordinates]) { [mapVC performSelectorOnMainThread:@selector(addAnnotation:) withObject:annotation waitUntilDone:YES]; } } [subpool release]; [pool release]; } - (void)loadData { if(contentsDictionaries) return; UIBarButtonItem *mapButton = [[UIBarButtonItem alloc] initWithTitle:@"Map" style:UIBarButtonItemStylePlain target:self action:@selector(mapButtonClicked:)]; super.navigationItem.rightBarButtonItem = mapButton; self.contentsDictionaries = [NSMutableArray array]; self.annotations = [NSMutableArray array]; [NSThread detachNewThreadSelector:@selector(readPhotosInNewThread) toTarget:self withObject:nil]; } - (void)dealloc { [annotations release]; [coordinates release]; [mapVC release]; [imageVC release]; [super dealloc]; } #pragma mark UITableViewDelegate - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { if(indexPath.section >= [annotations count]) return; NSString *path = [[annotations objectAtIndex:indexPath.section] path]; NSString *imageName = [[path lastPathComponent] stringByDeletingPathExtension]; // NSString *thmPath = [NSString stringWithFormat:@"/var/mobile/Media/DCIM/110APPLE/.MISC/%@.THM", imageName]; imageVC.path = path; imageVC.title = imageName; [self.navigationController pushViewController:imageVC animated:YES]; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { UITableViewCell *cell = [super tableView:tableView cellForRowAtIndexPath:indexPath]; cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; return cell; } @end ================================================ FILE: Classes/SPSourceTVC.h ================================================ // // SPSourceTVC.h // SpyPhone // // Created by Nicolas Seriot on 11/17/09. // Copyright 2009. All rights reserved. // Licensed under GPL 2.0 http://www.gnu.org/licenses/gpl-2.0.txt // #import #import "SPWebViewVC.h" /* [{[va, vb], k1}, {[vc, vd], k2}, {[ve, vf], k3}] */ @interface SPSourceTVC : UITableViewController { NSMutableArray *contentsDictionaries; } @property (retain) NSMutableArray *contentsDictionaries; - (UIImage *)image; - (void)loadData; @end ================================================ FILE: Classes/SPSourceTVC.m ================================================ // // SPSourceTVC.m // SpyPhone // // Created by Nicolas Seriot on 11/17/09. // Copyright 2009. All rights reserved. // Licensed under GPL 2.0 http://www.gnu.org/licenses/gpl-2.0.txt // #import "SPSourceTVC.h" #import "SPCell.h" @implementation SPSourceTVC @synthesize contentsDictionaries; - (UIImage *)image { NSString *className = NSStringFromClass([self class]); NSString *name = nil; if([className hasPrefix:@"SPSource"] && [className hasSuffix:@"TVC"]) { NSRange range = NSMakeRange(8, [className length] - 3 - 8); name = [className substringWithRange:range]; } NSString *imageName = [name stringByAppendingPathExtension:@"png"]; return [UIImage imageNamed:imageName]; } - (void)didReceiveMemoryWarning { // Releases the view if it doesn't have a superview. [super didReceiveMemoryWarning]; // Release any cached data, images, etc that aren't in use. } - (void)loadData { // to be overriden by subclasses } - (void)viewDidLoad { [super viewDidLoad]; if(!contentsDictionaries) [self loadData]; } - (void)dealloc { [super dealloc]; } #pragma mark UITableViewDataSource - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { return [contentsDictionaries count]; } - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section { NSDictionary *d = [contentsDictionaries objectAtIndex:section]; return [[d allKeys] lastObject]; } - (NSInteger)tableView:(UITableView *)table numberOfRowsInSection:(NSInteger)section { NSDictionary *d = [contentsDictionaries objectAtIndex:section]; NSArray *a = [[d allValues] lastObject]; return [a count]; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *SourceCellIdentifier = @"SPCell"; SPCell *cell = (SPCell *)[tableView dequeueReusableCellWithIdentifier:SourceCellIdentifier]; if (cell == nil) { cell = (SPCell *)[[[NSBundle mainBundle] loadNibNamed:@"SPCell" owner:self options:nil] lastObject]; } NSArray *a = [[[contentsDictionaries objectAtIndex:indexPath.section] allValues] lastObject]; cell.accessoryType = UITableViewCellAccessoryNone; cell.textLabel.adjustsFontSizeToFitWidth = YES; cell.textLabel.text = [a objectAtIndex:indexPath.row]; return cell; } @end ================================================ FILE: Classes/SPSourceWifiTVC.h ================================================ // // SPSourceWifiTVC.h // SpyPhone // // Created by Nicolas Seriot on 11/15/09. // Copyright 2009. // Licensed under GPL 2.0 http://www.gnu.org/licenses/gpl-2.0.txt // #import #import "SPSourceTVC.h" #import "OUILookupTool.h" @class SPWifiMapVC; @interface SPSourceWifiTVC : SPSourceTVC { NSMutableArray *annotations; NSMutableArray *accessPoints; IBOutlet SPWifiMapVC *mapVC; } @property (nonatomic, retain) NSMutableArray *annotations; @property (nonatomic, retain) NSMutableArray *accessPoints; @property (nonatomic, retain) SPWifiMapVC *mapVC; @end ================================================ FILE: Classes/SPSourceWifiTVC.m ================================================ // // SPSourceWifiTVC.m // SpyPhone // // Created by Nicolas Seriot on 11/15/09. // Copyright 2009. // Licensed under GPL 2.0 http://www.gnu.org/licenses/gpl-2.0.txt // #import "SPSourceWifiTVC.h" #import "OUILookupTool.h" #import "SPWifiMapVC.h" #import "SPWifiAnnotation.h" #import "SPCell.h" @implementation SPSourceWifiTVC @synthesize annotations; @synthesize accessPoints; @synthesize mapVC; - (void)loadData { if(contentsDictionaries) return; UIBarButtonItem *mapButton = [[UIBarButtonItem alloc] initWithTitle:@"Map" style:UIBarButtonItemStylePlain target:self action:@selector(mapButtonClicked:)]; super.navigationItem.rightBarButtonItem = mapButton; self.contentsDictionaries = [NSMutableArray array]; self.annotations = [NSMutableArray array]; self.accessPoints = [NSMutableArray array]; NSString *path = @"/Library/Preferences/SystemConfiguration/com.apple.wifi.plist"; NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:path]; if(!dict) return; NSArray *a = [dict valueForKey:@"List of known networks"]; if(!a) return; for(NSDictionary *d in a) { NSMutableDictionary *md = [NSMutableDictionary dictionaryWithDictionary:d]; [OUILookupTool locateWifiAccessPoint:md delegate:self]; NSString *name = [d valueForKey:@"SSID_STR"]; NSDate *joined = [md valueForKey:@"lastJoined"]; NSDate *autoJoined = [md valueForKey:@"lastAutoJoined"]; NSString *date = [NSString stringWithFormat:@"%@", autoJoined ? autoJoined : joined]; [contentsDictionaries addObject:[NSDictionary dictionaryWithObject:[NSArray arrayWithObject:name] forKey:date]]; [accessPoints addObject:md]; } } - (void)mapButtonClicked:(id)sender { mapVC.annotations = annotations; [self.navigationController pushViewController:mapVC animated:YES]; } - (void)dealloc { [accessPoints release]; [annotations release]; [mapVC release]; [super dealloc]; } #pragma mark OUILookupTool - (void)OUILookupTool:(OUILookupTool *)ouiLookupTool didLocateAccessPoint:(NSDictionary *)ap { //NSLog(@"-- %@", ap); [accessPoints addObject:ap]; SPWifiAnnotation *annotation = [SPWifiAnnotation annotationWithAccessPoint:ap]; if(annotation) [annotations addObject:annotation]; } #pragma mark UITableViewDelegate - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { NSDictionary *ap = [accessPoints objectAtIndex:indexPath.section]; SPWifiAnnotation *annotation = [SPWifiAnnotation annotationWithAccessPoint:ap]; if(annotation == nil) return; mapVC.annotations = [NSArray arrayWithObject:annotation]; [self.navigationController pushViewController:mapVC animated:YES]; } @end ================================================ FILE: Classes/SPWebViewVC.h ================================================ // // SPWebViewVC.h // SpyPhone // // Created by Nicolas Seriot on 11/15/09. // Copyright 2009. // Licensed under GPL 2.0 http://www.gnu.org/licenses/gpl-2.0.txt // #import @interface SPWebViewVC : UIViewController { NSURLRequest *request; IBOutlet UIWebView *webView; } @property (nonatomic, retain) NSURLRequest *request; @property (nonatomic, retain) UIWebView *webView; @end ================================================ FILE: Classes/SPWebViewVC.m ================================================ // // SPWebViewVC.m // SpyPhone // // Created by Nicolas Seriot on 11/15/09. // Copyright 2009. // Licensed under GPL 2.0 http://www.gnu.org/licenses/gpl-2.0.txt // #import "SPWebViewVC.h" @implementation SPWebViewVC @synthesize webView; @synthesize request; - (void)didReceiveMemoryWarning { // Releases the view if it doesn't have a superview. [super didReceiveMemoryWarning]; // Release any cached data, images, etc that aren't in use. } - (void)viewDidUnload { // Release any retained subviews of the main view. // e.g. self.myOutlet = nil; } - (void)viewDidAppear:(BOOL)animated { [webView loadRequest:request]; } - (void)viewDidDisappear:(BOOL)animated { [webView loadRequest:nil]; } - (void)dealloc { [request release]; [webView release]; [super dealloc]; } @end ================================================ FILE: Classes/SPWifiAnnotation.h ================================================ // // SPWifiAnnotation.h // SpyPhone // // Created by Nicolas Seriot on 10/31/10. // Copyright 2010 IICT. All rights reserved. // #import @protocol MKAnnotation; @interface SPWifiAnnotation : NSObject { NSDictionary *accessPoint; CLLocationCoordinate2D coordinate; } @property (nonatomic, retain) NSDictionary *accessPoint; @property (nonatomic, readwrite) CLLocationCoordinate2D coordinate; + (SPWifiAnnotation *)annotationWithAccessPoint:(NSDictionary *)d; - (NSString *)annotationViewIdentifier; - (NSString *)title; - (NSString *)subtitle; @end ================================================ FILE: Classes/SPWifiAnnotation.m ================================================ // // SPWifiAnnotation.m // SpyPhone // // Created by Nicolas Seriot on 10/31/10. // Copyright 2010 IICT. All rights reserved. // #import "SPWifiAnnotation.h" @implementation SPWifiAnnotation @synthesize coordinate; @synthesize accessPoint; - (NSString *)title { return [accessPoint valueForKey:@"SSID_STR"]; } - (NSString *)subtitle { NSDate *joined = [accessPoint valueForKey:@"lastJoined"]; NSDate *autoJoined = [accessPoint valueForKey:@"lastAutoJoined"]; NSDate *date = autoJoined ? autoJoined : joined; return [date description]; } + (SPWifiAnnotation *)annotationWithAccessPoint:(NSDictionary *)ap { NSString *latitude = [ap valueForKeyPath:@"location.latitude"]; NSString *longitude = [ap valueForKeyPath:@"location.longitude"]; if(latitude == nil || longitude == nil) return nil; SPWifiAnnotation *annotation = [[SPWifiAnnotation alloc] init]; annotation.accessPoint = ap; annotation.coordinate = CLLocationCoordinate2DMake([latitude doubleValue], [longitude doubleValue]);; return [annotation autorelease]; } - (void)dealloc { [accessPoint release]; [super dealloc]; } - (NSString *)annotationViewIdentifier { return [accessPoint valueForKey:@"BSSID"]; } @end ================================================ FILE: Classes/SPWifiMapVC.h ================================================ // // SPWifiMapVC.h // SpyPhone // // Created by Nicolas Seriot on 10/31/10. // Copyright 2010 IICT. All rights reserved. // #import #import @interface SPWifiMapVC : UIViewController { NSArray *annotations; IBOutlet MKMapView *mapView; } @property (nonatomic, retain) NSArray *annotations; - (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id )annotation; @end ================================================ FILE: Classes/SPWifiMapVC.m ================================================ // // SPWifiMapVC.m // SpyPhone // // Created by Nicolas Seriot on 10/31/10. // Copyright 2010 IICT. All rights reserved. // #import "SPWifiMapVC.h" @implementation SPWifiMapVC @synthesize annotations; //- (void)setAnnotations:(NSArray *)someAnnotations { // [mapView removeAnnotations:mapView.annotations]; // // [annotations autorelease]; // annotations = [someAnnotations retain]; // // [mapView addAnnotations:annotations]; //} - (void)loadView { [super loadView]; self.title = @"Wifi Map"; } - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; [mapView removeAnnotations:mapView.annotations]; [mapView addAnnotations:annotations]; id annotation = [annotations lastObject]; MKCoordinateSpan span = MKCoordinateSpanMake(0.03, 0.03); MKCoordinateRegion region = [mapView regionThatFits:MKCoordinateRegionMake(annotation.coordinate, span)]; @try { [mapView setRegion:region animated:NO]; } @catch (NSException *exception) { NSLog(@"-- %@", exception); } @finally { } } - (void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; if([annotations count] == 1) [mapView selectAnnotation:[annotations lastObject] animated:YES]; } - (MKAnnotationView *)mapView:(MKMapView *)aMapView viewForAnnotation:(id )annotation { if([annotation isKindOfClass:[MKUserLocation class]]) return nil; NSString *annID = @"SPWifiAnnotation"; MKAnnotationView *av = [aMapView dequeueReusableAnnotationViewWithIdentifier:annID]; if(av == nil) { av = [[[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:annID] autorelease]; av.canShowCallout = YES; } return av; } /* // Override to allow orientations other than the default portrait orientation. - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation { // Return YES for supported orientations return (interfaceOrientation == UIInterfaceOrientationPortrait); } */ - (void)mapView:(MKMapView *)aMapView didAddAnnotationViews:(NSArray *)views { [aMapView selectAnnotation:[aMapView.annotations lastObject] animated:YES]; } - (void)dealloc { [annotations release]; [super dealloc]; } @end ================================================ FILE: Classes/SPWifiMapVC.xib ================================================ 1024 10F569 804 1038.29 461.00 com.apple.InterfaceBuilder.IBCocoaTouchPlugin 123 YES YES com.apple.InterfaceBuilder.IBCocoaTouchPlugin YES YES YES YES IBFilesOwner IBCocoaTouchFramework IBFirstResponder IBCocoaTouchFramework 274 YES 274 {320, 460} YES YES IBCocoaTouchFramework {320, 460} 3 MQA IBCocoaTouchFramework YES view 3 mapView 5 delegate 6 YES 0 -1 File's Owner -2 1 YES 4 YES YES -1.CustomClassName -2.CustomClassName 1.IBEditorWindowLastContentRect 1.IBPluginDependency 4.IBPluginDependency 4.IBViewBoundsToFrameTransform YES SPWifiMapVC UIResponder {{556, 376}, {320, 480}} com.apple.InterfaceBuilder.IBCocoaTouchPlugin com.apple.InterfaceBuilder.IBCocoaTouchPlugin P4AAAL+AAADBMAAAxA2AAA YES YES YES YES 6 YES NSObject IBProjectSource JSON/NSObject+SBJSON.h NSObject IBProjectSource JSON/SBProxyForJson.h SPWifiMapVC UIViewController mapView MKMapView mapView mapView MKMapView IBProjectSource Classes/SPWifiMapVC.h YES MKMapView UIView IBFrameworkSource MapKit.framework/Headers/MKMapView.h NSObject IBFrameworkSource Foundation.framework/Headers/NSError.h NSObject IBFrameworkSource Foundation.framework/Headers/NSFileManager.h NSObject IBFrameworkSource Foundation.framework/Headers/NSKeyValueCoding.h NSObject IBFrameworkSource Foundation.framework/Headers/NSKeyValueObserving.h NSObject IBFrameworkSource Foundation.framework/Headers/NSKeyedArchiver.h NSObject IBFrameworkSource Foundation.framework/Headers/NSObject.h NSObject IBFrameworkSource Foundation.framework/Headers/NSRunLoop.h NSObject IBFrameworkSource Foundation.framework/Headers/NSThread.h NSObject IBFrameworkSource Foundation.framework/Headers/NSURL.h NSObject IBFrameworkSource Foundation.framework/Headers/NSURLConnection.h NSObject IBFrameworkSource UIKit.framework/Headers/UIAccessibility.h NSObject IBFrameworkSource UIKit.framework/Headers/UINibLoading.h NSObject IBFrameworkSource UIKit.framework/Headers/UIResponder.h UIResponder NSObject UISearchBar UIView IBFrameworkSource UIKit.framework/Headers/UISearchBar.h UISearchDisplayController NSObject IBFrameworkSource UIKit.framework/Headers/UISearchDisplayController.h UIView IBFrameworkSource UIKit.framework/Headers/UITextField.h UIView UIResponder IBFrameworkSource UIKit.framework/Headers/UIView.h UIViewController IBFrameworkSource MediaPlayer.framework/Headers/MPMoviePlayerViewController.h UIViewController IBFrameworkSource UIKit.framework/Headers/UINavigationController.h UIViewController IBFrameworkSource UIKit.framework/Headers/UIPopoverController.h UIViewController IBFrameworkSource UIKit.framework/Headers/UISplitViewController.h UIViewController IBFrameworkSource UIKit.framework/Headers/UITabBarController.h UIViewController UIResponder IBFrameworkSource UIKit.framework/Headers/UIViewController.h 0 IBCocoaTouchFramework com.apple.InterfaceBuilder.CocoaTouchPlugin.iPhoneOS com.apple.InterfaceBuilder.CocoaTouchPlugin.InterfaceBuilder3 YES ../SpyPhone.xcodeproj 3 123 ================================================ FILE: Classes/SpyPhoneAppDelegate.h ================================================ // // SpyPhoneAppDelegate.h // SpyPhone // // Created by Nicolas Seriot on 11/15/09. // Copyright IICT 2009. // Licensed under GPL 2.0 http://www.gnu.org/licenses/gpl-2.0.txt // #import @interface SpyPhoneAppDelegate : NSObject { UIWindow *window; UITabBarController *tabBarController; } @property (nonatomic, retain) IBOutlet UIWindow *window; @property (nonatomic, retain) IBOutlet UITabBarController *tabBarController; @end ================================================ FILE: Classes/SpyPhoneAppDelegate.m ================================================ // // SpyPhoneAppDelegate.m // SpyPhone // // Created by Nicolas Seriot on 11/15/09. // Copyright IICT 2009. // Licensed under GPL 2.0 http://www.gnu.org/licenses/gpl-2.0.txt // #import "SpyPhoneAppDelegate.h" #import "TVOutManager.h" @implementation SpyPhoneAppDelegate @synthesize window; @synthesize tabBarController; - (void)dealloc { [tabBarController release]; [window release]; [super dealloc]; } - (void)applicationDidFinishLaunching:(UIApplication *)application { // Add the tab bar controller's current view as a subview of the window [window addSubview:tabBarController.view]; /* // FIXME: TVOut does not work so well 1. start SP, suspend 2. plug 3. start SP, suspend 4. start SP, SP quits 5. start SP */ BOOL isTVOutEnabled = [[NSUserDefaults standardUserDefaults] boolForKey:@"TVOutEnabled"]; if(isTVOutEnabled) { [TVOutManager sharedInstance].tvSafeMode = NO; [[TVOutManager sharedInstance] startTVOut]; } } @end ================================================ FILE: Classes/TVOutManager.h ================================================ // // TVOutManager.h // TVOutOS4Test // // Created by Rob Terrell (rob@touchcentric.com) on 8/16/10. // Copyright 2010 TouchCentric LLC. All rights reserved. // #import @interface TVOutManager : NSObject { UIWindow* deviceWindow; UIWindow* tvoutWindow; NSTimer *updateTimer; UIImage *image; UIImageView *mirrorView; BOOL done; BOOL tvSafeMode; CGAffineTransform startingTransform; } @property(assign) BOOL tvSafeMode; + (TVOutManager *)sharedInstance; - (void) startTVOut; - (void) stopTVOut; - (void) updateTVOut; - (void) updateLoop; - (void) screenDidConnectNotification: (NSNotification*) notification; - (void) screenDidDisconnectNotification: (NSNotification*) notification; - (void) screenModeDidChangeNotification: (NSNotification*) notification; - (void) deviceOrientationDidChange: (NSNotification*) notification; @end ================================================ FILE: Classes/TVOutManager.m ================================================ // // TVOutManager.m // TVOutOS4Test // // Created by Rob Terrell (rob@touchcentric.com) on 8/16/10. // Copyright 2010 TouchCentric LLC. All rights reserved. // // http://www.touchcentric.com/blog/ // marco modifications // device orientation // display link sugegstion from github // CALayer // USE_UIGETSCREENIMAGE is defined // kFPS set to 30 // commented NSLog #import #import "TVOutManager.h" #define USE_LAYER #define kFPS 30 #define MethodBackgroundThread 0 #define MethodDisplayLink 1 #define MethodTimer 2 #define SynchronizationMethod MethodDisplayLink // // Warning: once again, we can't use UIGetScreenImage for shipping apps (as of late July 2010) // however, it gives a better result (shows the status bar, UIKit transitions, better fps) so // you may want to use it for non-app-store builds (i.e. private demo, trade show build, etc.) // Just uncomment both lines below. // #define USE_UIGETSCREENIMAGE CGImageRef UIGetScreenImage(); // @implementation TVOutManager @synthesize tvSafeMode; + (TVOutManager *)sharedInstance { static TVOutManager *sharedInstance; @synchronized(self) { if (!sharedInstance) sharedInstance = [[TVOutManager alloc] init]; return sharedInstance; } } - (id) init { self = [super init]; // catch screen-related notifications [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(screenDidConnectNotification:) name: UIScreenDidConnectNotification object: nil]; [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(screenDidDisconnectNotification:) name: UIScreenDidDisconnectNotification object: nil]; [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(screenModeDidChangeNotification:) name: UIScreenModeDidChangeNotification object: nil]; // catch orientation notifications [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications]; [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(deviceOrientationDidChange:) name: UIDeviceOrientationDidChangeNotification object: nil]; return self; } -(void) dealloc { [[NSNotificationCenter defaultCenter] removeObserver: self]; [super dealloc]; } -(void) setTvSafeMode:(BOOL) val { if (tvoutWindow) { if (tvSafeMode == YES && val == NO) { [UIView beginAnimations:@"zoomIn" context: nil]; tvoutWindow.transform = CGAffineTransformScale(tvoutWindow.transform, 1.25, 1.25); [UIView commitAnimations]; [tvoutWindow setNeedsDisplay]; } else if (tvSafeMode == NO && val == YES) { [UIView beginAnimations:@"zoomOut" context: nil]; tvoutWindow.transform = CGAffineTransformScale(tvoutWindow.transform, .8, .8); [UIView commitAnimations]; [tvoutWindow setNeedsDisplay]; } } tvSafeMode = val; } - (void) startTVOut { // you need to have a main window already open when you call start if ([[UIApplication sharedApplication] keyWindow] == nil) return; NSArray* screens = [UIScreen screens]; if ([screens count] <= 1) { // NSLog(@"TVOutManager: startTVOut failed (no external screens detected)"); return; } if (tvoutWindow) { // tvoutWindow already exists, so this is a re-connected cable, or a mode chane [tvoutWindow release], tvoutWindow = nil; } if (!tvoutWindow) { deviceWindow = [[UIApplication sharedApplication] keyWindow]; CGSize max; max.width = 0; max.height = 0; UIScreenMode *maxScreenMode = nil; UIScreen *external = [[UIScreen screens] objectAtIndex: 1]; for(int i = 0; i < [[external availableModes] count]; i++) { UIScreenMode *current = [[[[UIScreen screens] objectAtIndex:1] availableModes] objectAtIndex: i]; if (current.size.width > max.width) { max = current.size; maxScreenMode = current; } } external.currentMode = maxScreenMode; tvoutWindow = [[UIWindow alloc] initWithFrame: CGRectMake(0,0, max.width, max.height)]; tvoutWindow.userInteractionEnabled = NO; tvoutWindow.screen = external; // size the mirrorView to expand to fit the external screen CGRect mirrorRect = [[UIScreen mainScreen] bounds]; CGFloat horiz = max.width / CGRectGetWidth(mirrorRect); CGFloat vert = max.height / CGRectGetHeight(mirrorRect); CGFloat bigScale = horiz < vert ? horiz : vert; mirrorRect = CGRectMake(mirrorRect.origin.x, mirrorRect.origin.y, mirrorRect.size.width * bigScale, mirrorRect.size.height * bigScale); mirrorView = [[UIImageView alloc] initWithFrame: mirrorRect]; mirrorView.center = tvoutWindow.center; // TV safe area -- scale the window by 20% -- for composite / component, not needed for VGA output if (tvSafeMode) tvoutWindow.transform = CGAffineTransformScale(tvoutWindow.transform, .8, .8); [tvoutWindow addSubview: mirrorView]; [mirrorView release]; [tvoutWindow makeKeyAndVisible]; tvoutWindow.hidden = NO; tvoutWindow.backgroundColor = [UIColor darkGrayColor]; // orient the view properly if ([UIDevice currentDevice].orientation == UIDeviceOrientationLandscapeLeft) { mirrorView.transform = CGAffineTransformRotate(CGAffineTransformIdentity, M_PI * 1.5); } else if ([UIDevice currentDevice].orientation == UIDeviceOrientationLandscapeRight) { mirrorView.transform = CGAffineTransformRotate(CGAffineTransformIdentity, M_PI * -1.5); } startingTransform = mirrorView.transform; [deviceWindow makeKeyAndVisible]; [self updateTVOut]; if (SynchronizationMethod == MethodBackgroundThread) { [NSThread detachNewThreadSelector:@selector(updateLoop) toTarget:self withObject:nil]; } else if (SynchronizationMethod == MethodDisplayLink) { [self performSelectorInBackground:@selector(startDisplayLink) withObject:nil]; } else { updateTimer = [NSTimer scheduledTimerWithTimeInterval: (1.0/kFPS) target: self selector: @selector(updateTVOut) userInfo: nil repeats: YES]; [updateTimer retain]; } } } - (void) stopTVOut; { done = YES; if (updateTimer) { [updateTimer invalidate]; [updateTimer release], updateTimer = nil; } if (tvoutWindow) { [tvoutWindow release], tvoutWindow = nil; mirrorView = nil; } } - (void) updateTVOutForDisplayLink { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; [self updateTVOut]; [pool release]; } - (void) startDisplayLink { done = NO; NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; CADisplayLink *displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(updateTVOutForDisplayLink)]; [displayLink setFrameInterval:(60 / kFPS)]; [displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes]; while (!done) { [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1.0f]]; } [displayLink invalidate]; [pool release]; } - (void) updateTVOut; { #ifdef USE_UIGETSCREENIMAGE // UIGetScreenImage() is no longer allowed in shipping apps, see https://devforums.apple.com/thread/61338 // however, it's better for demos, since it includes the status bar and captures animated transitions CGImageRef cgScreen = UIGetScreenImage(); #ifdef USE_LAYER mirrorView.layer.contents = (id)cgScreen; #else if (cgScreen) image = [UIImage imageWithCGImage:cgScreen]; mirrorView.image = image; #endif CGImageRelease(cgScreen); #else // from http://developer.apple.com/iphone/library/qa/qa2010/qa1703.html // bonus, this works in the simulator; sadly, it doesn't capture the status bar // // if you are making an OpenGL app, use UIGetScreenImage() above or switch the // following code to match Apple's sample at http://developer.apple.com/iphone/library/qa/qa2010/qa1704.html // note that you'll need to pass in a reference to your eaglview to get that to work. UIGraphicsBeginImageContext(deviceWindow.bounds.size); CGContextRef context = UIGraphicsGetCurrentContext(); // get every window's contents (i.e. so you can see alerts, ads, etc.) for (UIWindow *window in [[UIApplication sharedApplication] windows]) { if (![window respondsToSelector:@selector(screen)] || [window screen] == [UIScreen mainScreen]) { CGContextSaveGState(context); CGContextTranslateCTM(context, [window center].x, [window center].y); CGContextConcatCTM(context, [window transform]); CGContextTranslateCTM(context, -[window bounds].size.width * window.layer.anchorPoint.x, -[window bounds].size.height * window.layer.anchorPoint.y); [[window layer] renderInContext:context]; CGContextRestoreGState(context); } } image = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); mirrorView.image = image; #endif } - (void)updateLoop; { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; done = NO; while ( ! done ) { [self performSelectorOnMainThread:@selector(updateTVOut) withObject:nil waitUntilDone:NO]; [NSThread sleepForTimeInterval: (1.0/kFPS) ]; } [pool release]; } -(void) screenDidConnectNotification: (NSNotification*) notification { //NSLog(@"Screen connected: %@", [notification object]); [self startTVOut]; } -(void) screenDidDisconnectNotification: (NSNotification*) notification { //NSLog(@"Screen disconnected: %@", [notification object]); [self stopTVOut]; } -(void) screenModeDidChangeNotification: (NSNotification*) notification { //NSLog(@"Screen mode changed: %@", [notification object]); [self startTVOut]; } -(void) deviceOrientationDidChange: (NSNotification*) notification { if (mirrorView == nil || done == YES) return; if ([UIDevice currentDevice].orientation == UIDeviceOrientationLandscapeLeft) { [UIView beginAnimations:@"turnLeft" context:nil]; mirrorView.transform = CGAffineTransformRotate(CGAffineTransformIdentity, M_PI * 1.5); [UIView commitAnimations]; } else if ([UIDevice currentDevice].orientation == UIDeviceOrientationLandscapeRight) { [UIView beginAnimations:@"turnRight" context:nil]; mirrorView.transform = CGAffineTransformRotate(CGAffineTransformIdentity, M_PI * -1.5); [UIView commitAnimations]; } else if (UIDeviceOrientationIsPortrait ([[UIDevice currentDevice] orientation])) { [UIView beginAnimations:@"turnUp" context:nil]; mirrorView.transform = CGAffineTransformIdentity; [UIView commitAnimations]; } } @end ================================================ FILE: Classes/TVOutManager_.m ================================================ // // TVOutManager.m // TVOutOS4Test // // Created by Rob Terrell (rob@touchcentric.com) on 8/16/10. // Copyright 2010 TouchCentric LLC. All rights reserved. // // http://www.touchcentric.com/blog/ #import #import "TVOutManager.h" #define kFPS 15 #define kUseBackgroundThread NO // // Warning: once again, we can't use UIGetScreenImage for shipping apps (as of late July 2010) // however, it gives a better result (shows the status bar, UIKit transitions, better fps) so // you may want to use it for non-app-store builds (i.e. private demo, trade show build, etc.) // Just uncomment both lines below. // #define USE_UIGETSCREENIMAGE CGImageRef UIGetScreenImage(); // @implementation TVOutManager @synthesize tvSafeMode; + (TVOutManager *)sharedInstance { static TVOutManager *sharedInstance; @synchronized(self) { if (!sharedInstance) sharedInstance = [[TVOutManager alloc] init]; return sharedInstance; } } - (id) init { self = [super init]; if (self) { // can't imagine why, but just in case [[NSNotificationCenter defaultCenter] removeObserver: self]; // catch screen-related notifications [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(screenDidConnectNotification:) name: UIScreenDidConnectNotification object: nil]; [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(screenDidDisconnectNotification:) name: UIScreenDidDisconnectNotification object: nil]; [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(screenModeDidChangeNotification:) name: UIScreenModeDidChangeNotification object: nil]; // catch orientation notifications [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications]; [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(deviceOrientationDidChange:) name: UIDeviceOrientationDidChangeNotification object: nil]; } return self; } -(void) dealloc { [[NSNotificationCenter defaultCenter] removeObserver: self]; [super dealloc]; } -(void) setTvSafeMode:(BOOL) val { if (tvoutWindow) { if (tvSafeMode == YES && val == NO) { [UIView beginAnimations:@"zoomIn" context: nil]; tvoutWindow.transform = CGAffineTransformScale(tvoutWindow.transform, 1.25, 1.25); [UIView commitAnimations]; [tvoutWindow setNeedsDisplay]; } else if (tvSafeMode == NO && val == YES) { [UIView beginAnimations:@"zoomOut" context: nil]; tvoutWindow.transform = CGAffineTransformScale(tvoutWindow.transform, .8, .8); [UIView commitAnimations]; [tvoutWindow setNeedsDisplay]; } } tvSafeMode = val; } - (void) startTVOut { // you need to have a main window already open when you call start if ([[UIApplication sharedApplication] keyWindow] == nil) return; NSArray* screens = [UIScreen screens]; if ([screens count] <= 1) { NSLog(@"TVOutManager: startTVOut failed (no external screens detected)"); return; } if (tvoutWindow) { // tvoutWindow already exists, so this is a re-connected cable, or a mode chane [tvoutWindow release], tvoutWindow = nil; } if (!tvoutWindow) { deviceWindow = [[UIApplication sharedApplication] keyWindow]; CGSize max; max.width = 0; max.height = 0; UIScreenMode *maxScreenMode = nil; UIScreen *external = [[UIScreen screens] objectAtIndex: 1]; for(int i = 0; i < [[external availableModes] count]; i++) { UIScreenMode *current = [[[[UIScreen screens] objectAtIndex:1] availableModes] objectAtIndex: i]; if (current.size.width > max.width) { max = current.size; maxScreenMode = current; } } external.currentMode = maxScreenMode; tvoutWindow = [[UIWindow alloc] initWithFrame: CGRectMake(0,0, max.width, max.height)]; tvoutWindow.userInteractionEnabled = NO; tvoutWindow.screen = external; // size the mirrorView to expand to fit the external screen CGRect mirrorRect = [[UIScreen mainScreen] bounds]; CGFloat horiz = max.width / CGRectGetWidth(mirrorRect); CGFloat vert = max.height / CGRectGetHeight(mirrorRect); CGFloat bigScale = horiz < vert ? horiz : vert; mirrorRect = CGRectMake(mirrorRect.origin.x, mirrorRect.origin.y, mirrorRect.size.width * bigScale, mirrorRect.size.height * bigScale); mirrorView = [[UIImageView alloc] initWithFrame: mirrorRect]; mirrorView.center = tvoutWindow.center; // TV safe area -- scale the window by 20% -- for composite / component, not needed for VGA output if (tvSafeMode) tvoutWindow.transform = CGAffineTransformScale(tvoutWindow.transform, .8, .8); [tvoutWindow addSubview: mirrorView]; [mirrorView release]; [tvoutWindow makeKeyAndVisible]; tvoutWindow.hidden = NO; tvoutWindow.backgroundColor = [UIColor darkGrayColor]; // orient the view properly if ([UIDevice currentDevice].orientation == UIDeviceOrientationLandscapeLeft) { mirrorView.transform = CGAffineTransformRotate(CGAffineTransformIdentity, M_PI * 1.5); } else if ([UIDevice currentDevice].orientation == UIDeviceOrientationLandscapeRight) { mirrorView.transform = CGAffineTransformRotate(CGAffineTransformIdentity, M_PI * -1.5); } startingTransform = mirrorView.transform; [deviceWindow makeKeyAndVisible]; [self updateTVOut]; if (kUseBackgroundThread) [NSThread detachNewThreadSelector:@selector(updateLoop) toTarget:self withObject:nil]; else { updateTimer = [NSTimer scheduledTimerWithTimeInterval: (1.0/kFPS) target: self selector: @selector(updateTVOut) userInfo: nil repeats: YES]; [updateTimer retain]; } } } - (void) stopTVOut; { done = YES; if (updateTimer) { [updateTimer invalidate]; [updateTimer release], updateTimer = nil; } if (tvoutWindow) { [tvoutWindow release], tvoutWindow = nil; mirrorView = nil; } } - (void) updateTVOut; { #ifdef USE_UIGETSCREENIMAGE // UIGetScreenImage() is no longer allowed in shipping apps, see https://devforums.apple.com/thread/61338 // however, it's better for demos, since it includes the status bar and captures animated transitions CGImageRef cgScreen = UIGetScreenImage(); if (cgScreen) image = [UIImage imageWithCGImage:cgScreen]; mirrorView.image = image; CGImageRelease(cgScreen); #else // from http://developer.apple.com/iphone/library/qa/qa2010/qa1703.html // bonus, this works in the simulator; sadly, it doesn't capture the status bar // // if you are making an OpenGL app, use UIGetScreenImage() above or switch the // following code to match Apple's sample at http://developer.apple.com/iphone/library/qa/qa2010/qa1704.html // note that you'll need to pass in a reference to your eaglview to get that to work. UIGraphicsBeginImageContext(deviceWindow.bounds.size); CGContextRef context = UIGraphicsGetCurrentContext(); // get every window's contents (i.e. so you can see alerts, ads, etc.) for (UIWindow *window in [[UIApplication sharedApplication] windows]) { if (![window respondsToSelector:@selector(screen)] || [window screen] == [UIScreen mainScreen]) { CGContextSaveGState(context); CGContextTranslateCTM(context, [window center].x, [window center].y); CGContextConcatCTM(context, [window transform]); CGContextTranslateCTM(context, -[window bounds].size.width * window.layer.anchorPoint.x, -[window bounds].size.height * window.layer.anchorPoint.y); [[window layer] renderInContext:context]; CGContextRestoreGState(context); } } image = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); mirrorView.image = image; #endif } - (void)updateLoop; { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; done = NO; while ( ! done ) { [self performSelectorOnMainThread:@selector(updateTVOut) withObject:nil waitUntilDone:NO]; [NSThread sleepForTimeInterval: (1.0/kFPS) ]; } [pool release]; } -(void) screenDidConnectNotification: (NSNotification*) notification { NSLog(@"Screen connected: %@", [notification object]); [self startTVOut]; } -(void) screenDidDisconnectNotification: (NSNotification*) notification { NSLog(@"Screen disconnected: %@", [notification object]); [self stopTVOut]; } -(void) screenModeDidChangeNotification: (NSNotification*) notification { NSLog(@"Screen mode changed: %@", [notification object]); [self startTVOut]; } -(void) deviceOrientationDidChange: (NSNotification*) notification { if (mirrorView == nil || done == YES) return; if ([UIDevice currentDevice].orientation == UIDeviceOrientationLandscapeLeft) { [UIView beginAnimations:@"turnLeft" context:nil]; mirrorView.transform = CGAffineTransformRotate(CGAffineTransformIdentity, M_PI * 1.5); [UIView commitAnimations]; } else if ([UIDevice currentDevice].orientation == UIDeviceOrientationLandscapeRight) { [UIView beginAnimations:@"turnRight" context:nil]; mirrorView.transform = CGAffineTransformRotate(CGAffineTransformIdentity, M_PI * -1.5); [UIView commitAnimations]; } else { [UIView beginAnimations:@"turnUp" context:nil]; mirrorView.transform = CGAffineTransformIdentity; [UIView commitAnimations]; } } @end ================================================ FILE: Classes/UIImage+GPS.h ================================================ // // UIImage+GPS.h // SpyPhone // // Created by Nicolas Seriot on 11/21/09. // Copyright 2009. // Licensed under GPL 2.0 http://www.gnu.org/licenses/gpl-2.0.txt // #import #import @interface UIImage (GPS) +(CLLocationCoordinate2D)coordinatesOfImageAtPath:(NSString *)path; @end ================================================ FILE: Classes/UIImage+GPS.m ================================================ // // UIImage+GPS.m // SpyPhone // // Created by Nicolas Seriot on 11/21/09. // Copyright 2009. // Licensed under GPL 2.0 http://www.gnu.org/licenses/gpl-2.0.txt // #import "UIImage+GPS.h" #import "EXF.h" @implementation UIImage (GPS) // adapted from http://davidjhinson.wordpress.com/2009/06/05/you-can-have-it-in-any-color-as-long-as-its-black/ +(CLLocationCoordinate2D)coordinatesOfImageAtPath:(NSString *)path { CLLocationCoordinate2D coord = {0.0, 0.0}; NSData *data =[NSData dataWithContentsOfFile:path]; if(!data) return coord; EXFJpeg* jpegScanner = [[EXFJpeg alloc] init]; [jpegScanner scanImageData:data]; EXFGPSLoc *lat = [jpegScanner.exifMetaData tagValue:[NSNumber numberWithInt:EXIF_GPSLatitude]]; NSString *latRef = [jpegScanner.exifMetaData tagValue:[NSNumber numberWithInt:EXIF_GPSLatitudeRef]]; EXFGPSLoc *lon = [jpegScanner.exifMetaData tagValue:[NSNumber numberWithInt:EXIF_GPSLongitude]]; NSString *lonRef = [jpegScanner.exifMetaData tagValue:[NSNumber numberWithInt:EXIF_GPSLongitudeRef]]; [jpegScanner release]; if([latRef length] == 0 || [lonRef length] == 0) return coord; coord.latitude = [[NSString stringWithFormat:@"%f", lat.degrees.numerator + ((float)lat.minutes.numerator / (float)lat.minutes.denominator) / 60.0] floatValue]; coord.longitude = [[NSString stringWithFormat:@"%f", lon.degrees.numerator + ((float)lon.minutes.numerator / (float)lon.minutes.denominator) / 60.0] floatValue]; if([[latRef substringToIndex:1] isEqualToString:@"S"]) coord.latitude = -coord.latitude; if([[lonRef substringToIndex:1] isEqualToString:@"W"]) coord.longitude = -coord.longitude; return coord; } @end ================================================ FILE: EXIF/EXF.h ================================================ /* * EXF.h * * * Created by steve woodcock on 23/03/2008. * Copyright 2008. // Licensed under GPL 2.0 http://www.gnu.org/licenses/gpl-2.0.txt * */ /*! @header EXF.h @abstract The group of the entire EXIF API headers @discussion These are: 1) EXFConstants.h 2) EXFMetaData.h 3) EXFJFIF.h 4) EXFJpeg.h 5) EXFGPS.h 6) EXFHandlers.h To use the framework just use the follwoign import statement #import "EXF.h" */ /* Details the Constants for the tag Ids, enums etc */ #import "EXFConstants.h" /* Details the EXFObject which represents the meta information of the JPEG image. */ #import "EXFMetaData.h" /* The JFIF is an alternative meta format used to encode information. */ #import "EXFJFIF.h" /* The entry point which is used to scan an existing file and reconstruct a new image */ #import "EXFJpeg.h" /* A GPS Location object */ #import "EXFGPS.h" /* The tag specific handlers which represent special processing required for some tags. */ #import "EXFHandlers.h" ================================================ FILE: EXIF/EXFConstants.h ================================================ /* * EXFConstants.h * * * Created by steve woodcock on 30/03/2008. * Copyright 2008. // Licensed under GPL 2.0 http://www.gnu.org/licenses/gpl-2.0.txt * * Constants used in the EXIF library. * * */ /*! @header EXFConstants.h @abstract EXFConstants.h provides the definition of commonly used enums, definitions and basic interfaces. */ /* Type defs for some of the internal definitions */ /*! @typedef ByteArray */ typedef UInt8 ByteArray; /*! @typedef EXFTagId @discussion The data type for tag ids. */ typedef UInt16 EXFTagId; /*! @enum EXFDataType @abstract The possible types that an EXIF tag data can be specified as. @discussion These are the only legal types to be used in the EXFTagDefinition to determine the type of data to be read/written and the number of bytes that each data type will then occupy. */ enum EXFDataType { FMT_BYTE = 1, FMT_STRING = 2, FMT_USHORT = 3, FMT_ULONG = 4, FMT_URATIONAL = 5, FMT_SBYTE = 6, FMT_UNDEFINED = 7, FMT_SSHORT = 8, FMT_SLONG = 9, FMT_SRATIONAL =10, FMT_SINGLE =11, FMT_DOUBLE =12 }; /*! @typedef EXFDataType */ typedef enum EXFDataType EXFDataType; /* EXF Tag Ids */ #define EXIF_ImageWidth 0x0100 #define EXIF_ImageLength 0x0101 #define EXIF_BitsPerSample 0x0102 #define EXIF_Compression 0x0103 #define EXIF_PhotometricInterpretation 0x0106 #define EXIF_ImageDescription 0x010e #define EXIF_Make 0x010f #define EXIF_Model 0x0110 #define EXIF_StripOffsets 0x0111 #define EXIF_Orientation 0x0112 #define EXIF_SamplesPerPixel 0x0115 #define EXIF_RowsPerStrip 0x0116 #define EXIF_StripByteCounts 0x0117 #define EXIF_XResolution 0x011a #define EXIF_YResolution 0x011b #define EXIF_PlanarConfiguration 0x011c #define EXIF_ResolutionUnit 0x0128 #define EXIF_Software 0x0131 #define EXIF_DateTime 0x0132 #define EXIF_Artist 0x013b #define EXIF_HostComputer 0x013c #define EXIF_Predictor 0x013d #define EXIF_WhitePoint 0x013e #define EXIF_PrimaryChromaticities 0x013f #define EXIF_JPEGInterchangeFormat 0x0201 #define EXIF_JPEGInterchangeFormatLength 0x0202 #define EXIF_YCbCrCoefficients 0x0211 #define EXIF_YCbCrSubSampling 0x0212 #define EXIF_YCbCrPositioning 0x0213 #define EXIF_ReferenceBlackWhite 0x0214 #define EXIF_Copyright 0x8298 #define EXIF_Exif 0x8769 #define EXIF_GPS 0x8825 #define EXIF_SpectralSensitivity 0x8824 #define EXIF_ExposureProgram 0x8822 #define EXIF_ISOSpeedratings 0x8827 #define EXIF_ExposureTime 0x829a #define EXIF_FNumber 0x829d #define EXIF_ExifVersion 0x9000 #define EXIF_DateTimeOriginal 0x9003 #define EXIF_DateTimeDigitized 0x9004 #define EXIF_ComponentsConfiguration 0x9101 #define EXIF_CompressedBitsPerPixel 0x9102 #define EXIF_ShutterSpeedValue 0x9201 #define EXIF_ApertureValue 0x9202 #define EXIF_BrightnessValue 0x9203 #define EXIF_ExposureBiasValue 0x9204 #define EXIF_MaxApertureRatioValue 0x9205 #define EXIF_SubjectDistance 0x9206 #define EXIF_MeteringMode 0x9207 #define EXIF_LightSource 0x9208 #define EXIF_Flash 0x9209 #define EXIF_FocalLength 0x920a #define EXIF_MakerNote 0x927c #define EXIF_UserComment 0x9286 #define EXIF_SubSecTime 0x9290 #define EXIF_SubSecTimeOriginal 0x9291 #define EXIF_SubSecTimeDigitized 0x9292 #define EXIF_FileSource 0xa300 #define EXIF_SceneType 0xa301 #define EXIF_CFAPattern 0xa302 #define EXIF_FlashpixVersion 0xa000 #define EXIF_ColorSpace 0xa001 #define EXIF_PixelXDimension 0xa002 #define EXIF_PixelYDimension 0xa003 #define EXIF_FocalPlaneXResolution 0xa20e #define EXIF_FocalPlaneYResolution 0xa20f #define EXIF_FocalPlaneResolutionUnit 0xa210 #define EXIF_SubjectLocation 0xa214 #define EXIF_ExposureIndex 0xa215 #define EXIF_SensingMethod 0xa217 #define EXIF_CustomRendered 0xa401 #define EXIF_ExposureMode 0xa402 #define EXIF_WhiteBalance 0xa403 #define EXIF_DigitalZoomRatio 0xa404 #define EXIF_FocalLengthIn35mmFilm 0xa405 #define EXIF_SceneCaptureType 0xa406 #define EXIF_GainControl 0xa407 #define EXIF_Contrast 0xa408 #define EXIF_Saturation 0xa409 #define EXIF_Sharpness 0xa40a #define EXIF_DeviceSettingDescription 0xa40b #define EXIF_SubjectDistanceRange 0xa40c #define EXIF_Gamma 0xa500 #define EXIF_GPSVersion 0x0000 #define EXIF_GPSLatitudeRef 0x0001 #define EXIF_GPSLatitude 0x0002 #define EXIF_GPSLongitudeRef 0x0003 #define EXIF_GPSLongitude 0x0004 #define EXIF_GPSAltitudeRef 0x0005 #define EXIF_GPSAltitude 0x0006 #define EXIF_GPSTimeStamp 0x0007 #define EXIF_GPSSatellites 0x0008 #define EXIF_GPSStatus 0x0009 #define EXIF_GPSMeasureMode 0x000a #define EXIF_GPSDOP 0x000b #define EXIF_GPSSpeedRef 0x000c #define EXIF_GPSSpeed 0x000d #define EXIF_GPSTrackRef 0x000e #define EXIF_GPSTrack 0x000f #define EXIF_GPSImgDirectionRef 0x0010 #define EXIF_GPSImgDirection 0x0011 #define EXIF_GPSMapDatum 0x0012 #define EXIF_GPSDestLatitudeRef 0x0013 #define EXIF_GPSDestLatitude 0x0014 #define EXIF_GPSDestLongitudeRef 0x0015 #define EXIF_GPSDestLongitude 0x0016 #define EXIF_GPSDestBearingRef 0x0017 #define EXIF_GPSDestBearing 0x0018 #define EXIF_GPSDestDistanceRef 0x0019 #define EXIF_GPSDestDistance 0x001a /*! @class EXFraction @abstract A simple fraction class used to store all rational data types @discussion The fraction class is used to avoid precision issues when converting from the fraction format stored in the JPEG image. The image data is stored as two longs (numerator and denominator) */ @interface EXFraction: NSObject { long numerator; long denominator; } /*! @method initWith @abstract initialises the EXFraction with a numerator and denominator */ -(id) initWith: (long) numerator: (long) denominator; /*! @property numerator @abstract the numerator part of the fraction */ @property (readonly) long numerator; /*! @property denominator @abstract the denominator part of the fraction */ @property (readonly) long denominator; /*! @method description @abstract Returns a String representing the double format of the fraction. @discussion If a true representation of the Fraction is required use the accessor methods to retrieve the two longs and construct the required format. */ -(NSString*) description; @end /*! @class EXFTag @abstract Definition data of an EXF Tag @discussion The EXFTag consists of an tagId, dataType, shortName, parentTagId, whether it is user editable and the number of components that each tag consists of. The dataType can only be one of the valid EXFDataType enum values. The shortName is as specified in the EXF specification. If localised or more user readable names are required you should use these as the key values to the localised form. The parentTagId shows the hierarchical parent Tag of each EXFTag. This is required in order to work out which directory or subdirectory a tag value should be inserted into. Editable tags are those that can be altered or add by users of the library. Attempting to alter a non-writable tag will result in an exception. Components defines the number of instances of each data type. As each data type is a certain number of bytes the actual byte size occupied for each tag is components * dataType size. For more detail see the EXF specification http://www.exif.org/Exif2-2.PDF */ @interface EXFTag : NSObject { EXFTagId tagId; EXFDataType dataType; int parentTagId; NSString* name; BOOL editable; int components; } /*! @method initWith */ -(id) initWith: (EXFTagId) aTagId: (EXFDataType)aDataType: (NSString*) aName: (int) parentTagId: (BOOL)editable: (int) components; @property (readonly) EXFTagId tagId; @property (readonly) EXFDataType dataType; @property (readonly, retain) NSString* name; @property (readonly) int parentTagId; @property (readonly) BOOL editable; @property (readonly) int components; @end ================================================ FILE: EXIF/EXFGPS.h ================================================ /*! @header EXFGPS Structures Created by steve woodcock on 30/03/2008. @copyright 2008. // Licensed under GPL 2.0 http://www.gnu.org/licenses/gpl-2.0.txt @discussion A set of fractions that represent the 3 rational numbers that make up the GPS Location. these are: Degrees Minutes Seconds The EXF Specification suggests the location should be displayed as 3 rationals. Although we use fractions to actually represent any stored number without getting precision errors. */ #import "EXFConstants.h" /*! @class EXFGPSLoc @abstract A GPS Location @discussion EXFGPSLoc represents a GPS Location. In order to remian aligned to the EXIF format for GPS data, the actual object is structured as 3 EXFraction objects, one each for degrees, minutes and seconds. */ @interface EXFGPSLoc : NSObject { EXFraction* degrees; EXFraction* minutes; EXFraction* seconds; } @property (retain) EXFraction* degrees; @property (retain) EXFraction* minutes; @property (retain) EXFraction* seconds; -(double) descriptionAsDecimal; @end /*! @class EXFGPSTimeStamp @abstract A GPS Timestamp @discussion EXFGPSTimeStamp represents a GPS Timestamp. In order to remian aligned to the EXIF format for GPS data, the actual object is structured as 3 EXFraction objects, one each for hours, minutes and seconds. */ @interface EXFGPSTimeStamp : NSObject { EXFraction* hours; EXFraction* minutes; EXFraction* seconds; } @property (retain) EXFraction* hours; @property (retain) EXFraction* minutes; @property (retain) EXFraction* seconds; @end ================================================ FILE: EXIF/EXFGPS.m ================================================ /* * EXFGPSLoc.m * * * Created by steve woodcock on 30/03/2008. * Copyright 2008. // Licensed under GPL 2.0 http://www.gnu.org/licenses/gpl-2.0.txt * * A set of fractions that represent the 3 rational numbers that make up the * GPS Location. these are: * Degrees * Minutes * Seconds * * The EXF Specification suggests the location should be displayed as 3 rationals. ALthough we use fractions * to actually represent any stored number without getting precision errors. */ #import "EXFGPS.h" @implementation EXFGPSLoc @synthesize degrees; @synthesize minutes; @synthesize seconds; -(NSString*) description{ return [NSString stringWithFormat:@"%@\xC2\xB0 %@' %@\"",degrees, minutes,seconds]; } -(double) descriptionAsDecimal{ return ((double)degrees.numerator/degrees.denominator) +(((double)minutes.numerator/ minutes.denominator)/60) + (((double)seconds.numerator /seconds.denominator)/3600) ; } -(void) dealloc{ self.degrees =nil; self.minutes=nil; self.seconds =nil; [super dealloc]; } @end @implementation EXFGPSTimeStamp @synthesize hours; @synthesize minutes; @synthesize seconds; -(NSString*) description{ NSString* hoursStr = [NSString stringWithFormat:@"%i", (int)hours.numerator/hours.denominator]; NSString* minutesStr = [NSString stringWithFormat:@"%i", (int)minutes.numerator/minutes.denominator]; NSString* secondsStr = [NSString stringWithFormat:@"%i", (int)seconds.numerator/seconds.denominator]; if ([hoursStr length] ==1){ hoursStr = [NSString stringWithFormat:@"0%@", hoursStr]; } if ([minutesStr length] ==1){ minutesStr = [NSString stringWithFormat:@"0%@", minutesStr]; } if ([secondsStr length] ==1){ secondsStr = [NSString stringWithFormat:@"0%@", secondsStr]; } return [NSString stringWithFormat:@"%@:%@:%@",hoursStr,minutesStr,secondsStr]; } -(void) dealloc{ self.hours =nil; self.minutes=nil; self.seconds =nil; [super dealloc]; } @end ================================================ FILE: EXIF/EXFHandlers.h ================================================ /*! @header EXFTagHandler @copyright 2008. Created by steve woodcock on 30/03/2008. // Licensed under GPL 2.0 http://www.gnu.org/licenses/gpl-2.0.txt @discussion The EXFHandlers are to enable either overriding of default tag handling, or to be able to handle tags that are not recognised by the parser. In order to use this the implementation must support the protocol that at the very least provides: 1) Decoding the tag data from a byte format 2) The size in bytes that the value will occupy 3) Whether it will support the object type provided 4) Encoding of the value into a byte form 5) (Optionally) the tagFomat (see EXFConstants for the tagFormat enumeration) */ /*! @protocol EXFTagHandler @abstract Prtocol defintion for User defined Tag Handlers */ @protocol EXFTagHandler /*! Decoding of the tag from a byte buffer and insertion into the provided dictionary under the key provided */ -(void)decodeTag:(NSMutableDictionary*) keyedValues: (NSNumber*) tagId: (CFDataRef*) tagData: (BOOL) bigEndianOrder; -(int) getSizeOfValue:(id)value; -(BOOL)supportsValueType:(id) value; -(void)encodeTag: (NSMutableData*) targetBuffer: (id) tagData:(BOOL) bigEndianOrder; @optional -(int) tagFormat; -(int) parentTagId; -(BOOL) isEditable; @end /* These are the internal Handlers used to provide specialised handling for certain tags. */ @interface EXFGPSLocationHandler: NSObject @end @interface EXFGPSTimeHandler: NSObject @end @interface EXFTextHandler: NSObject @end @interface EXFASCIIHandler: NSObject @end @interface EXFByteHandler: NSObject @end @interface EXFByteArrayHandler: NSObject @end ================================================ FILE: EXIF/EXFHandlers.m ================================================ // // EXFHandlers.m // iphone-test // // Created by steve woodcock on 30/03/2008. // Copyright 2008 __MyCompanyName__. // Licensed under GPL 2.0 http://www.gnu.org/licenses/gpl-2.0.txt // #import "EXFHandlers.h" #import "EXFUtils.h" #import "EXFGPS.h" #import "EXFConstants.h" // character set start byte for for text tags const UInt8 ASCIIChars[8] = {0x41,0x53,0x43,0x49,0x49,0x00,0x00,0x00}; const UInt8 JISChars[8] = {0x4a,0x49,0x53,0x00,0x00,0x0,0x00,0x00}; const UInt8 UNIChars[8] = {0x55,0x4e,0x49,0x43,0x4f,0x44,0x45,0x00}; @implementation EXFGPSLocationHandler -(void)decodeTag:(NSMutableDictionary*) keyedValues: (NSNumber*) tagId: (CFDataRef*) tagData: (BOOL) bigEndianOrder{ UInt8* ptr = (UInt8*) CFDataGetBytePtr(*tagData); // Debug(@"In timestamp method for %x",tag); // we first need to get the hours UInt32 num = [EXFUtils read4Bytes:&ptr:bigEndianOrder]; ptr +=4; UInt32 denom = [EXFUtils read4Bytes:&ptr:bigEndianOrder]; EXFGPSLoc* location = [[EXFGPSLoc alloc] init]; EXFraction* value = [[EXFraction alloc] initWith:num :denom]; location.degrees =value; [value release]; // Debug(@"Got hours num %i nad denom %i", num, denom); ptr +=4; num = [EXFUtils read4Bytes:&ptr:bigEndianOrder]; ptr+=4; denom = [EXFUtils read4Bytes:&ptr:bigEndianOrder]; value = [[EXFraction alloc] initWith:num :denom]; location.minutes =value; [value release]; // Debug(@"Got minutes num %i and denom %i", num, denom); // get seconds ptr+=4; num = [EXFUtils read4Bytes:&ptr:bigEndianOrder]; ptr+=4; denom = [EXFUtils read4Bytes:&ptr:bigEndianOrder]; // Debug(@"Got seconds num %i and denom %i", num, denom); value = [[EXFraction alloc] initWith:num :denom]; location.seconds =value; [value release]; // Debug(@"Got Timestamp %@ Degrees %@ Minutes %@ Seconds",timestamp.degrees , timestamp.minutes,timestamp.seconds); [keyedValues setObject: location forKey: tagId]; [location release]; } -(void)encodeTag: (NSMutableData*) targetBuffer: (id) tagData:(BOOL) bigEndianOrder{ // tag data is an array of NSNumber EXFGPSLoc* loc = (EXFGPSLoc*)tagData; [EXFUtils appendFractionToData:targetBuffer :loc.degrees :bigEndianOrder]; [EXFUtils appendFractionToData:targetBuffer :loc.minutes :bigEndianOrder]; [EXFUtils appendFractionToData:targetBuffer :loc.seconds :bigEndianOrder]; } -(BOOL)supportsValueType:(id) value{ if ([value isKindOfClass:[EXFGPSLoc class]]){ return TRUE; }else{ return FALSE; } } -(int) getSizeOfValue:(id)value{ // value should be a GPS Loc return 3; } -(NSString*) description{ return @"GPSLocation Handler"; } @end @implementation EXFGPSTimeHandler -(void)decodeTag:(NSMutableDictionary*) keyedValues: (NSNumber*) tagId: (CFDataRef*) tagData: (BOOL) bigEndianOrder{ UInt8* ptr = (UInt8*) CFDataGetBytePtr(*tagData); // Debug(@"In timestamp method for %x",tag); // we first need to get the hours UInt32 num = [EXFUtils read4Bytes:&ptr:bigEndianOrder]; ptr +=4; UInt32 denom = [EXFUtils read4Bytes:&ptr:bigEndianOrder]; EXFGPSTimeStamp* timestamp = [[EXFGPSTimeStamp alloc] init]; EXFraction* value = [[EXFraction alloc] initWith:num :denom]; timestamp.hours =value; [value release]; // Debug(@"Got hours num %i nad denom %i", num, denom); ptr +=4; num = [EXFUtils read4Bytes:&ptr:bigEndianOrder]; ptr+=4; denom = [EXFUtils read4Bytes:&ptr:bigEndianOrder]; value = [[EXFraction alloc] initWith:num :denom]; timestamp.minutes =value; [value release]; // Debug(@"Got minutes num %i and denom %i", num, denom); // get seconds ptr+=4; num = [EXFUtils read4Bytes:&ptr:bigEndianOrder]; ptr+=4; denom = [EXFUtils read4Bytes:&ptr:bigEndianOrder]; // Debug(@"Got seconds num %i and denom %i", num, denom); value = [[EXFraction alloc] initWith:num :denom]; timestamp.seconds =value; [value release]; // Debug(@"Got Timestamp %@ Degrees %@ Minutes %@ Seconds",timestamp.degrees , timestamp.minutes,timestamp.seconds); [keyedValues setObject: timestamp forKey: tagId]; [timestamp release]; } -(void)encodeTag: (NSMutableData*) targetBuffer: (id) tagData:(BOOL) bigEndianOrder{ // tag data is an array of NSNumber EXFGPSTimeStamp* time = (EXFGPSTimeStamp*)tagData; [EXFUtils appendFractionToData:targetBuffer :time.hours :bigEndianOrder]; [EXFUtils appendFractionToData:targetBuffer :time.minutes :bigEndianOrder]; [EXFUtils appendFractionToData:targetBuffer :time.seconds :bigEndianOrder]; } -(BOOL)supportsValueType:(id) value{ if ([value isKindOfClass:[EXFGPSTimeStamp class]]){ return TRUE; }else{ return FALSE; } } -(int) getSizeOfValue:(id)value{ // value should be a GPS Time return 3; } -(NSString*) description{ return @"GPSTime Handler"; } @end @implementation EXFTextHandler -(void)decodeTag:(NSMutableDictionary*) keyedValues: (NSNumber*) tagId: (CFDataRef*) tagData: (BOOL) bigEndianOrder{ // get the first 8 bytes to see the char set // Debug(@"offset %i" ,valueOffset); UInt8* ptr = (UInt8*) CFDataGetBytePtr(*tagData); CFIndex length = CFDataGetLength(*tagData); if (length <9){ return; } UInt8 bytes[8]; for(int i=0;i<8;i++){ bytes[i]=ptr[i]; } // Debug(@"got bytes %x,%x,%x,%x,%x,%x,%x,%x", bytes[0],bytes[1],bytes[2],bytes[3],bytes[4],bytes[5],bytes[6],bytes[7]); NSStringEncoding encoding = NSASCIIStringEncoding; if (bytes[0] == 0x0 && bytes[1] == 0x0 && bytes[2] == 0x0 && bytes[3] == 0x0 && bytes[4] == 0x0 && bytes[5] == 0x0 && bytes[6] == 0x0 && bytes[7] == 0x0 ){ // Debug(@"Undefined charset here"); // we can try ascii here }else if (bytes[0] == JISChars[0] && bytes[1] == JISChars[1] && bytes[2] == JISChars[2] ){ encoding = NSShiftJISStringEncoding; // Debug(@"JIF charset here"); }else if (bytes[0] == UNIChars[0] && bytes[1] == UNIChars[1] && bytes[2] == UNIChars[2] && bytes[3] == UNIChars[3] && bytes[4] == UNIChars[4] && bytes[5] == UNIChars[5] && bytes[6] == UNIChars[6] ){ encoding = NSUnicodeStringEncoding; // Debug(@"Unicode charset"); } else{ // Debug(@"Unknown charset %x,%x,%x,%x,%x,%x,%x,%x", bytes[0],bytes[1],bytes[2],bytes[3],bytes[4],bytes[5],bytes[6],bytes[7]); encoding = 0; } // now try and create the string if (encoding != 0){ UInt8* start_ptr = ptr+8; NSString* string =[EXFUtils newStringFromBuffer:&start_ptr: length-8:encoding]; // Debug(@"Got String in text field %@", string); [keyedValues setObject: string forKey: tagId]; [string release]; } } -(void)encodeTag: (NSMutableData*) targetBuffer: (id) tagData:(BOOL) bigEndianOrder{ // tag data is an array of NSNumber int length = [((NSString*)tagData) lengthOfBytesUsingEncoding:NSASCIIStringEncoding]; const char* cString = [((NSString*)tagData) cStringUsingEncoding:NSASCIIStringEncoding]; [targetBuffer appendBytes:ASCIIChars length:8]; [targetBuffer appendBytes: cString length:length]; } -(BOOL)supportsValueType:(id) value{ if ([value isKindOfClass:[NSString class]]){ return TRUE; }else{ return FALSE; } } -(int) getSizeOfValue:(id)value{ // value should be a GPS Loc if ([value isKindOfClass:[NSString class]]){ return[((NSString*)value) lengthOfBytesUsingEncoding:NSASCIIStringEncoding] +8; } return -1; } -(NSString*) description{ return @"EXF Text Handler"; } @end @implementation EXFASCIIHandler -(void)decodeTag:(NSMutableDictionary*) keyedValues: (NSNumber*) tagId: (CFDataRef*) tagData: (BOOL) bigEndianOrder{ UInt8* ptr = (UInt8*) CFDataGetBytePtr(*tagData); CFIndex byteLength = CFDataGetLength(*tagData); NSString* value = [EXFUtils newStringFromBuffer:&ptr: byteLength: NSASCIIStringEncoding]; // Debug(@"Assigned string %@",value); [keyedValues setObject: value forKey: tagId]; [value release]; } -(void)encodeTag: (NSMutableData*) targetBuffer: (id) tagData:(BOOL) bigEndianOrder{ // tag data is an array of NSNumber int length = [((NSString*)tagData) lengthOfBytesUsingEncoding:NSASCIIStringEncoding]; const char* cString = [((NSString*)tagData) cStringUsingEncoding:NSASCIIStringEncoding]; [targetBuffer appendBytes: cString length:length]; } -(BOOL)supportsValueType:(id) value{ if ([value isMemberOfClass:[NSString class]]){ return TRUE; }else{ return FALSE; } } -(int) getSizeOfValue:(id)value{ // value should be a GPS Loc if ([value isKindOfClass:[NSString class]]){ return[((NSString*)value) lengthOfBytesUsingEncoding:NSASCIIStringEncoding]; }else{ return -1; } } -(NSString*) description{ return @"EXF ASCII Handler"; } @end @implementation EXFByteHandler -(void)decodeTag:(NSMutableDictionary*) keyedValues: (NSNumber*) tagId: (CFDataRef*) tagData: (BOOL) bigEndianOrder{ UInt8* ptr = (UInt8*) CFDataGetBytePtr(*tagData); NSNumber* num = [[NSNumber alloc] initWithInt: (*ptr) & 0xff] ; [keyedValues setObject: num forKey: tagId]; [num release]; } -(void)encodeTag: (NSMutableData*) targetBuffer: (id) tagData:(BOOL) bigEndianOrder{ // tag data is an array of NSNumber UInt8 byte[1]; byte[0] = (UInt8) [((NSNumber*) tagData) intValue]; [targetBuffer appendBytes:byte length:1]; } -(BOOL)supportsValueType:(id) value{ if ([value isKindOfClass:[NSNumber class]]){ return TRUE; }else{ return FALSE; } } -(int) getSizeOfValue:(id)value{ // value should be a GPS Loc if ([value isKindOfClass:[NSNumber class]]){ return 1; }else{ return -1; } } -(NSString*) description{ return @"EXF Byte Handler"; } @end @implementation EXFByteArrayHandler -(void)decodeTag:(NSMutableDictionary*) keyedValues: (NSNumber*) tagId: (CFDataRef*) tagData: (BOOL) bigEndianOrder{ UInt8* ptr = (UInt8*) CFDataGetBytePtr(*tagData); CFIndex byteLength = CFDataGetLength(*tagData); NSMutableArray* byteArray = [[NSMutableArray alloc] init]; for (int i =0;i imageLength){ NSLog(@"ERROR: Length is bigger than image length "); return; } Warn(@"Length in image info %i ",len); int bitsPerPixel = [self readNextbyte]; len--; int height = [self readNext2bytes]; len -= 2; int width = [self readNext2bytes]; len -= 2; numComponents = [self readNextbyte]; len--; Warn(@"Skipping length %i", len); //skip over the remainder length - how do we check the length here? *(imageBytePtr += len); // set them into EXIF Data self.exifMetaData.height = height; self.exifMetaData.width =width; self.exifMetaData.bitsPerPixel = bitsPerPixel; } /** * skip the body after a marker */ - (void) skipVariable { int len = [self readNext2bytes] - 2; if (len < 0 ){ NSLog(@"Error in skip variable length"); return; } if (![self imageLengthCheck:len]){ NSLog(@"ERROR: Length is bigger than image length "); return; } // skip the rest Warn(@"Skipping length %i", len); //skip over the remainder length - how do we check the length here? *(imageBytePtr += len); } - (NSData*) processComment { int length; /* Get the marker parameter length count */ length = [self readNext2bytes]; Debug(@"Got length of comment of %i", length); /* Length includes itself, so must be at least 2 */ if (length < 2) { Debug(@"length must be at least 2"); // make sure we do not overun the image length } if (![self imageLengthCheck:length]){ NSLog(@"ERROR: Length is bigger than image length "); return nil; } length -=2; // get the comment characters - currently use iso latin - could this be different? NSData* commentData = [NSData dataWithBytes:imageBytePtr length:length]; Debug(@"comment data without length 2 bytes %i", [commentData length]); // skip the bytes we have just read [self skipBytes:length]; return commentData; } -(void) parseExif:(CFDataRef*) exifData { [self.exifMetaData parseExif:exifData]; } -(void) parseJfif:(CFDataRef*) jfifData { // we only need to set the jfif if it is a recognized one EXFJFIF* localJfif =[[EXFJFIF alloc]init]; [localJfif parseJfif:jfifData]; if (localJfif.identifier != nil){ self.jfif = localJfif; } [localJfif release]; // we may need to add the additional stuff here for jfif extensions } -(void) scanImageData: (NSData*) jpegData { Debug(@"Starting scan headers"); // pointer to the end of the EXIF Data and the start of the rest of the image ByteArray* endOfEXFPtr = NULL; imageLength = CFDataGetLength((CFDataRef)jpegData); // CFRetain(&imageLength); Debug(@"Length of image %i", imageLength); imageBytePtr = (UInt8 *) CFDataGetBytePtr((CFDataRef)jpegData); imageStartPtr = imageBytePtr; // check if a valid jpeg file UInt8 val = [self readNextbyte]; if (val != M_BEG){ Debug(@"Not a valid JPEG File"); return; } val = [self readNextbyte]; if (val != M_SOI){ Debug(@"Not a valid start of image JPEG File"); return; } // increment this to position after second byte BOOL finished =FALSE; while(!finished){ // increment the marker val = [self nextMarker]; Debug(@"Got next marker %x at byte count %i", val, (imageBytePtr - imageStartPtr)); switch(val){ case M_SOF0: /* Baseline */ case M_SOF1: /* Extended sequential, Huffman */ case M_SOF2: /* Progressive, Huffman */ case M_SOF3: /* Lossless, Huffman */ case M_SOF5: /* Differential sequential, Huffman */ case M_SOF6: /* Differential progressive, Huffman */ case M_SOF7: /* Differential lossless, Huffman */ case M_SOF9: /* Extended sequential, arithmetic */ case M_SOF10: /* Progressive, arithmetic */ case M_SOF11: /* Lossless, arithmetic */ case M_SOF13: /* Differential sequential, arithmetic */ case M_SOF14: /* Differential progressive, arithmetic */ case M_SOF15: /* Differential lossless, arithmetic */ // Remember the kind of compression we saw { int compression = *imageBytePtr; self.exifMetaData.compression = compression; // Get the intrinsic properties fo the image [self readImageInfo]; } break; case M_SOS: /* stop before hitting compressed data */ Debug(@"Found SOS at %i", imageBytePtr - imageStartPtr); // [self skipVariable]; // Update the EXIF // updateExif(); finished = TRUE; break; case M_EOI: /* in case it's a tables-only JPEG stream */ Debug(@"End of Image reached at %i ", imageBytePtr - imageStartPtr); finished =TRUE; break; case M_COM: Debug(@"Got com at %i",imageBytePtr - imageStartPtr); break; case M_APP0: case M_APP1: case M_APP2: case M_APP3: case M_APP4: case M_APP5: case M_APP6: case M_APP7: case M_APP8: case M_APP9: case M_APP10: case M_APP11: case M_APP12: case M_APP13: case M_APP14: case M_APP15: // Some digital camera makers put useful textual // information into APP1 and APP12 markers, so we print // those out too when in -verbose mode. { Debug(@"Found app %x at %i", val, imageBytePtr - imageStartPtr); NSData* commentData = [self processComment]; NSNumber* key = [[NSNumber alloc]initWithInt:val]; // add comments to dictionary [self.keyedHeaders setObject:commentData forKey:key]; [key release]; // will always mark the end of the app_x block endOfEXFPtr = imageBytePtr; // we pass a pointer to the NSData pointer here if (val == M_APP0){ Debug(@"Parsing JFIF APP_0 at %i", imageBytePtr - imageStartPtr); [self parseJfif:(CFDataRef*)&commentData]; } else if (val == M_APP1){ [self parseExif:(CFDataRef*)&commentData]; Debug(@"Finished App1 at %i", endOfEXFPtr - imageStartPtr); } else if (val == M_APP2){ Debug(@"Finished APP2 at %i", imageBytePtr - imageStartPtr); }else{ Debug(@"Finished App &x at %i", val, imageBytePtr - imageStartPtr); } } break; case M_SOI: Debug(@"SOI encountered at %i",imageBytePtr - imageStartPtr); break; default: // Anything else just gets skipped Debug(@"NOt handled %x skipping at %i",val, imageBytePtr - imageStartPtr); [self skipVariable]; // we assume it has a parameter count... break; } } // add in the bytes after the exf block NSData* theRemainingdata = [[NSData alloc] initWithBytes:endOfEXFPtr length:imageLength - (endOfEXFPtr - imageStartPtr)]; self.remainingData = theRemainingdata; [theRemainingdata release]; endOfEXFPtr = NULL; imageStartPtr = NULL; imageBytePtr = NULL; } -(void) populateImageData: (NSMutableData*) newImage { if (newImage ==nil){ NSLog(@"Image array cannot be null"); return; } UInt8 bytes[4]; UInt8* ptr = bytes; bytes[0] = M_BEG; bytes[1] = M_SOI; bytes [2] = bytes[3] =0; [newImage appendBytes:ptr length: 2]; for (int i =0xe0;i<0xf0;i++){ // use the values we have parsed for M_APP1 if (i == M_APP1){ if ([exifMetaData.keyedTagValues count] !=0){ bytes[0] = M_BEG; bytes[1] = (UInt8) i; bytes[2] = bytes[3] = 0; [newImage appendBytes:ptr length:4]; int initialSize = [newImage length] -2; Debug(@"Image length before is now %i",initialSize); // process the EXF Data and write into the image [exifMetaData getData: newImage]; // calculate the block size UInt8* ptr = bytes; [EXFUtils write2Bytes:&ptr :[NSNumber numberWithInt:[newImage length] -initialSize]:TRUE]; // now append this to the writer [newImage replaceBytesInRange:NSMakeRange(initialSize, 2) withBytes:bytes]; Debug(@"Image length after exif is now %i",[newImage length]); } }else{ NSNumber* key = [[NSNumber alloc] initWithInt:i]; NSData* data = [keyedHeaders objectForKey:key]; if (data != nil){ Debug(@"writing app %x with length %i to image",i,[data length] +2); [EXFUtils write2Bytes: &ptr:[NSNumber numberWithInt:[data length]+2] :TRUE]; bytes[2] = bytes[0]; bytes[3] = bytes[1]; bytes[0] = M_BEG; bytes[1] = (UInt8) i; [newImage appendBytes:ptr length:4]; [newImage appendData:data]; } [key release]; } } NSLog(@"About to append remaining data"); // add in the bytes after the exf block [newImage appendData:self.remainingData]; Debug(@"new Image length is now %i - original image length %i",[newImage length], imageLength); } @end ================================================ FILE: EXIF/EXFLogging.h ================================================ /* * logging.h * * * Created by steve woodcock on 28/02/2008. * Copyright 2008. // Licensed under GPL 2.0 http://www.gnu.org/licenses/gpl-2.0.txt * */ // Logging.h //extern BOOL gLogging; #define Debug(FMT,...) /*NSLog(@"DEBUG: " FMT, ##__VA_ARGS__)*/ #define Warn(FMT,...) /*NSLog(@"WARNING: " FMT, ##__VA_ARGS__)*/ ================================================ FILE: EXIF/EXFMetaData.h ================================================ /* * EXFMetaData.h * iphoneGeo * * Created by steve woodcock on 30/03/2008. * Copyright 2008. // Licensed under GPL 2.0 http://www.gnu.org/licenses/gpl-2.0.txt * * The EXFMetaData stores the EXIF Meta Data itself, as well as the meta data and bytes of the thumbnail image if there is one. * * */ #import "EXFTagDefinitionHolder.h" #import "EXFConstants.h" #import "EXFHandlers.h" @interface EXFMetaData : NSObject { // endian ordering for image BOOL bigEndianOrder; // Dictionary of special tag handlers NSMutableDictionary* keyedHandlers; // Dictionary of user supplied handlers NSMutableDictionary* userKeyedHandlers; // dictionary of parsed EXIF Data NSMutableDictionary* keyedTagValues; // Dictionary of parsed Thumbnail EXIF Data NSMutableDictionary* keyedThumbnailTagValues; // pointer to byte array of EXIF Block ByteArray* exif_ptr; // The NSByte array for the thumbnail data NSData* thumbnailBytes; // tag definitions EXFTagDefinitionHolder* tagDefinitions; // Image attributes outside EXIF int compression; int bitsPerPixel; int height; int width; int numComponents; // length of image CFIndex byteLength; } /* Add a user specified handler prior to parsing the data. Allows over-ride of existing behaviour or handling of tags that are not in the EXIF spec. The handler will throw an NSException if: 1) the Key is not a valid Number 2) The tag handler is nil 3) The tag handler does not conform to the EXFHandler protocol 4) The handler attempts to override one for the container tags that contains other tags 5) The handler does not conform to the optional part of the protocol if is being used to handle a tag that is not already defined. 6) The handler returns an invalid parent Id or tagformat if it supports the optional part of the protocol */ -(void) addHandler:(id) aTagHandler forKey:(NSNumber*) aKey; /* Remove a handler. Note this only removes user handlers and cannot be used to remove a built in handler */ -(void) removeHandler: (NSNumber*) aKey; /* Removes all user handlers. */ -(void) removeAllHandlers; // Returns a tag definition for a particular tag - (EXFTag*) tagDefinition: (NSNumber*)aTagId ; //returns all keys for parent id -(NSMutableArray*) tagDefinitionsForParent:(NSNumber*) parent withoutImmutable:(BOOL) includeImmutable ; // Gets the tag value from a parsed file (if any) - (id) tagValue: (NSNumber*)aTagId; // Gets the thumbnail tag value from a parsed file (if any) - (id) thumbnailTagValue: (NSNumber*)aTagId; -(void) addTagValue:(id)value forKey:(NSNumber*) atagKey; -(void) removeTagValue:(NSNumber*) atagKey; // returns the tag definitions for the EXIF Data @property (readonly,retain) NSDictionary* keyedTagDefinitions; // The parsed Exif tag values. @property (readonly,retain) NSMutableDictionary* keyedTagValues; // The parsed Exif thumbnail values @property (readonly,retain) NSMutableDictionary* keyedThumbnailTagValues; // The thumbnail bytes if any @property (readonly,retain) NSData* thumbnailBytes; // Compression value @property (readonly) int compression; // bits per pixel @property (readonly) int bitsPerPixel; // image height @property (readonly) int height; // image width @property (readonly) int width; // byte length @property (readonly) CFIndex byteLength; // byte order @property (readonly) BOOL bigEndianOrder; @end ================================================ FILE: EXIF/EXFMetaData.m ================================================ // // Exif.m // iphone-test // // Created by steve woodcock on 14/03/2008. // Copyright 2008. All rights reserved. // #import "EXFMutableMetaData.h" #import "EXFLogging.h" #import "EXFGPS.h" #import "EXFConstants.h" #import "EXFUtils.h" @implementation EXFraction -(id) initWith: (long) aNumerator : (long) aDenominator{ if (self = [super init]) { numerator = aNumerator; denominator = aDenominator; } return self; } @synthesize numerator; @synthesize denominator; -(NSString*) description{ return [NSString stringWithFormat:@"%@",[NSNumber numberWithDouble:(double)numerator/(double) denominator]]; } @end @interface EXFTag () @property (readwrite) EXFTagId tagId; @property (readwrite) int parentTagId; @property (readwrite) EXFDataType dataType; @property (readwrite, retain) NSString* name; @property (readwrite) BOOL editable; @property (readwrite) int components; @end @interface EXFWriter : NSObject { NSMutableData* tagData; NSMutableData* overflowData; } @property (readwrite, retain) NSMutableData* tagData; @property (readwrite, retain) NSMutableData* overflowData; @property (readonly) int blockLength; @end @implementation EXFTag @synthesize tagId; @synthesize parentTagId; @synthesize dataType; @synthesize name; @synthesize editable; @synthesize components; -(id) initWith: (EXFTagId) aTag: (EXFDataType)aDataType: (NSString*) aName : (int) aParentTagId: (BOOL)isEditable: (int)theComponets{ if (self = [super init]) { self.tagId = aTag; self.dataType = aDataType; self.name =aName; self.parentTagId = aParentTagId; self.editable = isEditable; self.components = theComponets; } return self; } -(void) dealloc{ self.name = nil; [super dealloc]; } @end @implementation EXFWriter @synthesize tagData; @synthesize overflowData; -(id) init{ if (self = [super init]) { NSMutableData* theData = [[NSMutableData alloc] init]; self.tagData = theData; [theData release]; NSMutableData* theOverflow = [[NSMutableData alloc] init]; self.overflowData = theOverflow; [theOverflow release]; } return self; } -(int) blockLength { // length of block in 2 bytes at start of tagData int temp =0; temp += [tagData length]; temp += [overflowData length]; return temp; } -(void) dealloc{ self.tagData = nil; self.overflowData = nil; [super dealloc]; } @end @implementation EXFMetaData @synthesize compression; @synthesize bitsPerPixel; @synthesize height; @synthesize width; @synthesize byteLength; @synthesize bigEndianOrder; @synthesize exif_ptr; @synthesize userKeyedHandlers; @synthesize keyedTagValues; @synthesize keyedHandlers; @synthesize tagDefinitions; @synthesize keyedThumbnailTagValues; @synthesize thumbnailBytes; // start of Exif String const UInt8 exifChars[5] = {0x45,0x78,0x69,0x66,0x00}; // Big endian or Little endian constant chars const UInt8 M_ORDER = 0x4d; const UInt8 I_ORDER = 0x49; // tag constants const UInt16 bytesPerFormat[] = {0,1,1,2,4,8,1,1,2,4,8,4,8}; // Tags that have nested tag sets const int TAG_EXIF_ROOT = -1; const UInt16 TAG_EXIF_OFFSET = 0x8769; const UInt16 TAG_INTEROP_OFFSET = 0xa005; const UInt16 TAG_EXIF_GPS = 0x8825; // allowed substitutes const NSString* typeMappings [13] ={@"",@"CciISs",@"NSString",@"SsCcIi",@"LlIiSsCc",@"EXFraction",@"cCiISs", @"NSData", @"IisSCc",@"LlIiSsCc",@"EXFraction",@"EXFraction",@"EXFraction"}; -(EXFTag*) tagDefinition: (NSNumber*) aTagId{ return [self.tagDefinitions.definitions objectForKey:aTagId]; } -(NSMutableArray*) tagDefinitionsForParent:(NSNumber*) parent withoutImmutable:(BOOL) includeImmutable { // see if a sub array NSMutableArray* returnArray = [NSMutableArray arrayWithCapacity:[self.tagDefinitions.definitions count]]; NSArray* keys = [self.tagDefinitions.definitions allKeys]; for(id key in keys){ EXFTag* tag = [self.tagDefinitions.definitions objectForKey:key]; if ([tag parentTagId] == [parent intValue]){ if ([tag editable]){ [returnArray addObject:tag]; }else if (includeImmutable){ [returnArray addObject:tag]; } } } return returnArray; } -(id) tagValue: (NSNumber*) aTagId{ // in order - first try and get the tag definition to find parent id value = [self.keyedTagValues objectForKey:aTagId]; if (value == nil){ EXFTag* tag = [self.tagDefinitions.definitions objectForKey:aTagId]; if (tag != nil){ NSDictionary* dict = [self.keyedTagValues objectForKey:[NSNumber numberWithInt: tag.parentTagId]]; if (dict != nil){ value = [dict objectForKey:aTagId]; } }else{ NSDictionary* dict = [self.keyedTagValues objectForKey:[NSNumber numberWithInt: EXIF_GPS ]]; value = [dict objectForKey:aTagId]; if (value ==nil){ dict = [self.keyedTagValues objectForKey:[NSNumber numberWithInt: EXIF_Exif]]; value = [dict objectForKey:aTagId]; } } // try the geo and then the // } return value; } -(id) thumbnailTagValue: (NSNumber*) aTagId{ return [self.keyedThumbnailTagValues objectForKey:aTagId]; } /* End of setter and getter methods */ /* Add the handler to the handler dictionary */ -(void) addHandler:(id)handler: (UInt16) keyValue{ NSNumber* _key =nil; // add the invocation into the handler map _key = [[NSNumber alloc] initWithUnsignedInt:keyValue]; [self.keyedHandlers setObject:handler forKey: _key]; // release number objects [_key release]; } /* Set up the handlers we know about. */ -(void) setupHandlers{ // Set up the GPS location handlers EXFGPSLocationHandler* locationHandler = [[EXFGPSLocationHandler alloc] init]; [self addHandler:locationHandler :EXIF_GPSLatitude]; [self addHandler:locationHandler :EXIF_GPSLongitude]; [self addHandler:locationHandler :EXIF_GPSDestLatitude]; [self addHandler:locationHandler :EXIF_GPSDestLongitude]; [locationHandler release]; // do the gps timestamp EXFGPSTimeHandler* timeHandler = [[EXFGPSTimeHandler alloc] init]; [self addHandler:timeHandler :EXIF_GPSTimeStamp]; [timeHandler release]; // do the char set tags EXFTextHandler* textHandler = [[EXFTextHandler alloc] init]; [self addHandler:textHandler :EXIF_UserComment]; [textHandler release]; // Set up the ascii handlers EXFASCIIHandler* asciiHandler = [[EXFASCIIHandler alloc] init]; [self addHandler:asciiHandler :EXIF_ExifVersion]; [self addHandler:asciiHandler :EXIF_FlashpixVersion]; [asciiHandler release]; // set up the byte handler for individual bytes and undefined tag types EXFByteHandler* byteHandler = [[EXFByteHandler alloc] init]; [self addHandler:byteHandler :EXIF_FileSource]; [byteHandler release]; // byte array tag handler EXFByteArrayHandler* byteArrayHandler = [[EXFByteArrayHandler alloc] init]; [self addHandler:byteArrayHandler :EXIF_ComponentsConfiguration]; [byteArrayHandler release]; } -(void) addHandler:(id) aTagHandler forKey:(NSNumber*) aKey{ // test key type if (aKey == nil || ! [aKey isMemberOfClass:[NSNumber class]]){ //throw an error here NSException* myException = [NSException exceptionWithName:@"InvalidKey" reason:@"Key is nil or not a Number" userInfo:nil]; @throw myException; } // test the tag handler is not null if (aTagHandler == nil){ //throw an error here NSException* myException = [NSException exceptionWithName:@"InvalidHandler" reason:@"Tag Handler is nil" userInfo:nil]; @throw myException; } if ( ! [((NSObject*)aTagHandler) conformsToProtocol:@protocol(EXFTagHandler)] ) { // Object does not conform to EXFTagHandler protocol NSException* myException = [NSException exceptionWithName:@"InvalidHandler" reason:@"Tag Handler Does not conform to protocol EXFTagHandler" userInfo:nil]; @throw myException; } // do not allow overwrite of nested values if ([aKey intValue] == TAG_EXIF_OFFSET || [aKey intValue] == TAG_INTEROP_OFFSET || [aKey intValue] == TAG_EXIF_GPS){ NSException* myException = [NSException exceptionWithName:@"InvalidHandler" reason:@"Tag Handler cannot override tags that are containers for other tag sets" userInfo:nil]; @throw myException; } // now check optional conformance - if no tag exists then it must implement all the methods EXFTag* tag = [self.keyedTagDefinitions objectForKey:aKey]; if (tag == nil){ // check that all the types are specified NSException* myException = nil; if ([((NSObject*)aTagHandler) respondsToSelector:@selector(tagFormat)] ){ if (([aTagHandler tagFormat] <0 && [aTagHandler tagFormat] != -99) || [aTagHandler tagFormat] < FMT_BYTE || [aTagHandler tagFormat] >FMT_DOUBLE){ myException = [NSException exceptionWithName:@"InvalidHandler" reason:@"Tag Handler tagFormat is not valid - please see documentation" userInfo:nil]; } }else{ myException = [NSException exceptionWithName:@"InvalidHandler" reason:@"Tag Handler must implement tagFormat to support new tag Id" userInfo:nil]; } if ([((NSObject*)aTagHandler) respondsToSelector:@selector(parentTagId)] ){ if ([aTagHandler parentTagId] != TAG_EXIF_GPS || [aTagHandler parentTagId] != TAG_EXIF_OFFSET || [aTagHandler parentTagId] != TAG_EXIF_ROOT || [aTagHandler parentTagId] != TAG_INTEROP_OFFSET){ myException = [NSException exceptionWithName:@"InvalidHandler" reason:@"Tag Handler parent Tag Id is invalid - please see documentation" userInfo:nil]; } }else{ myException = [NSException exceptionWithName:@"InvalidHandler" reason:@"Tag Handler must implement tagFormat to support new tag Id" userInfo:nil]; } if (![((NSObject*)aTagHandler) respondsToSelector:@selector(isEditable)] ){ myException = [NSException exceptionWithName:@"InvalidHandler" reason:@"Tag Handler must implement isEditable to support new tag Id" userInfo:nil]; } if (myException != nil){ @throw myException; } } // otherwise add it to the user handlers [self.userKeyedHandlers setObject:aTagHandler forKey:aKey]; } -(void) removeHandler: (NSNumber*) aKey{ [self.userKeyedHandlers removeObjectForKey:aKey]; } -(void) removeAllHandlers{ [self.userKeyedHandlers removeAllObjects]; } /* Init method */ -(id) init { if (self = [super init]) { // Initialize the tag definitions EXFTagDefinitionHolder* theTagDefs = [[EXFTagDefinitionHolder alloc] init]; self.tagDefinitions = theTagDefs; [theTagDefs release]; // initialise the tag values NSMutableDictionary* keyedValues = [[NSMutableDictionary alloc] init]; self.keyedTagValues =keyedValues; [keyedValues release]; // initialise the handlers NSMutableDictionary* handlerDict = [[NSMutableDictionary alloc] init]; self.keyedHandlers = handlerDict; [handlerDict release]; //initialise the user handlers NSMutableDictionary* userDict = [[NSMutableDictionary alloc] init]; self.userKeyedHandlers = userDict; [userDict release]; NSMutableDictionary* theKeyedThumbnailTagValues = [[NSMutableDictionary alloc] init]; self.keyedThumbnailTagValues = theKeyedThumbnailTagValues; [theKeyedThumbnailTagValues release]; self.thumbnailBytes =nil; // initialise the default image values self.height = 0; self.width=0; self.compression=0; self.bitsPerPixel=0; self.byteLength =0; self.bigEndianOrder =NO; self.exif_ptr =NULL; // set up the special handlers [self setupHandlers]; } return self; } -(void) dealloc{ self.exif_ptr = NULL; self.keyedHandlers = nil; self.keyedTagValues =nil; self.tagDefinitions=nil; self.userKeyedHandlers=nil; self.keyedThumbnailTagValues =nil; self.thumbnailBytes =nil; [super dealloc]; } -(void) addTagValue:(id)value forKey:(NSNumber*) aTagKey { // get tag definition - may be nil EXFTag* tag = [self.keyedTagDefinitions objectForKey:aTagKey]; int parentTagId = -1; // see if we have a user registered handler for support id handler = [self.userKeyedHandlers objectForKey:aTagKey]; if (handler == nil){ //see if we have one of our default handlers handler = [self.keyedHandlers objectForKey:aTagKey]; } if(handler != nil){ NSException *e =nil; if (![handler supportsValueType:value]){ e = [NSException exceptionWithName:@"InvalidTypeException" reason:[NSString stringWithFormat: @"Handler %@ does not support value for %@",handler, value] userInfo:nil]; } if ([((NSObject*)handler) respondsToSelector:@selector(isEditable)]){ if ([handler isEditable] == FALSE){ e = [NSException exceptionWithName:@"NonEditableKeyException" reason:[NSString stringWithFormat: @"Handler %@ does not support editing for %@",handler, aTagKey] userInfo:nil]; } }else{ if (![tag editable]){ e = [NSException exceptionWithName:@"NonEditableKeyException" reason:[NSString stringWithFormat: @"Tag does not support editing for %@", aTagKey] userInfo:nil]; } } if ([((NSObject*)handler) respondsToSelector:@selector(parentTagId)]){ parentTagId = [handler parentTagId]; }else{ if (tag != nil){ parentTagId = [tag parentTagId]; }else{ e = [NSException exceptionWithName:@"NonEditableKeyException" reason:[NSString stringWithFormat: @"Tag definition not found for %@ - and parentTagId not supported by handler", aTagKey] userInfo:nil]; } } if (e != nil){ @throw e; } } else{ if (tag == nil){ Debug(@"Tag %i is not found",tag.tagId); NSException *e = [NSException exceptionWithName:@"NonEditableKeyException" reason:[NSString stringWithFormat: @"No Tag found for Key %@", aTagKey] userInfo:nil]; @throw e; } // check if it exists that it is editable if (! tag.editable){ Debug(@"Tag %x is not editiable",tag.tagId); NSException *e = [NSException exceptionWithName:@"NonEditableKeyException" reason:@"Tag is not editable" userInfo:nil]; @throw e; } parentTagId = tag.parentTagId; // lets check the type mappings for the standard tags int type = tag.dataType; // else lets get the string that matches the types NSString* dataTypeStr = (NSString*) typeMappings[type]; if ([@"NSString" isEqualToString:dataTypeStr ]) { // it has to match the class name in the typemappings if(! [value isKindOfClass:[NSString class]] || (! [value canBeConvertedToEncoding:NSASCIIStringEncoding]) ){ NSException *e = [NSException exceptionWithName:@"InvalidTypeForHandlerException" reason:[NSString stringWithFormat: @"Tag %@ only supports NSString in ASCII format",aTagKey] userInfo:nil]; @throw e; } } else if ([@"NSData" isEqualToString:dataTypeStr] ){ if(! [value isKindOfClass:[NSData class]] ){ NSException *e = [NSException exceptionWithName:@"InvalidTypeForHandlerException" reason:[NSString stringWithFormat: @"Tag %@ only supports NSData",aTagKey] userInfo:nil]; @throw e; } } else if ([@"EXFraction" isEqualToString:dataTypeStr] ){ if(! [value isKindOfClass:[EXFraction class]] ){ NSException *e = [NSException exceptionWithName:@"InvalidTypeForHandlerException" reason:[NSString stringWithFormat: @"Tag %@ only supports EXFraction",aTagKey] userInfo:nil]; @throw e; } }else{ // it can opnly be a number - or an array of numbers // Array handling is a bit wierd here - to do in a more elegant manner id tempValue = value; int i=0; if ([value isKindOfClass:[NSArray class]]){ tempValue = [((NSArray*)value) objectAtIndex:i]; } while(true){ Debug(@"tempvalue class %@",[tempValue class]); if (! [tempValue isKindOfClass:[NSNumber class]] ) { NSException *e = [NSException exceptionWithName:@"InvalidTypeException" reason:[NSString stringWithFormat: @"Tag %@ supports only numeric types of %@ - unsupported type %@",aTagKey, dataTypeStr, [tempValue class]] userInfo:nil]; @throw e; } if (! [tempValue isKindOfClass:[NSNumber class]] || [dataTypeStr rangeOfString:[NSString stringWithFormat:@"%s",[tempValue objCType]]].location == NSNotFound) { NSException *e = [NSException exceptionWithName:@"InvalidTypeException" reason:[NSString stringWithFormat: @"Tag %@ does not support numeric type %c for %@",aTagKey, [tempValue objCType],value] userInfo:nil]; @throw e; } i++; if ([value isKindOfClass:[NSArray class]] && i<[((NSArray*)value) count]){ tempValue = [((NSArray*)value) objectAtIndex:i]; }else{ break; } } } } // now add the value - and make sure we add in the right sub dir NSMutableDictionary* dictionary = self.keyedTagValues ; if (parentTagId != -1) { // let dictionary = subdictionary NSNumber* parentTagNumber = [[NSNumber alloc] initWithInt:parentTagId]; dictionary = [self.keyedTagValues objectForKey: parentTagNumber]; if (dictionary == nil){ dictionary = [[NSMutableDictionary alloc] init]; [self.keyedTagValues setObject:dictionary forKey: parentTagNumber]; [dictionary release]; dictionary = [self.keyedTagValues objectForKey:parentTagNumber]; } [parentTagNumber release]; } // set the value [dictionary setObject:value forKey:aTagKey]; } -(void) removeTagValue:(NSNumber*) aTagKey { // get tag definition - may be nil EXFTag* tag = [self.keyedTagDefinitions objectForKey:aTagKey]; int parentTagId = -1; if (tag == nil){ // see if one of the sub tags if ([aTagKey intValue] == EXIF_Exif){ Debug(@"Tag %i is not found",tag.tagId); NSException *e = [NSException exceptionWithName:@"NonEditableKeyException" reason:[NSString stringWithFormat: @"Tag group %@ cannot be removed", aTagKey] userInfo:nil]; @throw e; } }else if (! tag.editable){ Debug(@"Tag %x is not editiable",tag.tagId); NSException *e = [NSException exceptionWithName:@"NonEditableKeyException" reason:@"Tag is not editable" userInfo:nil]; @throw e; } //it is either an editable tag or a gps block if (tag != nil){ parentTagId = tag.parentTagId; } // remove the tag NSMutableDictionary* dictionary = self.keyedTagValues ; if (parentTagId != -1) { // let dictionary = subdictionary NSNumber* parentTagNumber = [[NSNumber alloc] initWithInt:parentTagId]; dictionary = [self.keyedTagValues objectForKey: parentTagNumber]; if (dictionary == nil){ // we can just return as no parent to release Debug(@"No Parent tag %@ found for tag %@", parentTagNumber,aTagKey); } [parentTagNumber release]; } // otherwise rmove tag [dictionary removeObjectForKey:aTagKey]; //remove the parent if empty if ([dictionary count] ==0 && parentTagId != -1){ [self removeTagValue:[NSNumber numberWithInt:parentTagId]]; } } -(NSDictionary*) keyedTagDefinitions{ return self.tagDefinitions.definitions; } /* End of utility helper methods */ /* start of tag population methods */ -(void) assignElements: (NSMutableDictionary*) keyedValues: (NSNumber*) tag: (id) elements: (UInt32) components{ if (components > 1){ [keyedValues setObject: elements forKey: tag]; }else{ [keyedValues setObject: [elements objectAtIndex:0] forKey: tag]; } } -(void) assignSByte:(NSMutableDictionary*) keyedValues: (NSNumber*) tag: (UInt32) valueOffset: (UInt32) components{ // have to use byte count for list NSMutableArray* elements = [[NSMutableArray alloc] init]; for(int i =0;i FMT_DOUBLE)) { NSLog(@"*** Warning Unknown format %i for tag %i",format, tag); tagFailure =TRUE; continue; } // work out how many bytes we need for format UInt32 byteCount = components * bytesPerFormat[format]; // offset to read from if data in tag (default) UInt32 valueOffset = dirOffset + 8; // if more than 4 bytes then must be in overflow - so set the valueOffset to be the location in the overflow if (byteCount > 4) { ptr = exif_ptr + dirOffset +8; UInt32 offsetVal = [EXFUtils read4Bytes:&ptr: self.bigEndianOrder]; valueOffset = offsetBase + offsetVal; Debug(@"Offset %i found for tag %i with bytecount %i",valueOffset, tag,byteCount); } // sometimes thumbnail has no tag just runs on end of data - so keep track of where we are in block if (byteCount <4 && (valueOffset +4 > thumbnailDataCount)){ thumbnailDataCount = valueOffset + 4; }else if (valueOffset + byteCount > thumbnailDataCount){ thumbnailDataCount = valueOffset + byteCount; } // get a tagNumber object to work with the maps NSNumber* tagNumber = [[NSNumber alloc] initWithUnsignedInt:tag]; // if this a nested tag type then create a new value map and recurse into this method if (tag == TAG_EXIF_OFFSET || tag == TAG_INTEROP_OFFSET || tag == TAG_EXIF_GPS) { ptr = exif_ptr + valueOffset; UInt32 subdirOffset = [EXFUtils read4Bytes:&ptr: self.bigEndianOrder]; Debug(@"Nested pointer to %i found for tag %i with bytecount %i",subdirOffset +offsetBase, tag,byteCount); // create a new sub directory for the tag NSMutableDictionary* subDir = [[NSMutableDictionary alloc] init]; // set it into the current directory [keyedValues setObject: subDir forKey:tagNumber]; // process the sub dir - ignore the return value as should always be 0 [self processExifDir:subDir: offsetBase+subdirOffset: offsetBase: FALSE]; // release the map we created [subDir release]; }else{ // see if we have a user handler id handler = [[self userKeyedHandlers] objectForKey:tagNumber]; // if not see if we have a default specific handler if (handler == nil){ handler = [[self keyedHandlers] objectForKey:tagNumber]; } // a handler is dealing with this tag if (handler != nil){ Debug(@"Handler %@ invoked for tag %@ invoked with offset %i and byte count %i",handler, tagNumber, valueOffset, byteCount); // create the NSData object to pass to the handler NSData* tagData = [NSData dataWithBytes:&self.exif_ptr[valueOffset] length:byteCount]; Debug(@"Retain count for tagData is %i", [tagData retainCount]); // try and decode the tag here - catch any errors @try { [handler decodeTag: keyedValues: tagNumber:(CFDataRef*) &tagData: self.bigEndianOrder]; }@catch (NSException *theError) { NSLog(@"Unable to process Tag %i due to error %@", tagNumber, theError); } }else{ // see if we have a known tag definition EXFTag* tagDefinition = [self.keyedTagDefinitions objectForKey:tagNumber]; if (tagDefinition == nil){ // we should ignore this Debug(@"*** Ignoring unknown tag definition for %@", tagNumber); tagFailure = TRUE; continue; }else{ switch (format) { case FMT_UNDEFINED: // ignore this for now Debug(@"Undefined format found for tag %@ treating as NSData", tagNumber); // this gets put in as an nsdata entry [self assignData:keyedValues: tagNumber :valueOffset :components]; break; case FMT_STRING: [self assignString:keyedValues: tagNumber :valueOffset :components]; break; case FMT_SBYTE: [self assignSByte:keyedValues: tagNumber :valueOffset :components]; break; case FMT_BYTE: [self assignByte:keyedValues :tagNumber :valueOffset :components]; break; case FMT_USHORT: [self assignUShort:keyedValues: tagNumber :valueOffset :components]; break; case FMT_SSHORT: [self assignShort:keyedValues: tagNumber :valueOffset :components]; break; case FMT_SLONG: [self assignLong:keyedValues: tagNumber :valueOffset :components]; break; case FMT_ULONG: [self assignLong:keyedValues: tagNumber :valueOffset :components]; break; case FMT_URATIONAL: [self assignFraction:keyedValues: tagNumber :valueOffset :components]; break; case FMT_SRATIONAL: [self assignSignedFraction:keyedValues: tagNumber :valueOffset :components]; break; default: Debug(@"Unexpected format for tag %x", tag); tagFailure =TRUE; break; } } } } // make sure we release the tag number if ([keyedValues count] == processedValueCount){ tagFailure =TRUE; Debug(@"*** Warning No entry added for tag %@", tagNumber); } [tagNumber release]; } // now get the offset pointer to thumbnail if any int nextIFD = dirStart +2 +(12*numEntries); UInt8* nextPtr = exif_ptr + nextIFD; UInt32 nextOffset = [EXFUtils read4Bytes:&nextPtr: self.bigEndianOrder] ; Debug(@"Next Offset %i at %i",nextOffset, nextIFD); // bit of a hack but needs to be done here as some times we get thumbnail with no tags if(thumbnail){ long thumbnailStart = 0; long thumbnailLength =0; // get the offset to the data NSNumber* jpegOffset = [keyedValues objectForKey : [NSNumber numberWithInt: EXIF_JPEGInterchangeFormat]]; // if we have a tag identifying offset we should be able to get length if (jpegOffset != nil){ // get the length NSNumber* jpegLength = [keyedValues objectForKey : [NSNumber numberWithInt: EXIF_JPEGInterchangeFormatLength]]; thumbnailStart = [jpegOffset longValue]; thumbnailLength = [jpegLength longValue]; }else if (! tagFailure) { // tags are missing but we could still work this out - if we have not had a tag Failure // make sure if we are at the end of thumbnail tags we include overflow if (nextIFD +4 > thumbnailDataCount){ thumbnailStart = nextIFD +4; }else{ thumbnailStart = thumbnailDataCount; } thumbnailLength = self.byteLength - thumbnailStart; }else if (tagFailure){ NSLog(@"Tag Failure occurred so unable to calculate if any thumbnail exists"); } // see if thubnail data is less than block length - as there may be no thumbnail at all if (thumbnailLength >0 && thumbnailStart + thumbnailLength == self.byteLength){ // looks ok - try and get remaining block as thumbnail nextPtr = exif_ptr + thumbnailStart ; NSData* thumbnailDataArray = [[NSData alloc] initWithBytes:nextPtr length: thumbnailLength]; self.thumbnailBytes = thumbnailDataArray; [thumbnailDataArray release]; } else if (thumbnailStart + thumbnailLength > self.byteLength){ Debug(@"*** Thumbnail start of %i and length %i is more than total block length %i", thumbnailStart, thumbnailLength, self.byteLength); }else{ Debug(@"*** Thumbnail start of %i and length %i is less than total block length %i", thumbnailStart, thumbnailLength, self.byteLength); } } // if there has not been an offset value Debug(@"********** Leaving exif processing at %i with nextOffset %i***********",dirStart , nextOffset); return nextOffset; } -(void) parseExif: (CFDataRef*) exifData { // get the byte length self.byteLength = CFDataGetLength(*exifData); Debug(@"Length of exif %i", byteLength); // must be at least 13 bytes if (self.byteLength <13){ NSLog(@"***Warning byte length for EXIF is too short %i must be at least 13 bytes", self.byteLength); return; } //get the first 4 bytes and make sure they equal the exif chars UInt8 bytes[4]; CFDataGetBytes(*exifData, CFRangeMake(0,4), bytes); // test the start of the EXIF Data for (int i=0;i<4;i++){ if(exifChars[i] != bytes[i]){ NSLog(@"***Warning 'EXIF' string not present at start of Exif data"); return; } } // skip the next two padding bytes // get the endian of the bytes UInt8 order[2]; //CFDataGetBytes(*exifData, CFRangeMake(6,8), order); CFDataGetBytes(*exifData, CFRangeMake(6,2), order); if (M_ORDER == order[0] && M_ORDER == order[1]){ self.bigEndianOrder =YES; Debug(@"Big endian type found for data "); }else if (I_ORDER == order[0]&& I_ORDER == order[1]){ // intel order self.bigEndianOrder = NO; Debug(@"Little endian type found for data"); }else{ // we have an unrecognized type NSLog(@"*** Warning Unrecognized endian type %x %x", order[0], order[1]); return; } // create initial pointer to start of data self.exif_ptr = (UInt8*) CFDataGetBytePtr(*exifData); // check header is TIFF header UInt8* ptr = exif_ptr +8; UInt16 value = [EXFUtils read2Bytes:&ptr:self.bigEndianOrder]; if ( value != 0x2a) { NSLog(@"*** Warning Not a valid TIFF Header: %x should be 0x2a", value); return; } // get the first offset ptr = exif_ptr +10; UInt32 offset = [EXFUtils read4Bytes:&ptr:self.bigEndianOrder]; // now process the tag data and get back number of bytes processed int thumbnailOffset = [self processExifDir: self.keyedTagValues: offset+6:6:FALSE]; // if we are less than the image size then we need to see if we have a JPEG thumbnail if (thumbnailOffset > 0){ [self processExifDir: self.keyedThumbnailTagValues: thumbnailOffset+6:6:TRUE]; Debug(@"Got thumbnail data %@", self.keyedThumbnailTagValues); } // obviously we need the last 6 here as well // lastOffset+=6; Debug(@"Returning from parsing at %i ",thumbnailOffset); } /* Bytes 0-1 Tag Bytes 2-3 Type Bytes 4-7 Count Bytes 8-11 Value Offset */ -(void) writeDataToBuffer: (NSMutableData*) target: (id) obj : (int) dataType: (int) tagByteSize: (UInt8**) bytes{ // Appends data to the NSMutableData buffer depending on type - padding must be done OUTSIDE this method as // there is not enough information to deal with array structures switch (dataType) { case FMT_UNDEFINED:{ if ([obj isKindOfClass:[NSData class]]){ // assume this is an NSData object [target appendData: (NSData*)obj]; }else{ NSLog(@"Data %@ is not NSData class - populating empty tag data", obj); [target increaseLengthBy:tagByteSize]; } } break; case FMT_STRING:{ Debug(@"Writing ASCII String %@ with count of ", obj, tagByteSize); const char* cString = [((NSString*)obj) cStringUsingEncoding:NSASCIIStringEncoding]; [target appendBytes: cString length:tagByteSize]; // seems to be required for examples - check other files /* if (tagByteSize >4 && tagByteSize %2!= 0){ [target increaseLengthBy:1]; } */ }break; case FMT_SBYTE: [EXFUtils write1SignedByte:bytes :(NSNumber*)obj :self.bigEndianOrder]; [target appendBytes:*bytes length:1]; break; case FMT_BYTE: [EXFUtils write1Byte:bytes :(NSNumber*)obj :self.bigEndianOrder]; [target appendBytes:*bytes length:1]; break; case FMT_USHORT: [EXFUtils write2Bytes:bytes :(NSNumber*)obj :self.bigEndianOrder]; [target appendBytes:*bytes length:2]; break; case FMT_SSHORT: [EXFUtils write2SignedBytes:bytes :(NSNumber*)obj :self.bigEndianOrder]; [target appendBytes:*bytes length:2]; break; case FMT_SLONG: [EXFUtils write4SignedBytes:bytes :(NSNumber*)obj :self.bigEndianOrder]; [target appendBytes:*bytes length:4]; break; case FMT_ULONG: [EXFUtils write4Bytes:bytes :(NSNumber*)obj :self.bigEndianOrder]; [target appendBytes:*bytes length:4]; break; // these can't be 4 or less case FMT_URATIONAL: [EXFUtils appendFractionToData:target :(EXFraction*) obj :self.bigEndianOrder]; break; case FMT_SRATIONAL: [EXFUtils appendFractionToData:target :(EXFraction*) obj :self.bigEndianOrder]; break; default: Debug(@"Unexpected format for val %@ with tagSize %i", obj, tagByteSize); break; } } /* Note: 1) Tags which are containers for nested values have no definition 2) Tags that handlers deal with can have no definition 3) ALL other tags are expected to have a definition */ -(void) getDataFromMap: (NSDictionary*) dictionary :(NSMutableArray*) dataWriterArray: (UInt8**) bytes: (int) overflowOffset: (int) offsetBase{ // create a data writer for these tags EXFWriter* dataWriter = [[EXFWriter alloc] init]; Debug(@"****** Entering data map at %i", overflowOffset); // get the tag keys that we have NSArray* allKeys = [dictionary allKeys]; // a placeholder for nested tags that we need to deal with after all the other tags NSMutableArray* nestedTags = [[NSMutableArray alloc] init]; // Sort the tags so we do them in ascending order NSArray *sortedKeysArray = [allKeys sortedArrayUsingSelector:@selector(compare:)]; // get the size of the key list int size = [sortedKeysArray count] ; // the extra plus 4 is where the next ifd for any thumbnail is set // if no value then 00000000 pads between blocks int blockCount = (size *12 +2) +4; Debug(@"Number of ELements %i and block size of %i",size, blockCount); // write the number of elements in the first 2 bytes [EXFUtils write2Bytes:bytes :[NSNumber numberWithInt:size] :self.bigEndianOrder]; [dataWriter.tagData appendBytes:*bytes length:2]; // loop through all the elements and add to the current block for (int de =0;de handler = [[self userKeyedHandlers] objectForKey:key]; // if not see if we have a default handler if (handler == nil){ handler = [[self keyedHandlers] objectForKey:key]; } // get the dynamic tag byte data size from the handler if (handler != nil){ tagComponentSize = [handler getSizeOfValue: obj]; // override the tag type if necessary if ([(NSObject*)handler respondsToSelector: @selector(tagFormat)]){ tagDataType = [handler tagFormat]; }else if (tag != nil){ tagDataType = tag.dataType; } }else{ // set the data type from the tag definition tagDataType = tag.dataType; // now deal with the tags that have arbitrary sizes // this must be either NSData or NSString if (tag.components <0){ // must be size of the data // can be either -1 which is undefined or -99 which is any if ([obj isKindOfClass:[NSData class]]){ // this must be a byte length from the data tagComponentSize = [((NSData*)obj) length]; } else if ([obj isKindOfClass:[NSString class]]){ // String length is determined by ascii encoding only tagComponentSize = [((NSString*)obj) lengthOfBytesUsingEncoding:NSASCIIStringEncoding]; Debug(@"Got String value of %@",obj); }else{ // we have a problem here NSLog(@"*** Warning Data in undexpected format for key %@ got format class %@",key, [obj class] ); continue; } }else{ // static types are based on compoenets in tag definition * bytes for each format tagComponentSize = tag.components ; } } // now we have tag id/tag size/tag type - we need to get the data byte size int tagByteSize = tagComponentSize* bytesPerFormat[tagDataType]; // if we have a negative tag size we should ignore the tag if (tagByteSize <0){ NSLog(@"*** Warning Unexpected tagsize of %i returned for tag %@ - ignoring tag",tagByteSize, key); // we might want to have a dictionary of ignored tags here } else{ // this line tells us where int he final unified block the data will be Debug(@"Writing value for key %@ at final location %i",key, [dataWriter.tagData length] + overflowOffset + offsetBase); // for each tag write the tag id [EXFUtils write2Bytes:bytes :key :self.bigEndianOrder]; // now append this to the writer [dataWriter.tagData appendBytes:*bytes length:2]; // write the data type [EXFUtils write2Bytes:bytes :[NSNumber numberWithInt: tagDataType] :self.bigEndianOrder]; [dataWriter.tagData appendBytes:*bytes length:2]; // now write the count Debug(@"Writing byte count of %i key %@ ",tagComponentSize, key); [EXFUtils write4Bytes:bytes :[NSNumber numberWithInt: tagComponentSize] :self.bigEndianOrder]; [dataWriter.tagData appendBytes:*bytes length:4]; // the target data array is either the block if size is 4 bytes or less or // overflow block if more NSMutableData* target = nil; // now set up the data elements to be written if (tagByteSize <=4){ target = dataWriter.tagData; }else{ target = dataWriter.overflowData; // write location of the data offset block int temp = blockCount + overflowOffset + [dataWriter.overflowData length]; Debug(@"Writing overflow location of %i (%i) for key %@ ",temp,temp+offsetBase, key); [EXFUtils write4Bytes:bytes :[NSNumber numberWithInt: temp] :self.bigEndianOrder]; [dataWriter.tagData appendBytes:*bytes length:4]; Debug(@"Length of tagData %i ",[dataWriter.tagData length]); } // Now we write the data to the target buffer if (handler != nil){ Debug(@"Encoding with handler for tag %@", key); // We use a temporary buffer here to the user cannot change the real buffer NSMutableData* tagData = [[NSMutableData alloc] init]; // encode the data @try { [handler encodeTag:tagData :obj :self.bigEndianOrder]; }@catch (NSException *theError) { NSLog(@"***Warning Unable to process Tag %@ due to error %@ - writing empty bytes to tag", key, theError); } // the handler returned a different amount than it said if ([tagData length] != tagByteSize){ NSLog(@"***Warning Handler %@ returned %i bytes for tag %@ - expected %i. Altering buffer size to projected size", handler, [tagData length],key,tagByteSize); // we should truncate or expand based on the value here [tagData setLength:tagByteSize]; } // pad if we need to if ([tagData length] <4){ Debug(@"Tag data less than 4 for tag %@ from handler %@ - padding %i bytes", key, handler, 4 - [tagData length]); [tagData increaseLengthBy: 4 - [tagData length]]; } // write the data to the target [target appendData:tagData]; // release the temporary buffer [tagData release]; } else{ // if it is an array and undefined then treat as array of bytes if ([obj isKindOfClass:[NSArray class]] && tagDataType == FMT_UNDEFINED){ // treat this as a byte array [self appendDataFromBytes:target :(NSArray*)obj]; // else we can treat as array of object types }else if ([obj isKindOfClass:[NSArray class]]){ for (id val in ((NSArray*)obj)){ [self writeDataToBuffer:target :val :tagDataType :tagByteSize:bytes]; } // else it is a single type object - could be an nsdata block though }else{ [self writeDataToBuffer:target :obj :tagDataType :tagByteSize:bytes]; } // pad if we need to if (tagByteSize <4){ [target increaseLengthBy: 4 - tagByteSize]; } } Debug(@"Overflow size of %i", [dataWriter.overflowData length]); } } } // now do the offsets as we should know how big the tag data and the overflow data is // note the oustanding nested tags are all fixed size so we do not worry about those yet // add the current data writer to the accumulated array [dataWriterArray addObject:dataWriter]; // now do each nested set int nestedOffset= overflowOffset + blockCount + [dataWriter.overflowData length] ; for(int i=0;i< [nestedTags count];i++){ NSNumber* key = (NSNumber*) [nestedTags objectAtIndex:i]; Debug(@"Writing value for key %@ at location %i",key, [dataWriter.tagData length] + overflowOffset + offsetBase); // write the nested tag id [EXFUtils write2Bytes:bytes :key :self.bigEndianOrder]; // now append this to the writer [dataWriter.tagData appendBytes:*bytes length:2]; // write the data type [EXFUtils write2Bytes:bytes :[NSNumber numberWithInt: FMT_ULONG] :self.bigEndianOrder]; [dataWriter.tagData appendBytes:*bytes length:2]; // now write the count [EXFUtils write4Bytes:bytes :[NSNumber numberWithInt: 1] :self.bigEndianOrder]; [dataWriter.tagData appendBytes:*bytes length:4]; Debug(@"Writing nested tag location of %i (%i) for key %@ ",nestedOffset, nestedOffset +offsetBase, key); [EXFUtils write4Bytes:bytes :[NSNumber numberWithInt: nestedOffset] :self.bigEndianOrder]; [dataWriter.tagData appendBytes:*bytes length:4]; // now process the nested tag [self getDataFromMap:[dictionary objectForKey:key]: dataWriterArray: bytes: nestedOffset: offsetBase]; // release thye current data writer EXFWriter* lastWriter = [dataWriterArray lastObject]; nestedOffset += [lastWriter blockLength] + 4; // note the counts will be incorrect if there is more than 1 level nesting - fix this up later } [nestedTags release]; Debug(@"Expected blockCount %i - actual count %i",blockCount,[dataWriter.tagData length]); Debug(@"Overflow size is %i",[dataWriter.overflowData length]); Debug(@"Reporting block count of %i",[dataWriter blockLength]); [dataWriter release]; //make sure we clean up the tags Debug(@"****** Leaving data map at %i", overflowOffset); } -(void) getData: (NSMutableData*)imageData { //first create the NSData // first of all we have to construct a a data holder for the image data int initialSize = [imageData length]; NSMutableArray* dataWriters = [[NSMutableArray alloc] init]; // first set up a byte array to hold the temporary values UInt8 bytes[4]; UInt8* ptr = bytes; [self getDataFromMap:self.keyedTagValues: dataWriters: &ptr: 8: 6]; // add the first 14 bytes first then [imageData appendBytes:self.exif_ptr length:14]; int thumbnailOffsetPointer =0; for(int i=0;i<[dataWriters count];i++){ EXFWriter* temp = [dataWriters objectAtIndex:i]; [imageData appendData:temp.tagData]; // add back in any thubnail values here if (i ==0 && [self.keyedThumbnailTagValues count] >0){ Debug(@"Current image data %@", imageData); // take into account the first 6 chars in the file as a whole thumbnailOffsetPointer = [imageData length]; Debug(@"Got pointer to re-write thumbnail at %i", thumbnailOffsetPointer); } [imageData increaseLengthBy:4]; [imageData appendData:temp.overflowData]; } [dataWriters removeAllObjects]; // for each dictionary get the //add in the thumbnail - if any if (thumbnailOffsetPointer != 0){ [EXFUtils write4Bytes:&ptr :[NSNumber numberWithInt:([imageData length] -initialSize) -6]:TRUE]; Debug(@"Replacing bytes at %i with thumbnailOffset Pointer %i",thumbnailOffsetPointer,([imageData length] -initialSize)-6); [imageData replaceBytesInRange:NSMakeRange(thumbnailOffsetPointer, 4) withBytes:bytes]; Debug(@" Adding thumbnail Data %@", self.keyedThumbnailTagValues); [self getDataFromMap:self.keyedThumbnailTagValues: dataWriters: &ptr: 8: 6]; EXFWriter* temp = [dataWriters objectAtIndex:0]; [imageData appendData:temp.tagData]; // add back in any thubnail values here [imageData increaseLengthBy:4]; [imageData appendData:temp.overflowData]; if (self.thumbnailBytes != nil){ Debug(@"Adding %i thumbnail bytes to block",[self.thumbnailBytes length]); [imageData appendData:self.thumbnailBytes]; }else{ Debug(@"No Thumbnail bytes found"); } } else{ Debug(@"No thumbnail tags found"); } [dataWriters release]; Debug(@"Got Final data block of size %i", [imageData length] -initialSize); // write the size back out } @end ================================================ FILE: EXIF/EXFMutableMetaData.h ================================================ /* * EXFMutableMetaData.h * iphoneGeo * * Created by steve woodcock on 23/03/2008. * Copyright 2008 __MyCompanyName__. // Licensed under GPL 2.0 http://www.gnu.org/licenses/gpl-2.0.txt * */ #import "EXFMetaData.h" #import "EXFJFIF.h" /* Mutable interface Category for EXFJFIF */ @interface EXFJFIF () - (void) parseJfif:(CFDataRef*) theJfifData; @property (readwrite, retain) NSString* identifier; @property (readwrite, retain) NSString* version; @property (readwrite, retain) NSData* thumbnail; // primitive attributes @property (readwrite) JFIFUnits units; @property (readwrite) int length; @property (readwrite) int resolutionX; @property (readwrite) int resolutionY; @property (readwrite) int thumbnailX; @property (readwrite) int thumbnailY; @end /* Mutable interface Category for EXFObject */ @interface EXFMetaData () - (void) parseExif:(CFDataRef*) theExifData; - (void) getData: (NSMutableData*) imageData; -(void) setupHandlers; @property (readwrite,retain) NSMutableDictionary* userKeyedHandlers; @property (readwrite,retain) NSMutableDictionary* keyedHandlers; @property (readwrite,retain) EXFTagDefinitionHolder* tagDefinitions; @property (readwrite, retain) NSMutableDictionary* keyedTagValues; @property (readwrite,retain) NSMutableDictionary* keyedThumbnailTagValues; @property (readwrite,retain) NSData* thumbnailBytes; @property (readwrite) int compression; @property (readwrite) int bitsPerPixel; @property (readwrite) int height; @property (readwrite) int width; @property (readwrite) CFIndex byteLength; @property (readwrite) BOOL bigEndianOrder; @property (readwrite) ByteArray* exif_ptr; @end ================================================ FILE: EXIF/EXFTagDefinitionHolder.h ================================================ // // EXFTagDefinition.h // iphone-test // // Created by steve woodcock on 26/03/2008. // Copyright 2008 __MyCompanyName__. // Licensed under GPL 2.0 http://www.gnu.org/licenses/gpl-2.0.txt // #import "EXFConstants.h" @interface EXFTagDefinitionHolder :NSObject { NSMutableDictionary* definitions; } @property (readwrite, retain) NSDictionary* definitions; -(void) addTagDefinition: (EXFTag*) aTagDefinition forKey: (NSNumber*) aTagKey; @end ================================================ FILE: EXIF/EXFTagDefinitionHolder.m ================================================ // // EXFTagDefinition.m // iphone-test // // Created by steve woodcock on 26/03/2008. // Copyright 2008 __MyCompanyName__. // Licensed under GPL 2.0 http://www.gnu.org/licenses/gpl-2.0.txt // /* The Follwoing specifications were used for tag data: http://www.exif.org/Exif2-2.PDF http://ceres.informatik.fh-kl.de/pbw/lehre/20041/foto/resourcen/Dokumentation/Exif/cp3461.pdf */ #import "EXFTagDefinitionHolder.h" #import "EXFMetaData.h" @implementation EXFTagDefinitionHolder: NSObject @synthesize definitions; -(void) addTagDefinition: (EXFTag*) aTagDefinition forKey: (NSNumber*) aTagKey{ // [definitions setObject:aTagDefinition forKey:aTagKey]; } -(void) createTags { NSMutableDictionary* tags = [[NSMutableDictionary alloc] init]; EXFTag* tag = [[EXFTag alloc] initWith: 0x0100 : FMT_ULONG :@"ImageWidth":-1: TRUE:1]; [tags setObject:tag forKey:[NSNumber numberWithInt:0x100]]; [tag release]; tag = [[EXFTag alloc] initWith:0x0101 : FMT_ULONG :@"ImageLength":-1: TRUE:1]; [tags setObject:tag forKey:[NSNumber numberWithInt:0x101]]; [tag release]; tag = [[EXFTag alloc] initWith:0x0102 : FMT_USHORT :@"BitsPerSample":-1: TRUE:3]; [tags setObject:tag forKey:[NSNumber numberWithInt:0x102]]; [tag release]; tag = [[EXFTag alloc] initWith:0x0103 : FMT_USHORT :@"Compression":-1: TRUE:1]; [tags setObject:tag forKey:[NSNumber numberWithInt:0x103]]; [tag release]; tag = [[EXFTag alloc] initWith:0x0106 : FMT_USHORT :@"PhotometricInterpretation":-1: TRUE:1]; [tags setObject:tag forKey:[NSNumber numberWithInt:0x106]]; [tag release]; tag = [[EXFTag alloc] initWith:0x0111 : FMT_ULONG :@"StripOffsets":-1: TRUE:-1]; [tags setObject:tag forKey:[NSNumber numberWithInt:0x111]]; [tag release]; tag = [[EXFTag alloc] initWith:0x0112 : FMT_USHORT :@"Orientation":-1: TRUE:1]; [tags setObject:tag forKey:[NSNumber numberWithInt:0x112]]; [tag release]; tag = [[EXFTag alloc] initWith:0x0115 : FMT_USHORT :@"SamplesPerPixel":-1:TRUE:1]; [tags setObject:tag forKey:[NSNumber numberWithInt:0x115]]; [tag release]; tag = [[EXFTag alloc] initWith:0x0116 : FMT_ULONG :@"RowsPerStrip":-1:TRUE:1]; [tags setObject:tag forKey:[NSNumber numberWithInt:0x116]]; [tag release]; tag = [[EXFTag alloc] initWith:0x0117 : FMT_ULONG :@"StripByteCounts":-1:TRUE:-1]; [tags setObject:tag forKey:[NSNumber numberWithInt:0x117]]; [tag release]; tag = [[EXFTag alloc] initWith:0x010e : FMT_STRING :@"ImageDescription":-1: TRUE:-99]; [tags setObject:tag forKey:[NSNumber numberWithInt:0x010e]]; [tag release]; tag = [[EXFTag alloc] initWith:0x010f : FMT_STRING :@"Make":-1: TRUE:-99]; [tags setObject:tag forKey:[NSNumber numberWithInt:0x010f]]; [tag release]; tag = [[EXFTag alloc] initWith:0x0110 : FMT_STRING :@"Model":-1: TRUE:-99]; [tags setObject:tag forKey:[NSNumber numberWithInt:0x0110]]; [tag release]; tag = [[EXFTag alloc] initWith:0x011a : FMT_URATIONAL :@"XResolution":-1:TRUE:1]; [tags setObject:tag forKey:[NSNumber numberWithInt:0x011a]]; [tag release]; tag = [[EXFTag alloc] initWith:0x011b : FMT_URATIONAL :@"YResolution":-1:TRUE:1]; [tags setObject:tag forKey:[NSNumber numberWithInt:0x011b]]; [tag release]; tag = [[EXFTag alloc] initWith:0x011c : FMT_USHORT :@"PlanarConfiguration":-1:TRUE:1]; [tags setObject:tag forKey:[NSNumber numberWithInt:0x011c]]; [tag release]; tag = [[EXFTag alloc] initWith:0x0128 : FMT_USHORT :@"ResolutionUnit":-1:TRUE:1]; [tags setObject:tag forKey:[NSNumber numberWithInt:0x0128]]; [tag release]; tag = [[EXFTag alloc] initWith:0x0131 : FMT_STRING :@"Software":-1: TRUE:-99]; [tags setObject:tag forKey:[NSNumber numberWithInt:0x0131]]; [tag release]; tag = [[EXFTag alloc] initWith:0x0132 : FMT_STRING :@"DateTime":-1: TRUE:20]; [tags setObject:tag forKey:[NSNumber numberWithInt:0x0132]]; [tag release]; tag = [[EXFTag alloc] initWith:0x013b : FMT_STRING :@"Artist":-1: TRUE:-99]; [tags setObject:tag forKey:[NSNumber numberWithInt:0x013b]]; [tag release]; tag = [[EXFTag alloc] initWith:0x013c : FMT_STRING :@"HostComputer":-1: TRUE:-99]; [tags setObject:tag forKey:[NSNumber numberWithInt:0x013c]]; [tag release]; tag = [[EXFTag alloc] initWith:0x013d : FMT_USHORT :@"Predictor":-1: TRUE:1]; [tags setObject:tag forKey:[NSNumber numberWithInt:0x013d]]; [tag release]; tag = [[EXFTag alloc] initWith:0x013e : FMT_URATIONAL :@"WhitePoint":-1: TRUE:2]; [tags setObject:tag forKey:[NSNumber numberWithInt:0x013e]]; [tag release]; tag = [[EXFTag alloc] initWith:0x013f : FMT_URATIONAL :@"PrimaryChromaticities":-1: TRUE:6]; [tags setObject:tag forKey:[NSNumber numberWithInt:0x013f]]; [tag release]; tag = [[EXFTag alloc] initWith:0x0201 : FMT_ULONG :@"JPEGInterchangeFormat":-1: TRUE:1]; [tags setObject:tag forKey:[NSNumber numberWithInt:0x0201]]; [tag release]; tag = [[EXFTag alloc] initWith:0x0202 : FMT_ULONG :@"JPEGInterchangeFormatLength":-1: TRUE:1]; [tags setObject:tag forKey:[NSNumber numberWithInt:0x0202]]; [tag release]; tag = [[EXFTag alloc] initWith:0x0211 : FMT_URATIONAL :@"YCbCrCoefficients":-1: TRUE:3]; [tags setObject:tag forKey:[NSNumber numberWithInt:0x0211]]; [tag release]; tag = [[EXFTag alloc] initWith:0x0212 : FMT_USHORT :@"YCbCrSubSampling":-1: TRUE:2]; [tags setObject:tag forKey:[NSNumber numberWithInt:0x0212]]; [tag release]; tag = [[EXFTag alloc] initWith:0x0213 : FMT_USHORT :@"YCbCrPositioning":-1: TRUE:1]; [tags setObject:tag forKey:[NSNumber numberWithInt:0x0213]]; [tag release]; tag = [[EXFTag alloc] initWith:0x0214 : FMT_URATIONAL :@"ReferenceBlackWhite":-1: TRUE:6]; [tags setObject:tag forKey:[NSNumber numberWithInt:0x0214]]; [tag release]; tag = [[EXFTag alloc] initWith:0x8298 : FMT_STRING :@"Copyright":-1: TRUE:-99]; [tags setObject:tag forKey:[NSNumber numberWithInt:0x08298]]; [tag release]; // Exif ID Tags tag = [[EXFTag alloc] initWith:0x829a : FMT_URATIONAL :@"ExposureTime":0x8769:TRUE:1]; [tags setObject:tag forKey:[NSNumber numberWithInt:0x829a]]; [tag release]; tag = [[EXFTag alloc] initWith:0x829d : FMT_URATIONAL :@"FNumber":0x8769:TRUE:1]; [tags setObject:tag forKey:[NSNumber numberWithInt:0x829d]]; [tag release]; tag = [[EXFTag alloc] initWith:0x8822 : FMT_USHORT :@"ExposureProgram":0x8769:TRUE:-99]; [tags setObject:tag forKey:[NSNumber numberWithInt:0x8822]]; [tag release]; tag = [[EXFTag alloc] initWith:0x8824 : FMT_STRING :@"SpectralSensitivity":0x8769:TRUE:-99]; [tags setObject:tag forKey:[NSNumber numberWithInt:0x8824]]; [tag release]; tag = [[EXFTag alloc] initWith:0x8827 : FMT_STRING :@"ISOSpeedratings":0x8769:TRUE:-99]; [tags setObject:tag forKey:[NSNumber numberWithInt:0x8827]]; [tag release]; tag = [[EXFTag alloc] initWith:0x9000 : FMT_UNDEFINED :@"ExifVersion":0x8769:TRUE:4]; [tags setObject:tag forKey:[NSNumber numberWithInt:0x9000]]; [tag release]; tag = [[EXFTag alloc] initWith:0x9003 : FMT_STRING :@"DateTimeOriginal":0x8769:TRUE:20]; [tags setObject:tag forKey:[NSNumber numberWithInt:0x9003]]; [tag release]; tag = [[EXFTag alloc] initWith:0x9004 : FMT_STRING :@"DateTimeDigitized":0x8769:TRUE:20]; [tags setObject:tag forKey:[NSNumber numberWithInt:0x9004]]; [tag release]; tag = [[EXFTag alloc] initWith:0x9102 : FMT_URATIONAL :@"CompressedBitsPerPixel":0x8769:TRUE:1]; [tags setObject:tag forKey:[NSNumber numberWithInt:0x9102]]; [tag release]; tag = [[EXFTag alloc] initWith:0x9201 : FMT_SRATIONAL :@"ShutterSpeedValue":0x8769:TRUE:1]; [tags setObject:tag forKey:[NSNumber numberWithInt:0x9201]]; [tag release]; tag = [[EXFTag alloc] initWith:0x9202 : FMT_URATIONAL :@"ApertureValue":0x8769:TRUE:1]; [tags setObject:tag forKey:[NSNumber numberWithInt:0x9202]]; [tag release]; tag = [[EXFTag alloc] initWith:0x9203 : FMT_SRATIONAL :@"BrightnessValue":0x8769:TRUE:1]; [tags setObject:tag forKey:[NSNumber numberWithInt:0x9203]]; [tag release]; tag = [[EXFTag alloc] initWith:0x9204 : FMT_SRATIONAL :@"ExposureBiasValue":0x8769:TRUE:1]; [tags setObject:tag forKey:[NSNumber numberWithInt:0x9204]]; [tag release]; tag = [[EXFTag alloc] initWith:0x9205 : FMT_URATIONAL :@"MaxApertureRatioValue":0x8769:TRUE:1]; [tags setObject:tag forKey:[NSNumber numberWithInt:0x9205]]; [tag release]; tag = [[EXFTag alloc] initWith:0x9206 : FMT_URATIONAL :@"SubjectDistance":0x8769:TRUE:1]; [tags setObject:tag forKey:[NSNumber numberWithInt:0x9206]]; [tag release]; tag = [[EXFTag alloc] initWith:0x9207 : FMT_USHORT :@"MeteringMode":0x8769:TRUE:1]; [tags setObject:tag forKey:[NSNumber numberWithInt:0x9207]]; [tag release]; tag = [[EXFTag alloc] initWith:0x9208 : FMT_USHORT :@"LightSource":0x8769:TRUE:1]; [tags setObject:tag forKey:[NSNumber numberWithInt:0x9208]]; [tag release]; tag = [[EXFTag alloc] initWith:0x9209 : FMT_USHORT :@"Flash":0x8769:TRUE:1]; [tags setObject:tag forKey:[NSNumber numberWithInt:0x9209]]; [tag release]; tag = [[EXFTag alloc] initWith:0x920a : FMT_URATIONAL :@"FocalLength":0x8769:TRUE:1]; [tags setObject:tag forKey:[NSNumber numberWithInt:0x920a]]; [tag release]; tag = [[EXFTag alloc] initWith:0x927c : FMT_UNDEFINED :@"MakerNote":0x8769:TRUE:-99]; [tags setObject:tag forKey:[NSNumber numberWithInt:0x927c]]; [tag release]; tag = [[EXFTag alloc] initWith:0x9286 : FMT_UNDEFINED :@"UserComment":0x8769:TRUE:-99]; [tags setObject:tag forKey:[NSNumber numberWithInt:0x9286]]; [tag release]; tag = [[EXFTag alloc] initWith:0x9290 : FMT_STRING :@"SubSecTime":0x8769:TRUE:-99]; [tags setObject:tag forKey:[NSNumber numberWithInt:0x9290]]; [tag release]; tag = [[EXFTag alloc] initWith:0x9291 : FMT_STRING :@"SubSecTimeOriginal":0x8769:TRUE:-99]; [tags setObject:tag forKey:[NSNumber numberWithInt:0x9291]]; [tag release]; tag = [[EXFTag alloc] initWith:0x9292 : FMT_STRING :@"SubSecTimeDigitized":0x8769:TRUE:-99]; [tags setObject:tag forKey:[NSNumber numberWithInt:0x9292]]; [tag release]; tag = [[EXFTag alloc] initWith:0xa300 : FMT_UNDEFINED :@"FileSource":0x8769:TRUE:1]; [tags setObject:tag forKey:[NSNumber numberWithInt:0xa300]]; [tag release]; tag = [[EXFTag alloc] initWith:0xa301 : FMT_UNDEFINED :@"SceneType":0x8769:TRUE:1]; [tags setObject:tag forKey:[NSNumber numberWithInt:0xa301]]; [tag release]; tag = [[EXFTag alloc] initWith:0xa302 : FMT_UNDEFINED :@"CFAPattern":0x8769:TRUE:-99]; [tags setObject:tag forKey:[NSNumber numberWithInt:0xa302]]; [tag release]; tag = [[EXFTag alloc] initWith:0xa000 : FMT_UNDEFINED :@"FlashpixVersion":0x8769:TRUE:4]; [tags setObject:tag forKey:[NSNumber numberWithInt:0xa000]]; [tag release]; tag = [[EXFTag alloc] initWith:0xa001 : FMT_USHORT :@"ColorSpace":0x8769:TRUE:1]; [tags setObject:tag forKey:[NSNumber numberWithInt:0xa001]]; [tag release]; tag = [[EXFTag alloc] initWith:0xa002 : FMT_ULONG :@"PixelXDimension":0x8769:TRUE:1]; [tags setObject:tag forKey:[NSNumber numberWithInt:0xa002]]; [tag release]; tag = [[EXFTag alloc] initWith:0xa003 : FMT_ULONG :@"PixelYDimension":0x8769:TRUE:1]; [tags setObject:tag forKey:[NSNumber numberWithInt:0xa003]]; [tag release]; tag = [[EXFTag alloc] initWith:0xa20e : FMT_URATIONAL :@"FocalPlaneXResolution":0x8769:TRUE:1]; [tags setObject:tag forKey:[NSNumber numberWithInt:0xa20e]]; [tag release]; tag = [[EXFTag alloc] initWith:0xa20f : FMT_URATIONAL :@"FocalPlaneYResolution":0x8769:TRUE:1]; [tags setObject:tag forKey:[NSNumber numberWithInt:0xa20f]]; [tag release]; tag = [[EXFTag alloc] initWith:0xa210 : FMT_USHORT :@"FocalPlaneResolutionUnit":0x8769:TRUE:1]; [tags setObject:tag forKey:[NSNumber numberWithInt:0xa210]]; [tag release]; tag = [[EXFTag alloc] initWith:0xa214 : FMT_USHORT :@"SubjectLocation":0x8769:TRUE:2]; [tags setObject:tag forKey:[NSNumber numberWithInt:0xa214]]; [tag release]; tag = [[EXFTag alloc] initWith:0xa215 : FMT_URATIONAL :@"ExposureTime":0x8769:TRUE:1]; [tags setObject:tag forKey:[NSNumber numberWithInt:0xa215]]; [tag release]; tag = [[EXFTag alloc] initWith:0xa217 : FMT_USHORT :@"SensingMethod":0x8769:TRUE:1]; [tags setObject:tag forKey:[NSNumber numberWithInt:0xa217]]; [tag release]; tag = [[EXFTag alloc] initWith:0xa300 : FMT_UNDEFINED :@"FileSource":0x8769:TRUE:1]; [tags setObject:tag forKey:[NSNumber numberWithInt:0xa300]]; [tag release]; tag = [[EXFTag alloc] initWith:0xa302 : FMT_UNDEFINED :@"CFAPattern":0x8769:TRUE:-99]; [tags setObject:tag forKey:[NSNumber numberWithInt:0xa302]]; [tag release]; tag = [[EXFTag alloc] initWith:0xa301 : FMT_UNDEFINED :@"SceneType":0x8769:TRUE:1]; [tags setObject:tag forKey:[NSNumber numberWithInt:0xa301]]; [tag release]; tag = [[EXFTag alloc] initWith:0xa401 : FMT_USHORT :@"CustomRendered":0x8769:TRUE:1]; [tags setObject:tag forKey:[NSNumber numberWithInt:0xa401]]; [tag release]; tag = [[EXFTag alloc] initWith:0xa402 : FMT_USHORT :@"ExposureMode":0x8769:TRUE:1]; [tags setObject:tag forKey:[NSNumber numberWithInt:0xa402]]; [tag release]; tag = [[EXFTag alloc] initWith:0xa403 : FMT_USHORT :@"WhiteBalance":0x8769:TRUE:1]; [tags setObject:tag forKey:[NSNumber numberWithInt:0xa403]]; [tag release]; tag = [[EXFTag alloc] initWith:0xa404 : FMT_URATIONAL :@"DigitalZoomRatio":0x8769:TRUE:1]; [tags setObject:tag forKey:[NSNumber numberWithInt:0xa404]]; [tag release]; tag = [[EXFTag alloc] initWith:0xa405 : FMT_USHORT :@"FocalLengthIn35mmFilm":0x8769:TRUE:1]; [tags setObject:tag forKey:[NSNumber numberWithInt:0xa405]]; [tag release]; tag = [[EXFTag alloc] initWith:0xa406 : FMT_USHORT :@"SceneCaptureType":0x8769:TRUE:1]; [tags setObject:tag forKey:[NSNumber numberWithInt:0xa406]]; [tag release]; tag = [[EXFTag alloc] initWith:0xa407 : FMT_URATIONAL :@"GainControl":0x8769:TRUE:1]; [tags setObject:tag forKey:[NSNumber numberWithInt:0xa407]]; [tag release]; tag = [[EXFTag alloc] initWith:0xa408 : FMT_USHORT :@"Contrast":0x8769:TRUE:1]; [tags setObject:tag forKey:[NSNumber numberWithInt:0xa408]]; [tag release]; tag = [[EXFTag alloc] initWith:0xa409 : FMT_USHORT :@"Saturation":0x8769:TRUE:1]; [tags setObject:tag forKey:[NSNumber numberWithInt:0xa409]]; [tag release]; tag = [[EXFTag alloc] initWith:0xa40a : FMT_USHORT :@"Sharpness":0x8769:TRUE:1]; [tags setObject:tag forKey:[NSNumber numberWithInt:0xa40a]]; [tag release]; tag = [[EXFTag alloc] initWith:0xa40b : FMT_UNDEFINED :@"DeviceSettingDescription":0x8769:TRUE:-99]; [tags setObject:tag forKey:[NSNumber numberWithInt:0xa40b]]; [tag release]; tag = [[EXFTag alloc] initWith:0xa40c : FMT_USHORT :@"SubjectDistanceRange":0x8769:TRUE:1]; [tags setObject:tag forKey:[NSNumber numberWithInt:0xa40c]]; [tag release]; tag = [[EXFTag alloc] initWith:0xa500 : FMT_URATIONAL :@"Gamma":0x8769:FALSE:1]; [tags setObject:tag forKey:[NSNumber numberWithInt:0xa500]]; [tag release]; // gps tags tag = [[EXFTag alloc] initWith:0x0000 : FMT_BYTE :@"GPSVersion":0x8825:TRUE:4]; [tags setObject:tag forKey:[NSNumber numberWithInt:0x0000]]; [tag release]; tag = [[EXFTag alloc] initWith:0x0001 : FMT_STRING :@"GPSLatitudeRef":0x8825: TRUE:2]; [tags setObject:tag forKey:[NSNumber numberWithInt:0x0001]]; [tag release]; tag = [[EXFTag alloc] initWith:0x0002 : FMT_URATIONAL :@"GPSLatitude":0x8825: TRUE:3]; [tags setObject:tag forKey:[NSNumber numberWithInt:0x0002]]; [tag release]; tag = [[EXFTag alloc] initWith:0x0003 : FMT_STRING :@"GPSLongitudeRef":0x8825: TRUE:2]; [tags setObject:tag forKey:[NSNumber numberWithInt:0x0003]]; [tag release]; tag = [[EXFTag alloc] initWith:0x0004 : FMT_URATIONAL :@"GPSLongitude":0x8825: TRUE:3]; [tags setObject:tag forKey:[NSNumber numberWithInt:0x0004]]; [tag release]; tag = [[EXFTag alloc] initWith:0x0005 : FMT_BYTE :@"GPSAltitudeRef":0x8825: TRUE:1]; [tags setObject:tag forKey:[NSNumber numberWithInt:0x0005]]; [tag release]; tag = [[EXFTag alloc] initWith:0x0006 : FMT_URATIONAL :@"GPSAltitude":0x8825: TRUE:1]; [tags setObject:tag forKey:[NSNumber numberWithInt:0x0006]]; [tag release]; tag = [[EXFTag alloc] initWith:0x0007 : FMT_URATIONAL :@"GPSTimeStamp":0x8825: TRUE:3]; [tags setObject:tag forKey:[NSNumber numberWithInt:0x0007]]; [tag release]; tag = [[EXFTag alloc] initWith:0x0008 : FMT_STRING :@"GPSSatellites":0x8825: TRUE:-99]; [tags setObject:tag forKey:[NSNumber numberWithInt:0x0008]]; [tag release]; tag = [[EXFTag alloc] initWith:0x0009 : FMT_STRING :@"GPSStatus":0x8825: TRUE:2]; [tags setObject:tag forKey:[NSNumber numberWithInt:0x0009]]; [tag release]; tag = [[EXFTag alloc] initWith:0x000a : FMT_STRING :@"GPSMeasureMode":0x8825: TRUE:2]; [tags setObject:tag forKey:[NSNumber numberWithInt:0x000a]]; [tag release]; tag = [[EXFTag alloc] initWith:0x000b : FMT_URATIONAL :@"GPSDOP":0x8825: TRUE:1]; [tags setObject:tag forKey:[NSNumber numberWithInt:0x000b]]; [tag release]; tag = [[EXFTag alloc] initWith:0x000c : FMT_STRING :@"GPSSpeedRef":0x8825: TRUE:2]; [tags setObject:tag forKey:[NSNumber numberWithInt:0x000c]]; [tag release]; tag = [[EXFTag alloc] initWith:0x000d : FMT_URATIONAL :@"GPSSpeed":0x8825: TRUE:1]; [tags setObject:tag forKey:[NSNumber numberWithInt:0x000d]]; [tag release]; tag = [[EXFTag alloc] initWith:0x000e : FMT_STRING :@"GPSTrackRef":0x8825: TRUE:2]; [tags setObject:tag forKey:[NSNumber numberWithInt:0x000e]]; [tag release]; tag = [[EXFTag alloc] initWith:0x000f : FMT_URATIONAL :@"GPSTrack":0x8825: TRUE:1]; [tags setObject:tag forKey:[NSNumber numberWithInt:0x000f]]; [tag release]; tag = [[EXFTag alloc] initWith:0x0010 : FMT_STRING :@"GPSImgDirectionRef":0x8825: TRUE:2]; [tags setObject:tag forKey:[NSNumber numberWithInt:0x0010]]; [tag release]; tag = [[EXFTag alloc] initWith:0x0011 : FMT_URATIONAL :@"GPSImgDirection":0x8825: TRUE:1]; [tags setObject:tag forKey:[NSNumber numberWithInt:0x0011]]; [tag release]; tag = [[EXFTag alloc] initWith:0x0012 : FMT_STRING :@"GPSMapDatum":0x8825: TRUE:-99]; [tags setObject:tag forKey:[NSNumber numberWithInt:0x0012]]; [tag release]; tag = [[EXFTag alloc] initWith:0x0013 : FMT_STRING :@"GPSDestLatitudeRef":0x8825: TRUE:2]; [tags setObject:tag forKey:[NSNumber numberWithInt:0x0013]]; [tag release]; tag = [[EXFTag alloc] initWith:0x0014 : FMT_URATIONAL :@"GPSDestLatitude":0x8825: TRUE:3]; [tags setObject:tag forKey:[NSNumber numberWithInt:0x0014]]; [tag release]; tag = [[EXFTag alloc] initWith:0x0015 : FMT_STRING :@"GPSDestLongitudeRef":0x8825: TRUE:2]; [tags setObject:tag forKey:[NSNumber numberWithInt:0x0015]]; [tag release]; tag = [[EXFTag alloc] initWith:0x0016 : FMT_URATIONAL :@"GPSDestLongitude":0x8825: TRUE:3]; [tags setObject:tag forKey:[NSNumber numberWithInt:0x0016]]; [tag release]; tag = [[EXFTag alloc] initWith:0x0017 : FMT_STRING :@"GPSDestBearingRef":0x8825: TRUE:2]; [tags setObject:tag forKey:[NSNumber numberWithInt:0x0017]]; [tag release]; tag = [[EXFTag alloc] initWith:0x0018 : FMT_URATIONAL :@"GPSDestBearing":0x8825: TRUE:1]; [tags setObject:tag forKey:[NSNumber numberWithInt:0x0018]]; [tag release]; tag = [[EXFTag alloc] initWith:0x0019 : FMT_STRING :@"GPSDestDistanceRef":0x8825: TRUE:2]; [tags setObject:tag forKey:[NSNumber numberWithInt:0x0019]]; [tag release]; tag = [[EXFTag alloc] initWith:0x001a : FMT_URATIONAL :@"GPSDestDistance":0x8825: TRUE:1]; [tags setObject:tag forKey:[NSNumber numberWithInt:0x001a]]; [tag release]; self.definitions = tags; [tags release]; } -(id) init { if (self = [super init]) { [self createTags]; } return self; } -(void) dealloc{ self.definitions =nil; [super dealloc]; } @end ================================================ FILE: EXIF/EXFUtils.h ================================================ /* * EXFUtils.h * iphoneGeo * * Created by steve woodcock on 23/03/2008. * Copyright 2008. // Licensed under GPL 2.0 http://www.gnu.org/licenses/gpl-2.0.txt * * Static helper methods to deal with byte array read/write and big endian/little endian ordering */ #import "EXFConstants.h" @interface EXFUtils : NSObject { } +(UInt32) read4Bytes:(UInt8**) bytePtr: (BOOL) bigEndianOrder; +(SInt32) read4SignedBytes:(UInt8**) bytePtr: (BOOL) bigEndianOrder; +(UInt16) read2Bytes:(UInt8**) bytePtr: (BOOL) bigEndianOrder; +(SInt16) read2SignedBytes:(UInt8**) bytePtr: (BOOL) bigEndianOrder; +(void) write1Byte:(UInt8**) bytePtr: (id) value:(BOOL) bigEndianOrder; +(void) write1SignedByte:(UInt8**) bytePtr: (id) value:(BOOL) bigEndianOrder; +(void) write4Bytes:(UInt8**) bytePtr: (id) value: (BOOL) bigEndianOrder; +(void) write4SignedBytes:(UInt8**) bytePtr: (id) value: (BOOL) bigEndianOrder; +(void) write2Bytes:(UInt8**) bytePtr: (id) value:(BOOL) bigEndianOrder; +(void) write2SignedBytes:(UInt8**) bytePtr: (id) value: (BOOL) bigEndianOrder; +(NSString*)newStringFromBuffer:(UInt8**) ptr: (UInt32) byteCount: (NSStringEncoding) encoding; +(void) appendRationalToData:( NSMutableData*) target: (NSNumber*) rational: (BOOL) bigEndianOrder; +(void) appendFractionToData:( NSMutableData*) target: (EXFraction*) fraction: (BOOL) bigEndianOrder; +(void) convertRationalToFraction: (long**) numDenumArray: (NSNumber*) rational; @end ================================================ FILE: EXIF/EXFUtils.m ================================================ // // EXFUtils.m // iphone-test // // Created by steve woodcock on 30/03/2008. // Copyright 2008 __MyCompanyName__. // Licensed under GPL 2.0 http://www.gnu.org/licenses/gpl-2.0.txt // #import "EXFUtils.h" #import "EXFConstants.h" @implementation EXFUtils /* Start of utility helper methods */ +(UInt32) read4Bytes:(UInt8**) bytePtr: (BOOL) bigEndianOrder { UInt8* ptr = *bytePtr; UInt32 val =0; if (bigEndianOrder) val= ((ptr[0] << 24) | (ptr[1] << 16) | (ptr[2] << 8) | ptr[3]); else val = ((ptr[3] << 24) | (ptr[2] << 16) | (ptr[1] << 8) | ptr[0]); return val; } +(SInt32) read4SignedBytes:(UInt8**) bytePtr: (BOOL) bigEndianOrder { UInt8* ptr = *bytePtr; SInt32 val =0; if (bigEndianOrder) val= ((ptr[0] << 24) | (ptr[1] << 16) | (ptr[2] << 8) | ptr[3]); else val = ((ptr[3] << 24) | (ptr[2] << 16) | (ptr[1] << 8) | ptr[0]); return val; } +(UInt16) read2Bytes:(UInt8**) bytePtr: (BOOL) bigEndianOrder { UInt8* ptr = *bytePtr; UInt16 val =0; if (bigEndianOrder){ val = ((ptr[0] << 8) | ptr[1]); }else{ val =((ptr[1] << 8) | ptr[0]); } return val; } +(SInt16) read2SignedBytes:(UInt8**) bytePtr: (BOOL) bigEndianOrder { UInt8* ptr = *bytePtr; SInt16 val =0; if (bigEndianOrder){ val = ((ptr[0] << 8) | ptr[1]); }else{ val =((ptr[1] << 8) | ptr[0]); } return val; } +(void) write4Bytes:(UInt8**) bytePtr: (id) value: (BOOL) bigEndianOrder{ UInt32 val = [((NSNumber*)value) unsignedLongValue]; UInt8* ptr = *bytePtr; if (bigEndianOrder){ ptr[0] = (UInt8) (val >> 24); ptr[1] = (UInt8) ( val >> 16); ptr[2] = (UInt8) (val >> 8); ptr[3] = (UInt8) (val); } else{ ptr[3] = (UInt8) (val >> 24); ptr[2] = (UInt8) (val >> 16); ptr[1] = (UInt8) (val >> 8); ptr[0] = (UInt8) (val ); } } +(void) write4SignedBytes:(UInt8**) bytePtr: (id) value: (BOOL) bigEndianOrder{ SInt32 val = [((NSNumber*)value) longValue]; UInt8* ptr = *bytePtr; if (bigEndianOrder){ ptr[0] = (UInt8) (val >> 24); ptr[1] = (UInt8) (val >> 16); ptr[2] = (UInt8) (val >> 8); ptr[3] = (UInt8) (val & 0xff); } else{ ptr[3] = (UInt8) (val >> 24); ptr[2] = (UInt8) (val >> 16); ptr[1] = (UInt8) (val >> 8); ptr[0] = (UInt8) (val & 0xff); } } +(void) write2Bytes:(UInt8**) bytePtr: (id) value:(BOOL) bigEndianOrder{ UInt16 val = [((NSNumber*)value) unsignedIntValue]; UInt8* ptr = *bytePtr; if (bigEndianOrder){ ptr[0] = (UInt8) (val >> 8); ptr[1] = (UInt8) (val & 0xff); ptr[2] = ptr[3] =0; } else{ ptr[1] = (UInt8) (val >> 8); ptr[0] = (UInt8) (val & 0xff); ptr[2] = ptr[3] =0; } } +(void) write1Byte:(UInt8**) bytePtr: (id) value:(BOOL) bigEndianOrder{ UInt16 val = [((NSNumber*)value) unsignedCharValue]; UInt8* ptr = *bytePtr; ptr[0] = (UInt8) (val & 0xff); ptr[1] = ptr[2] = ptr[3] =0; } +(void) write1SignedByte:(UInt8**) bytePtr: (id) value:(BOOL) bigEndianOrder{ UInt16 val = [((NSNumber*)value) intValue]; UInt8* ptr = *bytePtr; ptr[0] = (UInt8) (val & 0xff); ptr[1] = ptr[2] = ptr[3] =0; } +(void) write2SignedBytes:(UInt8**) bytePtr: (id) value: (BOOL) bigEndianOrder{ SInt16 val = [((NSNumber*)value) intValue]; UInt8* ptr = *bytePtr; if (bigEndianOrder){ ptr[0] = (UInt8) (val >> 8); ptr[1] = (UInt8) (val & 0xff); ptr[2] = ptr[3] =0; } else{ ptr[1] = (UInt8) (val >> 8); ptr[0] = (UInt8) (val & 0xff); ptr[2] = ptr[3] =0; } } +(NSString*)newStringFromBuffer:(UInt8**) ptr: (UInt32) byteCount: (NSStringEncoding) encoding{ NSString* result = [[NSString alloc] initWithBytes:*ptr length:byteCount encoding:encoding]; // Debug(@"Created string %@", result); return result; } +(void) appendRationalToData:( NSMutableData*) target: (NSNumber*) rational: (BOOL) bigEndianOrder { UInt8* bytes[4]; UInt8* bytePtr = (UInt8*)bytes; long temp[2] = {0.0L, 0.0L}; long* ptr = temp; [EXFUtils convertRationalToFraction:&ptr :rational]; [EXFUtils write4Bytes:&bytePtr :[NSNumber numberWithLong:temp[0]] :bigEndianOrder]; [target appendBytes:bytePtr length:4]; [EXFUtils write4Bytes:&bytePtr :[NSNumber numberWithLong:temp[1]] :bigEndianOrder]; [target appendBytes:bytePtr length:4]; } +(void) appendFractionToData:( NSMutableData*) target: (EXFraction*) fraction: (BOOL) bigEndianOrder { UInt8* bytes[4]; UInt8* bytePtr = (UInt8*)bytes; [EXFUtils write4Bytes:&bytePtr :[NSNumber numberWithLong:fraction.numerator] :bigEndianOrder]; [target appendBytes:bytePtr length:4]; [EXFUtils write4Bytes:&bytePtr :[NSNumber numberWithLong:fraction.denominator] :bigEndianOrder]; [target appendBytes:bytePtr length:4]; } +(long) ofr_gcd_euclid: (long) n: (long) m { /* Finds greatest divisor, d, of n and m: n%d==0, m%d==0 Restate that as: n=n'*d, m=m'*d for some n', m',d; find d Note that if you have any numbers q,r such that q*m+r=n, then q*m+r=n'd --> r=(n'-q*m')d */ if (n < m) { long t = n; n = m; m = t; } for(;;) { // assert (n >= m); long r = n % m; if (r == 0) return m; /* n is a multiple of m */ n = m; m = r; } } +(void) convertRationalToFraction: (long**) numDenumArray: (NSNumber*) rational{ // see how many digits there are double originalNumber = [rational doubleValue]; double number = originalNumber; BOOL negative = FALSE; long long den =0; long long num =0; long* ptr = *numDenumArray; long long count =1; if (number <0){ negative = TRUE; } // we should now have number / thousands // now work out gcd; if (number == 0) { // set denominator to 1 to prevent divide by 0 issues den =1; } else if (number ==1){ // set all to 1 den= num = 1; } else{ // count the number of digits while (number != ((long long)number)){ number *=10; count*=10; // overflow - restrict to 9 decimal places if (number <0 || count >=10000000){ count =1000000; number = (long long)(originalNumber * count); break; } } long gcd = [EXFUtils ofr_gcd_euclid: number: count]; num = number/gcd; den = count/gcd; } if (negative){ num = abs(num); } ptr[0] = num; ptr[1] = den; } @end ================================================ FILE: FMDB/FMDatabase.h ================================================ #import #import "sqlite3.h" #import "FMResultSet.h" @interface FMDatabase : NSObject { sqlite3* db; NSString* databasePath; BOOL logsErrors; BOOL crashOnErrors; BOOL inUse; BOOL inTransaction; BOOL traceExecution; BOOL checkedOut; int busyRetryTimeout; BOOL shouldCacheStatements; NSMutableDictionary *cachedStatements; } + (id)databaseWithPath:(NSString*)inPath; - (id)initWithPath:(NSString*)inPath; - (BOOL)open; #if SQLITE_VERSION_NUMBER >= 3005000 - (BOOL)openWithFlags:(int)flags; #endif - (BOOL)close; - (BOOL)goodConnection; - (void)clearCachedStatements; // encryption methods. You need to have purchased the sqlite encryption extensions for these to work. - (BOOL)setKey:(NSString*)key; - (BOOL)rekey:(NSString*)key; - (NSString *)databasePath; - (NSString*)lastErrorMessage; - (int)lastErrorCode; - (BOOL)hadError; - (sqlite_int64)lastInsertRowId; - (sqlite3*)sqliteHandle; - (BOOL)executeUpdate:(NSString*)sql, ...; - (BOOL)executeUpdate:(NSString*)sql withArgumentsInArray:(NSArray *)arguments; - (id)executeQuery:(NSString *)sql withArgumentsInArray:(NSArray*)arrayArgs orVAList:(va_list)args; // you shouldn't ever need to call this. use the previous two instead. - (id)executeQuery:(NSString*)sql, ...; - (id)executeQuery:(NSString *)sql withArgumentsInArray:(NSArray *)arguments; - (BOOL)executeUpdate:(NSString*)sql withArgumentsInArray:(NSArray*)arrayArgs orVAList:(va_list)args; // you shouldn't ever need to call this. use the previous two instead. - (BOOL)rollback; - (BOOL)commit; - (BOOL)beginTransaction; - (BOOL)beginDeferredTransaction; - (BOOL)logsErrors; - (void)setLogsErrors:(BOOL)flag; - (BOOL)crashOnErrors; - (void)setCrashOnErrors:(BOOL)flag; - (BOOL)inUse; - (void)setInUse:(BOOL)value; - (BOOL)inTransaction; - (void)setInTransaction:(BOOL)flag; - (BOOL)traceExecution; - (void)setTraceExecution:(BOOL)flag; - (BOOL)checkedOut; - (void)setCheckedOut:(BOOL)flag; - (int)busyRetryTimeout; - (void)setBusyRetryTimeout:(int)newBusyRetryTimeout; - (BOOL)shouldCacheStatements; - (void)setShouldCacheStatements:(BOOL)value; - (NSMutableDictionary *)cachedStatements; - (void)setCachedStatements:(NSMutableDictionary *)value; + (NSString*)sqliteLibVersion; - (int)changes; @end @interface FMStatement : NSObject { sqlite3_stmt *statement; NSString *query; long useCount; } - (void)close; - (void)reset; - (sqlite3_stmt *)statement; - (void)setStatement:(sqlite3_stmt *)value; - (NSString *)query; - (void)setQuery:(NSString *)value; - (long)useCount; - (void)setUseCount:(long)value; @end ================================================ FILE: FMDB/FMDatabase.m ================================================ #import "FMDatabase.h" #import "unistd.h" @implementation FMDatabase + (id)databaseWithPath:(NSString*)aPath { return [[[self alloc] initWithPath:aPath] autorelease]; } - (id)initWithPath:(NSString*)aPath { self = [super init]; if (self) { databasePath = [aPath copy]; db = 0x00; logsErrors = 0x00; crashOnErrors = 0x00; busyRetryTimeout = 0x00; } return self; } - (void)dealloc { [self close]; [cachedStatements release]; [databasePath release]; [super dealloc]; } + (NSString*)sqliteLibVersion { return [NSString stringWithFormat:@"%s", sqlite3_libversion()]; } - (NSString *)databasePath { return databasePath; } - (sqlite3*)sqliteHandle { return db; } - (BOOL)open { int err = sqlite3_open([databasePath fileSystemRepresentation], &db ); if(err != SQLITE_OK) { NSLog(@"error opening!: %d", err); return NO; } return YES; } #if SQLITE_VERSION_NUMBER >= 3005000 - (BOOL)openWithFlags:(int)flags { int err = sqlite3_open_v2([databasePath fileSystemRepresentation], &db, flags, NULL /* Name of VFS module to use */); if(err != SQLITE_OK) { NSLog(@"error opening!: %d", err); return NO; } return YES; } #endif - (BOOL)close { [self clearCachedStatements]; if (!db) { return YES; } int rc; BOOL retry; int numberOfRetries = 0; do { retry = NO; rc = sqlite3_close(db); if (SQLITE_BUSY == rc) { retry = YES; usleep(20); if (busyRetryTimeout && (numberOfRetries++ > busyRetryTimeout)) { NSLog(@"%s:%d", __FUNCTION__, __LINE__); NSLog(@"Database busy, unable to close"); return NO; } } else if (SQLITE_OK != rc) { NSLog(@"error closing!: %d", rc); } } while (retry); db = nil; return YES; } - (void)clearCachedStatements { NSEnumerator *e = [cachedStatements objectEnumerator]; FMStatement *cachedStmt; while ((cachedStmt = [e nextObject])) { [cachedStmt close]; } [cachedStatements removeAllObjects]; } - (FMStatement*)cachedStatementForQuery:(NSString*)query { return [cachedStatements objectForKey:query]; } - (void)setCachedStatement:(FMStatement*)statement forQuery:(NSString*)query { //NSLog(@"setting query: %@", query); query = [query copy]; // in case we got handed in a mutable string... [statement setQuery:query]; [cachedStatements setObject:statement forKey:query]; [query release]; } - (BOOL)rekey:(NSString*)key { #ifdef SQLITE_HAS_CODEC if (!key) { return NO; } int rc = sqlite3_rekey(db, [key UTF8String], strlen([key UTF8String])); if (rc != SQLITE_OK) { NSLog(@"error on rekey: %d", rc); NSLog(@"%@", [self lastErrorMessage]); } return (rc == SQLITE_OK); #else return NO; #endif } - (BOOL)setKey:(NSString*)key { #ifdef SQLITE_HAS_CODEC if (!key) { return NO; } int rc = sqlite3_key(db, [key UTF8String], strlen([key UTF8String])); return (rc == SQLITE_OK); #else return NO; #endif } - (BOOL)goodConnection { if (!db) { return NO; } FMResultSet *rs = [self executeQuery:@"select name from sqlite_master where type='table'"]; if (rs) { [rs close]; return YES; } return NO; } - (void)compainAboutInUse { NSLog(@"The FMDatabase %@ is currently in use.", self); if (crashOnErrors) { NSAssert1(false, @"The FMDatabase %@ is currently in use.", self); } } - (NSString*)lastErrorMessage { return [NSString stringWithUTF8String:sqlite3_errmsg(db)]; } - (BOOL)hadError { int lastErrCode = [self lastErrorCode]; return (lastErrCode > SQLITE_OK && lastErrCode < SQLITE_ROW); } - (int)lastErrorCode { return sqlite3_errcode(db); } - (sqlite_int64)lastInsertRowId { if (inUse) { [self compainAboutInUse]; return NO; } [self setInUse:YES]; sqlite_int64 ret = sqlite3_last_insert_rowid(db); [self setInUse:NO]; return ret; } - (void)bindObject:(id)obj toColumn:(int)idx inStatement:(sqlite3_stmt*)pStmt; { if ((!obj) || ((NSNull *)obj == [NSNull null])) { sqlite3_bind_null(pStmt, idx); } // FIXME - someday check the return codes on these binds. else if ([obj isKindOfClass:[NSData class]]) { sqlite3_bind_blob(pStmt, idx, [obj bytes], (int)[obj length], SQLITE_STATIC); } else if ([obj isKindOfClass:[NSDate class]]) { sqlite3_bind_double(pStmt, idx, [obj timeIntervalSince1970]); } else if ([obj isKindOfClass:[NSNumber class]]) { if (strcmp([obj objCType], @encode(BOOL)) == 0) { sqlite3_bind_int(pStmt, idx, ([obj boolValue] ? 1 : 0)); } else if (strcmp([obj objCType], @encode(int)) == 0) { sqlite3_bind_int64(pStmt, idx, [obj longValue]); } else if (strcmp([obj objCType], @encode(long)) == 0) { sqlite3_bind_int64(pStmt, idx, [obj longValue]); } else if (strcmp([obj objCType], @encode(long long)) == 0) { sqlite3_bind_int64(pStmt, idx, [obj longLongValue]); } else if (strcmp([obj objCType], @encode(float)) == 0) { sqlite3_bind_double(pStmt, idx, [obj floatValue]); } else if (strcmp([obj objCType], @encode(double)) == 0) { sqlite3_bind_double(pStmt, idx, [obj doubleValue]); } else { sqlite3_bind_text(pStmt, idx, [[obj description] UTF8String], -1, SQLITE_STATIC); } } else { sqlite3_bind_text(pStmt, idx, [[obj description] UTF8String], -1, SQLITE_STATIC); } } - (id)executeQuery:(NSString *)sql withArgumentsInArray:(NSArray*)arrayArgs orVAList:(va_list)args { if (inUse) { [self compainAboutInUse]; return nil; } [self setInUse:YES]; FMResultSet *rs = nil; int rc = 0x00;; sqlite3_stmt *pStmt = 0x00;; FMStatement *statement = 0x00; if (traceExecution && sql) { NSLog(@"%@ executeQuery: %@", self, sql); } if (shouldCacheStatements) { statement = [self cachedStatementForQuery:sql]; pStmt = statement ? [statement statement] : 0x00; } int numberOfRetries = 0; BOOL retry = NO; if (!pStmt) { do { retry = NO; rc = sqlite3_prepare_v2(db, [sql UTF8String], -1, &pStmt, 0); if (SQLITE_BUSY == rc) { retry = YES; usleep(20); if (busyRetryTimeout && (numberOfRetries++ > busyRetryTimeout)) { NSLog(@"%s:%d Database busy (%@)", __FUNCTION__, __LINE__, [self databasePath]); NSLog(@"Database busy"); sqlite3_finalize(pStmt); [self setInUse:NO]; return nil; } } else if (SQLITE_OK != rc) { if (logsErrors) { NSLog(@"DB Error: %d \"%@\"", [self lastErrorCode], [self lastErrorMessage]); NSLog(@"DB Query: %@", sql); if (crashOnErrors) { //#if defined(__BIG_ENDIAN__) && !TARGET_IPHONE_SIMULATOR // asm{ trap }; //#endif NSAssert2(false, @"DB Error: %d \"%@\"", [self lastErrorCode], [self lastErrorMessage]); } } sqlite3_finalize(pStmt); [self setInUse:NO]; return nil; } } while (retry); } id obj; int idx = 0; int queryCount = sqlite3_bind_parameter_count(pStmt); // pointed out by Dominic Yu (thanks!) while (idx < queryCount) { if (arrayArgs) { obj = [arrayArgs objectAtIndex:idx]; } else { obj = va_arg(args, id); } if (traceExecution) { NSLog(@"obj: %@", obj); } idx++; [self bindObject:obj toColumn:idx inStatement:pStmt]; } if (idx != queryCount) { NSLog(@"Error: the bind count is not correct for the # of variables (executeQuery)"); sqlite3_finalize(pStmt); [self setInUse:NO]; return nil; } [statement retain]; // to balance the release below if (!statement) { statement = [[FMStatement alloc] init]; [statement setStatement:pStmt]; if (shouldCacheStatements) { [self setCachedStatement:statement forQuery:sql]; } } // the statement gets close in rs's dealloc or [rs close]; rs = [FMResultSet resultSetWithStatement:statement usingParentDatabase:self]; [rs setQuery:sql]; statement.useCount = statement.useCount + 1; [statement release]; [self setInUse:NO]; return rs; } - (id)executeQuery:(NSString*)sql, ... { va_list args; va_start(args, sql); id result = [self executeQuery:sql withArgumentsInArray:nil orVAList:args]; va_end(args); return result; } - (id)executeQuery:(NSString *)sql withArgumentsInArray:(NSArray *)arguments { return [self executeQuery:sql withArgumentsInArray:arguments orVAList:nil]; } - (BOOL)executeUpdate:(NSString*)sql withArgumentsInArray:(NSArray*)arrayArgs orVAList:(va_list)args { if (inUse) { [self compainAboutInUse]; return NO; } [self setInUse:YES]; int rc = 0x00; sqlite3_stmt *pStmt = 0x00; FMStatement *cachedStmt = 0x00; if (traceExecution && sql) { NSLog(@"%@ executeUpdate: %@", self, sql); } if (shouldCacheStatements) { cachedStmt = [self cachedStatementForQuery:sql]; pStmt = cachedStmt ? [cachedStmt statement] : 0x00; } int numberOfRetries = 0; BOOL retry = NO; if (!pStmt) { do { retry = NO; rc = sqlite3_prepare_v2(db, [sql UTF8String], -1, &pStmt, 0); if (SQLITE_BUSY == rc) { retry = YES; usleep(20); if (busyRetryTimeout && (numberOfRetries++ > busyRetryTimeout)) { NSLog(@"%s:%d Database busy (%@)", __FUNCTION__, __LINE__, [self databasePath]); NSLog(@"Database busy"); sqlite3_finalize(pStmt); [self setInUse:NO]; return NO; } } else if (SQLITE_OK != rc) { if (logsErrors) { NSLog(@"DB Error: %d \"%@\"", [self lastErrorCode], [self lastErrorMessage]); NSLog(@"DB Query: %@", sql); if (crashOnErrors) { //#if defined(__BIG_ENDIAN__) && !TARGET_IPHONE_SIMULATOR // asm{ trap }; //#endif NSAssert2(false, @"DB Error: %d \"%@\"", [self lastErrorCode], [self lastErrorMessage]); } } sqlite3_finalize(pStmt); [self setInUse:NO]; return NO; } } while (retry); } id obj; int idx = 0; int queryCount = sqlite3_bind_parameter_count(pStmt); while (idx < queryCount) { if (arrayArgs) { obj = [arrayArgs objectAtIndex:idx]; } else { obj = va_arg(args, id); } if (traceExecution) { NSLog(@"obj: %@", obj); } idx++; [self bindObject:obj toColumn:idx inStatement:pStmt]; } if (idx != queryCount) { NSLog(@"Error: the bind count is not correct for the # of variables (%@) (executeUpdate)", sql); sqlite3_finalize(pStmt); [self setInUse:NO]; return NO; } /* Call sqlite3_step() to run the virtual machine. Since the SQL being ** executed is not a SELECT statement, we assume no data will be returned. */ numberOfRetries = 0; do { rc = sqlite3_step(pStmt); retry = NO; if (SQLITE_BUSY == rc) { // this will happen if the db is locked, like if we are doing an update or insert. // in that case, retry the step... and maybe wait just 10 milliseconds. retry = YES; usleep(20); if (busyRetryTimeout && (numberOfRetries++ > busyRetryTimeout)) { NSLog(@"%s:%d Database busy (%@)", __FUNCTION__, __LINE__, [self databasePath]); NSLog(@"Database busy"); retry = NO; } } else if (SQLITE_DONE == rc || SQLITE_ROW == rc) { // all is well, let's return. } else if (SQLITE_ERROR == rc) { NSLog(@"Error calling sqlite3_step (%d: %s) SQLITE_ERROR", rc, sqlite3_errmsg(db)); NSLog(@"DB Query: %@", sql); } else if (SQLITE_MISUSE == rc) { // uh oh. NSLog(@"Error calling sqlite3_step (%d: %s) SQLITE_MISUSE", rc, sqlite3_errmsg(db)); NSLog(@"DB Query: %@", sql); } else { // wtf? NSLog(@"Unknown error calling sqlite3_step (%d: %s) eu", rc, sqlite3_errmsg(db)); NSLog(@"DB Query: %@", sql); } } while (retry); assert( rc!=SQLITE_ROW ); if (shouldCacheStatements && !cachedStmt) { cachedStmt = [[FMStatement alloc] init]; [cachedStmt setStatement:pStmt]; [self setCachedStatement:cachedStmt forQuery:sql]; [cachedStmt release]; } if (cachedStmt) { cachedStmt.useCount = cachedStmt.useCount + 1; rc = sqlite3_reset(pStmt); } else { /* Finalize the virtual machine. This releases all memory and other ** resources allocated by the sqlite3_prepare() call above. */ rc = sqlite3_finalize(pStmt); } [self setInUse:NO]; return (rc == SQLITE_OK); } - (BOOL)executeUpdate:(NSString*)sql, ... { va_list args; va_start(args, sql); BOOL result = [self executeUpdate:sql withArgumentsInArray:nil orVAList:args]; va_end(args); return result; } - (BOOL)executeUpdate:(NSString*)sql withArgumentsInArray:(NSArray *)arguments { return [self executeUpdate:sql withArgumentsInArray:arguments orVAList:nil]; } /* - (id) executeUpdate:(NSString *)sql arguments:(va_list)args { } */ - (BOOL)rollback { BOOL b = [self executeUpdate:@"ROLLBACK TRANSACTION;"]; if (b) { inTransaction = NO; } return b; } - (BOOL)commit { BOOL b = [self executeUpdate:@"COMMIT TRANSACTION;"]; if (b) { inTransaction = NO; } return b; } - (BOOL)beginDeferredTransaction { BOOL b = [self executeUpdate:@"BEGIN DEFERRED TRANSACTION;"]; if (b) { inTransaction = YES; } return b; } - (BOOL)beginTransaction { BOOL b = [self executeUpdate:@"BEGIN EXCLUSIVE TRANSACTION;"]; if (b) { inTransaction = YES; } return b; } - (BOOL)logsErrors { return logsErrors; } - (void)setLogsErrors:(BOOL)flag { logsErrors = flag; } - (BOOL)crashOnErrors { return crashOnErrors; } - (void)setCrashOnErrors:(BOOL)flag { crashOnErrors = flag; } - (BOOL)inUse { return inUse || inTransaction; } - (void)setInUse:(BOOL)b { inUse = b; } - (BOOL)inTransaction { return inTransaction; } - (void)setInTransaction:(BOOL)flag { inTransaction = flag; } - (BOOL)traceExecution { return traceExecution; } - (void)setTraceExecution:(BOOL)flag { traceExecution = flag; } - (BOOL)checkedOut { return checkedOut; } - (void)setCheckedOut:(BOOL)flag { checkedOut = flag; } - (int)busyRetryTimeout { return busyRetryTimeout; } - (void)setBusyRetryTimeout:(int)newBusyRetryTimeout { busyRetryTimeout = newBusyRetryTimeout; } - (BOOL)shouldCacheStatements { return shouldCacheStatements; } - (void)setShouldCacheStatements:(BOOL)value { shouldCacheStatements = value; if (shouldCacheStatements && !cachedStatements) { [self setCachedStatements:[NSMutableDictionary dictionary]]; } if (!shouldCacheStatements) { [self setCachedStatements:nil]; } } - (NSMutableDictionary *) cachedStatements { return cachedStatements; } - (void)setCachedStatements:(NSMutableDictionary *)value { if (cachedStatements != value) { [cachedStatements release]; cachedStatements = [value retain]; } } - (int)changes { return(sqlite3_changes(db)); } @end @implementation FMStatement - (void)dealloc { [self close]; [query release]; [super dealloc]; } - (void)close { if (statement) { sqlite3_finalize(statement); statement = 0x00; } } - (void)reset { if (statement) { sqlite3_reset(statement); } } - (sqlite3_stmt *)statement { return statement; } - (void)setStatement:(sqlite3_stmt *)value { statement = value; } - (NSString *)query { return query; } - (void)setQuery:(NSString *)value { if (query != value) { [query release]; query = [value retain]; } } - (long)useCount { return useCount; } - (void)setUseCount:(long)value { if (useCount != value) { useCount = value; } } - (NSString*)description { return [NSString stringWithFormat:@"%@ %d hit(s) for query %@", [super description], useCount, query]; } @end ================================================ FILE: FMDB/FMDatabaseAdditions.h ================================================ // // FMDatabaseAdditions.h // fmkit // // Created by August Mueller on 10/30/05. // Copyright 2005 Flying Meat Inc.. All rights reserved. // #import @interface FMDatabase (FMDatabaseAdditions) - (int)intForQuery:(NSString*)objs, ...; - (long)longForQuery:(NSString*)objs, ...; - (BOOL)boolForQuery:(NSString*)objs, ...; - (double)doubleForQuery:(NSString*)objs, ...; - (NSString*)stringForQuery:(NSString*)objs, ...; - (NSData*)dataForQuery:(NSString*)objs, ...; - (NSDate*)dateForQuery:(NSString*)objs, ...; // Notice that there's no dataNoCopyForQuery:. // That would be a bad idea, because we close out the result set, and then what // happens to the data that we just didn't copy? Who knows, not I. - (BOOL)tableExists:(NSString*)tableName; - (FMResultSet*)getSchema; - (FMResultSet*)getTableSchema:(NSString*)tableName; - (BOOL)columnExists:(NSString*)tableName columnName:(NSString*)columnName; @end ================================================ FILE: FMDB/FMDatabaseAdditions.m ================================================ // // FMDatabaseAdditions.m // fmkit // // Created by August Mueller on 10/30/05. // Copyright 2005 Flying Meat Inc.. All rights reserved. // #import "FMDatabase.h" #import "FMDatabaseAdditions.h" @implementation FMDatabase (FMDatabaseAdditions) #define RETURN_RESULT_FOR_QUERY_WITH_SELECTOR(type, sel) \ va_list args; \ va_start(args, query); \ FMResultSet *resultSet = [self executeQuery:query withArgumentsInArray:0x00 orVAList:args]; \ va_end(args); \ if (![resultSet next]) { return (type)0; } \ type ret = [resultSet sel:0]; \ [resultSet close]; \ [resultSet setParentDB:nil]; \ return ret; - (NSString*)stringForQuery:(NSString*)query, ...; { RETURN_RESULT_FOR_QUERY_WITH_SELECTOR(NSString *, stringForColumnIndex); } - (int)intForQuery:(NSString*)query, ...; { RETURN_RESULT_FOR_QUERY_WITH_SELECTOR(int, intForColumnIndex); } - (long)longForQuery:(NSString*)query, ...; { RETURN_RESULT_FOR_QUERY_WITH_SELECTOR(long, longForColumnIndex); } - (BOOL)boolForQuery:(NSString*)query, ...; { RETURN_RESULT_FOR_QUERY_WITH_SELECTOR(BOOL, boolForColumnIndex); } - (double)doubleForQuery:(NSString*)query, ...; { RETURN_RESULT_FOR_QUERY_WITH_SELECTOR(double, doubleForColumnIndex); } - (NSData*)dataForQuery:(NSString*)query, ...; { RETURN_RESULT_FOR_QUERY_WITH_SELECTOR(NSData *, dataForColumnIndex); } - (NSDate*)dateForQuery:(NSString*)query, ...; { RETURN_RESULT_FOR_QUERY_WITH_SELECTOR(NSDate *, dateForColumnIndex); } //check if table exist in database (patch from OZLB) - (BOOL)tableExists:(NSString*)tableName { BOOL returnBool; //lower case table name tableName = [tableName lowercaseString]; //search in sqlite_master table if table exists FMResultSet *rs = [self executeQuery:@"select [sql] from sqlite_master where [type] = 'table' and lower(name) = ?", tableName]; //if at least one next exists, table exists returnBool = [rs next]; //close and free object [rs close]; return returnBool; } //get table with list of tables: result colums: type[STRING], name[STRING],tbl_name[STRING],rootpage[INTEGER],sql[STRING] //check if table exist in database (patch from OZLB) - (FMResultSet*)getSchema { //result colums: type[STRING], name[STRING],tbl_name[STRING],rootpage[INTEGER],sql[STRING] FMResultSet *rs = [self executeQuery:@"SELECT type, name, tbl_name, rootpage, sql FROM (SELECT * FROM sqlite_master UNION ALL SELECT * FROM sqlite_temp_master) WHERE type != 'meta' AND name NOT LIKE 'sqlite_%' ORDER BY tbl_name, type DESC, name"]; return rs; } //get table schema: result colums: cid[INTEGER], name,type [STRING], notnull[INTEGER], dflt_value[],pk[INTEGER] - (FMResultSet*)getTableSchema:(NSString*)tableName { //result colums: cid[INTEGER], name,type [STRING], notnull[INTEGER], dflt_value[],pk[INTEGER] FMResultSet *rs = [self executeQuery:[NSString stringWithFormat: @"PRAGMA table_info(%@)", tableName]]; return rs; } //check if column exist in table - (BOOL)columnExists:(NSString*)tableName columnName:(NSString*)columnName { BOOL returnBool = NO; //lower case table name tableName = [tableName lowercaseString]; //lower case column name columnName = [columnName lowercaseString]; //get table schema FMResultSet *rs = [self getTableSchema: tableName]; //check if column is present in table schema while ([rs next]) { if ([[[rs stringForColumn:@"name"] lowercaseString] isEqualToString: columnName]) { returnBool = YES; break; } } //close and free object [rs close]; return returnBool; } @end ================================================ FILE: FMDB/FMResultSet.h ================================================ #import #import "sqlite3.h" #ifndef __has_feature // Optional. #define __has_feature(x) 0 // Compatibility with non-clang compilers. #endif #ifndef NS_RETURNS_NOT_RETAINED #if __has_feature(attribute_ns_returns_not_retained) #define NS_RETURNS_NOT_RETAINED __attribute__((ns_returns_not_retained)) #else #define NS_RETURNS_NOT_RETAINED #endif #endif @class FMDatabase; @class FMStatement; @interface FMResultSet : NSObject { FMDatabase *parentDB; FMStatement *statement; NSString *query; NSMutableDictionary *columnNameToIndexMap; BOOL columnNamesSetup; } + (id)resultSetWithStatement:(FMStatement *)statement usingParentDatabase:(FMDatabase*)aDB; - (void)close; - (NSString *)query; - (void)setQuery:(NSString *)value; - (FMStatement *)statement; - (void)setStatement:(FMStatement *)value; - (void)setParentDB:(FMDatabase *)newDb; - (BOOL)next; - (BOOL)hasAnotherRow; - (int)columnIndexForName:(NSString*)columnName; - (NSString*)columnNameForIndex:(int)columnIdx; - (int)intForColumn:(NSString*)columnName; - (int)intForColumnIndex:(int)columnIdx; - (long)longForColumn:(NSString*)columnName; - (long)longForColumnIndex:(int)columnIdx; - (long long int)longLongIntForColumn:(NSString*)columnName; - (long long int)longLongIntForColumnIndex:(int)columnIdx; - (BOOL)boolForColumn:(NSString*)columnName; - (BOOL)boolForColumnIndex:(int)columnIdx; - (double)doubleForColumn:(NSString*)columnName; - (double)doubleForColumnIndex:(int)columnIdx; - (NSString*)stringForColumn:(NSString*)columnName; - (NSString*)stringForColumnIndex:(int)columnIdx; - (NSDate*)dateForColumn:(NSString*)columnName; - (NSDate*)dateForColumnIndex:(int)columnIdx; - (NSData*)dataForColumn:(NSString*)columnName; - (NSData*)dataForColumnIndex:(int)columnIdx; - (const unsigned char *)UTF8StringForColumnIndex:(int)columnIdx; - (const unsigned char *)UTF8StringForColumnName:(NSString*)columnName; /* If you are going to use this data after you iterate over the next row, or after you close the result set, make sure to make a copy of the data first (or just use dataForColumn:/dataForColumnIndex:) If you don't, you're going to be in a world of hurt when you try and use the data. */ - (NSData*)dataNoCopyForColumn:(NSString*)columnName NS_RETURNS_NOT_RETAINED; - (NSData*)dataNoCopyForColumnIndex:(int)columnIdx NS_RETURNS_NOT_RETAINED; - (BOOL)columnIndexIsNull:(int)columnIdx; - (BOOL)columnIsNull:(NSString*)columnName; - (void)kvcMagic:(id)object; - (NSDictionary *)resultDict; @end ================================================ FILE: FMDB/FMResultSet.m ================================================ #import "FMResultSet.h" #import "FMDatabase.h" #import "unistd.h" @interface FMResultSet (Private) - (NSMutableDictionary *)columnNameToIndexMap; - (void)setColumnNameToIndexMap:(NSMutableDictionary *)value; @end @implementation FMResultSet + (id)resultSetWithStatement:(FMStatement *)statement usingParentDatabase:(FMDatabase*)aDB { FMResultSet *rs = [[FMResultSet alloc] init]; [rs setStatement:statement]; [rs setParentDB:aDB]; return [rs autorelease]; } - (void)dealloc { [self close]; [query release]; query = nil; [columnNameToIndexMap release]; columnNameToIndexMap = nil; [super dealloc]; } - (void)close { [statement reset]; [statement release]; statement = nil; // we don't need this anymore... (i think) //[parentDB setInUse:NO]; parentDB = nil; } - (void)setupColumnNames { if (!columnNameToIndexMap) { [self setColumnNameToIndexMap:[NSMutableDictionary dictionary]]; } int columnCount = sqlite3_column_count(statement.statement); int columnIdx = 0; for (columnIdx = 0; columnIdx < columnCount; columnIdx++) { [columnNameToIndexMap setObject:[NSNumber numberWithInt:columnIdx] forKey:[[NSString stringWithUTF8String:sqlite3_column_name(statement.statement, columnIdx)] lowercaseString]]; } columnNamesSetup = YES; } - (void)kvcMagic:(id)object { int columnCount = sqlite3_column_count(statement.statement); int columnIdx = 0; for (columnIdx = 0; columnIdx < columnCount; columnIdx++) { const char *c = (const char *)sqlite3_column_text(statement.statement, columnIdx); // check for a null row if (c) { NSString *s = [NSString stringWithUTF8String:c]; [object setValue:s forKey:[NSString stringWithUTF8String:sqlite3_column_name(statement.statement, columnIdx)]]; } } } - (NSDictionary *)resultDict { NSInteger num_cols = sqlite3_data_count(statement.statement); if (num_cols > 0) { NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithCapacity:num_cols]; NSInteger i; for (i = 0; i < num_cols; i++) { const char *col_name = sqlite3_column_name(statement.statement, i); if (col_name) { NSString *colName = [NSString stringWithUTF8String:col_name]; id value = nil; // fetch according to type switch (sqlite3_column_type(statement.statement, i)) { case SQLITE_INTEGER: { value = [NSNumber numberWithInt:[self intForColumnIndex:i]]; break; } case SQLITE_FLOAT: { value = [NSNumber numberWithDouble:[self doubleForColumnIndex:i]]; break; } case SQLITE_TEXT: { value = [self stringForColumnIndex:i]; break; } case SQLITE_BLOB: { value = [self dataForColumnIndex:i]; break; } } // save to dict if (value) { [dict setObject:value forKey:colName]; } } } return [[dict copy] autorelease]; } else { NSLog(@"Warning: There seem to be no columns in this set."); } return nil; } - (BOOL)next { int rc; BOOL retry; int numberOfRetries = 0; do { retry = NO; rc = sqlite3_step(statement.statement); if (SQLITE_BUSY == rc) { // this will happen if the db is locked, like if we are doing an update or insert. // in that case, retry the step... and maybe wait just 10 milliseconds. retry = YES; usleep(20); if ([parentDB busyRetryTimeout] && (numberOfRetries++ > [parentDB busyRetryTimeout])) { NSLog(@"%s:%d Database busy (%@)", __FUNCTION__, __LINE__, [parentDB databasePath]); NSLog(@"Database busy"); break; } } else if (SQLITE_DONE == rc || SQLITE_ROW == rc) { // all is well, let's return. } else if (SQLITE_ERROR == rc) { NSLog(@"Error calling sqlite3_step (%d: %s) rs", rc, sqlite3_errmsg([parentDB sqliteHandle])); break; } else if (SQLITE_MISUSE == rc) { // uh oh. NSLog(@"Error calling sqlite3_step (%d: %s) rs", rc, sqlite3_errmsg([parentDB sqliteHandle])); break; } else { // wtf? NSLog(@"Unknown error calling sqlite3_step (%d: %s) rs", rc, sqlite3_errmsg([parentDB sqliteHandle])); break; } } while (retry); if (rc != SQLITE_ROW) { [self close]; } return (rc == SQLITE_ROW); } - (BOOL)hasAnotherRow { return sqlite3_errcode([parentDB sqliteHandle]) == SQLITE_ROW; } - (int)columnIndexForName:(NSString*)columnName { if (!columnNamesSetup) { [self setupColumnNames]; } columnName = [columnName lowercaseString]; NSNumber *n = [columnNameToIndexMap objectForKey:columnName]; if (n) { return [n intValue]; } NSLog(@"Warning: I could not find the column named '%@'.", columnName); return -1; } - (int)intForColumn:(NSString*)columnName { return [self intForColumnIndex:[self columnIndexForName:columnName]]; } - (int)intForColumnIndex:(int)columnIdx { return sqlite3_column_int(statement.statement, columnIdx); } - (long)longForColumn:(NSString*)columnName { return [self longForColumnIndex:[self columnIndexForName:columnName]]; } - (long)longForColumnIndex:(int)columnIdx { return (long)sqlite3_column_int64(statement.statement, columnIdx); } - (long long int)longLongIntForColumn:(NSString*)columnName { return [self longLongIntForColumnIndex:[self columnIndexForName:columnName]]; } - (long long int)longLongIntForColumnIndex:(int)columnIdx { return sqlite3_column_int64(statement.statement, columnIdx); } - (BOOL)boolForColumn:(NSString*)columnName { return [self boolForColumnIndex:[self columnIndexForName:columnName]]; } - (BOOL)boolForColumnIndex:(int)columnIdx { return ([self intForColumnIndex:columnIdx] != 0); } - (double)doubleForColumn:(NSString*)columnName { return [self doubleForColumnIndex:[self columnIndexForName:columnName]]; } - (double)doubleForColumnIndex:(int)columnIdx { return sqlite3_column_double(statement.statement, columnIdx); } - (NSString*)stringForColumnIndex:(int)columnIdx { if (sqlite3_column_type(statement.statement, columnIdx) == SQLITE_NULL || (columnIdx < 0)) { return nil; } const char *c = (const char *)sqlite3_column_text(statement.statement, columnIdx); if (!c) { // null row. return nil; } return [NSString stringWithUTF8String:c]; } - (NSString*)stringForColumn:(NSString*)columnName { return [self stringForColumnIndex:[self columnIndexForName:columnName]]; } - (NSDate*)dateForColumn:(NSString*)columnName { return [self dateForColumnIndex:[self columnIndexForName:columnName]]; } - (NSDate*)dateForColumnIndex:(int)columnIdx { if (sqlite3_column_type(statement.statement, columnIdx) == SQLITE_NULL || (columnIdx < 0)) { return nil; } return [NSDate dateWithTimeIntervalSince1970:[self doubleForColumnIndex:columnIdx]]; } - (NSData*)dataForColumn:(NSString*)columnName { return [self dataForColumnIndex:[self columnIndexForName:columnName]]; } - (NSData*)dataForColumnIndex:(int)columnIdx { if (sqlite3_column_type(statement.statement, columnIdx) == SQLITE_NULL || (columnIdx < 0)) { return nil; } int dataSize = sqlite3_column_bytes(statement.statement, columnIdx); NSMutableData *data = [NSMutableData dataWithLength:dataSize]; memcpy([data mutableBytes], sqlite3_column_blob(statement.statement, columnIdx), dataSize); return data; } - (NSData*)dataNoCopyForColumn:(NSString*)columnName { return [self dataNoCopyForColumnIndex:[self columnIndexForName:columnName]]; } - (NSData*)dataNoCopyForColumnIndex:(int)columnIdx { if (sqlite3_column_type(statement.statement, columnIdx) == SQLITE_NULL || (columnIdx < 0)) { return nil; } int dataSize = sqlite3_column_bytes(statement.statement, columnIdx); NSData *data = [NSData dataWithBytesNoCopy:(void *)sqlite3_column_blob(statement.statement, columnIdx) length:dataSize freeWhenDone:NO]; return data; } - (BOOL)columnIndexIsNull:(int)columnIdx { return sqlite3_column_type(statement.statement, columnIdx) == SQLITE_NULL; } - (BOOL)columnIsNull:(NSString*)columnName { return [self columnIndexIsNull:[self columnIndexForName:columnName]]; } - (const unsigned char *)UTF8StringForColumnIndex:(int)columnIdx { if (sqlite3_column_type(statement.statement, columnIdx) == SQLITE_NULL || (columnIdx < 0)) { return nil; } return sqlite3_column_text(statement.statement, columnIdx); } - (const unsigned char *)UTF8StringForColumnName:(NSString*)columnName { return [self UTF8StringForColumnIndex:[self columnIndexForName:columnName]]; } // returns autoreleased NSString containing the name of the column in the result set - (NSString*)columnNameForIndex:(int)columnIdx { return [NSString stringWithUTF8String: sqlite3_column_name(statement.statement, columnIdx)]; } - (void)setParentDB:(FMDatabase *)newDb { parentDB = newDb; } - (NSString *)query { return query; } - (void)setQuery:(NSString *)value { [value retain]; [query release]; query = value; } - (NSMutableDictionary *)columnNameToIndexMap { return columnNameToIndexMap; } - (void)setColumnNameToIndexMap:(NSMutableDictionary *)value { [value retain]; [columnNameToIndexMap release]; columnNameToIndexMap = value; } - (FMStatement *)statement { return statement; } - (void)setStatement:(FMStatement *)value { if (statement != value) { [statement release]; statement = [value retain]; } } @end ================================================ FILE: JSON/JSON.h ================================================ /* Copyright (C) 2009-2010 Stig Brautaset. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** @mainpage A strict JSON parser and generator for Objective-C JSON (JavaScript Object Notation) is a lightweight data-interchange format. This framework provides two apis for parsing and generating JSON. One standard object-based and a higher level api consisting of categories added to existing Objective-C classes. This framework does its best to be as strict as possible, both in what it accepts and what it generates. For example, it does not support trailing commas in arrays or objects. Nor does it support embedded comments, or anything else not in the JSON specification. This is considered a feature. @section Links @li Project home page. @li Online version of the API documentation. */ // This setting of 1 is best if you copy the source into your project. // The build transforms the 1 to a 0 when building the framework and static lib. #if 1 #import "SBJsonParser.h" #import "SBJsonWriter.h" #import "SBJsonStreamWriter.h" #import "NSObject+SBJSON.h" #import "NSString+SBJSON.h" #else #import #import #import #import #import #endif ================================================ FILE: JSON/LICENSE ================================================ Copyright (C) 2007-2010 Stig Brautaset. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ================================================ FILE: JSON/NSObject+SBJSON.h ================================================ /* Copyright (C) 2009 Stig Brautaset. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #import /** @brief Adds JSON generation to Foundation classes This is a category on NSObject that adds methods for returning JSON representations of standard objects to the objects themselves. This means you can call the -JSONRepresentation method on an NSArray object and it'll do what you want. */ @interface NSObject (NSObject_SBJSON) /** @brief Returns a string containing the receiver encoded in JSON. This method is added as a category on NSObject but is only actually supported for the following objects: @li NSDictionary @li NSArray */ - (NSString *)JSONRepresentation; @end ================================================ FILE: JSON/NSObject+SBJSON.m ================================================ /* Copyright (C) 2009 Stig Brautaset. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #import "NSObject+SBJSON.h" #import "SBJsonWriter.h" @implementation NSObject (NSObject_SBJSON) - (NSString *)JSONRepresentation { SBJsonWriter *jsonWriter = [SBJsonWriter new]; NSString *json = [jsonWriter stringWithObject:self]; if (!json) NSLog(@"-JSONRepresentation failed. Error trace is: %@", [jsonWriter errorTrace]); [jsonWriter release]; return json; } @end ================================================ FILE: JSON/NSString+SBJSON.h ================================================ /* Copyright (C) 2009 Stig Brautaset. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #import /** @brief Adds JSON parsing methods to NSString This is a category on NSString that adds methods for parsing the target string. */ @interface NSString (NSString_SBJSON) /** @brief Returns the NSDictionary or NSArray represented by the current string's JSON representation. Returns the dictionary or array represented in the receiver, or nil on error. Returns the NSDictionary or NSArray represented by the current string's JSON representation. */ - (id)JSONValue; @end ================================================ FILE: JSON/NSString+SBJSON.m ================================================ /* Copyright (C) 2007-2009 Stig Brautaset. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #import "NSString+SBJSON.h" #import "SBJsonParser.h" @implementation NSString (NSString_SBJSON) - (id)JSONValue { SBJsonParser *jsonParser = [SBJsonParser new]; id repr = [jsonParser objectWithString:self]; if (!repr) NSLog(@"-JSONValue failed. Error trace is: %@", [jsonParser errorTrace]); [jsonParser release]; return repr; } @end ================================================ FILE: JSON/Readme.markdown ================================================ JSON Framework ============== JSON is a light-weight data interchange format that's easy to read and write for humans and computers alike. This framework implements a strict JSON parser and generator in Objective-C. Features -------- * BSD license. * Easy-to-use API. * Strict parsing & generation. * Stack of error available in case of failure so you can easily figure out what is wrong. * Optional pretty-printing of JSON output. * Optionally sorted dictionary keys in JSON output. * Configurable recursion depth for parsing, for added security. Links ----- * The GitHub [project page][src]. * The online [API documentation][api]. * The new [website][web]. [api]: http://stig.github.com/json-framework/api [web]: http://stig.github.com/json-framework [src]: http://github.com/stig/json-framework ================================================ FILE: JSON/SBJsonBase.h ================================================ /* Copyright (C) 2009 Stig Brautaset. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #import extern NSString * SBJSONErrorDomain; enum { EUNSUPPORTED = 1, EPARSENUM, EPARSE, EFRAGMENT, ECTRL, EUNICODE, EDEPTH, EESCAPE, ETRAILCOMMA, ETRAILGARBAGE, EEOF, EINPUT }; /** @brief Common base class for parsing & writing. This class contains the common error-handling code and option between the parser/writer. */ @interface SBJsonBase : NSObject { NSMutableArray *errorTrace; @protected NSUInteger depth, maxDepth; } /** @brief The maximum recursing depth. Defaults to 512. If the input is nested deeper than this the input will be deemed to be malicious and the parser returns nil, signalling an error. ("Nested too deep".) You can turn off this security feature by setting the maxDepth value to 0. */ @property NSUInteger maxDepth; /** @brief Return an error trace, or nil if there was no errors. Note that this method returns the trace of the last method that failed. You need to check the return value of the call you're making to figure out if the call actually failed, before you know call this method. */ @property(copy,readonly) NSArray* errorTrace; /// @internal for use in subclasses to add errors to the stack trace - (void)addErrorWithCode:(NSUInteger)code description:(NSString*)str; /// @internal for use in subclasess to clear the error before a new parsing attempt - (void)clearErrorTrace; @end ================================================ FILE: JSON/SBJsonBase.m ================================================ /* Copyright (C) 2009 Stig Brautaset. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #import "SBJsonBase.h" NSString * SBJSONErrorDomain = @"org.brautaset.JSON.ErrorDomain"; @implementation SBJsonBase @synthesize errorTrace; @synthesize maxDepth; - (id)init { self = [super init]; if (self) self.maxDepth = 512; return self; } - (void)dealloc { [errorTrace release]; [super dealloc]; } - (void)addErrorWithCode:(NSUInteger)code description:(NSString*)str { NSDictionary *userInfo; if (!errorTrace) { errorTrace = [NSMutableArray new]; userInfo = [NSDictionary dictionaryWithObject:str forKey:NSLocalizedDescriptionKey]; } else { userInfo = [NSDictionary dictionaryWithObjectsAndKeys: str, NSLocalizedDescriptionKey, [errorTrace lastObject], NSUnderlyingErrorKey, nil]; } NSError *error = [NSError errorWithDomain:SBJSONErrorDomain code:code userInfo:userInfo]; [self willChangeValueForKey:@"errorTrace"]; [errorTrace addObject:error]; [self didChangeValueForKey:@"errorTrace"]; } - (void)clearErrorTrace { [self willChangeValueForKey:@"errorTrace"]; [errorTrace release]; errorTrace = nil; [self didChangeValueForKey:@"errorTrace"]; } @end ================================================ FILE: JSON/SBJsonParser.h ================================================ /* Copyright (C) 2009 Stig Brautaset. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #import #import "SBJsonBase.h" /** @brief The JSON parser class. JSON is mapped to Objective-C types in the following way: @li Null -> NSNull @li String -> NSMutableString @li Array -> NSMutableArray @li Object -> NSMutableDictionary @li Boolean -> NSNumber (initialised with -initWithBool:) @li Number -> (NSNumber | NSDecimalNumber) Since Objective-C doesn't have a dedicated class for boolean values, these turns into NSNumber instances. These are initialised with the -initWithBool: method, and round-trip back to JSON properly. (They won't silently suddenly become 0 or 1; they'll be represented as 'true' and 'false' again.) As an optimisation short JSON integers turn into NSNumber instances, while complex ones turn into NSDecimalNumber instances. We can thus avoid any loss of precision as JSON allows ridiculously large numbers. */ @interface SBJsonParser : SBJsonBase { @private const char *c; } /** @brief Return the object represented by the given string Returns the object represented by the passed-in string or nil on error. The returned object can be a string, number, boolean, null, array or dictionary. @param repr the json string to parse */ - (id)objectWithString:(NSString *)repr; /** @brief Return the object represented by the given string Returns the object represented by the passed-in string or nil on error. The returned object can be a string, number, boolean, null, array or dictionary. @param jsonText the json string to parse @param error pointer to an NSError object to populate on error */ - (id)objectWithString:(NSString*)jsonText error:(NSError**)error; @end ================================================ FILE: JSON/SBJsonParser.m ================================================ /* Copyright (C) 2009,2010 Stig Brautaset. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #import "SBJsonParser.h" @interface SBJsonParser () - (BOOL)scanValue:(NSObject **)o; - (BOOL)scanRestOfArray:(NSMutableArray **)o; - (BOOL)scanRestOfDictionary:(NSMutableDictionary **)o; - (BOOL)scanRestOfNull:(NSNull **)o; - (BOOL)scanRestOfFalse:(NSNumber **)o; - (BOOL)scanRestOfTrue:(NSNumber **)o; - (BOOL)scanRestOfString:(NSMutableString **)o; // Cannot manage without looking at the first digit - (BOOL)scanNumber:(NSNumber **)o; - (BOOL)scanHexQuad:(unichar *)x; - (BOOL)scanUnicodeChar:(unichar *)x; - (BOOL)scanIsAtEnd; @end #define skipWhitespace(c) while (isspace(*c)) c++ #define skipDigits(c) while (isdigit(*c)) c++ @implementation SBJsonParser static char ctrl[0x22]; + (void)initialize { ctrl[0] = '\"'; ctrl[1] = '\\'; for (int i = 1; i < 0x20; i++) ctrl[i+1] = i; ctrl[0x21] = 0; } - (id)objectWithString:(NSString *)repr { [self clearErrorTrace]; if (!repr) { [self addErrorWithCode:EINPUT description:@"Input was 'nil'"]; return nil; } depth = 0; c = [repr UTF8String]; id o; if (![self scanValue:&o]) { return nil; } // We found some valid JSON. But did it also contain something else? if (![self scanIsAtEnd]) { [self addErrorWithCode:ETRAILGARBAGE description:@"Garbage after JSON"]; return nil; } NSAssert1(o, @"Should have a valid object from %@", repr); // Check that the object we've found is a valid JSON container. if (![o isKindOfClass:[NSDictionary class]] && ![o isKindOfClass:[NSArray class]]) { [self addErrorWithCode:EFRAGMENT description:@"Valid fragment, but not JSON"]; return nil; } return o; } - (id)objectWithString:(NSString*)repr error:(NSError**)error { id tmp = [self objectWithString:repr]; if (tmp) return tmp; if (error) *error = [self.errorTrace lastObject]; return nil; } /* In contrast to the public methods, it is an error to omit the error parameter here. */ - (BOOL)scanValue:(NSObject **)o { skipWhitespace(c); switch (*c++) { case '{': return [self scanRestOfDictionary:(NSMutableDictionary **)o]; break; case '[': return [self scanRestOfArray:(NSMutableArray **)o]; break; case '"': return [self scanRestOfString:(NSMutableString **)o]; break; case 'f': return [self scanRestOfFalse:(NSNumber **)o]; break; case 't': return [self scanRestOfTrue:(NSNumber **)o]; break; case 'n': return [self scanRestOfNull:(NSNull **)o]; break; case '-': case '0'...'9': c--; // cannot verify number correctly without the first character return [self scanNumber:(NSNumber **)o]; break; case '+': [self addErrorWithCode:EPARSENUM description: @"Leading + disallowed in number"]; return NO; break; case 0x0: [self addErrorWithCode:EEOF description:@"Unexpected end of string"]; return NO; break; default: [self addErrorWithCode:EPARSE description: @"Unrecognised leading character"]; return NO; break; } NSAssert(0, @"Should never get here"); return NO; } - (BOOL)scanRestOfTrue:(NSNumber **)o { if (!strncmp(c, "rue", 3)) { c += 3; *o = [NSNumber numberWithBool:YES]; return YES; } [self addErrorWithCode:EPARSE description:@"Expected 'true'"]; return NO; } - (BOOL)scanRestOfFalse:(NSNumber **)o { if (!strncmp(c, "alse", 4)) { c += 4; *o = [NSNumber numberWithBool:NO]; return YES; } [self addErrorWithCode:EPARSE description: @"Expected 'false'"]; return NO; } - (BOOL)scanRestOfNull:(NSNull **)o { if (!strncmp(c, "ull", 3)) { c += 3; *o = [NSNull null]; return YES; } [self addErrorWithCode:EPARSE description: @"Expected 'null'"]; return NO; } - (BOOL)scanRestOfArray:(NSMutableArray **)o { if (maxDepth && ++depth > maxDepth) { [self addErrorWithCode:EDEPTH description: @"Nested too deep"]; return NO; } *o = [NSMutableArray arrayWithCapacity:8]; for (; *c ;) { id v; skipWhitespace(c); if (*c == ']' && c++) { depth--; return YES; } if (![self scanValue:&v]) { [self addErrorWithCode:EPARSE description:@"Expected value while parsing array"]; return NO; } [*o addObject:v]; skipWhitespace(c); if (*c == ',' && c++) { skipWhitespace(c); if (*c == ']') { [self addErrorWithCode:ETRAILCOMMA description: @"Trailing comma disallowed in array"]; return NO; } } } [self addErrorWithCode:EEOF description: @"End of input while parsing array"]; return NO; } - (BOOL)scanRestOfDictionary:(NSMutableDictionary **)o { if (maxDepth && ++depth > maxDepth) { [self addErrorWithCode:EDEPTH description: @"Nested too deep"]; return NO; } *o = [NSMutableDictionary dictionaryWithCapacity:7]; for (; *c ;) { id k, v; skipWhitespace(c); if (*c == '}' && c++) { depth--; return YES; } if (!(*c == '\"' && c++ && [self scanRestOfString:&k])) { [self addErrorWithCode:EPARSE description: @"Object key string expected"]; return NO; } skipWhitespace(c); if (*c != ':') { [self addErrorWithCode:EPARSE description: @"Expected ':' separating key and value"]; return NO; } c++; if (![self scanValue:&v]) { NSString *string = [NSString stringWithFormat:@"Object value expected for key: %@", k]; [self addErrorWithCode:EPARSE description: string]; return NO; } [*o setObject:v forKey:k]; skipWhitespace(c); if (*c == ',' && c++) { skipWhitespace(c); if (*c == '}') { [self addErrorWithCode:ETRAILCOMMA description: @"Trailing comma disallowed in object"]; return NO; } } } [self addErrorWithCode:EEOF description: @"End of input while parsing object"]; return NO; } - (BOOL)scanRestOfString:(NSMutableString **)o { // if the string has no control characters in it, return it in one go, without any temporary allocations. size_t len = strcspn(c, ctrl); if (len && *(c + len) == '\"') { *o = [[[NSMutableString alloc] initWithBytes:(char*)c length:len encoding:NSUTF8StringEncoding] autorelease]; c += len + 1; return YES; } *o = [NSMutableString stringWithCapacity:16]; do { // First see if there's a portion we can grab in one go. // Doing this caused a massive speedup on the long string. len = strcspn(c, ctrl); if (len) { // check for id t = [[NSString alloc] initWithBytesNoCopy:(char*)c length:len encoding:NSUTF8StringEncoding freeWhenDone:NO]; if (t) { [*o appendString:t]; [t release]; c += len; } } if (*c == '"') { c++; return YES; } else if (*c == '\\') { unichar uc = *++c; switch (uc) { case '\\': case '/': case '"': break; case 'b': uc = '\b'; break; case 'n': uc = '\n'; break; case 'r': uc = '\r'; break; case 't': uc = '\t'; break; case 'f': uc = '\f'; break; case 'u': c++; if (![self scanUnicodeChar:&uc]) { [self addErrorWithCode:EUNICODE description: @"Broken unicode character"]; return NO; } c--; // hack. break; default: [self addErrorWithCode:EESCAPE description: [NSString stringWithFormat:@"Illegal escape sequence '0x%x'", uc]]; return NO; break; } CFStringAppendCharacters((CFMutableStringRef)*o, &uc, 1); c++; } else if (*c < 0x20) { [self addErrorWithCode:ECTRL description: [NSString stringWithFormat:@"Unescaped control character '0x%x'", *c]]; return NO; } else { NSLog(@"should not be able to get here"); } } while (*c); [self addErrorWithCode:EEOF description:@"Unexpected EOF while parsing string"]; return NO; } - (BOOL)scanUnicodeChar:(unichar *)x { unichar hi, lo; if (![self scanHexQuad:&hi]) { [self addErrorWithCode:EUNICODE description: @"Missing hex quad"]; return NO; } if (hi >= 0xd800) { // high surrogate char? if (hi < 0xdc00) { // yes - expect a low char if (!(*c == '\\' && ++c && *c == 'u' && ++c && [self scanHexQuad:&lo])) { [self addErrorWithCode:EUNICODE description: @"Missing low character in surrogate pair"]; return NO; } if (lo < 0xdc00 || lo >= 0xdfff) { [self addErrorWithCode:EUNICODE description:@"Invalid low surrogate char"]; return NO; } hi = (hi - 0xd800) * 0x400 + (lo - 0xdc00) + 0x10000; } else if (hi < 0xe000) { [self addErrorWithCode:EUNICODE description:@"Invalid high character in surrogate pair"]; return NO; } } *x = hi; return YES; } - (BOOL)scanHexQuad:(unichar *)x { *x = 0; for (int i = 0; i < 4; i++) { unichar uc = *c; c++; int d = (uc >= '0' && uc <= '9') ? uc - '0' : (uc >= 'a' && uc <= 'f') ? (uc - 'a' + 10) : (uc >= 'A' && uc <= 'F') ? (uc - 'A' + 10) : -1; if (d == -1) { [self addErrorWithCode:EUNICODE description:@"Missing hex digit in quad"]; return NO; } *x *= 16; *x += d; } return YES; } - (BOOL)scanNumber:(NSNumber **)o { BOOL simple = YES; const char *ns = c; // The logic to test for validity of the number formatting is relicensed // from JSON::XS with permission from its author Marc Lehmann. // (Available at the CPAN: http://search.cpan.org/dist/JSON-XS/ .) if ('-' == *c) c++; if ('0' == *c && c++) { if (isdigit(*c)) { [self addErrorWithCode:EPARSENUM description: @"Leading 0 disallowed in number"]; return NO; } } else if (!isdigit(*c) && c != ns) { [self addErrorWithCode:EPARSENUM description: @"No digits after initial minus"]; return NO; } else { skipDigits(c); } // Fractional part if ('.' == *c && c++) { simple = NO; if (!isdigit(*c)) { [self addErrorWithCode:EPARSENUM description: @"No digits after decimal point"]; return NO; } skipDigits(c); } // Exponential part if ('e' == *c || 'E' == *c) { simple = NO; c++; if ('-' == *c || '+' == *c) c++; if (!isdigit(*c)) { [self addErrorWithCode:EPARSENUM description: @"No digits after exponent"]; return NO; } skipDigits(c); } // If we are only reading integers, don't go through the expense of creating an NSDecimal. // This ends up being a very large perf win. if (simple) { BOOL negate = NO; long long val = 0; const char *d = ns; if (*d == '-') { negate = YES; d++; } while (isdigit(*d)) { val *= 10; if (val < 0) goto longlong_overflow; val += *d - '0'; if (val < 0) goto longlong_overflow; d++; } *o = [NSNumber numberWithLongLong:negate ? -val : val]; return YES; } else { // jumped to by simple branch, if an overflow occured longlong_overflow:; id str = [[NSString alloc] initWithBytesNoCopy:(char*)ns length:c - ns encoding:NSUTF8StringEncoding freeWhenDone:NO]; [str autorelease]; if (str && (*o = [NSDecimalNumber decimalNumberWithString:str])) return YES; [self addErrorWithCode:EPARSENUM description: @"Failed creating decimal instance"]; return NO; } } - (BOOL)scanIsAtEnd { skipWhitespace(c); return !*c; } @end ================================================ FILE: JSON/SBJsonStreamWriter.h ================================================ /* Copyright (c) 2010, Stig Brautaset. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of the the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #import @class SBJsonStreamWriterStateMachine; /** @brief The Stream Writer class. SBJsonStreamWriter accepts various messages and writes JSON text to an NSOutputStream designated at iniatilisation time. A range of high-, mid- and low-level methods. You can mix and match calls to these. For example, you may want to call -writeArrayOpen to start an array and then repeatedly call -writeObject: with an object. In JSON the keys of an object must be strings. NSDictionary keys need not be, but attempting to convert an NSDictionary with non-string keys into JSON will result in an error. NSNumber instances created with the +initWithBool: method are converted into the JSON boolean "true" and "false" values, and vice versa. Any other NSNumber instances are converted to a JSON number the way you would expect. */ @interface SBJsonStreamWriter : NSObject { @private NSString *error; SBJsonStreamWriterStateMachine **states; NSOutputStream *stream; NSUInteger depth, maxDepth; BOOL sortKeys, humanReadable; } /** @brief The maximum recursing depth. Defaults to 512. If the input is nested deeper than this the input will be deemed to be malicious and the parser returns nil, signalling an error. ("Nested too deep".) You can turn off this security feature by setting the maxDepth value to 0. */ @property NSUInteger maxDepth; /** @brief Whether we are generating human-readable (multiline) JSON. Set whether or not to generate human-readable JSON. The default is NO, which produces JSON without any whitespace between tokens. If set to YES, generates human-readable JSON with linebreaks after each array value and dictionary key/value pair, indented two spaces per nesting level. */ @property BOOL humanReadable; /** @brief Whether or not to sort the dictionary keys in the output. If this is set to YES, the dictionary keys in the JSON output will be in sorted order. (This is useful if you need to compare two structures, for example.) The default is NO. */ @property BOOL sortKeys; /** @brief Contains the error description after an error has occured. */ @property (copy, readonly) NSString *error; /** @brief Initialise a stream writer. You have to create an output stream first. You should not open/close the stream manually; this class takes care of that. */ - (id)initWithStream:(NSOutputStream*)stream; /** @brief Write an NSDictionary to the JSON stream. */ - (BOOL)writeObject:(NSDictionary*)dict; /** @brief Write an NSArray to the JSON stream. */ - (BOOL)writeArray:(NSArray *)array; /// Start writing an Object to the stream - (BOOL)writeObjectOpen; /// Close the current object being written - (BOOL)writeObjectClose; /// Start writing an Array to the stream - (BOOL)writeArrayOpen; /// Close the current Array being written - (BOOL)writeArrayClose; /// Write a null to the stream - (BOOL)writeNull; /// Write a boolean to the stream - (BOOL)writeBool:(BOOL)x; //- (BOOL)writeInteger:(long)l; //- (BOOL)writeDouble:(double)d; /// Write a Number to the stream - (BOOL)writeNumber:(NSNumber*)n; /// Write a String to the stream - (BOOL)writeString:(NSString*)s; @end ================================================ FILE: JSON/SBJsonStreamWriter.m ================================================ /* Copyright (c) 2010, Stig Brautaset. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of the the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #import "SBJsonStreamWriter.h" #import "SBProxyForJson.h" @interface SBJsonStreamWriter () @property(copy) NSString *error; @property(readonly) NSObject **states; @property(readonly) NSUInteger depth; @property(readonly) NSOutputStream *stream; - (BOOL)writeValue:(id)v; - (void)write:(char const *)utf8 len:(NSUInteger)len; @end @interface SBJsonStreamWriterStateMachine : NSObject - (BOOL)isInvalidState:(SBJsonStreamWriter*)writer; - (void)appendSeparator:(SBJsonStreamWriter*)writer; - (BOOL)expectingKey:(SBJsonStreamWriter*)writer; - (void)transitionState:(SBJsonStreamWriter*)writer; - (void)appendWhitespace:(SBJsonStreamWriter*)writer; @end @interface ObjectOpenState : SBJsonStreamWriterStateMachine @end @interface ObjectKeyState : ObjectOpenState @end @interface ObjectValueState : SBJsonStreamWriterStateMachine @end @interface ArrayOpenState : SBJsonStreamWriterStateMachine @end @interface ArrayValueState : SBJsonStreamWriterStateMachine @end @interface StartState : SBJsonStreamWriterStateMachine @end @interface CompleteState : SBJsonStreamWriterStateMachine @end @interface ErrorState : SBJsonStreamWriterStateMachine @end static NSMutableDictionary *stringCache; static NSDecimalNumber *notANumber; // States static StartState *openState; static CompleteState *closeState; static ErrorState *errorState; static ObjectOpenState *objectOpenState; static ObjectKeyState *objectKeyState; static ObjectValueState *objectValueState; static ArrayOpenState *arrayOpenState; static ArrayValueState *arrayValueState; @implementation SBJsonStreamWriterStateMachine - (BOOL)isInvalidState:(SBJsonStreamWriter*)writer { return NO; } - (void)appendSeparator:(SBJsonStreamWriter*)writer {} - (BOOL)expectingKey:(SBJsonStreamWriter*)writer { return NO; } - (void)transitionState:(SBJsonStreamWriter *)writer {} - (void)appendWhitespace:(SBJsonStreamWriter*)writer { [writer write:"\n" len:1]; for (int i = 0; i < writer.depth; i++) [writer write:" " len: 2]; } @end @implementation ObjectOpenState - (void)transitionState:(SBJsonStreamWriter *)writer { writer.states[writer.depth] = objectValueState; } - (BOOL)expectingKey:(SBJsonStreamWriter *)writer { writer.error = @"JSON object key must be string"; return YES; } @end @implementation ObjectKeyState - (void)appendSeparator:(SBJsonStreamWriter *)writer { [writer write:"," len:1]; } @end @implementation ObjectValueState - (void)appendSeparator:(SBJsonStreamWriter *)writer { [writer write:":" len:1]; } - (void)transitionState:(SBJsonStreamWriter *)writer { writer.states[writer.depth] = objectKeyState; } - (void)appendWhitespace:(SBJsonStreamWriter *)writer { [writer write:" " len:1]; } @end @implementation ArrayOpenState - (void)transitionState:(SBJsonStreamWriter *)writer { writer.states[writer.depth] = arrayValueState; } @end @implementation ArrayValueState - (void)appendSeparator:(SBJsonStreamWriter *)writer { [writer write:"," len:1]; } @end @implementation StartState - (void)transitionState:(SBJsonStreamWriter *)writer { writer.states[writer.depth] = closeState; [writer.stream close]; } - (void)appendSeparator:(SBJsonStreamWriter *)writer { [writer.stream open]; } @end @implementation CompleteState - (BOOL)isInvalidState:(SBJsonStreamWriter*)writer { writer.error = @"Stream is closed"; return YES; } @end @implementation ErrorState @end @implementation SBJsonStreamWriter @synthesize error; @dynamic depth; @synthesize maxDepth; @synthesize states; @synthesize stream; @synthesize humanReadable; @synthesize sortKeys; + (void)initialize { notANumber = [NSDecimalNumber notANumber]; stringCache = [NSMutableDictionary new]; openState = [StartState new]; closeState = [CompleteState new]; errorState = [ErrorState new]; objectOpenState = [ObjectOpenState new]; objectKeyState = [ObjectKeyState new]; objectValueState = [ObjectValueState new]; arrayOpenState = [ArrayOpenState new]; arrayValueState = [ArrayValueState new]; } #pragma mark Housekeeping - (id)initWithStream:(NSOutputStream*)stream_ { self = [super init]; if (self) { stream = [stream_ retain]; maxDepth = 512; states = calloc(maxDepth, sizeof(SBJsonStreamWriterStateMachine*)); NSAssert(states, @"States not initialised"); states[0] = openState; } return self; } - (void)dealloc { self.error = nil; free(states); [stream release]; [super dealloc]; } #pragma mark Methods - (BOOL)writeObject:(NSDictionary *)dict { if (![self writeObjectOpen]) return NO; NSArray *keys = [dict allKeys]; if (sortKeys) keys = [keys sortedArrayUsingSelector:@selector(compare:)]; for (id k in keys) { if (![k isKindOfClass:[NSString class]]) { self.error = [NSString stringWithFormat:@"JSON object key must be string: %@", k]; return NO; } if (![self writeString:k]) return NO; if (![self writeValue:[dict objectForKey:k]]) return NO; } return [self writeObjectClose]; } - (BOOL)writeArray:(NSArray*)array { if (![self writeArrayOpen]) return NO; for (id v in array) if (![self writeValue:v]) return NO; return [self writeArrayClose]; } - (BOOL)writeObjectOpen { SBJsonStreamWriterStateMachine *s = states[depth]; if ([s isInvalidState:self]) return NO; if ([s expectingKey:self]) return NO; [s appendSeparator:self]; if (humanReadable && depth) [s appendWhitespace:self]; if (maxDepth && ++depth > maxDepth) { self.error = @"Nested too deep"; return NO; } states[depth] = objectOpenState; [self write:"{" len:1]; return YES; } - (BOOL)writeObjectClose { SBJsonStreamWriterStateMachine *state = states[depth--]; if ([state isInvalidState:self]) return NO; if (humanReadable) [state appendWhitespace:self]; [self write:"}" len:1]; [states[depth] transitionState:self]; return YES; } - (BOOL)writeArrayOpen { SBJsonStreamWriterStateMachine *s = states[depth]; if ([s isInvalidState:self]) return NO; if ([s expectingKey:self]) return NO; [s appendSeparator:self]; if (humanReadable && depth) [s appendWhitespace:self]; if (maxDepth && ++depth > maxDepth) { self.error = @"Nested too deep"; return NO; } states[depth] = arrayOpenState; [self write:"[" len:1]; return YES; } - (BOOL)writeArrayClose { SBJsonStreamWriterStateMachine *state = states[depth--]; if ([state isInvalidState:self]) return NO; if ([state expectingKey:self]) return NO; if (humanReadable) [state appendWhitespace:self]; [self write:"]" len:1]; [states[depth] transitionState:self]; return YES; } - (BOOL)writeNull { SBJsonStreamWriterStateMachine *s = states[depth]; if ([s isInvalidState:self]) return NO; if ([s expectingKey:self]) return NO; [s appendSeparator:self]; if (humanReadable) [s appendWhitespace:self]; [self write:"null" len:4]; [s transitionState:self]; return YES; } - (BOOL)writeBool:(BOOL)x { SBJsonStreamWriterStateMachine *s = states[depth]; if ([s isInvalidState:self]) return NO; if ([s expectingKey:self]) return NO; [s appendSeparator:self]; if (humanReadable) [s appendWhitespace:self]; if (x) [self write:"true" len:4]; else [self write:"false" len:5]; [s transitionState:self]; return YES; } - (BOOL)writeValue:(id)o { if ([o isKindOfClass:[NSDictionary class]]) { return [self writeObject:o]; } else if ([o isKindOfClass:[NSArray class]]) { return [self writeArray:o]; } else if ([o isKindOfClass:[NSString class]]) { [self writeString:o]; return YES; } else if ([o isKindOfClass:[NSNumber class]]) { return [self writeNumber:o]; } else if ([o isKindOfClass:[NSNull class]]) { return [self writeNull]; } else if ([o respondsToSelector:@selector(proxyForJson)]) { return [self writeValue:[o proxyForJson]]; } self.error = [NSString stringWithFormat:@"JSON serialisation not supported for @%", [o class]]; return NO; } static const char *strForChar(int c) { switch (c) { case 0: return "\\u0000"; break; case 1: return "\\u0001"; break; case 2: return "\\u0002"; break; case 3: return "\\u0003"; break; case 4: return "\\u0004"; break; case 5: return "\\u0005"; break; case 6: return "\\u0006"; break; case 7: return "\\u0007"; break; case 8: return "\\b"; break; case 9: return "\\t"; break; case 10: return "\\n"; break; case 11: return "\\u000b"; break; case 12: return "\\f"; break; case 13: return "\\r"; break; case 14: return "\\u000e"; break; case 15: return "\\u000f"; break; case 16: return "\\u0010"; break; case 17: return "\\u0011"; break; case 18: return "\\u0012"; break; case 19: return "\\u0013"; break; case 20: return "\\u0014"; break; case 21: return "\\u0015"; break; case 22: return "\\u0016"; break; case 23: return "\\u0017"; break; case 24: return "\\u0018"; break; case 25: return "\\u0019"; break; case 26: return "\\u001a"; break; case 27: return "\\u001b"; break; case 28: return "\\u001c"; break; case 29: return "\\u001d"; break; case 30: return "\\u001e"; break; case 31: return "\\u001f"; break; case 34: return "\\\""; break; case 92: return "\\\\"; break; } NSLog(@"FUTFUTFUT: -->'%c'<---", c); return "FUTFUTFUT"; } - (BOOL)writeString:(NSString*)string { SBJsonStreamWriterStateMachine *s = states[depth]; if ([s isInvalidState:self]) return NO; [s appendSeparator:self]; if (humanReadable) [s appendWhitespace:self]; NSMutableData *data = [stringCache objectForKey:string]; if (data) { [self write:[data bytes] len:[data length]]; [s transitionState:self]; return YES; } NSUInteger len = [string lengthOfBytesUsingEncoding:NSUTF8StringEncoding]; const char *utf8 = [string UTF8String]; NSUInteger written = 0, i = 0; data = [NSMutableData dataWithCapacity:len * 1.1]; [data appendBytes:"\"" length:1]; for (i = 0; i < len; i++) { int c = utf8[i]; if (c >= 0 && c < 32 || c == '"' || c == '\\') { if (i - written) [data appendBytes:utf8 + written length:i - written]; written = i + 1; const char *t = strForChar(c); [data appendBytes:t length:strlen(t)]; } } if (i - written) [data appendBytes:utf8 + written length:i - written]; [data appendBytes:"\"" length:1]; [self write:[data bytes] len:[data length]]; [stringCache setObject:data forKey:string]; [s transitionState:self]; return YES; } - (BOOL)writeNumber:(NSNumber*)number { if ((CFBooleanRef)number == kCFBooleanTrue || (CFBooleanRef)number == kCFBooleanFalse) return [self writeBool:[number boolValue]]; SBJsonStreamWriterStateMachine *s = states[depth]; if ([s isInvalidState:self]) return NO; if ([s expectingKey:self]) return NO; [s appendSeparator:self]; if (humanReadable) [s appendWhitespace:self]; if ((CFNumberRef)number == kCFNumberPositiveInfinity) { self.error = @"+Infinity is not a valid number in JSON"; return NO; } else if ((CFNumberRef)number == kCFNumberNegativeInfinity) { self.error = @"-Infinity is not a valid number in JSON"; return NO; } else if ((CFNumberRef)number == kCFNumberNaN) { self.error = @"NaN is not a valid number in JSON"; return NO; } else if (number == notANumber) { self.error = @"NaN is not a valid number in JSON"; return NO; } const char *objcType = [number objCType]; char num[64]; size_t len; switch (objcType[0]) { case 'c': case 'i': case 's': case 'l': case 'q': len = sprintf(num, "%lld", [number longLongValue]); break; case 'C': case 'I': case 'S': case 'L': case 'Q': len = sprintf(num, "%llu", [number unsignedLongLongValue]); break; case 'f': case 'd': default: if ([number isKindOfClass:[NSDecimalNumber class]]) { char const *utf8 = [[number stringValue] UTF8String]; [self write:utf8 len: strlen(utf8)]; [s transitionState:self]; return YES; } len = sprintf(num, "%g", [number doubleValue]); break; } [self write:num len: len]; [s transitionState:self]; return YES; } #pragma mark Private methods - (NSUInteger)depth { return depth; } - (void)write:(char const *)utf8 len:(NSUInteger)len { NSUInteger written = 0; do { NSInteger w = [stream write:(const uint8_t *)utf8 maxLength:len - written]; if (w > 0) written += w; } while (written < len); } - (void)setMaxDepth:(NSUInteger)x { NSAssert(x, @"maxDepth must be greater than 0"); maxDepth = x; states = realloc(states, x); NSAssert(states, @"Failed to reallocate more memory for states"); } @end ================================================ FILE: JSON/SBJsonWriter.h ================================================ /* Copyright (C) 2009 Stig Brautaset. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #import #import "SBJsonBase.h" /** @brief The JSON writer class. Objective-C types are mapped to JSON types in the following way: @li NSNull -> Null @li NSString -> String @li NSArray -> Array @li NSDictionary -> Object @li NSNumber (-initWithBool:) -> Boolean @li NSNumber -> Number In JSON the keys of an object must be strings. NSDictionary keys need not be, but attempting to convert an NSDictionary with non-string keys into JSON will throw an exception. NSNumber instances created with the +initWithBool: method are converted into the JSON boolean "true" and "false" values, and vice versa. Any other NSNumber instances are converted to a JSON number the way you would expect. */ @interface SBJsonWriter : SBJsonBase { @private BOOL sortKeys, humanReadable; } /** @brief Whether we are generating human-readable (multiline) JSON. Set whether or not to generate human-readable JSON. The default is NO, which produces JSON without any whitespace. (Except inside strings.) If set to YES, generates human-readable JSON with linebreaks after each array value and dictionary key/value pair, indented two spaces per nesting level. */ @property BOOL humanReadable; /** @brief Whether or not to sort the dictionary keys in the output. If this is set to YES, the dictionary keys in the JSON output will be in sorted order. (This is useful if you need to compare two structures, for example.) The default is NO. */ @property BOOL sortKeys; /** @brief Return JSON representation for the given object. Returns a string containing JSON representation of the passed in value, or nil on error. If nil is returned and @p error is not NULL, @p *error can be interrogated to find the cause of the error. @param value any instance that can be represented as JSON text. */ - (NSString*)stringWithObject:(id)value; /** @brief Return JSON representation for the given object. Returns an NSData object containing JSON represented as UTF8 text, or nil on error. @param value any instance that can be represented as JSON text. */ - (NSData*)dataWithObject:(id)value; /** @brief Return JSON representation (or fragment) for the given object. Returns a string containing JSON representation of the passed in value, or nil on error. If nil is returned and @p error is not NULL, @p *error can be interrogated to find the cause of the error. @param value any instance that can be represented as a JSON fragment @param error pointer to object to be populated with NSError on failure */- (NSString*)stringWithObject:(id)value error:(NSError**)error; @end ================================================ FILE: JSON/SBJsonWriter.m ================================================ /* Copyright (C) 2009 Stig Brautaset. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #import "SBJsonWriter.h" #import "SBJsonStreamWriter.h" #import "SBProxyForJson.h" @interface SBJsonWriter () - (NSData*)dataWithObject:(id)value; @end @implementation SBJsonWriter @synthesize sortKeys; @synthesize humanReadable; - (NSString*)stringWithObject:(id)value { [self clearErrorTrace]; NSData *data = [self dataWithObject:value]; if (data) return [[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease]; return nil; } - (NSString*)stringWithObject:(id)value error:(NSError**)error { NSString *tmp = [self stringWithObject:value]; if (tmp) return tmp; if (error) *error = [self.errorTrace lastObject]; return nil; } - (NSData*)dataWithObject:(id)object { NSOutputStream *stream = [[[NSOutputStream alloc] initToMemory] autorelease]; SBJsonStreamWriter *streamWriter = [[[SBJsonStreamWriter alloc] initWithStream:stream] autorelease]; streamWriter.sortKeys = self.sortKeys; streamWriter.maxDepth = self.maxDepth; streamWriter.humanReadable = self.humanReadable; BOOL ok = NO; if ([object isKindOfClass:[NSDictionary class]]) ok = [streamWriter writeObject:object]; else if ([object isKindOfClass:[NSArray class]]) ok = [streamWriter writeArray:object]; else if ([object respondsToSelector:@selector(proxyForJson)]) return [self dataWithObject:[object proxyForJson]]; else { [self addErrorWithCode:EUNSUPPORTED description:@"Not valid type for JSON"]; return nil; } if (ok) return [stream propertyForKey:NSStreamDataWrittenToMemoryStreamKey]; [self addErrorWithCode:EUNSUPPORTED description:streamWriter.error]; return nil; } @end ================================================ FILE: JSON/SBProxyForJson.h ================================================ /* Copyright (c) 2010, Stig Brautaset. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of the the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #import /** @brief Allows generation of JSON for otherwise unsupported classes. If you have a custom class that you want to create a JSON representation for you can implement this method in your class. It should return a representation of your object defined in terms of objects that can be translated into JSON. For example, a Person object might implement it like this: @code - (id)proxyForJson { return [NSDictionary dictionaryWithObjectsAndKeys: name, @"name", phone, @"phone", email, @"email", nil]; } @endcode */ @interface NSObject (SBProxyForJson) - (id)proxyForJson; @end ================================================ FILE: MainWindow.xib ================================================ 1280 11C74 1938 1138.23 567.00 com.apple.InterfaceBuilder.IBCocoaTouchPlugin 933 YES IBUIImageView IBUITextView IBUILabel IBUINavigationBar IBProxyObject IBUICustomObject IBUIWindow IBUITabBar IBUINavigationItem IBUITabBarItem IBUIView IBUINavigationController IBUIViewController IBUITabBarController YES com.apple.InterfaceBuilder.IBCocoaTouchPlugin PluginDependencyRecalculationVersion YES IBFilesOwner IBCocoaTouchFramework IBFirstResponder IBCocoaTouchFramework IBCocoaTouchFramework 1316 {320, 480} 1 MSAxIDEAA NO NO IBCocoaTouchFramework YES 1 1 IBCocoaTouchFramework NO Data Sources Data Sources NSImage data.png IBCocoaTouchFramework YES 1 1 IBCocoaTouchFramework NO 256 {320, 44} NO YES YES IBCocoaTouchFramework YES Data Sources Data Sources IBCocoaTouchFramework Sources 1 1 IBCocoaTouchFramework NO YES SpyPhone 274 YES 306 {{80, 156}, {159, 43}} 3 MSAwAA 2 NO YES NO IBCocoaTouchFramework SpyPhone 1 10 1 Helvetica Helvetica 0 36 Helvetica 36 16 306 {{20, 238}, {280, 153}} 1 MSAxIDEgMAA NO YES YES IBCocoaTouchFramework NO NO NO NO NO NO 0.0 0.0 NO NO VGhpcyBhcHAgc2hvd3MgdGhlIGtpbmQgb2YgZGF0YQphIHJvZ3VlIGFwcGxpY2F0aW9uIGNhbiBjb2xs ZWN0LgoKTm8gcHJpdmF0ZSBBUElzIHdlcmUgdXNlZC4KVGhpcyBhcHAgZG9lcyBub3QgcGhvbmUgaG9t ZS4KCmh0dHA6Ly9zZXJpb3QuY2g 3 MQA 1 IBCocoaTouchFramework Helvetica Helvetica 0 16 Helvetica 16 16 292 {{40, 20}, {240, 128}} 3 MCAwAA NO NO 4 NO IBCocoaTouchFramework NSImage white_hat.png {{0, 20}, {320, 411}} 3 MAA NO IBCocoaTouchFramework About NSImage white_hat_mask.png IBCocoaTouchFramework 1 1 IBCocoaTouchFramework NO Report Email Report NSImage report.png IBCocoaTouchFramework SPEmailReportVC 1 1 IBCocoaTouchFramework NO 266 {{0, 431}, {320, 49}} NO IBCocoaTouchFramework YES delegate 99 window 9 tabBarController 147 allSources 148 YES 0 2 YES -1 File's Owner 3 106 YES 107 108 YES 111 -2 119 YES 121 123 134 YES 135 138 136 YES 137 144 145 YES 146 YES YES -1.CustomClassName -1.IBPluginDependency -2.CustomClassName -2.IBPluginDependency 106.IBPluginDependency 107.IBPluginDependency 108.IBPluginDependency 111.IBPluginDependency 119.IBPluginDependency 121.IBPluginDependency 123.IBPluginDependency 134.IBPluginDependency 135.IBPluginDependency 136.CustomClassName 136.IBPluginDependency 137.IBPluginDependency 138.IBPluginDependency 144.IBPluginDependency 145.CustomClassName 145.IBPluginDependency 146.IBPluginDependency 2.IBAttributePlaceholdersKey 2.IBPluginDependency 3.CustomClassName 3.IBPluginDependency YES UIApplication com.apple.InterfaceBuilder.IBCocoaTouchPlugin UIResponder com.apple.InterfaceBuilder.IBCocoaTouchPlugin com.apple.InterfaceBuilder.IBCocoaTouchPlugin com.apple.InterfaceBuilder.IBCocoaTouchPlugin com.apple.InterfaceBuilder.IBCocoaTouchPlugin com.apple.InterfaceBuilder.IBCocoaTouchPlugin com.apple.InterfaceBuilder.IBCocoaTouchPlugin com.apple.InterfaceBuilder.IBCocoaTouchPlugin com.apple.InterfaceBuilder.IBCocoaTouchPlugin com.apple.InterfaceBuilder.IBCocoaTouchPlugin com.apple.InterfaceBuilder.IBCocoaTouchPlugin SPAllSourcesTVC com.apple.InterfaceBuilder.IBCocoaTouchPlugin com.apple.InterfaceBuilder.IBCocoaTouchPlugin com.apple.InterfaceBuilder.IBCocoaTouchPlugin com.apple.InterfaceBuilder.IBCocoaTouchPlugin SPEmailReportVC com.apple.InterfaceBuilder.IBCocoaTouchPlugin com.apple.InterfaceBuilder.IBCocoaTouchPlugin YES com.apple.InterfaceBuilder.IBCocoaTouchPlugin SpyPhoneAppDelegate com.apple.InterfaceBuilder.IBCocoaTouchPlugin YES YES 148 0 IBCocoaTouchFramework com.apple.InterfaceBuilder.CocoaTouchPlugin.iPhoneOS com.apple.InterfaceBuilder.CocoaTouchPlugin.InterfaceBuilder3 YES 3 YES YES data.png report.png white_hat.png white_hat_mask.png YES {32, 29} {36, 39} {180, 124} {30, 30} 933 ================================================ FILE: OUILookupTool/OUILookupTool.h ================================================ // // OUILookupTool.h // OUILookup // // Created by Nicolas Seriot on 10/31/10. // Copyright 2010 IICT. All rights reserved. // #import @class OUILookupTool; @protocol OUILookupToolDelegate - (void)OUILookupTool:(OUILookupTool *)ouiLookupTool didLocateAccessPoint:(NSDictionary *)ap; @end @interface OUILookupTool : NSObject { NSObject *delegate; } @property (nonatomic, retain) NSObject *delegate; // ap should have a "bssid" key + (OUILookupTool *)locateWifiAccessPoint:(NSDictionary *)ap delegate:(NSObject *)aDelegate; @end ================================================ FILE: OUILookupTool/OUILookupTool.m ================================================ // // OUILookupTool.m // OUILookup // // Created by Nicolas Seriot on 10/31/10. // Copyright 2010 IICT. All rights reserved. // #import "OUILookupTool.h" #import "JSON.h" @implementation OUILookupTool @synthesize delegate; - (void)fetchLocationForAccessPointInNewThread:(NSMutableDictionary *)ap { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; NSString *aBSSID = [ap valueForKey:@"BSSID"]; NSDictionary *d = [NSDictionary dictionaryWithObject:aBSSID forKey:@"mac_address"]; NSArray *wifiTowers = [NSArray arrayWithObject:d]; NSDictionary *postDictionary = [[NSMutableDictionary alloc] initWithCapacity:3]; [postDictionary setValue:@"1.1.0" forKey:@"version"]; [postDictionary setValue:@"ouilookup" forKey:@"host"]; [postDictionary setValue:wifiTowers forKey:@"wifi_towers"]; NSString *postJSON = [postDictionary JSONRepresentation]; [postDictionary release]; NSData *data = [postJSON dataUsingEncoding:NSUTF8StringEncoding]; NSURL *url = [NSURL URLWithString:@"http://66.249.92.104/loc/json"]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; [request setHTTPBody:data]; [request setCachePolicy:NSURLRequestUseProtocolCachePolicy]; [request setHTTPMethod:@"POST"]; [request setValue:@"application/json" forHTTPHeaderField:@"Content-type"]; NSString *contentLength = [NSString stringWithFormat:@"%d", [data length]]; [request setValue:contentLength forHTTPHeaderField:@"Content-Length"]; [request setValue:@"www.google.com" forHTTPHeaderField:@"Host"]; NSError *error = nil; NSData *responseData = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:&error]; NSString *responseString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding]; NSDictionary *responseDict = [responseString JSONValue]; NSDictionary *params = [NSDictionary dictionaryWithObjectsAndKeys:ap, @"originalDict", responseDict, @"responseDict", nil]; [self performSelectorOnMainThread:@selector(didFinishLookup:) withObject:params waitUntilDone:YES]; [pool release]; } - (void)didFinishLookup:(NSDictionary *)params { // NSLog(@"-- %@", ap); NSMutableDictionary *ap = [params objectForKey:@"originalDict"]; NSDictionary *responseDict = [params objectForKey:@"responseDict"]; [ap addEntriesFromDictionary:responseDict]; [delegate OUILookupTool:self didLocateAccessPoint:ap]; } - (void)dealloc { [delegate release]; [super dealloc]; } + (NSString *)formattedBSSID:(NSString *)aBSSID { NSArray *comps = [aBSSID componentsSeparatedByString:@":"]; if([comps count] != 6) return nil; NSMutableArray *a = [NSMutableArray array]; for(NSString *comp in comps) { NSUInteger length = [comp length]; if(length == 0 || length > 2) return nil; NSString *s = length == 2 ? comp : [@"0" stringByAppendingString:comp]; [a addObject:s]; } return [a componentsJoinedByString:@":"]; } + (OUILookupTool *)locateWifiAccessPoint:(NSMutableDictionary *)ap delegate:(NSObject *)aDelegate { NSString *aBSSID = [ap valueForKey:@"BSSID"]; NSString *formattedBSSID = [self formattedBSSID:aBSSID]; if(formattedBSSID == nil) return nil; [ap setValue:formattedBSSID forKey:@"BSSID"]; OUILookupTool *olt = [[OUILookupTool alloc] init]; olt.delegate = aDelegate; [NSThread detachNewThreadSelector:@selector(fetchLocationForAccessPointInNewThread:) toTarget:olt withObject:ap]; return [olt autorelease]; } @end ================================================ FILE: README.markdown ================================================ At BlackHat DC 2010, I presented a paper called [iPhone Privacy](http://seriot.ch/resources/talks_papers/iPhonePrivacy.pdf). In this paper, I call the following Apple claim into question: > Applications on the device are "sandboxed" so they cannot access data stored by other applications. > In addition, system files, resources, and the kernel are shielded from the user's application space. > Source: [iPhone in Business - Security Overview](http://www.apple.com/iphone/business/docs/iPhone_Security.pdf) SpyPhone demoes it is not exactly true. It shows the kind of data a rogue application can collect in a non jailbroken iPhone. These data do certainly interest marketers, spammers, thieves, competitors and law enforcement officials. ================================================ FILE: SPCell.xib ================================================ 784 10C540 740 1038.25 458.00 com.apple.InterfaceBuilder.IBCocoaTouchPlugin 62 YES YES com.apple.InterfaceBuilder.IBCocoaTouchPlugin YES YES YES YES IBFilesOwner IBFirstResponder 292 YES 256 {320, 43} 3 MCAwAA NO YES 4 YES {320, 44} 1 MSAxIDEAA NO 1 NO SPCell YES YES 0 -1 File's Owner -2 2 YES YES YES -2.CustomClassName 2.CustomClassName 2.IBEditorWindowLastContentRect 2.IBPluginDependency YES UIResponder SPCell {{126, 652}, {320, 44}} com.apple.InterfaceBuilder.IBCocoaTouchPlugin YES YES YES YES 6 YES SPCell UITableViewCell IBProjectSource Classes/SPCell.h SPCell UITableViewCell label UILabel IBUserSource YES NSObject IBFrameworkSource Foundation.framework/Headers/NSError.h NSObject IBFrameworkSource Foundation.framework/Headers/NSFileManager.h NSObject IBFrameworkSource Foundation.framework/Headers/NSKeyValueCoding.h NSObject IBFrameworkSource Foundation.framework/Headers/NSKeyValueObserving.h NSObject IBFrameworkSource Foundation.framework/Headers/NSKeyedArchiver.h NSObject IBFrameworkSource Foundation.framework/Headers/NSNetServices.h NSObject IBFrameworkSource Foundation.framework/Headers/NSObject.h NSObject IBFrameworkSource Foundation.framework/Headers/NSPort.h NSObject IBFrameworkSource Foundation.framework/Headers/NSRunLoop.h NSObject IBFrameworkSource Foundation.framework/Headers/NSStream.h NSObject IBFrameworkSource Foundation.framework/Headers/NSThread.h NSObject IBFrameworkSource Foundation.framework/Headers/NSURL.h NSObject IBFrameworkSource Foundation.framework/Headers/NSURLConnection.h NSObject IBFrameworkSource Foundation.framework/Headers/NSXMLParser.h NSObject IBFrameworkSource UIKit.framework/Headers/UIAccessibility.h NSObject IBFrameworkSource UIKit.framework/Headers/UINibLoading.h NSObject IBFrameworkSource UIKit.framework/Headers/UIResponder.h UILabel UIView IBFrameworkSource UIKit.framework/Headers/UILabel.h UIResponder NSObject UITableViewCell UIView IBFrameworkSource UIKit.framework/Headers/UITableViewCell.h UIView IBFrameworkSource UIKit.framework/Headers/UITextField.h UIView UIResponder IBFrameworkSource UIKit.framework/Headers/UIView.h 0 com.apple.InterfaceBuilder.CocoaTouchPlugin.InterfaceBuilder3 YES MyData.xcodeproj 3 3.1 ================================================ FILE: SPEmailReportVC.xib ================================================ 1024 10F569 804 1038.29 461.00 com.apple.InterfaceBuilder.IBCocoaTouchPlugin 123 YES YES com.apple.InterfaceBuilder.IBCocoaTouchPlugin YES YES YES YES IBFilesOwner IBCocoaTouchFramework IBFirstResponder IBCocoaTouchFramework 292 YES 292 {{103, 275}, {114, 37}} NO NO IBCocoaTouchFramework 0 0 Helvetica-Bold 15 16 1 Send Report 3 MQA 1 MC4xOTYwNzg0MyAwLjMwOTgwMzkzIDAuNTIxNTY4NjYAA 3 MC41AA 292 {{20, 320}, {280, 21}} 3 MCAwAA NO YES NO IBCocoaTouchFramework 1 MSAxIDEAA 1 10 1 306 {{55, 20}, {210, 43}} 3 MSAwAA 2 NO YES NO IBCocoaTouchFramework Email Report Helvetica 36 16 1 10 1 306 {{20, 71}, {280, 196}} 1 MSAxIDEgMAA NO YES YES IBCocoaTouchFramework NO NO NO NO NO NO 0.0 0.0 NO NO WW91IGNhbiBzZW5kIGEgcmVwb3J0IGNvbnRhaW5pbmcgU3B5UGhvbmUgcGVyc29uYWwgZGF0YSBieSBl bWFpbC4KCllvdSBjYW4gY2hvb3NlIHRoZSBlbWFpbCBhZGRyZXNzLgoKWW91IHdpbGwgc2VlIHRoZSBy ZXBvcnQgYmVmb3JlIHlvdSBhZ3JlZSB0byBzZW5kIGl0LgoKTm90aGluZyBpcyBzZW50IGVsc2V3aGVy ZS4 3 MQA Helvetica 16 16 IBCocoaTouchFramework {320, 460} 1 MCAwIDAAA NO IBCocoaTouchFramework YES view 4 sendReport: 7 6 message 8 YES 0 -1 File's Owner -2 3 YES 5 7 9 10 YES YES -1.CustomClassName -2.CustomClassName 10.IBPluginDependency 3.IBEditorWindowLastContentRect 3.IBPluginDependency 5.IBPluginDependency 7.IBPluginDependency 9.IBPluginDependency YES SPEmailReportVC UIResponder com.apple.InterfaceBuilder.IBCocoaTouchPlugin {{483, 203}, {320, 460}} com.apple.InterfaceBuilder.IBCocoaTouchPlugin com.apple.InterfaceBuilder.IBCocoaTouchPlugin com.apple.InterfaceBuilder.IBCocoaTouchPlugin com.apple.InterfaceBuilder.IBCocoaTouchPlugin YES YES YES YES 10 YES SPAllSourcesTVC UITableViewController YES YES sourceAddressBookTVC sourceEmailTVC sourceKeyboardTVC sourceLocationTVC sourcePhoneTVC sourcePhotosTVC sourceSafariTVC sourceWifiTVC sourceYouTubeTVC YES SPSourceAddressBookTVC SPSourceEmailTVC SPSourceKeyboardTVC SPSourceLocationTVC SPSourcePhoneTVC SPSourcePhotosTVC SPSourceSafariTVC SPSourceWifiTVC SPSourceYouTubeTVC YES YES sourceAddressBookTVC sourceEmailTVC sourceKeyboardTVC sourceLocationTVC sourcePhoneTVC sourcePhotosTVC sourceSafariTVC sourceWifiTVC sourceYouTubeTVC YES sourceAddressBookTVC SPSourceAddressBookTVC sourceEmailTVC SPSourceEmailTVC sourceKeyboardTVC SPSourceKeyboardTVC sourceLocationTVC SPSourceLocationTVC sourcePhoneTVC SPSourcePhoneTVC sourcePhotosTVC SPSourcePhotosTVC sourceSafariTVC SPSourceSafariTVC sourceWifiTVC SPSourceWifiTVC sourceYouTubeTVC SPSourceYouTubeTVC IBProjectSource Classes/SPAllSourcesTVC.h SPEmailReportVC UIViewController sendReport: id sendReport: sendReport: id YES YES allSources message YES SPAllSourcesTVC UILabel YES YES allSources message YES allSources SPAllSourcesTVC message UILabel IBProjectSource Classes/SPEmailReportVC.h SPImageMapVC UIViewController YES YES imageVC mapView YES SPImageVC MKMapView YES YES imageVC mapView YES imageVC SPImageVC mapView MKMapView IBUserSource SPImageVC UIViewController imageView UIImageView imageView imageView UIImageView IBProjectSource SPImageVC.h SPSourceAddressBookTVC SPSourceTVC IBProjectSource Classes/SPSourceAddressBookTVC.h SPSourceEmailTVC SPSourceTVC IBProjectSource Classes/SPSourceEmailTVC.h SPSourceKeyboardTVC SPSourceTVC IBProjectSource Classes/SPSourceKeyboardTVC.h SPSourceLocationTVC SPSourceTVC IBProjectSource Classes/SPSourceLocationTVC.h SPSourcePhoneTVC SPSourceTVC IBProjectSource Classes/SPSourcePhoneTVC.h SPSourcePhotosTVC SPSourceTVC YES YES imageVC mapVC YES SPImageVC SPImageMapVC YES YES imageVC mapVC YES imageVC SPImageVC mapVC SPImageMapVC IBProjectSource Classes/SPSourcePhotosTVC.h SPSourceSafariTVC SPSourceTVC webViewVC SPWebViewVC webViewVC webViewVC SPWebViewVC IBProjectSource Classes/SPSourceSafariTVC.h SPSourceTVC UITableViewController IBProjectSource Classes/SPSourceTVC.h SPSourceWifiTVC SPSourceTVC IBProjectSource Classes/SPSourceWifiTVC.h SPSourceYouTubeTVC SPSourceTVC IBProjectSource Classes/SPSourceYouTubeTVC.h SPWebViewVC UIViewController webView UIWebView webView webView UIWebView IBProjectSource Classes/SPWebViewVC.h YES MKMapView UIView IBFrameworkSource MapKit.framework/Headers/MKMapView.h NSObject IBFrameworkSource Foundation.framework/Headers/NSError.h NSObject IBFrameworkSource Foundation.framework/Headers/NSFileManager.h NSObject IBFrameworkSource Foundation.framework/Headers/NSKeyValueCoding.h NSObject IBFrameworkSource Foundation.framework/Headers/NSKeyValueObserving.h NSObject IBFrameworkSource Foundation.framework/Headers/NSKeyedArchiver.h NSObject IBFrameworkSource Foundation.framework/Headers/NSNetServices.h NSObject IBFrameworkSource Foundation.framework/Headers/NSObject.h NSObject IBFrameworkSource Foundation.framework/Headers/NSPort.h NSObject IBFrameworkSource Foundation.framework/Headers/NSRunLoop.h NSObject IBFrameworkSource Foundation.framework/Headers/NSStream.h NSObject IBFrameworkSource Foundation.framework/Headers/NSThread.h NSObject IBFrameworkSource Foundation.framework/Headers/NSURL.h NSObject IBFrameworkSource Foundation.framework/Headers/NSURLConnection.h NSObject IBFrameworkSource Foundation.framework/Headers/NSXMLParser.h NSObject IBFrameworkSource UIKit.framework/Headers/UIAccessibility.h NSObject IBFrameworkSource UIKit.framework/Headers/UINibLoading.h NSObject IBFrameworkSource UIKit.framework/Headers/UIResponder.h UIButton UIControl IBFrameworkSource UIKit.framework/Headers/UIButton.h UIControl UIView IBFrameworkSource UIKit.framework/Headers/UIControl.h UIImageView UIView IBFrameworkSource UIKit.framework/Headers/UIImageView.h UILabel UIView IBFrameworkSource UIKit.framework/Headers/UILabel.h UIResponder NSObject UIScrollView UIView IBFrameworkSource UIKit.framework/Headers/UIScrollView.h UISearchBar UIView IBFrameworkSource UIKit.framework/Headers/UISearchBar.h UISearchDisplayController NSObject IBFrameworkSource UIKit.framework/Headers/UISearchDisplayController.h UITableViewController UIViewController IBFrameworkSource UIKit.framework/Headers/UITableViewController.h UITextView UIScrollView IBFrameworkSource UIKit.framework/Headers/UITextView.h UIView IBFrameworkSource UIKit.framework/Headers/UITextField.h UIView UIResponder IBFrameworkSource UIKit.framework/Headers/UIView.h UIViewController IBFrameworkSource UIKit.framework/Headers/UINavigationController.h UIViewController IBFrameworkSource UIKit.framework/Headers/UITabBarController.h UIViewController UIResponder IBFrameworkSource UIKit.framework/Headers/UIViewController.h UIWebView UIView IBFrameworkSource UIKit.framework/Headers/UIWebView.h 0 IBCocoaTouchFramework com.apple.InterfaceBuilder.CocoaTouchPlugin.InterfaceBuilder3 YES SpyPhone.xcodeproj 3 123 ================================================ FILE: SPImageMapVC.xib ================================================ 1024 10F569 804 1038.29 461.00 com.apple.InterfaceBuilder.IBCocoaTouchPlugin 123 YES YES com.apple.InterfaceBuilder.IBCocoaTouchPlugin YES YES YES YES IBFilesOwner IBCocoaTouchFramework IBFirstResponder IBCocoaTouchFramework 292 YES 274 {320, 460} NO YES 4 YES IBCocoaTouchFramework {320, 460} 3 MQA NO IBCocoaTouchFramework YES view 3 mapView 6 delegate 7 YES 0 -1 File's Owner -2 2 YES 4 YES YES -1.CustomClassName -2.CustomClassName 2.IBEditorWindowLastContentRect 2.IBPluginDependency 4.IBPluginDependency YES SPImageMapVC UIResponder {{451, 121}, {320, 460}} com.apple.InterfaceBuilder.IBCocoaTouchPlugin com.apple.InterfaceBuilder.IBCocoaTouchPlugin YES YES YES YES 8 YES SPImageMapVC UIViewController YES YES imageVC mapView YES SPImageVC MKMapView YES YES imageVC mapView YES imageVC SPImageVC mapView MKMapView IBUserSource SPImageVC UIViewController imageView UIImageView imageView imageView UIImageView IBProjectSource SPImageVC.h YES MKMapView UIView IBFrameworkSource MapKit.framework/Headers/MKMapView.h NSObject IBFrameworkSource Foundation.framework/Headers/NSError.h NSObject IBFrameworkSource Foundation.framework/Headers/NSFileManager.h NSObject IBFrameworkSource Foundation.framework/Headers/NSKeyValueCoding.h NSObject IBFrameworkSource Foundation.framework/Headers/NSKeyValueObserving.h NSObject IBFrameworkSource Foundation.framework/Headers/NSKeyedArchiver.h NSObject IBFrameworkSource Foundation.framework/Headers/NSNetServices.h NSObject IBFrameworkSource Foundation.framework/Headers/NSObject.h NSObject IBFrameworkSource Foundation.framework/Headers/NSPort.h NSObject IBFrameworkSource Foundation.framework/Headers/NSRunLoop.h NSObject IBFrameworkSource Foundation.framework/Headers/NSStream.h NSObject IBFrameworkSource Foundation.framework/Headers/NSThread.h NSObject IBFrameworkSource Foundation.framework/Headers/NSURL.h NSObject IBFrameworkSource Foundation.framework/Headers/NSURLConnection.h NSObject IBFrameworkSource Foundation.framework/Headers/NSXMLParser.h NSObject IBFrameworkSource UIKit.framework/Headers/UIAccessibility.h NSObject IBFrameworkSource UIKit.framework/Headers/UINibLoading.h NSObject IBFrameworkSource UIKit.framework/Headers/UIResponder.h UIImageView UIView IBFrameworkSource UIKit.framework/Headers/UIImageView.h UIResponder NSObject UISearchBar UIView IBFrameworkSource UIKit.framework/Headers/UISearchBar.h UISearchDisplayController NSObject IBFrameworkSource UIKit.framework/Headers/UISearchDisplayController.h UIView IBFrameworkSource UIKit.framework/Headers/UITextField.h UIView UIResponder IBFrameworkSource UIKit.framework/Headers/UIView.h UIViewController IBFrameworkSource UIKit.framework/Headers/UINavigationController.h UIViewController IBFrameworkSource UIKit.framework/Headers/UITabBarController.h UIViewController UIResponder IBFrameworkSource UIKit.framework/Headers/UIViewController.h 0 IBCocoaTouchFramework com.apple.InterfaceBuilder.CocoaTouchPlugin.InterfaceBuilder3 YES SpyPhone.xcodeproj 3 123 ================================================ FILE: SPImageVC.xib ================================================ 784 10C540 740 1038.25 458.00 com.apple.InterfaceBuilder.IBCocoaTouchPlugin 62 YES YES com.apple.InterfaceBuilder.IBCocoaTouchPlugin YES YES YES YES IBFilesOwner IBFirstResponder 292 YES 274 {320, 460} 1 MCAwIDAAA NO NO 1 NO {320, 460} 3 MQA NO YES view 5 imageView 7 YES 0 -1 File's Owner -2 4 YES 6 YES YES -1.CustomClassName -2.CustomClassName 4.IBEditorWindowLastContentRect 4.IBPluginDependency 6.IBPluginDependency YES SPImageVC UIResponder {{417, 270}, {320, 460}} com.apple.InterfaceBuilder.IBCocoaTouchPlugin com.apple.InterfaceBuilder.IBCocoaTouchPlugin YES YES YES YES 7 YES SPImageVC UIViewController imageView UIImageView IBProjectSource SPImageVC.h YES NSObject IBFrameworkSource Foundation.framework/Headers/NSError.h NSObject IBFrameworkSource Foundation.framework/Headers/NSFileManager.h NSObject IBFrameworkSource Foundation.framework/Headers/NSKeyValueCoding.h NSObject IBFrameworkSource Foundation.framework/Headers/NSKeyValueObserving.h NSObject IBFrameworkSource Foundation.framework/Headers/NSKeyedArchiver.h NSObject IBFrameworkSource Foundation.framework/Headers/NSNetServices.h NSObject IBFrameworkSource Foundation.framework/Headers/NSObject.h NSObject IBFrameworkSource Foundation.framework/Headers/NSPort.h NSObject IBFrameworkSource Foundation.framework/Headers/NSRunLoop.h NSObject IBFrameworkSource Foundation.framework/Headers/NSStream.h NSObject IBFrameworkSource Foundation.framework/Headers/NSThread.h NSObject IBFrameworkSource Foundation.framework/Headers/NSURL.h NSObject IBFrameworkSource Foundation.framework/Headers/NSURLConnection.h NSObject IBFrameworkSource Foundation.framework/Headers/NSXMLParser.h NSObject IBFrameworkSource UIKit.framework/Headers/UIAccessibility.h NSObject IBFrameworkSource UIKit.framework/Headers/UINibLoading.h NSObject IBFrameworkSource UIKit.framework/Headers/UIResponder.h UIImageView UIView IBFrameworkSource UIKit.framework/Headers/UIImageView.h UIResponder NSObject UISearchBar UIView IBFrameworkSource UIKit.framework/Headers/UISearchBar.h UISearchDisplayController NSObject IBFrameworkSource UIKit.framework/Headers/UISearchDisplayController.h UIView IBFrameworkSource UIKit.framework/Headers/UITextField.h UIView UIResponder IBFrameworkSource UIKit.framework/Headers/UIView.h UIViewController IBFrameworkSource UIKit.framework/Headers/UINavigationController.h UIViewController IBFrameworkSource UIKit.framework/Headers/UITabBarController.h UIViewController UIResponder IBFrameworkSource UIKit.framework/Headers/UIViewController.h 0 com.apple.InterfaceBuilder.CocoaTouchPlugin.InterfaceBuilder3 YES SpyPhone.xcodeproj 3 3.1 ================================================ FILE: SPSourceTVC.xib ================================================ 784 10C540 740 1038.25 458.00 com.apple.InterfaceBuilder.IBCocoaTouchPlugin 62 YES YES com.apple.InterfaceBuilder.IBCocoaTouchPlugin YES YES YES YES IBFilesOwner IBFirstResponder 274 {320, 247} 3 MQA NO YES NO NO 1 0 YES 44 22 22 YES dataSource 3 delegate 4 view 5 YES 0 -1 File's Owner -2 2 YES YES -1.CustomClassName -2.CustomClassName 2.IBPluginDependency YES SPSourceTVC UIResponder com.apple.InterfaceBuilder.IBCocoaTouchPlugin YES YES YES YES 5 YES SPSourceTVC UITableViewController webView SPWebViewVC IBUserSource SPWebViewVC UIViewController webView UIWebView IBUserSource YES NSObject IBFrameworkSource Foundation.framework/Headers/NSError.h NSObject IBFrameworkSource Foundation.framework/Headers/NSFileManager.h NSObject IBFrameworkSource Foundation.framework/Headers/NSKeyValueCoding.h NSObject IBFrameworkSource Foundation.framework/Headers/NSKeyValueObserving.h NSObject IBFrameworkSource Foundation.framework/Headers/NSKeyedArchiver.h NSObject IBFrameworkSource Foundation.framework/Headers/NSNetServices.h NSObject IBFrameworkSource Foundation.framework/Headers/NSObject.h NSObject IBFrameworkSource Foundation.framework/Headers/NSPort.h NSObject IBFrameworkSource Foundation.framework/Headers/NSRunLoop.h NSObject IBFrameworkSource Foundation.framework/Headers/NSStream.h NSObject IBFrameworkSource Foundation.framework/Headers/NSThread.h NSObject IBFrameworkSource Foundation.framework/Headers/NSURL.h NSObject IBFrameworkSource Foundation.framework/Headers/NSURLConnection.h NSObject IBFrameworkSource Foundation.framework/Headers/NSXMLParser.h NSObject IBFrameworkSource UIKit.framework/Headers/UIAccessibility.h NSObject IBFrameworkSource UIKit.framework/Headers/UINibLoading.h NSObject IBFrameworkSource UIKit.framework/Headers/UIResponder.h UIResponder NSObject UIScrollView UIView IBFrameworkSource UIKit.framework/Headers/UIScrollView.h UISearchBar UIView IBFrameworkSource UIKit.framework/Headers/UISearchBar.h UISearchDisplayController NSObject IBFrameworkSource UIKit.framework/Headers/UISearchDisplayController.h UITableView UIScrollView IBFrameworkSource UIKit.framework/Headers/UITableView.h UITableViewController UIViewController IBFrameworkSource UIKit.framework/Headers/UITableViewController.h UIView IBFrameworkSource UIKit.framework/Headers/UITextField.h UIView UIResponder IBFrameworkSource UIKit.framework/Headers/UIView.h UIViewController IBFrameworkSource UIKit.framework/Headers/UINavigationController.h UIViewController IBFrameworkSource UIKit.framework/Headers/UITabBarController.h UIViewController UIResponder IBFrameworkSource UIKit.framework/Headers/UIViewController.h UIWebView UIView IBFrameworkSource UIKit.framework/Headers/UIWebView.h 0 com.apple.InterfaceBuilder.CocoaTouchPlugin.InterfaceBuilder3 YES MyData.xcodeproj 3 3.1 ================================================ FILE: SPWebViewVC.xib ================================================ 784 10C540 740 1038.25 458.00 com.apple.InterfaceBuilder.IBCocoaTouchPlugin 62 YES YES com.apple.InterfaceBuilder.IBCocoaTouchPlugin YES YES YES YES IBFilesOwner IBFirstResponder 292 YES 274 {320, 460} 1 MSAxIDEAA YES YES 1 YES {320, 460} 3 MQA 2 NO YES webView 5 view 6 YES 0 -1 File's Owner -2 3 YES 4 YES YES -1.CustomClassName -2.CustomClassName 3.IBEditorWindowLastContentRect 3.IBPluginDependency 4.IBPluginDependency YES SPWebViewVC UIResponder {{315, 29}, {320, 460}} com.apple.InterfaceBuilder.IBCocoaTouchPlugin com.apple.InterfaceBuilder.IBCocoaTouchPlugin YES YES YES YES 6 YES SPWebViewVC UIViewController webView UIWebView IBUserSource YES NSObject IBFrameworkSource Foundation.framework/Headers/NSError.h NSObject IBFrameworkSource Foundation.framework/Headers/NSFileManager.h NSObject IBFrameworkSource Foundation.framework/Headers/NSKeyValueCoding.h NSObject IBFrameworkSource Foundation.framework/Headers/NSKeyValueObserving.h NSObject IBFrameworkSource Foundation.framework/Headers/NSKeyedArchiver.h NSObject IBFrameworkSource Foundation.framework/Headers/NSNetServices.h NSObject IBFrameworkSource Foundation.framework/Headers/NSObject.h NSObject IBFrameworkSource Foundation.framework/Headers/NSPort.h NSObject IBFrameworkSource Foundation.framework/Headers/NSRunLoop.h NSObject IBFrameworkSource Foundation.framework/Headers/NSStream.h NSObject IBFrameworkSource Foundation.framework/Headers/NSThread.h NSObject IBFrameworkSource Foundation.framework/Headers/NSURL.h NSObject IBFrameworkSource Foundation.framework/Headers/NSURLConnection.h NSObject IBFrameworkSource Foundation.framework/Headers/NSXMLParser.h NSObject IBFrameworkSource UIKit.framework/Headers/UIAccessibility.h NSObject IBFrameworkSource UIKit.framework/Headers/UINibLoading.h NSObject IBFrameworkSource UIKit.framework/Headers/UIResponder.h UIResponder NSObject UISearchBar UIView IBFrameworkSource UIKit.framework/Headers/UISearchBar.h UISearchDisplayController NSObject IBFrameworkSource UIKit.framework/Headers/UISearchDisplayController.h UIView IBFrameworkSource UIKit.framework/Headers/UITextField.h UIView UIResponder IBFrameworkSource UIKit.framework/Headers/UIView.h UIViewController IBFrameworkSource UIKit.framework/Headers/UINavigationController.h UIViewController IBFrameworkSource UIKit.framework/Headers/UITabBarController.h UIViewController UIResponder IBFrameworkSource UIKit.framework/Headers/UIViewController.h UIWebView UIView IBFrameworkSource UIKit.framework/Headers/UIWebView.h 0 com.apple.InterfaceBuilder.CocoaTouchPlugin.InterfaceBuilder3 YES MyData.xcodeproj 3 3.1 ================================================ FILE: Settings.bundle/Root.plist ================================================ StringsTable Root PreferenceSpecifiers Type PSToggleSwitchSpecifier Title TV OUT Key TVOutEnabled DefaultValue ================================================ FILE: Sources.xib ================================================ 1280 11C74 1938 1138.23 567.00 com.apple.InterfaceBuilder.IBCocoaTouchPlugin 933 YES IBUITableViewController IBUITableView IBUIViewController IBProxyObject YES com.apple.InterfaceBuilder.IBCocoaTouchPlugin PluginDependencyRecalculationVersion YES IBFilesOwner IBCocoaTouchFramework IBFirstResponder IBCocoaTouchFramework 274 {320, 247} 3 MQA NO YES NO 3 3 IBCocoaTouchFramework NO 1 0 YES 44 22 22 Email Accounts SPSourceTVC 1 1 IBCocoaTouchFramework NO SPWebViewVC 1 1 IBCocoaTouchFramework NO Wifi SPSourceTVC 1 1 IBCocoaTouchFramework NO Phone SPSourceTVC 1 1 IBCocoaTouchFramework NO Location SPSourceTVC 1 1 IBCocoaTouchFramework NO Photos SPSourceTVC 1 1 IBCocoaTouchFramework NO AddressBook SPSourceTVC 1 1 IBCocoaTouchFramework NO Keyboard Cache SPSourceTVC 1 1 IBCocoaTouchFramework NO Map SPImageMapVC 1 1 IBCocoaTouchFramework NO SPImageVC 1 1 IBCocoaTouchFramework NO SPWifiMapVC 1 1 IBCocoaTouchFramework NO YES view 9 sourceEmailTVC 72 sourceWifiTVC 73 sourcePhoneTVC 74 sourceLocationTVC 75 sourcePhotosTVC 78 sourceAddressBookTVC 79 sourceKeyboardTVC 80 dataSource 10 delegate 11 mapVC 93 mapVC 88 imageVC 91 imageVC 90 YES 0 YES -1 File's Owner -2 8 13 YES 49 60 YES 64 65 68 69 70 86 YES 89 92 YES YES -1.CustomClassName -1.IBPluginDependency -2.CustomClassName -2.IBPluginDependency 13.CustomClassName 13.IBPluginDependency 49.CustomClassName 49.IBPluginDependency 60.CustomClassName 60.IBPluginDependency 64.CustomClassName 64.IBPluginDependency 65.CustomClassName 65.IBPluginDependency 68.CustomClassName 68.IBPluginDependency 69.CustomClassName 69.IBPluginDependency 70.CustomClassName 70.IBPluginDependency 8.IBPluginDependency 86.CustomClassName 86.IBPluginDependency 89.CustomClassName 89.IBPluginDependency 92.CustomClassName 92.IBPluginDependency YES SPAllSourcesTVC com.apple.InterfaceBuilder.IBCocoaTouchPlugin UIResponder com.apple.InterfaceBuilder.IBCocoaTouchPlugin SPSourceEmailTVC com.apple.InterfaceBuilder.IBCocoaTouchPlugin SPWebViewVC com.apple.InterfaceBuilder.IBCocoaTouchPlugin SPSourceWifiTVC com.apple.InterfaceBuilder.IBCocoaTouchPlugin SPSourcePhoneTVC com.apple.InterfaceBuilder.IBCocoaTouchPlugin SPSourceLocationTVC com.apple.InterfaceBuilder.IBCocoaTouchPlugin SPSourcePhotosTVC com.apple.InterfaceBuilder.IBCocoaTouchPlugin SPSourceAddressBookTVC com.apple.InterfaceBuilder.IBCocoaTouchPlugin SPSourceKeyboardTVC com.apple.InterfaceBuilder.IBCocoaTouchPlugin com.apple.InterfaceBuilder.IBCocoaTouchPlugin SPImageMapVC com.apple.InterfaceBuilder.IBCocoaTouchPlugin SPImageVC com.apple.InterfaceBuilder.IBCocoaTouchPlugin SPWifiMapVC com.apple.InterfaceBuilder.IBCocoaTouchPlugin YES YES 93 YES SPAllSourcesTVC UITableViewController YES YES sourceAddressBookTVC sourceEmailTVC sourceKeyboardTVC sourceLocationTVC sourcePhoneTVC sourcePhotosTVC sourceWifiTVC YES SPSourceAddressBookTVC SPSourceEmailTVC SPSourceKeyboardTVC SPSourceLocationTVC SPSourcePhoneTVC SPSourcePhotosTVC SPSourceWifiTVC YES YES sourceAddressBookTVC sourceEmailTVC sourceKeyboardTVC sourceLocationTVC sourcePhoneTVC sourcePhotosTVC sourceWifiTVC YES sourceAddressBookTVC SPSourceAddressBookTVC sourceEmailTVC SPSourceEmailTVC sourceKeyboardTVC SPSourceKeyboardTVC sourceLocationTVC SPSourceLocationTVC sourcePhoneTVC SPSourcePhoneTVC sourcePhotosTVC SPSourcePhotosTVC sourceWifiTVC SPSourceWifiTVC IBProjectSource ./Classes/SPAllSourcesTVC.h SPImageMapVC UIViewController YES YES imageVC mapView YES SPImageVC MKMapView YES YES imageVC mapView YES imageVC SPImageVC mapView MKMapView IBProjectSource ./Classes/SPImageMapVC.h SPImageVC UIViewController imageView UIImageView imageView imageView UIImageView IBProjectSource ./Classes/SPImageVC.h SPSourceAddressBookTVC SPSourceTVC IBProjectSource ./Classes/SPSourceAddressBookTVC.h SPSourceEmailTVC SPSourceTVC IBProjectSource ./Classes/SPSourceEmailTVC.h SPSourceKeyboardTVC SPSourceTVC IBProjectSource ./Classes/SPSourceKeyboardTVC.h SPSourceLocationTVC SPSourceTVC IBProjectSource ./Classes/SPSourceLocationTVC.h SPSourcePhoneTVC SPSourceTVC IBProjectSource ./Classes/SPSourcePhoneTVC.h SPSourcePhotosTVC SPSourceTVC YES YES imageVC mapVC YES SPImageVC SPImageMapVC YES YES imageVC mapVC YES imageVC SPImageVC mapVC SPImageMapVC IBProjectSource ./Classes/SPSourcePhotosTVC.h SPSourceTVC UITableViewController IBProjectSource ./Classes/SPSourceTVC.h SPSourceWifiTVC SPSourceTVC mapVC SPWifiMapVC mapVC mapVC SPWifiMapVC IBProjectSource ./Classes/SPSourceWifiTVC.h SPWebViewVC UIViewController webView UIWebView webView webView UIWebView IBProjectSource ./Classes/SPWebViewVC.h SPWifiMapVC UIViewController mapView MKMapView mapView mapView MKMapView IBProjectSource ./Classes/SPWifiMapVC.h 0 IBCocoaTouchFramework com.apple.InterfaceBuilder.CocoaTouchPlugin.iPhoneOS com.apple.InterfaceBuilder.CocoaTouchPlugin.InterfaceBuilder3 YES 3 933 ================================================ FILE: SpyPhone-Info.plist ================================================ CFBundleDevelopmentRegion English CFBundleDisplayName ${PRODUCT_NAME} CFBundleExecutable ${EXECUTABLE_NAME} CFBundleIconFile CFBundleIdentifier ch.seriot.${PRODUCT_NAME:rfc1034identifier} CFBundleInfoDictionaryVersion 6.0 CFBundleName ${PRODUCT_NAME} CFBundlePackageType APPL CFBundleSignature ???? CFBundleVersion 1.0 LSRequiresIPhoneOS NSMainNibFile MainWindow CFBundleShortVersionString UIPrerenderedIcon YES ================================================ FILE: SpyPhone.xcodeproj/nst.pbxuser ================================================ // !$*UTF8*$! { 030AFB2B127D08BF00C9E0C6 /* SPWifiMapVC.h */ = { uiCtxt = { sepNavIntBoundsRect = "{{0, 0}, {705, 325}}"; sepNavSelRange = "{225, 0}"; sepNavVisRange = "{0, 498}"; }; }; 030AFB2C127D08BF00C9E0C6 /* SPWifiMapVC.m */ = { uiCtxt = { sepNavIntBoundsRect = "{{0, 0}, {1391, 1040}}"; sepNavSelRange = "{617, 0}"; sepNavVisRange = "{510, 776}"; }; }; 030AFBA2127D0D6F00C9E0C6 /* SPWifiAnnotation.h */ = { uiCtxt = { sepNavIntBoundsRect = "{{0, 0}, {1078, 411}}"; sepNavSelRange = "{187, 56}"; sepNavVisRange = "{0, 596}"; }; }; 030AFBA3127D0D6F00C9E0C6 /* SPWifiAnnotation.m */ = { uiCtxt = { sepNavIntBoundsRect = "{{0, 0}, {1078, 676}}"; sepNavSelRange = "{166, 33}"; sepNavVisRange = "{0, 812}"; }; }; 030AFF8F127D3AED00C9E0C6 /* PBXTextBookmark */ = { isa = PBXTextBookmark; fRef = 033805D1127CECF900EAFE64 /* NSString+SBJSON.m */; name = "NSString+SBJSON.m: 2"; rLen = 4; rLoc = 23; rType = 0; vrLen = 1618; vrLoc = 0; }; 030AFF90127D3AED00C9E0C6 /* PBXTextBookmark */ = { isa = PBXTextBookmark; name = "FirstViewController.h: 6"; rLen = 5; rLoc = 101; rType = 0; vrLen = 272; vrLoc = 0; }; 030AFF9A127D3BF000C9E0C6 /* PBXTextBookmark */ = { isa = PBXTextBookmark; fRef = 030AFB2C127D08BF00C9E0C6 /* SPWifiMapVC.m */; name = "SPWifiMapVC.m: 34"; rLen = 0; rLoc = 617; rType = 0; vrLen = 776; vrLoc = 510; }; 0310C65310C4BE0800E7ACD2 /* SPImageVC.h */ = { uiCtxt = { sepNavIntBoundsRect = "{{0, 0}, {1078, 435}}"; sepNavSelRange = "{191, 50}"; sepNavVisRange = "{0, 351}"; }; }; 0310C65410C4BE0800E7ACD2 /* SPImageVC.m */ = { uiCtxt = { sepNavIntBoundsRect = "{{0, 0}, {1078, 559}}"; sepNavSelRange = "{0, 0}"; sepNavVisRange = "{78, 644}"; }; }; 0312C759133561EA00E99BA4 /* PBXTextBookmark */ = { isa = PBXTextBookmark; fRef = 5F0DA1BA12829A6E00CD3B56 /* TVOutManager.h */; name = "TVOutManager.h: 16"; rLen = 0; rLoc = 311; rType = 0; vrLen = 727; vrLoc = 0; }; 0312C75B133561EA00E99BA4 /* PlistBookmark */ = { isa = PlistBookmark; fRef = 8D1107310486CEB800E47090 /* SpyPhone-Info.plist */; fallbackIsa = PBXBookmark; isK = 0; kPath = ( UIPrerenderedIcon, ); name = "/Users/nst/Projects/SpyPhone/SpyPhone-Info.plist"; rLen = 0; rLoc = 9223372036854775808; }; 0312C764133561EB00E99BA4 /* PBXTextBookmark */ = { isa = PBXTextBookmark; fRef = 0328F80E10B0A8B70074A5A1 /* SPSourcePhotosTVC.m */; name = "SPSourcePhotosTVC.m: 149"; rLen = 0; rLoc = 4871; rType = 0; vrLen = 2267; vrLoc = 1618; }; 0312C7731335623A00E99BA4 /* PlistBookmark */ = { isa = PlistBookmark; fRef = 8D1107310486CEB800E47090 /* SpyPhone-Info.plist */; fallbackIsa = PBXBookmark; isK = 0; kPath = ( ); name = "/Users/nst/Projects/SpyPhone/SpyPhone-Info.plist"; rLen = 0; rLoc = 9223372036854775807; }; 0312C7741335623A00E99BA4 /* PBXTextBookmark */ = { isa = PBXTextBookmark; fRef = 0328F80E10B0A8B70074A5A1 /* SPSourcePhotosTVC.m */; name = "SPSourcePhotosTVC.m: 149"; rLen = 0; rLoc = 4871; rType = 0; vrLen = 2267; vrLoc = 1618; }; 031748B710B6E35C00B6116E /* SPEmailASAccount.h */ = { uiCtxt = { sepNavIntBoundsRect = "{{0, 0}, {1078, 635}}"; sepNavSelRange = "{0, 0}"; sepNavVisRange = "{0, 262}"; }; }; 031748B810B6E35C00B6116E /* SPEmailASAccount.m */ = { uiCtxt = { sepNavIntBoundsRect = "{{0, 0}, {1078, 527}}"; sepNavSelRange = "{457, 7}"; sepNavVisRange = "{0, 742}"; }; }; 031748BE10B6E63E00B6116E /* SPEmailAccount.h */ = { uiCtxt = { sepNavIntBoundsRect = "{{0, 0}, {1078, 540}}"; sepNavSelRange = "{846, 0}"; sepNavVisRange = "{0, 876}"; }; }; 031748BF10B6E63E00B6116E /* SPEmailAccount.m */ = { uiCtxt = { sepNavIntBoundsRect = "{{0, 0}, {1391, 728}}"; sepNavSelRange = "{1259, 0}"; sepNavVisRange = "{646, 655}"; }; }; 031748C510B6E98800B6116E /* SPEmailPOPAccount.h */ = { uiCtxt = { sepNavIntBoundsRect = "{{0, 0}, {1078, 465}}"; sepNavSelRange = "{221, 0}"; sepNavVisRange = "{0, 268}"; }; }; 031748C610B6E98800B6116E /* SPEmailPOPAccount.m */ = { uiCtxt = { sepNavIntBoundsRect = "{{0, 0}, {1078, 527}}"; sepNavSelRange = "{488, 7}"; sepNavVisRange = "{0, 726}"; }; }; 031748C810B6E9A000B6116E /* SPEmailIToolsAccount.h */ = { uiCtxt = { sepNavIntBoundsRect = "{{0, 0}, {1078, 550}}"; sepNavSelRange = "{0, 0}"; sepNavVisRange = "{0, 269}"; }; }; 031748C910B6E9A000B6116E /* SPEmailIToolsAccount.m */ = { uiCtxt = { sepNavIntBoundsRect = "{{0, 0}, {1391, 442}}"; sepNavSelRange = "{586, 0}"; sepNavVisRange = "{214, 722}"; }; }; 0317498410B6F3A300B6116E /* SPEmailGmailAccount.h */ = { uiCtxt = { sepNavIntBoundsRect = "{{0, 0}, {1078, 540}}"; sepNavSelRange = "{138, 27}"; sepNavVisRange = "{0, 268}"; }; }; 0317498510B6F3A300B6116E /* SPEmailGmailAccount.m */ = { uiCtxt = { sepNavIntBoundsRect = "{{0, 0}, {1078, 540}}"; sepNavSelRange = "{830, 7}"; sepNavVisRange = "{0, 917}"; }; }; 0317498710B6F49300B6116E /* SPEmailIMAPAccount.h */ = { uiCtxt = { sepNavIntBoundsRect = "{{0, 0}, {1078, 540}}"; sepNavSelRange = "{164, 0}"; sepNavVisRange = "{0, 266}"; }; }; 0317498810B6F49300B6116E /* SPEmailIMAPAccount.m */ = { uiCtxt = { sepNavIntBoundsRect = "{{0, 0}, {1078, 540}}"; sepNavSelRange = "{493, 7}"; sepNavVisRange = "{0, 732}"; }; }; 0328F4AB10B05E890074A5A1 /* SpyPhone */ = { isa = PBXExecutable; activeArgIndices = ( ); argumentStrings = ( ); autoAttachOnCrash = 1; breakpointsEnabled = 1; configStateDict = { }; customDataFormattersEnabled = 1; dataTipCustomDataFormattersEnabled = 1; dataTipShowTypeColumn = 1; dataTipSortType = 0; debuggerPlugin = GDBDebugging; disassemblyDisplayState = 0; dylibVariantSuffix = ""; enableDebugStr = 1; environmentEntries = ( ); executableSystemSymbolLevel = 0; executableUserSymbolLevel = 0; libgmallocEnabled = 0; name = SpyPhone; savedGlobals = { }; showTypeColumn = 0; sourceDirectories = ( ); variableFormatDictionary = { }; }; 0328F4B810B05E8E0074A5A1 /* Source Control */ = { isa = PBXSourceControlManager; fallbackIsa = XCSourceControlManager; isSCMEnabled = 0; scmConfiguration = { repositoryNamesForRoots = { "" = ""; }; }; }; 0328F4B910B05E8E0074A5A1 /* Code sense */ = { isa = PBXCodeSenseManager; indexTemplatePath = ""; }; 0328F4CD10B0614A0074A5A1 /* SPAllSourcesTVC.h */ = { uiCtxt = { sepNavIntBoundsRect = "{{0, 0}, {1078, 546}}"; sepNavSelRange = "{575, 16}"; sepNavVisRange = "{42, 1059}"; }; }; 0328F4CE10B0614A0074A5A1 /* SPAllSourcesTVC.m */ = { uiCtxt = { sepNavIntBoundsRect = "{{0, 0}, {1391, 1612}}"; sepNavSelRange = "{1522, 0}"; sepNavVisRange = "{1089, 449}"; }; }; 0328F50810B065530074A5A1 /* SPCell.h */ = { uiCtxt = { sepNavIntBoundsRect = "{{0, 0}, {1391, 247}}"; sepNavSelRange = "{164, 15}"; sepNavVisRange = "{0, 240}"; }; }; 0328F50910B065530074A5A1 /* SPCell.m */ = { uiCtxt = { sepNavIntBoundsRect = "{{0, 0}, {1078, 479}}"; sepNavSelRange = "{144, 23}"; sepNavVisRange = "{0, 173}"; }; }; 0328F56810B071110074A5A1 /* SPSourceEmailTVC.h */ = { uiCtxt = { sepNavIntBoundsRect = "{{0, 0}, {1078, 507}}"; sepNavSelRange = "{285, 0}"; sepNavVisRange = "{0, 389}"; }; }; 0328F56910B071110074A5A1 /* SPSourceEmailTVC.m */ = { uiCtxt = { sepNavIntBoundsRect = "{{0, 0}, {1078, 832}}"; sepNavSelRange = "{671, 0}"; sepNavVisRange = "{470, 1758}"; }; }; 0328F58F10B072870074A5A1 /* objc_exception_throw */ = { isa = PBXSymbolicBreakpoint; actions = ( ); breakpointStyle = 1; continueAfterActions = 0; countType = 0; delayBeforeContinue = 0; hitCount = 0; ignoreCount = 0; location = libobjc.A.dylib; modificationTime = 322160371.991012; originalNumberOfMultipleMatches = 1; state = 1; symbolName = objc_exception_throw; }; 0328F61710B07F360074A5A1 /* SPSourceWifiTVC.h */ = { uiCtxt = { sepNavIntBoundsRect = "{{0, 0}, {1078, 411}}"; sepNavSelRange = "{410, 11}"; sepNavVisRange = "{0, 608}"; }; }; 0328F61810B07F360074A5A1 /* SPSourceWifiTVC.m */ = { uiCtxt = { sepNavIntBoundsRect = "{{0, 0}, {1132, 1287}}"; sepNavSelRange = "{1971, 0}"; sepNavVisRange = "{1882, 855}"; }; }; 0328F6BA10B088DD0074A5A1 /* SPSourcePhoneTVC.h */ = { uiCtxt = { sepNavIntBoundsRect = "{{0, 0}, {1078, 520}}"; sepNavSelRange = "{1048, 0}"; sepNavVisRange = "{42, 1075}"; }; }; 0328F6BB10B088DD0074A5A1 /* SPSourcePhoneTVC.m */ = { uiCtxt = { sepNavIntBoundsRect = "{{0, 0}, {1391, 2821}}"; sepNavSelRange = "{4446, 0}"; sepNavVisRange = "{3845, 792}"; }; }; 0328F75010B09AA60074A5A1 /* SPSourceLocationTVC.h */ = { uiCtxt = { sepNavIntBoundsRect = "{{0, 0}, {1078, 494}}"; sepNavSelRange = "{526, 22}"; sepNavVisRange = "{0, 930}"; }; }; 0328F75110B09AA60074A5A1 /* SPSourceLocationTVC.m */ = { uiCtxt = { sepNavIntBoundsRect = "{{0, 0}, {1097, 1326}}"; sepNavSelRange = "{513, 524}"; sepNavVisRange = "{305, 1346}"; }; }; 0328F80D10B0A8B70074A5A1 /* SPSourcePhotosTVC.h */ = { uiCtxt = { sepNavIntBoundsRect = "{{0, 0}, {1078, 435}}"; sepNavSelRange = "{414, 218}"; sepNavVisRange = "{0, 639}"; }; }; 0328F80E10B0A8B70074A5A1 /* SPSourcePhotosTVC.m */ = { uiCtxt = { sepNavIntBoundsRect = "{{0, 0}, {1132, 1937}}"; sepNavSelRange = "{4871, 0}"; sepNavVisRange = "{1618, 2267}"; sepNavWindowFrame = "{{15, 4}, {994, 869}}"; }; }; 0328F8BE10B0B1AE0074A5A1 /* SPWebViewVC.h */ = { uiCtxt = { sepNavIntBoundsRect = "{{0, 0}, {1078, 435}}"; sepNavSelRange = "{364, 0}"; sepNavVisRange = "{0, 408}"; }; }; 0328F8BF10B0B1AE0074A5A1 /* SPWebViewVC.m */ = { uiCtxt = { sepNavIntBoundsRect = "{{0, 0}, {1078, 598}}"; sepNavSelRange = "{269, 529}"; sepNavVisRange = "{198, 607}"; }; }; 0328F95F10B0CB140074A5A1 /* SPSourceAddressBookTVC.h */ = { uiCtxt = { sepNavIntBoundsRect = "{{0, 0}, {1078, 483}}"; sepNavSelRange = "{239, 0}"; sepNavVisRange = "{0, 292}"; }; }; 0328F96010B0CB140074A5A1 /* SPSourceAddressBookTVC.m */ = { uiCtxt = { sepNavIntBoundsRect = "{{0, 0}, {1078, 910}}"; sepNavSelRange = "{349, 0}"; sepNavVisRange = "{0, 881}"; }; }; 032A7E0410B84C1800E7FB65 /* UIImage+GPS.h */ = { uiCtxt = { sepNavIntBoundsRect = "{{0, 0}, {1078, 467}}"; sepNavSelRange = "{0, 0}"; sepNavVisRange = "{0, 335}"; }; }; 032A7E0510B84C1800E7FB65 /* UIImage+GPS.m */ = { uiCtxt = { sepNavIntBoundsRect = "{{0, 0}, {1167, 546}}"; sepNavSelRange = "{94, 4}"; sepNavVisRange = "{0, 1457}"; }; }; 032A7EAF10B85E9C00E7FB65 /* SPImageMapVC.h */ = { uiCtxt = { sepNavIntBoundsRect = "{{0, 0}, {1078, 435}}"; sepNavSelRange = "{192, 26}"; sepNavVisRange = "{0, 604}"; }; }; 032A7EB010B85E9C00E7FB65 /* SPImageMapVC.m */ = { uiCtxt = { sepNavIntBoundsRect = "{{0, 0}, {1078, 1313}}"; sepNavSelRange = "{2717, 0}"; sepNavVisRange = "{17, 914}"; }; }; 032A7EC910B8617C00E7FB65 /* SPImageAnnotation.h */ = { uiCtxt = { sepNavIntBoundsRect = "{{0, 0}, {1078, 412}}"; sepNavSelRange = "{359, 0}"; sepNavVisRange = "{0, 692}"; }; }; 032A7ECA10B8617C00E7FB65 /* SPImageAnnotation.m */ = { uiCtxt = { sepNavIntBoundsRect = "{{0, 0}, {1078, 559}}"; sepNavSelRange = "{330, 0}"; sepNavVisRange = "{208, 681}"; }; }; 032A819510B8E7C600E7FB65 /* SPEmailReportVC.h */ = { uiCtxt = { sepNavIntBoundsRect = "{{0, 0}, {1391, 325}}"; sepNavSelRange = "{602, 0}"; sepNavVisRange = "{105, 497}"; }; }; 032A819610B8E7C600E7FB65 /* SPEmailReportVC.m */ = { uiCtxt = { sepNavIntBoundsRect = "{{0, 0}, {669, 1196}}"; sepNavSelRange = "{805, 0}"; sepNavVisRange = "{450, 537}"; }; }; 033805B6127CEC4200EAFE64 /* OUILookupTool.h */ = { uiCtxt = { sepNavIntBoundsRect = "{{0, 0}, {1078, 452}}"; sepNavSelRange = "{638, 0}"; sepNavVisRange = "{0, 638}"; }; }; 033805B7127CEC4200EAFE64 /* OUILookupTool.m */ = { uiCtxt = { sepNavIntBoundsRect = "{{0, 0}, {1078, 1339}}"; sepNavSelRange = "{132, 0}"; sepNavVisRange = "{0, 1042}"; }; }; 033805CC127CECF900EAFE64 /* JSON.h */ = { uiCtxt = { sepNavIntBoundsRect = "{{0, 0}, {1078, 793}}"; sepNavSelRange = "{18, 4}"; sepNavVisRange = "{0, 1725}"; }; }; 033805CE127CECF900EAFE64 /* NSObject+SBJSON.h */ = { uiCtxt = { sepNavIntBoundsRect = "{{0, 0}, {1078, 624}}"; sepNavSelRange = "{18, 4}"; sepNavVisRange = "{0, 1606}"; }; }; 033805D0127CECF900EAFE64 /* NSString+SBJSON.h */ = { uiCtxt = { sepNavIntBoundsRect = "{{0, 0}, {1078, 598}}"; sepNavSelRange = "{18, 4}"; sepNavVisRange = "{0, 1602}"; }; }; 033805D1127CECF900EAFE64 /* NSString+SBJSON.m */ = { uiCtxt = { sepNavIntBoundsRect = "{{0, 0}, {1078, 559}}"; sepNavSelRange = "{23, 4}"; sepNavVisRange = "{0, 1618}"; }; }; 0364933F10B16DDD00C88803 /* SPSourceKeyboardTVC.h */ = { uiCtxt = { sepNavIntBoundsRect = "{{0, 0}, {1078, 452}}"; sepNavSelRange = "{241, 0}"; sepNavVisRange = "{0, 241}"; }; }; 0364934010B16DDD00C88803 /* SPSourceKeyboardTVC.m */ = { uiCtxt = { sepNavIntBoundsRect = "{{0, 0}, {1078, 1664}}"; sepNavSelRange = "{1701, 101}"; sepNavVisRange = "{1297, 926}"; }; }; 0364948A10B28BC800C88803 /* SPSourceTVC.h */ = { uiCtxt = { sepNavIntBoundsRect = "{{0, 0}, {1078, 412}}"; sepNavSelRange = "{490, 0}"; sepNavVisRange = "{0, 497}"; }; }; 0364948B10B28BC800C88803 /* SPSourceTVC.m */ = { uiCtxt = { sepNavIntBoundsRect = "{{0, 0}, {1391, 1131}}"; sepNavSelRange = "{1427, 0}"; sepNavVisRange = "{1123, 521}"; }; }; 037D3A7F10F3D57B003A85B0 /* SPEmailMobileMeAccount.h */ = { uiCtxt = { sepNavIntBoundsRect = "{{0, 0}, {1078, 540}}"; sepNavSelRange = "{0, 0}"; sepNavVisRange = "{0, 274}"; }; }; 037D3A8010F3D57B003A85B0 /* SPEmailMobileMeAccount.m */ = { uiCtxt = { sepNavIntBoundsRect = "{{0, 0}, {1391, 481}}"; sepNavSelRange = "{856, 0}"; sepNavVisRange = "{396, 640}"; }; }; 037F2DF1128D2DCF00EE7E19 /* PBXBookmark */ = { isa = PBXBookmark; fRef = 032A811B10B883FB00E7FB65 /* Default.png */; }; 037F2DF2128D2DCF00EE7E19 /* PBXTextBookmark */ = { isa = PBXTextBookmark; fRef = 03B6EFC910BB547600CF9139 /* gpl-2.0.txt */; name = "gpl-2.0.txt: 15"; rLen = 0; rLoc = 653; rType = 0; vrLen = 1773; vrLoc = 0; }; 037F2DF3128D2DCF00EE7E19 /* PBXTextBookmark */ = { isa = PBXTextBookmark; fRef = 033805B6127CEC4200EAFE64 /* OUILookupTool.h */; name = "OUILookupTool.h: 27"; rLen = 0; rLoc = 638; rType = 0; vrLen = 638; vrLoc = 0; }; 037F2E13128D2E6500EE7E19 /* PBXTextBookmark */ = { isa = PBXTextBookmark; fRef = 033805B7127CEC4200EAFE64 /* OUILookupTool.m */; name = "OUILookupTool.m: 8"; rLen = 0; rLoc = 132; rType = 0; vrLen = 1042; vrLoc = 0; }; 037F2E34128D3E6D00EE7E19 /* PBXTextBookmark */ = { isa = PBXTextBookmark; fRef = 5F0DA1BB12829A6E00CD3B56 /* TVOutManager.m */; name = "TVOutManager.m: 20"; rLen = 0; rLoc = 418; rType = 0; vrLen = 823; vrLoc = 0; }; 03B6EFC910BB547600CF9139 /* gpl-2.0.txt */ = { uiCtxt = { sepNavIntBoundsRect = "{{0, 0}, {1078, 4459}}"; sepNavSelRange = "{653, 0}"; sepNavVisRange = "{0, 1773}"; }; }; 03F2481D1236EFAC0017F214 /* NSNumber+SP.h */ = { uiCtxt = { sepNavIntBoundsRect = "{{0, 0}, {1078, 460}}"; sepNavSelRange = "{202, 0}"; sepNavVisRange = "{0, 238}"; }; }; 03F2481E1236EFAC0017F214 /* NSNumber+SP.m */ = { uiCtxt = { sepNavIntBoundsRect = "{{0, 0}, {1391, 481}}"; sepNavSelRange = "{483, 0}"; sepNavVisRange = "{261, 470}"; }; }; 1D3623240D0F684500981E51 /* SpyPhoneAppDelegate.h */ = { uiCtxt = { sepNavIntBoundsRect = "{{0, 0}, {1160, 446}}"; sepNavSelRange = "{462, 0}"; sepNavVisRange = "{0, 462}"; }; }; 1D3623250D0F684500981E51 /* SpyPhoneAppDelegate.m */ = { uiCtxt = { sepNavIntBoundsRect = "{{0, 0}, {1391, 689}}"; sepNavSelRange = "{748, 0}"; sepNavVisRange = "{453, 683}"; }; }; 1D6058900D05DD3D006BFB54 /* SpyPhone */ = { activeExec = 0; executables = ( 0328F4AB10B05E890074A5A1 /* SpyPhone */, ); }; 28A0AB4B0D9B1048005BE974 /* SpyPhone_Prefix.pch */ = { uiCtxt = { sepNavIntBoundsRect = "{{0, 0}, {1078, 465}}"; sepNavSelRange = "{0, 0}"; sepNavVisRange = "{0, 185}"; }; }; 29B97313FDCFA39411CA2CEA /* Project object */ = { activeBuildConfigurationName = Debug; activeExecutable = 0328F4AB10B05E890074A5A1 /* SpyPhone */; activeSDKPreference = iphoneos4.3; activeTarget = 1D6058900D05DD3D006BFB54 /* SpyPhone */; addToTargets = ( 1D6058900D05DD3D006BFB54 /* SpyPhone */, ); breakpoints = ( 0328F58F10B072870074A5A1 /* objc_exception_throw */, ); codeSenseManager = 0328F4B910B05E8E0074A5A1 /* Code sense */; executables = ( 0328F4AB10B05E890074A5A1 /* SpyPhone */, ); perUserDictionary = { "PBXConfiguration.PBXBreakpointsDataSource.v1:1CA23EDF0692099D00951B8B" = { PBXFileTableDataSourceColumnSortingDirectionKey = "-1"; PBXFileTableDataSourceColumnSortingKey = PBXBreakpointsDataSource_BreakpointID; PBXFileTableDataSourceColumnWidthsKey = ( 20, 20, 250, 20, 250, 250, 248, 20, ); PBXFileTableDataSourceColumnsKey = ( PBXBreakpointsDataSource_ActionID, PBXBreakpointsDataSource_TypeID, PBXBreakpointsDataSource_BreakpointID, PBXBreakpointsDataSource_UseID, PBXBreakpointsDataSource_LocationID, PBXBreakpointsDataSource_ConditionID, PBXBreakpointsDataSource_IgnoreCountID, PBXBreakpointsDataSource_ContinueID, ); }; PBXConfiguration.PBXFileTableDataSource3.PBXBookmarksDataSource = { PBXFileTableDataSourceColumnSortingDirectionKey = "-1"; PBXFileTableDataSourceColumnSortingKey = PBXBookmarksDataSource_NameID; PBXFileTableDataSourceColumnWidthsKey = ( 200, 200, 697.58349609375, ); PBXFileTableDataSourceColumnsKey = ( PBXBookmarksDataSource_LocationID, PBXBookmarksDataSource_NameID, PBXBookmarksDataSource_CommentsID, ); }; PBXConfiguration.PBXFileTableDataSource3.PBXExecutablesDataSource = { PBXFileTableDataSourceColumnSortingDirectionKey = "-1"; PBXFileTableDataSourceColumnSortingKey = PBXExecutablesDataSource_NameID; PBXFileTableDataSourceColumnWidthsKey = ( 22, 300, 776, ); PBXFileTableDataSourceColumnsKey = ( PBXExecutablesDataSource_ActiveFlagID, PBXExecutablesDataSource_NameID, PBXExecutablesDataSource_CommentsID, ); }; PBXConfiguration.PBXFileTableDataSource3.PBXFileTableDataSource = { PBXFileTableDataSourceColumnSortingDirectionKey = "-1"; PBXFileTableDataSourceColumnSortingKey = PBXFileDataSource_Filename_ColumnID; PBXFileTableDataSourceColumnWidthsKey = ( 20, 888, 20, 48, 43, 43, 20, ); PBXFileTableDataSourceColumnsKey = ( PBXFileDataSource_FiletypeID, PBXFileDataSource_Filename_ColumnID, PBXFileDataSource_Built_ColumnID, PBXFileDataSource_ObjectSize_ColumnID, PBXFileDataSource_Errors_ColumnID, PBXFileDataSource_Warnings_ColumnID, PBXFileDataSource_Target_ColumnID, ); }; PBXConfiguration.PBXFileTableDataSource3.PBXFindDataSource = { PBXFileTableDataSourceColumnSortingDirectionKey = "-1"; PBXFileTableDataSourceColumnSortingKey = PBXFindDataSource_LocationID; PBXFileTableDataSourceColumnWidthsKey = ( 200, 902, ); PBXFileTableDataSourceColumnsKey = ( PBXFindDataSource_MessageID, PBXFindDataSource_LocationID, ); }; PBXConfiguration.PBXFileTableDataSource3.XCSCMDataSource = { PBXFileTableDataSourceColumnSortingDirectionKey = "-1"; PBXFileTableDataSourceColumnSortingKey = PBXFileDataSource_Filename_ColumnID; PBXFileTableDataSourceColumnWidthsKey = ( 20, 20, 864, 20, 48, 43, 43, 20, ); PBXFileTableDataSourceColumnsKey = ( PBXFileDataSource_SCM_ColumnID, PBXFileDataSource_FiletypeID, PBXFileDataSource_Filename_ColumnID, PBXFileDataSource_Built_ColumnID, PBXFileDataSource_ObjectSize_ColumnID, PBXFileDataSource_Errors_ColumnID, PBXFileDataSource_Warnings_ColumnID, PBXFileDataSource_Target_ColumnID, ); }; PBXConfiguration.PBXTargetDataSource.PBXTargetDataSource = { PBXFileTableDataSourceColumnSortingDirectionKey = "-1"; PBXFileTableDataSourceColumnSortingKey = PBXFileDataSource_Filename_ColumnID; PBXFileTableDataSourceColumnWidthsKey = ( 20, 848, 60, 20, 48, 43, 43, ); PBXFileTableDataSourceColumnsKey = ( PBXFileDataSource_FiletypeID, PBXFileDataSource_Filename_ColumnID, PBXTargetDataSource_PrimaryAttribute, PBXFileDataSource_Built_ColumnID, PBXFileDataSource_ObjectSize_ColumnID, PBXFileDataSource_Errors_ColumnID, PBXFileDataSource_Warnings_ColumnID, ); }; PBXPerProjectTemplateStateSaveDate = 322265618; PBXWorkspaceStateSaveDate = 322265618; }; perUserProjectItems = { 030AFF8F127D3AED00C9E0C6 /* PBXTextBookmark */ = 030AFF8F127D3AED00C9E0C6 /* PBXTextBookmark */; 030AFF90127D3AED00C9E0C6 /* PBXTextBookmark */ = 030AFF90127D3AED00C9E0C6 /* PBXTextBookmark */; 030AFF9A127D3BF000C9E0C6 /* PBXTextBookmark */ = 030AFF9A127D3BF000C9E0C6 /* PBXTextBookmark */; 0312C759133561EA00E99BA4 /* PBXTextBookmark */ = 0312C759133561EA00E99BA4 /* PBXTextBookmark */; 0312C75B133561EA00E99BA4 /* PlistBookmark */ = 0312C75B133561EA00E99BA4 /* PlistBookmark */; 0312C764133561EB00E99BA4 /* PBXTextBookmark */ = 0312C764133561EB00E99BA4 /* PBXTextBookmark */; 0312C7731335623A00E99BA4 /* PlistBookmark */ = 0312C7731335623A00E99BA4 /* PlistBookmark */; 0312C7741335623A00E99BA4 /* PBXTextBookmark */ = 0312C7741335623A00E99BA4 /* PBXTextBookmark */; 037F2DF1128D2DCF00EE7E19 /* PBXBookmark */ = 037F2DF1128D2DCF00EE7E19 /* PBXBookmark */; 037F2DF2128D2DCF00EE7E19 /* PBXTextBookmark */ = 037F2DF2128D2DCF00EE7E19 /* PBXTextBookmark */; 037F2DF3128D2DCF00EE7E19 /* PBXTextBookmark */ = 037F2DF3128D2DCF00EE7E19 /* PBXTextBookmark */; 037F2E13128D2E6500EE7E19 /* PBXTextBookmark */ = 037F2E13128D2E6500EE7E19 /* PBXTextBookmark */; 037F2E34128D3E6D00EE7E19 /* PBXTextBookmark */ = 037F2E34128D3E6D00EE7E19 /* PBXTextBookmark */; }; sourceControlManager = 0328F4B810B05E8E0074A5A1 /* Source Control */; userBuildSettings = { }; }; 29B97316FDCFA39411CA2CEA /* main.m */ = { uiCtxt = { sepNavIntBoundsRect = "{{0, 0}, {1078, 535}}"; sepNavSelRange = "{390, 0}"; sepNavVisRange = "{0, 390}"; }; }; 5F0DA1BA12829A6E00CD3B56 /* TVOutManager.h */ = { uiCtxt = { sepNavIntBoundsRect = "{{0, 0}, {1391, 520}}"; sepNavSelRange = "{311, 0}"; sepNavVisRange = "{0, 497}"; }; }; 5F0DA1BB12829A6E00CD3B56 /* TVOutManager.m */ = { uiCtxt = { sepNavIntBoundsRect = "{{0, 0}, {1078, 4264}}"; sepNavSelRange = "{418, 0}"; sepNavVisRange = "{0, 823}"; }; }; } ================================================ FILE: SpyPhone.xcodeproj/nst.perspectivev3 ================================================ ActivePerspectiveName Project AllowedModules BundleLoadPath MaxInstances n Module PBXSmartGroupTreeModule Name Groups and Files Outline View BundleLoadPath MaxInstances n Module PBXNavigatorGroup Name Editor BundleLoadPath MaxInstances n Module XCTaskListModule Name Task List BundleLoadPath MaxInstances n Module XCDetailModule Name File and Smart Group Detail Viewer BundleLoadPath MaxInstances 1 Module PBXBuildResultsModule Name Detailed Build Results Viewer BundleLoadPath MaxInstances 1 Module PBXProjectFindModule Name Project Batch Find Tool BundleLoadPath MaxInstances n Module XCProjectFormatConflictsModule Name Project Format Conflicts List BundleLoadPath MaxInstances n Module PBXBookmarksModule Name Bookmarks Tool BundleLoadPath MaxInstances n Module PBXClassBrowserModule Name Class Browser BundleLoadPath MaxInstances n Module PBXCVSModule Name Source Code Control Tool BundleLoadPath MaxInstances n Module PBXDebugBreakpointsModule Name Debug Breakpoints Tool BundleLoadPath MaxInstances n Module XCDockableInspector Name Inspector BundleLoadPath MaxInstances n Module PBXOpenQuicklyModule Name Open Quickly Tool BundleLoadPath MaxInstances 1 Module PBXDebugSessionModule Name Debugger BundleLoadPath MaxInstances 1 Module PBXDebugCLIModule Name Debug Console BundleLoadPath MaxInstances n Module XCSnapshotModule Name Snapshots Tool BundlePath /Developer/Library/PrivateFrameworks/DevToolsInterface.framework/Resources Description AIODescriptionKey DockingSystemVisible Extension perspectivev3 FavBarConfig PBXProjectModuleGUID 0328F4B710B05E8E0074A5A1 XCBarModuleItemNames XCBarModuleItems FirstTimeWindowDisplayed Identifier com.apple.perspectives.project.defaultV3 MajorVersion 34 MinorVersion 0 Name All-In-One Notifications XCObserverAutoDisconnectKey XCObserverDefintionKey XCObserverFactoryKey XCPerspectivesSpecificationIdentifier XCObserverGUIDKey XCObserverProjectIdentifier XCObserverNotificationKey PBXStatusBuildStateMessageNotification XCObserverTargetKey XCMainBuildResultsModuleGUID XCObserverTriggerKey awakenModuleWithObserver: XCObserverValidationKey OpenEditors Content PBXProjectModuleGUID 03F248D81237A3EE0017F214 PBXProjectModuleLabel SPSourcePhotosTVC.m PBXSplitModuleInNavigatorKey Split0 PBXProjectModuleGUID 03F248D91237A3EE0017F214 PBXProjectModuleLabel SPSourcePhotosTVC.m _historyCapacity 10 bookmark 0312C7741335623A00E99BA4 history 0312C764133561EB00E99BA4 SplitCount 1 StatusBarVisibility Geometry Frame {{0, 20}, {994, 772}} PBXModuleWindowStatusBarHidden2 RubberWindowFrame 15 60 994 813 0 0 1440 878 PerspectiveWidths 1440 1440 Perspectives ChosenToolbarItems XCToolbarPerspectiveControl NSToolbarSeparatorItem active-combo-popup active-target-popup action NSToolbarFlexibleSpaceItem debugger-enable-breakpoints build-and-go com.apple.ide.PBXToolbarStopButton get-info toggle-editor NSToolbarFlexibleSpaceItem com.apple.pbx.toolbar.searchfield ControllerClassBaseName IconName WindowOfProject Identifier perspective.project IsVertical Layout ContentConfiguration PBXBottomSmartGroupGIDs 1C37FBAC04509CD000000102 1C37FAAC04509CD000000102 1C37FABC05509CD000000102 1C37FABC05539CD112110102 E2644B35053B69B200211256 1C37FABC04509CD000100104 1CC0EA4004350EF90044410B 1CC0EA4004350EF90041110B 1C77FABC04509CD000000102 PBXProjectModuleGUID 1CA23ED40692098700951B8B PBXProjectModuleLabel Files PBXProjectStructureProvided yes PBXSmartGroupTreeModuleColumnData PBXSmartGroupTreeModuleColumnWidthsKey 22 269 PBXSmartGroupTreeModuleColumnsKey_v4 TargetStatusColumn MainColumn PBXSmartGroupTreeModuleOutlineStateKey_v7 PBXSmartGroupTreeModuleOutlineStateExpansionKey 29B97314FDCFA39411CA2CEA 080E96DDFE201D6D7F000001 033805B5127CEC4200EAFE64 031748C210B6E68E00B6116E 29B97315FDCFA39411CA2CEA 29B97317FDCFA39411CA2CEA 0317DE7B10FF98E900C5C2D4 29B97323FDCFA39411CA2CEA 19C28FACFE9D520D11CA2CBB 1C37FBAC04509CD000000102 1C37FABC05509CD000000102 1C77FABC04509CD000000102 PBXSmartGroupTreeModuleOutlineStateSelectionKey 66 54 0 PBXSmartGroupTreeModuleOutlineStateVisibleRectKey {{0, 826}, {291, 712}} PBXTopSmartGroupGIDs XCIncludePerspectivesSwitch GeometryConfiguration Frame {{0, 0}, {308, 730}} GroupTreeTableConfiguration TargetStatusColumn 22 MainColumn 269 RubberWindowFrame 0 107 1440 771 0 0 1440 878 Module PBXSmartGroupTreeModule Proportion 308pt Dock ContentConfiguration PBXProjectModuleGUID 0328F4B210B05E8E0074A5A1 PBXProjectModuleLabel SpyPhone-Info.plist PBXSplitModuleInNavigatorKey Split0 PBXProjectModuleGUID 0328F4B310B05E8E0074A5A1 PBXProjectModuleLabel SpyPhone-Info.plist _historyCapacity 10 bookmark 0312C7731335623A00E99BA4 history 030AFF8F127D3AED00C9E0C6 030AFF90127D3AED00C9E0C6 030AFF9A127D3BF000C9E0C6 037F2DF1128D2DCF00EE7E19 037F2DF2128D2DCF00EE7E19 037F2DF3128D2DCF00EE7E19 037F2E13128D2E6500EE7E19 037F2E34128D3E6D00EE7E19 0312C759133561EA00E99BA4 0312C75B133561EA00E99BA4 SplitCount 1 StatusBarVisibility XCSharingToken com.apple.Xcode.CommonNavigatorGroupSharingToken GeometryConfiguration Frame {{0, 0}, {1127, 463}} RubberWindowFrame 0 107 1440 771 0 0 1440 878 Module PBXNavigatorGroup Proportion 463pt Proportion 262pt Tabs ContentConfiguration PBXProjectModuleGUID 1CA23EDF0692099D00951B8B PBXProjectModuleLabel Detail GeometryConfiguration Frame {{10, 27}, {1127, 235}} RubberWindowFrame 0 107 1440 771 0 0 1440 878 Module XCDetailModule ContentConfiguration PBXProjectModuleGUID 1CA23EE00692099D00951B8B PBXProjectModuleLabel Project Find GeometryConfiguration Frame {{10, 27}, {1127, 207}} Module PBXProjectFindModule ContentConfiguration PBXCVSModuleFilterTypeKey 1032 PBXProjectModuleGUID 1CA23EE10692099D00951B8B PBXProjectModuleLabel SCM Results GeometryConfiguration Frame {{10, 31}, {603, 297}} Module PBXCVSModule ContentConfiguration PBXProjectModuleGUID XCMainBuildResultsModuleGUID PBXProjectModuleLabel Build Results XCBuildResultsTrigger_Collapse 1021 XCBuildResultsTrigger_Open 1010 GeometryConfiguration Frame {{10, 27}, {1127, 221}} Module PBXBuildResultsModule Proportion 1127pt Name Project ServiceClasses XCModuleDock PBXSmartGroupTreeModule XCModuleDock PBXNavigatorGroup XCDockableTabModule XCDetailModule PBXProjectFindModule PBXCVSModule PBXBuildResultsModule TableOfContents 0312C76F1335621A00E99BA4 1CA23ED40692098700951B8B 0312C7701335621A00E99BA4 0328F4B210B05E8E0074A5A1 0312C7711335621A00E99BA4 1CA23EDF0692099D00951B8B 1CA23EE00692099D00951B8B 1CA23EE10692099D00951B8B XCMainBuildResultsModuleGUID ToolbarConfigUserDefaultsMinorVersion 2 ToolbarConfiguration xcode.toolbar.config.defaultV3 ChosenToolbarItems XCToolbarPerspectiveControl NSToolbarSeparatorItem active-combo-popup NSToolbarFlexibleSpaceItem debugger-enable-breakpoints build-and-go com.apple.ide.PBXToolbarStopButton debugger-restart-executable debugger-pause debugger-step-over debugger-step-into debugger-step-out NSToolbarFlexibleSpaceItem servicesModulebreakpoints debugger-show-console-window ControllerClassBaseName PBXDebugSessionModule IconName DebugTabIcon Identifier perspective.debug IsVertical Layout ContentConfiguration PBXProjectModuleGUID 1CCC7628064C1048000F2A68 PBXProjectModuleLabel Debugger Console GeometryConfiguration Frame {{0, 0}, {1440, 155}} Module PBXDebugCLIModule Proportion 155pt ContentConfiguration Debugger HorizontalSplitView _collapsingFrameDimension 0.0 _indexOfCollapsedView 0 _percentageOfCollapsedView 0.0 isCollapsed yes sizes {{0, 0}, {703, 190}} {{703, 0}, {737, 190}} VerticalSplitView _collapsingFrameDimension 0.0 _indexOfCollapsedView 0 _percentageOfCollapsedView 0.0 isCollapsed yes sizes {{0, 0}, {1440, 190}} {{0, 190}, {1440, 380}} LauncherConfigVersion 8 PBXProjectModuleGUID 1CCC7629064C1048000F2A68 PBXProjectModuleLabel Debug GeometryConfiguration DebugConsoleVisible None DebugConsoleWindowFrame {{200, 200}, {500, 300}} DebugSTDIOWindowFrame {{200, 200}, {500, 300}} Frame {{0, 160}, {1440, 570}} PBXDebugSessionStackFrameViewKey DebugVariablesTableConfiguration Name 120 Value 85 Summary 507 Frame {{703, 0}, {737, 190}} Module PBXDebugSessionModule Proportion 570pt Name Debug ServiceClasses XCModuleDock PBXDebugCLIModule PBXDebugSessionModule PBXDebugProcessAndThreadModule PBXDebugProcessViewModule PBXDebugThreadViewModule PBXDebugStackFrameViewModule PBXNavigatorGroup TableOfContents 0312C75F133561EB00E99BA4 1CCC7628064C1048000F2A68 1CCC7629064C1048000F2A68 0312C760133561EB00E99BA4 0312C761133561EB00E99BA4 0312C762133561EB00E99BA4 0312C763133561EB00E99BA4 0328F4B210B05E8E0074A5A1 ToolbarConfigUserDefaultsMinorVersion 2 ToolbarConfiguration xcode.toolbar.config.debugV3 PerspectivesBarVisible ShelfIsVisible SourceDescription file at '/Developer/Library/PrivateFrameworks/DevToolsInterface.framework/Resources/XCPerspectivesSpecification.xcperspec' StatusbarIsVisible TimeStamp 322265658.83325499 ToolbarConfigUserDefaultsMinorVersion 2 ToolbarDisplayMode 1 ToolbarIsVisible ToolbarSizeMode 1 Type Perspectives UpdateMessage WindowJustification 5 WindowOrderList 03F248D81237A3EE0017F214 /Users/nst/Projects/SpyPhone/SpyPhone.xcodeproj WindowString 0 107 1440 771 0 0 1440 878 WindowToolsV3 Identifier windowTool.debugger Layout Dock ContentConfiguration Debugger HorizontalSplitView _collapsingFrameDimension 0.0 _indexOfCollapsedView 0 _percentageOfCollapsedView 0.0 isCollapsed yes sizes {{0, 0}, {317, 164}} {{317, 0}, {377, 164}} VerticalSplitView _collapsingFrameDimension 0.0 _indexOfCollapsedView 0 _percentageOfCollapsedView 0.0 isCollapsed yes sizes {{0, 0}, {694, 164}} {{0, 164}, {694, 216}} LauncherConfigVersion 8 PBXProjectModuleGUID 1C162984064C10D400B95A72 PBXProjectModuleLabel Debug - GLUTExamples (Underwater) GeometryConfiguration DebugConsoleDrawerSize {100, 120} DebugConsoleVisible None DebugConsoleWindowFrame {{200, 200}, {500, 300}} DebugSTDIOWindowFrame {{200, 200}, {500, 300}} Frame {{0, 0}, {694, 380}} RubberWindowFrame 321 238 694 422 0 0 1440 878 Module PBXDebugSessionModule Proportion 100% Proportion 100% Name Debugger ServiceClasses PBXDebugSessionModule StatusbarIsVisible 1 TableOfContents 1CD10A99069EF8BA00B06720 1C0AD2AB069F1E9B00FABCE6 1C162984064C10D400B95A72 1C0AD2AC069F1E9B00FABCE6 ToolbarConfiguration xcode.toolbar.config.debugV3 WindowString 321 238 694 422 0 0 1440 878 WindowToolGUID 1CD10A99069EF8BA00B06720 WindowToolIsVisible 0 Identifier windowTool.build Layout Dock ContentConfiguration PBXProjectModuleGUID 1CD0528F0623707200166675 PBXProjectModuleLabel <No Editor> PBXSplitModuleInNavigatorKey Split0 PBXProjectModuleGUID 1CD052900623707200166675 SplitCount 1 StatusBarVisibility 1 GeometryConfiguration Frame {{0, 0}, {500, 215}} RubberWindowFrame 192 257 500 500 0 0 1280 1002 Module PBXNavigatorGroup Proportion 218pt BecomeActive 1 ContentConfiguration PBXProjectModuleGUID XCMainBuildResultsModuleGUID PBXProjectModuleLabel Build Results GeometryConfiguration Frame {{0, 222}, {500, 236}} RubberWindowFrame 192 257 500 500 0 0 1280 1002 Module PBXBuildResultsModule Proportion 236pt Proportion 458pt Name Build Results ServiceClasses PBXBuildResultsModule StatusbarIsVisible 1 TableOfContents 1C78EAA5065D492600B07095 1C78EAA6065D492600B07095 1CD0528F0623707200166675 XCMainBuildResultsModuleGUID ToolbarConfiguration xcode.toolbar.config.buildV3 WindowString 192 257 500 500 0 0 1280 1002 Identifier windowTool.find Layout Dock Dock ContentConfiguration PBXProjectModuleGUID 1CDD528C0622207200134675 PBXProjectModuleLabel <No Editor> PBXSplitModuleInNavigatorKey Split0 PBXProjectModuleGUID 1CD0528D0623707200166675 SplitCount 1 StatusBarVisibility 1 GeometryConfiguration Frame {{0, 0}, {781, 167}} RubberWindowFrame 62 385 781 470 0 0 1440 878 Module PBXNavigatorGroup Proportion 781pt Proportion 50% BecomeActive 1 ContentConfiguration PBXProjectModuleGUID 1CD0528E0623707200166675 PBXProjectModuleLabel Project Find GeometryConfiguration Frame {{8, 0}, {773, 254}} RubberWindowFrame 62 385 781 470 0 0 1440 878 Module PBXProjectFindModule Proportion 50% Proportion 428pt Name Project Find ServiceClasses PBXProjectFindModule StatusbarIsVisible 1 TableOfContents 1C530D57069F1CE1000CFCEE 1C530D58069F1CE1000CFCEE 1C530D59069F1CE1000CFCEE 1CDD528C0622207200134675 1C530D5A069F1CE1000CFCEE 1CE0B1FE06471DED0097A5F4 1CD0528E0623707200166675 WindowString 62 385 781 470 0 0 1440 878 WindowToolGUID 1C530D57069F1CE1000CFCEE WindowToolIsVisible 0 Identifier windowTool.snapshots Layout Dock Module XCSnapshotModule Proportion 100% Proportion 100% Name Snapshots ServiceClasses XCSnapshotModule StatusbarIsVisible Yes ToolbarConfiguration xcode.toolbar.config.snapshots WindowString 315 824 300 550 0 0 1440 878 WindowToolIsVisible Yes FirstTimeWindowDisplayed Identifier windowTool.debuggerConsole IsVertical Layout Dock ContentConfiguration PBXProjectModuleGUID 1C78EAAC065D492600B07095 PBXProjectModuleLabel Debugger Console GeometryConfiguration Frame {{0, 0}, {440, 359}} RubberWindowFrame 21 455 440 400 0 0 1440 878 Module PBXDebugCLIModule Proportion 359pt Proportion 359pt Name Debugger Console ServiceClasses PBXDebugCLIModule StatusbarIsVisible TableOfContents 1C530D5B069F1CE1000CFCEE 032E9C6E1236DB7000B08386 1C78EAAC065D492600B07095 ToolbarConfiguration xcode.toolbar.config.consoleV3 WindowString 21 455 440 400 0 0 1440 878 WindowToolGUID 1C530D5B069F1CE1000CFCEE WindowToolIsVisible Identifier windowTool.scm Layout Dock ContentConfiguration PBXProjectModuleGUID 1C78EAB2065D492600B07095 PBXProjectModuleLabel <No Editor> PBXSplitModuleInNavigatorKey Split0 PBXProjectModuleGUID 1C78EAB3065D492600B07095 SplitCount 1 StatusBarVisibility 1 GeometryConfiguration Frame {{0, 0}, {452, 0}} RubberWindowFrame 743 379 452 308 0 0 1280 1002 Module PBXNavigatorGroup Proportion 0pt BecomeActive 1 ContentConfiguration PBXProjectModuleGUID 1CD052920623707200166675 PBXProjectModuleLabel SCM GeometryConfiguration ConsoleFrame {{0, 259}, {452, 0}} Frame {{0, 7}, {452, 259}} RubberWindowFrame 743 379 452 308 0 0 1280 1002 TableConfiguration Status 30 FileName 199 Path 197.09500122070312 TableFrame {{0, 0}, {452, 250}} Module PBXCVSModule Proportion 262pt Proportion 266pt Name SCM ServiceClasses PBXCVSModule StatusbarIsVisible 1 TableOfContents 1C78EAB4065D492600B07095 1C78EAB5065D492600B07095 1C78EAB2065D492600B07095 1CD052920623707200166675 ToolbarConfiguration xcode.toolbar.config.scmV3 WindowString 743 379 452 308 0 0 1280 1002 Identifier windowTool.breakpoints IsVertical 0 Layout Dock BecomeActive 1 ContentConfiguration PBXBottomSmartGroupGIDs 1C77FABC04509CD000000102 PBXProjectModuleGUID 1CE0B1FE06471DED0097A5F4 PBXProjectModuleLabel Files PBXProjectStructureProvided no PBXSmartGroupTreeModuleColumnData PBXSmartGroupTreeModuleColumnWidthsKey 168 PBXSmartGroupTreeModuleColumnsKey_v4 MainColumn PBXSmartGroupTreeModuleOutlineStateKey_v7 PBXSmartGroupTreeModuleOutlineStateExpansionKey 1C77FABC04509CD000000102 PBXSmartGroupTreeModuleOutlineStateSelectionKey 0 PBXSmartGroupTreeModuleOutlineStateVisibleRectKey {{0, 0}, {168, 350}} PBXTopSmartGroupGIDs XCIncludePerspectivesSwitch 0 GeometryConfiguration Frame {{0, 0}, {185, 368}} GroupTreeTableConfiguration MainColumn 168 RubberWindowFrame 315 424 744 409 0 0 1440 878 Module PBXSmartGroupTreeModule Proportion 185pt ContentConfiguration PBXProjectModuleGUID 1CA1AED706398EBD00589147 PBXProjectModuleLabel Detail GeometryConfiguration Frame {{190, 0}, {554, 368}} RubberWindowFrame 315 424 744 409 0 0 1440 878 Module XCDetailModule Proportion 554pt Proportion 368pt MajorVersion 3 MinorVersion 0 Name Breakpoints ServiceClasses PBXSmartGroupTreeModule XCDetailModule StatusbarIsVisible 1 TableOfContents 1CDDB66807F98D9800BB5817 1CDDB66907F98D9800BB5817 1CE0B1FE06471DED0097A5F4 1CA1AED706398EBD00589147 ToolbarConfiguration xcode.toolbar.config.breakpointsV3 WindowString 315 424 744 409 0 0 1440 878 WindowToolGUID 1CDDB66807F98D9800BB5817 WindowToolIsVisible 1 Identifier windowTool.debugAnimator Layout Dock Module PBXNavigatorGroup Proportion 100% Proportion 100% Name Debug Visualizer ServiceClasses PBXNavigatorGroup StatusbarIsVisible 1 ToolbarConfiguration xcode.toolbar.config.debugAnimatorV3 WindowString 100 100 700 500 0 0 1280 1002 Identifier windowTool.bookmarks Layout Dock Module PBXBookmarksModule Proportion 166pt Proportion 166pt Name Bookmarks ServiceClasses PBXBookmarksModule StatusbarIsVisible 0 WindowString 538 42 401 187 0 0 1280 1002 FirstTimeWindowDisplayed Identifier windowTool.projectFormatConflicts IsVertical Layout Dock BecomeActive ContentConfiguration PBXProjectModuleGUID 03E9845810DAB6EA00F8E5F6 GeometryConfiguration Frame {{0, 0}, {472, 302}} RubberWindowFrame 21 533 472 322 0 0 1440 878 Module XCProjectFormatConflictsModule Proportion 302pt Proportion 302pt Name Project Format Conflicts ServiceClasses XCProjectFormatConflictsModule StatusbarIsVisible TableOfContents 03E9845910DAB6EA00F8E5F6 038823F410E622DD00CEA830 03E9845810DAB6EA00F8E5F6 WindowContentMinSize 450 300 WindowString 21 533 472 322 0 0 1440 878 WindowToolGUID 03E9845910DAB6EA00F8E5F6 WindowToolIsVisible Identifier windowTool.classBrowser Layout Dock BecomeActive 1 ContentConfiguration OptionsSetName Hierarchy, all classes PBXProjectModuleGUID 1CA6456E063B45B4001379D8 PBXProjectModuleLabel Class Browser - NSObject GeometryConfiguration ClassesFrame {{0, 0}, {369, 96}} ClassesTreeTableConfiguration PBXClassNameColumnIdentifier 208 PBXClassBookColumnIdentifier 22 Frame {{0, 0}, {616, 353}} MembersFrame {{0, 105}, {369, 395}} MembersTreeTableConfiguration PBXMemberTypeIconColumnIdentifier 22 PBXMemberNameColumnIdentifier 216 PBXMemberTypeColumnIdentifier 94 PBXMemberBookColumnIdentifier 22 PBXModuleWindowStatusBarHidden2 1 RubberWindowFrame 597 125 616 374 0 0 1280 1002 Module PBXClassBrowserModule Proportion 354pt Proportion 354pt Name Class Browser ServiceClasses PBXClassBrowserModule StatusbarIsVisible 0 TableOfContents 1C78EABA065D492600B07095 1C78EABB065D492600B07095 1CA6456E063B45B4001379D8 ToolbarConfiguration xcode.toolbar.config.classbrowser WindowString 597 125 616 374 0 0 1280 1002 FirstTimeWindowDisplayed Identifier windowTool.refactoring IncludeInToolsMenu 0 IsVertical Layout Dock ContentConfiguration PBXProjectModuleGUID 0364952110B2E3D100C88803 GeometryConfiguration Frame {{0, 0}, {500, 315}} RubberWindowFrame 21 499 500 356 0 0 1440 878 Module XCRefactoringModule Proportion 315pt Proportion 315pt Name Refactoring ServiceClasses XCRefactoringModule StatusbarIsVisible TableOfContents 0364952210B2E3D100C88803 030AFB45127D090100C9E0C6 0364952110B2E3D100C88803 WindowString 21 499 500 356 0 0 1440 878 WindowToolGUID 0364952210B2E3D100C88803 WindowToolIsVisible ================================================ FILE: SpyPhone.xcodeproj/project.pbxproj ================================================ // !$*UTF8*$! { archiveVersion = 1; classes = { }; objectVersion = 46; objects = { /* Begin PBXBuildFile section */ 030AFB2E127D08BF00C9E0C6 /* SPWifiMapVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 030AFB2C127D08BF00C9E0C6 /* SPWifiMapVC.m */; }; 030AFB2F127D08BF00C9E0C6 /* SPWifiMapVC.xib in Resources */ = {isa = PBXBuildFile; fileRef = 030AFB2D127D08BF00C9E0C6 /* SPWifiMapVC.xib */; }; 030AFBA4127D0D6F00C9E0C6 /* SPWifiAnnotation.m in Sources */ = {isa = PBXBuildFile; fileRef = 030AFBA3127D0D6F00C9E0C6 /* SPWifiAnnotation.m */; }; 0310C65510C4BE0800E7ACD2 /* SPImageVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 0310C65410C4BE0800E7ACD2 /* SPImageVC.m */; }; 031748B910B6E35C00B6116E /* SPEmailASAccount.m in Sources */ = {isa = PBXBuildFile; fileRef = 031748B810B6E35C00B6116E /* SPEmailASAccount.m */; }; 031748C010B6E63E00B6116E /* SPEmailAccount.m in Sources */ = {isa = PBXBuildFile; fileRef = 031748BF10B6E63E00B6116E /* SPEmailAccount.m */; }; 031748C710B6E98800B6116E /* SPEmailPOPAccount.m in Sources */ = {isa = PBXBuildFile; fileRef = 031748C610B6E98800B6116E /* SPEmailPOPAccount.m */; }; 031748CA10B6E9A000B6116E /* SPEmailIToolsAccount.m in Sources */ = {isa = PBXBuildFile; fileRef = 031748C910B6E9A000B6116E /* SPEmailIToolsAccount.m */; }; 0317498610B6F3A300B6116E /* SPEmailGmailAccount.m in Sources */ = {isa = PBXBuildFile; fileRef = 0317498510B6F3A300B6116E /* SPEmailGmailAccount.m */; }; 0317498910B6F49300B6116E /* SPEmailIMAPAccount.m in Sources */ = {isa = PBXBuildFile; fileRef = 0317498810B6F49300B6116E /* SPEmailIMAPAccount.m */; }; 031749AF10B6FB9C00B6116E /* AddressBook.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 031749AE10B6FB9C00B6116E /* AddressBook.framework */; }; 0317DE6910FF95CC00C5C2D4 /* MediaPlayer.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0317DE6810FF95CC00C5C2D4 /* MediaPlayer.framework */; }; 0317DE7C10FF98E900C5C2D4 /* Settings.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 0317DE7B10FF98E900C5C2D4 /* Settings.bundle */; }; 031CD55010BB4A29007C133E /* Email.png in Resources */ = {isa = PBXBuildFile; fileRef = 031CD54810BB4A29007C133E /* Email.png */; }; 031CD55110BB4A29007C133E /* Location.png in Resources */ = {isa = PBXBuildFile; fileRef = 031CD54910BB4A29007C133E /* Location.png */; }; 031CD55310BB4A29007C133E /* Phone.png in Resources */ = {isa = PBXBuildFile; fileRef = 031CD54B10BB4A29007C133E /* Phone.png */; }; 031CD55410BB4A29007C133E /* Photos.png in Resources */ = {isa = PBXBuildFile; fileRef = 031CD54C10BB4A29007C133E /* Photos.png */; }; 031CD5BC10BB4F7B007C133E /* AddressBook.png in Resources */ = {isa = PBXBuildFile; fileRef = 031CD5BB10BB4F7B007C133E /* AddressBook.png */; }; 031CD5BE10BB5004007C133E /* YouTube.png in Resources */ = {isa = PBXBuildFile; fileRef = 031CD5BD10BB5004007C133E /* YouTube.png */; }; 031CD5C210BB502A007C133E /* Wifi.png in Resources */ = {isa = PBXBuildFile; fileRef = 031CD5C110BB502A007C133E /* Wifi.png */; }; 031CD5EB10BB5152007C133E /* Safari.png in Resources */ = {isa = PBXBuildFile; fileRef = 031CD5EA10BB5152007C133E /* Safari.png */; }; 0328F4CF10B0614A0074A5A1 /* SPAllSourcesTVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 0328F4CE10B0614A0074A5A1 /* SPAllSourcesTVC.m */; }; 0328F50A10B065530074A5A1 /* SPCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 0328F50910B065530074A5A1 /* SPCell.m */; }; 0328F51410B065B80074A5A1 /* SPCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 0328F51310B065B80074A5A1 /* SPCell.xib */; }; 0328F56310B070AE0074A5A1 /* SPSourceTVC.xib in Resources */ = {isa = PBXBuildFile; fileRef = 0328F56210B070AE0074A5A1 /* SPSourceTVC.xib */; }; 0328F56A10B071110074A5A1 /* SPSourceEmailTVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 0328F56910B071110074A5A1 /* SPSourceEmailTVC.m */; }; 0328F61910B07F360074A5A1 /* SPSourceWifiTVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 0328F61810B07F360074A5A1 /* SPSourceWifiTVC.m */; }; 0328F6BC10B088DD0074A5A1 /* SPSourcePhoneTVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 0328F6BB10B088DD0074A5A1 /* SPSourcePhoneTVC.m */; }; 0328F74310B095BA0074A5A1 /* MapKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0328F74210B095BA0074A5A1 /* MapKit.framework */; }; 0328F75210B09AA60074A5A1 /* SPSourceLocationTVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 0328F75110B09AA60074A5A1 /* SPSourceLocationTVC.m */; }; 0328F80F10B0A8B70074A5A1 /* SPSourcePhotosTVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 0328F80E10B0A8B70074A5A1 /* SPSourcePhotosTVC.m */; }; 0328F8BC10B0B1780074A5A1 /* SPWebViewVC.xib in Resources */ = {isa = PBXBuildFile; fileRef = 0328F8BB10B0B1780074A5A1 /* SPWebViewVC.xib */; }; 0328F8C010B0B1AE0074A5A1 /* SPWebViewVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 0328F8BF10B0B1AE0074A5A1 /* SPWebViewVC.m */; }; 0328F96110B0CB140074A5A1 /* SPSourceAddressBookTVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 0328F96010B0CB140074A5A1 /* SPSourceAddressBookTVC.m */; }; 032A7D9010B844EF00E7FB65 /* CoreLocation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 032A7D8F10B844EF00E7FB65 /* CoreLocation.framework */; }; 032A7E0610B84C1800E7FB65 /* UIImage+GPS.m in Sources */ = {isa = PBXBuildFile; fileRef = 032A7E0510B84C1800E7FB65 /* UIImage+GPS.m */; }; 032A7EAD10B85E7F00E7FB65 /* SPImageMapVC.xib in Resources */ = {isa = PBXBuildFile; fileRef = 032A7EAC10B85E7F00E7FB65 /* SPImageMapVC.xib */; }; 032A7EB110B85E9C00E7FB65 /* SPImageMapVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 032A7EB010B85E9C00E7FB65 /* SPImageMapVC.m */; }; 032A7ECB10B8617C00E7FB65 /* SPImageAnnotation.m in Sources */ = {isa = PBXBuildFile; fileRef = 032A7ECA10B8617C00E7FB65 /* SPImageAnnotation.m */; }; 032A809A10B8799300E7FB65 /* SPImageVC.xib in Resources */ = {isa = PBXBuildFile; fileRef = 032A809910B8799300E7FB65 /* SPImageVC.xib */; }; 032A80F410B8816300E7FB65 /* Icon.png in Resources */ = {isa = PBXBuildFile; fileRef = 032A80F210B8816300E7FB65 /* Icon.png */; }; 032A810110B8821D00E7FB65 /* white_hat.png in Resources */ = {isa = PBXBuildFile; fileRef = 032A80F110B8816300E7FB65 /* white_hat.png */; }; 032A811C10B883FB00E7FB65 /* Default.png in Resources */ = {isa = PBXBuildFile; fileRef = 032A811B10B883FB00E7FB65 /* Default.png */; }; 032A819310B8E7BA00E7FB65 /* SPEmailReportVC.xib in Resources */ = {isa = PBXBuildFile; fileRef = 032A819210B8E7BA00E7FB65 /* SPEmailReportVC.xib */; }; 032A819710B8E7C600E7FB65 /* SPEmailReportVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 032A819610B8E7C600E7FB65 /* SPEmailReportVC.m */; }; 032A81CA10B8EB6100E7FB65 /* MessageUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 032A81C910B8EB6100E7FB65 /* MessageUI.framework */; }; 03380599127CEC2500EAFE64 /* FMDatabase.m in Sources */ = {isa = PBXBuildFile; fileRef = 03380594127CEC2500EAFE64 /* FMDatabase.m */; }; 0338059A127CEC2500EAFE64 /* FMDatabaseAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 03380596127CEC2500EAFE64 /* FMDatabaseAdditions.m */; }; 0338059B127CEC2500EAFE64 /* FMResultSet.m in Sources */ = {isa = PBXBuildFile; fileRef = 03380598127CEC2500EAFE64 /* FMResultSet.m */; }; 033805AE127CEC3100EAFE64 /* EXFGPS.m in Sources */ = {isa = PBXBuildFile; fileRef = 0338059F127CEC3100EAFE64 /* EXFGPS.m */; }; 033805AF127CEC3100EAFE64 /* EXFHandlers.m in Sources */ = {isa = PBXBuildFile; fileRef = 033805A1127CEC3100EAFE64 /* EXFHandlers.m */; }; 033805B0127CEC3100EAFE64 /* EXFJFIF.m in Sources */ = {isa = PBXBuildFile; fileRef = 033805A3127CEC3100EAFE64 /* EXFJFIF.m */; }; 033805B1127CEC3100EAFE64 /* EXFJpeg.m in Sources */ = {isa = PBXBuildFile; fileRef = 033805A5127CEC3100EAFE64 /* EXFJpeg.m */; }; 033805B2127CEC3100EAFE64 /* EXFMetaData.m in Sources */ = {isa = PBXBuildFile; fileRef = 033805A8127CEC3100EAFE64 /* EXFMetaData.m */; }; 033805B3127CEC3100EAFE64 /* EXFTagDefinitionHolder.m in Sources */ = {isa = PBXBuildFile; fileRef = 033805AB127CEC3100EAFE64 /* EXFTagDefinitionHolder.m */; }; 033805B4127CEC3100EAFE64 /* EXFUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 033805AD127CEC3100EAFE64 /* EXFUtils.m */; }; 033805B8127CEC4200EAFE64 /* OUILookupTool.m in Sources */ = {isa = PBXBuildFile; fileRef = 033805B7127CEC4200EAFE64 /* OUILookupTool.m */; }; 033805DC127CECF900EAFE64 /* LICENSE in Resources */ = {isa = PBXBuildFile; fileRef = 033805CD127CECF900EAFE64 /* LICENSE */; }; 033805DD127CECF900EAFE64 /* NSObject+SBJSON.m in Sources */ = {isa = PBXBuildFile; fileRef = 033805CF127CECF900EAFE64 /* NSObject+SBJSON.m */; }; 033805DE127CECF900EAFE64 /* NSString+SBJSON.m in Sources */ = {isa = PBXBuildFile; fileRef = 033805D1127CECF900EAFE64 /* NSString+SBJSON.m */; }; 033805DF127CECF900EAFE64 /* Readme.markdown in Resources */ = {isa = PBXBuildFile; fileRef = 033805D2127CECF900EAFE64 /* Readme.markdown */; }; 033805E0127CECF900EAFE64 /* SBJsonBase.m in Sources */ = {isa = PBXBuildFile; fileRef = 033805D4127CECF900EAFE64 /* SBJsonBase.m */; }; 033805E1127CECF900EAFE64 /* SBJsonParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 033805D6127CECF900EAFE64 /* SBJsonParser.m */; }; 033805E2127CECF900EAFE64 /* SBJsonStreamWriter.m in Sources */ = {isa = PBXBuildFile; fileRef = 033805D8127CECF900EAFE64 /* SBJsonStreamWriter.m */; }; 033805E3127CECF900EAFE64 /* SBJsonWriter.m in Sources */ = {isa = PBXBuildFile; fileRef = 033805DA127CECF900EAFE64 /* SBJsonWriter.m */; }; 0364934110B16DDD00C88803 /* SPSourceKeyboardTVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 0364934010B16DDD00C88803 /* SPSourceKeyboardTVC.m */; }; 0364948C10B28BC800C88803 /* SPSourceTVC.m in Sources */ = {isa = PBXBuildFile; fileRef = 0364948B10B28BC800C88803 /* SPSourceTVC.m */; }; 037D3A8110F3D57B003A85B0 /* SPEmailMobileMeAccount.m in Sources */ = {isa = PBXBuildFile; fileRef = 037D3A8010F3D57B003A85B0 /* SPEmailMobileMeAccount.m */; }; 037F2E0E128D2E6300EE7E19 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 037F2E0D128D2E6300EE7E19 /* QuartzCore.framework */; }; 03B2C04E10BB624F00E05ECB /* Keyboard.png in Resources */ = {isa = PBXBuildFile; fileRef = 03B2C04D10BB624F00E05ECB /* Keyboard.png */; }; 03B2C06110BB62A300E05ECB /* data.png in Resources */ = {isa = PBXBuildFile; fileRef = 03B2C06010BB62A300E05ECB /* data.png */; }; 03B2C09410BB66DB00E05ECB /* report.png in Resources */ = {isa = PBXBuildFile; fileRef = 03B2C09310BB66DB00E05ECB /* report.png */; }; 03B6EFCA10BB547600CF9139 /* gpl-2.0.txt in Resources */ = {isa = PBXBuildFile; fileRef = 03B6EFC910BB547600CF9139 /* gpl-2.0.txt */; }; 03B6F00410BB588A00CF9139 /* white_hat_mask.png in Resources */ = {isa = PBXBuildFile; fileRef = 03B6F00310BB588A00CF9139 /* white_hat_mask.png */; }; 03B6F03510BB5F4800CF9139 /* email_mask.png in Resources */ = {isa = PBXBuildFile; fileRef = 03B6F03410BB5F4800CF9139 /* email_mask.png */; }; 03F2472F1236E25E0017F214 /* libsqlite3.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 03F2472E1236E25E0017F214 /* libsqlite3.dylib */; }; 03F2481F1236EFAC0017F214 /* NSNumber+SP.m in Sources */ = {isa = PBXBuildFile; fileRef = 03F2481E1236EFAC0017F214 /* NSNumber+SP.m */; }; 1D3623260D0F684500981E51 /* SpyPhoneAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 1D3623250D0F684500981E51 /* SpyPhoneAppDelegate.m */; }; 1D60589B0D05DD56006BFB54 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 29B97316FDCFA39411CA2CEA /* main.m */; }; 1D60589F0D05DD5A006BFB54 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1D30AB110D05D00D00671497 /* Foundation.framework */; }; 1DF5F4E00D08C38300B7A737 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1DF5F4DF0D08C38300B7A737 /* UIKit.framework */; }; 282CCBFE0DB6C98000C4EA27 /* Sources.xib in Resources */ = {isa = PBXBuildFile; fileRef = 282CCBFD0DB6C98000C4EA27 /* Sources.xib */; }; 288765080DF74369002DB57D /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 288765070DF74369002DB57D /* CoreGraphics.framework */; }; 28AD73880D9D96C1002E5188 /* MainWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 28AD73870D9D96C1002E5188 /* MainWindow.xib */; }; 5F0DA1BC12829A6E00CD3B56 /* TVOutManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 5F0DA1BB12829A6E00CD3B56 /* TVOutManager.m */; }; 5F38C609127EBB37003CA424 /* CoreTelephony.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5F38C608127EBB37003CA424 /* CoreTelephony.framework */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ 030AFB2B127D08BF00C9E0C6 /* SPWifiMapVC.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPWifiMapVC.h; sourceTree = ""; }; 030AFB2C127D08BF00C9E0C6 /* SPWifiMapVC.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPWifiMapVC.m; sourceTree = ""; }; 030AFB2D127D08BF00C9E0C6 /* SPWifiMapVC.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = SPWifiMapVC.xib; path = Classes/SPWifiMapVC.xib; sourceTree = ""; }; 030AFBA2127D0D6F00C9E0C6 /* SPWifiAnnotation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPWifiAnnotation.h; sourceTree = ""; }; 030AFBA3127D0D6F00C9E0C6 /* SPWifiAnnotation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPWifiAnnotation.m; sourceTree = ""; }; 0310C65310C4BE0800E7ACD2 /* SPImageVC.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPImageVC.h; sourceTree = ""; }; 0310C65410C4BE0800E7ACD2 /* SPImageVC.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPImageVC.m; sourceTree = ""; }; 031748B710B6E35C00B6116E /* SPEmailASAccount.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPEmailASAccount.h; sourceTree = ""; }; 031748B810B6E35C00B6116E /* SPEmailASAccount.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPEmailASAccount.m; sourceTree = ""; }; 031748BE10B6E63E00B6116E /* SPEmailAccount.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPEmailAccount.h; sourceTree = ""; }; 031748BF10B6E63E00B6116E /* SPEmailAccount.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPEmailAccount.m; sourceTree = ""; }; 031748C510B6E98800B6116E /* SPEmailPOPAccount.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPEmailPOPAccount.h; sourceTree = ""; }; 031748C610B6E98800B6116E /* SPEmailPOPAccount.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPEmailPOPAccount.m; sourceTree = ""; }; 031748C810B6E9A000B6116E /* SPEmailIToolsAccount.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPEmailIToolsAccount.h; sourceTree = ""; }; 031748C910B6E9A000B6116E /* SPEmailIToolsAccount.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPEmailIToolsAccount.m; sourceTree = ""; }; 0317498410B6F3A300B6116E /* SPEmailGmailAccount.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPEmailGmailAccount.h; sourceTree = ""; }; 0317498510B6F3A300B6116E /* SPEmailGmailAccount.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPEmailGmailAccount.m; sourceTree = ""; }; 0317498710B6F49300B6116E /* SPEmailIMAPAccount.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPEmailIMAPAccount.h; sourceTree = ""; }; 0317498810B6F49300B6116E /* SPEmailIMAPAccount.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPEmailIMAPAccount.m; sourceTree = ""; }; 031749AE10B6FB9C00B6116E /* AddressBook.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AddressBook.framework; path = System/Library/Frameworks/AddressBook.framework; sourceTree = SDKROOT; }; 0317DE6810FF95CC00C5C2D4 /* MediaPlayer.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MediaPlayer.framework; path = System/Library/Frameworks/MediaPlayer.framework; sourceTree = SDKROOT; }; 0317DE7B10FF98E900C5C2D4 /* Settings.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = Settings.bundle; sourceTree = ""; }; 031CD54810BB4A29007C133E /* Email.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Email.png; sourceTree = ""; }; 031CD54910BB4A29007C133E /* Location.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Location.png; sourceTree = ""; }; 031CD54B10BB4A29007C133E /* Phone.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Phone.png; sourceTree = ""; }; 031CD54C10BB4A29007C133E /* Photos.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Photos.png; sourceTree = ""; }; 031CD5BB10BB4F7B007C133E /* AddressBook.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = AddressBook.png; sourceTree = ""; }; 031CD5BD10BB5004007C133E /* YouTube.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = YouTube.png; sourceTree = ""; }; 031CD5C110BB502A007C133E /* Wifi.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Wifi.png; sourceTree = ""; }; 031CD5EA10BB5152007C133E /* Safari.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Safari.png; sourceTree = ""; }; 0328F4CD10B0614A0074A5A1 /* SPAllSourcesTVC.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPAllSourcesTVC.h; sourceTree = ""; }; 0328F4CE10B0614A0074A5A1 /* SPAllSourcesTVC.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPAllSourcesTVC.m; sourceTree = ""; }; 0328F50810B065530074A5A1 /* SPCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPCell.h; sourceTree = ""; }; 0328F50910B065530074A5A1 /* SPCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPCell.m; sourceTree = ""; }; 0328F51310B065B80074A5A1 /* SPCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = SPCell.xib; sourceTree = ""; }; 0328F56210B070AE0074A5A1 /* SPSourceTVC.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = SPSourceTVC.xib; sourceTree = ""; }; 0328F56810B071110074A5A1 /* SPSourceEmailTVC.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPSourceEmailTVC.h; sourceTree = ""; }; 0328F56910B071110074A5A1 /* SPSourceEmailTVC.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPSourceEmailTVC.m; sourceTree = ""; }; 0328F61710B07F360074A5A1 /* SPSourceWifiTVC.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPSourceWifiTVC.h; sourceTree = ""; }; 0328F61810B07F360074A5A1 /* SPSourceWifiTVC.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPSourceWifiTVC.m; sourceTree = ""; }; 0328F6BA10B088DD0074A5A1 /* SPSourcePhoneTVC.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPSourcePhoneTVC.h; sourceTree = ""; }; 0328F6BB10B088DD0074A5A1 /* SPSourcePhoneTVC.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPSourcePhoneTVC.m; sourceTree = ""; }; 0328F74210B095BA0074A5A1 /* MapKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MapKit.framework; path = System/Library/Frameworks/MapKit.framework; sourceTree = SDKROOT; }; 0328F75010B09AA60074A5A1 /* SPSourceLocationTVC.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPSourceLocationTVC.h; sourceTree = ""; }; 0328F75110B09AA60074A5A1 /* SPSourceLocationTVC.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPSourceLocationTVC.m; sourceTree = ""; }; 0328F80D10B0A8B70074A5A1 /* SPSourcePhotosTVC.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPSourcePhotosTVC.h; sourceTree = ""; }; 0328F80E10B0A8B70074A5A1 /* SPSourcePhotosTVC.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPSourcePhotosTVC.m; sourceTree = ""; }; 0328F8BB10B0B1780074A5A1 /* SPWebViewVC.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = SPWebViewVC.xib; sourceTree = ""; }; 0328F8BE10B0B1AE0074A5A1 /* SPWebViewVC.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPWebViewVC.h; sourceTree = ""; }; 0328F8BF10B0B1AE0074A5A1 /* SPWebViewVC.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPWebViewVC.m; sourceTree = ""; }; 0328F95F10B0CB140074A5A1 /* SPSourceAddressBookTVC.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPSourceAddressBookTVC.h; sourceTree = ""; }; 0328F96010B0CB140074A5A1 /* SPSourceAddressBookTVC.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPSourceAddressBookTVC.m; sourceTree = ""; }; 032A7D8F10B844EF00E7FB65 /* CoreLocation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreLocation.framework; path = System/Library/Frameworks/CoreLocation.framework; sourceTree = SDKROOT; }; 032A7E0410B84C1800E7FB65 /* UIImage+GPS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIImage+GPS.h"; sourceTree = ""; }; 032A7E0510B84C1800E7FB65 /* UIImage+GPS.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIImage+GPS.m"; sourceTree = ""; }; 032A7EAC10B85E7F00E7FB65 /* SPImageMapVC.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = SPImageMapVC.xib; sourceTree = ""; }; 032A7EAF10B85E9C00E7FB65 /* SPImageMapVC.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPImageMapVC.h; sourceTree = ""; }; 032A7EB010B85E9C00E7FB65 /* SPImageMapVC.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPImageMapVC.m; sourceTree = ""; }; 032A7EC910B8617C00E7FB65 /* SPImageAnnotation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPImageAnnotation.h; sourceTree = ""; }; 032A7ECA10B8617C00E7FB65 /* SPImageAnnotation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPImageAnnotation.m; sourceTree = ""; }; 032A809910B8799300E7FB65 /* SPImageVC.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = SPImageVC.xib; sourceTree = ""; }; 032A80F110B8816300E7FB65 /* white_hat.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = white_hat.png; sourceTree = ""; }; 032A80F210B8816300E7FB65 /* Icon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Icon.png; sourceTree = ""; }; 032A811B10B883FB00E7FB65 /* Default.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Default.png; sourceTree = ""; }; 032A819210B8E7BA00E7FB65 /* SPEmailReportVC.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = SPEmailReportVC.xib; sourceTree = ""; }; 032A819510B8E7C600E7FB65 /* SPEmailReportVC.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPEmailReportVC.h; sourceTree = ""; }; 032A819610B8E7C600E7FB65 /* SPEmailReportVC.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPEmailReportVC.m; sourceTree = ""; }; 032A81C910B8EB6100E7FB65 /* MessageUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MessageUI.framework; path = System/Library/Frameworks/MessageUI.framework; sourceTree = SDKROOT; }; 03380593127CEC2500EAFE64 /* FMDatabase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FMDatabase.h; path = FMDB/FMDatabase.h; sourceTree = SOURCE_ROOT; }; 03380594127CEC2500EAFE64 /* FMDatabase.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = FMDatabase.m; path = FMDB/FMDatabase.m; sourceTree = SOURCE_ROOT; }; 03380595127CEC2500EAFE64 /* FMDatabaseAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FMDatabaseAdditions.h; path = FMDB/FMDatabaseAdditions.h; sourceTree = SOURCE_ROOT; }; 03380596127CEC2500EAFE64 /* FMDatabaseAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = FMDatabaseAdditions.m; path = FMDB/FMDatabaseAdditions.m; sourceTree = SOURCE_ROOT; }; 03380597127CEC2500EAFE64 /* FMResultSet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FMResultSet.h; path = FMDB/FMResultSet.h; sourceTree = SOURCE_ROOT; }; 03380598127CEC2500EAFE64 /* FMResultSet.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = FMResultSet.m; path = FMDB/FMResultSet.m; sourceTree = SOURCE_ROOT; }; 0338059C127CEC3100EAFE64 /* EXF.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = EXF.h; path = EXIF/EXF.h; sourceTree = SOURCE_ROOT; }; 0338059D127CEC3100EAFE64 /* EXFConstants.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = EXFConstants.h; path = EXIF/EXFConstants.h; sourceTree = SOURCE_ROOT; }; 0338059E127CEC3100EAFE64 /* EXFGPS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = EXFGPS.h; path = EXIF/EXFGPS.h; sourceTree = SOURCE_ROOT; }; 0338059F127CEC3100EAFE64 /* EXFGPS.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = EXFGPS.m; path = EXIF/EXFGPS.m; sourceTree = SOURCE_ROOT; }; 033805A0127CEC3100EAFE64 /* EXFHandlers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = EXFHandlers.h; path = EXIF/EXFHandlers.h; sourceTree = SOURCE_ROOT; }; 033805A1127CEC3100EAFE64 /* EXFHandlers.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = EXFHandlers.m; path = EXIF/EXFHandlers.m; sourceTree = SOURCE_ROOT; }; 033805A2127CEC3100EAFE64 /* EXFJFIF.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = EXFJFIF.h; path = EXIF/EXFJFIF.h; sourceTree = SOURCE_ROOT; }; 033805A3127CEC3100EAFE64 /* EXFJFIF.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = EXFJFIF.m; path = EXIF/EXFJFIF.m; sourceTree = SOURCE_ROOT; }; 033805A4127CEC3100EAFE64 /* EXFJpeg.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = EXFJpeg.h; path = EXIF/EXFJpeg.h; sourceTree = SOURCE_ROOT; }; 033805A5127CEC3100EAFE64 /* EXFJpeg.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = EXFJpeg.m; path = EXIF/EXFJpeg.m; sourceTree = SOURCE_ROOT; }; 033805A6127CEC3100EAFE64 /* EXFLogging.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = EXFLogging.h; path = EXIF/EXFLogging.h; sourceTree = SOURCE_ROOT; }; 033805A7127CEC3100EAFE64 /* EXFMetaData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = EXFMetaData.h; path = EXIF/EXFMetaData.h; sourceTree = SOURCE_ROOT; }; 033805A8127CEC3100EAFE64 /* EXFMetaData.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = EXFMetaData.m; path = EXIF/EXFMetaData.m; sourceTree = SOURCE_ROOT; }; 033805A9127CEC3100EAFE64 /* EXFMutableMetaData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = EXFMutableMetaData.h; path = EXIF/EXFMutableMetaData.h; sourceTree = SOURCE_ROOT; }; 033805AA127CEC3100EAFE64 /* EXFTagDefinitionHolder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = EXFTagDefinitionHolder.h; path = EXIF/EXFTagDefinitionHolder.h; sourceTree = SOURCE_ROOT; }; 033805AB127CEC3100EAFE64 /* EXFTagDefinitionHolder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = EXFTagDefinitionHolder.m; path = EXIF/EXFTagDefinitionHolder.m; sourceTree = SOURCE_ROOT; }; 033805AC127CEC3100EAFE64 /* EXFUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = EXFUtils.h; path = EXIF/EXFUtils.h; sourceTree = SOURCE_ROOT; }; 033805AD127CEC3100EAFE64 /* EXFUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = EXFUtils.m; path = EXIF/EXFUtils.m; sourceTree = SOURCE_ROOT; }; 033805B6127CEC4200EAFE64 /* OUILookupTool.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OUILookupTool.h; sourceTree = ""; }; 033805B7127CEC4200EAFE64 /* OUILookupTool.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OUILookupTool.m; sourceTree = ""; }; 033805CC127CECF900EAFE64 /* JSON.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSON.h; sourceTree = ""; }; 033805CD127CECF900EAFE64 /* LICENSE */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = LICENSE; sourceTree = ""; }; 033805CE127CECF900EAFE64 /* NSObject+SBJSON.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSObject+SBJSON.h"; sourceTree = ""; }; 033805CF127CECF900EAFE64 /* NSObject+SBJSON.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSObject+SBJSON.m"; sourceTree = ""; }; 033805D0127CECF900EAFE64 /* NSString+SBJSON.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSString+SBJSON.h"; sourceTree = ""; }; 033805D1127CECF900EAFE64 /* NSString+SBJSON.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSString+SBJSON.m"; sourceTree = ""; }; 033805D2127CECF900EAFE64 /* Readme.markdown */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Readme.markdown; sourceTree = ""; }; 033805D3127CECF900EAFE64 /* SBJsonBase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SBJsonBase.h; sourceTree = ""; }; 033805D4127CECF900EAFE64 /* SBJsonBase.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SBJsonBase.m; sourceTree = ""; }; 033805D5127CECF900EAFE64 /* SBJsonParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SBJsonParser.h; sourceTree = ""; }; 033805D6127CECF900EAFE64 /* SBJsonParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SBJsonParser.m; sourceTree = ""; }; 033805D7127CECF900EAFE64 /* SBJsonStreamWriter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SBJsonStreamWriter.h; sourceTree = ""; }; 033805D8127CECF900EAFE64 /* SBJsonStreamWriter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SBJsonStreamWriter.m; sourceTree = ""; }; 033805D9127CECF900EAFE64 /* SBJsonWriter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SBJsonWriter.h; sourceTree = ""; }; 033805DA127CECF900EAFE64 /* SBJsonWriter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SBJsonWriter.m; sourceTree = ""; }; 033805DB127CECF900EAFE64 /* SBProxyForJson.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SBProxyForJson.h; sourceTree = ""; }; 0364933F10B16DDD00C88803 /* SPSourceKeyboardTVC.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPSourceKeyboardTVC.h; sourceTree = ""; }; 0364934010B16DDD00C88803 /* SPSourceKeyboardTVC.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPSourceKeyboardTVC.m; sourceTree = ""; }; 0364948A10B28BC800C88803 /* SPSourceTVC.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPSourceTVC.h; sourceTree = ""; }; 0364948B10B28BC800C88803 /* SPSourceTVC.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPSourceTVC.m; sourceTree = ""; }; 037D3A7F10F3D57B003A85B0 /* SPEmailMobileMeAccount.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPEmailMobileMeAccount.h; sourceTree = ""; }; 037D3A8010F3D57B003A85B0 /* SPEmailMobileMeAccount.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPEmailMobileMeAccount.m; sourceTree = ""; }; 037F2E0D128D2E6300EE7E19 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; 03B2C04D10BB624F00E05ECB /* Keyboard.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Keyboard.png; sourceTree = ""; }; 03B2C06010BB62A300E05ECB /* data.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = data.png; sourceTree = ""; }; 03B2C09310BB66DB00E05ECB /* report.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = report.png; sourceTree = ""; }; 03B6EFC910BB547600CF9139 /* gpl-2.0.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "gpl-2.0.txt"; sourceTree = ""; }; 03B6F00310BB588A00CF9139 /* white_hat_mask.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = white_hat_mask.png; sourceTree = ""; }; 03B6F03410BB5F4800CF9139 /* email_mask.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = email_mask.png; sourceTree = ""; }; 03F2472E1236E25E0017F214 /* libsqlite3.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libsqlite3.dylib; path = /usr/lib/libsqlite3.dylib; sourceTree = ""; }; 03F2481D1236EFAC0017F214 /* NSNumber+SP.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSNumber+SP.h"; sourceTree = ""; }; 03F2481E1236EFAC0017F214 /* NSNumber+SP.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSNumber+SP.m"; sourceTree = ""; }; 1D30AB110D05D00D00671497 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 1D3623240D0F684500981E51 /* SpyPhoneAppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SpyPhoneAppDelegate.h; sourceTree = ""; }; 1D3623250D0F684500981E51 /* SpyPhoneAppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SpyPhoneAppDelegate.m; sourceTree = ""; }; 1D6058910D05DD3D006BFB54 /* SpyPhone.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SpyPhone.app; sourceTree = BUILT_PRODUCTS_DIR; }; 1DF5F4DF0D08C38300B7A737 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 282CCBFD0DB6C98000C4EA27 /* Sources.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = Sources.xib; sourceTree = ""; }; 288765070DF74369002DB57D /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 28A0AB4B0D9B1048005BE974 /* SpyPhone_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SpyPhone_Prefix.pch; sourceTree = ""; }; 28AD73870D9D96C1002E5188 /* MainWindow.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = MainWindow.xib; sourceTree = ""; }; 29B97316FDCFA39411CA2CEA /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 5F0DA1BA12829A6E00CD3B56 /* TVOutManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TVOutManager.h; sourceTree = ""; }; 5F0DA1BB12829A6E00CD3B56 /* TVOutManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TVOutManager.m; sourceTree = ""; }; 5F38C608127EBB37003CA424 /* CoreTelephony.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreTelephony.framework; path = System/Library/Frameworks/CoreTelephony.framework; sourceTree = SDKROOT; }; 8D1107310486CEB800E47090 /* SpyPhone-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "SpyPhone-Info.plist"; plistStructureDefinitionIdentifier = "com.apple.xcode.plist.structure-definition.iphone.info-plist"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ 1D60588F0D05DD3D006BFB54 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( 1D60589F0D05DD5A006BFB54 /* Foundation.framework in Frameworks */, 1DF5F4E00D08C38300B7A737 /* UIKit.framework in Frameworks */, 288765080DF74369002DB57D /* CoreGraphics.framework in Frameworks */, 0328F74310B095BA0074A5A1 /* MapKit.framework in Frameworks */, 031749AF10B6FB9C00B6116E /* AddressBook.framework in Frameworks */, 032A7D9010B844EF00E7FB65 /* CoreLocation.framework in Frameworks */, 032A81CA10B8EB6100E7FB65 /* MessageUI.framework in Frameworks */, 0317DE6910FF95CC00C5C2D4 /* MediaPlayer.framework in Frameworks */, 03F2472F1236E25E0017F214 /* libsqlite3.dylib in Frameworks */, 5F38C609127EBB37003CA424 /* CoreTelephony.framework in Frameworks */, 037F2E0E128D2E6300EE7E19 /* QuartzCore.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ 031748C110B6E67C00B6116E /* SPEmailAccounts */ = { isa = PBXGroup; children = ( 031748BE10B6E63E00B6116E /* SPEmailAccount.h */, 031748BF10B6E63E00B6116E /* SPEmailAccount.m */, 031748B710B6E35C00B6116E /* SPEmailASAccount.h */, 031748B810B6E35C00B6116E /* SPEmailASAccount.m */, 031748C510B6E98800B6116E /* SPEmailPOPAccount.h */, 031748C610B6E98800B6116E /* SPEmailPOPAccount.m */, 031748C810B6E9A000B6116E /* SPEmailIToolsAccount.h */, 031748C910B6E9A000B6116E /* SPEmailIToolsAccount.m */, 0317498410B6F3A300B6116E /* SPEmailGmailAccount.h */, 0317498510B6F3A300B6116E /* SPEmailGmailAccount.m */, 0317498710B6F49300B6116E /* SPEmailIMAPAccount.h */, 0317498810B6F49300B6116E /* SPEmailIMAPAccount.m */, 037D3A7F10F3D57B003A85B0 /* SPEmailMobileMeAccount.h */, 037D3A8010F3D57B003A85B0 /* SPEmailMobileMeAccount.m */, ); name = SPEmailAccounts; sourceTree = ""; }; 031748C210B6E68E00B6116E /* SPSources */ = { isa = PBXGroup; children = ( 0364948A10B28BC800C88803 /* SPSourceTVC.h */, 0364948B10B28BC800C88803 /* SPSourceTVC.m */, 0328F56810B071110074A5A1 /* SPSourceEmailTVC.h */, 0328F56910B071110074A5A1 /* SPSourceEmailTVC.m */, 0328F61710B07F360074A5A1 /* SPSourceWifiTVC.h */, 0328F61810B07F360074A5A1 /* SPSourceWifiTVC.m */, 0328F6BA10B088DD0074A5A1 /* SPSourcePhoneTVC.h */, 0328F6BB10B088DD0074A5A1 /* SPSourcePhoneTVC.m */, 0328F75010B09AA60074A5A1 /* SPSourceLocationTVC.h */, 0328F75110B09AA60074A5A1 /* SPSourceLocationTVC.m */, 0328F80D10B0A8B70074A5A1 /* SPSourcePhotosTVC.h */, 0328F80E10B0A8B70074A5A1 /* SPSourcePhotosTVC.m */, 0328F95F10B0CB140074A5A1 /* SPSourceAddressBookTVC.h */, 0328F96010B0CB140074A5A1 /* SPSourceAddressBookTVC.m */, 0364933F10B16DDD00C88803 /* SPSourceKeyboardTVC.h */, 0364934010B16DDD00C88803 /* SPSourceKeyboardTVC.m */, ); name = SPSources; sourceTree = ""; }; 032A7B9010B829D100E7FB65 /* EXIF */ = { isa = PBXGroup; children = ( 0338059C127CEC3100EAFE64 /* EXF.h */, 0338059D127CEC3100EAFE64 /* EXFConstants.h */, 0338059E127CEC3100EAFE64 /* EXFGPS.h */, 0338059F127CEC3100EAFE64 /* EXFGPS.m */, 033805A0127CEC3100EAFE64 /* EXFHandlers.h */, 033805A1127CEC3100EAFE64 /* EXFHandlers.m */, 033805A2127CEC3100EAFE64 /* EXFJFIF.h */, 033805A3127CEC3100EAFE64 /* EXFJFIF.m */, 033805A4127CEC3100EAFE64 /* EXFJpeg.h */, 033805A5127CEC3100EAFE64 /* EXFJpeg.m */, 033805A6127CEC3100EAFE64 /* EXFLogging.h */, 033805A7127CEC3100EAFE64 /* EXFMetaData.h */, 033805A8127CEC3100EAFE64 /* EXFMetaData.m */, 033805A9127CEC3100EAFE64 /* EXFMutableMetaData.h */, 033805AA127CEC3100EAFE64 /* EXFTagDefinitionHolder.h */, 033805AB127CEC3100EAFE64 /* EXFTagDefinitionHolder.m */, 033805AC127CEC3100EAFE64 /* EXFUtils.h */, 033805AD127CEC3100EAFE64 /* EXFUtils.m */, 032A7E0410B84C1800E7FB65 /* UIImage+GPS.h */, 032A7E0510B84C1800E7FB65 /* UIImage+GPS.m */, ); name = EXIF; sourceTree = ""; }; 033805B5127CEC4200EAFE64 /* OUILookupTool */ = { isa = PBXGroup; children = ( 033805B6127CEC4200EAFE64 /* OUILookupTool.h */, 033805B7127CEC4200EAFE64 /* OUILookupTool.m */, ); path = OUILookupTool; sourceTree = SOURCE_ROOT; }; 033805CB127CECF900EAFE64 /* JSON */ = { isa = PBXGroup; children = ( 033805CC127CECF900EAFE64 /* JSON.h */, 033805CD127CECF900EAFE64 /* LICENSE */, 033805CE127CECF900EAFE64 /* NSObject+SBJSON.h */, 033805CF127CECF900EAFE64 /* NSObject+SBJSON.m */, 033805D0127CECF900EAFE64 /* NSString+SBJSON.h */, 033805D1127CECF900EAFE64 /* NSString+SBJSON.m */, 033805D2127CECF900EAFE64 /* Readme.markdown */, 033805D3127CECF900EAFE64 /* SBJsonBase.h */, 033805D4127CECF900EAFE64 /* SBJsonBase.m */, 033805D5127CECF900EAFE64 /* SBJsonParser.h */, 033805D6127CECF900EAFE64 /* SBJsonParser.m */, 033805D7127CECF900EAFE64 /* SBJsonStreamWriter.h */, 033805D8127CECF900EAFE64 /* SBJsonStreamWriter.m */, 033805D9127CECF900EAFE64 /* SBJsonWriter.h */, 033805DA127CECF900EAFE64 /* SBJsonWriter.m */, 033805DB127CECF900EAFE64 /* SBProxyForJson.h */, ); path = JSON; sourceTree = SOURCE_ROOT; }; 03B6EFF510BB577A00CF9139 /* SPSourcesIcons */ = { isa = PBXGroup; children = ( 031CD54810BB4A29007C133E /* Email.png */, 031CD5C110BB502A007C133E /* Wifi.png */, 031CD54B10BB4A29007C133E /* Phone.png */, 031CD54910BB4A29007C133E /* Location.png */, 031CD5EA10BB5152007C133E /* Safari.png */, 031CD5BD10BB5004007C133E /* YouTube.png */, 031CD54C10BB4A29007C133E /* Photos.png */, 031CD5BB10BB4F7B007C133E /* AddressBook.png */, 03B2C04D10BB624F00E05ECB /* Keyboard.png */, ); name = SPSourcesIcons; sourceTree = ""; }; 03F2470B1236E1180017F214 /* FMDB */ = { isa = PBXGroup; children = ( 03380593127CEC2500EAFE64 /* FMDatabase.h */, 03380594127CEC2500EAFE64 /* FMDatabase.m */, 03380595127CEC2500EAFE64 /* FMDatabaseAdditions.h */, 03380596127CEC2500EAFE64 /* FMDatabaseAdditions.m */, 03380597127CEC2500EAFE64 /* FMResultSet.h */, 03380598127CEC2500EAFE64 /* FMResultSet.m */, ); name = FMDB; sourceTree = ""; }; 080E96DDFE201D6D7F000001 /* Classes */ = { isa = PBXGroup; children = ( 5F0DA1BA12829A6E00CD3B56 /* TVOutManager.h */, 5F0DA1BB12829A6E00CD3B56 /* TVOutManager.m */, 03F2470B1236E1180017F214 /* FMDB */, 032A7B9010B829D100E7FB65 /* EXIF */, 033805CB127CECF900EAFE64 /* JSON */, 033805B5127CEC4200EAFE64 /* OUILookupTool */, 031748C110B6E67C00B6116E /* SPEmailAccounts */, 031748C210B6E68E00B6116E /* SPSources */, 0328F50810B065530074A5A1 /* SPCell.h */, 0328F50910B065530074A5A1 /* SPCell.m */, 1D3623240D0F684500981E51 /* SpyPhoneAppDelegate.h */, 1D3623250D0F684500981E51 /* SpyPhoneAppDelegate.m */, 0328F4CD10B0614A0074A5A1 /* SPAllSourcesTVC.h */, 0328F4CE10B0614A0074A5A1 /* SPAllSourcesTVC.m */, 0328F8BE10B0B1AE0074A5A1 /* SPWebViewVC.h */, 0328F8BF10B0B1AE0074A5A1 /* SPWebViewVC.m */, 032A7EAF10B85E9C00E7FB65 /* SPImageMapVC.h */, 032A7EB010B85E9C00E7FB65 /* SPImageMapVC.m */, 030AFB2B127D08BF00C9E0C6 /* SPWifiMapVC.h */, 030AFB2C127D08BF00C9E0C6 /* SPWifiMapVC.m */, 0310C65310C4BE0800E7ACD2 /* SPImageVC.h */, 0310C65410C4BE0800E7ACD2 /* SPImageVC.m */, 032A7EC910B8617C00E7FB65 /* SPImageAnnotation.h */, 032A7ECA10B8617C00E7FB65 /* SPImageAnnotation.m */, 030AFBA2127D0D6F00C9E0C6 /* SPWifiAnnotation.h */, 030AFBA3127D0D6F00C9E0C6 /* SPWifiAnnotation.m */, 032A819510B8E7C600E7FB65 /* SPEmailReportVC.h */, 032A819610B8E7C600E7FB65 /* SPEmailReportVC.m */, 03F2481D1236EFAC0017F214 /* NSNumber+SP.h */, 03F2481E1236EFAC0017F214 /* NSNumber+SP.m */, ); path = Classes; sourceTree = ""; }; 19C28FACFE9D520D11CA2CBB /* Products */ = { isa = PBXGroup; children = ( 1D6058910D05DD3D006BFB54 /* SpyPhone.app */, ); name = Products; sourceTree = ""; }; 29B97314FDCFA39411CA2CEA /* MyData */ = { isa = PBXGroup; children = ( 03B6EFC910BB547600CF9139 /* gpl-2.0.txt */, 080E96DDFE201D6D7F000001 /* Classes */, 29B97315FDCFA39411CA2CEA /* Other Sources */, 29B97317FDCFA39411CA2CEA /* Resources */, 29B97323FDCFA39411CA2CEA /* Frameworks */, 19C28FACFE9D520D11CA2CBB /* Products */, ); name = MyData; sourceTree = ""; }; 29B97315FDCFA39411CA2CEA /* Other Sources */ = { isa = PBXGroup; children = ( 28A0AB4B0D9B1048005BE974 /* SpyPhone_Prefix.pch */, 29B97316FDCFA39411CA2CEA /* main.m */, ); name = "Other Sources"; sourceTree = ""; }; 29B97317FDCFA39411CA2CEA /* Resources */ = { isa = PBXGroup; children = ( 03B6F00310BB588A00CF9139 /* white_hat_mask.png */, 03B6F03410BB5F4800CF9139 /* email_mask.png */, 03B2C06010BB62A300E05ECB /* data.png */, 03B2C09310BB66DB00E05ECB /* report.png */, 03B6EFF510BB577A00CF9139 /* SPSourcesIcons */, 032A811B10B883FB00E7FB65 /* Default.png */, 032A80F210B8816300E7FB65 /* Icon.png */, 032A80F110B8816300E7FB65 /* white_hat.png */, 28AD73870D9D96C1002E5188 /* MainWindow.xib */, 282CCBFD0DB6C98000C4EA27 /* Sources.xib */, 0328F51310B065B80074A5A1 /* SPCell.xib */, 8D1107310486CEB800E47090 /* SpyPhone-Info.plist */, 0328F56210B070AE0074A5A1 /* SPSourceTVC.xib */, 0328F8BB10B0B1780074A5A1 /* SPWebViewVC.xib */, 032A7EAC10B85E7F00E7FB65 /* SPImageMapVC.xib */, 030AFB2D127D08BF00C9E0C6 /* SPWifiMapVC.xib */, 032A809910B8799300E7FB65 /* SPImageVC.xib */, 032A819210B8E7BA00E7FB65 /* SPEmailReportVC.xib */, 0317DE7B10FF98E900C5C2D4 /* Settings.bundle */, ); name = Resources; sourceTree = ""; }; 29B97323FDCFA39411CA2CEA /* Frameworks */ = { isa = PBXGroup; children = ( 5F38C608127EBB37003CA424 /* CoreTelephony.framework */, 03F2472E1236E25E0017F214 /* libsqlite3.dylib */, 1DF5F4DF0D08C38300B7A737 /* UIKit.framework */, 1D30AB110D05D00D00671497 /* Foundation.framework */, 288765070DF74369002DB57D /* CoreGraphics.framework */, 0328F74210B095BA0074A5A1 /* MapKit.framework */, 031749AE10B6FB9C00B6116E /* AddressBook.framework */, 032A7D8F10B844EF00E7FB65 /* CoreLocation.framework */, 032A81C910B8EB6100E7FB65 /* MessageUI.framework */, 0317DE6810FF95CC00C5C2D4 /* MediaPlayer.framework */, 037F2E0D128D2E6300EE7E19 /* QuartzCore.framework */, ); name = Frameworks; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ 1D6058900D05DD3D006BFB54 /* SpyPhone */ = { isa = PBXNativeTarget; buildConfigurationList = 1D6058960D05DD3E006BFB54 /* Build configuration list for PBXNativeTarget "SpyPhone" */; buildPhases = ( 1D60588D0D05DD3D006BFB54 /* Resources */, 1D60588E0D05DD3D006BFB54 /* Sources */, 1D60588F0D05DD3D006BFB54 /* Frameworks */, ); buildRules = ( ); dependencies = ( ); name = SpyPhone; productName = MyData; productReference = 1D6058910D05DD3D006BFB54 /* SpyPhone.app */; productType = "com.apple.product-type.application"; }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ 29B97313FDCFA39411CA2CEA /* Project object */ = { isa = PBXProject; attributes = { LastUpgradeCheck = 0420; }; buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "SpyPhone" */; compatibilityVersion = "Xcode 3.2"; developmentRegion = English; hasScannedForEncodings = 1; knownRegions = ( English, Japanese, French, German, ); mainGroup = 29B97314FDCFA39411CA2CEA /* MyData */; projectDirPath = ""; projectRoot = ""; targets = ( 1D6058900D05DD3D006BFB54 /* SpyPhone */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ 1D60588D0D05DD3D006BFB54 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( 28AD73880D9D96C1002E5188 /* MainWindow.xib in Resources */, 282CCBFE0DB6C98000C4EA27 /* Sources.xib in Resources */, 0328F51410B065B80074A5A1 /* SPCell.xib in Resources */, 0328F56310B070AE0074A5A1 /* SPSourceTVC.xib in Resources */, 0328F8BC10B0B1780074A5A1 /* SPWebViewVC.xib in Resources */, 032A7EAD10B85E7F00E7FB65 /* SPImageMapVC.xib in Resources */, 032A809A10B8799300E7FB65 /* SPImageVC.xib in Resources */, 032A80F410B8816300E7FB65 /* Icon.png in Resources */, 032A810110B8821D00E7FB65 /* white_hat.png in Resources */, 032A811C10B883FB00E7FB65 /* Default.png in Resources */, 032A819310B8E7BA00E7FB65 /* SPEmailReportVC.xib in Resources */, 031CD55010BB4A29007C133E /* Email.png in Resources */, 031CD55110BB4A29007C133E /* Location.png in Resources */, 031CD55310BB4A29007C133E /* Phone.png in Resources */, 031CD55410BB4A29007C133E /* Photos.png in Resources */, 031CD5BC10BB4F7B007C133E /* AddressBook.png in Resources */, 031CD5BE10BB5004007C133E /* YouTube.png in Resources */, 031CD5C210BB502A007C133E /* Wifi.png in Resources */, 031CD5EB10BB5152007C133E /* Safari.png in Resources */, 03B6EFCA10BB547600CF9139 /* gpl-2.0.txt in Resources */, 03B6F00410BB588A00CF9139 /* white_hat_mask.png in Resources */, 03B6F03510BB5F4800CF9139 /* email_mask.png in Resources */, 03B2C04E10BB624F00E05ECB /* Keyboard.png in Resources */, 03B2C06110BB62A300E05ECB /* data.png in Resources */, 03B2C09410BB66DB00E05ECB /* report.png in Resources */, 0317DE7C10FF98E900C5C2D4 /* Settings.bundle in Resources */, 033805DC127CECF900EAFE64 /* LICENSE in Resources */, 033805DF127CECF900EAFE64 /* Readme.markdown in Resources */, 030AFB2F127D08BF00C9E0C6 /* SPWifiMapVC.xib in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ 1D60588E0D05DD3D006BFB54 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 1D60589B0D05DD56006BFB54 /* main.m in Sources */, 1D3623260D0F684500981E51 /* SpyPhoneAppDelegate.m in Sources */, 0328F4CF10B0614A0074A5A1 /* SPAllSourcesTVC.m in Sources */, 0328F50A10B065530074A5A1 /* SPCell.m in Sources */, 0328F56A10B071110074A5A1 /* SPSourceEmailTVC.m in Sources */, 0328F61910B07F360074A5A1 /* SPSourceWifiTVC.m in Sources */, 0328F6BC10B088DD0074A5A1 /* SPSourcePhoneTVC.m in Sources */, 0328F75210B09AA60074A5A1 /* SPSourceLocationTVC.m in Sources */, 0328F80F10B0A8B70074A5A1 /* SPSourcePhotosTVC.m in Sources */, 0328F8C010B0B1AE0074A5A1 /* SPWebViewVC.m in Sources */, 0328F96110B0CB140074A5A1 /* SPSourceAddressBookTVC.m in Sources */, 0364934110B16DDD00C88803 /* SPSourceKeyboardTVC.m in Sources */, 0364948C10B28BC800C88803 /* SPSourceTVC.m in Sources */, 031748B910B6E35C00B6116E /* SPEmailASAccount.m in Sources */, 031748C010B6E63E00B6116E /* SPEmailAccount.m in Sources */, 031748C710B6E98800B6116E /* SPEmailPOPAccount.m in Sources */, 031748CA10B6E9A000B6116E /* SPEmailIToolsAccount.m in Sources */, 0317498610B6F3A300B6116E /* SPEmailGmailAccount.m in Sources */, 0317498910B6F49300B6116E /* SPEmailIMAPAccount.m in Sources */, 032A7E0610B84C1800E7FB65 /* UIImage+GPS.m in Sources */, 032A7EB110B85E9C00E7FB65 /* SPImageMapVC.m in Sources */, 032A7ECB10B8617C00E7FB65 /* SPImageAnnotation.m in Sources */, 032A819710B8E7C600E7FB65 /* SPEmailReportVC.m in Sources */, 0310C65510C4BE0800E7ACD2 /* SPImageVC.m in Sources */, 037D3A8110F3D57B003A85B0 /* SPEmailMobileMeAccount.m in Sources */, 03F2481F1236EFAC0017F214 /* NSNumber+SP.m in Sources */, 03380599127CEC2500EAFE64 /* FMDatabase.m in Sources */, 0338059A127CEC2500EAFE64 /* FMDatabaseAdditions.m in Sources */, 0338059B127CEC2500EAFE64 /* FMResultSet.m in Sources */, 033805AE127CEC3100EAFE64 /* EXFGPS.m in Sources */, 033805AF127CEC3100EAFE64 /* EXFHandlers.m in Sources */, 033805B0127CEC3100EAFE64 /* EXFJFIF.m in Sources */, 033805B1127CEC3100EAFE64 /* EXFJpeg.m in Sources */, 033805B2127CEC3100EAFE64 /* EXFMetaData.m in Sources */, 033805B3127CEC3100EAFE64 /* EXFTagDefinitionHolder.m in Sources */, 033805B4127CEC3100EAFE64 /* EXFUtils.m in Sources */, 033805B8127CEC4200EAFE64 /* OUILookupTool.m in Sources */, 033805DD127CECF900EAFE64 /* NSObject+SBJSON.m in Sources */, 033805DE127CECF900EAFE64 /* NSString+SBJSON.m in Sources */, 033805E0127CECF900EAFE64 /* SBJsonBase.m in Sources */, 033805E1127CECF900EAFE64 /* SBJsonParser.m in Sources */, 033805E2127CECF900EAFE64 /* SBJsonStreamWriter.m in Sources */, 033805E3127CECF900EAFE64 /* SBJsonWriter.m in Sources */, 030AFB2E127D08BF00C9E0C6 /* SPWifiMapVC.m in Sources */, 030AFBA4127D0D6F00C9E0C6 /* SPWifiAnnotation.m in Sources */, 5F0DA1BC12829A6E00CD3B56 /* TVOutManager.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin XCBuildConfiguration section */ 1D6058940D05DD3E006BFB54 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; COPY_PHASE_STRIP = NO; GCC_DYNAMIC_NO_PIC = NO; GCC_OPTIMIZATION_LEVEL = 0; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = SpyPhone_Prefix.pch; INFOPLIST_FILE = "SpyPhone-Info.plist"; LIBRARY_SEARCH_PATHS = ( "$(inherited)", "\"$(SRCROOT)\"", ); PRODUCT_NAME = SpyPhone; }; name = Debug; }; 1D6058950D05DD3E006BFB54 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; COPY_PHASE_STRIP = YES; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = SpyPhone_Prefix.pch; INFOPLIST_FILE = "SpyPhone-Info.plist"; LIBRARY_SEARCH_PATHS = ( "$(inherited)", "\"$(SRCROOT)\"", ); PRODUCT_NAME = SpyPhone; }; name = Release; }; C01FCF4F08A954540054247B /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ARCHS = "$(ARCHS_STANDARD_32_BIT)"; CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; GCC_C_LANGUAGE_STANDARD = c99; GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES; GCC_WARN_UNUSED_VARIABLE = YES; PROVISIONING_PROFILE = ""; "PROVISIONING_PROFILE[sdk=iphoneos*]" = ""; SDKROOT = iphoneos; }; name = Debug; }; C01FCF5008A954540054247B /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ARCHS = "$(ARCHS_STANDARD_32_BIT)"; CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; GCC_C_LANGUAGE_STANDARD = c99; GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES; GCC_WARN_UNUSED_VARIABLE = YES; PROVISIONING_PROFILE = ""; "PROVISIONING_PROFILE[sdk=iphoneos*]" = ""; SDKROOT = iphoneos; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ 1D6058960D05DD3E006BFB54 /* Build configuration list for PBXNativeTarget "SpyPhone" */ = { isa = XCConfigurationList; buildConfigurations = ( 1D6058940D05DD3E006BFB54 /* Debug */, 1D6058950D05DD3E006BFB54 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Debug; }; C01FCF4E08A954540054247B /* Build configuration list for PBXProject "SpyPhone" */ = { isa = XCConfigurationList; buildConfigurations = ( C01FCF4F08A954540054247B /* Debug */, C01FCF5008A954540054247B /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Debug; }; /* End XCConfigurationList section */ }; rootObject = 29B97313FDCFA39411CA2CEA /* Project object */; } ================================================ FILE: SpyPhone.xcodeproj/project.xcworkspace/contents.xcworkspacedata ================================================ ================================================ FILE: SpyPhone.xcodeproj/project.xcworkspace/xcuserdata/nst.xcuserdatad/WorkspaceSettings.xcsettings ================================================ IDEWorkspaceUserSettings_HasAskedToTakeAutomaticSnapshotBeforeSignificantChanges IDEWorkspaceUserSettings_SnapshotAutomaticallyBeforeSignificantChanges ================================================ FILE: SpyPhone_Prefix.pch ================================================ // // Prefix header for all source files of the 'SpyPhone' target in the 'SpyPhone' project // #ifdef __OBJC__ #import #import #endif ================================================ FILE: gpl-2.0.txt ================================================ GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. ================================================ FILE: main.m ================================================ // // main.m // SpyPhone // // Created by Nicolas Seriot on 11/15/09. // Copyright 2009. // Licensed under GPL 2.0 http://www.gnu.org/licenses/gpl-2.0.txt // #import int main(int argc, char *argv[]) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; int retVal = UIApplicationMain(argc, argv, nil, nil); [pool release]; return retVal; }