diff --git a/src/calibre/devices/smart_device_app/driver.py b/src/calibre/devices/smart_device_app/driver.py index 63119bf79b..5b067b1819 100644 --- a/src/calibre/devices/smart_device_app/driver.py +++ b/src/calibre/devices/smart_device_app/driver.py @@ -88,6 +88,7 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin): SEND_NOOP_EVERY_NTH_PROBE = 5 DISCONNECT_AFTER_N_SECONDS = 30*60 # 30 minutes + BROADCAST_PORTS = [54982, 48123, 39001, 44044, 59678] opcodes = { 'NOOP' : 12, @@ -525,19 +526,27 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin): self.device_socket = None self.is_connected = False - def _attach_to_port(self, port): + def _attach_to_port(self, sock, port): try: self._debug('try port', port) - self.listen_socket.bind(('', port)) + sock.bind(('', port)) except socket.error: self._debug('socket error on port', port) port = 0 except: - self._debug('Unknown exception while allocating listen socket') + self._debug('Unknown exception while attaching port to socket') traceback.print_exc() raise return port + def _close_listen_socket(self): + self.listen_socket.close() + self.listen_socket = None + self.is_connected = False + if getattr(self, 'broadcast_socket', None) is not None: + self.broadcast_socket.close() + self.broadcast_socket = None + # The public interface methods. @synchronous('sync_lock') @@ -569,6 +578,18 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin): except: self._close_device_socket() return (self.is_connected, self) + if getattr(self, 'broadcast_socket', None) is not None: + ans = select.select((self.broadcast_socket,), (), (), 0) + if len(ans[0]) > 0: + try: + packet = self.broadcast_socket.recvfrom(100) + remote = packet[1] + message = str(socket.gethostname().partition('.')[0] + '|') + str(self.port) + self._debug('received broadcast', packet, message) + self.broadcast_socket.sendto(message, remote) + except: + traceback.print_exc() + if getattr(self, 'listen_socket', None) is not None: ans = select.select((self.listen_socket,), (), (), 0) if len(ans[0]) > 0: @@ -976,31 +997,26 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin): message = _('Invalid port in options: %s')% \ self.settings().extra_customization[self.OPT_PORT_NUMBER] self._debug(message) - self.listen_socket.close() - self.listen_socket = None - self.is_connected = False + self._close_listen_socket() return message - port = self._attach_to_port(opt_port) + port = self._attach_to_port(self.listen_socket, opt_port) if port == 0: message = _('Failed to connect to port %d. Try a different value.')%opt_port self._debug(message) - self.listen_socket.close() - self.listen_socket = None - self.is_connected = False + self._close_listen_socket() return message else: while i < 100: # try up to 100 random port numbers i += 1 - port = self._attach_to_port(random.randint(8192, 32000)) + port = self._attach_to_port(self.listen_socket, + random.randint(8192, 32000)) if port != 0: break if port == 0: message = _('Failed to allocate a random port') self._debug(message) - self.listen_socket.close() - self.listen_socket = None - self.is_connected = False + self._close_listen_socket() return message try: @@ -1008,9 +1024,7 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin): except: message = 'listen on port %d failed' % port self._debug(message) - self.listen_socket.close() - self.listen_socket = None - self.is_connected = False + self._close_listen_socket() return message try: @@ -1018,21 +1032,40 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin): except: message = 'registration with bonjour failed' self._debug(message) - self.listen_socket.close() - self.listen_socket = None - self.is_connected = False + self._close_listen_socket() return message self._debug('listening on port', port) self.port = port + # Now try to open a UDP socket to receive broadcasts on + + try: + self.broadcast_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + except: + message = 'creation of broadcast socket failed. This is not fatal.' + self._debug(message) + return message + + for p in self.BROADCAST_PORTS: + port = self._attach_to_port(self.broadcast_socket, p) + if port != 0: + self._debug('broadcast socket listening on port', port) + break + + if port == 0: + self.broadcast_socket.close() + self.broadcast_socket = None + message = 'attaching port to broadcast socket failed. This is not fatal.' + self._debug(message) + return message + + @synchronous('sync_lock') def shutdown(self): if getattr(self, 'listen_socket', None) is not None: do_zeroconf(unpublish_zeroconf, self.port) - self.listen_socket.close() - self.listen_socket = None - self.is_connected = False + self._close_listen_socket() # Methods for dynamic control diff --git a/src/calibre/gui2/actions/device.py b/src/calibre/gui2/actions/device.py index 92fc34b105..a8475c3a3e 100644 --- a/src/calibre/gui2/actions/device.py +++ b/src/calibre/gui2/actions/device.py @@ -255,7 +255,7 @@ class ConnectShareAction(InterfaceAction): use_fixed_port = dm.get_option('smartdevice', 'use_fixed_port') port_number = dm.get_option('smartdevice', 'port_number') if show_port and use_fixed_port: - text = self.share_conn_menu.DEVICE_MSGS[1] + ' [%s port %s]'%( + text = self.share_conn_menu.DEVICE_MSGS[1] + ' [%s, port %s]'%( formatted_addresses, port_number) else: text = self.share_conn_menu.DEVICE_MSGS[1] + ' [' + formatted_addresses + ']' diff --git a/src/calibre/gui2/dialogs/smartdevice.py b/src/calibre/gui2/dialogs/smartdevice.py index e3028cb6e1..810ce67e91 100644 --- a/src/calibre/gui2/dialogs/smartdevice.py +++ b/src/calibre/gui2/dialogs/smartdevice.py @@ -27,7 +27,7 @@ def _cmp_ipaddr(l, r): return cmp(lparts, rparts) -def get_all_ip_addresses(): +def _get_all_ip_addresses(): ip_info = [netifaces.ifaddresses(x).get(netifaces.AF_INET, None) for x in netifaces.interfaces()] @@ -42,9 +42,14 @@ def get_all_ip_addresses(): all_ipaddrs.append(addrs['addr']) all_ipaddrs.sort(cmp=_cmp_ipaddr) - print(all_ipaddrs) return all_ipaddrs +_all_ip_addresses = [] +def get_all_ip_addresses(): + global _all_ip_addresses + if not _all_ip_addresses: + _all_ip_addresses = _get_all_ip_addresses() + return _all_ip_addresses class SmartdeviceDialog(QDialog, Ui_Dialog):