Showing preview only (720K chars total). Download the full file or copy to clipboard to get everything.
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 <Foundation/Foundation.h>
@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 <UIKit/UIKit.h>
@class SPSourceEmailTVC;
@class SPSourceWifiTVC;
@class SPSourcePhoneTVC;
@class SPSourceLocationTVC;
@class SPSourcePhotosTVC;
@class SPSourceAddressBookTVC;
@class SPSourceKeyboardTVC;
@interface SPAllSourcesTVC : UITableViewController <UITableViewDataSource, UITableViewDelegate> {
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 <UIKit/UIKit.h>
@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 <Foundation/Foundation.h>
// 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 <UIKit/UIKit.h>
#import <MessageUI/MFMailComposeViewController.h>
#import "SPAllSourcesTVC.h"
@interface SPEmailReportVC : UIViewController <MFMailComposeViewControllerDelegate> {
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 <MapKit/MapKit.h>
@protocol MKAnnotation;
@interface SPImageAnnotation : NSObject <MKAnnotation> {
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 <UIKit/UIKit.h>
#import <MapKit/MapKit.h>
@class SPImageVC;
@interface SPImageMapVC : UIViewController <MKMapViewDelegate> {
NSArray *annotations;
IBOutlet MKMapView *mapView;
IBOutlet SPImageVC *imageVC;
}
@property (nonatomic, retain) NSArray *annotations;
- (void)addAnnotation:(id <MKAnnotation>)annotation;
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)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 <MKAnnotation>)annotation {
[mapView addAnnotation:annotation];
}
- (MKAnnotationView *)mapView:(MKMapView *)aMapView viewForAnnotation:(id <MKAnnotation>)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 <MKAnnotation>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 <UIKit/UIKit.h>
@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 <UIKit/UIKit.h>
#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 <AddressBook/AddressBook.h>
@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 <UIKit/UIKit.h>
#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 <UIKit/UIKit.h>
#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 <UIKit/UIKit.h>
#import <MapKit/MapKit.h>
#import "SPSourceTVC.h"
@class CLLocation;
@interface SPSourceLocationTVC : SPSourceTVC /* <MKReverseGeocoderDelegate> */ {
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 <CoreLocation/CoreLocation.h>
#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 <UIKit/UIKit.h>
#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 <AddressBook/AddressBook.h>
#import "FMDatabase.h"
#import "NSNumber+SP.h"
#import <CoreTelephony/CTTelephonyNetworkInfo.h>
#import <CoreTelephony/CTCarrier.h>
@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 <CoreLocation/CoreLocation.h>
#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 <UIKit/UIKit.h>
#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 <UIKit/UIKit.h>
#import "SPSourceTVC.h"
#import "OUILookupTool.h"
@class SPWifiMapVC;
@interface SPSourceWifiTVC : SPSourceTVC <OUILookupToolDelegate> {
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 <UIKit/UIKit.h>
@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 <MapKit/MapKit.h>
@protocol MKAnnotation;
@interface SPWifiAnnotation : NSObject <MKAnnotation> {
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 <UIKit/UIKit.h>
#import <MapKit/MapKit.h>
@interface SPWifiMapVC : UIViewController {
NSArray *annotations;
IBOutlet MKMapView *mapView;
}
@property (nonatomic, retain) NSArray *annotations;
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id <MKAnnotation>)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 <MKAnnotation>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 <MKAnnotation>)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
================================================
<?xml version="1.0" encoding="UTF-8"?>
<archive type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="7.10">
<data>
<int key="IBDocument.SystemTarget">1024</int>
<string key="IBDocument.SystemVersion">10F569</string>
<string key="IBDocument.InterfaceBuilderVersion">804</string>
<string key="IBDocument.AppKitVersion">1038.29</string>
<string key="IBDocument.HIToolboxVersion">461.00</string>
<object class="NSMutableDictionary" key="IBDocument.PluginVersions">
<string key="NS.key.0">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string key="NS.object.0">123</string>
</object>
<object class="NSMutableArray" key="IBDocument.EditedObjectIDs">
<bool key="EncodedWithXMLCoder">YES</bool>
<integer value="1"/>
</object>
<object class="NSArray" key="IBDocument.PluginDependencies">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
</object>
<object class="NSMutableDictionary" key="IBDocument.Metadata">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="NSArray" key="dict.sortedKeys" id="0">
<bool key="EncodedWithXMLCoder">YES</bool>
</object>
<object class="NSMutableArray" key="dict.values">
<bool key="EncodedWithXMLCoder">YES</bool>
</object>
</object>
<object class="NSMutableArray" key="IBDocument.RootObjects" id="1000">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="IBProxyObject" id="372490531">
<string key="IBProxiedObjectIdentifier">IBFilesOwner</string>
<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
</object>
<object class="IBProxyObject" id="975951072">
<string key="IBProxiedObjectIdentifier">IBFirstResponder</string>
<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
</object>
<object class="IBUIView" id="191373211">
<reference key="NSNextResponder"/>
<int key="NSvFlags">274</int>
<object class="NSMutableArray" key="NSSubviews">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="IBMKMapView" id="157797824">
<reference key="NSNextResponder" ref="191373211"/>
<int key="NSvFlags">274</int>
<string key="NSFrameSize">{320, 460}</string>
<reference key="NSSuperview" ref="191373211"/>
<bool key="IBUIClipsSubviews">YES</bool>
<bool key="IBUIMultipleTouchEnabled">YES</bool>
<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
</object>
</object>
<string key="NSFrameSize">{320, 460}</string>
<reference key="NSSuperview"/>
<object class="NSColor" key="IBUIBackgroundColor">
<int key="NSColorSpace">3</int>
<bytes key="NSWhite">MQA</bytes>
</object>
<object class="IBUISimulatedStatusBarMetrics" key="IBUISimulatedStatusBarMetrics"/>
<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
</object>
</object>
<object class="IBObjectContainer" key="IBDocument.Objects">
<object class="NSMutableArray" key="connectionRecords">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="IBConnectionRecord">
<object class="IBCocoaTouchOutletConnection" key="connection">
<string key="label">view</string>
<reference key="source" ref="372490531"/>
<reference key="destination" ref="191373211"/>
</object>
<int key="connectionID">3</int>
</object>
<object class="IBConnectionRecord">
<object class="IBCocoaTouchOutletConnection" key="connection">
<string key="label">mapView</string>
<reference key="source" ref="372490531"/>
<reference key="destination" ref="157797824"/>
</object>
<int key="connectionID">5</int>
</object>
<object class="IBConnectionRecord">
<object class="IBCocoaTouchOutletConnection" key="connection">
<string key="label">delegate</string>
<reference key="source" ref="157797824"/>
<reference key="destination" ref="372490531"/>
</object>
<int key="connectionID">6</int>
</object>
</object>
<object class="IBMutableOrderedSet" key="objectRecords">
<object class="NSArray" key="orderedObjects">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="IBObjectRecord">
<int key="objectID">0</int>
<reference key="object" ref="0"/>
<reference key="children" ref="1000"/>
<nil key="parent"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">-1</int>
<reference key="object" ref="372490531"/>
<reference key="parent" ref="0"/>
<string key="objectName">File's Owner</string>
</object>
<object class="IBObjectRecord">
<int key="objectID">-2</int>
<reference key="object" ref="975951072"/>
<reference key="parent" ref="0"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">1</int>
<reference key="object" ref="191373211"/>
<object class="NSMutableArray" key="children">
<bool key="EncodedWithXMLCoder">YES</bool>
<reference ref="157797824"/>
</object>
<reference key="parent" ref="0"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">4</int>
<reference key="object" ref="157797824"/>
<reference key="parent" ref="191373211"/>
</object>
</object>
</object>
<object class="NSMutableDictionary" key="flattenedProperties">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="NSArray" key="dict.sortedKeys">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>-1.CustomClassName</string>
<string>-2.CustomClassName</string>
<string>1.IBEditorWindowLastContentRect</string>
<string>1.IBPluginDependency</string>
<string>4.IBPluginDependency</string>
<string>4.IBViewBoundsToFrameTransform</string>
</object>
<object class="NSMutableArray" key="dict.values">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>SPWifiMapVC</string>
<string>UIResponder</string>
<string>{{556, 376}, {320, 480}}</string>
<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<object class="NSAffineTransform">
<bytes key="NSTransformStruct">P4AAAL+AAADBMAAAxA2AAA</bytes>
</object>
</object>
</object>
<object class="NSMutableDictionary" key="unlocalizedProperties">
<bool key="EncodedWithXMLCoder">YES</bool>
<reference key="dict.sortedKeys" ref="0"/>
<object class="NSMutableArray" key="dict.values">
<bool key="EncodedWithXMLCoder">YES</bool>
</object>
</object>
<nil key="activeLocalization"/>
<object class="NSMutableDictionary" key="localizations">
<bool key="EncodedWithXMLCoder">YES</bool>
<reference key="dict.sortedKeys" ref="0"/>
<object class="NSMutableArray" key="dict.values">
<bool key="EncodedWithXMLCoder">YES</bool>
</object>
</object>
<nil key="sourceID"/>
<int key="maxID">6</int>
</object>
<object class="IBClassDescriber" key="IBDocument.Classes">
<object class="NSMutableArray" key="referencedPartialClassDescriptions">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="IBPartialClassDescription">
<string key="className">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBProjectSource</string>
<string key="minorKey">JSON/NSObject+SBJSON.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBProjectSource</string>
<string key="minorKey">JSON/SBProxyForJson.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">SPWifiMapVC</string>
<string key="superclassName">UIViewController</string>
<object class="NSMutableDictionary" key="outlets">
<string key="NS.key.0">mapView</string>
<string key="NS.object.0">MKMapView</string>
</object>
<object class="NSMutableDictionary" key="toOneOutletInfosByName">
<string key="NS.key.0">mapView</string>
<object class="IBToOneOutletInfo" key="NS.object.0">
<string key="name">mapView</string>
<string key="candidateClassName">MKMapView</string>
</object>
</object>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBProjectSource</string>
<string key="minorKey">Classes/SPWifiMapVC.h</string>
</object>
</object>
</object>
<object class="NSMutableArray" key="referencedPartialClassDescriptionsV3.2+">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="IBPartialClassDescription">
<string key="className">MKMapView</string>
<string key="superclassName">UIView</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">MapKit.framework/Headers/MKMapView.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">Foundation.framework/Headers/NSError.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">Foundation.framework/Headers/NSFileManager.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">Foundation.framework/Headers/NSKeyValueCoding.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">Foundation.framework/Headers/NSKeyValueObserving.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">Foundation.framework/Headers/NSKeyedArchiver.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">Foundation.framework/Headers/NSObject.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">Foundation.framework/Headers/NSRunLoop.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">Foundation.framework/Headers/NSThread.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">Foundation.framework/Headers/NSURL.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">Foundation.framework/Headers/NSURLConnection.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">UIKit.framework/Headers/UIAccessibility.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">UIKit.framework/Headers/UINibLoading.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier" id="381350413">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">UIKit.framework/Headers/UIResponder.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">UIResponder</string>
<string key="superclassName">NSObject</string>
<reference key="sourceIdentifier" ref="381350413"/>
</object>
<object class="IBPartialClassDescription">
<string key="className">UISearchBar</string>
<string key="superclassName">UIView</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">UIKit.framework/Headers/UISearchBar.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">UISearchDisplayController</string>
<string key="superclassName">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">UIKit.framework/Headers/UISearchDisplayController.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">UIView</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">UIKit.framework/Headers/UITextField.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">UIView</string>
<string key="superclassName">UIResponder</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">UIKit.framework/Headers/UIView.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">UIViewController</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">MediaPlayer.framework/Headers/MPMoviePlayerViewController.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">UIViewController</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">UIKit.framework/Headers/UINavigationController.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">UIViewController</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">UIKit.framework/Headers/UIPopoverController.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">UIViewController</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">UIKit.framework/Headers/UISplitViewController.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">UIViewController</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">UIKit.framework/Headers/UITabBarController.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">UIViewController</string>
<string key="superclassName">UIResponder</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBFrameworkSource</string>
<string key="minorKey">UIKit.framework/Headers/UIViewController.h</string>
</object>
</object>
</object>
</object>
<int key="IBDocument.localizationMode">0</int>
<string key="IBDocument.TargetRuntimeIdentifier">IBCocoaTouchFramework</string>
<object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDependencyDefaults">
<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaTouchPlugin.iPhoneOS</string>
<integer value="1024" key="NS.object.0"/>
</object>
<object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDevelopmentDependencies">
<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaTouchPlugin.InterfaceBuilder3</string>
<integer value="3000" key="NS.object.0"/>
</object>
<bool key="IBDocument.PluginDeclaredDependenciesTrackSystemTargetVersion">YES</bool>
<string key="IBDocument.LastKnownRelativeProjectPath">../SpyPhone.xcodeproj</string>
<int key="IBDocument.defaultPropertyAccessControl">3</int>
<string key="IBCocoaTouchPluginVersion">123</string>
</data>
</archive>
================================================
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 <UIKit/UIKit.h>
@interface SpyPhoneAppDelegate : NSObject <UIApplicationDelegate, UITabBarControllerDelegate> {
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 <Foundation/Foundation.h>
@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 <QuartzCore/QuartzCore.h>
#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 <QuartzCore/QuartzCore.h>
#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 <UIKit/UIKit.h>
#import <CoreLocation/CoreLocation.h>
@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 <a href="http://www.exif.org/Exif2-2.PDF">http://www.exif.org/Exif2-2.PDF</a>
*/
@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<EXFTagHandler>
@end
@interface EXFGPSTimeHandler: NSObject<EXFTagHandler>
@end
@interface EXFTextHandler: NSObject<EXFTagHandler>
@end
@interface EXFASCIIHandler: NSObject<EXFTagHandler>
@end
@interface EXFByteHandler: NSObject<EXFTagHandler>
@end
@interface EXFByteArrayHandler: NSObject<EXFTagHandler>
@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<byteLength;i++){
NSNumber* num = [[NSNumber alloc] initWithInt: (*(ptr+i)) & 0xff] ;
[byteArray addObject: num];
[num release];
}
// Debug(@"Assigned string %@",value);
[keyedValues setObject: byteArray forKey: tagId];
[byteArray release];
}
-(void)encodeTag: (NSMutableData*) targetBuffer: (id) tagData:(BOOL) bigEndianOrder{
// tag data is an array of NSNumber
UInt8 byte[1];
for(NSNumber* val in ((NSArray*)tagData)){
byte[0] =(UInt8) [val intValue];
[targetBuffer appendBytes:byte length:1];
}
}
-(BOOL)supportsValueType:(id) value{
if ([value isKindOfClass:[NSArray class]]){
return TRUE;
}else{
return FALSE;
}
}
-(int) getSizeOfValue:(id)value{
// value should be an array
if ([value isKindOfClass:[NSArray class]]){
return [((NSArray*)value) count];
}else{
return -1;
}
}
-(NSString*) description{
return @"EXF Byte Array Handler";
}
@end
================================================
FILE: EXIF/EXFJFIF.h
================================================
//
// EXFJFIF.h
// iphone-test
//
// Created by steve woodcock on 24/03/2008.
// Copyright 2008 __MyCompanyName__.
// Licensed under GPL 2.0 http://www.gnu.org/licenses/gpl-2.0.txt
//
#define EXIF_JPEGCoding 0x10
#define EXIF_1ByteCoding 0x11
#define EXIF_3ByteCoding 0x13
enum JFIFUnits {
JFIF_NONE =0,
JFIF_DPI = 1,
JFIF_DPC = 2
};
typedef enum JFIFUnits JFIFUnits;
@interface EXFJFIF : NSObject {
NSString* identifier;
int length;
int resolutionX;
int resolutionY;
int thumbnailX;
int thumbnailY;
NSData* thumbnail;
NSString* version;
JFIFUnits units;
}
@property (readonly, retain) NSString* identifier;
@property (readonly, retain) NSString* version;
@property (readonly) int length;
@property (readonly) int resolutionX;
@property (readonly) int resolutionY;
@property (readonly) int thumbnailX;
@property (readonly) int thumbnailY;
@property (readonly,retain) NSData* thumbnail;
@property (readonly) JFIFUnits units;
@end
================================================
FILE: EXIF/EXFJFIF.m
================================================
//
// EXFJFIF.m
// iphone-test
//
// Created by steve woodcock on 24/03/2008.
// Copyright 2008 __MyCompanyName__.
// Licensed under GPL 2.0 http://www.gnu.org/licenses/gpl-2.0.txt
//
#import "EXFMutableMetaData.h"
#import "EXFLogging.h"
@implementation EXFJFIF
@synthesize identifier;
@synthesize version;
@synthesize length;
@synthesize resolutionX;
@synthesize resolutionY;
@synthesize thumbnailX;
@synthesize thumbnailY;
@synthesize thumbnail;
@synthesize units;
const NSString* JFIF_IDENTIFIER = @"JFIF\0";
const int JFIF_MIN_LENGTH =14;
-(id) init{
if (self = [super init]) {
self.identifier =nil;
self.version =nil;
self.thumbnail =nil;
self.length =0;
}
return self;
}
-(void) dealloc{
self.identifier = nil;
self.version =nil;
self.thumbnail=nil;
[super dealloc];
}
- (void) parseJfif:(CFDataRef*) theJfifData{
CFIndex dataLen = CFDataGetLength(*theJfifData);
// make sure the data len is big enough for the parsing
if (dataLen < JFIF_MIN_LENGTH )
{
Debug(@"Length for JFIF is too short at %i", dataLen);
return;
}else{
int strLen =[JFIF_IDENTIFIER length];
// get a pointer in the array
UInt8* bytePtr = (UInt8 *) CFDataGetBytePtr(*theJfifData);
// get the text identifier
NSData* commentData = [NSData dataWithBytes:bytePtr length:strLen];
NSString* comments = [[NSString alloc] initWithBytes:[commentData bytes] length:strLen encoding:NSASCIIStringEncoding];
// if identifier is nil or not JFIF then it is some app specific thing that we can skip
if (comments != nil && ([JFIF_IDENTIFIER compare: comments] == NSOrderedSame)){
self.length = dataLen;
self.identifier =comments;
// get the version
UInt8 majorVersion = bytePtr[strLen];
UInt8 minorVersion = bytePtr[strLen+1];
NSString* ver = [[NSString alloc] initWithFormat:@"%x.%x",majorVersion, minorVersion];
self.version = ver;
[ver release];
// get the units
self.units = bytePtr[strLen+2];
//JFIF is always big endian
self.resolutionX = ((bytePtr[strLen+3] << 8) | bytePtr[strLen+4]);
self.resolutionY =((bytePtr[strLen+5] << 8) | bytePtr[strLen+6]);
// get the thumbnail data
self.thumbnailX = bytePtr[strLen+7];
self.thumbnailY = bytePtr[strLen+8];
if (self.thumbnailX !=0 && self.thumbnailY != 0){
// thumbnail is 3n where n = thumbnailX x thumnailY
long thumbnailBytes = 3 * (self.thumbnailX * self.thumbnailY);
// see if the data len is enough for the image
if (thumbnailBytes == (dataLen -JFIF_MIN_LENGTH)){
NSData* thumbnailData = [NSData dataWithBytes: &bytePtr[JFIF_MIN_LENGTH] length:thumbnailBytes];
self.thumbnail = thumbnailData;
}else{
Debug(@"Thumbnail bytes %i is not equal to data length remaining %i", thumbnailBytes,dataLen -JFIF_MIN_LENGTH);
}
}
}
// release our comments string
[comments release];
}
}
@end
================================================
FILE: EXIF/EXFJpeg.h
================================================
/*
* EXFJpeg.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 EXFJpeg object is used to scan the original image in order to extract the JFIF/EXIF data and
* following any changes to the EXIF object will return the bytes representing the new image.
*/
#import "EXFMetaData.h"
#import "EXFJFIF.h"
#import "EXFJpeg.h"
#import "EXFConstants.h"
@interface EXFJpeg : NSObject {
// stores length of the image in bytes
CFIndex imageLength;
// pointer to the start of the image byte array
ByteArray* imageStartPtr;
// pointer to the current parsing point in the byte array
ByteArray* imageBytePtr;
// A dictionary of the EXIF blocks in the file that have been parsed
NSMutableDictionary* keyedHeaders;
// The EXF MetaData image attributes
EXFMetaData* exifMetaData;
// Image attributes outside EXIF in the Components section of the file
int numComponents;
// The JFIF MetaData image attributes
EXFJFIF* jfif;
NSData* remainingData;
}
/*
Returns the EXIF MetaData object.
*/
@property (readonly, retain) EXFMetaData* exifMetaData;
/*
Returns the JFIF Meta Data of a scanned Image
*/
@property (readonly, retain) EXFJFIF* jfif;
/*
Scans the Image Data
*/
-(void) scanImageData:(NSData*) imageData;
/*
Returns the image byte array for the new image with amended data
*/
-(void) populateImageData: (NSMutableData*) newImageData;
@end
================================================
FILE: EXIF/EXFJpeg.m
================================================
//
// Jpeg.m
// iphone-test
//
// Created by steve woodcock on 10/03/2008.
// Copyright 2008 __MyCompanyName__. All rights reserved.
//
#import "EXFJpeg.h"
#import "EXFLogging.h"
#import "EXFConstants.h"
#import "EXFMutableMetaData.h"
#import "EXFUtils.h"
#define M_BEG 0xff /* Start of marker. Used for all markers tags in format ffxx */
/*The next set of bytes define the second part of the tag - e.g the xx part */
/* Start of image. This is the first tag in the file after the header */
#define M_SOI 0xd8
/* The app tags store the extra dat - usually we expect to see only app1 (and optionally app2) - but other apps could be
present */
#define M_APP0 0xe0 /* APP0 marker. */
#define M_APP1 0xe1 /* APP1 marker. */
#define M_APP2 0xe2 /* APP2 marker. */
#define M_APP3 0xe3 /* APP3 marker. */
#define M_APP4 0xe4 /* APP4 marker. */
#define M_APP5 0xe5 /* APP5 marker. */
#define M_APP6 0xe6 /* APP6 marker. */
#define M_APP7 0xe7 /* APP7 marker. */
#define M_APP8 0xe8 /* APP8 marker. */
#define M_APP9 0xe9 /* APP9 marker. */
#define M_APP10 0xea /* APP10 marker. */
#define M_APP11 0xeb /* APP11 marker. */
#define M_APP12 0xec /* APP12 marker. */
#define M_APP13 0xed /* APP13 marker. */
#define M_APP14 0xee /* APP14 marker. */
#define M_APP15 0xef /* APP15 marker. */
#define M_DQT 0xdb /* Quantatization Table. */
#define M_DHT 0xc4 /* Huffman Table. */
#define M_DRI 0xdd /* Restart Interoperability. */
/* Start of frame of image data... */
#define M_SOF0 0xc0
#define M_SOF1 0xc1
#define M_SOF2 0xc2
#define M_SOF3 0xc3
#define M_SOF5 0xc5
#define M_SOF6 0xc6
#define M_SOF7 0xc7
#define M_SOF9 0xc9
#define M_SOF10 0xca
#define M_SOF11 0xcb
#define M_SOF13 0xcd
#define M_SOF14 0xce
#define M_SOF15 0xcf
#define M_SOS 0xda /* Start of scan. */
#define M_EOI 0xd9 /* End of image. */
#define M_ERR 0x100
#define M_COM 0xfe
@interface EXFJpeg ()
@property (readwrite, retain) NSMutableDictionary* keyedHeaders;
@property (readwrite, retain) EXFMetaData* exifMetaData;
@property (readwrite, retain) EXFJFIF* jfif;
@property (readwrite, retain) NSData* remainingData;
@end
@implementation EXFJpeg
@synthesize keyedHeaders;
@synthesize exifMetaData;
@synthesize jfif;
@synthesize remainingData;
-(id) init {
if (self = [super init]) {
// Initializeyour own data
NSMutableDictionary* headerDictionary = [[NSMutableDictionary alloc] init];
self.keyedHeaders =headerDictionary;
[headerDictionary release];
EXFMetaData* exifParam =[[EXFMetaData alloc]init];
self.exifMetaData = exifParam;
[exifParam release];
self.jfif =nil;
// initialise pointers
imageBytePtr =NULL;
imageStartPtr =NULL;
}
return self;
}
-(void) dealloc{
self.keyedHeaders = nil;
self.exifMetaData = nil;
self.jfif =nil;
self.remainingData =nil;
imageBytePtr =NULL;
imageStartPtr =NULL;
// release super class
[super dealloc];
}
-(bool) imageLengthCheck:(int) length{
int remaining = imageLength -(imageBytePtr - imageStartPtr);
return (length < remaining);
}
- (void) skipBytes: (int) bytes
{
// increment to leave us at the next byte
*(imageBytePtr+=bytes);
}
- (UInt8) readNextbyte
{
UInt8 byte;
// increment the marker ptr
byte = *(imageBytePtr);
// increment to leave us at the next byte
*(imageBytePtr++);
return byte;
}
- (int) readNext2bytes
{
UInt8 b1, b2;
// increment the marker ptr
b1 = *imageBytePtr;
// get the next value
*(imageBytePtr++);
b2 = *imageBytePtr;
*(imageBytePtr++);
// return the values we have got
return ((b1 << 8) | b2);
}
-(UInt8) nextMarker {
UInt8 val = [self readNextbyte];
/* Find 0xFF byte; count and skip any non-FFs. */
while (val != M_BEG){
val = [self readNextbyte];
}
do {
val = [self readNextbyte];
} while(val == M_BEG);
// increment to one after
return val;
}
- (void) readImageInfo
{
int len = [self readNext2bytes] - 2;
if (len < 0 ){
// throw new JpegException("Erroneous JPEG marker length");
NSLog(@"ERROR: Length is negative in reading image info ");
return;
}
if (len > 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<EXFTagHandler>) 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<EXFTagHandler>) 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<EXFTagHandler> 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<components;i++){
SInt8 val = exif_ptr[valueOffset];
// Debug(@"Got Signed byte %i",val);
// create the byte
NSNumber* value = [[NSNumber alloc] initWithInt:val];
valueOffset+=1;
[elements addObject:value];
[value release];
}
[self assignElements:keyedValues :tag :elements :components];
[elements release];
}
-(void) assignByte:(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<components;i++){
UInt8 val = exif_ptr[valueOffset];
// Debug(@"Got byte %i",val);
NSNumber* value = [[NSNumber alloc] initWithUnsignedInt:val];
valueOffset+=1;
[elements addObject:value];
[value release];
}
// release the val
[self assignElements:keyedValues :tag :elements :components];
[elements release];
}
-(void) assignUShort:(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<components;i++){
UInt8* ptr = exif_ptr+valueOffset;
UInt16 val = [EXFUtils read2Bytes:&ptr: self.bigEndianOrder];
// Debug(@"Got U Short %i",val);
NSNumber* value = [[NSNumber alloc] initWithUnsignedInt:val];
valueOffset+=2;
[elements addObject:value];
[value release];
}
// release the val
[self assignElements:keyedValues :tag :elements :components];
[elements release];
}
-(void) assignShort:(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<components;i++){
UInt8* ptr = exif_ptr+valueOffset;
SInt16 val = [EXFUtils read2SignedBytes:&ptr: self.bigEndianOrder];
// Debug(@"Got U Short %i",val);
NSNumber* value = [[NSNumber alloc] initWithInt:val];
valueOffset+=2;
[elements addObject:value];
[value release];
}
// release the val
[self assignElements:keyedValues :tag :elements :components];
[elements release];
}
-(void) assignLong:(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<components;i++){
UInt8* ptr = exif_ptr+valueOffset;
SInt32 val = [EXFUtils read4SignedBytes:&ptr: self.bigEndianOrder];
// Debug(@"Got Long %i",val);
NSNumber* value = [[NSNumber alloc] initWithLong:val];
valueOffset+=4;
[elements addObject:value];
[value release];
}
// release the val
[self assignElements:keyedValues :tag :elements :components];
[elements release];
}
-(void) assignULong:(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<components;i++){
UInt8* ptr = exif_ptr+valueOffset;
UInt32 val = [EXFUtils read4Bytes:&ptr: self.bigEndianOrder];
// Debug(@"Got U Long %i",val);
NSNumber* value = [[NSNumber alloc] initWithUnsignedLong:val];
valueOffset+=4;
[elements addObject:value];
[value release];
}
// release the val
[self assignElements:keyedValues :tag :elements :components];
[elements release];
}
-(void) assignString:(NSMutableDictionary*) keyedValues: (NSNumber*) tag: (UInt32) valueOffset: (UInt32) components{
UInt8* ptr = exif_ptr+valueOffset;
NSString* value = [EXFUtils newStringFromBuffer:&ptr: components: NSASCIIStringEncoding];
// Debug(@"Assigned string %@",value);
[keyedValues setObject: value forKey: tag];
[value release];
}
-(void) assignData:(NSMutableDictionary*) keyedValues: (NSNumber*) tag: (UInt32) valueOffset: (UInt32) components{
UInt8* ptr = exif_ptr+valueOffset;
NSData* value = [[NSData alloc] initWithBytes:ptr length: components];
Debug(@"Assigned data block %@",value);
[keyedValues setObject: value forKey: tag];
[value release];
}
-(void) assignFraction:(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<components;i++){
UInt8* ptr = exif_ptr+valueOffset;
UInt32 num = [EXFUtils read4Bytes:&ptr: self.bigEndianOrder];
ptr = exif_ptr+valueOffset+4;
UInt32 denom = [EXFUtils read4Bytes:&ptr: self.bigEndianOrder];
EXFraction* value = [[EXFraction alloc] initWith:num: denom];
valueOffset+=8;
[elements addObject:value];
[value release];
}
// release the val
[self assignElements:keyedValues :tag :elements :components];
[elements release];
}
-(void) assignSignedFraction:(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<components;i++){
UInt8* ptr = exif_ptr+valueOffset;
SInt32 num = [EXFUtils read4SignedBytes:&ptr: self.bigEndianOrder];
ptr = exif_ptr+valueOffset+4;
SInt32 denom = [EXFUtils read4SignedBytes:&ptr: self.bigEndianOrder];
EXFraction* value = [[EXFraction alloc] initWith:num: denom];
// Debug(@"Assign signed rational called %@", value);
valueOffset+=8;
[elements addObject:value];
[value release];
}
// release the val
[self assignElements:keyedValues :tag :elements :components];
[elements release];
}
-(void) appendDataFromBytes: (NSMutableData*) data: (NSArray*) bytes{
UInt8 byte[1];
for(NSNumber* val in bytes){
byte[0] = [val intValue] && 0xff;
[data appendBytes:byte length:1];
}
}
-(int) processExifDir:(NSMutableDictionary*) keyedValues: (int) dirStart: (int) offsetBase: (BOOL) thumbnail{
// if we have a tag failure it is not safe to get the thumbnail as the offsets could be wrong
BOOL tagFailure =FALSE;
Debug(@"********** Entering exif processing at offset %i ********",dirStart);
UInt8* ptr = exif_ptr+dirStart;
UInt16 numEntries = [EXFUtils read2Bytes:&ptr: self.bigEndianOrder];
// the possible thumbnail offset with no overflow values
int thumbnailDataCount = dirStart;
Debug(@"Number of entries in block %i", numEntries);
for (int de =0;de <numEntries;de++){
int dirOffset = dirStart +2 +(12*de);
// count of current processed values
int processedValueCount = [keyedValues count];
// get the tag id
ptr = exif_ptr+dirOffset;
UInt16 tag = [EXFUtils read2Bytes:&ptr: self.bigEndianOrder];
// this is a hack for IPhone - which appears to have an off by one error in the EXIF block
// this may be a problem in the GPS blok if tag 0 is the last one
if (tag ==0 && de == numEntries -1){
numEntries -=1;
NSLog(@"*** Warning IPhone off by one count - skipping non-existent tag");
continue;
}
Debug(@"Parsing tag %i at
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
SYMBOL INDEX (43 symbols across 37 files)
FILE: Classes/SPCell.h
function interface (line 13) | interface SPCell : UITableViewCell {
FILE: Classes/SPEmailASAccount.h
function interface (line 12) | interface SPEmailASAccount : SPEmailAccount {
FILE: Classes/SPEmailAccount.h
function interface (line 14) | interface SPEmailAccount : NSObject {
FILE: Classes/SPEmailGmailAccount.h
function interface (line 13) | interface SPEmailGmailAccount : SPEmailAccount {
FILE: Classes/SPEmailIMAPAccount.h
function interface (line 13) | interface SPEmailIMAPAccount : SPEmailAccount {
FILE: Classes/SPEmailIToolsAccount.h
function interface (line 12) | interface SPEmailIToolsAccount : SPEmailAccount {
FILE: Classes/SPEmailMobileMeAccount.h
function interface (line 13) | interface SPEmailMobileMeAccount : SPEmailAccount {
FILE: Classes/SPEmailPOPAccount.h
function interface (line 12) | interface SPEmailPOPAccount : SPEmailAccount {
FILE: Classes/SPEmailReportVC.h
function interface (line 14) | interface SPEmailReportVC : UIViewController <MFMailComposeViewControlle...
FILE: Classes/SPImageAnnotation.h
function interface (line 14) | interface SPImageAnnotation : NSObject <MKAnnotation> {
FILE: Classes/SPImageMapVC.h
function interface (line 15) | interface SPImageMapVC : UIViewController <MKMapViewDelegate> {
FILE: Classes/SPImageVC.h
function interface (line 12) | interface SPImageVC : UIViewController {
FILE: Classes/SPSourceAddressBookTVC.h
function interface (line 13) | interface SPSourceAddressBookTVC : SPSourceTVC {
FILE: Classes/SPSourceEmailTVC.h
function interface (line 13) | interface SPSourceEmailTVC : SPSourceTVC {
FILE: Classes/SPSourceKeyboardTVC.h
function interface (line 13) | interface SPSourceKeyboardTVC : SPSourceTVC {
FILE: Classes/SPSourceLocationTVC.h
function interface (line 16) | interface SPSourceLocationTVC : SPSourceTVC /* <MKReverseGeocoderDelegat...
FILE: Classes/SPSourcePhoneTVC.h
function interface (line 13) | interface SPSourcePhoneTVC : SPSourceTVC {
FILE: Classes/SPSourcePhotosTVC.h
function interface (line 15) | interface SPSourcePhotosTVC : SPSourceTVC {
FILE: Classes/SPSourceTVC.h
function interface (line 19) | interface SPSourceTVC : UITableViewController {
FILE: Classes/SPSourceWifiTVC.h
function interface (line 16) | interface SPSourceWifiTVC : SPSourceTVC <OUILookupToolDelegate> {
FILE: Classes/SPWebViewVC.h
function interface (line 13) | interface SPWebViewVC : UIViewController {
FILE: Classes/SPWifiAnnotation.h
function interface (line 13) | interface SPWifiAnnotation : NSObject <MKAnnotation> {
FILE: Classes/SPWifiMapVC.h
function interface (line 12) | interface SPWifiMapVC : UIViewController {
FILE: Classes/TVOutManager.h
function interface (line 12) | interface TVOutManager : NSObject {
FILE: EXIF/EXFConstants.h
type UInt8 (line 24) | typedef UInt8 ByteArray;
type UInt16 (line 30) | typedef UInt16 EXFTagId;
type EXFDataType (line 38) | enum EXFDataType {
type EXFDataType (line 57) | typedef enum EXFDataType EXFDataType;
function interface (line 187) | interface EXFraction: NSObject {
FILE: EXIF/EXFGPS.h
function interface (line 28) | interface EXFGPSLoc : NSObject {
FILE: EXIF/EXFJFIF.h
type JFIFUnits (line 15) | enum JFIFUnits {
type JFIFUnits (line 20) | typedef enum JFIFUnits JFIFUnits;
function interface (line 22) | interface EXFJFIF : NSObject {
FILE: EXIF/EXFJpeg.h
function interface (line 19) | interface EXFJpeg : NSObject {
FILE: EXIF/EXFMetaData.h
function interface (line 19) | interface EXFMetaData : NSObject {
FILE: EXIF/EXFTagDefinitionHolder.h
function interface (line 12) | interface EXFTagDefinitionHolder :NSObject {
FILE: EXIF/EXFUtils.h
function interface (line 15) | interface EXFUtils : NSObject {
FILE: FMDB/FMDatabase.h
function interface (line 5) | interface FMDatabase : NSObject
FILE: FMDB/FMResultSet.h
function interface (line 19) | interface FMResultSet : NSObject {
FILE: JSON/SBJsonBase.h
function interface (line 55) | interface SBJsonBase : NSObject {
FILE: JSON/SBJsonParser.h
function interface (line 54) | interface SBJsonParser : SBJsonBase {
FILE: JSON/SBJsonStreamWriter.h
function interface (line 58) | interface SBJsonStreamWriter : NSObject {
FILE: JSON/SBJsonWriter.h
function interface (line 55) | interface SBJsonWriter : SBJsonBase {
Condensed preview — 120 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (776K chars).
[
{
"path": "Classes/NSNumber+SP.h",
"chars": 238,
"preview": "//\n// NSNumber+SL.h\n// SpotLook\n//\n// Created by Nicolas Seriot on 31.03.08.\n// Copyright 2008 __MyCompanyName__. Al"
},
{
"path": "Classes/NSNumber+SP.m",
"chars": 731,
"preview": "//\n// NSNumber+SL.m\n// SpotLook\n//\n// Created by Nicolas Seriot on 31.03.08.\n// Copyright 2008 __MyCompanyName__. Al"
},
{
"path": "Classes/SPAllSourcesTVC.h",
"chars": 954,
"preview": "//\n// SourcesTVController.h\n// SpyPhone\n//\n// Created by Nicolas Seriot on 11/15/09.\n// Copyright 2009. \n// License"
},
{
"path": "Classes/SPAllSourcesTVC.m",
"chars": 2602,
"preview": "//\n// SourcesTVController.m\n// SpyPhone\n//\n// Created by Nicolas Seriot on 11/15/09.\n// Copyright 2009. \n// License"
},
{
"path": "Classes/SPCell.h",
"chars": 240,
"preview": "//\n// SPCell.h\n// SpyPhone\n//\n// Created by Nicolas Seriot on 11/15/09.\n// Copyright 2009. \n// Licensed under GPL 2"
},
{
"path": "Classes/SPCell.m",
"chars": 217,
"preview": "//\n// SPCell.m\n// SpyPhone\n//\n// Created by Nicolas Seriot on 11/15/09.\n// Copyright 2009. \n// Licensed under GPL 2"
},
{
"path": "Classes/SPEmailASAccount.h",
"chars": 262,
"preview": "//\n// SPEmailASAccount.h\n// SpyPhone\n//\n// Created by Nicolas Seriot on 11/20/09.\n// Copyright 2009. \n// Licensed u"
},
{
"path": "Classes/SPEmailASAccount.m",
"chars": 742,
"preview": "//\n// SPEmailASAccount.m\n// SpyPhone\n//\n// Created by Nicolas Seriot on 11/20/09.\n// Copyright 2009. \n// Licensed u"
},
{
"path": "Classes/SPEmailAccount.h",
"chars": 876,
"preview": "//\n// SPEmailAccount.h\n// SpyPhone\n//\n// Created by Nicolas Seriot on 11/20/09.\n// Copyright 2009. \n// Licensed und"
},
{
"path": "Classes/SPEmailAccount.m",
"chars": 1301,
"preview": "//\n// SPEmailAccount.m\n// SpyPhone\n//\n// Created by Nicolas Seriot on 11/20/09.\n// Copyright 2009. \n// Licensed und"
},
{
"path": "Classes/SPEmailGmailAccount.h",
"chars": 268,
"preview": "//\n// SPEmailGmailAccount.h\n// SpyPhone\n//\n// Created by Nicolas Seriot on 11/20/09.\n// Copyright 2009. \n// License"
},
{
"path": "Classes/SPEmailGmailAccount.m",
"chars": 917,
"preview": "//\n// SPEmailGmailAccount.m\n// SpyPhone\n//\n// Created by Nicolas Seriot on 11/20/09.\n// Copyright 2009. \n// License"
},
{
"path": "Classes/SPEmailIMAPAccount.h",
"chars": 266,
"preview": "//\n// SPEmailIMAPAccount.h\n// SpyPhone\n//\n// Created by Nicolas Seriot on 11/20/09.\n// Copyright 2009. \n// Licensed"
},
{
"path": "Classes/SPEmailIMAPAccount.m",
"chars": 732,
"preview": "//\n// SPEmailIMAPAccount.m\n// SpyPhone\n//\n// Created by Nicolas Seriot on 11/20/09.\n// Copyright 2009. \n// Licensed"
},
{
"path": "Classes/SPEmailIToolsAccount.h",
"chars": 269,
"preview": "//\n// SPEmailIToolsAccount.h\n// SpyPhone\n//\n// Created by Nicolas Seriot on 11/20/09.\n// Copyright 2009. \n// Licens"
},
{
"path": "Classes/SPEmailIToolsAccount.m",
"chars": 942,
"preview": "//\n// SPEmailIToolsAccount.m\n// SpyPhone\n//\n// Created by Nicolas Seriot on 11/20/09.\n// Copyright 2009. \n// Licens"
},
{
"path": "Classes/SPEmailMobileMeAccount.h",
"chars": 274,
"preview": "//\n// SPEmailMobileMeAccount.h\n// SpyPhone\n//\n// Created by Nicolas Seriot on 11/20/09.\n// Copyright 2009. \n// Lice"
},
{
"path": "Classes/SPEmailMobileMeAccount.m",
"chars": 1036,
"preview": "//\n// SPEmailMobileMeAccount.m\n// SpyPhone\n//\n// Created by Nicolas Seriot on 11/20/09.\n// Copyright 2009. \n// Lice"
},
{
"path": "Classes/SPEmailPOPAccount.h",
"chars": 263,
"preview": "//\n// SPEMailPOPAccount.h\n// SpyPhone\n//\n// Created by Nicolas Seriot on 11/20/09.\n// Copyright 2009. \n// Licensed "
},
{
"path": "Classes/SPEmailPOPAccount.m",
"chars": 726,
"preview": "//\n// SPEMailPOPAccount.m\n// SpyPhone\n//\n// Created by Nicolas Seriot on 11/20/09.\n// Copyright 2009. \n// Licensed "
},
{
"path": "Classes/SPEmailReportVC.h",
"chars": 602,
"preview": "//\n// SPEmailReportVC.h\n// SpyPhone\n//\n// Created by Nicolas Seriot on 11/22/09.\n// Copyright 2009. \n// Licensed un"
},
{
"path": "Classes/SPEmailReportVC.m",
"chars": 2361,
"preview": "//\n// SPEmailReportVC.m\n// SpyPhone\n//\n// Created by Nicolas Seriot on 11/22/09.\n// Copyright 2009. \n// Licensed un"
},
{
"path": "Classes/SPImageAnnotation.h",
"chars": 721,
"preview": "//\n// SPImageAnnotation.h\n// SpyPhone\n//\n// Created by Nicolas Seriot on 11/21/09.\n// Copyright 2009. \n// Licensed "
},
{
"path": "Classes/SPImageAnnotation.m",
"chars": 997,
"preview": "//\n// SPImageAnnotation.m\n// SpyPhone\n//\n// Created by Nicolas Seriot on 11/21/09.\n// Copyright 2009. \n// Licensed "
},
{
"path": "Classes/SPImageMapVC.h",
"chars": 604,
"preview": "//\n// SPMapVC.h\n// SpyPhone\n//\n// Created by Nicolas Seriot on 11/21/09.\n// Copyright 2009. \n// Licensed under GPL "
},
{
"path": "Classes/SPImageMapVC.m",
"chars": 2790,
"preview": "//\n// SPMapVC.m\n// SpyPhone\n//\n// Created by Nicolas Seriot on 11/21/09.\n// Copyright 2009. \n// Licensed under GPL "
},
{
"path": "Classes/SPImageVC.h",
"chars": 351,
"preview": "//\n// SPImageVC.h\n// SpyPhone\n//\n// Created by Nicolas Seriot on 11/21/09.\n// Copyright 2009. All rights reserved.\n/"
},
{
"path": "Classes/SPImageVC.m",
"chars": 774,
"preview": "//\n// SPImageVC.m\n// SpyPhone\n//\n// Created by Nicolas Seriot on 11/21/09.\n// Copyright 2009. All rights reserved.\n/"
},
{
"path": "Classes/SPSourceAddressBookTVC.h",
"chars": 292,
"preview": "//\n// SPSourceAddressBookTVC.h\n// SpyPhone\n//\n// Created by Nicolas Seriot on 11/16/09.\n// Copyright 2009. \n// Lice"
},
{
"path": "Classes/SPSourceAddressBookTVC.m",
"chars": 1764,
"preview": "//\n// SPSourceAddressBookTVC.m\n// SpyPhone\n//\n// Created by Nicolas Seriot on 11/16/09.\n// Copyright 2009. \n// Lice"
},
{
"path": "Classes/SPSourceEmailTVC.h",
"chars": 389,
"preview": "//\n// SPSourceEmailTVC.h\n// SpyPhone\n//\n// Created by Nicolas Seriot on 11/15/09.\n// Copyright 2009. \n// Licensed u"
},
{
"path": "Classes/SPSourceEmailTVC.m",
"chars": 2228,
"preview": "//\n// SPSourceEmailTVC.m\n// SpyPhone\n//\n// Created by Nicolas Seriot on 11/15/09.\n// Copyright 2009. \n// Licensed u"
},
{
"path": "Classes/SPSourceKeyboardTVC.h",
"chars": 285,
"preview": "//\n// SPSourceKeyboardTVC.h\n// SpyPhone\n//\n// Created by Nicolas Seriot on 11/16/09.\n// Copyright 2009. \n// License"
},
{
"path": "Classes/SPSourceKeyboardTVC.m",
"chars": 3433,
"preview": "//\n// SPSourceKeyboardTVC.m\n// SpyPhone\n//\n// Created by Nicolas Seriot on 11/16/09.\n// Copyright 2009. \n// License"
},
{
"path": "Classes/SPSourceLocationTVC.h",
"chars": 935,
"preview": "//\n// SPSourceLocationTVC.h\n// SpyPhone\n//\n// Created by Nicolas Seriot on 11/15/09.\n// Copyright 2009. \n// License"
},
{
"path": "Classes/SPSourceLocationTVC.m",
"chars": 3873,
"preview": "//\n// SPSourceLocationTVC.m\n// SpyPhone\n//\n// Created by Nicolas Seriot on 11/15/09.\n// Copyright 2009. \n// License"
},
{
"path": "Classes/SPSourcePhoneTVC.h",
"chars": 1117,
"preview": "//\n// SPSourcePhoneTVC.h\n// SpyPhone\n//\n// Created by Nicolas Seriot on 11/15/09.\n// Copyright 2009. \n// Licensed u"
},
{
"path": "Classes/SPSourcePhoneTVC.m",
"chars": 8086,
"preview": "//\n// SPSourcePhoneTVC.m\n// SpyPhone\n//\n// Created by Nicolas Seriot on 11/15/09.\n// Copyright 2009. \n// Licensed u"
},
{
"path": "Classes/SPSourcePhotosTVC.h",
"chars": 639,
"preview": "//\n// SPSourcePhotosTVC.h\n// SpyPhone\n//\n// Created by Nicolas Seriot on 11/15/09.\n// Copyright 2009. \n// Licensed "
},
{
"path": "Classes/SPSourcePhotosTVC.m",
"chars": 5151,
"preview": "//\n// SPSourcePhotosTVC.m\n// SpyPhone\n//\n// Created by Nicolas Seriot on 11/15/09.\n// Copyright 2009. \n// Licensed "
},
{
"path": "Classes/SPSourceTVC.h",
"chars": 497,
"preview": "//\n// SPSourceTVC.h\n// SpyPhone\n//\n// Created by Nicolas Seriot on 11/17/09.\n// Copyright 2009. All rights reserved."
},
{
"path": "Classes/SPSourceTVC.m",
"chars": 2300,
"preview": "//\n// SPSourceTVC.m\n// SpyPhone\n//\n// Created by Nicolas Seriot on 11/17/09.\n// Copyright 2009. All rights reserved."
},
{
"path": "Classes/SPSourceWifiTVC.h",
"chars": 608,
"preview": "//\n// SPSourceWifiTVC.h\n// SpyPhone\n//\n// Created by Nicolas Seriot on 11/15/09.\n// Copyright 2009. \n// Licensed un"
},
{
"path": "Classes/SPSourceWifiTVC.m",
"chars": 2676,
"preview": "//\n// SPSourceWifiTVC.m\n// SpyPhone\n//\n// Created by Nicolas Seriot on 11/15/09.\n// Copyright 2009. \n// Licensed un"
},
{
"path": "Classes/SPWebViewVC.h",
"chars": 408,
"preview": "//\n// SPWebViewVC.h\n// SpyPhone\n//\n// Created by Nicolas Seriot on 11/15/09.\n// Copyright 2009. \n// Licensed under "
},
{
"path": "Classes/SPWebViewVC.m",
"chars": 805,
"preview": "//\n// SPWebViewVC.m\n// SpyPhone\n//\n// Created by Nicolas Seriot on 11/15/09.\n// Copyright 2009. \n// Licensed under "
},
{
"path": "Classes/SPWifiAnnotation.h",
"chars": 596,
"preview": "//\n// SPWifiAnnotation.h\n// SpyPhone\n//\n// Created by Nicolas Seriot on 10/31/10.\n// Copyright 2010 IICT. All rights"
},
{
"path": "Classes/SPWifiAnnotation.m",
"chars": 1206,
"preview": "//\n// SPWifiAnnotation.m\n// SpyPhone\n//\n// Created by Nicolas Seriot on 10/31/10.\n// Copyright 2010 IICT. All rights"
},
{
"path": "Classes/SPWifiMapVC.h",
"chars": 442,
"preview": "//\n// SPWifiMapVC.h\n// SpyPhone\n//\n// Created by Nicolas Seriot on 10/31/10.\n// Copyright 2010 IICT. All rights rese"
},
{
"path": "Classes/SPWifiMapVC.m",
"chars": 2234,
"preview": "//\n// SPWifiMapVC.m\n// SpyPhone\n//\n// Created by Nicolas Seriot on 10/31/10.\n// Copyright 2010 IICT. All rights rese"
},
{
"path": "Classes/SPWifiMapVC.xib",
"chars": 18249,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<archive type=\"com.apple.InterfaceBuilder3.CocoaTouch.XIB\" version=\"7.10\">\n\t<data"
},
{
"path": "Classes/SpyPhoneAppDelegate.h",
"chars": 513,
"preview": "//\n// SpyPhoneAppDelegate.h\n// SpyPhone\n//\n// Created by Nicolas Seriot on 11/15/09.\n// Copyright IICT 2009. \n// Li"
},
{
"path": "Classes/SpyPhoneAppDelegate.m",
"chars": 992,
"preview": "//\n// SpyPhoneAppDelegate.m\n// SpyPhone\n//\n// Created by Nicolas Seriot on 11/15/09.\n// Copyright IICT 2009. \n// Li"
},
{
"path": "Classes/TVOutManager.h",
"chars": 876,
"preview": "//\n// TVOutManager.h\n// TVOutOS4Test\n//\n// Created by Rob Terrell (rob@touchcentric.com) on 8/16/10.\n// Copyright 20"
},
{
"path": "Classes/TVOutManager.m",
"chars": 10296,
"preview": "//\n// TVOutManager.m\n// TVOutOS4Test\n//\n// Created by Rob Terrell (rob@touchcentric.com) on 8/16/10.\n// Copyright 20"
},
{
"path": "Classes/TVOutManager_.m",
"chars": 9136,
"preview": "//\n// TVOutManager.m\n// TVOutOS4Test\n//\n// Created by Rob Terrell (rob@touchcentric.com) on 8/16/10.\n// Copyright 20"
},
{
"path": "Classes/UIImage+GPS.h",
"chars": 335,
"preview": "//\n// UIImage+GPS.h\n// SpyPhone\n//\n// Created by Nicolas Seriot on 11/21/09.\n// Copyright 2009. \n// Licensed under "
},
{
"path": "Classes/UIImage+GPS.m",
"chars": 1671,
"preview": "//\n// UIImage+GPS.m\n// SpyPhone\n//\n// Created by Nicolas Seriot on 11/21/09.\n// Copyright 2009. \n// Licensed under "
},
{
"path": "EXIF/EXF.h",
"chars": 988,
"preview": "/*\n * EXF.h\n * \n *\n * Created by steve woodcock on 23/03/2008.\n * Copyright 2008. \n// Licensed under GPL 2.0 http:/"
},
{
"path": "EXIF/EXFConstants.h",
"chars": 10174,
"preview": "/*\n * EXFConstants.h\n * \n *\n * Created by steve woodcock on 30/03/2008.\n * Copyright 2008. \n// Licensed under GPL 2"
},
{
"path": "EXIF/EXFGPS.h",
"chars": 1595,
"preview": "/*!\n @header EXFGPS Structures\n Created by steve woodcock on 30/03/2008.\n @copyright 2008. \n// Licensed under GPL"
},
{
"path": "EXIF/EXFGPS.m",
"chars": 2044,
"preview": "/*\n * EXFGPSLoc.m\n * \n *\n * Created by steve woodcock on 30/03/2008.\n * Copyright 2008. \n// Licensed under GPL 2.0 "
},
{
"path": "EXIF/EXFHandlers.h",
"chars": 1749,
"preview": "/*!\n @header EXFTagHandler\n @copyright 2008. Created by steve woodcock on 30/03/2008. \n// Licensed under GPL 2.0 http"
},
{
"path": "EXIF/EXFHandlers.m",
"chars": 11781,
"preview": "//\n// EXFHandlers.m\n// iphone-test\n//\n// Created by steve woodcock on 30/03/2008.\n// Copyright 2008 __MyCompanyName_"
},
{
"path": "EXIF/EXFJFIF.h",
"chars": 1076,
"preview": "//\n// EXFJFIF.h\n// iphone-test\n//\n// Created by steve woodcock on 24/03/2008.\n// Copyright 2008 __MyCompanyName__. \n"
},
{
"path": "EXIF/EXFJFIF.m",
"chars": 3578,
"preview": "//\n// EXFJFIF.m\n// iphone-test\n//\n// Created by steve woodcock on 24/03/2008.\n// Copyright 2008 __MyCompanyName__. \n"
},
{
"path": "EXIF/EXFJpeg.h",
"chars": 1553,
"preview": "/*\n * EXFJpeg.h\n * iphoneGeo\n *\n * Created by steve woodcock on 30/03/2008.\n * Copyright 2008. \n// Licensed under G"
},
{
"path": "EXIF/EXFJpeg.m",
"chars": 15185,
"preview": "//\n// Jpeg.m\n// iphone-test\n//\n// Created by steve woodcock on 10/03/2008.\n// Copyright 2008 __MyCompanyName__. All "
},
{
"path": "EXIF/EXFLogging.h",
"chars": 333,
"preview": "/*\n * logging.h\n * \n *\n * Created by steve woodcock on 28/02/2008.\n * Copyright 2008. \n// Licensed under GPL 2.0 htt"
},
{
"path": "EXIF/EXFMetaData.h",
"chars": 3514,
"preview": "/*\n * EXFMetaData.h\n * iphoneGeo\n *\n * Created by steve woodcock on 30/03/2008.\n * Copyright 2008. \n// Licensed und"
},
{
"path": "EXIF/EXFMetaData.m",
"chars": 63858,
"preview": "//\n// Exif.m\n// iphone-test\n//\n// Created by steve woodcock on 14/03/2008.\n// Copyright 2008. All rights reserved.\n/"
},
{
"path": "EXIF/EXFMutableMetaData.h",
"chars": 1653,
"preview": "/*\n * EXFMutableMetaData.h\n * iphoneGeo\n *\n * Created by steve woodcock on 23/03/2008.\n * Copyright 2008 __MyCompany"
},
{
"path": "EXIF/EXFTagDefinitionHolder.h",
"chars": 458,
"preview": "//\n// EXFTagDefinition.h\n// iphone-test\n//\n// Created by steve woodcock on 26/03/2008.\n// Copyright 2008 __MyCompany"
},
{
"path": "EXIF/EXFTagDefinitionHolder.m",
"chars": 21386,
"preview": "//\n// EXFTagDefinition.m\n// iphone-test\n//\n// Created by steve woodcock on 26/03/2008.\n// Copyright 2008 __MyCompany"
},
{
"path": "EXIF/EXFUtils.h",
"chars": 1451,
"preview": "/*\n * EXFUtils.h\n * iphoneGeo\n *\n * Created by steve woodcock on 23/03/2008.\n * Copyright 2008. \n// Licensed under "
},
{
"path": "EXIF/EXFUtils.m",
"chars": 6966,
"preview": "//\n// EXFUtils.m\n// iphone-test\n//\n// Created by steve woodcock on 30/03/2008.\n// Copyright 2008 __MyCompanyName__. "
},
{
"path": "FMDB/FMDatabase.h",
"chars": 2696,
"preview": "#import <Foundation/Foundation.h>\n#import \"sqlite3.h\"\n#import \"FMResultSet.h\"\n\n@interface FMDatabase : NSObject \n{\n\tsqli"
},
{
"path": "FMDB/FMDatabase.m",
"chars": 18160,
"preview": "#import \"FMDatabase.h\"\n#import \"unistd.h\"\n\n@implementation FMDatabase\n\n+ (id)databaseWithPath:(NSString*)aPath {\n ret"
},
{
"path": "FMDB/FMDatabaseAdditions.h",
"chars": 947,
"preview": "//\n// FMDatabaseAdditions.h\n// fmkit\n//\n// Created by August Mueller on 10/30/05.\n// Copyright 2005 Flying Meat Inc."
},
{
"path": "FMDB/FMDatabaseAdditions.m",
"chars": 3980,
"preview": "//\n// FMDatabaseAdditions.m\n// fmkit\n//\n// Created by August Mueller on 10/30/05.\n// Copyright 2005 Flying Meat Inc."
},
{
"path": "FMDB/FMResultSet.h",
"chars": 2543,
"preview": "#import <Foundation/Foundation.h>\n#import \"sqlite3.h\"\n\n#ifndef __has_feature // Optional.\n#define __has_feature(x) "
},
{
"path": "FMDB/FMResultSet.m",
"chars": 10616,
"preview": "#import \"FMResultSet.h\"\n#import \"FMDatabase.h\"\n#import \"unistd.h\"\n\n@interface FMResultSet (Private)\n- (NSMutableDictiona"
},
{
"path": "JSON/JSON.h",
"chars": 2824,
"preview": "/*\n Copyright (C) 2009-2010 Stig Brautaset. All rights reserved.\n \n Redistribution and use in source and binary forms, w"
},
{
"path": "JSON/LICENSE",
"chars": 1484,
"preview": "Copyright (C) 2007-2010 Stig Brautaset. All rights reserved.\n\nRedistribution and use in source and binary forms, with or"
},
{
"path": "JSON/NSObject+SBJSON.h",
"chars": 2143,
"preview": "/*\n Copyright (C) 2009 Stig Brautaset. All rights reserved.\n \n Redistribution and use in source and binary forms, with o"
},
{
"path": "JSON/NSObject+SBJSON.m",
"chars": 1917,
"preview": "/*\n Copyright (C) 2009 Stig Brautaset. All rights reserved.\n \n Redistribution and use in source and binary forms, with o"
},
{
"path": "JSON/NSString+SBJSON.h",
"chars": 2032,
"preview": "/*\n Copyright (C) 2009 Stig Brautaset. All rights reserved.\n \n Redistribution and use in source and binary forms, with o"
},
{
"path": "JSON/NSString+SBJSON.m",
"chars": 1885,
"preview": "/*\n Copyright (C) 2007-2009 Stig Brautaset. All rights reserved.\n \n Redistribution and use in source and binary forms, w"
},
{
"path": "JSON/Readme.markdown",
"chars": 804,
"preview": "JSON Framework\n==============\n\nJSON is a light-weight data interchange format that's easy to read and\nwrite for humans a"
},
{
"path": "JSON/SBJsonBase.h",
"chars": 2946,
"preview": "/*\n Copyright (C) 2009 Stig Brautaset. All rights reserved.\n \n Redistribution and use in source and binary forms, with o"
},
{
"path": "JSON/SBJsonBase.m",
"chars": 2753,
"preview": "/*\n Copyright (C) 2009 Stig Brautaset. All rights reserved.\n \n Redistribution and use in source and binary forms, with o"
},
{
"path": "JSON/SBJsonParser.h",
"chars": 3215,
"preview": "/*\n Copyright (C) 2009 Stig Brautaset. All rights reserved.\n \n Redistribution and use in source and binary forms, with o"
},
{
"path": "JSON/SBJsonParser.m",
"chars": 15226,
"preview": "/*\n Copyright (C) 2009,2010 Stig Brautaset. All rights reserved.\n \n Redistribution and use in source and binary forms, w"
},
{
"path": "JSON/SBJsonStreamWriter.h",
"chars": 4715,
"preview": "/*\n Copyright (c) 2010, Stig Brautaset.\n All rights reserved.\n \n Redistribution and use in source and binary forms, with"
},
{
"path": "JSON/SBJsonStreamWriter.m",
"chars": 13915,
"preview": "/*\n Copyright (c) 2010, Stig Brautaset.\n All rights reserved.\n \n Redistribution and use in source and binary forms, with"
},
{
"path": "JSON/SBJsonWriter.h",
"chars": 4178,
"preview": "/*\n Copyright (C) 2009 Stig Brautaset. All rights reserved.\n \n Redistribution and use in source and binary forms, with o"
},
{
"path": "JSON/SBJsonWriter.m",
"chars": 3184,
"preview": "/*\n Copyright (C) 2009 Stig Brautaset. All rights reserved.\n \n Redistribution and use in source and binary forms, with o"
},
{
"path": "JSON/SBProxyForJson.h",
"chars": 2156,
"preview": "/*\n Copyright (c) 2010, Stig Brautaset.\n All rights reserved.\n \n Redistribution and use in source and binary forms, with"
},
{
"path": "MainWindow.xib",
"chars": 26360,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<archive type=\"com.apple.InterfaceBuilder3.CocoaTouch.XIB\" version=\"7.10\">\n\t<data"
},
{
"path": "OUILookupTool/OUILookupTool.h",
"chars": 638,
"preview": "//\n// OUILookupTool.h\n// OUILookup\n//\n// Created by Nicolas Seriot on 10/31/10.\n// Copyright 2010 IICT. All rights r"
},
{
"path": "OUILookupTool/OUILookupTool.m",
"chars": 3468,
"preview": "//\n// OUILookupTool.m\n// OUILookup\n//\n// Created by Nicolas Seriot on 10/31/10.\n// Copyright 2010 IICT. All rights r"
},
{
"path": "README.markdown",
"chars": 746,
"preview": "At BlackHat DC 2010, I presented a paper called [iPhone Privacy](http://seriot.ch/resources/talks_papers/iPhonePrivacy.p"
},
{
"path": "SPCell.xib",
"chars": 14707,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<archive type=\"com.apple.InterfaceBuilder3.CocoaTouch.XIB\" version=\"7.10\">\n\t<data"
},
{
"path": "SPEmailReportVC.xib",
"chars": 40761,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<archive type=\"com.apple.InterfaceBuilder3.CocoaTouch.XIB\" version=\"7.10\">\n\t<data"
},
{
"path": "SPImageMapVC.xib",
"chars": 19450,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<archive type=\"com.apple.InterfaceBuilder3.CocoaTouch.XIB\" version=\"7.10\">\n\t<data"
},
{
"path": "SPImageVC.xib",
"chars": 16709,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<archive type=\"com.apple.InterfaceBuilder3.CocoaTouch.XIB\" version=\"7.10\">\n\t<data"
},
{
"path": "SPSourceTVC.xib",
"chars": 17787,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<archive type=\"com.apple.InterfaceBuilder3.CocoaTouch.XIB\" version=\"7.10\">\n\t<data"
},
{
"path": "SPWebViewVC.xib",
"chars": 16719,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<archive type=\"com.apple.InterfaceBuilder3.CocoaTouch.XIB\" version=\"7.10\">\n\t<data"
},
{
"path": "Settings.bundle/Root.plist",
"chars": 507,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "Sources.xib",
"chars": 33848,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<archive type=\"com.apple.InterfaceBuilder3.CocoaTouch.XIB\" version=\"7.10\">\n\t<data"
},
{
"path": "SpyPhone-Info.plist",
"chars": 1013,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "SpyPhone.xcodeproj/nst.pbxuser",
"chars": 23300,
"preview": "// !$*UTF8*$!\n{\n\t030AFB2B127D08BF00C9E0C6 /* SPWifiMapVC.h */ = {\n\t\tuiCtxt = {\n\t\t\tsepNavIntBoundsRect = \"{{0, 0}, {705, "
},
{
"path": "SpyPhone.xcodeproj/nst.perspectivev3",
"chars": 46905,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "SpyPhone.xcodeproj/project.pbxproj",
"chars": 59134,
"preview": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 46;\n\tobjects = {\n\n/* Begin PBXBuildFile section *"
},
{
"path": "SpyPhone.xcodeproj/project.xcworkspace/contents.xcworkspacedata",
"chars": 153,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n version = \"1.0\">\n <FileRef\n location = \"self:SpyPhone.xcodep"
},
{
"path": "SpyPhone.xcodeproj/project.xcworkspace/xcuserdata/nst.xcuserdatad/WorkspaceSettings.xcsettings",
"chars": 382,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "SpyPhone_Prefix.pch",
"chars": 185,
"preview": "//\n// Prefix header for all source files of the 'SpyPhone' target in the 'SpyPhone' project\n//\n\n#ifdef __OBJC__\n #imp"
},
{
"path": "gpl-2.0.txt",
"chars": 17987,
"preview": "\t\t GNU GENERAL PUBLIC LICENSE\n\t\t Version 2, June 1991\n\n Copyright (C) 1989, 1991 Free Software Foundation, Inc."
},
{
"path": "main.m",
"chars": 390,
"preview": "//\n// main.m\n// SpyPhone\n//\n// Created by Nicolas Seriot on 11/15/09.\n// Copyright 2009. \n// Licensed under GPL 2.0"
}
]
// ... and 1 more files (download for full content)
About this extraction
This page contains the full source code of the nst/SpyPhone GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 120 files (683.5 KB), approximately 195.5k tokens, and a symbol index with 43 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.