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
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
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)
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 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
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
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
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)
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
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)
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)
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)