当然,但您恐怕付不起雇我们的签约金 :-)
说真的,如果您能够先证明您已读过下列这几个 faqs ,但遇到的问题并不单纯、非叁言两语即可回答的话,那麽您 post到 comp.infosystems.www.authoring.cgi上(如果是有关 http 、 html ,或 cgi通信协定)的问题可能也会得到口气和缓而有用的答覆。表面上看似 perl,但骨子里是 cgi之类的问题,如果 post到 comp.lang.perl.misc人家可能就不会这麽乐意地接受了。
几个实用的 faqs 分别是:
http://www.perl.com/perl/faq/idiots-guide.html http://www3.pair.com/webthing/docs/cgi/faqs/cgifaq.shtml http://www.perl.com/perl/faq/perl-cgi-faq.html http://www-genome.wi.mit.edu/www/faqs/www-security-faq.html http://www.boutell.com/faq/
【译者】上面第叁份文件,perl-cgi-faq的中译版可在 http://2ti.com/cgi-bin/2t/perl/perl-cgi-faq-chi/ 处取得。最後一份(www faq)的中译版可自 http://www.acer.net/document/cwwwfaq/ 取得。
最正确(尽管不是最快)的方法是使用 html::parse模组(可由 cpan取得,是所有写 web程式者必备的 libwww-perl 套件的一部分)。
许多人尝试用简陋的正规表示式来解决这个问题,譬如说像 s/<.*?>//g,但这个式子在很多情况下会失败,因为要处理的字串可能会跨越断行字元,也可能含有被 quote【跳脱】的箭头号,或有 html comment出现;再加上一些疏忽,譬如,人们常忘了转换如 <的 entities(跳脱字 元)。
以下这个「简陋」的方法对大多数的档案都有效:
#!/usr/bin/perl -p0777 s/<(?:[^>'"]*(['"]).*?\1)*>//gs
如果您想要更完整的解法,请看叁部曲的 striphtml 程式, http://www.perl.com/cpan/authors/tom_christiansen/scripts/striphtml.gz 。
一个快速但不完美的做法是
#!/usr/bin/perl -n00 # qxurl - tchrist@perl.com print "$2\n" while m{ < \s* a \s+ href \s* = \s* (["']) (.*?) \1 \s* > }gsix;
这个版本并不替相对式写法的 urls 作调整,也不懂代换 bases【< link base=``...''>】,或如何处理 html comments、同时处理同一个标签里的 href和 name 属性,或接受 url形式的参数。同时,它要比一个较「完整」、利用 lwp [libwww-perl]模组套件的解法,例如 http://www.perl.com/cpan/authors/tom_christiansen/scripts/xurl.gz这个程 式,快上一百倍。
如果是 html表格的话,您可以使用 multipart/form-data的编码格式。 cgi.pm(可自 cpan取得)中的 start_multipart_form()这个 method 就是为此设计的,它和 startform()这个 method 是两回事。
用 <select> 和 <option>这两个标签。 cgi.pm模组(可由 cpan取得)对这个 widget【此指跳出式选单这个介面成分】还有许多其他的介面成分都有支援【即有制作动态标签的函式】,其中有些是以巧妙模拟的方 式达成。
有一个方法是,如果您的系统上装有 lynx一类的文字模式的 html浏览器的话,那麽可以这麽做:
$html_code = `lynx -source $url`; $text_data = `lynx -dump $url`;
收录在 cpan里的 libwww-perl (lwp)模组则提供了更强的方法来做这件事。它不但可钻过 proxies,而且也不需要 lynx:
# print html from a url use lwp::simple; getprint "http://www.sn.no/libwww-perl/";;
# print ascii from html from a url use lwp::simple; use html::parse; use html::formattext; my ($html, $ascii); $html = get("http://www.perl.com/";); defined $html or die "can't fetch html from http://www.perl.com/";; $ascii = html::formattext->new->format(parse_html($html)); print $ascii;
以下是一个解码的实例:
$string = "http://altavista.digital.com/cgi-bin/query?pg=q&;what=news&fmt=.&q=%2bcgi-bin+%2bperl.exe"; $string =~ s/%([a-fa-f0-9]{2})/chr(hex($1))/ge;
编码比较困难一点,因为您不能盲目地把所有非字母数字的字元 (\w)都一律转换成十六进位的跳脱码。很重要的是有特殊意义的字元,如 / 和 ? 便不可以 转换。要做得正确,最简单的方法大概是使用现成的 uri::escape模组,而不要重新发明轮子。这个模组是 libwww-perl 套件 (lwp)的一部分,可自 cpan取得。
在您的回应中不要使用 content-type这个标头,相反地,用 location: 这个标头。按正式规定,应当 url: 才是正确的标头。因此 cgi.pm模组(可 由cpan取得)两个标头都送:
location: http://www.domain.com/newpage uri: http://www.domain.com/newpage
要注意的是,由於 servers采用「最高效率化」的运作方式,故在这些标头中如 果使用相对式的 urls可能会产生奇怪的後果。
不一定,要看情况。您需要读您 server的使用手册,或者查查看上头所列的几个 faqs。
httpd::useradmin 和 httpd::groupadmin等模组为这些档案提供了统一的物件导向介面,尽管这些档案可能以各种不同的格式储存。这些资料库可能是纯文字格式、 dbm、berkeley db或任何 dbi相容的资料库驱动程式 (drivers)。 httpd::useradmin支援`basic' 和 `digest'这两个认证模式所用的档案。以下是 一例:
use httpd::useradmin (); httpd::useradmin ->new(db => "/foo/.htpasswd") ->add($username => $password);
阅读 cgi security faq,(可在 http://www-genome.wi.mit.edu/www/faqs/www-security-faq.html取得),还有 perl cgi faq,在 http://www.perl.com/cpan/doc/faqs/cgi/perl-cgi-faq.html 。
简单一句话:使用 tainting(沾腥?)这项功能(详见 perlsec )。它会让所有不在您的 script中、来路不明的资料(譬如, cgi参数)无法放到 eval 或 system等呼叫中使用。除了使用 tainting之外,绝对不要使用单一参数 的方式下参数给 system()或 exec(),而应将欲执行的指令及其参数放在一个序 列或阵列里面,再传给 system()或 exec(),这样便可避免 globbing【即被 shell先做解译】。
如果您只需要一个「快而脏」的解法的话,您可以试试这个从再版的 ``programming perl''第 222页中拿出来的例子:
$/ = ''; $header = <msg>; $header =~ s/\n\s+/ /g; #将延续行合并成单行 %head = ( unix_from_line, split /^([-\w]+):\s*/m, $header );
譬如说,您若想保留所有 received栏位资料的话【因 received栏位通常不止一个】,这个解法便不太行了。一个完整的解法是使用收录在 cpan的 mail::header模组( mailtools 套件的一部分)。
很多人忍不住要自己写程式来处理这部分的工作,所以您们大概都看过一大堆其中有 $env{content_length} 和 $env{query_string}的程式码。没错,这麽 写是可以行得通,但事实上也有很多在网路上出没的这类程式根本不能用!
请不要忍不住去重新发明轮子【译者:这是英文的说法 (reinventing the wheels),也就是浪费时间做人家做过的事的意思】。请改用 cgi.pm或 cgi_lite.pm(可自 cpan取得)。如果您被困在无模组的 perl1 .. perl4的土地上,您可以试看看 cgi-lib.pl(可至 http://www.bio.cam.ac.uk/web/form.html取得)。
无法度。
如果没有寄封信到一个位址去试试看它会不会弹回来(即使是这麽做您还得面对停顿的问题),您是无法确定一个位址是否真的存在的。即使您套用 email 标头的标准规格来做检查的依据,您还是有可能会遇到问题,因为有些送得到的位址并不 符合 rfc-822(电子邮件标头的标准)的规定,但有些符合标准的位址却无法投 递。
许多人试图用一个简单的正规表示式,例如 /^[\w.-]+\@([\w.-]\.)+\w+$/来消除一些通常是无效的 email位址。不过,这样做也把很多合格的位址给一起滤掉了,而且对测试一个位址有没有希望投递成功完全没有帮助,所以在此建议大家不要这麽做;不过您可以看看: http://www.perl.com/cpan/authors/tom_christiansen/scripts/ckaddr.gz。这个 script真的彻底地依据所有的 rfc规定来做检验(除了内嵌式 comments外),同时会排除一些您可能不会想送信去的位址(如 bill clinton【美国柯林顿总统】或您的 postmaster),然後它会确定位址中的主机名称可在 dns中找得到。这个 script 跑起来不是很快,但至少有效。
不少 cgi scripts的作者使用另一个替代的方案:用一个简单的正规表示式,(如上头的那个)。如果一个位址能让这个式子对得上的话,那麽就接受这个位址。如果这个位址对不上这个式子的话,便再向使用者讯问,以确定她们填入的这个位 址正确无误。
mime-tools套件(可自 cpan取得)不但可处理这个问题而且有许多其他的功能。有了这个套件,解 base64码就变得像这麽容易:
use mime::base64; $decoded = decode_base64($encoded);
一个比较直接的解法是先做一点简单的转译,然後使用 unpack()这个函数的 ``u'' 格式:
tr#a-za-z0-9+/##cd; #去除非 base64字元 tr#a-za-z0-9+/# -_#; #转换成 uu码格式 $len = pack("c", 32 + 0.75*length); #计算长度字元 print unpack("u", $len . $_); # uu解码後 print在支援 getpwuid【unix系统呼叫】、 $<这个变数,和 sys::hostname模组(标准 perl 发行的一部分)的系统上,您可试试这样的做法:
use sys::hostname; $address = sprintf('%s@%s', getpwuid($<), hostname);有的公司对 email位址有统筹规画,因此这麽一来您可能会合成出不被公司的 email 主机接受的位址。所以如果有这类的顾虑的话,您应该直接向 users要他们的 email 位址。 而且,并不是所有能跑 perl的系统都像 unix一样,可以很容易得到这些资料。
cpan里的 mail::util模组( mailtools 套件的一部分)中有一个 mailaddress()函数,它会试着去猜 user的 email位址。这个函数使用比上面的 code聪明的方法,它会参考安装时所得到的设定资料,但是错误仍可能发生。所以,再一次地,最好的方法通常是直接问 user 本人。
送信:cpan 上头的 mail::mailer模组( mailtools套件的一部分)只适合在 unix 上使用,但利用到.net::smtp的 mail::inte.net模组则没有这个限制。 读信:用 cpan 上的 mail::folder模组( mailfolder 套件的一部分)或是用 cpan 上头的 mail::inte.net模组( 也是 mailtools 套件的一部分)。
#送信 use mail::inte.net; use mail::header; #设定使用哪台主机 $env{smtphosts} = 'mail.frii.com'; #制作标头 $header = new mail::header; $header->add('from', 'gnat@frii.com'); $header->add('subject', 'testing'); $header->add('to', 'gnat@frii.com'); #制作本文 $body = 'this is a test, ignore'; #产生 mail物件 $mail = new mail::inte.net(undef, header => $header, body => \[$body]); #送出 $mail->smtpsend or die;
长久以来许多 code都很草率地直接呼叫 `hostname`这个程式来取得主机名。 虽然这麽做很方便,但也同时增加了移植到其他平台上的困难。这是一个很典型的 例子,在方便和可移植性之间作抉择,不论选哪一边,必须付出一些牺牲和代价。
sys::hostname这个模组(标准 perl发行的一部分)可用来取得机器的名字,然後您便可利用 gethostbyname()这个系统呼叫来找出该机的 ip位址了(假定您的 dns 运作正常)。
use socket; use sys::hostname; my $host = hostname(); my $addr = .net_ntoa(scalar(gethostbyname($name 'localhost')));
至少在 unix底下,取得 dns网域名最简单的方法大概要算是直接从 /etc/resolv.conf这个档案里面找。当然,这麽做的前提是 resolv.conf这个档 案的设定必须照惯例的格式,还有就是这个档案必先存在才行。
(perl在非 unix系统下尚需要一有效的方法来测出机器和网域名)
使用.net::nntp或 news::nntpclient模组,两者皆可自 cpan下载。这些模组 让抓群组名录这类的差事变得这麽容易:
perl -mnews::nntpclient -e 'print news::nntpclient->;new->list("newsgroups")'
lwp::simple模组(可自 cpan下载)可以抓,但不能丢档案。.net::ftp模组(也可自 cpan下载)虽比较复杂,但可用来丢、也能抓档案。
有一个 dce::rpc模组正在发展阶段(但尚未完成)。一旦完成後它会随着 dce-perl这个套件发行(可由 cpan 下载)。至於 onc::rpc这样的模组则还没听说有人在发展。
Java Asp PHP .Net XML C/C++ CGI VB Jsp J2ee J2se J2me EJB Servlet Tomcat Resin Struts Weblogic Eclipse ANT GUI JMS Web servise IDEA Webphere Hibernate Spring Jboss Applet Swing Socket Javamail Perl Ajax P2P 安全 模式 框架 测试 开源 游戏
Windows XP Windows 2000 Windows 2003 Windows Me Windows 9.x Linux UNIX 注册表 操作系统 服务器 应用服务器