source: trunk/npemap.org.uk/cgi/submit.fcgi @ 41

Last change on this file since 41 was 41, checked in by David Sheldon, 15 years ago

Trim whitespace from submitted postcode.

Change default back link.

Change the DB link to connect to my DB sorry, we should probably extract this into a config file.

  • Property svn:executable set to *
File size: 5.2 KB
Line 
1#!/usr/bin/perl
2
3use strict;
4use warnings;
5
6use DBI;
7use CGI::Fast qw/:standard -debug/;
8use Geo::Postcode;
9use Date::Format;
10
11sub print_err;
12sub setup_dbh;
13
14# Set up database handler to try and make sure it's ready for the first
15# request
16# No point in handling errors here since they'll get handled by the request
17# handler
18my $dbh;
19setup_dbh();
20
21
22my @fields = qw(easting northing postcode);
23my $returnBaseURL = 'http://www.npemap.org.uk';
24
25my $cgi;
26# Process incoming requests
27REQUEST: while ($cgi = new CGI::Fast) {
28
29    # If we're given return URL parameters, basic sanity check to stop
30    # funny business
31   
32    my $returnlink = '<a href="/tiles/map.html?' .int($cgi->param('easting')/1000). ",".int($cgi->param('northing')/1000)  . ',1">Go back</a>';   
33
34    if (defined $cgi->param('returnX') and ($cgi->param('returnX') =~ /\d+/) and
35        defined $cgi->param('returnY') and ($cgi->param('returnY') =~ /\d+/) ) {
36        $returnlink = '<a href="' . $returnBaseURL . '/tiles/map.html?' . $cgi->param('returnX') . ',' . $cgi->param('returnY') . ',1">Go back to the map</a>';
37    }
38
39
40    # In case the database went away, make sure we have a connection
41    unless (setup_dbh()) {
42        print_err('Error setting up database connection', $returnlink);
43        next REQUEST;
44    }
45
46    my ($easting, $northing);
47
48    # Input validation
49    foreach my $field (@fields) {
50        unless (defined $cgi->param($field)) {
51            print_err ("Parameter '$field' missing", $returnlink);
52            next REQUEST;
53        }
54    }
55
56    # Is the Easting in a valid range?
57    if (($cgi->param('easting') > 700000) or
58        ($cgi->param('easting') < 0)) {
59        print_err ("Parameter 'easting' must be an integer between 0 and 700,000", $returnlink);
60        next REQUEST;
61    } else {
62        $easting = $cgi->param('easting');
63    }
64
65    # Is the Northing in a valid range?
66    if (($cgi->param('northing') > 1300000) or
67        ($cgi->param('northing') < 0)) {
68        print_err("Parameter 'northing' must be an integer between 0 and 1,300,000", $returnlink);
69        next REQUEST;
70    } else {
71        $northing = $cgi->param('northing');
72    }
73
74    my $sth;
75
76    # Now validate the postcode input format
77    my $raw_postcode = $cgi->param('postcode');
78    my $trimmed_postcode = $raw_postcode; 
79    $trimmed_postcode =~ s/^\s+//;
80    $trimmed_postcode =~ s/\s+$//;
81    my $postcode = Geo::Postcode->new($trimmed_postcode);
82    my ($first, $second, $third, $fourth) = @{$postcode->fragments};
83    my ($outward, $inward);
84
85    if ($postcode->valid) {
86        # We have a complete postcode; input it straight into the database
87        $outward = $first . $second;
88        $inward = $third . $fourth;
89    } elsif($postcode->valid_fragment) {
90        # We have a valid fragment; let's build up what we can
91        # We are guaranteed to have the first two
92        $outward = $first . $second;
93        $inward = '';
94        $inward .= $third if $third;
95        $inward .= $fourth if $fourth;
96    } else {
97        print_err("The postcode format is not valid", $returnlink);
98        next REQUEST;
99    }
100
101    # Check for a duplicate.
102    # We want to collect duplicates from different IP addresses as a kind of
103    # corroboration factor; this just catches accidental double-submission
104    # really.
105    $sth = $dbh->prepare('SELECT raw_postcode FROM postcodes WHERE raw_postcode = ? AND easting = ? AND northing = ? AND ip = ?');
106    unless ($sth->execute($raw_postcode, $easting, $northing, $ENV{'REMOTE_ADDR'})) {
107        print_err('Database error when checking for duplicate data :(', $returnlink);
108        next REQUEST;
109    }
110
111    if ($sth->rows) {
112        print_err('You, or someone with the same IP address, have already submitted this postcode with these co-ordinates.', $returnlink);
113        next REQUEST;
114    }
115
116    $sth = $dbh->prepare('INSERT INTO postcodes (outward, inward, raw_postcode, easting, northing, ip, created_at, source) VALUES (?, ?, ?, ?, ?, ?, ?, 0)');
117    if ($sth->execute($outward, $inward, $raw_postcode, $easting, $northing, $ENV{'REMOTE_ADDR'}, time2str('%Y-%m-%d %H:%M:%S', time))) {
118        print "Content-type: text/html\n\n";
119        print "<html><head><title>Thank you</title></head>\n";
120        print "<body><p>Thank you for telling us where your post code is!</p>\n";
121        print "<p>$returnlink</p>\n";
122        print "</body></html>";
123        next REQUEST;
124    } else {
125        print STDERR "DB error: " . $dbh->errstr . "\n";
126        print_err("Database error when adding your data :(", $returnlink);
127        next REQUEST;
128    }
129}
130
131# No more requests to serve, so tidy up
132$dbh->disconnect;
133
134# Helper routines
135sub print_err($$) {
136    my $err = shift;
137    my $returnlink = shift;
138    print "Content-type: text/html\n\n";
139    print "<html><head><title>Error submitting</title></head>\n";
140    print "<body><p>The following error occurred whilst submitting data:\n";
141    print CGI::escapeHTML($err);
142    print "</p><p>Your input was:</p>\n<ul>";
143    foreach my $field (@fields) {
144        my $param = $cgi->param($field) || '';
145        print "<li>$field: " . $param . "</li>\n";
146    }
147    print "</ul>\n";
148    print "<p>$returnlink</p>\n";
149    print "</body></html>\n";
150}
151
152sub setup_dbh {
153    # $dbh is global
154    return $dbh = DBI->connect_cached("dbi:Pg:dbname=npemaps;host=127.0.0.1","npemaps","npemaps");
155}
Note: See TracBrowser for help on using the repository browser.