#!/usr/bin/perl # # $Id: livechat.cgi,v 1.8.2.7 2004/08/23 16:42:52 vlado Exp $ # # ChatClient executable file # ChatClient package must be installed in the first level of your # web directory (i.e. if your web root directory is /usr/local/apache/data ChatClient # must be placed like this /usr/local/apache/data/ChatClient and you have to set # that the livechat.cgi is executable .cgi file) BEGIN { if ( $^O =~ /win/i ) { # OLD: #chdir( '{{{USER_CGI_BIN}}}ChatClient' ); # NEW: use File::Spec(); chdir ( File::Spec->catdir( '', 'ChatClient' ) ); } } use SharedLib; use strict; my $fm = new SharedLib; my $remotehost; my $text; my $norights = "You don't have the rights to chat in this room!"; my $sez = $fm->param('Sez'); my $sez2 = $fm->param('Sez2'); ######################################################################### my $settings_file = "settings/adminoptions.txt"; open ( ADM, "< $settings_file" ); my @conf = ; close ADM; ###### Begin Configuration Section ###### my $chatTitle = ( grep ( /^ChatTitle:/ , @conf ) )[0]; if ( $chatTitle ) { $chatTitle =~ s/^ChatTitle: (.*)$/$1/; $chatTitle =~ s/(.*?)\s+$/$1/; } # end of if ( $chatTitle ) else { $chatTitle = "LiveChat"; } my $log = ( grep ( /^Log:/, @conf ) )[0]; if ( $log ) { $log =~ s/^Log: (.*)$/$1/; $log =~ s/(.*?)\s+$/$1/; } # end of if ( $log ) else { $log = "daily"; } my $refresh = (grep(/^Refresh Time:/, @conf))[0]; if ( $refresh ) { $refresh =~ s/^Refresh Time: (.*)$/$1/; $refresh =~ s/(.*?)\s+$/$1/; } # end of if ( $refresh ) else { $refresh = "20"; } my $msgcount = (grep(/^Messages on page:/, @conf))[0]; if ( $msgcount ) { $msgcount =~ s/^Messages on page: (.*)$/$1/; $msgcount =~ s/(.*?)\s+$/$1/; } # end of if ( $msgcount ) else { $msgcount = 15; } my $style = (grep(/^Style:/, @conf))[0]; if ( $style ) { $style =~ s/^Style: (.*)(\d)$/$2/; $style =~ s/(.*?)\s+$/$1/; } # end of if ( $style ) else { $style = "grey"; } my $number_of_chats_to_display = $msgcount - 1; # how many rows to be displayed on page my $clear_chatroom_when_empty = 1; # # set to 0 if you don't want to clear chat messages when room is empty my $time_format = "MM/DD/YY HH:NM:SS "; my $log_all_chats = ""; # to log or not to log? That's the question :) if ($log =~ /none/) { $log_all_chats = "no"; } else { $log_all_chats = "yes"; } my $type_of_log = $log; # could be : none, daily, monthly or endless my $log_directory = "./logs"; # relative path to logs directory my $max_log_space = "1000000"; # ~ 1MB my $refresh_time = $refresh; # seconds between two refreshes my $idle_timeout = "5"; # time for autologout user if his/her is idle. In minutes my $censor_chat = 1; # set this to 0, if you don't to censor the chat for forbidden words my $censored_msg = '***'; # replacement string for forbidden words my $badwords_file = "./settings/badwords.txt"; # path to file with forbidden words my $path_to_banned_names_file = "./settings/banned-names.txt"; # path to file with banned usernames #my $url_of_livechat_cgi = "./livechat.cgi"; # path to executable file (this file) my $url_of_livechat_cgi = $ENV{'SCRIPT_NAME'}; # path to executable file (this file) my $path_to_active_users = "./settings/chatters.txt"; # path to file with online users information my $path_to_chat_file = "./livechat.html"; # path to chat conversation file my $chat_window_header = " $chatTitle
     
 

Welcome to $chatTitle!


"; #<-- This quote and semicolon are necessary. Don't backslash them! my $chat_window_footer = "
        "; #<-- This quote and semicolon are necessary. Don't backslash them! ##### End Configuration Section ##### ######################################################################## my $idle_time_min = $idle_timeout; $idle_timeout = $idle_timeout * 60; my $login = $fm->param('Login'); my $color = $fm->param('color'); $login =~ s/\W+//g; my $time = time; if($ENV{'REMOTE_HOST'} ne ""){ $remotehost = $ENV{'REMOTE_HOST'}; } else { $remotehost = $ENV{'REMOTE_ADDR'}; } if($fm->param('action') eq "ShowUsers"){ &show_users_online; exit(); } elsif($fm->param('action') eq "Logout"){ &logout; } elsif($fm->param('action') eq "Login"){ &check_banned_names; &print_frameset; } elsif($fm->param('action') eq "ChatMenu"){ &check_banned_names; &print_chat_menu; } elsif($fm->param('action') eq "Intro"){ &check_banned_names; &print_chat_intro; } elsif($fm->param('action') eq "LoginScreen"){ &check_banned_names; &loginscreen; } elsif($fm->param('action') eq "Talk"){ &check_banned_names; &talk; } elsif( not $fm->param() or $fm->param('logout') ) { my $p = { 'file' => "html/chatmain.html", 'script' => $ENV{'SCRIPT_NAME'}, 'logout' => $fm->param('logout'), }; print $fm->header(), $fm->load_file($p); exit(0); } ####################################################### # print_chat_intro() # Loads chat_intro.html template on chat login and logout with LOGOUT_OK message ####################################################### sub print_chat_intro { my $logoutMsg = "You were successfully logged out"; my $chat_intro_file = "./html/chat_intro.html"; my $chat_intro_para = { 'file' => $chat_intro_file, 'title' => $chatTitle || "", 'logoutMsg' => $fm->param('logout') ? $logoutMsg : "", }; print $fm->header(); print $fm->load_file( $chat_intro_para ); exit(); } ######################################################## # print_chat_menu() # Loads chatmenu.html template, this is the bottom frame in chat ######################################################## sub print_chat_menu { my $chat_menu_file = "./html/chatmenu.html"; my $chat_menu_para = { 'file' => $chat_menu_file, 'login' => $login || "", 'color' => $color || "", 'url_of_livechat_cgi' => $url_of_livechat_cgi || "", 'url_of_chat_file' => $path_to_chat_file || "", }; print $fm->header(); print $fm->load_file( $chat_menu_para ); exit(); } ######################################################## # loginscreen() # Loads loginscreen.html template, this is the bottom frame # in chat start, where user types his nick and choose the color ####################################################### sub loginscreen { my $loginscreen_file ="./html/loginscreen.html"; my $loginscreen_para = { 'file' => $loginscreen_file, 'url_of_livechat_cgi' => $url_of_livechat_cgi, 'title' => $chatTitle || "", }; print $fm->header(); print $fm->load_file( $loginscreen_para ); exit(); } ################################################################ # talk() # ################################################################ sub talk { &get_date('',$time_format); $sez =~ s/^\s+//g; $sez =~ s/\s+$//g; $sez =~ s/^(.{54})(.{66}).*$/ $1\n$2/ if ((length $sez) > 54); $sez2 =~ s/^\s+//g; $sez2 =~ s/\s+$//g; $sez2 =~ s/^(.{54})(.{66}).*$/ $1\n$2/ if ((length $sez2 > 54)); if($sez eq ""){$sez = $sez2;} open(CHATTERS, "< $path_to_active_users"); my $ison = (grep(/^$login\|/, ))[0]; close(CHATTERS); $ison =~ s/\s+$//g; my $loggedin; if ($ison ne "") { $loggedin = 1; } if ($loggedin != 1) { # If not, add to chatters. open(CHATTERS, ">> $path_to_active_users"); if($^O !~ /win/i){ flock(CHATTERS, 2); } else { binmode(CHATTERS); } seek(CHATTERS, 0, 2); print CHATTERS "$login|$remotehost|$time|$color\n"; flock(CHATTERS, 8) if($^O !~ /win/i); close(CHATTERS); } # If so, update time. else { my ($f_login,$f_remote,$f_time,$f_color) = split(/\|/, $ison); $time = time; open(CHATTERS, "+< $path_to_active_users"); if($^O !~ /win/i){ flock(CHATTERS, 2); } else{ binmode(CHATTERS); } my @chatters = ; my $nchatter; foreach my $chatter (@chatters){ $chatter =~ s/\s+$//g; if($chatter =~ /^$f_login\|/){ $nchatter .= "$f_login|$f_remote|$time|$f_color\n"; } else { $nchatter .= "$chatter\n"; } } truncate(CHATTERS, length($nchatter)); # Rewind to the beginning of the file... seek(CHATTERS, 0, 0); flock(CHATTERS, 8) if($^O !~ /win/i); # new print CHATTERS $nchatter; close(CHATTERS); &check_idle_timeouts; } if( $sez eq "" ) { $text = "<just chillin'>"; } else { $text = $sez; } if ( $loggedin != 1 ){ $text = "<Entered the chatroom> $text"; } my ($f_login,$f_remote,$f_time,$f_color) = split(/\|/, $ison); # Add to chat file. &write_to_chat_file($login,$remotehost,$text, $f_color); open(FILE, "< $path_to_chat_file"); print $fm->header(); while(){ print;} close(FILE); $/="\n"; exit(); } ############################################################### # get_date (time, Format) # params # time - timestamp, if time is '' then use current time # Format - format for returned time # returns # formated 'time' in format 'Format' ############################################################### sub get_date { my($testtime,$timefmt,$twodigityear); if($_[0] eq ""){$testtime = time;} else{$testtime = $_[0];} if($_[1] ne ""){$timefmt = $_[1];} else {$timefmt = "MM/DD/YY HH:NM:SS";} my ($sec,$min,$hour,$mday,$mon,$year) = (localtime($testtime))[0,1,2,3,4,5]; $mon = $mon + 1; if ($hour < 10) { $hour = "0$hour"; } if ($min < 10) { $min = "0$min"; } if ($sec < 10) { $sec = "0$sec"; } $year += 1900; $twodigityear = $year; $twodigityear =~ s/(\d\d)(\d\d)/$2/; $timefmt =~ s/MM/$mon/g; $timefmt =~ s/DD/$mday/g; $timefmt =~ s/YYYY/$year/g; $timefmt =~ s/YY/$twodigityear/g; $timefmt =~ s/HH/$hour/g; $timefmt =~ s/NM/$min/g; $timefmt =~ s/SS/$sec/g; return "$timefmt"; } ################################################################ # show_users_online() # check who is online in chatters.txt file and print them in the right frame ############################################################### sub show_users_online { my($p, $users_online, $found_users, $kphdate); my ($show_user,$show_remote_host,$time_on,$color); &check_idle_timeouts; open(FILE, "< $path_to_active_users"); my @clines = ; my $templ; $fm->load_template("./html/usersonline_row.html", \$templ); foreach my $cline (@clines){ ($show_user,$show_remote_host,$time_on,$color)=split(/\|/, $cline); ($show_user) = &check_badwords($show_user,"blah"); my $date = &get_date($time_on,$time_format); chomp($color); $color =~ s/\s+$//g; $p = { show_user => $show_user, date => $date, color => $color }; $users_online .= $fm->load_file($p, $templ); $found_users = $found_users + 1; } close(FILE); if ( $login ) { my @all_but_me = grep { ! /^$login\|/ } @clines; my ($me) = grep { /^$login\|/ } @clines; ($show_user,$show_remote_host,$time_on,$color)=split(/\|/, $me); open (CHATTERS, "> $path_to_active_users") || die "Cannot open users file: $!"; flock (CHATTERS, 2) or die "flock : $!"; print CHATTERS @all_but_me; print CHATTERS "$show_user|$show_remote_host|",time,"|$color"; flock (CHATTERS, 8); close (CHATTERS); } # end of "if ( $login )" my $empty_room; if( $found_users == 0 ) { $empty_room = qq{Empty Chat Room}; &clear_chatroom if($clear_chatroom_when_empty == 1); } # end of "if( $found_users == 0 )" $kphdate = &get_date('',$time_format); $p = { file => "./html/usersonline.html", url_of_livechat_cgi => $url_of_livechat_cgi, users_online => $users_online, empty_room => $empty_room, kphdate => $kphdate, found_users => $found_users, refresh => $refresh, login => $login, }; print $fm->header(), $fm->load_file($p); } ################################################################## # print_frameset() # check login name, if something is wrong loads appropriate Error Template # else loads main chat ################################################################## sub print_frameset { open(CHATTERS, "<$path_to_active_users"); my $isloggedon = (grep(/^$login\|/, ))[0]; close(CHATTERS); if($login eq "" || $login =~ /^\s+$/) { my $invalid_login = "ERROR: Invalid login.
Your login cannot contain punctuation or special characters."; my $accessDenied_file = "./html/accessdenied.html"; my $accessDenied_para = { 'file' => $accessDenied_file, 'title' => $chatTitle || "", 'server_name' => $ENV{'SERVER_NAME'}, 'invalid_login' => $invalid_login || "", }; print $fm->header(); print$fm->load_file( $accessDenied_para ); exit(); } elsif($isloggedon ne "") { my $used_nick = "ERROR: Login $login in use. Please choose a different username."; my $accessDenied_file = "./html/accessdenied.html"; my $accessDenied_para = { 'file' => $accessDenied_file, 'title' => $chatTitle || "", 'server_name' => $ENV{'SERVER_NAME'}, 'used_nick' => $used_nick || "", }; print $fm->header(); print $fm->load_file( $accessDenied_para ); exit(); } print $fm->header(); print < EOF open(CHATTERS, ">>$path_to_active_users"); if($^O !~ /win/i) { flock(CHATTERS, 2); } else { binmode(CHATTERS); } seek(CHATTERS, 0, 2); print CHATTERS "$login|$remotehost|$time|$color\n"; if($^O !~ /win/i) { flock(CHATTERS, 8); } close(CHATTERS); $text = "<Entered the chatroom>"; &write_to_chat_file($login,$remotehost,"$text",$color); exit(); } ############################################################## # logout() # Logout the user and redirect to login page with logout parameter. ############################################################## sub logout { open(FILE, "+< $path_to_active_users"); if ( $^O !~ /win/i ) { flock(FILE, 2); } else { binmode(FILE); } my @lines = ; my($nfile); foreach my $line (@lines) { $line =~ s/\s+$//g; $nfile .= "$line\n" if ( $line !~ /^$login\|/ ); } truncate(FILE, length($nfile)); seek(FILE, 0, 0); print FILE "$nfile"; close(FILE); &write_to_chat_file($login,$remotehost,"<Left the chatroom.>", $color) if ( $color ne "" ); &clear_chatroom if ( length($nfile) == 0 && $clear_chatroom_when_empty == 1 ); print $fm->redirect("$url_of_livechat_cgi?logout=1"); exit(); } ########################################################################## # write_to_chat_file ( login, remotehost, text, color ) # Writes to chat conversation file ( livechat.html ). # params: # login - username of the talking user # remotehost - IP of the user ( $ENV{'REMOTE_ADDR'} ) # text - message sended by the user # color - color assosiated with user login ########################################################################## sub write_to_chat_file { my($t_color); my( $login, $ip, $message, $t_color ) = @_; my $text = $message; $t_color = $color unless ( defined $t_color ); my $date = &get_date('',$time_format); undef $/; open(FILE, "+< $path_to_chat_file"); if($^O !~ /win/i) { flock(FILE, 2); } else { binmode(FILE); } # We assume that there is between each "statement" # because we put it there. :) my @entries = split("", ); # Now, we can get any entry in "@entries" by calling them by number. # this way, we can print only $number_of_chats_to_display to the file later. # Now, we've opened the file, but let's make sure it refreshes the next time. my $newhtml =< $chat_window_header
HTML # end of $newhtml # Don't allow them to put in html code if we don't want them to... $login =~ s//>/g; $text =~ s//>/gs; my $clogin = $login; my $ctext = $text; # Check for bad words. ($clogin,$ctext) = &check_badwords($clogin,$ctext); $ctext = "[$date]$clogin:$ctext
"; # Put the new info in. $newhtml .= "$ctext"; $newhtml .= "\n"; for(my $i = 1; $i <= $number_of_chats_to_display; $i++) { if($entries[$i] !~ /$chat_window_footer/) { $entries[$i] =~ s/\n//gs; $newhtml .= "$entries[$i]\n" if $entries[$i] ne ""; } } # Close the document cleanly. $newhtml .= "$chat_window_footer"; truncate(FILE, length($newhtml)); seek(FILE, 0, 0); print FILE $newhtml; flock(FILE, 8) if ( $^O !~ /win/i ); close(FILE); $/ = "\n"; # Log it if they want a transcript... &log("$ctext") if $log_all_chats eq "yes"; } ############################################################################# # clear_chatroom() # Clears chat conversation file ( livechat.html ) if there is nobody in chat ############################################################################# sub clear_chatroom { undef $/; open(FILE, "< $path_to_chat_file"); my @entries = split("", ); close(FILE); $/ = "\n"; my $i = 0; my $already_cleared; foreach my $entry (@entries) { if($entry =~ /table border/ && $entries[$i + 1] =~ /\QChatroom empty\E/) { $already_cleared = 1; last; } $i++; } if($already_cleared != 1) { # Now, we can get any entry in "@entries" by calling them by number. # this way, we can print only $number_of_chats_to_display to the file later. my $date = &get_date(time,$time_format); # Try to open a file that holds the statements everyone has been making # and lock it... open(FILE, "> $path_to_chat_file"); if($^O !~ /win/i) { flock(FILE, 2); } else { binmode(FILE); } # Now, we've opened the file, but let's make sure it refreshes the next time. print FILE " $chat_window_header
"; # Now, we're going to step through the previous entries, and print them # back to the file: # Starting with 1, until $i is equal to 10, add 1 to $i and keep looping... for($i = 1; $i <= $number_of_chats_to_display; $i++) { if($entries[$i] !~ /$chat_window_footer/) { $entries[$i] =~ s/\n//gs; print FILE "
\n"; } } # Close the document cleanly. print FILE "
$chat_window_footer"; close(FILE); $/ = "\n"; } # end of "if($already_cleared != 1)" } ########################################################################## # check_idle_timeouts() # Checks whether any user is idle more the $idle_timeout. # If any, then logout him/her. ########################################################################## sub check_idle_timeouts { my $time = time; my($line, @chatters,$one,$two,$stuff); open(CHATTERS, "+< $path_to_active_users"); if($^O !~ /win/i) { flock(CHATTERS, 2); } else { binmode(CHATTERS); } @chatters = ; foreach $line (@chatters) { $line =~ s/\s+$//g; my ($f_l,$f_rh,$f_t,$f_c) = split(/\|/, $line); # New time is the time since they last spoke ($f_t) plus # the idle_timeout. my $ntime = $f_t + $idle_timeout; # If this is less than the real time, then they need to # be cleared, otherwise ad them back to the file. if(($f_t + $idle_timeout) >= $time) { $one = $f_t + $idle_timeout; $two = $time; $stuff .= "$line\n"; } else { chomp($f_c); &write_to_chat_file($f_l,$f_rh,"<Left the chatroom (autologout). >",$f_c); } } truncate(CHATTERS, length($stuff)); seek(CHATTERS, 0, 0); print CHATTERS $stuff; flock(CHATTERS, 2) if($^O !~ /win/i); close(CHATTERS); &clear_chatroom if(length($stuff) == 0 && $clear_chatroom_when_empty == 1); } ####################################################################### # log ( text ) # Logs chat conversation if $log_all_chats is "yes" # params: # text - user message to log ####################################################################### sub log { my ( $logsize, $file ); $text = shift; my ($mday,$mon,$year) = (localtime(time))[3,4,5]; $mon += 1; $year += 1900; opendir(DIR, "$log_directory"); my @files = readdir(DIR); closedir(DIR); foreach $file (@files) { $logsize += (stat($file))[7] if (-f $file && $file =~ /^livechat-log/ ); } # end of "foreach $file (@files)" if ( $type_of_log eq "daily" ) { $file = "livechat-log-$mday-$mon-$year.html"; } elsif ( $type_of_log eq "monthly" ) { $file = "livechat-log-$mon-$year.html"; } elsif ( $type_of_log eq "endless" ) { $file = "livechat-log.html"; } if ( $logsize <= $max_log_space ) { mkdir( $log_directory ) unless ( -e $log_directory ); open(FILE, ">> $log_directory/$file") or warn( "Could not open file [$log_directory/$file] : $!" ) ; if($^O !~ /win/i) { flock(FILE, 2); } else { binmode(FILE); } seek(FILE, 0, 2); print FILE "$text
\n"; flock(FILE, 2) if( $^O !~ /win/i ); close(FILE); }# end of "if ( $logsize <= $max_log_space )" } ###################################################################### # {{{ proto check_badwords ( login, text ) # If $censor_chat is "1" checks for forbidden words in chat. If any # then substitute them with $censored_msg. # Params: # login - login name of the user. # text - message sended by th user. # Returns: # Array of checked login name and message text. ###################################################################### sub check_badwords { my ( $clogin, $ctext ) = @_; if ( $censor_chat == 1 ) { $/ = "\n"; open(BW, "< $badwords_file"); my @badwords = grep(!/^#/,); close(BW); foreach my $badword (@badwords) { chomp($badword); $clogin =~ s/$badword/\[$censored_msg\]/ig; $ctext =~ s/$badword/\[$censored_msg\]/ig; } # end of "foreach my $badword (@badwords)" } # end of "if ( $censor_chat == 1 )" my ( @results ) = ($clogin,$ctext); return @results; } # }}} # end of "sub check_badwords" ################################################################ # {{{ proto check_banned_names() # Checks if login name is forbidden ################################################################ sub check_banned_names { open(BN, "< $path_to_banned_names_file"); my @bnames = grep( ! /^#/ , ); close(BN); foreach my $bname (@bnames) { chomp($bname); if( $login =~ /^$bname/i ) { my $accessDenied_file = "./html/accessdenied.html"; my $accessDenied_para = { 'file' => $accessDenied_file, 'title' => $chatTitle || "", 'server_name' => $ENV{'SERVER_NAME'}, 'norights' => $norights || "", }; print $fm->header(); print$fm->load_file( $accessDenied_para ); exit(); }# end of if ( $login =~ /^bname/i) }# end of "foreach $bname (@bnames)" } #}}} # end of "sub check_banned_names"