Perl ile CGI Programlama

 

 

BÖLÜM 6: VERİ DOSYALARI İLE ÇALIŞMAK


CGI programlamada en çok işe yarayacak konulardan biri, formlardan gelen bilgileri bir dosyaya kaydetmek ve daha sonra bu bilgileri kullanmaktır. Örneğin, bir ziyaretçi defteri, bir sayaç programı, reklam cumlelerini içeren düz veri dosyalarının okunması vb uygulamalar bizim en çok işimize yarayacak programlardır. Bütün bu dediklerimizi greçekleştirmek için veri dosyalarının okunup yazılmasını öğrenmemiz gerekir. 

Çoğu web sunucusunda izin hakları oldukça sınırlandırılmış olarak çalışılır. Bu güvenlik içindir. Hem sistem, hem de bu sistem üzerinde çalışan sunucunun korunması sağlanır. Malesef bunun anlamı CGI programların okuma yada yazma için veri dosyalarına kolayca erişemeyecekleri anlamına gelir. Veri dosyaları ile olan işlemler, sistemin izin verdiği kadarı ile yapılabilir. İlk başta bu durum bize sıkıntılı anlar yaşatacaktır. Sunucu, normalde sizin web klasörünüzde dosya oluşturmaya izin vermez. Bu, sistemde izin haklarını ayarlamakla halledilebilir. Bir veri dosyasına yazmak için ona en az 666 hakkının verilmesi gerekir. 

chmod 666 veridosyasi.dat

NOT: UNIX'te izin hakları (permissions) şöyledir. 

Dosyalara/klasörlere 3 tür izin hakkı atanabilir: read (okuma, 4), write (yazma, 2) ve execute (çalıştırma, 1). Bir dosyanın/klasörün hakları sırasıyla sahibine, grubuna ve diğerlerine göre (rwxrwxrwx)  şeklinde ayrı ayrı belirtilir. Örneğin, bir dosyayı sahibi okusun, yazsın, çalıştırsın, gruptakiler ve diğerleri sadece okusunlar istersek, rwxr--r-- şeklinde hak atamak gerekir. Bunun sayısal karşılığı (4+2+1; 4+0+0; 4+0+0) = 744 olur. Execute hakkı sadece çalıştırılabilen dosyalara verilir. Veri dosyalarına verilmez. Eğer bir veri dosyasına okuma ve yazma için haklar atayacaksak, rw-rw-rw- izni verilir. Bunun sayısal karşılığı da (4+2+0; 4+2+0; 4+2+0) = 666 olur. Ancak, sisteme giren herhangi birisi bu izin hakları ile veri dosyanızı silebilir, değiştirebilir, adını değiştirebilir ve yapacağınız bir şey de yok. 

Bazı alternatifler var ki bize bu durumlarda yardımcı oluyorlar: CGIwrap ve Apache suEXEC. Her ikisi de web sunusucu altında yetkili kullanıcılar ve izinler tanımlarlar. Eğer siteniz sanal bir site ise (virtual host), Apache sanal siteler için de yetkili kullanıcılar ve gruplar tanımlamaya imkan vermektedir. Daha sonra veri dosyalarınızı aşağıdakilerden biri ile koruyabilirsiniz.

chmod 664 veridosyasi.dat  #  grup için okuma-yazma, diğerleri için sadece okuma
chmod 660 veridosyasi.dat  #  grup için okuma-yazma, diğerleri için hiçbir erişim izni yok

İzinler veri dosyalarını okurken daha az problem çıkartırlar. Yapacağınız, gruba ve diğerlerine okuma hakkı verin, CGI programlar da güvenli bir şekilde okusunlar.

Dosyaların Açılması

Dosyalarla çalışırken ilk önce onlara aşağıdaki deyim ile bir belirteç (handle) atanır ve okuma/yazma işlemleri bu belirteç üzerinden gerçekleştirilir.

open(belirteç, "dosya_adı");

Dosya adının önünde mod belirteci olarak ">" yada ">>" olabilir. Bunların anlamları, dosya çıktı modunda (dosya yeniden oluşturulacak) yada ekleme modunda açılacak demektir. Aşağıdaki örnekleri inceleyelim:

open(INPUT,  "input.txt";     # input.txt dosyası okuma amaçlı açılıyor.
open(OUTPUT, ">output.txt");  # output.txt dosyası yazma amaçlı yeniden oluşturuluyor.
open(OUTPUT, ">>output.txt"); # output.txt dosyası ekleme modunda açılıyor....

Buradaki INPUT ve OUTPUT'lara dosya belirteçleri (dosya değişkenleri) denmektedir. Anlaşılır olacak şekilde istediğiniz gibi dosya değişkenleri belirtebilirsiniz. Ayrıca dosyalara yol (path) da belirtebiliriz. Ancak, burada bir uyarıda bulunmak gerekirse; CGI altında kullanırken sadece dosya adı yerine dosyalara tam yolunu belirtmek zorunda kalabilirsiniz, örneğin, "/home/kullanici/public_html/dosyaadi.txt" gibi. Bu, Apache için problem değildir, ancak farklı sunucular  farklı davranabilirler. Yada dosyalar yol olarak web sunucusunda tanımlanan sanal (web sharing) yollar kullanıyorsanız, örneğin, "~kullanici/cgi-bin/veridosyasi.txt"  gibi -buradaki cgi-bin klasörü sanal klasör olarak belirtilmiştir-, web sunucundaki sınırlandırmalar nedeniyle başınız ağrıyabilir. Bu tür kullanımlardan kaçınmak gerekmektedir.

Dosyalarla çalışırken, dosyanın gerçekten açılıp açılmadığını kontrol etmek lazımdır.  Örneğin,

open(OUTPUT, ">output.txt") OR &dienice("Dosya açılamadı.");

Burada dienice, dosya açılamadığı takdirde kullanıcıya bir hata mesajı veren ve CGI'dan çıkan bir altprogramdır (Bölüm 4'e bakınız). Bu kontrolü tüm dosya açma işlemlerinde yapmalısınız. Çünkü, dosya açılmasa bile CGI çalışmaya devam eder  ve verileriniz kaybolabilir. Örneğin, siz haftaladır anket formu ile bilgiler toplayıp bir dosyaya kaydettiğiniz sanırsınız, ancak dosyaya hiçbir bilgi kaydedilmemiş olabilir (tecrübeyle sabittir..:) ). 

Şimdi, Bölüm 5'te verdiğimiz anket formunu işleyen survey2.cgi programcığını değiştirerek gelen verilerin bir dosyaya kaydedilmesini sağlayacak şekilde düzenleyelim.

#!/usr/bin/perl
print "Content-type:text/html\n\n";
 
read(STDIN, $buffer, $ENV{'CONTENT_LENGTH'});
@pairs = split(/&/, $buffer);
foreach $pair (@pairs) {
    ($name, $value) = split(/=/, $pair);
    $value =~ tr/+/ /;
    $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
    $value =~ s/\n/ /g;
    $FORM{$name} = $value;
}
 
open(OUTF, ">>anket.dat") or &dienice("Dosya açılamadı. Lütfen bu durumu yazara bildirin.");
 
print OUTF "$FORM{'isim'}|$FORM{'email'}|$FORM{'howreach'}|$FORM{'rating'}|";
 
%boxes=("des" => "Web sitesi hazırlama",
  "svr" => "Web sunucusu adminliği",
  "com" => "Elektronik ticaret",
  "mkt" => "Web Marketing",
  "edu" => "Web eğitimi");
foreach $key (keys %boxes) {
    if ($FORM{$key} == 1) {
  print OUTF "$key,";
    }
}
print OUTF "|$FORM{'comments'}\n";
close(OUTF);
 
print <<EndHTML;
<html><head><title>Sonuçlar</title></head>
<body bgcolor="lime">
<h2>Ankete katıldığınız için teşekkürler.</h2>
Devam etmek için <a href="/~adem/cgi-kitap/b6.html"> buraya </a> tıklayınız.<p>
</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/survey2.cgi.txt

Şimdi, içi boş bir çıktı dosyası oluşturup yazma hakkı vermek gerekir (CGI kendisi oluşturamayabilir :-8{)  ??) Bunun için komut satırna düşüp aşağıdaki komutları verelim.

