Commit c0fc50d3 authored by jocuri%softhome.net's avatar jocuri%softhome.net

Patch for bug 237369: implement relatively simple changes from %FORM to…

Patch for bug 237369: implement relatively simple changes from %FORM to $cgi->param variable; patch by Teemu Mannermaa <wicked@etlicon.fi>; r=kiko, justdave; a=justdave.
parent cecc6432
...@@ -53,8 +53,6 @@ use vars ...@@ -53,8 +53,6 @@ use vars
@legal_target_milestone @legal_target_milestone
@legal_versions @legal_versions
@legal_keywords @legal_keywords
%FORM
); );
# Use the global template variables defined in globals.pl # Use the global template variables defined in globals.pl
...@@ -92,7 +90,9 @@ $vars->{'field'} = [GetFieldDefs()]; ...@@ -92,7 +90,9 @@ $vars->{'field'} = [GetFieldDefs()];
# Determine how the user would like to receive the output; # Determine how the user would like to receive the output;
# default is JavaScript. # default is JavaScript.
my $format = GetFormat("config", $::FORM{'format'}, $::FORM{'ctype'} || "js"); my $cgi = Bugzilla->cgi;
my $format = GetFormat("config", scalar($cgi->param('format')),
scalar($cgi->param('ctype')) || "js");
# Return HTTP headers. # Return HTTP headers.
print "Content-Type: $format->{'ctype'}\n\n"; print "Content-Type: $format->{'ctype'}\n\n";
......
...@@ -22,7 +22,6 @@ ...@@ -22,7 +22,6 @@
# Bradley Baetz <bbaetz@student.usyd.edu.au> # Bradley Baetz <bbaetz@student.usyd.edu.au>
use vars qw( use vars qw(
%FORM
%legal_product %legal_product
$userid $userid
); );
...@@ -41,8 +40,9 @@ quietly_check_login(); ...@@ -41,8 +40,9 @@ quietly_check_login();
GetVersionTable(); GetVersionTable();
my $cgi = Bugzilla->cgi; my $cgi = Bugzilla->cgi;
my $product = $cgi->param('product');
if (!defined $::FORM{'product'}) { if (!$product) {
# Reference to a subset of %::proddesc, which the user is allowed to see # Reference to a subset of %::proddesc, which the user is allowed to see
my %products; my %products;
...@@ -73,11 +73,9 @@ if (!defined $::FORM{'product'}) { ...@@ -73,11 +73,9 @@ if (!defined $::FORM{'product'}) {
exit; exit;
} }
$::FORM{'product'} = (keys %products)[0]; $product = (keys %products)[0];
} }
my $product = $::FORM{'product'};
# Make sure the user specified a valid product name. Note that # Make sure the user specified a valid product name. Note that
# if the user specifies a valid product name but is not authorized # if the user specifies a valid product name but is not authorized
# to access that product, they will receive a different error message # to access that product, they will receive a different error message
......
...@@ -30,8 +30,6 @@ use Bugzilla::Config qw(:DEFAULT :admin $datadir); ...@@ -30,8 +30,6 @@ use Bugzilla::Config qw(:DEFAULT :admin $datadir);
require "CGI.pl"; require "CGI.pl";
use vars %::MFORM;
ConnectToDatabase(); ConnectToDatabase();
confirm_login(); confirm_login();
...@@ -52,13 +50,13 @@ my $howto = ""; ...@@ -52,13 +50,13 @@ my $howto = "";
foreach my $i (GetParamList()) { foreach my $i (GetParamList()) {
my $name = $i->{'name'}; my $name = $i->{'name'};
my $value = $::FORM{$name}; my $value = $cgi->param($name);
if (exists $::FORM{"reset-$name"}) { if (defined $cgi->param("reset-$name")) {
$value = $i->{'default'}; $value = $i->{'default'};
} else { } else {
if ($i->{'type'} eq 'm') { if ($i->{'type'} eq 'm') {
# This simplifies the code below # This simplifies the code below
$value = \@{$::MFORM{$name}}; $value = [ $cgi->param($name) ];
} else { } else {
# Get rid of windows/mac-style line endings. # Get rid of windows/mac-style line endings.
$value =~ s/\r\n?/\n/g; $value =~ s/\r\n?/\n/g;
......
...@@ -32,8 +32,6 @@ use lib qw(.); ...@@ -32,8 +32,6 @@ use lib qw(.);
require "globals.pl"; require "globals.pl";
require "CGI.pl"; require "CGI.pl";
use vars qw($buffer);
use Bugzilla; use Bugzilla;
use Bugzilla::Search; use Bugzilla::Search;
use Bugzilla::Config qw(:DEFAULT $datadir); use Bugzilla::Config qw(:DEFAULT $datadir);
...@@ -44,8 +42,8 @@ my $cgi = Bugzilla->cgi; ...@@ -44,8 +42,8 @@ my $cgi = Bugzilla->cgi;
# Go directly to the XUL version of the duplicates report (duplicates.xul) # Go directly to the XUL version of the duplicates report (duplicates.xul)
# if the user specified ctype=xul. Adds params if they exist, and directs # if the user specified ctype=xul. Adds params if they exist, and directs
# the user to a signed copy of the script in duplicates.jar if it exists. # the user to a signed copy of the script in duplicates.jar if it exists.
if ($::FORM{'ctype'} && $::FORM{'ctype'} eq "xul") { if (defined $cgi->param('ctype') && $cgi->param('ctype') eq "xul") {
my $params = CanonicaliseParams($::buffer, ["format", "ctype"]); my $params = CanonicaliseParams($cgi->query_string(), ["format", "ctype"]);
my $url = (-e "duplicates.jar" ? "duplicates.jar!/" : "") . my $url = (-e "duplicates.jar" ? "duplicates.jar!/" : "") .
"duplicates.xul" . ($params ? "?$params" : "") . "\n\n"; "duplicates.xul" . ($params ? "?$params" : "") . "\n\n";
...@@ -71,7 +69,7 @@ else { ...@@ -71,7 +69,7 @@ else {
Bugzilla->switch_to_shadow_db(); Bugzilla->switch_to_shadow_db();
use vars qw (%FORM $userid @legal_product); use vars qw ($userid @legal_product);
my %dbmcount; my %dbmcount;
my %count; my %count;
...@@ -80,7 +78,7 @@ my %before; ...@@ -80,7 +78,7 @@ my %before;
# Get params from URL # Get params from URL
sub formvalue { sub formvalue {
my ($name, $default) = (@_); my ($name, $default) = (@_);
return $FORM{$name} || $default || ""; return $cgi->param($name) || $default || "";
} }
my $sortby = formvalue("sortby"); my $sortby = formvalue("sortby");
...@@ -218,7 +216,7 @@ if (scalar(%count)) { ...@@ -218,7 +216,7 @@ if (scalar(%count)) {
} }
# Restrict to product if requested # Restrict to product if requested
if ($::FORM{'product'}) { if ($cgi->param('product')) {
$params->param('product', join(',', @query_products)); $params->param('product', join(',', @query_products));
} }
...@@ -267,13 +265,13 @@ $vars->{'changedsince'} = $changedsince; ...@@ -267,13 +265,13 @@ $vars->{'changedsince'} = $changedsince;
$vars->{'maxrows'} = $maxrows; $vars->{'maxrows'} = $maxrows;
$vars->{'openonly'} = $openonly; $vars->{'openonly'} = $openonly;
$vars->{'reverse'} = $reverse; $vars->{'reverse'} = $reverse;
$vars->{'format'} = $::FORM{'format'}; $vars->{'format'} = $cgi->param('format');
$vars->{'query_products'} = \@query_products; $vars->{'query_products'} = \@query_products;
$vars->{'products'} = \@::legal_product; $vars->{'products'} = \@::legal_product;
my $format = my $format = GetFormat("reports/duplicates", scalar($cgi->param('format')),
GetFormat("reports/duplicates", $::FORM{'format'}, $::FORM{'ctype'}); scalar($cgi->param('ctype')));
print $cgi->header($format->{'ctype'}); print $cgi->header($format->{'ctype'});
......
...@@ -1474,7 +1474,8 @@ sub FormatTimeUnit { ...@@ -1474,7 +1474,8 @@ sub FormatTimeUnit {
# Constructs a format object from URL parameters. You most commonly call it # Constructs a format object from URL parameters. You most commonly call it
# like this: # like this:
# my $format = GetFormat("foo/bar", $::FORM{'format'}, $::FORM{'ctype'}); # my $format = GetFormat("foo/bar", scalar($cgi->param('format')),
# scalar($cgi->param('ctype')));
sub GetFormat { sub GetFormat {
my ($template, $format, $ctype) = @_; my ($template, $format, $ctype) = @_;
......
...@@ -28,7 +28,7 @@ use Bugzilla; ...@@ -28,7 +28,7 @@ use Bugzilla;
require "CGI.pl"; require "CGI.pl";
use vars qw($userid @legal_keywords %FORM); use vars qw($userid @legal_keywords);
# Use global template variables. # Use global template variables.
use vars qw($template $vars); use vars qw($template $vars);
...@@ -69,9 +69,9 @@ my $generic_query = " ...@@ -69,9 +69,9 @@ my $generic_query = "
WHERE assign.userid = bugs.assigned_to AND report.userid = bugs.reporter WHERE assign.userid = bugs.assigned_to AND report.userid = bugs.reporter
AND bugs.product_id=products.id AND bugs.component_id=components.id"; AND bugs.product_id=products.id AND bugs.component_id=components.id";
my $buglist = $::FORM{'buglist'} || my $buglist = $cgi->param('buglist') ||
$::FORM{'bug_id'} || $cgi->param('bug_id') ||
$::FORM{'id'} || ""; $cgi->param('id') || "";
my @bugs; my @bugs;
......
...@@ -88,7 +88,7 @@ if (defined($height)) { ...@@ -88,7 +88,7 @@ if (defined($height)) {
# These shenanigans are necessary to make sure that both vertical and # These shenanigans are necessary to make sure that both vertical and
# horizontal 1D tables convert to the correct dimension when you ask to # horizontal 1D tables convert to the correct dimension when you ask to
# display them as some sort of chart. # display them as some sort of chart.
if ($::FORM{'format'} && $::FORM{'format'} eq "table") { if (defined $cgi->param('format') && $cgi->param('format') eq "table") {
if ($col_field && !$row_field) { if ($col_field && !$row_field) {
# 1D *tables* should be displayed vertically (with a row_field only) # 1D *tables* should be displayed vertically (with a row_field only)
$row_field = $col_field; $row_field = $col_field;
...@@ -256,7 +256,7 @@ $vars->{'width'} = $width if $width; ...@@ -256,7 +256,7 @@ $vars->{'width'} = $width if $width;
$vars->{'height'} = $height if $height; $vars->{'height'} = $height if $height;
$vars->{'query'} = $query; $vars->{'query'} = $query;
$vars->{'debug'} = $::FORM{'debug'}; $vars->{'debug'} = $cgi->param('debug');
my $formatparam = $cgi->param('format'); my $formatparam = $cgi->param('format');
...@@ -306,7 +306,7 @@ my $format = GetFormat("reports/report", $formatparam, $cgi->param('ctype')); ...@@ -306,7 +306,7 @@ my $format = GetFormat("reports/report", $formatparam, $cgi->param('ctype'));
# If we get a template or CGI error, it comes out as HTML, which isn't valid # If we get a template or CGI error, it comes out as HTML, which isn't valid
# PNG data, and the browser just displays a "corrupt PNG" message. So, you can # PNG data, and the browser just displays a "corrupt PNG" message. So, you can
# set debug=1 to always get an HTML content-type, and view the error. # set debug=1 to always get an HTML content-type, and view the error.
$format->{'ctype'} = "text/html" if $::FORM{'debug'}; $format->{'ctype'} = "text/html" if $cgi->param('debug');
my @time = localtime(time()); my @time = localtime(time());
my $date = sprintf "%04d-%02d-%02d", 1900+$time[5],$time[4]+1,$time[3]; my $date = sprintf "%04d-%02d-%02d", 1900+$time[5],$time[4]+1,$time[3];
...@@ -316,7 +316,7 @@ print $cgi->header(-type => $format->{'ctype'}, ...@@ -316,7 +316,7 @@ print $cgi->header(-type => $format->{'ctype'},
# Problems with this CGI are often due to malformed data. Setting debug=1 # Problems with this CGI are often due to malformed data. Setting debug=1
# prints out both data structures. # prints out both data structures.
if ($::FORM{'debug'}) { if ($cgi->param('debug')) {
require Data::Dumper; require Data::Dumper;
print "<pre>data hash:\n"; print "<pre>data hash:\n";
print Data::Dumper::Dumper(%data) . "\n\n"; print Data::Dumper::Dumper(%data) . "\n\n";
......
...@@ -40,7 +40,6 @@ use lib qw(.); ...@@ -40,7 +40,6 @@ use lib qw(.);
use Bugzilla::Config qw(:DEFAULT $datadir); use Bugzilla::Config qw(:DEFAULT $datadir);
require "CGI.pl"; require "CGI.pl";
use vars qw(%FORM); # globals from CGI.pl
require "globals.pl"; require "globals.pl";
use vars qw(@legal_product); # globals from er, globals.pl use vars qw(@legal_product); # globals from er, globals.pl
...@@ -71,7 +70,7 @@ my @myproducts; ...@@ -71,7 +70,7 @@ my @myproducts;
push( @myproducts, "-All-"); push( @myproducts, "-All-");
push( @myproducts, GetSelectableProducts()); push( @myproducts, GetSelectableProducts());
if (! defined $FORM{'product'}) { if (! defined $cgi->param('product')) {
print $cgi->header(); print $cgi->header();
PutHeader("Bug Charts"); PutHeader("Bug Charts");
...@@ -79,29 +78,29 @@ if (! defined $FORM{'product'}) { ...@@ -79,29 +78,29 @@ if (! defined $FORM{'product'}) {
PutFooter(); PutFooter();
} else { } else {
my $product = $cgi->param('product');
# For security and correctness, validate the value of the "product" form variable. # For security and correctness, validate the value of the "product" form variable.
# Valid values are those products for which the user has permissions which appear # Valid values are those products for which the user has permissions which appear
# in the "product" drop-down menu on the report generation form. # in the "product" drop-down menu on the report generation form.
grep($_ eq $FORM{'product'}, @myproducts) grep($_ eq $product, @myproducts)
|| ThrowUserError("invalid_product_name", {product => $FORM{'product'}}); || ThrowUserError("invalid_product_name", {product => $product});
# We don't want people to be able to view # We don't want people to be able to view
# reports for products they don't have permissions for... # reports for products they don't have permissions for...
if (($FORM{'product'} ne '-All-') if (($product ne '-All-') && (!CanEnterProduct($product))) {
&& (!CanEnterProduct($FORM{'product'}))) {
ThrowUserError("report_access_denied"); ThrowUserError("report_access_denied");
} }
# We've checked that the product exists, and that the user can see it # We've checked that the product exists, and that the user can see it
# This means that is OK to detaint # This means that is OK to detaint
trick_taint($FORM{'product'}); trick_taint($product);
print $cgi->header(-Content_Disposition=>'inline; filename=bugzilla_report.html'); print $cgi->header(-Content_Disposition=>'inline; filename=bugzilla_report.html');
PutHeader("Bug Charts"); PutHeader("Bug Charts");
show_chart(); show_chart($product);
PutFooter(); PutFooter();
} }
...@@ -189,21 +188,25 @@ sub daily_stats_filename { ...@@ -189,21 +188,25 @@ sub daily_stats_filename {
} }
sub show_chart { sub show_chart {
if (! $FORM{datasets}) { my ($product) = @_;
if (! defined $cgi->param('datasets')) {
ThrowUserError("missing_datasets"); ThrowUserError("missing_datasets");
} }
my $datasets = join('', $cgi->param('datasets'));
print <<FIN; print <<FIN;
<center> <center>
FIN FIN
my $type = chart_image_type(); my $type = chart_image_type();
my $data_file = daily_stats_filename($FORM{product}); my $data_file = daily_stats_filename($product);
my $image_file = chart_image_name($data_file, $type); my $image_file = chart_image_name($data_file, $type, $datasets);
my $url_image = "$graph_dir/" . url_quote($image_file); my $url_image = "$graph_dir/" . url_quote($image_file);
if (! -e "$graph_dir/$image_file") { if (! -e "$graph_dir/$image_file") {
generate_chart("$dir/$data_file", "$graph_dir/$image_file", $type); generate_chart("$dir/$data_file", "$graph_dir/$image_file", $type,
$product, $datasets);
} }
print <<FIN; print <<FIN;
...@@ -223,7 +226,7 @@ sub chart_image_type { ...@@ -223,7 +226,7 @@ sub chart_image_type {
} }
sub chart_image_name { sub chart_image_name {
my ($data_file, $type) = @_; my ($data_file, $type, $datasets) = @_;
# This routine generates a filename from the requested fields. The problem # This routine generates a filename from the requested fields. The problem
# is that we have to check the safety of doing this. We can't just require # is that we have to check the safety of doing this. We can't just require
...@@ -232,15 +235,16 @@ sub chart_image_name { ...@@ -232,15 +235,16 @@ sub chart_image_name {
# Instead, just require that each field name consists only of letters # Instead, just require that each field name consists only of letters
# and number # and number
if ($FORM{'datasets'} !~ m/[A-Za-z0-9:]/) { if ($datasets !~ m/[A-Za-z0-9:]/) {
die "Invalid datasets $FORM{'datasets'}"; die "Invalid datasets $datasets";
} }
# Since we pass the tests, consider it OK # Since we pass the tests, consider it OK
trick_taint($FORM{'datasets'}); trick_taint($datasets);
# Cache charts by generating a unique filename based on what they # Cache charts by generating a unique filename based on what they
# show. Charts should be deleted by collectstats.pl nightly. # show. Charts should be deleted by collectstats.pl nightly.
my $id = join ("_", split (":", $FORM{datasets})); my $id = join ("_", split (":", $datasets));
return "${data_file}_${id}.$type"; return "${data_file}_${id}.$type";
} }
...@@ -253,7 +257,7 @@ sub day_of_year { ...@@ -253,7 +257,7 @@ sub day_of_year {
} }
sub generate_chart { sub generate_chart {
my ($data_file, $image_file, $type) = @_; my ($data_file, $image_file, $type, $product, $datasets) = @_;
if (! open FILE, $data_file) { if (! open FILE, $data_file) {
ThrowCodeError("chart_data_not_generated"); ThrowCodeError("chart_data_not_generated");
...@@ -261,7 +265,7 @@ sub generate_chart { ...@@ -261,7 +265,7 @@ sub generate_chart {
my @fields; my @fields;
my @labels = qw(DATE); my @labels = qw(DATE);
my %datasets = map { $_ => 1 } split /:/, $FORM{datasets}; my %datasets = map { $_ => 1 } split /:/, $datasets;
my %data = (); my %data = ();
while (<FILE>) { while (<FILE>) {
...@@ -318,7 +322,7 @@ sub generate_chart { ...@@ -318,7 +322,7 @@ sub generate_chart {
my %settings = my %settings =
( (
"title" => "Status Counts for $FORM{'product'}", "title" => "Status Counts for $product",
"x_label" => "Dates", "x_label" => "Dates",
"y_label" => "Bug Counts", "y_label" => "Bug Counts",
"legend_labels" => \@labels, "legend_labels" => \@labels,
......
...@@ -100,11 +100,11 @@ sub AddLink { ...@@ -100,11 +100,11 @@ sub AddLink {
} }
} }
$::FORM{'rankdir'} = "LR" if !defined $::FORM{'rankdir'}; my $rankdir = $cgi->param('rankdir') || "LR";
if (!defined($::FORM{'id'}) && !defined($::FORM{'doall'})) { if (!defined $cgi->param('id') && !defined $cgi->param('doall')) {
ThrowCodeError("missing_bug_id"); ThrowCodeError("missing_bug_id");
} }
my ($fh, $filename) = File::Temp::tempfile("XXXXXXXXXX", my ($fh, $filename) = File::Temp::tempfile("XXXXXXXXXX",
SUFFIX => '.dot', SUFFIX => '.dot',
...@@ -113,13 +113,13 @@ my $urlbase = Param('urlbase'); ...@@ -113,13 +113,13 @@ my $urlbase = Param('urlbase');
print $fh "digraph G {"; print $fh "digraph G {";
print $fh qq{ print $fh qq{
graph [URL="${urlbase}query.cgi", rankdir=$::FORM{'rankdir'}, size="64,64"] graph [URL="${urlbase}query.cgi", rankdir=$rankdir, size="64,64"]
node [URL="${urlbase}show_bug.cgi?id=\\N", style=filled, color=lightgrey] node [URL="${urlbase}show_bug.cgi?id=\\N", style=filled, color=lightgrey]
}; };
my %baselist; my %baselist;
if ($::FORM{'doall'}) { if ($cgi->param('doall')) {
SendSQL("SELECT blocked, dependson FROM dependencies"); SendSQL("SELECT blocked, dependson FROM dependencies");
while (MoreSQLData()) { while (MoreSQLData()) {
...@@ -127,7 +127,7 @@ if ($::FORM{'doall'}) { ...@@ -127,7 +127,7 @@ if ($::FORM{'doall'}) {
AddLink($blocked, $dependson, $fh); AddLink($blocked, $dependson, $fh);
} }
} else { } else {
foreach my $i (split('[\s,]+', $::FORM{'id'})) { foreach my $i (split('[\s,]+', $cgi->param('id'))) {
$i = trim($i); $i = trim($i);
ValidateBugID($i); ValidateBugID($i);
$baselist{$i} = 1; $baselist{$i} = 1;
...@@ -179,7 +179,7 @@ foreach my $k (keys(%seen)) { ...@@ -179,7 +179,7 @@ foreach my $k (keys(%seen)) {
my @params; my @params;
if ($summary ne "" && $::FORM{'showsummary'}) { if ($summary ne "" && $cgi->param('showsummary')) {
$summary =~ s/([\\\"])/\\$1/g; $summary =~ s/([\\\"])/\\$1/g;
push(@params, qq{label="$k\\n$summary"}); push(@params, qq{label="$k\\n$summary"});
} }
...@@ -205,7 +205,7 @@ foreach my $k (keys(%seen)) { ...@@ -205,7 +205,7 @@ foreach my $k (keys(%seen)) {
# Show the bug summary in tooltips only if not shown on # Show the bug summary in tooltips only if not shown on
# the graph and it is non-empty (the user can see the bug) # the graph and it is non-empty (the user can see the bug)
if (!$::FORM{'showsummary'} && $summary ne "") { if (!$cgi->param('showsummary') && $summary ne "") {
$bugtitles{$k} .= " - $summary"; $bugtitles{$k} .= " - $summary";
} }
} }
...@@ -271,11 +271,11 @@ foreach my $f (@files) ...@@ -271,11 +271,11 @@ foreach my $f (@files)
} }
} }
$vars->{'bug_id'} = $::FORM{'id'}; $vars->{'bug_id'} = $cgi->param('id');
$vars->{'multiple_bugs'} = ($::FORM{'id'} =~ /[ ,]/); $vars->{'multiple_bugs'} = ($cgi->param('id') =~ /[ ,]/);
$vars->{'doall'} = $::FORM{'doall'}; $vars->{'doall'} = $cgi->param('doall');
$vars->{'rankdir'} = $::FORM{'rankdir'}; $vars->{'rankdir'} = $rankdir;
$vars->{'showsummary'} = $::FORM{'showsummary'}; $vars->{'showsummary'} = $cgi->param('showsummary');
# Generate and return the UI (HTML page) from the appropriate template. # Generate and return the UI (HTML page) from the appropriate template.
print $cgi->header(); print $cgi->header();
......
...@@ -31,8 +31,6 @@ require "CGI.pl"; ...@@ -31,8 +31,6 @@ require "CGI.pl";
# Use global template variables. # Use global template variables.
use vars qw($template $vars); use vars qw($template $vars);
use vars %::FORM;
ConnectToDatabase(); ConnectToDatabase();
quietly_check_login(); quietly_check_login();
...@@ -52,12 +50,12 @@ $::userid = $::userid; ...@@ -52,12 +50,12 @@ $::userid = $::userid;
# Make sure the bug ID is a positive integer representing an existing # Make sure the bug ID is a positive integer representing an existing
# bug that the user is authorized to access. # bug that the user is authorized to access.
ValidateBugID($::FORM{'id'}); my $id = $cgi->param('id');
my $id = $::FORM{'id'}; ValidateBugID($id);
my $hide_resolved = $::FORM{'hide_resolved'} ? 1 : 0; my $hide_resolved = $cgi->param('hide_resolved') ? 1 : 0;
my $maxdepth = $::FORM{'maxdepth'} || 0; my $maxdepth = $cgi->param('maxdepth') || 0;
if ($maxdepth !~ /^\d+$/) { $maxdepth = 0 }; if ($maxdepth !~ /^\d+$/) { $maxdepth = 0 };
################################################################################ ################################################################################
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment