local mobileme = require "mobileme"
local os = require "os"
local stdnse = require "stdnse"
local tab = require "tab"

description = [[
Retrieves the locations of all "Find my iPhone" enabled iOS devices by querying
the MobileMe web service (authentication required).
]]

---
-- @usage
-- nmap -sn -Pn --script http-icloud-findmyiphone --script-args='username=<user>,password=<pass>'
-- 
-- @output
-- Pre-scan script results:
-- | http-icloud-findmyiphone: 
-- |   name                           location        accuracy  date               type
-- |   Patrik Karlsson's MacBook Air  -,-             -         -                  -
-- |   Patrik Karlsson's iPhone       40.690,-74.045  65        04/10/12 16:56:37  Wifi
-- |_  Mac mini                       40.690,-74.045  65        04/10/12 16:56:36  Wifi
--
-- @args http-icloud-findmyiphone.username the Apple Id username
-- @args http-icloud-findmyiphone.password the Apple Id password
--

author = "Patrik Karlsson"
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
categories = {"discovery", "safe", "external"}


local arg_username = stdnse.get_script_args(SCRIPT_NAME .. ".username")
local arg_password = stdnse.get_script_args(SCRIPT_NAME .. ".password")

prerule = function() return true end

-- decode basic UTF8 encoded strings
-- iOS devices are commonly named after the user eg:
--  * Patrik Karlsson's Macbook Air
--  * Patrik Karlsson's iPhone
--
-- This function decodes the single quote as a start and should really
-- be replaced with a proper UTF-8 decoder in the future
local function decodeString(str)
	return str:gsub("\226\128\153", "'")
end

local function fail(err) return ("\n  ERROR: %s"):format(err or "") end

action = function()

	if ( not(arg_username) or not(arg_password) ) then
		return fail("No username or password was supplied")
	end

	local mobileme = mobileme.Helper:new(arg_username, arg_password)
	local status, response = mobileme:getLocation()

	if ( not(status) ) then
		stdnse.print_debug(2, "%s: %s", SCRIPT_NAME, response)
		return fail("Failed to retrieve location information")
	end
	
	local output = tab.new(4)
	tab.addrow(output, "name", "location", "accuracy", "date", "type")
	for name, info in pairs(response) do
		local loc
		if ( info.latitude and info.longitude ) then
			loc = ("%.3f,%.3f"):format(
				tonumber(info.latitude) or "-",
				tonumber(info.longitude) or "-")
		else
			loc = "-,-"
		end
		local ts
		if ( info.timestamp and 1000 < info.timestamp ) then
			ts = os.date("%x %X", info.timestamp/1000)
		else
			ts = "-"
		end
		tab.addrow(output, decodeString(name), loc, info.accuracy or "-", ts, info.postype or "-")
	end
	
	if ( 1 < #output ) then
		return stdnse.format_output(true, tab.dump(output))
	end
end
