2021-09-05 23:01:09 +00:00
import copy
2021-08-29 19:42:37 +00:00
import os
2021-11-14 03:55:48 +00:00
import re
2021-08-24 22:51:20 +00:00
import json
import requests
2022-08-14 17:20:00 +00:00
import tarfile
import shutil
2021-08-24 22:51:20 +00:00
import scss
2021-08-29 19:42:37 +00:00
import markdown
2021-11-14 03:55:48 +00:00
import bs4
2021-09-07 11:58:34 +00:00
import werkzeug # mainly for isinstance checks
2022-08-14 17:20:00 +00:00
import click
2021-08-24 22:51:20 +00:00
import flask
2021-09-07 11:58:34 +00:00
from flask import flash , abort , redirect
2021-08-29 19:42:37 +00:00
2022-05-27 01:31:11 +00:00
app = flask . Flask ( __name__ )
app . config . from_object ( ' defaults ' )
app . config . from_object ( ' config ' )
if ' RESOURCE_DIR ' not in app . config :
app . config [ ' RESOURCE_DIR ' ] = os . path . join ( os . getcwd ( ) , ' resources ' )
if ' ARTICLE_DIR ' not in app . config :
app . config [ ' ARTICLE_DIR ' ] = os . path . join ( os . getcwd ( ) , ' articles ' )
scss_compiler = scss . Compiler ( )
markdown_compiler = markdown . Markdown ( extensions = [ ' tables ' , ' footnotes ' ] )
2021-08-24 22:51:20 +00:00
2021-12-24 02:26:59 +00:00
class ExposedException ( Exception ) :
def __init__ ( self , message , status = 500 ) :
self . message = message
self . status = status
2021-08-24 22:51:20 +00:00
2022-03-23 00:44:11 +00:00
def headline_address ( text ) :
text = text . lower ( )
text = text . replace ( ' ' , ' - ' )
# purge everything that's not 'a' to 'z' or '-' with fire
text = re . sub ( r ' [^a-z0-9-] ' , ' ' , text )
return text
2021-10-03 22:51:59 +00:00
def replace_thingamabob ( value , invite_info , os , client , installer ) :
# FIXME: Brain up a less shitty name for this.
2022-03-23 00:44:11 +00:00
# Data preprocessing for invite_main
2021-10-03 22:51:59 +00:00
2022-05-27 01:31:11 +00:00
client_info = app . config [ ' CLIENT_INFO ' ] [ client ]
installer_info = app . config [ ' INSTALLER_INFO ' ] [ os ] [ installer ]
2021-10-03 22:51:59 +00:00
if ' {invite.uri} ' in value :
value = value . replace ( ' {invite.uri} ' , invite_info [ ' uri ' ] )
if ' {invite.register_url} ' in value :
2021-11-14 03:55:48 +00:00
value = value . replace ( ' {invite.register_url} ' , flask . url_for ( ' .invite_register ' , token = invite_info [ ' token ' ] , client = client ) )
2021-10-03 22:51:59 +00:00
if ' {client.name} ' in value :
value = value . replace ( ' {client.name} ' , client_info [ ' title ' ] )
if installer_info [ ' url_support ' ] :
2021-11-14 03:55:48 +00:00
if os in client_info [ ' installer_magic ' ] :
2021-10-03 22:51:59 +00:00
if ' {client.url_magic} ' in value :
2021-11-14 03:55:48 +00:00
value = value . replace ( ' {client.url_magic} ' , client_info [ ' installer_magic ' ] [ os ] [ installer ] [ ' url ' ] )
2021-10-03 22:51:59 +00:00
if ' {client.url_install} ' in value :
2021-11-14 03:55:48 +00:00
value = value . replace ( ' {client.url_install} ' , client_info [ ' installer_manual ' ] [ os ] [ installer ] [ ' url ' ] )
2021-10-03 22:51:59 +00:00
if installer_info [ ' pkgname_support ' ] :
if ' {client.pkgname} ' in value :
2021-11-14 03:55:48 +00:00
value = value . replace ( ' {client.pkgname} ' , client_info [ ' installer_manual ' ] [ os ] [ installer ] [ ' pkgname ' ] )
2021-10-03 22:51:59 +00:00
if ' guide ' in installer_info :
if ' {installer.guide} ' in value :
value = value . replace ( ' {installer.guide} ' , installer_info [ ' guide ' ] )
if ' {installer.step_install} ' in value :
2021-11-14 03:55:48 +00:00
if client_info [ ' installer_manual ' ] [ os ] [ installer ] [ ' gratis ' ] :
2021-10-03 22:51:59 +00:00
step = installer_info [ ' step_install ' ] [ ' gratis ' ]
else :
step = installer_info [ ' step_install ' ] [ ' paid ' ]
2021-12-24 02:26:59 +00:00
value = value . replace ( ' {installer.step_install} ' , step )
2021-10-03 22:51:59 +00:00
if ' {register} ' in value :
if ' invites ' in client_info [ ' features ' ] :
2022-05-27 01:31:11 +00:00
text = app . config [ ' REGISTER_INFO ' ] [ ' client ' ]
2021-10-03 22:51:59 +00:00
else :
2022-05-27 01:31:11 +00:00
text = app . config [ ' REGISTER_INFO ' ] [ ' web ' ]
2021-10-03 22:51:59 +00:00
value = value . replace ( ' {register} ' , replace_thingamabob ( text , invite_info , os , client , installer ) )
return value
2021-08-24 22:51:20 +00:00
def page ( f ) :
template_candidates = [
f " { f . __name__ } .jinja " ,
" main.jinja " ,
]
def wrapper ( * args , * * kwargs ) :
2021-12-24 02:26:59 +00:00
env = {
2022-05-27 01:31:11 +00:00
' claim ' : app . config [ ' CLAIM ' ] ,
' support_address ' : app . config [ ' SUPPORT_ADDRESS ' ] ,
2021-12-24 02:26:59 +00:00
' menu ' : menu ( ' main ' ) ,
' messages ' : flask . get_flashed_messages ( with_categories = True ) ,
}
try :
r = f ( * args , * * kwargs )
except ExposedException as e :
2022-05-27 02:09:04 +00:00
app . logger . error ( f " Caught error: { e . message } " )
2022-06-06 16:01:29 +00:00
return flask . render_template ( ' error.jinja ' , error = e , * * env ) , e . status
2022-01-01 19:26:51 +00:00
except Exception as e :
app . logger . error ( f " Caught unplanned error: " )
2022-05-27 02:01:11 +00:00
app . log_exception ( e )
2022-06-06 16:01:29 +00:00
return flask . render_template ( ' error.jinja ' , error = ExposedException ( ' Something went wrong. :( ' ) , * * env ) , 500
2021-12-24 02:26:59 +00:00
2021-09-07 11:58:34 +00:00
if isinstance ( r , werkzeug . wrappers . Response ) :
return r # directly pass constructed Responses (like redirects) through
2021-08-24 22:51:20 +00:00
if isinstance ( r , dict ) :
2021-12-24 02:26:59 +00:00
env . update ( r )
2021-08-24 22:51:20 +00:00
else :
2021-12-24 02:26:59 +00:00
env [ ' content ' ] : r
2021-08-29 19:42:37 +00:00
2021-12-24 02:31:09 +00:00
try :
rendered = flask . render_template ( template_candidates , * * env )
2022-01-01 19:26:51 +00:00
except Exception as e :
app . logger . error ( f " Caught templating error. " )
2022-05-27 02:01:11 +00:00
app . log_exception ( e )
2021-12-24 02:31:09 +00:00
return flask . render_template ( ' error.jinja ' , error = ExposedException ( ' Something went wrong because of a broken template. :( ' ) , * * env )
return rendered
2021-08-24 22:51:20 +00:00
wrapper . __name__ = f . __name__ # i sure hope this doesn't fuck shit up
return wrapper
2021-12-24 02:26:59 +00:00
def ensure_token ( token ) :
try :
invite_info = get_invite_info ( token )
except ValueError :
2022-06-06 16:59:46 +00:00
raise ExposedException ( " Couldn ' t get info for this token - are you sure it ' s valid? " , 410 )
2021-12-24 02:26:59 +00:00
except RuntimeError :
2022-06-06 16:01:29 +00:00
raise ExposedException ( ' Something seems to have gone wrong on our side.<img src= " /resources/sorry.gif " alt= " Sorry… " title= " Sorry… " /> ' )
2021-12-24 02:26:59 +00:00
2022-01-01 19:26:51 +00:00
if invite_info [ ' type ' ] != ' register ' :
raise ExposedException ( f """ You have some kind of invite for this service, but
this site doesn ' t handle this specific invite type: {invite_info['type']} .
2022-06-06 16:01:29 +00:00
Feel free to contact { app . config [ ' SUPPORT_ADDRESS ' ] } . """ , 204)
2022-01-01 19:26:51 +00:00
return invite_info
2021-08-24 22:51:20 +00:00
def best_os_guess ( request ) :
2021-10-03 22:51:59 +00:00
2021-08-24 22:51:20 +00:00
agent = request . headers [ " User-Agent " ]
if " Android " in agent :
return " android "
elif " iPhone " in agent :
return " ios "
elif " Windows " in agent :
return " windows "
elif " Mac OS " in agent :
return " macos "
elif " Linux " in agent :
return " linux "
elif " FreeBSD " in agent :
return " freebsd "
elif " OpenBSD " in agent :
return " openbsd "
elif " NetBSD " in agent :
return " netbsd "
return " other "
def get_invite_info ( token ) :
2021-09-05 23:01:09 +00:00
try :
2022-05-27 01:31:11 +00:00
r = requests . get ( f " { app . config [ ' API_BASE_URL ' ] } /invite/ { token } " )
2021-09-05 23:01:09 +00:00
except requests . exceptions . ConnectionError :
raise RuntimeError ( " Unable to establish connection to XMPP server! " )
2021-08-24 22:51:20 +00:00
if r . status_code == 200 :
return json . loads ( r . text )
elif r . status_code > = 400 and r . status_code < = 499 :
raise ValueError ( " Invalid data! " )
elif r . status_code > = 500 and r . status_code < = 599 :
raise RuntimeError ( " Server-side error indicated! " )
else :
raise RuntimeError ( f " Non-specific error: { r . text } " )
def register_with_invite ( username , password , token ) :
data = {
2021-10-03 22:51:59 +00:00
' username ' : username ,
2021-08-24 22:51:20 +00:00
' password ' : password ,
' token ' : token ,
}
payload = json . dumps ( data )
2022-05-27 01:31:11 +00:00
r = requests . post ( f " { app . config [ ' API_BASE_URL ' ] } /register " , payload , headers = { ' Content-Type ' : ' application/json ' } )
2021-08-24 22:51:20 +00:00
if r . status_code == 200 :
return
elif r . status_code == 409 :
raise ValueError ( " A user with this name already exists! " )
elif r . status_code > = 400 and r . status_code < = 499 :
raise ValueError ( " Invalid data! " )
elif r . status_code > = 500 and r . status_code < = 599 :
raise RuntimeError ( " Server-side error indicated! " )
else :
raise RuntimeError ( f " Non-specific error: { r . text } " )
2021-11-14 03:55:48 +00:00
def any_in ( value , candidates ) :
return any (
map (
lambda x : x . lower ( ) in value . lower ( ) ,
candidates
)
)
2021-12-24 02:26:59 +00:00
# ,- words that are considered a match
2021-11-26 07:39:56 +00:00
# |
2021-12-24 02:26:59 +00:00
# | ,- end of string or nonword
2021-11-26 07:39:56 +00:00
# | | char guarantees matched
# | | words stand on their own
# ↓ ↓
match_good = re . compile ( ' ^(good|yes|positive|true)($| \ W) ' , re . IGNORECASE )
match_bad = re . compile ( ' ^(bad|no|negative|false)($| \ W) ' , re . IGNORECASE )
match_meh = re . compile ( ' ^(meh|partial|unverified|mild)($| \ W) ' , re . IGNORECASE )
2021-08-29 19:42:37 +00:00
def article_load ( name ) :
article_path = os . path . join ( app . config [ ' ARTICLE_DIR ' ] , name + ' .md ' )
if os . path . exists ( article_path ) :
2021-11-14 03:55:48 +00:00
2022-05-27 02:02:59 +00:00
with open ( article_path , ' r ' , encoding = ' UTF-8 ' ) as fd :
2021-08-29 19:42:37 +00:00
text = fd . read ( )
2021-11-14 03:55:48 +00:00
text = markdown_compiler . convert ( text )
markdown_compiler . reset ( )
soup = bs4 . BeautifulSoup ( text , ' html.parser ' )
# Extract title and subtitle for specialized presentation
2022-04-23 01:12:45 +00:00
header = False
2021-11-14 03:55:48 +00:00
title = None
subtitle = None
2022-04-23 01:12:45 +00:00
scan_offset = 0
2021-11-14 03:55:48 +00:00
for element in soup . children :
if isinstance ( element , bs4 . Tag ) :
if title is None and element . name == ' h1 ' :
title = element . text
element . decompose ( )
2022-04-23 01:12:45 +00:00
scan_offset + = 1
2021-11-14 03:55:48 +00:00
continue # skip to next loop iteration
elif title != None and subtitle is None and element . name == ' h2 ' :
subtitle = element . text
element . decompose ( )
2022-04-23 01:12:45 +00:00
scan_offset + = 1
2021-11-14 03:55:48 +00:00
break # terminate loop
else :
# terminate loop, if first tag isn't h1 we don't have to
# scan the rest of the document
2021-12-24 02:26:59 +00:00
break
2021-11-14 03:55:48 +00:00
# Split rest of content into sections based on h2 elements
2022-04-23 01:12:45 +00:00
first_scanned_element = soup . contents [ scan_offset ]
if type ( first_scanned_element ) == bs4 . element . Tag and first_scanned_element . name == ' section ' :
header = str ( first_scanned_element ) # if the text (after title and subtitle) begins with a <section>, treat it as dedicated header
first_scanned_element . decompose ( )
2021-11-14 03:55:48 +00:00
section = soup . new_tag ( ' section ' )
section [ ' class ' ] = ' text '
2022-04-23 01:12:45 +00:00
soup . contents [ scan_offset ] . insert_before ( section )
for element in soup . contents [ scan_offset : ] :
2021-11-14 03:55:48 +00:00
if type ( element ) == bs4 . element . Tag :
if element != section :
2022-03-23 00:44:11 +00:00
if element . name in [ ' h2 ' , ' h3 ' , ' h4 ' , ' h5 ' , ' h6 ' ] :
name = headline_address ( element . text )
link = soup . new_tag ( ' a ' )
link [ ' name ' ] = name
link [ ' href ' ] = f ' # { name } '
link . append ( element . text )
element . clear ( )
element . append ( link )
2021-11-14 03:55:48 +00:00
if element . name == ' h2 ' and len ( section ) :
section = soup . new_tag ( ' section ' )
section [ ' class ' ] = ' text '
element . insert_before ( section )
2021-12-24 02:26:59 +00:00
elif element . name == ' section ' :
2021-11-14 03:55:48 +00:00
# let existing sections stand on their own, start new section afterwards
section = soup . new_tag ( ' section ' )
section [ ' class ' ] = ' text '
element . insert_after ( section )
continue # skip rest of the loop, do not add this section
2021-12-24 02:26:59 +00:00
2021-11-14 03:55:48 +00:00
section . append ( element . extract ( ) )
for td in soup . find_all ( ' td ' ) :
2021-11-26 07:39:56 +00:00
if len ( td ) and isinstance ( td . contents [ 0 ] , bs4 . NavigableString ) :
subject = td . contents [ 0 ] . string
else :
subject = td . text
if match_bad . match ( subject ) :
2021-11-14 03:55:48 +00:00
td [ ' class ' ] = ' bad '
2021-11-26 07:39:56 +00:00
elif match_good . match ( subject ) :
td [ ' class ' ] = ' good '
elif match_meh . match ( subject ) :
2021-11-14 03:55:48 +00:00
td [ ' class ' ] = ' meh '
2021-11-26 07:39:56 +00:00
for a in soup . find_all ( ' a ' ) :
if a [ ' href ' ] . startswith ( ' http:// ' ) :
a [ ' href ' ] = ' https:// ' + a [ ' href ' ] [ 7 : ]
if a [ ' href ' ] . startswith ( ' https:// ' ) :
a [ ' target ' ] = ' _blank '
a [ ' rel ' ] = ' noreferrer '
2021-11-14 03:55:48 +00:00
text = str ( soup )
return {
' title ' : title ,
' subtitle ' : subtitle ,
2022-04-23 01:12:45 +00:00
' header ' : header ,
2021-12-24 02:26:59 +00:00
' text ' : text
2021-11-14 03:55:48 +00:00
}
2021-08-29 19:42:37 +00:00
abort ( 404 , " No such content. *sad server noises* " )
2021-11-14 03:55:48 +00:00
def menu ( name = None ) :
2021-08-29 19:42:37 +00:00
links = [
{
' url ' : ' / ' ,
2022-05-26 22:27:46 +00:00
' caption ' : ' Home ' ,
2021-11-14 03:55:48 +00:00
} ,
{
' url ' : flask . url_for ( ' .article ' , name = ' chatcontrol ' ) ,
2022-05-26 22:27:46 +00:00
' caption ' : ' ChatControl ' ,
2021-11-14 03:55:48 +00:00
} ,
{
' url ' : flask . url_for ( ' .article ' , name = ' x-as-in-freedom ' ) ,
2022-05-26 22:27:46 +00:00
' caption ' : ' <em>X</em> as in <em>freedom</em> ' ,
} ,
{
' url ' : flask . url_for ( ' .article ' , name = ' foss-socialism ' ) ,
' caption ' : ' Why FOSS is Socialism ' ,
2021-11-14 03:55:48 +00:00
} ,
#{
# 'url': flask.url_for('.article', name='adversaries'),
# 'caption': 'Adversaries'
#},
2022-04-23 01:12:45 +00:00
{
' url ' : flask . url_for ( ' .clients ' ) ,
' caption ' : ' Supported clients ' ,
} ,
2022-01-01 19:26:51 +00:00
{
' url ' : flask . url_for ( ' .client ' ) ,
' caption ' : ' Web client ' ,
} ,
2021-08-29 19:42:37 +00:00
]
for link in links :
2021-11-14 03:55:48 +00:00
if flask . request . path == link [ ' url ' ] :
2021-08-29 19:42:37 +00:00
link [ ' active ' ] = True # propagates to links[idx]['active']
else :
link [ ' active ' ] = False # dito
2021-11-14 03:55:48 +00:00
return flask . render_template ( ' menu.jinja ' , links = links , name = name )
2021-08-29 19:42:37 +00:00
2022-08-14 17:20:00 +00:00
@app.cli.command ( )
@click.option ( ' --version ' , default = ' 9.1.1 ' )
def install_conversejs ( version ) :
directory = ' resources/converse '
directory_dist = f ' { directory } /dist '
version_name = f ' converse.js- { version } '
file_name = f ' { version_name } .tgz '
file_path = f ' { directory } / { file_name } '
archive_url = f ' https://github.com/conversejs/converse.js/releases/download/v { version } / { file_name } '
if not os . path . exists ( directory ) :
click . secho ( f " Created directory { directory } . " , fg = ' green ' )
os . mkdir ( directory )
if not os . path . isdir ( directory ) :
click . secho ( f " Not a directory: { directory } " , fg = ' red ' )
click . secho ( f " Downloading archive ' { file_name } ' … " )
response = requests . get ( archive_url )
if response . status_code == 200 :
click . secho ( " Success! " , fg = ' green ' )
click . secho ( " Saving… " )
with open ( file_path , ' wb ' ) as file :
file . write ( response . content )
click . secho ( " Extracting archive… " )
member_whitelist = [ ]
with tarfile . open ( file_path ) as tar :
for member in tar . getmembers ( ) :
if member . name . startswith ( ' package/dist ' ) :
member_whitelist . append ( member )
tar . extractall ( directory_dist , members = member_whitelist )
for extracted_name in os . listdir ( f ' { directory_dist } /package/dist ' ) :
shutil . move (
f ' { directory_dist } /package/dist/ { extracted_name } ' ,
f ' { directory_dist } / { extracted_name } '
)
shutil . rmtree ( f ' { directory_dist } /package ' )
2021-10-11 11:45:32 +00:00
@app.route ( ' /resources/<path:name> ' )
2021-08-29 19:42:37 +00:00
def serve_resource ( name ) :
2021-10-11 11:45:32 +00:00
2021-08-29 19:42:37 +00:00
resource_path = os . path . join ( app . config [ ' RESOURCE_DIR ' ] , name )
if os . path . exists ( resource_path ) :
extension = name . split ( ' . ' ) [ - 1 ]
if extension == ' scss ' :
return flask . Response ( scss_compiler . compile ( resource_path ) , mimetype = ' text/css ' )
return flask . send_file ( resource_path )
else :
return flask . Response ( " No such resource. " , status = 404 )
@app.route ( ' / ' )
@page
def home ( ) :
2021-11-14 03:55:48 +00:00
return article_load ( ' home ' )
@app.route ( ' /article/<string:name> ' )
@page
def article ( name ) :
return article_load ( name )
2021-08-24 22:51:20 +00:00
2022-03-06 18:46:48 +00:00
@app.route ( ' /clients/ ' )
@page
def clients ( ) :
client_info = { }
2022-05-27 01:31:11 +00:00
for client_name , info in app . config [ ' CLIENT_INFO ' ] . items ( ) :
2022-03-06 18:46:48 +00:00
#current_client_info = {}
current_client_info = info
if os . path . exists ( os . path . join ( app . config [ ' RESOURCE_DIR ' ] , ' third-party-logos ' , f ' { client_name } .svg ' ) ) :
current_client_info [ ' logo ' ] = f ' /resources/third-party-logos/ { client_name } .svg '
client_info [ client_name ] = current_client_info
os_info = { }
2022-05-27 01:31:11 +00:00
for os_name , info in app . config [ ' OS_INFO ' ] . items ( ) :
2022-03-06 18:46:48 +00:00
current_os_info = info
if os . path . exists ( os . path . join ( app . config [ ' RESOURCE_DIR ' ] , ' third-party-logos ' , f ' { os_name } .svg ' ) ) :
current_os_info [ ' logo ' ] = f ' /resources/third-party-logos/ { os_name } .svg '
os_info [ os_name ] = current_os_info
return {
' client_info ' : client_info ,
2022-05-27 01:31:11 +00:00
' feature_info ' : app . config [ ' FEATURE_INFO ' ] ,
' installer_info ' : app . config [ ' INSTALLER_INFO ' ] ,
2022-03-06 18:46:48 +00:00
' os_info ' : os_info
}
2021-11-14 03:55:48 +00:00
@app.route ( ' /invite/<string:token>/ ' )
2021-08-24 22:51:20 +00:00
@page
def invite_main ( token ) :
2022-01-01 19:26:51 +00:00
invite_info = ensure_token ( token )
2021-08-24 22:51:20 +00:00
2022-05-27 01:31:11 +00:00
welcome_msg = f ''' You have been invited to register on { app . config [ ' SERVICE_NAME ' ] } <br /> <span class= " claim " > { app . config [ ' CLAIM ' ] } </span> '''
2021-08-29 19:42:37 +00:00
2021-12-24 02:26:59 +00:00
probable_os = best_os_guess ( flask . request )
2021-09-05 23:01:09 +00:00
2021-12-24 02:26:59 +00:00
# redirect to #fragment of detected OS
# ?r acts as marker that we already redirected, to avoid looping
if not ' r ' in flask . request . args :
return redirect ( f ' ?r# { probable_os } ' )
2021-09-05 23:01:09 +00:00
2021-12-24 02:26:59 +00:00
processed_info = { }
2022-05-27 01:31:11 +00:00
for os_name , os_info in app . config [ ' OS_INFO ' ] . items ( ) :
2021-09-05 23:01:09 +00:00
2021-12-24 02:26:59 +00:00
current_os_info = {
' title ' : os_info [ ' title ' ] ,
' client_recommend ' : os_info [ ' client_recommend ' ] ,
' client_other ' : [ ] ,
' clients ' : { } ,
}
2021-09-11 22:52:54 +00:00
2022-05-27 01:31:11 +00:00
for client_name , client_info in app . config [ ' CLIENT_INFO ' ] . items ( ) :
2021-09-11 22:52:54 +00:00
2021-12-24 02:26:59 +00:00
if os_name in client_info [ ' os ' ] :
2021-11-14 03:55:48 +00:00
2021-12-24 02:26:59 +00:00
if client_name != current_os_info [ ' client_recommend ' ] :
current_os_info [ ' client_other ' ] . append ( client_name )
2021-11-14 03:55:48 +00:00
2021-12-24 02:26:59 +00:00
current_client_info = {
' title ' : client_info [ ' title ' ] ,
' description ' : client_info [ ' description ' ] ,
' features ' : client_info [ ' features ' ] ,
' project_url ' : client_info [ ' project_url ' ] ,
' os ' : client_info [ ' os ' ] ,
' installer_recommend ' : client_info [ ' installer_recommend ' ] [ os_name ] ,
}
2021-11-14 03:55:48 +00:00
2021-12-24 02:26:59 +00:00
if os . path . exists ( os . path . join ( app . config [ ' RESOURCE_DIR ' ] , ' third-party-logos ' , f ' { client_name } .svg ' ) ) :
current_client_info [ ' logo ' ] = f ' /resources/third-party-logos/ { client_name } .svg '
2021-09-11 22:52:54 +00:00
2021-12-24 02:26:59 +00:00
if ' help_url ' in client_info :
current_client_info [ ' help_url ' ] = client_info [ ' help_url ' ]
2021-09-11 22:52:54 +00:00
2021-12-24 02:26:59 +00:00
if os_name in client_info [ ' installer_magic ' ] :
current_client_info [ ' installer_magic ' ] = client_info [ ' installer_magic ' ] [ os_name ]
for magic_installer in current_client_info [ ' installer_magic ' ] :
# string replacement to inject invite redirect in magic installer URLs
current_client_info [ ' installer_magic ' ] [ magic_installer ] [ ' url ' ] = current_client_info [ ' installer_magic ' ] [ magic_installer ] [ ' url ' ] . replace ( ' {invite.uri} ' , flask . helpers . url_quote ( invite_info [ ' uri ' ] ) ) # this line is a monster
2021-09-11 22:52:54 +00:00
2021-12-24 02:26:59 +00:00
current_client_info [ ' installer_manual ' ] = client_info [ ' installer_manual ' ] [ os_name ]
2021-09-11 22:52:54 +00:00
2021-12-24 02:26:59 +00:00
# collect non-recommended installers for this OS
# we assume 'installer_manual' to hold *all* installers for this OS
current_client_info [ ' installer_other ' ] = set ( current_client_info [ ' installer_manual ' ] . keys ( ) )
current_client_info [ ' installer_other ' ] . remove ( current_client_info [ ' installer_recommend ' ] )
2021-09-11 22:52:54 +00:00
2021-12-24 02:26:59 +00:00
if os_name in client_info [ ' installer_magic ' ] and current_client_info [ ' installer_recommend ' ] in current_client_info [ ' installer_magic ' ] :
current_client_info [ ' action_recommend ' ] = ' installer_magic '
else :
current_client_info [ ' action_recommend ' ] = ' installer_manual '
2021-09-26 22:46:09 +00:00
2021-12-24 02:26:59 +00:00
current_client_info [ ' installers ' ] = { }
2022-05-27 01:31:11 +00:00
for installer_name , installer_info in app . config [ ' INSTALLER_INFO ' ] [ os_name ] . items ( ) :
2021-09-11 22:52:54 +00:00
2021-12-24 02:26:59 +00:00
if installer_name in client_info [ ' installer_manual ' ] [ os_name ] :
2021-09-11 22:52:54 +00:00
2021-12-24 02:26:59 +00:00
current_installer_info = {
' title ' : installer_info [ ' title ' ] ,
' gratis ' : current_client_info [ ' installer_manual ' ] [ installer_name ] [ ' gratis ' ] ,
' url_support ' : installer_info [ ' url_support ' ] ,
' magic_support ' : installer_info [ ' magic_support ' ] ,
' pkgname_support ' : installer_info [ ' pkgname_support ' ] ,
' guide_package ' : installer_info [ ' guide_package ' ] ,
}
2021-09-11 22:52:54 +00:00
2021-12-24 02:26:59 +00:00
# list of keys over whose values we want to run string replacement
keys_replace = [ ' guide_package ' ]
2021-09-11 22:52:54 +00:00
2021-12-24 02:26:59 +00:00
if installer_info [ ' magic_support ' ] :
current_installer_info [ ' guide_magic ' ] = installer_info [ ' guide_magic ' ]
keys_replace . append ( ' guide_magic ' )
2021-09-26 22:46:09 +00:00
2021-12-24 02:26:59 +00:00
if ' guide ' in installer_info : # pre-installed installers don't have 'guide'
current_installer_info [ ' guide ' ] = installer_info [ ' guide ' ]
# prepend 'guide'; has to come first so guide with
# replacements is available in 'guide_package'.
keys_replace = [ ' guide ' ] + keys_replace
2021-09-26 22:46:09 +00:00
2021-12-24 02:26:59 +00:00
for k in keys_replace :
2021-09-11 22:52:54 +00:00
2021-12-24 02:26:59 +00:00
value = replace_thingamabob ( current_installer_info [ k ] , invite_info , os_name , client_name , installer_name )
current_installer_info [ k ] = value
2021-09-11 22:52:54 +00:00
2021-12-24 02:26:59 +00:00
current_client_info [ ' installers ' ] [ installer_name ] = current_installer_info
2021-08-29 19:42:37 +00:00
2021-12-24 02:26:59 +00:00
current_os_info [ ' clients ' ] [ client_name ] = current_client_info
2021-08-29 19:42:37 +00:00
2021-12-24 02:26:59 +00:00
processed_info [ os_name ] = current_os_info
2021-08-24 22:51:20 +00:00
2021-12-24 02:26:59 +00:00
return {
' probable_os ' : probable_os ,
' welcome_msg ' : welcome_msg ,
' info ' : processed_info ,
2022-05-27 01:31:11 +00:00
' feature_info ' : app . config [ ' FEATURE_INFO ' ] ,
2021-12-24 02:26:59 +00:00
}
2021-09-26 22:46:09 +00:00
2021-11-14 03:55:48 +00:00
@app.route ( ' /invite/<string:token>/register/ ' , methods = [ ' GET ' , ' POST ' ] )
2021-08-24 22:51:20 +00:00
@page
def invite_register ( token ) :
2022-01-01 19:26:51 +00:00
invite_info = ensure_token ( token )
2021-08-24 22:51:20 +00:00
2021-11-14 03:55:48 +00:00
fakepost = ' fakepost ' in flask . request . args
2021-10-03 22:51:59 +00:00
2021-11-14 03:55:48 +00:00
if flask . request . method == ' POST ' or fakepost :
if ( ' username ' in flask . request . form and ' password ' in flask . request . form ) or fakepost :
if fakepost :
username = " FNORD "
password = " FNORD "
else :
username = flask . request . form [ ' username ' ]
password = flask . request . form [ ' password ' ]
2021-10-03 22:51:59 +00:00
try :
2021-11-14 03:55:48 +00:00
if not fakepost :
register_with_invite ( username , password , token )
2021-10-03 22:51:59 +00:00
except ValueError as e :
return e . args [ 0 ]
except RuntimeError as e :
2021-11-14 03:55:48 +00:00
return { ' message ' : " Sorry, that didn ' t work. Please try again. " }
2021-10-03 22:51:59 +00:00
else :
2021-11-14 03:55:48 +00:00
data = {
2022-05-27 01:31:11 +00:00
' help ' : app . config [ ' HELP_INFO ' ] ,
2021-12-24 02:26:59 +00:00
' message ' : """ Congratulations, your account
2021-11-14 03:55:48 +00:00
is ready for use ! """ ,
' username ' : username ,
' password ' : password ,
2022-05-27 01:31:11 +00:00
' jid ' : f " { username } @ { app . config [ ' XMPP_DOMAIN ' ] } " ,
2021-11-14 03:55:48 +00:00
}
if ' client ' in flask . request . args :
data [ ' client ' ] = flask . request . args [ ' client ' ]
2022-05-27 01:31:11 +00:00
data [ ' client_info ' ] = app . config [ ' CLIENT_INFO ' ] [ data [ ' client ' ] ]
2021-11-14 03:55:48 +00:00
flask . session [ ' jid ' ] = data [ ' jid ' ]
flask . session [ ' password ' ] = data [ ' password ' ]
return data
2021-10-03 22:51:59 +00:00
else :
2021-11-14 03:55:48 +00:00
return { ' message ' : " Malformatted data. " }
2021-08-24 22:51:20 +00:00
else :
2021-12-24 02:26:59 +00:00
return {
2022-05-27 01:31:11 +00:00
' help ' : app . config [ ' HELP_INFO ' ] ,
2021-12-24 02:26:59 +00:00
' message ' : " Almost there! Just tell us what your new identity should be. " ,
2022-05-27 01:31:11 +00:00
' domain ' : app . config [ ' XMPP_DOMAIN ' ] ,
2021-11-26 07:39:56 +00:00
' form ' : True ,
}
2021-08-24 22:51:20 +00:00
2021-10-11 11:45:32 +00:00
@app.route ( ' /client ' )
@page
2022-01-01 19:26:51 +00:00
def client ( ) :
2021-10-11 11:45:32 +00:00
return {
2022-08-14 17:20:00 +00:00
' bosh_url ' : app . config . get ( ' BOSH_URL ' ) ,
' websocket_url ' : app . config . get ( ' WEBSOCKET_URL ' ) ,
2021-12-24 02:26:59 +00:00
' jid ' : flask . session [ ' jid ' ] if ' jid ' in flask . session else None ,
' password ' : flask . session [ ' password ' ] if ' password ' in flask . session else None ,
2021-10-11 11:45:32 +00:00
}
2021-08-24 22:51:20 +00:00
if __name__ == ' __main__ ' :
2021-08-29 19:42:37 +00:00
app . config [ ' DEBUG ' ] = True
2022-03-23 00:44:11 +00:00
app . run ( ' 0.0.0.0 ' )