gradually hooking things back together again ...

app-gui
quadrismegistus 4 years ago
parent bd05ffbc44
commit 78fb19a4a5

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

@ -105,8 +105,36 @@ class MyLayout(MDBoxLayout):
class ProgressPopup(MDDialog): pass
class MessagePopup(MDDialog): pass
class MessagePopupCard(MDDialog): pass
class MessagePopup(MDDialog):
pass
# def __init__(self,*x,**y):
# super().__init__(*x,**y)
# self.ok_to_continue=False
# def on_dismiss(self):
# if not self.ok_to_continue:
# self.ok_to_continue=True
# logger.info('ouch!')
pass
class MessagePopupCard(MDDialog):
def __init__(self,*x,**y):
super().__init__(*x,**y)
self.ok_to_continue=False
def on_dismiss(self):
return True
def on_touch_down(self,touch):
self.ok_to_continue=True
logger.info('oof!')
# if hasattr(self,'msg_dialog'):
# logger.info(str(self.msg_dialog))
class MyBoxLayout(MDBoxLayout): pass
class MyLabel(MDLabel): pass
@ -370,25 +398,45 @@ class MainApp(MDApp, Logger):
self.dialog.open()
#stop
def stat(self,msg,komrade_name='Telephone',pause=False,**y):
from komrade.app.screens.feed.feed import PostCard
if not hasattr(self,'msg_dialog') or not self.msg_dialog:
self.msg_dialog = MessagePopupCard()
# self.msg_dialog.ids.msg_label.text=msg
self.msg_dialog.card = postcard = PostCard({
'author':komrade_name,
'author_prefix':'Komrade @',
'to_name':'me',
'content':msg,
'timestamp':time.time(),
})
postcard.size_hint=(None,None)
postcard.size=('600sp','600sp')
self.msg_dialog.add_widget(postcard)
self.msg_dialog.open()
time.sleep(5)
async def stat(self,msg,komrade_name='Telephone',pause=False,**y):
from komrade.app.screens.feed.feed import PostCard,PostCardPopup
if hasattr(self,'msg_dialog') and self.msg_dialog:# and hasattr(self.msg_dialog,'card') and self.msg_dialog.card:
self.msg_dialog0=self.msg_dialog
self.msg_dialog = MessagePopupCard()
# self.msg_dialog.ids.msg_label.text=msg
self.msg_dialog.card = postcard = PostCardPopup({
'author':komrade_name,
'author_prefix':'@',
'to_name':'me',
'content':msg,
'timestamp':time.time(),
'author_label_font_size':'18sp'
},
msg_dialog=self.msg_dialog)
postcard.font_size='16sp'
postcard.size_hint=(None,None)
postcard.size=('600sp','600sp')
postcard.ok_to_continue=False
self.msg_dialog.add_widget(postcard)
self.msg_dialog.open()
if hasattr(self,'msg_dialog0'):
self.msg_dialog0.remove_widget(self.msg_dialog0.card)
self.root.remove_widget(self.msg_dialog0)
await asyncio.sleep(0.1)
while not self.msg_dialog.ok_to_continue:
await asyncio.sleep(0.1)
# logger.info(str(postcard), postcard.ok_to_continue,'??')
# self.msg_dialog.dismiss()
# self.msg_dialog.remove_widget(postcard)
# self.msg_dialog.card = postcard = self.msg_dialog = None
await asyncio.sleep(0.1)
return {'success':True, 'status':'Delivered popup message'}
def open_msg_dialog(self,msg):
# from screens.post.post import MessagePopup,ProgressPopup

@ -140,8 +140,8 @@
size_hint: 1,1
# size: ('300dp','300dp')
# md_bg_color: rgb(*COLOR_CARD)
md_bg_color:0,0,0,1
size:('600sp','600sp')
md_bg_color:0,0,0,0
size:('600sp','100sp')
MDBoxLayout:
id: msg_popup_box_layout
@ -151,8 +151,8 @@
size_hint: (1, 1)
# adaptive_height: True
pos_hint: {"center_x": .5, "center_y": .5}
# md_bg_color: rgb(*COLOR_CARD)
md_bg_color:0,0,0,0
md_bg_color: rgb(*COLOR_CARD)
# md_bg_color:0,0,0,0
height: self.minimum_height
# radius:[20,]
# border_radius:20

