CodeCommitsIssuesPull requestsActionsInsightsSecurity
ad4b66d766fb2eaac6e7a26d4ed0b53cc5de2748

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

Cpanel/CloudFlare.pm

638lines · modecode

1package Cpanel::CloudFlare;
2
3# cpanel - Cpanel/CloudFlare.pm Copyright(c) 2011 CloudFlare, Inc.
4# All rights Reserved.
5# copyright@cloudflare.com http://cloudflare.com
6# @author ian@cloudflare.com
7# This code is subject to the cPanel license. Unauthorized copying is prohibited
8
9use Cpanel::DnsUtils::UsercPanel ();
10use Cpanel::AdminBin ();
11use Cpanel::Locale ();
12use Cpanel::Logger ();
13use Cpanel::UrlTools ();
14use Cpanel::SocketIP ();
15use Cpanel::Encoder::URI ();
16use Cpanel::AcctUtils ();
17use Cpanel::DomainLookup ();
18use Cpanel::DataStore ();
19
20use Socket ();
21use JSON::Syck ();
22use Digest::MD5 qw(md5_hex);
23use strict;
24
25my $logger = Cpanel::Logger->new();
26my $locale;
27my $cf_config_file = "/usr/local/cpanel/etc/cloudflare.json";
28my $cf_data_file_name = ".cloudflare_data.yaml";
29my $cf_old_data_file_name = "/usr/local/cpanel/etc/cloudflare_data.yaml";
30my $cf_data_file;
31my $cf_host_key;
32my $cf_host_name;
33my $cf_host_uri;
34my $cf_user_name;
35my $cf_user_uri;
36my $cf_host_port;
37my $cf_host_prefix;
38my $has_ssl;
39my $cf_debug_mode;
40my $hoster_name;
41my $cf_cp_version;
42my $cf_global_data = {};
43my $DEFAULT_HOSTER_NAME = "your web hosting provider";
44
45my %KEYMAP = ( 'line' => 'Line', 'ttl' => 'ttl', 'name' => 'name',
46 'class' => 'class', 'address' => 'address', 'type' => 'type',
47 'txtdata' => 'txtdata', 'preference' => 'preference', 'exchange' => 'exchange' );
48
49## Initialize vars here.
50sub CloudFlare_init {
51 my $data = JSON::Syck::LoadFile($cf_config_file);
52 $cf_host_key = $data->{"host_key"};
53 $cf_host_name = $data->{"host_name"};
54 $cf_host_uri = $data->{"host_uri"};
55 $cf_host_port = $data->{"host_port"};
56 $cf_host_prefix = $data->{"host_prefix"};
57 $cf_debug_mode = $data->{"debug"};
58 $cf_user_name = $data->{"user_name"};
59 $cf_user_uri = $data->{"user_uri"};
60 $cf_cp_version = $data->{"cp_version"};
61 $hoster_name = $data->{"host_formal_name"};
62 if (!$hoster_name) {
63 $hoster_name = $DEFAULT_HOSTER_NAME;
64 }
65
66 eval { use Net::SSLeay qw(post_https make_headers make_form); $has_ssl = 1 };
67 if ( !$has_ssl ) {
68 $logger->warn("Failed to load Net::SSLeay: $@.\nDisabling functionality until fixed.");
69 }
70}
71
72# Can only be called with json or xml api because it uses
73# a non-standard return
74sub api2_user_create {
75 my %OPTS = @_;
76
77 if (!$cf_host_key) {
78 $logger->info("Missing cf_host_key! Define this in $cf_config_file.");
79 return [];
80 }
81
82 __load_data_file($OPTS{"homedir"});
83 # Use a random string as a password.
84 my $password = crypt(int(rand(10000000)), time);
85 $logger->info("Creating Cloudflare user for " . $OPTS{"email"} . " -- " . $password);
86 $cf_global_data->{"cf_user_tokens"}->{$OPTS{"user"}} = md5_hex($OPTS{"user"} . $cf_host_key);
87 $logger->info("Making user token: " . $cf_global_data->{"cf_user_tokens"}->{$OPTS{"user"}});
88
89 ## Otherwise, try to create this user.
90 my $args = {
91 "host" => $cf_host_name,
92 "uri" => $cf_host_uri,
93 "port" => $cf_host_port,
94 "query" => {
95 "act" => "user_create",
96 "host_key" => $cf_host_key,
97 "cloudflare_email" => $OPTS{"email"},
98 "cloudflare_pass" => $password,
99 "unique_id" => $cf_global_data->{"cf_user_tokens"}->{$OPTS{"user"}},
100 },
101 };
102
103 Cpanel::DataStore::store_ref($cf_data_file, $cf_global_data);
104 my $result = __https_post_req->($args);
105 return JSON::Syck::Load($result);
106}
107
108# Can only be called with json or xml api because it uses
109# a non-standard return
110sub api2_user_lookup {
111 my %OPTS = @_;
112
113 __load_data_file($OPTS{"homedir"}, $OPTS{"user"});
114 if (!$cf_host_key) {
115 $logger->info("Missing cf_host_key! Define this in $cf_config_file.");
116 return [];
117 }
118
119 if ( !$has_ssl ) {
120 $logger->info("No SSL Configured");
121 return [{"result"=>"error",
122 "msg" => "CloudFlare is disabled until Net::SSLeay is installed on this server."}];
123 }
124
125 if ($cf_global_data->{"cf_user_tokens"}->{$OPTS{"user"}}) {
126 $logger->info("Using user token");
127 my $login_args = {
128 "host" => $cf_host_name,
129 "uri" => $cf_host_uri,
130 "port" => $cf_host_port,
131 "query" => {
132 "act" => "user_lookup",
133 "host_key" => $cf_host_key,
134 "unique_id" => $cf_global_data->{"cf_user_tokens"}->{$OPTS{"user"}},
135 },
136 };
137
138 my $result = __https_post_req->($login_args);
139 return JSON::Syck::Load($result);
140 } else {
141 $logger->info("Using user email");
142 my $login_args = {
143 "host" => $cf_host_name,
144 "uri" => $cf_host_uri,
145 "port" => $cf_host_port,
146 "query" => {
147 "act" => "user_lookup",
148 "host_key" => $cf_host_key,
149 "cloudflare_email" => $OPTS{"email"},
150 },
151 };
152
153 my $result = __https_post_req->($login_args);
154 return JSON::Syck::Load($result);
155 }
156}
157
158## Pulls certain stats for the passed in zone.
159sub api2_get_stats {
160 my %OPTS = @_;
161
162 if (!$OPTS{"user_api_key"}) {
163 $logger->info("Missing user_api_key!");
164 return [];
165 }
166
167 if ( !$has_ssl ) {
168 $logger->info("No SSL Configured");
169 return [{"result"=>"error",
170 "msg" => "CloudFlare is disabled until Net::SSLeay is installed on this server."}];
171 }
172
173 ## Otherwise, pull this users stats.
174 my $stats_args = {
175 "host" => $cf_user_name,
176 "uri" => $cf_user_uri,
177 "port" => $cf_host_port,
178 "query" => {
179 "a" => "stats",
180 "z" => $OPTS{"zone_name"},
181 "tkn" => $OPTS{"user_api_key"},
182 "u" => $OPTS{"user_email"},
183 "interval" => 30, # 30 = last 7 days, 20 = last 30 days 40 = last 24 hours
184 },
185 };
186
187 my $result = __https_post_req->($stats_args);
188 return JSON::Syck::Load($result);
189}
190
191sub api2_edit_cf_setting {
192 my %OPTS = @_;
193
194 if (!$OPTS{"user_api_key"}) {
195 $logger->info("Missing user_api_key!");
196 return [];
197 }
198
199 if ( !$has_ssl ) {
200 $logger->info("No SSL Configured");
201 return [{"result"=>"error",
202 "msg" => "CloudFlare is disabled until Net::SSLeay is installed on this server."}];
203 }
204
205 ## Otherwise, pull this users stats.
206 my $stats_args = {
207 "host" => $cf_user_name,
208 "uri" => $cf_user_uri,
209 "port" => $cf_host_port,
210 "query" => {
211 "a" => $OPTS{"a"},
212 "z" => $OPTS{"zone_name"},
213 "tkn" => $OPTS{"user_api_key"},
214 "u" => $OPTS{"user_email"},
215 "v" => $OPTS{"v"},
216 },
217 };
218
219 my $result = __https_post_req->($stats_args);
220 return JSON::Syck::Load($result);
221}
222
223# Can only be called with json or xml api because it uses
224# a non-standard return
225sub api2_zone_set {
226 my %OPTS = @_;
227
228 if (!$cf_host_key || !$cf_host_prefix) {
229 $logger->info("Missing cf_host_key or $cf_host_prefix! Define these in $cf_config_file.");
230 return [];
231 }
232
233 __load_data_file($OPTS{"homedir"});
234 my $domain = ".".$OPTS{"zone_name"}.".";
235 my $subs = $OPTS{"subdomains"};
236 $subs =~ s/${domain}//g;
237
238 ## Unpack the mapping from recs to lines (ugg, this is SOOO BAAD)
239 my $recs2lines = JSON::Syck::Load($OPTS{"cf_recs"});
240
241 ## Set up the zone_set args.
242 my $login_args = {
243 "host" => $cf_host_name,
244 "uri" => $cf_host_uri,
245 "port" => $cf_host_port,
246 "query" => {
247 "act" => "zone_set",
248 "host_key" => $cf_host_key,
249 "user_key" => $OPTS{"user_key"},
250 "zone_name" => $OPTS{"zone_name"},
251 "resolve_to" => $cf_host_prefix . "." . $OPTS{"zone_name"},
252 "subdomains" => $subs
253 },
254 };
255
256 if (!$subs) {
257 $login_args->{"query"}->{"act"} = "zone_delete";
258 $cf_global_data->{"cf_zones"}->{$OPTS{"zone_name"}} = 0
259 }
260
261 my $result = JSON::Syck::Load(__https_post_req->($login_args));
262
263 ## Args for updating local DNS.
264 my %zone_args = ("domain" => $OPTS{"zone_name"},
265 "class" => "IN",
266 "type" => "CNAME",
267 "name" => $cf_host_prefix,
268 "ttl" => 1400,
269 "cname" => $OPTS{"zone_name"},
270 );
271
272 my $is_cf = 0;
273
274 ## If we get an error, do nothing and return the error to the user.
275 if ($result->{"result"} eq "error") {
276 $logger->info("CloudFlare Error: " . $result->{"msg"});
277 } else {
278 ## Otherwise, update the dns for this zone.
279 my $dom = ".".$OPTS{"zone_name"};
280
281 ## First Make sure that the resolve to is set.
282 my $res = Cpanel::AdminBin::adminfetchnocache( 'zone', '', 'ADD', 'storable', $OPTS{"zone_name"},
283 __serialize_request( \%zone_args ) );
284
285 ## If there's a delete, remove this record from being CF.
286 if ($OPTS{"old_line"}) {
287 $zone_args{"line"} = $OPTS{"old_line"};
288 $zone_args{"name"} = $OPTS{"old_rec"};
289 $zone_args{"name"} =~ s/$domain//g;
290 $zone_args{"cname"} = $OPTS{"zone_name"};
291 $res = Cpanel::AdminBin::adminfetchnocache( 'zone', '', 'EDIT', 'storable', $OPTS{"zone_name"},
292 __serialize_request( \%zone_args ) );
293 }
294
295 ## Now, go ahead and update all of the CF enabled recs.
296 foreach my $ft (keys %{$result->{"response"}->{"forward_tos"}}) {
297 $zone_args{"line"} = $recs2lines->{$ft."."};
298 $zone_args{"name"} = $ft;
299 $zone_args{"name"} =~ s/$dom//g;
300 $zone_args{"cname"} = $result->{"response"}->{"forward_tos"}->{$ft};
301
302 $res = Cpanel::AdminBin::adminfetchnocache( 'zone', '', 'EDIT', 'storable', $OPTS{"zone_name"},
303 __serialize_request( \%zone_args ) );
304 if (!$res->{"status"}) {
305 ## Try again, bumping up the line by 1. -- no idea why this works.
306 $zone_args{"line"}++;
307 $res = Cpanel::AdminBin::adminfetchnocache( 'zone', '', 'EDIT', 'storable', $OPTS{"zone_name"},
308 __serialize_request( \%zone_args ) );
309 }
310
311 if (!$res->{"status"}) {
312 $logger->info("Failed to set DNS for CloudFlare record $ft!");
313 $logger->info(JSON::Syck::Dump($res));
314 $result->{"result"} = "error";
315 $result->{"msg"} = $res->{"statusmsg"};
316 }
317
318 ## Note that if at least one rec is on, this zone is on CF.
319 $cf_global_data->{"cf_zones"}->{$OPTS{"zone_name"}} = 1;
320 $is_cf = 1;
321 }
322 }
323
324 if (!$is_cf) {
325 $cf_global_data->{"cf_zones"}->{$OPTS{"zone_name"}} = 0;
326 }
327
328 ## Save the updated global data arg.
329 Cpanel::DataStore::store_ref($cf_data_file, $cf_global_data);
330
331 return $result;
332}
333
334sub api2_zone_delete {
335 my %OPTS = @_;
336
337 if (!$cf_host_key || !$cf_host_prefix) {
338 $logger->info("Missing cf_host_key or $cf_host_prefix! Define these in $cf_config_file.");
339 return [];
340 }
341
342 __load_data_file($OPTS{"homedir"});
343 my $domain = ".".$OPTS{"zone_name"}.".";
344
345 ## Unpack the mapping from recs to lines (ugg, this is SOOO BAAD)
346 my $recs2lines = JSON::Syck::Load($OPTS{"cf_recs"});
347
348 ## Set up the zone_set args.
349 my $login_args = {
350 "host" => $cf_host_name,
351 "uri" => $cf_host_uri,
352 "port" => $cf_host_port,
353 "query" => {
354 "act" => "zone_delete",
355 "host_key" => $cf_host_key,
356 "user_key" => $OPTS{"user_key"},
357 "zone_name" => $OPTS{"zone_name"}
358 },
359 };
360
361 $cf_global_data->{"cf_zones"}->{$OPTS{"zone_name"}} = 0;
362
363 my $result = JSON::Syck::Load(__https_post_req->($login_args));
364
365 ## Args for updating local DNS.
366 my %zone_args = ("domain" => $OPTS{"zone_name"},
367 "class" => "IN",
368 "type" => "CNAME",
369 "name" => $cf_host_prefix,
370 "ttl" => 1400,
371 "cname" => $OPTS{"zone_name"},
372 );
373
374 ## If we get an error, do nothing and return the error to the user.
375 if ($result->{"result"} eq "error") {
376 $logger->info("CloudFlare Error: " . $result->{"msg"});
377 } else {
378 ## Otherwise, update the dns for this zone.
379 my $dom = ".".$OPTS{"zone_name"};
380
381 ## Loop over list of subs, removing from CF.
382 my $res;
383 foreach my $linecom (split(/,/, $OPTS{"subdomains"})) {
384 my @line = split(':', $linecom);
385 $zone_args{"line"} = $line[1];
386 $zone_args{"name"} = $line[0];
387 $zone_args{"name"} =~ s/$domain//g;
388 $zone_args{"cname"} = $OPTS{"zone_name"};
389 $res = Cpanel::AdminBin::adminfetchnocache( 'zone', '', 'EDIT', 'storable', $OPTS{"zone_name"},
390 __serialize_request( \%zone_args ) );
391 }
392 }
393
394 ## Save the updated global data arg.
395 Cpanel::DataStore::store_ref($cf_data_file, $cf_global_data);
396
397 return $result;
398}
399
400sub api2_fetchzone {
401 my $raw = __fetchzone(@_);
402 my $results = [];
403 my %OPTS = @_;
404 my $domain = $OPTS{'domain'}.".";
405
406 foreach my $res (@{$raw->{"record"}}) {
407 if ((($res->{"type"} eq "CNAME") || ($res->{"type"} eq "A")) &&
408 ($res->{"name"} !~ /(^direct|^ssh|^ftp|ssl|mx|ns[^.]*|imap[^.]*|pop[^.]*|smtp[^.]*|mail[^.]*|mx[^.]*|exchange[^.]*|smtp[^.]*|google[^.]*|secure|sftp|svn|git|irc|email|mobilemail|pda|webmail|^e\.|video|vid|vids|sites|calendar|svn|cvs|git|cpanel|panel|repo|webstats|local|localhost|$cf_host_prefix)/) &&
409 ($res->{"name"} ne $domain) &&
410 ($res->{"cname"} !~ /google.com/)){
411 if ($res->{"cname"} =~ /cdn.cloudflare.net$/) {
412 $res->{"cloudflare"} = 1;
413 } else {
414 $res->{"cloudflare"} = 0;
415 }
416 push @$results, $res;
417 }
418 }
419 return $results;
420}
421
422sub api2_getbasedomains {
423 my %OPTS = @_;
424 __load_data_file($OPTS{"homedir"}, $OPTS{"user"});
425 my $res = Cpanel::DomainLookup::api2_getbasedomains(@_);
426 my $has_cf = 0;
427 foreach my $dom (@$res) {
428 if ($cf_global_data->{"cf_zones"}->{$dom->{"domain"}}) {
429 $dom->{"cloudflare"} = 1;
430 $has_cf = 1;
431 } else {
432 $dom->{"cloudflare"} = 0;
433 }
434 }
435 return {"has_cf" => $has_cf, "res" => $res, "hoster" => $hoster_name};
436}
437
438sub api2 {
439 my $func = shift;
440
441 my %API;
442
443 $API{'user_create'}{'func'} = 'api2_user_create';
444 $API{'user_create'}{'engine'} = 'hasharray';
445 $API{'user_lookup'}{'func'} = 'api2_user_lookup';
446 $API{'user_lookup'}{'engine'} = 'hasharray';
447 $API{'zone_set'}{'func'} = 'api2_zone_set';
448 $API{'zone_set'}{'engine'} = 'hasharray';
449 $API{'zone_delete'}{'func'} = 'api2_zone_delete';
450 $API{'zone_delete'}{'engine'} = 'hasharray';
451 $API{'fetchzone'}{'func'} = 'api2_fetchzone';
452 $API{'fetchzone'}{'engine'} = 'hasharray';
453 $API{'getbasedomains'}{'func'} = 'api2_getbasedomains';
454 $API{'getbasedomains'}{'engine'} = 'hasharray';
455 $API{'zone_get_stats'}{'func'} = 'api2_get_stats';
456 $API{'zone_get_stats'}{'engine'} = 'hasharray';
457 $API{'zone_edit_cf_setting'}{'func'} = 'api2_edit_cf_setting';
458 $API{'zone_edit_cf_setting'}{'engine'} = 'hasharray';
459
460 return ( \%{ $API{$func} } );
461}
462
463## Run the auto-update here
464sub check_auto_update {
465
466 print "Checking CloudFlare for latest version\n";
467 CloudFlare_init();
468 print "Current Version: $cf_cp_version\n";
469
470 my $check_args = {
471 "host" => $cf_host_name,
472 "uri" => $cf_host_uri,
473 "port" => $cf_host_port,
474 "query" => {
475 "act" => "cpanel_info",
476 "host_key" => $cf_host_key,
477 },
478 };
479
480 my $result = JSON::Syck::Load(__https_post_req->($check_args));
481 print "Latest Version: " . $result->{"response"}{"cpanel_latest"} . "\n";
482 if ($result->{"response"}{"cpanel_latest"} > $cf_cp_version) {
483 print "Downloading the latest version.\n";
484 `curl -L https://github.com/cloudflare/CloudFlare-CPanel/tarball/master > /tmp/cloudflare.tar.gz`
485 } else {
486 print "You already have the latest version.\n";
487 }
488}
489
490########## Internal Functions Defined Below #########
491
492sub __load_data_file {
493 my $home_dir = shift;
494 my $user = shift;
495 $cf_data_file = $home_dir . "/" . $cf_data_file_name;
496 if( Cpanel::DataStore::load_ref($cf_data_file, $cf_global_data ) ) {
497 $logger->info("Successfully loaded cf data -- $cf_data_file");
498 } else {
499 ## Try to load the data from the old default data file (if it exists)
500 if (-e $cf_old_data_file_name) {
501 $logger->info( "Failed to load cf data -- Trying to copy from $cf_old_data_file_name for $user");
502 my $tmp_data = {};
503 Cpanel::DataStore::load_ref($cf_old_data_file_name, $tmp_data);
504 $cf_global_data->{"cf_user_tokens"}->{$user} = $tmp_data->{"cf_user_tokens"}->{$user};
505 $cf_global_data->{"cf_zones"} = $tmp_data->{"cf_zones"};
506
507 } else {
508 $cf_global_data = {"cf_zones" => {}};
509 $logger->info( "Failed to load cf data -- storing blank data at $cf_data_file");
510 }
511 Cpanel::DataStore::store_ref($cf_data_file, $cf_global_data);
512 chmod 0600, $cf_data_file;
513 }
514}
515
516sub __fetchzone {
517 my %OPTS = @_;
518 my $domain = $OPTS{'domain'};
519 my $results = Cpanel::AdminBin::adminfetchnocache( 'zone', '', 'FETCH', 'storable', $domain, ($OPTS{'customonly'} ? 1 : 0) );
520
521 if ( ref $results->{'record'} eq 'ARRAY' ) {
522 for(0 .. $#{ $results->{'record'} }) {
523 $results->{'record'}->[$_]->{'record'} = ($results->{'record'}->[$_]->{'address'} || $results->{'record'}->[$_]->{'cname'} || $results->{'record'}->[$_]->{'txtdata'});
524 $results->{'record'}->[$_]->{'line'} = ($results->{'record'}->[$_]->{'Line'});
525 }
526 foreach my $key ( keys %KEYMAP ) {
527 if ( exists $OPTS{$key} && defined $OPTS{$key} ) {
528 my %MULTITYPES = map { $_ => undef } split(/[\|\,]/, $OPTS{$key});
529 @{ $results->{'record'} } = grep { exists $MULTITYPES{$_->{ $KEYMAP{$key} }} } @{ $results->{'record'} };
530 }
531 }
532 }
533
534 return $results;
535}
536
537sub __https_post_req {
538 my ( $args_hr ) = @_;
539 if ($args_hr->{'port'} ne "443") {
540 ## Downgrade to http
541 return __http_post_req($args_hr);
542 } else {
543 my ( $args_hr ) = @_;
544 my ($page, $response, %reply_headers)
545 = post_https($args_hr->{'host'}, $args_hr->{'port'}, $args_hr->{'uri'}, '',
546 make_form(%{$args_hr->{'query'}}));
547 if ($cf_debug_mode) {
548 $logger->info($response);
549 $logger->info($page);
550 }
551 return $page;
552 }
553}
554
555sub __http_post_req {
556 my ( $args_hr ) = @_;
557 my $query = $args_hr->{'query'};
558 if ( ref $args_hr->{'query'} eq 'HASH' ) {
559 $query = '';
560 foreach my $key ( keys %{ $args_hr->{'query'} } ) {
561 if ( ref $args_hr->{'query'}{$key} eq 'ARRAY' ) {
562 for my $val ( @{ $args_hr->{'query'}{$key} } ) {
563 $query .=
564 $query
565 ? "&$key=" . Cpanel::Encoder::URI::uri_encode_str($val)
566 : "$key=" . Cpanel::Encoder::URI::uri_encode_str($val);
567 }
568 }
569 else {
570 $query .=
571 $query
572 ? "&$key=" . Cpanel::Encoder::URI::uri_encode_str( $args_hr->{'query'}{$key} )
573 : "$key=" . Cpanel::Encoder::URI::uri_encode_str( $args_hr->{'query'}{$key} );
574 }
575 }
576 }
577
578 my $postdata_len = length($query);
579
580 if ($cf_debug_mode) {
581 $logger->info($query);
582 }
583
584 my $proto = getprotobyname('tcp');
585 return unless defined $proto;
586
587 socket( my $socket_fh, &Socket::AF_INET, &Socket::SOCK_STREAM, $proto );
588 return unless $socket_fh;
589
590 my $iaddr = gethostbyname($args_hr->{'host'});
591 my $port = $args_hr->{'port'};
592
593 return unless ( defined $iaddr && defined $port );
594
595 my $sin = Socket::sockaddr_in( $port, $iaddr );
596 return unless defined $sin;
597
598 my $result = "";
599 if ( connect( $socket_fh, $sin ) ) {
600 send $socket_fh, "POST /$args_hr->{'uri'} HTTP/1.0\nContent-Length: $postdata_len\nContent-Type: application/x-www-form-urlencoded\nHost: $args_hr->{'host'}\n\n$query", 0;
601
602 my $in_header = 1;
603 while (<$socket_fh>) {
604 if ( /^\n$/ || /^\r\n$/ || /^$/ ) {
605 $in_header = 0;
606 next;
607 }
608
609 if (!$in_header) {
610 $result .= $_;
611 }
612 }
613 }
614
615 close $socket_fh;
616
617 if ($cf_debug_mode) {
618 $logger->info($result);
619 }
620
621 return $result;
622}
623
624sub __serialize_request {
625 my $opt_ref = shift;
626 my @KEYLIST;
627 foreach my $opt ( keys %$opt_ref ) {
628 push @KEYLIST, Cpanel::Encoder::URI::uri_encode_str($opt) . '=' . Cpanel::Encoder::URI::uri_encode_str( $opt_ref->{$opt} );
629 }
630 return join( '&', @KEYLIST );
631}
632
633## Are we running from the command line?
634if ($ARGV[0] eq "check") {
635 check_auto_update();
636}
637
6381; # Ah, perl.