#
# gpcupsnotify.rb
# GoPrint notifier for CUPS 1.2+ integration.
#
# Copyright (c) 2003-2014 GoPrint Systems, Inc.  All rights reserved.
#

require GS4Home + "lib/gpcupsconfig"
require GS4Home + "lib/gpcupslogger"
require 'socket'
require 'timeout'

OPERATION_ATTRIBUTES_TAG = 0x01
JOB_ATTRIBUTES_TAG = 0x02
END_OF_ATTRIBUTES_TAG = 0x03
PRINTER_ATTRIBUTES_TAG = 0x04
UNSUPPORTED_ATTRIBUTES_TAG = 0x05

NOTIFY_JOB_ID = "notify-job-id"
PRINTER_NAME = "printer-name"
NOTIFY_SUBSCRIBED_EVENT = "notify-subscribed-event"
JOB_STATE_REASON = "job-state-reasons"
NOTIFY_TEXT = "notify-text"

class IPPResponse
	# Helper method to read a numeric value
	def readNumeric(input, length)
		value = 0
		for i in 1..length
			value = value << 8
			value += input.getc
		end

		return value
	end

	def readNumericTest(input, length, logFile)
		logFile.puts "Read Test"
		value = 0
		for i in 1..length
			value = value << 8
			value += input.getc
		end
		logFile.puts "End Test"

		return value
	end

	# Process a IPP Response from the given IO
	def processResponse(input)
		@attributes = Hash.new
		@versionNumber = readNumeric(input, 2)
		@operationID = readNumeric(input, 2)
		@requestID = readNumeric(input, 4)

		loop do
			tag = input.getc
			case tag
				when OPERATION_ATTRIBUTES_TAG
				when JOB_ATTRIBUTES_TAG
				when END_OF_ATTRIBUTES_TAG
					break
				when PRINTER_ATTRIBUTES_TAG
				when UNSUPPORTED_ATTRIBUTES_TAG
				when 0x06..0x0F
				else
					# Assume begin attribute tag for now
					nameLength = readNumeric(input, 2)
					name = input.read(nameLength)

					valueLength = readNumeric(input, 2)
					if tag >= 0x20  && tag <= 0x2F
						# Read as an integer
						value = readNumeric(input, valueLength)
						value = value.to_s
					else
						# Read as a string
						value = input.read(valueLength)
					end
					@attributes[name] = value
			end
		end
	end

	attr_reader :versionNumber, :operationID, :requestID, :attributes
end

class GoPrintCUPSNotifier
	include GoPrintCUPSLogger

	def initialize (config)
		@config = config
		super
	end

	def runNotifier(input)
		log("Notification received")
		# Create a new response
    while (1 == 1)
		response = IPPResponse.new
		# Process the response from the input
		response.processResponse(input)

		# Check to ensure the required values were reported
		printer = response.attributes[PRINTER_NAME]
		jobID = response.attributes[NOTIFY_JOB_ID]
		subscribedEvent = response.attributes[NOTIFY_SUBSCRIBED_EVENT]
		jobStateReason = response.attributes[JOB_STATE_REASON]
		# Wrap the notify text in quotes.
		notifyText = "\"" + response.attributes[NOTIFY_TEXT] + "\""

		# If the printer name is not given, fill a dummy printer name.  When jobs are deleted, the printer name is not given.
		if (printer.nil?)
			printer = @config.agentListenerDummyPrinterName
		end
		
		response.attributes.each {|key, value| log("Key: " + key + " Value: " + value) }

		if (!(printer.nil? || jobID.nil? || subscribedEvent.nil? || jobStateReason.nil? || notifyText.nil?))
			agentSocket = nil
			timeout(@config.agentListenerTimeout) do
				# Open the connection to the Agent
				agentSocket = TCPSocket.open('localhost', @config.agentListenerMonitorPort)
				notification = jobID + @config.agentListenerInfoDelimiter + printer + 
					@config.agentListenerInfoDelimiter + subscribedEvent + 
					@config.agentListenerInfoDelimiter + jobStateReason +
					@config.agentListenerInfoDelimiter + notifyText
				log("Sending notification: " + notification)
				# Send the job number and the printer name
				agentSocket.puts notification
				# Close the connection
				agentSocket.close
				# Nil out the agentSocket value
				agentSocket = nil
			end
    end
    end
	rescue
		log("Exception: " + $! + " at " + $@.to_s)
	end
end
