@ -2,20 +2,25 @@
# file: apistatusd.py
# date: 26.07.2019
# e mail: berhsi@web.de
# mail: berhsi@web.de
# Status server, listening for door status updates. The IPv4 address and port
# to listen on are configurable, by default localhost:10001 is used. The
# connection is secured by TLS and client side authentication.
import json
import logging
import os
import socket
import ssl
import sys
from time import time , sleep
import configparser
try :
import json
import logging
import os
import socket
import ssl
import sys
import requests
import threading
from time import time , localtime , strftime , sleep
import configparser
except ImportException as e :
print ( ' Import error: {} ' . format ( e ) )
def certs_readable ( config ) :
@ -32,7 +37,6 @@ def certs_readable(config):
return False
return True
def print_config ( config ) :
'''
Logs the config with level debug .
@ -41,7 +45,10 @@ def print_config(config):
for section in config . sections ( ) :
logging . debug ( ' Section {} ' . format ( section ) )
for i in config [ section ] :
logging . debug ( ' {} : {} ' . format ( i , config [ section ] [ i ] ) )
if i == ' token ' :
logging . debug ( ' {} : {} ' . format ( i , ' aaaaa-bbbbb-ccccc-ddddd-eeeee ' ) )
else :
logging . debug ( ' {} : {} ' . format ( i , config [ section ] [ i ] ) )
def print_ciphers ( cipherlist ) :
'''
@ -101,25 +108,22 @@ def receive_buffer_is_valid(raw_data):
logging . debug ( ' Argument is not valid: {} ' . format ( raw_data ) )
return False
def change_status ( raw_data , api ) :
def change_status ( status , timestamp , filename ) :
'''
Write the new status together with a timestamp into the Space API JSON .
param 1 : byte object
param 2 : string
return : boolean
'''
logging . debug ( ' Change status API ' )
# todo: use walrus operator := when migrating to python >= 3.8
data = read_api ( api )
data = read_api ( filename )
if data is False :
return False
status , timestamp = set_values ( raw_data )
if os . access ( api , os . W_OK ) :
if os . access ( filename , os . W_OK ) :
logging . debug ( ' API file is writable ' )
with open ( api , ' w ' ) as api_file :
with open ( filename , ' w ' ) as api_file :
logging . debug ( ' API file open successfull ' )
data [ " state " ] [ " open " ] = status
data [ " state " ] [ " lastchange " ] = timestamp
@ -133,10 +137,9 @@ def change_status(raw_data, api):
else :
logging . error ( ' API file is not writable. Wrong permissions? ' )
return False
logging . info ( ' Status successfull changed to {} ' . format ( status ) )
logging . info ( ' API file successfull changed to {} ' . format ( status ) )
return True
def read_api ( api ) :
'''
Reads the Space API JSON into a dict . Returns the dict on success and
@ -146,7 +149,6 @@ def read_api(api):
return : dict or boolean
'''
logging . debug ( ' Open API file: {} ' . format ( api ) )
# return early if the API JSON cannot be read
if not os . access ( api , os . R_OK ) :
logging . error ( ' Failed to read API file ' )
@ -163,13 +165,12 @@ def read_api(api):
return False
return api_json_data
def set_values ( raw_data ) :
def get_status_and_time ( raw_data ) :
'''
Create a timestamp , changes the value of the given byte into a string
and returns both .
param 1 : byte object
return : tuple
return : tuple ( boolean , integer )
'''
status = True if raw_data . decode ( ' utf-8 ' , ' strict ' ) == ' 1 ' else False
timestamp = int ( str ( time ( ) ) . split ( ' . ' ) [ 0 ] )
@ -178,6 +179,83 @@ def set_values(raw_data):
str ( timestamp ) , str ( status ) ) )
return ( status , timestamp )
def join_path ( host , api ) :
'''
Becomes two parts ( host and api ) of the mastodon url and concanate them
param1 : string
param2 : string
return : string
'''
url = ' '
try :
if host [ - 1 ] == ' / ' and api [ 0 ] == ' / ' :
url = ' ' . join ( ( host , api [ 1 : ] ) )
elif host [ - 1 ] != ' / ' and api [ 0 ] != ' / ' :
url = ' / ' . join ( ( host , api ) )
else :
url = ' ' . join ( ( host , api ) )
except TypeError as e :
logging . error ( ' Can´ t join path: {} ' . format ( e ) )
return url
class Toot ( threading . Thread ) :
'''
The thread to toot the status to mastodon .
'''
def __init__ ( self , status , timestamp , config ) :
'''
param1 : boolean
param2 : integer
param3 : dictionary
'''
threading . Thread . __init__ ( self )
self . status = status
self . config = config
self . timestamp = timestamp
self . api = ' /api/v1/statuses '
self . auth = { ' Authorization ' : ' ' }
self . data = { ' status ' : ' ' }
self . url = ' '
def run ( self ) :
'''
return : boolean
'''
timeformat = ' %d . % m. % Y % H: % M Uhr '
# check if status is valid
if self . status not in ( True , False ) :
logging . error ( ' Invalid status to toot ' )
return False
# convert seconds into timestring
try :
timestring = strftime ( timeformat , localtime ( self . timestamp ) )
except Exception as e :
logging . error ( ' Can not convert timestamp into timestring ' )
return False
# set status message
if self . status == True :
self . data [ ' status ' ] = ' Krautspace is open since: {} ' . format ( timestring )
elif self . status == False :
self . data [ ' status ' ] = ' Krautspace is closed since: {} ' . format ( timestring )
logging . debug ( ' Message: {} ' . format ( self . data [ ' status ' ] ) )
# build mastodon api url
self . url = join_path ( self . config [ ' mastodon ' ] [ ' host ' ] , self . api )
# build authentcation header
self . auth [ ' Authorization ' ] = ' Bearer {} ' . format (
self . config [ ' mastodon ' ] [ ' token ' ] )
# and finaly send request to mastodon
try :
logging . debug ( ' Try to toot status ' )
response = requests . post ( self . url , data = self . data ,
headers = self . auth )
if response . status_code == 200 :
logging . info ( ' Toot successful send ' )
return True
else :
logging . error ( ' Failed to toot. Response: {} ' . format ( response . status_code ) )
except Exception as e :
logging . error ( ' Exception occurred: {} ' . format ( e ) )
return False
def main ( ) :
'''
@ -188,7 +266,6 @@ def main():
OP_DONT_ISERT_EMPTY_FRAGMENTS : prevention agains CBC 4 attack
( cve - 2011 - 3389 )
'''
answer = ' 3 ' . encode ( encoding = ' utf-8 ' , errors = ' strict ' )
loglevel = logging . WARNING
formatstring = ' %(asctime)s : %(levelname)s : %(message)s '
@ -211,6 +288,11 @@ def main():
' api ' : {
' api ' : ' ./api ' ,
' template ' : ' ./api_template '
} ,
' mastodon ' : {
' send ' : ' false ' ,
' host ' : ' localhost ' ,
' token ' : ' aaaaa-bbbbb-ccccc-ddddd-eeeee '
}
}
configfile = ' ./apistatusd.conf '
@ -293,8 +375,19 @@ def main():
raw_data = conn . recv ( 1 )
if receive_buffer_is_valid ( raw_data ) is True :
if change_status ( raw_data , config [ ' api ' ] [ ' api ' ] ) is True :
status , timestamp = get_status_and_time ( raw_data )
if change_status ( status , timestamp , config [ ' api ' ] [ ' api ' ] ) is True :
answer = raw_data
if config [ ' mastodon ' ] [ ' send ' ] . lower ( ) == ' true ' :
logging . debug ( ' Toot is set to true ' )
try :
toot_thread = Toot ( status , timestamp , config )
toot_thread . run ( )
except InitException as e :
logging . error ( ' InitException: {} ' . format ( e ) )
except Exception as ex :
logging . debug ( ' Exception: {} ' . format ( ex ) )
else : logging . debug ( ' Toot is set to false ' )
if conn :
logging . debug ( ' Send {} back ' . format ( raw_data ) )
conn . send ( answer )