diff --git a/winrm/protocol.py b/winrm/protocol.py index 3ec634a1..e3699887 100644 --- a/winrm/protocol.py +++ b/winrm/protocol.py @@ -395,6 +395,39 @@ def cleanup_command(self, shell_id, command_id): # TODO change assert into user-friendly exception assert uuid.UUID(relates_to.replace('uuid:', '')) == message_id + def send_command_input(self, shell_id, command_id, stdin_input, end=False): + """ + Send input to the given shell and command. + @param string shell_id: The shell id on the remote machine. + See #open_shell + @param string command_id: The command id on the remote machine. + See #run_command + @param string stdin_input: The input unicode string or byte string to be sent. + @param bool end: Boolean value which will close the stdin stream. If end=True then the stdin pipe to the + remotely running process will be closed causing the next read by the remote process to stdin to return a + EndOfFile error; the behavior of each process when this error is encountered is defined by the process, but most + processes ( like CMD and powershell for instance) will just exit. Setting this value to 'True' means that no + more input will be able to be sent to the process and attempting to do so should result in an error. + @return: None + """ + if isinstance(stdin_input, text_type): + stdin_input = stdin_input.encode("437") + req = {'env:Envelope': self._get_soap_header( + resource_uri='http://schemas.microsoft.com/wbem/wsman/1/windows/shell/cmd', # NOQA + action='http://schemas.microsoft.com/wbem/wsman/1/windows/shell/Send', # NOQA + shell_id=shell_id)} + stdin_envelope = req['env:Envelope'].setdefault('env:Body', {}).setdefault( + 'rsp:Send', {}).setdefault('rsp:Stream', {}) + stdin_envelope['@CommandId'] = command_id + stdin_envelope['@Name'] = 'stdin' + if end: + stdin_envelope['@End'] = "true" + else: + stdin_envelope['@End'] = "false" + stdin_envelope['@xmlns:rsp'] = 'http://schemas.microsoft.com/wbem/wsman/1/windows/shell' + stdin_envelope['#text'] = base64.b64encode(stdin_input) + self.send_message(xmltodict.unparse(req)) + def get_command_output(self, shell_id, command_id): """ Get the Output of the given shell and command diff --git a/winrm/tests/conftest.py b/winrm/tests/conftest.py index b7ab2781..7f406eef 100644 --- a/winrm/tests/conftest.py +++ b/winrm/tests/conftest.py @@ -311,6 +311,188 @@ """ +run_cmd_req_input = """\ + + + + http://windows-host:5985/wsman + + http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous + + 153600 + uuid:11111111-1111-1111-1111-111111111111 + + + PT20S + http://schemas.microsoft.com/wbem/wsman/1/windows/shell/cmd + http://schemas.microsoft.com/wbem/wsman/1/windows/shell/Command + + 11111111-1111-1111-1111-111111111113 + + + TRUE + FALSE + + + + + cmd + + +""" + + +run_cmd_req_input_response = """\ + + + +http://schemas.microsoft.com/wbem/wsman/1/windows/shell/CommandResponse +uuid:11111111-1111-1111-1111-111111111114 +http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous +uuid:11111111-1111-1111-1111-111111111112 + + + +11111111-1111-1111-1111-111111111111 + + + +""" + +run_cmd_send_input = """\ + + + + http://windows-host:5985/wsman + + http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous + + 153600 + uuid:11111111-1111-1111-1111-111111111111 + + + PT20S + http://schemas.microsoft.com/wbem/wsman/1/windows/shell/cmd + http://schemas.microsoft.com/wbem/wsman/1/windows/shell/Send + + 11111111-1111-1111-1111-111111111113 + + + + + ZWNobyAiaGVsbG8gd29ybGQiICYmIGV4aXQNCg== + + + +""" + +run_cmd_send_input_response = """\ + + + +http://schemas.microsoft.com/wbem/wsman/1/windows/shell/SendResponse +uuid:72371E37-E073-474B-B4BA-6559D8D94632 +http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous +uuid:9c3de121-c3a4-452b-8f82-36b84e25b7fe + + + + + +""" + +run_cmd_send_input_get_output = """\ + + + +http://windows-host:5985/wsman + +http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous + +153600 +uuid:11111111-1111-1111-1111-111111111111 + + +PT20S +http://schemas.microsoft.com/wbem/wsman/1/windows/shell/cmd +http://schemas.microsoft.com/wbem/wsman/1/windows/shell/Receive + +11111111-1111-1111-1111-111111111113 + + + + +stdout stderr + + + +""" + +run_cmd_send_input_get_output_response = """\ + + + + http://schemas.microsoft.com/wbem/wsman/1/windows/shell/ReceiveResponse + uuid:6468086A-377E-4BE3-AC71-1155F0F1D4E1 + http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous + uuid:02f258b6-186f-4ac0-adc3-51550a131e64 + + + + TWljcm9zb2Z0IFdpbmRvd3MgW1ZlcnNpb24gMTAuMC4xNzc2My4xMDdd + DQooYykgMjAxOCBNaWNyb3NvZnQgQ29ycG9yYXRpb24uIEFsbCByaWdodHMgcmVzZXJ2ZWQuDQoNCkM6XFVzZXJzXHJ3ZWJlcj5lY2hvIGhlbGxvIHdvcmxkICYmIGV4aXQNCmhlbGxvIHdvcmxkIA0K + + + + 0 + + + + +""" + +stdin_cmd_cleanup = """\ + + + + http://windows-host:5985/wsman + + http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous + + 153600 + uuid:11111111-1111-1111-1111-111111111111 + + + PT20S + http://schemas.microsoft.com/wbem/wsman/1/windows/shell/cmd + http://schemas.microsoft.com/wbem/wsman/1/windows/shell/Signal + + 11111111-1111-1111-1111-111111111113 + + + + + http://schemas.microsoft.com/wbem/wsman/1/windows/shell/signal/terminate + + + +""" + +stdin_cmd_cleanup_response = """\ + + + + http://schemas.microsoft.com/wbem/wsman/1/windows/shell/SignalResponse + uuid:8A875405-3494-4400-A988-B47A563922E7 + http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous + uuid:11111111-1111-1111-1111-111111111111 + + + + + +""" + def sort_dict(ordered_dict): items = sorted(ordered_dict.items(), key=lambda x: x[0]) ordered_dict.clear() @@ -348,6 +530,14 @@ def send_message(self, message): return get_cmd_output_response elif xml_str_compare(message, get_cmd_ps_output_request % '2'): return get_ps_output_response + elif xml_str_compare(message, run_cmd_req_input): + return run_cmd_req_input_response + elif xml_str_compare(message, run_cmd_send_input): + return run_cmd_send_input_response + elif xml_str_compare(message, run_cmd_send_input_get_output): + return run_cmd_send_input_get_output_response + elif xml_str_compare(message, stdin_cmd_cleanup): + return stdin_cmd_cleanup_response else: raise Exception('Message was not expected\n\n%s' % message) diff --git a/winrm/tests/test_protocol.py b/winrm/tests/test_protocol.py index bcea47ea..ee5a2aa7 100644 --- a/winrm/tests/test_protocol.py +++ b/winrm/tests/test_protocol.py @@ -41,6 +41,20 @@ def test_get_command_output(protocol_fake): protocol_fake.close_shell(shell_id) +def test_send_command_input(protocol_fake): + shell_id = protocol_fake.open_shell() + command_id = protocol_fake.run_command(shell_id, u'cmd') + protocol_fake.send_command_input(shell_id, command_id, u'echo "hello world" && exit\r\n') + std_out, std_err, status_code = protocol_fake.get_command_output( + shell_id, command_id) + assert status_code == 0 + assert b'hello world' in std_out + assert len(std_err) == 0 + + protocol_fake.cleanup_command(shell_id, command_id) + protocol_fake.close_shell(shell_id) + + def test_set_timeout_as_sec(): protocol = Protocol('endpoint', username='username',