Commits

Jeff Laing committed 7023294

Initial checkin.

Comments (0)

Files changed (5)

+/* ex:set ts=4 sw=4: <- for vi
+ *
+ * IPod.h
+ *
+ * This is based on information extracted from around the Internet, by people
+ * who wanted to look a lot further under the covers than I did.  I've deliberately
+ * pushed all Apple's datatypes back to being opaque, since there is nothing
+ * to be gained by looking inside them, and everything to lose.
+ *
+ * This file doesn't contain any of the 'recovery-mode' or other 'jailbreak-related' stuff
+ *
+ * http://iphonesvn.halifrag.com/svn/iPhone/
+ * http://www.theiphonewiki.com/wiki/index.php?title=MobileDevice_Library#MobileDevice_Header_.28mobiledevice.h.29
+ * http://code.google.com/p/iphonebrowser/issues/detail?id=52
+ * http://www.koders.com/csharp/fidE79340F4674D47FFF3EFB6F949A1589D942798F3.aspx
+ *
+ * As far as I'm concerned, this header is in the Public Domain.
+ */
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * its critical that we spell out all integer sizes, for the entry points
+ * and data structures in the external DLL/framework that we have no
+ * control over.
+ */
+#include <stdint.h>
+
+#if defined(WIN32)
+#include <CoreFoundation.h>
+typedef uint32_t mach_error_t;
+#elif defined(__APPLE__)
+#include <CoreFoundation/CoreFoundation.h>
+#include <mach/error.h>
+#endif
+
+/* Error codes */
+#define MDERR_APPLE_MOBILE		(err_system(0x3a))
+#define MDERR_IPHONE			(err_sub(0))
+
+/* Apple Mobile (AM*) errors */
+#define MDERR_OK                ERR_SUCCESS
+#define MDERR_SYSCALL           (ERR_MOBILE_DEVICE | 0x01)
+#define MDERR_OUT_OF_MEMORY     (ERR_MOBILE_DEVICE | 0x03)
+#define MDERR_QUERY_FAILED      (ERR_MOBILE_DEVICE | 0x04) 
+#define MDERR_INVALID_ARGUMENT  (ERR_MOBILE_DEVICE | 0x0b)
+#define MDERR_NO_SUCH_SERVICE   (ERR_MOBILE_DEVICE | 0x22)
+#define MDERR_DICT_NOT_LOADED   (ERR_MOBILE_DEVICE | 0x25)
+
+/* Apple File Connection (AFC*) errors */
+#define MDERR_AFC_OUT_OF_MEMORY 0x03
+
+/* Services, found in /System/Library/Lockdown/Services.plist */
+#define AMSVC_AFC                   "com.apple.afc"
+#define AMSVC_AFC2                  "com.apple.afc2"
+#define AMSVC_BACKUP                "com.apple.mobilebackup"
+#define AMSVC_CRASH_REPORT_COPY     "com.apple.crashreportcopy"
+#define AMSVC_DEBUG_IMAGE_MOUNT     "com.apple.mobile.debug_image_mount"
+#define AMSVC_NOTIFICATION_PROXY    "com.apple.mobile.notification_proxy"
+#define AMSVC_PURPLE_TEST           "com.apple.purpletestr"
+#define AMSVC_SOFTWARE_UPDATE       "com.apple.mobile.software_update"
+#define AMSVC_SYNC                  "com.apple.mobilesync"
+#define AMSVC_SCREENSHOT            "com.apple.screenshotr"
+#define AMSVC_SYSLOG_RELAY          "com.apple.syslog_relay"
+#define AMSVC_SYSTEM_PROFILER       "com.apple.mobile.system_profiler"
+
+typedef uint32_t afc_error_t;
+typedef uint64_t afc_file_ref;
+
+/* opaque structures */
+typedef struct _am_device				*am_device;
+typedef struct _am_service				*am_service;
+typedef struct _afc_connection			*afc_connection;
+typedef struct _am_device_notification	*am_device_notification;
+typedef struct _afc_directory			*afc_directory;
+typedef struct _afc_dictionary			*afc_dictionary;
+
+/* Messages passed to device notification callbacks: passed as part of
+ * am_device_notification_callback_info. */
+typedef enum {
+	ADNCI_MSG_CONNECTED     = 1,
+	ADNCI_MSG_DISCONNECTED  = 2,
+	ADNCI_MSG_UNKNOWN       = 3
+} adnci_msg;
+
+struct am_device_notification_callback_info {
+	am_device	dev;				/* 0    device */ 
+	uint32_t	msg;				/* 4    one of adnci_msg */
+} __attribute__ ((packed));
+
+/* The type of the device notification callback function. */
+typedef void (*am_device_notification_callback)(
+	struct am_device_notification_callback_info *,
+	void* callback_data);
+
+/* ----------------------------------------------------------------------------
+ *   Public routines
+ * ------------------------------------------------------------------------- */
+
+/* Registers a notification with the current run loop. The callback gets
+ * copied into the notification struct, as well as being registered with the
+ * current run loop. callback_data gets passed to the callback in addition
+ * to the info block.
+ * unused0 and unused1 are both 0 when iTunes calls this.
+ *
+ *  Returns:
+ *      MDERR_OK            if successful
+ *      MDERR_SYSCALL       if CFRunLoopAddSource() failed
+ *      MDERR_OUT_OF_MEMORY if we ran out of memory
+ */
+
+mach_error_t AMDeviceNotificationSubscribe(
+	am_device_notification_callback callback,
+	uint32_t unused0,
+	uint32_t unused1,
+	void *callback_data,
+	am_device_notification *notification);
+
+/*
+ * Unregisters notifications. Buggy (iTunes 8.2): if you subscribe, unsubscribe and subscribe again, arriving 
+           notifications will contain cookie and subscription from 1st call to subscribe, not the 2nd one. iTunes 
+           calls this function only once on exit.
+        */
+mach_error_t AMDeviceNotificationUnsubscribe(
+	am_device_notification subscription);
+
+/*  Connects to the iPhone. Pass in the am_device structure that the
+ *  notification callback will give to you.
+ *
+ *  Returns:
+ *      MDERR_OK                if successfully connected
+ *      MDERR_SYSCALL           if setsockopt() failed
+ *      MDERR_QUERY_FAILED      if the daemon query failed
+ *      MDERR_INVALID_ARGUMENT  if USBMuxConnectByPort returned 0xffffffff
+ */
+
+mach_error_t AMDeviceConnect(
+	am_device device);
+
+/*  Calls PairingRecordPath() on the given device, than tests whether the path
+ *  which that function returns exists. During the initial connect, the path
+ *  returned by that function is '/', and so this returns 1.
+ *
+ *  Returns:
+ *      0   if the path did not exist
+ *      1   if it did
+ */
+
+int AMDeviceIsPaired(
+	am_device device);
+
+// __DLLIMPORT mach_error_t AMDevicePair(struct am_device device);
+
+/*  iTunes calls this function immediately after testing whether the device is
+ *  paired. It creates a pairing file and establishes a Lockdown connection.
+ *
+ *  Returns:
+ *      MDERR_OK                if successful
+ *      MDERR_INVALID_ARGUMENT  if the supplied device is null
+ *      MDERR_DICT_NOT_LOADED   if the load_dict() call failed
+ */
+
+mach_error_t AMDeviceValidatePairing(
+	am_device device);
+
+/*  Creates a Lockdown session and adjusts the device structure appropriately
+ *  to indicate that the session has been started. iTunes calls this function
+ *  after validating pairing.
+ *
+ *  Returns:
+ *      MDERR_OK                if successful
+ *      MDERR_INVALID_ARGUMENT  if the Lockdown conn has not been established
+ *      MDERR_DICT_NOT_LOADED   if the load_dict() call failed
+ */
+
+mach_error_t AMDeviceStartSession(
+	am_device device);
+
+/* Starts a service and returns a handle that can be used in order to further
+ * access the service. You should stop the session and disconnect before using
+ * the service. iTunes calls this function after starting a session. It starts 
+ * the service and the SSL connection. unknown may safely be
+ * NULL (it is when iTunes calls this), but if it is not, then it will be
+ * filled upon function exit. service_name should be one of the AMSVC_*
+ * constants. If the service is AFC (AMSVC_AFC), then the handle is the handle
+ * that will be used for further AFC* calls.
+ *
+ * Returns:
+ *      MDERR_OK                if successful
+ *      MDERR_SYSCALL           if the setsockopt() call failed
+ *      MDERR_INVALID_ARGUMENT  if the Lockdown conn has not been established
+ */
+
+mach_error_t AMDeviceStartService(
+	am_device device,
+	CFStringRef service_name,
+	am_service *handle,
+	uint32_t *unknown);
+
+/* Stops a session. You should do this before accessing services.
+ *
+ * Returns:
+ *      MDERR_OK                if successful
+ *      MDERR_INVALID_ARGUMENT  if the Lockdown conn has not been established
+ */
+
+mach_error_t AMDeviceStopSession(
+	am_device device);
+
+/* Opens an Apple File Connection. You must start the appropriate service
+ * first with AMDeviceStartService(). In iTunes, io_timeout is 0.
+ *
+ * Returns:
+ *      MDERR_OK                if successful
+ *      MDERR_AFC_OUT_OF_MEMORY if malloc() failed
+ */
+
+afc_error_t AFCDirectoryAccessOpen(
+	am_service handle,
+	uint32_t io_timeout,
+    afc_connection *conn);
+
+/* Retrieves an afc_dictionary that describes the connected device.  To
+ * extract values from the dictionary, use AFCKeyValueRead() and close
+ * it when finished with AFCKeyValueClose()
+ */
+afc_error_t AFCDeviceInfoOpen(
+	afc_connection conn,
+	afc_dictionary *info);
+
+/* Turns debug mode on if the environment variable AFCDEBUG is set to a numeric
+ * value, or if the file '/AFCDEBUG' is present and contains a value. */
+void AFCPlatformInit();
+
+/* Opens a directory on the iPhone. Retrieves an afc_directory which can be
+ * queried (with AFCDirectoryRead()) to enumerate through the filenames in
+ * the directory.  Once finished, close with AFCDirectoryClose()
+ *
+ * Note that this normally only accesses the iTunes sandbox/partition as the
+ * root, which is /var/root/Media. Pathnames are specified with '/' delimiters
+ * as in Unix style.
+ *
+ * Returns:
+ *      MDERR_OK                if successful
+ */
+
+afc_error_t AFCDirectoryOpen(
+	afc_connection conn,
+	const char *path,
+	afc_directory *dir);
+
+/* Acquires the next entry in a directory previously opened with
+ * AFCDirectoryOpen(). When dirent is filled with a NULL value, then the end
+ * of the directory has been reached. '.' and '..' will be returned as the
+ * first two entries in each directory except the root; you may want to skip
+ * over them.
+ *
+ * Returns:
+ *      MDERR_OK                if successful, even if no entries remain
+ */
+
+afc_error_t AFCDirectoryRead(
+	afc_connection conn,
+	afc_directory dir,
+    char **dirent);
+
+/* Close the directory previously opened with AFCDirectoryOpen()
+ */
+afc_error_t AFCDirectoryClose(
+	afc_connection conn,
+	afc_directory dir);
+
+/* Create a new directory on the device.
+ */
+afc_error_t AFCDirectoryCreate(
+	afc_connection conn,
+	const char *dirname);
+
+/* Removes an existing file or directory from the device.
+ */
+afc_error_t AFCRemovePath(
+	afc_connection conn,
+	const char *dirname);
+
+/* Renames an existing file or directory on the device */
+afc_error_t AFCRenamePath(
+	afc_connection conn,
+	const char *from,
+	const char *to);
+
+/* Returns the context field of the given AFC connection. */
+uint32_t AFCDirectoryAccessGetContext(
+	afc_connection conn);
+
+/* Set the context field of the given AFC connection.
+ */
+uint32_t AFCDirectoryAccessSetContext(
+	afc_connection conn,
+	uint32_t ctx);
+
+/* Returns the fs_block_size field of the given AFC connection. */
+uint32_t AFCDirectoryAccessGetFSBlockSize(
+	afc_connection conn);
+
+/* Returns the io_timeout field of the given AFC connection. In iTunes this is 0. */
+uint32_t AFCDirectoryAccessGetIOTimeout(
+	afc_connection conn);
+	
+uint32_t AFCDirectoryAccessSetIOTimeout(
+	afc_connection conn,
+	uint32_t timeout);
+
+/* Returns the sock_block_size field of the given AFC connection. */
+uint32_t AFCDirectoryAccessGetSocketBlockSize(
+	afc_connection conn);
+
+/* Closes the given AFC connection. */
+afc_error_t AFCDirectoryAccessClose(
+	afc_connection conn);
+
+/* ----------------------------------------------------------------------------
+ *   Less-documented public routines
+ * ------------------------------------------------------------------------- */
+
+afc_error_t AFCFileRefOpen(
+	afc_connection conn,
+	const char *path,				/* pathname of file to open */
+    uint64_t mode,					/* 1=read, 2=write, 3=read/write */
+	afc_file_ref *ref);
+	
+afc_error_t AFCFileRefSeek(
+	afc_connection conn,
+	afc_file_ref ref,
+	int64_t offset,				/* signed offset from pos */
+	uint64_t pos);				/* 0=SEEK_SET, 1=SEEK_CUR, 2=SEEK_END */
+
+afc_error_t AFCFileRefTell(
+	afc_connection conn,
+	afc_file_ref ref,
+	uint64_t *offset);
+	
+// afc_error_t AFCFileRefLock(afc_connection *conn, afc_file_ref ref,
+//    ...);
+// int _AFCDirectoryAccessIsValid(afc_connection *conn)
+
+afc_error_t AFCFileRefRead(
+	afc_connection conn,
+	afc_file_ref ref,
+    void *buf,
+	uint32_t *len);
+
+afc_error_t AFCFileRefSetFileSize(
+	afc_connection conn,
+	afc_file_ref ref,
+    uint64_t offset);
+	
+afc_error_t AFCFileRefWrite(
+	afc_connection conn,
+	afc_file_ref ref,
+    const void *buf,
+	uint32_t len);
+	
+afc_error_t AFCFileRefClose(
+	afc_connection conn,
+	afc_file_ref ref);
+
+afc_error_t AFCFileInfoOpen(
+	afc_connection conn,
+	const char *path,
+	afc_dictionary *info);
+	
+afc_error_t AFCKeyValueRead(
+	afc_dictionary dict,
+	char **key,
+	char **val);
+	
+afc_error_t AFCKeyValueClose(
+	afc_dictionary dict);
+
+uint32_t AMDeviceGetConnectionID(am_device device);
+mach_error_t AMDeviceEnterRecovery(am_device device);
+mach_error_t AMDeviceDisconnect(am_device device);
+mach_error_t AMDeviceRetain(am_device device);
+mach_error_t AMDeviceRelease(am_device device);
+
+uint32_t AMDeviceGetInterfaceType(
+	am_device device); // { return 1; }
+
+uint32_t AMDeviceGetInterfaceSpeed(
+	am_device device); // { return 0x78000; }
+
+/* Reads various device settings; returns nil if no value is found for
+ * the nominated key
+ *
+ * Must be balanced by CFRelease()
+ *
+ * Possible values for key:
+ * ActivationState
+ * ActivationStateAcknowledged
+ * BasebandBootloaderVersion
+ * BasebandVersion
+ * BluetoothAddress
+ * BuildVersion
+ * DeviceCertificate
+ * DeviceClass
+ * DeviceName
+ * DevicePublicKey
+ * FirmwareVersion
+ * HostAttached
+ * IntegratedCircuitCardIdentity
+ * InternationalMobileEquipmentIdentity
+ * InternationalMobileSubscriberIdentity
+ * ModelNumber
+ * PhoneNumber
+ * ProductType
+ * ProductVersion
+ * ProtocolVersion
+ * RegionInfo
+ * SBLockdownEverRegisteredKey
+ * SIMStatus
+ * SerialNumber
+ * SomebodySetTimeZone
+ * TimeIntervalSince1970
+ * TimeZone
+ * TimeZoneOffsetFromUTC
+ * TrustedHostAttached
+ * UniqueDeviceID
+ * Uses24HourClock
+ * WiFiAddress
+ * iTunesHasConnected
+ */
+CFStringRef AMDeviceCopyValue(
+	am_device device,
+	uint32_t mbz,
+	CFStringRef key);
+
+/*
+ * Returns the udid of the specified device.  The same value is returned
+ * from AMDeviceCopyValue(device,0,"UniqueDeviceID").
+ *
+ * Must be balanced by CFRelease()
+ */
+CFStringRef AMDeviceCopyDeviceIdentifier(
+	am_device device);
+
+mach_error_t AMDShutdownNotificationProxy(
+	void *);
+
+/*edits by geohot*/
+mach_error_t AMDeviceDeactivate(am_device device);
+mach_error_t AMDeviceActivate(am_device device, CFMutableDictionaryRef);
+/*end*/
+
+#ifdef __cplusplus
+}
+#endif

