forked from pyfa-org/Pyfa
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathesi.py
180 lines (140 loc) · 6.11 KB
/
esi.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
# noinspection PyPackageRequirements
import wx
from logbook import Logger
import threading
import time
import base64
import json
import config
import webbrowser
import eos.db
from service.const import EsiLoginMethod, EsiSsoMode
from eos.saveddata.ssocharacter import SsoCharacter
from service.esiAccess import APIException
import gui.globalEvents as GE
from gui.ssoLogin import SsoLogin, SsoLoginServer
from service.server import StoppableHTTPServer, AuthHandler
from service.settings import EsiSettings
from service.esiAccess import EsiAccess
import gui.mainFrame
from requests import Session
pyfalog = Logger(__name__)
class Esi(EsiAccess):
_instance = None
@classmethod
def getInstance(cls):
if cls._instance is None:
cls._instance = Esi()
return cls._instance
def __init__(self):
self.settings = EsiSettings.getInstance()
super().__init__()
# these will be set when needed
self.httpd = None
self.state = None
self.ssoTimer = None
self.implicitCharacter = None
# until I can get around to making proper caching and modifications to said cache, storee deleted fittings here
# so that we can easily hide them in the fitting browser
self.fittings_deleted = set()
# need these here to post events
import gui.mainFrame # put this here to avoid loop
self.mainFrame = gui.mainFrame.MainFrame.getInstance()
def delSsoCharacter(self, id):
char = eos.db.getSsoCharacter(id, config.getClientSecret())
# There is an issue in which the SSO character is not removed from any linked characters - a reference to the
# sso character remains even though the SSO character is deleted which should have deleted the link. This is a
# work around until we can figure out why. Manually delete SSOCharacter from all of it's characters
for x in char.characters:
x._Character__ssoCharacters.remove(char)
eos.db.remove(char)
wx.PostEvent(self.mainFrame, GE.SsoLogout(charID=id))
def getSsoCharacters(self):
chars = eos.db.getSsoCharacters(config.getClientSecret())
return chars
def getSsoCharacter(self, id):
char = eos.db.getSsoCharacter(id, config.getClientSecret())
eos.db.commit()
return char
def getSkills(self, id):
char = self.getSsoCharacter(id)
resp = super().getSkills(char)
return resp.json()
def getSecStatus(self, id):
char = self.getSsoCharacter(id)
resp = super().getSecStatus(char)
return resp.json()
def getFittings(self, id):
char = self.getSsoCharacter(id)
resp = super().getFittings(char)
return resp.json()
def postFitting(self, id, json_str):
# @todo: new fitting ID can be recovered from resp.data,
char = self.getSsoCharacter(id)
resp = super().postFitting(char, json_str)
return resp
def delFitting(self, id, fittingID):
char = self.getSsoCharacter(id)
super().delFitting(char, fittingID)
self.fittings_deleted.add(fittingID)
def login(self):
# always start the local server if user is using client details. Otherwise, start only if they choose to do so.
if self.settings.get('ssoMode') == EsiSsoMode.CUSTOM or self.settings.get('loginMode') == EsiLoginMethod.SERVER:
with gui.ssoLogin.SsoLoginServer(6461 if self.settings.get('ssoMode') == EsiSsoMode.CUSTOM else 0) as dlg:
dlg.ShowModal()
else:
with gui.ssoLogin.SsoLogin() as dlg:
if dlg.ShowModal() == wx.ID_OK:
self.handleLogin({'SSOInfo': [dlg.ssoInfoCtrl.Value.strip()]})
def stopServer(self):
pyfalog.debug("Stopping Server")
if self.httpd:
self.httpd.stop()
self.httpd = None
def startServer(self, port): # todo: break this out into two functions: starting the server, and getting the URI
pyfalog.debug("Starting server")
# we need this to ensure that the previous get_request finishes, and then the socket will close
if self.httpd:
self.stopServer()
time.sleep(1)
self.httpd = StoppableHTTPServer(('localhost', port), AuthHandler)
port = self.httpd.socket.getsockname()[1]
self.serverThread = threading.Thread(target=self.httpd.serve, args=(self.handleServerLogin,))
self.serverThread.name = "SsoCallbackServer"
self.serverThread.daemon = True
self.serverThread.start()
return 'http://localhost:{}'.format(port)
def handleLogin(self, message):
# we already have authenticated stuff for the auto mode
if self.settings.get('ssoMode') == EsiSsoMode.AUTO:
ssoInfo = message['SSOInfo'][0]
auth_response = json.loads(base64.b64decode(ssoInfo))
else:
# otherwise, we need to fetch the information
auth_response = self.auth(message['code'][0])
res = self._session.get(
self.oauth_verify,
headers=self.get_oauth_header(auth_response['access_token'])
)
if res.status_code != 200:
raise APIException(
self.oauth_verify,
res.status_code,
res.json()
)
cdata = res.json()
currentCharacter = self.getSsoCharacter(cdata['CharacterName'])
if currentCharacter is None:
currentCharacter = SsoCharacter(cdata['CharacterID'], cdata['CharacterName'], config.getClientSecret())
Esi.update_token(currentCharacter, auth_response)
eos.db.save(currentCharacter)
wx.PostEvent(self.mainFrame, GE.SsoLogin(character=currentCharacter))
# get (endpoint, char, data?)
def handleServerLogin(self, message):
if not message:
raise Exception("Could not parse out querystring parameters.")
if message['state'][0] != self.state:
pyfalog.warn("OAUTH state mismatch")
raise Exception("OAUTH State Mismatch.")
pyfalog.debug("Handling SSO login with: {0}", message)
self.handleLogin(message)