From aa3ae3d3a16d06a34202a09c508dd2c1d549f4b3 Mon Sep 17 00:00:00 2001 From: KF7EEL Date: Sun, 25 Apr 2021 08:55:26 -0700 Subject: [PATCH] genetate app list, use sms_aprs_config.py --- access_systems.txt | 33 ---- authorized_users.txt | 9 -- full_bridge.py | 44 ++++-- rules_SAMPLE.py | 51 ++++++ sms_aprs_config.py | 369 +++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 447 insertions(+), 59 deletions(-) delete mode 100644 access_systems.txt delete mode 100644 authorized_users.txt create mode 100755 sms_aprs_config.py diff --git a/access_systems.txt b/access_systems.txt deleted file mode 100644 index 82b5758..0000000 --- a/access_systems.txt +++ /dev/null @@ -1,33 +0,0 @@ -{ -# Shortcut used in SMS message - 'XYZ':{ - # Mode of transfer, this case, message transfer - 'mode':'msg_xfer', - # Public or Private auth - 'auth_type':'public', - # Name of the server/network - 'network_name':'My HBlink Server', - # URL to the dashboard of the server/network - 'url':'http://example.net/', - # Username and password given to you by network operator - 'user':'test_name', - 'password':'passw0rd' - }, - # Shortcut used in SMS message - 'BBD':{ - # Mode for application, operates differently than msg_xfer - 'mode':'app', - # Name of external application - 'app_name':'Multi Network Bulletin Board', - # Endpoint URL of API - 'url':'http://hbl.ink/bb/post', - # Website for users to get info - 'website':'http://hbl.ink', - }, - 'HBL':{ - 'mode':'msg_xfer', - 'auth_type':'public', - 'network_name':'KF7EEL Development Server', - 'url':'http://hbl.ink/d-aprs/', - } -} diff --git a/authorized_users.txt b/authorized_users.txt deleted file mode 100644 index 92fa1b6..0000000 --- a/authorized_users.txt +++ /dev/null @@ -1,9 +0,0 @@ -# This file contains the access credentials of other servers/networks that can -# send messages to this server. -{ - 'DEF':{ - 'mode':'msg_xfer', - 'user':'test_name', - 'password':'passw0rd' - } -} diff --git a/full_bridge.py b/full_bridge.py index 319d137..a61d4f0 100644 --- a/full_bridge.py +++ b/full_bridge.py @@ -48,7 +48,7 @@ from twisted.internet import reactor, task from hblink import HBSYSTEM, OPENBRIDGE, systems, hblink_handler, reportFactory, REPORT_OPCODES, mk_aliases from dmr_utils3.utils import bytes_3, int_id, get_alias, bytes_4 from dmr_utils3 import decode, bptc, const -import config +import sms_aprs_config import log from const import * @@ -61,7 +61,7 @@ logger = logging.getLogger(__name__) import traceback # Import UNIT time from rules.py -from rules import UNIT_TIME, STATIC_UNIT +from rules import UNIT_TIME, STATIC_UNIT, local_apps, authorized_users # modules from gps_data.py from bitarray import bitarray @@ -309,17 +309,27 @@ def send_email(to_email, email_subject, email_message): smtp_server.close() def generate_apps(): - global combined - local_acess_systems = ast.literal_eval(os.popen('cat ' + access_systems_file).read()) + global access_systems + #local_apps = ast.literal_eval(os.popen('cat ' + access_systems_file).read()) public_systems_file = requests.get(CONFIG['GPS_DATA']['PUBLIC_APPS_LIST']) public_apps = ast.literal_eval(public_systems_file.text) - combined = public_apps.items() + local_acess_systems.items() - print(type(public_apps)) - print(type(local_acess_systems)) - print() - print(combined) + access_systems = {} + #combined = public_apps.items() + local_acess_systems.items() + if CONFIG['GPS_DATA']['USE_PUBLIC_APPS'] == True: + for i in public_apps.items(): + key = str(i[0]) + access_systems[key] = i[1] + for i in local_apps.items(): + key = str(i[0]) + access_systems[key] = i[1] + print(access_systems) + + #print(type(public_apps)) + #print(type(local_acess_systems)) + #print() + #print(combined) #print(local_acess_systems.update(public_apps)) - return combined + return access_systems # Thanks for this forum post for this - https://stackoverflow.com/questions/2579535/convert-dd-decimal-degrees-to-dms-degrees-minutes-seconds-in-python @@ -489,8 +499,8 @@ def process_sms(_rf_src, sms): print(use_api) if use_api == True: auth_tokens = ast.literal_eval(os.popen('cat ' + auth_token_file).read()) - access_systems = ast.literal_eval(os.popen('cat ' + access_systems_file).read()) - authorized_users = ast.literal_eval(os.popen('cat ' + authorized_users_file).read()) + #access_systems = ast.literal_eval(os.popen('cat ' + access_systems_file).read()) + #authorized_users = ast.literal_eval(os.popen('cat ' + authorized_users_file).read()) system = parse_sms[0][1:] #print(access_systems[system]) #print(authorized_users) @@ -2233,17 +2243,17 @@ if __name__ == '__main__': # CLI argument parser - handles picking up the config file from the command line, and sending a "help" message parser = argparse.ArgumentParser() - parser.add_argument('-c', '--config', action='store', dest='CONFIG_FILE', help='/full/path/to/config.file (usually hblink.cfg)') + parser.add_argument('-c', '--config', action='store', dest='CONFIG_FILE', help='/full/path/to/config.file (usually full_bridge.cfg)') parser.add_argument('-r', '--rules', action='store', dest='RULES_FILE', help='/full/path/to/rules.file (usually rules.py)') parser.add_argument('-l', '--logging', action='store', dest='LOG_LEVEL', help='Override config file logging level.') cli_args = parser.parse_args() # Ensure we have a path for the config file, if one wasn't specified, then use the default (top of file) if not cli_args.CONFIG_FILE: - cli_args.CONFIG_FILE = os.path.dirname(os.path.abspath(__file__))+'/hblink.cfg' + cli_args.CONFIG_FILE = os.path.dirname(os.path.abspath(__file__))+'/full_bridge.cfg' # Call the external routine to build the configuration dictionary - CONFIG = config.build_config(cli_args.CONFIG_FILE) + CONFIG = sms_aprs_config.build_config(cli_args.CONFIG_FILE) data_id = int(CONFIG['GPS_DATA']['DATA_DMR_ID']) #echo_id = int(CONFIG['GPS_DATA']['ECHO_DMR_ID']) @@ -2303,8 +2313,8 @@ if __name__ == '__main__': #API variables auth_token_file = CONFIG['GPS_DATA']['AUTHORIZED_TOKENS_FILE'] use_api = CONFIG['GPS_DATA']['USE_API'] - access_systems_file = CONFIG['GPS_DATA']['ACCESS_SYSTEMS_FILE'] - authorized_users_file = CONFIG['GPS_DATA']['AUTHORIZED_USERS_FILE'] + #access_systems_file = CONFIG['GPS_DATA']['ACCESS_SYSTEMS_FILE'] + #authorized_users_file = CONFIG['GPS_DATA']['AUTHORIZED_USERS_FILE'] if Path(auth_token_file).is_file(): pass else: diff --git a/rules_SAMPLE.py b/rules_SAMPLE.py index 53e9f24..3a7863f 100755 --- a/rules_SAMPLE.py +++ b/rules_SAMPLE.py @@ -74,6 +74,57 @@ STATIC_UNIT = [ [ 9099, 'D-APRS'] ] +authorized_users = { +## 'DEF':{ +## 'mode':'msg_xfer', +## 'user':'test_name', +## 'password':'passw0rd' +## }, +## 'DEF':{ +## 'mode':'msg_xfer', +## 'user':'test_name', +## 'password':'passw0rd' +## } +} + +local_systems = { +# Shortcut used in SMS message +## 'XYZ':{ +## # Mode of transfer, this case, message transfer +## 'mode':'msg_xfer', +## # Public or Private auth +## 'auth_type':'public', +## # Name of the server/network +## 'network_name':'My HBlink Server', +## # URL to the dashboard of the server/network +## 'url':'http://example.net/', +## # Username and password given to you by network operator +## 'user':'test_name', +## 'password':'passw0rd' +## }, +## # Shortcut used in SMS message +## 'BBD':{ +## # Mode for application, operates differently than msg_xfer +## 'mode':'app', +## # Name of external application +## 'app_name':'Multi Network Bulletin Board', +## # Endpoint URL of API +## 'url':'http://hbl.ink/bb/post', +## # Website for users to get info +## 'website':'http://hbl.ink', +## }, +## 'HBL':{ +## 'mode':'msg_xfer', +## 'auth_type':'public', +## 'network_name':'KF7EEL Development Server', +## 'url':'http://hbl.ink/d-aprs/', +## } +} + + +''' + +} ''' This is for testing the syntax of the file. It won't eliminate all errors, but running this file diff --git a/sms_aprs_config.py b/sms_aprs_config.py new file mode 100755 index 0000000..41ce8db --- /dev/null +++ b/sms_aprs_config.py @@ -0,0 +1,369 @@ +#!/usr/bin/env python +# +############################################################################### +# Copyright (C) 2016-2018 Cortney T. Buffington, N0MJS +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +############################################################################### + +''' +This module generates the configuration data structure for hblink.py and +assoicated programs that use it. It has been seaparated into a different +module so as to keep hblink.py easeier to navigate. This file only needs +updated if the items in the main configuraiton file (usually hblink.cfg) +change. +''' + +import configparser +import sys +import const + +from socket import gethostbyname + +# Does anybody read this stuff? There's a PEP somewhere that says I should do this. +__author__ = 'Cortney T. Buffington, N0MJS' +__copyright__ = 'Copyright (c) 2016-2018 Cortney T. Buffington, N0MJS and the K0USY Group' +__credits__ = 'Colin Durbridge, G4EML, Steve Zingman, N4IRS; Mike Zingman, N4IRR; Jonathan Naylor, G4KLX; Hans Barthen, DL5DI; Torsten Shultze, DG1HT' +__license__ = 'GNU GPLv3' +__maintainer__ = 'Cort Buffington, N0MJS' +__email__ = 'n0mjs@me.com' + +# Processing of ALS goes here. It's separated from the acl_build function because this +# code is hblink config-file format specific, and acl_build is abstracted +def process_acls(_config): + # Global registration ACL + _config['GLOBAL']['REG_ACL'] = acl_build(_config['GLOBAL']['REG_ACL'], const.PEER_MAX) + + # Global subscriber and TGID ACLs + for acl in ['SUB_ACL', 'TG1_ACL', 'TG2_ACL']: + _config['GLOBAL'][acl] = acl_build(_config['GLOBAL'][acl], const.ID_MAX) + + # System level ACLs + for system in _config['SYSTEMS']: + # Registration ACLs (which make no sense for peer systems) + if _config['SYSTEMS'][system]['MODE'] == 'MASTER': + _config['SYSTEMS'][system]['REG_ACL'] = acl_build(_config['SYSTEMS'][system]['REG_ACL'], const.PEER_MAX) + + # Subscriber and TGID ACLs (valid for all system types) + for acl in ['SUB_ACL', 'TG1_ACL', 'TG2_ACL']: + _config['SYSTEMS'][system][acl] = acl_build(_config['SYSTEMS'][system][acl], const.ID_MAX) + +# Create an access control list that is programatically useable from human readable: +# ORIGINAL: 'DENY:1-5,3120101,3120124' +# PROCESSED: (False, set([(1, 5), (3120124, 3120124), (3120101, 3120101)])) +def acl_build(_acl, _max): + if not _acl: + return(True, set((const.ID_MIN, _max))) + + acl = [] #set() + sections = _acl.split(':') + + if sections[0] == 'PERMIT': + action = True + else: + action = False + + for entry in sections[1].split(','): + if entry == 'ALL': + acl.append((const.ID_MIN, _max)) + break + + elif '-' in entry: + start,end = entry.split('-') + start,end = int(start), int(end) + if (const.ID_MIN <= start <= _max) or (const.ID_MIN <= end <= _max): + acl.append((start, end)) + else: + sys.exit('ACL CREATION ERROR, VALUE OUT OF RANGE ({} - {})IN RANGE-BASED ENTRY: {}'.format(const.ID_MIN, _max, entry)) + else: + id = int(entry) + if (const.ID_MIN <= id <= _max): + acl.append((id, id)) + else: + sys.exit('ACL CREATION ERROR, VALUE OUT OF RANGE ({} - {}) IN SINGLE ID ENTRY: {}'.format(const.ID_MIN, _max, entry)) + + return (action, acl) + +def build_config(_config_file): + config = configparser.ConfigParser() + + if not config.read(_config_file): + sys.exit('Configuration file \''+_config_file+'\' is not a valid configuration file! Exiting...') + + CONFIG = {} + CONFIG['GLOBAL'] = {} + CONFIG['REPORTS'] = {} + CONFIG['LOGGER'] = {} + CONFIG['GPS_DATA'] = {} + CONFIG['ALIASES'] = {} + CONFIG['SYSTEMS'] = {} + + try: + for section in config.sections(): + if section == 'GLOBAL': + CONFIG['GLOBAL'].update({ + 'PATH': config.get(section, 'PATH'), + 'PING_TIME': config.getint(section, 'PING_TIME'), + 'MAX_MISSED': config.getint(section, 'MAX_MISSED'), + 'USE_ACL': config.get(section, 'USE_ACL'), + 'REG_ACL': config.get(section, 'REG_ACL'), + 'SUB_ACL': config.get(section, 'SUB_ACL'), + 'TG1_ACL': config.get(section, 'TGID_TS1_ACL'), + 'TG2_ACL': config.get(section, 'TGID_TS2_ACL') + }) + + elif section == 'REPORTS': + CONFIG['REPORTS'].update({ + 'REPORT': config.getboolean(section, 'REPORT'), + 'REPORT_INTERVAL': config.getint(section, 'REPORT_INTERVAL'), + 'REPORT_PORT': config.getint(section, 'REPORT_PORT'), + 'REPORT_CLIENTS': config.get(section, 'REPORT_CLIENTS').split(',') + }) + + elif section == 'LOGGER': + CONFIG['LOGGER'].update({ + 'LOG_FILE': config.get(section, 'LOG_FILE'), + 'LOG_HANDLERS': config.get(section, 'LOG_HANDLERS'), + 'LOG_LEVEL': config.get(section, 'LOG_LEVEL'), + 'LOG_NAME': config.get(section, 'LOG_NAME') + }) + if not CONFIG['LOGGER']['LOG_FILE']: + CONFIG['LOGGER']['LOG_FILE'] = '/dev/null' + + elif section == 'GPS_DATA': + CONFIG['GPS_DATA'].update({ + 'DATA_DMR_ID': config.get(section, 'DATA_DMR_ID'), + 'USER_APRS_SSID': config.get(section, 'USER_APRS_SSID'), + 'CALL_TYPE': config.get(section, 'CALL_TYPE'), + 'UNIT_SMS_TS': config.get(section, 'UNIT_SMS_TS'), + 'USER_APRS_COMMENT': config.get(section, 'USER_APRS_COMMENT'), + 'APRS_LOGIN_CALL': config.get(section, 'APRS_LOGIN_CALL'), + 'APRS_LOGIN_PASSCODE': config.get(section, 'APRS_LOGIN_PASSCODE'), + 'APRS_SERVER': config.get(section, 'APRS_SERVER'), + 'APRS_PORT': config.get(section, 'APRS_PORT'), + 'APRS_FILTER': config.get(section, 'APRS_FILTER'), + 'IGATE_BEACON_TIME': config.get(section, 'IGATE_BEACON_TIME'), + 'IGATE_BEACON_ICON': config.get(section, 'IGATE_BEACON_ICON'), + 'IGATE_BEACON_COMMENT': config.get(section, 'IGATE_BEACON_COMMENT'), + 'IGATE_LATITUDE': config.get(section, 'IGATE_LATITUDE'), + 'IGATE_LONGITUDE': config.get(section, 'IGATE_LONGITUDE'), + 'APRS_STATIC_REPORT_INTERVAL': config.get(section, 'APRS_STATIC_REPORT_INTERVAL'), + 'APRS_STATIC_MESSAGE': config.get(section, 'APRS_STATIC_MESSAGE'), + 'EMAIL_SENDER': config.get(section, 'EMAIL_SENDER'), + 'EMAIL_PASSWORD': config.get(section, 'EMAIL_PASSWORD'), + 'SMTP_SERVER': config.get(section, 'SMTP_SERVER'), + 'SMTP_PORT': config.get(section, 'SMTP_PORT'), + 'LOCATION_FILE': config.get(section, 'LOCATION_FILE'), + 'BULLETIN_BOARD_FILE': config.get(section, 'BULLETIN_BOARD_FILE'), + 'MAILBOX_FILE': config.get(section, 'MAILBOX_FILE'), + 'EMERGENCY_SOS_FILE': config.get(section, 'EMERGENCY_SOS_FILE'), + 'USER_SETTINGS_FILE': config.get(section, 'USER_SETTINGS_FILE'), + 'USE_API': config.getboolean(section, 'USE_API'), + 'AUTHORIZED_TOKENS_FILE': config.get(section, 'AUTHORIZED_TOKENS_FILE'), + 'AUTHORIZED_USERS_FILE': config.get(section, 'AUTHORIZED_USERS_FILE'), + 'ACCESS_SYSTEMS_FILE': config.get(section, 'ACCESS_SYSTEMS_FILE'), + 'USE_PUBLIC_APPS': config.getboolean(section, 'USE_PUBLIC_APPS'), + 'PUBLIC_APPS_LIST': config.get(section, 'PUBLIC_APPS_LIST'), + 'MY_API_NAME': config.get(section, 'MY_API_NAME'), + 'DASHBOARD_URL': config.get(section, 'DASHBOARD_URL'), + 'SERVER_NAME': config.get(section, 'SERVER_NAME'), + + + }) + if not CONFIG['LOGGER']['LOG_FILE']: + CONFIG['LOGGER']['LOG_FILE'] = '/dev/null' + + elif section == 'ALIASES': + CONFIG['ALIASES'].update({ + 'TRY_DOWNLOAD': config.getboolean(section, 'TRY_DOWNLOAD'), + 'PATH': config.get(section, 'PATH'), + 'PEER_FILE': config.get(section, 'PEER_FILE'), + 'SUBSCRIBER_FILE': config.get(section, 'SUBSCRIBER_FILE'), + 'TGID_FILE': config.get(section, 'TGID_FILE'), + 'PEER_URL': config.get(section, 'PEER_URL'), + 'SUBSCRIBER_URL': config.get(section, 'SUBSCRIBER_URL'), + 'STALE_TIME': config.getint(section, 'STALE_DAYS') * 86400, + }) + + elif config.getboolean(section, 'ENABLED'): + if config.get(section, 'MODE') == 'PEER': + CONFIG['SYSTEMS'].update({section: { + 'MODE': config.get(section, 'MODE'), + 'ENABLED': config.getboolean(section, 'ENABLED'), + 'LOOSE': config.getboolean(section, 'LOOSE'), + 'SOCK_ADDR': (gethostbyname(config.get(section, 'IP')), config.getint(section, 'PORT')), + 'IP': gethostbyname(config.get(section, 'IP')), + 'PORT': config.getint(section, 'PORT'), + 'MASTER_SOCKADDR': (gethostbyname(config.get(section, 'MASTER_IP')), config.getint(section, 'MASTER_PORT')), + 'MASTER_IP': gethostbyname(config.get(section, 'MASTER_IP')), + 'MASTER_PORT': config.getint(section, 'MASTER_PORT'), + 'PASSPHRASE': bytes(config.get(section, 'PASSPHRASE'), 'utf-8'), + 'CALLSIGN': bytes(config.get(section, 'CALLSIGN').ljust(8)[:8], 'utf-8'), + 'RADIO_ID': config.getint(section, 'RADIO_ID').to_bytes(4, 'big'), + 'RX_FREQ': bytes(config.get(section, 'RX_FREQ').ljust(9)[:9], 'utf-8'), + 'TX_FREQ': bytes(config.get(section, 'TX_FREQ').ljust(9)[:9], 'utf-8'), + 'TX_POWER': bytes(config.get(section, 'TX_POWER').rjust(2,'0'), 'utf-8'), + 'COLORCODE': bytes(config.get(section, 'COLORCODE').rjust(2,'0'), 'utf-8'), + 'LATITUDE': bytes(config.get(section, 'LATITUDE').ljust(8)[:8], 'utf-8'), + 'LONGITUDE': bytes(config.get(section, 'LONGITUDE').ljust(9)[:9], 'utf-8'), + 'HEIGHT': bytes(config.get(section, 'HEIGHT').rjust(3,'0'), 'utf-8'), + 'LOCATION': bytes(config.get(section, 'LOCATION').ljust(20)[:20], 'utf-8'), + 'DESCRIPTION': bytes(config.get(section, 'DESCRIPTION').ljust(19)[:19], 'utf-8'), + 'SLOTS': bytes(config.get(section, 'SLOTS'), 'utf-8'), + 'URL': bytes(config.get(section, 'URL').ljust(124)[:124], 'utf-8'), + 'SOFTWARE_ID': bytes(config.get(section, 'SOFTWARE_ID').ljust(40)[:40], 'utf-8'), + 'PACKAGE_ID': bytes(config.get(section, 'PACKAGE_ID').ljust(40)[:40], 'utf-8'), + 'GROUP_HANGTIME': config.getint(section, 'GROUP_HANGTIME'), + 'OPTIONS': bytes(config.get(section, 'OPTIONS'), 'utf-8'), + 'USE_ACL': config.getboolean(section, 'USE_ACL'), + 'SUB_ACL': config.get(section, 'SUB_ACL'), + 'TG1_ACL': config.get(section, 'TGID_TS1_ACL'), + 'TG2_ACL': config.get(section, 'TGID_TS2_ACL') + }}) + CONFIG['SYSTEMS'][section].update({'STATS': { + 'CONNECTION': 'NO', # NO, RTPL_SENT, AUTHENTICATED, CONFIG-SENT, YES + 'CONNECTED': None, + 'PINGS_SENT': 0, + 'PINGS_ACKD': 0, + 'NUM_OUTSTANDING': 0, + 'PING_OUTSTANDING': False, + 'LAST_PING_TX_TIME': 0, + 'LAST_PING_ACK_TIME': 0, + }}) + + if config.get(section, 'MODE') == 'XLXPEER': + CONFIG['SYSTEMS'].update({section: { + 'MODE': config.get(section, 'MODE'), + 'ENABLED': config.getboolean(section, 'ENABLED'), + 'LOOSE': config.getboolean(section, 'LOOSE'), + 'SOCK_ADDR': (gethostbyname(config.get(section, 'IP')), config.getint(section, 'PORT')), + 'IP': gethostbyname(config.get(section, 'IP')), + 'PORT': config.getint(section, 'PORT'), + 'MASTER_SOCKADDR': (gethostbyname(config.get(section, 'MASTER_IP')), config.getint(section, 'MASTER_PORT')), + 'MASTER_IP': gethostbyname(config.get(section, 'MASTER_IP')), + 'MASTER_PORT': config.getint(section, 'MASTER_PORT'), + 'PASSPHRASE': bytes(config.get(section, 'PASSPHRASE'), 'utf-8'), + 'CALLSIGN': bytes(config.get(section, 'CALLSIGN').ljust(8)[:8], 'utf-8'), + 'RADIO_ID': config.getint(section, 'RADIO_ID').to_bytes(4, 'big'), + 'RX_FREQ': bytes(config.get(section, 'RX_FREQ').ljust(9)[:9], 'utf-8'), + 'TX_FREQ': bytes(config.get(section, 'TX_FREQ').ljust(9)[:9], 'utf-8'), + 'TX_POWER': bytes(config.get(section, 'TX_POWER').rjust(2,'0'), 'utf-8'), + 'COLORCODE': bytes(config.get(section, 'COLORCODE').rjust(2,'0'), 'utf-8'), + 'LATITUDE': bytes(config.get(section, 'LATITUDE').ljust(8)[:8], 'utf-8'), + 'LONGITUDE': bytes(config.get(section, 'LONGITUDE').ljust(9)[:9], 'utf-8'), + 'HEIGHT': bytes(config.get(section, 'HEIGHT').rjust(3,'0'), 'utf-8'), + 'LOCATION': bytes(config.get(section, 'LOCATION').ljust(20)[:20], 'utf-8'), + 'DESCRIPTION': bytes(config.get(section, 'DESCRIPTION').ljust(19)[:19], 'utf-8'), + 'SLOTS': bytes(config.get(section, 'SLOTS'), 'utf-8'), + 'URL': bytes(config.get(section, 'URL').ljust(124)[:124], 'utf-8'), + 'SOFTWARE_ID': bytes(config.get(section, 'SOFTWARE_ID').ljust(40)[:40], 'utf-8'), + 'PACKAGE_ID': bytes(config.get(section, 'PACKAGE_ID').ljust(40)[:40], 'utf-8'), + 'GROUP_HANGTIME': config.getint(section, 'GROUP_HANGTIME'), + 'XLXMODULE': config.getint(section, 'XLXMODULE'), + 'OPTIONS': '', + 'USE_ACL': config.getboolean(section, 'USE_ACL'), + 'SUB_ACL': config.get(section, 'SUB_ACL'), + 'TG1_ACL': config.get(section, 'TGID_TS1_ACL'), + 'TG2_ACL': config.get(section, 'TGID_TS2_ACL') + }}) + CONFIG['SYSTEMS'][section].update({'XLXSTATS': { + 'CONNECTION': 'NO', # NO, RTPL_SENT, AUTHENTICATED, CONFIG-SENT, YES + 'CONNECTED': None, + 'PINGS_SENT': 0, + 'PINGS_ACKD': 0, + 'NUM_OUTSTANDING': 0, + 'PING_OUTSTANDING': False, + 'LAST_PING_TX_TIME': 0, + 'LAST_PING_ACK_TIME': 0, + }}) + + elif config.get(section, 'MODE') == 'MASTER': + CONFIG['SYSTEMS'].update({section: { + 'MODE': config.get(section, 'MODE'), + 'ENABLED': config.getboolean(section, 'ENABLED'), + 'STATIC_APRS_POSITION_ENABLED': config.getboolean(section, 'STATIC_APRS_POSITION_ENABLED'), + 'REPEAT': config.getboolean(section, 'REPEAT'), + 'MAX_PEERS': config.getint(section, 'MAX_PEERS'), + 'IP': gethostbyname(config.get(section, 'IP')), + 'PORT': config.getint(section, 'PORT'), + 'PASSPHRASE': bytes(config.get(section, 'PASSPHRASE'), 'utf-8'), + 'GROUP_HANGTIME': config.getint(section, 'GROUP_HANGTIME'), + 'USE_ACL': config.getboolean(section, 'USE_ACL'), + 'REG_ACL': config.get(section, 'REG_ACL'), + 'SUB_ACL': config.get(section, 'SUB_ACL'), + 'TG1_ACL': config.get(section, 'TGID_TS1_ACL'), + 'TG2_ACL': config.get(section, 'TGID_TS2_ACL') + }}) + CONFIG['SYSTEMS'][section].update({'PEERS': {}}) + + elif config.get(section, 'MODE') == 'OPENBRIDGE': + CONFIG['SYSTEMS'].update({section: { + 'MODE': config.get(section, 'MODE'), + 'ENABLED': config.getboolean(section, 'ENABLED'), + 'NETWORK_ID': config.getint(section, 'NETWORK_ID').to_bytes(4, 'big'), + 'IP': gethostbyname(config.get(section, 'IP')), + 'PORT': config.getint(section, 'PORT'), + 'PASSPHRASE': bytes(config.get(section, 'PASSPHRASE').ljust(20,'\x00')[:20], 'utf-8'), + 'TARGET_SOCK': (gethostbyname(config.get(section, 'TARGET_IP')), config.getint(section, 'TARGET_PORT')), + 'TARGET_IP': gethostbyname(config.get(section, 'TARGET_IP')), + 'TARGET_PORT': config.getint(section, 'TARGET_PORT'), + 'BOTH_SLOTS': config.getboolean(section, 'BOTH_SLOTS'), + 'USE_ACL': config.getboolean(section, 'USE_ACL'), + 'SUB_ACL': config.get(section, 'SUB_ACL'), + 'TG1_ACL': config.get(section, 'TGID_ACL'), + 'TG2_ACL': 'PERMIT:ALL' + }}) + + + except configparser.Error as err: + sys.exit('Error processing configuration file -- {}'.format(err)) + + process_acls(CONFIG) + + return CONFIG + +# Used to run this file direclty and print the config, +# which might be useful for debugging +if __name__ == '__main__': + import sys + import os + import argparse + from pprint import pprint + from dmr_utils3.utils import int_id + + # Change the current directory to the location of the application + os.chdir(os.path.dirname(os.path.realpath(sys.argv[0]))) + + # CLI argument parser - handles picking up the config file from the command line, and sending a "help" message + parser = argparse.ArgumentParser() + parser.add_argument('-c', '--config', action='store', dest='CONFIG_FILE', help='/full/path/to/config.file (usually hblink.cfg)') + cli_args = parser.parse_args() + + + # Ensure we have a path for the config file, if one wasn't specified, then use the execution directory + if not cli_args.CONFIG_FILE: + cli_args.CONFIG_FILE = os.path.dirname(os.path.abspath(__file__))+'/hblink.cfg' + + CONFIG = build_config(cli_args.CONFIG_FILE) + pprint(CONFIG) + + def acl_check(_id, _acl): + id = int_id(_id) + for entry in _acl[1]: + if entry[0] <= id <= entry[1]: + return _acl[0] + return not _acl[0] + + print(acl_check(b'\x00\x01\x37', CONFIG['GLOBAL']['TG1_ACL']))