MobileDeviceAccess.h

+/*! \mainpage MobileDeviceAccess
+ *
+ * This module is intended to provide access to the iPhone and iPod Touch file systems.
+ * It achieves this via the same mechanism that iTunes uses, relying on direct entry
+ * points into an undocumented private framework provided by Apple.
+ *
+ * It will be necessary to include /System/Library/PrivateFrameworks/MobileDevice.framework
+ * in any project using this library
+ *
+ * \author Jeff Laing
+ * <br>
+ * Copyright 2009 Tristero Computer Systems. All rights reserved.
+ * \section intro_sec Typical Usage
+ *
+ * The application delegate will usually register itself as a listener to the MobileDeviceAccess
+ * singleton.  It will then be called back for every iPhone and iPod Touch that connects.  To access
+ * files on the AMDevice, create an AFCDirectoryAccess using \p -newAFCDirectoryAccess:
+ *
+ * \section refs_sec References
+ *
+ * This is based on information extracted from around the Internet, by people
+ * who wanted to look a lot further under the covers than I did.  I've deliberately
+ * pushed all Apple's datatypes back to being opaque, since there is nothing
+ * to be gained by looking inside them, and everything to lose.
+ *
+ * This library doesn't contain any of the 'recovery-mode' or other 'jailbreak-related' stuff
+ *
+ * Some of the places I looked include:
+ * - http://iphonesvn.halifrag.com/svn/iPhone/
+ * - http://www.theiphonewiki.com/wiki/index.php?title=MobileDevice_Library
+ * - http://code.google.com/p/iphonebrowser/issues/detail?id=52
+ * - http://www.koders.com/csharp/fidE79340F4674D47FFF3EFB6F949A1589D942798F3.aspx
+ * - http://iphone-docs.org/doku.php?id=docs:protocols:screenshot
+ * I can't guarantee that they're still there.
+ */
+#pragma once
+
+#import <Foundation/Foundation.h>
+//#import <CoreGraphics/CoreGraphics.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Apple's opaque types
+typedef uint32_t afc_error_t;
+typedef uint64_t afc_file_ref;
+
+/* opaque structures */
+typedef struct _am_device				*am_device;
+typedef struct _am_service				*am_service;
+typedef struct _afc_connection			*afc_connection;
+typedef struct _am_device_notification	*am_device_notification;
+
+/// This class represents a service running on the mobile device.  To create
+/// an instance of this class, send the \p -startService: message to an instance
+/// of AMDevice.
+///
+/// On a jailbroken 3.1.2 iPod Touch, the file
+/// \p /System/Library/Lockdown/Services.plist lists the following as
+/// valid service names:
+/// - \p "com.apple.afc" - see AFCMediaDirectory
+/// - \p "com.apple.crashreportcopymobile" - see AFCCrashLogDirectory
+/// - \p "com.apple.mobile.house_arrest" - see AFCApplicationDirectory
+/// - \p "com.apple.mobile.installation_proxy" - see AMInstallationProxy
+/// - \p "com.apple.syslog_relay" - see AMSyslogRelay
+/// - \p "com.apple.mobile.file_relay" - see AMFileRelay
+/// - \p "com.apple.springboardservices" - see AMSpringboardServices
+/// - \p "com.apple.mobile.notification_proxy" - see AMNotificationProxy
+/// - \p "com.apple.mobilesync" - see AMMobileSync
+///			(implemented as /usr/libexec/SyncAgent --lockdown --oneshot -v)
+/// - \p "com.apple.crashreportcopy"
+///			(implemented as /usr/libexec/CrashReportCopyAgent --lockdown --oneshot)
+/// - \p "com.apple.crashreportcopymover"
+///			(renamed to com.apple.crashreportmover at 3.1.2)
+///			(implemented as /usr/libexec/crash_mover --lockdown)
+/// - \p "com.apple.misagent"
+///			(implemented as /usr/libexec/misagent)
+/// - \p "com.apple.debug_image_mount"
+///			(renamed to com.apple.mobile.debug_image_mount at 3.1.2)
+///			(implemented as /usr/libexec/debug_image_mount)
+/// - \p "com.apple.mobile.integrity_relay"
+///			(implemented as /usr/libexec/mobile_integrity_relay)
+/// - \p "com.apple.mobile.MCInstall"
+///			(implemented as /usr/libexec/mc_mobile_tunnel)
+/// - \p "com.apple.mobile.mobile_image_mounter"
+///			(implemented as /usr/libexec/mobile_image_mounter)
+///			(see also http://iphone-docs.org/doku.php?id=docs:protocols:mobile_image_mounter)
+/// - \p "com.apple.mobilebackup"
+///			(implemented as /usr/libexec/BackupAgent --lockdown)
+///
+/// If you use pwnage you get these as well - blackra1n doesn't
+/// set them up
+/// - \p "com.apple.afc2" - see AFCRootDirectory
+/// - \p "org.devteam.utility"
+///			(implemented as ??????)
+///
+/// The following are mentioned in \p Services.plist but the corresponding binaries
+/// do not appear to be installed.
+///
+/// - \p "com.apple.purpletestr"
+///			(implemented as /usr/libexec/PurpleTestr --lockdown --oneshot)
+/// - \p "com.apple.mobile.diagnostics_relay"
+///			(implemented as /usr/libexec/mobile_diagnostics_relay)
+/// - \p "com.apple.mobile.factory_proxy"
+///			(implemented as /usr/libexec/mobile_factory_proxy)
+/// - \p "com.apple.mobile.software_update"
+///			(implemented as /usr/libexec/software_update)
+/// - \p "com.apple.mobile.system_profiler"
+///			(implemented as /usr/sbin/system_profiler)
+///
+/// The Internet suggests that the following existed in the past:
+/// - \p "com.apple.screenshotr"
+/// or that its available IFF you are have the Developer disk image
+/// mounted
+@interface AMService : NSObject {
+@protected
+	am_service _service;
+	NSString *_lasterror;
+}
+
+/// The last error that occurred on this service
+///
+/// The object remembers the last error that occurred, which allows most other api's
+/// to return YES/NO as their failure condition.  If no error occurred,
+/// this property will be nil.
+@property (readonly) NSString *lasterror;
+
+@end
+
+/// This class represents an installed application on the device.  To retrieve
+/// the list of installed applications on a device use [AMDevice installedApplications]
+/// or one of the methods on AMInstallationProxy.
+///
+/// Information about an application is derived from and maintained in an internal
+/// dictionary similar to the following:
+/// <PRE>
+///    ApplicationType = System;
+///    CFBundleDevelopmentRegion = English;
+///    CFBundleExecutable = MobileSafari;
+///    CFBundleIdentifier = "com.apple.mobilesafari";
+///    CFBundleInfoDictionaryVersion = "6.0";
+///    CFBundlePackageType = APPL;
+///    CFBundleResourceSpecification = "ResourceRules.plist";
+///    CFBundleSignature = "????";
+///    CFBundleSupportedPlatforms = ( iPhoneOS );
+///    CFBundleURLTypes = (
+///        {
+///            CFBundleURLName = "Web site URL";
+///            CFBundleURLSchemes = ( http, https );
+///        }, {
+///            CFBundleURLName = "Radar URL";
+///            CFBundleURLSchemes = ( rdar, radar );
+///        }, {
+///            CFBundleURLName = "FTP URL";
+///            CFBundleURLSchemes = ( ftp );
+///        }, {
+///            CFBundleURLName = "RSS URL";
+///            CFBundleURLSchemes = ( feed, feeds );
+///        }
+///    );
+///    CFBundleVersion = "528.16";
+///    DTPlatformName = iphoneos;
+///    DTSDKName = "iphoneos3.1.2.internal";
+///    LSRequiresIPhoneOS = 1;
+///    MinimumOSVersion = "3.1.2";
+///    Path = "/Applications/MobileSafari.app";
+///    PrivateURLSchemes = ( webclip );
+///    SBIsRevealable = 1;
+///    SBUsesNetwork = 3;
+///    SafariProductVersion = "4.0";
+///    UIHasPrefs = 1;
+///    UIJetsamPriority = 75;
+/// </PRE>
+/// The contents of the dictionary are key-value coded in a manner that allows
+/// NSPredicate to be used to filter applications. 
+/// For example, to locate all applications which use networking, you could use
+/// <PRE>
+/// [NSPredicate predicateWithFormat:@"SBUsesNetwork != nil"]
+/// </PRE>
+/// To locate hidden applications, you could use:
+/// <PRE>
+/// [NSPredicate predicateWithFormat:@"SBAppTags contains 'hidden'" ];
+/// </PRE>
+@interface AMApplication : NSObject {
+@private
+	NSDictionary *_info;
+	NSString *_appname;
+	NSString *_bundleid;
+}
+
+/// Return the internal dictionary that contains all our information
+- (NSDictionary*) info;
+
+/// Return the name (usually) visible in the Springboard.  To get the actual name
+/// being displayed, use AMSpringboardServices getIconState method and search
+/// using the AMApplication's bundleid.
+- (NSString*) appname;
+
+/// Return the CFBundleID value from
+/// the applications Info.plist.
+- (NSString*) bundleid;
+
+/// Return the full pathname of the directory that
+/// the .app file is installed in
+- (NSString*) appdir;
+
+@end
+
+
+/// This class represents the com.apple.mobile.notification_proxy service
+/// running on the device.  It allows programs on the Mac to send simple
+/// notifications to programs running on the device.
+///
+/// To create one, send the \p -newAMNotificationProxy message to an instance of AMDevice.
+///
+/// To receive notifications on the mobile device, add an observer to the
+/// Darwin Notification Center as follows:
+/// <pre>
+/// static void gotNotification(
+///    CFNotificationCenterRef center,
+///    void                    *observer,
+///    CFStringRef             name,
+///    const void              *alwaysZero1,
+///    CFDictionaryRef         alwaysZero2)
+/// {
+///     ...
+/// }
+/// ...
+///    CFNotificationCenterAddObserver(
+///        CFNotificationCenterGetDarwinNotifyCenter(),
+///        observer,
+///        &gotNotification,
+///        name,			// eg, CFSTR("com.myapp.notification")
+///        NULL,
+///        0 );
+/// </pre>
+///
+/// Alternately, use the AMNotificationCenter class defined in
+/// "MobileDeviceAccessIPhone.h"
+///
+/// Under the covers, it is implemented as a service called \p "com.apple.mobile.notification_proxy" which
+/// executes the following command on the device:
+/// <PRE>
+///	/usr/libexec/notification_proxy
+/// </PRE>
+#if 0
+https://gist.github.com/149443/6a40bf5cb9e47abe8a4b406c6396940e8a30dc7a suggests
+
+com.apple.language.changed
+com.apple.AddressBook.PreferenceChanged
+com.apple.mobile.data_sync.domain_changed
+com.apple.mobile.lockdown.device_name_changed
+com.apple.mobile.developer_image_mounted
+com.apple.mobile.lockdown.trusted_host_attached
+com.apple.mobile.lockdown.host_detached
+com.apple.mobile.lockdown.host_attached
+com.apple.mobile.lockdown.phone_number_changed
+com.apple.mobile.lockdown.registration_failed
+com.apple.mobile.lockdown.activation_state
+com.apple.mobile.lockdown.brick_state
+com.apple.itunes-client.syncCancelRequest
+com.apple.itunes-client.syncSuspendRequest
+com.apple.itunes-client.syncResumeRequest
+com.apple.springboard.attemptactivation
+com.apple.mobile.application_installed
+com.apple.mobile.application_uninstalled
+#endif
+
+@interface AMNotificationProxy : AMService {
+@private
+	NSMutableDictionary *_messages;
+}
+
+/// Send the named notification to the
+/// Darwin Notification Center on the device.  Note that there is no
+/// possibility to send any information with the notification.
+/// @param notification
+- (void)postNotification:(NSString*)notification;
+
+/// Add an observer for a specific message.  Whenever this message is
+/// recieved by the proxy, it will be passed to all observers who
+/// are registered, in an indeterminate order.
+/// @param notificationObserver
+/// @param notificationSelector
+/// @param notificationName
+- (void)addObserver:(id)notificationObserver
+           selector:(SEL)notificationSelector
+               name:(NSString *)notificationName;
+
+/// Remove an observer for a specific message.  Once this message
+/// is processed, the \p notificationObserver object will no longer
+/// recieve notifications.
+/// @param notificationObserver
+/// @param notificationName
+- (void)removeObserver:(id)notificationObserver
+                  name:(NSString *)notificationName;
+
+/// Remove an observer for all messages.
+/// @param notificationObserver
+- (void)removeObserver:(id)notificationObserver;
+
+@end
+
+/// This class allows certain bits of information to be retrieved
+/// from the springboard.
+/// 
+/// Under the covers, it is implemented as a service called \p "com.apple.springboardservices" which
+/// executes the following command on the device:
+/// <PRE>
+///	/usr/libexec/springboardservicesrelay
+/// </PRE>
+@interface AMSpringboardServices : AMService {
+}
+
+/// This method seems to return an NSArray which contains one entry
+/// per "page" on the iPod, though the first page appears to be for
+/// the icons displayed in the dock.
+///
+/// Each page appears to be an NSArray of entries, each of which
+/// describes an icon position on the page.  Each icon is represented
+/// by an NSDictionary containing the following keys
+/// -               bundleIdentifier = "com.apple.mobileipod";
+/// -               displayIdentifier = "com.apple.mobileipod-AudioPlayer";
+/// -               displayName = Music;
+/// -               iconModDate = 2009-09-26 20:45:29 +1000;
+///
+/// If a position is unoccupied, the entry will be an NSInteger (0)
+/// The array for each page appears to be padded to a multiple of
+/// 4, rather than reserving a full 16 entries per page.
+- (id)getIconState;
+
+/// This method returns an NSDictionary containing a single entry with
+/// the key "pngData", which contains an NSData holding the .png data
+/// for the requested application.
+///
+/// The key required appears to be the displayIdentifier rather than
+/// the bundleIdentifier.
+- (id)getIconPNGData:(NSString*)displayIdentifier;
+
+/// This method returns an NSImage holding the icon .png data
+/// for the requested application.
+///
+/// The key required appears to be the displayIdentifier rather than
+/// the bundleIdentifier.
+- (NSImage*)getIcon:(NSString*)displayIdentifier;
+
+@end
+
+/// This class communicates with the mobile_installation_proxy.  It can be used
+/// to retrieve information about installed applications on the device (as well as other
+/// installation operations that are not supported by this framework).
+///
+/// Under the covers, it is implemented as a service called \p "com.apple.mobile.installation_proxy" which
+/// executes the following command on the device:
+/// <PRE>
+///	/usr/libexec/mobile_installation_proxy
+/// </PRE>
+/// See also: http://iphone-docs.org/doku.php?id=docs:protocols:installation_proxy
+@interface AMInstallationProxy : AMService
+
+/// Return an array of all installed applications (see AMApplication) matching the input type.
+/// @param type may be "User", "System" or "Internal".  If specified as \p nil, it is ignored
+/// and all application types are returned.
+///
+/// This is used indirectly by [AMDevice installedApplications] which may be a more convenient
+/// interface.
+- (NSArray *)browse:(NSString*)type;
+
+/// Returns an array of all installed applications (see AMApplication) that match the input predicate.
+/// @param filter defines the conditions for accepting an application.
+- (NSArray *)browseFiltered:(NSPredicate*)filter;
+
+/// Return a dictionary (indexed by bundleid) of all installed applications (see AMApplication) matching the input type,
+/// and optionally filtering those that have a specific attribute in their Info.plist.
+/// @param type may be "User", "System", "Internal" or "Any"
+/// @param attr can be any attribute (like CFBundlePackageType, etc).  Note, however that
+/// you can't filter on the value of the attribute, just its existance.
+///
+/// You probably don't want to use lookupType:withAttribute - see browseFiltered: instead.
+- (NSDictionary*)lookupType:(NSString*)type withAttribute:(NSString*)attr;
+
+@end
+
+/// This class communicates with the MobileSync service.  There is a fairly complicated protocol
+/// required.
+///
+/// Under the covers, it is implemented as a service called \p "com.apple.mobilesync" which
+/// executes the following command on the device:
+/// <PRE>
+///	/usr/libexec/SyncAgent --lockdown --oneshot -v
+/// </PRE>
+@interface AMMobileSync : AMService
+- (id)getContactData;
+@end
+
+
+/// This class communicates with the syslog_relay.
+///
+/// To create one, send \p -newAMSyslogRelay:\p message: to an instance of AMDevice.
+///
+/// The message must conform to the prototype
+/// <PRE>
+/// -(void)syslogMessageRead:(NSString*)line
+/// </PRE>
+///
+/// Under the covers, it is implemented as a service called \p "com.apple.syslog_relay" which
+/// executes the following command on the device:
+/// <PRE>
+///	/usr/libexec/syslog_relay --lockdown
+/// </PRE>
+@interface AMSyslogRelay : AMService {
+	CFReadStreamRef _readstream;
+	id _listener;
+	SEL _message;
+}
+@end
+
+/// This class copies back specific files or sets of files from
+/// the device, in CPIO file format.  The file format is non-negotiable and individual files
+/// cannot be requested.  Instead, the caller specifies one or more "fileset names" from the
+/// following table.
+/// <TABLE>
+///		<TR><TH>Set Name</TH><TH>File/Directory</TH></TR>
+///		<TR><TD>AppleSupport</TD><TD>/private/var/logs/AppleSupport</TD></TR>
+///		<TR><TD>Caches</TD><TD>/private/var/mobile/Library/Caches</TD></TR>
+///		<TR><TD>CrashReporter</TD><TD>/Library/Logs/CrashReporter<BR>/private/var/mobile/Library/Logs/CrashReporter</TD></TR>
+///		<TR><TD>MobileWirelessSync</TD><TD>/private/var/mobile/Library/Logs/MobileWirelessSync</TD></TR>
+///		<TR><TD>Lockdown</TD><TD>/private/var/root/Library/Lockdown/activation_records
+/// <BR>/private/var/root/Library/Lockdown/data_ark.plist
+/// <BR>/private/var/root/Library/Lockdown/pair_records
+/// <BR>/Library/Logs/lockdownd.log</TD></TR>
+///		<TR><TD>MobileInstallation</TD><TD>/var/mobile/Library/Logs/MobileInstallation
+/// <BR>/var/mobile/Library/Caches/com.apple.mobile.installation.plist
+/// <BR>/var/mobile/Library/MobileInstallation/ArchivedApplications.plist
+/// <BR>/var/mobile/Library/MobileInstallation/ApplicationAttributes.plist
+/// <BR>/var/mobile/Library/MobileInstallation/SafeHarbor.plist</TD></TR>
+///		<TR><TD>SafeHarbor</TD><TD>/var/mobile/Library/SafeHarbor</TD></TR>
+///		<TR><TD>Network</TD><TD>/private/var/log/ppp</TD></TR>
+/// <BR>/private/var/log/racoon.log
+/// <BR>/var/log/eapolclient.en0.log</TD></TR>
+///		<TR><TD>SystemConfiguration</TD><TD>/Library/Preferences/SystemConfiguration</TD></TR>
+///		<TR><TD>UserDatabases</TD><TD>/private/var/mobile/Library/AddressBook</TD></TR>
+/// <BR>/private/var/mobile/Library/Calendar
+/// <BR>/private/var/mobile/Library/CallHistory
+/// <BR>/private/var/mobile/Library/Mail/Envelope Index
+/// <BR>/private/var/mobile/Library/SMS</TD></TR>
+///		<TR><TD>VPN</TD><TD>/private/var/log/racoon.log</TD></TR>
+///		<TR><TD>WiFi</TD><TD>/var/log/wifimanager.log
+/// <BR>/var/log/eapolclient.en0.log</TD></TR>
+///		<TR><TD>tmp</TD><TD>/private/var/tmp</TD></TR>
+/// </TABLE>
+/// The special fileset name "All" includes the files from all the other sets.
+///
+/// Due to the protocol used by the "com.apple.mobile.file_relay" service, the AMFileRelay
+/// can only be used once and must be released afterward.
+///
+/// Under the covers, it is implemented as a service called \p "com.apple.mobile.file_relay" which
+/// executes the following command on the device:
+/// <PRE>
+///	/usr/libexec/mobile_file_relay
+/// </PRE>
+@interface AMFileRelay : AMService {
+	bool _used;
+}
+
+/// Gets one or more filesets and writes the results to the nominated stream.
+/// If a problem occurs during the request, the method returns NO and
+/// lasterror will be set to an appropriate
+/// error code / message.
+/// - AlreadyUsed - AMFileRelay object has already been used
+/// - InvalidSource	- fileset name is invalid
+/// - StagingEmpty - fileset contains no files to transfer
+/// - CreateStagingPathFailed - failed to create temporary work directory on device
+/// - CopierCreationFailed - BOMCopierNew() failed (on device)
+/// - PopulationFailed - BOMCopierCopy() failed (on device)
+- (bool)getFileSets:(NSArray*)set into:(NSOutputStream*)output;
+
+/// Gets a single fileset and writes the result to the nominated stream.  This is
+/// a convenience wrapper around \p -getFileSets:
+- (bool)getFileSet:(NSString*)name into:(NSOutputStream*)output;
+@end
+
+/// This class represents an open file on the device.
+/// The caller can read from or write to the file depending on the
+/// file open mode.
+@interface AFCFileReference : NSObject
+{
+@private
+	afc_file_ref _ref;
+	afc_connection _afc;
+	NSString *_lasterror;
+}
+
+/// The last error that occurred on this file
+///
+/// The object remembers the last error that occurred, which allows most other api's
+/// to return YES/NO as their failure condition.  If no previous error occurred,
+/// this property will be nil.
+@property (readonly) NSString *lasterror;
+
+/// Close the file.  
+/// Any outstanding writes are flushed to disk.
+- (bool)closeFile;
+
+/// Change the current position within the file.
+/// @param offset is the number of bytes to move by
+/// @param mode must be one of the following:
+/// - \p SEEK_SET (0) - offset is relative to the start of the file
+/// - \p SEEK_CUR (1) - offset is relative to the current position
+/// - \p SEEK_END (2) - offset is relative to the end of the file
+- (bool)seek:(int64_t)offset mode:(int)mode;
+
+/// Return the current position within the file.
+/// The position is suitable to be passed to \p seek: \p mode:SEEK_SET
+- (bool)tell:(uint64_t*)offset;
+
+/// Read \p n
+/// bytes from the file into the nominated buffer (which must be at
+/// least \p n bytes long).  Returns the number of bytes actually read.
+- (size_t)readN:(size_t)n bytes:(char *)buff;
+
+/// Write \p n bytes to the file.  Returns \p true if the write was
+/// successful and \p false otherwise.
+- (bool)writeN:(size_t)n bytes:(const char *)buff;
+
+/// Write the contents of an NSData to the file.  Returns \p true if the
+/// write was successful and \p false otherwise
+- (bool)writeNSData:(NSData*)data;
+
+/// Set the size of the file
+///
+/// Truncates the file to the specified size.
+- (bool)setFileSize:(off_t)size;
+
+@end
+
+/// This object manages a single file server connection to the connected device.
+/// Using it, you can open files for reading or writing.  It also provides higher-order
+/// functions such as directory scanning, directory creation and file copying.
+///
+/// You should not use these directly.  Instead, see AFCMediaDirectory, AFCApplicationDirectory
+/// and AFCRootDirectory
+@interface AFCDirectoryAccess : AMService
+{
+@protected
+	afc_connection _afc;						///< the low-level connection
+}
+
+/**
+ * Return a dictionary containing information about the connected device.
+ *
+ * Keys in the result dictionary include:
+ *	- \p "Model"
+ *	- \p "FSFreeBytes"
+ *	- \p "FSBlockSize"
+ *	- \p "FSTotalBytes"
+ */
+- (NSDictionary*)deviceInfo;
+
+/**
+ * Return a dictionary containing information about the specified file.
+ * @param path Full pathname to the file to retrieve information for
+ *
+ * Keys in the result dictionary include:
+ *	- \p "st_ifmt" - file type
+ *		- \p "S_IFREG" - regular file
+ *		- \p "S_IFDIR" - directory
+ *		- \p "S_IFCHR" - character device
+ *		- \p "S_IFBLK" - block device
+ *		- \p "S_IFLNK" - symbolic link (see LinkTarget)
+ *
+ *	- \p "st_blocks" - number of disk blocks occupied by file
+ *
+ *	- \p "st_nlink" - number of "links" occupied by file
+ *
+ *	- \p "st_size" - number of "bytes" in file
+ *
+ *	- \p "LinkTarget" - target of symbolic link (only if st_ifmt="S_IFLNK")
+ */
+- (NSDictionary*)getFileInfo:(NSString*)path;
+
+/**
+ * Return YES if the specified file/directory exists on the device.
+ * @param path Full pathname to file/directory to check
+ */
+- (BOOL)fileExistsAtPath:(NSString *)path;
+
+/**
+ * Return a array containing a list of simple filenames found
+ * in the specified directory.  The entries for "." and ".." are
+ * not included.
+ * @param path Full pathname to the directory to scan
+ */
+- (NSArray*)directoryContents:(NSString*)path;
+
+/**
+ * Open a file for reading.
+ * @param path Full pathname to the file to open
+ */
+- (AFCFileReference*)openForRead:(NSString*)path;
+
+/**
+ * Open a file for writing.
+ * @param path Full pathname to the file to open
+ */
+- (AFCFileReference*)openForWrite:(NSString*)path;
+
+/**
+ * Open a file for reading or writing.
+ * @param path Full pathname to the file to open
+ */
+- (AFCFileReference*)openForReadWrite:(NSString*)path;
+
+/**
+ * Create a new directory on the device.
+ * @param path Full pathname of the directory to create
+ */
+- (BOOL)mkdir:(NSString*)path;
+
+/**
+ * Unlink a file or directory on the device.  If a directory is specified, it must
+ * be empty.
+ * @param path Full pathname of the directory to delete
+ */
+- (BOOL)unlink:(NSString*)path;
+
+/**
+ * Rename a file or directory on the device.
+ * @param oldpath Full pathname of file or directory to rename
+ * @param newpath Full pathname to rename file or directory to
+ */
+- (BOOL)rename:(NSString*)oldpath to:(NSString*)newpath;
+
+/**
+ * Copy the contents of a local file (on the Mac) to the device.
+ * @param frompath Full pathname of the local file
+ * @param topath Full pathname of the device file to copy into
+ */
+- (BOOL)copyLocalFile:(NSString*)frompath  toRemoteFile:(NSString*)topath;
+
+/**
+ * Copy the contents of a device file to a file on the Mac.
+ * @param frompath Full pathname of the device file
+ * @param topath Full pathname of the local file to copy into
+ */
+- (BOOL)copyRemoteFile:(NSString*)frompath toLocalFile:(NSString*)topath;
+
+/**
+ * Close this connection.  From this point on, none of the other functions
+ * will run correctly.
+ */
+- (void)close;
+
+@end
+
+/// This class represents an AFC connection that is rooted to the devices
+/// Media directory (/var/mobile/Media).
+///
+/// To create one, send the \p -newAFCMediaDirectory message to an instance of AMDevice.
+///
+/// Under the covers, it is implemented as a service called \p "com.apple.afc" which
+/// executes the following command on the device:
+/// <PRE>
+///	/usr/libexec/afcd --lockdown -d /var/mobile/Media -u mobile
+/// </PRE>
+@interface AFCMediaDirectory : AFCDirectoryAccess {
+}
+@end
+
+/// This class represents an AFC connection that is rooted to the devices
+/// crash-log directory (/var/mobile/Library/Logs/CrashReporter).
+///
+/// To create one, send the \p -newAFCCrashLogDirectory message to an instance of AMDevice.
+///
+/// Under the covers, it is implemented as a service called \p "com.apple.crashreportcopymobile" which
+/// executes the following command on the device:
+/// <PRE>
+/// /usr/libexec/afcd --lockdown -d /var/mobile/Library/Logs/CrashReporter -u mobile
+/// </PRE>
+@interface AFCCrashLogDirectory : AFCDirectoryAccess {
+}
+
+@end
+
+/// This class represents an AFC connection on a jail-broken device.  It has
+/// full access to the devices filesystem.
+///
+/// To create one, send the \p -newAFCRootDirectory message to an instance of AMDevice.
+///
+/// Note, instances of this class will only operate correctly on devices that are
+/// running the \p "com.apple.afc2" service.  If your device was jailbroken with
+/// blackra1n this service may be missing in which case it can be installed via
+/// Cydia.
+///
+/// Under the covers, it is implemented as a service called \p "com.apple.afc2" which
+/// executes the following command on the device:
+/// <PRE>
+/// /usr/libexec/afcd --lockdown -d /
+/// </PRE>
+@interface AFCRootDirectory : AFCDirectoryAccess {
+}
+@end
+
+/// This class represents an AFC connection that is rooted to a single
+/// application's sandbox.  You must know the \p CFBundleIdentifier value
+/// from the application's \p Info.plist
+///
+/// To create one, send the \p -newAFCApplicationDirectory: message to an instance of AMDevice.
+///
+/// The current user will be 'mobile' and will only be able to
+/// access files within the sandbox.  The root directory will appear to contain
+/// - the application
+/// - Documents
+/// - Library
+/// - tmp
+///
+/// Under the covers, it is implemented as a service called \p "com.apple.mobile.house_arrest" which
+/// executes the following command on the device:
+/// <PRE>
+///	/usr/libexec/mobile_house_arrest
+/// </PRE>
+@interface AFCApplicationDirectory : AFCDirectoryAccess {
+}
+@end
+
+/// This class represents a connected device
+/// (iPhone or iPod Touch).
+@interface AMDevice : NSObject {
+@private
+	am_device _device;
+	NSString *_lasterror;
+	NSString *_deviceName;
+	NSString *_udid;
+	
+	bool _connected, _insession;
+}
+
+/// The last error that occurred on this device
+///
+/// The object remembers the last error that occurred, which allows most other api's
+/// to return YES/NO as their failure condition.  If no previous error occurred,
+/// this property will be nil.
+@property (readonly) NSString *lasterror;
+
+/// Returns the device name (the name visible in the Devices section
+/// in iTunes and labelled as Name: in the Summary pane in iTunes).
+///
+/// The same value may be retrieved by passing \p "DeviceName" to \p -deviceValueForKey:
+@property (readonly) NSString *deviceName;		// configured name
+
+/// Returns the 40 character UDID (the field labeled as Identifier: in the Summary pane
+/// in iTunes - if its not visible, click the Serial Number: label).
+///
+/// The same value may be retrieved by passing \p "UniqueDeviceID" to \p -deviceValueForKey:
+@property (readonly) NSString *udid;			// "ed9896a213aa2341274928472234127492847211"
+
+/// Specific class of device.  eg, "iPod1,1"
+///
+/// The same value may be retrieved by passing
+/// \p "ProductType" to \p -deviceValueForKey:
+@property (readonly) NSString *productType;		// eg, "iPod1,1"
+
+/// General class of device.  eg, "iPod"
+///
+/// The same value may be retrieved by passing
+/// \p "DeviceClass" to \p -deviceValueForKey:
+@property (readonly) NSString *deviceClass;
+
+/// Returns the serial number (the field labeled as Serial Number: in the Summary pane
+/// in iTunes - if its not visible, click the Identifier: label)
+///
+/// The same value may be retrieved by passing \p "SerialNumber" to \p -deviceValueForKey:
+@property (readonly) NSString *serialNumber;	// "5984999T14P"
+
+/// Create a file service connection which can access the media directory.
+/// This uses the service \p "com.apple.afc" which is present on all devices
+/// and only allows access to the \p "/var/mobile/Media" directory structure
+/// as the user 'mobile'
+- (AFCMediaDirectory*)newAFCMediaDirectory;
+
+/// Create a file service connection which can access the crash log directory.
+/// This uses the service \p "com.apple.crashreportcopymobile" which is present on all devices
+/// and only allows access to the \p "/var/mobile/Library/Logs/CrashReporter" directory structure
+/// as the user 'mobile'
+- (AFCCrashLogDirectory*)newAFCCrashLogDirectory;
+
+
+/// Create a file service connection rooted in the sandbox for the nominated
+/// application.  This uses the service \p "com.apple.mobile.house_arrest" which is
+/// present on all devices
+/// and only allows access to the applications directory structure
+/// as the user 'mobile'
+/// @param bundleId This is the identifier value for the application.
+- (AFCApplicationDirectory*)newAFCApplicationDirectory:(NSString*)bundleId;
+
+/// Create a file service connection which can access the entire file system.
+/// This uses the service \p "com.apple.afc2" which is only present on
+/// jailbroken devices (and may need to be added manually with Cydia if you used
+/// blackra1n to jailbreak).
+- (AFCRootDirectory*)newAFCRootDirectory;
+
+/// Create a notification proxy service.
+/// This allows notifications to be posted on the device.
+- (AMNotificationProxy*)newAMNotificationProxy;
+
+/// Create a springboard services relay.
+/// This allows info about icons and png data to be retrieved
+- (AMSpringboardServices*)newAMSpringboardServices;
+
+/// Create an installation proxy relay.
+- (AMInstallationProxy*)newAMInstallationProxy;
+
+/// Create a fileset relay.  This can be used to en-masse extract certain groups
+/// of information from the device. For more information, see AMFileRelay.
+- (AMFileRelay*)newAMFileRelay;
+
+/// Create an instance of AMSyslogRelay which will relay us message
+/// from the syslog daemon on the device.
+/// @param listener This object will be notified for every message
+/// recieved by the relay service from the syslog daemon
+/// @param message This is the message sent to the \p listener object.
+- (AMSyslogRelay*)newAMSyslogRelay:(id)listener message:(SEL)message;
+
+/// Create a mobile sync relay.  Allows synchronisation of information
+/// with the device. For more information, see AMMobileSync.
+- (AMMobileSync*)newAMMobileSync;
+
+/// Returns an informational value from the device's root domain.
+/// @param key can apparently be any value from
+/// the following:
+/// <TABLE>
+///		<TR><TH>Key</TH><TH>Typical value</TH></TR>
+///     <TR><TD><TT>ActivationState</TT></TD><TD>"Activated"</TD></TR>
+///     <TR><TD><TT>ActivationStateAcknowledged</TT></TD><TD>1</TD></TR>
+///     <TR><TD><TT>BasebandBootloaderVersion</TT></TD><TD>"5.8_M3S2"</TD></TR>
+///     <TR><TD><TT>BasebandVersion</TT></TD><TD>"01.45.00"</TD></TR>
+///     <TR><TD><TT>BluetoothAddress</TT></TD><TD>?</TD></TR>
+///     <TR><TD><TT>BuildVersion</TT></TD><TD>"7A341"</TD></TR>
+///     <TR><TD><TT>DeviceCertificate</TT></TD><TD>lots of bytes</TD></TR>
+///     <TR><TD><TT>DeviceClass</TT></TD><TD>"iPod"</TD></TR>
+///     <TR><TD><TT>DeviceName</TT></TD><TD>"SmartArray"</TD></TR>
+///     <TR><TD><TT>DevicePublicKey</TT></TD><TD>lots of bytes</TD></TR>
+///     <TR><TD><TT>FirmwareVersion</TT></TD><TD>"iBoot-596.24"</TD></TR>
+///     <TR><TD><TT>HostAttached</TT></TD><TD>1</TD></TR>
+///     <TR><TD><TT>IntegratedCircuitCardIdentity</TT></TD><TD>?</TD></TR>
+///     <TR><TD><TT>InternationalMobileEquipmentIdentity</TT></TD><TD>?</TD></TR>
+///     <TR><TD><TT>InternationalMobileSubscriberIdentity</TT></TD><TD>?</TD></TR>
+///     <TR><TD><TT>ModelNumber</TT></TD><TD>"MA627"</TD></TR>
+///     <TR><TD><TT>PhoneNumber</TT></TD><TD>?</TD></TR>
+///     <TR><TD><TT>ProductType</TT></TD><TD>"iPod1,1"</TD></TR>
+///     <TR><TD><TT>ProductVersion</TT></TD><TD>"3.0"</TD></TR>
+///     <TR><TD><TT>ProtocolVersion</TT></TD><TD>2</TD></TR>
+///     <TR><TD><TT>RegionInfo</TT></TD><TD>"ZP/B"</TD></TR>
+///     <TR><TD><TT>SBLockdownEverRegisteredKey</TT></TD><TD>0</TD></TR>
+///     <TR><TD><TT>SIMStatus</TT></TD><TD>"kCTSIMSupportSIMStatusReady"</TD></TR>
+///     <TR><TD><TT>SerialNumber</TT></TD><TD>"5282327"</TD></TR>
+///     <TR><TD><TT>SomebodySetTimeZone</TT></TD><TD>1</TD></TR>
+///     <TR><TD><TT>TimeIntervalSince1970</TT></TD><TD>1249723940</TD></TR>
+///     <TR><TD><TT>TimeZone</TT></TD><TD>"Australia/Sydney"</TD></TR>
+///     <TR><TD><TT>TimeZoneOffsetFromUTC</TT></TD><TD>36000</TD></TR>
+///     <TR><TD><TT>TrustedHostAttached</TT></TD><TD>1</TD></TR>
+///     <TR><TD><TT>UniqueDeviceID</TT></TD><TD>"ab9999db56f0c444441b1c3cf6bb6666c53eea47"</TD></TR>
+///     <TR><TD><TT>Uses24HourClock</TT></TD><TD>0</TD></TR>
+///     <TR><TD><TT>WiFiAddress</TT></TD><TD>"00:1e:a1:52:91:ed"</TD></TR>
+///     <TR><TD><TT>iTunesHasConnected</TT></TD><TD>1</TD></TR>
+///     <TR><TD><TT>HardwareModel</TT></TD><TD>???</TD></TR>
+///     <TR><TD><TT>UniqueChipID</TT></TD><TD>???</TD></TR>
+///     <TR><TD><TT>ProductionSOC</TT></TD><TD>???</TD></TR>
+/// </TABLE>
+- (id)deviceValueForKey:(NSString*)key;
+
+/// Same as deviceValueForKey: but queries the specified domain.  According to
+/// http://iphone-docs.org/doku.php?id=docs:protocols:lockdownd domains include:
+/// - com.apple.springboard.curvedBatteryCapacity
+/// - com.apple.mobile.debug
+//EnableVPNLogs
+//Enable8021XLogs
+//EnableWiFiManagerLogs
+//EnableLockdownLogToDisk
+//EnableLockdownExtendedLogging
+//RemoveVPNLogs
+//Remove8021XLogs
+//RemoveLockdownLog
+//RemoveWiFiManagerLogs
+/// - com.apple.mobile.lockdown
+/// - com.apple.mobile.nikita
+/// - com.apple.mobile.data_sync
+/*
+	extern CFStringRef kLockdownSyncSupportsCalDAV;
+	extern CFStringRef kLockdownDeviceHandlesDefaultCalendar;
+	extern CFStringRef kLockdownSupportsEncryptedBackups;
+#if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_3_1
+	extern CFStringRef kLockdownDeviceSupportsClearingDataKey;
+#endif
+*/
+
+/// - com.apple.fairplay
+/*
+	extern CFStringRef kLockdownFairPlayContextIDKey;
+	extern CFStringRef kLockdownFairPlayKeyDataKey;	// ?
+	extern CFStringRef kLockdownRentalBagRequestKey;
+	extern CFStringRef kLockdownRentalBagRequestVersionKey;
+	extern CFStringRef kLockdownRentalBagResponseKey;
+	extern CFStringRef kLockdownRentalCheckinAckRequestKey;
+	extern CFStringRef kLockdownRentalCheckinAckResponseKey;	// ?
+	extern CFStringRef kLockdownFairPlayRentalClockBias;
+*/
+///
+/// However, none of them return anything for me (on a 1st gen iPod Touch).  The following do:
+/// - com.apple.mobile.iTunes.store
+/*
+	static const CFStringRef kLockdownSoftwareCUIDKey = CFSTR("SoftwareCUID");
+*/
+/// <PRE>
+///    AccountKind = 0;
+///    AppleID = stevejobs;
+///    CreditDisplayString = "";
+///    DSPersonID = 1;
+///    KnownAccounts =     (
+///                {
+///            AccountKind = 0;
+///            AppleID = stevejobs;
+///            CreditDisplayString = "";
+///            DSPersonID = 1;
+///        }
+///    );
+///    PreferHQTracks = 1;
+///    PurchaseTypes = 0;
+///    Storefront = "1,1";
+///    UserName = "Steve Jobs";
+/// </PRE>
+/// - com.apple.mobile.lockdown_cache
+/// <PRE>
+///    ActivationState = Activated;
+/// </PRE>
+/// - com.apple.mobile.mobile_application_usage
+/// <PRE>
+///    "01028E50-0F1B-409D-B39A-98151466B4BB" = 148302989;
+///    "034052C6-0573-46EF-BE17-86622EEB2CE3" = 2832934;
+///    "03F43111-D9C5-40D3-9B01-50DBD68D6C87" = 12780530;
+///		...
+///    "05DFBBF2-C3E6-413F-B466-D358BFD88054" = 3141019;
+/// </PRE>
+/// - com.apple.mobile.user_preferences
+/// <PRE>
+///    DiagnosticsAllowed = 0;
+/// </PRE>
+/// - com.apple.mobile.battery
+/// <PRE>
+///     BatteryCurrentCapacity = 100;
+///     BatteryIsCharging = 0;
+/// </PRE>
+/// - com.apple.mobile.iTunes
+/*
+	static const CFStringRef kLockdownLibraryApplicationsKey = CFSTR("LibraryApplications");
+	static const CFStringRef kLockdownSyncedApplicationsKey = CFSTR("SyncedApplications");
+*/
+/// <PRE>
+///     64Bit = 3;
+///     AlbumArt = (
+///         3005,
+///         {
+///             AlignRowBytes = 1;
+///             BackColor = 00000000;
+///             ColorAdjustment = 0;
+///             Crop = 0;
+///             FormatId = 3005;
+///             GammaAdjustment = 2.2;
+///             Interlaced = 0;
+///             OffsetAlignment = 4096;
+///             PixelFormat = 4C353535;
+///             RenderHeight = 320;
+///             RenderWidth = 320;
+///             RowBytesAlignment = 16;
+///             Sizing = 1;
+///         },
+///         3006,
+///		    ...
+///     );
+///     AppleDRMVersion = {
+///         Format = 2;
+///         Maximum = 4;
+///         Minimum = 0;
+///     };
+///     AudioCodecs = {
+///         AAC = {
+///             AppleDRM = 1;
+///             LC =             {
+///                 PerceptualNoiseSubsitution = 1;
+///                 VariableBitRate = 1;
+///             };
+///             MaximumSampleRate = 48000;
+///         };
+///         AIFF =         {
+///             MaximumBitDepth = 16;
+///             MaximumSampleRate = 48000;
+///             Mono = 1;
+///             Multichannel = 0;
+///             Stereo = 1;
+///         };
+///         AppleLossless =         {
+///             AppleDRM = 1;
+///             MaximumBitDepth = 32;
+///             MaximumBitDepthUntruncated = 16;
+///             MaximumSampleRate = 48000;
+///             Mono = 1;
+///             Multichannel = 0;
+///             Stereo = 1;
+///         };
+///         Audible =         {
+///             AAC = 1;
+///             Type1 = 0;
+///             Type2 = 1;
+///             Type3 = 1;
+///             Type4 = 1;
+///         };
+///         MP3 =         {
+///             MaximumDataRate = 320;
+///             MaximumSampleRate = 48000;
+///             Mono = 1;
+///             Stereo = 1;
+///         };
+///         WAV =         {
+///             MaximumBitDepth = 16;
+///             MaximumSampleRate = 48000;
+///             Mono = 1;
+///             Multichannel = 0;
+///             Stereo = 1;
+///         };
+///     };
+///     BatteryPollInterval = 60;
+///     ChapterImageSpecs =     (
+///         3005,
+///         {
+///             AlignRowBytes = 1;
+///             BackColor = 00000000;
+///             ColorAdjustment = 0;
+///             Crop = 0;
+///             FormatId = 3005;
+///             GammaAdjustment = 2.2;
+///             Interlaced = 0;
+///             OffsetAlignment = 4096;
+///             PixelFormat = 4C353535;
+///             RenderHeight = 320;
+///             RenderWidth = 320;
+///             RowBytesAlignment = 16;
+///             Sizing = 1;
+///         },
+///         3006,
+///         ...
+///     );
+///     ConnectedBus = USB;
+///     CustomerRingtones = 1;
+///     DBVersion = 4;
+///     FairPlayCertificate = <308202c7  4224122a .... 757521>;
+///     FairPlayGUID = db9999db56f0c2e2141b1c3cf6bb481dc53eea47;
+///     FairPlayID = <db9999db 56f0c2e2 141b1c3c f6bb481d c53eea47>;
+///     FamilyID = 10001;
+///     GeniusConfigMaxVersion = 20;
+///     GeniusConfigMinVersion = 1;
+///     GeniusMetadataMaxVersion = 20;
+///     GeniusMetadataMinVersion = 1;
+///     GeniusSimilaritiesMaxVersion = 20;
+///     GeniusSimilaritiesMinVersion = 1;
+///     ImageSpecifications =     (
+///         3004,
+///         {
+///             AlignRowBytes = 1;
+///             BackColor = FFFFFFFF;
+///             ColorAdjustment = 0;
+///             Crop = 1;
+///             FormatId = 3004;
+///             GammaAdjustment = 2.2;
+///             Interlaced = 0;
+///             OffsetAlignment = 4096;
+///             PixelFormat = 4C353535;
+///             RenderHeight = 55;
+///             RenderWidth = 55;
+///             RowBytesAlignment = 16;
+///         },
+///         3011,
+///			...
+///     );
+///     MinITunesVersion = "8.2";
+///     OEMA = 1;
+///     OEMID = 0;
+///     PhotoVideosSupported = 1;
+///     PodcastsSupported = 1;
+///     RentalsSupported = 1;
+///     SQLiteDB = 1;
+///     SupportsAntiPhishing = 1;
+///     SupportsApplicationInstall = 1;
+///     SupportsConfigurationBlobs = 1;
+///     SupportsDownloadedPodcasts = 1;
+///     SupportsGenius = 1;
+///     SupportsGeniusMixes = 1;
+///     SupportsProvisioningBlobs = 1;
+///     SyncDataClasses = (
+///         Contacts,
+///         Calendars,
+///         Bookmarks,
+///         "Mail Accounts",
+///         Notes
+///     );
+///     UseVoiceMemosFolder = 1;
+///     VideoCodecs =     {
+///         "H.264" =         {
+///             AAC =             {
+///                 AppleDRM = 1;
+///                 LC =                 {
+///                     Multichannel = 0;
+///                     VariableBitRate = 1;
+///                 };
+///                 MaximumBitRate = 256;
+///                 MaximumSampleRate = 48000;
+///             };
+///             AppleVideoDRM =             {
+///                 MaximumEncryptionPercentage = 12.5;
+///             };
+///             Level = 30;
+///             MaximumHeight = 576;
+///             MaximumPixelsPerSecond = 10368000;
+///             MaximumResolution = 414720;
+///             MaximumWidth = 720;
+///             MinimumHeight = 32;
+///             MinimumWidth = 32;
+///             Profile = B;
+///         };
+///         "H.264LC" =         {
+///             AAC =             {
+///                 AppleDRM = 1;
+///                 LC =                 {
+///                     Multichannel = 0;
+///                     VariableBitRate = 1;
+///                 };
+///                 MaximumBitRate = 256;
+///                 MaximumSampleRate = 48000;
+///             };
+///             AppleVideoDRM =             {
+///                 MaximumEncryptionPercentage = 6.25;
+///             };
+///             ComplexityLevel = 1;
+///             Level = 30;
+///             MaximumHeight = 480;
+///             MaximumResolution = 307200;
+///             MaximumWidth = 640;
+///             MinimumHeight = 32;
+///             MinimumWidth = 32;
+///             Profile = B;
+///         };
+///         MPEG4 =         {
+///             AAC =             {
+///                 AppleDRM = 1;
+///                 LC =                 {
+///                     Multichannel = 0;
+///                     VariableBitRate = 1;
+///                 };
+///                 MaximumBitRate = 256;
+///                 MaximumSampleRate = 48000;
+///             };
+///             MaximumAverageBitRate = 5000;
+///             MaximumHeight = 576;
+///             MaximumResolution = 307200;
+///             MaximumWidth = 720;
+///             MinimumHeight = 16;
+///             MinimumWidth = 16;
+///             "Profile-Level-ID" = 3;
+///         };
+///     };
+///     VideoPlaylistsSupported = 1;
+///     VoiceMemosSupported = 1;
+///     iPhoneCheckpointVersion = 1;
+///     iTunesStoreCapable = 1;
+/// }
+/// </PRE>
+/// - com.apple.disk_usage
+/// <PRE>
+///     AmountCameraAvailable = 556707840;
+///     AmountCameraUsageChanged = -58721;
+///     AmountDataAvailable = 556707840;
+///     AmountDataReserved = 167772160;
+///     CalendarUsage = 311296;
+///     CameraUsage = 27063896;
+///     MediaCacheUsage = 0;
+///     MobileApplicationUsage = 5058054749;
+///     NotesUsage = 40960;
+///     PhotoUsage = 6096396;
+///     TotalDataAvailable = 724480000;
+///     TotalDataCapacity = 15715639296;
+///     TotalDiskCapacity = 16239927296;
+///     TotalSystemAvailable = 121962496;
+///     TotalSystemCapacity = 524288000;
+///     VoicemailUsage = 28672;
+///     WebAppCacheUsage = 600064;
+/// </PRE>
+/// - com.apple.mobile.sync_data_class
+/// <PRE>
+///     Bookmarks =     {
+///     };
+///     Calendars =     {
+///     };
+///     Contacts =     {
+///     };
+///     DeviceHandlesDefaultCalendar = 1;
+///     DeviceSupportsClearingData = 1;
+///     "Mail Accounts" =     {
+///         ReadOnly = 1;
+///     };
+///     Notes =     {
+///     };
+///     SupportsEncryptedBackups = 1;
+///     SyncSupportsCalDAV = 1;
+/// </PRE>
+/// - com.apple.international
+/// <PRE>
+///     Keyboard = "en_AU";
+///     Language = en;
+///     Locale = "en_AU";
+///     SupportedKeyboards =     (
+///         "ar_YE",
+///         "cs_CZ",
+///         "da_DK",
+/// 		...
+///         "zh_Hant-HWR",
+///         "zh_Hant-Pinyin"
+///     );
+///     SupportedLanguages =     (
+///         en,
+///         fr,
+///         de,
+/// 		...
+///         id,
+///         ms
+///     );
+///     SupportedLocales =     (
+///         "ar_LY",
+///         "kok_IN",
+///         "mk_MK",
+///         "ms_MY",
+/// 		...
+///         "nl_BE",
+///         "af_NA"
+///     );
+/*
+	extern CFStringRef kLockdownSupportsAccessibilityKey;	
+*/
+/// </PRE>
+/// - com.apple.xcode.developerdomain
+/// <PRE>
+///		DeveloperStatus = Development;
+/// </PRE>
+/// - com.apple.mobile.iTunes.SQLMusicLibraryPostProcessCommands
+/// <PRE>
+///    SQLCommands =     {
+///        AddAlbumArtistBlankColumn = "ALTER TABLE item ADD COLUMN album_artist_blank INTEGER NOT NULL DEFAULT 0;";
+///        AddAlbumArtistNameBlankColumn = "ALTER TABLE album_artist ADD COLUMN name_blank INTEGER NOT NULL DEFAULT 0;";
+///        AddAlbumArtistSectionOrderColumn = "ALTER TABLE item ADD COLUMN album_artist_section_order BLOB;";
+///        AddAlbumArtistSortNameSectionColumn = "ALTER TABLE album_artist ADD COLUMN sort_name_section INTEGER NOT NULL DEFAULT 0;";
+///        ...
+///        CreateItemArtistIndex = "CREATE INDEX IF NOT EXISTS item_idx_artist ON item (artist);";
+///        CreateItemArtistPidIndex = "CREATE INDEX IF NOT EXISTS item_idx_artist_pid ON item (artist_pid);";
+///        ...
+///        UpdateItemArtistNameBlankColumn = "UPDATE item_artist SET name_blank = 1 WHERE (name = '' OR name IS NULL);";
+///        UpdateItemInSongsCollectionBlankColumns = "UPDATE item SET title_blank = (title = '' OR title IS NULL), artist_blank = (artist = '' OR artist IS NULL), composer_blank = (composer = '' OR composer IS NULL), album_blank = (album = '' OR album IS NULL), album_artist_blank = (album_artist = '' OR album_artist IS NULL), in_songs_collection = ((media_kind&33) AND ((media_kind&2)=0 AND is_rental=0));";
+///        Version = 19;
+///    };
+///    UserVersionCommandSets = {
+///        8 = {
+///            Commands = (
+///                DropUpdateItemInSongsCollectionTrigger,
+///                DropUpdateItemTitleBlankTrigger,
+///                DropUpdateItemArtistBlankTrigger,
+///                ...
+///                "MarkITunesCommandsExecuted_CurrentVersion"
+///            );
+///            SchemaDependencies = {
+///                artist = (
+///                    DropItemArtistTable,
+///                    DropAlbumArtistTable,
+///                    Artist2RenameArtistTable,
+///                    ...
+///                    DeleteEmptyAlbumArtists
+///                );
+///            };
+///        };
+///    };
+/// </PRE>
+/// - com.apple.mobile.software_behavior
+/// <PRE>
+///		Valid = 0;
+///		GoogleMail = ???;		// not observed, but deduced
+///		VolumeLimit = ???;		//
+///		ShutterClick = ???;		//
+///		NTSC = ???;				//
+///		NoWiFi = ???;			//
+///		ChinaBrick = ???;		//
+/// </PRE>
+/// - com.apple.mobile.internal
+/// <PRE>
+///		VoidWarranty = ???;					// not observed but deduced
+///		IsInternal = ???;					//
+///		PasswordProtected = ???;			//
+///		ActivationStateAcknowledged = ???;	//
+/// </PRE>
+///	- com.apple.mobile.lockdownd
+/// <PRE>
+///		LogToDisk = ???;				// not observed but deduced
+///		ExtendedLogging	= ???;			//
+/// </PRE>
+///	- com.apple.mobile.restriction
+/// <PRE>
+///		ProhibitAppInstall = ???;		// not observed but deduced
+/// </PRE>
+- (id)deviceValueForKey:(NSString*)key inDomain:(NSString*)domain;
+
+/// Returns a dictionary of "most" informational values for the device.  If called
+/// with a nil domain value, the keys
+/// correspond to those shown in the table for \ref deviceValueForKey: but it appears
+/// that it doesn't always return *all* values.
+- (id)allDeviceValuesForDomain:(NSString*)domain;
+
+/// Return a array of applications, each of which is represented by an instance
+/// of AMApplication.  Note that this only returns details for applications installed
+/// by iTunes.  For other (system) applications, use NSInstallationProxy to browse.
+- (NSArray*)installedApplications;
+
+@end
+
+/// An object must implement this protocol if it is to be passed as a listener
+/// to the MobileDeviceAccess singleton object.
+@protocol MobileDeviceAccessListener
+@optional
+/// This method will be called whenever a device is connected
+- (void)deviceConnected:(AMDevice*)device;
+/// This method will be called whenever a device is disconnected
+- (void)deviceDisconnected:(AMDevice*)device;
+@end
+
+/// This class provides the high-level interface onto the MobileDevice
+/// framework.  Instances of this class should not be created directly;
+/// instead, use \ref singleton "+singleton"
+@interface MobileDeviceAccess : NSObject {
+@private
+	id _listener;
+	BOOL _subscribed;
+	am_device_notification _notification;
+	NSMutableArray *_devices;
+	BOOL _waitingInRunLoop;
+}
+
+/// Returns an array of AMDevice objects representing the currently
+/// connected devices.
+@property (readonly) NSArray *devices;
+
+/// Returns the one true instance of \c MobileDeviceAccess
++ (MobileDeviceAccess*)singleton;
+
+/// Nominate the entity that will recieve notifications about device
+/// connections and disconnections.  The listener object must implement
+/// the MobileDeviceAccessListener protocol.
+- (bool)setListener:(id<MobileDeviceAccessListener>)listener;
+
+/// \deprecated
+///
+/// This method allows the caller to wait till a connection has been
+/// made.  It sits in a run loop and does not return till a device
+/// connects.
+- (bool)waitForConnection;
+
+/// Call this method to treat the nominated device as "disconnected".  Note,
+/// this does not disconnect the device from Mac OS X - only from the
+/// MobileDeviceAccess singleton
+/// @param device The device to disconnect.
+- (void)detachDevice:(AMDevice*)device;
+
+/// Returns the client-library version string.  On my MacOSX machine, the
+/// value returned was "@(#)PROGRAM:afc  PROJECT:afc-84"
+- (NSString*)clientVersion;
+
+@end
+
+#ifdef __cplusplus
+}
+#endif

MobileDeviceAccess.m

+//
+//  MobileDeviceAccess.m
+//  MobileDeviceAccess
+//
+//  Created by Jeff Laing on 6/08/09.
+//  Copyright 2009 Tristero Computer Systems. All rights reserved.
+//
+#import "MobileDeviceAccess.h"
+#include <unistd.h>
+#include <stdlib.h>
+#include <syslog.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <mach/error.h>
+#include <AppKit/NSApplication.h>
+
+#pragma mark MobileDevice.framework internals
+
+// opaque structures
+typedef struct _afc_directory			*afc_directory;
+typedef struct _afc_dictionary			*afc_dictionary;
+
+// Messages passed to device notification callbacks: passed as part of
+// am_device_notification_callback_info.
+typedef enum {
+	ADNCI_MSG_CONNECTED		= 1,
+	ADNCI_MSG_DISCONNECTED	= 2,
+	ADNCI_MSG_UNSUBSCRIBED	= 3
+} adnci_msg;
+
+struct am_device_notification_callback_info {
+	am_device	dev;				// 0    device
+	uint32_t	msg;				// 4    one of adnci_msg
+} __attribute__ ((packed));
+
+// The type of the device notification callback function.
+typedef void (*am_device_notification_callback)(struct am_device_notification_callback_info *,void* callback_data);
+
+// notification related functions
+mach_error_t AMDeviceNotificationSubscribe(
+	am_device_notification_callback callback,
+	uint32_t unused0,
+	uint32_t unused1,
+	void *callback_data,
+	am_device_notification *notification);
+
+mach_error_t AMDeviceNotificationUnsubscribe(
+	am_device_notification subscription);
+
+// device related functions
+mach_error_t	AMDeviceConnect(am_device device);
+mach_error_t	AMDeviceDisconnect(am_device device);
+//uint32_t		AMDeviceGetInterfaceType(am_device device);
+//uint32_t		AMDeviceGetInterfaceSpeed(am_device device);
+//uint32_t		AMDeviceGetConnectionID(am_device device);
+//CFStringRef		AMDeviceCopyDeviceIdentifier(am_device device);
+CFStringRef		AMDeviceCopyValue(am_device device,CFStringRef domain,CFStringRef key);
+mach_error_t	AMDeviceRetain(am_device device);
+mach_error_t	AMDeviceRelease(am_device device);
+
+// I can see these in the framework, but they don't seem to be exported
+// in a way that lets us link directly against them
+//?notexp? CFStringRef AMDeviceCopyDeviceLocation(am_device device);
+//		looks to just return dword[device+28]
+//?notexp? uint32_t AMDeviceUSBDeviceID(am_device device);
+//?notexp? uint32_t AMDeviceUSBLocationID(am_device device);
+//?notexp? uint32_t AMDeviceUSBProductID(am_device device);
+
+// int AMDeviceIsPaired(am_device device);
+// 000039f5 T _AMDevicePair (am_device)
+// mach_error_t AMDeviceValidatePairing(am_device device);
+// 000037e6 T _AMDeviceUnpair (am_device)
+
+mach_error_t AMDeviceLookupApplications(am_device device, CFStringRef apptype, CFDictionaryRef *result);
+
+// 0000251f T _AMDeviceActivate (am_device, int32 )
+// 000088ad T _AMDeviceArchiveApplication (am_device, int32, int32, int32, int32)
+// 0000891e T _AMDeviceBrowseApplications (am_device, int32)
+// 00008ecd T _AMDeviceCheckCapabilitiesMatch
+// 0000179b T _AMDeviceConvertError
+// 00009063 T _AMDeviceCopyProvisioningProfiles
+// 00004133 T _AMDeviceCreate
+// 000041d7 T _AMDeviceCreateFromProperties
+// 0000243d T _AMDeviceDeactivate (am_device)
+// 0000235b T _AMDeviceEnterRecovery (am_device)
+// 00008c74 T _AMDeviceInstallApplication
+// 0000a598 T _AMDeviceInstallPackage
+// 00008f59 T _AMDeviceInstallProvisioningProfile
+// 0000159a T _AMDeviceIsValid
+// 00008710 T _AMDeviceLookupApplicationArchives
+// 00008990 T _AMDeviceLookupApplications (am_device, int32, int32)
+// 00009353 T _AMDeviceMountImage
+// 000087cb T _AMDeviceRemoveApplicationArchive
+// 00008fde T _AMDeviceRemoveProvisioningProfile
+// 00002608 T _AMDeviceRemoveValue
+// 0000883c T _AMDeviceRestoreApplication
+// 0000121a T _AMDeviceSerialize
+// 0000280f T _AMDeviceSetValue
+// 00005ec5 T _AMDeviceSoftwareUpdate
+// 0000794b T _AMDeviceTransferApplication
+// 00008a91 T _AMDeviceUninstallApplication
+// 0000a232 T _AMDeviceUninstallPackage
+// 00003e21 T _AMDeviceUnserialize
+// 00008b02 T _AMDeviceUpgradeApplication
+// 000035d3 T _AMDeviceValidatePairing
+
+// session related functions
+mach_error_t AMDeviceStartSession(am_device device);
+mach_error_t AMDeviceStopSession(am_device device);
+
+// service related functions
+mach_error_t AMDeviceStartService(am_device device,CFStringRef service_name,am_service *handle,uint32_t *unknown);
+// 00002f48 T _AMDeviceStartServiceWithOptions
+// 000067f9 T _AMDeviceStartHouseArrestService
+
+// AFC connection functions
+afc_error_t AFCConnectionOpen(am_service handle,uint32_t io_timeout,afc_connection *conn);
+afc_error_t AFCConnectionClose(afc_connection conn);
+// int _AFCConnectionIsValid(afc_connection *conn)
+uint32_t AFCConnectionGetContext(afc_connection conn);
+uint32_t AFCConnectionSetContext(afc_connection conn, uint32_t ctx);
+uint32_t AFCConnectionGetFSBlockSize(afc_connection conn);
+uint32_t AFCConnectionSetFSBlockSize(afc_connection conn, uint32_t size);
+uint32_t AFCConnectionGetIOTimeout(afc_connection conn);
+uint32_t AFCConnectionSetIOTimeout(afc_connection conn, uint32_t timeout);
+uint32_t AFCConnectionGetSocketBlockSize(afc_connection conn);
+uint32_t AFCConnectionSetSocketBlockSize(afc_connection conn, uint32_t size);
+
+// 0001b8e6 T _AFCConnectionCopyLastErrorInfo
+// 0001a8b6 T _AFCConnectionCreate
+// 0001b8ca T _AFCConnectionGetStatus
+// 0001a867 T _AFCConnectionGetTypeID
+// 0001ae99 T _AFCConnectionInvalidate
+// 0001b662 T _AFCConnectionProcessOperation
+// 0001b8c1 T _AFCConnectionProcessOperations
+// 0001abdc T _AFCConnectionScheduleWithRunLoop
+// 0001b623 T _AFCConnectionSubmitOperation
+// 0001ad5a T _AFCConnectionUnscheduleFromRunLoop
+
+const char * AFCGetClientVersionString(void);		// "@(#)PROGRAM:afc  PROJECT:afc-80"
+
+// directory related functions
+afc_error_t AFCDirectoryOpen(afc_connection conn,const char *path,afc_directory *dir);
+afc_error_t AFCDirectoryRead(afc_connection conn,afc_directory dir,char **dirent);
+afc_error_t AFCDirectoryClose(afc_connection conn,afc_directory dir);
+
+afc_error_t AFCDirectoryCreate(afc_connection conn,const char *dirname);
+afc_error_t AFCRemovePath(afc_connection conn,const char *dirname);
+afc_error_t AFCRenamePath(afc_connection conn,const char *from,const char *to);
+//afc_error_t AFCLinkPath(afc_connection conn,const char *from,const char *to);
+//	NSLog(@"linkpath returned %#lx",AFCLinkPath(_afc,"/tmp/aaa","/tmp/bbb"));
+
+// file i/o functions
+afc_error_t AFCFileRefOpen(afc_connection conn, const char *path, uint64_t mode,afc_file_ref *ref);
+afc_error_t AFCFileRefClose(afc_connection conn,afc_file_ref ref);
+afc_error_t AFCFileRefSeek(afc_connection conn,	afc_file_ref ref, int64_t offset, uint64_t mode);
+afc_error_t AFCFileRefTell(afc_connection conn, afc_file_ref ref, uint64_t *offset);
+afc_error_t AFCFileRefRead(afc_connection conn,afc_file_ref ref,void *buf,uint32_t *len);
+afc_error_t AFCFileRefSetFileSize(afc_connection conn,afc_file_ref ref, uint64_t offset);
+afc_error_t AFCFileRefWrite(afc_connection conn,afc_file_ref ref, const void *buf, uint32_t len);
+// afc_error_t AFCFileRefLock(afc_connection *conn, afc_file_ref ref, ...);
+// 00019747 T _AFCFileRefUnlock
+
+// device/file information functions
+afc_error_t AFCDeviceInfoOpen(afc_connection conn, afc_dictionary *info);
+afc_error_t AFCFileInfoOpen(afc_connection conn, const char *path, afc_dictionary *info);
+afc_error_t AFCKeyValueRead(afc_dictionary dict, const char **key, const char **val);
+afc_error_t AFCKeyValueClose(afc_dictionary dict);
+
+// Notification stuff - only call these on "com.apple.mobile.notification_proxy" (AMSVC_NOTIFICATION_PROXY)
+mach_error_t AMDPostNotification(am_service socket, CFStringRef notification, CFStringRef userinfo);
+mach_error_t AMDShutdownNotificationProxy(am_service socket);
+mach_error_t AMDObserveNotification(am_service socket, CFStringRef notification);
+typedef void (*NOTIFY_CALLBACK)(CFStringRef notification, void* data);
+mach_error_t AMDListenForNotifications(am_service socket, NOTIFY_CALLBACK cb, void* data);
+
+#if 0
+Yeah, I’ve just found out how to handle this and terminate sync when user slides cancel switch.
+
+We need following functions (meta-language):
+
+ERROR AMDObserveNotification(HANDLE proxy, CFSTR notification);
+
+ERROR AMDListenForNotifications(HANDLE proxy, NOTIFY_CALLBACK cb, USERDATA data);
+
+and callback delegate:
+
+typedef void (*NOTIFY_CALLBACK)(CFSTR notification, USERDATA data);
+
+First, we need AMDObserveNotification to subscribe notifications about “com.apple.itunes-client.syncCancelRequest”. Then we should start listening for notifications (second function) until we get “AMDNotificationFaceplant”.
+That’s it. When notification got, you should unlock and close lock file handle (don’t sure if you need to post “syncDidFinish” to proxy, seems it doesn’t matter) and terminate sync gracefully.
+
+P.S. The same notification is also got when you unplug your device, so you should always be ready for errors.
+#endif
+@interface AMDevice(Private)
+- (am_service)_startService:(NSString*)name;
+@end
+
+@implementation AMService
+
+@synthesize lasterror = _lasterror;
+
+- (void)clearLastError
+{
+	[_lasterror release];
+	_lasterror = nil;
+}
+
+- (void)setLastError:(NSString*)msg
+{
+	[self clearLastError];
+	_lasterror = [msg retain];
+}
+
+- (void)dealloc
+{
+	[_lasterror release];
+	[super dealloc];
+}
+
+- (id)initWithName:(NSString*)name onDevice:(AMDevice*)device
+{
+	if (self = [super init]) {
+		_service = [device _startService:name];
+		if (_service == nil) {
+			[self release];
+			return nil;
+		}
+		
+	}
+	return self;
+}
+
++ (AMService*)serviceWithName:(NSString *)name onDevice:(AMDevice*)device
+{
+	return [[[AMService alloc] initWithName:name onDevice:device] autorelease];
+}
+
+- (bool)sendXMLRequest:(id)message
+{
+	bool result = NO;
+	CFPropertyListRef messageAsXML = CFPropertyListCreateXMLData(NULL, message);
+	if (messageAsXML) {
+		CFIndex xmlLength = CFDataGetLength(messageAsXML);
+		uint32_t sz;
+		int sock = (int)((ssize_t)_service);
+		sz = htonl(xmlLength);
+		if (send(sock, &sz, sizeof(sz), 0) != sizeof(sz)) {
+			[self setLastError:@"Can't send message size"];
+		} else {
+			if (send(sock, CFDataGetBytePtr(messageAsXML), xmlLength,0) != xmlLength) {
+				[self setLastError:@"Can't send message text"];
+			} else {
+				[self clearLastError];
+				result = YES;
+			}
+		}
+		CFRelease(messageAsXML);
+	} else {
+		[self setLastError:@"Can't convert request to XML"];
+	}
+	return(result);
+}
+
+- (id)readXMLReply
+{
+	id result = nil;
+	int sock = (int)((ssize_t)_service);
+	size_t sz;
+
+	/* now wait for the reply */
+
+	if (sizeof(long) != recv(sock, &sz, sizeof(sz), 0)) {
+		[self setLastError:@"Can't receive reply size"];
+	} else {
+		sz = ntohl(sz);
+		if (sz) {
+			// we need to be careful in here, because there is a fixed buffer size in the
+			// socket, and it may be smaller than the message we are going to recieve.  we
+			// need to allocate a buffer big enough for the final result, but loop calling
+			// recv() until we recieve the complete reply.
+			unsigned char *buff = malloc(sz);
+			unsigned char *p = buff;
+			size_t left = sz;
+			while (left) {
+				size_t rc =recv(sock, p, left,0);
+				if (rc==0) {
+					[self setLastError:[NSString stringWithFormat:@"Reply was truncated, expected %d more bytes",left]];
+					free(buff);
+					return(nil);
+				}
+				left -= rc;
+				p += rc;
+			}
+			CFDataRef r = CFDataCreateWithBytesNoCopy(0,buff,sz,kCFAllocatorNull);
+			CFPropertyListRef reply = CFPropertyListCreateFromXMLData(0,r,0,0);
+//			CFPropertyListRef reply = CFPropertyListCreateWithData(0,(CFDataRef)plistdata, kCFPropertyListImmutable, NULL, NULL);
+			CFRelease(r);
+			free(buff);
+			result = [[(id)reply copy] autorelease];
+			CFRelease(reply);
+			[self clearLastError];
+		}
+	}
+
+	return(result);
+}
+
+- (NSInputStream*)inputStreamFromSocket
+{
+	CFReadStreamRef s;
+	int sock = (int)((ssize_t)_service);
+
+	CFStreamCreatePairWithSocket(
+		kCFAllocatorDefault, (CFSocketNativeHandle)sock, &s, NULL);
+	return [(NSInputStream*)s autorelease];
+}
+
+/*
+am_service socket;
+AMDeviceStartService(dev, CFSTR("com.apple.mobile.notification_proxy"), &socket, NULL);
+AMDPostNotification(socket, CFSTR("com.apple.itunes-mobdev.syncWillStart"), NULL);
+AMDPostNotification(socket, &CFSTR("com.apple.itunes-mobdev.syncDidFinish"), NULL);
+AMDShutdownNotificationProxy(socket);
+*/
+@end
+
+@implementation AFCFileReference
+
+@synthesize lasterror = _lasterror;
+
+- (void)clearLastError
+{
+	[_lasterror release];
+	_lasterror = nil;
+}
+
+- (void)setLastError:(NSString*)msg
+{
+	[self clearLastError];
+	_lasterror = [msg retain];
+}
+
+- (bool)checkStatus:(afc_error_t)ret from:(const char *)func
+{
+	if (ret != 0) {
+		[self setLastError:[NSString stringWithFormat:@"%s failed: %lx",func,ret]];
+		return NO;
+	}
+	[self clearLastError];
+	return YES;
+}
+
+- (bool)ensureFileIsOpen
+{
+	if (_ref) return YES;
+	[self setLastError:@"File is not open"];
+	return NO;
+}
+
+- (void)dealloc
+{
+	[self closeFile];
+	[_lasterror release];
+	[super dealloc];
+}
+
+- (id)initWithPath:(NSString*)path reference:(afc_file_ref)ref afc:(afc_connection)afc
+{
+	if (self=[super init]) {
+		_ref = ref;
+		_afc = afc;
+	}
+	return self;
+}
+
+- (bool)closeFile
+{
+	if (![self ensureFileIsOpen]) return NO;
+	if (![self checkStatus:AFCFileRefClose(_afc, _ref) from:"AFCFileRefClose"]) return NO;
+	_ref = 0;
+	return YES;
+}
+
+- (bool)seek:(int64_t)offset mode:(int)m
+{
+	if (![self ensureFileIsOpen]) return NO;