1# Licensed to the Apache Software Foundation (ASF) under one or more
2# contributor license agreements.  See the NOTICE file distributed with
3# this work for additional information regarding copyright ownership.
4# The ASF licenses this file to You under the Apache License, Version 2.0
5# (the "License"); you may not use this file except in compliance with
6# the License.  You may obtain a copy of the License at
7#
8#     http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15import re
16
17from libcloud.common.base import ConnectionUserAndKey
18from libcloud.common.base import Response
19from libcloud.common.types import ProviderError
20
21
22OK_CODES = ['200', '211', '212', '213']
23ERROR_CODES = ['401', '403', '405', '406', '407', '408', '409', '410', '411',
24               '412', '413', '414', '450', '451']
25
26
27class WorldWideDNSException(ProviderError):
28    def __init__(self, value, http_code, code, driver=None):
29        self.code = code
30        super(WorldWideDNSException, self).__init__(value, http_code, driver)
31
32
33class SuspendedAccount(WorldWideDNSException):
34    def __init__(self, http_code, driver=None):
35        value = "Login ID you supplied is SUSPENDED, you need to renew" + \
36                " your account"
37        super(SuspendedAccount, self).__init__(value, http_code, 401,
38                                               driver)
39
40
41class LoginOrPasswordNotMatch(WorldWideDNSException):
42    def __init__(self, http_code, driver=None):
43        value = "Login ID and/or Password you supplied is not on file or" + \
44                " does not match"
45        super(LoginOrPasswordNotMatch, self).__init__(value, http_code, 403,
46                                                      driver)
47
48
49class NonExistentDomain(WorldWideDNSException):
50    def __init__(self, http_code, driver=None):
51        value = "Domain name supplied is not in your account"
52        super(NonExistentDomain, self).__init__(value, http_code, 405,
53                                                driver)
54
55
56class CouldntRemoveDomain(WorldWideDNSException):
57    def __init__(self, http_code, driver=None):
58        value = "Error occured removing domain from name server, try again"
59        super(CouldntRemoveDomain, self).__init__(value, http_code, 406,
60                                                  driver)
61
62
63class LimitExceeded(WorldWideDNSException):
64    def __init__(self, http_code, driver=None):
65        value = "Your limit was exceeded, you need to upgrade your account"
66        super(LimitExceeded, self).__init__(value, http_code, 407,
67                                            driver)
68
69
70class ExistentDomain(WorldWideDNSException):
71    def __init__(self, http_code, driver=None):
72        value = "Domain already exists on our servers"
73        super(ExistentDomain, self).__init__(value, http_code, 408,
74                                             driver)
75
76
77class DomainBanned(WorldWideDNSException):
78    def __init__(self, http_code, driver=None):
79        value = "Domain is listed in DNSBL and is banned from our servers"
80        super(DomainBanned, self).__init__(value, http_code, 409,
81                                           driver)
82
83
84class InvalidDomainName(WorldWideDNSException):
85    def __init__(self, http_code, driver=None):
86        value = "Invalid domain name"
87        super(InvalidDomainName, self).__init__(value, http_code, 410,
88                                                driver)
89
90
91class ErrorOnReloadInNameServer(WorldWideDNSException):
92    def __init__(self, server, http_code, driver=None):
93        if server == 1:
94            value = "Name server #1 kicked an error on reload, contact support"
95            code = 411
96        elif server == 2:
97            value = "Name server #2 kicked an error on reload, contact support"
98            code = 412
99        elif server == 3:
100            value = "Name server #3 kicked an error on reload, contact support"
101            code = 413
102        super(ErrorOnReloadInNameServer, self).__init__(value, http_code, code,
103                                                        driver)
104
105
106class NewUserNotValid(WorldWideDNSException):
107    def __init__(self, http_code, driver=None):
108        value = "New userid is not valid"
109        super(NewUserNotValid, self).__init__(value, http_code, 414,
110                                              driver)
111
112
113class CouldntReachNameServer(WorldWideDNSException):
114    def __init__(self, http_code, driver=None):
115        value = "Couldn't reach the name server, try again later"
116        super(CouldntReachNameServer, self).__init__(value, http_code, 450,
117                                                     driver)
118
119
120class NoZoneFile(WorldWideDNSException):
121    def __init__(self, http_code, driver=None):
122        value = "No zone file in the name server queried"
123        super(NoZoneFile, self).__init__(value, http_code, 451,
124                                         driver)
125
126
127ERROR_CODE_TO_EXCEPTION_CLS = {
128    '401': SuspendedAccount,
129    '403': LoginOrPasswordNotMatch,
130    '405': NonExistentDomain,
131    '406': CouldntRemoveDomain,
132    '407': LimitExceeded,
133    '408': ExistentDomain,
134    '409': DomainBanned,
135    '410': InvalidDomainName,
136    '411': ErrorOnReloadInNameServer,
137    '412': ErrorOnReloadInNameServer,
138    '413': ErrorOnReloadInNameServer,
139    '414': NewUserNotValid,
140    '450': CouldntReachNameServer,
141    '451': NoZoneFile,
142}
143
144
145class WorldWideDNSResponse(Response):
146
147    def parse_body(self):
148        """
149        Parse response body.
150
151        :return: Parsed body.
152        :rtype: ``str``
153        """
154        if self._code_response(self.body):
155            codes = re.split('\r?\n', self.body)
156            for code in codes:
157                if code in OK_CODES:
158                    continue
159                elif code in ERROR_CODES:
160                    exception = ERROR_CODE_TO_EXCEPTION_CLS.get(code)
161                    if code in ['411', '412', '413']:
162                        server = int(code[2])
163                        raise exception(server, self.status)
164                    raise exception(self.status)
165        return self.body
166
167    def _code_response(self, body):
168        """
169        Checks if the response body contains code status.
170
171        :rtype: ``bool``
172        """
173        available_response_codes = OK_CODES + ERROR_CODES
174        codes = re.split('\r?\n', body)
175        if codes[0] in available_response_codes:
176            return True
177        return False
178
179
180class WorldWideDNSConnection(ConnectionUserAndKey):
181    host = 'www.worldwidedns.net'
182    responseCls = WorldWideDNSResponse
183
184    def add_default_params(self, params):
185        """
186        Add parameters that are necessary for every request
187
188        This method adds ``NAME`` and ``PASSWORD`` to
189        the request.
190        """
191        params["NAME"] = self.user_id
192        params["PASSWORD"] = self.key
193
194        reseller_id = getattr(self, 'reseller_id', None)
195        if reseller_id:
196            params["ID"] = reseller_id
197
198        return params
199