Beefy Boxes and Bandwidth Generously Provided by pair Networks
Keep It Simple, Stupid
 
PerlMonks  

CGI- based calendar

by mikkoh (Beadle)
on Jun 02, 2000 at 03:27 UTC ( [id://15923]=sourcecode: print w/replies, xml ) Need Help??
Category:
Author/Contact Info mikko_h@beer.com
Description: This script uses the Linux cal- program to output a CGI- based calendar. It allows users to add and remove entries to each day. It also hilights each day with entries in it. ATTN: This script should't be used in public environments as there are some security issues. I'll be working to patch them up - see posts below.
The main script - calendar.pl

#!/usr/bin/perl -Tw

use strict;
use CGI;

# Set PATH to cal
#########################
$ENV{'PATH'} = '/usr/bin';

# The directory to store entries for dates
#########################
my $date_dir = "dates/";

my $cgi = new CGI;
my $cal_prog = '/usr/bin/cal';

# A style sheet
#########################
my $cal_style= <<"EOT";
<!--
A     { text-decoration: none; }
A.visited { color: #0000ff; }
h3     { font-size: 10px; }
.wd     { color: #ffffff; }
.highlight    {
            color: #ff0000;
            font-weight: bold;
        } 
-->
EOT

# Colors for the backgrounds
##########################
my $month_bg_color='#ff0000';
my $week_bg_color='#0000ff';
my $date_bg_color='#e0e0e0';


my $year = 2000;
my $num_of_months = 12;

# The number of months shown per row
##########################
my $months_per_row = 3;

print     $cgi->header(),
    $cgi->start_html(
        -title=>'Calendar',
        -BGCOLOR=>'#ffffff',
        -style=>{-code=>$cal_style}
        );
        
print $cgi->h1('Calendar for the year '. $year);

print '<table cellspacing=5 cellpadding=0 border=0><tr valign="top">';

# A table for all months
############################
my $row_count = 1;
for(;$row_count <= $num_of_months ; $row_count++){ 
    
    open(CAL,"$cal_prog -m $row_count $year |") || 
        die "Cannot open CAL: $!\n";
    my @month = <CAL>;
    close(CAL);
    
    print '<td bgcolor="',$date_bg_color,'">';

    my $month_name = splice(@month,0,1);
    $month_name =~ s/^\s+(\w+) \d+$/$1/g;
    
    my $days = splice(@month,0,1);

    # A table for each of the months
    ###################################
    print '<table cellspacing=0 cellpadding=0 border=0>',
        '<tr><td bgcolor="',
        $month_bg_color,
        '" colspan=7>',
        $cgi->h3($month_name),
        '</td></tr>',
        '<tr>';
    # Mo Tu We Th Fr Sa Su
    ###############################
    my @day_names = split(" ",$days);
    foreach(@day_names){
        print '<td class="wd" bgcolor="' . $week_bg_color . '">',$_,'<
+/td>';
    }
    
    # Start spitting out the dates
    ###############################
    foreach(@month){
            s/[^\d]  /\-\- /g;
            s/ (\d)( |\n)/0$1$2/g;
            
        print '<tr>';
            
        my @dates = split(/ /g, $_);
             foreach(@dates){
            unless(/^--/){
                s/\n$//;
                chomp($month_name);
                my $date_file = $date_dir . $month_name . $_;
                my @date_stats = stat($date_file);
                                
            # Check    for entries per date, if any highlight it      
+          
            #################################################
                if( -e $date_file && $date_stats[7] > 0){
                    print '<td><a class="highlight" href="showDate.pl?
+month=',
                    $month_name,
                    '&date=',$_,'">',$_;
                    
                }
                else{
                    print '<td><a href="showDate.pl?month=',
                    $month_name,
                    '&date=',$_,'">',$_;
                }
                print '</a>&nbsp;</td>';
            }
            else{
                print '<td>--</td>';
            }
             }
        
        print '</td></tr>';
    }

    print '</table>';
    
    print '</td>';
    
    if($row_count % $months_per_row == 0){
        print '</tr><tr valign="top">';
    }
}
print '</tr></table>';

print $cgi->end_html;

The script for wieving daily events - showDate.pl

#!/usr/bin/perl

use strict;
use Fcntl;
use CGI;

my $cgi = new CGI;
my $date_dir = 'dates/';

# Check month & date params
###############################
unless($cgi->param('month') =~ /\w+/ && $cgi->param('date') =~ /\d{2}/
+){
     die "Invalid parameters!\n";
}

my $date_file = $date_dir . $cgi->param('month') . $cgi->param('date')
+;

my $file_identifier = '### CALENDAR.PL DATE FILE ' . $cgi->param('mont
+h') . $cgi->param('date');

# Style sheet
#########################
my $cal_style= <<"EOT";
<!--
.wd     { color: #ffffff; }
.action    {
    text-decoration: none; 
    font-size: 8px; 
    }
-->
EOT

# Some color settings
##########################
my $time_bg_color = '#ff0000';
my $entry_bg_color = '#efefef';
my $alt_entry_bg_color = '#c0c0c0';

print $cgi->header(),
    $cgi->start_html(
        -title=>'Calendar',
        -BGCOLOR=>'#ffffff',
        -style=>{ -code=>$cal_style },
        );

print $cgi->h2(
    'Entries for ',
    $cgi->param('month'),'-',
    $cgi->param('date'),'-2000',
    );
    
# Read in old entries if any
#############################
my(@entries);
if(-e $date_file){
    sysopen(ENTRIES,$date_file,O_RDONLY) || 
        die "Cannot open $date_file: $!\n";
    flock(ENTRIES,1);
    @entries = <ENTRIES>;
    close(ENTRIES);

        
}

# Check to see if requested date file is valid
################################################
unless(splice(@entries,0,1) eq  $file_identifier){
     die "Not a valid date file!\n";
}

print '<table cellspacing=1 cellpadding=0 border=0><tr>';

# Start day at $time_count
##########################
my $time_count = 8;

for(;$time_count < 25; $time_count++){
    print '<tr><td class="wd" bgcolor="',
        $time_bg_color,
        '">&nbsp;&nbsp;',
        $time_count,
        ':00&nbsp;&nbsp;</td>';
        
    # A different coloring for each line
    ######################################
    print '<td width=400';
    if($time_count & 1){
        print ' bgcolor="',
            $entry_bg_color,
            '">&nbsp;';
    }
    else{
        print ' bgcolor="',
            $alt_entry_bg_color,
            '">&nbsp;';
    }
    foreach(@entries){
        if(/^$time_count:([^\n].+)/){
            print $1 . '&nbsp;&nbsp;';
            
        }
    }
    print '</td><td';
    
    if($time_count & 1){
                print ' bgcolor="',
            $entry_bg_color,
            '">&nbsp;';
        } 
    else{
        print ' bgcolor="',
            $alt_entry_bg_color,
            '">&nbsp;';
    }
    
    # A link to add an entry
    ###############################
    print '[<a class="action" href="alterDate.pl?date=',
         $cgi->param('date'),
         '&month=',
         $cgi->param('month'), 
         '&time=',
         $time_count,
         '&action=add">ADD ENTRY</a> |';
    
    # ..and to remove one
    print '<a class="action" href="alterDate.pl?date=',
         $cgi->param('date'),
         '&month=',
         $cgi->param('month'), 
         '&time=',
         $time_count,
         '&action=rem"> REMOVE</a>]&nbsp;';
         
    print '</tr>';
}

print '</table>';

print $cgi->p;
print '<a href="calendar.pl">Back to the calendar</a>';

print $cgi->end_html;

And finally, the script for adding and removing stuff - alterDate.pl

#!/usr/bin/perl

use CGI;
use strict;
use Fcntl;

my $cgi = new CGI;
my $date_dir = 'dates/';

# Check month, date & time params
###############################
unless($cgi->param('month') =~ /\w+/ && $cgi->param('date') =~ /\d{2}/
+ && $cgi->param('time') =~ /\d{1,2}/){
     die "Invalid parameters!\n";
}


my $date_file = $date_dir . $cgi->param('month') . $cgi->param('date')
+;
my $file_identifier = '### CALENDAR.PL DATE FILE ' . $cgi->param('mont
+h') . $cgi->param('date');


my $new_entry;
my $redirect = 'showDate.pl?month=' . $cgi->param('month') . '&date=' 
+. $cgi->param('date');

# If we just added an entry write it and bounce back
#######################################################
if($cgi->param('action') eq 'Add new entry'){

    sysopen(DATE_FILE,$date_file, O_RDWR | O_CREAT,0666) || 
        die "Cannot open $date_file: $!\n";
    flock(DATE_FILE,2);
    
    my @old_entries = <DATE_FILE>;

        # Check to see if requested date file is valid
        ################################################
        unless(splice(@old_entries,0,1) eq  $file_identifier){
             die "Not a valid date file!\n";
        }


    seek(DATE_FILE, 0, 0);
    truncate(DATE_FILE, 0);
    
    $new_entry = $cgi->param('time') . ":" . $cgi->param('entry');
    push(@old_entries,$new_entry);
    print DATE_FILE $file_identifier    
    foreach(@old_entries){
        unless(/^\s$/){
            print DATE_FILE $_ . "\n";
        }
    }
    close(DATE_FILE);
    
    print $cgi->redirect($redirect);
}

# Remove an entry
###############################
elsif($cgi->param('action') eq 'rem'){
    sysopen(DATE_FILE,$date_file,O_RDWR) || 
        die "Cannot open $date_file: $!\n";
    flock(DATE_FILE,2);

    my(@old_entries,@new_entries);
        @old_entries = <DATE_FILE>;
        
        # Check to see if requested date file is valid
        ################################################
        unless(splice(@old_entries,0,1) eq  $file_identifier){
             die "Not a valid date file!\n";
        }
       
    my $time = $cgi->param('time');
    while(@old_entries){
        unless(/^$time:.+/){
            push(@new_entries,$_);
        }
    }

    seek(DATE_FILE, 0, 0);
    truncate(DATE_FILE, 0);
        print DATE_FILE $file_identifier;
    foreach(@new_entries){
        unless(/^\s$/){
            print DATE_FILE $_ . "\n";
        }
    }
    close(DATE_FILE);
    
    print $cgi->redirect($redirect);
}

# The form to add an entry
#################################
else{
    print $cgi->header(),
        $cgi->start_html(
            -title=>'Add entry',
            -BGCOLOR=>'#ffffff',
            );

    print $cgi->h2(
        $cgi->param('month'),'-',
        $cgi->param('date'),'-2000 at',
        $cgi->param('time'),':00',
        );

    
    print $cgi->start_form(
        -action=>'alterDate.pl',
        -method=>'post',
    );
    
    print $cgi->p,
        "Enter text:",
        $cgi->br,
        $cgi->textfield(
            -name=>'entry',
            -size=>'40',
        ),
        $cgi->p,
        $cgi->submit(
            -value=>'Add new entry',
            -name=>'action',
        ),
        '<input type="hidden" name="time" value="' . $cgi->param('time
+') . '">',
        '<input type="hidden" name="month" value="' . $cgi->param('mon
+th') . '">',
        '<input type="hidden" name="date" value="' . $cgi->param('date
+') . '">';

    print $cgi->end_form();
    print $cgi->end_html();
}

Some TODO:

  • See post below for security issues..
  • A choice for either 12- or 24-hour formats (currently 24)
  • Substitute <table> -stuff with $cgi->table() stuff (once I get the hang of it .. :)
  • Allow users to modify existing entries
Replies are listed 'Best First'.
do not use this script in a public environment!
by antihec (Sexton) on Jun 02, 2000 at 16:46 UTC
    I did a little audit of this app
    Here's what I found:

    calendar.pl
    some older /usr/bin/cal don't know about '-m'.

    showDate.pl
    Using something like showDate.pl?month=../../../../../../etc/&date=inittab in the URL one can open files ro with the executive uid of the user the server is running as. Only the regexp controlling what is written out keeps one from reading files contents. So what a pitty inittab just has 6 entries...
    If - however - you'd happen to have users with numerical names in the range of 8..24, showDate.pl would happily spit their /etc/password entries at you.
    Not too interesting, this one.

    alterDate.pl
    this seems more 'promising', cause it let's you open files O_RDWR | O_CREAT,0666, opening all kinds of doors. An URL like alterDate.pl?month=../../../../../../etc/&date=passwd&time=r00t&entry=:0:0::/:/bin/sh&action=Add%20new%20entry really makes you wish you wouldn't have your server running as root...

    I didn't play with action=rem, but it looks like it let's you remove any line containing a ':' from any file writable by the user the webserver is running as. (such as logfiles, if you want to hide your traces from playing with action=Add%20new%20entry)

    so, as a bottom line, please be sure to check user input in your cgis, esp. when you post them to some public place. You never know just who's gonna use them in what surroundings.

    -- bash$ :(){ :|:&};:
      Damn! Thanks a lot for pointing these facts out, I sorta hoped for some guidance with security issues. If you have any suggestions as for where to find tutorials etc. on writing safe CGI- scripts, please go ahead (yes, I know how to use a search engine, but still.. ). I hope nobody actually has used this.. //mjh
        A great source for CGI security info is CGI Programming with Perl. Chapter 8 on Security is free! on line at at O'Reilly.
        > If you have any suggestions as for where
        > to find tutorials etc. on writing safe CGI-
        > scripts, please go ahead

        Well, actually I don't know any resources. Perhaps we should go start creating one around the Monastery here?

        Would Q&A be an ok Area for such a thing, or should we perhaps make it into a tutorial. I can't say I know enough about security to cover Everything(tm) - but with the help of fellow monks it could get a nice (and IMHO needed) thing.

        What are your thoughts on this?

        super: now I'm done writing this, I note a certain "perlcgi" obsoletes my node before even having finished it. Thanks a lot!
        ;-)

        -- bash$ :(){ :|:&};:
      OK, here's what I've come up with:
      1. Check for a string like "### CALENDAR.PL DATE FILE May13" in the beginning of each date file where May13 would be replaced with whatever the month+date are
      2. Check to see that param('month') contains nothing but word characters, param('date') two digits and param('time') one or two digits.
      Do you think that this is sufficient, or should the path to the date_file also be validated in some way..? //mjh

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: sourcecode [id://15923]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others scrutinizing the Monastery: (4)
As of 2024-04-24 00:37 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found