@ -27,6 +27,8 @@ class BaseScreen(MDScreen):
def channel(self):
return self.app.channel
def stat(self,*x,**y): return self.app.stat(*x,**y)
# class CardScreen(BaseScreen):

@ -162,9 +162,9 @@
id: post
orientation: "vertical"
padding: "20dp"
size_hint: (0.75, None)
size_hint: (0.75, 0.75)
# size:('800sp','800sp')
# adaptive_height: True
adaptive_height: True
pos_hint: {"center_x": .5, "center_y": .5}
md_bg_color: rgb(*COLOR_CARD)
height: self.minimum_height

@ -1,3 +1,7 @@
import os,sys; sys.path.append(os.path.abspath(os.path.join(os.path.abspath(os.path.join(os.path.dirname(__file__),'..')),'..')))
from komrade import *
from kivymd.uix.label import MDLabel
from kivy.uix.gridlayout import GridLayout
from kivy.uix.image import AsyncImage, Image
@ -13,6 +17,8 @@ from threading import Thread
import asyncio
from misc import *
from kivy.core.window import Window
import logging
logger=logging.getLogger(__name__)
@ -142,10 +148,13 @@ class PostCard(MDCard):
if self.recipient:
recip=self.recipient
recip='@'+recip if recip and recip[0].isalpha() else recip
self.author_label.text+='\n[size=14sp]to '+recip+'[/size]'
self.author_label.text+=f'\n[size={recip_label_font_size}]to '+recip+'[/size]'
self.author_label.markup=True
self.author_label.font_size = author_label_font_size
self.author_avatar = author_avatar = PostAuthorAvatar(source=f'assets/avatars/{self.author}.png') #self.img_src)
img_src = os.path.join(PATH_GUI_ASSETS, 'avatars', f'{self.author}.png')
if not os.path.exists(img_src):
img_src=PATH_DEFAULT_AVATAR
self.author_avatar = author_avatar = PostAuthorAvatar(source=img_src) #self.img_src)
self.author_section_layout.add_widget(author_avatar)
self.author_section_layout.add_widget(author_label)
# self.author_section_layout.add_widget(MDSeparator(height='1sp',size_hint=(None,None)))
@ -242,6 +251,20 @@ class PostCard(MDCard):
#####
class PostCardPopup(PostCard):
def __init__(self,*x,msg_dialog=None,**y):
super().__init__(*x,**y)
self.ok_to_continue=False
self.msg_dialog=msg_dialog
def on_touch_down(self,touch):
# if self.collide_point(*touch.pos):# and not self.ok_to_continue:
# logger.info('ouch!!!')
# The touch has occurred inside the widgets area. Do stuff!
self.ok_to_continue=True
if self.msg_dialog: self.msg_dialog.ok_to_continue=True
return True
class FeedScreen(BaseScreen):
posts = ListProperty()
@ -253,19 +276,22 @@ class FeedScreen(BaseScreen):
i=0
lim=25
self.app.komrade.get_updates()
posts=self.get_posts()
for i,post in enumerate(reversed(posts)):
if i>lim: break
data = {
'author':post.from_name,
'to_name':post.to_name,
'content':post.msg.get('txt') if type(post.msg)==dict else str(post.msg)
}
post_obj = PostCard(data)
self.posts.append(post_obj)
self.ids.post_carousel.add_widget(post_obj)
async def go():
await self.app.komrade.get_updates()
posts=self.get_posts()
for i,post in enumerate(reversed(posts)):
if i>lim: break
data = {
'author':post.from_name,
'to_name':post.to_name,
'content':post.msg.get('txt') if type(post.msg)==dict else str(post.msg)
}
post_obj = PostCard(data)
self.posts.append(post_obj)
self.ids.post_carousel.add_widget(post_obj)
asyncio.create_task(go())
# def on_pre_enter(self):
# self.clear_deck()
# # for i,x