touch anket.dat
chmod a+w anket.dat

touch komutu, içi boş anket.dat dosyasını oluşturmaktadır. chmod komutu ile de grubun dışındakilere yani diğerlerine (all - a) yazma hakkı vermekteyiz. Şimdi http://alaeddin.cc.selcuk.edu.tr/~adem/survey2.html linkini takip ederek anket formunu doldurup yollayalım. Daha sonra da anket.dat dosyasının içini görelim. Dosyada aşağıdakine benzer satırlar göreceksiniz.

Emre Güneş|emre_gunes@hotmail.com|2|4|svr,mkt,des,edu,com,|Güzel bir site
Adem|adem@alaeddin.cc.selcuk.edu.tr|5|3|des,edu,|Bu da ne böyle...

İşte size, içerisinde veriler içeren, herbir satırında bir kayıt tutan düz-dosya veritabanı örneği... Bu örnekte kayıt ayıracı olarak yenisatır karakteri (\n) kullanılmıştır. Herbir alanı ayırmak için de | -dikey çubuk karakteri kullanılmıştır. Eğer gelen verilerde (örneğin, yorumlarda) yenisatır karakteri gelirse işler karışabilir. Gelen verilerden bu karakteri ayıklamak gerekir. Bunun için küçük bir değişiklik yeterli olur...

$value =~ s/\n/ /g;
CGI programında birden çok print deyimi ile verileri dosyaya yazdırmamıza rağmen tüm veriler aynı satırda yer alıyorlar. 
Dosyaya yazdırırken de aşağı satıra geçmek için print OUTF "...\n" şeklinde kullanmak gerekir... Örneğin,
print "Bugün ";
print "hava ";
print "güzel.\n"
print "Değil mi?\n";

deyimlerinin çıktısı şöyledir.

Bugün hava güzel.
Değil mi?

Dosyaların Kapatılması

Bir dosyaya yazma işleminiz bittiyse değişikliklerin kaydedilmesi için dosyayı kapatmanız gerekir:

close(OUTF);

Dosyaların Okunması

Örnekte olduğu gibi, anket formundan gelen bilgileri bir dosyaya kaydettik ve onları değerlendirmek istiyoruz. Dosyadaki bilgileri ele alıp gelen karmaşık anlaşılmaz veriler üzerinde bazı işlemler (örn, istatistik) yapmak isteyelim. 

Bunun için iki farklı yöntem düşünebiliriz. Her seferinde dosyadan tek bir satır okuyarak işlemler yapmak yada dosyayı tamamen okuduktan sonra işlemler yapmak. İşte okuma için kullanmamız gereken komutlar:

open(INF, "anket.dat") or &dienice("Dosya açılamadı.");
 
$a = <INF>; # dosyadan tek bir satır oku ve $a değişkeninde sakla
@b = <INF>; # Dosyanın tamamını oku ve @b dizisinde sakla
 
close(INF);

Görüldüğü gibi, dosyadan okuma işlemi okunan verinin nasıl saklanacağına bağlı olarak "değişken = <INF>" deyimi ile gerçekleştirilmektedir.

Aşağıdaki örnekte dosyanın tamamı önce bir dizi değişkene okunmakta, sonra dizi elemanları üzerinde işlemler gerçekleştirilmektedir. Her döngüde satır sonlarında yer alan yenisatır karakteri ayıklanmaktadır.

open(INF, "anket.dat") or &dienice("Dosya açılamadı.");
@dizi = <INF>;
close(INF);
 
foreach $satir (@dizi){
  chomp($satir);
  print "$satir\n";
}

Bu kodda dosyanın açık olma süresi minimize edilmiştir. Çünkü, dosyanın açık olması, sistem kaynaklarının daha çok tüketilmesi demektir. Bundan sonraki dosya okuma örneklerinde hep bu yöntem kullanılacaktır.

Şimdi tekrar ankete döneli. Farzedelim ki verileriden şu özet bilgileri elde edeceğiz: kaç kişi ankete katılmış, kaç kişi siteyi keşfedebilmiş, ortalama rating nedir, kaç kişi web sitesi tasarlıyor, kullanıcıların yorumlarını listeleme vb.

