UNPKG

mobile-cli-lib

Version:
468 lines (467 loc) 20.5 kB
"use strict"; var __extends = (this && this.__extends) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; var ref = require("ref"); var path = require("path"); var iOSCore = require("./ios-core"); var helpers = require("../../../helpers"); var plistlib = require("plistlib"); var Future = require("fibers/future"); var fiberBootstrap = require("../../../fiber-bootstrap"); var MobileServices = (function () { function MobileServices() { } MobileServices.APPLE_FILE_CONNECTION = "com.apple.afc"; MobileServices.INSTALLATION_PROXY = "com.apple.mobile.installation_proxy"; MobileServices.HOUSE_ARREST = "com.apple.mobile.house_arrest"; MobileServices.NOTIFICATION_PROXY = "com.apple.mobile.notification_proxy"; MobileServices.SYSLOG = "com.apple.syslog_relay"; MobileServices.MOBILE_IMAGE_MOUNTER = "com.apple.mobile.mobile_image_mounter"; MobileServices.DEBUG_SERVER = "com.apple.debugserver"; MobileServices.NO_WIFI_SYNC_ERROR_CODE = 3892314239; return MobileServices; }()); exports.MobileServices = MobileServices; var AfcBase = (function () { function AfcBase($logger) { this.$logger = $logger; } AfcBase.prototype.tryExecuteAfcAction = function (action) { var result; for (var currentTry = 0; currentTry < AfcBase.NUMBER_OF_RETRIES && result !== 0; currentTry++) { try { result = action(); } catch (err) { this.$logger.trace("Error #" + currentTry + " while trying to execute action. Error is: ", err); } } return result; }; AfcBase.NUMBER_OF_RETRIES = 5; return AfcBase; }()); exports.AfcBase = AfcBase; var AfcFile = (function (_super) { __extends(AfcFile, _super); function AfcFile(path, mode, afcConnection, $mobileDevice, $errors, $logger) { var _this = this; _super.call(this, $logger); this.afcConnection = afcConnection; this.$mobileDevice = $mobileDevice; this.$errors = $errors; this.$logger = $logger; this.open = false; var modeValue = 0; if (mode.indexOf("r") > -1) { modeValue = 0x1; } if (mode.indexOf("w") > -1) { modeValue = 0x2; } var afcFileRef = ref.alloc(ref.types.uint64); this.open = false; var result = this.tryExecuteAfcAction(function () { return _this.$mobileDevice.afcFileRefOpen(_this.afcConnection, path, modeValue, afcFileRef); }); if (result !== 0) { this.$errors.fail("Unable to open file reference: '%s' with path '%s", result, path); } this.afcFile = ref.deref(afcFileRef); if (this.afcFile === 0) { this.$errors.fail("Invalid file reference"); } this.open = true; } AfcFile.prototype.read = function (len) { var _this = this; var readLengthRef = ref.alloc(iOSCore.CoreTypes.uintType, len); var data = new Buffer(len * iOSCore.CoreTypes.pointerSize); var result = this.tryExecuteAfcAction(function () { data = new Buffer(len * iOSCore.CoreTypes.pointerSize); _this.$mobileDevice.afcFileRefRead(_this.afcConnection, _this.afcFile, data, readLengthRef); }); if (result !== 0) { this.$errors.fail("Unable to read data from file '%s'. Result is: '%s'", this.afcFile, result); } var readLength = readLengthRef.deref(); return data.slice(0, readLength); }; AfcFile.prototype.write = function (buffer, byteLength) { var _this = this; var result = this.tryExecuteAfcAction(function () { return _this.$mobileDevice.afcFileRefWrite(_this.afcConnection, _this.afcFile, buffer, byteLength); }); if (result !== 0) { this.$errors.fail("Unable to write to file: '%s'. Result is: '%s'", this.afcFile, result); } return true; }; AfcFile.prototype.close = function () { var _this = this; if (this.open) { var result = this.tryExecuteAfcAction(function () { return _this.$mobileDevice.afcFileRefClose(_this.afcConnection, _this.afcFile); }); if (result !== 0) { this.$errors.fail("Unable to close afc file connection: '%s'. Result is: '%s'", this.afcFile, result); } this.open = false; } }; Object.defineProperty(AfcFile.prototype, "writable", { get: function () { return true; }, enumerable: true, configurable: true }); return AfcFile; }(AfcBase)); exports.AfcFile = AfcFile; var AfcClient = (function (_super) { __extends(AfcClient, _super); function AfcClient(service, $mobileDevice, $coreFoundation, $fs, $errors, $injector, $logger) { _super.call(this, $logger); this.service = service; this.$mobileDevice = $mobileDevice; this.$coreFoundation = $coreFoundation; this.$fs = $fs; this.$errors = $errors; this.$injector = $injector; this.$logger = $logger; this.afcConnection = null; var afcConnection = ref.alloc(ref.refType(ref.types.void)); var result = $mobileDevice.afcConnectionOpen(this.service, 0, afcConnection); if (result !== 0) { $errors.fail("Unable to open apple file connection: %s", result); } this.afcConnection = ref.deref(afcConnection); } AfcClient.prototype.open = function (path, mode) { return this.$injector.resolve(AfcFile, { path: path, mode: mode, afcConnection: this.afcConnection }); }; AfcClient.prototype.mkdir = function (path) { var _this = this; var result = this.tryExecuteAfcAction(function () { return _this.$mobileDevice.afcDirectoryCreate(_this.afcConnection, path); }); if (result !== 0) { this.$errors.fail("Unable to make directory: " + path + ". Result is " + result + "."); } }; AfcClient.prototype.listDir = function (path) { var _this = this; var afcDirectoryRef = ref.alloc(ref.refType(ref.types.void)); var result = this.tryExecuteAfcAction(function () { return _this.$mobileDevice.afcDirectoryOpen(_this.afcConnection, path, afcDirectoryRef); }); if (result !== 0) { this.$errors.fail("Unable to open AFC directory: '%s' %s ", path, result); } var afcDirectoryValue = ref.deref(afcDirectoryRef); var name = ref.alloc(ref.refType(ref.types.char)); var entries = []; while (this.$mobileDevice.afcDirectoryRead(this.afcConnection, afcDirectoryValue, name) === 0) { var value = ref.deref(name); if (ref.address(value) === 0) { break; } var filePath = ref.readCString(value, 0); if (filePath !== "." && filePath !== "..") { entries.push(filePath); } } this.$mobileDevice.afcDirectoryClose(this.afcConnection, afcDirectoryValue); return entries; }; AfcClient.prototype.close = function () { var _this = this; var result = this.tryExecuteAfcAction(function () { return _this.$mobileDevice.afcConnectionClose(_this.afcConnection); }); if (result !== 0) { this.$errors.failWithoutHelp("Unable to close apple file connection: " + result); } }; AfcClient.prototype.transferPackage = function (localFilePath, devicePath) { var _this = this; return (function () { _this.transfer(localFilePath, devicePath).wait(); }).future()(); }; AfcClient.prototype.deleteFile = function (devicePath) { var removeResult = this.$mobileDevice.afcRemovePath(this.afcConnection, devicePath); this.$logger.trace("Removing device file '%s', result: %s", devicePath, removeResult.toString()); }; AfcClient.prototype.transfer = function (localFilePath, devicePath) { var _this = this; return (function () { var future = new Future(); try { _this.ensureDevicePathExist(path.dirname(devicePath)); var reader = _this.$fs.createReadStream(localFilePath, { bufferSize: 1024 * 1024 * 15, highWaterMark: 1024 * 1024 * 15 }); devicePath = helpers.fromWindowsRelativePathToUnix(devicePath); _this.deleteFile(devicePath); var target_1 = _this.open(devicePath, "w"); var localFilePathSize_1 = _this.$fs.getFileSize(localFilePath).wait(), futureThrow_1 = function (err) { if (!future.isResolved()) { future.throw(err); } }; reader.on("data", function (data) { try { target_1.write(data, data.length); _this.$logger.trace("transfer-> localFilePath: '%s', devicePath: '%s', localFilePathSize: '%s', transferred bytes: '%s'", localFilePath, devicePath, localFilePathSize_1.toString(), data.length.toString()); } catch (err) { if (err.message.indexOf("Result is: '21'") !== -1) { _this.$logger.warn(err.message); } else { futureThrow_1(err); } } }); reader.on("error", function (error) { futureThrow_1(error); }); reader.on("end", function () { return target_1.close(); }); reader.on("close", function () { if (!future.isResolved()) { future.return(); } }); } catch (err) { _this.$logger.trace("Error while transferring files. Error is: ", err); if (!future.isResolved()) { future.throw(err); } } future.wait(); }).future()(); }; AfcClient.prototype.ensureDevicePathExist = function (deviceDirPath) { var _this = this; var filePathParts = deviceDirPath.split(path.sep); var currentDevicePath = ""; filePathParts.forEach(function (filePathPart) { if (filePathPart !== "") { currentDevicePath = helpers.fromWindowsRelativePathToUnix(path.join(currentDevicePath, filePathPart)); _this.mkdir(currentDevicePath); } }); }; return AfcClient; }(AfcBase)); exports.AfcClient = AfcClient; var InstallationProxyClient = (function () { function InstallationProxyClient(device, $logger, $injector, $errors) { this.device = device; this.$logger = $logger; this.$injector = $injector; this.$errors = $errors; this.plistService = null; } InstallationProxyClient.prototype.deployApplication = function (packageFile) { var _this = this; return (function () { var service = _this.device.startService(MobileServices.APPLE_FILE_CONNECTION); var afcClient = _this.$injector.resolve(AfcClient, { service: service }); var devicePath = path.join("PublicStaging", path.basename(packageFile)); afcClient.transferPackage(packageFile, devicePath).wait(); _this.plistService = _this.getPlistService(); _this.plistService.sendMessage({ Command: "Install", PackagePath: helpers.fromWindowsRelativePathToUnix(devicePath) }); _this.plistService.receiveMessage().wait(); }).future()(); }; InstallationProxyClient.prototype.sendMessage = function (message) { var _this = this; return (function () { _this.plistService = _this.getPlistService(); _this.plistService.sendMessage(message); var response = _this.plistService.receiveMessage().wait(); if (response.Error) { _this.$errors.failWithoutHelp(response.Error); } return response; }).future()(); }; InstallationProxyClient.prototype.closeSocket = function () { if (this.plistService) { return this.plistService.close(); } }; InstallationProxyClient.prototype.getPlistService = function () { var service = this.getInstallationService(); return this.$injector.resolve(iOSCore.PlistService, { service: service, format: iOSCore.CoreTypes.kCFPropertyListBinaryFormat_v1_0 }); }; InstallationProxyClient.prototype.getInstallationService = function () { var service; try { service = this.device.startService(MobileServices.INSTALLATION_PROXY); } catch (err) { if (err.code === MobileServices.NO_WIFI_SYNC_ERROR_CODE) { this.$logger.trace("Unable to start " + MobileServices.INSTALLATION_PROXY + ". Looks like the problem is with WIFI sync: " + err.message); this.$logger.printMarkdown("Unable to start installation service. Looks like `Sync over Wi-Fi` option in iTunes is enabled. " + "Try disabling it, reconnect the device and execute your command again."); } this.$errors.failWithoutHelp(err); } return service; }; return InstallationProxyClient; }()); exports.InstallationProxyClient = InstallationProxyClient; $injector.register("installationProxyClient", InstallationProxyClient); var NotificationProxyClient = (function () { function NotificationProxyClient(device, $injector) { this.device = device; this.$injector = $injector; this.plistService = null; this.observers = {}; this.buffer = ""; } NotificationProxyClient.prototype.postNotification = function (notificationName) { this.plistService = this.$injector.resolve(iOSCore.PlistService, { service: this.device.startService(MobileServices.NOTIFICATION_PROXY), format: iOSCore.CoreTypes.kCFPropertyListBinaryFormat_v1_0 }); this.postNotificationCore(notificationName); }; NotificationProxyClient.prototype.postNotificationAndAttachForData = function (notificationName) { this.openSocket(); this.postNotificationCore(notificationName); }; NotificationProxyClient.prototype.addObserver = function (name, callback) { this.openSocket(); var result = this.plistService.sendMessage({ "Command": "ObserveNotification", "Name": name }); var array = this.observers[name]; if (!array) { array = new Array(); this.observers[name] = array; } array.push(callback); return result; }; NotificationProxyClient.prototype.removeObserver = function (name, callback) { var array = this.observers[name]; if (array) { var index = array.indexOf(callback); if (index !== -1) { array.splice(index, 1); } } }; NotificationProxyClient.prototype.openSocket = function () { if (!this.plistService) { this.plistService = this.$injector.resolve(iOSCore.PlistService, { service: this.device.startService(MobileServices.NOTIFICATION_PROXY), format: iOSCore.CoreTypes.kCFPropertyListBinaryFormat_v1_0 }); if (this.plistService.receiveAll) { this.plistService.receiveAll(this.handleData.bind(this)); } } }; NotificationProxyClient.prototype.handleData = function (data) { var _this = this; this.buffer += data.toString(); var PLIST_HEAD = "<plist"; var PLIST_TAIL = "</plist>"; var start = this.buffer.indexOf(PLIST_HEAD); var end = this.buffer.indexOf(PLIST_TAIL); while (start >= 0 && end >= 0) { var plist = this.buffer.substr(start, end + PLIST_TAIL.length); this.buffer = this.buffer.substr(end + PLIST_TAIL.length); plistlib.loadString(plist, function (err, plist) { if (!err && plist) { _this.handlePlistNotification(plist); } }); start = this.buffer.indexOf("<plist"); end = this.buffer.indexOf("</plist>"); } }; NotificationProxyClient.prototype.postNotificationCore = function (notificationName) { this.plistService.sendMessage({ "Command": "PostNotification", "Name": notificationName, "ClientOptions": "" }); }; NotificationProxyClient.prototype.closeSocket = function () { this.plistService.close(); }; NotificationProxyClient.prototype.handlePlistNotification = function (plist) { if (plist.type !== "dict") { return; } var value = plist.value; if (!value) { return; } var command = value["Command"]; var name = value["Name"]; if (command.type !== "string" || command.value !== "RelayNotification" || name.type !== "string") { return; } var notification = name.value; var observers = this.observers[notification]; if (!observers) { return; } observers.forEach(function (observer) { return observer(notification); }); }; return NotificationProxyClient; }()); exports.NotificationProxyClient = NotificationProxyClient; var HouseArrestClient = (function () { function HouseArrestClient(device, $injector, $errors) { this.device = device; this.$injector = $injector; this.$errors = $errors; this.plistService = null; } HouseArrestClient.prototype.getAfcClientCore = function (command, applicationIdentifier) { var service = this.device.startService(MobileServices.HOUSE_ARREST); this.plistService = this.$injector.resolve(iOSCore.PlistService, { service: service, format: iOSCore.CoreTypes.kCFPropertyListXMLFormat_v1_0 }); this.plistService.sendMessage({ "Command": command, "Identifier": applicationIdentifier }); var response = this.plistService.receiveMessage().wait(); if (response.Error) { this.$errors.failWithoutHelp(HouseArrestClient.PREDEFINED_ERRORS[response.Error] || response.Error); } return this.$injector.resolve(AfcClient, { service: service }); }; HouseArrestClient.prototype.getAfcClientForAppContainer = function (applicationIdentifier) { return this.getAfcClientCore("VendContainer", applicationIdentifier); }; HouseArrestClient.prototype.getAfcClientForAppDocuments = function (applicationIdentifier) { return this.getAfcClientCore("VendDocuments", applicationIdentifier); }; HouseArrestClient.prototype.closeSocket = function () { this.plistService.close(); }; HouseArrestClient.PREDEFINED_ERRORS = { ApplicationLookupFailed: "Unable to find the application on a connected device. Ensure that the application is installed and try again." }; return HouseArrestClient; }()); exports.HouseArrestClient = HouseArrestClient; var IOSSyslog = (function () { function IOSSyslog(device, $logger, $injector, $deviceLogProvider, $devicePlatformsConstants) { this.device = device; this.$logger = $logger; this.$injector = $injector; this.$deviceLogProvider = $deviceLogProvider; this.$devicePlatformsConstants = $devicePlatformsConstants; this.plistService = this.$injector.resolve(iOSCore.PlistService, { service: this.device.startService(MobileServices.SYSLOG), format: undefined }); } IOSSyslog.prototype.read = function () { var _this = this; var printData = function (data) { fiberBootstrap.run(function () { return _this.$deviceLogProvider.logData(data, _this.$devicePlatformsConstants.iOS, _this.device.deviceInfo.identifier); }); }; this.plistService.readSystemLog(printData); }; return IOSSyslog; }()); exports.IOSSyslog = IOSSyslog;