1
0
mirror of https://github.com/craigerl/aprsd.git synced 2025-09-04 06:07:48 -04:00

Fixed all pep8 errors and some py3 errors

This introduced the six lib which can translate common
py2 vs py3 incompatibilities.
https://six.readthedocs.io/
This commit is contained in:
Hemna 2020-12-04 08:56:26 -05:00
parent 06a63f541e
commit 4084ddfe31
4 changed files with 168 additions and 179 deletions

View File

@ -1,18 +1,10 @@
#!/bin/python
import argparse import argparse
import logging import logging
import os
import select
import signal
import socket
import sys import sys
import time import time
import threading import socketserver
import Queue
from logging.handlers import RotatingFileHandler from logging.handlers import RotatingFileHandler
from telnetsrv.green import TelnetHandler, command
from aprsd import utils from aprsd import utils
@ -34,37 +26,10 @@ parser.add_argument("--ip",
default='127.0.0.1', default='127.0.0.1',
help="The IP to listen on ") help="The IP to listen on ")
args = parser.parse_args()
CONFIG = None CONFIG = None
LOG = logging.getLogger('ARPSSERVER') LOG = logging.getLogger('ARPSSERVER')
class MyAPRSServer(TelnetHandler):
@command('echo')
def command_echo(self, params):
LOG.debug("ECHO %s" % params)
self.writeresponse(' '.join(params))
@command('user')
def command_user(self, params):
LOG.debug("User auth command")
self.writeresponse('')
@command('quit')
def command_quit(self, params):
LOG.debug("quit called")
self.writeresponse('quitting')
os.kill(os.getpid(), signal.SIGINT)
def signal_handler(signal, frame):
LOG.info("Ctrl+C, exiting.")
# sys.exit(0) # thread ignores this
os._exit(0)
# Setup the logging faciility # Setup the logging faciility
# to disable logging to stdout, but still log to file # to disable logging to stdout, but still log to file
# use the --quiet option on the cmdln # use the --quiet option on the cmdln
@ -96,96 +61,33 @@ def setup_logging(args):
LOG.addHandler(sh) LOG.addHandler(sh)
class ClientThread(threading.Thread): class MyAPRSTCPHandler(socketserver.BaseRequestHandler):
def __init__(self, msg_q, ip, port, conn, *args, **kwargs):
super(ClientThread, self).__init__()
self.msg_q = msg_q
self.ip = ip
self.port = port
self.conn = conn
LOG.info("[+] New thread started for %s:%s" % (ip, port))
def send_command(self, msg): def handle(self):
LOG.info("Sending command '%s'" % msg) # self.request is the TCP socket connected to the client
self.conn.send(msg) self.data = self.request.recv(1024).strip()
LOG.debug("{} wrote:".format(self.client_address[0]))
def run(self): LOG.debug(self.data)
while True: # just send back the same data, but upper-cased
LOG.debug("Wait for data") self.request.sendall(self.data.upper())
readable, writeable, exceptional = select.select([self.conn],
[], [],
1)
LOG.debug("select returned %s" % readable)
if readable:
data = self.conn.recv(2048)
LOG.info("got data '%s'" % data)
else:
try:
msg = self.msg_q.get(True, 0.05)
if msg:
LOG.info("Sending message '%s'" % msg)
self.conn.send(msg + "\n")
except Queue.Empty:
pass
class InputThread(threading.Thread): def main():
def __init__(self, msg_q): global CONFIG
super(InputThread, self).__init__() args = parser.parse_args()
self.msg_q = msg_q
LOG.info("User input thread started")
def run(self):
while True:
text = raw_input("Prompt> ")
LOG.debug("Got input '%s'" % text)
if text == 'quit':
LOG.info("Quitting Input Thread")
sys.exit(0)
else:
LOG.info("add '%s' to message Q" % text)
self.msg_q.put(text)
threads = []
def main(args):
global CONFIG, threads
setup_logging(args) setup_logging(args)
LOG.info("Test APRS server starting.") LOG.info("Test APRS server starting.")
time.sleep(1) time.sleep(1)
signal.signal(signal.SIGINT, signal_handler)
CONFIG = utils.parse_config(args) CONFIG = utils.parse_config(args)
msg_q = Queue.Queue()
tcpsock = socket.socket(socket.AF_INET,
socket.SOCK_STREAM)
tcpsock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
ip = CONFIG['aprs']['host'] ip = CONFIG['aprs']['host']
port = CONFIG['aprs']['port'] port = CONFIG['aprs']['port']
LOG.info("Start server listening on %s:%s" % (args.ip, args.port)) LOG.info("Start server listening on %s:%s" % (args.ip, args.port))
tcpsock.bind((ip, port))
in_t = None with socketserver.TCPServer((ip, port), MyAPRSTCPHandler) as server:
while True: server.serve_forever()
tcpsock.listen(4)
LOG.info("Waiting for incomming connections....")
(conn, (ip, port)) = tcpsock.accept()
newthread = ClientThread(msg_q, ip, port, conn)
newthread.start()
threads.append(newthread)
if not in_t:
in_t = InputThread(msg_q)
in_t.daemon = True
in_t.start()
in_t.join()
for t in threads:
t.join()
if __name__ == "__main__": if __name__ == "__main__":
main(args) main()