Yeni bir dosya oluşturalım ve adını surveysumm.cgi koyalım. Bu programımız, veri dosyasını bir diziye okumakta, sonra bir dögü içerisinde çeşitli sayaçlar kullanarak istatistiki veriler elde etmeye çalışmaktadır.

#!/usr/bin/perl
print "Content-type:text/html\n\n";
open(INF,"anket.dat") or dienice("Dosya açılamadı\n");
@data = <INF>;
close(INF);
# İlk önce bazı sayaçlar oluşturup sıfırlamalıyız...
 
$count = 0;
$ratings = 0;
$commentary = "";
%howreach_counts = ();
%involved = ();
%howreach = (
  0 => "",
  1 => "Dergilerden",
  2 => "Önceden imlenen sitelerden",
  3 => "Bir arama motorundan",
  4 => "Bir başka sitedeki linkten",
  5 => "Bir kitaptan",
  6 => "Diğer");
 
foreach $i (@data) {
  chomp($i);
  ($name,$email,$how,$rating,$boxes,$comments) = split(/\|/,$i);
  
  $count++;
  $ratings = $ratings + $rating;
  $commentary .= "$comments\n<br>";
  $howreach_counts{$how}++;
  @invlist = split(/,/,$boxes);
  foreach $j (@invlist) {
         $involved{$j}++;
  }
}
 
if ($count > 0) { # sıfıra bölme hatası için kontrol!
  $avg_rating = int($ratings / $count);
} else {
  $avg_rating = 0;
}
 
# Şimdi özel bilgileri ekrana yazdıralım....
print <<EndHTML;
<html><head><title>Anket Sonuçları</title></head>
<body bgcolor="aqua">
<h2 align="CENTER">Anket Sonuçları</h2>
Toplam ziyaretçi sayısı : $count<p>
Bu sitenin ortalama ratingi: $avg_rating<p>
Ziyaretçiler bu siteyi nasıl keşfettiler:<br>
<ul>
  <li>(cevap yok)  - $howreach_counts{0}
  <li>$howreach{1} - $howreach_counts{1}
  <li>$howreach{2} - $howreach_counts{2}
  <li>$howreach{3} - $howreach_counts{3}
  <li>$howreach{4} - $howreach_counts{4}
  <li>$howreach{5} - $howreach_counts{5}
  <li>$howreach{6} - $howreach_counts{6}
</ul>
Ziyaretçilerin meslekleri: <br>
<ul>
  <li>Web sitesi hazırlama: $involved{'des'}
  <li>Web sunucusu adminliği: $involved{'svr'}
  <li>Elektronik ticaret: $involved{'com'}
  <li>Web Marketing: $involved{'mkt'}
  <li>Web eğitimi: $involved{'edu'}
</ul>
<p>
 
Yorumlar:<p>
$commentary
 
EndHTML
;
 
sub dienice {
  my($msg) = @_;
  print "<h2>Error</h2>\n";
  print $msg;
  exit;
}

http://alaeddin.cc.selcuk.edu.tr/~adem/surveysumm.cgi

http://alaeddin.cc.selcuk.edu.tr/~adem/surveysumm.cgi.txt

Burada anket.dat dosyasındaki verileri okuyarak bu veriler üzerinde bazı istatistiksel sonuçlar elde ettik. Ancak, kullanıcı isimleri ve email adresleri üzerinde herhangi bir şey yapmadık. Belki bunları ayıklayıp bir başka dosyaya aktarabilirdik ki daha sonradan bu kullanıcılarla yazışabiliriz, teşekkür mesajları gönderebiliriz...

Bu örnekte tek bir dosyadan okuma işlemi gerçekleştirdik. İleri seviye CGI programlarda birden çok dosyadan aynı anda veri okuyabiliriz, yada yazabiliriz. Düz-dosya şeklindeki veri dosyaları, online katalog dosyaları olarak kullanılabilir. Örneğin, şirketlere ait reklam cümlelerinin yer aldığı bir dosya düşünelim, sitemize bağlanan her yeni kullanıcıya farklı bir reklam cümlesi sunabiliriz...

 


Bölüm 5Bölüm 7