#!/usr/bin/perl

use strict;
use warnings;
use CGI qw/:standard/;

# File: uploadpatch.cgi
# Author: Ben Hearsum
# Description:
#   This cgi script displays a simple form that allows a user to submit a diff
#   that will eventually be uploaded to a Buildbot installation.
#   Along with the patch this script generates a .info file that contains the
#   name of the submitter, as read from $ENV['REMOTE_USER'], the date, in unix
#   time, the name of the patchfile, and the description read from the form.
#   This information is used by the download script to generate the
#   'buildbot sendchange' command.


# where the patches will go after being submitted
my $PATCH_DIR = "/var/www/patches";
# the size limit for the file, in bytes
my $SIZELIMIT = 10*1024*1024;
# the URL to the buildbot installation the patches will eventually go to
my $BUILDBOT_URL = "http://localhost:8010";

$CGI::POST_MAX = $SIZELIMIT;

sub write_success_page {
    print "Content-type: text/html\n\n";
    print <<ENDOFHTML;
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
          "http://www.w3.org/TR/html4/strict.dtd">
<html lang="en">

<head><title>Patch uploaded successfully</title></head>

<body>
<h3 style="text-align: center">Patch Uploaded Successfully</h3>
<div style="text-align: center">
  Look for your patch <a href="$BUILDBOT_URL">here</a>
</div>
</body>
</html>
ENDOFHTML

}

sub write_page {
    my $description = shift;
    my $err = shift;

    my $limit = $SIZELIMIT / 1024;

    print "Content-type: text/html\n\n";
    print <<ENDOFHTML;
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
          "http://www.w3.org/TR/html4/strict.dtd">
<html lang="en">

<head>
<title>Upload a Patch to Buildbot</title>
<style type="text/css">
  body {
    margin-left: auto;
    margin-right: auto;
  }

  div {
    text-align: center;
  }

  p {
    text-align: center;
    color: red;
    font-weight: bold;
  }

  table {
    width: 35%;
    margin-left: auto;
    margin-right: auto;
  }

  td {
    margin-top: 2px;
    margin-bottom: 2px;
    border: none;
    width: 50%;
  }

  tr#title {
    padding-bottom: 6px;
  }

  th {
    text-align: center;
  }

  td.lbl {
    text-align: right;
    vertical-align: top;
    padding-top: 5px;
  }

  td.field {
    text-align: left;
  }
</style>
</head>

<body>

<form action="/cgi-bin/uploadpatch.cgi" method="post"
      enctype="multipart/form-data">
<div>
<table>
  <tr id="title">
    <th colspan="2">Upload a patch to Buildbot</th>
  </tr>
  <tr>
    <td class="lbl">Description:</td>
    <td class="field">
      <textarea name="description" cols="35" rows="6">$description</textarea>
    </td>
  </tr>
  <tr>
    <td class="lbl">File:</td>
    <td class="field">
      <input type="file" name="patchfile">
    </td>
  </tr>
  <tr>
    <td colspan="2" style="text-align: center">
      <input type="submit" />
    </td>
  </tr>
</table>
</div>

</form>
<p>Note: Uploaded files must be less than ${limit}kB</p>
ENDOFHTML

    if ($err) {
        print "<p>$err</p>";
    }
    print "</body>\n</html>";
}

sub process {
    my $time = time();
    # get the parameters
    my $name = "";
    $name = $ENV{'REMOTE_USER'};
    if ($name eq "") {
        $name = "Noname";
    }

    my $description = param("description");
    my $patchfile = param("patchfile");

    if ($description eq "") {
        $description = "No description given.";
    }
    else {
        if ($description =~ /([`]|\$\()/) {
            write_page($description,
                       "Description must not contain backticks or '\$('");
        }
        $description =~ s/\n//g;
    }

    # only allow alphanumeric, hyphens, and single dots
    if ($patchfile !~ /^([\w-]|\.[\w-])+$/) {
        # ends the script
        
        write_page($description,
                   "Invalid filename. Please use only alphanumeric, -, _, "
                   . "and single dots");
        return;
    }

    # if we get here the file is small enough and passes the filename test

    # pull all of the contents of the file
    my $patch_handle = upload("patchfile");

    # strip off everything except the filename itself
    $patchfile =~ s/.*[\/\\](.*)/$1/;

    # generate the filenames
    $patchfile = "$time-$patchfile";
    my $infofile = "$patchfile.info";

    # make sure the file has a non-zero length
    # this also handles a case where the file specified doesn't exist
    if (-z $patch_handle) {
        write_page($description, "Specified file has a length of zero...");
        return;
    }

    # write the patch
    my $filename = "$PATCH_DIR/$patchfile";
    open PATCH, ">$filename" or write_page($description,
      "Server error - Could not open file for writing.") and return;
    binmode PATCH;
    
    while ( <$patch_handle> ) {
      print PATCH;
    }
    close PATCH;

    # now write the infofile
    $filename = "$PATCH_DIR/$infofile";

    open INFO, ">$filename";

    print INFO "submitter: $name\n";
    print INFO "date: $time\n";
    print INFO "patchfile: $patchfile\n";
    print INFO "description: $description\n";

    close INFO;

    write_success_page();
}

if (param()) {
    process();
}
else {
    write_page();
}
