📄 ch19.htm
字号:
to the user. The user would select one, and then that selection
would become part of that users set of hidden input tags. The
room hidden tag would then be recognized by the chat room CGI
program, and <TT><FONT FACE="Courier">if</FONT></TT> statements
could be used to set the variable that decided the name of the
directory that held the .post files, for instance.
<H2><A NAME="ExtensiontotheBasicChatRoom"><FONT SIZE=5 COLOR=#FF0000>Extension
to the Basic Chat Room</FONT></A></H2>
<P>
As with any program, there is good, and then there is better.
A chat room is better than no chat room at all. A chat room that
allows you to carry on private conversations with your best friend
or block out any posts from that annoying 13-year-old weenie who
wants to call himself !!!!!!THE MAGNIFICENT DEATH glaDIATOR!!!!!!
would be better yet. I'll devote most of the rest of this chapter
to describing situations that an advanced chat room could address.
<H2><A NAME="IntelligentUserIdentification"><FONT SIZE=5 COLOR=#FF0000>Intelligent
User Identification</FONT></A></H2>
<P>
So far, I've introduced a cryptic datum called the <TT><FONT FACE="Courier">ID#</FONT></TT>.
What good is it? Well, so far it's not being used for much. The
next few pages will change that.
<P>
Why is it a good idea to keep track of users within a chat room?
There are two broad categories of reasons: to allow beneficial
things for the good users of the chat room and to show the door
to troublemakers. Unfortunately, they exist and are legion. I
don't think the problem is nearly so great in a members-only chat
site; people only feel safe being obnoxious in the extreme when
they're anonymous. But if you're planning on building a publicly
available, anonymous chat site, you'd better plan some defenses.
<P>
The <TT><FONT FACE="Courier">ID#</FONT></TT> is a hash based on
a few individual items:
<UL>
<LI><FONT COLOR=#000000>The numeric IP address of the user.</FONT>
<LI><FONT COLOR=#000000>The name of the user's browser, as passed
by the </FONT><TT><FONT FACE="Courier">HTTP_USER_AGENT</FONT></TT>
environment variable.
<LI><FONT COLOR=#000000>The Process Identification Number (PID)
of the chat.cgi program the first time the user activates it.</FONT>
</UL>
<P>
The most important factors are the first two because these are
dependent on information about the person using the chat room.
The PID is thrown in just as a way to build up the number a bit
more. I take the last two digits of PID into <TT><FONT FACE="Courier">$id_snippet</FONT></TT>,
which is displayed to the chat room. The idea behind this is to
give two people the opportunity to use the same handle ("Molly
Millions," for instance), but they won't look absolutely
identical to other users.
<P>
As defined in chat.cgi, the <TT><FONT FACE="Courier">ID#</FONT></TT>
can't be "reverse-engineered" to provide the numeric
IP or browser names, even given the algorithm that created it.
I have intentionally done this to preserve privacy on-line. It's
good enough for that job I'll be using it for; it doesn't have
to go any further.
<P>
In addition to the preceding, the section of the <TT><FONT FACE="Courier">ID#</FONT></TT>
that relates to numeric IP address and browser name isn't even
unique. If someone else enters the chat room with the same browser
and from the same IP, chat.cgi will determine that part of the
<TT><FONT FACE="Courier">ID#</FONT></TT> for someone else matches
your information. My intention is to then forbid that second user
to enter the chat room. This seems reasonable to me as a way of
preventing one user from having multiple chat sessions going at
the same time. The drawback of potentially having other legitimate
users denied access exists, but I don't think it's worth dwelling
on. With the predominance of Internet dial-up giving people their
own IP addresses and the slim odds that two people from the same
lab will want to access one specific chat site simultaneously,
this is a restriction I'm willing to allow.
<P>
So now that we have an individual <TT><FONT FACE="Courier">ID#</FONT></TT>
for each user, what do we do with this data? I'm going to put
them all in a file. This file will have four data fields: ID#,
Handle, expiry time, and "blocked status." Here's what
I'm going to do:
<UL>
<LI><FONT COLOR=#000000>Each time chat.cgi is invoked, the datafile
is read into memory.</FONT>
<LI><FONT COLOR=#000000>The entry in the datafile that corresponds
to the user who invoked chat.cgi is updated such that his or her
expiry time is the current time plus five minutes.</FONT>
<LI><FONT COLOR=#000000>The datafile is parsed, and info regarding
each user name is added to the form as a checkbox. If that checkbox
is turned on as of the next form submission, then any posts from
that person won't be displayed in your stack.</FONT>
<LI><FONT COLOR=#000000>Also, the form will have a menu that will
allow the user to specify which person his or her current posting
will be directed towards. "</FONT><TT><FONT FACE="Courier">Everyone</FONT></TT>"
will be the default option.
</UL>
<P>
All this is accomplished with use of an <TT><FONT FACE="Courier">ID#</FONT></TT>,
an auxiliary data file, and a lot of brain-racking code. Well,
sort of.
<P>
<CENTER><TABLE BORDERCOLOR=#000000 BORDER=1 WIDTH=80%>
<TR><TD><B>Caution</B></TD></TR>
<TR><TD>
<BLOCKQUOTE>
Since two (or more) people could make their form submission very near in time to each other, this auxiliary data file stands the chance of being overwritten. To avoid this, a file- locking mechanism must be devised. You'll see how I do this in my code
example.</BLOCKQUOTE>
<BLOCKQUOTE>
File locking is one of the fundamental concepts in multi-user systems programming. Be on the lookout for circumstances in your code, chat room or otherwise, where it should be used.</BLOCKQUOTE>
</TD></TR>
</TABLE></CENTER>
<P>
<H2><A NAME="ChatMasterTheChatRoomAdministrator"><FONT SIZE=5 COLOR=#FF0000>ChatMaster-The
Chat Room Administrator</FONT></A></H2>
<P>
In the chat rooms I have administered, I've found that users get
a sense of security knowing that they're talking to the person
"in charge of it all." To that end, I've created a feature
in my chat rooms that I call "ChatMaster Mode."
<P>
There are three special privileges possessed by the ChatMaster:
<UL>
<LI><FONT COLOR=#000000>The </FONT><TT><FONT FACE="Courier">ID#</FONT></TT>
is customized.
<LI><FONT COLOR=#000000>The ChatMaster can use HTML.</FONT>
<LI><FONT COLOR=#000000>If the ChatMaster blocks a user's posts,
then those posts are blocked for </FONT><I>everyone</I>. This
is accomplished by setting the "blocked status" field
to <TT><FONT FACE="Courier">BLOCKED</FONT></TT> in the auxiliary
data file mentioned before.
</UL>
<P>
ChatMaster mode is activated when a handle is entered into the
form that has the value of a special ChatMaster password. There
is also error trapping for direct entry of the handle "ChatMaster."
<P>
The first two aspects of ChatMaster mode are easily implemented
by <TT><FONT FACE="Courier">if</FONT></TT> statements within the
code. Universal blocking is accomplished by storing the term "<TT><FONT FACE="Courier">BLOCKED</FONT></TT>"
as the fourth data field within the auxiliary data file.
<H2><A NAME="PrivateMessaging"><FONT SIZE=5 COLOR=#FF0000>Private
Messaging</FONT></A></H2>
<P>
It would be good to provide the users of this chat room with the
capability to direct messages to specific people within the room.
The form interface will now include a drop-down selection menu
that will allow the user to choose a recipient for his or her
current message. The default value is <TT><FONT FACE="Courier">everyone</FONT></TT>,
a recognized special term indicating that all members of the chat
room should receive the message. The selection menu is created
by parsing the <TT><FONT FACE="Courier">@userdata</FONT></TT>
array for a list of current users of the chat room.
<P>
From the point of view of the program, an <TT><FONT FACE="Courier">$input{'private'}</FONT></TT>
array entry signals the <TT><FONT FACE="Courier">&update_stack</FONT></TT>
subroutine to make a stack entry with a special naming convention.
Also, a special header will be given to private messages. The
<TT><FONT FACE="Courier">&print_stack</FONT></TT> subroutine
has been rewritten to be aware of private messages and to ignore
all private messages that aren't being sent to the invoking user.
<P>
Intelligent user identification, blocking, ChatMaster mode, and
private message features are included in newchat.cgi. The source
code for this is presented in Listing 19.2, and it resides on
the Web at
<BLOCKQUOTE>
<TT><FONT FACE="Courier"><A HREF="http://www.anadas.com/cgiunleashed/chatrooms/newchat.cgi">http://www.anadas.com/cgiunleashed/chatrooms/newchat.cgi</A></FONT></TT>
</BLOCKQUOTE>
<HR>
<BLOCKQUOTE>
<B>Listing 19.2. <FONT SIZE=2 FACE="MCPdigital-B">newchat.cgi</FONT>-A
more sophisticated chat room that incorporates some of the features
discussed in this chapter.<BR>
</B>
</BLOCKQUOTE>
<BLOCKQUOTE>
<TT><FONT FACE="Courier">#!/usr/bin/perl<BR>
#<BR>
# This program was written by Richard Dice of Anadas Software
Development<BR>
# as part of the Sams Net "CGI Programming Unleashed"
book. The author<BR>
# intends this code to be used for instructional purposes and
not for<BR>
# resale or commercial gain.<BR>
#<BR>
# Any questions or comments regarding this program are welcome. You
<BR>
# may contact the author by Internet email: rdice@anadas.com<BR>
#<BR>
<BR>
# ====== Configuration Variables ======<BR>
$progname = 'newchat.cgi#Comments';<BR>
$baseurl = 'http://www.anadas.com/cgiunleashed/chatrooms';<BR>
$html = 0; # set to 1 if HTML is allowed in postings<BR>
$maxlines = 100; # sets the size of the stack<BR>
$admin_name = 'Richard Dice';<BR>
$admin_email = 'rdice@anadas.com';<BR>
$cmpswd = 'Aaron Thunderfist'; # Handle entry which gives ChatMaster
access<BR>
$datafile = 'userdata.dat';<BR>
$reset_time = 600; # 600 seconds without update before session
is terminated<BR>
<BR>
#<BR>
# I put a few of the CGI environment variables in their own variables
<BR>
# for ease of understanding later on in the program. also,
I create<BR>
# a variable $time which records the current time and date<BR>
#<BR>
$refer = $ENV{HTTP_REFERER};<BR>
$ip = $ENV{REMOTE_ADDR};<BR>
$browsername = $ENV{HTTP_USER_AGENT};<BR>
chop($time = `date`);<BR>
<BR>
#<BR>
# read datafile into the @userdata array, remove terminating newlines
<BR>
# from all array elements<BR>
#<BR>
<BR>
#--<BR>
while ( -e "lock_file" ) {<BR>
sleep(1);<BR>
}<BR>
#---<BR>
open(LF,"> lock_file");<BR>
print LF "Locked!\n";<BR>
close(LF);<BR>
chmod 0660,'lock_file';<BR>
#---<BR>
open(DF,$datafile);<BR>
chop(@userdata = <DF>);<BR>
close(DF);<BR>
#---<BR>
unlink 'lock_file';<BR>
#---<BR>
<BR>
#<BR>
# remove all elements of @userdata which are past their expiry
time<BR>
#<BR>
foreach $ud ( @userdata ) {<BR>
@field = split(/\t/,$ud);<BR>
push(@udtemp,$ud) if $field[2] > time;<BR>
}<BR>
@userdata = @udtemp;<BR>
undef(@udtemp);<BR>
<BR>
# puts all POST query information the variable $input_line<BR>
read(stdin, $input_line, $ENV{CONTENT_LENGTH});<BR>
<BR>
# replace all '+' coded spaces with real spaces<BR>
$input_line =~ tr/+/ /;<BR>
<BR>
# creates array of all data files in $input_line from & separated
info<BR>
@fields = split(/\&/,$input_line);<BR>
$input_line = (); # free up memory<BR>
<BR>
#<BR>
# decodes hex info for each name/value pair and places pairs in
<BR>
# %input associative array<BR>
#<BR>
foreach $i (0 .. $#fields) {<BR>
($name,$value) = split(/=/,$fields[$i]);<BR>
$name =~ s/%(..)/pack("c",hex($1))/ge;
<BR>
$value =~ s/%(..)/pack("c",hex($1))/ge;
<BR>
if ($name ne 'block') {<BR>
$input{$name} = $value;<BR>
} else {<BR>
$input{$name} .= $value .
"$;";<BR>
}<BR>
}<BR>
chop($input{'block'}) if defined($input{'block'}); # remove trailing
$;<BR>
<BR>
print "Content-type: text/html\n\n";<BR>
<BR>
#<BR>
# if this is a first-time access and another user is from the
IP and using <BR>
# the same browser as this user, prevent this user from using
the chat room<BR>
#<BR>
if (!defined($input{'id'})) {<BR>
<BR>
#<BR>
# "postulate" an ID# for this new user<BR>
#<BR>
($temp_id = $ip) =~ s/\.//g;<BR>
substr($temp_id,$[,3) = ''; # removes first
3 digits of IP info<BR>
for $i ( 0 .. (length($browsername)-1) ) {<BR>
$temp_id += ord(substr($browsername,$[+$i,1));
<BR>
} # adds Browser info to end of the string<BR>
<BR>
#<BR>
# compares the "postulated id#" against those id#s found
in the @userdata<BR>
# array... if it finds a match, terminate this login<BR>
#<BR>
foreach (@userdata) {<BR>
@field = split(/\t/);<BR>
substr($field[0],index($field[0],'.'))
= '';<BR>
if ( $field[0] eq $temp_id
) {<BR>
&id_conflict_error;
<BR>
}<BR>
}<BR>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -