2015-12-07 02:40:13 +01:00
"""
conf . py - PyLink configuration core .
2016-06-21 19:56:53 +02:00
This module is used to access the configuration of the current PyLink instance .
It provides simple checks for validating and loading YAML - format configurations from arbitrary files .
2015-12-07 02:40:13 +01:00
"""
2016-07-20 02:45:43 +02:00
try :
import yaml
except ImportError :
2016-12-10 02:15:53 +01:00
raise ImportError ( " PyLink requires PyYAML to function; please install it and try again. " )
2016-07-20 02:45:43 +02:00
2015-08-04 04:27:19 +02:00
import sys
2016-11-03 06:23:45 +01:00
import os . path
2015-08-29 04:27:38 +02:00
from collections import defaultdict
2016-06-21 03:18:54 +02:00
from . import world
2015-05-31 21:20:09 +02:00
2017-02-21 19:10:54 +01:00
class ConfigValidationError ( Exception ) :
""" Error when config conditions aren ' t met. """
2016-06-25 23:37:06 +02:00
conf = { ' bot ' :
{
' nick ' : ' PyLink ' ,
' user ' : ' pylink ' ,
' realname ' : ' PyLink Service Client ' ,
' serverdesc ' : ' Unconfigured PyLink '
} ,
' logging ' :
{
' stdout ' : ' INFO '
} ,
' servers ' :
# Wildcard defaultdict! This means that
# any network name you try will work and return
# this basic template:
defaultdict ( lambda : { ' ip ' : ' 0.0.0.0 ' ,
' port ' : 7000 ,
' recvpass ' : " unconfigured " ,
' sendpass ' : " unconfigured " ,
' protocol ' : " null " ,
' hostname ' : " pylink.unconfigured " ,
' sid ' : " 000 " ,
' maxnicklen ' : 20 ,
' sidrange ' : ' 0## '
} )
}
confname = ' unconfigured '
2017-02-21 19:10:54 +01:00
def validate ( condition , errmsg ) :
""" Convenience function to validate conditions in validateConf(). """
if not condition :
raise ConfigValidationError ( errmsg )
2016-12-06 07:43:01 +01:00
def validateConf ( conf , logger = None ) :
2015-09-28 20:30:51 +02:00
""" Validates a parsed configuration dict. """
2017-02-21 19:10:54 +01:00
validate ( type ( conf ) == dict ,
" Invalid configuration given: should be type dict, not %s . "
% type ( conf ) . __name__ )
2016-02-08 03:01:12 +01:00
for section in ( ' bot ' , ' servers ' , ' login ' , ' logging ' ) :
2017-02-21 19:10:54 +01:00
validate ( conf . get ( section ) , " Missing %r section in config. " % section )
2016-02-08 03:01:12 +01:00
2016-11-08 06:01:28 +01:00
# Make sure at least one form of authentication is valid.
2016-11-23 06:06:54 +01:00
# Also we'll warn them that login:user/login:password is deprecated
if conf [ ' login ' ] . get ( ' password ' ) or conf [ ' login ' ] . get ( ' user ' ) :
2016-12-06 07:43:01 +01:00
e = " The ' login:user ' and ' login:password ' options are deprecated since PyLink 1.1. " \
" Please switch to the new ' login:accounts ' format as outlined in the example config. "
if logger :
logger . warning ( e )
else :
# FIXME: we need a better fallback when log isn't available on first
# start.
print ( ' WARNING: %s ' % e )
2016-11-23 06:06:54 +01:00
2016-11-08 06:01:28 +01:00
old_login_valid = type ( conf [ ' login ' ] . get ( ' password ' ) ) == type ( conf [ ' login ' ] . get ( ' user ' ) ) == str
newlogins = conf [ ' login ' ] . get ( ' accounts ' , { } )
2017-02-25 06:07:28 +01:00
validate ( old_login_valid or newlogins , " No accounts were set, aborting! " )
2016-11-08 06:01:28 +01:00
for account , block in newlogins . items ( ) :
2017-02-21 19:10:54 +01:00
validate ( type ( account ) == str , " Bad username format %s " % account )
validate ( type ( block . get ( ' password ' ) ) == str , " Bad password %s for account %s " % ( block . get ( ' password ' ) , account ) )
2016-11-08 06:01:28 +01:00
2017-02-21 19:10:54 +01:00
validate ( conf [ ' login ' ] . get ( ' password ' ) != " changeme " , " You have not set the login details correctly! " )
2016-02-08 03:01:12 +01:00
2017-02-25 06:07:28 +01:00
if newlogins and not old_login_valid :
2017-02-21 19:10:54 +01:00
validate ( conf . get ( ' permissions ' ) , " New-style accounts enabled but no permissions block was found. You will not be able to administrate your PyLink instance! " )
2017-02-21 03:08:35 +01:00
2015-09-28 20:30:51 +02:00
return conf
2016-11-08 06:01:28 +01:00
2016-12-06 07:43:01 +01:00
def loadConf ( filename , errors_fatal = True , logger = None ) :
2015-09-28 20:30:51 +02:00
""" Loads a PyLink configuration file from the filename given. """
2016-06-21 20:31:39 +02:00
global confname , conf , fname
2016-11-03 06:23:45 +01:00
# Note: store globally the last loaded conf filename, for REHASH in coremods/control.
2016-06-21 20:31:39 +02:00
fname = filename
2016-11-03 06:23:45 +01:00
# For the internal config name, strip off any .yml extensions and absolute paths
confname = os . path . basename ( filename ) . split ( ' . ' , 1 ) [ 0 ]
2016-07-20 02:40:22 +02:00
try :
with open ( filename , ' r ' ) as f :
2015-09-28 20:30:51 +02:00
conf = yaml . load ( f )
2016-12-06 07:43:01 +01:00
conf = validateConf ( conf , logger = logger )
2016-07-20 02:40:22 +02:00
except Exception as e :
print ( ' ERROR: Failed to load config from %r : %s : %s ' % ( filename , type ( e ) . __name__ , e ) , file = sys . stderr )
print ( ' Users upgrading from users < 0.9-alpha1 should note that the default configuration has been renamed to *pylink.yml*, not *config.yml* ' , file = sys . stderr )
if errors_fatal :
2017-02-27 16:25:46 +01:00
sys . exit ( 1 )
2016-07-20 02:40:22 +02:00
raise
else :
return conf