1#
2# Copyright (C) 2010-2017 Samuel Abels
3# The MIT License (MIT)
4#
5# Permission is hereby granted, free of charge, to any person obtaining
6# a copy of this software and associated documentation files
7# (the "Software"), to deal in the Software without restriction,
8# including without limitation the rights to use, copy, modify, merge,
9# publish, distribute, sublicense, and/or sell copies of the Software,
10# and to permit persons to whom the Software is furnished to do so,
11# subject to the following conditions:
12#
13# The above copyright notice and this permission notice shall be
14# included in all copies or substantial portions of the Software.
15#
16# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23from Exscript import Account
24from Exscript.stdlib.util import secure_function
25
26
27@secure_function
28def authenticate(scope):
29    """
30    Looks for any username/password prompts on the current connection
31    and logs in using the login information that was passed to Exscript.
32    """
33    scope.get('__connection__').app_authenticate()
34    return True
35
36
37@secure_function
38def authenticate_user(scope, user=[None], password=[None]):
39    """
40    Like authenticate(), but logs in using the given user and password.
41    If a user and password are not given, the function uses the same
42    user and password that were used at the last login attempt; it is
43    an error if no such attempt was made before.
44
45    :type  user: string
46    :param user: A username.
47    :type  password: string
48    :param password: A password.
49    """
50    conn = scope.get('__connection__')
51    user = user[0]
52    if user is None:
53        conn.app_authenticate()
54    else:
55        account = Account(user, password[0])
56        conn.app_authenticate(account)
57    return True
58
59
60@secure_function
61def authorize(scope, password=[None]):
62    """
63    Looks for a password prompt on the current connection
64    and enters the given password.
65    If a password is not given, the function uses the same
66    password that was used at the last login attempt; it is
67    an error if no such attempt was made before.
68
69    :type  password: string
70    :param password: A password.
71    """
72    conn = scope.get('__connection__')
73    password = password[0]
74    if password is None:
75        conn.app_authorize()
76    else:
77        account = Account('', password)
78        conn.app_authorize(account)
79    return True
80
81
82@secure_function
83def auto_authorize(scope, password=[None]):
84    """
85    Executes a command on the remote host that causes an authorization
86    procedure to be started, then authorizes using the given password
87    in the same way in which authorize() works.
88    Depending on the detected operating system of the remote host the
89    following commands are started:
90
91      - on IOS, the "enable" command is executed.
92      - nothing on other operating systems yet.
93
94    :type  password: string
95    :param password: A password.
96    """
97    conn = scope.get('__connection__')
98    password = password[0]
99    if password is None:
100        conn.auto_app_authorize()
101    else:
102        account = Account('', password)
103        conn.auto_app_authorize(account)
104    return True
105
106
107@secure_function
108def autoinit(scope):
109    """
110    Make the remote host more script-friendly by automatically executing
111    one or more commands on it.
112    The commands executed depend on the currently used driver.
113    For example, the driver for Cisco IOS would execute the
114    following commands::
115
116        term len 0
117        term width 0
118    """
119    scope.get('__connection__').autoinit()
120    return True
121
122
123@secure_function
124def close(scope):
125    """
126    Closes the existing connection with the remote host. This function is
127    rarely used, as normally Exscript closes the connection automatically
128    when the script has completed.
129    """
130    conn = scope.get('__connection__')
131    conn.close(1)
132    scope.define(__response__=conn.response)
133    return True
134
135
136@secure_function
137def exec_(scope, data):
138    """
139    Sends the given data to the remote host and waits until the host
140    has responded with a prompt.
141    If the given data is a list of strings, each item is sent, and
142    after each item a prompt is expected.
143
144    This function also causes the response of the command to be stored
145    in the built-in __response__ variable.
146
147    :type  data: string
148    :param data: The data that is sent.
149    """
150    conn = scope.get('__connection__')
151    response = []
152    for line in data:
153        conn.send(line)
154        conn.expect_prompt()
155        response += conn.response.split('\n')[1:]
156    scope.define(__response__=response)
157    return True
158
159
160@secure_function
161def execline(scope, data):
162    """
163    Like exec(), but appends a newline to the command in data before sending
164    it.
165
166    :type  data: string
167    :param data: The data that is sent.
168    """
169    conn = scope.get('__connection__')
170    response = []
171    for line in data:
172        conn.execute(line)
173        response += conn.response.split('\n')[1:]
174    scope.define(__response__=response)
175    return True
176
177
178@secure_function
179def guess_os(scope):
180    """
181    Guesses the operating system of the connected host.
182
183    The recognition is based on the past conversation that has happened
184    on the host; Exscript looks for known patterns and maps them to specific
185    operating systems.
186
187    :rtype:  string
188    :return: The operating system.
189    """
190    conn = scope.get('__connection__')
191    return [conn.guess_os()]
192
193
194@secure_function
195def send(scope, data):
196    """
197    Like exec(), but does not wait for a response of the remote host after
198    sending the command.
199
200    :type  data: string
201    :param data: The data that is sent.
202    """
203    conn = scope.get('__connection__')
204    for line in data:
205        conn.send(line)
206    return True
207
208
209@secure_function
210def sendline(scope, data):
211    """
212    Like execline(), but does not wait for a response of the remote host after
213    sending the command.
214
215    :type  data: string
216    :param data: The data that is sent.
217    """
218    conn = scope.get('__connection__')
219    for line in data:
220        conn.send(line + '\r')
221    return True
222
223
224@secure_function
225def wait_for(scope, prompt):
226    """
227    Waits until the response of the remote host contains the given pattern.
228
229    :type  prompt: regex
230    :param prompt: The prompt pattern.
231    """
232    conn = scope.get('__connection__')
233    conn.expect(prompt)
234    scope.define(__response__=conn.response)
235    return True
236
237
238@secure_function
239def set_prompt(scope, prompt=None):
240    """
241    Defines the pattern that is recognized at any future time when Exscript
242    needs to wait for a prompt.
243    In other words, whenever Exscript waits for a prompt, it searches the
244    response of the host for the given pattern and continues as soon as the
245    pattern is found.
246
247    Exscript waits for a prompt whenever it sends a command (unless the send()
248    method was used). set_prompt() redefines as to what is recognized as a
249    prompt.
250
251    :type  prompt: regex
252    :param prompt: The prompt pattern.
253    """
254    conn = scope.get('__connection__')
255    conn.set_prompt(prompt)
256    return True
257
258
259@secure_function
260def set_error(scope, error_re=None):
261    """
262    Defines a pattern that, whenever detected in the response of the remote
263    host, causes an error to be raised.
264
265    In other words, whenever Exscript waits for a prompt, it searches the
266    response of the host for the given pattern and raises an error if the
267    pattern is found.
268
269    :type  error_re: regex
270    :param error_re: The error pattern.
271    """
272    conn = scope.get('__connection__')
273    conn.set_error_prompt(error_re)
274    return True
275
276
277@secure_function
278def set_timeout(scope, timeout):
279    """
280    Defines the time after which Exscript fails if it does not receive a
281    prompt from the remote host.
282
283    :type  timeout: int
284    :param timeout: The timeout in seconds.
285    """
286    conn = scope.get('__connection__')
287    conn.set_timeout(int(timeout[0]))
288    return True
289