@ -23,9 +23,11 @@ class LoginButton(MDRectangleFlatButton): pass
class RegisterButton(MDRectangleFlatButton,Logger):
def enter(self):
un=self.parent.parent.parent.username_field.text
pw=self.parent.parent.parent.password_field.text
login_screen = self.parent.parent.parent
login_screen.boot(un)
time.sleep(0.1)
asyncio.create_task(login_screen.boot(un,pw))
# logger.info('types',type(self.parent),type(self.parent.parent.parent))
@ -156,21 +158,19 @@ class LoginScreen(BaseScreen):
)
self.dialog.open()
def getpass_func(self,why_msg):
return self.password_field.text
def getpass_func(self,why_msg,passphrase=None):
return self.password_field.text if not passphrase else passphrase
def boot(self,un):
async def boot(self,un,pw=None):
name=un
from komrade.backend import Komrade
kommie = Komrade(un,getpass_func=lambda why: pw)
self.log('KOMMIE!?!?',kommie)
# self.app.stat(
# 'You chose the username '+un
# )
# return
kommie = Komrade(un,getpass_func=self.getpass_func)
# self.show_pass_opt()
logger.info(f'booted kommie: {kommie}')
if kommie.exists_locally_as_account():
await self.app.stat('You have already created this account. Logging you back in...')
logger.info(f'is account')
self.login_status.text='You should be able to log into this account.'
if kommie.privkey:
@ -187,10 +187,15 @@ class LoginScreen(BaseScreen):
# self.layout.add_widget(self.layout_password)
elif kommie.exists_locally_as_contact():
self.login_status.text='Komrade exists as a contact of yours.'
await self.app.stat('This is a contact of yours')
self.login_status.text='Komrade exists as a contact of yours.'
else:
self.login_status.text='Komrade not known on this device. Registering...'
res = kommie.register(logfunc=self.app.stat)
await self.app.stat('Account does not exist on hardware, maybe not on server. Try to register?')
# self.login_status.text='Komrade not known on this device. Registering...'
### REGISTER
res = await self.register(kommie,logfunc=self.app.stat,passphrase=pw)
if kommie.privkey:
self.login_status.text='Registered'
self.app.is_logged_in=True
@ -199,4 +204,142 @@ class LoginScreen(BaseScreen):
self.remove_widget(self.layout)
self.app.change_screen('feed')
else:
self.login_status.text = 'Sign up failed...'
self.login_status.text = 'Sign up failed...'
return 1
async def register(self,kommie,logfunc=None,passphrase=None):
if not logfunc: logfunc=self.app.stat
name=kommie.name
# already have it?
if kommie.exists_locally_as_account():
return {'success':False, 'status':'You have already created this account.'}
if kommie.exists_locally_as_contact():
return {'success':False, 'status':'This is already a contact of yours'}
await logfunc(f'Hello, this is Komrade @{name}.\n\nI would like to sign up for the socialist network revolution.',pause=True,komrade_name=name)
await logfunc(f'Excellent. But to communicate with komrades securely, you must first cut your public & private encryption keys.',pause=True,clear=True)
# ## 2) Make pub public/private keys
from komrade.backend.keymaker import KomradeAsymmetricKey
from komrade.cli.artcode import ART_KEY_PAIR
keypair = KomradeAsymmetricKey()
logger.info('cut keypair!')
pubkey,privkey = keypair.pubkey_obj,keypair.privkey_obj
await logfunc(f'I have cut for you a private and public asymmetric key pair, using the iron-clad Elliptic curve algorithm:',komrade_name='Keymaker')
await logfunc('(1) {pubkey}\n\n(2) {privkey}',clear=True,pause=True,komrade_name='Keymaker')
kommie._keychain['pubkey']=pubkey
kommie._keychain['privkey']=privkey
from komrade.utils import dict_format
self.log('My keychain now looks like:' + dict_format(kommie.keychain()))
# return
### PRIVATE KEY
await logfunc(f"(2) Your PRIVATE key, on the other hand, must be stored only on your device hardware.",pause=True)
await logfunc('''Your private key is so sensitive we'll even encrypt it before storing it.''',pause=True,use_prefix=False)
passhash = hasher(passphrase)
privkey_decr = KomradeSymmetricKeyWithPassphrase(passhash=passhash)
print()
await logfunc(f'''Let's immediately run whatever you typed in for your password through a 1-way hashing algorithm (SHA-256), inflating it to (redacted):\n\n{make_key_discreet_str(passhash)}''',pause=True,clear=False)
privkey_encr = privkey_decr.encrypt(privkey.data)
privkey_encr_obj = KomradeEncryptedAsymmetricPrivateKey(privkey_encr)
kommie._keychain['privkey_encr']=privkey_encr_obj
self.log('My keychain now looks like v2:',dict_format(kommie.keychain()))
await logfunc('With this inflated password we can encrypt your super-sensitive private key.',pause=True,clear=True)
await logfunc(f"Your original private key looks like this (redacted):\n\n{privkey}",pause=True,clear=False)
await logfunc(f"After we encrypt it with your passworded key, it looks like this (redacted):\n\n{privkey_encr_obj}",pause=True,clear=False)
await logfunc('Only this encrypted version is stored.',pause=True,clear=True)
# ### PUBLIC KEY
qr_str=kommie.qr_str(pubkey.data_b64)
await logfunc(f'(1) You may store your public key both on your device hardware, as well as share it with anyone you wish:\n\n{pubkey.data_b64_s}') #\n\nIt will also be stored as a QR code on your device:\n{qr_str}',pause=True,clear=True)
await logfunc('You must also register your username and public key with Komrade @Operator on the remote server',pause=False,clear=False)
await logfunc('Connecting you to the @Operator...',komrade_name='Telephone')
## CALL OP WITH PUBKEY
self.app.open_dialog('Calling @Operator...')
logger.info('got here!')
resp_msg_d = await kommie.ring_ring(
{
'name':name,
'pubkey': pubkey.data,
},
route='register_new_user'
)
self.app.close_dialog()
# print()
await logfunc(resp_msg_d.get('status'),komrade_name='Operator',pause=True)
if not resp_msg_d.get('success'):
await logfunc('''That's too bad. Cancelling registration for now.''',pause=True,clear=True)
return
# clear_screen()
await logfunc('Great. Komrade @Operator now has your name and public key on file (and nothing else!).',pause=True,clear=True)
kommie.name=resp_msg_d.get('name')
pubkey_b = resp_msg_d.get('pubkey')
assert pubkey_b == pubkey.data
uri_id = pubkey.data_b64
sec_login = resp_msg_d.get('secret_login')
# stop
await logfunc(f'''Saving keys to device:''',pause=True)
await logfunc(f'''(1) {pubkey}''',pause=True,use_prefix=False)
await logfunc(f'''(2) {privkey_encr_obj}''',pause=True,use_prefix=False)
await logfunc(f'''(3) [Shared Login Secret with @Operator]\n({make_key_discreet(sec_login)})''',pause=True,use_prefix=False)
# print()
kommie.crypt_keys.set(name, pubkey_b, prefix='/pubkey/')
kommie.crypt_keys.set(uri_id, name, prefix='/name/')
kommie.crypt_keys.set(uri_id,sec_login,prefix='/secret_login/')
# store privkey pieces
kommie.crypt_keys.set(uri_id, privkey_encr_obj.data, prefix='/privkey_encr/')
# just to show we used a passphrase -->
kommie.crypt_keys.set(uri_id, KomradeSymmetricKeyWithPassphrase.__name__, prefix='/privkey_decr/')
# save qr too:
_fnfn=kommie.save_uri_as_qrcode(uri_id)
await logfunc(f'Also saving public key as QR code to: {_fnfn}.',pause=True,clear=False,use_prefix=False)
# done!
await logfunc(f'Congratulations. Welcome, {kommie}.',pause=True,clear=True)
# last minute: get posts
if 'res_posts' in resp_msg_d and resp_msg_d['res_posts'].get('success'):
id2post=resp_msg_d.get('res_posts').get('posts',{})
if id2post:
kommie.log('found starter posts:',list(id2post.keys()))
kommie.save_posts(id2post)
resp_msg_d['status']+=f' You\'ve got {len(id2post)} new posts and 0 new messages.'
await logfunc('returning...')
return resp_msg_d

