Perl ile CGI Programlama

 

BÖLÜM 4: FORMLARIN İŞLENMESİ


Bundan sonra oluşturacağımız çoğu formda verileri göndermek için POST metodunu kullanacağız. POST metodu, daha önce de belirtildiği gibi, GET metoduna göre oldukça güvenlidir ve daha çok uzunluktaki verileri gönderilebilir. Ayrıca web tarayıcınız, web sunucunuz veya proxy sunucunuz GET sorgularını cache'leyebilir, fakat POST metodunda veriler her seferinde yeniden gönderilir/alınır. Ancak, POST ile gönderilen verileri ayrıştırmak için biraz zahmete girmek gerekir.

Web tarayıcılar veya web sunucular, form verilerini CGI programına gönderirken, verileri şu şekilde kodlayarak gönderirler: Standart alfanümerik karakterler aynen korunurlar, boşluklar + artı işaretine çevrilirler, diğer karakterler, örneğin, tab karakterleri, tırnaklar ve extra karakterler, %HH şeklinde kodlanarak gönderilirler. Burada HH, ilgili karakterin hexadesimal ASCII kodudur. Bu kodlamaya URL kodlama (URL encoding) denilmektedir. Aşağıda sıkça kullanılan karakterlerin kodlamaları verilmektedir:
 

Normal Karakter

URL Kodlanmış stringi 
\t (tab) %09
\n (return) %0A
/ %2F
~ %7E
: %3A
; %3B
@ %40
& %26

Kullanabilmek için gelen verileri ilk önce çözmek (decode) ve ayrıştırmak gerekir. Allahtan, bu işlemi Perl'de gerçekleştirmek için iki tane yöntem var: eşleştirme (substitute) ve çevirme (translate). Perl'in çok güçlü bir desen eşleme (pattern mathing) ve yerine koyma-değiştirme özellikleri vardır. String'ler üzerindeki bütün bu işlemleri düzenli ifadeler (regular expressions) ile gerçekleştirilebilir (Bölüm 14'e bakınız). Bir eşleştirmenin sintaksı şöyledir: 

$mystring =~ s/desen/yerine_konan/;

Bu komut, "$mystring" içindeki "desen" yerine "yerine_konan" metni koyar. Buradaki =~ (eşittir tilda) özel bir operatordür ve işlev olarak desen uydurma veya yerdeğiştirme işleminin gerçekleştirilmesini sağlar. "s" ise "substitute" yapılacak demektir. Örneğin,

$tebrikler = "Merhaba. Benim ismim xisimx.\n";
$tebrikler =~ s/xisimx/Adem/;
print $tebrikler;

Yukarıdaki kod ekrana "Merhaba. Benim ismim Adem." yazar. Görüldüğü gibi komut, "xisimx" gördüğü yere "Adem" koymuştur. 

Buna benzeyen, ancak yaptığı iş farklı olan çevir (translate) komutunun yapısı ise şöyledir.

$mystring =~ tr/arama_listesi/yerine_konan_liste/;

Bu komut, "arama_listesi" içindeki tüm karakterleri karşılık gelen ilgili "yerine_konan_liste"dekiler ile karakter karakter yerdeğiştirir. "tr" ise "translate" anlamındadır. Yerdeğiştirmenin en yagın kullanımlarından biri, bir metin içindeki bütün büyük harfleri küçük harflere çevirmektir. Örneğin, 

$kucukharf =~ tr/[A-Z]/[a-z]/;

Biz form işlemeye dönersek... Kullanıcıdan gelen verileri benzer şekilde elden geçirmemiz gerekir. Bunun için iki önemli dönüştürme yapmak gerekir.

$value =~ tr/+/ /;
$value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;

Birinci satırda, tüm + işaretleri boşluğa çevirilmiştir. İkinci satır biraz karmaşık olmasına rağmen yapılmak istenen şey, pack() fonksiyonu kullanılarak %HH hexadecimal karakterler yerine ASCII karşılıkları yerleştirilmiştir. 

Şimdi, bütün öğrendiklerimizi uygulayacağımız bir CGI yazalım ve adını da post.cgi koyalım.  

#!/usr/bin/perl

print "Content-type:text/html\n\n";