View File

@ -41,12 +41,12 @@ import time
import urllib import urllib
from email.mime.text import MIMEText from email.mime.text import MIMEText
import imapclient
import imaplib
from logging.handlers import RotatingFileHandler from logging.handlers import RotatingFileHandler
# external lib imports
from imapclient import IMAPClient, SEEN
# local imports here # local imports here
import aprsd
from aprsd.fuzzyclock import fuzzy from aprsd.fuzzyclock import fuzzy
from aprsd import utils from aprsd import utils
@ -118,13 +118,13 @@ def setup_connection():
sock.connect((CONFIG['aprs']['host'], 14580)) sock.connect((CONFIG['aprs']['host'], 14580))
sock.settimeout(60) sock.settimeout(60)
connected = True connected = True
except Exception, e: except Exception as e:
print "Unable to connect to APRS-IS server.\n" print("Unable to connect to APRS-IS server.\n")
print str(e) print(str(e))
time.sleep(5) time.sleep(5)
continue continue
#os._exit(1) # os._exit(1)
sock_file = sock.makefile(mode='r', bufsize=0 ) sock_file = sock.makefile(mode='r')
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) # disable nagle algorithm sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) # disable nagle algorithm
sock.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 512) # buffer size sock.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 512) # buffer size
@ -145,8 +145,9 @@ def parse_email(msgid, data, server):
from_addr = f.group(1) from_addr = f.group(1)
else: else:
from_addr = "noaddr" from_addr = "noaddr"
LOG.debug("Got a message from '{}'".format(from_addr))
m = server.fetch([msgid], ['RFC822']) m = server.fetch([msgid], ['RFC822'])
msg = email.message_from_string(m[msgid]['RFC822']) msg = email.message_from_string(m[msgid][b'RFC822'].decode())
if msg.is_multipart(): if msg.is_multipart():
text = "" text = ""
html = None html = None
@ -192,6 +193,7 @@ def parse_email(msgid, data, server):
body = text.strip() body = text.strip()
# strip all html tags # strip all html tags
body = body.decode()
body = re.sub('<[^<]+?>', '', body) body = re.sub('<[^<]+?>', '', body)
# strip CR/LF, make it one line, .rstrip fails at this # strip CR/LF, make it one line, .rstrip fails at this
body = body.replace("\n", " ").replace("\r", " ") body = body.replace("\n", " ").replace("\r", " ")
@ -199,6 +201,88 @@ def parse_email(msgid, data, server):
# end parse_email # end parse_email
def _imap_connect():
imap_port = CONFIG['imap'].get('port', 143)
use_ssl = CONFIG['imap'].get('use_ssl', False)
host = CONFIG['imap']['host']
msg = ("{}{}:{}".format(
'TLS ' if use_ssl else '',
host,
imap_port
))
LOG.debug("Connect to IMAP host {} with user '{}'".
format(msg, CONFIG['imap']['login']))
try:
server = imapclient.IMAPClient(CONFIG['imap']['host'], port=imap_port,
use_uid=True, ssl=use_ssl)
except Exception:
LOG.error("Failed to connect IMAP server")
return
LOG.debug("Connected to IMAP host {}".format(msg))
try:
server.login(CONFIG['imap']['login'], CONFIG['imap']['password'])
except (imaplib.IMAP4.error, Exception) as e:
msg = getattr(e, 'message', repr(e))
LOG.error("Failed to login {}".format(msg))
return
LOG.debug("Logged in to IMAP, selecting INBOX")
server.select_folder('INBOX')
return server
def _smtp_connect():
host = CONFIG['smtp']['host']
smtp_port = CONFIG['smtp']['port']
use_ssl = CONFIG['smtp'].get('use_ssl', False)
msg = ("{}{}:{}".format(
'SSL ' if use_ssl else '',
host,
smtp_port
))
LOG.debug("Connect to SMTP host {} with user '{}'".
format(msg, CONFIG['imap']['login']))
try:
if use_ssl:
server = smtplib.SMTP_SSL(host=host, port=smtp_port)
else:
server = smtplib.SMTP(host=host, port=smtp_port)
except Exception:
LOG.error("Couldn't connect to SMTP Server")
return
LOG.debug("Connected to smtp host {}".format(msg))
try:
server.login(CONFIG['smtp']['login'], CONFIG['smtp']['password'])
except Exception:
LOG.error("Couldn't connect to SMTP Server")
return
LOG.debug("Logged into SMTP server {}".format(msg))
return server
def validate_email():
"""function to simply ensure we can connect to email services.
This helps with failing early during startup.
"""
LOG.info("Checking IMAP configuration")
imap_server = _imap_connect()
LOG.info("Checking SMTP configuration")
smtp_server = _smtp_connect()
if imap_server and smtp_server:
return True
else:
return False
def resend_email(count, fromcall): def resend_email(count, fromcall):
date = datetime.datetime.now() date = datetime.datetime.now()
month = date.strftime("%B")[:3] # Nov, Mar, Apr month = date.strftime("%B")[:3] # Nov, Mar, Apr
@ -210,12 +294,11 @@ def resend_email(count, fromcall):
# swap key/value # swap key/value
shortcuts_inverted = dict([[v, k] for k, v in shortcuts.items()]) shortcuts_inverted = dict([[v, k] for k, v in shortcuts.items()])
LOG.debug("resend_email: Connect to IMAP host '%s' with user '%s'" % try:
(CONFIG['imap']['host'], server = _imap_connect()
CONFIG['imap']['login'])) except Exception as e:
server = IMAPClient(CONFIG['imap']['host'], use_uid=True) LOG.exception("Failed to Connect to IMAP. Cannot resend email ", e)
server.login(CONFIG['imap']['login'], CONFIG['imap']['password']) return
server.select_folder('INBOX')
messages = server.search(['SINCE', today]) messages = server.search(['SINCE', today])
LOG.debug("%d messages received today" % len(messages)) LOG.debug("%d messages received today" % len(messages))
@ -229,7 +312,7 @@ def resend_email(count, fromcall):
# one at a time, otherwise order is random # one at a time, otherwise order is random
(body, from_addr) = parse_email(msgid, data, server) (body, from_addr) = parse_email(msgid, data, server)
# unset seen flag, will stay bold in email client # unset seen flag, will stay bold in email client
server.remove_flags(msgid, [SEEN]) server.remove_flags(msgid, [imapclient.SEEN])
if from_addr in shortcuts_inverted: if from_addr in shortcuts_inverted:
# reverse lookup of a shortcut # reverse lookup of a shortcut
from_addr = shortcuts_inverted[from_addr] from_addr = shortcuts_inverted[from_addr]
@ -260,6 +343,7 @@ def check_email_thread(check_email_delay):
while True: while True:
# threading.Timer(55, check_email_thread).start() # threading.Timer(55, check_email_thread).start()
LOG.debug("Top of check_email_thread.")
time.sleep(check_email_delay) time.sleep(check_email_delay)
@ -273,22 +357,17 @@ def check_email_thread(check_email_delay):
year = date.year year = date.year
today = "%s-%s-%s" % (day, month, year) today = "%s-%s-%s" % (day, month, year)
LOG.debug("Connect to IMAP host '%s' with user '%s'" % server = None
(CONFIG['imap']['host'],
CONFIG['imap']['login']))
try: try:
server = IMAPClient(CONFIG['imap']['host'], use_uid=True, timeout=5) server = _imap_connect()
server.login(CONFIG['imap']['login'], CONFIG['imap']['password']) except Exception as e:
except Exception: LOG.exception("Failed to get IMAP server Can't check email.", e)
LOG.exception("Failed to login with IMAP server")
# return if not server:
continue continue
server.select_folder('INBOX')
messages = server.search(['SINCE', today]) messages = server.search(['SINCE', today])
LOG.debug("%d messages received today" % len(messages)) LOG.debug("{} messages received today".format(len(messages)))
for msgid, data in server.fetch(messages, ['ENVELOPE']).items(): for msgid, data in server.fetch(messages, ['ENVELOPE']).items():
envelope = data[b'ENVELOPE'] envelope = data[b'ENVELOPE']
@ -306,7 +385,7 @@ def check_email_thread(check_email_delay):
server.fetch([msgid], ['RFC822']) server.fetch([msgid], ['RFC822'])
(body, from_addr) = parse_email(msgid, data, server) (body, from_addr) = parse_email(msgid, data, server)
# unset seen flag, will stay bold in email client # unset seen flag, will stay bold in email client
server.remove_flags(msgid, [SEEN]) server.remove_flags(msgid, [imapclient.SEEN])
if from_addr in shortcuts_inverted: if from_addr in shortcuts_inverted:
# reverse lookup of a shortcut # reverse lookup of a shortcut
@ -319,7 +398,7 @@ def check_email_thread(check_email_delay):
# flag message as sent via aprs # flag message as sent via aprs
server.add_flags(msgid, ['APRS']) server.add_flags(msgid, ['APRS'])
# unset seen flag, will stay bold in email client # unset seen flag, will stay bold in email client
server.remove_flags(msgid, [SEEN]) server.remove_flags(msgid, [imapclient.SEEN])
server.logout() server.logout()
@ -336,7 +415,7 @@ def send_ack_thread(tocall, ack, retry_count):
LOG.info("To : {}".format(tocall)) LOG.info("To : {}".format(tocall))
LOG.info("Ack number : {}".format(ack)) LOG.info("Ack number : {}".format(ack))
# tn.write(line) # tn.write(line)
sock.send(line) sock.send(line.encode())
# aprs duplicate detection is 30 secs? # aprs duplicate detection is 30 secs?
# (21 only sends first, 28 skips middle) # (21 only sends first, 28 skips middle)
time.sleep(31) time.sleep(31)
@ -347,6 +426,7 @@ def send_ack_thread(tocall, ack, retry_count):
def send_ack(tocall, ack): def send_ack(tocall, ack):
retry_count = 3 retry_count = 3
thread = threading.Thread(target=send_ack_thread, thread = threading.Thread(target=send_ack_thread,
name="send_ack",
args=(tocall, ack, retry_count)) args=(tocall, ack, retry_count))
thread.start() thread.start()
return() return()
@ -375,7 +455,7 @@ def send_message_thread(tocall, message, this_message_number, retry_count):
LOG.info("To : {}".format(tocall)) LOG.info("To : {}".format(tocall))
LOG.info("Message : {}".format(message)) LOG.info("Message : {}".format(message))
# tn.write(line) # tn.write(line)
sock.send(line) sock.send(line.encode())
# decaying repeats, 31 to 93 second intervals # decaying repeats, 31 to 93 second intervals
sleeptime = (retry_count - i + 1) * 31 sleeptime = (retry_count - i + 1) * 31
time.sleep(sleeptime) time.sleep(sleeptime)
@ -410,6 +490,7 @@ def send_message(tocall, message):
message = message[:67] message = message[:67]
thread = threading.Thread( thread = threading.Thread(
target=send_message_thread, target=send_message_thread,
name="send_message",
args=(tocall, message, message_number, retry_count)) args=(tocall, message, message_number, retry_count))
thread.start() thread.start()
return() return()
@ -463,18 +544,17 @@ def send_email(to_addr, content):
msg['Subject'] = subject msg['Subject'] = subject
msg['From'] = CONFIG['smtp']['login'] msg['From'] = CONFIG['smtp']['login']
msg['To'] = to_addr msg['To'] = to_addr
s = smtplib.SMTP_SSL(CONFIG['smtp']['host'], server = _smtp_connect()
CONFIG['smtp']['port']) if server:
s.login(CONFIG['smtp']['login'], try:
CONFIG['smtp']['password']) server.sendmail(CONFIG['smtp']['login'], [to_addr], msg.as_string())
try: except Exception as e:
s.sendmail(CONFIG['smtp']['login'], [to_addr], msg.as_string()) msg = getattr(e, 'message', repr(e))
except Exception: LOG.error("Sendmail Error!!!! '{}'", msg)
LOG.exception("Sendmail Error!!!!!!!!!") server.quit()
s.quit() return(-1)
return(-1) server.quit()
s.quit() return(0)
return(0)
# end send_email # end send_email
@ -492,7 +572,7 @@ def setup_logging(args):
log_level = levels[args.loglevel] log_level = levels[args.loglevel]
LOG.setLevel(log_level) LOG.setLevel(log_level)
log_format = ("%(asctime)s [%(threadName)-12.12s] [%(levelname)-5.5s]" log_format = ("%(asctime)s [%(threadName)-12s] [%(levelname)-5.5s]"
" %(message)s") " %(message)s")
date_format = '%m/%d/%Y %I:%M:%S %p' date_format = '%m/%d/%Y %I:%M:%S %p'
log_formatter = logging.Formatter(fmt=log_format, log_formatter = logging.Formatter(fmt=log_format,
@ -515,34 +595,38 @@ def main(args=args):
CONFIG = utils.parse_config(args) CONFIG = utils.parse_config(args)
signal.signal(signal.SIGINT, signal_handler) signal.signal(signal.SIGINT, signal_handler)
LOG.info("APRSD Started")
LOG.debug(CONFIG)
setup_logging(args) setup_logging(args)
LOG.info("APRSD Started version: {}".format(aprsd.__version__))
time.sleep(2) time.sleep(2)
setup_connection() setup_connection()
valid = validate_email()
if not valid:
LOG.error("Failed to validate email config options")
sys.exit(-1)
user = CONFIG['aprs']['login'] user = CONFIG['aprs']['login']
password = CONFIG['aprs']['password'] password = CONFIG['aprs']['password']
LOG.info("LOGIN to APRSD with user '%s'" % user) LOG.debug("LOGIN to APRSD with user '%s'" % user)
# tn.write("user %s pass %s vers aprsd 0.99\n" % (user, password)) msg = ("user {} pass {} vers aprsd {}\n".format(user, password,
sock.send("user %s pass %s vers https://github.com/craigerl/aprsd 2.00\n" % (user, password)) aprsd.__version__))
sock.send(msg.encode())
time.sleep(2) time.sleep(2)
check_email_delay = 60 # initial email check interval check_email_delay = 60 # initial email check interval
checkemailthread = threading.Thread(target=check_email_thread, args=(check_email_delay, )) # args must be tuple checkemailthread = threading.Thread(target=check_email_thread,
name="check_email",
args=(check_email_delay, )) # args must be tuple
checkemailthread.start() checkemailthread.start()
LOG.info("Start main loop") LOG.info("Start main loop")
while True: while True:
line = "" line = ""
try: try:
# for char in tn.read_until("\n", 100):
# line = line + char
# line = line.replace('\n', '')
line = sock_file.readline().strip() line = sock_file.readline().strip()
LOG.info(line) if line:
LOG.info(line)
searchstring = '::%s' % user searchstring = '::%s' % user
# is aprs message to us, not beacon, status, etc # is aprs message to us, not beacon, status, etc
if re.search(searchstring, line): if re.search(searchstring, line):
@ -609,7 +693,9 @@ def main(args=args):
m = stm.tm_min m = stm.tm_min
cur_time = fuzzy(h, m, 1) cur_time = fuzzy(h, m, 1)
reply = cur_time + " (" + str(h) + ":" + str(m).rjust(2, '0') + "PDT)" + " (" + message.rstrip() + ")" reply = cur_time + " (" + str(h) + ":" + str(m).rjust(2, '0') + "PDT)" + " (" + message.rstrip() + ")"
thread = threading.Thread(target=send_message, args=(fromcall, reply)) thread = threading.Thread(target=send_message,
name="send_message",
args=(fromcall, reply))
thread.start() thread.start()
# FORTUNE (f) # FORTUNE (f)
@ -705,15 +791,15 @@ def main(args=args):
except Exception as e: except Exception as e:
LOG.error("Error in mainline loop:") LOG.error("Error in mainline loop:")
LOG.error("%s" % str(e)) LOG.error("%s" % str(e))
if str(e) == "timed out" or str(e) == "Temporary failure in name resolution" or str(e) == "Network is unreachable": if (str(e) == "timed out" or str(e) == "Temporary failure in name resolution" or str(e) == "Network is unreachable"):
LOG.error("Attempting to reconnect.") LOG.error("Attempting to reconnect.")
sock.shutdown(0) sock.shutdown(0)
sock.close() sock.close()
setup_connection() setup_connection()
sock.send("user %s pass %s vers https://github.com/craigerl/aprsd 2.00\n" % (user, password)) sock.send("user %s pass %s vers https://github.com/craigerl/aprsd 2.00\n" % (user, password))
continue continue
#LOG.error("Exiting.") # LOG.error("Exiting.")
#os._exit(1) # os._exit(1)
time.sleep(5) time.sleep(5)
continue # don't know what failed, so wait and then continue main loop again continue # don't know what failed, so wait and then continue main loop again

View File

@ -53,7 +53,7 @@ def get_config():
config_file = os.path.expanduser("~/.aprsd/config.yml") config_file = os.path.expanduser("~/.aprsd/config.yml")
if os.path.exists(config_file): if os.path.exists(config_file):
with open(config_file, "r") as stream: with open(config_file, "r") as stream:
config = yaml.load(stream) config = yaml.load(stream, Loader=yaml.FullLoader)
return config return config
else: else:
log.critical("%s is missing, please create config file" % config_file) log.critical("%s is missing, please create config file" % config_file)

View File

@ -21,6 +21,7 @@ packages =
[entry_points] [entry_points]
console_scripts = console_scripts =
aprsd = aprsd.main:main aprsd = aprsd.main:main
fake_aprs = aprsd.fake_aprs:main
[build_sphinx] [build_sphinx]
source-dir = doc/source source-dir = doc/source