def uPnPDiscover(service, timeout=2, retries=1):
    group = ("239.255.255.250", 1900)
    message = "\r\n".join([
        "M-SEARCH * HTTP/1.1",
        "HOST: " + group[0] + ":" + RPFrameworkUtils.to_str(group[1]),
        "MAN: ""ssdp:discover""",
        "ST: " + service,"MX: 3","",""])
    socket.setdefaulttimeout(timeout)
    responses = {}
    for _ in range(retries):
        sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
        sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 2)
        sock.sendto(message, group)
        while True:
            try:
                response = SSDPResponse(sock.recv(1024))
                responses[response.location] = response
            except socket.timeout:
                break
    return responses.values()
def uPnPDiscover(service, timeout=3, retries=1):
    group = ("239.255.255.250", 1900)
    message = "\r\n".join([
        "M-SEARCH * HTTP/1.1",
        "HOST: " + group[0] + ":" + RPFrameworkUtils.to_str(group[1]),
        "MAN: ""ssdp:discover""",
        "ST: " + service,"MX: 3","",""])
    socket.setdefaulttimeout(timeout)
    responses = {}
    for _ in range(retries):
        sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
        sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 2)
        sock.sendto(message, group)
        while True:
            try:
                response = SSDPResponse(sock.recv(1024))
                responses[response.location] = response
            except socket.timeout:
                break
    return responses.values()
	def concurrentCommandProcessingThread(self, commandQueue):
		try:
			self.hostPlugin.logDebugMessage(u'Concurrent Processing Thread started for device ' + RPFrameworkUtils.to_unicode(self.indigoDevice.id), RPFrameworkPlugin.DEBUGLEVEL_MED)
		
			# obtain the IP or host address that will be used in connecting to the
			# RESTful service via a function call to allow overrides
			deviceHTTPAddress = self.getRESTfulDeviceAddress()
			if deviceHTTPAddress is None:
				indigo.server.log(u'No IP address specified for device ' + RPFrameworkUtils.to_unicode(self.indigoDevice.id) + u'; ending command processing thread.', isError=True)
				return
			
			# retrieve any configuration information that may have been setup in the
			# plugin configuration and/or device configuration
			updateStatusPollerPropertyName = self.hostPlugin.getGUIConfigValue(self.indigoDevice.deviceTypeId, GUI_CONFIG_RESTFULSTATUSPOLL_INTERVALPROPERTY, u'updateInterval')
			updateStatusPollerInterval = int(self.indigoDevice.pluginProps.get(updateStatusPollerPropertyName, u'90'))
			updateStatusPollerNextRun = None
			updateStatusPollerActionId = self.hostPlugin.getGUIConfigValue(self.indigoDevice.deviceTypeId, GUI_CONFIG_RESTFULSTATUSPOLL_ACTIONID, u'')
			emptyQueueReducedWaitCycles = int(self.hostPlugin.getGUIConfigValue(self.indigoDevice.deviceTypeId, GUI_CONFIG_RESTFULDEV_EMPTYQUEUE_SPEEDUPCYCLES, u'80'))
			
			# spin up the database connection, if this plugin supports databases
			self.dbConn = self.hostPlugin.openDatabaseConnection(self.indigoDevice.deviceTypeId)
			
			# begin the infinite loop which will run as long as the queue contains commands
			# and we have not received an explicit shutdown request
			continueProcessingCommands = True
			lastQueuedCommandCompleted = 0
			while continueProcessingCommands == True:
				# process pending commands now...
				while not commandQueue.empty():
					lenQueue = commandQueue.qsize()
					self.hostPlugin.logDebugMessage(u'Command queue has ' + RPFrameworkUtils.to_unicode(lenQueue) + u' command(s) waiting', RPFrameworkPlugin.DEBUGLEVEL_HIGH)
					
					# the command name will identify what action should be taken... we will handle the known
					# commands and dispatch out to the device implementation, if necessary, to handle unknown
					# commands
					command = commandQueue.get()
					if command.commandName == RPFrameworkCommand.CMD_INITIALIZE_CONNECTION:
						# specialized command to instanciate the concurrent thread
						# safely ignore this... just used to spin up the thread
						self.hostPlugin.logDebugMessage(u'Create connection command de-queued', RPFrameworkPlugin.DEBUGLEVEL_MED)
						
						# if the device supports polling for status, it may be initiated here now; however, we should implement a pause to ensure that
						# devices are created properly (RESTFul devices may respond too fast since no connection need be established)
						statusUpdateStartupDelay = float(self.hostPlugin.getGUIConfigValue(self.indigoDevice.deviceTypeId, GUI_CONFIG_RESTFULSTATUSPOLL_STARTUPDELAY, u'3'))
						if statusUpdateStartupDelay > 0.0:
							commandQueue.put(RPFrameworkCommand.RPFrameworkCommand(RPFrameworkCommand.CMD_PAUSE_PROCESSING, commandPayload=str(statusUpdateStartupDelay)))
						commandQueue.put(RPFrameworkCommand.RPFrameworkCommand(RPFrameworkCommand.CMD_UPDATE_DEVICE_STATUS_FULL, parentAction=updateStatusPollerActionId))
						
					elif command.commandName == RPFrameworkCommand.CMD_TERMINATE_PROCESSING_THREAD:
						# a specialized command designed to stop the processing thread indigo
						# the event of a shutdown						
						continueProcessingCommands = False
						
					elif command.commandName == RPFrameworkCommand.CMD_PAUSE_PROCESSING:
						# the amount of time to sleep should be a float found in the
						# payload of the command
						try:
							pauseTime = float(command.commandPayload)
							self.hostPlugin.logDebugMessage(u'Initiating sleep of ' + RPFrameworkUtils.to_unicode(pauseTime) + u' seconds from command.', RPFrameworkPlugin.DEBUGLEVEL_MED)
							time.sleep(pauseTime)
						except:
							indigo.server.log(u'Invalid pause time requested', isError=True)
							
					elif command.commandName == RPFrameworkCommand.CMD_UPDATE_DEVICE_STATUS_FULL:
						# this command instructs the plugin to update the full status of the device (all statuses
						# that may be read from the device should be read)
						if updateStatusPollerActionId != u'':
							self.hostPlugin.logDebugMessage(u'Executing full status update request...', RPFrameworkPlugin.DEBUGLEVEL_MED)
							self.hostPlugin.executeAction(None, indigoActionId=updateStatusPollerActionId, indigoDeviceId=self.indigoDevice.id, paramValues=None)
							updateStatusPollerNextRun = time.time() + updateStatusPollerInterval
						else:
							self.hostPlugin.logDebugMessage(u'Ignoring status update request, no action specified to update device status', RPFrameworkPlugin.DEBUGLEVEL_HIGH)
							
					elif command.commandName == RPFrameworkCommand.CMD_NETWORKING_WOL_REQUEST:
						# this is a request to send a Wake-On-LAN request to a network-enabled device
						# the command payload should be the MAC address of the device to wake up
						try:
							RPFrameworkNetworkingWOL.sendWakeOnLAN(command.commandPayload)
						except:
							self.hostPlugin.logErrorMessage(u'Failed to send Wake-on-LAN packet')
						
					elif command.commandName == CMD_RESTFUL_GET or command.commandName == CMD_RESTFUL_PUT or command.commandName == CMD_DOWNLOADFILE or command.commandName == CMD_DOWNLOADIMAGE:
						try:
							self.hostPlugin.logDebugMessage(u'Processing GET operation: ' + RPFrameworkUtils.to_unicode(command.commandPayload), RPFrameworkPlugin.DEBUGLEVEL_MED)
							
							# gather all of the parameters from the command payload
							# the payload should have the following format:
							# [0] => request method (http|https|etc.)
							# [1] => path for the GET operation
							# [2] => authentication type: none|basic|digest
							# [3] => username
							# [4] => password
							#
							# CMD_DOWNLOADFILE or CMD_DOWNLOADIMAGE
							# [5] => download filename/path
							# [6] => image resize width
							# [7] => image resize height
							#
							# CMD_RESTFUL_PUT
							# [5] => data to post as the body (if any, may be blank)
							commandPayloadList = command.getPayloadAsList()
							fullGetUrl = commandPayloadList[0] + u'://' + deviceHTTPAddress[0] + u':' + RPFrameworkUtils.to_unicode(deviceHTTPAddress[1]) + commandPayloadList[1]
							
							customHeaders = {}
							self.addCustomHTTPHeaders(customHeaders)
							
							authenticationParam = None
							authenticationType = u'none'
							username = u''
							password = u''
							if len(commandPayloadList) >= 3:
								authenticationType = commandPayloadList[2]
							if len(commandPayloadList) >= 4:
								username = commandPayloadList[3]
							if len(commandPayloadList) >= 5:
								password = commandPayloadList[4]
							if authenticationType != 'none' and username != u'':
								self.hostPlugin.logDebugMessage(u'Using login credentials... Username=> ' + username + u'; Password=>' + RPFrameworkUtils.to_unicode(len(password)) + u' characters long', RPFrameworkPlugin.DEBUGLEVEL_HIGH)
								authenticationParam = (username, password)
							
							# execute the URL fetching depending upon the method requested
							if command.commandName == CMD_RESTFUL_GET or command.commandName == CMD_DOWNLOADFILE or command.commandName == CMD_DOWNLOADIMAGE:
								responseObj = requests.get(fullGetUrl, auth=authenticationParam, headers=customHeaders, verify=False)
							elif command.commandName == CMD_RESTFUL_PUT:
								dataToPost = None
								if len(commandPayloadList) >= 6:
									dataToPost = commandPayloadList[5]
								responseObj = requests.post(fullGetUrl, auth=authenticationParam, headers=customHeaders, verify=False, data=dataToPost)
								
							# if the network command failed then allow the error processor to handle the issue
							if responseObj.status_code == 200:
								# the response handling will depend upon the type of command... binary returns must be
								# handled separately from (expected) text-based ones
								if command.commandName == CMD_DOWNLOADFILE or command.commandName == CMD_DOWNLOADIMAGE:
									# this is a binary return that should be saved to the file system without modification
									if len(commandPayloadList) >= 6:
										saveLocation = commandPayloadList[5]
									
										# execute the actual save from the binary response stream
										try:
											localFile = open(RPFrameworkUtils.to_str(saveLocation), "wb")
											localFile.write(responseObj.content)
											self.hostPlugin.logDebugMessage(u'Command Response: [' + RPFrameworkUtils.to_unicode(responseObj.status_code) + u'] -=- binary data written to ' + RPFrameworkUtils.to_unicode(saveLocation) + u'-=-', RPFrameworkPlugin.DEBUGLEVEL_HIGH)
										
											if command.commandName == CMD_DOWNLOADIMAGE:
												imageResizeWidth = 0
												imageResizeHeight = 0
												if len(command.commandPayload) >= 7:
													imageResizeWidth = int(command.commandPayload[6])
												if len(command.commandPayload) >= 8:
													imageResizeHeight = int(command.commandPayload[7])
								
												resizeCommandLine = u''
												if imageResizeWidth > 0 and imageResizeHeight > 0:
													# we have a specific size as a target...
													resizeCommandLine = u'sips -z ' + RPFrameworkUtils.to_unicode(imageResizeHeight) + u' ' + RPFrameworkUtils.to_unicode(imageResizeWidth) + u' ' + saveLocation
												elif imageResizeWidth > 0:
													# we have a maximum size measurement
													resizeCommandLine = u'sips -Z ' + RPFrameworkUtils.to_unicode(imageResizeWidth) + u' ' + saveLocation
									
												# if a command line has been formed, fire that off now...
												if resizeCommandLine == u'':
													self.hostPlugin.logDebugMessage(u'No image size specified for ' + RPFrameworkUtils.to_unicode(saveLocation) + u'; skipping resize.', RPFrameworkPlugin.DEBUGLEVEL_MED)
												else:
													self.hostPlugin.logDebugMessage(u'Executing resize via command line "' + resizeCommandLine + u'"', RPFrameworkPlugin.DEBUGLEVEL_HIGH)
													try:
														subprocess.Popen(resizeCommandLine, shell=True)
														self.hostPlugin.logDebugMessage(saveLocation + u' resized via sip shell command', RPFrameworkPlugin.DEBUGLEVEL_HIGH)
													except:
														self.hostPlugin.logErrorMessage(u'Error resizing image via sips')
										finally:
											if not localFile is None:
												localFile.close()					
									else:
										indigo.server.log(u'Unable to complete download action - no filename specified', isError=True)
								else:
									# handle this return as a text-based return
									self.hostPlugin.logDebugMessage(u'Command Response: [' + RPFrameworkUtils.to_unicode(responseObj.status_code) + u'] ' + RPFrameworkUtils.to_unicode(responseObj.text), RPFrameworkPlugin.DEBUGLEVEL_HIGH)
									self.hostPlugin.logDebugMessage(command.commandName + u' command completed; beginning response processing', RPFrameworkPlugin.DEBUGLEVEL_HIGH)
									self.handleDeviceTextResponse(responseObj, command)
									self.hostPlugin.logDebugMessage(command.commandName + u' command response processing completed', RPFrameworkPlugin.DEBUGLEVEL_HIGH)
									
							elif responseObj.status_code == 401:
								self.handleRESTfulError(command, u'401 - Unauthorized', responseObj)
							
							else:
								self.handleRESTfulError(command, str(responseObj.status_code), responseObj)
							 	
						except Exception, e:
							self.handleRESTfulError(command, e, responseObj)
						
					elif command.commandName == CMD_SOAP_REQUEST or command.commandName == CMD_JSON_REQUEST:
						responseObj = None
						try:
							# this is to post a SOAP request to a web service... this will be similar to a restful put request
							# but will contain a body payload
							self.hostPlugin.logDebugMessage(u'Received SOAP/JSON command request: ' + command.commandPayload, RPFrameworkPlugin.DEBUGLEVEL_HIGH)
							soapPayloadParser = re.compile("^\s*([^\n]+)\n\s*([^\n]+)\n(.*)$", re.DOTALL)
							soapPayloadData = soapPayloadParser.match(command.commandPayload)
							soapPath = soapPayloadData.group(1).strip()
							soapAction = soapPayloadData.group(2).strip()
							soapBody = soapPayloadData.group(3).strip()							
							fullGetUrl = u'http://' + deviceHTTPAddress[0] + u':' + RPFrameworkUtils.to_str(deviceHTTPAddress[1]) + RPFrameworkUtils.to_str(soapPath)
							self.hostPlugin.logDebugMessage(u'Processing SOAP/JSON operation to ' + fullGetUrl, RPFrameworkPlugin.DEBUGLEVEL_MED)

							customHeaders = {}
							self.addCustomHTTPHeaders(customHeaders)
							if command.commandName == CMD_SOAP_REQUEST:
								customHeaders["Content-type"] = "text/xml; charset=\"UTF-8\""
								customHeaders["SOAPAction"] = RPFrameworkUtils.to_str(soapAction)
							else:
								customHeaders["Content-type"] = "application/json"
							
							# execute the URL post to the web service
							self.hostPlugin.logDebugMessage(u'Sending SOAP/JSON request:\n' + RPFrameworkUtils.to_str(soapBody), RPFrameworkPlugin.DEBUGLEVEL_HIGH)
							responseObj = requests.post(fullGetUrl, headers=customHeaders, verify=False, data=RPFrameworkUtils.to_str(soapBody))
							
							if responseObj.status_code == 200:
								# handle this return as a text-based return
								self.hostPlugin.logDebugMessage(u'Command Response: [' + RPFrameworkUtils.to_unicode(responseObj.status_code) + u'] ' + RPFrameworkUtils.to_unicode(responseObj.text), RPFrameworkPlugin.DEBUGLEVEL_HIGH)
								self.hostPlugin.logDebugMessage(command.commandName + u' command completed; beginning response processing', RPFrameworkPlugin.DEBUGLEVEL_HIGH)
								self.handleDeviceTextResponse(responseObj, command)
								self.hostPlugin.logDebugMessage(command.commandName + u' command response processing completed', RPFrameworkPlugin.DEBUGLEVEL_HIGH)
								
							else:
								self.handleRESTfulError(command, str(responseObj.status_code), responseObj)

						except Exception, e:
							self.handleRESTfulError(command, e, responseObj)
					
					else:
    def concurrentCommandProcessingThread(self, commandQueue):
        try:
            self.hostPlugin.logger.debug(
                u'Concurrent Processing Thread started for device ' +
                RPFrameworkUtils.to_unicode(self.indigoDevice.id))

            # obtain the IP or host address that will be used in connecting to the
            # RESTful service via a function call to allow overrides
            deviceHTTPAddress = self.getRESTfulDeviceAddress()
            if deviceHTTPAddress is None:
                self.hostPlugin.logger.error(
                    u'No IP address specified for device ' +
                    RPFrameworkUtils.to_unicode(self.indigoDevice.id) +
                    u'; ending command processing thread.')
                return

            # retrieve any configuration information that may have been setup in the
            # plugin configuration and/or device configuration
            updateStatusPollerPropertyName = self.hostPlugin.getGUIConfigValue(
                self.indigoDevice.deviceTypeId,
                GUI_CONFIG_RESTFULSTATUSPOLL_INTERVALPROPERTY,
                u'updateInterval')
            updateStatusPollerInterval = int(
                self.indigoDevice.pluginProps.get(
                    updateStatusPollerPropertyName, u'90'))
            updateStatusPollerNextRun = None
            updateStatusPollerActionId = self.hostPlugin.getGUIConfigValue(
                self.indigoDevice.deviceTypeId,
                GUI_CONFIG_RESTFULSTATUSPOLL_ACTIONID, u'')
            emptyQueueReducedWaitCycles = int(
                self.hostPlugin.getGUIConfigValue(
                    self.indigoDevice.deviceTypeId,
                    GUI_CONFIG_RESTFULDEV_EMPTYQUEUE_SPEEDUPCYCLES, u'80'))

            # spin up the database connection, if this plugin supports databases
            self.dbConn = self.hostPlugin.openDatabaseConnection(
                self.indigoDevice.deviceTypeId)

            # begin the infinite loop which will run as long as the queue contains commands
            # and we have not received an explicit shutdown request
            continueProcessingCommands = True
            lastQueuedCommandCompleted = 0
            while continueProcessingCommands == True:
                # process pending commands now...
                while not commandQueue.empty():
                    lenQueue = commandQueue.qsize()
                    self.hostPlugin.logger.threaddebug(
                        u'Command queue has ' +
                        RPFrameworkUtils.to_unicode(lenQueue) +
                        u' command(s) waiting')

                    # the command name will identify what action should be taken... we will handle the known
                    # commands and dispatch out to the device implementation, if necessary, to handle unknown
                    # commands
                    command = commandQueue.get()
                    if command.commandName == RPFrameworkCommand.CMD_INITIALIZE_CONNECTION:
                        # specialized command to instanciate the concurrent thread
                        # safely ignore this... just used to spin up the thread
                        self.hostPlugin.logger.threaddebug(
                            u'Create connection command de-queued')

                        # if the device supports polling for status, it may be initiated here now; however, we should implement a pause to ensure that
                        # devices are created properly (RESTFul devices may respond too fast since no connection need be established)
                        statusUpdateStartupDelay = float(
                            self.hostPlugin.getGUIConfigValue(
                                self.indigoDevice.deviceTypeId,
                                GUI_CONFIG_RESTFULSTATUSPOLL_STARTUPDELAY,
                                u'3'))
                        if statusUpdateStartupDelay > 0.0:
                            commandQueue.put(
                                RPFrameworkCommand.RPFrameworkCommand(
                                    RPFrameworkCommand.CMD_PAUSE_PROCESSING,
                                    commandPayload=str(
                                        statusUpdateStartupDelay)))
                        commandQueue.put(
                            RPFrameworkCommand.RPFrameworkCommand(
                                RPFrameworkCommand.
                                CMD_UPDATE_DEVICE_STATUS_FULL,
                                parentAction=updateStatusPollerActionId))

                    elif command.commandName == RPFrameworkCommand.CMD_TERMINATE_PROCESSING_THREAD:
                        # a specialized command designed to stop the processing thread indigo
                        # the event of a shutdown
                        continueProcessingCommands = False

                    elif command.commandName == RPFrameworkCommand.CMD_PAUSE_PROCESSING:
                        # the amount of time to sleep should be a float found in the
                        # payload of the command
                        try:
                            pauseTime = float(command.commandPayload)
                            self.hostPlugin.logger.threaddebug(
                                u'Initiating sleep of ' +
                                RPFrameworkUtils.to_unicode(pauseTime) +
                                u' seconds from command.')
                            time.sleep(pauseTime)
                        except:
                            self.hostPlugin.logger.warning(
                                u'Invalid pause time requested')

                    elif command.commandName == RPFrameworkCommand.CMD_UPDATE_DEVICE_STATUS_FULL:
                        # this command instructs the plugin to update the full status of the device (all statuses
                        # that may be read from the device should be read)
                        if updateStatusPollerActionId != u'':
                            self.hostPlugin.logger.debug(
                                u'Executing full status update request...')
                            self.hostPlugin.executeAction(
                                None,
                                indigoActionId=updateStatusPollerActionId,
                                indigoDeviceId=self.indigoDevice.id,
                                paramValues=None)
                            updateStatusPollerNextRun = time.time(
                            ) + updateStatusPollerInterval
                        else:
                            self.hostPlugin.logger.threaddebug(
                                u'Ignoring status update request, no action specified to update device status'
                            )

                    elif command.commandName == RPFrameworkCommand.CMD_NETWORKING_WOL_REQUEST:
                        # this is a request to send a Wake-On-LAN request to a network-enabled device
                        # the command payload should be the MAC address of the device to wake up
                        try:
                            RPFrameworkNetworkingWOL.sendWakeOnLAN(
                                command.commandPayload)
                        except:
                            self.hostPlugin.logger.error(
                                u'Failed to send Wake-on-LAN packet')

                    elif command.commandName == CMD_RESTFUL_GET or command.commandName == CMD_RESTFUL_PUT or command.commandName == CMD_DOWNLOADFILE or command.commandName == CMD_DOWNLOADIMAGE:
                        try:
                            self.hostPlugin.logger.debug(
                                u'Processing GET operation: ' +
                                RPFrameworkUtils.to_unicode(
                                    command.commandPayload))

                            # gather all of the parameters from the command payload
                            # the payload should have the following format:
                            # [0] => request method (http|https|etc.)
                            # [1] => path for the GET operation
                            # [2] => authentication type: none|basic|digest
                            # [3] => username
                            # [4] => password
                            #
                            # CMD_DOWNLOADFILE or CMD_DOWNLOADIMAGE
                            # [5] => download filename/path
                            # [6] => image resize width
                            # [7] => image resize height
                            #
                            # CMD_RESTFUL_PUT
                            # [5] => data to post as the body (if any, may be blank)
                            commandPayloadList = command.getPayloadAsList()
                            fullGetUrl = commandPayloadList[
                                0] + u'://' + deviceHTTPAddress[
                                    0] + u':' + RPFrameworkUtils.to_unicode(
                                        deviceHTTPAddress[1]
                                    ) + commandPayloadList[1]

                            customHeaders = {}
                            self.addCustomHTTPHeaders(customHeaders)

                            authenticationParam = None
                            authenticationType = u'none'
                            username = u''
                            password = u''
                            if len(commandPayloadList) >= 3:
                                authenticationType = commandPayloadList[2]
                            if len(commandPayloadList) >= 4:
                                username = commandPayloadList[3]
                            if len(commandPayloadList) >= 5:
                                password = commandPayloadList[4]
                            if authenticationType != 'none' and username != u'':
                                self.hostPlugin.logger.threaddebug(
                                    u'Using login credentials... Username=> ' +
                                    username + u'; Password=>' +
                                    RPFrameworkUtils.to_unicode(len(
                                        password)) + u' characters long')
                                if authenticationType.lower() == 'digest':
                                    self.hostPlugin.logger.threaddebug(
                                        u'Enabling digest authentication')
                                    authenticationParam = HTTPDigestAuth(
                                        username, password)
                                else:
                                    authenticationParam = (username, password)

                            # execute the URL fetching depending upon the method requested
                            if command.commandName == CMD_RESTFUL_GET or command.commandName == CMD_DOWNLOADFILE or command.commandName == CMD_DOWNLOADIMAGE:
                                responseObj = requests.get(
                                    fullGetUrl,
                                    auth=authenticationParam,
                                    headers=customHeaders,
                                    verify=False)
                            elif command.commandName == CMD_RESTFUL_PUT:
                                dataToPost = None
                                if len(commandPayloadList) >= 6:
                                    dataToPost = commandPayloadList[5]
                                responseObj = requests.post(
                                    fullGetUrl,
                                    auth=authenticationParam,
                                    headers=customHeaders,
                                    verify=False,
                                    data=dataToPost)

                            # if the network command failed then allow the error processor to handle the issue
                            if responseObj.status_code == 200:
                                # the response handling will depend upon the type of command... binary returns must be
                                # handled separately from (expected) text-based ones
                                if command.commandName == CMD_DOWNLOADFILE or command.commandName == CMD_DOWNLOADIMAGE:
                                    # this is a binary return that should be saved to the file system without modification
                                    if len(commandPayloadList) >= 6:
                                        saveLocation = commandPayloadList[5]

                                        # execute the actual save from the binary response stream
                                        try:
                                            localFile = open(
                                                RPFrameworkUtils.to_str(
                                                    saveLocation), "wb")
                                            localFile.write(
                                                responseObj.content)
                                            self.hostPlugin.logger.threaddebug(
                                                u'Command Response: [' +
                                                RPFrameworkUtils.to_unicode(
                                                    responseObj.status_code) +
                                                u'] -=- binary data written to '
                                                + RPFrameworkUtils.to_unicode(
                                                    saveLocation) + u'-=-')

                                            if command.commandName == CMD_DOWNLOADIMAGE:
                                                imageResizeWidth = 0
                                                imageResizeHeight = 0
                                                if len(command.commandPayload
                                                       ) >= 7:
                                                    imageResizeWidth = int(
                                                        command.
                                                        commandPayload[6])
                                                if len(command.commandPayload
                                                       ) >= 8:
                                                    imageResizeHeight = int(
                                                        command.
                                                        commandPayload[7])

                                                resizeCommandLine = u''
                                                if imageResizeWidth > 0 and imageResizeHeight > 0:
                                                    # we have a specific size as a target...
                                                    resizeCommandLine = u'sips -z ' + RPFrameworkUtils.to_unicode(
                                                        imageResizeHeight
                                                    ) + u' ' + RPFrameworkUtils.to_unicode(
                                                        imageResizeWidth
                                                    ) + u' ' + saveLocation
                                                elif imageResizeWidth > 0:
                                                    # we have a maximum size measurement
                                                    resizeCommandLine = u'sips -Z ' + RPFrameworkUtils.to_unicode(
                                                        imageResizeWidth
                                                    ) + u' ' + saveLocation

                                                # if a command line has been formed, fire that off now...
                                                if resizeCommandLine == u'':
                                                    self.hostPlugin.logger.debug(
                                                        u'No image size specified for '
                                                        + RPFrameworkUtils.
                                                        to_unicode(
                                                            saveLocation) +
                                                        u'; skipping resize.')
                                                else:
                                                    self.hostPlugin.logger.threaddebug(
                                                        u'Executing resize via command line "'
                                                        + resizeCommandLine +
                                                        u'"')
                                                    try:
                                                        subprocess.Popen(
                                                            resizeCommandLine,
                                                            shell=True)
                                                        self.hostPlugin.logger.debug(
                                                            saveLocation +
                                                            u' resized via sip shell command'
                                                        )
                                                    except:
                                                        self.hostPlugin.logger.error(
                                                            u'Error resizing image via sips'
                                                        )

                                            # we have completed the download and processing successfully... allow the
                                            # device (or its descendants) to process successful operations
                                            self.notifySuccessfulDownload(
                                                command, saveLocation)
                                        finally:
                                            if not localFile is None:
                                                localFile.close()
                                    else:
                                        self.hostPlugin.logger.error(
                                            u'Unable to complete download action - no filename specified'
                                        )
                                else:
                                    # handle this return as a text-based return
                                    self.hostPlugin.logger.threaddebug(
                                        u'Command Response: [' +
                                        RPFrameworkUtils.to_unicode(
                                            responseObj.status_code) + u'] ' +
                                        RPFrameworkUtils.to_unicode(
                                            responseObj.text))
                                    self.hostPlugin.logger.threaddebug(
                                        command.commandName +
                                        u' command completed; beginning response processing'
                                    )
                                    self.handleDeviceTextResponse(
                                        responseObj, command)
                                    self.hostPlugin.logger.threaddebug(
                                        command.commandName +
                                        u' command response processing completed'
                                    )

                            elif responseObj.status_code == 401:
                                self.handleRESTfulError(
                                    command, u'401 - Unauthorized',
                                    responseObj)

                            else:
                                self.handleRESTfulError(
                                    command, str(responseObj.status_code),
                                    responseObj)

                        except Exception, e:
                            # the response value really should not be defined here as it bailed without
                            # catching any of our response error conditions
                            self.handleRESTfulError(command, e, None)

                    elif command.commandName == CMD_SOAP_REQUEST or command.commandName == CMD_JSON_REQUEST:
                        responseObj = None
                        try:
                            # this is to post a SOAP request to a web service... this will be similar to a restful put request
                            # but will contain a body payload
                            self.hostPlugin.logger.threaddebug(
                                u'Received SOAP/JSON command request: ' +
                                command.commandPayload)
                            soapPayloadParser = re.compile(
                                "^\s*([^\n]+)\n\s*([^\n]+)\n(.*)$", re.DOTALL)
                            soapPayloadData = soapPayloadParser.match(
                                command.commandPayload)
                            soapPath = soapPayloadData.group(1).strip()
                            soapAction = soapPayloadData.group(2).strip()
                            soapBody = soapPayloadData.group(3).strip()
                            fullGetUrl = u'http://' + deviceHTTPAddress[
                                0] + u':' + RPFrameworkUtils.to_str(
                                    deviceHTTPAddress[1]
                                ) + RPFrameworkUtils.to_str(soapPath)
                            self.hostPlugin.logger.debug(
                                u'Processing SOAP/JSON operation to ' +
                                fullGetUrl)

                            customHeaders = {}
                            self.addCustomHTTPHeaders(customHeaders)
                            if command.commandName == CMD_SOAP_REQUEST:
                                customHeaders[
                                    "Content-type"] = "text/xml; charset=\"UTF-8\""
                                customHeaders[
                                    "SOAPAction"] = RPFrameworkUtils.to_str(
                                        soapAction)
                            else:
                                customHeaders[
                                    "Content-type"] = "application/json"

                            # execute the URL post to the web service
                            self.hostPlugin.logger.threaddebug(
                                u'Sending SOAP/JSON request:\n' +
                                RPFrameworkUtils.to_unicode(soapBody))
                            self.hostPlugin.logger.threaddebug(
                                u'Using headers: \n' +
                                RPFrameworkUtils.to_unicode(customHeaders))
                            responseObj = requests.post(
                                fullGetUrl,
                                headers=customHeaders,
                                verify=False,
                                data=RPFrameworkUtils.to_str(soapBody))

                            if responseObj.status_code == 200:
                                # handle this return as a text-based return
                                self.hostPlugin.logger.threaddebug(
                                    u'Command Response: [' +
                                    RPFrameworkUtils.to_unicode(
                                        responseObj.status_code) + u'] ' +
                                    RPFrameworkUtils.to_unicode(
                                        responseObj.text))
                                self.hostPlugin.logger.threaddebug(
                                    command.commandName +
                                    u' command completed; beginning response processing'
                                )
                                self.handleDeviceTextResponse(
                                    responseObj, command)
                                self.hostPlugin.logger.threaddebug(
                                    command.commandName +
                                    u' command response processing completed')

                            else:
                                self.hostPlugin.logger.threaddebug(
                                    u'Command Response was not HTTP OK, handling RESTful error'
                                )
                                self.handleRESTfulError(
                                    command, str(responseObj.status_code),
                                    responseObj)

                        except Exception, e:
                            self.handleRESTfulError(command, e, responseObj)

                    else:
    def isValueValid(self, proposedValue):
        # if the value is required but empty then error here
        if proposedValue == None or proposedValue == u'':
            return not self.isRequired

        # now validate that the type is correct...
        if self.paramType == ParamTypeInteger:
            try:
                proposedIntValue = int(proposedValue)
                if proposedIntValue < self.minValue or proposedIntValue > self.maxValue:
                    raise u'Param value not in range'
                return True
            except:
                return False

        elif self.paramType == ParamTypeFloat:
            try:
                proposedFltValue = float(proposedValue)
                if proposedFltValue < self.minValue or proposedFltValue > self.maxValue:
                    raise u'Param value not in range'
                return True
            except:
                return False

        elif self.paramType == ParamTypeBoolean:
            if type(proposedValue) is bool:
                return True
            else:
                return proposedValue.lower() == u'true'

        elif self.paramType == ParamTypeOSDirectoryPath:
            # validate that the path exists... and that it is a directory
            return os.path.isdir(RPFrameworkUtils.to_str(proposedValue))

        elif self.paramType == ParamTypeOSFilePath:
            # validate that the file exists (and that it is a file)
            return os.path.isfile(RPFrameworkUtils.to_str(proposedValue))

        elif self.paramType == ParamTypeIPAddress:
            # validate the IP address using IPv4 standards for now...
            return self.isIPv4Valid(RPFrameworkUtils.to_str(proposedValue))

        elif self.paramType == ParamTypeList:
            # validate that the list contains between the minimum and maximum
            # number of entries
            if len(proposedValue) < self.minValue or len(
                    proposedValue) > self.maxValue:
                return False
            else:
                return True

        else:
            # default is a string value... so this will need to check against the
            # validation expression, if set, and string length
            if self.validationExpression != u'':
                if re.search(self.validationExpression, proposedValue,
                             re.I) == None:
                    return False

            strLength = len(proposedValue)
            if strLength < self.minValue or strLength > self.maxValue:
                return False

            # if string processing makes it here then all is good
            return True
	def isValueValid(self, proposedValue):
		# if the value is required but empty then error here
		if proposedValue == None or proposedValue == u'':
			return not self.isRequired
		
		# now validate that the type is correct...
		if self.paramType == ParamTypeInteger:
			try:
				proposedIntValue = int(proposedValue)
				if proposedIntValue < self.minValue or proposedIntValue > self.maxValue:
					raise u'Param value not in range'
				return True
			except:
				return False
				
		elif self.paramType == ParamTypeFloat:
			try:
				proposedFltValue = float(proposedValue)
				if proposedFltValue < self.minValue or proposedFltValue > self.maxValue:
					raise u'Param value not in range'
				return True
			except:
				return False
				
		elif self.paramType == ParamTypeBoolean:
			if type(proposedValue) is bool:
				return True
			else:
				return proposedValue.lower() == u'true'
			
		elif self.paramType == ParamTypeOSDirectoryPath:
			# validate that the path exists... and that it is a directory
			return os.path.isdir(RPFrameworkUtils.to_str(proposedValue))
			
		elif self.paramType == ParamTypeOSFilePath:
			# validate that the file exists (and that it is a file)
			return os.path.isfile(RPFrameworkUtils.to_str(proposedValue))
		
		elif self.paramType == ParamTypeIPAddress:
			# validate the IP address using IPv4 standards for now...
			return self.isIPv4Valid(RPFrameworkUtils.to_str(proposedValue))
			
		elif self.paramType == ParamTypeList:
			# validate that the list contains between the minimum and maximum
			# number of entries
			if len(proposedValue) < self.minValue or len(proposedValue) > self.maxValue:
				return False
			else:
				return True
			
		else:
			# default is a string value... so this will need to check against the
			# validation expression, if set, and string length
			if self.validationExpression != u'':
				if re.search(self.validationExpression, proposedValue, re.I) == None:
					return False
					
			strLength = len(proposedValue)
			if strLength < self.minValue or strLength > self.maxValue:
				return False
				
			# if string processing makes it here then all is good
			return True