Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

First public commit

  • Loading branch information...
commit a818e8132ad10a1799f691e78c0e359e2238fda8 0 parents
Nicholas Penree authored
69 README.markdown
@@ -0,0 +1,69 @@
+
+# Susumi - Find My Phone support for node.js
+
+Find My Phone support for Nodejs. This is essentially a port of Tyler Hall's [PHP Sosumi Class](http://github.com/tylerhall/sosumi).
+
+## Installation
+
+ Install the [npm package manager for nodejs](http://github.com/isaacs/npm)
+ and run:
+
+ $ npm install sosumi
+
+## Examples
+
+Event handling is optional
+
+ var sys = require('sys'),
+ Sosumi = require('sosumi'),
+ fmiService = new Sosumi('stevejobs', 'jizzmodo');
+
+// by default, devices are fetched when you create a new Sosumi object, a 'devices'
+// event is emitted on successful fetch. At this point, fmiService.devices is populated
+// as well.
+fmiService.on('devices', function (devices) {
+ sys.puts('Got our devices: ' + sys.inspect(devices));
+
+ if (devices != null) {
+ // Blast a message out to all your registered devices
+ for (var deviceIndex = 0; deviceIndex < devices.length; deviceIndex++) {
+ fmiService.sendMessage('This is a test of the emergency broadcast system.', false, deviceIndex, 'Important Message');
+
+ // We can grab an event when a specific message has been sent
+ fmiService.on('messageSent', function (content) {
+ sys.puts('Sent the message: ' + sys.inspect(content));
+ });
+ }
+ }
+});
+
+// if something goes wrong, an error event is emitted with a description of the problem.
+fmiService.on('error', function(err) {
+ throw err;
+});
+
+
+## License
+
+(The MIT License)
+
+Copyright (c) 2010 Nicholas Penree <drudge@conceited.net>
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
35 examples/broadcast.js
@@ -0,0 +1,35 @@
+//
+// broadcast.js
+// node-sosumi
+//
+// Created by Nicholas Penree on 8/21/10.
+// Copyright 2010 Conceited Software. All rights reserved.
+//
+
+var sys = require('sys'),
+ Sosumi = require('sosumi'),
+ fmiService = new Sosumi('stevejobs', 'jizzmodo');
+
+// by default, devices are fetched when you create a new Sosumi object, a 'devices'
+// event is emitted on successful fetch. At this point, fmiService.devices is populated
+// as well.
+fmiService.on('devices', function (devices) {
+ sys.puts('Got our devices: ' + sys.inspect(devices));
+
+ if (devices != null) {
+ // Blast a message out to all your registered devices
+ for (var deviceIndex = 0; deviceIndex < devices.length; deviceIndex++) {
+ fmiService.sendMessage('This is a test of the emergency broadcast system.', false, deviceIndex, 'Important Message');
+
+ // We can grab an event when a specific message has been sent
+ fmiService.on('messageSent', function (content) {
+ sys.puts('Sent the message: ' + sys.inspect(content));
+ });
+ }
+ }
+});
+
+// if something goes wrong, an error event is emitted with a description of the problem.
+fmiService.on('error', function(err) {
+ throw err;
+});
37 examples/deviceids.js
@@ -0,0 +1,37 @@
+//
+// deviceids.js
+// node-sosumi
+//
+// Created by Nicholas Penree on 8/21/10.
+// Copyright 2010 Conceited Software. All rights reserved.
+//
+
+var sys = require('sys'),
+ Sosumi = require('sosumi'),
+ fmiService = new Sosumi('stevejobs', 'jizzmodo');
+
+// by default, devices are fetched when you create a new Sosumi object, a 'devices'
+// event is emitted on successful fetch. At this point, fmiService.devices is populated
+// as well.
+fmiService.on('devices', function (devices) {
+ sys.puts('======================================================================');
+ sys.puts('=== Listing Device IDs for MobileMe account: ' + fmiService.username);
+ sys.puts('======================================================================');
+
+ if (devices != null) {
+ // Let's loop through and print our device ids, this is useful to save ids
+ // for later use, without relying on that extra api call.
+ for (var deviceIndex = 0; deviceIndex < devices.length; deviceIndex++) {
+ sys.puts(' ' + devices[deviceIndex].name + ': ');
+ sys.puts(' ' + devices[deviceIndex].id);
+ }
+ }
+
+ sys.puts('======================================================================');
+ sys.puts('Listed '+ devices.length + ' id' + (devices.length == 1)? '' : 's' );
+});
+
+// if something goes wrong, an error event is emitted with a description of the problem.
+fmiService.on('error', function(err) {
+ throw err;
+});
26 examples/notify.js
@@ -0,0 +1,26 @@
+//
+// notify.js
+// node-sosumi
+//
+// Created by Nicholas Penree on 8/21/10.
+// Copyright 2010 Conceited Software. All rights reserved.
+//
+
+var Sosumi = require('sosumi'),
+var exports.notify = notify = function(subject, message, critical)
+{
+ // pass a third parameter of false so it doesn't fetch the device list if we don't need it
+ sosumi = new Sosumi('stevejobs', 'jizzmodo', false);
+ sosumi.devices = [{ id: 'OA#$3jasdsdkasdkasdasdsaDASDQ@DASDSDS~', name: 'Steve\'s iPhone 4' }];
+
+ // default to no audible alerts
+ if (typeof critical === 'undefined') {
+ critical = false
+ }
+
+ // send off the message. no need to check the response unless you really care if it made it
+ sosumi.sendMessage(message, critical, 0, subject);
+}
+
+// test it out
+notify('Gizmodo Leak Detected', 'All sensors reporting disturbance in the force.', true);
211 lib/sosumi.js
@@ -0,0 +1,211 @@
+//
+// sosumi.js
+// node-sosumi
+//
+// Created by Nicholas Penree on 8/21/10.
+// Copyright 2010 Conceited Software. All rights reserved.
+//
+
+var sys = require('sys'),
+ events = require('events'),
+ http = require('http'),
+ Buffer = require('buffer').Buffer,
+ Sosumi = exports = module.exports =
+
+function Sosumi(mobileMeUsername, mobileMeUserPassword, performUpdateNow)
+{
+ var self = this;
+ events.EventEmitter.call(this);
+ this.devices = [];
+ this.username = mobileMeUsername;
+ this.password = mobileMeUserPassword;
+
+ if (typeof performUpdateNow === 'undefined' || performUpdateNow === true) {
+ sys.puts('doing update');
+ self.updateDevices();
+ }
+}
+
+sys.inherits(Sosumi, events.EventEmitter);
+
+Sosumi.prototype.locate = function(deviceIndex, timeout)
+{
+ var self = this;
+
+ self.updateDevices();
+
+ self.on('devices', function (devices) {
+ var seekedDevice = self.devices[deviceIndex];
+
+ if (seekedDevice.locationFinished) {
+ self.emit('located', {
+ latitude: seekedDevice.location.latitude,
+ longitude: seekedDevice.location.longitude,
+ accuracy: seekedDevice.location.horizontalAccuracy,
+ timestamp: seekedDevice.location.timeStamp,
+ type: seekedDevice.location.positionType
+ });
+ } else {
+ setTimeout(self.locate(deviceIndex, 10000));
+ }
+ });
+}
+
+Sosumi.prototype.sendMessage = function(msg, alarm, deviceIndex, subject)
+{
+ if (deviceIndex >= this.devices.length) return;
+
+ var self = this,
+ body = JSON.stringify({
+ "clientContext" : {
+ "appName" : "FindMyiPhone",
+ "appVersion" : "1.0",
+ "buildVersion" : "57",
+ "deviceUDID" : "0000000000000000000000000000000000000000",
+ "inactiveTime" : 5911,
+ "osVersion" : "3.2",
+ "productType" : "iPad1,1",
+ "selectedDevice" : this.devices[deviceIndex].id,
+ "shouldLocate":false
+ },
+ "device" : this.devices[deviceIndex].id,
+ "serverContext" : {
+ "callbackIntervalInMS": 3000,
+ "clientId" : "0000000000000000000000000000000000000000",
+ "deviceLoadStatus" : "203",
+ "hasDevices" : true,
+ "lastSessionExtensionTime" : null,
+ "maxDeviceLoadTime" : 60000,
+ "maxLocatingTime" : 90000,
+ "preferredLanguage" : "en",
+ "prefsUpdateTime" : 1276872996660,
+ "sessionLifespan" : 900000,
+ "timezone" : {
+ "currentOffset" : -25200000,
+ "previousOffset" : -28800000,
+ "previousTransition" : 1268560799999,
+ "tzCurrentName" : "Pacific Daylight Time",
+ "tzName" : "America/Los_Angeles"
+ },
+ "validRegion" : true
+ },
+ "sound" : (alarm) ? 'true' : 'false',
+ "subject" : subject,
+ "text" : msg
+ });
+
+ this.postAPICall('/fmipservice/device/' + this.username + '/sendMessage', body, function (content) {
+ self.emit('messageSent', { subject: subject, message: msg, alarm: alarm, deviceIndex: deviceIndex });
+ });
+}
+
+Sosumi.prototype.remoteLock = function(passcode, deviceIndex)
+{
+ if (deviceIndex >= this.devices.length) return;
+
+ var self = this,
+ body = JSON.stringify({
+ "clientContext" : {
+ "appName" : "FindMyiPhone",
+ "appVersion" : "1.0",
+ "buildVersion" : "57",
+ "deviceUDID" : "0000000000000000000000000000000000000000",
+ "inactiveTime" : 5911,
+ "osVersion" : "3.2",
+ "productType" : "iPad1,1",
+ "selectedDevice" : this.devices[deviceIndex].id,
+ "shouldLocate":false
+ },
+ "device" : this.devices[deviceIndex].id,
+ "serverContext" : {
+ "callbackIntervalInMS": 3000,
+ "clientId" : "0000000000000000000000000000000000000000",
+ "deviceLoadStatus" : "203",
+ "hasDevices" : true,
+ "lastSessionExtensionTime" : null,
+ "maxDeviceLoadTime" : 60000,
+ "maxLocatingTime" : 90000,
+ "preferredLanguage" : "en",
+ "prefsUpdateTime" : 1276872996660,
+ "sessionLifespan" : 900000,
+ "timezone" : {
+ "currentOffset" : -25200000,
+ "previousOffset" : -28800000,
+ "previousTransition" : 1268560799999,
+ "tzCurrentName" : "Pacific Daylight Time",
+ "tzName" : "America/Los_Angeles"
+ },
+ "validRegion" : true
+ },
+ "oldPasscode" : "",
+ "passcode" : passcode
+ });
+
+ this.postAPICall('/fmipservice/device/' + this.username + '/remoteLock', body, function (content) {
+ self.emit('locked', { passcode: passcode, deviceIndex: deviceIndex });
+ });
+}
+
+Sosumi.prototype.postAPICall = function(url, body, callback)
+{
+ var self = this;
+ var headers = {
+ 'Host' : 'fmipmobile.me.com',
+ 'Authorization' : 'Basic ' + new Buffer(this.username + ':' + this.password, 'utf8').toString('base64'),
+ 'X-Apple-Realm-Support' : '1.0',
+ 'Content-Type' : 'application/json; charset=utf-8',
+ 'Content-Length' : body.length,
+ 'X-Client-Name' : 'Steve\'s iPad',
+ 'X-Client-Uuid' : '0cf3dc491ff812adb0b202baed4f94873b210853'
+ },
+ fmi = http.createClient(443, 'fmipmobile.me.com', true),
+ request = fmi.request('POST', url, headers);
+ request.end(body, 'utf8');
+
+ request.on('response', function (response) {
+ response.setEncoding('utf8');
+ var result = '';
+
+ if (response.statusCode == 200 ) {
+ response.on('data', function (chunk) {
+ sys.puts('got ' + chunk.length + ' bytes');
+ result = result + chunk;
+ });
+
+ response.on('end', function () {
+ var jsonData = JSON.parse(result);
+
+ if (typeof jsonData !== 'undefined' && jsonData.statusCode == '200') {
+ callback(jsonData.content);
+ } else {
+ callback(null);
+ }
+ });
+ } else if (response.statusCode == 401 || response.statusCode == 403) {
+ self.emit('error', new Error('Invalid credentials passed for MobileMe account: ' + self.username));
+ } else {
+ self.emit('error', new Error('HTTP Status returned non-successful: ' + response.statusCode));
+ }
+ });
+}
+
+Sosumi.prototype.updateDevices = function()
+{
+ var self = this,
+ body = JSON.stringify({
+ "clientContext" : {
+ "appName" : "FindMyiPhone",
+ "appVersion" : "1.0",
+ "buildVersion" : "57",
+ "deviceUDID" : "0cf3dc989ff812adb0b202baed4f37274b210853",
+ "inactiveTime" : 2147483647,
+ "osVersion" : "3.2",
+ "productType" : "iPad1,1"
+ }
+ });
+
+ this.postAPICall('/fmipservice/device/' + this.username + '/initClient', body, function (devices) {
+ self.devices = devices;
+ self.emit('devices', self.devices);
+ });
+}
24 package.json
@@ -0,0 +1,24 @@
+{ "name" : "sosumi"
+, "description" : "API for interacting with Find My iPhone Service in node"
+, "version" : "0.2.0"
+, "homepage" : "http://github/drudge/node-sosumi"
+, "author" : "Nicholas Penree <drudge@conceited.net> (http://penree.com)"
+, "contributors" :
+ [ "Tyler Hall <tylerhall@gmail.com> (http://github.com/tylerhall/sosumi)" ]
+, "repository" :
+ { "type" : "git"
+ , "url" : "http://github.com/drudge/node-sosumi.git"
+ }
+, "bugs" :
+ { "mail" : "drudge@conceited.net"
+ , "web" : "http://github.com/drudge/node-sosumi/issues"
+ }
+, "directories" : { "lib" : "./lib" }
+, "main" : "./lib/sosumi"
+, "engines" : { "node" : ">=0.1.103" }
+, "licenses" :
+ [ { "type" : "MIT"
+ , "url" : "http://github.com/drudge/node-sosumi/raw/master/LICENSE"
+ }
+ ]
+}
Please sign in to comment.
Something went wrong with that request. Please try again.