#!/usr/bin/perl
#
# Conference Database Management Tool
#
# Markus Kuhn <http://www.cl.cam.ac.uk/~mgk25/>
#
# $Id: conf-tool.pl,v 1.28 2012-02-08 12:21:51+00 mgk25 Exp $

require 5.008001;
use strict 'subs';
use POSIX qw(strftime);

print <<'End' if $#ARGV < 0;
Conf-Tool --  Conference Database Management Tool -- Markus Kuhn

Usage: trtool {commands}

Commands:

  sortdbt[:filename]       Output database file sorted by time
  sortdbs[:filename]       Output database file sorted by shortname
  tables[:filename]        Output several TMPL/HTML table extracts
  all[:filename]           Output one TMPL/HTML table with all content
  submitform[:filename]    Output TMPL/HTML submission form
  forms <file> <file> ...  Reformat form submission email files
                           into database lines for cut & past
  catchup                  List conferences that had their last deadline
                           in the database 9-24 months ago

End
exit 1 if $#ARGV < 0;

sub utf8_to_sgml {
    my ($s) = @_;

    $s =~ s/&/&amp;/g;
    $s =~ s/</&lt;/g;
    $s =~ s/>/&gt;/g;

    return $s;
}

sub utf8_to_sgmlatt {
    my ($s) = @_;

    $s =~ s/&/&amp;/g;
    $s =~ s/</&lt;/g;
    $s =~ s/>/&gt;/g;
    $s =~ s/\"/&quot;/g;

    return $s;
}

my @month = ('???', 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
             'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec');

# turn '2002-01-14' into 'Jan 14', etc.
sub month_date {
    my ($d) = @_;
    
    if ($d =~ /^\d{4}$/) {
        return undef;
    } elsif ($d =~ /^(\d{4})-?(\d{2})$/) {
        return $month[$2];
    } elsif ($d =~ /^(\d{4})-?(\d{2})-?(\d{2})$/) {
        return $month[$2] . sprintf(" %d", $3);
    } elsif ($d =~ /^(\d{4})-?(\d{2})-?(\d{2})\/(\d{4})-?(\d{2})-?(\d{2})$/) {
	if (($1 eq $4) && ($2 eq $5)) {
	    return $month[$2] . sprintf("&nbsp;%d&ndash;%d", $3, $6);
	} else {
	    return $month[$2] . sprintf("&nbsp;%d&ndash;", $3) .
		$month[$5] . sprintf("&nbsp;%d", $6);
	}
    } else {
	die("Unknown date form '$d'");
    }

    return $d;
}

# shortcuts for common proceedings URLs:
sub expand_proceedings_url {
    my ($url) = @_;
    
    if ($url =~ /^lncs:(\d+)$/) {
	return "http://www.springerlink.com/openurl.asp?genre=issue&issn=0302-9743&volume=$1";
    }
    
    return $url;
}


my $dbfile = "conf-db.txt";

# Read-in database

open(DB, "<:utf8", $dbfile)
    || die("Can't open conference database '$dbfile':\n$!\n");
