--- ddclient-3.8.1	2011-07-11 22:04:21.000000000 +0100
+++ ddclient	2012-07-16 22:45:31.694192957 +0100
@@ -13,6 +13,8 @@
 # Support for multiple IP numbers added by
 # Astaro AG, Ingo Schwarze <ischwarze-OOs/4mkCeqbQT0dZR+AlfA@public.gmane.org> September 16, 2008
 #
+# Modified to work with Cloudflare by Robert Ian Hawdon 2012-07-16: http://robertianhawdon.me.uk/
+#
 ######################################################################
 require 5.004;
 use strict;
@@ -22,7 +24,7 @@
 
 my ($VERSION) = q$Revision: 130 $ =~ /(\d+)/;
 
-my $version  = "3.8.1";
+my $version  = "3.8.1-r". $VERSION;
 my $programd  = $0; 
 $programd =~ s%^.*/%%;
 my $program   = $programd;
@@ -429,6 +431,13 @@
 	'login'               => setv(T_LOGIN,  0, 0, 0, 'unused',            undef),
 	'client'              => setv(T_STRING, 0, 1, 1, $program,            undef),
     },
+    'cloudflare-common-defaults'       => {
+	'server'	          => setv(T_FQDNP,  1, 0, 1, 'www.cloudflare.com', undef),
+	'static'              => setv(T_BOOL,   0, 1, 1, 0,                   undef),
+	'wildcard'            => setv(T_BOOL,   0, 1, 1, 0,                   undef),
+	'mx'	              => setv(T_OFQDN,  0, 1, 1, '',                  undef),
+	'backupmx'            => setv(T_BOOL,   0, 1, 1, 0,                   undef),
+    },
 );
 my %services = (
     'dyndns1' => {
@@ -563,6 +572,17 @@
 			  $variables{'service-common-defaults'},
 		        ),
     },
+    'cloudflare' => {
+        'updateable' => undef,
+        'update'     => \&nic_cloudflare_update,
+        'examples'   => \&nic_cloudflare_examples,
+        'variables'  => merge(
+            { 'server'       => setv(T_FQDNP,  1, 0, 1, 'www.cloudflare.com', undef)          },
+            { 'min-interval' => setv(T_DELAY,  0, 0, 1, interval('5m'), 0),},
+            $variables{'cloudflare-common-defaults'},
+            $variables{'service-common-defaults'},
+            ),
+    },
 );
 $variables{'merged'} = merge($variables{'global-defaults'},
 			     $variables{'service-common-defaults'},
@@ -3671,6 +3691,132 @@
     }
 }
 
