def getFilesStreams(self, mtimes, oldmtimes, oldstreams, newsharedfiles, rebuild=False, yieldcall=None): streams = {} shared = self.config.sections["transfers"]["shared"] for directory in mtimes.keys(): virtualdir = self.real2virtual(directory) if self.hiddenCheck({'dir': directory}): continue if not rebuild and directory in oldmtimes: if mtimes[directory] == oldmtimes[directory]: if os.path.exists(directory): # No change try: streams[virtualdir] = oldstreams[virtualdir] continue except KeyError: log.addwarning(_("Inconsistent cache for '%(vdir)s', rebuilding '%(dir)s'") % { 'vdir': virtualdir, 'dir': directory }) else: log.adddebug(_("Dropping missing directory %(dir)s") % {'dir': directory}) continue streams[virtualdir] = self.getDirStream(newsharedfiles[virtualdir]) if yieldcall is not None: yieldcall() return streams
def ok_to_respond(self, room, nick, request, seconds_limit_min=30): self.room = room self.nick = nick self.request = request willing_to_respond = True current_time = time() if room not in list(self.plugin_usage.keys()): self.plugin_usage[room] = { 'last_time': 0, 'last_request': "", 'last_nick': "" } last_time = self.plugin_usage[room]['last_time'] last_nick = self.plugin_usage[room]['last_nick'] last_request = self.plugin_usage[room]['last_request'] port = False try: ip, port = self.frame.np.users[nick].addr except: port = True if nick in self.frame.np.config.sections["server"]["ignorelist"]: willing_to_respond, reason = False, "The nick is ignored" elif self.frame.UserIpIsIgnored(nick): willing_to_respond, reason = False, "The nick's Ip is ignored" elif not port: willing_to_respond, reason = False, "Request likely from simple PHP based griefer bot" elif [nick, request] == [last_nick, last_request]: if (current_time - last_time) < 12 * seconds_limit_min: willing_to_respond, reason = False, "Too soon for same nick to request same resource in room" elif (request == last_request): if (current_time - last_time) < 3 * seconds_limit_min: willing_to_respond, reason = False, "Too soon for different nick to request same resource in room" else: recent_responses = 0 for responded_room in self.plugin_usage: if (current_time - self.plugin_usage[responded_room]['last_time'] ) < seconds_limit_min: recent_responses += 1 if responded_room == room: willing_to_respond, reason = False, "Responded in specified room too recently" break if recent_responses > 3: willing_to_respond, reason = False, "Responded in multiple rooms enough" if self.logging: if not willing_to_respond: base_log_msg = "{} plugin request rejected - room '{}', nick '{}'".format( self.plugin_name, room, nick) log.adddebug("{} - {}".format(base_log_msg, reason)) return willing_to_respond
def getFilesStreamsUnicode(self, mtimes, oldmtimes, oldstreams, newsharedfiles, rebuild=False, yieldcall=None): streams = {} shared = self.config.sections["transfers"]["shared"] for directory in mtimes.keys(): virtualdir = self.real2virtual(directory) # force Unicode for reading from disk u_directory = u"%s" % directory str_directory = str(directory) if self.hiddenCheck({'dir': directory}): continue if directory in oldmtimes and directory not in oldstreams: # Partial information, happened with unicode paths that N+ couldn't handle properly del oldmtimes[directory] if not rebuild and directory in oldmtimes: if mtimes[directory] == oldmtimes[directory]: if os.path.exists(u_directory): # No change try: streams[virtualdir] = oldstreams[virtualdir] continue except KeyError: log.addwarning( _("Inconsistent cache for '%(vdir)s', rebuilding '%(dir)s'" ) % { 'vdir': virtualdir, 'dir': directory }) else: log.adddebug( _("Dropping missing directory %(dir)s") % {'dir': directory}) continue streams[virtualdir] = self.getDirStream(newsharedfiles[virtualdir]) if yieldcall is not None: yieldcall() return streams
def ok_to_respond(self, room, nick, request, seconds_limit_min=30): self.room = room self.nick = nick self.request = request willing_to_respond = True current_time = time() if room not in list(self.plugin_usage.keys()): self.plugin_usage[room] = {'last_time': 0, 'last_request': "", 'last_nick': ""} last_time = self.plugin_usage[room]['last_time'] last_nick = self.plugin_usage[room]['last_nick'] last_request = self.plugin_usage[room]['last_request'] port = False try: ip, port = self.frame.np.users[nick].addr except: port = True if nick in self.frame.np.config.sections["server"]["ignorelist"]: willing_to_respond, reason = False, "The nick is ignored" elif self.frame.UserIpIsIgnored(nick): willing_to_respond, reason = False, "The nick's Ip is ignored" elif not port: willing_to_respond, reason = False, "Request likely from simple PHP based griefer bot" elif [nick, request] == [last_nick, last_request]: if (current_time - last_time) < 12 * seconds_limit_min: willing_to_respond, reason = False, "Too soon for same nick to request same resource in room" elif (request == last_request): if (current_time - last_time) < 3 * seconds_limit_min: willing_to_respond, reason = False, "Too soon for different nick to request same resource in room" else: recent_responses = 0 for responded_room in self.plugin_usage: if (current_time - self.plugin_usage[responded_room]['last_time']) < seconds_limit_min: recent_responses += 1 if responded_room == room: willing_to_respond, reason = False, "Responded in specified room too recently" break if recent_responses > 3: willing_to_respond, reason = False, "Responded in multiple rooms enough" if self.logging: if not willing_to_respond: base_log_msg = "{} plugin request rejected - room '{}', nick '{}'".format(self.plugin_name, room, nick) log.adddebug("{} - {}".format(base_log_msg, reason)) return willing_to_respond
def FindSuitableExternalWANPort(self): """Function to find a suitable external WAN port to map to the client. It will detect if a port mapping to the client already exist. """ # Output format: (ePort, protocol, (intClient, iPort), desc, enabled, # rHost, duration) log.adddebug( 'Existing Port Mappings: %s' % (sorted(self.existingportsmappings, key=lambda tup: tup[0]))) # Analyze ports mappings for m in sorted(self.existingportsmappings, key=lambda tup: tup[0]): (ePort, protocol, (intClient, iPort), desc, enabled, rhost, duration) = m # A Port Mapping is already in place with the client: we will # rewrite it to avoid a timeout on the duration of the mapping if protocol == "TCP" and \ str(intClient) == str(self.internalipaddress) and \ iPort == self.internallanport: log.adddebug('Port Mapping already in place: %s' % str(m)) self.externalwanport = ePort self.foundexistingmapping = True break # If no mapping already in place we try to found a suitable external # WAN port if not self.foundexistingmapping: # Find the first external WAN port > requestedwanport that's not # already reserved tcpportsreserved = [ x[0] for x in sorted(self.existingportsmappings) if x[1] == "TCP" ] while self.externalwanport in tcpportsreserved: if self.externalwanport + 1 <= 65535: self.externalwanport += 1 else: raise AssertionError( _('Failed to find a suitable external WAN port, ' + 'bailing out.'))
def getFilesStreams(self, mtimes, oldmtimes, oldstreams, newsharedfiles, rebuild=False, yieldcall=None): streams = {} shared = self.config.sections["transfers"]["shared"] for directory in mtimes.keys(): virtualdir = self.real2virtual(directory) if self.hiddenCheck({'dir': directory}): continue if not rebuild and directory in oldmtimes: if mtimes[directory] == oldmtimes[directory]: if os.path.exists(directory): # No change try: streams[virtualdir] = oldstreams[virtualdir] continue except KeyError: log.addwarning( _("Inconsistent cache for '%(vdir)s', rebuilding '%(dir)s'" ) % { 'vdir': virtualdir, 'dir': directory }) else: log.adddebug( _("Dropping missing directory %(dir)s") % {'dir': directory}) continue streams[virtualdir] = self.getDirStream(newsharedfiles[virtualdir]) if yieldcall is not None: yieldcall() return streams
def FindSuitableExternalWANPort(self): """Function to find a suitable external WAN port to map to the client. It will detect if a port mapping to the client already exist. """ # Output format: (ePort, protocol, (intClient, iPort), desc, enabled, # rHost, duration) log.adddebug('Existing Port Mappings: %s' % ( sorted(self.existingportsmappings, key=lambda tup: tup[0]))) # Analyze ports mappings for m in sorted(self.existingportsmappings, key=lambda tup: tup[0]): (ePort, protocol, (intClient, iPort), desc, enabled, rhost, duration) = m # A Port Mapping is already in place with the client: we will # rewrite it to avoid a timeout on the duration of the mapping if protocol == "TCP" and \ str(intClient) == str(self.internalipaddress) and \ iPort == self.internallanport: log.adddebug('Port Mapping already in place: %s' % str(m)) self.externalwanport = ePort self.foundexistingmapping = True break # If no mapping already in place we try to found a suitable external # WAN port if not self.foundexistingmapping: # Find the first external WAN port > requestedwanport that's not # already reserved tcpportsreserved = [x[0] for x in sorted( self.existingportsmappings) if x[1] == "TCP"] while self.externalwanport in tcpportsreserved: if self.externalwanport + 1 <= 65535: self.externalwanport += 1 else: raise AssertionError( _('Failed to find a suitable external WAN port, ' + 'bailing out.'))
def getFilesStreamsUnicode(self, mtimes, oldmtimes, oldstreams, newsharedfiles, rebuild=False, yieldcall=None): streams = {} shared = self.config.sections["transfers"]["shared"] for directory in mtimes.keys(): virtualdir = self.real2virtual(directory) # force Unicode for reading from disk u_directory = u"%s" % directory str_directory = str(directory) if self.hiddenCheck({'dir': directory}): continue if directory in oldmtimes and directory not in oldstreams: # Partial information, happened with unicode paths that N+ couldn't handle properly del oldmtimes[directory] if not rebuild and directory in oldmtimes: if mtimes[directory] == oldmtimes[directory]: if os.path.exists(u_directory): # No change try: streams[virtualdir] = oldstreams[virtualdir] continue except KeyError: log.addwarning(_("Inconsistent cache for '%(vdir)s', rebuilding '%(dir)s'") % { 'vdir': virtualdir, 'dir': directory }) else: log.adddebug(_("Dropping missing directory %(dir)s") % {'dir': directory}) continue streams[virtualdir] = self.getDirStream(newsharedfiles[virtualdir]) if yieldcall is not None: yieldcall() return streams
def getFilesListUnicode(self, mtimes, oldmtimes, oldlist, yieldcall=None, progress=None, rebuild=False): """ Get a list of files with their filelength, bitrate and track length in seconds """ list = {} count = 0 for directory in mtimes: directory = os.path.expanduser(directory) virtualdir = self.real2virtual(directory) count += 1 if progress: percent = float(count)/len(mtimes) if percent <= 1.0: gobject.idle_add(progress.set_fraction, percent) # force Unicode for reading from disk u_directory = u"%s" % directory str_directory = str(directory) if self.hiddenCheck({'dir': directory}): continue if directory in oldmtimes and directory not in oldlist: # Partial information, happened with unicode paths that N+ couldn't handle properly del oldmtimes[directory] if not rebuild and directory in oldmtimes: if mtimes[directory] == oldmtimes[directory]: if os.path.exists(directory): try: list[virtualdir] = oldlist[virtualdir] continue except KeyError: log.addwarning(_("Inconsistent cache for '%(vdir)s', rebuilding '%(dir)s'") % { 'vdir': virtualdir, 'dir': directory }) else: log.adddebug(_("Dropping missing directory %(dir)s") % {'dir': directory}) continue list[virtualdir] = [] try: contents = os.listdir(u_directory) except OSError, errtuple: print str(errtuple) self.logMessage(str(errtuple)) continue contents.sort() for filename in contents: if self.hiddenCheck({'dir': directory, 'file': filename}): continue path = os.path.join(directory, filename) s_path = str(path) ppath = unicode(path) s_filename = str(filename) try: # try to force Unicode for reading from disk isfile = os.path.isfile(ppath) except OSError, errtuple: message = _("Scanning Error: %(error)s Path: %(path)s") % {'error': errtuple, 'path': path} print str(message) self.logMessage(message) displayTraceback(sys.exc_info()[2]) continue else: if isfile: # Get the metadata of the file via mutagen data = self.getFileInfoUnicode(s_filename, s_path) if data is not None: list[virtualdir].append(data) if yieldcall is not None: yieldcall()
def AddPortMappingModule(self): """Function to create a Port Mapping via the python binding: miniupnpc. IGDv1: If a Port Mapping already exist: It's updated with a new static port mapping that does not expire. IGDv2: If a Port Mapping already exist: It's updated with a new lease duration of 7 days. """ import miniupnpc u = miniupnpc.UPnP() u.discoverdelay = self.discoverdelay # Discovering devices log.adddebug('Discovering... delay=%sms' % u.discoverdelay) try: log.adddebug('%s device(s) detected' % u.discover()) except Exception as e: raise RuntimeError( _('UPnP exception (should never happen): %(error)s') % {'error': str(e)}) # Select an IGD try: u.selectigd() except Exception as e: raise RuntimeError( _('Cannot select an IGD : %(error)s') % {'error': str(e)}) self.externalipaddress = u.externalipaddress() log.adddebug('IGD selected : External IP address: %s' % (self.externalipaddress)) # Build existing ports mappings list log.adddebug('Listing existing Ports Mappings...') i = 0 while True: p = u.getgenericportmapping(i) if p is None: break self.existingportsmappings.append(p) i += 1 # Find a suitable external WAN port to map to based on the existing # mappings self.FindSuitableExternalWANPort() # Do the port mapping log.adddebug('Trying to redirect %s port %s TCP => %s port %s TCP' % (self.externalipaddress, self.externalwanport, self.internalipaddress, self.internallanport)) try: u.addportmapping(self.externalwanport, 'TCP', self.internalipaddress, self.internallanport, 'Nicotine+', '') except Exception as e: log.adddebug('Failed') raise RuntimeError( _('Failed to map the external WAN port: %(error)s') % {'error': str(e)}) log.adddebug('Success')
def AddPortMappingBinary(self): """Function to create a Port Mapping via MiniUPnPc binary: upnpc. It tries to reconstruct a datastructure identical to what the python module does by parsing the output of the binary. This help to have a bunch of common code to find a suitable external WAN port later. IGDv1: If a Port Mapping already exist: It's updated with a new static port mapping that does not expire. IGDv2: If a Port Mapping already exist: It's updated with a new lease duration of 7 days. """ # Listing existing ports mappings log.adddebug('Listing existing Ports Mappings...') command = [self.upnpcbinary, '-l'] try: output = self.run_binary(command) except Exception as e: raise RuntimeError( _('Failed to use UPnPc binary: %(error)s') % {'error': str(e)}) # Build a list of tuples of the mappings # with the same format as in the python module # (ePort, protocol, (intClient, iPort), desc, enabled, rHost, duration) # (15000, 'TCP', ('192.168.0.1', 2234), 'Nicotine+', '1', '', 0) # # Also get the external WAN IP # # Output format : # ... # ExternalIPAddress = X.X.X.X # ... # i protocol exPort->inAddr:inPort description remoteHost leaseTime # 0 TCP 15000->192.168.0.1:2234 'Nicotine+' '' 0 re_ip = re.compile( r""" ^ ExternalIPAddress \s+ = \s+ (?P<ip> \d+ \. \d+ \. \d+ \. \d+ )? $ """, re.VERBOSE) re_mapping = re.compile( r""" ^ \d+ \s+ (?P<protocol> \w+ ) \s+ (?P<ePort> \d+ ) -> (?P<intClient> \d+ \. \d+ \. \d+ \. \d+ ) : (?P<iPort> \d+ ) \s+ ' (?P<desc> .* ) ' \s+ ' (?P<rHost> .* ) ' \s+ (?P<duration> \d+ ) $ """, re.VERBOSE) for line in output.split('\n'): line = line.strip() ip_match = re.match(re_ip, line) mapping_match = re.match(re_mapping, line) if ip_match: self.externalipaddress = ip_match.group('ip') next if mapping_match: enabled = '1' self.existingportsmappings.append( (int(mapping_match.group('ePort')), mapping_match.group('protocol'), (mapping_match.group('intClient'), int(mapping_match.group('iPort'))), mapping_match.group('desc'), enabled, mapping_match.group('rHost'), int(mapping_match.group('duration')))) # Find a suitable external WAN port to map to based # on the existing mappings self.FindSuitableExternalWANPort() # Do the port mapping log.adddebug('Trying to redirect %s port %s TCP => %s port %s TCP' % (self.externalipaddress, self.externalwanport, self.internalipaddress, self.internallanport)) command = [ self.upnpcbinary, '-e', '"Nicotine+"', '-a', str(self.internalipaddress), str(self.internallanport), str(self.externalwanport), 'TCP' ] try: output = self.run_binary(command) except Exception as e: raise RuntimeError( _('Failed to use UPnPc binary: %(error)s') % {'error': str(e)}) for line in output.split('\n'): if line.startswith("external ") and \ line.find(" is redirected to internal ") > -1: log.adddebug('Success') return if line.find(" failed with code ") > -1: log.adddebug('Failed') raise RuntimeError( _('Failed to map the external WAN port: %(error)s') % {'error': str(line)}) raise AssertionError( _('UPnPc binary failed, could not parse output: %(output)s') % {'output': str(output)})
def getFilesListUnicode(self, mtimes, oldmtimes, oldlist, yieldcall=None, progress=None, rebuild=False): """ Get a list of files with their filelength, bitrate and track length in seconds """ list = {} count = 0 for directory in mtimes: directory = os.path.expanduser(directory) virtualdir = self.real2virtual(directory) count += 1 if progress: percent = float(count) / len(mtimes) if percent <= 1.0: gobject.idle_add(progress.set_fraction, percent) # force Unicode for reading from disk u_directory = u"%s" % directory str_directory = str(directory) if self.hiddenCheck({'dir': directory}): continue if directory in oldmtimes and directory not in oldlist: # Partial information, happened with unicode paths that N+ couldn't handle properly del oldmtimes[directory] if not rebuild and directory in oldmtimes: if mtimes[directory] == oldmtimes[directory]: if os.path.exists(directory): try: list[virtualdir] = oldlist[virtualdir] continue except KeyError: log.addwarning( _("Inconsistent cache for '%(vdir)s', rebuilding '%(dir)s'" ) % { 'vdir': virtualdir, 'dir': directory }) else: log.adddebug( _("Dropping missing directory %(dir)s") % {'dir': directory}) continue list[virtualdir] = [] try: contents = os.listdir(u_directory) except OSError, errtuple: print str(errtuple) self.logMessage(str(errtuple)) continue contents.sort() for filename in contents: if self.hiddenCheck({'dir': directory, 'file': filename}): continue path = os.path.join(directory, filename) s_path = str(path) ppath = unicode(path) s_filename = str(filename) try: # try to force Unicode for reading from disk isfile = os.path.isfile(ppath) except OSError, errtuple: message = _("Scanning Error: %(error)s Path: %(path)s") % { 'error': errtuple, 'path': path } print str(message) self.logMessage(message) displayTraceback(sys.exc_info()[2]) continue else: if isfile: # Get the metadata of the file via mutagen data = self.getFileInfoUnicode(s_filename, s_path) if data is not None: list[virtualdir].append(data) if yieldcall is not None: yieldcall()
def AddPortMappingModule(self): """Function to create a Port Mapping via the python binding: miniupnpc. IGDv1: If a Port Mapping already exist: It's updated with a new static port mapping that does not expire. IGDv2: If a Port Mapping already exist: It's updated with a new lease duration of 7 days. """ import miniupnpc u = miniupnpc.UPnP() u.discoverdelay = self.discoverdelay # Discovering devices log.adddebug('Discovering... delay=%sms' % u.discoverdelay) try: log.adddebug('%s device(s) detected' % u.discover()) except Exception as e: raise RuntimeError( _('UPnP exception (should never happen): %(error)s') % {'error': str(e)}) # Select an IGD try: u.selectigd() except Exception as e: raise RuntimeError( _('Cannot select an IGD : %(error)s') % {'error': str(e)}) self.externalipaddress = u.externalipaddress() log.adddebug('IGD selected : External IP address: %s' % (self.externalipaddress)) # Build existing ports mappings list log.adddebug('Listing existing Ports Mappings...') i = 0 while True: p = u.getgenericportmapping(i) if p is None: break self.existingportsmappings.append(p) i += 1 # Find a suitable external WAN port to map to based on the existing # mappings self.FindSuitableExternalWANPort() # Do the port mapping log.adddebug('Trying to redirect %s port %s TCP => %s port %s TCP' % ( self.externalipaddress, self.externalwanport, self.internalipaddress, self.internallanport ) ) try: u.addportmapping(self.externalwanport, 'TCP', self.internalipaddress, self.internallanport, 'Nicotine+', '') except Exception as e: log.adddebug('Failed') raise RuntimeError( _('Failed to map the external WAN port: %(error)s') % {'error': str(e)} ) log.adddebug('Success')
def AddPortMappingBinary(self): """Function to create a Port Mapping via MiniUPnPc binary: upnpc. It tries to reconstruct a datastructure identical to what the python module does by parsing the output of the binary. This help to have a bunch of common code to find a suitable external WAN port later. IGDv1: If a Port Mapping already exist: It's updated with a new static port mapping that does not expire. IGDv2: If a Port Mapping already exist: It's updated with a new lease duration of 7 days. """ # Listing existing ports mappings log.adddebug('Listing existing Ports Mappings...') command = [self.upnpcbinary, '-l'] try: output = self.run_binary(command) except Exception as e: raise RuntimeError( _('Failed to use UPnPc binary: %(error)s') % {'error': str(e)}) # Build a list of tuples of the mappings # with the same format as in the python module # (ePort, protocol, (intClient, iPort), desc, enabled, rHost, duration) # (15000, 'TCP', ('192.168.0.1', 2234), 'Nicotine+', '1', '', 0) # # Also get the external WAN IP # # Output format : # ... # ExternalIPAddress = X.X.X.X # ... # i protocol exPort->inAddr:inPort description remoteHost leaseTime # 0 TCP 15000->192.168.0.1:2234 'Nicotine+' '' 0 re_ip = re.compile(r""" ^ ExternalIPAddress \s+ = \s+ (?P<ip> \d+ \. \d+ \. \d+ \. \d+ )? $ """, re.VERBOSE) re_mapping = re.compile(r""" ^ \d+ \s+ (?P<protocol> \w+ ) \s+ (?P<ePort> \d+ ) -> (?P<intClient> \d+ \. \d+ \. \d+ \. \d+ ) : (?P<iPort> \d+ ) \s+ ' (?P<desc> .* ) ' \s+ ' (?P<rHost> .* ) ' \s+ (?P<duration> \d+ ) $ """, re.VERBOSE) for line in output.split('\n'): line = line.strip() ip_match = re.match(re_ip, line) mapping_match = re.match(re_mapping, line) if ip_match: self.externalipaddress = ip_match.group('ip') next if mapping_match: enabled = '1' self.existingportsmappings.append( ( int(mapping_match.group('ePort')), mapping_match.group('protocol'), (mapping_match.group('intClient'), int(mapping_match.group('iPort'))), mapping_match.group('desc'), enabled, mapping_match.group('rHost'), int(mapping_match.group('duration')) ) ) # Find a suitable external WAN port to map to based # on the existing mappings self.FindSuitableExternalWANPort() # Do the port mapping log.adddebug('Trying to redirect %s port %s TCP => %s port %s TCP' % ( self.externalipaddress, self.externalwanport, self.internalipaddress, self.internallanport ) ) command = [ self.upnpcbinary, '-e', '"Nicotine+"', '-a', str(self.internalipaddress), str(self.internallanport), str(self.externalwanport), 'TCP' ] try: output = self.run_binary(command) except Exception as e: raise RuntimeError( _('Failed to use UPnPc binary: %(error)s') % {'error': str(e)}) for line in output.split('\n'): if line.startswith("external ") and \ line.find(" is redirected to internal ") > -1: log.adddebug('Success') return if line.find(" failed with code ") > -1: log.adddebug('Failed') raise RuntimeError( _('Failed to map the external WAN port: %(error)s') % {'error': str(line)}) raise AssertionError( _('UPnPc binary failed, could not parse output: %(output)s') % {'output': str(output)})