$events = 0;
while (<DB>) {
    if (/^\s*\#/) {
        # collect and skip comments
	$comments .= $_;
	next;
    }
    next if /^\s*$/;  # skip comments and empty lines;
    chop;
    if (/^([A-Za-z0-9-]+)=([^\|]*)\|([^\|]*)\|([^\|]*)$/) {
	$shortname = $1;
	if (defined $name{$shortname}) {
	    die("Double definition of short name '$shortname' in line $.:\n$_");
	}
	$name{$shortname} = $2;
	$description{$shortname} = $3;
	$series_url{$shortname} = $4;
    } elsif (/^((\d{4}(?:-\d{2}(?:-\d{2})?)?)(?:\/(\d{4}-\d{2}-\d{2}))?)\s*\|(\d{4}-\d{2}-\d{2})?\s*\|([A-Za-z0-9-]+)\|([^\|]*)\|([^\|]*, [A-Z]{2})?\|([^\|]*)\|([^\|]*)$/) {
	$eventdays[$events] = $1;
	$firstday[$events] = $2;
        my $lastday = $3;
	$deadline[$events] = $4;
	$shortname[$events] = $5;
	$note[$events] = $6;
	$place[$events] = $7;
	$event_url[$events] = $8;
	$proceedings_url[$events] = $9;
        $online_proceedings{$shortname} = 1 if $9;
	$latest_deadline{$shortname} = $events
	    if $deadline[$events] gt $deadline[$latest_deadline{$shortname}];
	if (!defined $name{$shortname[$events]}) {
            /^(.*?\|.*?\|)/;
	    die("$dbfile:$.:" . (length($1)+1) . ": unknown short name " .
                "'$shortname[$events]'\n$_\n");
	}
	if ($lastday &&
	    ($lastday lt $firstday[$events])) {
            /^(.*?\/)/;
	    die("$dbfile:$.:" . (length($1)+1) .
                ": last day of event is before first day\n$_\n");
	}
	if ($lastday &&
	    ($lastday eq $firstday[$events])) {
            /^(.*?\/)/;
	    die("$dbfile:$.:" . (length($1)+1) .
                ": do not enter a last day for single-day events\n$_\n");
	}
        if (defined $deadline[$events] &&
	    ($deadline[$events] gt ($lastday || $firstday[$events]))) {
            /^(.*?\|)/;
	    die("$dbfile:$.:" . (length($1)+1) .
                ": deadline ($deadline[$events]) is after last day (" .
		($lastday || $firstday[$events]) . ") of event\n$_\n");
	}
	$events++;
    } else {
        die("$dbfile:$.: syntax error\n$_\n");
    }
}
close(DB);
printf STDERR "We have %d serieses and $events events.\n", scalar(keys(%name));

$today = `date --iso-8601`;
chop $today;

