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
7
8import ap_spec
9import dynamic_ap_configurator
10from selenium.common.exceptions import NoSuchElementException as \
11    SeleniumNoSuchElementException
12from selenium.common.exceptions import WebDriverException
13from selenium.common.exceptions import TimeoutException as \
14    SeleniumTimeoutException
15
16
17class BelkinF9K1102APConfigurator(
18        dynamic_ap_configurator.DynamicAPConfigurator):
19    """Base class for Belkin F9K1102 router."""
20
21
22    def _security_alert(self, alert):
23        text = alert.text
24        if "It is recommended to use WPA/WPA2 when WPS is enabled" in text:
25            alert.accept()
26        elif "Selecting WEP Encryption will disable the WPS" in text:
27            alert.accept()
28        elif "Changing your security type will disable WPS" in text:
29            alert.accept()
30        elif 'Key0 is not complete' in text:
31            raise RuntimeError('Got %s error. You should click the generate '
32                               'button to generate a key first' % alert.text)
33        else:
34            raise RuntimeError('Unknown alert dialog' + alert.text)
35
36
37    def get_supported_bands(self):
38        return [{'band': ap_spec.BAND_2GHZ,
39                 'channels': ['Auto', 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]},
40                {'band': ap_spec.BAND_5GHZ,
41                 'channels': ['Auto', 36, 40, 44, 48, 149, 153, 157, 161, 165]}]
42
43
44    def get_supported_modes(self):
45        return [{'band': ap_spec.BAND_2GHZ,
46                 'modes': [ap_spec.MODE_G,
47                           ap_spec.MODE_B | ap_spec.MODE_G | ap_spec.MODE_N]},
48                {'band': ap_spec.BAND_5GHZ,
49                 'modes': [ap_spec.MODE_A, ap_spec.MODE_A | ap_spec.MODE_N]}]
50
51
52    def get_number_of_pages(self):
53        return 2
54
55
56    def is_security_mode_supported(self, security_mode):
57        """
58        Returns if a given security_type is supported.
59
60        @param security_mode: one security modes defined in the APSpec
61
62        @return True if the security mode is supported; False otherwise.
63
64        """
65        return security_mode in (ap_spec.SECURITY_TYPE_DISABLED,
66                                 ap_spec.SECURITY_TYPE_WPAPSK,
67                                 ap_spec.SECURITY_TYPE_WEP)
68
69
70    def navigate_to_page(self, page_number):
71        """
72        Navigates to the page corresponding to the given page number.
73
74        This method performs the translation between a page number and a url to
75        load. This is used internally by apply_settings.
76
77        @param page_number: page number of the page to load
78
79        """
80        if page_number == 1:
81            page_url = urlparse.urljoin(self.admin_interface_url,
82                                        'wls_chan.html')
83            self._load_the_page(page_url, page_title="Network Name")
84        elif page_number == 2:
85            page_url = urlparse.urljoin(self.admin_interface_url,
86                                        'wls_sec.html')
87            self._load_the_page(page_url, page_title="Security")
88        else:
89            raise RuntimeError('Invalid page number passed. Number of pages '
90                               '%d, page value sent was %d' %
91                               (self.get_number_of_pages(), page_number))
92
93
94    def _load_the_page(self, page_url, page_title):
95        """
96        Load the given page and check if the title matches and we see the
97        save_button object.
98
99        @param page_url: The url of the page to load.
100        @param page_title: The expected title of the page after it loads.
101
102        """
103        try:
104            self.get_url(page_url, page_title)
105        except:
106            if 'Login' in self.driver.title:
107                self._login(page_url)
108        finally:
109            try:
110                self.wait_for_object_by_id('itsbutton1')
111            except SeleniumTimeoutException, e:
112                if 'Unable to find the object by xpath' in str(e):
113                    xpath = "//h2[contains(.,'Duplicate Administrator')]"
114                    if self.wait_for_object_by_xpath(xpath):
115                        raise RuntimeError('We got a Duplicate Admin error.')
116                else:
117                    raise RuntimeError(str(e))
118
119
120    def _login(self, page_url):
121        """
122        Login and wait for the object with obj_id to show up.
123
124        @param page_url: The url of the page to load.
125
126        """
127        try:
128            self.wait_for_object_by_id('p1210Password')
129        except SeleniumNoSuchElementException, e:
130            if (page_url in self.driver.current_url):
131                logging.debug("In the login method, but we are already "
132                              "logged in.")
133            else:
134                raise RuntimeError('We got a NoSuchElementException: ' + str(e))
135        self.set_content_of_text_field_by_id('password', 'p1210Password',
136                                             abort_check=True)
137        self.click_button_by_id('p1210a005')
138        self.wait_for_object_by_id('itsbutton1', wait_time=10)
139
140
141    def save_page(self, page_number):
142        """
143        Saves the given page.
144
145        @param page_number: Page number of the page to save.
146
147        """
148        button_id = 'itsbutton1'
149        self.click_button_by_id(button_id, alert_handler=self._security_alert)
150        self.set_wait_time(30)
151        try:
152            self.wait.until(lambda _:'Dashboard.htm' in self.driver.current_url)
153        except SeleniumTimeoutException, e:
154            if not (self.wait_for_object_by_id(button_id, wait_time=30)):
155                raise RuntimeError('We did not save the page. '
156                                   'We got a TimeoutException ' + str(e))
157        finally:
158            self.restore_default_wait_time()
159
160
161    def set_radio(self, enabled=True):
162        logging.debug('This router (%s) does not set the radio', self.name)
163        return None
164
165
166    def set_ssid(self, ssid):
167        self.add_item_to_command_list(self._set_ssid, (ssid,), 1, 900)
168
169
170    def _set_ssid(self, ssid):
171        xpath = '//input[@name="wl_ssid"]'
172        if self.current_band == ap_spec.BAND_5GHZ:
173            xpath = '//input[@name="wl_ssid_5g"]'
174        self.set_content_of_text_field_by_xpath(ssid, xpath, abort_check=False)
175        self._ssid = ssid
176
177
178    def set_channel(self, channel):
179        self.add_item_to_command_list(self._set_channel, (channel,), 1, 900)
180
181
182    def _set_channel(self, channel):
183        position = self._get_channel_popup_position(channel)
184        channel_choices = ['Auto', '1', '2', '3', '4', '5', '6', '7', '8',
185                           '9', '10', '11']
186        xpath = '//select[@name="wl_channel"]'
187        if self.current_band == ap_spec.BAND_5GHZ:
188            xpath = '//select[@name="wl_channel_5g"]'
189            channel_choices = ['Auto', '36', '40', '44', '48', '149', '153',
190                               '157', '161', '165']
191        self.select_item_from_popup_by_xpath(channel_choices[position], xpath)
192
193
194    def set_mode(self, mode, band=None):
195        self.add_item_to_command_list(self._set_mode, (mode, band,), 1, 900)
196
197
198    def _set_mode(self, mode, band=None):
199        mode_mapping = {ap_spec.MODE_G: '802.11g', ap_spec.MODE_A: '802.11a',
200                        ap_spec.MODE_N: '802.11n',
201                        ap_spec.MODE_A | ap_spec.MODE_N: '802.11a & 802.11n',
202                        ap_spec.MODE_B | ap_spec.MODE_G | ap_spec.MODE_N:
203                        '802.11b & 802.11g & 802.11n'}
204        mode_name = mode_mapping.get(mode)
205        if not mode_name:
206            raise RuntimeError('The mode %d not supported by router %s. ',
207                               hex(mode), self.name)
208        xpath = '//select[@name="wl_gmode"]'
209        if self.current_band == ap_spec.BAND_5GHZ:
210            xpath = '//select[@name="wl_gmode_5g"]'
211        self.select_item_from_popup_by_xpath(mode_name, xpath)
212
213
214    def set_band(self, band):
215        if band == ap_spec.BAND_2GHZ:
216            self.current_band = ap_spec.BAND_2GHZ
217        elif band == ap_spec.BAND_5GHZ:
218            self.current_band = ap_spec.BAND_5GHZ
219        else:
220            raise RuntimeError('Invalid band sent %s' % band)
221
222
223    def _set_security(self, option, wait_for_xpath=None):
224        popup = '//select[@name="security_mode"]'
225        if self.current_band == ap_spec.BAND_5GHZ:
226            popup = '//select[@name="security_mode_5g"]'
227        try:
228            self.select_item_from_popup_by_xpath(option, popup,
229                                                 wait_for_xpath=wait_for_xpath,
230                                                 alert_handler=
231                                                 self._security_alert)
232        except WebDriverException, e:
233            message = str(e)
234            if 'Selecting WEP Encryption will disable the WPS' in message:
235                alert = self.driver.switch_to_alert()
236                alert.accept()
237
238
239    def set_security_disabled(self):
240        self.add_item_to_command_list(self._set_security_disabled, (), 2, 1000)
241
242
243    def _set_security_disabled(self):
244        self._set_security('Off')
245
246
247    def set_security_wep(self, key_value, authentication):
248        self.add_item_to_command_list(self._set_security_wep,
249                                      (key_value, authentication), 2, 1000)
250
251
252    def _set_security_wep(self, key_value, authentication):
253        text_field = '//input[@name="passphrase_64"]'
254        generate_button = '//a[@id="wep64a_btn"]'
255        key_field = '//input[@name="ENC11"]'
256        if self.current_band == ap_spec.BAND_5GHZ:
257            text_field = '//input[@name="passphrase_64_5g"]'
258            generate_button = '//a[@id="wep64a_5_btn"]'
259            key_field = '//input[@name="ENC511"]'
260        self._set_security('64bit WEP', wait_for_xpath=text_field)
261        self.set_content_of_text_field_by_xpath(key_value, text_field,
262                                                abort_check=True)
263        self.click_button_by_xpath(generate_button, alert_handler=
264                                   self._security_alert)
265        field = self.wait_for_object_by_xpath(key_field)
266        self.wait.until(lambda _: field.get_attribute('value'))
267
268
269    def set_security_wpapsk(self, security, shared_key, update_interval=None):
270        self.add_item_to_command_list(self._set_security_wpapsk,
271                                      (security, shared_key, update_interval),
272                                       2, 900)
273
274
275    def _set_security_wpapsk(self, security, shared_key, update_interval=None):
276        auth_popup = '//select[@name="wl_sec_auth"]'
277        psk_field = '//input[@name="wl_wpa2_psk2"]'
278        if self.current_band == ap_spec.BAND_5GHZ:
279            auth_popup = '//select[@name="wl_sec_auth_5g"]'
280            psk_field = '//input[@name="wl_wpa2_psk2_5g"]'
281        auth_type = 'WPA-PSK'
282        if security == ap_spec.SECURITY_TYPE_WPA2PSK:
283            auth_type = 'WPA2-PSK'
284        self._set_security('WPA/WPA2-Personal(PSK)', wait_for_xpath=auth_popup)
285        self.select_item_from_popup_by_xpath(auth_type, auth_popup,
286                                             wait_for_xpath=psk_field,
287                                             alert_handler=
288                                             self._security_alert)
289        self.set_content_of_text_field_by_xpath(shared_key, psk_field,
290                                                abort_check=True)
291
292
293    def is_visibility_supported(self):
294        """
295        Returns if AP supports setting the visibility (SSID broadcast).
296
297        @return True if supported; False otherwise.
298        """
299        return False
300
301
302    def is_update_interval_supported(self):
303        """
304        Returns True if setting the PSK refresh interval is supported.
305
306        @return True is supported; False otherwise
307        """
308        return False
309