read(STDIN, $buffer, $ENV{'CONTENT_LENGTH'});
@pairs = split(/&/, $buffer);

foreach $pair (@pairs) {
	($varname, $value) = split(/=/, $pair);
	$value =~ tr/+/ /;
	$value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
	$FORM{$varname} = $value;
}

print "<html><head><title>Formdan Yollanan Bilgiler</title></head><body>";
print "<h3>Formdan Yollanan Bilgiler</h2>\n";
print "--------------------------<br>\n";

foreach $key (keys(%FORM)) {
	print "$key = $FORM{$key}<br>",
}

print "</body></html>";

http://alaeddin.cc.selcuk.edu.tr/~adem/cgi-kitap/cgi-bin/post.cgi.txt

Bu CGI, giriş kanalından gelen verileri $buffer adlı bir skaler değişkene aktarmakta ve sonra da "değişken=değer" şeklindeki çiftleri ayrıştırarak $pairs adlı bir diziye aktarmaktadır. foreach döngüsü ile her bir çift normal düz metne çevirilmekte ve elde edilen veriler %FORM adlı özel tabloda saklanmaktadır. Bu FORM tablosunun anahtarları, form girdi isimlerinin kendileridirler. Sonra, örneğin, formdaki değişkenlere -ki bunlar; username, email-address ve age'dir- $FORM{'username'}, $FORM{'email-address'} ve $FORM{'age'} şeklinde erişmek mümkün olmaktadır.

Bu kod herhangi bir formdan, yani basit bir ziyaretçi defterinden çok daha karmaşık sipariş formlarına kadar, girilen verileri elde etmek için kullanılabilir. Buradaki örnekte hem değişkenlerin isimleri (kendileri), hem de içerdikleri veriler ekrana yazdırılmıştır. 

Dikkat çekmemiz gereken bir diğer fonksiyon daha var: split fonksiyonu. Bu fonksiyon, verilen bir stringi belirtilen bir karaktere veya desene göre ayrıştırarak sonucu liste halinde verir. Örneğin,

$MyString = "Kırmızı&Mavi&Yeşil";
@MyArray  = split(/&/, $MyString);

@MyArray dizisi şuanda ("Kırmızı","Mavi","Yeşil") oldu.

Şimdi bu script'i test edelim. Bunun için aşağıdaki formu hazırlayalım.

<html><head><title>Post.html</title></head>
<body>

<form action="post.cgi" method="POST">
<pre>
         İsminiz : <input type="text" name="username">
e-mail adresiniz : <input type="text" name="email">
         Yaşınız : <input type="text" name="age">
 Favori Renginiz : <input type="text" name="favorite_color">
</pre>
<input type="submit" value="Gönder">
<input type="reset" value="Formu Temizle">
</form>

</body>
</html>

http://alaeddin.cc.selcuk.edu.tr/~adem/cgi-kitap/cgi-bin/post.html

İstenen bilgileri form üzerinde doldurarak Gönder butonuna basın. Ne gördünüz? Değişken isimleri ve sizin girdiğiniz veriler mi? Evet ise devam.

Şimdi bir adım daha ileriye gidelim: Çoğu yerde görmüşüzdür, bir formu doldurup email olarak yollamak mümkündür. Yada formu yolladıktan sonra bize bilgilerin ulaştığına dair bir geridönüş bilgisi içeren sayfalar gelir. İşte biz de buna benzeyen, formun ulaştığını bir email ile kullanıcıya bildiren CGI yazalım. Buna genelde form-mail denilmektedir. İlk önce bir mail alma/gönderme işini yürüten bir programa (sendmail) ihtiyacımız olacak. Yani, bu programın sistemimizdeki yolu (bulunduğu dizin) bilmek gerekecek. (Bunun için which sendmail yada whereis sendmail komutlarını kullanabilirsiniz. Genelde /usr/sbin/sendmail olur.) 

post.cgi programını kopyalayarak mail.cgi olarak çoğaltın. foreach döngüsünde bir değişiklik yapacağız. Standart çıktıya yazdırmak yerine değişkenlerin değerlerini bir email mesajına yazdıracağız. O halde, ilkin, sendmail programını girdi olarak açmalıyız. Tabii bu emailin kime gideceğini de belirtmek gerekecektir. 