# process command line arguments
while ($_ = shift(@ARGV)) {
    if (/^sortdbt(:(.+))?$/) {
        my $fn = $2 ? $2 : '-';
        open(F, ">:utf8", $fn) || die("Can't write into '$fn': $!\n");
	$comments =~ s/(\s*\#\s*(Conference serieses:|Conference events:)?\s*\n)*$/\n/;
	print F $comments;
	@list = sort {$a cmp $b} keys %name;
	if (@list) {
	    print F "#\n# Conference serieses:\n#\n";
	    foreach $sn (@list) {
		print F "$sn=$name{$sn}|$description{$sn}|$series_url{$sn}\n";
	    }
	}
	@list = (0 .. $events-1);
	@list = sort {"$eventdays[$a] $shortname[$a] $deadline[$a]" cmp
		      "$eventdays[$b] $shortname[$b] $deadline[$b]"} (0 .. $events-1);
	if (@list) {
	    print F "#\n# Conference events:\n#\n";
	    for $event (@list) {
		printf F '%-21s|%-10s|%s|%s|', $eventdays[$event],
		$deadline[$event], $shortname[$event], $note[$event];
		print F "$place[$event]|$event_url[$event]|$proceedings_url[$event]\n";
	    }
	}
	close(F);
    } elsif (/^sortdbs(:(.+))?$/) {
        my $fn = $2 ? $2 : '-';
        open(F, ">:utf8", $fn) || die("Can't write into '$fn': $!\n");
	$comments =~ s/(\s*\#\s*(Conference serieses:|Conference events:)?\s*\n)*$/\n/;
	print F $comments;
	my @slist = sort {$a cmp $b} keys %name;
	my @elist = sort { "$shortname[$a] $eventdays[$a] $deadline[$a]" cmp
			   "$shortname[$b] $eventdays[$b] $deadline[$b]" } (0 .. $events-1);
	print F "#\n# Conference serieses:\n";
	for $event (@elist) {
	    while (@slist && $slist[0] le $shortname[$event]) {
		$sn = shift @slist;
		print F "#\n$sn=$name{$sn}|$description{$sn}|$series_url{$sn}\n";
	    }
	    printf F '%-21s|%-10s|%s|%s|', $eventdays[$event],
	    $deadline[$event], $shortname[$event], $note[$event];
	    print F "$place[$event]|$event_url[$event]|$proceedings_url[$event]\n";
	}
	while ($sn = shift @slist) {
	    print F "#\n$sn=$name{$sn}|$description{$sn}|$series_url{$sn}\n";
	}
	close(F);
    } elsif (/^table(:(.+))?$/) {
        my $fn = $2 ? $2 : '-';
        open(F, ">:utf8", $fn) || die("Can't write into '$fn': $!\n");
	    print F "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n";
	    print F "<!-- FILE IS GENERATED AUTOMATICALLY, DO NOT EDIT -->\n";
	    print F "<title>Conferences</title>\n";
	    print F "<h1>Security conference database</h1>\n";
        print F <<'EOT';
<p>This is a list of some meetings on computer security, aimed to
provide both an archive of links to proceedings of past conferences,
as well as a list of upcoming events and submission deadlines.

<!-- <p>Although every effort was made to make this list accurate, please
check in advance the authoritative home page for details. If you
notice any error or know of any other event that is not yet listed
below and you think it should be, please fill out our <a
href="submit.html">submission form</a>. -->

<div class="alert warning"><p><b>Warning:</b> This service here has not
been routinely maintained since 2007. A replacement implemented and
operated by <a href="http://www.cl.cam.ac.uk/~jra40/">Jonathan
Anderson</a> is now available at <a
href="http://securityconferences.net/">securityconferences.net</a>.
</div>

<ul>
<li><a href="#deadline">Sorted by submission deadline</a>
<li><a href="#date">Sorted by event date</a>
<li><a href="#proc">Proceedings of past conferences</a>
<li><a href="all.html">All information in our database</a>
<li><a href="conf-db.txt">Raw database file</a>
<!-- <li><a href="submit.html">Submission form for additions and changes</a> -->
</ul>

EOT

	print F "<h2 id=deadline>Sorted by submission deadline</h2>\n";

        @list = (0 .. $events-1);
	@list = grep {$deadline[$_] ge $today} @list;
	@list = sort {$deadline[$a] cmp $deadline[$b]} @list;
	if (@list) {
	    print F <<'EOT';

<table width="100%" border=0 cellpadding=4 cellspacing=4>
  <tr bgcolor="#aaccff">
    <th width="10%">Papers due</th>
    <th width="10%">Event date</th>
    <th width="60%">Event</th>
    <th width="20%">Location</th>
  </tr>
EOT
            my $year;
	    for $event (@list) {
		my $sn = $shortname[$event];
		my $url;
		if ($year ne substr($deadline[$event], 0, 4)) {
		    $year = substr($deadline[$event], 0, 4);
		    print F "<tr><td colspan=4 bgcolor=\"#cccccc\" align=center><b>$year</b>\n";
		}
		print F '<tr valign=top bgcolor="#eeeeee">';
		print F "\n  <td align=center>" . month_date($deadline[$event]);
		print F "\n  <td align=center>" . month_date($eventdays[$event]);
		print F "\n  <td>";
		if ($event_url[$event]) {
		    $url = $event_url[$event];
		} elsif ($series_url{$sn}) {
		    $url = $series_url{$sn};
		}
		print F '<a href="' . utf8_to_sgmlatt($url) . '">' if $url;
		print F utf8_to_sgml($name{$sn}) . " (" .
		    utf8_to_sgml($sn) . ")";
		
		print F '</a>' if $url;
		print F ' &ndash; ' . utf8_to_sgml($note[$event])
		    if $note[$event];
		print F '<sup><a href="#P:' . $sn . '">P</a></sup>'
		    if $online_proceedings{$sn};
		print F "\n  <td>";
		print F utf8_to_sgml($place[$event]) if $place[$event];
		print F "\n</tr>\n";
	    }
	    print F "</table>\n";
	}

	print F "<h2 id=date>Sorted by event date</h2>\n";

        @list = (0 .. $events-1);
	@list = grep {$firstday[$_] ge $today} @list;
	@list = sort {$firstday[$a] cmp $firstday[$b]} @list;
	if (@list) {
	    print F <<'EOT';
<table width="100%" border=0 cellpadding=4 cellspacing=4>
  <tr bgcolor="#aaccff">
    <th width="10%">Event date</th>
    <th width="60%">Event</th>
    <th width="20%">Location</th>
  </tr>
EOT
	    my $year;
	    for $event (@list) {
		my $sn = $shortname[$event];
		my $url;
		if ($year ne substr($firstday[$event], 0, 4)) {
		    $year = substr($firstday[$event], 0, 4);
		    print F "<tr><td colspan=4 bgcolor=\"#cccccc\" align=center><b>$year</b>\n";
		}
		print F '<tr valign=top bgcolor="#eeeeee">';
		print F "\n  <td align=center>" . month_date($eventdays[$event]);
		print F "\n  <td>";
		if ($event_url[$event]) {
		    $url = $event_url[$event];
		} elsif ($series_url{$sn}) {
		    $url = $series_url{$sn};
		}
		print F '<a href="' . utf8_to_sgmlatt($url) . '">' if $url;
		print F utf8_to_sgml($name{$shortname[$event]}) . " (" .
		    utf8_to_sgml($shortname[$event]) . ")";
		
		print F '</a>' if $url;
		print F ' &ndash; ' . utf8_to_sgml($note[$event])
		    if $note[$event];
		print F '<sup><a href="#P:' . $sn . '">P</a></sup>'
		    if $online_proceedings{$sn};
		print F "\n  <td>";
		print F utf8_to_sgml($place[$event]) if $place[$event];
		print F "\n</tr>\n";
	    }
	    print F "</table>\n";
	}

	print F "<h2 id=proc>Proceedings of past meetings</h2>\n";

        @list = (0 .. $events-1);
	@list = grep {length($proceedings_url[$_]) > 0} @list;
	@list = sort {"$name{$shortname[$a]}\n$shortname[$a] $eventdays[$a]"
	     cmp "$name{$shortname[$b]}\n$shortname[$b] $eventdays[$b]"} @list;
	my $lastsn;
	for $event (@list) {
	    my $sn = $shortname[$event];
	    if ($lastsn ne $sn) {
		print F '<h3 id="P:' . $sn . '">';
		print F '<a href="' . utf8_to_sgmlatt($series_url{$sn}) . '">'
		    if $series_url{$sn};
		print F utf8_to_sgml($name{$shortname[$event]}) . " (" .
		    utf8_to_sgml($shortname[$event]) . ")";
		print F '</a>' if $series_url{$sn};
		print F "</h3>\n<p>";
		$lastsn = $sn;
	    }
	    print F '<a href="' .
		utf8_to_sgmlatt(
		    expand_proceedings_url($proceedings_url[$event]))
		. '">' . substr($firstday[$event], 0, 4) . "</a>\n";
	}
        close(F);
    } elsif (/^all(:(.+))?$/) {
        my $fn = $2 ? $2 : '-';
        open(F, ">:utf8", $fn) || die("Can't write into '$fn': $!\n");
	print F "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01//EN\">\n";
	print F "<!-- FILE IS GENERATED AUTOMATICALLY, DO NOT EDIT -->\n";
	print F "<title>All entries</title>\n";
	print F "<h1>Full database content</h1>\n";
        print F <<'EOT';
<p>This is the full listing of the content of our <a
href="conf-db.txt">security conference database file</a>.

<!-- <p>If you want to contribute additions or corrections, please use our
<a href="submit.html">submission form</a>. -->

EOT
        print F "<h3>Content</h3>\n";
        @list = sort {$a cmp $b} keys %name;
	print F '<p>' if @list;
	for $sn (@list) {
	    print F '<a href="#' . utf8_to_sgmlatt($sn) . '">' .
		utf8_to_sgml($sn) . "</a>\n";
 	}
        @list = sort {"$name{$a}\n$a"
		  cmp "$name{$b}\n$b"} keys %name;
	print F "<ul>\n" if @list;
	for $sn (@list) {
	    print F '<li>';
	    print F '<a href="#' . utf8_to_sgmlatt($sn) . '">';
	    print F utf8_to_sgml($name{$sn}) . " (" .
		utf8_to_sgml($sn) . ")";
	    print F "</a>\n";
 	}
	print F "</ul>\n";
        @list = sort {"$name{$shortname[$a]}\n$shortname[$a] $eventdays[$a]"
		  cmp "$name{$shortname[$b]}\n$shortname[$b] $eventdays[$b]"} (0 .. $events-1);
	my $lastsn;
	for $event (@list) {
	    my $sn = $shortname[$event];
	    if ($lastsn ne $sn) {
		print F "</ul>\n" if $lastsn;
		print F '<h3 id="' . utf8_to_sgmlatt($sn) . '">';
		print F '<a href="' . utf8_to_sgmlatt($series_url{$sn}) . '">'
		    if $series_url{$sn};
		print F utf8_to_sgml($name{$sn}) . " (" .
		    utf8_to_sgml($sn) . ")";
		print F '</a>' if $series_url{$sn};
		print F "</h3>\n";
		print F '<p>' . utf8_to_sgml($description{$sn}) . "\n"
		    if $description{$sn};
		print F "<ul>\n";
		$lastsn = $sn;
	    }
	    print F '<li>' . join(' ', grep {$_} (substr($eventdays[$event], 0, 4),
						 month_date($eventdays[$event])));

	    print F ', ', utf8_to_sgml($place[$event]) if $place[$event];
	    if ($deadline[$event]) {
		print F ', deadline: ';
		print F substr($deadline[$event], 0, 4) . ' '
		    if (substr($deadline[$event], 0, 4) ne
			substr($eventdays[$event], 0, 4));
		print F month_date($deadline[$event]);
	    }
	    print F ', <a href="' .
		utf8_to_sgmlatt($event_url[$event])
		. '">link</a>' if $event_url[$event];
	    print F ', <a href="' .
		utf8_to_sgmlatt(
		    expand_proceedings_url($proceedings_url[$event]))
		. '">proceedings</a>' if $proceedings_url[$event];
            print F "\n";
	    print F '<br>' . utf8_to_sgml($note[$event]) . "\n"
		if $note[$event];
	}
	print F "</ul>\n";
        close(F);
    } elsif (/^submitform(:(.+))?$/) {
        my $fn = $2 ? $2 : '-';
        open(F, ">:utf8", $fn) || die("Can't write into '$fn': $!\n");
	print F "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01//EN\">\n";
	print F "<!-- FILE IS GENERATED AUTOMATICALLY, DO NOT EDIT -->\n";
	print F "<title>Add/correct entry</title>\n";
	print F "<h1>Add/correct security conference</h1>\n";
	print F <<'EOT';
<!-- http://nms-cgi.sourceforge.net/formmail_compat-3.11c1/README -->

<p>Please use this form if you would like to suggest a new entry to
our <a
href="http://www.cl.cam.ac.uk/Research/Security/conferences/">security
conference list</a>, or if you would like to correct or update an
existing one.

<form method="post" action="/cgi-bin/FormMail.pl">
<input type="hidden" name="recipient" value="mgk25" />
<input type="hidden" name="subject" value="Add/correct security conference form" />
<input type="hidden" name="env_report" value="REMOTE_HOST,REMOTE_ADDR,REMOTE_USER,HTTP_USER_AGENT" />

<p>Database information:

<table width="90%" border=0 cellpadding=4 cellspacing=4 align=center>
<tr valign=top bgcolor="#eeeeee">
  <td>I&nbsp;would&nbsp;like&nbsp;to:
  <td><table><tr valign=top>
               <td><input type="radio" name="operation" value="add">
               <td>add a new entry<br>
             <tr valign=top>
               <td><input type="radio" name="operation" value="change">
               <td>change an existing entry<br>
               <small>(provide <em>series name</em>, <em>start year</em>
               and any fields you want to update)</small>
      </table>
<tr valign=top bgcolor="#eeeeee">
  <td>Series name:
  <td><select name="shortname">
         <option value="*new*">Select a known conference series here or add name,
         abbreviation and description of a new one below</option>
EOT
	@list = sort {"$name{$a}\n$a" cmp "$name{$b}\n$b"} keys %name;
	my $lastsn;
	for $sn (@list) {
	    print F '         <option value="' . $sn . '">' .
		utf8_to_sgml($name{$sn}) . " ($sn)</option>\n";
	}
        print F <<'EOT';
      </select><br>
      <input type="text" name="new-name" size=80>
      (<input type="text" name="new-shortname" size=7>)
<tr valign=top bgcolor="#eeeeee">
  <td>Description:</td>
  <td><input type="text" name="series-description" size=80><br>
      <small>Optional brief descriptive text about this entire conference series. For notes
      relating to the particular event this year, use "Notes:" below instead.</small></td>
<tr valign=top bgcolor="#eeeeee">
  <td>Start&nbsp;date:</td>
  <td><input type="text" size=4 maxlength=4 name="start-year">-<input type="text" size=2 maxlength=2 name="start-month">-<input type="text" size=2 maxlength=2 name="start-day"> (YYYY-MM-DD)<br>
  <small>First day of the conference.</small>
<tr valign=top bgcolor="#eeeeee">
  <td>End&nbsp;date:</td>
  <td><input type="text" size=4 maxlength=4 name="end-year">-<input type="text" size=2 maxlength=2 name="end-month">-<input type="text" size=2 maxlength=2 name="end-day"> (YYYY-MM-DD)<br>
  <small>Last day of the conference. (Only fields that differ from above
  need to be filled in.)</small>
<tr valign=top bgcolor="#eeeeee">
  <td>Deadline:</td>
  <td><input type="text" size=4 maxlength=4 name="deadline-year">-<input type="text" size=2 maxlength=2 name="deadline-month">-<input type="text" size=2 maxlength=2 name="deadline-day"> (YYYY-MM-DD)<br>
  <small>Date by which draft papers must have been sent to the program
  committee.</small>
<tr valign=top bgcolor="#eeeeee">
  <td>Note:</td>
  <td><input type="text" name="event-note" size=80><br>
      <small>Optional text after the conference name for this particular event only,
      e.g. a special track or topic this year.</small></td>
<tr valign=top bgcolor="#eeeeee">
  <td>Place:</td>
  <td><input type="text" name="place" size=80><br>
      <small>Typically of the form "Berkeley, CA, US" or "Cambridge, GB", where the country code
      is from <a href="http://www.iso.ch/iso/en/prods-services/iso3166ma/02iso-3166-code-lists/list-en1.html">ISO 3166-1</a>
      alpha-2.</small></td>
<tr valign=top bgcolor="#eeeeee">
  <td>Event&nbsp;URL:</td>
  <td><input type="text" name="event-url" size=80><br>
      <small>Typically a web page that leads to the call for papers, registration information, program, etc.</small></td>
<tr valign=top bgcolor="#eeeeee">
  <td>Series&nbsp;URL:</td>
  <td><input type="text" name="series-url" size=80><br>
      <small>Typically a URL that does not contain a year number and that
      leads to information about both forthcoming and past conferences in
      this seris.</small>
<tr valign=top bgcolor="#eeeeee">
  <td>Online proceedings:</td>
  <td><select name="proceedings-type">
         <option value=url>URL</option>
         <option value=lncs>LNCS #</option>
      </select>
      <input type="text" name="proceedings-url" size=72><br>
      <small>For Springer <a href="http://www.springerlink.com/openurl.asp?genre=journal&amp;issn=0302-9743">Lecture Notes in Computer Science</a>, just give the
      LNCS number.<br>Otherwise give the URL where the proceedings can be
      downloaded.</small>
</table>

<p>Alternatively, you can also send us direct changes to our <a
href="conf-db.txt">database file</a>, which contains comments that
specify its format. This may be a more convenient for larger or
automatically generated contributions:

<table border=0 cellpadding=4 cellspacing=4 align=center>
<tr valign=top bgcolor="#eeeeee">
<td>Remove lines:<br>
<textarea name="del-lines" cols=100 rows=3></textarea>
<br>Add lines:<br>
<textarea name="add-lines" cols=100 rows=3></textarea>
</table>

<p>Optional information for the human recipient:

<table width="90%" border=0 cellpadding=4 cellspacing=4 align=center>
<tr valign=top bgcolor="#eeeeee">
  <td>Your name:
  <td><input type="text" name="realname" size=80>
<tr valign=top bgcolor="#eeeeee">
  <td>Your email:
  <td><input type="text" name="email" size=80>
<tr valign=top bgcolor="#eeeeee">
  <td>Comments:
  <td><textarea name="comments" cols=80 rows=5></textarea>
</table>

<p><input type="submit" value="Submit">

</form>
EOT
    close(F);
    } elsif (/^forms$/) {
        while ($fn = shift(@ARGV)) {
	    open(F, "<$fn") || die("Can't open input file '$fn': $!\n");
	    my $m;
	    while (<F>) {
		$m .= $_;
	    }
	    close(F);
	    $m =~ /^(.*?\n)-{75}\n(.*)\n\n-{75}\n\n(.*?)$/s
		or die("This does not look like a form submission:\n$m");
	    $header = $1;
	    $body = $2;
	    $footer = $3;
	    my @splitlist = split(/\n\n([a-z-]+): /m, $body);
	    shift @splitlist and die("Unexpected start of body:\n$body\n" .
				     "SPLIT:\n" . join('|', @splitlist) . "\n");
	    my %field = ( @splitlist );
	    my $nb; # new body
	    if (exists $field{operation}) {
		$nb .= "OPERATION: $field{operation}\n\n";
	    }
	    # determine series entry
	    if ($field{'shortname'} eq '*new*') {
		$field{shortname} = $field{'new-shortname'};
	    }
	    my $sn = $field{shortname};
	    $field{'new-name'} = $name{$sn} unless $field{'new-name'};
	    $field{'series-description'} = $description{$sn} unless $field{'series-description'};
	    # try to find preexisting changed event
	    my $event;
	    $nb .= "SERIES AND EVENT DATABASE ENTRIES:\n";
	    if ($field{operation} eq 'change') {
		($event) = grep {substr($eventdays[$_], 0, 4) eq $field{'start-year'} &&
				     $shortname[$_] eq $sn} (0 .. $events-1);
		$nb .= "WARNING: NO EXISTING EVENT MATCHES\n"
		    unless defined $event;
	    }
	    if ($field{operation} eq 'add' and exists $name{$sn}) {
		$nb .= "WARNING: SHORTNAME '$sn' EXISTS ALREADY\n";
	    }
	    # reformat and complete date fields
	    $field{'start-year'}     = ($field{'start-year'}  && (sprintf("%04d", $field{'start-year'}))  || (defined $event && substr($eventdays[$event], 0, 4)));
	    $field{'start-month'}    = ($field{'start-month'} && (sprintf("%02d", $field{'start-month'})) || (defined $event && substr($eventdays[$event], 5, 2)));
	    $field{'start-day'}      = ($field{'start-day'}   && (sprintf("%02d", $field{'start-day'}))   || (defined $event && substr($eventdays[$event], 8, 2)));
	    $field{'end-year'}       = ($field{'end-year'}    && (sprintf("%04d", $field{'end-year'}))    || (defined $event && substr($eventdays[$event], 11, 4)) ||
					$field{'start-year'});
	    $field{'end-month'}      = ($field{'end-month'}   && (sprintf("%02d", $field{'end-month'}))   || (defined $event && substr($eventdays[$event], 16, 2)) ||
					$field{'start-month'});
	    $field{'end-day'}        = ($field{'end-day'}     && (sprintf("%02d", $field{'end-day'}))     || (defined $event && substr($eventdays[$event], 19, 2)) ||
					$field{'start-day'});
	    $field{'deadline-year'}  = ($field{'deadline-year'}  && (sprintf("%04d", $field{'deadline-year'}))  || (defined $event && substr($deadline[$event], 0, 4)));
	    $field{'deadline-month'} = ($field{'deadline-month'} && (sprintf("%02d", $field{'deadline-month'})) || (defined $event && substr($deadline[$event], 5, 2)));
	    $field{'deadline-day'}   = ($field{'deadline-day'}   && (sprintf("%02d", $field{'deadline-day'}))   || (defined $event && substr($deadline[$event], 8, 2)));
	    if (defined $event) {
		$field{'event-note'}  ||= $note[$event];
		$field{'place'}       ||= $place[$event];
		$field{'event-url'}   ||= $event_url[$event];
	    }
	    if ($field{'proceedings-type'} eq 'lncs' && $field{'proceedings-url'} =~ /^\d+$/) {
		$field{'proceedings-url'} = 'lncs:' . $field{'proceedings-url'};	
	    }
	    $field{'proceedings-url'} ||= $proceedings[$event] if defined $event;
	    # output series line in database syntax
	    $nb .= "$sn=" .
		($field{'new-name'} || $name{$sn}) . '|' .
		($field{'series-description'} || $description{$sn}) . '|' .
		($field{'series-url'} || $series_url{$sn}) . "\n";
	    # output event line in database syntax
	    $nb .= $field{'start-year'} and
		$nb .= '-' . $field{'start-month'} and
		$nb .= '-' . $field{'start-day'} and
		$nb .= '/' . $field{'end-year'} and
		$nb .= '-' . $field{'end-month'} and
		$nb .= '-' . $field{'end-day'};
	    $nb .= '|';
            if ($field{'deadline-year'} &&
	        $field{'deadline-month'} &&
		$field{'deadline-day'}) {
		$nb .= sprintf('%04d-%02d-%02d',
			       $field{'deadline-year'},
			       $field{'deadline-month'},
			       $field{'deadline-day'});
	    }
	    $nb .= "|$sn|$field{'event-note'}|$field{'place'}|$field{'event-url'}|$field{'proceedings-url'}\n\n";
	    if (exists $field{'add-lines'} || exists $field{'del-lines'}) {
		$nb .= "DELETE-LINES:\n$field{'del-lines'}\nADD-LINES:\n$field{'add-lines'}";
	    }
	    print "Message $fn:\n\n${header}--------------------------------\n" .
		"$body\n--------------------------------\n$nb" . 
		"--------------------------------\nFOOTER:\n$footer";
	}
    } elsif (/^catchup$/) {
	my @slist = sort {$a cmp $b} keys %name;
	my $begin = strftime("%Y-%m-%d", gmtime(time - 3600*24*30 * 24));
	my $end = strftime("%Y-%m-%d", gmtime(time - 3600*24*30 * 9));
	print "Events with a latest deadline between $begin and $end:\n";
	for $sname (@slist) {
	    my $latest = $latest_deadline{$sname};
	    next unless defined $latest;
	    next if $deadline[$latest] lt $begin;
	    next if $deadline[$latest] gt $end;
	    print "$sname=$name{$sname}\n";
	    print "  last deadline: $deadline[$latest]\n";
	    print "  $series_url{$sname}\n" if $series_url{$sname};
	    print "  $event_url[$latest]\n" if $event_url[$latest];
	}
    } else {
        die("Unknown command line command '$_'\n");
    }
}
