ging/social_stream

View on GitHub
presence/ejabberd/ejabberd_scripts/emanagement

Summary

Maintainability
Test Coverage
#!/usr/bin/env ruby

####################################
#          EMANAGEMENT
#Ejabberd Management script 
#New features for management and maintenance ejabberd
#@author Aldo Gordillo < agordillos@gmail.com >
#@version 2.1 - 28-2-2012
####################################

require 'logger'

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


#Configuration variables
$verbose = (getOption("verbose=")=="true")
$ejabberd_user = getOption("ejabberd_server_user=")
$checkEjabberdctlQuotedString = false


PARAMS_FOR_COMMANDS = {
    'addBuddyToRoster' => 5,
    'removeBuddyFromRoster' => 2,
    'setBidireccionalBuddys' => 6,
    'unsetBidireccionalBuddys' => 4,
    'getRoster' => 1,
    'removeRoster' => 1,
    'removeAllRostersByDomain' => 1,
    'removeAllRosters' => 0,
    'getAllUserJidsWithRosterByDomain' => 1,
    'printAllRostersByDomain' => 1,
    'printAllRosters' => 0,
    'printAllBidirecctionalBuddysByDomain' => 1,
    'checkUserJid' => 1,
    'checkBidirecctionalBuddys' => 2,
    'sendPresence' => 2,
    'setPresence' => 1,
    'unsetPresence' => 1,
    'sendMessageToUser' => 3,
    'getUserResource' => 1,
    'isEjabberdNodeStarted' => 0,
    'broadcast' => 3,
    'broadcastToConnectedUsers' => 3,
        'getConnectedJidsByDomain' => 1,
    'getConnectedJids' => 0,
    'kickUserJid' => 1,
    'createPersistentRoom' => 2,
    'createRoom' => 2,
    'destroyRoom' => 2,
    'destroyAllRoomsByDomain' => 1,
    'destroyAllRooms' => 0,
    'getAllJidsOfRoom' => 2,
    'getAffiliationsOfRoom' => 2,
    'setJidAffiliationOfRoom' => 4,
    'printAllRoomsByDomain' => 1,
    'printAllRooms' => 0,
    'help' => 0,
}

SYNTAX_FOR_COMMANDS = {
    'addBuddyToRoster' => 'addBuddyToRoster userJid buddyJid buddyNick buddyGroup subscription_type',
    'removeBuddyFromRoster' => 'removeBuddyFromRoster userJid buddyJid',
    'setBidireccionalBuddys' => 'setBidireccionalBuddys userAJid userBJid userANick userBNick groupForA groupForB',
    'unsetBidireccionalBuddys' => 'unsetBidireccionalBuddys userJid oldFriendJid oldFriendNick groupForOldFriend',
    'getRoster' => 'getRoster userJid',
    'removeRoster' => 'removeRoster userJid',
    'removeAllRostersByDomain' => 'removeAllRostersByDomain domain',
    'removeAllRosters' => 'removeAllRosters',
    'getAllUserJidsWithRosterByDomain' => 'getAllUserJidsWithRosterByDomain domain',
    'printAllRostersByDomain' => 'printAllRostersByDomain domain',
    'printAllRosters' => 'printAllRosters',
    'printAllBidirecctionalBuddysByDomain' => 'printAllBidirecctionalBuddysByDomain domain',
    'checkUserJid' => 'checkUserJid userJid',
    'checkBidirecctionalBuddys' => 'checkBidirecctionalBuddys userAJid userBJid',
    'sendPresence' => 'sendPresence userJid show',
    'setPresence' => 'setPresence userJid',
    'unsetPresence' => 'unsetPresence userJid',
    'sendMessageToUser' => 'sendMessageToUser fromJid toJid msg',
    'getUserResource' => 'getUserResource userJid',
    'isEjabberdNodeStarted' => 'isEjabberdNodeStarted',
    'broadcast' => 'broadcast admin userJids msg ',
    'broadcastToConnectedUsers' => 'broadcastToConnectedUsers admin userJids msg (userJids value: "all" or jids array [jid1,jid2,...,jidN])',
        'getConnectedJidsByDomain' => 'getConnectedJidsByDomain domain',
    'getConnectedJids' => 'getConnectedJids',
    'kickUserJid' => 'kickUserJid(userJid)',
    'createPersistentRoom' => 'createPersistentRoom roomName domain',
    'createRoom' => 'createRoom roomName domain',
    'destroyRoom' => 'destroyRoom roomName domain',
    'destroyAllRoomsByDomain' => 'destroyAllRoomsByDomain domain',
    'destroyAllRooms' => 'destroyAllRooms',
    'getAllJidsOfRoom' => 'getAllJidsOfRoom roomName domain',
    'getAffiliationsOfRoom' => 'getAffiliationsOfRoom roomName domain',
    'setJidAffiliationOfRoom' => 'setJidAffiliationOfRoom roomName domain jid affiliation (affiliation value: owner/admin/member/outcast/none)',
    'printAllRoomsByDomain' => 'printAllRoomsByDomain domain',
    'printAllRooms' => 'printAllRooms',
    'help' => 'help',
}