Uyarı: @ işaretinin özel anlamı olduğundan bu işareti email adreslerinde kullanırken karışıklığa yol açmamak için, email adreslerini "..."  çift tırnak arasına değil '...' tek tırnak arasına almak gerekir. Örneğin, 'adem@alaeddin.cc.selcuk.edu.tr' gibi veya "adem\@alaeddin.cc.selcuk.edu.tr" gibi.

#!/usr/bin/perl

print "Content-type:text/html\n\n";

read(STDIN, $buffer, $ENV{'CONTENT_LENGTH'});
@pairs = split(/&/, $buffer);
foreach $pair (@pairs) {
	($varname, $value) = split(/=/, $pair);
	$value =~ tr/+/ /;
	$value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
	$FORM{$varname} = $value;
}
$mailprog = '/usr/sbin/sendmail';
$recipient = 'adem@alaeddin.cc.selcuk.edu.tr';

open (MAIL, "|$mailprog -t") or &dienice("$mailprog bulunamadı.!\n");
print MAIL "From: adem_gunes@hotmail.com\n";
print MAIL "To: $recipient\n";
print MAIL "Reply-to: $FORM{'email'} ($FORM{'username'})\n";
print MAIL "Subject: Form-mail Denemesi\n\n";

foreach $key (keys(%FORM)) {
	print MAIL "$key = $FORM{$key}\n";
}

close(MAIL);

print <<EndHTML;
<h2>Teşekkürler</h2>
İlginiz için çok teşşekkür ederiz. Mesajınız alınmıştır..<p>
Ana sayfaya gitmek için <a href="http://alaeddin.cc.selcuk.edu.tr/~adem/">
buraya tıklayınız.</a>\n";
</body></html>
EndHTML
;

sub dienice {
	($errmsg) = @_;
	print "<h2>Hata</h2>\n";
	print "$errmsg<p>\n";
	print "</body></html>\n";
	exit;
}

http://alaeddin.cc.selcuk.edu.tr/~adem/cgi-kitap/cgi-bin/mail.cgi.txt

Yukarıdaki betikte yeni bir yapı kullandık: "dienice" adında bir altprogram. Bir altprogram, yazdığımız kodun belli bir kısmı olup çağrılınca çalışan mini bir programcıktır. Bu örnekte eğer ana program sendmail programını bulamazsa hemen devreye "dienice" altprogramı giriyor, ekrana hata mesajını yazıyor ve kapatılmayan tag'leri kapatıyor. 

Bir altprogramı çağırmak için &subname  veya  &subname(args) şeklinde kullanılır. Burada args, altprograma değer göndermek için kullanılan değişkenlerdir.

Biz sunucuda oluşabilecek hataları bu şekilde kontrol altına alarak sıradan bir hata mesajı yerine kullanıcıları yönlendiren, alternatifler sunan  bir yapı oluşturabiliriz. Yukarıda yazdığımız CGI programını kullanmak için aşağıdaki gibi bir form hazırlayalım. 

<html><head><title>Mail.html</title></head>
<body>

<form action="mail.cgi" method="POST">
<pre>
	         İsminiz : <input type="text" name="username">
	E-mail Adresiniz : <input type="text" name="email">
	         Yaşınız : <input type="text" name="age">
	 Favori Renginiz : <input type="text" name="favorite_color">
</pre>
<input type="submit" value="Gönder">
<input type="reset" value="Formu Temizle">
</form>

</body>
</html>

http://alaeddin.cc.selcuk.edu.tr/~adem/cgi-kitap/cgi-bin/mail.html

Bu formu doldurun, Gönder'e tıklayın. Biraz beklerseniz gönderdiğiniz bilgileri içeren yeni bir email almanız gerekir. Eğer aynı email'i birden çok kimseye aynı anda yollamak isterseniz, mail.cgi dosyasında aşağıdaki değişikliği yapın:

$recipient = 'adem@alaeddin.cc.selcuk.edu.tr, adem_gunes@hotmail.com';


Bölüm 3  Bölüm 5
(C) Copyright Adem GÜNEŞ - Konya 1999, adem@alaeddin.cc.selcuk.edu.tr