require "rexml/document"
require "cgi"
require 'digest'
require 'json'
require 'date'
require 'time'
require 'logger'
require_relative 'antimalwarecommon'

class Sophos

	def self.detect()
        begin
			sophosPath = "/opt/sophos-av/bin/savdstatus"

            if !File.file?('/opt/sophos-av/bin/savdstatus')			
				findSophosCmd = `readlink $(which savscan)`.lines.map(&:chomp)
				if !$?.success? || findSophosCmd.nil? || findSophosCmd.empty?
					return false
				else
					savscanPath = findSophosCmd[0].strip
					sophosPathArray = savscanPath.split("/")
					sophosPathArray[sophosPathArray.length-1] = "savdstatus"
					sophosPath = sophosPathArray.join('/')
					if !File.file?(sophosPath)		
						return false
					end
				end
			end

			detectioncmd = `#{sophosPath} --version 2>&1`.lines.map(&:chomp)
			
			if !$?.success? || detectioncmd.nil? || detectioncmd.empty?						
				return false
			else
				sophosInfo = detectioncmd[1].split(" = ")				
				sophosName = sophosInfo[0].strip
				sophosVersion = sophosInfo[1].strip				
				if sophosName != "Sophos Anti-Virus"
					#puts "Sophos Name is not Sophos Anti-Virus"					
					return false
				elsif sophosVersion.split(".")[0].to_i  < 9	
					#puts "Sophos version is less than 9, please install latest Sophos"					
					return false
				end
			end
			#puts "Sophos is detected on the machine at path " + sophosPath
			return true
		rescue => e
			#puts "Getting exception when checking sophos is installed or not: " + e.message	
			return false			
		end
	end

	def self.getprotectionstatus()		 
        ret = {}

        sophosName = "Sophos Anti-Virus"
        sophosVersion = "NA"
        
        lastUpdate = "NA"
        lastUpdateTime = "NA"
        buildRevision = "NA"
		threatDetectionEngine = "NA"
		threatData = "NA"
		threatCount = "NA"
		threatDataRelease = "NA"
				
	    ondemandscan = "NA"
		scheduledscan = "NA"
        
        rmsstatus = "NA"
		onaccessscan = "NA"		
		liveprotection = "NA"

		scanDate = ""
		protectionStatusDetails = ""
		protectionStatusAlertArray = []
		protectionStatusDetailsArray = []

		error = ""

		($ProtectionStatusRank, $ProtectionStatus) = AntimalwareCommon::UnknownProtectionCode
        ($ThreatStatusRank, $ThreatStatus) = AntimalwareCommon::UnknownThreatCode
        
        begin
			sophosPath = "/opt/sophos-av/bin/savdstatus"
			#if !File.file?('/opt/sophos-av/bin/savdstatus')			
				#findSophosCmd = `find / -iname savdstatus | grep /bin/savdstatus 2>&1`.lines.map(&:chomp)
				#if !$?.success? || findSophosCmd.nil? || findSophosCmd.empty?
				#error += "Getting issue when trying to find custmized sophos path, Sophos not detected; "
				#else
					#sophosPath = findSophosCmd[0]
				#end
			#end

			if !File.file?('/opt/sophos-av/bin/savdstatus')			
				findSophosCmd = `readlink $(which savscan)`.lines.map(&:chomp)
				if !$?.success? || findSophosCmd.nil? || findSophosCmd.empty?
					error += "Getting issue when trying to find custmized sophos path, Sophos not detected; "
				else
					savscanPath = findSophosCmd[0].strip
					sophosPathArray = savscanPath.split("/")
					sophosPathArray[sophosPathArray.length-1] = "savdstatus"
					sophosPath = sophosPathArray.join('/')
				end
			end
			
			detectioncmd = `LANG=en_US.UTF-8 #{sophosPath} --version 2>&1`.lines.map(&:chomp)
		
			if !$?.success? || detectioncmd.nil? || detectioncmd.empty?
			    error += "Getting issue when running sophos version cmd; "		
			else
				sophosInfo = detectioncmd[1].split(" = ")								
				sophosVersion = sophosInfo[1].strip				
				buildRevision = detectioncmd[2].split(" = ")[1].strip
				threatDetectionEngine = detectioncmd[3].split(" = ")[1].strip
				threatData = detectioncmd[4].split(" = ")[1].strip
				threatCount = detectioncmd[5].split(" = ")[1].strip
				threatDataRelease = detectioncmd[6].split(" = ")[1].split("UTC")[0].strip
                lastUpdate = detectioncmd[7].split(" = ")[1].split("UTC")[0].strip
                
                #250
                #signature date
                #threat release date
				lastUpdate = Time.strptime(lastUpdate, '%a %d %b %Y %I:%M:%S %p')
				threatDataRelease = Time.strptime(threatDataRelease, '%a %d %b %Y %I:%M:%S %p')
				protectionStatusDetailsArray.push("buildRevision: " + buildRevision)				
				protectionStatusDetailsArray.push("threatData: " + threatData)
				protectionStatusDetailsArray.push("threatCount: " + threatCount)
                #puts "threatDataRelease " + threatDataRelease.to_s
                if (!threatDataRelease.nil? && threatDataRelease != "NA")
                    #if (threatDataRelease < (Time.now - 30*24*3600).utc)
                        #($ProtectionStatusRank, $ProtectionStatus) = AntimalwareCommon::SignaturesOutOfDateProtectionCode
                        #protectionStatusAlertArray.push("Threat Data Release Update out of 30 days: " + threatDataRelease.to_s)
                    #else
                    protectionStatusDetailsArray.push("Threat Data Release Update: " + threatDataRelease.to_s)
                    #end
                else
                    error += "threat Data Release not found; "
                    ($ProtectionStatusRank, $ProtectionStatus) = AntimalwareCommon::SignaturesOutOfDateProtectionCode
                    protectionStatusAlertArray.push("Threat Data Release not found: " + threatDataRelease)
                end

				#puts "lastUpdate " + lastUpdate.to_s               
                if (!lastUpdate.nil? && lastUpdate != "NA")
                    if (lastUpdate < (Time.now - 7*24*3600).utc)
                        ($ProtectionStatusRank, $ProtectionStatus) = AntimalwareCommon::SignaturesOutOfDateProtectionCode
                        protectionStatusAlertArray.push("Last Update out of 7 days: " + lastUpdate.to_s)
                    else
                        protectionStatusDetailsArray.push("Last Update within 7 days: " + lastUpdate.to_s)
                    end
                else
                    error += "Last Update not found, running lastupdate cmd; "
                    lutcmd = `LANG=en_US.UTF-8 #{sophosPath} --lastupdate 2>&1`.lines.map(&:chomp)
			        
			        if !$?.success? || lutcmd.nil? || lutcmd.empty?
                        error += "Fail to run last update time cmd; "
                        ($ProtectionStatusRank, $ProtectionStatus) = AntimalwareCommon::SignaturesOutOfDateProtectionCode
                        protectionStatusAlertArray.push("Last Update not  when running last update cmd: " + lastUpdateTime)							
			        else
				        lastUpdateTime = lutcmd[0].split("UTC")[0].strip
				        if(lastUpdateTime == "NA") or (lastUpdateTime.include? "Never updated")
					        ($ProtectionStatusRank, $ProtectionStatus) = AntimalwareCommon::SignaturesOutOfDateProtectionCode
                            protectionStatusAlertArray.push("Last Update not found: " + lastUpdateTime)
				        else
					        lastUpdateTime = Time.strptime(lastUpdateTime, '%a %d %b %Y %I:%M:%S %p')
					        if (lastUpdateTime < (Time.now - 7*24*3600).utc)
						        ($ProtectionStatusRank, $ProtectionStatus) = AntimalwareCommon::SignaturesOutOfDateProtectionCode
                                protectionStatusAlertArray.push("Last Update out of 7 days: " + lastUpdateTime.to_s)
					        else
						        protectionStatusDetailsArray.push("Last Update within 7 days: " + lastUpdateTime.to_s)
					        end
				        end
			        end
                end
			end
			#############################

            #270
            #on access scan
            #live protection
            
            #################################################
			oascmd = `#{sophosPath} -v 2>&1`.lines.map(&:chomp)
			
			if !$?.success? || oascmd.nil? || oascmd.empty?
				error += "Fail to run On Access Scan cmd; "
			else
				onaccessscan = oascmd[0].strip + "; " + oascmd[1].strip
				if(onaccessscan == "NA") or (onaccessscan.include? "On-access scanning is not running")
                    ($ProtectionStatusRank, $ProtectionStatus) = AntimalwareCommon::NoRealTimeProtectionProtectionCode
                    protectionStatusAlertArray.push("On Access Scan is not running: " + onaccessscan)
                else
                    protectionStatusDetailsArray.push(onaccessscan)
				end				
			end
            
            ###########################
            sophosconfigpathArray = sophosPath.split("/")
			sophosconfigpathArray[sophosconfigpathArray.length-1] = "savconfig"
			sophosconfigpath = sophosconfigpathArray.join('/')
			lpcmd = `#{sophosconfigpath} get LiveProtection 2>&1`.lines.map(&:chomp)

			if !$?.success? || lpcmd.nil? || lpcmd.empty?
				error += "live protection cmd failed; "
			else
				liveProtection = lpcmd[0].strip
				if(liveProtection == "NA") or (liveProtection != "enabled")
                    ($ProtectionStatusRank, $ProtectionStatus) = AntimalwareCommon::NoRealTimeProtectionProtectionCode
                    protectionStatusAlertArray.push("liveProtection is not enabled: " + liveProtection)
                else
                    protectionStatusDetailsArray.push("Live Protection is enabled")
				end		
			end
			

            #350
            #find scan date
            #rms not connected
            #############################
            rmscmd = `#{sophosPath} --rms 2>&1`.lines.map(&:chomp)
			status = $?
			
			if rmscmd.nil? || rmscmd.empty?				
				error += "Fail to run Remote Management status cmd; "
			else
				rmsstatus = rmscmd[0].strip
                if(rmsstatus == "NA") or (rmsstatus.include? "inactive")
                    #($ProtectionStatusRank, $ProtectionStatus) = AntimalwareCommon::ActionRequiredProtectionCode
                    protectionStatusDetailsArray.push("Remote Management status is not active: " + rmsstatus)
                else
                    protectionStatusDetailsArray.push(rmsstatus)
				end
			end           
            
            #############################
            
            sophoslogArray = sophosPath.split("/")
			sophoslogArray[sophoslogArray.length-1] = "savlog"
			sophoslogpath = sophoslogArray.join('/')
			
			scancmd = `LANG=en_US.UTF-8 #{sophoslogpath} --maxage=7 | grep "scan finished" | tail -1 2>&1`.lines.map(&:chomp)
			
			if !$?.success? || scancmd.nil? || scancmd.empty?
				error += "On demand scan cmd failed; "
			else
				ondemandscan = scancmd[0].split("UTC: savscan.log")[0].strip
                if(ondemandscan == "NA" || ondemandscan.nil? || ondemandscan.empty? )
					#puts "On demand scanDate within 7 days not found"
				else
					ondemandscan = Time.strptime(ondemandscan, '%a %d %b %Y %I:%M:%S %p')
				end
			end
			#puts ondemandscan.to_s
            #########################################			
			scheduleScancmd = `LANG=en_US.UTF-8 #{sophoslogpath} --maxage=7 | grep -i "Scheduled scan .* completed" | tail -1`.lines.map(&:chomp)
		
			if !$?.success? || scheduleScancmd.nil? || scheduleScancmd.empty?
				error += "Scheduled scan cmd failed; "
			else
				scheduledscan = scheduleScancmd[0].split("UTC: scheduled.scan.log")[0].strip
                if(scheduledscan == "NA" || scheduledscan.nil? || scheduledscan.empty? )
					#puts "scheduled scanDate within 7 days not found"
				else
					scheduledscan = Time.strptime(scheduledscan, '%a %d %b %Y %I:%M:%S %p ')
				end
			end
            #puts scheduledscan.to_s
            #############################
            if (scheduledscan == "NA" && ondemandscan == "NA")
                ($ProtectionStatusRank, $ProtectionStatus) = AntimalwareCommon::ActionRequiredProtectionCode
                protectionStatusAlertArray.push("No On demand Scan or scheduled Scan found in past 7 days, please run an active scan")
            else
                if(ondemandscan != "NA")
                    scanDate = ondemandscan
                    protectionStatusDetailsArray.push("On Demand Scan Date: " + ondemandscan.to_s)
                end
                if(scheduledscan != "NA")
                    scanDate = scheduledscan
                    protectionStatusDetailsArray.push("Scheduled Scan Date: " + scheduledscan.to_s)
                end
            end

            if protectionStatusAlertArray.length == 0
				($ProtectionStatusRank, $ProtectionStatus) = AntimalwareCommon::RealTimeProtectionCode
				protectionStatusDetails += "Sophos is running healthy. "
				protectionStatusDetails += protectionStatusDetailsArray.join('; ')
			else
                protectionStatusDetails += protectionStatusDetailsArray.join('; ')
                protectionStatusDetails += "; "
                protectionStatusDetails += protectionStatusAlertArray.join('; ')
            end
            
		rescue => e
            error += "Getting exception when trying to find Sophos health info: " + e.message + " " + e.backtrace.inspect
			ret["Error"] = error				
        end
        ret["ProtectionStatusRank"] = $ProtectionStatusRank
    	ret["ProtectionStatus"] = $ProtectionStatus
    	ret["ProtectionStatusDetails"] = protectionStatusDetails
    	ret["DetectionId"] = SecureRandom.uuid
    	ret["Threat"] = ""
    	ret["ThreatStatusRank"] = $ThreatStatusRank
    	ret["ThreatStatus"] = $ThreatStatus
		ret["ThreatStatusDetails"] = "Threat Status is currently not supported in Linux Sophos"
		ret["Signature"] = (threatDetectionEngine.nil? || threatDetectionEngine.empty? || threatDetectionEngine == "NA")? "Signature version not found" : threatDetectionEngine
    	ret["ScanDate"] = scanDate
    	ret["DateCollected"] = DateTime.now.strftime("%m/%d/%Y %H:%M")
    	ret["Tool"] = sophosName
		ret["AMProductVersion"] = (sophosVersion.nil? || sophosVersion.empty? || sophosVersion == "NA")? "Sophos version not found" : sophosVersion
		return ret
        
    end
end