@ -1,4 +1,6 @@
from screens.base import ProtectedScreen
import os,sys; sys.path.append(os.path.abspath(os.path.join(os.path.abspath(os.path.join(os.path.dirname(__file__),'..')),'..')))
from komrade import *
class NotificationsScreen(ProtectedScreen): pass

@ -1,3 +1,7 @@
import os,sys; sys.path.append(os.path.abspath(os.path.join(os.path.abspath(os.path.join(os.path.dirname(__file__),'..')),'..')))
from komrade import *
from screens.base import ProtectedScreen,BaseScreen
from plyer import filechooser
from kivy.uix.button import Button

@ -199,13 +199,11 @@ class ProfileScreen(BaseScreen):
clock_scheduled=None
def make_profile_img(self,width,do_crop=True,circ_img=None,bw=False,circularize=True):
img_src = os.path.join(PATH_GUI_ASSETS, 'avatars', f'{self.app.username}.png')
if not os.path.exists(img_src):
img_src=PATH_DEFAULT_AVATAR
img_src = f'assets/avatars/{self.app.username}.png'
if not os.path.exists(img_src): img_src = 'assets/avatars/marx.png'
circ_img = circularize_img(img_src,width,do_crop=do_crop,bw=bw,circularize=circularize)
avatar_layout = LayoutAvatar()
byte=io.BytesIO(circ_img.read())

