def test_qmd5_hash(self):
        """
        Test MD5 hash from salt+data pass (Qt)
        """
        # WHEN: Given a known salt+data
        hash_ = qmd5_hash(salt=salt.encode('utf-8'), data=pin.encode('utf-8'))

        # THEN: Validate return has is same
        self.assertEquals(hash_, test_hash, 'Qt-MD5 should have returned a good hash')
    def test_qmd5_hash_bad(self):
        """
        Test MD5 hash from salt+hash fail (Qt)
        """
        # WHEN: Given a different salt+hash
        hash_ = qmd5_hash(salt=pin.encode('utf-8'), data=salt.encode('utf-8'))

        # THEN: return data is different
        self.assertNotEquals(hash_, test_hash, 'Qt-MD5 should have returned a bad hash')
Esempio n. 3
0
    def test_qmd5_hash_bad(self):
        """
        Test MD5 hash from salt+hash fail (Qt)
        """
        # WHEN: Given a different salt+hash
        hash_ = qmd5_hash(salt=pin.encode('utf-8'), data=salt.encode('utf-8'))

        # THEN: return data is different
        assert hash_ is not test_hash, 'Qt-MD5 should have returned a bad hash'
Esempio n. 4
0
    def test_qmd5_hash(self):
        """
        Test MD5 hash from salt+data pass (Qt)
        """
        # WHEN: Given a known salt+data
        hash_ = qmd5_hash(salt=salt.encode('utf-8'), data=pin.encode('utf-8'))

        # THEN: Validate return has is same
        assert hash_ == test_hash, 'Qt-MD5 should have returned a good hash'
Esempio n. 5
0
    def test_qmd5_hash_bad(self):
        """
        Test MD5 hash from salt+hash fail (Qt)
        """
        # WHEN: Given a different salt+hash
        hash_ = qmd5_hash(salt=pin.encode('ascii'), data=salt.encode('ascii'))

        # THEN: return data is different
        self.assertNotEquals(hash_.decode('ascii'), test_hash,
                             'Qt-MD5 should have returned a bad hash')
Esempio n. 6
0
    def test_qmd5_hash(self):
        """
        Test MD5 hash from salt+data pass (Qt)
        """
        # WHEN: Given a known salt+data
        hash_ = qmd5_hash(salt=salt.encode('ascii'), data=pin.encode('ascii'))

        # THEN: Validate return has is same
        self.assertEquals(hash_.decode('ascii'), test_hash,
                          'Qt-MD5 should have returned a good hash')
Esempio n. 7
0
    def check_login(self, data=None):
        """
        Processes the initial connection and authentication (if needed).
        Starts poll timer if connection is established.

        NOTE: Qt md5 hash function doesn't work with projector authentication. Use the python md5 hash function.

        :param data: Optional data if called from another routine
        """
        log.debug('({ip}) check_login(data="{data}")'.format(ip=self.ip, data=data))
        if data is None:
            # Reconnected setup?
            if not self.waitForReadyRead(2000):
                # Possible timeout issue
                log.error('({ip}) Socket timeout waiting for login'.format(ip=self.ip))
                self.change_status(E_SOCKET_TIMEOUT)
                return
            read = self.readLine(self.max_size)
            dontcare = self.readLine(self.max_size)  # Clean out the trailing \r\n
            if read is None:
                log.warning('({ip}) read is None - socket error?'.format(ip=self.ip))
                return
            elif len(read) < 8:
                log.warning('({ip}) Not enough data read)'.format(ip=self.ip))
                return
            data = decode(read, 'ascii')
            # Possibility of extraneous data on input when reading.
            # Clean out extraneous characters in buffer.
            dontcare = self.readLine(self.max_size)
            log.debug('({ip}) check_login() read "{data}"'.format(ip=self.ip, data=data.strip()))
        # At this point, we should only have the initial login prompt with
        # possible authentication
        # PJLink initial login will be:
        # 'PJLink 0' - Unauthenticated login - no extra steps required.
        # 'PJLink 1 XXXXXX' Authenticated login - extra processing required.
        if not data.upper().startswith('PJLINK'):
            # Invalid response
            return self.disconnect_from_host()
        if '=' in data:
            # Processing a login reply
            data_check = data.strip().split('=')
        else:
            # Process initial connection
            data_check = data.strip().split(' ')
        log.debug('({ip}) data_check="{data}"'.format(ip=self.ip, data=data_check))
        # Check for projector reporting an error
        if data_check[1].upper() == 'ERRA':
            # Authentication error
            self.disconnect_from_host()
            self.change_status(E_AUTHENTICATION)
            log.debug('({ip}) emitting projectorAuthentication() signal'.format(ip=self.name))
            return
        elif data_check[1] == '0' and self.pin is not None:
            # Pin set and no authentication needed
            log.warning('({ip}) Regular connection but PIN set'.format(ip=self.name))
            self.disconnect_from_host()
            self.change_status(E_AUTHENTICATION)
            log.debug('({ip}) Emitting projectorNoAuthentication() signal'.format(ip=self.name))
            self.projectorNoAuthentication.emit(self.name)
            return
        elif data_check[1] == '1':
            # Authenticated login with salt
            if self.pin is None:
                log.warning('({ip}) Authenticated connection but no pin set'.format(ip=self.name))
                self.disconnect_from_host()
                self.change_status(E_AUTHENTICATION)
                log.debug('({ip}) Emitting projectorAuthentication() signal'.format(ip=self.name))
                self.projectorAuthentication.emit(self.name)
                return
            else:
                log.debug('({ip}) Setting hash with salt="{data}"'.format(ip=self.ip, data=data_check[2]))
                log.debug('({ip}) pin="{data}"'.format(ip=self.ip, data=self.pin))
                data_hash = str(qmd5_hash(salt=data_check[2].encode('utf-8'), data=self.pin.encode('utf-8')),
                                encoding='ascii')
        else:
            data_hash = None
        # We're connected at this point, so go ahead and setup regular I/O
        self.readyRead.connect(self.get_data)
        self.projectorReceivedData.connect(self._send_command)
        # Initial data we should know about
        self.send_command(cmd='CLSS', salt=data_hash)
        self.waitForReadyRead()
        if (not self.no_poll) and (self.state() == self.ConnectedState):
            log.debug('({ip}) Starting timer'.format(ip=self.ip))
            self.timer.setInterval(2000)  # Set 2 seconds for initial information
            self.timer.start()
