-
Notifications
You must be signed in to change notification settings - Fork 33
/
Copy pathEngine.cls
260 lines (225 loc) · 8.27 KB
/
Engine.cls
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
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
/// Web Terminal version 4.9.5 WebSocket client.
/// This class represents a connected client via WebSocket.
Class WebTerminal.Engine Extends (%CSP.WebSocket, Common, Trace, Autocomplete)
{
/// Timeout in minutes when connection key expires.
Parameter WSKEYEXPIRES = 3600;
/// How long to wait for authorization key when connection established
Parameter AuthorizationTimeout = 5;
Property CurrentNamespace As %String;
Property InitialZName As %String;
Property InitialZNamespace As %String;
/// The process ID of the terminal core.
Property corePID As %Numeric [ InitialExpression = 0 ];
/// The last known namespace in child process.
Property childNamespace As %String;
Property StartupRoutine As %String;
/// Output flag
Property echo As %Boolean [ InitialExpression = 1 ];
/// flag which enables output buffering
Property bufferOutput As %Boolean [ InitialExpression = 0 ];
/// Used to buffer the output ("o" flag) when bufferOutput flag is set
Property outputBuffer As %Stream.TmpCharacter [ InitialExpression = {##class(%Stream.TmpCharacter).%New()} ];
/// Output flag
Property handler As %Boolean [ InitialExpression = 0, Private ];
Method GetMessage(timeout As %Integer = 86400) As %ZEN.proxyObject
{
#define err(%e, %s) if (%e '= $$$OK) { set obj = ##class(%ZEN.proxyObject).%New() set obj.error = %s return obj }
set data = ..Read(, .st, timeout)
return:((st = $$$CSPWebSocketTimeout) || (st = $$$CSPWebSocketClosed)) ""
$$$err(st, "%wsReadErr: "_$System.Status.GetErrorText(st))
set st = ##class(%ZEN.Auxiliary.jsonProvider).%ConvertJSONToObject(data, , .obj, 1)
$$$err(st, "%wsParseErr: "_$System.Status.GetErrorText(st))
return obj
}
/// Do not remove this method in future versions of WebTerminal, it is used by update.
Method Send(handler As %String = "", data = "") As %Status
{
if (handler = "o") && (..bufferOutput = 1) {
do ..outputBuffer.Write(data)
return $$$OK
}
return:((handler = "o") && (..echo = 0)) $$$OK
return:(handler = "o") ..Write("o"_data) // Enables 2013.2 support (no JSON)
set obj = ##class(%ZEN.proxyObject).%New()
set obj.h = handler
if (..handler '= 0) {
set obj."_cb" = ..handler
}
set obj.d = data
return ..Write(..GetJSONString(obj))
}
Method OnPreServer() As %Status
{
set ..InitialZName = $zname
set ..InitialZNamespace = $znspace
quit $$$OK
}
Method OnPostServer() As %Status
{
if (..corePID '= 0) {
do ..SendChunk(..corePID, "e")
}
quit $$$OK
}
ClassMethod WriteToFile(filename As %String, data As %String) As %Status
{
set file=##class(%File).%New(filename)
do file.Open("WSN")
do file.WriteLine(data)
do file.Close()
}
Method ExecuteSQL(query As %String = "") As %Status
{
set tStatement = ##class(%SQL.Statement).%New()
set qStatus = tStatement.%Prepare(query)
if qStatus'=1 {
write $System.Status.DisplayError(qStatus)
} else {
set rset = tStatement.%Execute()
do rset.%Display()
}
quit $$$OK
}
/// This method performs the authorization and login to WebTerminal.
/// It returns a list with data (see Router.Auth method), which is used then to set up the
/// initial values for the client.
Method RequireAuthorization() As %List
{
set data = ..GetMessage(..#AuthorizationTimeout)
return:(data = "") $LB("%wsReadErr")
return:('$IsObject(data.d)) $LB($case(data.error = "", 1: "Unresolved WS message format", :data.error))
return:(data.d.key = "") $LB("Missing key")
set authKey = data.d.key
set key = $ORDER(^WebTerminal("AuthUser", ""))
set list = ""
while (key '= "") {
set lb = $GET(^WebTerminal("AuthUser", key))
if ((lb '= "") && (key = authKey)) {
set list = lb
}
set time = $LISTGET(lb, 2)
if (time '= "") && ($System.SQL.DATEDIFF("s", time, $h) > ..#WSKEYEXPIRES) {
kill ^WebTerminal("AuthUser", key)
}
set key = $ORDER(^WebTerminal("AuthUser", key))
}
if (list = "") { // not found
return $LB("Invalid key")
}
set username = $LISTGET(list, 1)
set namespace = $LISTGET(list, 3)
set ns = $Namespace
znspace "%SYS"
do ##class(Security.Users).Get(username, .userProps)
znspace ns
set namespace = $case(namespace, "":userProps("NameSpace"), :namespace)
if ($get(userProps("Routine")) '= "") {
set ..StartupRoutine = userProps("Routine")
}
if $get(userProps("Enabled")) '= 1 {
return $LB("User " _ username _ " is not enabled in the system")
}
set $LIST(list, 3) = namespace
set loginStatus = $System.Security.Login(username)
if (loginStatus '= 1) {
return $LB($System.Status.GetErrorText(loginStatus))
}
return $LB("", list)
}
/// See WebTerminal.Handlers
Method ProcessRequest(handler As %String, data) As %Status [ Private ]
{
try {
return $CLASSMETHOD("WebTerminal.Handlers", handler, $this, data)
} catch (e) {
set ..echo = 1
return e.AsSystemError()
}
}
/// Main method for every new client.
Method ClientLoop() As %Status [ Private ]
{
job ##class(WebTerminal.Core).Loop(..StartupRoutine):($NAMESPACE):5
if ($TEST '= 1) { // $TEST=0 for JOB only when timeouted
do ..Send("error", "%noJob")
return $$$NOTOK
}
set ..corePID = $ZCHILD
set ..childNamespace = $NAMESPACE
if (..StartupRoutine = "") {
do ..Send("prompt", ..childNamespace)
} else {
set message = ##class(%ZEN.proxyObject).%New()
set status = $CLASSMETHOD("WebTerminal.Handlers", "Execute", $this, "", 1)
goto loopEnd
}
//try { // temp
for {
set message = ..GetMessage()
quit:(message = "") // if client is gone, finish looping
if (message.error '= "") {
if (message.error '[ "ERROR #7951") { // don't try and send message if it was a WS close error
set st = ..Send("error", message.error)
}
quit
}
if (message."_cb" '= "") { set ..handler = message."_cb" }
set status = ..ProcessRequest(message.h, message.d)
set ..handler = 0
set ..echo = 1
if (status '= "") && (status '= $$$OK) {
set eType = $EXTRACT(status, 1, 1)
do ..Send("oLocalized", $C(13,10) _ $case(eType = 0, 1: $System.Status.GetErrorText(status), :status))
continue
}
}
loopEnd
//} catch (e) { do ..Send("o", $System.Status.GetErrorText(e)) } // temp
return $$$OK
}
/// This method sends basic login info to the user. Use this method to set client variables
/// during the WebTerminal initialization.
/// <parameter>authList</parameter> See Router.Auth method.
Method SendLoginInfo(authList As %List)
{
set obj = ##class(%ZEN.proxyObject).%New()
set obj.username = $USERNAME
set obj.name = $get(^WebTerminal("Name"))
set obj.cleanStart = $ListGet(authList, 4)
set obj.system = $SYSTEM
set obj.firstLaunch = ($get(^WebTerminal("FirstLaunch"), 1) '= 0)
set obj.InstanceGUID = ##class(%SYS.System).InstanceGUID()
set obj.zv = $ZVersion
set ^WebTerminal("FirstLaunch") = 0
do ..Send("init", obj)
}
/// Triggered when new connection established.
Method Server() As %Status
{
set authRes = ..RequireAuthorization()
set authMessage = $ListGet(authRes, 1)
if (authMessage = "") {
set authList = $ListGet(authRes, 2) // see Router.Auth method
set namespace = $ListGet(authList, 3)
if (namespace '= "") && (namespace '= $Namespace) {
try {
znspace namespace
} catch (e) {
do ..Send("oLocalized",
$Char(27) _ "[31m%unNS(" _ namespace _ ")"_ $Char(27) _ "[0m" _ $Char(13,10)
)
}
}
set ..CurrentNamespace = $Namespace
do ..SendLoginInfo(authList)
do ..ClientLoop()
do ..Send("oLocalized", "%wsNormalClose"_$C(13,10))
} else {
do ..Send("oLocalized", "%wsRefuse(" _ authMessage _ ")")
}
do ..EndServer()
set %session.EndSession = 1
quit $$$OK
}
}