return $mask;
}
-register_format('CIDR', \&pve_verify_cidr);
-sub pve_verify_cidr {
+register_format('CIDRv6', \&pve_verify_cidrv6);
+sub pve_verify_cidrv6 {
my ($cidr, $noerr) = @_;
- if ($cidr =~ m!^(?:$IPV4RE)(?:/(\d+))$! && ($1 > 7) && ($1 < 32)) {
+ if ($cidr =~ m!^(?:$IPV6RE)(?:/(\d+))$! && ($1 > 7) && ($1 <= 120)) {
return $cidr;
- } elsif ($cidr =~ m!^(?:$IPV6RE)(?:/(\d+))$! && ($1 > 7) && ($1 <= 120)) {
+ }
+
+ return undef if $noerr;
+ die "value does not look like a valid IPv6 CIDR network\n";
+}
+
+register_format('CIDRv4', \&pve_verify_cidrv4);
+sub pve_verify_cidrv4 {
+ my ($cidr, $noerr) = @_;
+
+ if ($cidr =~ m!^(?:$IPV4RE)(?:/(\d+))$! && ($1 > 7) && ($1 < 32)) {
return $cidr;
}
return undef if $noerr;
- die "value does not look like a valid CIDR network\n";
+ die "value does not look like a valid IPv4 CIDR network\n";
+}
+
+register_format('CIDR', \&pve_verify_cidr);
+sub pve_verify_cidr {
+ my ($cidr, $noerr) = @_;
+
+ if (!(pve_verify_cidrv4($cidr, 1) ||
+ pve_verify_cidrv6($cidr, 1)))
+ {
+ return undef if $noerr;
+ die "value does not look like a valid CIDR network\n";
+ }
+
+ return $cidr;
+}
+
+register_format('pve-ipv4-config', \&pve_verify_ipv4_config);
+sub pve_verify_ipv4_config {
+ my ($config, $noerr) = @_;
+
+ return $config if $config =~ /^(?:dhcp|manual)$/ ||
+ pve_verify_cidrv4($config, 1);
+ return undef if $noerr;
+ die "value does not look like a valid ipv4 network configuration\n";
+}
+
+register_format('pve-ipv6-config', \&pve_verify_ipv6_config);
+sub pve_verify_ipv6_config {
+ my ($config, $noerr) = @_;
+
+ return $config if $config =~ /^(?:auto|dhcp|manual)$/ ||
+ pve_verify_cidrv6($config, 1);
+ return undef if $noerr;
+ die "value does not look like a valid ipv6 network configuration\n";
}
register_format('email', \&pve_verify_email);
sub pve_verify_email {
my ($email, $noerr) = @_;
- # we use same regex as extjs Ext.form.VTypes.email
- if ($email !~ /^(\w+)([\-+.][\w]+)*@(\w[\-\w]*\.){1,5}([A-Za-z]){2,6}$/) {
+ # we use same regex as in Utils.js
+ if ($email !~ /^(\w+)([\-+.][\w]+)*@(\w[\-\w]*\.){1,5}([A-Za-z]){2,63}$/) {
return undef if $noerr;
die "value does not look like a valid email address\n";
}
});
sub check_format {
- my ($format, $value) = @_;
+ my ($format, $value, $path) = @_;
+ return parse_property_string($format, $value, $path) if ref($format) eq 'HASH';
return if $format eq 'regex';
if ($format =~ m/^(.*)-a?list$/) {
die "undefined format '$format'\n" if !$code;
+ return parse_property_string($code, $value, $path) if ref($code) eq 'HASH';
&$code($value);
}
}
+sub parse_property_string {
+ my ($format, $data, $path) = @_;
+
+ my $default_key;
+
+ my $res = {};
+ foreach my $part (split(/,/, $data)) {
+ next if $part =~ /^\s*$/;
+
+ if ($part =~ /^([^=]+)=(.+)$/) {
+ my ($k, $v) = ($1, $2);
+ die "duplicate key in comma-separated list property: $k" if defined($res->{$k});
+ my $schema = $format->{$k};
+ die "invalid key in comma-separated list property: $k" if !$schema;
+ if ($schema->{type} && $schema->{type} eq 'boolean') {
+ $v = 1 if $v =~ m/^(1|on|yes|true)$/i;
+ $v = 0 if $v =~ m/^(0|off|no|false)$/i;
+ }
+ $res->{$k} = $v;
+ } elsif ($part !~ /=/) {
+ die "duplicate key in comma-separated list property: $default_key" if $default_key;
+ foreach my $key (keys %$format) {
+ if ($format->{$key}->{default_key}) {
+ $default_key = $key;
+ if (!$res->{$default_key}) {
+ $res->{$default_key} = $part;
+ last;
+ }
+ die "duplicate key in comma-separated list property: $default_key";
+ }
+ }
+ } else {
+ die "missing key in comma-separated list property";
+ }
+ }
+
+ my $errors = {};
+ check_object($path, $format, $res, undef, $errors);
+ if (scalar(%$errors)) {
+ raise "format error", errors => $errors;
+ }
+
+ return $res;
+}
+
sub add_error {
my ($errors, $path, $msg) = @_;
}
}
+sub check_object_warn {
+ my ($path, $schema, $value, $additional_properties) = @_;
+ my $errors = {};
+ check_object($path, $schema, $value, $additional_properties, $errors);
+ if (scalar(%$errors)) {
+ foreach my $k (keys %$errors) {
+ warn "parse error: $k: $errors->{$k}\n";
+ }
+ return 0;
+ }
+ return 1;
+}
+
sub check_prop {
my ($value, $schema, $path, $errors) = @_;
} else {
if (my $format = $schema->{format}) {
- eval { check_format($format, $value); };
+ eval { check_format($format, $value, $path); };
if ($@) {
add_error($errors, $path, "invalid format - $@");
return;
optional => 1,
description => "This provides a description of the purpose the instance property. The value can be a string or it can be an object with properties corresponding to various different instance languages (with an optional default property indicating the default description).",
},
+ format_description => {
+ type => "string",
+ optional => 1,
+ description => "This provides a shorter (usually just one word) description for a property used to generate descriptions for comma separated list property strings.",
+ },
title => {
type => "string",
optional => 1,
description => "indicates a required property or a schema that must be validated if this property is present",
},
format => {
- type => "string",
+ type => [ "string", "object" ],
optional => 1,
description => "This indicates what format the data is among some predefined formats which may include:\n\ndate - a string following the ISO format \naddress \nschema - a schema definition object \nperson \npage \nhtml - a string representing HTML",
},
+ default_key => {
+ type => "boolean",
+ optional => 1,
+ description => "Whether this is the default key in a comma separated list property string.",
+ },
default => {
type => "any",
optional => 1,
description => "JSON Schema for parameters.",
optional => 1,
},
+ formatter => {
+ type => 'object',
+ description => "Used to store page formatter information (set by PVE::RESTHandler->register_page_formatter).",
+ optional => 1,
+ },
returns => {
type => 'object',
description => "JSON Schema for return value.",
}
}
+ Getopt::Long::Configure('prefix_pattern=(--|-)');
+
my $opts = {};
raise("unable to parse option\n", code => HTTP_BAD_REQUEST)
if !Getopt::Long::GetOptionsFromArray($args, $opts, @getopt);
if ($pd->{format} =~ m/-list/) {
# allow --vmid 100 --vmid 101 and --vmid 100,101
# allow --dow mon --dow fri and --dow mon,fri
- $opts->{$p} = join(",", @{$opts->{$p}});
+ $opts->{$p} = join(",", @{$opts->{$p}}) if ref($opts->{$p}) eq 'ARRAY';
} elsif ($pd->{format} =~ m/-alist/) {
# we encode array as \0 separated strings
# Note: CGI.pm also use this encoding
return $data;
}
+sub generate_typetext {
+ my ($schema) = @_;
+ my $typetext = '';
+ my (@optional, @required);
+ foreach my $key (sort keys %$schema) {
+ next if !$schema->{$key}->{format_description} &&
+ !$schema->{$key}->{typetext};
+ if ($schema->{$key}->{optional}) {
+ push @optional, $key;
+ } else {
+ push @required, $key;
+ }
+ }
+ my ($pre, $post) = ('', '');
+ my $add = sub {
+ my ($key) = @_;
+ $typetext .= $pre;
+ my $entry = $schema->{$key};
+ if (my $desc = $entry->{format_description}) {
+ $typetext .= $entry->{default_key} ? "[$key=]" : "$key=";
+ $typetext .= "<$desc>";
+ } elsif (my $text = $entry->{typetext}) {
+ $typetext .= $text;
+ } else {
+ die "internal error: neither format_description nor typetext found";
+ }
+ $typetext .= $post;
+ };
+ foreach my $key (@required) {
+ &$add($key);
+ $pre = ', ';
+ }
+ $pre = $pre ? ' [,' : '[';
+ $post = ']';
+ foreach my $key (@optional) {
+ &$add($key);
+ $pre = ' [,';
+ }
+ return $typetext;
+}
+
1;