Beruflich Dokumente
Kultur Dokumente
/usr/bin/perl
=pod
-----BEGIN PGP SIGNED MESSAGE----/####################################################################
#
#
#
#
#####
#### # # ### #####
/\_/\
#
#
#
#
#
# # # #
#
( o.o )
#
#
#
###
#
##### #####
#
> ^ <
#
#
#
#
#
# # # #
#
v1.14
#
#
##### #####
#### # # # #
#
2014-06-22
#
#
#
# Installation instructions:
#
#
#
# Upload this file into your cgi-bin directory (you can rename it #
# to whatever you want, if you prefer) and make it executable #
# (chmod 711). If you have language data or backups you want to #
# restore initially, you can copy that all into a textfile named #
# "lechat.txt" and put it next to your script file on the server. #
# Then call the script in your browser with parameter like this: #
#
#
# http://(server)/(cgi-path)/(script-name).cgi?action=setup
#
#
#
# All necessary installation settings can be made from there.
#
# The server needs to support Perl CGI scripts, obviously. If you #
# have trouble, make sure the file is uploaded in ASCII-mode and #
# Perl scripts are allowed to create files and folders.
#
# Banner killers for some known hosts are built in, please report #
# back any problems there or any other hosts you want added!
#
#
#
# If you make translations and want to share them, please send me #
# a copy of the backup data for my website, thanks! All text can #
# be edited conveniently as superuser on the setup page.
#
#
#
# This script comes without any warranties. Use it at your own #
# risk, don't blame me for any modifications you make. Verify my #
# attached PGP-signature, to make sure you got the original and #
# not an altered copy! Really, do verify it!
#
# If you spread the script, please only give away the original, #
# or better, just refer to: http://4fvfamdpoulu2nms.onion/lechat/ #
#
#
# If you add your own cool features and want to share with others, #
# feel free, but please modify at least the version tag and add #
# your name to it, so it is clear that it is not my original code #
# anymore. If you send me a copy of your edited script, I might #
# use some of your ideas in future versions. Thank you!
#
#
#
# I wrote the script from scratch and all the code is my own, but #
# as you may notice, I took some ideas from other scripts that #
# are out there. Bug reports and feedback are very welcome.
#
#
#
# The "LE" in the name you can take as "Lucky Eddie", or since #
# the script was meant to be lean and easy on server resources, #
# as "Light Edition". It may even be the french word for "the" if #
# you prefer. Translated from french to english, "le chat" means: #
# "the cat".
#
#
#
# Other than that, enjoy! ;)
#
#
#
# Lucky Eddie
#
#
#
####################################################################/
=cut
use strict;
use Fcntl qw(:DEFAULT :flock);
######################################################################
# Data directory, could be changed, but shouldn't be necessary.
######################################################################
my $datadir='./lcdat';
######################################################################
# No need to change anything below. Always use: *.cgi?action=setup
######################################################################
my($version,$lastchanged)=('v1.14','2014-06-22');
my($S,%Q)=(&GetScript,&GetQuery,&GetParam);
my %T;# Test flags
my %U;# this User data
my %P;# all Present users (nick=>[hex,status,style])
my %A;# All registered members (nick=>[hex,status,style])
my @M;# Members: display names
my @G;# Guests: display names
my %F;# Fonts
my %I;# Internal texts and error messages
my %L;# Language editing
my %C;# Configuration
my %H;# HTML-stuff
load_config();
######################################################################
# main program: decide what to do based on queries
######################################################################
if($Q{action}[0]eq'setup'){
send_init() unless -e"$datadir/admin";
send_alogin() unless valid_admin($Q{nick}[0],$Q{pass}[0],$Q{hexpass}[0])
;
if($Q{do}[0]eq'config'){
set_config();
save_config();
}elsif($Q{do}[0]eq'chataccess'){
set_chat_access($Q{set}[0]);
}elsif($Q{do}[0]eq'backup'){
$I{backdat}=get_backup($Q{what}[0]);
}elsif($Q{do}[0]eq'restore'){
$I{backdat}=get_restore_results();
}elsif($Q{do}[0]eq'mainadmin'and$U{status}==9){
send_setup(change_admin_status());
}elsif($Q{do}[0]eq'resetlanguage'and$U{status}==9){
delete_file('language');
load_config();
}
send_setup();
}
elsif($Q{action}[0]eq'init' and !-e"$datadir/admin"){
init_chat();
}
elsif($Q{action}[0]eq'language'){
send_profile();
}
elsif($Q{action}[0]eq'entry'){
check_session();
send_entry();
}
elsif($Q{action}[0]eq'logout'){
kill_session();
send_logout();
}
elsif($Q{action}[0]eq'colours'){
check_session();
send_colours();
}
elsif($Q{action}[0]eq'help'){
check_session();
send_help();
}
elsif($Q{action}[0]eq'admin'){
check_session();
send_login() unless $U{status}>=6;
if($Q{do}[0]eq'clean'){
send_choose_messages() if $Q{what}[0]eq'choose';
clean_selected() if $Q{what}[0]eq'selected';
clean_room() if $Q{what}[0]eq'room';
send_messages();
}
elsif($Q{do}[0]eq'kick'){
send_admin() if $Q{name}[0]eq'';
unless(kick_chatter(pack('H*',$Q{name}[0]),$Q{kickmessage}[0])){
$I{errcantkick}=~s/<NICK>/pack('H*',$Q{name}[0])/e;
send_admin($I{errcantkick});
}
del_all_messages(pack('H*',$Q{name}[0])) if $Q{what}[0]eq'purge'
;
check_session();
send_messages();
}
elsif($Q{do}[0]eq'logout'){
send_admin() if $Q{name}[0]eq'';
unless(logout_chatter(pack('H*',$Q{name}[0]))){
$I{errcantlogout}=~s/<NICK>/pack('H*',$Q{name}[0])/e;
send_admin($I{errcantlogout});
}
check_session();
send_messages();
}
elsif($Q{do}[0]eq'sessions'){
send_sessions();
}
elsif($Q{do}[0]eq'guests'){
set_guests_access($Q{set}[0]) if($U{status}>=7 or $Q{set}[0]==0)
;
}
elsif($Q{do}[0]eq'register'){
register_guest();
check_session();
send_messages();
}
elsif($Q{do}[0]eq'status'){
change_status();
}
elsif($Q{do}[0]eq'regnew'){
register_new();
}
elsif($Q{do}[0]eq'newcomers' and $U{status}>=7){
edit_waiting_sessions($Q{what}[0]);
send_waiting_admin();
}
send_admin();
}
else{
send_login();
}
exit;
######################################################################
# html output subs
######################################################################
sub print_headers{print "Content-Type: text/html; charset=$H{encoding}\nContentLanguage: $I{languagecode}\nPragma: no-cache\nExpires: 0\n"}
sub print_stylesheet{print qq|\n<style type="text/css"><!--\n$C{cssglobal}\n|,$C
{'css'.$_[0]},"\n$H{add_css}--></style>\n";}
sub print_end{print $H{end_body},$H{end_html};exit}
sub frmpst{"<$H{form}>".hidden('action',$_[0]).hidden('session',$U{session}).($_
[1]?hidden('what',$_[1]).hidden('sendto',$Q{sendto}[0]).hidden('multi',$Q{multi}
[0]):'')}
sub frmlng{"<$H{form}>".hidden('action','language').hidden('nick',$Q{nick}[0]).h
idden('hexpass',$Q{hexpass}[0]||unpack('H*',$Q{pass}[0])).($_[0]?hidden('do',$_[
0]):'')}
sub frmset{"<$H{form}>".hidden('action','setup').hidden('nick',$Q{nick}[0]).hidd
en('hexpass',$Q{hexpass}[0]||unpack('H*',$Q{pass}[0])).($_[0]?hidden('do',$_[0])
:'').($_[1]?hidden('what',$_[1]):'')}
sub frmadm{"<$H{form}>".hidden('action','admin').hidden('do',$_[0]).hidden('sess
ion',$U{session})}
sub hidden{qq|<input type="hidden" name="$_[0]" value="$_[1]">|}
sub submit{qq|<input type="submit" value="$_[0]"$_[1]>|}
sub thr{'<tr><td'.($_[0]?qq| colspan="$_[0]"|:'').'><hr></td></tr>'}
sub cfgyn{qq|<tr><td align="left">$I{$_[0]}</td><td align="right"><input type="r
adio" name="$_[0]" id="$_[0]1" value="1"|.($C{$_[0]}?' checked':'').qq|><label f
or="$_[0]1"> $I{yes}</label> <input type="radio" name="$_[
0]" id="$_[0]0" value="0"|.($C{$_[0]}?'':' checked').qq|><label for="$_[0]0">&nb
sp;$I{no}</label></td></tr>|}
sub cfgta{qq|<tr><td align="left" valign="top">$I{$_[0]}</td><td align="right"><
textarea name="$_[0]" rows="4" cols="40" wrap="off">$C{$_[0]}</textarea></td></t
r>|}
sub cfgt{qq|<tr><td align="left">$I{$_[0]}</td><td align="right"><input type="te
xt" name="$_[0]" value="$C{$_[0]}" size="$_[1]"|.($_[2]?qq| maxlength="$_[2]"|:'
').qq|></td></tr>|}
sub cfgts{cfgt($_[0],'7','6')}
sub cfgtm{cfgt($_[0],'30')}
sub cfgtb{cfgt($_[0],'50')}
sub print_start{my($css,$ref,$url)=@_;
$url=~s/&/&/g if$url;# Don't escape "&" in URLs here, it breaks some
(older) browsers!
print_headers();
print "Refresh: $ref; URL=$url\n"if$url;
print "$H{begin_html}<head>$H{meta_html}";
tr>|;
my $start=tell(DATA);
while(<DATA>){
if($_=~/^#/){
my ($sect)=$_=~/^#\s*(.+)/;
print qq|<tr><td colspan="2" align="left"><hr></td></tr>
<tr><td colspan="2" align="left"><h3>$sect</h3></td></tr>|;
}else{
my($ikey,$ival)=$_=~/^([a-z_]+)\s*=(.+)/i;
if($ikey and 'stop_action'ne$ikey){
$I{$ikey}=$ival;
my $rows=int(length($ival)/75)+1;
$ival=formsafe($ival);
$L{$ikey}=formsafe($L{$ikey});
my $iinp=qq|<textarea rows="$rows" cols="75" nam
e="$ikey" wrap="virtual">$L{$ikey}</textarea>|;
print qq|<tr><td valign="top" align="left">$ikey
</td><td valign="top" align="left">$ival<br>$iinp</td></tr>|;
}
}
}
seek(DATA,$start,0);
print qq|<tr><td colspan="2" align="left"><hr></td></tr><tr><td colspan=
"2" align="center">|,submit($I{savechanges}),qq|</td></tr></table></form><br>$H{
backtosetup}<br>$H{versiontag}</center>|;
print_end();
}
sub send_setup{
read_members()if($U{status}==9);
print_start('admin');
$I{nickhelp}=~s/<MAX>/$C{maxname}/;
$I{passhelp}=~s/<MIN>/$C{minpass}/;
foreach(keys %C){next if $_ eq 'textfilters';$C{$_}=formsafe($C{$_})}
print qq|<center><h2>$I{chatsetup}</h2>|,frmset('chataccess'),qq|<table
cellspacing="0"><tr><td><b>$I{chataccess}</b></td><td> </td><td><input type
="radio" name="set" id="off" value="0"|,$T{access}==0?' checked':'',qq|></td><td
><label for="off">$I{suspend}</label></td><td> </td><td><input type="radio"
name="set" id="on" value="1"|,$T{access}==1?' checked':'',qq|></td><td><label f
or="on">$I{enabled}</label></td><td> </td><td><input type="radio" name="set
" id="deref" value="2"|,$T{access}==2?' checked':'',qq|></td><td><label for="der
ef">$I{derefonly}</label></td><td> </td><td>|,submit($I{butset}),qq|</td></
table></form><br><h2>$I{backups}</h2><table cellspacing="0"><tr><td align="left"
><table cellspacing="0"><tr><td>|;
print frmset('backup','members'),submit($I{backmem}),qq|</form></td><td>
⇓ </td><td>|,frmset('backup','config'),submit($I{backcfg}),qq|</form><
/td><td>⇓ </td></tr></table></td></tr><tr><td>|,frmset('restore'),qq|<
table cellspacing="0"><tr><td><textarea name="backupdata" rows="8" cols="80" wra
p="off">$I{backdat}\n</textarea></td></tr><tr><td align="right"><table cellspaci
ng="0"><tr><td> ⇒</td><td>|,submit($I{restore}),qq|</td></tr></table><
/td></tr></table></form></td></tr></table><br>|;
if($U{status}==9){
print qq|<h2>$I{mainadmins}</h2><i>$_[0]</i><table cellspacing="
0">|,thr(),qq|<tr><td align="left"><b>$I{regadmin}</b></td></tr><tr><td align="r
ight">|,frmset('mainadmin','new'),qq|<table cellspacing="0"><tr title="$I{nickhe
lp}"><td> </td><td align="left">$I{nickname}</td><td><input type="text" nam
e="admnick" size="20"></td><td> </td></tr><tr title="$I{passhelp}"><td>&nbs
p;</td><td align="left">$I{password}</td><td><input type="text" name="admpass" s
ize="20"></td><td>|,submit($I{butregadmin}),qq|</td></tr></table></form></td></t
r>|,thr(),qq|<tr><td align="left"><b>$I{raiseadmin}</b></td></tr><tr><td align="
right">|,frmset('mainadmin','up'),qq|<table cellspacing="0"><tr><td> </td><
td><select name="admnick" size="1"><option value="">$I{selchoose}</option>|;
print_memberslist(7);
print qq|</select></td><td align="right">|,submit($I{butraise}),
qq|</td></tr></table></form></td></tr>|,thr(),qq|<tr><td align="left"><b>$I{lowe
radmin}</b></td></tr><tr><td align="right">|,frmset('mainadmin','down'),qq|<tabl
e cellspacing="0"><tr><td> </td><td><select name="admnick" size="1"><option
value="">$I{selchoose}</option>|;
print_memberslist(8);
print qq|</select></td><td>|,submit($I{butlower}),qq|</td></tr><
/table></form></td></tr>|,thr(),qq|</table><br><br><h2>$I{cfglanguage}</h2>|,frm
lng(),submit($I{editlanguage}),qq|</form><br>|,frmset('resetlanguage'),submit($I
{resetlanguage},-e"$datadir/language"?'':' disabled'),qq|</form><br><h2>$I{cfgse
ttings}</h2>$I{cfgmainadm}<br><br>|;
}else{
print "<h2>$I{cfgsettings}</h2>",frmset('config'),'<table cellsp
acing="0">',thr(2),cfgyn('redirifsusp'),cfgtm('redirtourl'),thr(2),cfgyn('allowf
onts'),cfgyn('allowmultiline'),cfgyn('allowpms'),cfgyn('rndguestcol'),thr(2),cfg
yn('createlinks'),cfgyn('useextderef'),cfgtm('extderefurl'),thr(2);
print_filters();
print thr(2),cfgts('sessionexpire'),cfgts('guestsexpire'),cfgts(
'messageexpire'),cfgts('kickpenalty'),cfgts('waitingexpire'),thr(2),cfgts('defau
ltrefresh'),cfgts('minrefresh'),cfgts('maxrefresh'),cfgts('floodlimit'),thr(2),
cfgts('boxwidthdef'),cfgts('boxheightdef'),cfgts('maxmes
sage'),cfgts('maxname'),cfgts('minpass'),thr(2),cfgtm('title'),cfgtm('favicon'),
cfgtm('noguests'),cfgtm('loginbutton'),thr(2),cfgta('header'),cfgta('footer'),th
r(2),cfgta('rulestxt'),thr(2),cfgtb('nowchatting'),thr(2),cfgtb('entrymessage'),
cfgtb('logoutmessage'),cfgtb('kickederror'),thr(2),
cfgtb('roomentry'),cfgtb('roomexit'),cfgtb('regmessage')
,cfgtb('kickedmessage'),cfgtb('roomclean'),thr(2),cfgtb('mesall'),cfgtb('mesmem'
),cfgtb('messtaff'),cfgtb('mespm'),thr(2),cfgts('colbg'),cfgts('coltxt'),cfgts('
collnk'),cfgts('colvis'),cfgts('colact'),thr(2),
cfgta('cssglobal'),cfgtb('styleback'),thr(2),cfgta('cssl
ogin'),cfgtb('stylelogintext'),cfgtb('stylecolselect'),cfgtb('styleenter'),thr(2
),cfgta('csspost'),cfgtb('styleposttext'),cfgtb('stylepostsend'),cfgtb('stylesen
dlist'),cfgtb('styledellast'),cfgtb('styledelall'),cfgtb('styleswitch'),thr(2),
cfgta('cssview'),cfgtb('styledelsome'),cfgtb('stylecheck
wait'),thr(2),cfgta('csswait'),cfgtb('stylewaitrel'),thr(2),cfgta('csscontrols')
,cfgtb('stylerelpost'),cfgtb('stylerelmes'),cfgtb('styleprofile'),cfgtb('stylead
min'),cfgtb('stylerules'),cfgtb('styleexit'),thr(2),
cfgta('cssprofile'),thr(2),cfgta('cssrules'),thr(2),cfgt
a('cssadmin'),thr(2),cfgta('csserror'),thr(2),cfgtb('tableattributes'),cfgtb('fr
ameattributes'),cfgtb('framesizes'),thr(2);
print qq|<tr><td colspan="2" align="center"><small>$I{lastchange
d} $C{lastchangedat}/$C{lastchangedby}</small><br><br></td></tr><tr><td colspan=
"2" align="center">|,submit($I{savechanges}),'</td></tr></table></form><br>';
}
print "<$H{form}>",hidden('action','setup'),submit($I{butlogout}),"</for
m><br>$H{versiontag}</center>";
print_end();
}
sub send_admin{
read_members();
print_start('admin');
$I{admkick}=~s/<KICK>/$C{kickpenalty}/;
$I{nickhelp}=~s/<MAX>/$C{maxname}/;
$I{passhelp}=~s/<MIN>/$C{minpass}/;
my $chlist=qq|<select name="name" size="1"><option value="">$I{selchoose
}</option>|;foreach(sort {lc($a) cmp lc($b)} keys %P){$chlist.=qq|<option value=
my $url="$S?action=view&session=$U{session}&nocache=";
print_start('view',$U{refresh},$url.substr($^T,-6));
print '<a name="top"></a>';
print_chatters();
# <a href="#bottom"> does not work in some older browsers, full URL is n
eeded with anchors!
print qq|<table cellspacing="0" width="100%"><tr><td valign="top" align=
"right"><a href="$url$Q{nocache}[0]#bottom">$I{navbot}</a></td></tr></table>|;
print_messages();
print qq|<a name="bottom"></a><table cellspacing="0" width="100%"><tr><t
d align=right><a href="$url$Q{nocache}[0]#top">$I{navtop}</a></td></tr></table>|
;
print_end();
}
sub send_choose_messages{
print_start('view');
print frmadm('clean'),hidden('what','selected'),submit($I{butdelsome},qq
| style="$C{styledelsome}"|),'<br><br>';
print_messages($U{status});
print "</form><br>$H{backtochat}";
print_end();
}
sub send_post{
$U{postid}=substr($^T,-6);
print_start('post');
print qq|<center><table cellspacing="0"><tr><td align="center">|,frmpst(
'post'),hidden('postid',$U{postid}),$C{allowmultiline}?hidden('multi',$Q{multi}[
0]):'';
print qq|<table cellspacing="0"><tr><td valign="top">$U{displayname}</td
><td valign="top">:</td>|;
if($Q{multi}[0] and $C{allowmultiline}){print qq|<td valign="top"><texta
rea name="message" wrap="virtual" rows="$U{boxheight}" cols="$U{boxwidth}" style
="$C{styleposttext};background-color:#$C{colbg};$U{style}">$U{rejected}</textare
a></td>|}
else{print qq|<td valign="top"><input type="text" name="message" value="
$U{rejected}" size="$U{boxwidth}" maxlength="$C{maxmessage}" style="$C{stylepost
text};background-color:#$C{colbg};$U{style}"></td>|}
print qq|<td valign="top">|,submit($I{butsendto},qq| style="$C{stylepost
send}"|),qq|</td><td valign="top"><select name="sendto" size="1" style="$C{style
sendlist};background-color:#$C{colbg};color:#$C{coltxt}">|;
print '<option ',$Q{sendto}[0]eq'*'?'selected ':'','value="*">-',$I{selt
oall},'-</option>';
print '<option ',$Q{sendto}[0]eq'?'?'selected ':'','value="?">-',$I{selt
omem},'-</option>'if$U{status}>=2;
print '<option ',$Q{sendto}[0]eq'#'?'selected ':'','value="#">-',$I{selt
oadm},'-</option>'if$U{status}>=6;
if($C{allowpms}){foreach(sort {lc($a) cmp lc($b)} keys %P){print '<optio
n ',$Q{sendto}[0]eq$P{$_}[0]?'selected ':'',qq|value="$P{$_}[0]" style="$P{$_}[2
]">$_</option>|unless$U{nickname}eq$_}}
print '</select></td></tr></table></form></td></tr><tr><td height="8"></
td></tr><tr><td align="center"><table cellspacing="0"><tr><td>',frmpst('delete',
'last'),submit($I{butdellast},qq| style="$C{styledellast}"|),'</form></td><td>',
frmpst('delete','all'),submit($I{butdelall},qq| style="$C{styledelall}"|),'</for
m></td><td width="10"></td><td>';
print frmpst('post'),hidden('sendto',$Q{sendto}[0]),hidden('multi',$Q{mu
lti}[0]?'':'on'),submit($Q{multi}[0]?$I{butsingleline}:$I{butmultiline},qq| styl
e="$C{styleswitch}"|),'</form></td>'if$C{allowmultiline};
print '</tr></table></td></tr></table></center>';
print_end();
}
sub send_help{
unless($C{rulestxt}=~/<br>/i){$C{rulestxt}=~s/\r\n/<br>/g;$C{rulestxt}=~
s/\n/<br>/g;$C{rulestxt}=~s/\r/<br>/g}
$C{rulestxt}=~s/<IP>/$ENV{REMOTE_ADDR}/g;
print_start('rules');
print "<h2>$I{rules}</h2>$C{rulestxt}<br><br><hr><h2>$I{help}</h2>",$U{s
tatus}>=1?"$I{helpguests}<br>":'',$U{status}>=2?"<br>$I{helpregs}<br>":'',$U{sta
tus}>=6?"<br>$I{helpmods}<br>":'',$U{status}>=7?"<br>$I{helpadmins}<br>":'',"<br
><hr><center>$H{backtochat}<br>$H{versiontag}</center>";
print_end();
}
sub send_profile{
$I{passhelp}=~s/<MIN>/$C{minpass}/;
$I{refreshrate}=~s/<MIN>/$C{minrefresh}/;
$I{refreshrate}=~s/<MAX>/$C{maxrefresh}/;
$I{entryrefresh}=~s/<DEFAULT>/$C{defaultrefresh}/;
print_start('profile');
print "<center><$H{form}>",hidden('action','profile'),hidden('do','save'
),hidden('session',$U{session}),qq|<h2>$I{profileheader}</h2><i>$_[0]</i><table
cellspacing="0">|,thr(),qq|<tr><td><table cellspacing="0" width="100%"><tr><td a
lign="left"><b>$I{refreshrate}</b></td><td align="right"><table cellspacing="0">
<tr><td> </td><td><input type="text" name="refresh" size="3" maxlength="3"
value="$U{refresh}"></td></tr></table></td></tr></table></td></tr>|,thr(),qq|<tr
><td><table cellspacing="0" width="100%"><tr><td align="left"><b>$I{fontcolour}<
/b> (<a href="$S?action=colours&session=$U{session}" target="view">$I{viewco
lours}</a>)</td><td align="right"><table cellspacing="0"><tr><td> </td><td>
<input type="text" size="7" maxlength="6" value="$U{colour}" name="colour"></td>
</tr></table></td></tr></table></td></tr>|,thr();
if($U{status}>=2 and $C{allowfonts}){
print qq|<tr><td><table cellspacing="0" width="100%"><tr><td ali
gn="left"><b>$I{fontface}</b></td><td align="right"><table cellspacing="0"><tr><
td> </td><td><select name="font" size="1"><option value="">* $I{fontdefault
} *</option>|;
foreach(sort keys %F){print '<option style="',get_style($F{$_}),
'" ',$U{fontinfo}=~/$F{$_}/?'selected ':'','value="',$_,'">',$_,'</option>'}
print qq|</select></td><td> </td><td><input type="checkbox"
name="bold" id="bold" value="on"|,$U{fontinfo}=~/<i?bi?>/?' checked':'',qq|></t
d><td><label for="bold"><b>$I{fontbold}</b></label></td><td> </td><td><inpu
t type="checkbox" name="italic" id="italic" value="on"|,$U{fontinfo}=~/<b?ib?>/?
' checked':'',qq|></td><td><label for="italic"><i>$I{fontitalic}</i></label></td
></tr></table></td></tr></table></td></tr>|,thr();
}
print qq|<tr><td align="center">$U{displayname} : |,style_this($I{f
ontexample},$U{fontinfo}),'</td></tr>',thr(),qq|<tr><td><table cellspacing="0" w
idth="100%"><tr><td align="left"><b>$I{boxsizes}</b></td><td align="right"><tabl
e cellspacing="0"><tr><td> </td><td>$I{boxwidth}</td><td><input type="text"
name="boxwidth" size="3" maxlength="3" value="$U{boxwidth}"></td>|,$C{allowmult
iline}?qq|<td> </td><td>$I{boxheight}</td><td><input type="text" name="boxh
eight" size="3" maxlength="3" value="$U{boxheight}"></td>|:'',qq|</tr></table></
td></tr></table></td></tr>|,thr();
if($U{status}>=2){
print qq|<tr><td><table cellspacing="0" width="100%"><tr><td ali
gn="left"><b>$I{entryrefresh}</b></td><td align="right"><table cellspacing="0"><
tr><td> </td><td><input type="text" name="entryrefresh" size="3" maxlength=
"3" value="$U{entryrefresh}"></td></tr></table></td></tr></table></td></tr>|,thr
(),qq|<tr><td><table cellspacing="0" width="100%"><tr><td align="left"><b>$I{cha
for(my $blue=0x00;$blue<=0xFF;$blue+=0x33){
my $hcol=sprintf('%02X',$red).sprintf('%02X',$gr
een).sprintf('%02X',$blue);
print qq|<font color="#$hcol"><b>$hcol</b></font
> |;
}print '<br>';
}print '<br>';
}
print "</tt>$H{backtoprofile}</center>";
print_end();
}
sub send_login{
$I{nickhelp}=~s/<MAX>/$C{maxname}/;
$I{passhelp}=~s/<MIN>/$C{minpass}/;
$C{header}=~s/<IP>/$ENV{REMOTE_ADDR}/g;
$C{footer}=~s/<IP>/$ENV{REMOTE_ADDR}/g;
$C{header}=~s/<VER>/$H{versiontag}/g;
$C{footer}=~s/<VER>/$H{versiontag}/g;
print_start('login');
print qq|<center>$C{header}<$H{form} target="_parent">|,hidden('action',
'login'),qq|<table $C{tableattributes}><tr title="$I{nickhelp}"><td align="left
">$I{nickname}</td><td align="right"><input type="text" name="nick" size="15" st
yle="$C{stylelogintext}"></td></tr><tr title="$I{passhelp}"><td align="left">$I{
password}</td><td align="right"><input type="password" name="pass" size="15" sty
le="$C{stylelogintext}"></td></tr>|;
get_nowchatting();
unless($T{noguests}){
print qq|<tr><td colspan="2" align="center">$I{selcolguests}<br>
<select style="$C{stylecolselect};color:#$C{coltxt};background-color:#$C{colbg};
" name="colour"><option value="">* |,$C{rndguestcol}?$I{selcolrandom}:$I{selcold
efault},' *</option>';
print_colours();
print '</select></td></tr>';
}
elsif($C{noguests}){
print qq|<tr><td colspan="2" align="center">$C{noguests}</td></t
r>|;
}
print '<tr><td colspan="2" align="center">',submit($C{loginbutton},qq| s
tyle="$C{styleenter}"|),"</td></tr></table></form>$C{nowchatting}<br>$C{footer}<
/center>";
print_end();
}
sub send_error{my($err,$mes)=@_;
$err=~s/<NICK>/$U{displayname}/g;
$mes=htmlsafe($mes).'<br><br>'if$mes;
print_start('error');
print "<h2>$I{error} $err</h2>$mes$H{backtologin}";
print_end();
}
sub send_fatal{
return if $Q{action}[0]=~/^setup|init$/;
set_config_defaults();
set_html_vars();
print_start('error');
print "<h2>$I{fatalerror}</h2>$_[0]";
print_end();
}
sub print_chatters{
print '<table cellspacing="0"><tr>';
print_waiting()if$T{waitings};
print qq|<td valign="top"><b>$I{members}</b></td><td> </td><td vali
gn="top">|,join(' ',@M),'</td>',@G?'<td> </td>':''if@M;
print qq|<td valign="top"><b>$I{guests}</b></td><td> </td><td valig
n="top">|,join(' ',@G),'</td>'if@G;
print '</tr></table>';
}
sub print_waiting{
$I{butcheckwait}=~s/<COUNT>/$T{waitings}/;
print qq|<td valign="top"><$H{form}>|,hidden('action','admin'),hidden('d
o','newcomers'),hidden('session',$U{session}),submit($I{butcheckwait},qq| style=
"$C{stylecheckwait}"|),'</form></td><td> </td>';
}
sub print_memberslist{
foreach(sort {lc($a) cmp lc($b)} keys %A){
if(!$_[0] or $A{$_}[1]==$_[0]){
print qq|<option value="$A{$_}[0]" style="$A{$_}[2]">$_|
;
unless($_[0]){
print ' ',$I{symdenied}if$A{$_}[1]==0;
print ' ',$I{symmod} if$A{$_}[1]==6;
print ' ',$I{symadmin} if$A{$_}[1]>=7;
}
print '</option>';
}
}
}
######################################################################
# content filters
######################################################################
sub print_filters{
print qq|<tr><td colspan="2" align="left">$I{filterslist}</td></tr>|;
my $i=1;foreach(split('<>',$C{textfilters})){print_filter($i++,$_)}
print qq|<tr><td colspan="2" align="left">$I{filtersnew}</td></tr>|;
print_filter();
}
sub print_filter{my($no,$filter)=@_;
$no='new' unless $no;
my($type,$match,$action,$replace,$active)=split('"',$filter);
$match=htmlactive($match);
$replace=htmlactive($replace);
my $fchoose='';
my @selected;
my $rxerror;
if($no eq 'new'){
$fchoose=' selected';
}else{
$fchoose=' disabled';
# compile regex and check for errors
if($type==2){
my $rx='m/$match/';
eval $rx;
if($@){
$rxerror=$I{fregexerror};
$active=2;
}
}
$selected[$type]=' selected';
$selected[$action+2]=' selected';
$selected[$active+5]=' selected';
$match=htmlsafe($match);
$replace=htmlsafe($replace);
}
print qq|<tr><td colspan=2 align=right><select name="ftype$no"><option v
alue="$type"$fchoose>$I{fchoosetype}</option><option value="$type" disabled>$I{f
separator}</option><option value="1"$selected[1]>$I{ftypetext}</option><option v
alue="2"$selected[2]>$I{ftyperegex}</option></select><input type="text" name="fm
atch$no" size="60" value="$match"></td></tr><tr><td colspan=2 align=right><selec
t name="faction$no"><option value="$action"$fchoose>$I{fchooseaction}</option><o
ption value="$action" disabled>$I{fseparator}</option><option value="1"$selected
[3]>$I{factionreplace}</option><option value="2"$selected[4]>$I{factionkick}</op
tion><option value="3"$selected[5]>$I{factionpurge}</option></select><input type
="text" name="freplace$no" size="60" value="$replace"></td></tr>|;
print qq|<tr><td colspan=2 align=right>$rxerror <select name="fact
ive$no"><option value="1"$selected[6]>$I{factive}</option><option value="2"$sele
cted[7]>$I{fdisabled}</option><option value="3">$I{fdelete}</option></select></t
d></tr>| if $active;
print qq|<tr><td colspan=2> </td></tr>|;
}
sub filters_from_queries{
# type
0: undef 1: text
2: regex
# action 0: undef 1: replace 2: kick
3: purge
# active 0: undef 1: active
2: disabled 3: delete
# 'type"match"action"replacement"active<>....<>....<>'
my @filter;
for(my $i=1;;$i++){
last if !$Q{"ftype$i"}[0];
# empty
next if $Q{"factive$i"}[0]==3; # delete
next if $Q{"ftype$i"}[0]!~/^1|2$/;
next if $Q{"faction$i"}[0]!~/^1|2|3$/;
next if $Q{"factive$i"}[0]!~/^1|2$/;
foreach("fmatch$i","freplace$i"){$Q{$_}[0]=~s/^\s+|\s+$//g}
$Q{"factive$i"}[0]=2 unless $Q{"fmatch$i"}[0];
push @filter,join('"',$Q{"ftype$i"}[0],htmlsafe($Q{"fmatch$i"}[0
]),$Q{"faction$i"}[0],htmlsafe($Q{"freplace$i"}[0]),$Q{"factive$i"}[0]);
}
foreach("fmatchnew","freplacenew"){$Q{$_}[0]=~s/^\s+|\s+$//g}
if($Q{"ftypenew"}[0]=~/^1|2$/ && $Q{"factionnew"}[0]=~/^1|2|3$/ && $Q{"f
matchnew"}[0]ne''){
push @filter,join('"',$Q{"ftypenew"}[0],htmlsafe($Q{"fmatchnew"}
[0]),$Q{"factionnew"}[0],htmlsafe($Q{"freplacenew"}[0]),'1');
}
return join('<>',@filter);
}
sub apply_filters{
my $n;
foreach(split('<>',$C{textfilters})){
my($type,$match,$action,$replace,$active)=split('"',$_);
next unless $match;
if($active==1){
$match=htmlactive($match);
$replace=htmlactive($replace);
if($type==1){# text
$match=~s/\s*\|\s*/|/g;
$match=~s/\|+/|/g;
$match=~s/^\||\|$//g;
$match=~s/([^\w\d\s\|])/'\\x'.unpack('H*',$1)/ge
;
my $rx='$n=($U{message}=~s/$match/$replace/ig)';
eval $rx;
}
elsif($type==2){# regex
# Evaluating arbitrary user-input is a huge secu
rity risk!!!
# => Escape all special characters, only allow $
1,$2,.. for replacements.
$replace=~s/([^\w\d\s\$]|[\$](?![1-9]))/'\\x'.un
pack('H*',$1)/ge;
my $rx='$n=($U{message}=~s/$match/qq{qq{$replace
}}/igee)';
eval $rx;
}
if($n>0){# matches found
$U{autokick}=$action if $action>=2;
$U{kickmessage}=$replace if $action==3;
}
}
last if $U{autokick}==3;
}
}
######################################################################
# session management
######################################################################
sub create_session{
$I{errbadnick}=~s/<MAX>/$C{maxname}/;
$I{errbadpass}=~s/<MIN>/$C{minpass}/;
$U{nickname}=cleanup_nick($Q{nick}[0]);
$U{passhash}=hash_this($U{nickname}.$Q{pass}[0]);
$U{colour}=$Q{colour}[0];
$U{status}=1;
send_error($I{errbadnick}) unless valid_nick($U{nickname});
check_member();# checked before allowed_nick/pass, in case character lim
its got changed e.g. so that registered nicks are not locked out
add_user_defaults();
if($U{status}==1){# no known member, we have a guest:
send_error($I{errnoguests}) unless $T{guests};
send_error($I{errbadnick}) unless allowed_nick($U{nickname});
send_error($I{errbadpass}) unless allowed_pass($Q{pass}[0]);
create_waiting_session() if $T{guests}==3;# send guest to waitin
g room
}
write_new_session();
}
sub write_new_session{
# read and update current sessions
my @lines=parse_sessions(open_file_rw(my$SESSIONS,'sessions',my$ferr));s
end_error($ferr)if$ferr;
my %sids;my $reentry=0;my $inuse=0;my $kicked=0;
for(my $i=$#lines; $i>=0;$i--){
my %temp=sessionhash($lines[$i]);
$sids{$temp{session}}=1;# collect all existing ids
if($temp{nickname}eq$U{nickname}){# nick already here?
if($U{passhash}eq$temp{passhash}){
%U=%temp;
add_user_defaults();
$U{status}==0?$kicked=1:$reentry=1;
splice(@lines,$i,1)if$reentry;
}else{
$inuse=1;
}
}elsif(similar_nick($temp{nickname},$U{nickname})){
$inuse=1 if $U{status}==1;
}
}
# create new session:
unless($inuse or $kicked){
unless($U{status}==1 and $T{noguests}){
do{$U{session}=hash_this(time.rand().$U{nickname})}while
($sids{$U{session}});# check for hash collision
push(@lines,sessionline(%U));
}
}
print $SESSIONS @lines;
$ferr=close_file($SESSIONS,'sessions');send_error($ferr)if$ferr;
send_error($I{errbadlogin}) if $inuse;
send_error($C{kickederror},$U{kickmessage}) if $kicked;
send_error($I{errnoguests}) if($U{status}==1 and $T{noguests});
clean_room() unless(keys %P);
add_system_message($C{roomentry}) unless $reentry;
}
sub kick_chatter{my($name,$mes)=@_;
my $kickednick='';
my @lines=parse_sessions(open_file_rw(my$SESSIONS,'sessions',my$ferr));s
end_error($ferr)if$ferr;
foreach(@lines){
my %temp=sessionhash($_);
if($temp{nickname}eq$name and $temp{status}!=0){
if($U{status}>$temp{status} or $U{nickname}eq$name){# ve
rify if status is sufficient to kick
$temp{status}='0';
$temp{lastpost}=60*($C{kickpenalty}-$C{guestsexp
ire})+$^T;
$temp{kickmessage}=$mes;
$_=sessionline(%temp);
$kickednick=style_this($temp{nickname},$temp{fon
tinfo});
}
}
}
print $SESSIONS @lines;
$ferr=close_file($SESSIONS,'sessions');send_error($ferr)if$ferr;
add_system_message($C{kickedmessage},$kickednick)if$kickednick;
return $kickednick;
}
my @f=split('l',$_[0]);
my %s=(
session
=>
$f[ 0] ,
nickname
=>pack('H*',$f[ 1]),
status
=>pack('H*',$f[ 2]),
refresh
=>pack('H*',$f[ 3]),
fontinfo
=>pack('H*',$f[ 4]),
lastpost
=>pack('H*',$f[ 5]),
passhash
=>
$f[ 6] ,
postid
=>pack('H*',$f[ 7]),
entryrefresh=>pack('H*',$f[ 8]),
boxwidth
=>pack('H*',$f[ 9]),
boxheight =>pack('H*',$f[10]),
ip
=>pack('H*',$f[11]),
useragent =>pack('H*',$f[12]),
kickmessage =>pack('H*',$f[13]),
);
return %s;
}
sub sessionline{
my %h=@_;
my $s=
$h{session}
.'l'.
unpack('H*',$h{nickname})
.'l'.
unpack('H*',$h{status})
.'l'.
unpack('H*',$h{refresh})
.'l'.
unpack('H*',$h{fontinfo})
.'l'.
unpack('H*',$h{lastpost})
.'l'.
$h{passhash}
.'l'.
unpack('H*',$h{postid})
.'l'.
unpack('H*',$h{entryrefresh}).'l'.
unpack('H*',$h{boxwidth})
.'l'.
unpack('H*',$h{boxheight}) .'l'.
unpack('H*',$h{ip})
.'l'.
unpack('H*',$h{useragent}) .'l'.
unpack('H*',$h{kickmessage}) .'l'.
"\n";
return $s;
}
######################################################################
# waiting room handling
######################################################################
sub create_waiting_session{
# check if name used in room already
my @lines=parse_sessions(slurp_file('sessions',my$ferr));
send_error($ferr)if$ferr;
foreach(@lines){
my %temp=sessionhash($_);
if(similar_nick($temp{nickname},$U{nickname})){
if($temp{passhash}eq$U{passhash}){# reentry, approved al
ready
%U=%temp;
add_user_defaults();
send_error($C{kickederror},$U{kickmessage}) if $
U{status}==0;
send_frameset();
}
$ferr=close_file($WAITING,'waiting');
}
sub send_waiting_room{
$I{waitmessage}=~s/<REFRESH>/$C{defaultrefresh}/;
$I{waitmessage}=~s/<NICK>/$U{displayname}/;
print_start('wait',$C{defaultrefresh},"$S?action=wait&session=$U{session
}&nocache=".substr($^T,-6));
print "<center><h2>$I{waitroom}</h2>$I{waitmessage}<br><br>";
print '<hr width="',substr($^T,-2),'%">';
print "<$H{form}>",hidden('action','wait'),hidden('session',$U{session})
,submit($I{butreloadw},qq| style="$C{stylewaitrel}"|),'</form></center>';
print_end();
}
sub send_waiting_admin{
my @lines=parse_waitings(slurp_file('waiting',my$ferr));
send_error($ferr)if$ferr;
print_start('admin');
print qq|<center><h2>$I{admwaiting}</h2>|;
if($T{waitings}){
print qq|<$H{form}>|,hidden('action','admin'),hidden('do','newco
mers'),hidden('session',$U{session}),qq|<table cellpadding="5"><thead align="lef
t"><tr><th><b>$I{nicklist}</b></th><th><b>$I{ip}</b></th><th><b>$I{useragent}</b
></th></tr></thead><tbody align="left" valign="middle">|;
foreach(@lines){
my %temp=waitinghash($_);
next if $temp{status}!=1;
print qq|<tr>|,hidden('alls',$temp{session}),qq|<td><inp
ut type="checkbox" name="csid" id="$temp{session}" value="$temp{session}"><label
for="$temp{session}"> <font color="#$temp{colour}">$temp{nickname}</font><
/label></td><td>$temp{ip}</td><td>$temp{useragent}</td></tr>|;
}
print qq|</tbody></table><br><table><tr><td><input type="radio"
name="what" value="allowchecked" id="allowchecked" checked></td><td><label for="
allowchecked"> $I{allowchecked} </label></td><td><input type="radio"
name="what" value="allowall" id="allowall"></td><td><label for="allowall"> $I{al
lowall} </label></td><td><input type="radio" name="what" value="denyc
hecked" id="denychecked"></td><td><label for="denychecked"> $I{denychecked} 
; </label></td><td><input type="radio" name="what" value="denyall" id="deny
all"></td><td><label for="denyall"> $I{denyall} </label></td></tr><tr
><td colspan="8" align="center">$I{denymessage} <input type="text" name="ki
ckmessage" size="45"></td></tr><tr><td colspan="8" align="center">|,submit($I{bu
tallowdeny}),qq|</td></tr></table></form><br>|;
}else{
print "$I{waitempty}<br><br>";
}
print "$H{backtochat}</center>";
print_end();
}
sub get_waiting_count{
# return number of sessions to be approved, for admin-button
parse_waitings(slurp_file('waiting',my$ferr));
send_error($ferr)if$ferr;
return $T{waitings};
}
sub parse_waitings{my @lines=@_;
# returns cleaned up sessions and populates global variables
$T{waitings}=0;
for(my $i=$#lines; $i>=0;$i--){
my %temp=waitinghash($lines[$i]);
if(expiredw($temp{timestamp})){
splice(@lines,$i,1);
}else{
if($Q{session}[0]eq$temp{session}){
%U=%temp;
add_user_defaults();
}
$T{waitings}++ if $temp{status}==1;
}
}
return @lines;
}
sub edit_waiting_sessions{
my %sids=();my $newstatus=1;
if($_[0]eq'allowchecked'){
foreach(@{$Q{csid}}){$sids{$_}=1}
$newstatus=2;
}elsif($_[0]eq'denychecked'){
foreach(@{$Q{csid}}){$sids{$_}=1}
$newstatus=0;
}elsif($_[0]eq'allowall'){
foreach(@{$Q{alls}}){$sids{$_}=1}
$newstatus=2;
}elsif($_[0]eq'denyall'){
foreach(@{$Q{alls}}){$sids{$_}=1}
$newstatus=0;
}else{return}
my @lines=parse_waitings(open_file_rw(my$WAITING,'waiting',my$ferr));sen
d_error($ferr)if$ferr;
foreach my $wait (@lines){
my %temp=waitinghash($wait);
if($temp{status}==1 and $sids{$temp{session}}){
$temp{status}=$newstatus;
$temp{kickmessage}=$Q{kickmessage}[0] if $newstatus==0;
}
$wait=waitingline(%temp);
}
print $WAITING @lines;
$ferr=close_file($WAITING,'waiting');send_error($ferr)if$ferr;
}
sub waitinghash{
my @f=split('l',$_[0]);
my %w=(
session
=>
$f[0] ,
timestamp =>pack('H*',$f[1]),
nickname =>pack('H*',$f[2]),
passhash =>
$f[3] ,
colour
=>pack('H*',$f[4]),
ip
=>pack('H*',$f[5]),
useragent =>pack('H*',$f[6]),
status
=>pack('H*',$f[7]),
kickmessage=>pack('H*',$f[8]),
);
return %w;
}
sub waitingline{
my %h=@_;
my $w=
$h{session}
.'l'.
unpack('H*',$h{timestamp}) .'l'.
unpack('H*',$h{nickname}) .'l'.
$h{passhash}
.'l'.
unpack('H*',$h{colour})
.'l'.
unpack('H*',$h{ip})
.'l'.
unpack('H*',$h{useragent}) .'l'.
unpack('H*',$h{status})
.'l'.
unpack('H*',$h{kickmessage}).'l'.
"\n";
return $w;
}
######################################################################
# member handling
######################################################################
sub valid_admin{
($U{nickname},$U{pass},$U{hexpass})=@_;
$U{pass}||=pack('H*',$U{hexpass});# masked pass on setup pages
# superuser?
$U{passhash}=hash_this($U{nickname}.hash_this($U{nickname}).hash_this($U
{pass}).$U{pass});
my($sudata)=slurp_file('admin',my$ferr);
send_error($ferr)if$ferr;
if($U{passhash}eq$sudata){
$U{status}=9;
return 2
}
# main admin?
$U{passhash}=hash_this($U{nickname}.$U{pass});
my @lines=slurp_file('members',$ferr);
send_error($ferr)if$ferr;
foreach(@lines){
my %temp=memberhash($_);
if($temp{nickname}eq$U{nickname} and $temp{passhash}eq$U{passhas
h} and $temp{status}==8){
return 1
}
}
# no admin
return 0
}
sub check_member{
my @lines=slurp_file('members',my$ferr);
send_error($ferr)if$ferr;
my $similar=0;
foreach(@lines){
my %temp=memberhash($_);
if($temp{nickname}eq$U{nickname}){
if($temp{passhash}eq$U{passhash}){
%U=%temp;
last;
}else{send_error($I{errbadlogin})}
}
$similar=1 if similar_nick($temp{nickname},$U{nickname});
}
send_error($I{errbadlogin}) if($U{status}==1 and $similar);
send_error($I{erraccdenied}) if($U{status}==0 or $U{status}>8);
}
sub read_members{
my @lines=slurp_file('members',my$ferr);
send_error($ferr)if$ferr;
%A=();
foreach(@lines){
my %temp=memberhash($_);
$A{$temp{nickname}}=[unpack('H*',$temp{nickname}),$temp{status},
get_style("#$temp{colour} $F{$temp{fontface}} <$temp{fonttags}>")];
}
}
sub register_guest{
send_admin() if $Q{name}[0]eq'';
unless($P{pack('H*',$Q{name}[0])}[1]==1){
$I{errcantreg}=~s/<NICK>/pack('H*',$Q{name}[0])/e;
send_admin($I{errcantreg});
}
my @lines=open_file_rw(my$SESSIONS,'sessions',my$ferr);send_error($ferr)
if$ferr;
my %reg;
foreach(@lines){
my %temp=sessionhash($_);
if(unpack('H*',$temp{nickname})eq$Q{name}[0] and $temp{status}==
1){
$temp{status}=2;
%reg=%temp;
($reg{colour})=$reg{fontinfo}=~/#([a-f0-9]{6})/i;
print $SESSIONS sessionline(%temp);
}
else{
print $SESSIONS $_ unless expireds($temp{lastpost},$temp
{status});
}
}
$ferr=close_file($SESSIONS,'sessions');send_error($ferr)if$ferr;
unless($reg{status}){
$I{errcantreg}=~s/<NICK>/pack('H*',$Q{name}[0])/e;
send_admin($I{errcantreg});
}
@lines=open_file_rw(my$MEMBERS,'members',$ferr);send_error($ferr)if$ferr
;
print $MEMBERS @lines;
foreach(@lines){
my %temp=memberhash($_);
if(unpack('H*',$temp{nickname})eq$Q{name}[0]){
$ferr=close_file($MEMBERS,'members');send_error($ferr)if
$ferr;
$I{erralreadyreg}=~s/<NICK>/pack('H*',$Q{name}[0])/e;
send_admin($I{erralreadyreg});
}
}
print $MEMBERS memberline(%reg);
$ferr=close_file($MEMBERS,'members');send_error($ferr)if$ferr;
add_system_message($C{regmessage},style_this($reg{nickname},$reg{fontinf
o}));
}
sub register_new{
$Q{name}[0]=cleanup_nick($Q{name}[0]);
send_admin() if $Q{name}[0]eq'';
$I{errbadnick}=~s/<MAX>/$C{maxname}/;
$I{errbadpass}=~s/<MIN>/$C{minpass}/;
if($P{$Q{name}[0]}){
$I{errcantregnew}=~s/<NICK>/$Q{name}[0]/;
send_admin($I{errcantregnew});
}
send_admin($I{errbadnick}) unless valid_nick($Q{name}[0]);
send_admin($I{errbadpass}) unless allowed_pass($Q{pass}[0]);
my @lines=open_file_rw(my$MEMBERS,'members',my$ferr);send_error($ferr)if
$ferr;
print $MEMBERS @lines;
foreach(@lines){
my %temp=memberhash($_);
if($temp{nickname}eq$Q{name}[0]){
$ferr=close_file($MEMBERS,'members');send_error($ferr)if
$ferr;
$I{erralreadyreg}=~s/<NICK>/$Q{name}[0]/;
send_admin($I{erralreadyreg});
}
}
my %reg=(
nickname
=>$Q{name}[0],
passhash
=>hash_this($Q{name}[0].$Q{pass}[0]),
status
=>'2',
refresh
=>$C{defaultrefresh},
colour
=>$C{coltxt},
fontface
=>'',
fonttags
=>'',
entryrefresh=>$C{defaultrefresh}
);
print $MEMBERS memberline(%reg);
$ferr=close_file($MEMBERS,'members');send_error($ferr)if$ferr;
$I{succreg}=~s/<NICK>/$Q{name}[0]/;
send_admin($I{succreg});
}
sub change_status{
send_admin()if($Q{name}[0]eq'' or $Q{set}[0]eq'');
my $nick=pack('H*',$Q{name}[0]);
if($U{status}<=$Q{set}[0] or $Q{set}[0]!~/^[0267\-]$/){
$I{errcantstatus}=~s/<NICK>/$nick/;
send_admin($I{errcantstatus})
}
my $found=0;
my @lines=open_file_rw(my$MEMBERS,'members',my$ferr);send_error($ferr)if
$ferr;
foreach(@lines){
my %temp=memberhash($_);
if($temp{nickname}eq$nick and $U{status}>$temp{status}){
$found=1;
next if $Q{set}[0]eq'-';
$found=2;
$temp{status}=$Q{set}[0];
print $MEMBERS memberline(%temp);
}
else{
print $MEMBERS $_;
}
}
$ferr=close_file($MEMBERS,'members');send_error($ferr)if$ferr;
if($found==1){
$I{succdelmem}=~s/<NICK>/$nick/;
send_admin($I{succdelmem})
}
elsif($found==2){
$I{succstatus}=~s/<NICK>/$nick/;
send_admin($I{succstatus})
}else{
$I{errcantstatus}=~s/<NICK>/$nick/;
send_admin($I{errcantstatus})
}
}
sub amend_profile{
foreach(qw(refresh boxwidth boxheight entryrefresh)){$Q{$_}[0]=~y/0-9//c
d;$Q{$_}[0]=~s/^0+//}
$U{refresh}=$Q{refresh}[0]||$C{defaultrefresh};
$U{refresh}=$C{minrefresh}if$U{refresh}<$C{minrefresh};
$U{refresh}=$C{maxrefresh}if$U{refresh}>$C{maxrefresh};
$U{colour}=($Q{colour}[0]=~/^[a-f0-9]{6}$/i)?$Q{colour}[0]:$C{coltxt};
$U{fonttags}='';
$U{fonttags}.='b'if($Q{bold}[0]and$U{status}>=2);
$U{fonttags}.='i'if($Q{italic}[0]and$U{status}>=2);
$U{fontface}=$Q{font}[0]if($F{$Q{font}[0]}and$U{status}>=2);
$U{fontinfo}="#$U{colour} $F{$U{fontface}} <$U{fonttags}>";
$U{displayname}=$U{nickname};
$U{displayname}=~s/\s+/ /g;
$U{displayname}=style_this($U{nickname},$U{fontinfo});
$U{boxwidth}=$Q{boxwidth}[0]if$Q{boxwidth}[0]>0;
$U{boxheight}=$Q{boxheight}[0]if$Q{boxheight}[0]>0;
$U{boxwidth}=$C{boxwidthdef}if$U{boxwidth}>=1000;
$U{boxheight}=$C{boxheightdef}if$U{boxheight}>=1000;
$U{entryrefresh}=$Q{entryrefresh}[0]||$C{defaultrefresh};
$U{entryrefresh}=1if$U{entryrefresh}<1;
$U{entryrefresh}=$C{defaultrefresh}if$U{entryrefresh}>$C{defaultrefresh}
;
}
sub save_profile{
if(!$Q{oldpass}[0]and($Q{newpass}[0]or$Q{confirmpass}[0])){
check_session();
send_profile($I{errwrongpass});
}
if($Q{newpass}[0]ne$Q{confirmpass}[0]){
check_session();
send_profile($I{errdiffpass});
}
if($Q{oldpass}[0]and!allowed_pass($Q{newpass}[0])){
$I{errbadpass}=~s/<MIN>/$C{minpass}/;
check_session();
send_profile($I{errbadpass});
}
# check and rewrite session
my @lines=parse_sessions(open_file_rw(my$SESSIONS,'sessions',my$ferr));s
end_error($ferr)if$ferr;
$U{oldhash}=$Q{oldpass}[0]?hash_this($U{nickname}.$Q{oldpass}[0]):$U{pas
shash};
$U{newhash}=$Q{newpass}[0]?hash_this($U{nickname}.$Q{newpass}[0]):$U{pas
shash};
$U{orihash}=$U{passhash};
foreach(@lines){
my %temp=sessionhash($_);
if($temp{session}eq$U{session} and $temp{status}>0 and $temp{pas
shash}eq$U{oldhash}){
amend_profile();
$U{passhash}=$U{newhash};
print $SESSIONS sessionline(%U);
}else{
print $SESSIONS $_;
}
}
$ferr=close_file($SESSIONS,'sessions');send_error($ferr)if$ferr;
send_error($I{errexpired}) unless $U{session};
send_error($C{kickederror},'',$U{kickmessage}) if $U{status}==0;
send_error($I{errnoguests}) if($U{status}==1 and $T{noguests});
send_profile($I{errwrongpass}) if $U{orihash}ne$U{oldhash};
# rewrite member file
if($U{status}>=2){
my $err='';
my @lines=open_file_rw(my$MEMBERS,'members',$ferr);send_error($f
err)if$ferr;
foreach(@lines){
my %temp=memberhash($_);
if($temp{nickname}eq$U{nickname}){
$U{sessionstatus}=$U{status};
$U{status}=$temp{status};
$err=$I{errwrongpass} unless $temp{passhash} eq
$U{orihash};
print $MEMBERS $err?$_:memberline(%U);
$U{status}=$U{sessionstatus};
}
else{
print $MEMBERS $_;
}
}
$ferr=close_file($MEMBERS,'members');send_error($ferr)if$ferr;
send_profile($err) if $err;
}
send_profile($I{succchanged});
}
sub memberhash{
my @f=split('l',$_[0]);
my %m=(
nickname
=>pack('H*',$f[0]),
passhash
=>
$f[1] ,
status
=>pack('H*',$f[2]),
refresh
=>pack('H*',$f[3]),
colour
=>pack('H*',$f[4]),
fontface
=>pack('H*',$f[5]),
fonttags
=>pack('H*',$f[6]),
entryrefresh =>pack('H*',$f[7]),
boxwidth
=>pack('H*',$f[8]),
boxheight
=>pack('H*',$f[9]),
);
return %m;
}
sub memberline{
my %h=@_;
my $m=
unpack('H*',$h{nickname})
.'l'.
$h{passhash}
.'l'.
unpack('H*',$h{status})
.'l'.
unpack('H*',$h{refresh})
.'l'.
unpack('H*',$h{colour})
.'l'.
unpack('H*',$h{fontface})
.'l'.
unpack('H*',$h{fonttags})
.'l'.
unpack('H*',$h{entryrefresh}).'l'.
unpack('H*',$h{boxwidth})
.'l'.
unpack('H*',$h{boxheight}) .'l'.
"\n";
return $m;
}
sub add_user_defaults{
$U{ip}=$ENV{REMOTE_ADDR};
$U{useragent}=htmlsafe($ENV{HTTP_USER_AGENT});
$U{refresh}||=$C{defaultrefresh};
unless($U{fontinfo}){
unless($U{colour}=~/^[a-f0-9]{6}$/i){
$U{colour}=$C{coltxt};
if($C{rndguestcol}){
do{$U{colour}=sprintf('%02X',int(rand(256))).spr
intf('%02X',int(rand(256))).sprintf('%02X',int(rand(256)))}until(abs(greyval($U{
colour})-greyval($C{colbg}))>75);
}
}
$U{fontinfo}="#$U{colour}";
$U{fontinfo}.=" $F{$U{fontface}} <$U{fonttags}>" if $C{allowfont
s};
}
($U{colour})=$U{fontinfo}=~/#([0-9A-Fa-f]{6})/;
$U{style}=get_style($U{fontinfo});
$U{entryrefresh}||=$C{defaultrefresh};
$U{boxwidth}||=$C{boxwidthdef};
$U{boxheight}||=$C{boxheightdef};
$U{timestamp}||=$^T;
$U{lastpost}||=$^T;
$U{postid}||='OOOOOO';
$U{displayname}=$U{nickname};
$U{displayname}=~s/\s+/ /g;
$U{displayname}=style_this($U{displayname},$U{fontinfo});
}
######################################################################
# message handling
######################################################################
sub validate_input{
$U{message}=substr($Q{message}[0],0,$C{maxmessage});
$U{rejected}=substr($Q{message}[0],$C{maxmessage}) unless $U{rejected};
if($U{message}=~/&[^;]{0,8}$/ and $U{rejected}=~/^([^;]{0,8};)/){
$U{message}.=$1;
$U{rejected}=~s/^$1//;
}
if($U{rejected}){
$U{rejected}=htmlsafe($U{rejected});
$U{rejected}=~s/<br>(<br>)+/<br><br>/g;
$U{rejected}=~s/<br><br>$/<br>/;
$C{allowmultiline}?$U{rejected}=~s/<br>/\n/g:$U{rejected}=~s/<br
>/ /g;
$U{rejected}=~s/^\s+|\s+$//g;
}
$U{message}=htmlsafe($U{message});
apply_filters();
if($C{allowmultiline} and $Q{multi}[0]){
$U{message}=~s/<br>(<br>)+/<br><br>/g;
$U{message}=~s/<br><br>$/<br>/;
$U{message}=~s/ / /g;
$U{message}=~s/<br> /<br> /g;
}else{
$U{message}=~s/<br>/ /g;
$U{message}=~s/^\s+|\s+$//g;
$U{message}=~s/\s+/ /g;
}
create_hotlinks();
$U{delstatus}=$U{status};
if($Q{sendto}[0]eq'*'){
$U{poststatus}='1';
$U{displaysend}=$C{mesall};
}
elsif($Q{sendto}[0]eq'?' and $U{status}>=2){
$U{poststatus}='2';
$U{displaysend}=$C{mesmem};
}
elsif($Q{sendto}[0]eq'#' and $U{status}>=6){
$U{poststatus}='6';
$U{displaysend}=$C{messtaff};
}
elsif($C{allowpms}){# known nick in room?
foreach(keys %P){if($Q{sendto}[0]eq$P{$_}[0]){
$U{recipient}=$_;
$U{displayrecp}=style_this($_,$P{$_}[2]);
}}
if($U{recipient}){
$U{poststatus}='9';
$U{delstatus}='9';
$U{displaysend}=$C{mespm};
}
else{# nick left already
$U{message}='';
$U{rejected}='';
}
}
else{# invalid recipient
$U{message}='';
$U{rejected}='';
}
$U{displaysend}=~s/<NICK>/$U{displayname}/;
$U{displaysend}=~s/<RECP>/$U{displayrecp}/;
}
sub formsafe{my $m=$_[0];
$m=~s/&/&/g;
$m=~s/"/"/g;
$m=~s/</</g;
$m=~s/>/>/g;
return $m;
}
sub htmlsafe{my $m=$_[0];
$m=~s/&(?![\w\d\#]{2,8};)/&/g;
$m=~s/</</g;
$m=~s/>/>/g;
$m=~s/"/"/g;
$m=~s/\r\n/<br>/g;
$m=~s/\n/<br>/g;
$m=~s/\r/<br>/g;
return $m;
}
sub htmlactive{my $m=$_[0];
$m=~s/"/"/g;
$m=~s/</</g;
$m=~s/>/>/g;
$m=~s/&/&/g;
$m=~s/\r\n/\n/g;
$m=~s/\r/\n/g;
return $m;
}
sub create_hotlinks{
return unless $C{createlinks};
########################################################################
###############
# Make hotlinks for URLs, redirect through dereferrer script to prevent
session leakage
########################################################################
###############
# 1. all explicit schemes with whatever xxx://yyyyyyy
$U{message}=~s~(\w*://[^\s<>]+)~<<$1>>~ig;
# 2. valid URLs without scheme:
$U{message}=~s~((?:[^\s<>]*:[^\s<>]*@)?[a-z0-9\-]+(?:\.[a-z0-9\-]+)+(?::
\d*)?/[^\s<>]*)(?![^<>]*>)~<<$1>>~ig; # server/path given
$U{message}=~s~((?:[^\s<>]*:[^\s<>]*@)?[a-z0-9\-]+(?:\.[a-z0-9\-]+)+:\d+
)(?![^<>]*>)~<<$1>>~ig; # server:port given
$U{message}=~s~([^\s<>]*:[^\s<>]*@[a-z0-9\-]+(?:\.[a-z0-9\-]+)+(?::\d+)?
)(?![^<>]*>)~<<$1>>~ig; # au:th@server given
# 3. likely servers without any hints but not filenames like *.rar zip e
xe etc.
$U{message}=~s~((?:[a-z0-9\-]+\.)*[a-z0-9]{16}\.onion)(?![^<>]*>)~<<$1>>
~ig;# *.onion
$U{message}=~s~([a-z0-9\-]+(?:\.[a-z0-9\-]+)+(?:\.(?!rar|zip|exe|gz|7z|b
at|doc)[a-z]{2,}))(?=[^a-z0-9\-\.]|$)(?![^<>]*>)~<<$1>>~ig;# xxx.yyy.zzz
# Convert every <<....>> into proper links:
$U{message}=~s/<<([^<>]+)>>/url2hotlink($1)/ge;
}
sub url2hotlink{
# check for surrounding < > " " ( ) etc. and create hotlink
my($pre,$url,$app)=$_[0]=~/^((?:&\w{1,7};)*)(.*?)((?:&\w{1,7};|[\{\[\(\)
\]\}])*)$/;
my $href=$url;
$href=~s/([\:\/\?\#\[\]\@\!\$\&\'\(\)\*\+\,\;\=\%])/sprintf("%%%02X",ord
($1))/eg;
return qq|$pre<a href="|.($C{useextderef}?$C{extderefurl}:$S).qq|?action
=redirect&url=$href" target="_blank">$url</a>$app|;
}
sub add_message{
return unless $U{message};
my %newmessage=(
postdate =>$^T,
postid
=>$U{postid},
poststatus=>$U{poststatus},
poster
=>$U{nickname},
recipient =>$U{recipient},
text
=>$U{displaysend}.style_this($U{message},$U{fontinfo})
,
delstatus =>$U{delstatus}
);
write_message(%newmessage);
}
sub add_staff_message{
my $mes=$_[0]||$U{message};return unless $mes;
my $nick=$_[1]||$U{displayname};
$mes=~s/<NICK>/$nick/g;
$C{messtaff}=~s/<NICK>//g;
my %sysmessage=(
postdate=>$^T,
postid=>substr(rand(),-6),
poststatus=>'6',
text=>$C{messtaff}.$mes,
delstatus=>'9'
);
write_message(%sysmessage);
}
sub add_system_message{
my $mes=$_[0]||$U{message};return unless $mes;
my $nick=$_[1]||$U{displayname};
$mes=~s/<NICK>/$nick/g;
my %sysmessage=(
postdate=>$^T,
postid=>substr(rand(),-6),
poststatus=>'1',
text=>$mes,
delstatus=>'9'
);
write_message(%sysmessage);
}
sub write_message{my%message=@_;
my @lines=open_file_rw(my$MESSAGES,'messages',my$ferr);send_error($ferr)
if$ferr;
while(my$line=shift@lines){
my %temp=messagehash($line);
print $MESSAGES $line unless expiredm($temp{postdate});
}
print $MESSAGES messageline(%message);
$ferr=close_file($MESSAGES,'messages');send_error($ferr)if$ferr;
}
sub clean_room{
my %sysmessage=(
postdate=>$^T,
postid=>substr(rand(),-6),
poststatus=>'1',
text=>$C{roomclean},
delstatus=>'9'
);
my $ferr=write_file('messages',messageline(%sysmessage));send_error($fer
r)if$ferr;
}
sub clean_selected{
my %mids;foreach(@{$Q{mid}}){$mids{$_}=1}
my @lines=open_file_rw(my$MESSAGES,'messages',my$ferr);send_error($ferr)
if$ferr;
while(my $line=shift@lines){
my %temp=messagehash($line);
print $MESSAGES $line unless(expiredm($temp{postdate}) or $mids{
$temp{postdate}.$temp{postid}});
}
$ferr=close_file($MESSAGES,'messages');send_error($ferr)if$ferr;
}
sub del_last_message{
my @lines=open_file_rw(my$MESSAGES,'messages',my$ferr);send_error($ferr)
if$ferr;
for(my$i=@lines;$i>=0;$i--){
my %temp=messagehash($lines[$i]);
if($U{nickname}eq$temp{poster}){
splice(@lines,$i,1);
last;
}
}
while(my$line=shift@lines){
my %temp=messagehash($line);
print $MESSAGES $line unless expiredm($temp{postdate});
}
$ferr=close_file($MESSAGES,'messages');send_error($ferr)if$ferr;
}
sub del_all_messages{
my $nick=$_[0]||$U{nickname};
my @lines=open_file_rw(my$MESSAGES,'messages',my$ferr);send_error($ferr)
if$ferr;
while(my $line=shift@lines){
my %temp=messagehash($line);
print $MESSAGES $line unless(expiredm($temp{postdate}) or $temp{
poster}eq$nick);
}
$ferr=close_file($MESSAGES,'messages');send_error($ferr)if$ferr;
}
sub print_messages{my $delstatus=$_[0];
my @lines=slurp_file('messages',my$ferr);
return if$ferr;
while(my $line=pop@lines){
my %message=messagehash($line);
if($delstatus){# select messages to delete
if($U{status}>$message{delstatus}){
print qq|<input type="checkbox" name="mid" id="$
message{postdate}$message{postid}" value="$message{postdate}$message{postid}"><l
abel for="$message{postdate}$message{postid}"> $message{text}</label><br>|u
nless expiredm($message{postdate});
}
}
elsif($U{status}>=$message{poststatus} or $U{nickname}eq$message
{poster} or $U{nickname}eq$message{recipient}){
print "$message{text}<br>" unless expiredm($message{post
date});
}
}
}
sub messagehash{
my @fields=split('l',$_[0]);
my %mes=(
postdate =>pack('H*',$fields[0]),
postid
=>pack('H*',$fields[1]),
poststatus=>pack('H*',$fields[2]),
poster
=>pack('H*',$fields[3]),
recipient =>pack('H*',$fields[4]),
text
=>pack('H*',$fields[5]),
delstatus =>pack('H*',$fields[6])
);
return %mes;
}
sub messageline{
my %mes=@_;
my $m=
unpack('H*',$mes{postdate}).'l'.
unpack('H*',$mes{postid}).'l'.
unpack('H*',$mes{poststatus}).'l'.
unpack('H*',$mes{poster}).'l'.
unpack('H*',$mes{recipient}).'l'.
unpack('H*',$mes{text}).'l'.
unpack('H*',$mes{delstatus})."l\n";
return $m;
}
######################################################################
# Superuser Setup
######################################################################
sub send_init{
my $result=create_files();
$I{setsuhelp}=~s/<DATA>/$datadir/;
$I{nickhelp}=~s/<MAX>/$C{maxname}/;
$I{passhelp}=~s/<MIN>/$C{minpass}/;
print_start('admin');
print "<center><h2>LE CHAT - $I{initsetup}</h2>";
print "<$H{form}>",hidden('action','init'),qq|<table cellspacing="0" wid
th="1"><tr><td align=center><h3>$I{setsu}</h3><table cellspacing="0"><tr title="
$I{nickhelp}"><td>$I{setsunick}</td><td><input type="text" name="sunick" size="1
5"></td></tr><tr title="$I{passhelp}"><td>$I{setsupass}</td><td><input type="tex
t" name="supass" size="15"></td></tr><tr title="$I{passhelp}"><td>$I{setsupassco
nf}</td><td><input type="text" name="supassc" size="15"></td></tr></table><br><b
r></td></tr><tr><td align="left">$I{setsuhelp}<br><br><br></td></tr><tr><td alig
n="center">|;
print qq|<h3>$I{initback}</h3></td></tr><tr><td align="left">$I{initback
help}<br></td></tr><tr><td align="center"><textarea name="backupdata" rows="8" c
ols="80" wrap="off">$result</textarea><br><br></td></tr><tr><td align="center"><
tr><td align="center"><br>|,submit($I{initbut}),"</td></tr></table></form><br>$H
{versiontag}</center>";
print_end();
}
sub create_files{
my $result='';
# create directories if needed
my @dirs=split('/',$datadir);my $dir='';
while($_=shift@dirs){$dir.=$_;mkdir($dir,0700) unless -d$dir;$dir.='/'}
chmod(0700,$datadir);
# create files, keep existing ones
create_file(qw(config members sessions messages waiting langedit));
# check initial data in lechat.txt
if(-e'./lechat.txt'){
my $backupdata='';
sysopen(LECHAT,'./lechat.txt',O_RDONLY) or return "$I{initlechat
txt}\n$I{errfile} (lechat.txt/open)";
flock(LECHAT,LOCK_SH) or return "$I{initlechattxt}\n$I{errfile}
(lechat.txt/lock)";
while(<LECHAT>){$backupdata.=$_};
close(LECHAT);
$result=get_restore_results('',$backupdata);
$result="$I{initlechattxt}\n$result";
unlink('./lechat.txt');
load_config();
}
return $result;
}
sub init_chat{
$I{suerrbadnick}=~s/<MAX>/$C{maxname}/;
$I{suerrbadpass}=~s/<MIN>/$C{minpass}/;
# restore backups if given
my $restore=$I{invalidbackup};
$restore=get_restore_results() if $Q{backupdata}[0];
$restore=~s/\n/<br>/g;
# write superuser into "admin"
my $suwrite;
if(-e"$datadir/admin"){
$suwrite=$I{suerrfileexist};
}elsif(!valid_nick($Q{sunick}[0])){
$suwrite=$I{suerrbadnick};
}elsif(!allowed_pass($Q{supass}[0])){
$suwrite=$I{suerrbadpass};
}elsif($Q{supass}[0]ne$Q{supassc}[0]){
$suwrite=$I{suerrbadpassc};
}else{# all good data here
$suwrite=write_admin(hash_this($Q{sunick}[0].hash_this($Q{sunick
}[0]).hash_this($Q{supass}[0]).$Q{supass}[0]));
}
# Print results:
print_start('admin');
print "<center><h2>LE CHAT - $I{initsetup}</h2><br><h3>$I{setsu}</h
3>$suwrite<br><br><br><h3>$I{initback}</h3>$restore<br><br><br>";
print "<$H{form}>",hidden('action','setup'),hidden('nick',$Q{sunick}[0])
,hidden('hexpass',unpack('H*',$Q{supass}[0])),submit($I{initgotosetup}),"</form>
<br>$H{versiontag}</center>";
print_end();
}
sub write_admin{
write_file('admin',$_[0]);
# read and verify data
my($suverify)=slurp_file('admin');
return $I{suwritesucc}if($_[0]eq$suverify);
# delete again if not written correctly
delete_file('admin');
return $I{suwritefail};
}
sub change_admin_status{
$I{errbadnick}=~s/<MAX>/$C{maxname}/;
$I{errbadpass}=~s/<MIN>/$C{minpass}/;
my $err;my %temp;
return $I{errnonick} unless $Q{admnick}[0];
return $I{errbaddata} unless $Q{what}[0]=~/^new|up|down$/;
if($Q{what}[0]eq'new'){
return $I{errbadnick} unless valid_nick($Q{admnick}[0]);
return $I{errbadpass} unless allowed_pass($Q{admpass}[0]);
$Q{admnick}[0]=unpack('H*',cleanup_nick($Q{admnick}[0]));
}
my @lines=open_file_rw(my$MEMBERS,'members',my$ferr);send_error($ferr)if
$ferr;
foreach(@lines){
%temp=memberhash($_);
if(unpack('H*',$temp{nickname})eq$Q{admnick}[0]){
if($Q{what}[0]eq'new'){$err=$I{errexistnick}}
elsif($Q{what}[0]eq'up'){
if($temp{status}==7){$temp{status}=8;$err=$I{rai
semainsucc}}
elsif($temp{status}==8){$err=$I{raisemaindone}}
else{$err=$I{raisemainfail}}
}
elsif($Q{what}[0]eq'down'){
if($temp{status}==8){$temp{status}=7;$err=$I{low
erregsucc}}
elsif($temp{status}==7){$err=$I{lowerregdone}}
else{$err=$I{lowerregfail}}
}
print $MEMBERS memberline(%temp);
}
else{
print $MEMBERS $_;
}
$err=~s/<NICK>/$temp{nickname}/ if $err;
}
if($Q{what}[0]eq'new' and !$err){
%temp=(nickname
=>pack('H*',$Q{admnick}[0]),
passhash
=>hash_this(pack('H*',$Q{admnick}[0]).$Q{adm
pass}[0]),
status
=>8,
refresh
=>$C{defaultrefresh},
colour
=>$C{coltxt},
entryrefresh =>$C{defaultrefresh},
boxwidth
=>$C{boxwidthdef},
boxheight
=>$C{boxheightdef});
print $MEMBERS memberline(%temp);
$err=$I{newmainreg};
$err=~s/<NICK>/$temp{nickname}/;
}
$ferr=close_file($MEMBERS,'members');send_error($ferr)if$ferr;
return $err;
}
######################################################################
# backup and restore
######################################################################
sub get_backup{my $fname=$_[0];
my $finfo='langedit'eq$fname?'LANGUAGE':uc($fname);
my $blob=join('n',slurp_file($fname,my$err)).'n';
return $err if $err;
my $fcomm=get_timestamp((stat("$datadir/$fname"))[9])." - $C{title}";
$fcomm="Language: $I{languagename} ($I{languagecode}) - $version ($lastc
hanged)"if'language'eq$fname;
$fcomm="Language: $L{languagename} ($L{languagecode}) - $version ($lastc
hanged)"if'langedit'eq$fname;
$blob=~s/[^a-f0-9ln]//gi;
$blob.='h'.hash_this($blob).'hi'.$finfo.'i';
$blob="-----BEGIN LE CHAT $finfo-----\n$fcomm\n\n".encode_this($blob)."----END LE CHAT $finfo-----";
return $blob;
}
sub get_restore_results{my($fname,$bdata)=@_;
$fname||='language config members';
$bdata||=$Q{backupdata}[0];
$bdata=~s/\r\n/<br>/g;
$bdata=~s/\n/<br>/g;
$bdata=~s/\r/<br>/g;
$bdata=~s/<br>/\n/g;
(my $conf)=$bdata=~/-----BEGIN LE CHAT CONFIG-----.*?\n\n(.*)-----END LE
CHAT CONFIG-----/s;
(my $memb)=$bdata=~/-----BEGIN LE CHAT MEMBERS-----.*?\n\n(.*)-----END L
E CHAT MEMBERS-----/s;
(my $lang)=$bdata=~/-----BEGIN LE CHAT LANGUAGE-----.*?\n\n(.*)-----END
LE CHAT LANGUAGE-----/s;
return $I{invalidbackup} unless($memb or $conf or $lang);
my $result='';
$result.=restore_file('langedit',$lang)."\n"if$fname=~/langedit/ and$lan
g;
$result.=restore_file('language',$lang)."\n"if$fname=~/language/ and$lan
g;
$result.=restore_file('config' ,$conf)."\n"if$fname=~/config/ and$con
f;
$result.=restore_file('members' ,$memb)."\n"if$fname=~/members/ and$mem
b;
return $result;
}
sub restore_file{my($fname,$fdata)=@_;
my $finfo='langedit'eq$fname?'LANGUAGE':uc($fname);
my $ftype=substr($fname,0,4);
my $content=decode_this($fdata);
my($blob,$hash,$info)=$content=~/^([a-f0-9ln]*)h([a-f0-9]+)hi([A-Z]+)i$/
;
if(hash_this($blob)eq$hash and $finfo eq $info){
$blob=~tr/n/\n/s;
my $ferror=write_file($fname,$blob);
if($ferror){return $I{$ftype.'restfail'}.' '.$ferror}
load_config();
return $I{$ftype.'restsucc'};
}
else{
return $I{$ftype.'restinval'};
}
}
sub load_langedit{
if(-e"$datadir/langedit"){
my @lines=slurp_file('langedit',my$ferr);
send_error($ferr)if$ferr;
foreach(@lines){
next unless /^[0-9a-f]/;
my($ikey,$ival)=split('l',$_);
$L{pack('H*',$ikey)}=pack('H*',$ival);
}
}
}
sub save_langedit{
open_file_wo(my$LANG,'langedit',my$ferr);send_error($ferr)if$ferr;
my $start=tell(DATA);
while(<DATA>){
my($ikey,$ival)=$_=~/^([a-z_]+)\s*=(.+)/i;
next unless $ikey;
$ival=$Q{$ikey}[0]unless'stop_action'eq$ikey;
$ival=~s/^\s*|\s*$//g;$ival=~s/\n//;$ival=~s/\r//;
$ival=htmlactive($ival);
print $LANG unpack('H*',$ikey),'l',unpack('H*',$ival),"l\n" if $
ival;
}
seek(DATA,$start,0);
$ferr=close_file($LANG,'langedit');send_error($ferr)if$ferr;
}
######################################################################
# file handling
######################################################################
sub get_guests_access{-s"$datadir/guests"||0}
sub get_chat_access{-s"$datadir/access"||0}
sub set_guests_access{
return if($_[0]<0 or $_[0]>3);
write_file('guests','#'x$_[0]);
expire_waiting_sessions()if$_[0]<3;
$T{guests}=get_guests_access();
}
sub set_chat_access{
return if($_[0]<0 or $_[0]>2);
write_file('access','#'x$_[0]);
$T{access}=get_chat_access();
}
(my $fcolour)=$styleinfo=~/(#.{6})/;
(my $fface)=$styleinfo=~/face="([^"]+)"/;
(my $sface)=$styleinfo=~/font-family:([^;]+);/;
if($fface ne ''){$sface=$fface;$sface=~s/^/'/;$sface=~s/$/'/;$sface=~s/,
/','/g}
else{$fface=$sface;$fface=~s/'//}
my $fstyle='';
$fstyle.="color:$fcolour;"if$fcolour;
$fstyle.="font-family:$sface;"if$sface;
$fstyle.='font-size:smaller;'if$fsmall;
$fstyle.='font-style:italic;'if$fitalic;
$fstyle.='font-weight:bold;'if$fbold;
return $fstyle;
}
sub style_this{my($text,$styleinfo)=@_;
(my $fbold)=$styleinfo=~/(<i?bi?>|:bold)/;
(my $fitalic)=$styleinfo=~/(<b?ib?>|:italic)/;
(my $fsmall)=$styleinfo=~/(size="-1"|:smaller)/;
(my $fcolour)=$styleinfo=~/(#.{6})/;
(my $fface)=$styleinfo=~/face="([^"]+)"/;
(my $sface)=$styleinfo=~/font-family:([^;]+);/;
if($fface ne ''){$sface=$fface;$sface=~s/^/'/;$sface=~s/$/'/;$sface=~s/,
/','/g}
else{$fface=$sface;$fface=~s/'//}
my $fstyle='';
$fstyle.="color:$fcolour;"if$fcolour;
$fstyle.="font-family:$sface;"if$sface;
$fstyle.='font-size:smaller;'if$fsmall;
$fstyle.='font-style:italic;'if$fitalic;
$fstyle.='font-weight:bold;'if$fbold;
my $fstart='<font';
$fstart.=qq| color="$fcolour"|if$fcolour;
$fstart.=qq| face="$fface"|if$fface;
$fstart.=qq| size="-1"|if$fsmall;
$fstart.=qq| style="$fstyle"|if$fstyle;
$fstart.='>';
$fstart.='<b>'if$fbold;
$fstart.='<i>'if$fitalic;
my $fend='';
$fend.='</i>'if$fitalic;
$fend.='</b>'if$fbold;
$fend.='</font>';
return "$fstart$text$fend";
}
######################################################################
# configuration, defaults and internals
######################################################################
sub load_config{
set_internal_defaults();
if(-e"$datadir/language"){# load language file
open_file_ro(my$LANG,'language',my$ferr);send_fatal($ferr)if$fer
r;
while(<$LANG>){
next unless /^[0-9a-f]/;
my($ikey,$ival)=split('l',$_);
$ikey=pack('H*',$ikey);
$ival=pack('H*',$ival);
sub set_config{
foreach(keys %C){$C{$_}=htmlactive($Q{$_}[0]);$C{$_}=~s/^\s+|\s+$//g;}
$C{textfilters}=filters_from_queries();
$C{lastchangedby}=$U{nickname};
$C{lastchangedat}=get_timestamp();
check_config();
set_html_vars();
}
sub check_config{
# Revert to defaults if invalid or empty
foreach(qw(colbg coltxt collnk colvis colact)){$C{$_}=''if$C{$_}!~/^[a-f
0-9]{6}$/i}
$C{colbg} ||='000000';
$C{coltxt}||='FFFFFF';
$C{collnk}||='6666FF';
$C{colvis}||='FF66FF';
$C{colact}||='FF0033';
foreach(qw(sessionexpire guestsexpire messageexpire waitingexpire kickpe
nalty defaultrefresh minrefresh maxrefresh maxmessage maxname minpass floodlimit
boxwidthdef boxheightdef)){$C{$_}=~y/0-9//cd;$C{$_}=~s/^0+//}
$C{sessionexpire} ||='15';
$C{guestsexpire} ||='10';
$C{messageexpire} ||='10';
$C{waitingexpire} ||='5';
$C{kickpenalty} ||='10';
$C{defaultrefresh}||='20';
$C{minrefresh}
||='15';
$C{maxrefresh}
||='150';
$C{maxmessage}
||='1000';
$C{maxname}
||='20';
$C{minpass}
||='10';
$C{floodlimit}
||='1';
$C{boxwidthdef} ||='40';
$C{boxheightdef} ||='3';
# Use language defaults if emptied
$C{title}||='LE CHAT';
foreach(qw(header footer noguests loginbutton rulestxt entrymessage logo
utmessage kickederror roomentry roomexit regmessage kickedmessage roomclean nowc
hatting mesall mesmem mespm messtaff)){$C{$_}||=$I{"c$_"}}
$C{framesizes}='100,*,80'if($C{framesizes}!~/^(?:\d+\%?|\*)\,(?:\d+\%?|\
*)\,(?:\d+\%?|\*)$/);
}
sub set_config_defaults{
# define keys
%C=();foreach(qw(
lastchangedby lastchangedat
redirifsusp redirtourl
createlinks useextderef extderefurl allowmultiline allowfonts al
lowpms
title favicon
header footer noguests loginbutton rulestxt entrymessage logoutm
essage kickederror roomentry roomexit regmessage kickedmessage roomclean nowchat
ting
mesall mesmem mespm messtaff
textfilters
rndguestcol sessionexpire guestsexpire messageexpire waitingexpi
re kickpenalty defaultrefresh minrefresh maxrefresh maxmessage maxname minpass f
DeepSkyBlue=sky blue
Gold
=gold
Grey
=grey
Green
=green
HotPink
=hot pink
Indigo
=indigo
LightBlue =light blue
LightGreen =light green
LimeGreen =lime green
Magenta
=magenta
Olive
=olive
Orange
=orange
OrangeRed =orange red
Purple
=purple
Red
=red
RoyalBlue =royal blue
SeaGreen =sea green
Sienna
=sienna
Silver
=silver
Tan
=tan
Teal
=teal
Violet
=violet
White
=white
Yellow
=yellow
YellowGreen=yellow green
# link redirects
linkredirect=Redirecting to:
linknonhttp =Non-http link requested:
linktryhttp =Try link as http:
errnolinks =Link redirection is disabled.
# suspended page
suspended=Suspended
susptext =This chat is currently not available. Please try again later!
redirtext=Please try this alternate address!
# error messages
fatalerror =Fatal Error!
error
=Error:
errfile
=file error
errexpired =invalid/expired session
erraccdenied=access denied
errnoguests =no guests allowed at this time
backtologin =Back to the login page.
# messages frame
members
=Members:
guests
=Guests:
butcheckwait=Check <COUNT> Newcomer(s)
navbot
=bottom
navtop
=top
# config default text
cheader
=<h1>LE CHAT</h1>Your IP address is <IP><br><br>
cfooter
=<br><VER>
cnoguests
=Only members at this time!
cloginbutton =Enter LE CHAT
crulestxt
=Just be nice!
centrymessage =Welcome <NICK> to LE CHAT
clogoutmessage=Bye <NICK>, visit again soon!
ckickederror =<NICK>, you have been kicked out of LE CHAT!
croomentry
=<NICK> enters LE CHAT!
croomexit
=<NICK> leaves LE CHAT.
cregmessage =<NICK> is now a registered member of LE CHAT.
butraise
=raise
butlower
=lower
cfglanguage =Language Settings
editlanguage =create/edit language files
resetlanguage=reset language to the default (english)
cfgsettings =Change Configuration Settings
cfgmainadm =Log in with your main admin nick instead of the superuser to chang
e particular chat settings!
butlogout
=log out
lastchanged =Last changed:
# redirection
redirifsusp =Redirect to alternate URL if suspended
redirtourl =Redirection URL
# auto hotlinks
createlinks =Create hotlinks from URLs
useextderef =Use external link redirection script
extderefurl =External link redirection script URL
# options
allowfonts
=Allow change of font face
allowmultiline =Allow multiline messages
allowpms
=Allow private messages
rndguestcol
=Randomise default colour for guests
yes
=yes
no
=no
# text filters
filterslist
=Content filters
filtersnew
=Add new filter
factive
=Filter is active.
fdisabled
=Filter is disabled.
fdelete
=Delete this filter!
fregexerror
=Regular expression contains errors!
fchoosetype
=(choose filter type)
ftypetext
=Find exact words (separated by "|"):
ftyperegex
=Match regular expression:
fchooseaction =(choose filter action)
factionreplace =Replace matched text with:
factionkick
=Kick chatter, replace text with:
factionpurge =Kick, purge and send message:
fseparator
=-----------------------------------------------------------# values
sessionexpire =Minutes of silence until member session expires
guestsexpire =Minutes of silence until guest session expires
messageexpire =Minutes until messages get removed
kickpenalty
=Minutes nickname is blocked after beeing kicked
waitingexpire =Minutes until guest entry requests expire
defaultrefresh =Default refresh time (seconds)
minrefresh
=Minimum refresh time (seconds)
maxrefresh
=Maximum refresh time (seconds)
floodlimit
=Minimum time between posts from same nick (seconds)
boxwidthdef
=Default post box width
boxheightdef =Default post box height
maxmessage
=Maximum message length
maxname
=Maximum characters for nickname
minpass
=Minimum characters for password
# text
title
=Browser title / name of the chat
favicon
=Browser icon URL (favicon)
noguests
=Text if no guests allowed
loginbutton
=Login button text
header
=Login page header (<IP>=users IP-address, <VER>=
version)
footer
=Login page footer (<IP>=users IP-address, <VER>=
version)
rulestxt
=Rules (<IP> shows users IP-address)
nowchatting
=Now chatting (<NAMES>=list, <COUNT>=number)
entrymessage =Entry message (use <NICK> for name)
logoutmessage =Logout message (use <NICK> for name)
kickederror
=Kicked error message (use <NICK> for name)
roomentry
=Entry notification (use <NICK> for name)
roomexit
=Exit notification (use <NICK> for name)
regmessage
=Register notification (use <NICK> for name)
kickedmessage =Kick notification (use <NICK> for name)
roomclean
=Cleaning message
# message enclosures
mesall
=Message to all (use <NICK> for name)
mesmem
=Message to members (use <NICK> for name)
mespm
=Private Messages (<NICK>=poster, <RECP>=recipien
t)
messtaff
=Staff Messages (use <NICK> for name)
# default colors for body and non-CSS browsers
colbg
=Background colour
coltxt
=Text colour
collnk
=Link colour
colvis
=Visited link colour
colact
=Active link colour
# styles
cssglobal
=CSS for all pages
styleback
=back button style
csslogin
=CSS login page
stylelogintext =textfield style
stylecolselect =selection style
styleenter
=login button style
csspost
=CSS post frame
styleposttext =post text style
stylepostsend =talk to button style
stylesendlist =send list style
styledellast =delete last button style
styledelall
=delete all button style
styleswitch
=multiline button style
cssview
=CSS messages frame
styledelsome =delete selected button style
stylecheckwait =check newcomers button style
csswait
=CSS waiting room
stylewaitrel =reload button style
csscontrols
=CSS controls frame
stylerelpost =reload post box button style
stylerelmes
=reload messages button style
styleprofile =profile button style
styleadmin
=admin button style
stylerules
=rules button style
styleexit
=exit button style
cssprofile
=CSS profile page
cssrules
=CSS rules page
cssadmin
=CSS admin pages
csserror
=CSS error pages
# layout
tableattributes=table attributes login page
frameattributes=frame attributes
framesizes
=frame sizes
# initialisation stuff