1# Copyright (c) 2012 The Chromium Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5"""Class to control the Belkin F9K router."""
6
7import logging
8import urlparse
9import time
10import dynamic_ap_configurator
11import ap_spec
12from selenium.webdriver.common.keys import Keys
13from selenium.common.exceptions import WebDriverException
14from selenium.common.exceptions import TimeoutException as \
15    SeleniumTimeoutException
16
17
18class BelkinF9KAPConfigurator(
19        dynamic_ap_configurator.DynamicAPConfigurator):
20    """Class to configure Blekin f9k1002v4 router."""
21
22
23    def __init__(self, ap_config):
24        super(BelkinF9KAPConfigurator, self).__init__(ap_config)
25        self._dhcp_delay = 30
26
27
28    security_popup = '//select[@name="security_type"]'
29
30    def _security_alert(self, alert):
31        text = alert.text
32        if 'Invalid character' in text:
33            alert.accept()
34        elif 'It is recommended to use WPA/WPA2 when WPS is enabled' in text:
35            alert.accept()
36        elif 'After changing to 11g mode' in text:
37            alert.accept()
38        elif 'After changing to 11b&g&n or 11n mode' in text:
39            alert.accept()
40        else:
41            alert.accept()
42            raise RuntimeError('Unhandeled modal dialog. %s' % text)
43
44
45    def open_new_tab(self):
46         """Re-Opens tab on the browser"""
47         body = self.driver.find_element_by_tag_name("body")
48         body.send_keys(Keys.CONTROL + 't')
49
50
51    def _login(self):
52        """Opens the login page and logs in using the password.
53           We need to login before doing any other change to make sure that
54           we have access to the router."""
55        page_url = urlparse.urljoin(self.admin_interface_url,'login.stm')
56        xpath = '//input[@name="pws"]'
57        try:
58            self.open_new_tab()
59            self.get_url(page_url, page_title='login.stm')
60            self.wait_for_object_by_xpath(xpath, wait_time=10)
61        except WebDriverException as e:
62            logging.info("Page did not load or %s", str(e))
63            self.driver.refresh()
64        self.wait_for_object_by_xpath(xpath, wait_time=10)
65        self.set_content_of_text_field_by_xpath('password', xpath,
66                                                abort_check=True)
67        self.click_button_by_xpath('//input[@value="Submit"]',
68                                   alert_handler=self._security_alert)
69
70
71    def get_supported_bands(self):
72        return [{'band': ap_spec.BAND_2GHZ,
73                 'channels': ['Auto', 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]}]
74
75
76    def get_supported_modes(self):
77        return [{'band': ap_spec.BAND_2GHZ,
78                 'modes': [ap_spec.MODE_G, ap_spec.MODE_N,
79                           ap_spec.MODE_B | ap_spec.MODE_G | ap_spec.MODE_N]}]
80
81
82    def get_number_of_pages(self):
83        return 2
84
85
86    def is_security_mode_supported(self, security_mode):
87        """
88        Returns if a given security_type is supported.
89
90        @param security_mode: one security modes defined in the APSpec
91
92        @return True if the security mode is supported; False otherwise.
93
94        """
95        return security_mode in (ap_spec.SECURITY_TYPE_DISABLED,
96                                 ap_spec.SECURITY_TYPE_WPAPSK,
97                                 ap_spec.SECURITY_TYPE_WPA2PSK,
98                                 ap_spec.SECURITY_TYPE_WEP)
99
100
101    def navigate_to_page(self, page_number):
102        """
103        Navigates to the page corresponding to the given page number.
104
105        This method performs the translation between a page number and a url to
106        load. This is used internally by apply_settings.
107
108        @param page_number: page number of the page to load
109
110        """
111        self._login()
112        if page_number == 1:
113            page_url = urlparse.urljoin(self.admin_interface_url,
114                                        'wireless_id.stm')
115            self.get_url(page_url, page_title='wireless_id')
116            self.wait_for_object_by_xpath('//input[@name="ssid"]')
117        elif page_number == 2:
118            page_url = urlparse.urljoin(self.admin_interface_url,
119                                        'wireless_e.stm')
120            try:
121                self.get_url(page_url, page_title='wireless')
122                self.wait_for_object_by_xpath(self.security_popup)
123            except (WebDriverException, SeleniumTimeoutException), e:
124                message = str(e)
125                if 'Timed out receiving message from renderer' in message:
126                    self.driver.refresh()
127                    self.wait_for_object_by_xpath(self.security_popup)
128                elif (any(alert in message for alert in
129                    ['unexpected alert open', 'An open modal dialog blocked'])):
130                    self._security_alert(self.driver.switch_to_alert())
131                else:
132                    raise RuntimeError(message)
133        else:
134            raise RuntimeError('Invalid page number passed. Number of pages '
135                               '%d, page value sent was %d' %
136                               (self.get_number_of_pages(), page_number))
137
138
139    def save_page(self, page_number):
140        """Save changes and logout from the router.
141
142        @param page_number: the page number to save as an integer.
143
144        """
145        self.click_button_by_xpath('//input[@type="submit" and '
146                                   '@value="Apply Changes"]',
147                                   alert_handler=self._security_alert)
148        try:
149            self.wait_for_object_by_xpath_to_vanish('//input[@name= \
150                                                    "timeRemain]"',wait_time=30)
151            self.wait.until(lambda _:'setup.htm' in self.driver.title)
152        except WebDriverException, e:
153            logging.info("Driver title page did not load or %s", str(e))
154            self.open_new_tab()
155        finally:
156            self.restore_default_wait_time()
157
158
159    def set_ssid(self, ssid):
160        self.add_item_to_command_list(self._set_ssid, (ssid,), 1, 900)
161
162
163    def _set_ssid(self, ssid):
164        xpath = '//input[@name="ssid"]'
165        self.set_content_of_text_field_by_xpath(ssid, xpath, abort_check=True)
166        self._ssid = ssid
167
168
169    def set_channel(self, channel):
170        self.add_item_to_command_list(self._set_channel, (channel,), 1, 900)
171
172
173    def _set_channel(self, channel):
174        position = self._get_channel_popup_position(channel)
175        channel_choices = ['Auto', '1', '2', '3', '4', '5', '6', '7', '8',
176                           '9', '10', '11']
177        xpath = '//select[@name="wchan"]'
178        self.select_item_from_popup_by_xpath(channel_choices[position], xpath)
179
180
181    def set_mode(self, mode):
182        self.add_item_to_command_list(self._set_mode, (mode,), 1, 900)
183
184
185    def _set_mode(self, mode):
186        mode_mapping = {ap_spec.MODE_G: '802.11g',
187                        ap_spec.MODE_N: '802.11n',
188                        ap_spec.MODE_B | ap_spec.MODE_G | ap_spec.MODE_N:
189                        '802.11b&802.11g&802.11n'}
190        mode_name = mode_mapping.get(mode)
191        if not mode_name:
192            raise RuntimeError('The mode %d not supported by router %s. ',
193                               hex(mode), self.name)
194        xpath = '//select[@name="wbr"]'
195        self.wait_for_object_by_xpath(xpath)
196        while self.number_of_items_in_popup_by_xpath(xpath) < 3:
197            time.sleep(0.25)
198        self.select_item_from_popup_by_xpath(mode_name, xpath,
199                                             wait_for_xpath=None,
200                                             alert_handler=self._security_alert)
201
202
203    def set_ch_width(self, channel_width):
204        """
205        Adjusts the channel width.
206
207        @param channel_width: the channel width
208        """
209        self.add_item_to_command_list(self._set_ch_width,(channel_width,),
210                                      1, 900)
211
212
213    def _set_ch_width(self, channel_width):
214        channel_choice = ['20MHz', '20/40MHz']
215        xpath = '//select[@name="bandwidth"]'
216        self.select_item_from_popup_by_xpath(channel_choice[channel_width],
217                                             xpath)
218
219
220    def set_radio(self, enabled=True):
221        logging.debug('This router (%s) does not support radio', self.name)
222        return None
223
224
225    def set_band(self, band):
226        logging.debug('This router %s does not support multiple bands.',
227                      self.name)
228        return None
229
230
231    def set_security_disabled(self):
232        self.add_item_to_command_list(self._set_security_disabled, (), 2, 1000)
233
234
235    def _set_security_disabled(self):
236        self.select_item_from_popup_by_xpath('Disabled',
237                                             self.security_popup,
238                                             alert_handler=self._security_alert)
239
240
241    def set_security_wep(self, key_value, authentication):
242        self.add_item_to_command_list(self._set_security_wep,
243                                      (key_value, authentication), 2, 1000)
244
245
246    def _set_security_wep(self, key_value, authentication):
247        text_field = '//input[@name="passphrase"]'
248        try:
249            self.select_item_from_popup_by_xpath('64bit WEP',
250                    self.security_popup, wait_for_xpath=text_field,
251                    alert_handler=self._security_alert)
252        except WebDriverException, e:
253            message = str(e)
254            if message.find('An open modal dialog blocked') == -1:
255               raise RuntimeError(message)
256               return
257            self._security_alert(self.driver.switch_to_alert())
258        self.set_content_of_text_field_by_xpath(key_value, text_field,
259                                                abort_check=True)
260        self.click_button_by_xpath('//input[@class="submitBtn" and '
261                                   '@value="Generate"]',
262                                   alert_handler=self._security_alert)
263
264
265    def set_security_wpapsk(self, security, shared_key, update_interval=None):
266        self.add_item_to_command_list(self._set_security_wpapsk,
267                                      (security, shared_key, update_interval),
268                                       2, 900)
269
270
271    def _set_security_wpapsk(self, security, shared_key, update_interval=None):
272        key_field = '//input[@name="wpa_key_pass"]'
273        psk = '//select[@name="authentication"]'
274        self.select_item_from_popup_by_xpath('WPA/WPA2-Personal (PSK)',
275                                             self.security_popup,
276                                             wait_for_xpath=key_field,
277                                             alert_handler=self._security_alert)
278        auth_type = 'WPA-PSK'
279        if security == ap_spec.SECURITY_TYPE_WPA2PSK:
280            auth_type = 'WPA2-PSK'
281        self.select_item_from_popup_by_xpath(auth_type, psk,
282                                             wait_for_xpath=key_field,
283                                             alert_handler=self._security_alert)
284        self.set_content_of_text_field_by_xpath(shared_key, key_field,
285                                                abort_check=True)
286
287
288    def is_visibility_supported(self):
289        """
290        Returns if AP supports setting the visibility (SSID broadcast).
291
292        @return True if supported; False otherwise.
293        """
294        return False
295
296
297    def is_update_interval_supported(self):
298        """
299        Returns True if setting the PSK refresh interval is supported.
300
301        @return True is supported; False otherwise
302        """
303        return False
304