@ -12,9 +12,9 @@ class Caller(Operator):
Variant of an Operator which handles local keys and keymaking.
"""
def ring_ring(self,msg,**y):
async def ring_ring(self,msg,**y):
# stop
return super().ring_ring(
return await super().ring_ring(
msg,
to_whom=self.op,
get_resp_from=self.phone.ring_ring,

@ -81,10 +81,11 @@ class KomradeX(Caller):
## Talking with Operator
def ring_ring(self,msg,route=None,**y):
async def ring_ring(self,msg,route=None,**y):
logger.info('got here 2!')
if type(msg)==dict and not ROUTE_KEYNAME in msg:
msg[ROUTE_KEYNAME]=route
return super().ring_ring(msg,caller=self,**y)
return await super().ring_ring(msg,caller=self,**y)
@ -95,6 +96,9 @@ class KomradeX(Caller):
## (1) Logging in and registering ##
####################################
async def log_async(self,*x,**y):
return self.log(*x,**y)
def register(self, name = None, passphrase = None, is_group=None, show_intro=0,show_body=True,logfunc=None):
@ -579,7 +583,7 @@ class KomradeX(Caller):
)
## Getting updates
def get_updates(self,include_posts=True):
async def get_updates(self,include_posts=True):
# get any parameters we need
# post_ids_read = list(self.inbox_read_db.values)
if not self.pubkey:
@ -593,7 +597,7 @@ class KomradeX(Caller):
}
self.log('msg to op ->',msg_to_op)
res = self.ring_ring(
res = await self.ring_ring(
msg_to_op,
route='get_updates'
)

@ -249,7 +249,7 @@ class Operator(Keymaker):
return new_msg_obj
def ring_ring(self,msg,to_whom,get_resp_from=None,route=None,caller=None):
async def ring_ring(self,msg,to_whom,get_resp_from=None,route=None,caller=None):
# ring ring
from komrade.cli.artcode import ART_PHONE_SM1
import textwrap as tw
@ -277,8 +277,10 @@ class Operator(Keymaker):
# pass through the telephone wire by the get_resp_from function
if not get_resp_from: get_resp_from=to_whom.ring_ring
resp_msg_obj = get_resp_from(msg_obj.msg_d,caller=caller)
#self.log('resp_msg_obj <-',resp_msg_obj)
resp_msg_obj = await get_resp_from(msg_obj.msg_d,caller=caller)
self.log('resp_msg_obj <-',str(resp_msg_obj))
if not resp_msg_obj:
print('!! no response from op !!')
exit()

@ -38,7 +38,7 @@ class TheTelephone(Operator):
return OPERATOR_API_URL
def send_and_receive(self,msg_d,**y):
async def send_and_receive(self,msg_d,**y):
# self.log('send and receive got incoming msg:',msg_d)
# assert that people can speak only with operator in their first enclosed message!
@ -66,7 +66,9 @@ class TheTelephone(Operator):
URL = self.api_url + msg_b64_str_esc + '/'
self.log("DIALING THE OPERATOR:",URL)
phonecall=self.komrade_request(URL)
phonecall=await self.komrade_request_async(URL)
if phonecall.status_code!=200:
self.log('!! error in request',phonecall.status_code,phonecall.text)
return
@ -95,8 +97,8 @@ class TheTelephone(Operator):
# return self.pronto_pronto(resp_msg_obj)
def ring_ring(self,msg,**y):
return super().ring_ring(
async def ring_ring(self,msg,**y):
return await super().ring_ring(
msg,
to_whom=self.op,
get_resp_from=self.send_and_receive,
@ -109,6 +111,13 @@ class TheTelephone(Operator):
return self.tor_request(url)
return requests.get(url,timeout=600)
async def komrade_request_async(self,url,allow_clearnet=ALLOW_CLEARNET):
import requests_async as requests
if '.onion' in url or not allow_clearnet:
return await self.tor_request_async(url)
return await requests.get(url,timeout=600)
def tor_request(self,url):
return self.tor_request_in_python(url)
# return tor_request_in_proxy(url)
@ -123,9 +132,7 @@ class TheTelephone(Operator):
async def tor_request_in_python_async(self,url):
import requests_async as requests
tor = TorClient(
callbacks=self._callbacks
)
tor = TorClient()
with tor.get_guard() as guard:
adapter = TorHttpAdapter(guard, 3, retries=RETRIES)
@ -133,7 +140,9 @@ class TheTelephone(Operator):
s.headers.update({'User-Agent': 'Mozilla/5.0'})
s.mount('http://', adapter)
s.mount('https://', adapter)
return await s.get(url, timeout=60)
r = await s.get(url, timeout=60)
self.log('<-- r',r)
return r
def tor_request_in_python(self,url):

@ -49,12 +49,24 @@ BSEP3=b'##########'
OPERATOR_NAME = 'Operator'
TELEPHONE_NAME = 'Telephone'
WORLD_NAME = 'komrades'
PATH_APP = os.path.abspath(os.path.dirname(__file__))
PATH_REPO = os.path.abspath(
os.path.join(
os.path.dirname(__file__),
'..'
)
)
PATH_GUI = os.path.join(PATH_REPO,'komrade','app')
PATH_GUI_ASSETS = os.path.join(PATH_GUI,'assets')
PATH_DEFAULT_AVATAR = os.path.join(PATH_GUI_ASSETS,'avatars','marxbot.png')
PATH_REPO = PATH_APP = os.path.abspath(os.path.dirname(__file__))
# PATH_APP = os.path.join(PATH_REPO,'komrade')
# PATH_BUILTIN_KEYCHAINS_ENCR = os.path.join(PATH_APP,'.builtin.keychains.encr')
PATH_BUILTIN_KEYCHAIN = os.path.join(PATH_APP,'.builtin.keys')
PATH_OMEGA_KEY = os.path.join(PATH_APP,'.omega.key')
# PATH_BUILTIN_KEYCHAINS_DECR = os.path.join(PATH_APP,'.builtin.keychains.decr')
PATH_GUI = os.path.join(PATH_APP, )
# key names

Loading…
Cancel
Save