#########################
#Debug methods
#########################
def ejabberdLog(text)
    $logger.info "Ejabberd Management Script: " + text
    if $verbose
        #puts "Writing to ejabberdLog: " + "Ejabberd Management Script: " + text
    end
end

def log(msg)
    logWithTitle(msg,nil)
end

def logWithTitle(msg,title)
    puts "------------------------"
    if title
        puts ("<<<" + title + ">>>")
    end
    puts msg
    puts "------------------------"
end


#########################
#Methods to manage rosters from Social Stream Rails App
#########################
def setBidireccionalBuddys(userAJid,userBJid,userANick,userBNick,groupForA,groupForB)
    addBuddyToRoster(userAJid,userBJid,userBNick,groupForB,"both")
    addBuddyToRoster(userBJid,userAJid,userANick,groupForA,"both")
    return "Done"
end

def unsetBidireccionalBuddys(userJid,oldFriendJid,oldFriendNick,groupForOldFriend)
    if checkBidirecctionalBuddys(userJid,oldFriendJid)
        removeBuddyFromRoster(userJid,oldFriendJid)
        removeBuddyFromRoster(oldFriendJid,userJid)
        addBuddyToRoster(userJid,oldFriendJid,oldFriendNick,groupForOldFriend,"to")
        return "Done"
    else
        return userJid + " and " + oldFriendJid + " aren't bidireccional buddys"
    end
end

def addBuddyToRoster(userJid,buddyJid,buddyNick,buddyGroup,subscription_type)
    user = getUsernameFromJid(userJid)
    buddy = getUsernameFromJid(buddyJid)
    userDomain = getDomainFromJid(userJid)
    buddyDomain = getDomainFromJid(buddyJid)
    executeCommand("ejabberdctl add-rosteritem " + user + " " + userDomain + " " + buddy + " " + buddyDomain + " " + buddyNick + " " + buddyGroup + " " +                 subscription_type)
    return "Done"
end

def removeBuddyFromRoster(userJid,buddyJid)
    user = getUsernameFromJid(userJid)
    buddy = getUsernameFromJid(buddyJid)
    userDomain = getDomainFromJid(userJid)
    buddyDomain = getDomainFromJid(buddyJid)
    if checkUserJidInRoster(buddyJid,getRoster(userJid))
        executeCommand("ejabberdctl delete_rosteritem " + user + " " + userDomain + " " + buddy + " " + buddyDomain)
        return "Done"
    else
        return "User " + buddyJid + " not found in " + userJid + " roster."
    end
end


#########################
#Roster Utilities
#########################
def getRoster(userJid)
    if checkUserJid(userJid)
        executeCommand("ejabberdctl get_roster " + getUsernameFromJid(userJid)  + " " + getDomainFromJid(userJid))
    else
        return "Roster not found for user " + userJid
    end
end

