#!/usr/bin/env ruby
####################################
#      Rest Api Client Script 
#Generate and send requests to Social Stream presence API
#@author Aldo Gordillo < agordillos@gmail.com >
#@version 2.0 - 24-2-2012
####################################


require 'logger'
require 'rest_client'

path = "/var/log/ejabberd/scripts.log"
file = File.open(path, File::WRONLY | File::APPEND | File::CREAT)
file.sync = true
$logger = Logger.new(file)
$logger.level = Logger::DEBUG

def getOption(option)
  File.open('/etc/ejabberd/ssconfig.cfg', 'r') do |f1|  
    while line = f1.gets  
      line = line.gsub(/\n/,'')
      if line.match(/^#/)
        #Comments
      elsif line.match(/^#{option}/)
        return line.gsub(/#{option}/,'')
      end  
    end  
  end
  return "Undefined"
end

#Constants
$secure_rest_api = getOption("secure_rest_api=")
$pass = getOption("ejabberd_password=")
$scripts_path = getOption("scripts_path=")
$script_title = "Rest Api Client"


def log(title,text)
	$logger.info title + ": " + text
end

def getMethodName
  caller[0] =~ /`(.*?)'/
  $1
end


#####################
#####  Example  #####
#####################
#def myHook(param1,param2)
#    log(getMethodName,"(My message: #{param1},#{param2})")
#    url = "http://" + getWebDomainUrlFromDomain(domain) + "/xmpp/hookRoute"

#    params = {}
#    encrypted_params = {}
#    #Add params to sent in clear
#    params[:param1_in_server]=param1
#    #Add params to sent cipher
#    encrypted_params[:param2_in_server]=param2

#    return [getMethodName,generic_api_call(url,params,encrypted_params)]
#end

def setConnection(userJid)
    log($script_title,"#{getMethodName}(#{userJid})")
    url = "http://" + getWebDomainUrlFromDomain(getDomainFromJid(userJid)) + "/xmpp/setConnection"

    params = {}
    encrypted_params = {}
    encrypted_params[:name]=getUsernameFromJid(userJid)

    return [getMethodName,generic_api_call(url,params,encrypted_params)]
end


def unsetConnection(userJid)
    log($script_title,"#{getMethodName}(#{userJid})")
    url = "http://" +  getWebDomainUrlFromDomain(getDomainFromJid(userJid)) + "/xmpp/unsetConnection"

    params = {}
    encrypted_params = {}
    encrypted_params[:name]=getUsernameFromJid(userJid)

    return [getMethodName,generic_api_call(url,params,encrypted_params)]
end


def setPresence(userJid,status)
    log($script_title,"#{getMethodName}(#{userJid},#{status})")
    url = "http://" + getWebDomainUrlFromDomain(getDomainFromJid(userJid)) + "/xmpp/setPresence"

    params = {}
    encrypted_params = {}
    encrypted_params[:name]=getUsernameFromJid(userJid)
    encrypted_params[:status]=status

    return [getMethodName,generic_api_call(url,params,encrypted_params)]
end


def unsetPresence(userJid)
    log($script_title,"#{getMethodName}(#{userJid})")
    url = "http://" + getWebDomainUrlFromDomain(getDomainFromJid(userJid)) + "/xmpp/unsetPresence"

    params = {}
    encrypted_params = {}
    encrypted_params[:name]=getUsernameFromJid(userJid)

    return [getMethodName,generic_api_call(url,params,encrypted_params)]
end


#Reset all domains connections
def resetConnection()
    log($script_title,"Call #{getMethodName}()")
    url = "http://" + getWebDomainUrlFromDomain("all") + "/xmpp/resetConnection"

    params = {}
    encrypted_params = {}

    return [getMethodName,generic_api_call(url,params,encrypted_params)]
end


def synchronize(domain)
    log($script_title,"Call #{getMethodName}(#{domain})")
    url = "http://" + getWebDomainUrlFromDomain(domain) + "/xmpp/synchronizePresence"
    
    #Get connected users using Emanagement
    jids = []
    if domain=="all"
	command = $scripts_path + "/emanagement getConnectedJids"
    else
	command = $scripts_path + "/emanagement getConnectedJidsByDomain " + domain
    end
    output = %x[#{command}]
    sessions = output.split("\n")

    sessions.each do |session|
      jids << session.split("/")[0]
    end
    #Finish

    #In this cases users always will be sent in clear (not cipher)
    #(Too much data to cipher)
    #Anyway, we must to build the hash to pass the authentication
    params = {}
    encrypted_params = {}
    params[:name]=jids.join(",")

    return [getMethodName,generic_api_call(url,params,encrypted_params)]
end



#Params must include the password and all the parameters to be sent in clear.
#Anyway, If "secure_rest_api" is disable, encrypted_params will be send in clear.
def generic_api_call(url,params,encrypted_params)

  begin
    unless params
 	params = {}
    end

    unless encrypted_params
 	 encrypted_params = {}
    end

    params[:password]=$pass

    response=sendHttpRequest(url,params,encrypted_params)
    puts response.body

    if response.body.include?("Ok")
    	return true
    else
    	return false
    end

  rescue => e
    log($script_title,"#{e.class.name}: #{e.message}")
    puts("#{e.class.name}: #{e.message}")
    return false
  end
end


#Send HTTP Request to Social Stream Presence API
def sendHttpRequest(url,params,encrypted_params)

    unless params[:password]
	return "params[:password] required in sendHttpRequest";
    end


    if $secure_rest_api == "true"
	#Require libraries
	require 'openssl'

	xmpp_private_key_path = $scripts_path + "/rsa_keys/xmpp_rsa_key_private.pem";
    	web_public_key_path = $scripts_path + "/rsa_keys/web_rsa_key_public.pem";
    	xmpp_private_key = OpenSSL::PKey::RSA.new(File.read(xmpp_private_key_path))
    	web_public_key = OpenSSL::PKey::RSA.new(File.read(web_public_key_path))

	request_params = {};

	#Copy non encypted params
        params.each do |key,value|
	  unless key.to_s == "password"
		request_params[key] = value
	  end 
        end

	#Include encrypted params
	if encrypted_params and encrypted_params.empty? == false
	  request_params[:encrypted_params] = web_public_key.public_encrypt(encrypted_params.to_s)
	end
	
	#Generating stamp
	#1 Get constant password
	password = params[:password];
	#2 Generating timestamp
	timestamp = Time.now.utc.to_s
	#3 Calculating Hash
    	hash = calculateHash(request_params)

	#Add cipher stamp to the request
	request_params[:password] = xmpp_private_key.private_encrypt(password+"#####"+timestamp+"#####"+hash)
	
	#Replace previous params
	params = request_params
    else
	#Non secure mode: send encrypted params in clear
	if encrypted_params
	  encrypted_params.each do |key,value|
	      params[key] = value
	  end
	end
    end
    
    response = RestClient.get url, :params => params
    return response
end


def calculateHash(request_params)
	require 'digest/md5'

	unless request_params
		request_params = {};
	end

	hash = "";
        request_params.each do |key,value|
              hash = hash + key.to_s + value.to_s
        end
        return Digest::MD5.hexdigest(hash)
end




def invokeApiCall(method,args)
	length = args.length;
	case length
	when 0
	  return send(method)
	when 1
	  return send(method,args[0])
	when 2
	  return send(method,args[0],args[1])
	when 3
	  return send(method,args[0],args[1],args[2])
	when 4
	  return send(method,args[0],args[1],args[2],args[3])
	else
	  return send(method,args)
	end
end


def getUsernameFromJid(jid)
	return jid.split("@")[0];
end

def getDomainFromJid(jid)
	return jid.split("@")[1];
end

def getWebDomainUrlFromDomain(domain)

	if domain=="all"
	  return getWebDomainUrlFromDomain(getWebDomains()[0])
	end

	web_domain = getOption(domain + "=");
	if (web_domain != "Undefined")
		return web_domain
	else
		return domain
	end
end

def getWebDomains()
	begin
		web_domains = getOption("web_domains=");
		#web_domains=["domain1","domain2"]
		return getElementsFromStringArray(web_domains);
	rescue Exception=>e
		return [""]
	end
end

def getElementsFromStringArray(stringArray)
	stringArray=stringArray[1,stringArray.length-2]
	return stringArray.split(",")
end


#Main Program

begin
	if !ARGV[0]
		puts("Syntax Error: ./synchronize_presence_script domain")
		exit 1
	end

	args = ARGV[0]
	method = "synchronize"

	if (response = invokeApiCall(method, args) and response[1])
	  	puts $script_title  + ": #{response[0]} [OK]"
	  	log( $script_title  , "#{response[0]} [OK]" )
	else
	  	puts $script_title  + ": #{response[0]} [FAIL]"
	  	log( $script_title  , "#{response[0]} [FAIL]" )
	end

rescue => e
	log($script_title,"#{e.class.name}: #{e.message}")
   	puts("#{e.class.name}: #{e.message}")
    	exit 1
end