2014-03-14 14:13:41 -04:00
// Software License Agreement (BSD License)
//
// Copyright (c) 2007, Peter Dennis Bartok <PeterDennisBartok@gmail.com>
// All rights reserved.
//
// Redistribution and use of this software in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above
// copyright notice, this list of conditions and the
// following disclaimer.
//
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the
// following disclaimer in the documentation and/or other
// materials provided with the distribution.
//
// * Neither the name of Peter Dennis Bartok nor the names of its
// contributors may be used to endorse or promote products
// derived from this software without specific prior
// written permission of Yahoo! Inc.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
// PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
// TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
using System ;
using System.Collections ;
using System.Collections.Generic ;
using System.ComponentModel ;
using System.IO ;
using System.Runtime.InteropServices ;
using System.Text ;
using System.Diagnostics ;
using MobileDeviceInterface ;
namespace Manzana
{
public class MobileDeviceInstanceManager
{
/// <summary>
/// Registered device notification callback
/// </summary>
private static DeviceNotificationCallback DeviceCallbackHandle ;
/// <summary>
/// The <c>Connect</c> event is triggered when a iPhone is connected to the computer
/// </summary>
public static event ConnectEventHandler ConnectEH ;
/// <summary>
/// The <c>Disconnect</c> event is triggered when the iPhone is disconnected from the computer
/// </summary>
public static event ConnectEventHandler DisconnectEH ;
/// <summary>
/// List of connected devices (device ptr -> device instance)
/// </summary>
public static Dictionary < TypedPtr < AppleMobileDeviceConnection > , MobileDeviceInstance > ConnectedDevices = new Dictionary < TypedPtr < AppleMobileDeviceConnection > , MobileDeviceInstance > ( ) ;
public static IEnumerable < MobileDeviceInstance > GetSnapshotInstanceList ( )
{
// Clone a copy to prevent problems from delayed enumeration
List < MobileDeviceInstance > Result = new List < MobileDeviceInstance > ( ) ;
Result . AddRange ( ConnectedDevices . Values ) ;
return Result ;
}
/// <summary>
/// Returns true if any devices are currently connected
/// </summary>
/// <returns></returns>
public static bool AreAnyDevicesConnected ( )
{
lock ( ConnectedDevices )
{
return ConnectedDevices . Count > 0 ;
}
}
/// <summary>
/// Initialize the mobile device manager, which handles discovery of connected Apple mobile devices
/// </summary>
/// <param name="myConnectHandler"></param>
/// <param name="myDisconnectHandler"></param>
public static void Initialize ( ConnectEventHandler myConnectHandler , ConnectEventHandler myDisconnectHandler )
{
ConnectEH + = myConnectHandler ;
DisconnectEH + = myDisconnectHandler ;
DeviceCallbackHandle = new DeviceNotificationCallback ( NotifyCallback ) ;
2014-12-11 16:20:07 -05:00
int ret = MobileDevice . DeviceImpl . NotificationSubscribe ( DeviceCallbackHandle ) ;
2014-03-14 14:13:41 -04:00
if ( ret ! = 0 )
{
throw new Exception ( "AMDeviceNotificationSubscribe failed with error " + ret ) ;
}
}
/// <summary>
/// Raises the <see>Connect</see> event.
/// </summary>
/// <param name="args">A <see cref="ConnectEventArgs"/> that contains the event data.</param>
protected static void OnConnect ( ConnectEventArgs args )
{
ConnectEventHandler handler = ConnectEH ;
if ( handler ! = null )
{
handler ( null , args ) ;
}
}
/// <summary>
/// Raises the <see>Disconnect</see> event.
/// </summary>
/// <param name="args">A <see cref="ConnectEventArgs"/> that contains the event data.</param>
protected static void OnDisconnect ( ConnectEventArgs args )
{
ConnectEventHandler handler = DisconnectEH ;
if ( handler ! = null )
{
handler ( null , args ) ;
}
}
private static void NotifyCallback ( ref AMDeviceNotificationCallbackInfo callback )
{
if ( callback . msg = = NotificationMessage . Connected )
{
MobileDeviceInstance Inst ;
if ( ConnectedDevices . TryGetValue ( callback . dev , out Inst ) )
{
// Already connected, not sure why we got another message...
}
else
{
Inst = new MobileDeviceInstance ( callback . dev ) ;
ConnectedDevices . Add ( callback . dev , Inst ) ;
}
if ( Inst . ConnectToPhone ( ) )
{
OnConnect ( new ConnectEventArgs ( callback ) ) ;
}
}
else if ( callback . msg = = NotificationMessage . Disconnected )
{
MobileDeviceInstance Inst ;
if ( ConnectedDevices . TryGetValue ( callback . dev , out Inst ) )
{
Inst . connected = false ;
OnDisconnect ( new ConnectEventArgs ( callback ) ) ;
ConnectedDevices . Remove ( callback . dev ) ;
}
}
}
}
/// <summary>
/// Exposes access to a mobile device running iOS
/// </summary>
public class MobileDeviceInstance
{
#region Locals
private DeviceRestoreNotificationCallback drn1 ;
private DeviceRestoreNotificationCallback drn2 ;
private DeviceRestoreNotificationCallback drn3 ;
private DeviceRestoreNotificationCallback drn4 ;
internal TypedPtr < AppleMobileDeviceConnection > iPhoneHandle ;
internal TypedPtr < AFCCommConnection > AFCCommsHandle ;
internal IntPtr hService ;
internal IntPtr hInstallService ;
public bool connected ;
private string current_directory ;
#endregion // Locals
#region Constructors
/// <summary>
/// Initializes a new iPhone object.
/// </summary>
private void doConstruction ( )
{
drn1 = new DeviceRestoreNotificationCallback ( DfuConnectCallback ) ;
drn2 = new DeviceRestoreNotificationCallback ( RecoveryConnectCallback ) ;
drn3 = new DeviceRestoreNotificationCallback ( DfuDisconnectCallback ) ;
drn4 = new DeviceRestoreNotificationCallback ( RecoveryDisconnectCallback ) ;
2014-12-11 16:20:07 -05:00
int ret = MobileDevice . DeviceImpl . RestoreRegisterForDeviceNotifications ( drn1 , drn2 , drn3 , drn4 , 0 , IntPtr . Zero ) ;
2014-03-14 14:13:41 -04:00
if ( ret ! = 0 )
{
throw new Exception ( "AMRestoreRegisterForDeviceNotifications failed with error " + ret ) ;
}
current_directory = "/" ;
}
/// <summary>
/// Creates a new iPhone object. If an iPhone is connected to the computer, a connection will automatically be opened.
/// </summary>
public MobileDeviceInstance ( TypedPtr < AppleMobileDeviceConnection > Connection )
{
iPhoneHandle = Connection ;
doConstruction ( ) ;
}
#endregion // Constructors
#region Properties
/// <summary>
/// Gets the current activation state of the phone
/// </summary>
public string ActivationState
{
get
{
return MobileDevice . AMDeviceCopyValue ( iPhoneHandle , "ActivationState" ) ;
}
}
/// <summary>
/// Returns true if an iPhone is connected to the computer
/// </summary>
public bool IsConnected
{
get
{
return connected ;
}
}
/// <summary>
/// Returns the Device information about the connected iPhone
/// </summary>
public TypedPtr < AppleMobileDeviceConnection > Device
{
get
{
return iPhoneHandle ;
}
}
///<summary>
/// Returns the 40-character UUID of the device
///</summary>
public string DeviceId
{
get
{
return MobileDevice . AMDeviceCopyValue ( iPhoneHandle , "UniqueDeviceID" ) ;
}
}
///<summary>
/// Returns the type of the device, should be either 'iPhone' or 'iPod'.
///</summary>
public string DeviceType
{
get
{
return MobileDevice . AMDeviceCopyValue ( iPhoneHandle , "DeviceClass" ) ;
}
}
///<summary>
/// Returns the current OS version running on the device (2.0, 2.2, 3.0, 3.1, etc).
///</summary>
public string DeviceVersion
{
get
{
return MobileDevice . AMDeviceCopyValue ( iPhoneHandle , "ProductVersion" ) ;
}
}
///<summary>
/// Returns the name of the device, like "Dan's iPhone"
///</summary>
public string DeviceName
{
get
{
return MobileDevice . AMDeviceCopyValue ( iPhoneHandle , "DeviceName" ) ;
}
}
///<summary>
/// Returns the model number of the device, like "MA712"
///</summary>
public string ModelNumber
{
get
{
return MobileDevice . AMDeviceCopyValue ( iPhoneHandle , "ModelNumber" ) ;
}
}
///<summary>
/// Returns the product type of the device, like "iPhone1,1"
///</summary>
public string ProductType
{
get
{
return MobileDevice . AMDeviceCopyValue ( iPhoneHandle , "ProductType" ) ;
}
}
/// <summary>
/// Returns the handle to the iPhone com.apple.afc service
/// </summary>
public TypedPtr < AFCCommConnection > AFCHandle
{
get
{
return AFCCommsHandle ;
}
}
/// <summary>
/// Gets/Sets the current working directory, used by all file and directory methods
/// </summary>
public string CurrentDirectory
{
get
{
return current_directory ;
}
set
{
string new_path = FullPath ( current_directory , value ) ;
if ( ! IsDirectory ( new_path ) )
{
throw new Exception ( "Invalid directory specified" ) ;
}
current_directory = new_path ;
}
}
#endregion // Properties
#region Events
/// <summary>
/// Write Me
/// </summary>
public event EventHandler DfuConnect ;
/// <summary>
/// Raises the <see>DfuConnect</see> event.
/// </summary>
/// <param name="args">A <see cref="DeviceNotificationEventArgs"/> that contains the event data.</param>
protected void OnDfuConnect ( DeviceNotificationEventArgs args )
{
EventHandler handler = DfuConnect ;
if ( handler ! = null )
{
handler ( this , args ) ;
}
}
/// <summary>
/// Write Me
/// </summary>
public event EventHandler DfuDisconnect ;
/// <summary>
/// Raises the <see>DfiDisconnect</see> event.
/// </summary>
/// <param name="args">A <see cref="DeviceNotificationEventArgs"/> that contains the event data.</param>
protected void OnDfuDisconnect ( DeviceNotificationEventArgs args )
{
EventHandler handler = DfuDisconnect ;
if ( handler ! = null )
{
handler ( this , args ) ;
}
}
/// <summary>
/// The RecoveryModeEnter event is triggered when the attached iPhone enters Recovery Mode
/// </summary>
public event EventHandler RecoveryModeEnter ;
/// <summary>
/// Raises the <see>RecoveryModeEnter</see> event.
/// </summary>
/// <param name="args">A <see cref="DeviceNotificationEventArgs"/> that contains the event data.</param>
protected void OnRecoveryModeEnter ( DeviceNotificationEventArgs args )
{
EventHandler handler = RecoveryModeEnter ;
if ( handler ! = null )
{
handler ( this , args ) ;
}
}
/// <summary>
/// The RecoveryModeLeave event is triggered when the attached iPhone leaves Recovery Mode
/// </summary>
public event EventHandler RecoveryModeLeave ;
/// <summary>
/// Raises the <see>RecoveryModeLeave</see> event.
/// </summary>
/// <param name="args">A <see cref="DeviceNotificationEventArgs"/> that contains the event data.</param>
protected void OnRecoveryModeLeave ( DeviceNotificationEventArgs args )
{
EventHandler handler = RecoveryModeLeave ;
if ( handler ! = null )
{
handler ( this , args ) ;
}
}
#endregion // Events
#region Filesystem
/// <summary>
/// Sanitizes a filename for use on PC (just the filename, not a full path)
/// </summary>
static public string SanitizeFilename ( string InputFilename )
{
char [ ] Filename = InputFilename . ToCharArray ( ) ;
char [ ] BadChars = Path . GetInvalidFileNameChars ( ) ;
for ( int i = 0 ; i < Filename . Length ; + + i )
{
foreach ( char BadChar in BadChars )
{
if ( Filename [ i ] = = BadChar )
{
Filename [ i ] = '_' ;
break ;
}
}
}
return new string ( Filename ) ;
}
/// <summary>
/// Sanitizes a path for use on PC (just the path, no filename)
/// </summary>
static public string SanitizePathNoFilename ( string InputPath )
{
char [ ] DirectoryName = InputPath . ToCharArray ( ) ;
char [ ] BadChars = Path . GetInvalidPathChars ( ) ;
for ( int i = 0 ; i < DirectoryName . Length ; + + i )
{
foreach ( char BadChar in BadChars )
{
if ( ( DirectoryName [ i ] = = BadChar ) | | ( DirectoryName [ i ] = = ':' ) )
{
DirectoryName [ i ] = '_' ;
break ;
}
}
}
return new string ( DirectoryName ) ;
}
void RecursiveBackup ( string SourceFolderOnDevice , string TargetFolderOnPC )
{
string [ ] Directories = GetDirectories ( SourceFolderOnDevice ) ;
foreach ( string Directory in Directories )
{
string NewSourceFolder = SourceFolderOnDevice + Directory + "/" ;
string NewTargetFolder = Path . Combine ( TargetFolderOnPC , SanitizePathNoFilename ( Directory ) ) ;
RecursiveBackup ( NewSourceFolder , NewTargetFolder ) ;
}
string [ ] Filenames = GetFiles ( SourceFolderOnDevice ) ;
foreach ( string Filename in Filenames )
{
string SourceFilename = SourceFolderOnDevice + Filename ;
string DestFilename = Path . Combine ( TargetFolderOnPC , SanitizeFilename ( Filename ) ) ;
WriteProgressLine ( "Copying '{0}' -> '{1}' ..." , 0 , SourceFilename , DestFilename ) ;
CopyFileFromPhone ( DestFilename , SourceFilename , 1024 * 1024 ) ;
}
}
2014-12-11 16:20:07 -05:00
void RecursiveCopy ( string SourceFolderOnPC , string TargetFolderOnDevice )
{
string [ ] Directories = System . IO . Directory . GetDirectories ( SourceFolderOnPC ) ;
foreach ( string Directory in Directories )
{
string NewSourceFolder = Directory ;
string NewTargetFolder = TargetFolderOnDevice + Directory . Substring ( Directory . LastIndexOf ( Path . DirectorySeparatorChar ) + 1 ) + "/" ;
WriteProgressLine ( "Copying folder {0} to {1}" , 0 , NewSourceFolder , NewTargetFolder ) ;
RecursiveCopy ( NewSourceFolder , NewTargetFolder ) ;
}
string [ ] Filenames = System . IO . Directory . GetFiles ( SourceFolderOnPC ) ;
foreach ( string Filename in Filenames )
{
string SourceFilename = Filename ;
string DestFilename = TargetFolderOnDevice + Path . GetFileName ( Filename ) ;
WriteProgressLine ( "Copying '{0}' -> '{1}' ..." , 0 , SourceFilename , DestFilename ) ;
CopyFileToPhone ( SourceFilename , DestFilename , 1024 * 1024 ) ;
}
}
2014-03-14 14:13:41 -04:00
public void DumpInstalledApplications ( )
{
Dictionary < string , object > AppBundles ;
2014-12-11 16:20:07 -05:00
MobileDevice . DeviceImpl . LookupApplications ( iPhoneHandle , IntPtr . Zero , out AppBundles ) ;
2014-03-14 14:13:41 -04:00
foreach ( var Bundle in AppBundles )
{
WriteProgressLine ( String . Format ( "Application bundle {0} has the following pairs:" , Bundle . Key ) , 0 ) ;
Dictionary < object , object > BundlePairs = ( Dictionary < object , object > ) Bundle . Value ;
foreach ( var KVP in BundlePairs )
{
WriteProgressLine ( String . Format ( " {0} -> {1}" , KVP . Key , KVP . Value ) , 0 ) ;
}
}
}
/// <summary>
/// Tries to back up all of the files on a phone in a particular directory to the PC
/// (requires the bundle identifier to be able to mount that directory)
/// </summary>
public bool TryBackup ( string BundleIdentifier , string SourceFolderOnDevice , string TargetFolderOnPC )
{
if ( ConnectToBundle ( BundleIdentifier ) )
{
WriteProgressLine ( "Connected to bundle '{0}'" , 0 , BundleIdentifier ) ;
try
{
RecursiveBackup ( SourceFolderOnDevice , TargetFolderOnPC ) ;
return true ;
}
catch ( Exception ex )
{
WriteProgressLine ( "Failed to transfer a file, extended error is '{0}'" , 100 , ex . Message ) ;
return false ;
}
}
else
{
WriteProgressLine ( "Error: Failed to connect to bundle '{0}'" , 100 , BundleIdentifier ) ;
return false ;
}
}
2014-12-11 16:20:07 -05:00
/// <summary>
/// Tries to copy all of the files in a particular directory on the PC to the phone directory
/// (requires the bundle identifier to be able to mount that directory)
/// </summary>
public bool TryCopy ( string BundleIdentifier , string SourceFolderOnPC , string TargetFolderOnDevice )
{
if ( ConnectToBundle ( BundleIdentifier ) )
{
WriteProgressLine ( "Connected to bundle '{0}'" , 0 , BundleIdentifier ) ;
try
{
RecursiveCopy ( SourceFolderOnPC , TargetFolderOnDevice ) ;
return true ;
}
catch ( Exception ex )
{
WriteProgressLine ( "Failed to transfer a file, extended error is '{0}'" , 100 , ex . Message ) ;
return false ;
}
}
else
{
WriteProgressLine ( "Error: Failed to connect to bundle '{0}'" , 100 , BundleIdentifier ) ;
return false ;
}
}
/// <summary>
/// Tries to copy all of the files in the manifest on the PC to the documents directory
/// (requires the bundle identifier to be able to mount that directory)
/// </summary>
public bool TryCopy ( string BundleIdentifier , string Manifest )
{
if ( ConnectToBundle ( BundleIdentifier ) )
{
WriteProgressLine ( "Connected to bundle '{0}'" , 0 , BundleIdentifier ) ;
try
{
string BaseFolder = Path . GetDirectoryName ( Manifest ) ;
string Files = File . ReadAllText ( Manifest ) ;
string [ ] FileList = Files . Split ( '\n' ) ;
foreach ( string Filename in FileList )
{
if ( ! string . IsNullOrEmpty ( Filename ) & & ! string . IsNullOrWhiteSpace ( Filename ) )
{
string Trimmed = Filename . Trim ( ) ;
string SourceFilename = BaseFolder + "\\" + Trimmed ;
SourceFilename = SourceFilename . Replace ( '/' , '\\' ) ;
2015-04-21 07:07:27 -04:00
string DestFilename = "/Documents/" + Trimmed . Replace ( "cookeddata/" , "" ) ;
2014-12-11 16:20:07 -05:00
DestFilename = DestFilename . Replace ( '\\' , '/' ) ;
WriteProgressLine ( "Copying '{0}' -> '{1}' ..." , 0 , SourceFilename , DestFilename ) ;
CopyFileToPhone ( SourceFilename , DestFilename , 1024 * 1024 ) ;
}
}
return true ;
}
catch ( Exception ex )
{
WriteProgressLine ( "Failed to transfer a file, extended error is '{0}'" , 100 , ex . Message ) ;
return false ;
}
}
else
{
WriteProgressLine ( "Error: Failed to connect to bundle '{0}'" , 100 , BundleIdentifier ) ;
return false ;
}
}
/// <summary>
/// Tries to copy all of the files in a particular directory on the PC to the phone directory
/// (requires the bundle identifier to be able to mount that directory)
/// </summary>
public bool TryBackup ( string BundleIdentifier , string [ ] Files )
{
if ( ConnectToBundle ( BundleIdentifier ) )
{
WriteProgressLine ( "Connected to bundle '{0}'" , 0 , BundleIdentifier ) ;
try
{
string SafeDeviceName = MobileDeviceInstance . SanitizePathNoFilename ( DeviceName ) ;
foreach ( string Filename in Files )
{
// string BaseFolder = Path.GetDirectoryName(Filename);
string Manifest = Path . GetDirectoryName ( Filename ) + "\\" + SafeDeviceName + "_" + Path . GetFileName ( Filename ) ;
CopyFileFromPhone ( Manifest , "/Documents/" + Path . GetFileName ( Filename ) , 1024 * 1024 ) ;
}
return true ;
}
catch ( Exception ex )
{
WriteProgressLine ( "Failed to transfer a file, extended error is '{0}'" , 100 , ex . Message ) ;
return false ;
}
}
else
{
string SafeDeviceName = MobileDeviceInstance . SanitizePathNoFilename ( DeviceName ) ;
foreach ( string Filename in Files )
{
// string BaseFolder = Path.GetDirectoryName(Filename);
string Manifest = Path . GetDirectoryName ( Filename ) + "\\" + SafeDeviceName + "_" + Path . GetFileName ( Filename ) ;
File . WriteAllText ( Manifest , "" ) ;
}
WriteProgressLine ( "Error: Failed to connect to bundle '{0}'" , 100 , BundleIdentifier ) ;
return false ;
}
}
2014-03-14 14:13:41 -04:00
/// <summary>
/// Returns the names of files in a specified directory
/// </summary>
/// <param name="path">The directory from which to retrieve the files.</param>
/// <returns>A <c>String</c> array of file names in the specified directory. Names are relative to the provided directory</returns>
public string [ ] GetFiles ( string path )
{
return GetFiles ( path , false ) ;
}
public string [ ] GetFiles ( string path , bool bIncludeDirs )
{
if ( ! IsConnected )
{
throw new Exception ( "Not connected to phone" ) ;
}
string full_path = FullPath ( CurrentDirectory , path ) ;
IntPtr hAFCDir = IntPtr . Zero ;
2014-12-11 16:20:07 -05:00
if ( MobileDevice . DeviceImpl . DirectoryOpen ( AFCCommsHandle , full_path , ref hAFCDir ) ! = 0 )
2014-03-14 14:13:41 -04:00
{
throw new Exception ( "Path does not exist" ) ;
}
string buffer = null ;
ArrayList paths = new ArrayList ( ) ;
2014-12-11 16:20:07 -05:00
MobileDevice . DeviceImpl . DirectoryRead ( AFCCommsHandle , hAFCDir , ref buffer ) ;
2014-03-14 14:13:41 -04:00
while ( buffer ! = null )
{
if ( ! IsDirectory ( FullPath ( full_path , buffer ) ) )
{
paths . Add ( buffer ) ;
}
else
{
if ( bIncludeDirs )
{
paths . Add ( buffer + "/" ) ;
}
}
2014-12-11 16:20:07 -05:00
MobileDevice . DeviceImpl . DirectoryRead ( AFCCommsHandle , hAFCDir , ref buffer ) ;
2014-03-14 14:13:41 -04:00
}
2014-12-11 16:20:07 -05:00
MobileDevice . DeviceImpl . DirectoryClose ( AFCCommsHandle , hAFCDir ) ;
2014-03-14 14:13:41 -04:00
return ( string [ ] ) paths . ToArray ( typeof ( string ) ) ;
}
/// <summary>
/// Returns the FileInfo dictionary
/// </summary>
/// <param name="path">The file or directory for which to retrieve information.</param>
public Dictionary < string , string > GetFileInfo ( string path )
{
Dictionary < string , string > ans = new Dictionary < string , string > ( ) ;
TypedPtr < AFCDictionary > Data ;
2014-12-11 16:20:07 -05:00
int ret = MobileDevice . DeviceImpl . FileInfoOpen ( AFCCommsHandle , path , out Data ) ;
2014-03-14 14:13:41 -04:00
if ( ( ret = = 0 ) & & ( Data . Handle ! = IntPtr . Zero ) )
{
IntPtr pname ;
IntPtr pvalue ;
2014-12-11 16:20:07 -05:00
while ( MobileDevice . DeviceImpl . KeyValueRead ( Data , out pname , out pvalue ) = = 0 & & pname ! = IntPtr . Zero & & pvalue ! = IntPtr . Zero )
2014-03-14 14:13:41 -04:00
{
string name = Marshal . PtrToStringAnsi ( pname ) ;
string value = Marshal . PtrToStringAnsi ( pvalue ) ;
if ( ( name ! = null ) & & ( value ! = null ) )
{
ans . Add ( name , value ) ;
}
else
{
break ;
}
}
2014-12-11 16:20:07 -05:00
MobileDevice . DeviceImpl . KeyValueClose ( Data ) ;
2014-03-14 14:13:41 -04:00
}
return ans ;
}
/// <summary>
/// Returns the st_ifmt of a path
/// </summary>
/// <param name="path">Path to query</param>
/// <returns>string representing value of st_ifmt</returns>
private string Get_st_ifmt ( string path )
{
Dictionary < string , string > fi = GetFileInfo ( path ) ;
return fi [ "st_ifmt" ] ;
}
/// <summary>
/// Returns the size and type of the specified file or directory.
/// </summary>
/// <param name="path">The file or directory for which to retrieve information.</param>
/// <param name="size">Returns the size of the specified file or directory</param>
/// <param name="directory">Returns <c>true</c> if the given path describes a directory, false if it is a file.</param>
public void GetFileInfo ( string path , out ulong size , out bool directory )
{
Dictionary < string , string > fi = GetFileInfo ( path ) ;
size = fi . ContainsKey ( "st_size" ) ? System . UInt64 . Parse ( fi [ "st_size" ] ) : 0 ;
bool SLink = false ;
directory = false ;
if ( fi . ContainsKey ( "st_ifmt" ) )
{
switch ( fi [ "st_ifmt" ] )
{
case "S_IFDIR" : directory = true ; break ;
case "S_IFLNK" : SLink = true ; break ;
}
}
if ( SLink )
{
// test for symbolic directory link
IntPtr hAFCDir = IntPtr . Zero ;
2014-12-11 16:20:07 -05:00
if ( directory = ( MobileDevice . DeviceImpl . DirectoryOpen ( AFCCommsHandle , path , ref hAFCDir ) = = 0 ) )
2014-03-14 14:13:41 -04:00
{
2014-12-11 16:20:07 -05:00
MobileDevice . DeviceImpl . DirectoryClose ( AFCCommsHandle , hAFCDir ) ;
2014-03-14 14:13:41 -04:00
}
}
}
/// <summary>
/// Returns the size of the specified file or directory.
/// </summary>
/// <param name="path">The file or directory for which to obtain the size.</param>
/// <returns></returns>
public ulong FileSize ( string path )
{
bool is_dir ;
ulong size ;
GetFileInfo ( path , out size , out is_dir ) ;
return size ;
}
/// <summary>
/// Creates the directory specified in path
/// </summary>
/// <param name="path">The directory path to create</param>
/// <returns>true if directory was created</returns>
public bool CreateDirectory ( string path )
{
2014-12-11 16:20:07 -05:00
return ! ( MobileDevice . DeviceImpl . DirectoryCreate ( AFCCommsHandle , FullPath ( CurrentDirectory , path ) ) ! = 0 ) ;
2014-03-14 14:13:41 -04:00
}
/// <summary>
/// Gets the names of subdirectories in a specified directory.
/// </summary>
/// <param name="path">The path for which an array of subdirectory names is returned.</param>
/// <returns>An array of type <c>String</c> containing the names of subdirectories in <c>path</c>.</returns>
public string [ ] GetDirectories ( string path )
{
if ( ! IsConnected )
{
2015-03-24 16:31:05 -04:00
Reconnect ( ) ;
if ( ! IsConnected )
{
throw new Exception ( "Not connected to phone" ) ;
}
2014-03-14 14:13:41 -04:00
}
IntPtr hAFCDir = IntPtr . Zero ;
string full_path = FullPath ( CurrentDirectory , path ) ;
//full_path = "/private"; // bug test
2014-12-11 16:20:07 -05:00
int res = MobileDevice . DeviceImpl . DirectoryOpen ( AFCCommsHandle , full_path , ref hAFCDir ) ;
2014-03-14 14:13:41 -04:00
if ( res ! = 0 )
{
throw new Exception ( "Path does not exist: " + res . ToString ( ) ) ;
}
string buffer = null ;
ArrayList paths = new ArrayList ( ) ;
2014-12-11 16:20:07 -05:00
MobileDevice . DeviceImpl . DirectoryRead ( AFCCommsHandle , hAFCDir , ref buffer ) ;
2014-03-14 14:13:41 -04:00
while ( buffer ! = null )
{
if ( ( buffer ! = "." ) & & ( buffer ! = ".." ) & & IsDirectory ( FullPath ( full_path , buffer ) ) )
{
paths . Add ( buffer ) ;
}
2014-12-11 16:20:07 -05:00
MobileDevice . DeviceImpl . DirectoryRead ( AFCCommsHandle , hAFCDir , ref buffer ) ;
2014-03-14 14:13:41 -04:00
}
2014-12-11 16:20:07 -05:00
MobileDevice . DeviceImpl . DirectoryClose ( AFCCommsHandle , hAFCDir ) ;
2014-03-14 14:13:41 -04:00
return ( string [ ] ) paths . ToArray ( typeof ( string ) ) ;
}
/// <summary>
/// Moves a file or a directory and its contents to a new location or renames a file or directory if the old and new parent path matches.
/// </summary>
/// <param name="sourceName">The path of the file or directory to move or rename.</param>
/// <param name="destName">The path to the new location for <c>sourceName</c>.</param>
/// <remarks>Files cannot be moved across filesystem boundaries.</remarks>
public bool Rename ( string sourceName , string destName )
{
2014-12-11 16:20:07 -05:00
return MobileDevice . DeviceImpl . RenamePath ( AFCCommsHandle , FullPath ( CurrentDirectory , sourceName ) , FullPath ( CurrentDirectory , destName ) ) = = 0 ;
2014-03-14 14:13:41 -04:00
}
/// <summary>
/// Returns the root information for the specified path.
/// </summary>
/// <param name="path">The path of a file or directory.</param>
/// <returns>A string containing the root information for the specified path. </returns>
public string GetDirectoryRoot ( string path )
{
return "/" ;
}
/// <summary>
/// Determines whether the given path refers to an existing file or directory on the phone.
/// </summary>
/// <param name="path">The path to test.</param>
/// <returns><c>true</c> if path refers to an existing file or directory, otherwise <c>false</c>.</returns>
public bool Exists ( string path )
{
TypedPtr < AFCDictionary > data = IntPtr . Zero ;
2014-12-11 16:20:07 -05:00
int ret = MobileDevice . DeviceImpl . FileInfoOpen ( AFCCommsHandle , path , out data ) ;
2014-03-14 14:13:41 -04:00
if ( ret = = 0 )
{
2014-12-11 16:20:07 -05:00
MobileDevice . DeviceImpl . KeyValueClose ( data ) ;
2014-03-14 14:13:41 -04:00
}
return ret = = 0 ;
}
/// <summary>
/// Determines whether the given path refers to an existing directory on the phone.
/// </summary>
/// <param name="path">The path to test.</param>
/// <returns><c>true</c> if path refers to an existing directory or is a symbolic link to one, otherwise <c>false</c>.</returns>
public bool IsDirectory ( string path )
{
bool is_dir ;
ulong size ;
GetFileInfo ( path , out size , out is_dir ) ;
return is_dir ;
}
/// <summary>
/// Test if path represents a regular file
/// </summary>
/// <param name="path">path to query</param>
/// <returns>true if path refers to a regular file, false if path is a link or directory</returns>
public bool IsFile ( string path )
{
return Get_st_ifmt ( path ) = = "S_IFREG" ;
}
/// <summary>
/// Test if path represents a link
/// </summary>
/// <param name="path">path to test</param>
/// <returns>true if path is a symbolic link</returns>
public bool IsLink ( string path )
{
return Get_st_ifmt ( path ) = = "S_IFLNK" ;
}
/// <summary>
/// Deletes an empty directory from a specified path.
/// </summary>
/// <param name="path">The name of the empty directory to remove. This directory must be writable and empty.</param>
public void DeleteDirectory ( string path )
{
string full_path = FullPath ( CurrentDirectory , path ) ;
if ( IsDirectory ( full_path ) )
{
2014-12-11 16:20:07 -05:00
MobileDevice . DeviceImpl . RemovePath ( AFCCommsHandle , full_path ) ;
2014-03-14 14:13:41 -04:00
}
}
/// <summary>
/// Deletes the specified directory and, if indicated, any subdirectories in the directory.
/// </summary>
/// <param name="path">The name of the directory to remove.</param>
/// <param name="recursive"><c>true</c> to remove directories, subdirectories, and files in path; otherwise, <c>false</c>. </param>
public void DeleteDirectory ( string path , bool recursive )
{
if ( ! recursive )
{
DeleteDirectory ( path ) ;
return ;
}
string full_path = FullPath ( CurrentDirectory , path ) ;
if ( IsDirectory ( full_path ) )
{
InternalDeleteDirectory ( path ) ;
}
}
/// <summary>
/// Deletes the specified file.
/// </summary>
/// <param name="path">The name of the file to remove.</param>
public void DeleteFile ( string path )
{
string full_path = FullPath ( CurrentDirectory , path ) ;
if ( Exists ( full_path ) )
{
2014-12-11 16:20:07 -05:00
MobileDevice . DeviceImpl . RemovePath ( AFCCommsHandle , full_path ) ;
2014-03-14 14:13:41 -04:00
}
}
#endregion // Filesystem
#region Public Methods
/// <summary>
/// Close the AFC connection
/// </summary>
public void Disconnect ( )
{
if ( AFCCommsHandle . Handle ! = IntPtr . Zero )
{
2014-12-11 16:20:07 -05:00
MobileDevice . DeviceImpl . ConnectionClose ( AFCCommsHandle ) ;
MobileDevice . DeviceImpl . StopSession ( iPhoneHandle ) ;
MobileDevice . DeviceImpl . Disconnect ( iPhoneHandle ) ;
2014-03-14 14:13:41 -04:00
}
AFCCommsHandle = IntPtr . Zero ;
}
/// <summary>
/// Close and Reopen AFC Connection
/// </summary>
/// <returns>status from reopen</returns>
public void Reconnect ( )
{
Disconnect ( ) ;
ConnectToPhone ( ) ;
}
#endregion // public Methods
void CopyFileToPhone ( string PathOnPC , string PathOnPhone )
{
CopyFileToPhone ( PathOnPC , PathOnPhone , 1024 * 1024 ) ;
}
void CopyFileToPhone ( string PathOnPC , string PathOnPhone , int ChunkSize )
{
DateTime StartTime = DateTime . Now ;
byte [ ] buffer = new byte [ ChunkSize ] ;
2015-03-24 16:31:05 -04:00
// verify we are still connected
if ( ! IsConnected )
{
Reconnect ( ) ;
}
// Make sure the directory exists on the phone
string DirectoryOnPhone = PathOnPhone . Remove ( PathOnPhone . LastIndexOf ( '/' ) ) ;
2015-03-12 15:27:35 -04:00
if ( ! IsDirectory ( DirectoryOnPhone ) )
{
Console . WriteLine ( "Directory (" + DirectoryOnPhone + ") doesn't exist!" ) ;
if ( ! CreateDirectory ( DirectoryOnPhone ) )
{
Console . WriteLine ( "CreateDirectory (" + DirectoryOnPhone + ") failed" ) ;
// throw new IOException("CreateDirectory (" + DirectoryOnPhone + ") failed");
}
}
2014-03-14 14:13:41 -04:00
2015-03-24 16:31:05 -04:00
2014-03-14 14:13:41 -04:00
FileStream SourceFile = File . OpenRead ( PathOnPC ) ;
iPhoneFile DestinationFile = iPhoneFile . OpenWrite ( this , PathOnPhone ) ;
long ProgressInterval = Math . Max ( buffer . Length , ( SourceFile . Length / TransferProgressDivisor ) ) ;
long NextProgressPrintout = ProgressInterval ;
long TotalBytesRead = 0 ;
int BytesRead = SourceFile . Read ( buffer , 0 , buffer . Length ) ;
while ( BytesRead > 0 )
{
if ( TotalBytesRead > = NextProgressPrintout )
{
NextProgressPrintout + = ProgressInterval ;
if ( OnGenericProgress ! = null )
{
int PercentDone = ( int ) ( ( 100 * TotalBytesRead ) / SourceFile . Length ) ;
string Msg = "Transferred " + ( SourceFile . Position / 1024 ) + " KB of " + ( SourceFile . Length / 1024 ) + " KB" ;
OnGenericProgress ( Msg , PercentDone ) ;
}
else
{
Console . WriteLine ( " ... Transferred " + ( SourceFile . Position / 1024 ) + " KB of " + ( SourceFile . Length / 1024 ) + " KB" ) ;
}
}
DestinationFile . Write ( buffer , 0 , BytesRead ) ;
BytesRead = SourceFile . Read ( buffer , 0 , buffer . Length ) ;
TotalBytesRead + = BytesRead ;
}
2014-12-11 16:20:07 -05:00
// DestinationFile.Flush();
2014-03-14 14:13:41 -04:00
DestinationFile . Close ( ) ;
SourceFile . Close ( ) ;
TimeSpan CopyLength = DateTime . Now - StartTime ;
Console . WriteLine ( " ... Finished copying to '{1}' in {0:0.00} s" , CopyLength . TotalSeconds , PathOnPhone ) ;
}
// Default level is 6, and 0,3,7 are regularly used. Bump the logging level up to 7 to get verbose logs.
void SetLoggingLevel ( int Threshold )
{
Int32 LoggingThreshold = Math . Min ( Math . Max ( 0 , Threshold ) , 7 ) ;
2014-12-11 16:20:07 -05:00
MobileDevice . CoreImpl . CFPreferencesSetAppValue (
2014-03-14 14:13:41 -04:00
( IntPtr ) MobileDevice . CFStringMakeConstantString ( "LogLevel" ) ,
( IntPtr ) MobileDevice . CFNumberCreate ( LoggingThreshold ) ,
( IntPtr ) MobileDevice . CFStringMakeConstantString ( "com.apple.MobileDevice" ) ) ;
}
void WriteProgressLine ( string Fmt , int ProgressCount , params object [ ] Args )
{
string Line = String . Format ( Fmt , Args ) ;
2014-12-11 16:20:07 -05:00
if ( OnGenericProgress ! = null )
2014-03-14 14:13:41 -04:00
{
OnGenericProgress ( Line , ProgressCount ) ;
}
else
{
Console . WriteLine ( Line ) ;
}
}
/// <summary>
/// Copies a file from the phone to the PC
/// </summary>
void CopyFileFromPhone ( string PathOnPC , string PathOnPhone , int ChunkSize )
{
DateTime StartTime = DateTime . Now ;
byte [ ] buffer = new byte [ ChunkSize ] ;
// Make sure the directory exists on the PC
string DirectoryOnPC = Path . GetDirectoryName ( PathOnPC ) ;
Directory . CreateDirectory ( DirectoryOnPC ) ;
iPhoneFile SourceFile = iPhoneFile . OpenRead ( this , PathOnPhone ) ;
FileStream DestinationFile = File . OpenWrite ( PathOnPC ) ;
long TotalBytesRead = 0 ;
int BytesRead = SourceFile . Read ( buffer , 0 , buffer . Length ) ;
while ( BytesRead > 0 )
{
DestinationFile . Write ( buffer , 0 , BytesRead ) ;
BytesRead = SourceFile . Read ( buffer , 0 , buffer . Length ) ;
TotalBytesRead + = BytesRead ;
}
DestinationFile . Flush ( ) ;
DestinationFile . Close ( ) ;
SourceFile . Close ( ) ;
TimeSpan CopyLength = DateTime . Now - StartTime ;
WriteProgressLine ( " ... Finished copying to '{1}' in {0:0.00} s" , 100 , CopyLength . TotalSeconds , PathOnPC ) ;
}
string MakeUnixPath ( string PlatformPath )
{
string Result = Path . GetFullPath ( PlatformPath ) ;
// Convert C:\ to /C\, the \ will get converted to / in the next step
if ( Path . IsPathRooted ( Result ) )
{
string Root = Path . GetPathRoot ( Result ) ;
Result = '/' + Root . Replace ( ":" , "" ) + Result . Substring ( Root . Length ) ;
}
// Convert slash directions
if ( Path . DirectorySeparatorChar ! = '/' )
{
Result = Result . Replace ( Path . DirectorySeparatorChar , '/' ) ;
}
return Result ;
}
void ReconnectWithInstallProxy ( )
{
Reconnect ( ) ;
2014-12-11 16:20:07 -05:00
if ( MobileDevice . DeviceImpl . StartService ( iPhoneHandle , MobileDevice . CFStringMakeConstantString ( "com.apple.mobile.installation_proxy" ) , ref hInstallService , IntPtr . Zero ) ! = 0 )
2014-03-14 14:13:41 -04:00
{
Console . WriteLine ( "Unable to start installation_proxy service!" ) ;
}
}
public delegate void ProgressCallback ( string Message , int PercentDone ) ;
public ProgressCallback OnGenericProgress = null ;
public int TransferProgressDivisor = 25 ;
/// <summary>
/// Generic progress callback implementation (used for install/uninstall/etc...)
/// </summary>
void HandleProgressCallback ( string OuterFunction , TypedPtr < CFDictionary > SourceDict )
{
2014-12-11 16:20:07 -05:00
Dictionary < string , object > Dict = MobileDevice . ConvertCFDictionaryToDictionaryString ( SourceDict ) ;
2014-03-14 14:13:41 -04:00
// Expecting:
// string,string -> "Status",PhaseOfInstaller
// string,number -> "PercentComplete",%Done
try
{
string Phase = Dict [ "Status" ] as string ;
int PercentDone = ( int ) ( ( Double ) ( Dict [ "PercentComplete" ] ) ) ;
if ( OnGenericProgress ! = null )
{
string Msg = String . Format ( "{0} is {1}% complete at phase '{2}'" , OuterFunction , PercentDone , Phase ) ;
OnGenericProgress ( Msg , PercentDone ) ;
}
else
{
Console . WriteLine ( " ... {0} {1}% complete (phase '{2}')" , OuterFunction , PercentDone , Phase ) ;
}
}
catch ( System . Exception )
{
}
}
/// <summary>
/// Progress callback for upgrading or installing
/// </summary>
void InstallProgressCallback ( IntPtr UntypedSourceDict , IntPtr UserData )
{
HandleProgressCallback ( "Install" , UntypedSourceDict ) ;
}
/// <summary>
/// Progress callback for uninstalling
/// </summary>
void UninstallProgressCallback ( IntPtr UntypedSourceDict , IntPtr UserData )
{
HandleProgressCallback ( "Uninstall" , UntypedSourceDict ) ;
}
/// <summary>
/// Try to install an IPA on the mobile device (it must have already been copied to the PublicStaging directory, with the same filename as the path passed in)
/// </summary>
public bool TryInstall ( string IPASourcePathOnPC )
{
DateTime StartTime = DateTime . Now ;
ReconnectWithInstallProxy ( ) ;
IntPtr LiveConnection = IntPtr . Zero ;
IntPtr ClientOptions = IntPtr . Zero ;
TypedPtr < CFURL > UrlPath = MobileDevice . CreateFileUrlHelper ( IPASourcePathOnPC , false ) ;
2014-12-11 16:20:07 -05:00
// string UrlPathAsString = MobileDevice.GetStringForUrl(UrlPath);
2014-03-14 14:13:41 -04:00
2014-12-11 16:20:07 -05:00
int Result = MobileDevice . DeviceImpl . SecureInstallApplication ( LiveConnection , iPhoneHandle , UrlPath , ClientOptions , InstallProgressCallback , IntPtr . Zero ) ;
2014-03-14 14:13:41 -04:00
if ( Result = = 0 )
{
Console . WriteLine ( "Install of \"{0}\" completed successfully in {2:0.00} seconds" , Path . GetFileName ( IPASourcePathOnPC ) , Result , ( DateTime . Now - StartTime ) . TotalSeconds ) ;
}
else
{
Console . WriteLine ( "Install of \"{0}\" failed with error code {1} in {2:0.00} seconds" , Path . GetFileName ( IPASourcePathOnPC ) , MobileDevice . GetErrorString ( Result ) , ( DateTime . Now - StartTime ) . TotalSeconds ) ;
}
return Result = = 0 ;
}
/// <summary>
/// Try to update an IPA on the mobile device (it must have already been copied to the PublicStaging directory, with the same filename as the path passed in)
/// </summary>
public bool TryUpgrade ( string IPASourcePathOnPC )
{
DateTime StartTime = DateTime . Now ;
ReconnectWithInstallProxy ( ) ;
IntPtr LiveConnection = IntPtr . Zero ;
IntPtr ClientOptions = IntPtr . Zero ;
TypedPtr < CFURL > UrlPath = MobileDevice . CreateFileUrlHelper ( IPASourcePathOnPC , false ) ;
2014-12-11 16:20:07 -05:00
// string UrlPathAsString = MobileDevice.GetStringForUrl(UrlPath);
2014-03-14 14:13:41 -04:00
2014-12-11 16:20:07 -05:00
int Result = MobileDevice . DeviceImpl . SecureUpgradeApplication ( LiveConnection , iPhoneHandle , UrlPath , ClientOptions , InstallProgressCallback , IntPtr . Zero ) ;
2014-03-14 14:13:41 -04:00
if ( Result = = 0 )
{
Console . WriteLine ( "Install \\ Update of \"{0}\" finished in {2:0.00} seconds" , Path . GetFileName ( IPASourcePathOnPC ) , Result , ( DateTime . Now - StartTime ) . TotalSeconds ) ;
}
else
{
Console . WriteLine ( "Install \\ Update of \"{0}\" failed with {1} in {2:0.00} seconds" , Path . GetFileName ( IPASourcePathOnPC ) , MobileDevice . GetErrorString ( Result ) , ( DateTime . Now - StartTime ) . TotalSeconds ) ;
}
return Result = = 0 ;
}
public bool TryUninstall ( string ApplicationIdentifier )
{
DateTime StartTime = DateTime . Now ;
ReconnectWithInstallProxy ( ) ;
TypedPtr < CFString > CF_ApplicationIdentifier = MobileDevice . CFStringMakeConstantString ( ApplicationIdentifier ) ;
IntPtr CF_ClientOptions = IntPtr . Zero ;
2014-12-11 16:20:07 -05:00
// IntPtr ExtraKey = IntPtr.Zero;
// IntPtr ExtraValue = IntPtr.Zero;
2014-03-14 14:13:41 -04:00
IntPtr ConnectionHandle = IntPtr . Zero ;
2014-12-11 16:20:07 -05:00
int Result = MobileDevice . DeviceImpl . SecureUninstallApplication ( ConnectionHandle , iPhoneHandle , CF_ApplicationIdentifier , CF_ClientOptions , UninstallProgressCallback , IntPtr . Zero ) ;
2014-03-14 14:13:41 -04:00
if ( Result = = 0 )
{
Console . WriteLine ( "Uninstall of \"{0}\" completed successfully in {2:0.00} seconds" , ApplicationIdentifier , Result , ( DateTime . Now - StartTime ) . TotalSeconds ) ;
}
else
{
Console . WriteLine ( "Uninstall of \"{0}\" failed with {1} in {2:0.00} seconds" , ApplicationIdentifier , MobileDevice . GetErrorString ( Result ) , ( DateTime . Now - StartTime ) . TotalSeconds ) ;
}
return Result = = 0 ;
}
/// <summary>
/// Copies a file to the PublicStaging directory on the mobile device, preserving the filename
/// </summary>
/// <param name="SourceFile"></param>
public void CopyFileToPublicStaging ( string SourceFile )
{
string IpaFilename = Path . GetFileName ( SourceFile ) ;
2015-03-12 15:27:35 -04:00
CopyFileToPhone ( SourceFile , "PublicStaging/" + IpaFilename ) ;
2014-03-14 14:13:41 -04:00
//Dictionary<string, string> fi = GetFileInfo("/PublicStaging/" + IpaFilename);
}
#region Private Methods
public bool ConnectToPhone ( )
{
SetLoggingLevel ( 7 ) ;
// Make sure we can connect to the phone and that it has been paired with this machine previously
2014-12-11 16:20:07 -05:00
if ( MobileDevice . DeviceImpl . Connect ( iPhoneHandle ) ! = 0 )
2014-03-14 14:13:41 -04:00
{
2015-03-24 16:31:05 -04:00
Console . WriteLine ( "Connect: Failed to Connect" ) ;
2014-03-14 14:13:41 -04:00
return false ;
}
2014-12-11 16:20:07 -05:00
if ( MobileDevice . DeviceImpl . IsPaired ( iPhoneHandle ) ! = 1 )
2014-03-14 14:13:41 -04:00
{
2015-03-24 16:31:05 -04:00
Console . WriteLine ( "Connect: Device is not paired" ) ;
2014-03-14 14:13:41 -04:00
return false ;
}
2014-12-11 16:20:07 -05:00
if ( MobileDevice . DeviceImpl . ValidatePairing ( iPhoneHandle ) ! = 0 )
2014-03-14 14:13:41 -04:00
{
2015-03-24 16:31:05 -04:00
Console . WriteLine ( "Connect: Pairing not valid" ) ;
2014-03-14 14:13:41 -04:00
return false ;
}
// Start a session
2014-12-11 16:20:07 -05:00
if ( MobileDevice . DeviceImpl . StartSession ( iPhoneHandle ) = = 1 )
2014-03-14 14:13:41 -04:00
{
2015-03-24 16:31:05 -04:00
Console . WriteLine ( "Connect: Couldn't start session" ) ;
2014-03-14 14:13:41 -04:00
return false ;
}
2014-12-11 16:20:07 -05:00
if ( MobileDevice . DeviceImpl . StartService ( iPhoneHandle , MobileDevice . CFStringMakeConstantString ( "com.apple.afc" ) , ref hService , IntPtr . Zero ) ! = 0 )
2014-03-14 14:13:41 -04:00
{
2015-03-24 16:31:05 -04:00
Console . WriteLine ( "Connect: Couldn't Start AFC service" ) ;
2014-03-14 14:13:41 -04:00
return false ;
}
// Open a file sharing connection
2014-12-11 16:20:07 -05:00
if ( MobileDevice . DeviceImpl . ConnectionOpen ( hService , 0 , out AFCCommsHandle ) ! = 0 )
2014-03-14 14:13:41 -04:00
{
2015-03-24 16:31:05 -04:00
Console . WriteLine ( "Connect: Couldn't Open File Sharing Connection" ) ;
2014-03-14 14:13:41 -04:00
return false ;
}
connected = true ;
return true ;
}
public bool ConnectToBundle ( string BundleName )
{
Reconnect ( ) ;
TypedPtr < CFString > CFBundleName = MobileDevice . CFStringMakeConstantString ( BundleName ) ;
// Open the bundle
2014-12-11 16:20:07 -05:00
int Result = MobileDevice . DeviceImpl . StartHouseArrestService ( iPhoneHandle , CFBundleName , IntPtr . Zero , ref hService , 0 ) ;
2014-03-14 14:13:41 -04:00
if ( Result ! = 0 )
{
Console . WriteLine ( "Failed to connect to bundle '{0}' with {1}" , BundleName , MobileDevice . GetErrorString ( Result ) ) ;
return false ;
}
// Open a file sharing connection
2014-12-11 16:20:07 -05:00
if ( MobileDevice . DeviceImpl . ConnectionOpen ( hService , 0 , out AFCCommsHandle ) ! = 0 )
2014-03-14 14:13:41 -04:00
{
return false ;
}
return true ;
}
private void DfuConnectCallback ( ref AMRecoveryDevice callback )
{
OnDfuConnect ( new DeviceNotificationEventArgs ( callback ) ) ;
}
private void DfuDisconnectCallback ( ref AMRecoveryDevice callback )
{
OnDfuDisconnect ( new DeviceNotificationEventArgs ( callback ) ) ;
}
private void RecoveryConnectCallback ( ref AMRecoveryDevice callback )
{
OnRecoveryModeEnter ( new DeviceNotificationEventArgs ( callback ) ) ;
}
private void RecoveryDisconnectCallback ( ref AMRecoveryDevice callback )
{
OnRecoveryModeLeave ( new DeviceNotificationEventArgs ( callback ) ) ;
}
private void InternalDeleteDirectory ( string path )
{
string full_path = FullPath ( CurrentDirectory , path ) ;
string [ ] contents = GetFiles ( path ) ;
for ( int i = 0 ; i < contents . Length ; i + + )
{
DeleteFile ( full_path + "/" + contents [ i ] ) ;
}
contents = GetDirectories ( path ) ;
for ( int i = 0 ; i < contents . Length ; i + + )
{
InternalDeleteDirectory ( full_path + "/" + contents [ i ] ) ;
}
DeleteDirectory ( path ) ;
}
static char [ ] path_separators = { '/' } ;
internal string FullPath ( string path1 , string path2 )
{
if ( ( path1 = = null ) | | ( path1 = = String . Empty ) )
{
path1 = "/" ;
}
if ( ( path2 = = null ) | | ( path2 = = String . Empty ) )
{
path2 = "/" ;
}
string [ ] path_parts ;
if ( path2 [ 0 ] = = '/' )
{
path_parts = path2 . Split ( path_separators ) ;
}
else if ( path1 [ 0 ] = = '/' )
{
path_parts = ( path1 + "/" + path2 ) . Split ( path_separators ) ;
}
else
{
path_parts = ( "/" + path1 + "/" + path2 ) . Split ( path_separators ) ;
}
string [ ] result_parts = new string [ path_parts . Length ] ;
int target_index = 0 ;
for ( int i = 0 ; i < path_parts . Length ; i + + )
{
if ( path_parts [ i ] = = ".." )
{
if ( target_index > 0 )
{
target_index - - ;
}
}
else if ( ( path_parts [ i ] = = "." ) | | ( path_parts [ i ] = = "" ) )
{
// Do nothing
}
else
{
result_parts [ target_index + + ] = path_parts [ i ] ;
}
}
return "/" + String . Join ( "/" , result_parts , 0 , target_index ) ;
}
#endregion // Private Methods
}
}