def getBuddyJidsFromRoster(roster)
    buddyJids = []
        lines = roster.split("\n")
    lines.each do |line|
        buddyJids << line.split(" ")[0]
    end
    buddyJids
end

def removeRoster(userJid)
    if checkUserJid(userJid)
        executeCommand("ejabberdctl process_rosteritems delete any any " + userJid + " any")
        return "Done"    
    else
        return "Roster not found for userJid " + userJid
    end
end

def removeAllRostersByDomain(domain)
    if(domain=="all")
        executeCommand("ejabberdctl process_rosteritems delete any any any any")
    else
        executeCommand("ejabberdctl process_rosteritems delete any any *@" + domain + " any")
    end
    return "Done";
end

def removeAllRosters()
    return removeAllRostersByDomain("all")
end

def getAllUserJidsWithRosterByDomain(domain)
    if(domain=="all")
        output = executeCommand("ejabberdctl process_rosteritems list any any any any")
    else
        output = executeCommand("ejabberdctl process_rosteritems list any any *@" + domain + " any")
    end

    userJids = []
    lines = output.split("\n");
    items = lines[0].split(" ")[2]

    #test if items string contains a correct number
    if items.to_i.to_s == items
        if items.to_i > 0
            lines.each do |line|
                if line.split(":")[0]=="Matches"
                    userJid = line.split(" ")[1]
                    #puts "Line:" + line
                    unless userJids.include?(userJid)
                        userJids << userJid
                    end    
                end
            end
        end
    end
    
    return userJids;
end

def getAllUserJidsWithRoster()
    return getAllUserJidsWithRosterByDomain("all")
end

def getAllRostersByDomain(domain)
    rosterList = { }

    userJids = getAllUserJidsWithRosterByDomain(domain)

    userJids.each do |userJid|
        roster = getRoster(userJid)
        rosterList.store(userJid,roster)
    end
    return rosterList
end

def getAllRosters()
    return getAllRostersByDomain("all");
end

def printAllRostersByDomain(domain)
    rosterList = getAllRostersByDomain(domain)
    rosterList.keys.each do |userJid|
        puts "\n"
        puts "-------------------------------------"
        puts userJid + " Roster"
        puts "-------------------------------------"
        puts rosterList[userJid]
        puts "-------------------------------------"
        puts "\n"
    end
    return "Done"
end

def printAllRosters()
    return printAllRostersByDomain("all")
end

def getAllBidirecctionalBuddysByDomain(domain)
    b_buddyJids = []
    userJids = getAllUserJidsWithRosterByDomain(domain)
    nonCheckedUsers = userJids
    userJids.each do |userJid|
        nonCheckedUsers.delete(userJid)
        nonCheckedUsers.each do |checkUserJid|
            if checkBidirecctionalBuddys(userJid,checkUserJid)
                b_buddyJids << [userJid,checkUserJid]
            end
        end
    end
    return b_buddyJids
end

def getAllBidirecctionalBuddys()
    return getAllBidirecctionalBuddysByDomain("all")
end

def printAllBidirecctionalBuddysByDomain(domain)
    puts "This may take a while..."
    b_buddys = getAllBidirecctionalBuddysByDomain(domain)
    b_buddys.each do |contact|
        puts "[" + contact[0] + "," + contact[1] + "]"
    end
    return "Done"
end

def printAllBidirecctionalBuddys()
    return printAllBidirecctionalBuddysByDomain("all")
end

#Check if the user have a roster in his domain
def checkUserJid(userJid)
    domain = getDomainFromJid(userJid)
    return getAllUserJidsWithRosterByDomain(domain).include?(userJid)
end

def checkUserJidInRoster(userJid,roster)
    return getBuddyJidsFromRoster(roster).include?(userJid)
end

def checkBidirecctionalBuddys(userAJid,userBJid)
    rosterA = getRoster(userAJid)
    rosterB = getRoster(userBJid)
    return (checkUserJidInRoster(userAJid,rosterB) and checkUserJidInRoster(userBJid,rosterA))
end


#########################
#Manage stanzas Utilities
#########################
def setPresence(userJid)
    sendPresenceStanzaWithType(userJid,userJid,"available")
