📄 dbmedit.cgi
字号:
#!/usr/local/bin/perl## DBMEdit 1.0## Add, edit, or delete records from a DBM file, via a Web interface.# The DBM records are assumed to be several fields concatenated into # one long string with some delimiting string (default is "\0").## This program displays a DBM database as a table, and provides# auto-sized forms to add or edit records. It protects data reasonably# well in multi-user situations.### TO INSTALL/CONFIGURE:## 1) Put this script where you want it.## 2) Create a directory below that called "data/" (the name is# user-configurable, below).## 3) Put your existing DBM files in the data/ directory, or make # symbolic links in there that point to the real DBM files.## 4) Figure out how you want to handle permissions. The Web server# must be able to write the files in data/, and the data/ directory# itself must be writable if you want to create new files in it# via this script.## Note that if data/ is writable by the Web server's user, then any # local hacker with CGI can overwrite your data. If you work around # this with setgid or setuid, see the security note in the # USER CONFIGURATION section below.## !! 5) PASSWORD-PROTECT THE URL OF THIS SCRIPT! Otherwise, anyone can # edit your DBM files-- probably not what you want. Also, set# @ALLOWED_USERS in the USER CONFIGURATION section below.## Security within the script is limited at best; it relies on the # authentication of whoever's running the script.### TO USE:## You can create new DBM databases with this program, or edit existing# ones that follow the same field-delimiting scheme.## Define each database by the DBM filename, the list of column names,# and the delimiter between fields within each record. This database # definition is saved in the URL, so you can bookmark it directly or # put it in an HTML link.## FILENAME: # Leave out the extension. Don't point into another directory.## COLUMNS:# Comma-separated list of text strings, for display only. Each # column name may be followed by a ':' and optional one-letter # flags. Currently supported flags are:# r read-only (for convenience only, NOT security)# t textarea (multi-line) input instead of one-line input## Example column list:## Name, Birthdate:r, Favorite Quote:t## DELIMITER:# Any string of characters can be used. Express it as a list of# ASCII codes, as decimal numbers. For example, CRLF is "13 10".# The default is one null character, which is "0".## Note that the data in the database fields can't contain the# delimiter string, or the database will get messed up. If you need# to put arbitrary binary data in a field, use a long sequence of# random bytes here, like "188 45 217 206 51". Five bytes means# you'll mess it up about once for every terabyte (1000 GB) you # store.## Once you've defined and loaded your database, be sure to bookmark the# full URL or copy it to an HTML link.### Further comments are at the end of this file.## written by James Marshall, james@jmarshall.com# see http://www.jmarshall.com/tools/dbmedit/ for more info##----- USER CONFIGURATION (NORMALLY UNNEEDED) ------------------------# For security, only let this script modify DBMs in a certain directory.# If you have DBMs all over the place, put symbolic links in this# directory to point to the actual locations.# This directory must be accessible by the uid that the Web server runs as.$DATA_DIR= 'data' ;# Set this to a list of allowed usernames, to restrict who REMOTE_USER can # be, or leave empty for no restrictions. This guards against a few# potential security holes. For example, someone could make a symbolic # link to your copy of this script, bypassing any password-protection.@ALLOWED_USERS= qw( ) ;# If you run this setuid or setgid, there is a slight security risk# of someone running this from the command line in another directory# with certain symbolic links, and potentially modifying DBM files# in other directories of yours. If you care about this, then set# one or both of the following two variables.# Username or UID the Web server runs under (either will work).# If you set this, the program will verify this is the real user running it.$WEB_SERVER_USER= '' ;# Directory where this program should be run, i.e. where it lives.# If you set this, the program will chdir to the directory before running.$HOME_DIRECTORY= '' ;# The delimiter between fields in the DBM file, if none is specified.$DEFAULT_DELIM= "\0" ;#----- END OF (USEFUL) USER CONFIGURATION ----------------------------use Fcntl qw(:DEFAULT :flock) ;use NDBM_File ;# Where all the lock files go. This will be created if it doesn't exist.$lockdir= "$DATA_DIR/locks" ;# The default title for simple error responses$errtitle= "$0 error" ;# Guard against unauthorized access, if neededif (@ALLOWED_USERS) { &HTMLdie("Sorry, you're not authorized to run this script.") unless grep( ($_ eq $ENV{'REMOTE_USER'} ), @ALLOWED_USERS ) ;}# Guard against a slim security holechdir($HOME_DIRECTORY) || &HTMLdie("Couldn't chdir: $!") if $HOME_DIRECTORY ne '' ;# Guard against a slim security hole, take 2if ($WEB_SERVER_USER ne '') { # First, convert to numeric UID if needed $WEB_SERVER_USER= getpwnam($WEB_SERVER_USER) if $WEB_SERVER_USER=~ /\D/ ; &HTMLdie("Access forbidden.") unless ( $WEB_SERVER_USER == $< ) ;}%in= &getcgivars ;$in{'file'}=~ s/(^\s*|\s*$)//g ; # standardize on no leading/trailing blanks$in{'referer'}||= $ENV{'HTTP_REFERER'} ;&displaystartform unless $in{'file'} ;# Only allow files with no paths.# Heck, only allow word chars for now.&HTMLdie("The filename '$in{'file'}' is not allowed.") if ($in{'file'}=~ m#/|\.\.#) || ($in{'file'}=~ /[^\w.-]/) ;# Homespun lock mechanism-- can't figure out how to use flock() on DBM file :(# Make a lock file to get a lock on-- safer for interruptable processes.mkdir($lockdir, 0777) || &HTMLdie("Couldn't create lock directory: $!") unless -e $lockdir ;chmod(0777, $lockdir) ; # otherwise, it's tough to get rid of$lockfile= "$lockdir/$in{'file'}.lock"; # safe because $in{'file'} is safesystem('touch', $lockfile) unless -e $lockfile ;open(DB_LOCK, ">$lockfile") || &HTMLdie("Couldn't open lockfile: $!") ;# For some reason, LOCK_SH doesn't always work-- gets "Bad file number". :P# So, we'll just do an exclusive lock for everything. Best we can do. :(flock(DB_LOCK, LOCK_EX) || &HTMLdie("Couldn't get lock: $!") ;# $now is saved in the form, and is used for safe updates.# Note that file will not be modified until at least the end of this script,# so $now is "equivalent" to the time the form will be generated.$now= time ; # for (@goodmen)tie %dbdata, 'NDBM_File', "$DATA_DIR/$in{'file'}", O_RDWR|O_CREAT, 0664 ;# Used to test modification time for safe updates.# DBM filenames vary, so see which files exist. Try .pag, else take .db.# What other extensions are created with DBMs?$dbfilename= -e "$DATA_DIR/$in{'file'}.pag" ? "$DATA_DIR/$in{'file'}.pag" : "$DATA_DIR/$in{'file'}.db" ;# Perhaps we should allow the user to read the file even if it's not# writable? To do so, set $topmsg, and alter the flags on "tie", above.&HTMLdie("Web server couldn't create DBM file.") unless -e $dbfilename ;&HTMLdie("DBM file isn't readable by Web server.") unless -r $dbfilename ;&HTMLdie("DBM file isn't writable by Web server.") unless -w $dbfilename ;&calcglobals ;#----- end of initialization, main block below -----------------------# a catch-all way to cancel actions: show message and do default commandif ($in{'noconfirm'}) { $topmsg= "<h2><font color=red>\u$safein{'cmd'} cancelled.</font></h2>" ; $in{'cmd'}= $safein{'cmd'}= '' ;}# Main switch statementif (($in{'cmd'} eq 'show') || ($in{'cmd'} eq '')) { &displaymaintable ;} elsif ($in{'cmd'} eq 'edit') { &displayeditform ;} elsif ($in{'cmd'} eq 'add') { &addrecord ; $topmsg= "<h2><font color=green>Record added.</font></h2>" ; &displaymaintable ;} elsif ($in{'cmd'} eq 'update') { &updaterecord ; $topmsg= "<h2><font color=green>Record updated.</font></h2>" ; &displaymaintable ;} elsif ($in{'cmd'} eq 'delete') { &deleterecord ; $topmsg= "<h2><font color=green>Record deleted.</font></h2>" ; &displaymaintable ;} else { &HTMLdie("The command <b>$safein{'cmd'}</b> is not supported.", "Command not supported") ;}untie %dbdata;close(DB_LOCK) ;# unlink $lockfile ; # not needed, but optionalexit ;#----- blocks to perform the various commands ------------------------# Add a new record to the DBM filesub addrecord { unless ($in{'confirm'}) { if (defined($dbdata{$in{'key'}})) { &verifycmd("A record with that key already exists. You should " . "normally use the Update function to change an existing " . "record. Would you like to overwrite the existing record " . "with the values you just entered?") ; } } # Generate sequential key if key was not entered if (!length($in{'key'})) { $in{'key'}= sprintf("%05d", int((sort { $b<=>$a } keys %dbdata)[0]) + 1) ; } &putfieldstodb ;}# Update a record in the DBM filesub updaterecord { unless ($in{'confirm'}) { unless (defined($dbdata{$in{'key'}})) { &verifycmd("That record has apparently been deleted recently. " . "Would you like to add it back with the values you just " . "entered?") ; } if ($in{'time'} && $in{'time'}<(stat($dbfilename))[9]) { &verifycmd("The database has changed since this record was " . "presented to you for editing. The record itself may or " . "may not have changed. Do you still want to update " . "this record?") ; } } &putfieldstodb ;}# Delete a record from the DBM filesub deleterecord { unless ($in{'confirm'}) { verifycmd("Are you sure you want to delete this record?") ; } delete($dbdata{$in{'key'}}) ;}# Require the user to verify a commandsub verifycmd { my($msg)= @_ ; my($userdata)= &hiddenvars( &subhash(*in, 'key', grep(/^in_\d\d\d$/, keys %in)) ) ; &printheader ; print <<EOF ;<h3><font color=red>Warning:</font> $msg</h3><form action="$ENV{'SCRIPT_NAME'}" method=post><input type=hidden name="cmd" value="$safein{'cmd'}"><input type=hidden name="time" value="$safein{'time'}">$dbdefnpost$userdata<input type=submit name="confirm" value=" Yes, continue "><input type=submit name="noconfirm" value="No, cancel this request"></form><p><i>Tip: Creative use of the forward and back browser buttons can be veryhelpful here, to view current data or recover lost data.</i>EOF &printfooter ; exit ; # hmm, not the cleanest}# Copy "in_nnn" fields into $dbdata{$in{'key'}} (used by add and update)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -