中文版 Perl CGI 程式寫作常問問題集(五)

- 中國WEB開發者網絡 (http://www.webasp.net)
-- 技術教程 (http://www.webasp.net/article/)
--- 中文版 Perl CGI 程式寫作常問問題集(五) (http://www.webasp.net/article/8/7743.htm)
-- 作者:未知
-- 發佈日期: 2003-09-13
Q4.6: 要如何用一個 Perl 的取代指令將所有 HTML
標籤從一份文件中刪除?

以下這個簡單的 regular expression 可用來去除 HTML 標籤*:

【譯者】
1. 要讓這個 regular expression 跨行執行,您必須先將您的
script 由預設的按行執行模式 (line mode) 改為按段執行模
式 (paragraph mode)。您可以在指令列以:

perl -00 -we '...'

的方式;或是在 script 中以:

#!/usr/bin/perl -00



$/ = "";

的方式來設定按段執行模式。
2. 除非您需要對欲刪除的 HTML 標籤中的內容做進一步的處理或
利用,否則本例中最外圍的一對括弧可去掉。

$line =~ s/<(([^ >]|\n)*)>//g;

詳細的相關資料,請看 Tom [Christiansen] 的 striphtml 程式

(<ftp://perl.com/perl/scripts/striphtml>,這個程式同時也收錄在他的
tour of perl5 regexps
(<http://perl.com/perl/all_about/regexps.html>
講義中。


----------------------------------------------------------------------

Q4.7: 要如何知道是誰/哪台機器/哪個瀏覽器執行了我的程式?

您可以從 HTTP_USER_AGENT 這個環境變數得知使用者所用的瀏覽器。

【摘自 WWW FAQ】

您的 CGI script
可以利用五個重要的環境變數來幫忙辨識使用者的身份。

* HTTP_FROM
這個環境變數理論上應設為使用者的email地址。但是許多瀏覽

器完全不加以設定【即不支援】,而大部份支援這個變數的瀏覽器又讓使
用者自由設定這個值。因此,建議讀者頂多拿它來做為 email form
中回
信地址的預設值。

* REMOTE_USER 這個變數唯有當 script
在安全認證的保護下執行時才會被
設定。從 AUTH_TYPE
這個變數可以知道所用的認證方法是屬於哪一個類
型。REMOTE_USER
則會含有正接受認證的使用者的名字。要注意的是,
REMOTE_USER
只有在使用安全認證的時候才會被設定,而且不是所有的
servers 都支援。在 NCSA server
底下,如果認證所使用的傳輸方式沒
有列入 access.conf 檔中(也就是說,應使用 <Limit GET
POST>,而不是
僅僅用預設的 <Limit GET>),認證可能會出人意外地失敗。

* REMOTE_IDENT 如果 server 能連接上客戶端的 IDENT
server,它會將這
個變數設成遠方使用者的身份。但由於向IDENT server
查詢的動作太花
時間,大部份的 servers
都把這項功能關掉。更何況,客戶端的機器是
否會回應查詢,又是否會誠實以對,都是無法確定的。

* REMOTE_HOST
*
這個變數的設定值並不包括遠端使用者的真實身份,但是會提供使用者正
用來連線的機器名稱,只要 server
能找得出來。由於我們無法確切得知

使用者的真實身份【請看前一個環境變數的說明】,有的時候使用可確認
的位址來替代,不失為一個可行的變通方法。在 server
查不到遠端的機
器名稱,或者是為增加 server
的處理速度而將這個查詢功能關掉的情況
下,這個變數是空的;請看底下 REMOTE_ADDRESS
一項的說明。還有,別
忘了您可能會發現所有使用同一個 proxy (代理人) server
的使用者的
機器名都變成了那台 proxy server 的名字。

* REMOTE_ADDR
這個變數的設定值並不包括遠端使用者的真實身份,但是會
提供使用者正用來連線的機器的資料。REMOTE_ADDR 會包含客戶端的
IP

位址,以用點隔開的十進位數字的形式來表示。由於我們無法確切得知使
用者的真實身份
[請看前一個環境變數的說明],有的時候使用可確認的
位址來替代,不失為一個可行的變通方法。和前一項 REMOTE_HOST
不同

的是,這個變數一定會被設定。還有,別忘了您可能會發現所有使用同一
個 proxy (代理人) server 的使用者的機器位址都變成了那台
proxy
server 的位址。

【摘錄自 WWW FAQ 部份完】


----------------------------------------------------------------------

Q4.8: 人家看得到我的 Perl CGI
程式嗎?如果是這樣的話,那不就讓他們知道
我的程式是怎麼運作的了。這是個安全漏洞嗎?我要怎麼把它隱藏起來?

如果您將您的 server 設成對所有在一個特定目錄(如
cgi-bin)下的檔案,或
者是具有某些副檔名(如 ``.pl''、``.tcl''、``.sh'')的檔案一律都以
CGI
程式看待,那麼 server 只會執行這些程式。至於使用者是無法看到
script
本身的內容的。

但是如果您允許人們看您的 script (譬如把它放到 HTML
文件的根目錄下),

那麼只要是這個程式沒有安全上的漏洞,這並不能算是安全問題。如果這個程

式真的有安全上的破綻而您又允許使用者看這個程式,那麼他們便有機可乘,
進而利用這個弱點。

【譯者】上面這段原文作者是就遠方的客戶端的使用者而言。和這個
主題相關的一 個常問問題是:

Q: 我的 Perl CGI scripts
必須將權限設為全世界可讀。可是這樣一
來,和我同
機器有帳戶的人,只要知道我的程式名稱,就可以瀏覽我
的 Perl 程式的內容;尤 其當其中牽涉到密碼的問題時。

A: 至少有兩個解決方法,一個簡單,一個複雜:

簡單的方法是,請您的系統管理者(如果不是您自己的話),將您的
CGI scripts 及密碼檔(如果您選擇將密碼存放在另一個檔案中的
話)的所有者設成 Web server 跑的使用者(最常見的是使用者
nobody ;使用群 nogroup 或 nobody), 然後將 CGI scripts
的使
用權限設定成 550 (-r-xr-x---),密碼檔的權限設成 440
(-r--r-----)。如此一來,一方面您的程式得以執行,而且其他同機
器上的 使用者也沒有辦法偷看到您的程式和密碼。

比較複雜的解決方法是先挑個難破的密碼將整個程式加密起來,然後
再使用 Filter::decrypt 這個模組在臨執行前將其解開,在此不多
說。有興趣的讀者請看 Filter::decrypt 的使用說明;此外新的
perl FAQ 第三部分中這一段:``How can I hide the source for
my
Perl program?'' ,大家也可參考。


----------------------------------------------------------------------

Q4.9: 我需要將整個 Perl library 都複製到我的 htdocs 目錄底下嗎?

不需要。您的 CGI scripts 可以使用 server
和文件根目錄之外的任何檔案,
除非 server 是在一個 chroot 的環境下執行。


----------------------------------------------------------------------

Q4.10:
我為什麼不該叫使用者輸入他們的密碼或身份證字號或信用卡號碼?有
一個 TYPE="password" 不是就是拿來做這個的嗎?

No! form 的介面中有一個 ``password''
的欄位,但是您不應該拿它來處理任
何機密性的資料。不該這麼做的原因是因為所有的 form 資料(包括
``password'' 欄) 都是以純文字形式,而非以加密形式由瀏覽器送至
server。

如果您想要安全地傳送資料,那麼您需要使用具有安全功能的
server,例如
Netscape 的 Commerce Server

(<http://home.netscape.com/comprod/products/iapps/platform.html>*。

【譯者】Apache SSL ,例如 Stronghold 版
(<http://stronghold.c2.net>,同樣具有這個功能。


----------------------------------------------------------------------

Q4.11: 我要如何產生專門替 Netscape
設計的網頁,以別於世上其他的瀏覽
器?

您可以透過 HTTP_USER_AGENT 這個環境變數在您的 CGI script
中得知是否
Netscape 正在執行您的 script。以下為一例:

$browser = $ENV{'HTTP_USER_AGENT'};
if ($browser =~ /Mozilla/) {
#
# Netscape
#
} else {
#
# Non Netscape
#
}


----------------------------------------------------------------------

Q4.12: 為什麼我的 system() 所產生的資料輸出順序不對?


這是由於標準輸出的產生方式通常是先累積相當的資料再輸出(buffered)。要
讓輸出的資料以正確的順序顯示,您必須藉由 $| 這個變數的設定將
buffering
的特性關掉。


----------------------------------------------------------------------

Q4.13: 我聽說 Netscape 會支援 Java*。這是不是說我現在得棄
Perl,改
Java 了?是不是該這麼做?

【譯者】原 FAQ 已有相當一段時間未更新。這句話現在應該改作
「Netscape 和 IE 兩大瀏覽器都已支援 Java」。

不、不、不。Java 和 CGI 的概念完全不同。CGI 是在 server
端執行,而
Java則是在 client 端執行。有些東西(如動畫)可藉由使用 Java
而得到較好
的效果。但您可繼續使用 perl 來發展 server 端的應用程式。

如果您需要有關 Java 進一步的資料,底下列了幾個文件您可以去看看*:

* 升陽公司的 Java 文件 (<http://java.sun.com>
* Tom C.所寫的 Java uber Alles(Java 的種種)
* Java, the Illusion(Java 幻像)

【譯者】後面這兩篇文章對 Java 及這個熱潮作了很嚴厲的批判。本
FAQ 作者 Tom C. 的 Java uber Alles 中的論點主要著重於技術層
面。Tom 對 Java 的態度或許代表了不少 Perl 陣營人仕的心聲。


----------------------------------------------------------------------

Q4.14: 我要如何讀取環境變數?為什麼它們有時候會不一樣?

您可以透過 %ENV 這個關連陣列來讀取環境變數。以下這個簡單的 script
會把
所有的環境變數印出來(排好順序):

#!/usr/local/bin/perl -w
print "Content-type: text/plain", "\n\n";

foreach $key (sort keys %ENV) {
print $key, " = ", $ENV{$key}, "\n";
}
exit 0;


很不幸的,有些環境變數會被某些瀏覽器忽略掉。譬如,有些瀏覽器就不設定
HTTP_REFERER。

----------------------------------------------------------------------

Q4.15: 為什麼我輸出的資料被攪亂了(如 ``b < a'' 會被破壞掉)?

如果您送的 MIME 類型是 HTML 的話,您必須「跳脫」 (escape)
某些符號,
如 ``<''、``&anp;'',及 ``>'',否則瀏覽器會以為它是 HTML
【標籤】。

您必須使用以下格式來跳脫特殊字元:

&#ASCII 代碼;

您可以在指令列執行這個簡單的 script,便可得到非字母數字性字元
(non
alpha-numeric characters) 所對應的 ASCII 碼:

#!/usr/local/bin/perl -w
print '請輸入字串: ';
chop($string = <STDIN>);
$string =~ s/([^\w\s])/sprintf("&#%d;", ord($1))/ge;
print '跳脫過的字串是: ', "$string\n";
exit 0;


----------------------------------------------------------------------

Q4.16: 為什麼我的Perl CGI
程式可以由指令列,卻無法從瀏覽器去執行?

最可能的原因是權限的問題。別忘了,您的 server 可能是以
``nobody''、

``www'',或其他權限很低的帳戶身份來執行的。因此,除非它有足夠的權限,
否則是無法執行您的 script 的。

----------------------------------------------------------------------

Q4.17: 為什麼我的 Perl CGI 程式能跑,但是不會把資料寫到檔案中?

這又是權限在作怪!server
除非有足夠的權限否則是無法將資料寫進某目錄下
的某檔案裡去的。

您應該養成習慣檢查 open 指令遞回的錯誤狀態 (error status):

print "Content-type: text/plain\n\n";
.
.
.
open(FILE, ">/some/dir/some.file") ||
print '無法寫進檔案: ', "$!\n";
.
.
.


----------------------------------------------------------------------

Q4.18: 要如何做一個會維繫狀態,或允許【同一使用者】多次連線的
form?

您可以用 CGI::MiniSvr 這個 module
來維持【記住】幾次不同的連線之間的狀
態資料。

或者,您可以制做一系列的動態文件,在彼此之間相互傳遞一個期間代碼
(session ID),此代碼可以以詢問
(query)、額外路徑,或隱藏式欄位等形式存
在*。

【譯者】CGI.pm 會替您把這部份(維持狀態)做好 (用上述的原理
),故使用 CGI.pm 可自動享受這項功能,不需要自己去做。這又多
了一個該使用 CGI.pm 的理由。


----------------------------------------------------------------------

Q4.19: 如果不從瀏覽器去執行我的 CGI 程式,要如何替它除錯?

CGI 程式不容易除錯。您可以藉著手動設定環境變數來模擬 server:

setenv HTTP_USER_AGENT "Mozilla/2.0b6" (csh)



export HTTP_USER_AGENT = "Mozilla/2.0b6" (ksh, bash)

要模擬 POST 請求,您可以把資料先放進一個檔案裡,然後把它 pipe
到您的程
式去:

cat data.file | some_program.pl

或者您可以用 CGI.pm 來幫您除錯。假設您有一個像下面這樣的
script,它會
把所有您傳給它的索引/設定值對應資料 (key/value pairs)
都列印出來。

#!/usr/local/bin/perl -w
use CGI;
$cgi = new CGI;
print $cgi->header;
print $cgi->start_html("Simple CGI.pm Program");
print "<H1>Simple CGI.pm Program</H1>\n";
print "<HR >";
print '以下所列的是您傳送的設定值:';
print $cgi->dump;
exit 0;

這個 script 不會在乎您是透過 GET、POST,或 ISINDEX
請求,或者是由指令

列、標準輸入,或文字檔將資料傳送給它。為了方便除錯,我們就直接從指令
列傳一些資料給它吧:

% simple.cgi first=shishir last=gundavaram document='CGI\
FAQ'



% simple.cgi "first=shishir&last=gundavaram&document='CGI\
FAQ'"

在第二個例子中,整個字串周圍必須加引號("),否則 shell 看到 ``&''
這個
符號會誤解。好,接下來是從標準輸入來除錯的方法:

% simple.cgi
(waiting for standard input)
first=shishir
last=gundavaram
document=CGI\ FAQ
^D

當然,您也可以先用一個檔案來儲存資料,然後再做輸入轉向,像這樣:

% simple.cgi < form.data

您也可以用 CGI Lint
(即將出版)。它能達到相同的功效。另外,它也能幫忙
檢查有無安全問題,不當使用 open(),以及不正確的 HTTP 標頭等。


----------------------------------------------------------------------

Q4.20: 如果不靠<FORM>標籤,要如何叫出 Perl CGI 程式?

您可以直接去打開該 CGI 程式的 URL:

http://some.machine/cgi-bin/your_program.pl

您也可以在文件中使用連結的方式,例如:

<A HREF="http://some.machine/cgi-bin/your_program.pl>
要試試我的CGI程式請在這裡點一下</A>


----------------------------------------------------------------------

Q4.21: 要如何避免旁人不先填欄位就執行我的
form?他們為什麼一直不斷這麼
做?

這些人欄位完全空白就去執行 form 是因為他們把這個 form 的 URL
儲存起來
【儲存到書籤裡面】的關係。當他們下次叫出這個 form
的時候,這個請求就會
變成是一個空的 GET (而非 POST 或填有資料的 GET)。


您可以先檢查所有欄位中的資料,如果其中有欄位留白的話,您可以送回一個
``No Content'' 的狀態屬性*。以下是一例(假設關連陣列 %form
中含有您
form 的資料):

【譯者】狀態碼 204 的屬性已由 HTTP 0.9
(<http://www.w3.org/pub/WWW/Protocols/HTTP/HTTP2.html> 的
``No Response'' 變為 HTTP 1.0
(<ftp://ds.internic.net/rfc/rfc1945.txt> 和 HTTP 1.1
(<ftp://ds.internic.net/rfc/rfc2068.txt> 中的 ``No
Content'' 了。

$error = 0;
foreach $value (values %form) {
$value =~ s/\s//g;
$error = 1 unless $value;
}
if ($error) {
print "Content-type: text/plain\n";
print "Status: 204 No Content\n\n";
print '除非您的瀏覽器不支援狀態碼 204
,否則您不該看到這部份',
"\n";
} else {
#
# Process Data Here
#
}


----------------------------------------------------------------------

Q4.22: 那些server 回應碼 (server response codes)

(<http://www.w3.org/hypertext/WWW/Protocols/HTTP/HTRESP.html>是幹什麼
用的?有什麼意義?

CGI 程式可以傳送 server 然後 server
會把它轉送給瀏覽器。例如:假設您
想送``No Content''
(意思是告訴瀏覽器不要再重新下載該網頁),那麼您得
送一個 204 的回應碼(見上例)。


----------------------------------------------------------------------

Q4.23: 為什麼 print "Location: http://host/page.html\n 不
work?又為
什麼它只 work 一次,但隨後的轉向就都弄錯了呢?

CGI 程式只能送一個 Location 標頭。還有,如果您要 server
做轉向的動作
您就不該送 MIME 類別。譬如,以下的例子是錯誤的示範,儘管在有些
servers
上行的通:

#!/usr/local/bin/perl -w
.
.
.
print "Content-type: text/plain\n"
print "Location: http://some.machine/some.doc\n\n";


----------------------------------------------------------------------

Q4.24: 要如何讓 server 在每個 HTML
網頁的底部都自動加上一個:「最近更
新日期: ...」的告示?或者,是不是只有 SSI 的網頁才能這麼做?CGI
程式
的日期要如何取得?

如果您是透過 CGI
以動態方式來產生您的文件,那麼要插入一個時間標記非常
簡單。以下是一例(僅適用於 Perl 5):

$last_updated = localtime;
print '最近更新日期: ',"$last_updated\n";

或者是:

require "ctime.pl";
$last_updated = &ctime(time);
print '最近更新日期: ', "$last_updated\n";

甚至像這樣:

chop($date = `/usr/local/bin/date`);
print '最近更新日期: ', "$last_updated\n";

您可以用 SSI 來達到這個效果,像這樣:

<--#echo var="LAST_MODIFIED"-->


----------------------------------------------------------------------

Q4.25: 什麼樣的場合下以 Perl 寫 CGI 程式會顯得太小題大作,因為用
shell
就可以做到?而什麼樣的場合對 Perl 來說又過於困難?用 C++
做這類的事不
是好得多嗎?那用 C 呢?


每一個語言都有其長處和短處。相信這句話您聽過很多次了。所以一切全看您要
做的是什麼而定。如果您預期正準備寫的 CGI
程式每個鐘頭會有幾千幾萬人次
連去使用,那麼您應該選用 C 或
C++來寫。如果您求快的話(指發展所花費的
時間而言),那麼 Perl 是正確的選擇!

一般說來,您應避免用 shell 來做任何形式的 CGI 程式設計,因為
shell 在
先天上容易產生安全問題。 

webasp.net