1# Copyright (c) 2013 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
5import logging
6import urlparse
7import ap_spec
8import dynamic_ap_configurator
9from selenium.common.exceptions import WebDriverException
10from selenium.common.exceptions import TimeoutException as \
11    SeleniumTimeoutException
12
13
14class BelkinF9K1103APConfigurator(
15        dynamic_ap_configurator.DynamicAPConfigurator):
16    """Class to configure Belkin F9K1103 v1 (01c) router."""
17
18
19    def _security_alert(self, alert):
20        text = alert.text
21        if 'Invalid character' in text:
22            alert.accept()
23        elif 'It is recommended to use WPA/WPA2 when WPS is enabled' in text:
24            alert.accept()
25        else:
26            alert.accept()
27            raise RuntimeError('Unhandeled modal dialog. %s' % text)
28
29
30    def _login(self):
31        """Opens the login page and logs in using the password.
32           We need to login before doing any other change to make sure that
33           we have access to the router.
34        """
35        self.driver.delete_all_cookies()
36        page_url = urlparse.urljoin(self.admin_interface_url,'login.htm')
37        self.get_url(page_url)
38        self.driver.switch_to_default_content()
39        frame = self.driver.find_element_by_name('mainFrame')
40        self.driver.switch_to_frame(frame)
41        xpath = '//input[@name="ui_pws"]'
42        try:
43            self.set_content_of_text_field_by_xpath('password', xpath,
44                                                    abort_check=True)
45            self.click_button_by_id('412')
46        except WebDriverException, e:
47            element = self.driver.find_element_by_id('60')
48            if 'Duplicate Administrator' in element.text:
49                raise RuntimeError('Cannot login. Someone has already '
50                                   'logged into the router. ' + str(e))
51            else:
52                raise WebDriverException('Cannot login. ' + str(e))
53
54
55    def get_supported_bands(self):
56        return [{'band': ap_spec.BAND_2GHZ,
57                 'channels': ['Auto', 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]},
58                {'band': ap_spec.BAND_5GHZ,
59                 'channels': ['Auto', 149, 153, 157, 161, 165]}]
60
61
62    def get_supported_modes(self):
63        return [{'band': ap_spec.BAND_2GHZ,
64                 'modes': [ap_spec.MODE_G, ap_spec.MODE_N,
65                           ap_spec.MODE_B | ap_spec.MODE_G | ap_spec.MODE_N]},
66                {'band': ap_spec.BAND_5GHZ,
67                 'modes': [ap_spec.MODE_A, ap_spec.MODE_N,
68                           ap_spec.MODE_A | ap_spec.MODE_N]}]
69
70
71    def get_number_of_pages(self):
72        return 2
73
74
75    def is_security_mode_supported(self, security_mode):
76        """
77        Returns if a given security_type is supported.
78
79        @param security_mode: one security modes defined in the APSpec
80
81        @return True if the security mode is supported; False otherwise.
82
83        """
84        return security_mode in (ap_spec.SECURITY_TYPE_DISABLED,
85                                 ap_spec.SECURITY_TYPE_WPAPSK,
86                                 ap_spec.SECURITY_TYPE_WEP)
87
88
89    def navigate_to_page(self, page_number):
90        """
91        Navigates to the page corresponding to the given page number.
92
93        This method performs the translation between a page number and a url to
94        load. This is used internally by apply_settings.
95
96        @param page_number: page number of the page to load
97
98        """
99        self._login()
100        if page_number == 1:
101            page_url = urlparse.urljoin(self.admin_interface_url, 'wifi_id.htm')
102            self.get_url(page_url, page_title='Channel and SSID')
103            self.driver.switch_to_default_content()
104            frame = self.driver.find_element_by_name('mainFrame')
105            self.driver.switch_to_frame(frame)
106            self.wait_for_object_by_xpath('//input[@name="wifi_ssid"]')
107        elif page_number == 2:
108            page_url = urlparse.urljoin(self.admin_interface_url, 'wifi_e.htm')
109            try:
110                self.driver.get(page_url)
111                self.wait.until(lambda _:'index.htm' in self.driver.title)
112            except SeleniumTimeoutException, e:
113                # The security page does not load properly, hence we
114                # refresh if we don't get the intended page
115                try:
116                    self.driver.get(page_url)
117                    self.wait.until(lambda _:'index.htm' in self.driver.title)
118                except SeleniumTimeoutException, e:
119                    raise SeleniumTimeoutException('Page did not load. '
120                                                   + str(e))
121            try:
122                self.driver.switch_to_default_content()
123                frame = self.driver.find_element_by_name('mainFrame')
124                self.driver.switch_to_frame(frame)
125                self.wait_for_object_by_xpath('//select[@name=wl_authmod]')
126            except (WebDriverException, SeleniumTimeoutException), e:
127                message = str(e)
128                if (not any(alert in message for alert in [
129                    'unexpected alert open', 'An open modal dialog blocked'])):
130                    raise RuntimeError(message)
131                    return
132                self._security_alert(self.driver.switch_to_alert())
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="button" and '
146                                   '@value="Apply Changes"]',
147                                   alert_handler=self._security_alert)
148        self.set_wait_time(120)
149        try:
150            self.wait.until(lambda _:'Status' in self.driver.title)
151        except SeleniumTimeoutException, e:
152            try:
153                # The webpages of this router have a tendency to not load
154                # completely, hence we refresh before we raise an excpetion.
155                self.driver.refresh()
156                self.wait.until(lambda _:'Status' in self.driver.title)
157            except SeleniumTimeoutException, e:
158                raise SeleniumTimeoutException('The changes were not saved. '
159                                               '%s' % str(e))
160        finally:
161            self.restore_default_wait_time()
162
163
164    def set_ssid(self, ssid):
165        self.add_item_to_command_list(self._set_ssid, (ssid,), 1, 900)
166
167
168    def _set_ssid(self, ssid):
169        xpath = '//input[@name="wifi_ssid"]'
170        if self.current_band == ap_spec.BAND_5GHZ:
171            xpath = '//input[@name="wifi_ssid1"]'
172        self.set_content_of_text_field_by_xpath(ssid, xpath, abort_check=False)
173        self._ssid = ssid
174
175
176    def set_channel(self, channel):
177        self.add_item_to_command_list(self._set_channel, (channel,), 1, 900)
178
179
180    def _set_channel(self, channel):
181        position = self._get_channel_popup_position(channel)
182        channel_choices = ['Auto', '1', '2', '3', '4', '5', '6', '7', '8',
183                           '9', '10', '11']
184        xpath = '//select[@name="wchan"]'
185        if self.current_band == ap_spec.BAND_5GHZ:
186            xpath = '//select[@name="wchan1"]'
187            channel_choices = ['Auto', '149', '153', '157', '161', '165']
188        self.select_item_from_popup_by_xpath(channel_choices[position], xpath)
189
190
191    def set_mode(self, mode):
192        self.add_item_to_command_list(self._set_mode, (mode,), 1, 900)
193
194
195    def _set_mode(self, mode):
196        mode_mapping = {ap_spec.MODE_G: '802.11g', ap_spec.MODE_A: '802.11a',
197                        ap_spec.MODE_N: '802.11n',
198                        ap_spec.MODE_A | ap_spec.MODE_N: '802.11a & 802.11n',
199                        ap_spec.MODE_B | ap_spec.MODE_G | ap_spec.MODE_N:
200                        '802.11b & 802.11g & 802.11n'}
201        mode_name = mode_mapping.get(mode)
202        if not mode_name:
203            raise RuntimeError('The mode %d not supported by router %s. ',
204                               hex(mode), self.name)
205        xpath = '//select[@name="wbr"]'
206        if self.current_band == ap_spec.BAND_5GHZ:
207            xpath = '//select[@name="wbr1"]'
208        self.select_item_from_popup_by_xpath(mode_name, xpath,
209                                             wait_for_xpath=None,
210                                             alert_handler=self._security_alert)
211
212
213    def set_radio(self, enabled=True):
214        logging.debug('This router (%s) does not support radio',
215                      self.name)
216        return None
217
218
219    def set_band(self, band):
220        if band == ap_spec.BAND_2GHZ:
221            self.current_band = ap_spec.BAND_2GHZ
222        elif band == ap_spec.BAND_5GHZ:
223            self.current_band = ap_spec.BAND_5GHZ
224        else:
225            raise RuntimeError('Invalid band sent %s' % band)
226
227
228    def _set_security(self, option, wait_for_xpath=None):
229        popup = '//select[@name="wl_authmod"]'
230        if self.current_band == ap_spec.BAND_5GHZ:
231            popup = '//select[@name="wl_authmod1"]'
232        try:
233            self.select_item_from_popup_by_xpath(option, popup,
234                                                 wait_for_xpath=wait_for_xpath,
235                                                 alert_handler=
236                                                 self._security_alert)
237        except WebDriverException, e:
238            message = str(e)
239            if 'Selecting WEP Encryption will disable the WPS' in message:
240                alert = self.driver.switch_to_alert()
241                alert.accept()
242
243
244    def set_security_disabled(self):
245        self.add_item_to_command_list(self._set_security_disabled, (), 2, 1000)
246
247
248    def _set_security_disabled(self):
249        self._set_security('Disabled')
250
251
252    def set_security_wep(self, key_value, authentication):
253        self.add_item_to_command_list(self._set_security_wep,
254                                      (key_value, authentication), 2, 1000)
255
256
257    def _set_security_wep(self, key_value, authentication):
258        text_field = '//input[@name="wl_phrase"]'
259        generate_button = '//input[@id="811" and type="button" and \
260                          @onclick="Gen64bitkey(0)"]'
261        if self.current_band == ap_spec.BAND_5GHZ:
262            text_field = '//input[@name="wl1_phrase"]'
263            generate_button = '//input[@id="811" and type="button" and \
264                               @onclick="Gen64bitkey(1)"]'
265        self._set_security('64bit WEP', wait_for_xpath=text_field)
266        self.set_content_of_text_field_by_xpath(key_value, text_field,
267                                                abort_check=True)
268        self.click_button_by_id(generate_button)
269
270
271    def set_security_wpapsk(self, shared_key, update_interval=None):
272        self.add_item_to_command_list(self._set_security_wpapsk,
273                                      (shared_key, update_interval), 2, 900)
274
275
276    def _set_security_wpapsk(self, shared_key, update_interval=None):
277        auth_popup = '//select[@name="wl_auth"]'
278        psk_field = '//input[@name="wl_wpa_ks_txt"]'
279        if self.current_band == ap_spec.BAND_5GHZ:
280            auth_popup = '//select[@name="wl1_auth"]'
281            psk_field = '//input[@name="wl1_wpa_ks_txt"]'
282        self._set_security('WPA/WPA2-Personal (PSK)', wait_for_xpath=auth_popup)
283        self.select_item_from_popup_by_xpath('WPA-PSK', auth_popup,
284                                             wait_for_xpath=psk_field,
285                                             alert_handler=
286                                             self._security_alert)
287        self.set_content_of_text_field_by_xpath(shared_key, psk_field,
288                                                abort_check=True)
289
290
291    def is_visibility_supported(self):
292        """
293        Returns if AP supports setting the visibility (SSID broadcast).
294
295        @return True if supported; False otherwise.
296        """
297        return False
298
299
300    def is_update_interval_supported(self):
301        """
302        Returns True if setting the PSK refresh interval is supported.
303
304        @return True is supported; False otherwise
305        """
306        return False
307