@ -41,13 +41,13 @@ from sabnzbd.utils.happyeyeballs import happyeyeballs
# Timeout penalty in minutes for each cause
# Timeout penalty in minutes for each cause
_PENALTY_UNKNOWN = 3 # Unknown cause
_PENALTY_UNKNOWN = 3 # Unknown cause
_PENALTY_502 = 5 # Unknown 502
_PENALTY_502 = 5 # Unknown 502
_PENALTY_TIMEOUT = 10 # Server doesn't give an answer (multiple times)
_PENALTY_TIMEOUT = 10 # Server doesn't give an answer (multiple times)
_PENALTY_SHARE = 10 # Account sharing detected
_PENALTY_SHARE = 10 # Account sharing detected
_PENALTY_TOOMANY = 10 # Too many connections
_PENALTY_TOOMANY = 10 # Too many connections
_PENALTY_PERM = 10 # Permanent error, like bad username/password
_PENALTY_PERM = 10 # Permanent error, like bad username/password
_PENALTY_SHORT = 1 # Minimal penalty when no_penalties is set
_PENALTY_SHORT = 1 # Minimal penalty when no_penalties is set
_PENALTY_VERYSHORT = 0.1 # Error 400 without cause clues
_PENALTY_VERYSHORT = 0.1 # Error 400 without cause clues
@ -55,9 +55,24 @@ TIMER_LOCK = RLock()
class Server :
class Server :
def __init__ (
def __init__ ( self , server_id , displayname , host , port , timeout , threads , priority , ssl , ssl_verify , ssl_ciphers ,
self ,
send_group , username = None , password = None , optional = False , retention = 0 ) :
server_id ,
displayname ,
host ,
port ,
timeout ,
threads ,
priority ,
ssl ,
ssl_verify ,
ssl_ciphers ,
send_group ,
username = None ,
password = None ,
optional = False ,
retention = 0 ,
) :
self . id = server_id
self . id = server_id
self . newid = None
self . newid = None
@ -82,12 +97,12 @@ class Server:
self . idle_threads = [ ]
self . idle_threads = [ ]
self . active = True
self . active = True
self . bad_cons = 0
self . bad_cons = 0
self . errormsg = ' '
self . errormsg = " "
self . warning = ' '
self . warning = " "
self . info = None # Will hold getaddrinfo() list
self . info = None # Will hold getaddrinfo() list
self . ssl_info = ' ' # Will hold the type and cipher of SSL connection
self . ssl_info = " " # Will hold the type and cipher of SSL connection
self . request = False # True if a getaddrinfo() request is pending
self . request = False # True if a getaddrinfo() request is pending
self . have_body = ' free.xsusenet.com ' not in host
self . have_body = " free.xsusenet.com " not in host
self . have_stat = True # Assume server has "STAT", until proven otherwise
self . have_stat = True # Assume server has "STAT", until proven otherwise
for i in range ( threads ) :
for i in range ( threads ) :
@ -105,30 +120,30 @@ class Server:
# Check if already a successful ongoing connection
# Check if already a successful ongoing connection
if self . busy_threads and self . busy_threads [ 0 ] . nntp :
if self . busy_threads and self . busy_threads [ 0 ] . nntp :
# Re-use that IP
# Re-use that IP
logging . debug ( ' %s : Re-using address %s ' , self . host , self . busy_threads [ 0 ] . nntp . host )
logging . debug ( " %s : Re-using address %s " , self . host , self . busy_threads [ 0 ] . nntp . host )
return self . busy_threads [ 0 ] . nntp . host
return self . busy_threads [ 0 ] . nntp . host
# Determine new IP
# Determine new IP
if cfg . load_balancing ( ) == 0 and self . info :
if cfg . load_balancing ( ) == 0 and self . info :
# Just return the first one, so all next threads use the same IP
# Just return the first one, so all next threads use the same IP
ip = self . info [ 0 ] [ 4 ] [ 0 ]
ip = self . info [ 0 ] [ 4 ] [ 0 ]
logging . debug ( ' %s : Connecting to address %s ' , self . host , ip )
logging . debug ( " %s : Connecting to address %s " , self . host , ip )
elif cfg . load_balancing ( ) == 1 and self . info and len ( self . info ) > 1 :
elif cfg . load_balancing ( ) == 1 and self . info and len ( self . info ) > 1 :
# Return a random entry from the possible IPs
# Return a random entry from the possible IPs
rnd = random . randint ( 0 , len ( self . info ) - 1 )
rnd = random . randint ( 0 , len ( self . info ) - 1 )
ip = self . info [ rnd ] [ 4 ] [ 0 ]
ip = self . info [ rnd ] [ 4 ] [ 0 ]
logging . debug ( ' %s : Connecting to address %s ' , self . host , ip )
logging . debug ( " %s : Connecting to address %s " , self . host , ip )
elif cfg . load_balancing ( ) == 2 and self . info and len ( self . info ) > 1 :
elif cfg . load_balancing ( ) == 2 and self . info and len ( self . info ) > 1 :
# RFC6555 / Happy Eyeballs:
# RFC6555 / Happy Eyeballs:
ip = happyeyeballs ( self . host , port = self . port , ssl = self . ssl )
ip = happyeyeballs ( self . host , port = self . port , ssl = self . ssl )
if ip :
if ip :
logging . debug ( ' %s : Connecting to address %s ' , self . host , ip )
logging . debug ( " %s : Connecting to address %s " , self . host , ip )
else :
else :
# nothing returned, so there was a connection problem
# nothing returned, so there was a connection problem
ip = self . host
ip = self . host
logging . debug ( ' %s : No successful IP connection was possible ' , self . host )
logging . debug ( " %s : No successful IP connection was possible " , self . host )
else :
else :
ip = self . host
ip = self . host
return ip
return ip
@ -152,6 +167,7 @@ class Server:
class Downloader ( Thread ) :
class Downloader ( Thread ) :
""" Singleton Downloader Thread """
""" Singleton Downloader Thread """
do = None
do = None
def __init__ ( self , paused = False ) :
def __init__ ( self , paused = False ) :
@ -187,7 +203,7 @@ class Downloader(Thread):
self . write_fds = { }
self . write_fds = { }
self . servers = [ ]
self . servers = [ ]
self . server_dict = { } # For faster lookups, but is not updated later!
self . server_dict = { } # For faster lookups, but is not updated later!
self . server_nr = 0
self . server_nr = 0
self . _timers = { }
self . _timers = { }
@ -235,8 +251,23 @@ class Downloader(Thread):
break
break
if create and enabled and host and port and threads :
if create and enabled and host and port and threads :
server = Server ( newserver , displayname , host , port , timeout , threads , priority , ssl , ssl_verify ,
server = Server (
ssl_ciphers , send_group , username , password , optional , retention )
newserver ,
displayname ,
host ,
port ,
timeout ,
threads ,
priority ,
ssl ,
ssl_verify ,
ssl_ciphers ,
send_group ,
username ,
password ,
optional ,
retention ,
)
self . servers . append ( server )
self . servers . append ( server )
self . server_dict [ newserver ] = server
self . server_dict [ newserver ] = server
@ -255,7 +286,7 @@ class Downloader(Thread):
# Do not notify when SABnzbd is still starting
# Do not notify when SABnzbd is still starting
if self . paused and sabnzbd . WEB_DIR :
if self . paused and sabnzbd . WEB_DIR :
logging . info ( " Resuming " )
logging . info ( " Resuming " )
notifier . send_notification ( " SABnzbd " , T ( ' Resuming ' ) , ' download ' )
notifier . send_notification ( " SABnzbd " , T ( " Resuming " ) , " download " )
self . paused = False
self . paused = False
@NzbQueueLocker
@NzbQueueLocker
@ -264,7 +295,7 @@ class Downloader(Thread):
if not self . paused :
if not self . paused :
self . paused = True
self . paused = True
logging . info ( " Pausing " )
logging . info ( " Pausing " )
notifier . send_notification ( " SABnzbd " , T ( ' Paused ' ) , ' download ' )
notifier . send_notification ( " SABnzbd " , T ( " Paused " ) , " download " )
if self . is_paused ( ) :
if self . is_paused ( ) :
BPSMeter . do . reset ( )
BPSMeter . do . reset ( )
if cfg . autodisconnect ( ) :
if cfg . autodisconnect ( ) :
@ -289,13 +320,13 @@ class Downloader(Thread):
"""
"""
if value :
if value :
mx = cfg . bandwidth_max . get_int ( )
mx = cfg . bandwidth_max . get_int ( )
if ' % ' in str ( value ) or ( 0 < from_units ( value ) < 101 ) :
if " % " in str ( value ) or ( 0 < from_units ( value ) < 101 ) :
limit = value . strip ( ' % ' )
limit = value . strip ( " % " )
self . bandwidth_perc = from_units ( limit )
self . bandwidth_perc = from_units ( limit )
if mx :
if mx :
self . bandwidth_limit = mx * self . bandwidth_perc / 100
self . bandwidth_limit = mx * self . bandwidth_perc / 100
else :
else :
logging . warning ( T ( ' You must set a maximum bandwidth before you can set a bandwidth limit ' ) )
logging . warning ( T ( " You must set a maximum bandwidth before you can set a bandwidth limit " ) )
else :
else :
self . bandwidth_limit = from_units ( value )
self . bandwidth_limit = from_units ( value )
if mx :
if mx :
@ -347,11 +378,11 @@ class Downloader(Thread):
# Was it resolving problem?
# Was it resolving problem?
if server . info is False :
if server . info is False :
# Warn about resolving issues
# Warn about resolving issues
errormsg = T ( ' Cannot connect to server %s [ %s ] ' ) % ( server . host , T ( ' Server name does not resolve ' ) )
errormsg = T ( " Cannot connect to server %s [ %s ] " ) % ( server . host , T ( " Server name does not resolve " ) )
if server . errormsg != errormsg :
if server . errormsg != errormsg :
server . errormsg = errormsg
server . errormsg = errormsg
logging . warning ( errormsg )
logging . warning ( errormsg )
logging . warning ( T ( ' Server %s will be ignored for %s minutes ' ) , server . host , _PENALTY_TIMEOUT )
logging . warning ( T ( " Server %s will be ignored for %s minutes " ) , server . host , _PENALTY_TIMEOUT )
# Not fully the same as the code below for optional servers
# Not fully the same as the code below for optional servers
server . bad_cons = 0
server . bad_cons = 0
@ -363,7 +394,7 @@ class Downloader(Thread):
if server . optional and server . active and ( server . bad_cons / server . threads ) > 3 :
if server . optional and server . active and ( server . bad_cons / server . threads ) > 3 :
server . bad_cons = 0
server . bad_cons = 0
server . active = False
server . active = False
logging . warning ( T ( ' Server %s will be ignored for %s minutes ' ) , server . host , _PENALTY_TIMEOUT )
logging . warning ( T ( " Server %s will be ignored for %s minutes " ) , server . host , _PENALTY_TIMEOUT )
self . plan_server ( server , _PENALTY_TIMEOUT )
self . plan_server ( server , _PENALTY_TIMEOUT )
# Remove all connections to server
# Remove all connections to server
@ -389,23 +420,27 @@ class Downloader(Thread):
# See if we need to delay because the queues are full
# See if we need to delay because the queues are full
logged = False
logged = False
while not self . shutdown and ( sabnzbd . decoder . Decoder . do . queue_full ( ) or sabnzbd . assembler . Assembler . do . queue_full ( ) ) :
while not self . shutdown and (
sabnzbd . decoder . Decoder . do . queue_full ( ) or sabnzbd . assembler . Assembler . do . queue_full ( )
) :
if not logged :
if not logged :
# Only log once, to not waste any CPU-cycles
# Only log once, to not waste any CPU-cycles
logging . debug ( " Delaying - Decoder queue: %s - Assembler queue: %s " ,
logging . debug (
sabnzbd . decoder . Decoder . do . decoder_queue . qsize ( ) ,
" Delaying - Decoder queue: %s - Assembler queue: %s " ,
sabnzbd . assembler . Assembler . do . queue . qsize ( ) )
sabnzbd . decoder . Decoder . do . decoder_queue . qsize ( ) ,
sabnzbd . assembler . Assembler . do . queue . qsize ( ) ,
)
logged = True
logged = True
time . sleep ( 0.05 )
time . sleep ( 0.05 )
def run ( self ) :
def run ( self ) :
# First check IPv6 connectivity
# First check IPv6 connectivity
sabnzbd . EXTERNAL_IPV6 = sabnzbd . test_ipv6 ( )
sabnzbd . EXTERNAL_IPV6 = sabnzbd . test_ipv6 ( )
logging . debug ( ' External IPv6 test result: %s ' , sabnzbd . EXTERNAL_IPV6 )
logging . debug ( " External IPv6 test result: %s " , sabnzbd . EXTERNAL_IPV6 )
# Then we check SSL certificate checking
# Then we check SSL certificate checking
sabnzbd . CERTIFICATE_VALIDATION = sabnzbd . test_cert_checking ( )
sabnzbd . CERTIFICATE_VALIDATION = sabnzbd . test_cert_checking ( )
logging . debug ( ' SSL verification test: %s ' , sabnzbd . CERTIFICATE_VALIDATION )
logging . debug ( " SSL verification test: %s " , sabnzbd . CERTIFICATE_VALIDATION )
# Kick BPS-Meter to check quota
# Kick BPS-Meter to check quota
BPSMeter . do . update ( )
BPSMeter . do . update ( )
@ -462,7 +497,7 @@ class Downloader(Thread):
if server . retention and article . nzf . nzo . avg_stamp < time . time ( ) - server . retention :
if server . retention and article . nzf . nzo . avg_stamp < time . time ( ) - server . retention :
# Let's get rid of all the articles for this server at once
# Let's get rid of all the articles for this server at once
logging . info ( ' Job %s too old for %s , moving on ' , article . nzf . nzo . final_name , server . host )
logging . info ( " Job %s too old for %s , moving on " , article . nzf . nzo . final_name , server . host )
while article :
while article :
self . decode ( article , None )
self . decode ( article , None )
article = article . nzf . nzo . get_article ( server , self . servers )
article = article . nzf . nzo . get_article ( server , self . servers )
@ -480,7 +515,12 @@ class Downloader(Thread):
logging . info ( " %s @ %s : Initiating connection " , nw . thrdnum , server . host )
logging . info ( " %s @ %s : Initiating connection " , nw . thrdnum , server . host )
nw . init_connect ( self . write_fds )
nw . init_connect ( self . write_fds )
except :
except :
logging . error ( T ( ' Failed to initialize %s @ %s with reason: %s ' ) , nw . thrdnum , server . host , sys . exc_info ( ) [ 1 ] )
logging . error (
T ( " Failed to initialize %s @ %s with reason: %s " ) ,
nw . thrdnum ,
server . host ,
sys . exc_info ( ) [ 1 ] ,
)
self . __reset_nw ( nw , " failed to initialize " )
self . __reset_nw ( nw , " failed to initialize " )
# Exit-point
# Exit-point
@ -530,7 +570,7 @@ class Downloader(Thread):
# Now let's check if it was stable in the last 10 seconds
# Now let's check if it was stable in the last 10 seconds
self . can_be_slowed = BPSMeter . do . get_stable_speed ( timespan = 10 )
self . can_be_slowed = BPSMeter . do . get_stable_speed ( timespan = 10 )
self . can_be_slowed_timer = 0
self . can_be_slowed_timer = 0
logging . debug ( ' Downloader-slowdown: %r ' , self . can_be_slowed )
logging . debug ( " Downloader-slowdown: %r " , self . can_be_slowed )
else :
else :
read , write , error = ( [ ] , [ ] , [ ] )
read , write , error = ( [ ] , [ ] , [ ] )
@ -540,8 +580,11 @@ class Downloader(Thread):
time . sleep ( 1.0 )
time . sleep ( 1.0 )
DOWNLOADER_CV . acquire ( )
DOWNLOADER_CV . acquire ( )
while ( sabnzbd . nzbqueue . NzbQueue . do . is_empty ( ) or self . is_paused ( ) or self . postproc ) and not \
while (
self . shutdown and not self . __restart :
( sabnzbd . nzbqueue . NzbQueue . do . is_empty ( ) or self . is_paused ( ) or self . postproc )
and not self . shutdown
and not self . __restart
) :
DOWNLOADER_CV . wait ( )
DOWNLOADER_CV . wait ( )
DOWNLOADER_CV . release ( )
DOWNLOADER_CV . release ( )
@ -599,7 +642,9 @@ class Downloader(Thread):
try :
try :
nw . finish_connect ( nw . status_code )
nw . finish_connect ( nw . status_code )
if sabnzbd . LOG_ALL :
if sabnzbd . LOG_ALL :
logging . debug ( " %s @ %s last message -> %s " , nw . thrdnum , nw . server . host , nntp_to_msg ( nw . data ) )
logging . debug (
" %s @ %s last message -> %s " , nw . thrdnum , nw . server . host , nntp_to_msg ( nw . data )
)
nw . clear_data ( )
nw . clear_data ( )
except NNTPPermanentError as error :
except NNTPPermanentError as error :
# Handle login problems
# Handle login problems
@ -607,45 +652,45 @@ class Downloader(Thread):
penalty = 0
penalty = 0
msg = error . response
msg = error . response
ecode = int_conv ( msg [ : 3 ] )
ecode = int_conv ( msg [ : 3 ] )
display_msg = ' [ %s ] ' % msg
display_msg = " [ %s ] " % msg
logging . debug ( ' Server login problem: %s , %s ' , ecode , msg )
logging . debug ( " Server login problem: %s , %s " , ecode , msg )
if ecode in ( 502 , 400 , 481 , 482 ) and clues_too_many ( msg ) :
if ecode in ( 502 , 400 , 481 , 482 ) and clues_too_many ( msg ) :
# Too many connections: remove this thread and reduce thread-setting for server
# Too many connections: remove this thread and reduce thread-setting for server
# Plan to go back to the full number after a penalty timeout
# Plan to go back to the full number after a penalty timeout
if server . active :
if server . active :
errormsg = T ( ' Too many connections to server %s ' ) % display_msg
errormsg = T ( " Too many connections to server %s " ) % display_msg
if server . errormsg != errormsg :
if server . errormsg != errormsg :
server . errormsg = errormsg
server . errormsg = errormsg
logging . warning ( T ( ' Too many connections to server %s ' ) , server . host )
logging . warning ( T ( " Too many connections to server %s " ) , server . host )
self . __reset_nw ( nw , None , warn = False , destroy = True , send_quit = True )
self . __reset_nw ( nw , None , warn = False , destroy = True , send_quit = True )
self . plan_server ( server , _PENALTY_TOOMANY )
self . plan_server ( server , _PENALTY_TOOMANY )
server . threads - = 1
server . threads - = 1
elif ecode in ( 502 , 481 , 482 ) and clues_too_many_ip ( msg ) :
elif ecode in ( 502 , 481 , 482 ) and clues_too_many_ip ( msg ) :
# Account sharing?
# Account sharing?
if server . active :
if server . active :
errormsg = T ( ' Probable account sharing ' ) + display_msg
errormsg = T ( " Probable account sharing " ) + display_msg
if server . errormsg != errormsg :
if server . errormsg != errormsg :
server . errormsg = errormsg
server . errormsg = errormsg
name = ' ( %s ) ' % server . host
name = " ( %s ) " % server . host
logging . warning ( T ( ' Probable account sharing ' ) + name )
logging . warning ( T ( " Probable account sharing " ) + name )
penalty = _PENALTY_SHARE
penalty = _PENALTY_SHARE
block = True
block = True
elif ecode in ( 452 , 481 , 482 , 381 ) or ( ecode == 502 and clues_login ( msg ) ) :
elif ecode in ( 452 , 481 , 482 , 381 ) or ( ecode == 502 and clues_login ( msg ) ) :
# Cannot login, block this server
# Cannot login, block this server
if server . active :
if server . active :
errormsg = T ( ' Failed login for server %s ' ) % display_msg
errormsg = T ( " Failed login for server %s " ) % display_msg
if server . errormsg != errormsg :
if server . errormsg != errormsg :
server . errormsg = errormsg
server . errormsg = errormsg
logging . error ( T ( ' Failed login for server %s ' ) , server . host )
logging . error ( T ( " Failed login for server %s " ) , server . host )
penalty = _PENALTY_PERM
penalty = _PENALTY_PERM
block = True
block = True
elif ecode in ( 502 , 482 ) :
elif ecode in ( 502 , 482 ) :
# Cannot connect (other reasons), block this server
# Cannot connect (other reasons), block this server
if server . active :
if server . active :
errormsg = T ( ' Cannot connect to server %s [ %s ] ' ) % ( ' ' , display_msg )
errormsg = T ( " Cannot connect to server %s [ %s ] " ) % ( " " , display_msg )
if server . errormsg != errormsg :
if server . errormsg != errormsg :
server . errormsg = errormsg
server . errormsg = errormsg
logging . warning ( T ( ' Cannot connect to server %s [ %s ] ' ) , server . host , msg )
logging . warning ( T ( " Cannot connect to server %s [ %s ] " ) , server . host , msg )
if clues_pay ( msg ) :
if clues_pay ( msg ) :
penalty = _PENALTY_PERM
penalty = _PENALTY_PERM
else :
else :
@ -654,16 +699,16 @@ class Downloader(Thread):
elif ecode == 400 :
elif ecode == 400 :
# Temp connection problem?
# Temp connection problem?
if server . active :
if server . active :
logging . debug ( ' Unspecified error 400 from server %s ' , server . host )
logging . debug ( " Unspecified error 400 from server %s " , server . host )
penalty = _PENALTY_VERYSHORT
penalty = _PENALTY_VERYSHORT
block = True
block = True
else :
else :
# Unknown error, just keep trying
# Unknown error, just keep trying
if server . active :
if server . active :
errormsg = T ( ' Cannot connect to server %s [ %s ] ' ) % ( ' ' , display_msg )
errormsg = T ( " Cannot connect to server %s [ %s ] " ) % ( " " , display_msg )
if server . errormsg != errormsg :
if server . errormsg != errormsg :
server . errormsg = errormsg
server . errormsg = errormsg
logging . warning ( T ( ' Cannot connect to server %s [ %s ] ' ) , server . host , msg )
logging . warning ( T ( " Cannot connect to server %s [ %s ] " ) , server . host , msg )
penalty = _PENALTY_UNKNOWN
penalty = _PENALTY_UNKNOWN
block = True
block = True
if block or ( penalty and server . optional ) :
if block or ( penalty and server . optional ) :
@ -675,8 +720,12 @@ class Downloader(Thread):
self . __reset_nw ( nw , None , warn = False , send_quit = True )
self . __reset_nw ( nw , None , warn = False , send_quit = True )
continue
continue
except :
except :
logging . error ( T ( ' Connecting %s @ %s failed, message= %s ' ) ,
logging . error (
nw . thrdnum , nw . server . host , nntp_to_msg ( nw . data ) )
T ( " Connecting %s @ %s failed, message= %s " ) ,
nw . thrdnum ,
nw . server . host ,
nntp_to_msg ( nw . data ) ,
)
# No reset-warning needed, above logging is sufficient
# No reset-warning needed, above logging is sufficient
self . __reset_nw ( nw , None , warn = False )
self . __reset_nw ( nw , None , warn = False )
@ -686,7 +735,7 @@ class Downloader(Thread):
elif nw . status_code == 223 :
elif nw . status_code == 223 :
done = True
done = True
logging . debug ( ' Article < %s > is present ' , article . article )
logging . debug ( " Article < %s > is present " , article . article )
elif nw . status_code == 211 :
elif nw . status_code == 211 :
done = False
done = False
@ -697,27 +746,32 @@ class Downloader(Thread):
elif nw . status_code in ( 411 , 423 , 430 ) :
elif nw . status_code in ( 411 , 423 , 430 ) :
done = True
done = True
logging . debug ( ' Thread %s @ %s : Article %s missing (error= %s ) ' ,
logging . debug (
nw . thrdnum , nw . server . host , article . article , nw . status_code )
" Thread %s @ %s : Article %s missing (error= %s ) " ,
nw . thrdnum ,
nw . server . host ,
article . article ,
nw . status_code ,
)
nw . clear_data ( )
nw . clear_data ( )
elif nw . status_code == 500 :
elif nw . status_code == 500 :
if nzo . precheck :
if nzo . precheck :
# Assume "STAT" command is not supported
# Assume "STAT" command is not supported
server . have_stat = False
server . have_stat = False
logging . debug ( ' Server %s does not support STAT ' , server . host )
logging . debug ( " Server %s does not support STAT " , server . host )
else :
else :
# Assume "BODY" command is not supported
# Assume "BODY" command is not supported
server . have_body = False
server . have_body = False
logging . debug ( ' Server %s does not support BODY ' , server . host )
logging . debug ( " Server %s does not support BODY " , server . host )
nw . clear_data ( )
nw . clear_data ( )
self . __request_article ( nw )
self . __request_article ( nw )
if done :
if done :
server . bad_cons = 0 # Successful data, clear "bad" counter
server . bad_cons = 0 # Successful data, clear "bad" counter
server . errormsg = server . warning = ' '
server . errormsg = server . warning = " "
if sabnzbd . LOG_ALL :
if sabnzbd . LOG_ALL :
logging . debug ( ' Thread %s @ %s : %s done ' , nw . thrdnum , server . host , article . article )
logging . debug ( " Thread %s @ %s : %s done " , nw . thrdnum , server . host , article . article )
self . decode ( article , nw . data )
self . decode ( article , nw . data )
nw . soft_reset ( )
nw . soft_reset ( )
@ -749,9 +803,9 @@ class Downloader(Thread):
if warn and reset_msg :
if warn and reset_msg :
server . warning = reset_msg
server . warning = reset_msg
logging . info ( ' Thread %s @ %s : %s ' , nw . thrdnum , server . host , reset_msg )
logging . info ( " Thread %s @ %s : %s " , nw . thrdnum , server . host , reset_msg )
elif reset_msg :
elif reset_msg :
logging . debug ( ' Thread %s @ %s : %s ' , nw . thrdnum , server . host , reset_msg )
logging . debug ( " Thread %s @ %s : %s " , nw . thrdnum , server . host , reset_msg )
if nw in server . busy_threads :
if nw in server . busy_threads :
server . busy_threads . remove ( nw )
server . busy_threads . remove ( nw )
@ -777,7 +831,7 @@ class Downloader(Thread):
nw . hard_reset ( wait , send_quit = send_quit )
nw . hard_reset ( wait , send_quit = send_quit )
# Empty SSL info, it might change on next connect
# Empty SSL info, it might change on next connect
server . ssl_info = ' '
server . ssl_info = " "
def __request_article ( self , nw ) :
def __request_article ( self , nw ) :
try :
try :
@ -785,25 +839,25 @@ class Downloader(Thread):
if nw . server . send_group and nzo . group != nw . group :
if nw . server . send_group and nzo . group != nw . group :
group = nzo . group
group = nzo . group
if sabnzbd . LOG_ALL :
if sabnzbd . LOG_ALL :
logging . debug ( ' Thread %s @ %s : GROUP < %s > ' , nw . thrdnum , nw . server . host , group )
logging . debug ( " Thread %s @ %s : GROUP < %s > " , nw . thrdnum , nw . server . host , group )
nw . send_group ( group )
nw . send_group ( group )
else :
else :
if sabnzbd . LOG_ALL :
if sabnzbd . LOG_ALL :
logging . debug ( ' Thread %s @ %s : BODY %s ' , nw . thrdnum , nw . server . host , nw . article . article )
logging . debug ( " Thread %s @ %s : BODY %s " , nw . thrdnum , nw . server . host , nw . article . article )
nw . body ( nzo . precheck )
nw . body ( nzo . precheck )
fileno = nw . nntp . sock . fileno ( )
fileno = nw . nntp . sock . fileno ( )
if fileno not in self . read_fds :
if fileno not in self . read_fds :
self . read_fds [ fileno ] = nw
self . read_fds [ fileno ] = nw
except socket . error as err :
except socket . error as err :
logging . info ( ' Looks like server closed connection: %s ' , err )
logging . info ( " Looks like server closed connection: %s " , err )
self . __reset_nw ( nw , " server broke off connection " , send_quit = False )
self . __reset_nw ( nw , " server broke off connection " , send_quit = False )
except :
except :
logging . error ( T ( ' Suspect error in downloader ' ) )
logging . error ( T ( " Suspect error in downloader " ) )
logging . info ( " Traceback: " , exc_info = True )
logging . info ( " Traceback: " , exc_info = True )
self . __reset_nw ( nw , " server broke off connection " , send_quit = False )
self . __reset_nw ( nw , " server broke off connection " , send_quit = False )
#------------------------------------------------------------------------------
# ------------------------------------------------------------------------------
# Timed restart of servers admin.
# Timed restart of servers admin.
# For each server all planned events are kept in a list.
# For each server all planned events are kept in a list.
# When the first timer of a server fires, all other existing timers
# When the first timer of a server fires, all other existing timers
@ -817,7 +871,7 @@ class Downloader(Thread):
# Overwrite in case of no_penalties
# Overwrite in case of no_penalties
interval = _PENALTY_SHORT
interval = _PENALTY_SHORT
logging . debug ( ' Set planned server resume %s in %s mins ' , server . host , interval )
logging . debug ( " Set planned server resume %s in %s mins " , server . host , interval )
if server . id not in self . _timers :
if server . id not in self . _timers :
self . _timers [ server . id ] = [ ]
self . _timers [ server . id ] = [ ]
stamp = time . time ( ) + 60.0 * interval
stamp = time . time ( ) + 60.0 * interval
@ -828,7 +882,7 @@ class Downloader(Thread):
@synchronized ( TIMER_LOCK )
@synchronized ( TIMER_LOCK )
def trigger_server ( self , server_id , timestamp ) :
def trigger_server ( self , server_id , timestamp ) :
""" Called by scheduler, start server if timer still valid """
""" Called by scheduler, start server if timer still valid """
logging . debug ( ' Trigger planned server resume for server-id %s ' , server_id )
logging . debug ( " Trigger planned server resume for server-id %s " , server_id )
if server_id in self . _timers :
if server_id in self . _timers :
if timestamp in self . _timers [ server_id ] :
if timestamp in self . _timers [ server_id ] :
del self . _timers [ server_id ]
del self . _timers [ server_id ]
@ -845,7 +899,7 @@ class Downloader(Thread):
# Activate server if it was inactive
# Activate server if it was inactive
for server in self . servers :
for server in self . servers :
if server . id == server_id and not server . active :
if server . id == server_id and not server . active :
logging . debug ( ' Unblock server %s ' , server . host )
logging . debug ( " Unblock server %s " , server . host )
self . init_server ( server_id , server_id )
self . init_server ( server_id , server_id )
break
break
@ -862,7 +916,7 @@ class Downloader(Thread):
kicked = [ ]
kicked = [ ]
for server_id in self . _timers . keys ( ) :
for server_id in self . _timers . keys ( ) :
if not [ stamp for stamp in self . _timers [ server_id ] if stamp > = now ] :
if not [ stamp for stamp in self . _timers [ server_id ] if stamp > = now ] :
logging . debug ( ' Forcing re-evaluation of server-id %s ' , server_id )
logging . debug ( " Forcing re-evaluation of server-id %s " , server_id )
del self . _timers [ server_id ]
del self . _timers [ server_id ]
self . init_server ( server_id , server_id )
self . init_server ( server_id , server_id )
kicked . append ( server_id )
kicked . append ( server_id )
@ -870,7 +924,7 @@ class Downloader(Thread):
for server in self . servers :
for server in self . servers :
if server . id not in self . _timers :
if server . id not in self . _timers :
if server . id not in kicked and not server . active :
if server . id not in kicked and not server . active :
logging . debug ( ' Forcing activation of server %s ' , server . host )
logging . debug ( " Forcing activation of server %s " , server . host )
self . init_server ( server . id , server . id )
self . init_server ( server . id , server . id )
def update_server ( self , oldserver , newserver ) :
def update_server ( self , oldserver , newserver ) :
@ -886,7 +940,7 @@ class Downloader(Thread):
def stop ( self ) :
def stop ( self ) :
self . shutdown = True
self . shutdown = True
notifier . send_notification ( " SABnzbd " , T ( ' Shutting down ' ) , ' startup ' )
notifier . send_notification ( " SABnzbd " , T ( " Shutting down " ) , " startup " )
def stop ( ) :
def stop ( ) :
@ -905,7 +959,7 @@ def stop():
def clues_login ( text ) :
def clues_login ( text ) :
""" Check for any " failed login " clues in the response code """
""" Check for any " failed login " clues in the response code """
text = text . lower ( )
text = text . lower ( )
for clue in ( ' username ' , ' password ' , ' invalid ' , ' authen ' , ' access denied ' ) :
for clue in ( " username " , " password " , " invalid " , " authen " , " access denied " ) :
if clue in text :
if clue in text :
return True
return True
return False
return False
@ -914,9 +968,9 @@ def clues_login(text):
def clues_too_many ( text ) :
def clues_too_many ( text ) :
""" Check for any " too many connections " clues in the response code """
""" Check for any " too many connections " clues in the response code """
text = text . lower ( )
text = text . lower ( )
for clue in ( ' exceed ' , ' connections ' , ' too many ' , ' threads ' , ' limit ' ) :
for clue in ( " exceed " , " connections " , " too many " , " threads " , " limit " ) :
# Not 'download limit exceeded' error
# Not 'download limit exceeded' error
if ( clue in text ) and ( ' download ' not in text ) and ( ' byte ' not in text ) :
if ( clue in text ) and ( " download " not in text ) and ( " byte " not in text ) :
return True
return True
return False
return False
@ -924,7 +978,7 @@ def clues_too_many(text):
def clues_too_many_ip ( text ) :
def clues_too_many_ip ( text ) :
""" Check for any " account sharing " clues in the response code """
""" Check for any " account sharing " clues in the response code """
text = text . lower ( )
text = text . lower ( )
for clue in ( ' simultaneous ip ' , ' multiple ip ' ) :
for clue in ( " simultaneous ip " , " multiple ip " ) :
if clue in text :
if clue in text :
return True
return True
return False
return False
@ -933,7 +987,7 @@ def clues_too_many_ip(text):
def clues_pay ( text ) :
def clues_pay ( text ) :
""" Check for messages about payments """
""" Check for messages about payments """
text = text . lower ( )
text = text . lower ( )
for clue in ( ' credits ' , ' paym ' , ' expired ' , ' exceeded ' ) :
for clue in ( " credits " , " paym " , " expired " , " exceeded " ) :
if clue in text :
if clue in text :
return True
return True
return False
return False