mirror of
https://github.com/EDCD/EDMarketConnector.git
synced 2025-04-14 08:17:13 +03:00
parent
2f154ffd03
commit
fd492cdf94
@ -53,7 +53,7 @@ import prefs
|
||||
import plug
|
||||
from hotkey import hotkeymgr
|
||||
from monitor import monitor
|
||||
from protocol import ProtocolHandler
|
||||
from protocol import protocolhandler
|
||||
from dashboard import dashboard
|
||||
from theme import theme
|
||||
|
||||
@ -71,7 +71,7 @@ class AppWindow:
|
||||
def __init__(self, master):
|
||||
|
||||
# Start a protocol handler to handle cAPI registration
|
||||
self.protocolhandler = ProtocolHandler(master)
|
||||
protocolhandler.setmaster(master)
|
||||
|
||||
self.holdofftime = config.getint('querytime') + companion.holdoff
|
||||
|
||||
@ -588,7 +588,7 @@ class AppWindow:
|
||||
# cAPI auth
|
||||
def auth(self, event=None):
|
||||
try:
|
||||
companion.session.auth_callback(self.protocolhandler.lastpayload)
|
||||
companion.session.auth_callback()
|
||||
self.status['text'] = _('Authentication successful') # Successfully authenticated with the Frontier website
|
||||
except companion.ServerError as e:
|
||||
self.status['text'] = unicode(e)
|
||||
@ -680,7 +680,7 @@ class AppWindow:
|
||||
if platform!='darwin' or self.w.winfo_rooty()>0: # http://core.tcl.tk/tk/tktview/c84f660833546b1b84e7
|
||||
config.set('geometry', '+{1}+{2}'.format(*self.w.geometry().split('+')))
|
||||
self.w.withdraw() # Following items can take a few seconds, so hide the main window while they happen
|
||||
self.protocolhandler.close()
|
||||
protocolhandler.close()
|
||||
hotkeymgr.unregister()
|
||||
dashboard.close()
|
||||
monitor.close()
|
||||
|
@ -219,22 +219,22 @@ Windows:
|
||||
Running from source
|
||||
--------
|
||||
|
||||
Download and extract the source code of the [latest release](https://github.com/Marginal/EDMarketConnector/releases/latest).
|
||||
Download and extract the [latest source code](https://github.com/Marginal/EDMarketConnector/archive/master.zip) (or fork and clone if you're comfortable with using `git`). To use the cAPI you will need to visit the [Frontier authentication website](https://auth.frontierstore.net/), login with your Frontier credentials, register a "Developer App" and obtain the "Client Id".
|
||||
|
||||
Mac:
|
||||
|
||||
* Requires the Python “keyring”, “requests” and “watchdog” modules, plus an up-to-date “py2app” module if you also want to package the app - install these with `easy_install -U keyring requests watchdog py2app` .
|
||||
* Run with `python ./EDMarketConnector.py` .
|
||||
* Run with `CLIENT_ID=XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX python ./EDMarketConnector.py` .
|
||||
|
||||
Windows:
|
||||
|
||||
* Requires Python2.7 and the Python “keyring”, “requests” and “watchdog” modules, plus “py2exe” 0.6 if you also want to package the app.
|
||||
* Run with `EDMarketConnector.py` .
|
||||
* Run with `set CLIENT_ID=XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX && EDMarketConnector.py` .
|
||||
|
||||
Linux:
|
||||
|
||||
* Requires the Python “imaging-tk”, “iniparse”, “keyring” and “requests” modules. On Debian-based systems install these with `sudo apt-get install python-imaging-tk python-iniparse python-keyring python-requests` .
|
||||
* Run with `./EDMarketConnector.py` .
|
||||
* Run with `CLIENT_ID=XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX ./EDMarketConnector.py` .
|
||||
|
||||
Command-line
|
||||
--------
|
||||
|
14
companion.py
14
companion.py
@ -16,11 +16,13 @@ import webbrowser
|
||||
import zlib
|
||||
|
||||
from config import appname, appversion, config
|
||||
from protocol import protocolhandler
|
||||
|
||||
|
||||
holdoff = 60 # be nice
|
||||
timeout = 10 # requests timeout
|
||||
|
||||
CLIENT_ID = None # Replace with FDev Client Id
|
||||
CLIENT_ID = os.getenv('CLIENT_ID') # Obtain from https://auth.frontierstore.net/client/signup
|
||||
SERVER_AUTH = 'https://auth.frontierstore.net'
|
||||
URL_AUTH = '/auth'
|
||||
URL_TOKEN = '/token'
|
||||
@ -189,8 +191,8 @@ class Auth:
|
||||
print 'Auth\tNew authorization request'
|
||||
self.verifier = self.base64URLEncode(os.urandom(32))
|
||||
self.state = self.base64URLEncode(os.urandom(8))
|
||||
# Won't work under IE <= 10 : https://blogs.msdn.microsoft.com/ieinternals/2011/07/13/understanding-protocols/
|
||||
webbrowser.open('%s%s?response_type=code&audience=frontier&scope=capi&client_id=%s&code_challenge=%s&code_challenge_method=S256&state=%s&redirect_uri=edmc://auth' % (SERVER_AUTH, URL_AUTH, CLIENT_ID, self.base64URLEncode(hashlib.sha256(self.verifier).digest()), self.state))
|
||||
# Won't work under IE: https://blogs.msdn.microsoft.com/ieinternals/2011/07/13/understanding-protocols/
|
||||
webbrowser.open('%s%s?response_type=code&audience=frontier&scope=capi&client_id=%s&code_challenge=%s&code_challenge_method=S256&state=%s&redirect_uri=%s' % (SERVER_AUTH, URL_AUTH, CLIENT_ID, self.base64URLEncode(hashlib.sha256(self.verifier).digest()), self.state, protocolhandler.redirect))
|
||||
|
||||
def authorize(self, payload):
|
||||
# Handle OAuth authorization code callback. Returns access token if successful, otherwise raises CredentialsError
|
||||
@ -218,7 +220,7 @@ class Auth:
|
||||
'client_id': CLIENT_ID,
|
||||
'code_verifier': self.verifier,
|
||||
'code': data['code'][0],
|
||||
'redirect_uri': 'edmc://auth',
|
||||
'redirect_uri': protocolhandler.redirect,
|
||||
}
|
||||
r = self.session.post(SERVER_AUTH + URL_TOKEN, data=data, timeout=timeout)
|
||||
if r.status_code == requests.codes.ok:
|
||||
@ -311,11 +313,11 @@ class Session:
|
||||
# Wait for callback
|
||||
|
||||
# Callback from protocol handler
|
||||
def auth_callback(self, payload):
|
||||
def auth_callback(self):
|
||||
if self.state != Session.STATE_AUTH:
|
||||
raise CredentialsError() # Shouldn't be getting a callback
|
||||
try:
|
||||
self.start(self.auth.authorize(payload))
|
||||
self.start(self.auth.authorize(protocolhandler.lastpayload))
|
||||
self.auth = None
|
||||
except:
|
||||
self.state = Session.STATE_INIT # Will try to authorize again on next login or query
|
||||
|
81
protocol.py
81
protocol.py
@ -3,27 +3,30 @@
|
||||
|
||||
import threading
|
||||
import urllib2
|
||||
from sys import platform
|
||||
import sys
|
||||
|
||||
from config import appname
|
||||
|
||||
|
||||
class GenericProtocolHandler:
|
||||
|
||||
def __init__(self, master):
|
||||
self.master = master
|
||||
def __init__(self):
|
||||
self.redirect = 'edmc://auth' # Base redirection URL
|
||||
self.master = None
|
||||
self.lastpayload = None
|
||||
|
||||
def setmaster(self, master):
|
||||
self.master = master
|
||||
|
||||
def close(self):
|
||||
pass
|
||||
|
||||
def event(self, url):
|
||||
if url.startswith('edmc://'):
|
||||
self.lastpayload = url[7:]
|
||||
self.master.event_generate('<<CompanionAuthEvent>>', when="tail")
|
||||
self.lastpayload = url
|
||||
self.master.event_generate('<<CompanionAuthEvent>>', when="tail")
|
||||
|
||||
|
||||
if platform == 'darwin':
|
||||
if sys.platform == 'darwin' and getattr(sys, 'frozen', False):
|
||||
|
||||
import struct
|
||||
import objc
|
||||
@ -36,15 +39,14 @@ if platform == 'darwin':
|
||||
|
||||
POLL = 100 # ms
|
||||
|
||||
def __init__(self, master):
|
||||
GenericProtocolHandler.__init__(self, master)
|
||||
def __init__(self):
|
||||
GenericProtocolHandler.__init__(self)
|
||||
self.eventhandler = EventHandler.alloc().init()
|
||||
self.eventhandler.handler = self
|
||||
self.lasturl = None
|
||||
|
||||
def poll(self):
|
||||
# No way of signalling to Tkinter from within the callback handler block that doesn't cause Python to crash, so poll.
|
||||
if self.lasturl:
|
||||
if self.lasturl and self.lasturl.startswith(self.redirect):
|
||||
self.event(self.lasturl)
|
||||
self.lasturl = None
|
||||
|
||||
@ -56,11 +58,11 @@ if platform == 'darwin':
|
||||
return self
|
||||
|
||||
def handleEvent_withReplyEvent_(self, event, replyEvent):
|
||||
self.handler.lasturl = urllib2.unquote(event.paramDescriptorForKeyword_(keyDirectObject).stringValue()).strip()
|
||||
self.handler.master.after(ProtocolHandler.POLL, self.handler.poll)
|
||||
protocolhandler.lasturl = urllib2.unquote(event.paramDescriptorForKeyword_(keyDirectObject).stringValue()).strip()
|
||||
protocolhandler.master.after(ProtocolHandler.POLL, protocolhandler.poll)
|
||||
|
||||
|
||||
elif platform == 'win32':
|
||||
elif sys.platform == 'win32' and getattr(sys, 'frozen', False):
|
||||
|
||||
from ctypes import *
|
||||
from ctypes.wintypes import *
|
||||
@ -135,8 +137,8 @@ elif platform == 'win32':
|
||||
|
||||
class ProtocolHandler(GenericProtocolHandler):
|
||||
|
||||
def __init__(self, master):
|
||||
GenericProtocolHandler.__init__(self, master)
|
||||
def __init__(self):
|
||||
GenericProtocolHandler.__init__(self)
|
||||
self.thread = threading.Thread(target=self.worker, name='DDE worker')
|
||||
self.thread.daemon = True
|
||||
self.thread.start()
|
||||
@ -177,7 +179,9 @@ elif platform == 'win32':
|
||||
args = wstring_at(GlobalLock(msg.lParam)).strip()
|
||||
GlobalUnlock(msg.lParam)
|
||||
if args.lower().startswith('open("') and args.endswith('")'):
|
||||
self.event(urllib2.unquote(args[6:-2]).strip())
|
||||
url = urllib2.unquote(args[6:-2]).strip()
|
||||
if url.startswith(self.redirect):
|
||||
self.event(url)
|
||||
SetForegroundWindow(GetParent(self.master.winfo_id())) # raise app window
|
||||
PostMessage(msg.wParam, WM_DDE_ACK, hwnd, PackDDElParam(WM_DDE_ACK, 0x80, msg.lParam))
|
||||
else:
|
||||
@ -190,8 +194,47 @@ elif platform == 'win32':
|
||||
else:
|
||||
print 'Failed to register DDE for cAPI'
|
||||
|
||||
else: # Linux
|
||||
else: # Linux / Run from source
|
||||
|
||||
from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler
|
||||
|
||||
class ProtocolHandler(GenericProtocolHandler):
|
||||
pass
|
||||
|
||||
def __init__(self):
|
||||
GenericProtocolHandler.__init__(self)
|
||||
self.httpd = HTTPServer(('localhost', 0), HTTPRequestHandler)
|
||||
self.redirect = 'http://localhost:%d/auth' % self.httpd.server_port
|
||||
self.thread = threading.Thread(target=self.worker, name='DDE worker')
|
||||
self.thread.daemon = True
|
||||
self.thread.start()
|
||||
|
||||
def close(self):
|
||||
thread = self.thread
|
||||
if thread:
|
||||
self.thread = None
|
||||
self.httpd.shutdown()
|
||||
thread.join() # Wait for it to quit
|
||||
|
||||
def worker(self):
|
||||
self.httpd.serve_forever()
|
||||
|
||||
class HTTPRequestHandler(BaseHTTPRequestHandler):
|
||||
|
||||
def do_HEAD(self):
|
||||
self.do_GET()
|
||||
|
||||
def do_GET(self):
|
||||
url = urllib2.unquote(self.path)
|
||||
if url.startswith('/auth'):
|
||||
protocolhandler.event(url)
|
||||
self.send_response(200)
|
||||
else:
|
||||
self.send_response(404) # Not found
|
||||
self.end_headers()
|
||||
|
||||
def log_request(self, code, size=None):
|
||||
pass
|
||||
|
||||
|
||||
# singleton
|
||||
protocolhandler = ProtocolHandler()
|
||||
|
Loading…
x
Reference in New Issue
Block a user