示例#1
0
	def __init__(self, frame, plugindir=None):
		self.frame = frame
		log.add("Loading plugin handler")
		self.myUsername = self.frame.np.config.sections["server"]["login"]
		self.plugindirs = []
		self.enabled_plugins = {}
		self.loaded_plugins = {}
		self.type2cast = {'integer':int,'int':int, 'float':float, 'string':str,'str':str, }
		
		if not plugindir:
			if WIN32:
				try:
					mydir = os.path.join(os.environ['APPDATA'], 'nicotine')
				except KeyError:
					# windows 9x?
					mydir,x = os.path.split(sys.argv[0])
				self.plugindir = os.path.join(mydir, "plugins")
			else:
				self.plugindir = os.path.join(os.path.expanduser("~"),'.nicotine','plugins')
		else:
			self.plugindir = plugindir
		try:
			os.makedirs(self.plugindir)
		except:
			pass
		self.plugindirs.append(self.plugindir)
		if os.path.isdir(self.plugindir):
			#self.load_directory(self.plugindir)
			self.load_enabled()
		else:
			log.add("It appears '%s' is not a directory, not loading plugins." % self.plugindir)
    def plugin_settings(self, plugin):
        try:
            if not hasattr(plugin, "settings"):
                return
            if plugin.__id__ not in self.frame.np.config.sections["plugins"]:
                self.frame.np.config.sections["plugins"][
                    plugin.__id__] = plugin.settings
            for i in plugin.settings:
                if i not in self.frame.np.config.sections["plugins"][
                        plugin.__id__]:
                    self.frame.np.config.sections["plugins"][
                        plugin.__id__][i] = plugin.settings[i]
            customsettings = self.frame.np.config.sections["plugins"][
                plugin.__id__]

            for key in customsettings:
                if key in plugin.settings:
                    plugin.settings[key] = customsettings[key]

                else:
                    log.add(
                        _("Stored setting '%(name)s' is no longer present in the plugin"
                          ) % {'name': key})
        except KeyError:
            log.add("No custom settings found for %s" % (plugin.__name__, ))
            pass
示例#3
0
    def __init__(self, frame, plugindir):
        self.frame = frame
        log.add(_("Loading plugin handler"))
        self.myUsername = self.frame.np.config.sections["server"]["login"]
        self.plugindirs = []
        self.enabled_plugins = {}
        self.loaded_plugins = {}
        self.type2cast = {
            'integer': int,
            'int': int,
            'float': float,
            'string': str,
            'str': str
        }

        try:
            os.makedirs(plugindir)
        except:
            pass

        self.plugindirs.append(plugindir)

        if os.path.isdir(plugindir):
            # self.load_directory(self.plugindir)
            self.load_enabled()
        else:
            log.add(
                _("It appears '%s' is not a directory, not loading plugins.") %
                plugindir)
 def _TriggerCommand(self, strfunc, command, source, args):
     for module, plugin in list(self.enabled_plugins.items()):
         try:
             if plugin.PLUGIN is None:
                 continue
             func = eval(strfunc)
             ret = func(command, source, args)
             if ret is not None:
                 if ret == returncode['zap']:
                     return True
                 elif ret == returncode['pass']:
                     pass
                 else:
                     log.add(
                         _("Plugin %(module)s returned something weird, '%(value)s', ignoring"
                           ) % {
                               'module': module,
                               'value': str(ret)
                           })
         except:
             log.add(
                 _("Plugin %(module)s failed with error %(errortype)s: %(error)s.\nTrace: %(trace)s\nProblem area:%(area)s"
                   ) % {
                       'module': module,
                       'errortype': sys.exc_info()[0],
                       'error': sys.exc_info()[1],
                       'trace': ''.join(format_list(extract_stack())),
                       'area': ''.join(
                           format_list(extract_tb(sys.exc_info()[2])))
                   })
     return False
 def TriggerEvent(self, function, args):
     """Triggers an event for the plugins. Since events and notifications
     are precisely the same except for how n+ responds to them, both can be
     triggered by this function."""
     hotpotato = args
     for module, plugin in list(self.enabled_plugins.items()):
         try:
             func = eval("plugin.PLUGIN." + function)
             ret = func(*hotpotato)
             if ret is not None and type(ret) != tupletype:
                 if ret == returncode['zap']:
                     return None
                 elif ret == returncode['break']:
                     return hotpotato
                 elif ret == returncode['pass']:
                     pass
                 else:
                     log.add(_("Plugin %(module)s returned something weird, '%(value)s', ignoring") % {'module': module, 'value': ret})
             if ret is not None:
                 hotpotato = ret
         except:
             log.add(_("Plugin %(module)s failed with error %(errortype)s: %(error)s.\nTrace: %(trace)s\nProblem area:%(area)s") % {
                         'module': module,
                         'errortype': sys.exc_info()[0],
                         'error': sys.exc_info()[1],
                         'trace': ''.join(format_list(extract_stack())),
                         'area': ''.join(format_list(extract_tb(sys.exc_info()[2])))
                     })
     return hotpotato
示例#6
0
	def process_queue(self, queue, conns, connsinprogress, server_socket, maxsockets=MAXFILELIMIT):
		""" Processes messages sent by UI thread. server_socket is a server connection
		socket object, queue holds the messages, conns and connsinprogress 
		are dictionaries holding Connection and PeerConnectionInProgress 
		messages."""
		msgList = []
		needsleep = False
		numsockets = 0
		if server_socket is not None:
			numsockets += 1
		numsockets += len(conns) + len(connsinprogress)
		while not queue.empty():
			msgList.append(queue.get())
		for msgObj in msgList:
			if issubclass(msgObj.__class__, ServerMessage):
				try:
					msg = msgObj.makeNetworkMessage()
					if server_socket in conns:
						conns[server_socket].obuf = conns[server_socket].obuf + struct.pack("<ii", len(msg)+4, self.servercodes[msgObj.__class__]) + msg
					else:
						queue.put(msgObj)
						needsleep = True
				except Exception, error:
					self._ui_callback([_("Error packaging message: %(type)s %(msg_obj)s, %(error)s") %{'type':msgObj.__class__, 'msg_obj':vars(msgObj), 'error': str(error)}])
			elif issubclass(msgObj.__class__, PeerMessage):
				if msgObj.conn in conns:
					# Pack Peer and File and Search Messages 
					if msgObj.__class__ is PierceFireWall:
						conns[msgObj.conn].piercefw = msgObj
						msg = msgObj.makeNetworkMessage()
						conns[msgObj.conn].obuf = conns[msgObj.conn].obuf + struct.pack("<i", len(msg) + 1) + chr(0) + msg
					elif msgObj.__class__ is PeerInit:
						conns[msgObj.conn].init = msgObj
						msg = msgObj.makeNetworkMessage()
						if conns[msgObj.conn].piercefw is None:
							conns[msgObj.conn].obuf = conns[msgObj.conn].obuf + struct.pack("<i", len(msg) + 1) + chr(1) + msg
					
					elif msgObj.__class__ is FileRequest:
						conns[msgObj.conn].filereq = msgObj
						msg = msgObj.makeNetworkMessage()
						conns[msgObj.conn].obuf = conns[msgObj.conn].obuf + msg
						self._ui_callback([msgObj])
					else:
						checkuser = 1
						if msgObj.__class__ is FileSearchResult and msgObj.geoip and self.geoip and self._geoip:
							cc = self.geoip.country_code_by_addr(conns[msgObj.conn].addr[0])
							if not cc and self._geoip[0]:
								checkuser = 0
							elif cc and self._geoip[1][0].find(cc) >= 0:
								checkuser = 0
						if checkuser:
							msg = msgObj.makeNetworkMessage()
							conns[msgObj.conn].obuf += struct.pack("<ii", len(msg) + 4, self.peercodes[msgObj.__class__]) + msg
				else:
					if msgObj.__class__ not in [PeerInit, PierceFireWall, FileSearchResult]:
						message = _("Can't send the message over the closed connection: %(type)s %(msg_obj)s") %{'type':msgObj.__class__, 'msg_obj':vars(msgObj)}
						log.add(message, 3)
示例#7
0
	def processQueue(self):
		while len(self.guiqueue) > 0:
			i = self.guiqueue.pop(0)
			if i['type'] == 'logtext':
				log.add(i['text'])
			elif i['type'] == 'sayprivate':
				# If we use the np the chat lines only show up on the receiving end, we won't see anything ourselves.
				self.frame.privatechats.users[i['user']].SendMessage(i['text'])
			elif i['type'] == 'sendprivate':
				self.frame.privatechats.SendMessage(i['user'], i['text'])
			else:
				log.add(_('Unknown queue item %s: %s' % (i['type'], repr(i))))
		return False
 def disable_plugin(self, pluginname):
     if pluginname not in self.enabled_plugins:
         return
     try:
         plugin = self.enabled_plugins[pluginname]
         log.add(_("Disabled plugin {}".format(plugin.PLUGIN.__name__)))
         del self.enabled_plugins[pluginname]
         plugin.disable(self)
     except:
         print_exc()
         log.addwarning(_("Unable to fully disable plugin %s") % pluginname)
         # common.log_exception(logger)
         return False
     return True
 def disable_plugin(self, pluginname):
     if pluginname not in self.enabled_plugins:
         return
     try:
         plugin = self.enabled_plugins[pluginname]
         log.add(_("Disabled plugin {}".format(plugin.PLUGIN.__name__)))
         del self.enabled_plugins[pluginname]
         plugin.disable(self)
     except:
         print_exc()
         log.addwarning(_("Unable to fully disable plugin %s") % pluginname)
         # common.log_exception(logger)
         return False
     return True
示例#10
0
	def enable_plugin(self, pluginname):
		if pluginname in self.enabled_plugins:
			return
		try:
			plugin = self.load_plugin(pluginname)
			if not plugin: raise Exception("Error loading plugin '%s'" % pluginname)
			plugin.enable(self)
			self.enabled_plugins[pluginname] = plugin
			log.add(_("Loaded plugin %s") % plugin.PLUGIN.__name__)
		except:
			traceback.print_exc()
			log.addwarning(_("Unable to enable plugin %s")%pluginname)
			#common.log_exception(logger)
			return False
		return True
示例#11
0
 def load_plugin(self, pluginname):
     path = self.__findplugin(pluginname)
     if path is None:
         log.add(_("Failed to load plugin '%s', could not find it.") % pluginname)
         return False
     sys.path.insert(0, path)
     plugin = imp.load_source(pluginname, os.path.join(path, '__init__.py'))
     instance = plugin.Plugin(self)
     self.plugin_settings(instance)
     instance.LoadNotification()
     # log.add("Loaded plugin %s (version %s) from %s" % (instance.__name__, instance.__version__, modulename))
     # self.plugins.append((module, instance))
     sys.path = sys.path[1:]
     self.loaded_plugins[pluginname] = plugin
     return plugin
 def enable_plugin(self, pluginname):
     if pluginname in self.enabled_plugins:
         return
     try:
         plugin = self.load_plugin(pluginname)
         if not plugin:
             raise Exception("Error loading plugin '%s'" % pluginname)
         plugin.enable(self)
         self.enabled_plugins[pluginname] = plugin
         log.add(_("Enabled plugin %s") % plugin.PLUGIN.__name__)
     except:
         print_exc()
         log.addwarning(_("Unable to enable plugin %s") % pluginname)
         # common.log_exception(logger)
         return False
     return True
 def processQueue(self):
     while len(self.guiqueue) > 0:
         i = self.guiqueue.pop(0)
         if i['type'] == 'logtext':
             log.add(i['text'])
         elif i['type'] == 'sayprivate':
             # If we use the np the chat lines only show up on the receiving end, we won't see anything ourselves.
             self.frame.privatechats.users[i['user']].SendMessage(i['text'])
         elif i['type'] == 'sendprivate':
             self.frame.privatechats.SendMessage(i['user'], i['text'])
         else:
             log.add(
                 _('Unknown queue item %(type)s: %(item)s' % {
                     'type': i['type'],
                     'item': repr(i)
                 }))
     return False
 def load_plugin(self, pluginname):
     path = self.__findplugin(pluginname)
     if path is None:
         log.add(
             _("Failed to load plugin '%s', could not find it.") %
             pluginname)
         return False
     sys.path.insert(0, path)
     plugin = imp.load_source(pluginname, os.path.join(path, '__init__.py'))
     instance = plugin.Plugin(self)
     self.plugin_settings(instance)
     instance.LoadNotification()
     # log.add("Loaded plugin %s (version %s) from %s" % (instance.__name__, instance.__version__, modulename))
     # self.plugins.append((module, instance))
     sys.path = sys.path[1:]
     self.loaded_plugins[pluginname] = plugin
     return plugin
示例#15
0
	def plugin_settings(self, plugin):
		try:
			#customsettings = self.frame.np.config.sections["plugins"][plugin.__id__]
			if not hasattr(plugin, "settings"):
				return
			if plugin.__id__ not in self.frame.np.config.sections["plugins"]:
				self.frame.np.config.sections["plugins"][plugin.__id__] = plugin.settings
			for i in plugin.settings:
				if i not in self.frame.np.config.sections["plugins"][plugin.__id__]:
					self.frame.np.config.sections["plugins"][plugin.__id__][i] = plugin.settings[i]
			customsettings = self.frame.np.config.sections["plugins"][plugin.__id__]
			#if customsettings = self.frame.np.config.sections["plugins"][plugin.__id__]
			#for settingname, info in plugin.metasettings.items():
				
				#if settingname not in ('<hr>',):
					#settingdescr = info["description"]
					#settingtype = info["type"]
					#try:
						#value = customsettings[settingname]
						#try:
							#if settingtype.startswith('list '):
								#value = list(value)
								#(junk, junk, listtype) = settingtype.partition(' ')
								#index = 0
								#for index in xrange(0, len(value)):
									#value[index] = self.type2cast[listtype](value[index])
							#else:
								#value = self.type2cast[settingtype](value)
								#plugin.settings[settingname] = value
						#except ValueError:
							#log.add(_("Failed to cast the value '%(value)s', stored under '%(name)s', to %(type)s. Using default value." %
								#{'value':value, 'name':settingname, 'type':settingtype}))
						#except KeyError:
							#log.add(_("Unknown setting type '%(type)s'." % {'type':settingtype}))
					#except KeyError:
						#pass
			for key in customsettings:
				if key in plugin.settings:
					plugin.settings[key] = customsettings[key]
					
				else:
					log.add(_("Stored setting '%(name)s' is no longer present in the plugin") % {'name':key})
		except KeyError:
			log.add("No custom settings found for %s" % (plugin.__name__,))
			pass
示例#16
0
    def plugin_settings(self, plugin):
        try:
            if not hasattr(plugin, "settings"):
                return
            if plugin.__id__ not in self.frame.np.config.sections["plugins"]:
                self.frame.np.config.sections["plugins"][plugin.__id__] = plugin.settings
            for i in plugin.settings:
                if i not in self.frame.np.config.sections["plugins"][plugin.__id__]:
                    self.frame.np.config.sections["plugins"][plugin.__id__][i] = plugin.settings[i]
            customsettings = self.frame.np.config.sections["plugins"][plugin.__id__]

            for key in customsettings:
                if key in plugin.settings:
                    plugin.settings[key] = customsettings[key]

                else:
                    log.add(_("Stored setting '%(name)s' is no longer present in the plugin") % {'name': key})
        except KeyError:
            log.add("No custom settings found for %s" % (plugin.__name__,))
            pass
    def __init__(self, frame, plugindir=None):
        self.frame = frame
        log.add(_("Loading plugin handler"))
        self.myUsername = self.frame.np.config.sections["server"]["login"]
        self.plugindirs = []
        self.enabled_plugins = {}
        self.loaded_plugins = {}
        self.type2cast = {
            'integer': int,
            'int': int,
            'float': float,
            'string': str,
            'str': str
        }

        if not plugindir:
            if WIN32:
                try:
                    mydir = os.path.join(os.environ['APPDATA'], 'nicotine')
                except KeyError:
                    # windows 9x?
                    mydir, x = os.path.split(sys.argv[0])
                self.plugindir = os.path.join(mydir, "plugins")
            else:
                self.plugindir = os.path.join(os.path.expanduser("~"),
                                              '.nicotine', 'plugins')
        else:
            self.plugindir = plugindir
        try:
            os.makedirs(self.plugindir)
        except:
            pass
        self.plugindirs.append(self.plugindir)
        if os.path.isdir(self.plugindir):
            # self.load_directory(self.plugindir)
            self.load_enabled()
        else:
            log.add(
                _("It appears '%s' is not a directory, not loading plugins.") %
                self.plugindir)
示例#18
0
	def _TriggerCommand(self, strfunc, command, source, args):
		for module, plugin in self.enabled_plugins.items():
			try:
				if plugin.PLUGIN is None:
					continue
				func = eval(strfunc)
				ret = func(command, source, args)
				if ret is not None:
					if ret == returncode['zap']:
						return True
					elif ret == returncode['pass']:
						pass
					else:
						log.add(_("Plugin %(module)s returned something weird, '%(value)s', ignoring") % {'module':module, 'value':str(ret)})
			except:
				log.add(_("Plugin %(module)s failed with error %(errortype)s: %(error)s.\nTrace: %(trace)s\nProblem area:%(area)s") %
					{'module':module,
					'errortype':sys.exc_info()[0],
					'error':sys.exc_info()[1],
					'trace':''.join(format_list(extract_stack())),
					'area':''.join(format_list(extract_tb(sys.exc_info()[2])))})
		return False
 def TriggerEvent(self, function, args):
     """Triggers an event for the plugins. Since events and notifications
     are precisely the same except for how n+ responds to them, both can be
     triggered by this function."""
     hotpotato = args
     for module, plugin in list(self.enabled_plugins.items()):
         try:
             func = eval("plugin.PLUGIN." + function)
             ret = func(*hotpotato)
             if ret is not None and type(ret) != tupletype:
                 if ret == returncode['zap']:
                     return None
                 elif ret == returncode['break']:
                     return hotpotato
                 elif ret == returncode['pass']:
                     pass
                 else:
                     log.add(
                         _("Plugin %(module)s returned something weird, '%(value)s', ignoring"
                           ) % {
                               'module': module,
                               'value': ret
                           })
             if ret is not None:
                 hotpotato = ret
         except:
             log.add(
                 _("Plugin %(module)s failed with error %(errortype)s: %(error)s.\nTrace: %(trace)s\nProblem area:%(area)s"
                   ) % {
                       'module': module,
                       'errortype': sys.exc_info()[0],
                       'error': sys.exc_info()[1],
                       'trace': ''.join(format_list(extract_stack())),
                       'area': ''.join(
                           format_list(extract_tb(sys.exc_info()[2])))
                   })
     return hotpotato
示例#20
0
					except socket.error, err:
						self._ui_callback([ConnectError(value, err)])
			# Listen / Peer Port
			if p in input[:]:
				try:
					incconn, incaddr = p.accept()
				except:
					time.sleep(0.01)
				else:
					ip, port = self.getIpPort(incaddr)
					if self.ipBlocked(ip):
						message = _("Ignoring connection request from blocked IP Address %(ip)s:%(port)s" % {
							'ip': ip,
							'port': port
						})
						log.add(message, 3)
					else:
						conns[incconn] = PeerConnection(incconn, incaddr, "", "")
						self._ui_callback([IncConn(incconn, incaddr)])
					
			# Manage Connections
			curtime = time.time()
			for connection_in_progress in connsinprogress.keys()[:]:
				if (curtime - connsinprogress[connection_in_progress].lastactive) > self.IN_PROGRESS_STALE_AFTER:
					connection_in_progress.close()
					del connsinprogress[connection_in_progress]
					continue
				try:
					msgObj = connsinprogress[connection_in_progress].msgObj
					if connection_in_progress in input:
						connection_in_progress.recv(0)
示例#21
0
    def AddPortMapping(self, frame, np):
        """Wrapper to redirect the Port Mapping creation to either:

        - The MiniUPnPc binary: upnpc.
        - The python binding to the MiniUPnPc binary: miniupnpc.

        Both method support creating a Port Mapping
        via the UPnP IGDv1 and IGDv2 protocol.

        Need a reference to NicotineFrame to update the interface with the WAN
        external port chosen and connect to the slsk network.
        Also need a reference to the np object to extract the internal LAN
        local from the protothread socket.

        From the UPnP IGD reference:
        http://upnp.org/specs/gw/UPnP-gw-WANIPConnection-v2-Service.pdf

        IGDv1 and IGDV2: AddPortMapping:
        This action creates a new port mapping or overwrites
        an existing mapping with the same internal client.
        If the ExternalPort and PortMappingProtocol pair is already mapped
        to another internal client, an error is returned.

        IGDv1: NewLeaseDuration:
        This argument defines the duration of the port mapping.
        If the value of this argument is 0, it means it's a static port mapping
        that never expire.

        IGDv2: NewLeaseDuration:
        This argument defines the duration of the port mapping.
        The value of this argument MUST be greater than 0.
        A NewLeaseDuration with value 0 means static port mapping,
        but static port mappings can only be created through
        an out-of-band mechanism.
        If this parameter is set to 0, default value of 604800 MUST be used.

        BTW since we don't recheck periodically ports mappings
        while nicotine+ runs, any UPnP port mapping done with IGDv2
        (any modern router does that) will expire after 7 days.
        The client won't be able to send/receive files anymore...
        """

        log.add(_('Creating Port Mapping rule via UPnP...'))

        # Hack to found out the local LAN IP
        # See https://stackoverflow.com/questions/166506/finding-local-ip-addresses-using-pythons-stdlib/28950776#28950776

        # Create a UDP socket
        s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

        # Send a broadcast packet on a local address (doesn't need to be reachable)
        s.connect(('10.255.255.255', 0))

        # This returns the "primary" IP on the local box, even if that IP is a NAT/private/internal IP.
        self.internalipaddress = s.getsockname()[0]

        # Close the socket
        s.close()

        # Store the Local LAN port
        self.internallanport = np.protothread._p.getsockname()[1]

        # The function depends on what method of configuring port mapping is
        # available
        functiontocall = getattr(self, 'AddPortMapping' + self.mode)

        try:
            functiontocall()
        except Exception as e:
            log.addwarning(_('UPnP exception: %(error)s') % {'error': str(e)})
            log.addwarning(
                _('Failed to automate the creation of ' +
                  'UPnP Port Mapping rule.'))
            return

        log.add(
            _('Managed to map external WAN port %(externalwanport)s ' +
              'on your external IP %(externalipaddress)s ' +
              'to your local host %(internalipaddress)s ' +
              'port %(internallanport)s.') % {
                  'externalwanport': self.externalwanport,
                  'externalipaddress': self.externalipaddress,
                  'internalipaddress': self.internalipaddress,
                  'internallanport': self.internallanport
              })

        # Set the external WAN port in the GUI
        frame.networkcallback([slskmessages.IncPort(self.externalwanport)])

        # Establish the connection to the slsk network
        frame.OnConnect(-1)
