--- ddclient.orig	2013-09-26 01:06:12.399990673 +0100
+++ ddclient	2013-09-26 06:25:36.768189452 +0100
@@ -13,12 +13,17 @@
 # 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/
+#
+# Further modified to work with Cloudflare by Peter Roberts 2013-9-26, 2014-6-22: blog.peter-r.co.uk
+#
 ######################################################################
-require 5.004;
+require 5.014;
 use strict;
 use Getopt::Long;
 use Sys::Hostname;
 use IO::Socket;
+use JSON::Any;
 
 my $version  = "3.8.0";
 my $programd  = $0; 
@@ -416,6 +421,14 @@
 	'warned-min-interval'       => setv(T_ANY,    0, 1, 0, 0,             undef),
 	'warned-min-error-interval' => setv(T_ANY,    0, 1, 0, 0,             undef),
     },
+    'cloudflare-common-defaults'       => {
+	'server'	          => setv(T_FQDNP,  1, 0, 1, 'www.cloudflare.com', undef),
+        'zone'                => setv(T_FQDN,   1, 0, 1, '',                  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' => {
@@ -529,6 +542,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'},
@@ -3386,7 +3410,128 @@
 }
 
 ######################################################################
-# vim: ai ts=4 sw=4 tw=78 :
 
+######################################################################
+## nic_cloudflare_examples
+##
+## written by Ian Pye
+##
+######################################################################
+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
+  zone=dns.zone		       ## your domain
+  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,
+  zone=dns-zone,                                                \\
+  login=my-cloudflare.com-login,                                \\
+  password=my-cloudflare.com-api-key                            \\
+  myhost.com 
+
+  ## multiple host update to the custom DNS service
+  protocol=cloudflare,
+  zone=dns.zone                                                 \\
+  login=my-cloudflare.com-login,                                \\
+  password=my-cloudflare.com-api-key                            \\
+  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 zone) ]);
+
+    ## update each set of hosts that had similar configurations
+    foreach my $sig (keys %groups) {
+    	my @hosts = @{$groups{$sig}};
+    	my $hosts = join(',', @hosts);
+    	my $key   = $hosts[0];
+    	my $ip    = $config{$key}{'wantip'};
+    	
+    	# FQDNs
+    	for my $domain (@hosts) {
+	    my $hostname = $domain =~ s/\.$config{$key}{zone}$//r;
+	    delete $config{$domain}{'wantip'};
+	    
+	    info("setting IP address to %s for %s", $ip, $domain);
+	    verbose("UPDATE:","updating %s", $domain);
+
+	    # Get domain ID
+	    my $url = "https://$config{$key}{'server'}/api_json.html?a=rec_load_all";
+	    $url   .= "&z=".$config{$key}{'zone'};
+	    $url   .= "&email=".$config{$key}{'login'};	
+	    $url   .= "&tkn=".$config{$key}{'password'};
+	    
+	    my $reply = geturl(opt('proxy'), $url);
+	    unless ($reply) {
+		failed("updating %s: Could not connect to %s.", $domain, $config{$key}{'server'});
+		last;
+	    }
+	    last if !header_ok($domain, $reply);
+	    
+	    # Strip header
+	    $reply =~ s/^.*?\n\n//s;
+	    my $response = JSON::Any->jsonToObj($reply);
+	    if ($response->{result} eq 'error') {
+		failed ("%s", $response->{msg});
+		next; 
+	    }
+	    
+	    # Pull the ID out of the json, messy
+	    my ($id) = map { $_->{name} eq $domain ? $_->{rec_id} : () } @{ $response->{response}->{recs}->{objs} };
+	    unless($id) {
+		failed("updating %s: No domain ID found.", $domain);
+		next;
+	    }
+	    
+	    # Set domain
+	    $url   = "https://$config{$key}{'server'}/api_json.html?a=rec_edit&type=A&ttl=1";
+	    $url     .= "&name=$hostname";
+	    $url     .= "&z=".$config{$key}{'zone'};
+	    $url     .= "&id=".$id;	
+	    $url     .= "&email=".$config{$key}{'login'};	
+	    $url     .= "&tkn=".$config{$key}{'password'};    
+	    $url     .= "&content=";
+	    $url     .= "$ip"       if $ip;
+	    
+	    $reply = geturl(opt('proxy'), $url);
+	    unless ($reply) {
+		failed("updating %s: Could not connect to %s.", $domain, $config{$domain}{'server'});
+		last;
+	    }
+	    last if !header_ok($domain, $reply);
+	    
+	    # Strip header
+	    $reply =~ s/^.*?\n\n//s;
+	    $response = JSON::Any->jsonToObj($reply);
+	    if ($response->{result} eq 'error') {
+		failed ("%s", $response->{msg});	
+	    } else {
+		success ("%s -- Updated Successfully to %s", $domain, $ip);
+		 
+	    }
+	    
+	    # Cache
+	    $config{$key}{'ip'}     = $ip;
+	    $config{$key}{'mtime'}  = $now;
+	    $config{$key}{'status'} = 'good';
+	}
+    }
+}
 
 __END__