end

def unsetPresence(userJid)
    sendPresenceStanzaWithType(userJid,userJid,"unavailable")
end

def sendPresence(username,show)
    sendPresenceWithShow(username,username,show)
end

def sendPresenceWithShow(fromJid,toJid,show)
    resource = getUserResource(fromJid);  
    from_name = getUsernameFromJid(fromJid)
    domain = getDomainFromJid(fromJid)
    pres_stanza = "\\<" + buildQuotedString("presence from=") + "\\\"" + buildQuotedString(fromJid) + "\\\"" + buildQuotedString(" to=") + "\\\"" +
            buildQuotedString(toJid) + "\\\"\\>\\<" + buildQuotedString("show") + "\\>" + buildQuotedString(show) + "\\<" +
            buildQuotedString("/show") + "\\>\\<" + buildQuotedString("/presence") + "\\>"
    executeCommand("ejabberdctl send_stanza_c2s " + from_name + " " + domain + " " + resource + " " + pres_stanza)
    return "Done"
end

def sendPresenceStanzaWithType(fromJid,toJid,presence_type)
    resource = getUserResource(fromJid);
    from_name = getUsernameFromJid(fromJid)
    domain = getDomainFromJid(fromJid)
    pres_stanza = "\\<" + buildQuotedString("presence type=") + "\\\"" + buildQuotedString(presence_type) + "\\\"" + buildQuotedString(" from=") + "\\\"" + 
        buildQuotedString(fromJid) + "\\\"" + buildQuotedString(" to=") + "\\\"" + buildQuotedString(toJid) + "\\\"\\>\\<" + buildQuotedString("/presence") + "\\>" 
    executeCommand("ejabberdctl send_stanza_c2s " + from_name + " " + domain + " " + resource + " " + pres_stanza)
    return "Done"
end

def sendMessageToUser(fromJid,toJid,msg)
    executeCommand("ejabberdctl send_message_chat " + fromJid + " " + toJid + " " + buildQuotedString(msg))
    return "Done"
end

def getUserResource(userJid)
    output = executeCommand("ejabberdctl connected-users")
        lines = output.split("\n");
    lines.each do |line|
        if line.split("/")[0] == userJid
            #puts "Find " + userJid
            resource = line.split("/")[1];
            return resource;    
        end
    end
    return userJid + " no have any active session"
end


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

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

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


#Determine how to scape characters for build quoted strings
def checkEjabberdctlQuotedString
    puts "checkForSimpleSlash: " + checkForSimpleSlash.to_s()
    puts "checkForDoubleSlash: " + checkForDoubleSlash.to_s()
end

def checkAndSetEjabberdctlQuotedString
    if checkForSimpleSlash
        $checkForSimpleSlash = true
    end
    if checkForDoubleSlash
        $checkForDoubleSlash = true
    end
    $checkEjabberdctlQuotedString = true    
end