Esempio n. 8
0
    def check_login(self, data=None):
        """
        Processes the initial connection and authentication (if needed).
        Starts poll timer if connection is established.

        NOTE: Qt md5 hash function doesn't work with projector authentication. Use the python md5 hash function.

        :param data: Optional data if called from another routine
        """
        log.debug('({ip}) check_login(data="{data}")'.format(ip=self.ip, data=data))
        if data is None:
            # Reconnected setup?
            if not self.waitForReadyRead(2000):
                # Possible timeout issue
                log.error('({ip}) Socket timeout waiting for login'.format(ip=self.ip))
                self.change_status(E_SOCKET_TIMEOUT)
                return
            read = self.readLine(self.max_size)
            dontcare = self.readLine(self.max_size)  # Clean out the trailing \r\n
            if read is None:
                log.warning('({ip}) read is None - socket error?'.format(ip=self.ip))
                return
            elif len(read) < 8:
                log.warning('({ip}) Not enough data read)'.format(ip=self.ip))
                return
            data = decode(read, 'ascii')
            # Possibility of extraneous data on input when reading.
            # Clean out extraneous characters in buffer.
            dontcare = self.readLine(self.max_size)
            log.debug('({ip}) check_login() read "{data}"'.format(ip=self.ip, data=data.strip()))
        # At this point, we should only have the initial login prompt with
        # possible authentication
        # PJLink initial login will be:
        # 'PJLink 0' - Unauthenticated login - no extra steps required.
        # 'PJLink 1 XXXXXX' Authenticated login - extra processing required.
        if not data.upper().startswith('PJLINK'):
            # Invalid response
            return self.disconnect_from_host()
        if '=' in data:
            # Processing a login reply
            data_check = data.strip().split('=')
        else:
            # Process initial connection
            data_check = data.strip().split(' ')
        log.debug('({ip}) data_check="{data}"'.format(ip=self.ip, data=data_check))
        # Check for projector reporting an error
        if data_check[1].upper() == 'ERRA':
            # Authentication error
            self.disconnect_from_host()
            self.change_status(E_AUTHENTICATION)
            log.debug('({ip}) emitting projectorAuthentication() signal'.format(ip=self.name))
            return
        elif data_check[1] == '0' and self.pin is not None:
            # Pin set and no authentication needed
            log.warning('({ip}) Regular connection but PIN set'.format(ip=self.name))
            self.disconnect_from_host()
            self.change_status(E_AUTHENTICATION)
            log.debug('({ip}) Emitting projectorNoAuthentication() signal'.format(ip=self.name))
            self.projectorNoAuthentication.emit(self.name)
            return
        elif data_check[1] == '1':
            # Authenticated login with salt
            if self.pin is None:
                log.warning('({ip}) Authenticated connection but no pin set'.format(ip=self.name))
                self.disconnect_from_host()
                self.change_status(E_AUTHENTICATION)
                log.debug('({ip}) Emitting projectorAuthentication() signal'.format(ip=self.name))
                self.projectorAuthentication.emit(self.name)
                return
            else:
                log.debug('({ip}) Setting hash with salt="{data}"'.format(ip=self.ip, data=data_check[2]))
                log.debug('({ip}) pin="{data}"'.format(ip=self.ip, data=self.pin))
                data_hash = str(qmd5_hash(salt=data_check[2].encode('utf-8'), data=self.pin.encode('utf-8')),
                                encoding='ascii')
        else:
            data_hash = None
        # We're connected at this point, so go ahead and setup regular I/O
        self.readyRead.connect(self.get_data)
        self.projectorReceivedData.connect(self._send_command)
        # Initial data we should know about
        self.send_command(cmd='CLSS', salt=data_hash)
        self.waitForReadyRead()
        if (not self.no_poll) and (self.state() == self.ConnectedState):
            log.debug('({ip}) Starting timer'.format(ip=self.ip))
            self.timer.setInterval(2000)  # Set 2 seconds for initial information
            self.timer.start()