示例#22
0
    def AddPortMapping(self, frame, np):
        """Wrapper to redirect the Port Mapping creation to either:

        - The MiniUPnPc binary: upnpc.
        - The python binding to the MiniUPnPc binary: miniupnpc.

        Both method support creating a Port Mapping
        via the UPnP IGDv1 and IGDv2 protocol.

        Need a reference to NicotineFrame to update the interface with the WAN
        external port chosen and connect to the slsk network.
        Also need a reference to the np object to extract the internal LAN
        local from the protothread socket.

        From the UPnP IGD reference:
        http://upnp.org/specs/gw/UPnP-gw-WANIPConnection-v2-Service.pdf

        IGDv1 and IGDV2: AddPortMapping:
        This action creates a new port mapping or overwrites
        an existing mapping with the same internal client.
        If the ExternalPort and PortMappingProtocol pair is already mapped
        to another internal client, an error is returned.

        IGDv1: NewLeaseDuration:
        This argument defines the duration of the port mapping.
        If the value of this argument is 0, it means it's a static port mapping
        that never expire.

        IGDv2: NewLeaseDuration:
        This argument defines the duration of the port mapping.
        The value of this argument MUST be greater than 0.
        A NewLeaseDuration with value 0 means static port mapping,
        but static port mappings can only be created through
        an out-of-band mechanism.
        If this parameter is set to 0, default value of 604800 MUST be used.

        BTW since we don't recheck periodically ports mappings
        while nicotine+ runs, any UPnP port mapping done with IGDv2
        (any modern router does that) will expire after 7 days.
        The client won't be able to send/receive files anymore...
        """

        log.add(_('Creating Port Mapping rule via UPnP...'))

        # Local LAN IP
        self.internalipaddress = gethostbyname(gethostname())

        # Store the Local LAN port
        self.internallanport = np.protothread._p.getsockname()[1]

        # The function depends on what method of configuring port mapping is
        # available
        functiontocall = getattr(self, 'AddPortMapping' + self.mode)

        try:
            functiontocall()
        except Exception as e:
            log.addwarning(_('UPnP exception: %(error)s') % {'error': str(e)})
            log.addwarning(
                _('Failed to automate the creation of ' +
                    'UPnP Port Mapping rule.'))
            return

        log.add(
            _('Managed to map external WAN port %(externalwanport)s ' +
                'on your external IP %(externalipaddress)s ' +
                'to your local host %(internalipaddress)s ' +
                'port %(internallanport)s.') %
            {
                'externalwanport': self.externalwanport,
                'externalipaddress': self.externalipaddress,
                'internalipaddress': self.internalipaddress,
                'internallanport': self.internallanport
            }
        )

        # Set the external WAN port in the GUI
        frame.networkcallback([slskmessages.IncPort(self.externalwanport)])

        # Establish the connection to the slsk network
        frame.OnConnect(-1)