# internal imports import os,sys; sys.path.append(os.path.abspath(os.path.join(os.path.abspath(os.path.join(os.path.dirname(__file__),'..')),'..'))) from comrad import * from comrad.backend import * from comrad.backend.phonelines import * from comrad.backend.operators import CALLBACKS import requests from torpy.cell_socket import TorSocketConnectError from requests.exceptions import ConnectionError # def TheTelephone(*x,**y): # return Comrad(TELEPHONE_NAME,*x,**y) ### ACTUAL PHONE CONNECTIONS class TheTelephone(Operator): """ API client class for Caller to interact with The Operator. """ def __init__(self, caller=None, callbacks={}): super().__init__(name=TELEPHONE_NAME,callbacks=callbacks) self.caller=caller from comrad.backend.phonelines import check_phonelines keychain = check_phonelines()[TELEPHONE_NAME] self._keychain ={**self.load_keychain_from_bytes(keychain)} self.log(f'Starting up with callbacks: {self._callbacks}') @property def api_url(self): #if 'COMRAD_OPERATOR_API_URL' in os.environ and os.environ['COMRAD_OPERATOR_API_URL']: # return os.environ #os.environ['COMRAD_OPERATOR_API_URL'] = OPERATOR_API_URL_TOR if 'COMRAD_USE_TOR' in os.environ and os.environ['COMRAD_USE_TOR']=='1': return OPERATOR_API_URL_TOR elif 'COMRAD_USE_CLEARNET' in os.environ and os.environ['COMRAD_USE_CLEARNET']=='1': return OPERATOR_API_URL_CLEARNET else: return OPERATOR_API_URL async def send_and_receive(self,msg_d,n_attempts=3,**y): # self.log('send and receive got incoming msg:',msg_d) # assert that people can speak only with operator in their first enclosed message! # if so, dropping the "to" if msg_d['to'] != self.op.pubkey.data: raise ComradException('Comrades must communicate securely with Operator first.') # opp = Operator(pubkey=msg_d['to']) # self.log('got opp:',opp.pubkey.data == msg_d['to'], self.op.pubkey.data == msg_d['to']) # self.log('got msg_d:',msg_d) # self.log('but going to send just msg?',msg_b) msg_b=msg_d["msg"] # msg_b64 = b64encode(msg_b) # msg_b64_str = msg_b64.decode() # self.log(f'''Sending the encrypted content package:\n\n{msg_b64_str}''') # self.log(f'''Sending the encrypted content package:\n\n{msg_b}''') # seal for transport # msg_b64_str_esc = msg_b64_str.replace('/','_') # dial the operator # URL = self.api_url + msg_b64_str_esc + '/' self.log("DIALING THE OPERATOR:",self.api_url,'...') # phonecall=await self.comrad_request_async(URL) # import asyncio # loop = asyncio.get_event_loop() texec = ThreadExecutor() for n_attempt in range(n_attempts): self.log('making first attempt to connect via Tor') try: # phonecall=self.comrad_request(URL) # phonecall = await texec(self.comrad_request, URL) phonecall = await texec( self.comrad_request_post, self.api_url, msg_b ) break except (TorSocketConnectError,ConnectionError) as e: self.log(f'!! {e} trying again?') pass if phonecall.status_code!=200: self.log('!! error in request',phonecall.status_code,phonecall) return # response back from Operator! # resp_msg_b64_str = phonecall.text # self.log(f'{self}: Received response from Operator! We got back:\n\n',resp_msg_b64_str) # resp_msg_b64 = resp_msg_b64_str.encode() # resp_msg_b = b64decode(resp_msg_b64) # self.log('resp_msg_b:',resp_msg_b) resp_msg_b = phonecall.content self.log(f'{self}: Received response from Operator!')# We got back:\n\n',resp_msg_b) resp_msg_d = pickle.loads(resp_msg_b) # self.log('unpickled:',resp_msg_d) # unseal from comrad.backend.messages import Message resp_msg_obj = Message(resp_msg_d) # res = resp_msg_b_unsealed # self.log('Decoding binary, message discovered:\n',resp_msg_obj) # decrypt # resp_msg_obj.decrypt() # self.log('returning decrypted form:',resp_msg_obj) return resp_msg_obj # return self.pronto_pronto(resp_msg_obj) async def ring_ring(self,msg,**y): return await super().ring_ring( msg, to_whom=self.op, get_resp_from=self.send_and_receive, **y ) ### Requests functionality def comrad_request(self,url,allow_clearnet = ALLOW_CLEARNET): if '.onion' in url or not allow_clearnet: return self.tor_request(url) return requests.get(url,timeout=60) async def comrad_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=60) def tor_request(self,url): return self.tor_request_in_python(url) # return tor_request_in_proxy(url) def tor_request_post(self,url,data=''): return self.tor_request_in_python_post(url,data=data) # return tor_request_in_proxy_post(url,data=data) async def tor_request_async(self,url): return await self.tor_request_in_python_async(url) def tor_request_in_proxy(self,url): with self.get_tor_proxy_session() as s: return s.get(url,timeout=600) async def tor_request_in_python_async(self,url): import requests_async tor = TorClient() with tor.get_guard() as guard: adapter = TorHttpAdapter(guard, 3, retries=RETRIES) async with requests_async.Session() as s: s.headers.update({'User-Agent': 'Mozilla/5.0'}) s.mount('http://', adapter) s.mount('https://', adapter) r = s.get(url, timeout=600) self.log('<-- r',r) return r def comrad_request_post(self,url,data='',allow_clearnet = ALLOW_CLEARNET): if '.onion' in url or not allow_clearnet: return self.tor_request_post(url,data=data) return requests.post(url,data=data,timeout=60) def tor_request_in_python(self,url): tor = TorClient() with tor.get_guard() as guard: adapter = TorHttpAdapter(guard, 3, retries=RETRIES) with requests.Session() as s: s.headers.update({'User-Agent': 'Mozilla/5.0'}) s.mount('http://', adapter) s.mount('https://', adapter) r = s.get(url, timeout=600) return r def tor_request_in_python_post(self,url,data=''): tor = TorClient() with tor.get_guard() as guard: adapter = TorHttpAdapter(guard, 3, retries=RETRIES) with requests.Session() as s: s.headers.update({'User-Agent': 'Mozilla/5.0'}) s.mount('http://', adapter) s.mount('https://', adapter) r = s.post(url, data=data, timeout=600) return r def get_tor_proxy_session(self): session = requests.session() # Tor uses the 9050 port as the default socks port session.proxies = {'http': 'socks5://127.0.0.1:9050', 'https': 'socks5://127.0.0.1:9050'} return session def get_async_tor_proxy_session(self): import requests_futures from requests_futures.sessions import FuturesSession session = FuturesSession() # Tor uses the 9050 port as the default socks port session.proxies = {'http': 'socks5://127.0.0.1:9050', 'https': 'socks5://127.0.0.1:9050'} return session def test_call(): phone = TheTelephone() pprint(phone.keychain()) # caller = Caller('marx33') #Caller('marx') # caller.boot(create=True) # print(caller.keychain()) # phone = TheTelephone() # req_json = {'_route':'forge_new_keys','name':name, 'pubkey_is_public':pubkey_is_public}} # req_json_s = jsonify(req_json) # res = phone.req({'forge_new_keys':{'name':'marx', 'pubkey_is_public':True}}) # print(res) # asyncio.run(caller.get_new_keys()) # x=caller.get_new_keys(passphrase='1869') # print('YEAH COOL',x) ## main if __name__=='__main__': test_call()