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

Last change on this file since 139 was 139, checked in by Dominic Hargreaves, 15 years ago

Should fix #17

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