+######################################################################
+
+######################################################################
+## nic_cloudflare_examples
+##
+## written by Ian Pye
+##
+## https://www.cloudflare.com/api.html?a=DIUP&u=myemail@mydomain.com&tkn=SecretPass&ip=192.168.10.4&hosts=example.com
+##
+######################################################################
+sub nic_cloudflare_examples {
+    return <<EoEXAMPLE;
+o 'cloudflare'
+
+The 'cloudflare' protocol is used by DNS service offered by www.cloudflare.com.
+
+Configuration variables applicable to the 'cloudflare' protocol are:
+  protocol=cloudflare          ## 
+  server=fqdn.of.service       ## defaults to www.cloudflare.com
+  login=service-login          ## login name and password  registered with the service
+  password=service-password    ##
+  fully.qualified.host         ## the host registered with the service.
+
+Example ${program}.conf file entries:
+  ## single host update
+  protocol=cloudflare,                                         \\
+  login=my-cloudflare.com-login,                               \\
+  password=my-cloudflare.com-secure-token                      \\
+  myhost.com 
+
+  ## multiple host update to the custom DNS service
+  protocol=cloudflare,                                         \\
+  login=my-cloudflare.com-login,                               \\
+  password=my-cloudflare.com-secure-token                      \\
+  my-toplevel-domain.com,my-other-domain.com
+EoEXAMPLE
+}
+######################################################################
+## nic_cloudflare_update
+######################################################################
+sub nic_cloudflare_update {
+    debug("\nnic_cloudflare_update -------------------");
+
+    ## group hosts with identical attributes together 
+    my %groups = group_hosts_by([ @_ ], [ qw(ssh login password server wildcard mx backupmx) ]);
+
+    ## each host is in a group by itself
+    ##my %groups = map { $_ => [ $_ ] } @_;
+
+    my %errors = (
+       'E_NOUPDATE' => 'No changes made to the hostname(s). Continual updates with no changes lead to blocked clients.',
+       'E_NOHOST' => 'No valid FQDN (fully qualified domain name) was specified',
+       'E_INVLDHST'=> 'An invalid hostname was specified. This may be due to the fact the hostname has not been created in the system. Creating new host names via clients is not supported.',
+        'E_UNAUTH'  => 'The username specified is not authorized to update this hostname and domain.',
+        'E_INVLDIP' => 'The IP address given is not valid.',
+        'E_DUPHST' => 'Duplicate values exist for a record. Only single values for records are supported currently.',
+    );
+
+    ## update each set of hosts that had similar configurations
+    foreach my $sig (keys %groups) {
+
+    	my @hosts = @{$groups{$sig}};
+    	my $hosts = join(',', @hosts);
+    	my $h     = $hosts[0];
+        my $ip    = $config{$h}{'wantip'};
+
+        delete $config{$_}{'wantip'} foreach @hosts;
+        
+        info("setting IP address to %s for %s", $ip, $hosts);
+        verbose("UPDATE:","updating %s", $hosts);
+                
+        my $url;
+        $url   = "https://$config{$h}{'server'}/api.html?a=DIUP";
+        $url  .= "&hosts=$hosts";
+        $url  .= "&u=".$config{$h}{'login'};	
+        $url  .= "&tkn=".$config{$h}{'password'};    
+        $url  .= "&ip=";
+        $url  .= "$ip"            if $ip;
+        
+        my $reply = geturl(opt('proxy'), $url);
+        if (!defined($reply) || !$reply) {
+            failed("updating %s: Could not connect to %s.", $hosts, $config{$h}{'server'});
+            last;
+        }
+        last if !header_ok($hosts, $reply);
+        
+        my @reply = split /\n/, $reply;
+        my @body = ();
+        my $in_header = 1;
+        foreach my $line (@reply) {
+            if ($line eq "") {
+                $in_header = 0;
+            } elsif (!$in_header) {
+                push(@body, $line);
+            }
+        }
+
+        if ($reply =~ /E_UNAUTH/) {
+            failed ("%s", $errors{"E_UNAUTH"});
+        } elsif ($reply =~ /E_NOHOST/) {
+            failed ("%s", $errors{"E_NOHOST"});
+        } elsif ($reply =~ /E_INVLDHST/) {
+            failed ("%s", $errors{"E_INVLDHST"});
+        } elsif ($reply =~ /E_INVLDIP/) {
+            failed ("%s", $errors{"E_INVLDIP"});
+        } elsif ($reply =~ /E_DUPHST/) {
+            failed ("%s", $errors{"E_DUPHST"});
+        } else {
+            foreach my $line (@body) {
+                my @res = split / /, $line;
+                if ($res[1] eq "E_NOUPDATE") {
+                    $config{$res[0]}{'ip'}     = $ip;
+                    $config{$res[0]}{'mtime'}  = $now;
+                    $config{$res[0]}{'status'} = 'good';
+                    warning ("%s -- %s", $res[0], $errors{"E_NOUPDATE"});
+                } elsif ($res[1] eq "OK") {
+                    $config{$res[0]}{'ip'}     = $ip;
+                    $config{$res[0]}{'mtime'}  = $now;
+                    $config{$res[0]}{'status'} = 'good';
+                    success ("%s -- Updated Successfully to %s", $res[0], $ip);
+                }
+            }
+        }
+    }
+}
+
 ######################################################################
 # vim: ai ts=4 sw=4 tw=78 :
 
