GCC Code Coverage Report


Directory: src/gate/
File: src/gate/net/winrmclients.c
Date: 2025-12-12 23:40:09
Exec Total Coverage
Lines: 0 454 0.0%
Functions: 0 31 0.0%
Branches: 0 180 0.0%

Line Branch Exec Source
1 /* GATE PROJECT LICENSE:
2 +----------------------------------------------------------------------------+
3 | Copyright(c) 2018-2025, Stefan Meislinger <sm@opengate.at> |
4 | All rights reserved. |
5 | |
6 | Redistribution and use in source and binary forms, with or without |
7 | modification, are permitted provided that the following conditions are met:|
8 | |
9 | 1. Redistributions of source code must retain the above copyright notice, |
10 | this list of conditions and the following disclaimer. |
11 | 2. Redistributions in binary form must reproduce the above copyright |
12 | notice, this list of conditions and the following disclaimer in the |
13 | documentation and/or other materials provided with the distribution. |
14 | |
15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"|
16 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
17 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
18 | ARE DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
19 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
20 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
21 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
22 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
23 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
24 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
25 | THE POSSIBILITY OF SUCH DAMAGE. |
26 +----------------------------------------------------------------------------+
27 */
28
29 #include "gate/net/winrmclients.h"
30 #include "gate/results.h"
31 #include "gate/debugging.h"
32 #include "gate/uris.h"
33 #include "gate/net/httpclients.h"
34 #include "gate/encode/base64.h"
35 #include "gate/encode/xml.h"
36
37 #define WINRM_TOKEN_TARGET_URL "${WINRM_TARGET}"
38 #define WINRM_TOKEN_URI_SHELL_CMD "${WINRM_SHELL_CMD}"
39 #define WINRM_TOKEN_MESSAGE_ID "${WINRM_MESSAGE_ID}"
40 #define WINRM_TOKEN_WORKDIR "${WINRM_WORKDIR}"
41 #define WINRM_TOKEN_SHELL_UID "${WINRM_SHELL_UID}"
42 #define WINRM_TOKEN_SHELL_COMMAND "${WINRM_SHELL_COMMAND}"
43 #define WINRM_TOKEN_SHELL_ARG "${WINRM_SHELL_ARG}"
44 #define WINRM_TOKEN_SHELL_ARGS "${WINRM_SHELL_ARGS}"
45 #define WINRM_TOKEN_COMMAND_UID "${WINRM_COMMAND_UID}"
46 #define WINRM_TOKEN_COMMAND_INPUT "${WINRM_COMMAND_INPUT}"
47 #define WINRM_TOKEN_TIMEOUT "${WINRM_TOKEN_TIMEOUT}"
48
49 /* https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-wsmv/7f4a1f31-47d8-4599-a23b-c3834ffae21f */
50
51 static gate_bool_t replace_token_raw(gate_strbuilder_t* builder, char const* token, gate_string_t const* value)
52 {
53 gate_size_t const replace_count = gate_strbuilder_replace_str(
54 builder, token, gate_str_length(token), gate_string_ptr(value, 0), gate_string_length(value), 0, 16);
55 return replace_count > 0;
56 }
57
58 static void print_guid(gate_guid_t const* message_id, char* buffer)
59 {
60 if (message_id)
61 {
62 gate_guid_to_string(message_id, buffer);
63 }
64 else
65 {
66 gate_guid_t new_id;
67 gate_guid_generate(&new_id);
68 gate_guid_to_string(message_id, buffer);
69 }
70 }
71
72 static gate_bool_t replace_token_with_guid(gate_strbuilder_t* builder, char const* token, gate_guid_t const* guid)
73 {
74 char guid_text_buffer[128];
75 gate_string_t guid_str = GATE_STRING_INIT_EMPTY;
76
77 print_guid(guid, guid_text_buffer);
78 gate_string_create_static_len(&guid_str, guid_text_buffer, gate_str_length(guid_text_buffer));
79 return replace_token_raw(builder, token, &guid_str);
80 }
81
82 static gate_bool_t xml_encode(gate_string_t const* raw_input, gate_string_t* xml_output)
83 {
84 /* TODO */
85 return gate_string_duplicate(xml_output, raw_input) != NULL;
86 }
87
88 static gate_bool_t replace_token_with_xml_content(gate_strbuilder_t* builder, char const* token, gate_string_t const* value)
89 {
90 gate_bool_t ret = false;
91 gate_string_t xml;
92 if (xml_encode(value, &xml))
93 {
94 ret = replace_token_raw(builder, token, &xml);
95 gate_string_release(&xml);
96 }
97 return ret;
98 }
99
100 static gate_size_t winrm_print_timeout(gate_uint32_t timeout_ms, char* buffer, gate_size_t buffer_len)
101 {
102 gate_strbuilder_t builder;
103 const gate_real64_t timeout = (gate_real64_t)timeout_ms / 1000.0;
104
105 gate_strbuilder_create_static(&builder, buffer, buffer_len, 0);
106 gate_strbuilder_append_cstr(&builder, "PT");
107 gate_strbuilder_append_real(&builder, timeout, 0, 3, 0);
108 gate_strbuilder_append_cstr(&builder, "S");
109 return gate_strbuilder_length(&builder);
110 }
111
112 static gate_bool_t replace_token_with_timeout(gate_strbuilder_t* sb, char const* token, gate_uint32_t timeout_ms)
113 {
114 char buffer[64];
115 gate_size_t len = winrm_print_timeout(timeout_ms, buffer, sizeof(buffer));
116 gate_string_t content = { buffer, len, NULL };
117
118 return replace_token_raw(sb, token, &content);
119 }
120
121
122 static gate_bool_t extract_token(gate_string_t const* content,
123 gate_string_t const* begin_marker, gate_string_t const* end_marker,
124 gate_string_t* extracted_data)
125 {
126 gate_size_t pos_begin;
127 gate_size_t pos_end;
128
129 pos_begin = gate_string_pos(content, begin_marker, 0);
130 if (pos_begin == GATE_STR_NPOS)
131 {
132 return false;
133 }
134 pos_begin += gate_string_length(begin_marker);
135
136 pos_end = gate_string_pos(content, end_marker, pos_begin);
137 if (pos_begin == GATE_STR_NPOS)
138 {
139 return false;
140 }
141
142 return NULL != gate_string_substr(extracted_data, content, pos_begin, pos_end - pos_begin);
143 }
144
145 static gate_bool_t extract_xml_content(gate_string_t const* content,
146 gate_string_t const* begin_marker, gate_string_t const* end_marker,
147 gate_string_t* decoded_data)
148 {
149 gate_bool_t ret = false;
150 gate_string_t data = GATE_STRING_INIT_EMPTY;
151 if (extract_token(content, begin_marker, end_marker, &data))
152 {
153 /* TODO: XML decoding */
154 ret = NULL != gate_string_create_copy(decoded_data, &data);
155 gate_string_release(&data);
156 }
157 return ret;
158 }
159
160
161
162 static char const WINRM_SOAP_MSG_CREATESHELL_REQUEST[] =
163 "<s:Envelope xmlns:s=\"http://www.w3.org/2003/05/soap-envelope\" xmlns:a=\"http://schemas.xmlsoap.org/ws/2004/08/addressing\" xmlns:wsman=\"http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd\">"
164 "<s:Header>"
165 "<a:To>" WINRM_TOKEN_TARGET_URL "</a:To>"
166 "<wsman:ResourceURI s:mustUnderstand=\"true\">http://schemas.microsoft.com/wbem/wsman/1/windows/shell/cmd</wsman:ResourceURI>"
167 "<a:ReplyTo><a:Address s:mustUnderstand=\"true\">http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</a:Address></a:ReplyTo>"
168 "<a:Action s:mustUnderstand=\"true\">http://schemas.xmlsoap.org/ws/2004/09/transfer/Create</a:Action>"
169 "<wsman:MaxEnvelopeSize s:mustUnderstand=\"true\">153600</wsman:MaxEnvelopeSize>"
170 "<a:MessageID>uuid:" WINRM_TOKEN_MESSAGE_ID "</a:MessageID>"
171 "<wsman:Locale xml:lang=\"en-US\" s:mustUnderstand=\"false\" />"
172 "<wsman:OptionSet xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">"
173 "<wsman:Option Name=\"WINRS_NOPROFILE\">TRUE</wsman:Option>"
174 "<wsman:Option Name=\"WINRS_CODEPAGE\">437</wsman:Option>"
175 "</wsman:OptionSet>"
176 "<wsman:OperationTimeout>PT60.000S</wsman:OperationTimeout>"
177 "</s:Header>"
178 "<s:Body>"
179 "<rsp:Shell xmlns:rsp=\"http://schemas.microsoft.com/wbem/wsman/1/windows/shell\">"
180 /*
181 "<rsp:Environment>"
182 "<rsp:Variable Name=\"test\">1</rsp:Variable>"
183 "</rsp:Environment>"
184 */
185 WINRM_TOKEN_WORKDIR
186 "<rsp:InputStreams>stdin</rsp:InputStreams>"
187 "<rsp:OutputStreams>stdout stderr</rsp:OutputStreams>"
188 "</rsp:Shell>"
189 "</s:Body>"
190 "</s:Envelope>";
191
192 gate_string_t* winrm_create_shell_request(gate_string_t* output,
193 gate_string_t const* target_url,
194 gate_guid_t const* message_id,
195 gate_string_t const* work_dir)
196 {
197 gate_string_t* ret = NULL;
198 gate_strbuilder_t sb;
199
200 gate_strbuilder_create(&sb, 2048);
201 gate_strbuilder_append_text(&sb, WINRM_SOAP_MSG_CREATESHELL_REQUEST, sizeof(WINRM_SOAP_MSG_CREATESHELL_REQUEST) - 1);
202
203 replace_token_with_xml_content(&sb, WINRM_TOKEN_TARGET_URL, target_url);
204 replace_token_with_guid(&sb, WINRM_TOKEN_MESSAGE_ID, message_id);
205 if (!gate_string_is_empty(work_dir))
206 {
207 gate_strbuilder_t wd_builder = GATE_INIT_EMPTY;
208 gate_string_t wd = GATE_STRING_INIT_EMPTY;
209 gate_strbuilder_create(&wd_builder, 128);
210 gate_strbuilder_append_cstr(&wd_builder, "<rsp:WorkingDirectory>");
211 gate_strbuilder_append_string(&wd_builder, work_dir);
212 gate_strbuilder_append_cstr(&wd_builder, "</rsp:WorkingDirectory>");
213 gate_strbuilder_to_string(&wd_builder, &wd);
214 replace_token_with_xml_content(&sb, WINRM_TOKEN_WORKDIR, &wd);
215 gate_strbuilder_release(&wd_builder);
216 gate_string_release(&wd);
217 }
218
219 ret = gate_strbuilder_to_string(&sb, output);
220 gate_strbuilder_release(&sb);
221 return ret;
222 }
223
224
225 static char const WINRM_SOAP_MSG_EXECUTECOMMAND_REQUEST[] =
226 "<s:Envelope xmlns:s=\"http://www.w3.org/2003/05/soap-envelope\" xmlns:a=\"http://schemas.xmlsoap.org/ws/2004/08/addressing\" xmlns:wsman=\"http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd\">"
227 "<s:Header>"
228 "<a:To>" WINRM_TOKEN_TARGET_URL "</a:To>"
229 "<wsman:ResourceURI s:mustUnderstand=\"true\">http://schemas.microsoft.com/wbem/wsman/1/windows/shell/cmd</wsman:ResourceURI>"
230 "<a:ReplyTo><a:Address s:mustUnderstand=\"true\">http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</a:Address></a:ReplyTo>"
231 "<a:Action s:mustUnderstand=\"true\">http://schemas.microsoft.com/wbem/wsman/1/windows/shell/Command</a:Action>"
232 "<wsman:MaxEnvelopeSize s:mustUnderstand=\"true\">153600</wsman:MaxEnvelopeSize>"
233 "<a:MessageID>uuid:" WINRM_TOKEN_MESSAGE_ID "</a:MessageID>"
234 "<wsman:Locale xml:lang=\"en-US\" s:mustUnderstand=\"false\" />"
235 "<wsman:SelectorSet><wsman:Selector Name=\"ShellId\">" WINRM_TOKEN_SHELL_UID "</wsman:Selector></wsman:SelectorSet>"
236 "<wsman:OptionSet xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">"
237 "<wsman:Option Name=\"WINRS_CONSOLEMODE_STDIN\">TRUE</wsman:Option>"
238 /* "<wsman:Option Name=\"WINRS_SKIP_CMD_SHELL\">FALSE</wsman:Option>" */
239 "</wsman:OptionSet>"
240 "<wsman:OperationTimeout>PT60.000S</wsman:OperationTimeout>"
241 "</s:Header>"
242 "<s:Body>"
243 "<rsp:CommandLine xmlns:rsp=\"http://schemas.microsoft.com/wbem/wsman/1/windows/shell\">"
244 "<rsp:Command>" WINRM_TOKEN_SHELL_COMMAND "</rsp:Command>"
245 /* "<rsp:Arguments>" WINRM_TOKEN_SHELL_ARG "</rsp:Arguments>" */
246 WINRM_TOKEN_SHELL_ARGS
247 "</rsp:CommandLine>"
248 "</s:Body>"
249 "</s:Envelope>";
250
251 gate_string_t* winrm_shell_execute_request(gate_string_t* output,
252 gate_string_t const* target_url,
253 gate_guid_t const* message_id,
254 gate_string_t const* shell_uid,
255 gate_string_t const* shell_command,
256 gate_string_t const* shell_args, gate_size_t shell_args_count)
257 {
258 gate_string_t* ret = NULL;
259 gate_strbuilder_t sb;
260 gate_string_t cmd_args = GATE_STRING_INIT_EMPTY;
261
262 gate_strbuilder_create(&sb, 2048);
263 gate_strbuilder_append_text(&sb, WINRM_SOAP_MSG_EXECUTECOMMAND_REQUEST, sizeof(WINRM_SOAP_MSG_EXECUTECOMMAND_REQUEST) - 1);
264
265 if (shell_args_count > 0)
266 {
267 gate_strbuilder_t args_builder;
268 gate_size_t ndx;
269 gate_strbuilder_create(&args_builder, 256);
270 for (ndx = 0; ndx != shell_args_count; ++ndx)
271 {
272 gate_strbuilder_append_cstr(&args_builder, "<rsp:Arguments>");
273 gate_strbuilder_append_string(&args_builder, &shell_args[ndx]);
274 gate_strbuilder_append_cstr(&args_builder, "</rsp:Arguments>");
275 }
276
277 gate_strbuilder_to_string(&args_builder, &cmd_args);
278 gate_strbuilder_release(&args_builder);
279 }
280
281 replace_token_with_xml_content(&sb, WINRM_TOKEN_TARGET_URL, target_url);
282 replace_token_with_guid(&sb, WINRM_TOKEN_MESSAGE_ID, message_id);
283 replace_token_with_xml_content(&sb, WINRM_TOKEN_SHELL_UID, shell_uid);
284 replace_token_with_xml_content(&sb, WINRM_TOKEN_SHELL_COMMAND, shell_command);
285 replace_token_raw(&sb, WINRM_TOKEN_SHELL_ARGS, &cmd_args);
286
287 ret = gate_strbuilder_to_string(&sb, output);
288 gate_strbuilder_release(&sb);
289 gate_string_release(&cmd_args);
290 return ret;
291 }
292
293 static char const WINRM_SOAP_MSG_RECEIVEOUTPUT_REQUEST[] =
294 "<s:Envelope xmlns:s=\"http://www.w3.org/2003/05/soap-envelope\" xmlns:a=\"http://schemas.xmlsoap.org/ws/2004/08/addressing\" xmlns:wsman=\"http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd\">"
295 "<s:Header>"
296 "<a:To>" WINRM_TOKEN_TARGET_URL "</a:To>"
297 "<a:ReplyTo><a:Address s:mustUnderstand=\"true\">http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</a:Address></a:ReplyTo>"
298 "<a:Action s:mustUnderstand=\"true\">http://schemas.microsoft.com/wbem/wsman/1/windows/shell/Receive</a:Action>"
299 "<wsman:MaxEnvelopeSize s:mustUnderstand=\"true\">153600</wsman:MaxEnvelopeSize>"
300 "<a:MessageID>uuid:" WINRM_TOKEN_MESSAGE_ID "</a:MessageID>"
301 "<wsman:Locale xml:lang=\"en-US\" s:mustUnderstand=\"false\" />"
302 "<wsman:ResourceURI xmlns:wsman=\"http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd\">http://schemas.microsoft.com/wbem/wsman/1/windows/shell/cmd</wsman:ResourceURI>"
303 "<wsman:SelectorSet xmlns:wsman=\"http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd\" xmlns=\"http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd\">"
304 "<wsman:Selector Name=\"ShellId\">" WINRM_TOKEN_SHELL_UID "</wsman:Selector>"
305 "</wsman:SelectorSet>"
306 "<wsman:OperationTimeout>" WINRM_TOKEN_TIMEOUT "</wsman:OperationTimeout>"
307 "</s:Header>"
308 "<s:Body>"
309 "<rsp:Receive xmlns:rsp=\"http://schemas.microsoft.com/wbem/wsman/1/windows/shell\" SequenceId=\"0\">"
310 "<rsp:DesiredStream CommandId=\"" WINRM_TOKEN_COMMAND_UID "\">stdout stderr</rsp:DesiredStream>"
311 "</rsp:Receive>"
312 "</s:Body>"
313 "</s:Envelope>";
314
315
316 gate_string_t* winrm_shell_receiveoutput_request(gate_string_t* output,
317 gate_string_t const* target_url,
318 gate_guid_t const* message_id,
319 gate_string_t const* shell_id,
320 gate_string_t const* command_id,
321 gate_uint32_t timeout_ms)
322 {
323 gate_string_t* ret = NULL;
324 gate_strbuilder_t sb;
325
326 gate_strbuilder_create(&sb, 2048);
327 gate_strbuilder_append_text(&sb, WINRM_SOAP_MSG_RECEIVEOUTPUT_REQUEST, sizeof(WINRM_SOAP_MSG_RECEIVEOUTPUT_REQUEST) - 1);
328
329 replace_token_with_xml_content(&sb, WINRM_TOKEN_TARGET_URL, target_url);
330 replace_token_with_guid(&sb, WINRM_TOKEN_MESSAGE_ID, message_id);
331 replace_token_with_xml_content(&sb, WINRM_TOKEN_SHELL_UID, shell_id);
332 replace_token_with_xml_content(&sb, WINRM_TOKEN_COMMAND_UID, command_id);
333 replace_token_with_timeout(&sb, WINRM_TOKEN_TIMEOUT, timeout_ms);
334
335 ret = gate_strbuilder_to_string(&sb, output);
336 gate_strbuilder_release(&sb);
337 return ret;
338 }
339
340 static char const WINRM_SOAP_MSG_SENDINPUT_REQUEST[] =
341 "<s:Envelope xmlns:s=\"http://www.w3.org/2003/05/soap-envelope\" xmlns:a=\"http://schemas.xmlsoap.org/ws/2004/08/addressing\" xmlns:wsman=\"http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd\">"
342 "<s:Header>"
343 "<a:To>" WINRM_TOKEN_TARGET_URL "</a:To>"
344 "<a:ReplyTo><a:Address s:mustUnderstand=\"true\">http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</a:Address></a:ReplyTo>"
345 "<a:Action s:mustUnderstand=\"true\">http://schemas.microsoft.com/wbem/wsman/1/windows/shell/Send</a:Action>"
346 "<wsman:MaxEnvelopeSize s:mustUnderstand=\"true\">153600</wsman:MaxEnvelopeSize>"
347 "<a:MessageID>uuid:" WINRM_TOKEN_MESSAGE_ID "</a:MessageID>"
348 "<wsman:Locale xml:lang=\"en-US\" s:mustUnderstand=\"false\" />"
349 "<wsman:ResourceURI xmlns:wsman=\"http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd\">http://schemas.microsoft.com/wbem/wsman/1/windows/shell/cmd</wsman:ResourceURI>"
350 "<wsman:SelectorSet xmlns:wsman=\"http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd\" xmlns=\"http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd\">"
351 "<wsman:Selector Name=\"ShellId\">" WINRM_TOKEN_SHELL_UID "</wsman:Selector>"
352 "</wsman:SelectorSet>"
353 "<wsman:OperationTimeout>PT60.000S</wsman:OperationTimeout>"
354 "</s:Header>"
355 "<s:Body>"
356 "<rsp:Send xmlns:rsp=\"http://schemas.microsoft.com/wbem/wsman/1/windows/shell\">"
357 "<rsp:Stream xmlns:rsp=\"http://schemas.microsoft.com/wbem/wsman/1/windows/shell\" Name=\"stdin\" CommandId=\"" WINRM_TOKEN_COMMAND_UID "\">"
358 WINRM_TOKEN_COMMAND_INPUT
359 "</rsp:Stream>"
360 "</rsp:Send>"
361 "</s:Body>"
362 "</s:Envelope>";
363
364 gate_string_t* winrm_shell_sendinput_request(gate_string_t* output,
365 gate_string_t const* target_url,
366 gate_guid_t const* message_id,
367 gate_string_t const* shell_id,
368 gate_string_t const* command_id,
369 gate_string_t const* input)
370 {
371 gate_string_t* ret = NULL;
372 gate_strbuilder_t sb;
373
374 gate_strbuilder_create(&sb, 2048);
375 gate_strbuilder_append_text(&sb, WINRM_SOAP_MSG_SENDINPUT_REQUEST, sizeof(WINRM_SOAP_MSG_SENDINPUT_REQUEST) - 1);
376
377 replace_token_with_xml_content(&sb, WINRM_TOKEN_TARGET_URL, target_url);
378 replace_token_with_guid(&sb, WINRM_TOKEN_MESSAGE_ID, message_id);
379 replace_token_with_xml_content(&sb, WINRM_TOKEN_SHELL_UID, shell_id);
380 replace_token_with_xml_content(&sb, WINRM_TOKEN_COMMAND_UID, command_id);
381 replace_token_with_xml_content(&sb, WINRM_TOKEN_COMMAND_INPUT, input);
382
383 ret = gate_strbuilder_to_string(&sb, output);
384 gate_strbuilder_release(&sb);
385 return ret;
386 }
387
388 static char const WINRM_SOAP_MSG_TERMINATEOPERATION_REQUEST[] =
389 "<s:Envelope xmlns:s=\"http://www.w3.org/2003/05/soap-envelope\" xmlns:a=\"http://schemas.xmlsoap.org/ws/2004/08/addressing\" xmlns:wsman=\"http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd\">"
390 "<s:Header>"
391 "<a:To>" WINRM_TOKEN_TARGET_URL "</a:To>"
392 "<a:ReplyTo><a:Address s:mustUnderstand=\"true\">http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</a:Address></a:ReplyTo>"
393 "<a:Action s:mustUnderstand=\"true\">http://schemas.microsoft.com/wbem/wsman/1/windows/shell/Signal</a:Action>"
394 "<wsman:MaxEnvelopeSize s:mustUnderstand=\"true\">153600</wsman:MaxEnvelopeSize>"
395 "<wsa:MessageID>uuid:" WINRM_TOKEN_MESSAGE_ID "</wsa:MessageID>"
396 "<wsman:Locale xml:lang=\"en-US\" s:mustUnderstand=\"false\"/>"
397 "<wsman:ResourceURI xmlns:wsman=\"http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd\">http://schemas.microsoft.com/wbem/wsman/1/windows/shell/cmd</wsman:ResourceURI>"
398 "<wsman:SelectorSet xmlns:wsman=\"http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd\" xmlns=\"http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd\">"
399 "<wsman:Selector Name=\"ShellId\">uuid:" WINRM_TOKEN_SHELL_UID "</wsman:Selector>"
400 "</wsman:SelectorSet>"
401 "<wsman:OperationTimeout>PT60.000S</wsman:OperationTimeout>"
402 "</s:Header>"
403 "<s:Body>"
404 "<rsp:Signal xmlns:rsp=\"http://schemas.microsoft.com/wbem/wsman/1/windows/shell\" CommandId=\"" WINRM_TOKEN_COMMAND_UID "\">"
405 "<rsp:Code>http://schemas.microsoft.com/wbem/wsman/1/windows/shell/signal/ctrl_c</rsp:Code>"
406 "</rsp:Signal>"
407 "</s:Body>"
408 "</s:Envelope>";
409
410 gate_string_t* winrm_shell_terminate_request(gate_string_t* output,
411 gate_string_t const* target_url,
412 gate_guid_t const* message_id,
413 gate_string_t const* shell_id,
414 gate_string_t const* command_id)
415 {
416 gate_string_t* ret = NULL;
417 gate_strbuilder_t sb;
418
419 gate_strbuilder_create(&sb, 2048);
420 gate_strbuilder_append_text(&sb, WINRM_SOAP_MSG_TERMINATEOPERATION_REQUEST, sizeof(WINRM_SOAP_MSG_TERMINATEOPERATION_REQUEST) - 1);
421
422 replace_token_with_xml_content(&sb, WINRM_TOKEN_TARGET_URL, target_url);
423 replace_token_with_guid(&sb, WINRM_TOKEN_MESSAGE_ID, message_id);
424 replace_token_with_xml_content(&sb, WINRM_TOKEN_SHELL_UID, shell_id);
425 replace_token_with_xml_content(&sb, WINRM_TOKEN_COMMAND_UID, command_id);
426
427 ret = gate_strbuilder_to_string(&sb, output);
428 gate_strbuilder_release(&sb);
429 return ret;
430 }
431
432 static char const WINRM_SOAP_MSG_DELETESHELL_REQUEST[] =
433 "<s:Envelope xmlns:s=\"http://www.w3.org/2003/05/soap-envelope\" xmlns:a=\"http://schemas.xmlsoap.org/ws/2004/08/addressing\" xmlns:wsman=\"http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd\">"
434 "<s:Header>"
435 "<a:To>" WINRM_TOKEN_TARGET_URL "</a:To>"
436 "<a:ReplyTo><a:Address s:mustUnderstand=\"true\">http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</a:Address></a:ReplyTo>"
437 "<a:Action s:mustUnderstand=\"true\">http://schemas.xmlsoap.org/ws/2004/09/transfer/Delete</a:Action>"
438 "<wsman:MaxEnvelopeSize s:mustUnderstand=\"true\">153600</wsman:MaxEnvelopeSize>"
439 "<a:MessageID>uuid:" WINRM_TOKEN_MESSAGE_ID "</a:MessageID>"
440 "<wsman:Locale xml:lang=\"en-US\" s:mustUnderstand=\"false\" />"
441 "<wsman:ResourceURI xmlns:wsman=\"http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd\">http://schemas.microsoft.com/wbem/wsman/1/windows/shell/cmd</wsman:ResourceURI>"
442 "<wsman:SelectorSet xmlns:wsman=\"http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd\" xmlns=\"http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd\">"
443 "<wsman:Selector Name=\"ShellId\">uuid:" WINRM_TOKEN_SHELL_UID "</wsman:Selector>"
444 "</wsman:SelectorSet>"
445 "<wsman:OperationTimeout>PT60.000S</wsman:OperationTimeout>"
446 "</s:Header>"
447 "<s:Body></s:Body>"
448 "</s:Envelope>";
449
450 gate_string_t* winrm_delete_shell_request(gate_string_t* output,
451 gate_string_t const* target_url,
452 gate_guid_t const* message_id,
453 gate_string_t const* shell_id)
454 {
455 gate_string_t* ret = NULL;
456 gate_strbuilder_t sb;
457
458 gate_strbuilder_create(&sb, 2048);
459 gate_strbuilder_append_text(&sb, WINRM_SOAP_MSG_DELETESHELL_REQUEST, sizeof(WINRM_SOAP_MSG_DELETESHELL_REQUEST) - 1);
460
461 replace_token_with_xml_content(&sb, WINRM_TOKEN_TARGET_URL, target_url);
462 replace_token_with_guid(&sb, WINRM_TOKEN_MESSAGE_ID, message_id);
463 replace_token_with_xml_content(&sb, WINRM_TOKEN_SHELL_UID, shell_id);
464
465 ret = gate_strbuilder_to_string(&sb, output);
466 gate_strbuilder_release(&sb);
467 return ret;
468 }
469
470
471 gate_result_t gate_winrmclient_create(gate_winrmclient_t* client,
472 gate_string_t const* server, gate_uint16_t port,
473 gate_string_t const* user, gate_string_t const* password,
474 gate_enumint_t flags)
475 {
476 static gate_string_t const wsman_path = GATE_STRING_INIT_STATIC("/wsman");
477 gate_result_t ret = GATE_RESULT_FAILED;
478 gate_bool_t const secure_flag = GATE_FLAG_ENABLED(flags, GATE_WINRMCLIENT_FLAG_SECURE);
479
480 do
481 {
482 if (!client || !server)
483 {
484 ret = GATE_RESULT_INVALIDARG;
485 break;
486 }
487
488 gate_mem_clear(client, sizeof(gate_winrmclient_t));
489
490 ret = gate_uri_init(&client->uri);
491 GATE_BREAK_IF_FAILED(ret);
492
493 gate_string_create_static(&client->uri.scheme, secure_flag ? GATE_URI_SCHEME_HTTPS : GATE_URI_SCHEME_HTTP);
494
495 if (NULL == gate_string_clone(&client->uri.host, server))
496 {
497 ret = GATE_RESULT_OUTOFMEMORY;
498 break;
499 }
500
501 if (NULL == gate_string_clone(&client->uri.absolute_path, &wsman_path))
502 {
503 ret = GATE_RESULT_OUTOFMEMORY;
504 break;
505 }
506
507 if (!gate_string_is_empty(user) || !gate_string_is_empty(password))
508 {
509 ret = gate_uri_build_user_info(user, password, &client->uri.user_info);
510 GATE_BREAK_IF_FAILED(ret);
511 }
512
513 client->uri.port = (port == 0) ? (secure_flag ? 5986 : 5985) : port;
514
515 ret = GATE_RESULT_OK;
516
517 } while (0);
518
519 if (client && GATE_FAILED(ret))
520 {
521 gate_uri_destroy(&client->uri);
522 }
523 return ret;
524 }
525
526 gate_result_t gate_winrmclient_release(gate_winrmclient_t* client)
527 {
528 gate_result_t ret;
529
530 GATE_DEBUG_ASSERT(client != NULL);
531 do
532 {
533 ret = gate_uri_destroy(&client->uri);
534 } while (0);
535
536 return ret;
537 }
538
539 static gate_string_t* winrm_create_target_url(gate_string_t* target_url, gate_winrmclient_t* from_client)
540 {
541 gate_string_t* ret = NULL;
542 gate_uri_t uri;
543
544 do
545 {
546 gate_result_t result;
547 gate_mem_clear(&uri, sizeof(uri));
548
549 result = gate_uri_copy(&uri, &from_client->uri);
550 GATE_BREAK_IF_FAILED(result);
551
552 gate_string_release(&uri.user_info);
553 gate_string_release(&uri.query);
554
555 result = gate_uri_to_string(&uri, target_url, false);
556 GATE_BREAK_IF_FAILED(result);
557
558 ret = target_url;
559 } while (0);
560
561 gate_uri_destroy(&uri);
562 return ret;
563 }
564
565 static gate_result_t winrm_send_soap_request(gate_winrmclient_t* client, gate_string_t const* soap_request, gate_uint32_t* ptr_status_code, gate_string_t* ptr_soap_response)
566 {
567 gate_result_t ret;
568 gate_httpclient_t http = GATE_INIT_EMPTY;
569 gate_http_request_t req = GATE_INIT_EMPTY;
570 gate_http_response_t resp = GATE_INIT_EMPTY;
571 gate_memstream_impl_t upload_impl;
572 gate_size_t upload_size = gate_string_length(soap_request);
573 gate_stringstream_t* ss = NULL;
574 static gate_string_t const http_post = GATE_STRING_INIT_STATIC(GATE_HTTP_METHOD_POST);
575 static gate_string_t const header_content_type = GATE_STRING_INIT_STATIC(GATE_HTTP_HEADER_CONTENTTYPE);
576 static gate_string_t const header_content_type_value = GATE_STRING_INIT_STATIC("application/soap+xml;charset=UTF-8");
577 static gate_string_t const header_connection = GATE_STRING_INIT_STATIC(GATE_HTTP_HEADER_CONNECTION);
578 static gate_string_t const header_connection_keepalive = GATE_STRING_INIT_STATIC(GATE_HTTP_HEADERVALUE_CONNECTION_KEEPALIVE);
579
580 do
581 {
582 ret = gate_httpclient_create(&http, &client->uri.host, client->uri.port, 0);
583 GATE_BREAK_IF_FAILED(ret);
584
585 ret = gate_http_request_init_uri(&req, &http_post, &client->uri);
586 GATE_BREAK_IF_FAILED(ret);
587
588 req.upload_stream = (gate_stream_t*)gate_memstream_create_static_unmanaged_readonly(
589 &upload_impl, gate_string_ptr(soap_request, 0), upload_size, upload_size);
590
591 gate_map_add(&req.headers, &header_content_type, &header_content_type_value);
592 gate_map_add(&req.headers, &header_connection, &header_connection_keepalive);
593
594 ret = gate_httpclient_send_request(&http, &req, &resp);
595 GATE_BREAK_IF_FAILED(ret);
596
597 ss = gate_stringstream_create(256);
598 if (ss == NULL)
599 {
600 ret = GATE_RESULT_OUTOFMEMORY;
601 break;
602 }
603
604 if (ptr_status_code)
605 {
606 *ptr_status_code = resp.status_code;
607 }
608
609 ret = gate_stream_transfer(resp.response_stream, (gate_stream_t*)ss);
610 GATE_BREAK_IF_FAILED(ret);
611
612 ret = gate_stringstream_to_string(ss, ptr_soap_response);
613 GATE_BREAK_IF_FAILED(ret);
614
615 /* success case */
616 ret = GATE_RESULT_OK;
617 } while (0);
618
619 gate_http_response_release(&resp);
620 gate_http_request_release(&req);
621 gate_httpclient_release(&http);
622
623 return ret;
624 }
625
626 static gate_result_t winrm_prepare_request(gate_winrmclient_t* client, gate_guid_t* ptr_new_msg_id, gate_string_t* ptr_target_url)
627 {
628 gate_result_t ret = gate_guid_generate(ptr_new_msg_id);
629 if (GATE_SUCCEEDED(ret))
630 {
631 if (NULL == winrm_create_target_url(ptr_target_url, client))
632 {
633 ret = GATE_RESULT_OUTOFMEMORY;
634 }
635 }
636 return ret;
637 }
638
639 static gate_result_t check_status_ok(gate_uint32_t status_code)
640 {
641 switch (status_code)
642 {
643 case 200: return GATE_RESULT_OK;
644 default: return GATE_RESULT_FAILED;
645 }
646 }
647
648 gate_result_t gate_winrmclient_shell_new(gate_winrmclient_t* client, gate_string_t const* workdir, gate_array_t const* env_vars, gate_string_t* shell_id)
649 {
650 gate_result_t ret;
651 gate_string_t soap_request = GATE_STRING_INIT_EMPTY;
652 gate_string_t target_url = GATE_STRING_INIT_EMPTY;
653 gate_guid_t msg_id = GATE_INIT_EMPTY;
654 gate_uint32_t status_code = 0;
655 gate_string_t soap_response = GATE_STRING_INIT_EMPTY;
656 static gate_string_t const response_shell_id_begin = GATE_STRING_INIT_STATIC("ShellId>");
657 static gate_string_t const response_shell_id_end = GATE_STRING_INIT_STATIC("</");
658
659 do
660 {
661 ret = winrm_prepare_request(client, &msg_id, &target_url);
662 GATE_BREAK_IF_FAILED(ret);
663
664 if (NULL == winrm_create_shell_request(&soap_request, &target_url, &msg_id, workdir))
665 {
666 ret = GATE_RESULT_OUTOFMEMORY;
667 break;
668 }
669
670 ret = winrm_send_soap_request(client, &soap_request, &status_code, &soap_response);
671 GATE_BREAK_IF_FAILED(ret);
672
673 ret = check_status_ok(status_code);
674 GATE_BREAK_IF_FAILED(ret);
675
676 if (!extract_xml_content(&soap_response, &response_shell_id_begin, &response_shell_id_end, shell_id))
677 {
678 ret = GATE_RESULT_INVALIDCONTENT;
679 break;
680 }
681
682 ret = GATE_RESULT_OK;
683 } while (0);
684
685 gate_string_release(&soap_response);
686 gate_string_release(&soap_request);
687 gate_string_release(&target_url);
688
689 return ret;
690 }
691
692 gate_result_t gate_winrmclient_shell_delete(gate_winrmclient_t* client, gate_string_t const* shell_id)
693 {
694 gate_result_t ret = GATE_RESULT_FAILED;
695 gate_guid_t msg_id = GATE_INIT_EMPTY;
696 gate_string_t target_url = GATE_STRING_INIT_EMPTY;
697 gate_string_t soap_request = GATE_STRING_INIT_EMPTY;
698 gate_string_t soap_response = GATE_STRING_INIT_EMPTY;
699 gate_uint32_t status_code = 0;
700
701 do
702 {
703 ret = winrm_prepare_request(client, &msg_id, &target_url);
704 GATE_BREAK_IF_FAILED(ret);
705
706 if (NULL == winrm_delete_shell_request(&soap_request, &target_url, &msg_id, shell_id))
707 {
708 ret = GATE_RESULT_OUTOFMEMORY;
709 break;
710 }
711
712 ret = winrm_send_soap_request(client, &soap_request, &status_code, &soap_response);
713 GATE_BREAK_IF_FAILED(ret);
714
715 ret = check_status_ok(status_code);
716 GATE_BREAK_IF_FAILED(ret);
717
718 ret = GATE_RESULT_OK;
719 } while (0);
720
721 gate_string_release(&soap_request);
722 gate_string_release(&soap_response);
723 gate_string_release(&target_url);
724 return ret;
725 }
726
727
728 gate_result_t gate_winrmclient_shell_command_start(
729 gate_winrmclient_t* client, gate_string_t const* shell_id, gate_string_t const* command, gate_array_t const* args,
730 gate_string_t* command_id)
731 {
732 gate_result_t ret = GATE_RESULT_FAILED;
733 gate_guid_t msg_id = GATE_INIT_EMPTY;
734 gate_string_t target_url = GATE_STRING_INIT_EMPTY;
735 gate_string_t soap_request = GATE_STRING_INIT_EMPTY;
736 gate_string_t soap_response = GATE_STRING_INIT_EMPTY;
737 gate_uint32_t status_code = 0;
738
739 do
740 {
741 static gate_string_t const resp_command_id_begin = GATE_STRING_INIT_STATIC("CommandId>");
742 static gate_string_t const resp_command_id_end = GATE_STRING_INIT_STATIC("</");
743
744 ret = winrm_prepare_request(client, &msg_id, &target_url);
745 GATE_BREAK_IF_FAILED(ret);
746
747 if (NULL == winrm_shell_execute_request(&soap_request, &target_url, &msg_id, shell_id, command, NULL, 0))
748 {
749 ret = GATE_RESULT_OUTOFMEMORY;
750 break;
751 }
752
753 ret = winrm_send_soap_request(client, &soap_request, &status_code, &soap_response);
754 GATE_BREAK_IF_FAILED(ret);
755
756 ret = check_status_ok(status_code);
757 GATE_BREAK_IF_FAILED(ret);
758
759 if (!extract_xml_content(&soap_response, &resp_command_id_begin, &resp_command_id_end, command_id))
760 {
761 ret = GATE_RESULT_INVALIDCONTENT;
762 break;
763 }
764
765 ret = GATE_RESULT_OK;
766 } while (0);
767
768 gate_string_release(&soap_request);
769 gate_string_release(&soap_response);
770 gate_string_release(&target_url);
771 return ret;
772 }
773
774
775 gate_result_t gate_winrmclient_shell_command_abort(gate_winrmclient_t* client,
776 gate_string_t const* shell_id, gate_string_t const* command_id)
777 {
778 gate_result_t ret = GATE_RESULT_FAILED;
779 gate_guid_t msg_id = GATE_INIT_EMPTY;
780 gate_string_t target_url = GATE_STRING_INIT_EMPTY;
781 gate_string_t soap_request = GATE_STRING_INIT_EMPTY;
782 gate_string_t soap_response = GATE_STRING_INIT_EMPTY;
783 gate_uint32_t status_code = 0;
784
785 do
786 {
787 ret = winrm_prepare_request(client, &msg_id, &target_url);
788 GATE_BREAK_IF_FAILED(ret);
789
790 if (NULL == winrm_shell_terminate_request(&soap_request, &target_url, &msg_id, shell_id, command_id))
791 {
792 ret = GATE_RESULT_OUTOFMEMORY;
793 break;
794 }
795
796 ret = winrm_send_soap_request(client, &soap_request, &status_code, &soap_response);
797 GATE_BREAK_IF_FAILED(ret);
798
799 ret = check_status_ok(status_code);
800 GATE_BREAK_IF_FAILED(ret);
801
802 ret = GATE_RESULT_OK;
803 } while (0);
804
805 gate_string_release(&soap_request);
806 gate_string_release(&soap_response);
807 gate_string_release(&target_url);
808 return ret;
809 }
810
811
812 gate_result_t gate_winrmclient_shell_command_send(gate_winrmclient_t* client,
813 gate_string_t const* shell_id, gate_string_t const* command_id, gate_blob_t const* data_buffer)
814 {
815 gate_result_t ret = GATE_RESULT_FAILED;
816 gate_guid_t msg_id = GATE_INIT_EMPTY;
817 gate_string_t target_url = GATE_STRING_INIT_EMPTY;
818 gate_string_t soap_request = GATE_STRING_INIT_EMPTY;
819 gate_string_t soap_response = GATE_STRING_INIT_EMPTY;
820 gate_string_t input_b64 = GATE_STRING_INIT_EMPTY;
821 gate_uint32_t status_code = 0;
822 char const* ptr_data = (char const*)gate_blob_data(data_buffer);
823 gate_size_t data_len = gate_blob_length(data_buffer);
824 gate_string_t input_raw = { ptr_data, data_len, NULL };
825 gate_strbuilder_t input_builder = GATE_INIT_EMPTY;
826
827 do
828 {
829 /* encode input as base64 string */
830 gate_strbuilder_create(&input_builder, data_len * 4 / 3 + 3);
831 gate_base64_encode(&input_raw, &input_builder);
832 gate_strbuilder_to_string(&input_builder, &input_b64);
833
834 ret = winrm_prepare_request(client, &msg_id, &target_url);
835 GATE_BREAK_IF_FAILED(ret);
836
837 if (NULL == winrm_shell_sendinput_request(&soap_request, &target_url, &msg_id, shell_id, command_id, &input_b64))
838 {
839 ret = GATE_RESULT_OUTOFMEMORY;
840 break;
841 }
842
843 ret = winrm_send_soap_request(client, &soap_request, &status_code, &soap_response);
844 GATE_BREAK_IF_FAILED(ret);
845
846 ret = check_status_ok(status_code);
847 GATE_BREAK_IF_FAILED(ret);
848
849 ret = GATE_RESULT_OK;
850 } while (0);
851
852 gate_strbuilder_release(&input_builder);
853 gate_string_release(&input_b64);
854 gate_string_release(&soap_request);
855 gate_string_release(&soap_response);
856 gate_string_release(&target_url);
857 return ret;
858
859 }
860
861 static gate_size_t get_child_index_by_local_name(gate_xml_node_t const* parent, gate_string_t const* search_local_name, gate_size_t begin_index)
862 {
863 gate_size_t const count = gate_xml_node_children_count(parent);
864 gate_size_t ndx;
865 for (ndx = begin_index; ndx < count; ++ndx)
866 {
867 gate_xml_node_t const* const child = gate_xml_node_child_ptr(parent, ndx);
868 if (!child)
869 {
870 continue;
871 }
872 else
873 {
874 gate_string_t loc_name = GATE_STRING_INIT_EMPTY;
875 gate_result_t const result = gate_xml_parse_decompose_name(&child->tag, NULL, &loc_name);
876 if (GATE_SUCCEEDED(result))
877 {
878 gate_bool_t const tag_found = gate_string_equals(&loc_name, search_local_name);
879 gate_string_release(&loc_name);
880 if (tag_found)
881 {
882 return ndx;
883 }
884 }
885 }
886 }
887 return GATE_STR_NPOS;
888 }
889
890 static gate_xml_node_t const* get_child_ptr_by_local_name(gate_xml_node_t const* parent, gate_string_t const* search_local_name)
891 {
892 gate_size_t ndx = get_child_index_by_local_name(parent, search_local_name, 0);
893 if(ndx == GATE_STR_NPOS)
894 {
895 return NULL;
896 }
897 return gate_xml_node_child_ptr(parent, ndx);
898 }
899
900
901 static gate_result_t extract_received_output(gate_string_t const* xml,
902 gate_winrmclient_output_callback output_callback,
903 void* user_param)
904 {
905 gate_result_t ret = GATE_RESULT_FAILED;
906 gate_xml_doc_t xdoc = GATE_INIT_EMPTY;
907 gate_strbuilder_t outtext;
908 static gate_string_t const xtag_body = GATE_STRING_INIT_STATIC("Body");
909 static gate_string_t const xtag_receive = GATE_STRING_INIT_STATIC("ReceiveResponse");
910 static gate_string_t const xtag_stream = GATE_STRING_INIT_STATIC("Stream");
911 static gate_string_t const xtag_state = GATE_STRING_INIT_STATIC("CommandState");
912 static gate_string_t const xtag_exit = GATE_STRING_INIT_STATIC("ExitCode");
913 static gate_string_t const xattr_name = GATE_STRING_INIT_STATIC("Name");
914 static gate_string_t const xattr_end = GATE_STRING_INIT_STATIC("End");
915 static gate_string_t const xattr_state = GATE_STRING_INIT_STATIC("State");
916
917 do
918 {
919 gate_xml_node_t const* xroot = NULL;
920 gate_xml_node_t const* xbody = NULL;
921 gate_xml_node_t const* xrec = NULL;
922 gate_size_t next_stream_ndx = 0;
923 gate_bool_t continue_processing = true;
924
925 gate_strbuilder_create(&outtext, 32);
926
927 ret = gate_xml_doc_load_text(&xdoc, xml);
928 GATE_BREAK_IF_FAILED(ret);
929
930 ret = gate_xml_doc_root_element(&xdoc, &xroot);
931 GATE_BREAK_IF_FAILED(ret);
932
933 xbody = get_child_ptr_by_local_name(xroot, &xtag_body);
934 if (xbody == NULL)
935 {
936 ret = GATE_RESULT_INVALIDCONTENT;
937 break;
938 }
939 xrec = get_child_ptr_by_local_name(xbody, &xtag_receive);
940 if (xbody == NULL)
941 {
942 ret = GATE_RESULT_INVALIDCONTENT;
943 break;
944 }
945
946 gate_strbuilder_create(&outtext, 64);
947 for (next_stream_ndx = 0;
948 GATE_STR_NPOS != (next_stream_ndx = get_child_index_by_local_name(xrec, &xtag_stream, next_stream_ndx));
949 ++next_stream_ndx)
950 {
951 gate_blob_t chunk;
952 gate_result_t result;
953 gate_string_t attr_text;
954 gate_bool_t is_stderr = false;
955 gate_bool_t is_endofstream = false;
956 gate_xml_node_t const* xstream = gate_xml_node_child_ptr(xrec, next_stream_ndx);
957
958 result = gate_xml_node_attribute_by_name(xstream, &xattr_name, &attr_text);
959 if(GATE_SUCCEEDED(result))
960 {
961 is_stderr = gate_string_equals_str_ic(&attr_text, "stderr");
962 gate_string_release(&attr_text);
963 }
964
965 result = gate_xml_node_attribute_by_name(xstream, &xattr_end, &attr_text);
966 if(GATE_SUCCEEDED(result))
967 {
968 is_endofstream = gate_string_equals_str_ic(&attr_text, "true");
969 gate_string_release(&attr_text);
970 }
971
972 /* reset local output buffer */
973 gate_strbuilder_discard(&outtext, gate_strbuilder_length(&outtext));
974 result = gate_base64_decode(&xstream->content, &outtext);
975
976 if(GATE_SUCCEEDED(result) && (output_callback != NULL))
977 {
978 if(gate_strbuilder_length(&outtext) > 0)
979 {
980 /* notify output content */
981 gate_blob_create_static(&chunk, gate_strbuilder_ptr(&outtext, 0), gate_strbuilder_length(&outtext));
982 continue_processing = output_callback(
983 is_stderr ? GATE_WINRMCLIENT_OUTPUT_TYPE_STDERR : GATE_WINRMCLIENT_OUTPUT_TYPE_STDOUT,
984 &chunk, user_param);
985 gate_blob_release(&chunk);
986 if(!continue_processing)
987 {
988 break;
989 }
990 }
991 if(is_endofstream)
992 {
993 /* notify end of stream */
994 continue_processing = output_callback(
995 is_stderr ? GATE_WINRMCLIENT_OUTPUT_TYPE_STDERR : GATE_WINRMCLIENT_OUTPUT_TYPE_STDOUT,
996 NULL, user_param);
997 if(!continue_processing)
998 {
999 break;
1000 }
1001 }
1002 }
1003 } /* for */
1004 gate_strbuilder_release(&outtext);
1005
1006 if(continue_processing)
1007 {
1008 /* try to detect process-exit */
1009 gate_result_t result;
1010 gate_string_t attr_text;
1011 gate_xml_node_t const* xstate = get_child_ptr_by_local_name(xrec, &xtag_state);
1012 if(xstate == NULL)
1013 {
1014 break;
1015 }
1016
1017 /* command state was transmitted */
1018 result = gate_xml_node_attribute_by_name(xstate, &xattr_state, &attr_text);
1019 if(GATE_SUCCEEDED(result))
1020 {
1021 gate_bool_t process_terminated = gate_string_ends_with_str(&attr_text, "Done");
1022 if(process_terminated)
1023 {
1024 gate_blob_t chunk = GATE_INIT_EMPTY;
1025 gate_xml_node_t const* xexit = get_child_ptr_by_local_name(xstate, &xtag_exit);
1026 if(xexit != NULL)
1027 {
1028 gate_blob_create_static(&chunk, gate_string_ptr(&xexit->content, 0), gate_string_length(&xexit->content));
1029 }
1030 if((output_callback != NULL))
1031 {
1032 output_callback(GATE_WINRMCLIENT_OUTPUT_TYPE_EXIT, &chunk, user_param);
1033 }
1034 gate_blob_release(&chunk);
1035 }
1036 gate_string_release(&attr_text);
1037 }
1038 }
1039
1040 ret = GATE_RESULT_OK;
1041 } while (0);
1042
1043 gate_strbuilder_release(&outtext);
1044
1045 gate_xml_doc_destroy(&xdoc);
1046 return ret;
1047 }
1048
1049 static gate_bool_t check_timeout(gate_uint32_t status_code, gate_string_t const* response)
1050 {
1051 static gate_string_t const token_fault = GATE_STRING_INIT_STATIC("Fault>");
1052 static gate_string_t const token_timeout = GATE_STRING_INIT_STATIC("TimedOut</");
1053
1054 if(status_code < 500)
1055 {
1056 return false;
1057 }
1058 return gate_string_contains(response, &token_fault) && gate_string_contains(response, &token_timeout);
1059 }
1060
1061 gate_result_t gate_winrmclient_shell_command_receive(gate_winrmclient_t* client,
1062 gate_string_t const* shell_id,
1063 gate_string_t const* command_id,
1064 gate_uint32_t timeout_ms,
1065 gate_winrmclient_output_callback output_callback,
1066 void* user_param)
1067 {
1068 gate_result_t ret = GATE_RESULT_FAILED;
1069 gate_guid_t msg_id = GATE_INIT_EMPTY;
1070 gate_string_t target_url = GATE_STRING_INIT_EMPTY;
1071 gate_string_t soap_request = GATE_STRING_INIT_EMPTY;
1072 gate_string_t soap_response = GATE_STRING_INIT_EMPTY;
1073 gate_uint32_t status_code = 0;
1074
1075 do
1076 {
1077 ret = winrm_prepare_request(client, &msg_id, &target_url);
1078 GATE_BREAK_IF_FAILED(ret);
1079
1080 if (NULL == winrm_shell_receiveoutput_request(&soap_request, &target_url, &msg_id, shell_id, command_id, timeout_ms))
1081 {
1082 ret = GATE_RESULT_OUTOFMEMORY;
1083 break;
1084 }
1085
1086 ret = winrm_send_soap_request(client, &soap_request, &status_code, &soap_response);
1087 GATE_BREAK_IF_FAILED(ret);
1088
1089 if(check_timeout(status_code, &soap_response))
1090 {
1091 /* no output within specified timeout */
1092 ret = GATE_RESULT_OK;
1093 }
1094 else
1095 {
1096 ret = check_status_ok(status_code);
1097 GATE_BREAK_IF_FAILED(ret);
1098
1099 ret = extract_received_output(&soap_response, output_callback, user_param);
1100 }
1101 } while (0);
1102
1103 gate_string_release(&soap_request);
1104 gate_string_release(&soap_response);
1105 gate_string_release(&target_url);
1106 return ret;
1107
1108 }
1109
1110