def checkForDoubleSlash
    command = "ejabberdctl send_message_chat example@localhost example@localhost \\'Hello quoted string\\'"
    if execute_as_sudo
        command = "sudo " + command
    end
    
    output = %x[#{command}]
    firstLine = ""
    lines = output.split("\n")
    lines.each do |line|
        if line != ""
            firstLine = line
            break
        end
    end

    if firstLine==""
        return true
    elsif firstLine.split(":")[0]=="Error"
        return false
    else
        #Unknown error        
        return false
    end
end

def checkForSimpleSlash
    command = "ejabberdctl send_message_chat example@localhost example@localhost \'Hello quoted string\'"
    if execute_as_sudo
        command = "sudo " + command
    end
    #puts "Executing " + command
    output = %x[#{command}]
    firstLine = ""
    lines = output.split("\n")
    lines.each do |line|
        if line != ""
            firstLine = line
            break
        end
    end

    if firstLine==""
        return true
    elsif firstLine.split(":")[0]=="Error"
        return false
    else
        #Unknown error        
        return false
    end
end

def buildQuotedString(msg)
    if !$checkEjabberdctlQuotedString
        checkAndSetEjabberdctlQuotedString
    end
    
    if $checkForSimpleSlash
        return "\'" + msg + "\'"
    end
    if $checkForDoubleSlash
        return "\\'" + msg + "\\'"
    end
    return msg
end


#########################
#Connection Info
#########################
def isEjabberdNodeStarted
    output = executeCommand("ejabberdctl status")
    if firstLine = output.split("\n")[0]
      return ((firstLine.split(":")[1]).strip()=="started")
    end
    return false
end

def getConnectedJidsByDomain(domain)
        jids = []
    if(domain=="all")
        output = executeCommand("ejabberdctl connected-users")
    else
        output = executeCommand("ejabberdctl connected-users | grep @" + domain + "/")
    end
    sessions = output.split("\n")
    sessions.each do |session|  
      jids << session.split("/")[0]
    end
    return jids
end

def getConnectedJids()
    return getConnectedJidsByDomain("all")
end


#########################
#Advanced features
#########################
def broadcast(admin,userJids,msg)
    getElementsFromStringArray(userJids).each do |userJid|
        sendMessageToUser(admin,userJid,msg)
    end
    return "Done"
end

def broadcastToConnectedUsers(admin,userJids,msg)
    connectedJids = getConnectedJids()
    if(userJids=="all")
        connectedJids.each do |userJid|
            sendMessageToUser(admin,userJid,msg)
        end
    else
        getElementsFromStringArray(userJids).each do |userJid|
            if (connectedJids.include?(userJid))
                sendMessageToUser(admin,userJid,msg)
            end
        end
    end
    return "Done"
end

def kickUserJid(userJid)
    user = getUsernameFromJid(userJid)
    userDomain = getDomainFromJid(userJid)
    resource = getUserResource(userJid);  
    reason = "0"
    executeCommand("ejabberdctl kick_session " + user + " " + userDomain + " " + resource + " " + reason)
    return "Done"
end

def banUserJid(userJid)
    kickUserJid(userJid)
    #Notify Web server to ban user permanently
end



#########################
#MUC Methods (Multi-user-chat)
#########################

$muc_host = "conference"

def printAllRoomsByDomain(domain)
    if(domain=="all")
        return printAllRooms
    end
    output = executeCommand("ejabberdctl muc_online_rooms " + domain)
    return output;
end

def printAllRooms
    output = executeCommand("ejabberdctl muc_online_rooms global")
    return output;
end

def createPersistentRoom(roomName,domain)
    createRoom(roomName,domain)
    setRoomPersistence(roomName,domain,true)
    return "Done"
end

def createRoom(roomName,domain)
    executeCommand("ejabberdctl create_room " + roomName + " " + $muc_host + "." + domain + " " + domain)
    return "Done"
end

def setRoomPersistence(roomName,domain,persistence)
    if (persistence==true)
        executeCommand("ejabberdctl change_room_option " + roomName + " " + $muc_host + "." + domain + " persistent true")
    else
        executeCommand("ejabberdctl change_room_option " + roomName + " " + $muc_host + "." + domain + " persistent false")
    end
end

def destroyAllRoomsByDomain(domain)
    rooms = getAllRoomsByDomain(domain)
    rooms.each do |room|
        destroyRoom(getNameFromRoom(room),getDomainFromRoom(room))
    end
    return "Done"
end

def destroyAllRooms()
    return destroyAllRoomsByDomain("all")
end

def destroyRoom(roomName,domain)
    executeCommand("ejabberdctl destroy_room " + roomName + " " + $muc_host + "." + domain + " " + domain)
    return "Done"
end

def getAllRoomsByDomain(domain)
    if(domain=="all")
        output = executeCommand("ejabberdctl muc_online_rooms global")
    else
        output = executeCommand("ejabberdctl muc_online_rooms " + domain)
    end
    rooms = output.split("\n")
    return rooms
end

def getAllJidsOfRoomObject(room)
    return getAllJidsOfRoom(getNameFromRoom(room),getDomainFromRoom(room))
end

def getAllJidsOfRoom(roomName,domain)
    jids = []
    output = executeCommand("ejabberdctl get_room_occupants " + roomName + " " +  $muc_host + "." + domain)
    lines = output.split("\n")
    lines.each do |line|
        jids << line.split("/")[0]
    end
    return jids
end

def getAffiliationsOfRoomObject(room)
    return getAffiliationsOfRoom(getNameFromRoom(room),getDomainFromRoom(room))
end

def getAffiliationsOfRoom(roomName,domain)
    affiliateds = []
    output = executeCommand("ejabberdctl get_room_affiliations " + roomName + " " +  $muc_host + "." + domain)
    lines = output.split("\n")
    lines.each do |line|
        split=line.split(" ");
        jid = split[0]+"@"+split[1];
        affiliation=split[2]
        affiliateds << [jid,affiliation]
    end
    return affiliateds
end

#affiliation = Owner/Admin/Member/Outcast/None
def setJidAffiliationOfRoom(roomName,domain,jid,affiliation)
    output = executeCommand("ejabberdctl set_room_affiliation " + roomName + " " +  $muc_host + "." + domain + " " + jid + " " + affiliation)
    return "Done"
end

def getDomainFromRoom(room)
    return room.split(".")[1];
end

def getNameFromRoom(room)
    return room.split("@")[0];
end


#########################
#Execution commands methods
#########################
def executeCommand(command)
    #Building...
    command = buildCommand(command)
    
    if $verbose
        #Logging...
        puts "Executing: " + command
        ejabberdLog("Executing (#{command})")
    end

    #Executing...
    output = %x[#{command}]
    return output
end

def buildCommand(command)
    if execute_as_sudo
        command = "sudo -u " + $ejabberd_user + " " + command
    end
    return command
end

def execute_as_sudo
    sudo_users = getOption("users_require_sudo=")

    if sudo_users=="all"
     return true
    end

    sudo_users_array = sudo_users.split(",")
        current_user = %x["whoami"].split("\n")[0]
    if sudo_users_array.include?(current_user)
        return true
    end
end


#########################
#Support
#########################
def help
    log("Command list")
    SYNTAX_FOR_COMMANDS.values.each do |command|
        puts command
    end
    puts ""
end



#########################
#Main thread
#########################
#log("Init Ejabberd Maintenance script")

begin
    if ARGV[0] and PARAMS_FOR_COMMANDS.keys.include?(ARGV[0])
        if (ARGV.length == (PARAMS_FOR_COMMANDS[ARGV[0]]+1))

            ejabberdLog("Executing (#{ARGV})")

            length = ARGV.length;
            case length
            when 1
              puts send(ARGV[0])
            when 2
              puts send(ARGV[0],ARGV[1])
            when 3
              puts send(ARGV[0],ARGV[1],ARGV[2])
            when 4
              puts send(ARGV[0],ARGV[1],ARGV[2],ARGV[3])
            when 5
              puts send(ARGV[0],ARGV[1],ARGV[2],ARGV[3],ARGV[4])
            when 6
              puts send(ARGV[0],ARGV[1],ARGV[2],ARGV[3],ARGV[4],ARGV[5])
            when 7
              puts send(ARGV[0],ARGV[1],ARGV[2],ARGV[3],ARGV[4],ARGV[5],ARGV[6])
            else
              puts send(ARGV[0],ARGV)
            end
            puts ""
        else 
            puts "Error: Params required for command " + (ARGV[0]).to_s() + ": " + (PARAMS_FOR_COMMANDS[ARGV[0]]).to_s()
            puts "Syntax for command " + (ARGV[0]).to_s() + "\n" + (SYNTAX_FOR_COMMANDS[ARGV[0]]).to_s()
            puts "Use 'help' to get information about command syntax"
        end
    else
        if (ARGV.length==0)
            help
        else
            puts "Error: Command not recognized"
            puts "Use 'help' to get information about command syntax"
        end
    end
rescue
    puts "Syntax error"
    puts "Use 'help' to get information about command syntax"
end