Esempio n. 9
0
    def get_data(self, buff, *args, **kwargs):
        """
        Process received data

        :param buff:    Data to process.
        """
        log.debug('({ip}) get_data(buffer="{buff}"'.format(ip=self.entry.name, buff=buff))
        ignore_class = 'ignore_class' in kwargs
        # NOTE: Class2 has changed to some values being UTF-8
        data_in = decode(buff, 'utf-8') if isinstance(buff, bytes) else buff
        data = data_in.strip()
        self.receive_data_signal()
        # Initial packet checks
        if len(data) < 7:
            self._trash_buffer(msg='get_data(): Invalid packet - length')
            return
        elif len(data) > self.max_size:
            self._trash_buffer(msg='get_data(): Invalid packet - too long ({length} bytes)'.format(length=len(data)))
            return
        elif not data.startswith(PJLINK_PREFIX):
            self._trash_buffer(msg='get_data(): Invalid packet - PJLink prefix missing')
            return
        elif data[6] != '=' and data[8] != '=':
            # data[6] = standard command packet
            # data[8] = initial PJLink connection (after mangling)
            self._trash_buffer(msg='get_data(): Invalid reply - Does not have "="')
            return
        log.debug('({ip}) get_data(): Checking new data "{data}"'.format(ip=self.entry.name, data=data))
        header, data = data.split('=')
        log.debug('({ip}) get_data() header="{header}" data="{data}"'.format(ip=self.entry.name,
                                                                             header=header, data=data))
        # At this point, the header should contain:
        #   "PVCCCC"
        #   Where:
        #       P = PJLINK_PREFIX
        #       V = PJLink class or version
        #       C = PJLink command
        version, cmd = header[1], header[2:].upper()
        log.debug('({ip}) get_data() version="{version}" cmd="{cmd}" data="{data}"'.format(ip=self.entry.name,
                                                                                           version=version,
                                                                                           cmd=cmd,
                                                                                           data=data))
        if cmd not in PJLINK_VALID_CMD:
            self._trash_buffer('get_data(): Invalid packet - unknown command "{data}"'.format(data=cmd))
            return
        elif version not in PJLINK_VALID_CMD[cmd]['version']:
            self._trash_buffer(msg='get_data() Command reply version does not match a valid command version')
            return
        elif int(self.pjlink_class) < int(version):
            if not ignore_class:
                log.warning('({ip}) get_data(): Projector returned class reply higher '
                            'than projector stated class'.format(ip=self.entry.name))
                return

        chk = process_command(self, cmd, data)
        if chk is None:
            # Command processed normally and not initial connection, so skip other checks
            return
        # PJLink initial connection checks
        elif chk == S_DATA_OK:
            # Previous command returned OK
            log.debug('({ip}) OK returned - resending command'.format(ip=self.entry.name))
            self.send_command(cmd=cmd, priority=True)
        elif chk == S_CONNECT:
            # Normal connection
            log.debug('({ip}) Connecting normal'.format(ip=self.entry.name))
            self.change_status(S_CONNECTED)
            self.send_command(cmd='CLSS', priority=True)
            self.readyRead.connect(self.get_socket)
        elif chk == S_AUTHENTICATE:
            # Connection with pin
            log.debug('({ip}) Connecting with pin'.format(ip=self.entry.name))
            data_hash = str(qmd5_hash(salt=chk[1].encode('utf-8'), data=self.pin.encode('utf-8')),
                            encoding='ascii')
            self.change_status(S_CONNECTED)
            self.readyRead.connect(self.get_socket)
            self.send_command(cmd='CLSS', salt=data_hash, priority=True)
        elif chk == E_AUTHENTICATION:
            # Projector did not like our pin
            log.warning('({ip}) Failed authentication - disconnecting'.format(ip=self.entry.name))
            self.disconnect_from_host()
            self.projectorAuthentication.emit(self.entry.name)
            self.change_status(status=E_AUTHENTICATION)

        return