]> git.ipfire.org Git - ddns.git/blame - src/ddns/providers.py
Add Zoneedit as new provider.
[ddns.git] / src / ddns / providers.py
CommitLineData
f22ab085 1#!/usr/bin/python
3fdcb9d1
MT
2###############################################################################
3# #
4# ddns - A dynamic DNS client for IPFire #
5# Copyright (C) 2012 IPFire development team #
6# #
7# This program is free software: you can redistribute it and/or modify #
8# it under the terms of the GNU General Public License as published by #
9# the Free Software Foundation, either version 3 of the License, or #
10# (at your option) any later version. #
11# #
12# This program is distributed in the hope that it will be useful, #
13# but WITHOUT ANY WARRANTY; without even the implied warranty of #
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
15# GNU General Public License for more details. #
16# #
17# You should have received a copy of the GNU General Public License #
18# along with this program. If not, see <http://www.gnu.org/licenses/>. #
19# #
20###############################################################################
f22ab085 21
7399fc5b 22import logging
3b16fdb1 23import urllib2
d1cd57eb 24import xml.dom.minidom
7399fc5b
MT
25
26from i18n import _
27
f22ab085
MT
28# Import all possible exception types.
29from .errors import *
30
7399fc5b
MT
31logger = logging.getLogger("ddns.providers")
32logger.propagate = 1
33
f22ab085
MT
34class DDNSProvider(object):
35 INFO = {
36 # A short string that uniquely identifies
37 # this provider.
38 "handle" : None,
39
40 # The full name of the provider.
41 "name" : None,
42
43 # A weburl to the homepage of the provider.
44 # (Where to register a new account?)
45 "website" : None,
46
47 # A list of supported protocols.
48 "protocols" : ["ipv6", "ipv4"],
49 }
50
51 DEFAULT_SETTINGS = {}
52
53 def __init__(self, core, **settings):
54 self.core = core
55
56 # Copy a set of default settings and
57 # update them by those from the configuration file.
58 self.settings = self.DEFAULT_SETTINGS.copy()
59 self.settings.update(settings)
60
61 def __repr__(self):
62 return "<DDNS Provider %s (%s)>" % (self.name, self.handle)
63
64 def __cmp__(self, other):
65 return cmp(self.hostname, other.hostname)
66
67 @property
68 def name(self):
69 """
70 Returns the name of the provider.
71 """
72 return self.INFO.get("name")
73
74 @property
75 def website(self):
76 """
77 Returns the website URL of the provider
78 or None if that is not available.
79 """
80 return self.INFO.get("website", None)
81
82 @property
83 def handle(self):
84 """
85 Returns the handle of this provider.
86 """
87 return self.INFO.get("handle")
88
89 def get(self, key, default=None):
90 """
91 Get a setting from the settings dictionary.
92 """
93 return self.settings.get(key, default)
94
95 @property
96 def hostname(self):
97 """
98 Fast access to the hostname.
99 """
100 return self.get("hostname")
101
102 @property
103 def username(self):
104 """
105 Fast access to the username.
106 """
107 return self.get("username")
108
109 @property
110 def password(self):
111 """
112 Fast access to the password.
113 """
114 return self.get("password")
115
7399fc5b
MT
116 @property
117 def protocols(self):
118 return self.INFO.get("protocols")
119
46687828
SS
120 @property
121 def token(self):
122 """
123 Fast access to the token.
124 """
125 return self.get("token")
126
9da3e685
MT
127 def __call__(self, force=False):
128 if force:
129 logger.info(_("Updating %s forced") % self.hostname)
130
7399fc5b 131 # Check if we actually need to update this host.
9da3e685 132 elif self.is_uptodate(self.protocols):
7399fc5b
MT
133 logger.info(_("%s is already up to date") % self.hostname)
134 return
135
136 # Execute the update.
5f402f36
MT
137 self.update()
138
139 def update(self):
f22ab085
MT
140 raise NotImplementedError
141
7399fc5b
MT
142 def is_uptodate(self, protos):
143 """
144 Returns True if this host is already up to date
145 and does not need to change the IP address on the
146 name server.
147 """
148 for proto in protos:
149 addresses = self.core.system.resolve(self.hostname, proto)
150
151 current_address = self.get_address(proto)
152
153 if not current_address in addresses:
154 return False
155
156 return True
157
f22ab085
MT
158 def send_request(self, *args, **kwargs):
159 """
160 Proxy connection to the send request
161 method.
162 """
163 return self.core.system.send_request(*args, **kwargs)
164
165 def get_address(self, proto):
166 """
167 Proxy method to get the current IP address.
168 """
169 return self.core.system.get_address(proto)
170
171
3b16fdb1
SS
172class DDNSProviderAllInkl(DDNSProvider):
173 INFO = {
174 "handle" : "all-inkl.com",
175 "name" : "All-inkl.com",
176 "website" : "http://all-inkl.com/",
177 "protocols" : ["ipv4",]
178 }
179
180 # There are only information provided by the vendor how to
181 # perform an update on a FRITZ Box. Grab requried informations
182 # from the net.
183 # http://all-inkl.goetze.it/v01/ddns-mit-einfachen-mitteln/
184
185 url = "http://dyndns.kasserver.com"
186
187 def update(self):
188
189 # There is no additional data required so we directly can
190 # send our request.
191 try:
192 # Send request to the server.
193 response = self.send_request(self.url, username=self.username, password=self.password)
194
195 # Handle 401 HTTP Header (Authentication Error)
196 except urllib2.HTTPError, e:
197 if e.code == 401:
198 raise DDNSAuthenticationError
199
200 raise
201
202 # Get the full response message.
203 output = response.read()
204
205 # Handle success messages.
206 if output.startswith("good") or output.startswith("nochg"):
207 return
208
209 # If we got here, some other update error happened.
210 raise DDNSUpdateError
211
212
f3cf1f70
SS
213class DDNSProviderDHS(DDNSProvider):
214 INFO = {
215 "handle" : "dhs.org",
216 "name" : "DHS International",
217 "website" : "http://dhs.org/",
218 "protocols" : ["ipv4",]
219 }
220
221 # No information about the used update api provided on webpage,
222 # grabed from source code of ez-ipudate.
223 url = "http://members.dhs.org/nic/hosts"
224
5f402f36 225 def update(self):
f3cf1f70
SS
226 data = {
227 "domain" : self.hostname,
228 "ip" : self.get_address("ipv4"),
229 "hostcmd" : "edit",
230 "hostcmdstage" : "2",
231 "type" : "4",
232 }
233
234 # Send update to the server.
175c9b80 235 response = self.send_request(self.url, username=self.username, password=self.password,
f3cf1f70
SS
236 data=data)
237
238 # Handle success messages.
239 if response.code == 200:
240 return
241
242 # Handle error codes.
4caed6ed 243 elif response.code == 401:
f3cf1f70
SS
244 raise DDNSAuthenticationError
245
246 # If we got here, some other update error happened.
247 raise DDNSUpdateError
248
249
39301272
SS
250class DDNSProviderDNSpark(DDNSProvider):
251 INFO = {
252 "handle" : "dnspark.com",
253 "name" : "DNS Park",
254 "website" : "http://dnspark.com/",
255 "protocols" : ["ipv4",]
256 }
257
258 # Informations to the used api can be found here:
259 # https://dnspark.zendesk.com/entries/31229348-Dynamic-DNS-API-Documentation
260 url = "https://control.dnspark.com/api/dynamic/update.php"
261
5f402f36 262 def update(self):
39301272
SS
263 data = {
264 "domain" : self.hostname,
265 "ip" : self.get_address("ipv4"),
266 }
267
268 # Send update to the server.
175c9b80 269 response = self.send_request(self.url, username=self.username, password=self.password,
39301272
SS
270 data=data)
271
272 # Get the full response message.
273 output = response.read()
274
275 # Handle success messages.
276 if output.startswith("ok") or output.startswith("nochange"):
277 return
278
279 # Handle error codes.
280 if output == "unauth":
281 raise DDNSAuthenticationError
282 elif output == "abuse":
283 raise DDNSAbuseError
284 elif output == "blocked":
285 raise DDNSBlockedError
286 elif output == "nofqdn":
287 raise DDNSRequestError(_("No valid FQDN was given."))
288 elif output == "nohost":
289 raise DDNSRequestError(_("Invalid hostname specified."))
290 elif output == "notdyn":
291 raise DDNSRequestError(_("Hostname not marked as a dynamic host."))
292 elif output == "invalid":
293 raise DDNSRequestError(_("Invalid IP address has been sent."))
294
295 # If we got here, some other update error happened.
296 raise DDNSUpdateError
297
43b2cd59
SS
298
299class DDNSProviderDtDNS(DDNSProvider):
300 INFO = {
301 "handle" : "dtdns.com",
302 "name" : "DtDNS",
303 "website" : "http://dtdns.com/",
304 "protocols" : ["ipv4",]
305 }
306
307 # Information about the format of the HTTPS request is to be found
308 # http://www.dtdns.com/dtsite/updatespec
309 url = "https://www.dtdns.com/api/autodns.cfm"
310
43b2cd59
SS
311 def update(self):
312 data = {
313 "ip" : self.get_address("ipv4"),
314 "id" : self.hostname,
315 "pw" : self.password
316 }
317
318 # Send update to the server.
319 response = self.send_request(self.url, data=data)
320
321 # Get the full response message.
322 output = response.read()
323
324 # Remove all leading and trailing whitespace.
325 output = output.strip()
326
327 # Handle success messages.
328 if "now points to" in output:
329 return
330
331 # Handle error codes.
332 if output == "No hostname to update was supplied.":
333 raise DDNSRequestError(_("No hostname specified."))
334
335 elif output == "The hostname you supplied is not valid.":
336 raise DDNSRequestError(_("Invalid hostname specified."))
337
338 elif output == "The password you supplied is not valid.":
339 raise DDNSAuthenticationError
340
341 elif output == "Administration has disabled this account.":
342 raise DDNSRequestError(_("Account has been disabled."))
343
344 elif output == "Illegal character in IP.":
345 raise DDNSRequestError(_("Invalid IP address has been sent."))
346
347 elif output == "Too many failed requests.":
348 raise DDNSRequestError(_("Too many failed requests."))
349
350 # If we got here, some other update error happened.
351 raise DDNSUpdateError
352
353
bfed6701
SS
354class DDNSProviderDynDNS(DDNSProvider):
355 INFO = {
356 "handle" : "dyndns.org",
357 "name" : "Dyn",
358 "website" : "http://dyn.com/dns/",
359 "protocols" : ["ipv4",]
360 }
361
362 # Information about the format of the request is to be found
363 # http://http://dyn.com/support/developers/api/perform-update/
364 # http://dyn.com/support/developers/api/return-codes/
365 url = "https://members.dyndns.org/nic/update"
366
88f39629 367 def _prepare_request_data(self):
bfed6701
SS
368 data = {
369 "hostname" : self.hostname,
370 "myip" : self.get_address("ipv4"),
371 }
372
88f39629
SS
373 return data
374
375 def update(self):
376 data = self._prepare_request_data()
377
bfed6701 378 # Send update to the server.
88f39629
SS
379 response = self.send_request(self.url, data=data,
380 username=self.username, password=self.password)
bfed6701
SS
381
382 # Get the full response message.
383 output = response.read()
384
385 # Handle success messages.
386 if output.startswith("good") or output.startswith("nochg"):
387 return
388
389 # Handle error codes.
390 if output == "badauth":
391 raise DDNSAuthenticationError
392 elif output == "aduse":
393 raise DDNSAbuseError
394 elif output == "notfqdn":
395 raise DDNSRequestError(_("No valid FQDN was given."))
396 elif output == "nohost":
397 raise DDNSRequestError(_("Specified host does not exist."))
398 elif output == "911":
399 raise DDNSInternalServerError
400 elif output == "dnserr":
401 raise DDNSInternalServerError(_("DNS error encountered."))
402
403 # If we got here, some other update error happened.
404 raise DDNSUpdateError
405
406
3a8407fa
SS
407class DDNSProviderDynU(DDNSProviderDynDNS):
408 INFO = {
409 "handle" : "dynu.com",
410 "name" : "Dynu",
411 "website" : "http://dynu.com/",
412 "protocols" : ["ipv6", "ipv4",]
413 }
414
415
416 # Detailed information about the request and response codes
417 # are available on the providers webpage.
418 # http://dynu.com/Default.aspx?page=dnsapi
419
420 url = "https://api.dynu.com/nic/update"
421
422 def _prepare_request_data(self):
54d3efc8
MT
423 data = DDNSProviderDynDNS._prepare_request_data(self)
424
425 # This one supports IPv6
426 data.update({
3a8407fa 427 "myipv6" : self.get_address("ipv6"),
54d3efc8
MT
428 })
429
430 return data
3a8407fa
SS
431
432
ee071271
SS
433class DDNSProviderEasyDNS(DDNSProviderDynDNS):
434 INFO = {
435 "handle" : "easydns.com",
436 "name" : "EasyDNS",
437 "website" : "http://www.easydns.com/",
438 "protocols" : ["ipv4",]
439 }
440
441 # There is only some basic documentation provided by the vendor,
442 # also searching the web gain very poor results.
443 # http://mediawiki.easydns.com/index.php/Dynamic_DNS
444
445 url = "http://api.cp.easydns.com/dyn/tomato.php"
446
447
aa21a4c6
SS
448class DDNSProviderFreeDNSAfraidOrg(DDNSProvider):
449 INFO = {
450 "handle" : "freedns.afraid.org",
451 "name" : "freedns.afraid.org",
452 "website" : "http://freedns.afraid.org/",
453 "protocols" : ["ipv6", "ipv4",]
454 }
455
456 # No information about the request or response could be found on the vendor
457 # page. All used values have been collected by testing.
458 url = "https://freedns.afraid.org/dynamic/update.php"
459
460 @property
461 def proto(self):
462 return self.get("proto")
463
464 def update(self):
465 address = self.get_address(self.proto)
466
467 data = {
468 "address" : address,
469 }
470
471 # Add auth token to the update url.
472 url = "%s?%s" % (self.url, self.token)
473
474 # Send update to the server.
475 response = self.send_request(url, data=data)
476
aa21a4c6
SS
477 if output.startswith("Updated") or "has not changed" in output:
478 return
479
480 # Handle error codes.
481 if output == "ERROR: Unable to locate this record":
482 raise DDNSAuthenticationError
483 elif "is an invalid IP address" in output:
484 raise DDNSRequestError(_("Invalid IP address has been sent."))
485
aa21a4c6 486
a08c1b72
SS
487class DDNSProviderLightningWireLabs(DDNSProvider):
488 INFO = {
489 "handle" : "dns.lightningwirelabs.com",
490 "name" : "Lightning Wire Labs",
491 "website" : "http://dns.lightningwirelabs.com/",
492 "protocols" : ["ipv6", "ipv4",]
493 }
494
495 # Information about the format of the HTTPS request is to be found
496 # https://dns.lightningwirelabs.com/knowledge-base/api/ddns
497 url = "https://dns.lightningwirelabs.com/update"
498
5f402f36 499 def update(self):
a08c1b72
SS
500 data = {
501 "hostname" : self.hostname,
502 }
503
504 # Check if we update an IPv6 address.
505 address6 = self.get_address("ipv6")
506 if address6:
507 data["address6"] = address6
508
509 # Check if we update an IPv4 address.
510 address4 = self.get_address("ipv4")
511 if address4:
512 data["address4"] = address4
513
514 # Raise an error if none address is given.
515 if not data.has_key("address6") and not data.has_key("address4"):
516 raise DDNSConfigurationError
517
518 # Check if a token has been set.
519 if self.token:
520 data["token"] = self.token
521
522 # Check for username and password.
523 elif self.username and self.password:
524 data.update({
525 "username" : self.username,
526 "password" : self.password,
527 })
528
529 # Raise an error if no auth details are given.
530 else:
531 raise DDNSConfigurationError
532
533 # Send update to the server.
cb455540 534 response = self.send_request(self.url, data=data)
a08c1b72
SS
535
536 # Handle success messages.
537 if response.code == 200:
538 return
539
540 # Handle error codes.
2e5ad318 541 if response.code == 403:
a08c1b72 542 raise DDNSAuthenticationError
2e5ad318 543 elif response.code == 400:
a08c1b72
SS
544 raise DDNSRequestError
545
546 # If we got here, some other update error happened.
547 raise DDNSUpdateError
548
549
d1cd57eb
SS
550class DDNSProviderNamecheap(DDNSProvider):
551 INFO = {
552 "handle" : "namecheap.com",
553 "name" : "Namecheap",
554 "website" : "http://namecheap.com",
555 "protocols" : ["ipv4",]
556 }
557
558 # Information about the format of the HTTP request is to be found
559 # https://www.namecheap.com/support/knowledgebase/article.aspx/9249/0/nc-dynamic-dns-to-dyndns-adapter
560 # https://community.namecheap.com/forums/viewtopic.php?f=6&t=6772
561
562 url = "https://dynamicdns.park-your-domain.com/update"
563
564 def parse_xml(self, document, content):
565 # Send input to the parser.
566 xmldoc = xml.dom.minidom.parseString(document)
567
568 # Get XML elements by the given content.
569 element = xmldoc.getElementsByTagName(content)
570
571 # If no element has been found, we directly can return None.
572 if not element:
573 return None
574
575 # Only get the first child from an element, even there are more than one.
576 firstchild = element[0].firstChild
577
578 # Get the value of the child.
579 value = firstchild.nodeValue
580
581 # Return the value.
582 return value
583
584 def update(self):
585 # Namecheap requires the hostname splitted into a host and domain part.
586 host, domain = self.hostname.split(".", 1)
587
588 data = {
589 "ip" : self.get_address("ipv4"),
590 "password" : self.password,
591 "host" : host,
592 "domain" : domain
593 }
594
595 # Send update to the server.
596 response = self.send_request(self.url, data=data)
597
598 # Get the full response message.
599 output = response.read()
600
601 # Handle success messages.
602 if self.parse_xml(output, "IP") == self.get_address("ipv4"):
603 return
604
605 # Handle error codes.
606 errorcode = self.parse_xml(output, "ResponseNumber")
607
608 if errorcode == "304156":
609 raise DDNSAuthenticationError
610 elif errorcode == "316153":
611 raise DDNSRequestError(_("Domain not found."))
612 elif errorcode == "316154":
613 raise DDNSRequestError(_("Domain not active."))
614 elif errorcode in ("380098", "380099"):
615 raise DDNSInternalServerError
616
617 # If we got here, some other update error happened.
618 raise DDNSUpdateError
619
620
88f39629 621class DDNSProviderNOIP(DDNSProviderDynDNS):
f22ab085
MT
622 INFO = {
623 "handle" : "no-ip.com",
624 "name" : "No-IP",
625 "website" : "http://www.no-ip.com/",
626 "protocols" : ["ipv4",]
627 }
628
629 # Information about the format of the HTTP request is to be found
630 # here: http://www.no-ip.com/integrate/request and
631 # here: http://www.no-ip.com/integrate/response
632
88f39629 633 url = "http://dynupdate.no-ip.com/nic/update"
2de06f59 634
88f39629 635 def _prepare_request_data(self):
2de06f59
MT
636 data = {
637 "hostname" : self.hostname,
f22ab085
MT
638 "address" : self.get_address("ipv4"),
639 }
640
88f39629 641 return data
f22ab085
MT
642
643
a508bda6
SS
644class DDNSProviderOVH(DDNSProviderDynDNS):
645 INFO = {
646 "handle" : "ovh.com",
647 "name" : "OVH",
648 "website" : "http://www.ovh.com/",
649 "protocols" : ["ipv4",]
650 }
651
652 # OVH only provides very limited information about how to
653 # update a DynDNS host. They only provide the update url
654 # on the their german subpage.
655 #
656 # http://hilfe.ovh.de/DomainDynHost
657
658 url = "https://www.ovh.com/nic/update"
659
660 def _prepare_request_data(self):
54d3efc8
MT
661 data = DDNSProviderDynDNS._prepare_request_data(self)
662 data.update({
663 "system" : "dyndns",
664 })
665
666 return data
a508bda6
SS
667
668
ef33455e
SS
669class DDNSProviderRegfish(DDNSProvider):
670 INFO = {
671 "handle" : "regfish.com",
672 "name" : "Regfish GmbH",
673 "website" : "http://www.regfish.com/",
674 "protocols" : ["ipv6", "ipv4",]
675 }
676
677 # A full documentation to the providers api can be found here
678 # but is only available in german.
679 # https://www.regfish.de/domains/dyndns/dokumentation
680
681 url = "https://dyndns.regfish.de/"
682
683 def update(self):
684 data = {
685 "fqdn" : self.hostname,
686 }
687
688 # Check if we update an IPv6 address.
689 address6 = self.get_address("ipv6")
690 if address6:
691 data["ipv6"] = address6
692
693 # Check if we update an IPv4 address.
694 address4 = self.get_address("ipv4")
695 if address4:
696 data["ipv4"] = address4
697
698 # Raise an error if none address is given.
699 if not data.has_key("ipv6") and not data.has_key("ipv4"):
700 raise DDNSConfigurationError
701
702 # Check if a token has been set.
703 if self.token:
704 data["token"] = self.token
705
706 # Raise an error if no token and no useranem and password
707 # are given.
708 elif not self.username and not self.password:
709 raise DDNSConfigurationError(_("No Auth details specified."))
710
711 # HTTP Basic Auth is only allowed if no token is used.
712 if self.token:
713 # Send update to the server.
714 response = self.send_request(self.url, data=data)
715 else:
716 # Send update to the server.
717 response = self.send_request(self.url, username=self.username, password=self.password,
718 data=data)
719
720 # Get the full response message.
721 output = response.read()
722
723 # Handle success messages.
724 if "100" in output or "101" in output:
725 return
726
727 # Handle error codes.
728 if "401" or "402" in output:
729 raise DDNSAuthenticationError
730 elif "408" in output:
731 raise DDNSRequestError(_("Invalid IPv4 address has been sent."))
732 elif "409" in output:
733 raise DDNSRequestError(_("Invalid IPv6 address has been sent."))
734 elif "412" in output:
735 raise DDNSRequestError(_("No valid FQDN was given."))
736 elif "414" in output:
737 raise DDNSInternalServerError
738
739 # If we got here, some other update error happened.
740 raise DDNSUpdateError
741
742
f22ab085
MT
743class DDNSProviderSelfhost(DDNSProvider):
744 INFO = {
745 "handle" : "selfhost.de",
746 "name" : "Selfhost.de",
747 "website" : "http://www.selfhost.de/",
748 "protocols" : ["ipv4",],
749 }
750
2de06f59 751 url = "https://carol.selfhost.de/update"
f22ab085 752
5f402f36 753 def update(self):
2de06f59
MT
754 data = {
755 "username" : self.username,
756 "password" : self.password,
757 "textmodi" : "1",
758 }
f22ab085 759
2de06f59 760 response = self.send_request(self.url, data=data)
f22ab085
MT
761
762 match = re.search("status=20(0|4)", response.read())
763 if not match:
764 raise DDNSUpdateError
b09b1545
SS
765
766
767class DDNSProviderSPDNS(DDNSProviderDynDNS):
768 INFO = {
769 "handle" : "spdns.org",
770 "name" : "SPDNS",
771 "website" : "http://spdns.org/",
772 "protocols" : ["ipv4",]
773 }
774
775 # Detailed information about request and response codes are provided
776 # by the vendor. They are using almost the same mechanism and status
777 # codes as dyndns.org so we can inherit all those stuff.
778 #
779 # http://wiki.securepoint.de/index.php/SPDNS_FAQ
780 # http://wiki.securepoint.de/index.php/SPDNS_Update-Tokens
781
782 url = "https://update.spdns.de/nic/update"
4ec90b93
MT
783
784
c8c7ca8f
SS
785class DDNSProviderVariomedia(DDNSProviderDynDNS):
786 INFO = {
787 "handle" : "variomedia.de",
788 "name" : "Variomedia",
789 "website" : "http://www.variomedia.de/",
790 "protocols" : ["ipv6", "ipv4",]
791 }
792
793 # Detailed information about the request can be found here
794 # https://dyndns.variomedia.de/
795
796 url = "https://dyndns.variomedia.de/nic/update"
797
798 @property
799 def proto(self):
800 return self.get("proto")
801
802 def _prepare_request_data(self):
803 data = {
804 "hostname" : self.hostname,
805 "myip" : self.get_address(self.proto)
806 }
54d3efc8
MT
807
808 return data
98fbe467
SS
809
810
811class DDNSProviderZoneedit(DDNSProvider):
812 INFO = {
813 "handle" : "zoneedit.com",
814 "name" : "Zoneedit",
815 "website" : "http://www.zoneedit.com",
816 "protocols" : ["ipv6", "ipv4",]
817 }
818
819 # Detailed information about the request and the response codes can be
820 # obtained here:
821 # http://www.zoneedit.com/doc/api/other.html
822 # http://www.zoneedit.com/faq.html
823
824 url = "https://dynamic.zoneedit.com/auth/dynamic.html"
825
826 @property
827 def proto(self):
828 return self.get("proto")
829
830 def update(self):
831 data = {
832 "dnsto" : self.get_address(self.proto),
833 "host" : self.hostname
834 }
835
836 # Send update to the server.
837 response = self.send_request(self.url, username=self.username, password=self.password,
838 data=data)
839
840 # Get the full response message.
841 output = response.read()
842
843 # Handle success messages.
844 if output.startswith("<SUCCESS"):
845 return
846
847 # Handle error codes.
848 if output.startswith("invalid login"):
849 raise DDNSAuthenticationError
850 elif output.startswith("<ERROR CODE=\"704\""):
851 raise DDNSRequestError(_("No valid FQDN was given."))
852 elif output.startswith("<ERROR CODE=\"702\""):
853 raise DDNSInternalServerError
854
855 # If we got here, some other update error happened.
856 raise DDNSUpdateError