12 CGI-ohjelmointi. 12 CGI-ohjelmointi

Samankaltaiset tiedostot
Taustaa. CGI-ohjelmointi

Ehto- ja toistolauseet

Java-kielen perusteet

Johdatus Ohjelmointiin

Koottu lause; { ja } -merkkien väliin kirjoitetut lauseet muodostavat lohkon, jonka sisällä lauseet suoritetaan peräkkäin.

Perusteet. Pasi Sarolahti Aalto University School of Electrical Engineering. C-ohjelmointi Kevät Pasi Sarolahti

3. Muuttujat ja operaatiot 3.1

IDL - proseduurit. ATK tähtitieteessä. IDL - proseduurit

Ohjelmoinnin perusteet Y Python

ATK tähtitieteessä. Osa 3 - IDL proseduurit ja rakenteet. 18. syyskuuta 2014

Sisällys. 3. Muuttujat ja operaatiot. Muuttujat ja operaatiot. Muuttujat. Operaatiot. Imperatiivinen laskenta. Muuttujat. Esimerkkejä: Operaattorit.

Perusteet. Pasi Sarolahti Aalto University School of Electrical Engineering. C-ohjelmointi Kevät Pasi Sarolahti

Java-kielen perusteet

Ohjelmoinnin perusteet Y Python

Ohjelmoinnin perusteet Y Python

815338A Ohjelmointikielten periaatteet Harjoitus 6 Vastaukset

Ohjelmoinnin perusteet Y Python

WWW-sivut HTML-kielellä esitettyä hypertekstiaineistoa

Alkuarvot ja tyyppimuunnokset (1/5) Alkuarvot ja tyyppimuunnokset (2/5) Alkuarvot ja tyyppimuunnokset (3/5)

Ctl160 Tekstikorpusten tietojenkäsittely Kolmas luento,

LOAD R1, =2 Sijoitetaan rekisteriin R1 arvo 2. LOAD R1, 100

Järjestelmäarkkitehtuuri (TK081702)

Ohjelmoinnin perusteet Y Python

Ohjelmoinnin perusteet Y Python

Tutoriaaliläsnäoloista

Ohjelmoinnin perusteet Y Python

4. Lausekielinen ohjelmointi 4.1

Ohjelmoinnin peruskurssi Y1

Sisällys. 17. Ohjelmoinnin tekniikkaa. Aritmetiikkaa toisin merkiten. for-lause lyhemmin

Harjoitustyö: virtuaalikone

Java-kielen perusteita

Ohjelmoinnin perusteet Y Python

Ohjelmoinnin perusteet Y Python

Muuttujatyypit ovat Boolean, Byte, Integer, Long, Double, Currency, Date, Object, String, Variant (oletus)

811120P Diskreetit rakenteet

PERL. TIE Principles of Programming Languages. Ryhmä 4: Joonas Lång & Jasmin Laitamäki

Ohjelmoinnin perusteet Y Python


13. Loogiset operaatiot 13.1

Ohjelmointitaito (ict1td002, 12 op) Kevät Java-ohjelmoinnin alkeita. Tietokoneohjelma. Raine Kauppinen

Ohjelmoinnin perusteet Y Python

Sisällys. 16. Ohjelmoinnin tekniikkaa. Aritmetiikkaa toisin merkiten. Aritmetiikkaa toisin merkiten

Pythonin Kertaus. Cse-a1130. Tietotekniikka Sovelluksissa. Versio 0.01b

16. Ohjelmoinnin tekniikkaa 16.1

Sisällys. 3. Muuttujat ja operaatiot. Muuttujat ja operaatiot. Muuttujat ja operaatiot

Palvelinpuolen ohjelmointi

16. Ohjelmoinnin tekniikkaa 16.1

Tietueet. Tietueiden määrittely

Taulukot. Jukka Harju, Jukka Juslin

12. Javan toistorakenteet 12.1

Ohjelmassa henkilön etunimi ja sukunimi luetaan kahteen muuttujaan seuraavasti:

3.1 Mitä tarkoittaan heredoc? Milloin sitä kannattaa käyttää? Kirjoita esimerkki sen käyttämisestä.

Ohjelmoinnin perusteet Y Python

Luento 3. Jouni Ikonen - Jouni.Ikonen lut.fi

Ohjelmoinnin peruskurssi Y1

Zeon PDF Driver Trial

etunimi, sukunimi ja opiskelijanumero ja näillä

Python-ohjelmointi Harjoitus 5

Perinteiset tietokoneohjelmat alkavat pääohjelmasta, c:ssä main(), jossa edetään rivi riviltä ja käsky käskyltä.

Sisällys. 12. Näppäimistöltä lukeminen. Yleistä. Yleistä

Python-ohjelmointi Harjoitus 2

Hieman linkkejä: lyhyt ohje komentoriviohjelmointiin.

7. Näytölle tulostaminen 7.1

11. Javan toistorakenteet 11.1

Harjoitus 2 (viikko 45)

12. Javan toistorakenteet 12.1

C-ohjelma. C-ohjelma. C-ohjelma. C-ohjelma. C-ohjelma. C-ohjelma. Operaatioiden suoritusjärjestys

Sisältö. 22. Taulukot. Yleistä. Yleistä

Ohjelmointitaito (ict1td002, 12 op) Kevät Java-ohjelmoinnin alkeita. Tietokoneohjelma. Raine Kauppinen

Ohjelmoinnin perusteet Y Python

Ohjelmoinnin peruskurssi Y1

Concurrency - Rinnakkaisuus. Group: 9 Joni Laine Juho Vähätalo

Digitaalisen median tekniikat xhtml - jatkuu

Ohjelmoinnin perusteet Y Python

Digitaalisen median tekniikat xhtml - jatkuu Harri Laine 1

Ohjelmoinnin perusteet Y Python

4. Lausekielinen ohjelmointi 4.1

Ohjelmoinnin perusteet Y Python

Ohjelmoinnin peruskurssi Y1

Luento 5. Timo Savola. 28. huhtikuuta 2006

Maastotietokannan torrent-jakelun shapefile-tiedostojen purkaminen zip-arkistoista Windows-komentojonoilla

Ohjelmoinnin peruskurssi Y1

Erittäin nopea tapa saada kehitysympäristö php:lle pystyyn Voidaan asentaa muistitikulle

KServer Etäohjaus Spesifikaatio asiakaspuolen toteutuksille

Algoritmit 1. Luento 4 Ke Timo Männikkö

Ohjelmoinnin peruskurssi Y1

Kirjoita oma versio funktioista strcpy ja strcat, jotka saavat parametrinaan kaksi merkkiosoitinta.

Toinen harjoitustyö. ASCII-grafiikkaa 2017

Sisällys. 11. Javan toistorakenteet. Laskurimuuttujat. Yleistä

815338A Ohjelmointikielten periaatteet Harjoitus 7 Vastaukset

Asko Ikävalko, k TP02S-D. Ohjelmointi (C-kieli) Projektityö. Työn valvoja: Olli Hämäläinen

811120P Diskreetit rakenteet

Ohjelmoinnin perusteet Y Python

Harjoitus 5. Esimerkki ohjelman toiminnasta: Lausekielinen ohjelmointi I Kesä 2018 Avoin yliopisto 1 / 5

Pythonin alkeet Syksy 2010 Pythonin perusteet: Ohjelmointi, skriptaus ja Python

12. Näppäimistöltä lukeminen 12.1

ELM GROUP 04. Teemu Laakso Henrik Talarmo

Sisältö. 2. Taulukot. Yleistä. Yleistä

Ohjelmoinnin perusteet Y Python

Sisällys. 12. Javan toistorakenteet. Yleistä. Laskurimuuttujat

Transkriptio:

234 Tässä luvussa kerrotaan www-dokumentteihin vuorovaikutteisuutta ja dynaamisuutta lisäävästä tekniikasta, CGI-rajapinnasta. Ensimmäisessä alaluvussa käsitellään CGI-ohjelmoinnin perusteita ja teoriaa, ja se voi olla kohtuullisen raskasta luettavaa henkilölle, joka ei ole harrastanut ohjelmointia. Luku olisi syytä kuitenkin käydä läpi CGI-ohjelmoinnin perusteiden ymmärtämiseksi. On myös mahdollista hypätä suoraan myöhempiin alalukuihin ja tehdä CGI-ohjelmia niissä esitettyjen ohjeiden ja esimerkkien avulla, mutta varsinkin CGI-ohjelmien turvallisuuden takaamiseksi perusteet olisi syytä hallita. Toisessa alaluvussa käsitellään Perl-ohjelmointikielen perusteita. Perl on tällä hetkellä käytetyin ohjelmointikieli CGI-ohjelmoinnissa. Tämä johtuu siitä, että Perl soveltuu mainiosti merkkijonojen käsittelyyn, jolloin se on kuin luotu HTML-muotoiltujen lomakkeiden tietojen jäsentämiseen ja käsittelyyn. Samoin tiedostojen ja hakemistojen käsittely sekä verkon käyttö ovat Perl-kielen avulla ylivertaisen helppoa muihin ohjelmointikieliin verrattuna. Perl on tulkattava kieli, jonka haittapuolena on suorituksen hitaus käännettyihin ohjelmiin verrattuna, mutta etuna suuri laitteistoriippumattomuus. Hitautta on pyritty ja pystyttykin parantamaan uusilla, nopeilla Perl-tulkeilla. Sama Perl-kielellä tehty ohjelma toimii ilman muutoksia lähes kaikissa palvelinkoneissa, riippumatta siitä, ovatko ne Unix-, Linux-, Windows- tai vaikkapa MacIntoshpalvelimia. Lisäksi Perl-kieli on vapaasti saatavissa ja edelleen jaeltavissa. Kolmannessa alaluvussa käsitellään dynaamisia dokumentteja, joilla tarkoitetaan dokumentteja, joiden sisältö vaihtuu ympäristöehtojen esimerkiksi päivän, käyttäjän selainohjelman, tai vaikkapa satunnaisuuden mukaan ilman, että kenenkään tarvitsee erikseen tehdä muutoksia dokumentteihin tai ohjelmiin. Www-sivuilla olevat laskurit lienevät tyyppillisimpiä esimerkkejä CGIohjelmilla toteutetusta dynaamisuudesta. Neljännessä alaluvussa kerrotaan lomakkeiden käsittelystä lomakedatan turvallisesta lukemisesta, sovelluslogiikan lisäämisestä ja palautteen antamisesta. Lomakkeiden avulla www-sivuille voidaan luoda vuorovaikutteisuutta, joka ei muilla tekniikoilla ole yhtä helposti toteutettavissa. Luvussa käsitellään myös käyttäjän syöttämien tietojen lisääminen tiedostoihin, ja tietokantyyppisten tekstitiedostojen käsittely ja raportointi. Useat palveluntarjoajat sallivat CGI-sovellusten asentamisen palvelinkoneelle. Heiltä tulee varmistaa, minkälaiset asetukset CGI-ohjelmia varten palvelimelle on luotu. Yleensä CGI-ohjelmat asennetaan hakemistoon cgi-bin, mutta jotkin palvelimet voivat tunnistaa CGI-ohjelmat tiedostotarkentimen perusteella, joka yleensä on *.cgi.

235 CGI-ohjelmille tulee antaa luku- ja suoritusoikeudet, mikä UNIX-järjestelmissä tapahtuu komennolla chmod 755 tiedoston_nimi (tai chmod a+rx tiedoston_nimi). Mikäli CGI-ohjelman on tarkoitus kirjoittaa tiedostoihin ja hakemistoihin, tulee kyseisille tiedostoille ja hakemistoille antaa riittävät oikeudet. Nämäkin oikeudet vaihtelevat eri palvelimissa johtuen erilaisista asetuksista, ja siitä, minkälaisilla oikeuksilla CGI-ohjelmaa suoritetaan, joten asia kannattaa varmistaa ennen käyttöönottoa. Kuten HTML-sivutkin olisi syytä testata ennen julkistamista omalla kovalevyllään olevilla tiedostoilla, myös CGI-skriptit kannattaa testata huolellisesti ennen varsinaiselle palvelinkoneelle siirtämistä. Linux- ja Unixjärjestelmiin on olemassa ilmaisia palvelinohjelmistoja, joista suosituin on Apache. Yleensä kotikoneissa on kuitenkin Windows 95-, 98- tai NT järjestelmä, joihin niinikään on olemassa ilmaisia palvelinohjelmistoja. Jos haluaa kevyen ja helposti käyttöönotettavan palvelinohjelmiston esimerkiksi CGI-ohjelmien testausta varten, niin yksi varteenotettavista ehdokkaista on Xitami Web Server, jonka voi hakea ilmaiseksi osoitteesta www.imatix.com. Kyseisen ohjelman asentaminen kestää noin kymmenen sekuntia, minkä jälkeen palvelin on toimintakunnossa, ja CGI-ohjelmia voidaan testata todellisessa käytössä www-selaimen kautta. Ohjeet CGI-ohjelmien käyttämisestä ko. palvelinohjelmistossa seuraavat asennuspaketin mukana. 12.1 CGI-ohjelmoinnin teoriaa Vuodesta 1993 alkaen www:ssä käytössä ollut CGI (Common Gateway Interface) on NCSA:n (the National Center for Supercomputing Applications) ja CERNin (the European Laboratory for Particle Physics) kehittämä laiteympäristöriippumaton rajapintastandardi, jonka tämän hetkinen versio on CGI/1.1. Standardissa määritellään abstraktit parametrit eli ympäristömuuttujat, jotka kuvaavat selainohjelman lähettämiä pyyntöjä ja määrittelevät rajapinnan CGI-sovelluksen ja palvelimen välille. CGI-rajapinnan tehtävänä on myös toimittaa sovellukselle informaatiota palvelimesta ja asiakkaan selainohjelmasta. CGI-sovellukseksi sanotaan palvelinkoneella olevaa ohjelmaa, kirjastoa tai alirutiinia, joka kommunikoi CGI-rajapinnan välityksellä www-dokumentin kanssa. Dokumentti voi olla mikä tahansa www:ssä käytettävä tiedosto, esimerkiksi HTML-, teksti- tai kuvatiedosto. Kirjallisuudessa CGI-sovelluksista käytetään usein myös nimitystä skripti (script). Sovellus voi olla ns. kuorisovellus (shell script), tulkattavalla ohjelmointikielellä suoritettava ASCIItiedosto tai käännetty binääriohjelma. Sovellusohjelmien ja www-dokumenttien välillä oleva CGI-rajapinta mahdollistaa esimerkiksi käyttäjän syötteistä riippuvien dynaamisten ja vuorovaikutteisten www-sivujen luomisen, mikä ei olisi mahdollista pelkästään

236 HTML-muotoilukielellä. CGI:n yksinkertaisuus ja soveltuvuus eri palvelinkonetyypeille ovat johtaneet rajapinnan laajaan käyttöön. Rajapinnalle on myös kehitetty runsaasti ohjelmointia helpottavia työkaluja, esimerkiksi vapaasti saatavilla olevat Perl- ja C-kirjastot. CGI-rajapinnan avulla kaikki data voidaan tallentaa tietokantaan, jossa sitä voidaan haluttaessa ylläpitää. Kun käyttäjä pyytää tietoa www-sivun välityksellä tästä tietokannasta, CGI-sovellus hakee tarvittavan datan ja esittää sen käyttäjälle selainohjelmassa. Tarvittaessa käyttäjälle voidaan sallia myös datan muuttaminen, tietueiden lisäykset, poistot ja muokkaukset. CGI-sovellusohjelmat mahdollistavat www-sivujen tekemisen vuorovaikutteisiksi. HTML-muotoilukielen FORM-elementin sisästä saatu data on suoraan sovellusohjelman käytettävissä. Käyttäjän täytettyä lomakkeessa olevat kentät niiden perusteella voidaan generoida uusi sivu, jossa käyttäjälle annetaan palautetta hänen syötteistään. Tämä mahdollistaa esimerkiksi monentyyppiset pelit ja tilauslomakkeet. The Finnish Fitness Planin ja Keuhkovammaliiton Teens-projektin Kalenteri 2000 -sivulla on voinut voittaa joka päivä vuoden 1999 loppuun saakka. Voittajien arvonta, tietokannan ylläpito ja sivun generointi ovat toteutetut CGIsovelluksilla (ffp.uku.fi/teens/2000.htm).

237 CGI-sovellusohjelmien avulla voidaan rakentaa myös ns. ilmoitustauluja ja yleisönosastoja. Tällöin käyttäjät voivat kirjoittaa viestinsä www-lomakkeelle, ja viesti tulee osaksi www-sivua. Nämä voidaan toteuttaa yksinkertaisesti esimerkiksi siten, että kaikki viestit ovat peräkkäin ilmoitustaululla tai tekemällä monitasoisia Usenet-tyyppisiä viestialueita, joissa viestit ja niiden vastaukset ovat järjestetyt kronologisesti aihealueittain. CGI-sovellusten avulla voidaan luoda myös reaaliaikaisia keskusteluympäristöjä tai virtuaalisia ostoskeskuksia. Common Gateway Interfacen standardissa CGI/1.1 määritellään ne elementit, joita tarvitaan rajapinnan kautta tapahtuvaan kommunikointiin. Palvelin ja CGIsovellus kommunikoivat ympäristömuuttujien, standardisyötteiden ja standarditulostusten välityksellä. Kun palvelimesta haetaan tietoa URLosoitteella, joka viittaa suoritettavien CGI-ohjelmien tiedostoon, palvelin ei lähetä selainohjelmalle tiedostoa sellaisenaan, vaan URL-osoitteessa mainittu CGI-sovellus suoritetaan, ja sen tulostamat tiedot lähetetään eteenpäin. Palvelin käsittelee selainohjelmien pyyntöjä. Vuorovaikutus palvelimen ja selainohjelman välillä alkaa yleensä selainohjelman lähettäessä pyynnön palvelimelle. Pyyntö sisältää tietoja selainohjelmasta ja palvelimelta haetusta objektista ja lähetetään HTTP:n pyyntöotsikkotiedoissa (HTTP Request Header). Palvelin vastaanottaa pyynnön ja poimii otsikkotiedoista tarvitsemansa informaation kyetäkseen lähettämään pyydetyn objektin selainohjelmalle. Objekti lähetetään HTTP:n vastausotsikkotiedoissa (HTTP response header). Selainohjelma käyttää vastausotsikon tietoja ratkaistessaan, kuinka esittää palvelimen palauttama objekti, joka yleensä on HTML-kielellä muotoiltu dokumentti. Pyyntöotsikkotiedoista ja vastausotsikkotiedoista käytetään myös yhteisnimitystä otsikkotiedot. Selainohjelman lähettäessä pyynnön CGI-sovelluksen suorittamisesta palvelin käynnistää kyseisen ohjelman ja ohjaa sille HTTP:n otsikkotiedot, joihin tallennettu informaatio on siten CGI-sovelluksen käytettävissä. CGI-sovelluksen suorituksen päättyessä tulos lähetetään takaisin palvelimelle, joka muotoilee HTTP:n otsikkotiedot ja lähettää vastauksen selainohjelmalle. On myös mahdollista, että CGI-sovellus muotoilee itse HTTP:n otsikkotiedot ja lähettää datan suoraan selainohjelmalle.

238 CGI-ohjelman toiminta. Selainohjelma lähettää pyynnön, palvelin käynnistää CGI-ohjelman ja ohjaa sille HTTP:n otsikkotiedot. CGI-ohjelma suorittaa tehtävän ja tulostaa vastauksen palvelimelle, joka muotoilee HTTP:n otsikkotiedot ja lähettää vastauksen selainohjelmalle. HTTP on tilaton protokolla eli se ei säilytä tietoa edellisestä yhteydestä. HTTP on myös rajoittunut yhteen vastaukseen yhtä pyyntöä kohden. Aina, kun selainohjelma tekee pyynnön, muodostuu uusi yhteys palvelimeen. Jotkut selainohjelmat odottavat edellisen yhteyden päättymistä ennen kuin avaavat uuden yhteyden, toiset avaavat monta samanaikaista yhteyttä. Jotkut selainohjelmat sallivat käyttäjän määrittelevän yhteyksien lukumäärän. Kun selainohjelma ottaa yhteyttä palvelimeen, selainohjelman täytyy lähettää halutun dokumentin polku ja käytettävä pyyntömetodi (request method). HTTPstandardissa määritellään pyyntömetodit CHECKIN, GET, HEAD, CHECKOUT, SHOWMETHOD, PUT, POST ja TEXTSEARCH. GET ja POST ovat näistä tärkeimmät CGIohjelmoijalle. 12.1.1 GET-metodi GET-metodia käytetään pyydettäessä palvelimelta jotain dokumenttia. Aina, kun käyttäjä valitsee HTML-tiedostoon johtavan linkin, selainohjelma käyttää GETmetodia valitun dokumentin saamiseen. Jos pyydetty URL osoittaa CGIsovellukseen, tämä generoi joko dokumentin, virheilmoituksen tai viittauksen toiseen dokumenttiin. CGI-sovellus voi tunnistaa käytetyn metodin tutkimalla

239 REQUEST_METHOD-ympäristömuuttujaa, jonka arvoksi tässä tapauksessa on asetettu merkkijono GET. Tämän jälkeen CGI-sovellus saa lomakkeen arvot käyttöönsä QUERY_STRING-ympäristömuuttujan arvosta. GET-metodia on ajateltu käytettäväksi CGI-sovelluksille, jotka eivät kutsuttaessa aiheuta pysyviä muutoksia (esimerkiksi sovellukset, jotka vain etsivät tietoa tietokannasta ja näyttävät ne käyttäjälle). Koska GET-metodia käytettäessä tulevat tiedot ovat URL-osoitteessa, sen voi tallettaa selainohjelman kirjanmerkkilistaan ja valita myöhemmin uudelleen. Esimerkiksi valmiin haun tuloksen voi tallentaa kirjanmerkiksi ja suorittaa koska tahansa uudelleen ilman, että hakulomaketta tarvitsee täyttää uudelleen. GETmetodin huono puoli on se, että tiedot näkyvät URL-osoitteessa, ja siten myös palvelimien tilastoissa, selainohjelmien ruudulla ja historiavalikossa. Esimerkiksi käyttäjän kirjoittamat salasanat ovat näin kaikkien näkyvissä. GET-metodilla ei voi myöskään lähettää kovin pitkiä tietoja. Jotkut järjestelmät saattavat leikata liian pitkiä URL-jonoja (yli 255 merkkiä), ja joissain ympäristöissä ohjelman parametrien pituus on rajoitettu.

240 12.1.2 POST-metodi POST-metodissa ohjelman saamia tietoja ei välitetä URL-osoitteen osana, vaan erillistä siirtokanavaa pitkin eli HTTP-metodin POST mukana. Tätä menetelmää käytetään erityisesti lomakkeiden kanssa. Ohjelma saa tällöin lomakkeelta syötetyt tiedot standardisyötteenä (stdin), eikä ympäristömuuttujien kautta. POSTmetodia on ajateltu käytettäväksi lomakkeille, jotka muuttavat jotakin, kun niitä kutsutaan (esimerkiksi tallentamalla tiedot johonkin tiedostoon). Tässä tapauksessa REQUEST_METHOD-ympäristömuuttujaan asetetaan arvoksi POST. CONTENT_LENGTH-ympäristömuuttujassa kerrotaan saapuvien tavujen lukumäärä. Kun standardisyötteestä luetaan CONTENT_LENGTH-ympäristömuuttujan ilmoittama määrä tavuja, niistä muodostuu URL-koodattu merkkijono. POST-metodia käytettäessä lähetetyt tiedot eivät jää palvelimen logitietoihin eivätkä selainohjelmien kirjanmerkkilistoihin. POST-metodilla voi lähettää enemmän tietoa kuin GET-metodilla. Lomakkeita täyttämällä saatavat toiminnot eivät kuitenkaan ole toistettavissa. Kun CGI-sovelluksen suoritus päättyy, se tavallisesti lähettää tuloksensa palvelimelle standarditulostuksen kautta riippumatta siitä, oliko selainohjelman lähettämänä pyyntömetodina GET vai POST. Tulokset saatuaan palvelin muotoilee HTTP:n otsikkotiedot, ja palauttaa kaiken datan selainohjelmalle. 12.1.3 Otsikkotiedot Standardin mukaisten otsikkotietojen täytyy edeltää kaikkea CGI-sovellukselta palvelimelle lähetettävää dataa. Nämä otsikkotiedot ovat CGI-sovelluksen koodirivejä, jotka palvelin jäsentelee. Otsikkotiedot ovat samassa muodossa kuin HTTP-standardissa määritellyt otsikkotiedot ja voivat sisältää mitä vain CGIympäristömuuttujien nimiä. Otsikkotietoa täytyy aina välittömästi seurata tyhjä rivi. Kaikki rivit otsikkotiedoissa, jotka eivät ole ohjeita palvelimelle, lähetetään takaisin selainohjelmalle osana HTTP:n otsikkotietoja. CGI/1.1. spesifioi kolme palvelindirektiiviä, jotka ovat Content_Type, Location ja Status. Content_Type yksilöi lähetettävän datan sisältötyypin (MIME). Location sisältää CGI-sovelluksen selainohjelmalle palauttaman dokumentin URL-osoitteen muodossa Location:url, jota seuraa tyhjä rivi. Tällöin palvelin ohjaa selainohjelman pyynnön uuden URL-osoitteen viittamaan dokumenttiin. Status palauttaa palvelimelle HTTP-standardissa määritellyn vastauskoodin (status code), joka lähetetään selainohjelmalle. Tyypillisimpiä virhekoodeja ovat viestit, jotka kertovat, että dokumenttia ei löytynyt (404 Not Found) tai että dokumentin saanti on estetty (403 Forbidden). Status-otsikkotietoa ei tulisi lähettää, jos CGI-

241 sovellus palauttaa Location-otsikkotiedon. Viimeinen osa vastauksesta sisältää varsinaisen dokumentin. 12.1.4 Ympäristömuuttujat Osa palvelimista on tehnyt laajennuksia CGI-standardin määrittelemään ympäristömuuttujien joukkoon. Palvelinkohtaisten laajennusten käyttö heikentää kirjoitettavien sovellusten siirrettävyyttä käyttöympäristöstä toiseen ja vie pohjaa pois CGI-rajapinnan järjestelmäriippumattomuudelta. Ympäristömuuttujien nimet voivat hieman vaihdella riippuen käytetystä palvelimesta ja CGI-ohjelmointikielestä, mutta tietojen merkitys on sama. Ympäristömuuttujista tärkeimpiä CGI-ohjelmoinnin kannalta ovat REQUEST_METHOD, QUERY_STRING, CONTENT_LENGTH ja CONTENT_TYPE. REQUEST_METHOD on palvelua käynnistettäessä käytetty metodi eli HTTPprotokollaa käytettäessä esimerkiksi GET tai POST. Tämän vastauksen nojalla päätellään, mistä varsinainen data löytyy. Mikäli metodi on GET, tieto on tallennettu ympäristömuuttujaan QUERY_STRING, POST-metodilla lähetetyn HTMLtiedoston data on saatavilla standardisyötteestä. Jos ohjelmalle on annettu URL-osoitteessa parametreja kysymysmerkin jälkeen, ne näkyvät yhtenä merkkijonona QUERY_STRING-ympäristömuuttujassa. Parametrit näkyvät samassa muodossa kuin ne ovat URL:ssa, mikä tarkoittaa sitä, että erikoismerkit kuten välilyönnit ja rivinvaihdot on koodattu heksadesimaalimuotoon (%xx), ja joillakin merkeillä on erikoismerkitys. GETmetodia käytettäessä data on sijoitettu QUERY_STRING-ympäristömuuttujan arvoksi. CONTENT_LENGTH-ympäristömuuttujasta löytyy standaridisyötteenä (stdinpuskurissa) olevan merkkijonon pituus käytettäessä POST-metodia. Jos ohjelmaa on kutsuttu käyttäen metodia, joka pystyy lähettämään tietoa erillistä siirtokanavaa pitkin (esimerkiksi POST), CONTENT_TYPEympäristömuuttujassa näkyy tulevan tiedon tyyppi, joka kerrotaan sisältötyyppinä (MIME). Useimmissa tapauksissa kaikki CGI-sovelluksen tarvitsema informaatio sisältyy ympäristömuuttujiin. Selainohjelman pyytäessä CGI-sovellusta palvelimelta POST-metodilla käyttäjän kirjoittama data lähetetään kuitenkin standardisyötteenä. Palvelimella ei ole velvoitetta lähettää mahdollista tiedoston loppua (eof) sen jälkeen, kun CGI-sovellus on lukenut CONTENT_LENGTH-ympäristömuuttujan ilmoittaman tavumäärän.

242 12.1.5 Lomaketietojen käsittely Lähettipä palvelin käyttäjän täyttämän datan standardisyötteenä tai liittämällä sen QUERY_STRING-ympäristömuuttujaan, niin data lähetetään aina yhtenä pitkänä merkkijonona, joka koostuu URL-koodatuista nimi-arvo-pareista. Tällöin CONTENT_TYPE-ympäristömuuttujan arvoksi tulee application/x-www-formurlencoded. Koodauksessa välilyönnit on muutettu plus-merkeiksi ( + ) ja erikoismerkit heksadesimaalisiksi. Ennenkuin tämän datan kanssa voidaan työskennellä, merkkijono tulee jäsennellä ja erotella siitä nimi-arvo -parit. Jokainen nimi-arvo-pari koostuu HTML-tiedoston kentän nimestä ja arvosta, jotka on erotettu toisistaan yhtäsuuruusmerkillä. Kentän nimi on yleensä otettu HTML-kielen INPUT-, TEXTAREA- tai SELECT-elementin name-attribuutista, ja arvo on tavallisesti käyttäjän täyttämä tieto kyseiseen kenttään. Nimi-arvo-parit erotetaan toisistaan &-merkillä. Kutsuttavan CGI-sovelluksen täytyy palauttaa tuloksensa palvelimelle, joka lähettää ne pyynnön esittäneelle selainohjelmalle. CGI-sovellus voi myös ohittaa palvelimen ja palauttaa tulokset suoraan selainohjelmalle. Kummassakin tapauksessa CGI-sovelluksessa täytyy määritellä standarin mukainen otsikkotieto. Tavallisesti sovellukset tuottavat standarditulostuksen, mikä tulkitaan ja lähetetään pyynnön esittäneelle selainohjelmalle. Tällöin sovellusohjelman ei tarvitse lähettää täyttä HTTP/1.0 standardissa määriteltyä otsikkotietoa jokaiseen pyyntöön. 12.1.6 Vastauksen tulostus CGI-sovelluksella on valittavissa kaksi tulostuksen muotoa: jäsennelty (Parsed headers) ja jäsentelemätön (No parsed headers, nph). Palvelimen ei välttämättä tarvitse tukea nph-tyyppistä tulostamisen muotoa. CGI-sovelluksen jäsennellyssä tulostuksessa dokumentti tulee aloittaa ilmoittamalla sisällön MIME-tyyppi CONTENT_TYPE-ympäristömuuttujan jäljessä. Tämän jälkeen täytyy tulostaa vähintään yksi tyhjä rivi, joka ilmoittaa varsinaisen tiedoston alkamisesta. Palvelin lisää näihin tietoihin vastauskoodin 200, joka tarkoittaa, että selainohjelman lähettämä pyyntö ei ollut virheellinen, että palvelin löysi pyydetyn dokumentin, ja että dokumentti on tulossa. Lähes jokaisen CGI-sovelluksen ensimmäinen tulostusrivi on siis muotoa print "Content-Type: text/html\n\n";

243 Tavallisesti palvelin muotoilee HTTP-standardin määrittelemän vastauskoodin, mutta joissakin CGI-sovelluksissa on tarpeellista lähettää vastauskoodi suoraan sellaisenaan selainohjelmalle. Erottaakseen tällaiset sovellukset muista CGIstandardissa on määritelty kyseisten sovellusten nimen alkavan tekstillä nph-. Palvelin ei koske nph-ohjelmien tulostamaan tietoon millään tavalla, vaan lähettää sen sellaisenaan selainohjelmalle käsiteltäväksi. Tällöin ohjelman vastuulle jää oikeiden vastauskoodien tulostaminen. 12.1.7 Lomakkeet CGI-sovelluksia kutsutaan yleensä HTML-muotoilluista lomakkeista. Lomakkeet laajentavat sivujen käyttömahdollisuuksia esimerkiksi tavaroiden tilaamiseen. Lomake voi sisältää monipuolisen valikoiman elementtejä, tekstikenttiä, tekstialueita, valikoita, vierityspalkkeja, valintanappeja, piilotettuja k1enttiä, valintaruutuja sekä lähetys- ja tyhjennyspainikkeita, joita voidaan käyttää lomakkeen täyttämisen helpottamiseen tai ohjaamiseen. Kaikkien kenttien sisällöt lähetetään palvelimelle yhdellä kertaa, tavallisesti tähän tarkoitukseen varatun painonapin avulla. Palvelin vastaanottaa lomakkeen tiedot ja käynnistää erillisen CGI-ohjelman, joka käsittelee lomakkeen tiedot. Www-dokumenttiin upotetun lomakkeen voidaan ajatella koostuvan kahdesta eri elementistä. Toinen on dokumentin lukijalle näkyvä, sivuille upotettu varsinainen lomake, ja toinen on palvelimella toimiva CGI-sovellus, joka osaa purkaa ja käsitellä selainohjelman lähettämän lomakkeen tiedot. Monet Internetin hakupalvelut käyvät hyvästä esimerkistä lomakkeen ja CGI-sovelluksen yhteistyöstä. Käyttäjä täyttää yksinkertaisen lomakkeen, tekstin syöttökentän. Selainohjelma lähettää tekstikentän sisällön hakupalvelua pyörittävälle sovellukselle, joka käsittelee sen, suorittaa syötetyn tiedon mukaisen haun tietokantaansa ja generoi uuden sivun, linkkilistan, joka palautetaan käyttäjälle. Jo HTML 2.0 -standardissa määriteltiin lomakkeet ja niiden sisältämät elementit. Lomakkeen määrittely alkaa komennolla <FORM> ja loppuu komentoon </FORM>. <FORM>-merkinnällä on kolme attribuuttia: ACTION, METHOD ja ENCTYPE. ACTION-attribuutin arvona on sen CGI-sovelluksen URL-osoite, jota kutsutaan, kun käyttäjä on täyttänyt lomakkeen kentät ja painaa lähetyspainiketta. METHODmääreen antamaa menetelmää käytetään lähetettäessä lomakkeen sisältö CGIsovellukselle; lomakeohjelmoinnissa tämä on GET tai POST. ENCTYPE-attribuutilla ilmoitetaan, mitä mediatyyppiä lomakkeen informaation koodaamiseen käytetään, ennen kuin se lähetetään selainohjelmalta CGI-sovellukselle. Jos CGIsovellusta kutsutaan URL-osoitteella, joka alkaa http://, ENCTYPE-määrettä ei tarvitse käyttää, koska tällöin se on oletusarvoisesti application/x-www-formurlencoded. Lomake-elementeistä on kerrottu luvussa 10.

244 Painettaessa lomakkeen SUBMIT-lähetyspainiketta selainohjelma koodaa lomakkeen tiedot nimi-arvo -pareiksi, jotka yhdistetään yhdeksi merkkijonoksi &-merkillä. Lomakkeen tiedot lähetetään palvelimen kautta CGI-sovellukselle, joka käsittelee ne haluamallaan tavalla. Ellei ENCTYPE-attribuutilla toisin ilmoiteta, lomakkeen tietoja lähetettäessä MIME-tyypiksi asetetaan application/x-www-form-urlencoded. CGI-sovellus lukee CONTENT_TYPEympäristömuuttujaa ja sen arvon ollessa x-www-form-urlencoded tulkitsee lähetettävän datan olevan peräisin HTML-muotoillusta lomakkeesta. Tarkasteltuaan lähetystä ohjelma tyypillisesti generoi joko uuden dokumentin, virheilmoituksen tai viittauksen toisen dokumentin URL-osoitteeseen. Lomakkeessa olevien kenttien NAME-attribuuttien arvojen ja CGI-sovellusten muuttujien nimien tulee vastata toisiaan. 12.1.8 Turvallisuus HTTP-standardissa on erikseen määritelty käytettävien siirtometodien turvallisuuden huomioonottaminen. Esimerkiksi palvelimen tukiessa autentikointimenetelmiä selainohjelman tulee lähettää otsikkotiedot, jotka sisältävät käyttäjätunnuksen ja salasanan. Standardin määritelmien mukaan palvelimen ei CGI-sovelluksen tehtävänä on käsitellä tämä informaatio, eikä sitä pitäisi kuljettaa HTTP_AUTHORIZATION-ympäristömuuttujassa. CGI-sovelluksia suoritetaan palvelimen käyttäjätunnuksella. Siksi palvelimen asetuksissa tulisi varmistaa, ettei CGI-sovellus pääse tahallaan tai vahingossa käsiksi palvelinprosesseihin, palvelimen asetuksiin tai suojattuihin dokumentteihin. CGI-standardissa suositellaan otettavaksi varokeinot käyttöön myös silloin, kun CGI-sovellusta suoritetaan kutsumalla palvelimen funktioita, tarvittaessa estämällä epäluotettavan ohjelman suoritus. CGI-sovellukset sijoitetaan tarkoitusta varten tehtyyn CGI-hakemistoon, joka yleensä on cgi-bin. Tuossa hakemistossa olevia tiedostoja ei voi ladata näkyviin selainohjelman ruudulle, vaan sieltä pyydetyt tiedostot suoritetaan ja saadut tulosteet annetaan selainohjelman näytölle. Jos ohjelmaa ei voi suorittaa, palvelin käyttäytyy kuin pyydettyä tiedostoa ei olisi olemassakaan ja palauttaa selainohjelmalle virheilmoituksen. Tällä järjestelyllä taataan tietoturvan säilyminen: selainohjelmat pääsevät kiinni vain palvelimen näkemän hakemistopuun sisälle ja tämän ulkopuoliset hakemistot ovat piilossa. Kirjoitettaessa omia CGI-sovelluksia vastuu siirtyy ohjelmoijalle. Monet kaupalliset palvelijat toteuttavat monimutkaisempiakin suojausprosesseja verkkojakelussa.

245 Jotkut CGI-sovellukset eivät poista komennoille välittyvistä merkkijonoista kiellettyjä merkkejä kuten puolipistettä ja rivinvaihtoa. Näin CGI-sovelluksen avulla voidaan välittää vapaamuotoisia suoritettavia komentoja suoraan käyttöjärjestelmälle. Huonosti toteuttetujen suodattimien vuoksi monissa palvelimissa voi välittää käyttöjärjestelmälle haluttuja joskus haitallisia komentoja. Esimerkiksi palautelomakkeen toiminnasta vastaava CGI-sovellus saattaa ottaa käyttäjän syöttämästä lomakkeen kentästä suoraan sähköpostiosoitteen ja rakentaa tämän avulla postituskomennon kutsumalla unixin sendmail-ohjelmaa. Jos lomakkeen kentästä saatua tietoa ei tarkisteta, voidaan kentässä antaa normaalin sähköpostiosoitteen sijaan jokin epätoivotun tuloksen tuottava merkkijono: hacker@somewhere.com;mail hacker@somewhere.com < /etc/passwd Kun tämä merkkijono välitetään CGI-sovellukselle, on tuloksena komentoketju, jossa puolipisteen jälkeen tuleva merkkijono suorittaa ei-toivotun komennon: järjestelmän salasanatiedosto (passwd) postitetaan järjestelmään murtautumista suunnittelevalle käyttäjälle. Yhtä hyvin CGI-sovelluksen kautta olisi voitu suorittaa lähes mikä tahansa Unix-komento. Tällaiset CGI-sovelluksiin liittyvät ongelmat eivät rajoitu vain Unix-ympäristöön, vaan vastaavanlaisia on tavattu myös Windows-pohjaisissa palvelimissa. Perl-tulkkeja käytetään yleisesti monissa Unix- ja Windows-pohjaisissa palvelimissa, joihin on lisätty Perl-kielellä luotuja CGI-sovelluksia. Jos Perltulkki on Windows-pavelimessa sijoitettu samaan hakemistoon tulkattavien CGIsovellusten kanssa, voidaan sille välittää vapaavalintaisia komentoja, esimerkiksi tuhota kaikki hakemistossa olevat tiedostot käyttämällä Perlin unlink-kutsua. Ongelmalta vältytään sijoittamalla Perl-tulkki CGI-sovelluksille määritellyn hakemiston ulkopuolelle. Ongelma koskee lähinnä Netscapen ja WebSiten Windows NT -pohjaisia palvelimia. CGI-sovellusten ei ole pakko sijaita tietyssä hakemistossa. Tarkentimella *.cgi varustettujen tiedostojen lisääminen suoritettaviksi CGI-sovelluksiksi saattaa vaarantaa palvelimen turvallisuuden. Kaikkien käyttäjien tekemiä CGIsovelluksia ajetaan palvelimen käyttäjätunnuksella, ja palvelimen käyttäjä voi tehdä kotisivulle ohjelman, joka hävittää kaikki palvelimessa sijaitsevat tiedostot. CGI-ohjelmien turvallisuusriskit vaikuttaisivat johtuvan lähinnä ohjelmoijien ja ylläpitäjien tekemistä tahallisista tai tahattomista virheistä. Noudattamalla CGIstandardissa annettuja yleisiä vaatimuksia palvelimen asetuksista, CGIsovellusten sijoittamisesta ja välttämällä ainakin tuntemattomia - mahdollisesti kaikkia - ilmaiseksi jaettavia CGI-sovelluksia turvallisuusriskit saadaan minimoitua. CGI-ohjelmia ja -skriptejä voi kirjoittaa melkeinpä millä kielellä tahansa. Ainoastaan skriptien ajoympäristö saattaa aiheuttaa pieniä rajoituksia kielen suhteen. Suosituimpia kieliä ovat Perl, C ja C++.

246 CGI-rajapinnalla on lukuisia etuja kilpailijoihinsa nähden. Www:n HTTPprotokollaa laajentavista tekniikoista se on ollut käytössä pisimpään. Myös laitteistoriippumattomuus on toteutunut useimpia muita välineitä paremmin. Koska CGI-sovellukset suoritetaan palvelinkoneessa, ei käyttäjän selainohjelmalla ja laiteympäristöllä ole merkitystä suoritukseen. HTML-kieleen upotetuista skriptikielistä poiketen lähdekoodit ovat käyttäjän ulottumattomissa. Ilman CGI-rajapinnan hyödyntämistä skriptikielien avulla ei voida ylläpitää tietokantoja verkkosovelluksissa. CGI-rajapinnan käytöllä on myös huonot puolensa. CGI-sovellus ei voi antaa käyttäjälle välitöntä palautetta, koska vastaussyötteet käsitellään palvelinkoneella. Myös turvallisuus koetaan edelleenkin ongelmalliseksi, eikä syyttä: jatkuvasti löytyy uusia CGI-sovelluksia, joiden kautta käyttäjät pääsevät murtautumaan palvelimiin. CGI-rajapinnan puuttellisuuksista huolimatta sen suosio osoittaa sen, että ainakin tietyn tyyppisten www-sovellusten toteuttamisessa se on vielä pitkään ohittamaton työväline. 12.2 Perl-kielen perusteita Larry Wallin kehittämä Perl (Practical Extraction and Report Language) on tulkattava ohjelmointikieli, joka on lainannut syntaktisia ja semanttisia piirteitä monilta Unix-työkaluilta. Se on tällä hetkellä käytetyin kieli CGI-ohjelmoinnissa, mikä johtuu siitä, että Perl soveltuu mainiosti merkkijonojen manipulointiin, jolloin se on kuin luotu HTML-muotoiltujen lomakkeiden tietojen jäsentämiseen ja käsittelyyn. Samoin tiedostojen, hakemistojen ja verkon käyttö on Perl-kielen avulla ylivertaisen helppoa muihin ohjelmointikieliin verrattuna. Perl on tulkattava kieli, jonka haittapuolena on suorituksen hitaus käännettyihin ohjelmiin verrattuna, mutta etuna suuri laitteistoriippumattomuus. Hitautta on pyritty ja pystyttykin parantamaan uusilla, nopeilla Perl-tulkeilla. Sama Perlkielellä tehty ohjelma toimii ilman muutoksia lähes kaikissa palvelinkoneissa, riippumatta siitä, ovatko ne Unix-, Linux-, Windows- tai vaikkapa MacIntoshpalvelimia. Lisäksi Perl-kieli on vapaasti saatavissa ja edelleen jaeltavissa. Vaikka Perl ei ole käännettävä kieli, se on nopeampi kuin monet muut tulkattavat kielet. Tämä johtuu siitä, että suoritusympäristö lukee koko lähdekoodin ensin muistiinsa ja kääntää sen saman tien sisäiseen binäärimuotoon, joka sitten pääsee suoritukseen. Tämä binäärinen muoto toimii nopeammin kuin tekstissä tulkittavat komennot. Perl on tavallaan hyvin yksinkertainen kieli. Koska se on syntaktisesti tuttu lähes kaikille ohjelmoijille muita kieliä muistuttavalta kieliopiltaan, alkuun

247 pääseminen on kohtuullisen helppoa. Perlillä syntyvät nopeasti ensimmäiset sovellukset, ja niiden suorittaminen on vaivatonta. Mutta Perl on myös hyvin vaikea kieli, jos sen tahtoo hallita perusteellisesti. Kieli on laaja ja monipuolinen, ja niinpä sen kaikkien ominaisuuksien tuominen esille yhden kirjan puitteissa on vaikeaa jopa mahdotonta. Tässä kirjassa ei pyritäkään selventämään Perlkielestä kuin ne perusteet, joiden avulla pääsee CGI-ohjelmoinnin alkuun (ja saa kipinän Perl-kielen opiskeluun). Perl-kielen oppaita on lukuisia, ja paras niistä lienee Programming Perl, jossa yhtenä kirjoittajana on kielen kehittänyt Larry Wall. Lisäksi kannattaa kahlata läpi Perl-tulkin mukana tulevat manuaalit, joista kertyy tulostettuna noin 1500 sivua. Perl-kielen kotisivu on osoitteessa www.perl.com. Perl-kielen kommenttimerkki on #. # Tämä on kommmentti, jossa kerrotaan ei-mitään. # Tulkki ohittaa nämä kaksi kommenttiriviä Kommenttimerkin jäljessä samalla rivillä oleva teksti on kommenttia, eikä siis vaikuta ohjelman suoritukseen muualla kuin jokaisen Perl-kielisen ohjelman ensimmäisellä rivillä, jossa tulee aina määritellä koneella olevan Perl-tulkin sijainti. Määrittely tapahtuu siten, että #-merkin jäljessä on huutomerkki (!), jonka perään kirjoitetaan Perl-tulkin sijainti absoluuttisena polkuna, esimerkiksi Unix-palvelinkoneissa Perl-ohjelman ensimmäinen rivi voisi olla #!/usr/bin/perl Unix-palvelinkoneissa sijainti saadaan selville esimerkiksi komennolla which perl. Perl-kielessä kuten muissakin ohjelmointikielissä muuttujia käytetään tiedon säilytyspaikkana ohjelman suorituksen ajan. Muuttujilla tulee olla mahdollisimman kuvaava nimi, yksilöllinen tunniste, jolla siihen voidaan viitata ohjelman suorituksen eri kohdissa. Perl-kielessä muuttujille ei määritellä tyyppiä esittelyn yhteydessä, niinkuin monissa muissa ohjelmointikielissä tehdään, vaan tyyppi määräytyy suorituksen aikana. Muuttujan nimen eteen laitetaan $-merkki, josta tulkki tietää, että kyseessä on viittaus muuttujan sisältämään arvoon. Muuttuja alustetaan johonkin arvoon esittelyn yhteydessä. Alustaminen tapahtuu Perlin sijoitusoperaation avulla, joka on monista ohjelmointikielistä tuttu =. Laillisia muuttujia ovat esimerkiksi $muuttuja1 = "Olen muuttuja, jolla on merkkijono-muotoinen arvo."; $tama_on_muuttuja_aika_pitka_nimeltaan = "$muuttuja1 Tosin pitkä."; $muuttuja_jolla_on_kokonaisluku_alkuarvo = 10; $pii = 3.14159; Merkkijono-muotoisissa muuttujissa käytetään yleensä normaaleja kaksinkertaisia lainausmerkkejä ("), jolloin niiden sisällä olevat muuttujat muunnetaan arvoonsa tulkitsemisen yhteydessä.

248 $m1 = "Olen merkkijono. "; $m2 = "$m1 Toinen sellainen.";# $m2-muuttujan arvo on: # "Olen merkkijono. Toinen sellainen." Jos merkkijonolle käytetään -hipsumerkkejä, niiden sisällä $-merkki ei viittaakaan muuttujaan, vaan tulkitaan normaaliksi merkiksi. $m3 = Jenkeissä kaikki maksaa $10 ; # $m3-muuttujan arvo on: # "Jenkeissä kaikki maksaa $10" Perl-kielessä on joukko vakiofunktioita, joita voidaan käyttää omissa ohjelmissa. CGI-ohjelmoijan kannalta yksi tärkeimmistä on print, jonka avulla saadaan tulostettua stadardivirtaan, mikä CGI-ohjelmoinnin yhteydessä tarkoittaa yleensä sitä, että kyseinen tulostus ohjautuu palvelimen kautta www-selainohjelmalle käyttäjän katseltavaksi. print "Tämä on ensimmäinen tulostukseni."; print "Ja heti perään toinen!"; Nämä tiedot jo lähes riittävätkin yksinkertaisen CGI-ohjelman tekoon ainakin kun huomautetaan, että Perl-kielessä voidaan merkkijonoissa erikoismerkkejä tulostaa takakeno-merkin (\) avulla siten, että esimerkiksi \n tarkoittaa rivinvaihtoa ja \t tabulointia. print "Tämä on jo kolmas tulostukseni.\n"; # Mukana rivinvaihto print "Ja heti perään neljäs ja eri riville!"; Luvussa 5.1 kerrottiin, että CGI-ohjelman tulee aloittaa tulostamisensa sisältötyypin määrittämisellä. Kun se lisätään tässä luvussa esitettyihin asioihin, on ensimmäinen Perl-kielinen CGI-ohjelma lähes valmis julkaistavaksi. Lisätään tulostuksen joukkoon vielä HTML-kielen muotoilukomentoja, samalla tavalla kuin tehtäisiin perinteistä staattista www-sivua, mutta tällä kertaa muotoilukomennot ovat Perl-kielen print-komennon sisällä. Kannattaa huomata, että jokainen Perl-kielen komento päättyy aina puolipisteeseen. #! d:\perl\bin\perl $muuttuja1 = "Terve"; $muuttuja2 = "Itsellesi"; $pilkku = ","; $huutomerkki = "!"; # SEURAAVALLA RIVILLÄ MÄÄRITELLÄÄN SISÄLTÖ-TYYPPI print "Content-Type: text/html\n\n"; # EDELLINEN RIVI ON PAKOLLINEN KAIKISSA CGI-OHJELMISSA... #... ENNEN TULOSTAMISEN ALOITTAMISTA... #...HUOMAA TYHJÄT RIVIT MÄÄRITTÄVÄT \n\n... #... SISÄLTÖTYYPIN MÄÄRITTÄMISEN JÄLKEEN!!! print "<HTML>\n";

249 print "<HEAD><TITLE>$muuttuja1</TITLE></HEAD>\n"; print "<BODY>\n"; print "$muuttuja1\n"; print "<BR>\n"; print "$muuttuja1 $pilkku $muuttuja2 $huutomerkki\n"; print "<BR>\n"; print "Tulostinko äsken, että:\n"; print "<BR>\n"; print "Terve, Itsellesi!\n"; print "<BR>\n"; print "</BODY>\n"; print "</HTML>\n"; Kun yllä olevan esimerkin koodi on sijoitettu palvelimen (tai oman tietokoneen palvelinohjelmiston) cgi-bin-hakemistoon, ja annettu kyseiselle tekstitiedostolle luku- ja suoritusoikeudet (Unix- ja Linux-järjestelmissä chmod 755 tiedoston_nimi), se on valmis suoritettavaksi. Jos palvelin on konfiguroitu siten, että käyttäjät asentavat omaan cgi-bin-alihakemistoonsa suoritettavat ohjelmat, niin ohjelmaa voidaan kutsua selainohjelmasta esimerkiksi URL-osoitteella http://www.palvelinkone.fi/cgi-bin/~kayttajatunnus/ohjelman_nimi. Palveluntarjoajalta tai palvelinkoneen ylläpitäjältä kannattaa varmistaa se, mihin CGI-ohjelmat asennetaan, ja minkälaisella URL-osoitteella niitä kutsutaan, koska nämä seikat vaihtelevat eri palvelimissa ja eri palveluntarjoajilla. Jotkut palveluntarjoajat saattavat vaatia myös tiedostimen päättyvän johonkin tiettyyn tarkentimeen, esim. *.cgi tai Perl-kielisissä ohjelmissa *.pl. Onnistuneen suorituksen jälkeen ohjelma tulostaa standardivirtaan eli palvelimen kautta selainohjelmalle alla olevan koodin, ja selainohjelma näyttää käyttäjälle oheisen kuvan mukaisen www-sivun. <HTML> <HEAD><TITLE>Terve</TITLE></HEAD> <BODY> Terve <BR> Terve, Itsellesi! <BR> Tulostinko äsken, että: <BR> Terve, Itsellesi! <BR> </BODY> </HTML>

250 Ensimmäinen CGI-ohjelma selainohjelmassa. Periaate kaikissa CGI-ohjelmien tekemissä tulosteissa on sama kuin edellä olleessa esimerkissä. Jos ohjelmilla aiotaan kuitenkin tehdä myös jotain järkevää, on Perl-kielestä syytä tietää hieman enemmän.

251 12.2.1 Tietotyypit Perlissä ei muuttujien tyyppiä määritellä esittelyn yhteydessä, eikä kielessä ole vastaavanlaisia tietotyyppejä kuin monissa muissa ohjelmointikielissä. Perlissä on tavallaan kolmenlaisia tietotyyppejä: skalaareja, joihin viittaavat muuttujat määritellään $-alkuisilla tunnisteilla, taulukoita, jotka merkitään @-alkuisilla tunnisteilla, ja jotka koostuvat skalaareista sekä assosiatiivisia taulukoita, jotka merkitään %-alkuisilla tunnisteilla, ja jotka nekin koostuvat skalaareista. Myös esim. tiedosto- ja hakemistokahvojen sekä aliohjelmaviittauksien voidaan ajatella olevan Perlin tietotyyppejä. Taulukolla tarkoitetaan alkioista koostuvaa listaa, jossa kullakin alkiolla on oma indeksinsä, jolla siihen voidaan viitata. Perlissä taulukoiden indeksointi alkaa paikasta 0. Kokonaiseen taulukkoon viitataan Perl-kielessä @-alkuisella tunnisteella, mutta taulukon yksittäiseen alkioon, joka on skalaari, $-alkuisella tunnisteella. Taulukon viimeiseen indeksiin voidaan viitata #-merkillä. $viikonpva = "maanantai"; # Skalaarimuuttuja @viikonpaivat = {"maanantai","hauskantai","sunnuntai"; # Taulukko $viikonpaivat[0]; # Eli "maanantai" $viikonpaivat[$#viikonpaivat]; # Eli "sunnuntai" Assosiatiivisen taulukon alkiot ovat nimi/arvo-pareja. Sijoittaminen ja alkion arvoihin viittaminen voidaan tehdä seuraavalla tavalla: %varit = ('punainen',0x00f,'sininen',0x0f0,'vihrea',0xf00); $varit{'punainen'; # Viitataan arvoon 0x00f Aliohjelmilla ohjelmaan saadaan modulaarisuutta. Perl-kielessä aliohjelmien kutsua voidaan merkitä &-merkillä, joka on virallinen tapa sille, mutta &-merkki voidaan jättää kutsusta poiskin. Kutsun yhteydessä aliohjelmalle voidaan välittää parametreja seuraavasti: &ALI_OHJELMA($param1, $param2, $param3); Aliohjelma voidaan määritellä missä kohden ohjelmakoodia vain sen ei tarvitse siis olla ennen pääohjelmaa tai pääohjelman jälkeen. Toisin kuin monissa muissa ohjelmointikielissä, aliohjelman määrittelyssä ei tarvitse eikä saa olla parametrilistaa, sillä Perlissä välitetyt parametrit saadaan aliohjelman käyttöön taulukkomuuttujasta @_, missä aliohjelmakutsun ensimmäinen parametri on aliohjelmassa @_[0], toinen parametri @_[1], jne. Aliohjelman tunnistaa varatusta sanasta sub. sub ALI_OHJELMA { my $eka_parametri = @_[0]; my $toka_parametri = @_[1];

252 Taulukon käyttäminen parametrivälityksessä mahdollistaa sen, että parametrien määrä voi vaihdella kutsukohtaisesti. Perl-kielen my-määreellä tehdään muuttujamäärittelystä lohkokohtainen, eli ylläolevassa esimerkissä $eka_parametri ja $toka_parametri näkyvät vain kyseisessä aliohjelmassa, ja mahdolliset muutokset niissä eivät näy muihin aliohjelmiin tai pääohjelmaan. 12.2.2 Operaattorit Operaattoreita käytetään ohjelmointikielissä muuttamaan ohjelmointikielen lausekkeiden arvoa. Perl-kielen aritmeettisia operaattoreita ovat + (yhteenlasku), - (vähennyslasku), * (kertolasku), / (jakolasku), % (jakojäännös) ja ** (potenssiin korotus). Toisin kuin monissa muissa ohjelmointikielissä, yhteenlaskumerkkiä ei käytetä merkkijonon katenointiin (yhteenliittämiseen), vaan se tehdään pistemerkillä (.). Alla olevassa esimerkissä tehdään kaikkia edellä mainittuja yhteenlaskutoimituksia ja viimeisellä rivillä merkkijonojen katenointi. Tämä onnistuu, koska Perl-kielessä ei ole tietotyyppejä perinteisessä mielessä. #! d:\perl\bin\perl $luku1 = 10; $luku2 = 20; $summa = $luku1 + $luku2; # $summa-muuttujan arvo on 30. $erotus = $luku1 - $luku2; # $erotus-muuttujan arvo on -10. $tulo = $luku1 * $luku2; # $tulo-muuttujan arvo on 200. $osamaara = $luku1 / $luku2; # $osamaara-muuttujan arvo on 0.5 $jakojaannos = $luku1 % $luku2; # $jakojaannos-muuttujan arvo on 10. $potenssi = $luku1 ** $luku2; # $potenssi-muuttujan arvo on 1e+20 # eli 10 potenssiin 20. print $luku1 + $luku2; # Tulostaa 30 print "\n"; print $luku1. $luku2; # Tulostaa 1020 Vertailuoperaattoreilla verrataan operandien yhtäsuuruutta, erisuuruutta tai keskinäistä järjestystä. Perl-kielen vertailuoperaattoreita ovat < (pienempi kuin), > (suurempi kuin), <= (pienempi tai yhtä suuri kuin), >= (suurempi tai yhtä suuri kuin), == (yhtä suuri kuin),!= (eri suuri kuin) ja <=>, jolla yhtäsuuruus palauttaa arvon 0, vasen operandi suurempi kuin oikea operandi palauttaa arvon 1, ja muussa tapauksessa operaattori palauttaa arvon 1. Muut vertailuoperaattorit palauttavat arvon 1, jos vertailu on tosi ja muussa tapauksessa tyhjän merkkijonon. Kaikille edellä luetuille vertailuoperaattoreille on omat vastineensa merkkijonojen vertailua varten, ja niistä kerrotaan luvussa 12.2.4.

253 Perl-kielen sijoitusoperaattori on monista ohjelmointikielistä tuttu =. Edellä on jo käytettykin operaattoria sijoitettaessa muuttujaan jokin arvo. Sijoitus tapahtuu siten, että vasemmalla puolella olevaan muuttujaan sijoitetaan oikealla puolella oleva arvo. Oikealla puolella voi olla myös esim. lauseke, joka palauttaa jonkin arvon. $muuttuja1 = 10; $muuttuja2 = &potenssi(10); Perl-kielessä käytetään myös C-kielestä tuttua lyhennettyä merkintätapaa tilanteissa, joissa sama muuttuja esiintyy sijoitusoperaattorin kummallakin puolella. Lyhennelmää voi käyttää lähes kaikissa operaatiossa, joissa on kaksi operandia (esim. kaksi yhteenlaskettavaa). Niinpä seuraavat kaksi lausetta ovat identtiset. $i = $i + 3; $i += 3; # Sama lyhyempänä Myös Perl-kielen arvonmuunto-operaattorit ovat samanlaiset kuin C-kielessä. ++operaatio kasvattaa operandiansa yhdellä, ja - vähentää yhdellä. Näistä operaattoreista on prefix- ja postfix-muotonsa, eli operaattorit voidaan kirjoittaa muuttujan jommalle kummalle puolelle. Mikäli ne kirjoitetaan muuttujan eteen, muuttujan arvo muuttuu ennen kuin arvoa käytetään. Jos operaattori on muuttujan jälkeen, muuttujan arvo muuttuu sen jälkeen, kun arvo on käytetty siinä lausekkeessa, jossa se sijaitsee. $uusi_muuttuja1 = ++$i; # Arvoa kasvatetaan ennen operaatiota $uusi_muuttuja2 = $i++; # Arvoa kasvatetaan operaation jälkeen Perl-kielen loogisia operaattoreita ovat!, &&,, not, and ja or.! on negaation merkki ja sen synonyymi on luonnollista kieltä muistuttava not. Vastaavasti && on and-operaation synonyymi ja sen merkitys on ja. puolestaan on or-operaation synonyymi ja sen merkitys tai. Näistä && ja ovat ehdollisia operaattoreita. Perlissä on vielä yksi ehdollinen operaatio:?:- operaatio, joka on lyhempi tapa merkitä if-else-lausetta. Esim. if (b < c) a = c; else a = b; voidaan kirjoittaa lyhyemmin ehdollisena lausekkeena: a = (b < c? c : b); Perl-kielessä on lisäksi bittitason operaattoreita, jotka toimivat nimensä mukaisesti bittitasolla. Bittitason operaatiot kohdistuvat luvun bittien nollaamiseen ja asettamiseen. & ja ovat bittioperaattoreita ja vastaavat edellä

254 esitettyjä loogisia operaattoreita and ja or. Näiden lisäksi on määritelty ^ (xor), joka on poissulkeva tai operaattori (joko-tai) sekä bittien siirto- operaattorit << (kierrätä vasemmalle) ja >> (kierrätä oikealle eli kopioi etumerkkiä). Bittien siirto-operaattoreita voi käyttää esimerkiksi näin: a = a << 2 # Kierrättää muuttujan a:n bittejä vasemmalle kahdella eli # tulos kerrotaan neljällä Perl-kielessä kutsutaan nuolioperaattoriksi merkintää ->, jota käytetään viittausten yhteydessä. Operaattorilla on myös oliomainen viittausmerkitys, mikä selviää luvun 12.3 lopussa olevasta esimerkistä. Perl-kielessä on lisäksi määritelty joukko tiedoston testausoperaattoreita, esimerkiksi e, jolla voidaan testata tiedoston olemassaolo, -d, jolla testataan onko tiedosto hakemisto ja s, joka palauttaa tiedoston koon. Tiedoston olemassaolo voidaan siis testata seuraavasti: if (!-e $tiedosto) { print "Tiedostoa $tiedosto ei ole olemassa!\n"; else { # Tee tiedostolle jotain Muita Perl-kielen operaattoreita kannattaa hakea aiemmin mainituista Perlmanuaaleista ja oppaista. 12.2.3 Ohjausrakenteet Ohjelman ja algoritmin suoritusta ohjaillaan yleensä peräkkäisyyden, valinnan ja toiston avulla. Luonnollisesti myös Perl-kielestä löytyvät nämä ominaisuudet, ja seuraavassa tarkastellaan Perl-kielen valinta- ja toistolauseita, joista osa on tuttuja C- ja Java-ohjelmoijille, mutta osa kaikille Perl-kieltä tuntemattomille täysin uusia. Luvussa 5.2.2 esiteltiin jo if-else-rakenteen vaihtoehtoinen muoto. if-lauseella voidaan suorittaa vaihtoehtoisia toimintoja. if-lause toimii siten, että testataan, onko jokin ehto tosi, ja mikäli on, suoritetaan { ja sulkujen välissä oleva ohjelmakoodi, jota sanotaan lohkoksi. Edellä oli jo esimerkki tiedoston olemassaolon testaamisesta, jonka lohkoon voi lisätä useitakin suoritettavia lauseita: if (!-e $tiedosto) { print "Tiedostoa $tiedosto ei ole olemassa!\n"; print "Ole hyvä, ja luo tiedosto...\n";

255 print "...tai jätä homma sikseen!\n"; Edellisen luvun esimerkissä oli myös else-lohko, jolla kuvataan vaihtoehtoista suoritusta. Jos if-ehto ei toteudu, suoritetaan else-lohkossa oleva ohjelmakoodi. Jos taas if-lauseen ehto toteutuu, else-lohkoa ei suoriteta, vaan siirrytään ohjelmakoodissa lohkoa seuraavaan lauseeseen. Myös else-lohkossa voi olla useita suoritettavia lauseita: if (!-e $tiedosto) { print "Tiedostoa $tiedosto ei ole olemassa!\n"; print "Ole hyvä, ja luo tiedosto...\n"; print "...tai jätä homma sikseen!\n"; else { open(tiedosto,$tiedosto); while(<tiedosto>) { # Tulosta tiedoston sisältö: print $_; close(tiedosto); Joskus voi olla tarpeen luoda useita vaihtoehtoisia toimintoja, jotka nekin tarkastavat jonkin ehdon täyttymisen ennen lohkossa olevan ohjelmakoodin suoritusta. Perl-kielessä on olemassa elseif-lause, joka esiintyy aina if- tai elseif-lauseen jälkeen ja sitä seuraa elseif- tai else-lause. Mikäli if-lauseen ehto ei toteudu, siirrytään elseif-lauseeseen. Jos sen ehto on tosi, suoritetaan kyseisen elseif-lauseen ohjelmalohko. Jos se ei toteudu, prosessia jatketaan, kunnes löytyy tosi elseif-ehto tai tullaan else-lauseeseen. if (!-e $tiedosto) { print "Tiedostoa $tiedosto ei ole olemassa!\n"; print "Ole hyvä, ja luo tiedosto...\n"; print "...tai jätä homma sikseen!\n"; elseif ($tiedosto ne "autoexec.bat") { print "Et halua tulostaa käynnistiedostoa autoexec.bat.\n"; print "Onpa omituista!\n"; elseif ($tiedosto ne "command.com") { print "Et halua tulostaa käynnistiedostoa command.com.\n"; print "Onpa omituista!\n"; else { open(tiedosto,$tiedosto); while(<tiedosto>) { # Tulosta tiedoston sisältö: print $_; close(tiedosto); Perl-kielessä on olemassa myös unless-valintarakenne, joka muuttaa ehtolauseen testaamisen käänteiseksi. Sama asia voidaan ilmaista myös if-ehdon negaatiolla,

256 mutta se voi toisinaan olla hankalaa luettavaa. Seuraavat esimerkit ovat siis ekvivalentit: unless (-e $tiedosto) { print "Tiedostoa $tiedosto ei ole olemassa!\n"; print "Ole hyvä, ja luo tiedosto...\n"; print "...tai jätä homma sikseen!\n"; if (!-e $tiedosto) { print "Tiedostoa $tiedosto ei ole olemassa!\n"; print "Ole hyvä, ja luo tiedosto...\n"; print "...tai jätä homma sikseen!\n"; if- ja unless-lauseita näkee usein käytettävän myös eräänlaisina muuntimina, jolloin ehto esiintyy suoritettavan lauseen jäljessä. Tämä on jälleen kerran yksi Perl-kielen tapa lisätä ohjelmakoodiin luettavuutta. $roskat->vie( ulos ) if $meinaat_asua_kotonasi; &vaikene() unless $haluat_minun_lahtevan; Monista ohjelmointikielistä tuttua case-rakennetta, jolla kuvataan useita erilaisia vaihtoehtoja, ei Perl-kielessä ole, mutta sen voi ohittaa paljaalla ohjelmalohkolla ja last-lauseen avulla, esimerkiksi: VIIKONPAIVAT: { $paiva = "maanantai", last VIIKONPAIVAT if /^MO/; $paiva = "tiistai", last VIIKONPAIVAT if /^TU/; $paiva = "keskiviikko", last VIIKONPAIVAT if /^WE/; $paiva = "torstai", last VIIKONPAIVAT if /^TO/; $paiva = "perjantai", last VIIKONPAIVAT if /^FR/; $paiva = "lauantai", last VIIKONPAIVAT if /^SA/; $paiva = "sunnuntai", last VIIKONPAIVAT if /^SU/; $paiva = "hauskantai"; Yllä olevassa koodissa käytetään säännönmukaisia lausekkeita, jotka on merkitty //-merkkien sisään. Niistä kerrotaan lisää seuraavassa luvussa. Perl-kielessä on olemassa toistolauseet while, until, for ja foreach. whilelausetta käytetään, kun jotakin ohjelmakoodin osaa halutaan toistaa niin kauan kuin jokin ehto voimassa. $i = 10; $j = 0; while ($i > $j) { print "$i on suurempi kuin $j\n"; $j++;

257 Ikuinen silmukka saadaan while-lauseella aikaan hyvin helposti: while (1 > 0) { # Tee jotain Kun while-sana korvataan until-sanalla, muuttuu ehdon testaaminen negaatioksi vastaavalla tavalla kuin korvattaessa if-sana unless-sanalla. Yleensä until esiintyy do-lauseen kanssa: $i = 0; $j = 10; do { print "$i on pienempi kuin $j\n"; $i++; # Tällä kertaa kasvatetaan $i-muuttujaa... Miksi? until ($i >= $j); for-silmukka toimii kuten C- tai Java-kielissä, askeltavana toistona, jossa sulkujen sisässä ilmoitetaan alkuarvo, ehto ja lisäys. for (alkuarvo; ehto; lisäys) lausekkeet for-silmukkaa käytetään siis, kun jotakin ohjelmalohkoa halutaan toistaa, ja toiston aloituskohta ja määrä ovat ennalta tiedossa, ja ne voidaan ilmoittaa alkuarvona ja loppuehtona. $laskuriplus = 0; $laskurimiinus = 0; for ($i = 1; $i <= 5; $i++) { $plus = ++$laskuriplus; $miinus = --$laskurimiinus; print Kierros $i: $plus $miinus\n ; Myös for-lauseella saadaan ikuinen silmukka luotua helposti: for (;;) { # Tee jotain Perlissä on olemassa myös foreach-silmukka, jolla voidaan käsitellä listoja. Silmukka toimii siten, että se käy läpi kaikki listan yleensä taulukon alkiot. Esimerkiksi @taulukko-nimisen taulukon alkiot saadaan tulostettua ohjelmakoodilla foreach $alkio (@taulukko) { print $alkio\n ;

258 CGI-ohjelmoinnissa foreach-silmukkaa käytetään yleensä lomakkeeseen syötettyjen tietojen lukemisessa ja käsittelyssä. Sen käyttö taulukoiden yhteydessä on yksinkertaista ja turvallista, koska ohjelmoijan ei tarvitse tietää taulukon alkioiden lukumäärää. Perl-kielessä on lisäksi olemassa hyppylauseita, esimerkiksi next, last ja goto, joista viimeinen on tuttu C-ohjelmoijille. Perlissä goto-lausetta ei tarvitse käyttää oikeastaan koskaan, koska sen sijaan voidaan yleensä käyttää jotain parempaa ratkaisua. Last-komennolla voidaan murtautua ulos silmukasta. Komennolle voidaan antaa osoite, mistä silmukasta siirrytään ulos, mutta jos osoite puuttuu, siirrytään ulos sisimmästä silmukasta. C- ja Java-kielissä on vastaavana komentona break. Aiemmin oli jo esillä murtautuminen ulos case-tyyppisestä rakenteesta: VIIKONPAIVAT: { $paiva = maanantai, last VIIKONPAIVAT if /^MO/; $paiva = tiistai, last VIIKONPAIVAT if /^TU/; $paiva = keskiviikko, last VIIKONPAIVAT if /^WE/; $paiva = torstai, last VIIKONPAIVAT if /^TO/; $paiva = perjantai, last VIIKONPAIVAT if /^FR/; $paiva = lauantai, last VIIKONPAIVAT if /^SA/; $paiva = sunnuntai, last VIIKONPAIVAT if /^SU/; $paiva = hauskantai ; Yllä olevassa koodissa käytetään säännönmukaisia lausekkeita, jotka on merkitty //-merkkien sisään. Niistä kerrotaan lisää seuraavassa luvussa. Perl-kielessä on myös C-kielen silmukkarakenteiden continue-sanaa vastaava komento: next. Next-komennon avulla voidaan hypätä meneillään olevan silmukan kierroksen loppuun ja aloittaa uusi kierros. Myös next-komennolle voidaan antaa osoite, jolla määritellään, minkä silmukan suoritusta ohjaillaan. Jos esimerkiksi halutaan lukea Perl-koodia ja sivuuttaa kaikki rivit, jotka alkavat kommenteilla, voitaisiin tehdä seuraavan kaltainen silmukka: RIVI: while (<STDIN>) { next RIVI if /^#/; # Jos rivi alkaa kommentilla, se ohitetaan print; # Tulostetaan kaikki muut rivit Perl-kielessä on lukuisa määrä muitakin ohjauskomentoja, jotka löytyvät Perlmanuaaleista ja oppaista. Edellä kerrotut rakenteet riittävät yleensä kohtuullisen hyvin täyttämään CGI-ohjelmoijan tarpeet.

12.2.4 Merkkijonojen käsittely 259 Perl-kielen yhteydessä muistetaan aina mainita se, kuinka hyvin kieli soveltuu merkkijonojen käsittelyyn. Kielen kehittäjä Larry Wall on koulutukseltaan kielitieteilijä, ja voi olla että sillä on merkitystä Perlin kehittyneisiin merkkijonorutiineihin. Kielen nimi Perl (Practical Extraction and Report Language) viittaa myös siihen, että kyseessä on käytännöllinen tietojen koonti- ja raportointiväline. Perl-kielessä merkkijonot voidaan kirjoittaa kaksinkertaisten lainausmerkkien sisään, jolloin mukana voi olla myös muuttujia, joiden tilalle Perl-tulkki sijoittaa muuttujia vastaavat arvot: $password = "timantti"; print "Salasana on $password.\n"; # Tulostaa: Salasana on timantti. Kaksinkertaisten lainausmerkkien sisällä voidaan käyttää kenoviivaa pakotettaessa jokin merkki osaksi merkkijonoa. Kenoviivalla on myös joitakin erikoismerkityksiä, esimerkiksi rivinvaihto on \n, tabulaattori \t, sivunvaihto \f ja kappaleenvaihtomerkki \r. print "Emailini on email\@palvelin.fi.\n"; # Tulostaa: Emailini on email@palvelin.fi. Ja rivinvaihdon Merkkijono voidaan kirjoittaa myös yksinkertaisten lainausmerkkien sisään, jolloin siellä ei voida suoraan hyödyntää muuttujien arvoja, koska $- ja @-merkit tulkitaan omiksi merkeikseen, eikä skalaari- tai taulukkomuuttujien alkutunnisteiksi: $osoite = email@palvelin.fi ; $hinta = $100 ; Merkkijonot voidaan lisäksi sulkea vasemmalle puolelle kallellaan olevien heittomerkkien (`) sisään, mutta tällöin merkkijono tulkitaan suoritettavan ulkoisen komennon kutsuksi. Tämä on vain yksi Perl-kielen lukuisista tavoista suorittaa ulkopuolinen ohjelma, mutta siinä mielessä kätevä, että suoritettavan ohjelman tulokset palautuvat Perl-kieliseen ohjelmaan, ja voidaan haluttaessa sijoittaa esimerkiksi johonkin muuttujaan: $cmd = `ls -al`; open(tiedosto, ">>logitiedosto.log"); print TIEDOSTO "Hakemiston sisältö tänään:\n"; print TIEDOSTO "$cmd\n"; close(tiedosto);

260 Itse asiassa Perl-kielessä voidaan lainausmerkit jättää merkkijonojen yhteydessä kokonaan pois, mutta vain sellaisissa tilanteissa, kun merkkijonoilla voi olla ainoastaan yksi tulkinta. Siksi on aina turvallisempaa käyttää lainausmerkkejä. Perl-kielen merkkijonojen vertailuoperaattoreita ovat lt (pienempi kuin), gt (suurempi kuin), le (pienempi tai yhtä suuri kuin), ge (suurempi tai yhtä suuri kuin), eq (yhtä suuri kuin), ne (eri suuri kuin) ja cmp, jolla yhtäsuuruus palautaa arvon 0, vasen operandi suurempi kuin oikea operandi palauttaa arvon 1, ja muussa tapauksessa operaattori palauttaa arvon 1. Muut vertailuoperaattorit palauttavat arvon 1, jos vertailu on tosi ja muussa tapauksessa tyhjän merkkijonon. Yleensä merkkijonojen vertailuoperaattoreita tarvitaan vain yhtäsuuruutta (tai erisuuruutta) vertailtaessa. if ($password ne "salasana") { print "Salasanasi ei ollut salasana vaan $password.\n"; if ($password eq "12345678") { print "Salasanasi on 12345678.\n"; Merkkijonoille on olemassa myös yhteenlasku- ja kertolaskuoperaattorit. Yhteenlasku merkkijonojen yhteydessä tarkoittaa merkkijonojen katenointia eli yhteenliittämistä ja operaattorin merkki on piste (.). Perlin merkkijonojen kertolaskussa ensimmäinen operaattori on merkkijono ja toista käsitellään lukuna. Kertolaskun operaattori on x. $luku1 = 10; $luku2 = 5; print $luku1. $luku2; # Tulostetaan 105 print $luku1 x $luku2; # Tulostetaan 1010101010 Merkkijonojen kertolaskusta on hyötyä, jos jotakin merkkiä halutaan tulostaa useita peräkkäin: $rivin_levys = 60; print "*" x $rivin_leveys; Myös merkkijono-operaattoreiden kanssa voidaan käyttää lyhennelmiä: $merkki = "*"; $monta x= 80; # Sama kuin $monta = $monta x 80 $merkki.= $monta # Sama kuin $merkki = $merkki. $monta Perl-kielen yksi suurista vahvuuksista on kyky käsitellä ns. säännönmukaisia lausekkeita (regular expressions, regexps), joilla tarkoitetaan merkkijonojen joukkoa, joita ei tarvitse erikseen sijoittaa taulukkoon käsittelyä varten. Säännölliset lausekkeet ovat mukana monissa Unix- ja Linux-ohjelmissa ja työkaluissa, joten niiden käyttäjille asia on jo entuudestaan tuttu.

261 Säännönmukaisia lausekkeita voidaan käyttää esimerkiksi tutkittaessa jonkin ehdon toteutumista, korvattaessa merkkijono (tai osajono) toisella merkkijonolla (tai osajonolla) tai pilkottaessa erottimia tiedostojen tietokentistä (tai vaikkapa lomakesyötteestä). Jos esimerkiksi halutaan etsiä HTML-tiedostosta kohta, missä alkaa TEXTAREA-elementti, ja tulostaa sen jälkeen kaikki sitä seuraava teksti, voidaan kirjoittaa: if ($rivi =~ /<TEXT/) { print $rivi; Tällöin hyödynnetään myös Perl-kielen sidontaoperaattoria =~, joka palauttaa totuusarvon true, mikäli riviltä löytyvät merkit <TEXT täsmälleen tässä muodossa ja järjestyksessä (eli yhtenäinen osajono). Muussa tapauksessa operaattori palauttaa arvon false. Mikäli käsitellään jotain (HTML-)tiedostoa, voidaan rivin lukemisessa tiedostosta käyttää myös Perl-kielen oletusmuuttujaa $_ (jota ei tarvitse kirjoittaa näkyviin), jolloin ohjelmakoodin, joka suorittaa edellä kuvatun toiminnon voisi kirjoittaa hyvin lyhyesti (ja niille, jotka eivät tunne Perl-kieltä, täysin käsittämättömään) muotoon: while (<FILE>) { print if /<TEXT/; Perl-kielessä on mahdollista asettaa myös ns. ankkureita, joilla rajoitetaan merkkijonohakujen osumia niiden ympäristön ehtojen mukaan. Esimerkiksi ^- merkillä asetetaan ankkuri, jolla etsintä rajoitetaan kohdistumaan merkkijonon alkuun. Niinpä aiemmin next-komennon yhteydessä esitetty koodinpätkä saa ehkä hiukan selkeämmän merkityksen: RIVI: while (<STDIN>) { next RIVI if /^#/; # Jos rivi alkaa kommentilla, se ohitetaan print; # Tulostetaan kaikki muut rivit Edellä olevassa koodissa tutkitaan, alkaako rivi (oletusmuuttujassa) #-merkillä, joka on Perl-kielen kommenttimerkki. Tutkinta kohdistuu taas oletusmuuttujaan $_, jota ei tarvitse kirjoittaa näkyviin. Jos rivi alkaa #-merkillä suoritetaan nextkomento, joka kehottaa hyppämään kohtaan RIVI, joka taas viittaa whilesilmukan alkuun (ja ehdon testaamiseen). Jos etsintä kohdistuisi $rivi-nimiseen muuttujaan, next-lause voitaisiin kirjoittaa muotoon: next RIVI if $rivi =~/^#/; Merkkijonoja voidaan etsiä m//-etsintäoperaattorilla. Mikäli m-operaattorin erottimina käytetään kauttaviivoja (/), voidaan operaattorista jättää m-kirjain pois(!). Niinpä seuraavat kaksi esimerkkiä ovat täysin identtiset: while (<FILE>) {

262 print if /<TEXT/; # m-kirjainta ei välttämättä tarvita... while (<FILE>) { print if m/<text/ ; #...mutta sitä voi halutessaan käyttää. Merkkijonoja voidaan sijoittaa s///-sijoitusoperaattorilla. Seuraavassa esimerkissä avataan testi.htm-tiedosto lukemista varten. Jos tiedostosta löytyy HTML-muotoilukomentoja, pienempi kuin merkit (<) muutetaan merkkijonoksi < ja suurempi kuin merkit (>) merkkijonoksi >. Nämä myös tulostetaan näytölle, eli tiedostoon itseensä ei tehdä muutoksia. Tämä koodi voi olla hyödyllinen, jos CGI-ohjelmasta haluaa tulostaa HTML-koodia siten, että tagit näkyvät koodina, eivätkä muotoilukomentoina. Muuntimella g etsitään kaikki esiintymät. open(file,"testi.htm"); while (<FILE>) { print if ( (s/</</g) && (s/>/>/g) ); close(file); Muuntimilla voidaan vaikuttaa säännönmukaisen lausekkeen tulkintaan siten, että esimerkiksi muuntimella i etsitään sekä isoja että pieniä kirjaimia, muuntimen m avulla voidaan käsitellä useita rivejä pitkiä merkkijonoja ja muuntimen s avulla merkkijonoa käsitellään kuin se sisältäisi vain yhden rivin. Seuraavalla komennolla korvataan kaikki t-kirjaimet merkkijonolla TEE, i- muunnin siis aiheuttaa sen, että korvataan sekä pienet t- että isot T-kirjaimet, ja g-muuntimen ansiosta etsitään kaikki t- ja T-kirjaimet. Ilman g-muunninta etsintä ja sijoitus tehtäisiin ainoastaan kunkin rivin ensimmäiseen esiintymään. s/t/tee/ig; Joitakin erikoismerkkejä etsittäessä ja sijoitettaessa täytyy käyttää keno-merkkiä (\), jotta merkki osattaisiin tulkita oikein. Esimerkiksi keno-merkki itse on erikoismerkki, joten sitä etsittäessä tai sijoitettaessa tulee käyttää kahta peräkkäistä keno-merkkiä: \\. Lisäksi keno-merkillä voidaan rajoittaa etsintää siten, että esimerkiksi \A pakottaa etsimään säännönmukaista lauseketta merkkijonon alusta ja \Z merkkijonon lopusta. tr///-komennolla saadaan korvattua listan alkiot toisella listalla alkioita. Niinpä esimerkiksi isot kirjaimet A..Z saadaan muutettua pieniksi kirjaimiksi a..z komennolla tr/a-z/a-z/; Mikäli haluttaisiin käsitellä esimerkiksi testi.htm-nimistä tiedostoa, ja korvata siinä kaikki kirjaimet myös skandinaaviset pienillä kirjaimilla, voidaan se tehdä seuraavasti:

263 open(file,"testi.htm"); while (<FILE>) { $_ =~ tr/a-zåäö/a-zåäö/; print $_; close(file); Koodi voidaan tehdä myös lyhyemmin, koska käytetään yhteen riviin viittaavaa oletusmuuttujaa $_, jota ei tarvitse merkitä näkyviin: open(file,"testi.htm"); while (<FILE>) { tr/a-zåäö/a-zåäö/; print; close(file); Perl-kielessä on lisäksi joukko hyödyllisiä funktioita merkkijonojen käsittelyyn, joista osa on Unix-osaajille tuttuja. chop-funktiolla voidaan poistaa merkkijonon viimeinen merkki, käytetään lopussa olevan rivinvaihtomerkin poistamisessa. chomp-funktio toimii muuten samoin, mutta se poistaa ainoastaan rivinvaihtomerkin ja on siinä mielessä turvallisempi käyttää. grep-funktiolla voidaan hakea tiettyä lauseketta tai lohkoa listan alkioista. Ensimmäinen parametri funktiolle on etsittävä merkkijono ja jälkimmäinen lista. Säännönmukaisten lausekkeiden sääntöjä voidaan soveltaa myös grep-funktiolle. Seuraavassa esimerkissä tulostetaan testi.htm-nimisestä tiedostosta kaikki ne rivit, jotka eivät ala pienempi kuin merkillä (<). open(tiedosto,"testi.htm"); while(<tiedosto>) { print unless grep (/^</,$_); close(tiedosto); sort-funktiolla voidaan lajitella lista. Paluuarvona on sama lista, mutta aakkosjärjestykseen lajiteltuna. Seuraava esimerkki lukee tiedoston testi.htm rivit, lajittelee ne aakkosjärjestykseen ja tulostaa näytölle (esimerkissä ei ole järjen hiventäkään vähän älykkäämpi esimerkki on luvun 12.4 lopussa). open(tiedosto,"testi.htm"); $i = 0; while(<tiedosto>) { @taulukko[++$i] = $_; close(tiedosto); print sort @taulukko;

264 reverse-funktio palauttaa taulukon käänteisessä järjestyksessä. Edellinen esimerkki voidaan tehdä jos mahdollista vieläkin älyttömämmäksi ja tulostaa tiedoston rivit käänteisessä aakkosjärjestyksessä korvaamalla viimeinen rivi uudella rivillä: print reverse sort @taulukko; split-funktiota käytetään lähes jokaisessa CGI-ohjelmassa. Se etsii erottimia merkkijonosta ja jakaa merkkijonon osamerkkijonoihin, jotka voidaan tarvittaessa tallentaa muuttujiin. Niinpä esimerkiksi lomaketietojen käsittelyssä voidaan &-merkit poistaa ja lomakekenttien nimi-arvo-parit tallentaa taulukon alkioiksi komennolla: @taulukko = split(/&/,$merkkijono); Tämän jälkeen yleensä nimi-arvo-parit, joiden erottimena on =-merkki, erotellaan toisistaan komennolla: ($tmp1, $tmp2) = split(/=/,$_); Yllä olevassa esimerkissä split-funktiolla erotellaan oletusmuuttujasta $_ nimiarvo-parit toisistaan siten, että lomakekentän nimi tulee $tmp1-muuttujan, ja lomakekentän arvo $tmp2-muuttujan arvoksi. Lisää split-funktion käytöstä kerrotaan luvussa 12.4. 12.3 Dynaamiset dokumentit Luvussa 12.2 luotiin jo ensimmäinen dynaaminen dokumentti. Varsinaista HTML-tiedostoa ei siis ole olemassa kyseisessä esimerkissä, vaan Perl-kielinen CGI-skriptin tulostus on HTML-muotoiltua tekstiä, joka lähetetään selaimelle. Dynaamisesti luodut dokumentit sallivat kuitenkin paljon äskeistä monipuolisempiakin vaihtoehtoja. Seuraavassa luodaan dynaaminen dokumentti, joka tulostaa selainohjelman näytölle päivämäärän. Sivun sisältö vaihtuu siis päivittäin. Esimerkin aluksi määritellään Perl-tulkin paikka. Tämän jälkeen tulostetaan standardivirtaan, ja tulostus ohjautuu palvelinkoneelta käyttäjän selainohjelmaan. #!/usr/bin/perl print "Content-Type: text/html\n\n"; print "<HTML>\n<HEAD>\n"; print "<TITLE>Tänään</TITLE>\n</HEAD>\n\n"; print "<BODY>\n ";

265 print "<BR>\n<CENTER>\n"; Esimerkissä kutsutaan localtime-nimistä Perl-funktiota, jolla on parametrinaan time-funktio. localtime muuntaa parametrin palauttaman arvon listaksi, jonka alkiot voidaan sijoittaa niitä vastaaviin muuttujiin (esimerkissä näitä muuttujia ovat $sec, $min, $hour, $mday, $mon, $year, $wday, $yday ja $isdst). Kuukausien numerointi alkaa nollasta, ja niinpä $mon-muuttujan arvoa tulee kasvattaa yhdellä. ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time); $mon = $mon +1; Tulostuksen muotoilua varten käyteteen sprintf-funktiota, joka toimii tulostamisessa muuten samalla tavalla kuin printf, mutta se palauttaa printfmuotoilun antaman tuloksen, joka voidaan sijoittaa muuttujaan tässä esimerkissä muuttujaan $pvm. Lopuksi tulostetaan HTML-tiedoston loppu. $pvm = sprintf("%02d.%02d.%02d", $mday, $mon, $year); print "Tänään on <B>$pvm"; print "</B>.<BR>\n</CENTER>\n"; print "</BODY>\n"; print "</HTML>\n"; Alla on Perl-kielisen CGI-ohjelman tulostus, joka ohjautuu palvelimen kautta selainohjelman tulkittavaksi. <HTML> <HEAD> <TITLE>Tänään</TITLE> </HEAD> <BODY> <BR> <CENTER> Tänään on <B>19.09.99</B>.<BR> </CENTER> </BODY> </HTML>

266 CGI-ohjelman toteutuminen selainohjelman näytöllä. Yllä olevaa esimerkkiä voisi laajentaa esimerkiksi siten, että luodaan dynaaminen dokumentti, jossa hyödynnetään useissa Unix- ja Linuxjärjestelmissä valmiina olevaa Calendar-ohjelmaa ja varsinkin sen tekstitiedostoja. Seuraavassa tutkitaan näistä Calendar-tiedostoista kaikkiaan kuutta erilaista, ja käyttäjälle tulostetaan ne kalenteritiedot, jotka liittyvät tähän päivään. Näin ollen luotavan www-dokumentin sisältö muuttuu joka päivä. Esimerkin pääohjelma on jo tuttu edellisestä esimerkistä ainoana uutuutena ovat aliohjelmakutsut, joissa parametrina aliohjelmalle TUTKI välitetään kussakin kutsussa eri Calendar-tiedosto. Tähän olisi Perl-kielessä olemassa muitakin ja joidenkin mielestä sofistikoituneempia ratkaisuja, mutta yksinkertaisuuden ja selkeyden vuoksi ratkaisu on esitetty tällä tavalla. Kannattaa huomioida, että aliohjelmakutsun eteen tulee Perl-kielessä laittaa &-merkki. #!/usr/bin/perl print "Content-Type: text/html\n\n"; print "<HTML>\n"; print "<HEAD>\n<TITLE>Tänään tapahtunutta</title>\n</head>\n\n"; print "<BODY BGCOLOR=BLACK TEXT=YELLOW><BR>\n\n"; print "<BR><BR>\n"; ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time); $mon = $mon +1; $vertailu = sprintf("%02d/%02d", $mon, $mday); &TUTKI("/usr/lib/calendar/1998/calendar.christian"); &TUTKI("/usr/lib/calendar/1998/calendar.history"); &TUTKI("/usr/lib/calendar/1998/calendar.holiday"); &TUTKI("/usr/lib/calendar/1998/calendar.birthday"); &TUTKI("/usr/lib/calendar/1998/calendar.music"); &TUTKI("/usr/lib/calendar/1998/calendar.computer"); print "</BODY>\n</HTML>\n"; Aliohjelmassa TUTKI avataan tiedosto Perl-kielen open-funktiolla. Funktion ensimmäinen parametri on ns. tiedostokahva, jolla myöhemmässä

267 ohjelmakoodissa pystytään viittaamaan kyseiseen tiedostoon. Jälkimmäinen parametri on aliohjelmakutsusta välittyvä Calendar-tiedoston nimi. Perl-kielessä aliohjelmalle välittyviin parametreihin viitataan $_[]-taulukkoalkioilla siten, että ensimmäisen parametrin indeksi on 0, toisen 1, jne. sub TUTKI { open(tiedosto,"$_[0]"); while(<tiedosto>) { $in_line = $_; ($paivamaara, $teksti) = split(/\t/,$in_line); if ($vertailu eq $paivamaara) { print "<LI>$teksti\n"; close(tiedosto); Tiedoston sisältö luetaan kokonaan, mikä onnistuu while-toistolauseen avulla, jota toistetaan kunnes kohdataan tiedoston lopetusmerkki. Perl-kielen <>operaattorin (väkäsoperaattori) avulla voidaan lukea rivejä tiedostokahvasta. $in_line-nimiseen muuttujaan tallennetaan kunkin rivin sisältö Perl-kielen oletusmuuttujasta $_. Calendar-tiedostot ovat tekstitiedostoja, jotka on muotoiltu siten, että kullakin rivillä on ensin päivämäärä, sen jälkeen tabulointi, ja sen perässä päivämäärän liittyvä teksti. split-funktiolla etsitään tabulointimerkkiä (joka tunnistetaan merkkijonoissa merkinnällä \t) $in_line-nimisestä muuttujasta (eli kultakin riviltä), ja sijoitetaan päivämäärä $paivamaara-nimiseen muuttujaan ja tabulointimerkin jälkeinen päivämäärään liittyvä teksti $tekstinimiseen muuttujaan. Tämän jälkeen vertaillaan tiedoston rivin päivämäärää tähän päivämäärään (eq-operaattorilla verrataan merkkijonojen yhtäsuuruutta), ja mikäli vertailu on tosi, tulostetaan päivämäärään liittyvä teksti ja HTML-kielen <BR>-komento, joka selainohjelmassa näkyy rivinvaihtona. while-silmukan jälkeen tulee tiedosto vielä muistaa sulkea close-funktiolla. Ohessa on CGIskriptin tuloste, joka ohjautuu palvelinkoneelta selainohjelmalle, ja sen alla sivun toteutuminen selainohjelman näytöllä. <HTML> <HEAD> <TITLE>Tänään tapahtunutta</title> </HEAD> <BODY BGCOLOR=BLACK TEXT=YELLOW><BR> <BR><BR> <LI>First meeting of the National Research Council, 1916 <LI>Magellan leaves Spain on the First Round the World Passage, 1519 <LI>The Roxy Theater opens in Hollywood, 1973 <LI>Upton (Beall) Sinclair born, 1878 <LI>Jim Croce dies in a plane crash, 1973 <LI>Harlan Herrick runs first FORTRAN program, 1954 </BODY> </HTML>

268 Calendar-tiedostoja tutkivan CGI-ohjelman tulosteen toteutuminen selainohjelman näytöllä. Tarkastellaan vielä yhtä esimerkkiä dynaamisesta dokumentista, jossa käytetään verkkoa hyödyntävää pakettia LWP (Library for www access in Perl). Paketti ei välttämättä kuulu normaaliin Perl-jakeluun, mutta sen saa hakea ilmaiseksi osoitteesta www.sn.no/libwww-perl/ tai www.perl.com/cpan/. Kyseinen paketti on kokoelma Perl-moduuleja, jotka muodostavat yksinkertaisen ja helppokäyttöisen rajapinnan www:iin. Tämä onnistuu HTTP-tyylisellä kommunikoinnilla, joka mahdollistaa http-, https-, gopher-, ftp-, news-, file- ja mailto-protokollien käytön paketin palveluiden kautta. Suurin osa paketin moduuleista on oliopohjaisia, mikä helpottaa niiden käyttöä, vaikkei varsinainen pakettia käyttävä ohjelma noudattaisikaan olio-ohjelmoinnin sääntöjä. HTTP-protokolla pohjautuu pyyntö/vastaus-paradigmaan. Tähän perustuu myös LWP:n toiminta. Jokainen LWP-pakettia käyttävä ohjelma luo request-olion, joka vastaa selainohjelman lähettämää pyyntöä palvelimelle. Tämä olio ohjataan palvelinkoneelle, jolta saadaan vastauksena response-olio tutkittavaksi. Response-olio vastaa palvelimen lähettämää vastausta selainohjelmalle. Koska HTTP on tilaton protokolla, aiemmat pyynnöt palvelimelle eivät vaikuta tämän pyynnön käsittelyyn. LWP:n request-olio on ilmentymä luokasta HTTP::Request. Luokan attribuutteja ovat mm. method, jolla määritellään tiedonsiirrossa käytettävä metodi (esim. GET

269 tai POST) ja url, joka on URL-muodossa oleva merkkijono ja vastaa haettavaa dokumenttia. LWP:n response-olio on ilmentymä luokasta HTTP::Response. Luokan contentattribuutti sisältää varsinaisen datan. Pakettiin kuuluvan LWP::UserAgent-luokan ilmentymä tavallaan simuloi selainohjelmaa, eli esittää pyynnön palvelimelle haettavasta dokumentista ja vastaanottaa palvelimen lähettämän datan, joka yleensä on HTML-muotoiltua tekstiä. Tämän luokan tärkein metodi on request, joka palauttaa HTTP::Response-luokan ilmentymän (eli olion), joka sisältää mm. palvelimen lähettämän datan. LWP::UserAgent-luokasta luodaan yleensä yksi ilmentymä ohjelmakoodissa. Sen sijaan HTTP::Request-luokan ilmentymiä voidaan luoda useitakin. Alla olevassa esimerkissä paketista otetaan käyttöön luokka UserAgent ohjelmakoodin alussa heti tulkin määrittämisen jälkeen use-komennolla. #!/usr/bin/perl use LWP::UserAgent; Esimerkkiohjelma käy lukemassa verkon yli Kuopion yliopistoalueen Amicaruokaloiden ruokalistatiedostot osoitteessa www.uku.fi/ulkop/amica, ja tulostaa tämän päivän ruokalistat selainohjelmalle. new-funktio on Perl-kielessä eräänlainen luokan muodostaja, alustusmetodi (tai alustusfunktio), jolla luokasta luodaan ilmentymä eli olio. Tässä esimerkissä luodaan LWP::UserAgent-luokan ilmentymä $ua-nimiseen muuttujaan new-komennolla, jolle välitetään se luokka, josta ilmentymä halutaan tehdä, ja jonka palveluja eli valmiiksi rakennettuja funktioita halutaan hyödyntää. $ua = new LWP::UserAgent; Tämän jälkeen luokan palveluihin voidaan osoittaa $ua-nimisellä muuttujalla oliokielissä puhuttaisiin oliosta ja luokan ominaisuuksiin viittausten luomiseen tarkoitetulla nuolioperaattorilla ->. LWP::UserAgent-luokan agent-nimisellä ominaisuudella määritellään oliolle nimi, jota sovellus itsestään käyttää verkossa. $ua->agent("agentname/0.1"); HTTP::Request-luokasta luodaan $req-niminen ilmentymä, ja sen kahdelle attribuutille määritellään arvot: method-attribuutille siirrossa käytettävä metodi (GET) ja url-attribuutille haettavan tiedoston URL-osoite. Perl-kielen my-

270 määreellä tehdään muuttujasta (tai tässä tapauksessa oliosta) paikallinen, eli se näkyy ohjelmakoodissa kyseisen suorituslohkon loppuun. my $req = new HTTP::Request 'GET','http://www.uku.fi/ulkop/amica/'; $req-olion content-type-nimiselle ominaisuudelle asetetaan arvoksi application/x-www-form-urlencoded, joka kertoo palvelimelle esitettevän pyynnön sisältötyypin. Tyyppi on siis sama, jota käytetään lomakkeiden yhteydessä. $req->content_type('application/x-www-form-urlencoded'); Tämän jälkeen sijoitetaan paikalliseen $res-muuttujaan $ua-olion eli LWP::UserAgent-luokan ilmentymän request-metodin palauttama arvo. Metodille välitetään parametrina $req-olio (eli HTTP::Request-luokan ilmentymä) eli vastauksena saadaan HTTP::Response-luokan olio, joka sisältää mm. haettavan tiedoston datan. my $res = $ua->request($req); Tämän jälkeen tulostetaan normaaliin tapaan HTML-muotoiltua tekstiä tulevan www-dokumentin alkua. print "Content-Type: text/html\n\n"; print "<HTML>\n"; print "<HEAD>\n<TITLE>Ruokalista</TITLE>\n</HEAD>\n\n"; print "<BODY BGCOLOR=#FFFFFF><BR>\n\n<CENTER>"; print "<TABLE BORDER=1 BGCOLOR=#ABCDEF><TR><TD></TD><TD></TD>\n"; print "<TD><B>Snellmania</B></TD><TD><B>Canthia</B></TD>\n"; print "<TD><B>Teknia</B></TD><TD><B>Bioteknia</B></TD></TR>\n"; print "<TR><TD>\n"; Tiedosto avataan Perlin open-funktiolla. Ensimmäinen parametri sille on tiedostokahva, jonka avulla tiedostoa voidaan käsitellä myöhemmässä ohjelmakoodissa, ja jälkimmäinen parametri tiedoston nimi. Esimerkissä viitataan alihakemistossa data olevaan tiedostoon tmp.tmp. Koska tiedoston nimeä edeltää suurempi kuin merkki (>), niin tiedosto avataan kirjoittamista varten, ja mahdollisesti aiemmin luotu samanniminen tiedosto tuhotaan. Jos tiedoston nimeä olisi edeltänyt kaksi suurempi kuin merkkiä (>>), tiedostoon olisi kirjoitettu, mutta vasta olemassa olevan tiedoston perään, jolloin aiemmin tallennettu tieto samassa tiedostossa olisi säilynyt. Tässä esimerkissä tiedostoon tulostetaan $res-olion content-ominaisuus, joka sisältää haetun dokumentin sisällön - tässä tapauksessa HTML-tiedoston, jossa on Amican tämän viikon ruokalista. Tulostamisen jälkeen tiedosto suljetaan close-funktiolla.

271 open(tied, ">./data/tmp.tmp"); print TIED $res->content; close(tied); Päivämäärä luetaan Perlin localtime-funktiolla vastaavalla tavalla kuin aiemmissa esimerkeissä. Tässä esimerkissä tarvitaan ainoastaan tieto viikonpäivästä, joka tallennetaan $wday-nimiseen muuttujaan. ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time); Tämän jälkeen verrataan viikonpäivää $wday-muuttujalla siten, että 1 on maanantai, 2 on tiistai, 3 keskiviikko, jne. Haettu ruokalan HTML-tiedosto on ainakin tähän asti noudattanut samaa formaattia, joten halutun tiedon hakeminen siitä onnistuu esimerkiksi if-vertailulla, jossa etsitään tulostettavan viikonpäivän ruokalistan alku- ja loppurivit. Viikonpäivästä riippuen haetut merkkijonot tallennetaan $paiva- ja $loppu-muuttujiin. if ($wday eq 1) { $paiva = "<TH VALIGN=\"CENTER\">ma</TH>\n"; $loppu = "<!-- TIISTAI--------------------------- -->\n"; elsif ($wday eq 2) { $paiva = "<TH VALIGN=\"CENTER\">ti</TH>\n"; $loppu = "<!-- KESKIVIIKKO--------------------------- -->\n"; elsif ($wday eq 3) { $paiva = "<TH VALIGN=\"CENTER\">ke</TH>\n"; $loppu = "<!-- TORSTAI--------------------------- -->\n"; elsif ($wday eq 4) { $paiva = "<TH VALIGN=\"CENTER\">to</TH>\n"; $loppu = "<!-- PERJANTAI--------------------------- -->\n"; else { $paiva = "<TH VALIGN=\"CENTER\">pe</TH>\n"; $loppu = "</TABLE>\n"; $tulosta-niminen muuttuja alustetaan nollaksi, joka vastaa totuusarvoa false. Aiemmin luotu tiedosto tmp.tmp data-alihakemistossa avataan tällä kertaa ainoastaan lukemista varten, jolloin suurempi kuin -merkit puuttuvat openfunktiolla, jonka ensimmäinen parametri on tiedostokahva, ja toinen parametri avattavan tiedoston nimi. $tulosta = 0; open(tied, "./data/tmp.tmp");

272 Tämän jälkeen tiedoston sisältö luetaan while-toistolauseen avulla kuten aiemmissa esimerkeissä. Mikäli tiedostoon tallennetun HTML-tiedoston rivi vastaa $paiva-muuttujan sisältöä, asetetaan $tulosta-muuttujan arvoksi 1, joka vastaa totuusarvoa true. Jos taas rivi vastaa $loppu-muuttujan sisältöä, $tulostamuuttujalle annetaan arvo 0, false. Silmukan lopuksi tulostetaan rivi, mikäli se kuuluu ruokalistassa tämän päivän tietoihin. Tiedosto suljetaan close-funktiolla. while (<TIED>) { $in_line = $_; if ($in_line eq $paiva) { $tulosta = 1; if ($in_line eq $loppu) { $tulosta = 0; if ($tulosta) { print "$in_line"; close(tied); Tämän jälkeen tulostetaan normaaliin tapaan HTML-muotoiltua tekstiä wwwdokumentin loppu. print "</TABLE>\n"; print "</CENTER>\n</BODY>\n</HTML>\n"; Ylläoleva esimerkki tulostaa seuraavanlaisen HTML-dokumentin osa HTMLkoodista tulee siis toiselta palvelinkoneelta toisesta dokumentista: <HTML><HEAD><TITLE>Ruokalista</TITLE></HEAD> <BODY BGCOLOR=#FFFFFF> <TABLE><TR><TD></TD><TD></TD> <TD><B>Snellmania</B></TD><TD><B>Canthia</B></TD> <TD><B>Teknia</B></TD><TD><B>Bioteknia</B></TD></TR> <TR><TD> <TH VALIGN="CENTER">to</TH> <TD><FONT FACE='Arial,Helvetica' SIZE='2'> Italial.nakkipata <BR>Paist.kalaa <BR>Linssi-kasvispata </TD><TD><FONT FACE='Arial,Helvetica' SIZE='2'> Kalkkunafile <BR>Punajuuri-poropihvi <BR>Lohilaatiikko <BR>Mannapuuro +kiisseli </TD><TD><FONT FACE='Arial,Helvetica' SIZE='2'> Lihapullat <BR>Tandorinbroileri <BR>Pinaattiohukkaat <BR>

273 </TD><TD><FONT FACE='Arial,Helvetica' SIZE='2'> Paist.kirjolohi <BR>Jauhelihakastike <BR>Tom.sipulipaistos </TD> </TABLE> </BODY> </HTML> Erään päivän ruokalistat Kuopion yliopiston campus-alueen Amican ravintoloissa.

274 12.4 Lomakkeiden käsittely Lomakkeiden avulla saadaan www-dokumentteihin luotua lisää vuorovaikutteisuutta. Oikeastaan on sisällöntuottajan ja toteuttajan mielikuvituksesta kiinni, minkälaista vuorovaikutusta halutaan tehdä, ja kuinka sitä hyödynnetään. Käyttäjä voi antaa palautetta kirjoittamalla nimensä, sähköpostiosoitteensa ja palautetekstin www-lomakkeelle. CGI-ohjelma käsittelee lomakkeelle kirjoitetut tiedot, lähettää ne sähköpostitse ylläpitäjälle ja luo käyttäjälle HTML-sivun, jossa kiitetään palautteesta. Lomakesovellukset koostuvat oikeastaan kahdesta osasta: HTML-muotoillusta lomakkeesta, jolla käyttäjä tekee valintoja ja johon hän syöttää tietoja. Toisen osan muodostaa palvelinkoneella oleva CGI-ohjelma, joka käsittelee nämä tiedot.

275 Tyypillisin lomakkeita hyödyntävä CGI-sovellus taitaa olla sivu, jolla kävijä voi antaa palautetta sivun ylläpitäjille. Alla olevassa esimerkissä on tällainen palautesivu ja sen käsittelevä CGI-ohjelma. Ohjelma lukee käyttäjän kirjoittaman nimen, sähköpostin ja palautteen, postittaa tiedot sivun ylläpitäjälle ja luo uuden HTML-sivun, jossa käyttäjää kiitetään palautteen antamisesta. Alla on palautesivun HTML-koodista olennaisin osa: se, jossa luodaan lomake-elementit. Tässä esimerkissä CGI-ohjelma on samalla palvelinkoneella kuin HTMLdokumenttikin, hakemistossa cgi-bin/teens ja on nimeltään omaperäisesti palaute.pl. Lomakkeella on kaksi tekstikenttää (<INPUT TYPE="text">) ja yksi tekstialue (TEXTAREA). Lomaketta luotaessa on jonkin verran hyödynnetty taulukoita. <FORM ACTION="/cgi-bin/Teens/palaute.pl" METHOD=POST> <TABLE BORDER=0> <TR><TD><B> Nimi/nimimerkki: </B></TD> <TD><INPUT TYPE="text" NAME="nimi" SIZE=30></TD></TR> <TR><TD><B> E-mail (jos haluat): </B></TD> <TD><INPUT TYPE="text" NAME="email" SIZE=30></TD></TR> <TR><TD VALIGN=TOP><B> Palautetta: </B></TD> <TD><TEXTAREA NAME="palaute" COLS=50 ROWS=10> </TEXTAREA></TD></TR> </TABLE> <INPUT TYPE=SUBMIT VALUE=" Lähetä "> <INPUT TYPE=RESET VALUE=" Peruuta "> </FORM> Lomake siis ohjautuu CGI-ohjelman (nimeltään palaute.pl) käsiteltäväksi (FORMelementin ACTION-attribuutin arvo). Ohjelman aluksi luetaan standardivirrasta (koska lomakkeen lähetysmetodi oli POST) CONTENT_LENGTH-ympäristömuuttujan ilmoittaman määrän verran tietoa, joka sijoitetaan $mjono-nimiseen muuttujaan: read(stdin, $mjono, $ENV{CONTENT_LENGTH); Stadardivirrassa oleva tieto on pitkä koodattu merkkijono, jonka muoto on kerrottu luvussa 12.1. Tämän jälkeen taulukkomuuttujaan @promptit sijoitetaan $mjono-nimisestä muuttujasta tiedot siten, että split-funktiolla poistetaan kaikki &-merkit, ja kaikki muut tiedot tulevat taulukkomuuttujan alkioiksi: @promptit = split(/&/,$mjono); Perlin foreach-toistolause käy läpi koko taulukon, eikä käyttäjän tarvitse huolehtia siitä, kuinka monta alkiota taulukossa on. Toistosilmukan sisällä splitfunktiolla aluksi erotellaan kustakin nimi-arvo-parista lomakekenttien nimet, jotka sijoitetaan $tmp1-nimiseen muuttujaan ja käyttäjän syöttämät arvot, jotka sijoitetaan $tmp2-nimiseen muuttujaan. Sijoituksen jälkeen käytetään Perl-kielen sidontaoperaattoria =~ kaikkiaan neljä kertaa neljällä eri rivillä, ja sillä määritellään, että seuraava operaatio ei kohdistu Perl-kielen oletusmuuttujaan, vaan tässä tapauksessa $tmp2-muuttujaan, joka on kullakin rivillä sidontaoperaattorin vasemmalla puolella. s/// on merkkijonon

276 sijoitusoperaattori, jota käytetään yhdessä sidontaoperaattorin kanssa. Se etsii tiettyyn säännölliseen lausekkeeseen sopivan merkkijonon, ja mikäli löytää sellaisen, korvaa sen uudella merkkijonolla. Operaattorin syntaksi on muotoa s/lauseke/uusi_lauseke/[muunnin]. Mikäli muunnin saa arvon g, niinkuin tässä esimerkissä, korvauksia voidaan tehdä useampi kuin yksi. Korvaus tehdään siten, että ensimmäisellä korvausrivillä +-merkki (x2b) korvataan välilyönnillä (x20), sen jälkeen korvataan koodautuneet pilkku- ja sulkumerkit ymmärrettävään muotoon. Korvausten jälkeen $tmp2-muuttujan arvo sijoitetaan assosiatiiviseen taulukkoon $fields, jossa sitä vastaa $tmp1-muuttuja. Näin saadaan sidottua lomakkeen nimi-arvo-parit assosiatiiviseen taulukkoon siten, että lomakekentän nimi vastaa indeksiä ja arvo indeksin osoittamaa alkiota. foreach (@promptit) { ($tmp1, $tmp2) = split(/=/,$_); $tmp2 =~ s/\x2b/\x20/g; $tmp2 =~ s/%2c/\x2c/g; $tmp2 =~ s/%28/\x28/g; $tmp2 =~ s/%29/\x29/g; $fields{$tmp1=$tmp2; foreach-silmukan jälkeen kun kaikki nimi-arvo-parit on käsitelty ja sijoitettu assosiatiiviseen taulukkoon lomakekenttien arvot sijoitetaan vielä erillisiin skalaarimuuttujiin. Tämä tehdään pelkästään mukavuussyistä, lyhyt muuttujan nimi on myöhemmässä ohjelmakoodissa helpompi kirjoittaa kuin pitkässä muodossa oleva assosiatiiviseen taulukkoon viittaaminen. Muuttujien nimien ei tässä välttämättä tarvitse olla samoja HTML-lomakkeen kenttien nimien kanssa. $nimi = $fields{'nimi'; $email = $fields{'email'; $palaute = $fields{'palaute'; Seuraavaksi ohjelmakoodissa lähetetään palaute sähköpostitse henkilölle, joka käsittelee palautetekstit. Tämä CGI-ohjelma sijaitsee Linux-palvelinkoneella, jossa on hyvin helppokäyttöinen sendmail-sähköpostiohjelma, jota tämäkin ohjelma hyödyntää. Sendmail-ohjelman sijainti tallennetaan $mailprogmuuttujaan, ja $kenelle-muuttujaan tallennetaan sähköpostiosoite, johon palaute myöhemmin ohjelmakoodissa postitetaan. $mailprog = '/usr/sbin/sendmail'; $kenelle = 'jniskane@cs.uku.fi'; Putkeksi sanotaan I/O-kanavaa, jolla voidaan siirää bittivirtaa prosessista toiseen. Perlin open-funktio avaa putken tiedoston asemasta, jos putkimerkki on funktion toisen parametrin edessä tai jäljessä. Tässä esimerkissä luodaan MAIL-niminen kahva (open-funktion ensimmäinen parametri) putken avulla $mailprog-nimiseen kohteeseen, joka siis viittaa sendmail-ohjelmaan. Ohjelmalle välitetään parametrina myös sähköpostin vastaanottajan osoite $kenelle-muuttujassa. Parametrin putkea seuraava osa tulkitaan siis komennoksi, ja komentoa voidaan

277 ohjailla kahvan avulla tässä esimerkissä kahvaa käytetään bittivirtaan tulostamiseen print-funktion avulla, ja bittivirta ohjautuu sendmail-ohjelmalle, joka postittaa sen haluttuun kohteeseen. Lopuksi täytyy muistaa sulkea putki close-komennon avulla. open (MAIL, " $mailprog $kenelle"); if ($email eq "") { if ($nimi eq "") { print MAIL "From: teens\@ffp.uku.fi\n"; else { print MAIL "From: $nimi\n"; else { print MAIL "From: $email\n"; print MAIL "Subject: TEENS-palautetta\n"; print MAIL "TEENS-palautetta:\n"; print MAIL "------------------------------------------------------\n"; if ($nimi eq "") { print MAIL "Nimetön"; else { print MAIL "$nimi"; print MAIL " kirjoitti ",$pvm,":\n"; print MAIL "$palaute\n"; print MAIL "\n"; print MAIL "------------------------------------------------------\n"; print MAIL "\n"; close (MAIL); Esimerkin lopuksi tulostetaan tuttuun tapaan HTML-muotoiltua tekstiä, jossa käyttäjää kiitetään palautteen antamista. Tämä tulostaminen noudattaa samoja sääntöjä kuin edellisessä luvussa kerrotuissa dynaamisissa dokumenteissa. print "Content-Type: text/html\n\n"; print "<!-- Perl-skriptin generoima tulostesivu -->\n"; print "<!-- jniskane\@cs.uku.fi -->\n\n"; print "<HTML>\n"; print "<HEAD>\n"; print "<TITLE>TEENS: Kiitos palautteesta!</title>\n"; print "</HEAD>\n\n"; print "<BODY BGCOLOR=#FFFFFF TEXT=#000000 LINK=#000000\n"; print " VLINK=#000000>\n\n"; print "<CENTER>\n"; print "<H1>KIITOS PALAUTTEESTASI!</H1>\n</CENTER>\n\n"; print "Kiitos sinulle"; if ($nimi eq "") { print ", nimetön."; else { print ", ",$nimi,"."; print "<HR><BR>\n\n"; print "Viestisi oli:\n",$palaute,"<br CLEAR=ALL><BR>\n\n"; print "<A HREF=\"http://ffp.uku.fi/teens/main.htm\"

278 TARGET=\"_top\">TEENS-sivuille</A>.\n"; print "<BR><BR>\n\n"; print "</BODY>\n"; print "</HTML>\n"; Ohjelma toimii siis siten, että käyttäjä täyttää lomakesivulle nimensä, sähköpostiosoitteensa ja palautteen. Kun hän painaa Lähetä -nappia, lomakkeen tiedot ohjautuvat CGI-ohjelmalle, joka lukee ne, lähettää palautteen haluttuun sähköpostiosoitteeseen, ja lähettää käyttäjän selainohjelmalle www-sivun, jossa kiitetään palautteesta. Periaate kaikissa vuorovaikutteisissa lomakeohjelmissa on sama kuin edellä kuvatussa esimerkissä. Sisällöntuottajien ja ohjelmoijien mielikuvituksesta on kiinni se, mitä kaikkea CGI-ohjelmilla pystytään toteuttamaan. Katsotaan vielä pari erikoisempaa esimerkkiä lomakeohjelmoinnin suomista mahdollisuuksista. Seuraavassa esimerkissä tarkastellaan The Finnish Fitness Plan projektin Kuntoneuvola-sivustolla olevaa Terveyskuntopassi-osiota. Terveyskuntopassin saa täyttämällä ilmoittautumislomakkeen www-sivulla. Käyttäjä valitsee itselleen käyttäjätunnuksen Terveyskuntopassiin, kirjoittaa sen lomakkeelle muiden tietojensa lisäksi ja painaa Lähetä -nappia, jonka jälkeen tiedot ohjautuvat CGIohjelmalla ja sitä kautta Terveyskuntopassin ylläpitäjille vastaavalla tavalla kuin edellä olleessa palautelomake-esimerkissä. Tämän jälkeen käyttäjä saa itselleen oman henkilökohtaisen sivuston, johon kukaan muu ulkopuolinen ei pääse. Näillä sivuilla hän voi syöttää mm. terveystutkimustensa ja kuntomittaustensa tuloksia ja pitää virtuaalista päiväkirjaa, jota kukaan muu ei pääse lukemaan. Tulokset ja muut tiedot tallennetaan tietokantaan, ja käyttäjä voi vertailla tuoreita tuloksiaan aiemmin tallennettuihin. Halutessaan hän voi verrata tuloksiaan myös muiden Terveyskuntopassin ja Kuntoneuvolan käyttäjien tuloksiin kuitenkin niin, ettei hän saa tietää muiden käyttäjien henkilöllisyyttä ainoastaan tuloksia koko aineistosta, johon hänen omaa tulostaan haluttaessa verrataan. Käyttäjä pääsee Terveyskuntopassi-sivustolleen kirjoittamalla käyttäjätunnuksen tekstikenttään sen jälkeen kun on rekisteröitynyt Terveyskuntopassin käyttäjäksi ilmoittautumissivulla. Lomakkeen HTML-koodi on varsin yksinkertainen, ja lomakeosuuskin kovin lyhyt: <FORM METHOD=POST ACTION="/cgi-bin/Passi/passport.pl"> <INPUT TYPE=PASSWORD NAME="salasana" VALUE=""> <INPUT TYPE=SUBMIT VALUE=" * TERVEYSKUNTOPASSI * "> </FORM> Lomakkeen tiedot eli salasana-niminen kenttä arvoineen ohjataan passport.plnimiselle tiedostolle, joka on alihakemistossa /cgi-bin/passi.

279 Kirjoittamalla käyttäjätunnuksensa salasana-kenttään pääsee omalle henkilökohtaiselle Terveyskuntopassi-sivustolleen. Koska passport.pl on kohtuullisen suuri tiedosto ja siinä tehdään paljon sellaisia asioita, jotka ovat olleet esillä jo aiemmissa esimerkeissä, ohjelmasta katsotaan vain pääpiirteet. Koko Terveyskuntopassi-järjestelmässä on 60 CGI-ohjelmaa, joiden avulla järjestelmä toimii. Näistä käsitellään kuitenkin vain joitakin ohjelmia, mutta kaikkien toiminta on rakennettavissa soveltamalla muita esimerkkejä. Kuten yleensäkin lomakeohjelmissa, myös tässä luetaan ensin lomakkeen tiedot, eli tässä tapauksessa käyttäjän kirjoittama käyttäjätunnus. Mikäli käyttäjätunnuskenttä on tyhjä, tulostetaan HTML-sivu, joka on samanlainen kuin staattinen HTML-sivu edellä tällä kertaa CGI-ohjelman dynaamisesti luomana. Muussa tapauksessa avataan Terveyskuntopassin henkilöt sisältävä tiedosto lukemista varten vastaavalla tavalla kuin aiemmin esillä olleessa Calendar-esimerkissä. Jos sitä käyttäjätunnusta, jonka käyttäjä syötti tekstikenttään, ei löydy ollenkaan tiedostosta, ilmoitetaan virheestä ja luodaan sama lomakkeen sisältävä HTMLsivu kuin aiemmin, ja käyttäjä voi uudelleen syöttää käyttäjätunnuksensa. Jos käyttäjätunnus löytyy tiedostosta, $loytyi-nimiselle muuttujalla annetaan arvoksi 1, joka Perl-kielessä vastaa totuusarvoa true. Henkilöstä otetaan talteen sukupuoli ja nimi myöhempää tulostusta varten.

280 if ($password eq $pwd) { $loytyi = 1; $sex = $sukupuoli; $name = $nimi; Tämän jälkeen suljetaan henkilöiden tiedot sisältävä tiedosto ja testataan $loytyi-muuttujan totuusarvo. Mikäli käyttäjätunnus on löytynyt henkilöiden tietokannasta, etsitään kaikki muut häneen liittyvät tiedot kutsumalla ETSI_TIEDOT-nimistä aliohjelmaa, jolle välitetään parametrina $pwd-niminen muuttuja, joka sisältää käyttäjätunnuksen. if ($loytyi) { &ETSI_TIEDOT($pwd); Terveyskuntopassin jokaisesta erilaisesta testistä on olemassa oma tiedostonsa, johon tallennetaan minimissään käyttäjätunnus, päivämäärä ja testin tulos. Näitä tiedostoja on kaikkiaan 30 kappaletta. Lisäksi on olemassa 7 muuta tiedostoa, joihin on laskettu arvio usean yhteenkuuluvan testin tai mittauksen perusteella esim. käyttäjän fyysisen kunnon tilasta. ETSI_TIEDOT-aliohjelmassa avataan nämä 7 koostetiedostoa lukemista varten, ja niiden perusteella käyttäjälle tulostetaan Terveyskuntopassi, jossa naamojen värit ovat riippuvaisia hänen testiensä ja mittaustensa tuloksista. Samalla naamojen kuvat toimivat linkkeinä tai oikeastaan lähetyspainikkeina vastaavalla tavalla kuin <INPUT TYPE="submit"> - uusiin CGI-skripteihin, joissa pääsee syöttämään uusia testi- ja mittaustuloksiaan, tarkastelemaan omia aiempia tuloksiaan tai vertailemaan tuloksiaan muuhun Terveyskuntopassi-aineistoon. Alla olevassa koodissa tulostetaan sivulle uusi lomake, joka johtaa CGIohjelmaan rakenne.pl alihakemistossa /cgi-bin/passi. Aluksi avataan rakenne.dat-niminen tiedosto alihakemistossa data. if-lausessa testataan, löytyykö tiedostosta tämän käyttäjän käyttäjätunnus ja mikäli löytyy, tämän henkilön $vari-muuttujan arvo tallennetaan $uusi_vari-nimiseen muuttujaan. $uusi_vari-muuttuja määrittelee sen, minkälainen väri kuvatiedoston naamalla on. Naaman kuva toimii siis lähetyspainikkeena CGI-ohjelmalle rakenne.pl, ja piilokentässä ohjelmalle välitetään käyttäjätunnus käyttäjän ei siis tarvitse syöttää sitä uudelleen. Kun arvot halutaan mukaan siirryttäessä www-sivulta toiselle, käytetään yleensä piilotettuja kenttiä, jotka luodaan antamalla attribuutille TYPE arvo HIDDEN. Piilotetulla kentällä on nimi ja arvo, samoin kuin muillakin lomakeobjekteilla. Ero on se, että piilotettuja kenttiä ei näytetä käyttäjälle, joten niiden arvoa ei voi muuttaa. Selainohjelma lähettää piilotettujen kenttien nimet ja arvot takaisin yhdessä muiden kenttien kanssa ja näin sovellus voi muistaa edellisen lomakkeen tiedot. VALUE-parametri määrittelee lähetettävän piilotekstin. Piilotetun kentän sisältö on kuitenkin nähtävissä HTML-lähdekoodissa.

281 print "<FORM METHOD=POST ACTION=\"/cgi-bin/Passi/rakenne.pl\">\n"; open(file, "./data/rakenne.dat"); while (<FILE>) { $in_line = $_; ($pwd, $pvm, $vari) = split(/:/,$in_line); if ($pwd eq $_[0]) { $uusi_vari = $vari; close(file); print "<INPUT TYPE=HIDDEN NAME=\"password\" VALUE=\"$_[0]\">\n"; print "<INPUT TYPE=IMAGE SRC=\"$passport_path/$uusi_vari\">\n"; print "</FORM>\n"; Kaikki koostetiedostot on avattu ja kuvien värit sekä naaman muodot määritelty tiedostoista löytyneiden tietojen perusteella. Lokikirjan eli virtuaalisen päiväkirjan kuva näyttää aina iloista naamaa. Kun käyttäjä painaa Rakenne -naaman kuvaa, hän siirtyy rakenne.pl-nimiseen CGI-ohjelmaan. Ohjelman aluksi luetaan edeltävän lomakkeen tiedot eli tässä tapauksessa piilokentässä välittynyt käyttäjätunnus. Lomakkeen lukeminen tehdään samalla tavalla kuin aiemmissakin esimerkeissä. Tämän jälkeen

282 henkilötiedostosta haetaan käyttäjätunnusta vastaava nimi ja sukupuoli vastaavalla tavalla kuin aiemmin. Samoin luetaan koostetiedostosta tämän osion naama-kuvan väri ja ilme. Tämän jälkeen luetaan kolme tiedostoa, joista löytyvät tämän käyttäjän mahdollisesti aiemmin syöttämät tiedot painoindeksi-, vyötärölantio-suhde sekä rasvaprosenttikyselyihin. Tuoreimmat arvot ja niihin liittyvät päivämäärät otetaan talteen omiin muuttujiinsa, jotka on koodin alussa alustettu tyhjiksi. Ohessa esimerkkinä painoindeksin lukeminen tiedostosta: # LUETAAN MAHDOLLISESTI VIIMEKSI SYÖTETYT ARVOT BMI-KYSELYYN: open(tiedosto, "./data/painoindeksi.dat"); while (<TIEDOSTO>) { $in_line = $_; ($tunnus, $pvm, $pituus, $paino, $bmi) = split(/:/,$in_line); if ($tunnus eq $salasana) { $bmi_luokka_viimeksi = $bmi; $bmi_paiva_viimeksi = $pvm; close(tiedosto); Käyttäjän tuoreimman tulokset tulostetaan sivulle. Yläkulmassa oleva naaman kuva on linkki JavaScriptin luomaan uuteen ikkunaan, jossa on koostettuna käyttäjän kaikki aiemmat tulokset. Alaosan kolme kuvaa johtavat uusiin CGIohjelmiin.

283 Tämän jälkeen tulostetaan uusi HTML-sivu vastaavalla tavalla kuin aiemmissa esimerkeissä. Tähän sivuun tulostetaan kaikki käyttäjän tulokset JavaScriptikkunaan, jonka saa halutessaan esiin klikkaamalla sivulla olevaa kuvaa. Varsinaiselle sivulle tulostetaan aiemmin tehdyistä testeistä tuoreimmat tulokset. Lisäksi sivulle luodaan kolme lomaketta, jotka sisältävät piilokentän, jossa on käyttäjätunnus ja kuvasta muodostuvan lähetyspainikkeen. Kukin lomake johtaa omaan CGI-ohjelmaansa, joissa puolestaan luodaan lomakkeet uusien tulosten syöttämistä varten. Jos käyttäjä painaa esimerkiksi Pituus ja Paino kuvaa, hän siirtyy seuraavalle CGI-ohjelman generoimalle sivulle. Ohjelman aluksi luetaan tavalliseen tapaan lomakkeella oleva tieto, joka tässä tapauksessa on piilokentässä välitetty käyttäjätunnus. Tämän jälkeen haetaan koostetiedostosta tieto kuvan väristä ja ilmeestä sekä painoindeksi-tietokannasta tämän käyttäjän aiemmat tulokset. Ohjelma tulostaa uuden HTML-muotoillun sivun normaaliin tapaan. Tälle sivulle sijoitetaan lomake, jolle käyttäjä voi syöttää painoindeksiin liittyvät tiedot. Tietokannasta löytyneet käyttäjän aiemmat tiedot asetetaan lomakekenttiin oletusarvoiksi. print "<CENTER>\n<FORM METHOD=POST\n"; print " ACTION=\"$CGI_path/bmi2.pl\">\n"; print "<TABLE BORDER=5>\n"; print "<INPUT TYPE=HIDDEN NAME=\"password\" VALUE=\"$salasana\">"; print "<TR><TD>Pituus (cm): "; print "<INPUT TYPE=TEXT NAME=\"pituus\" SIZE=5\n"; print " VALUE=\"$pituus_viimeksi\">\n"; print " Paino (kg): "; print "<INPUT TYPE=TEXT NAME=\"paino\" SIZE=5\n"; print " VALUE=\"$paino_viimeksi\">\n"; print "</TD></TR>\n"; print "<TR><TD ALIGN=CENTER>\n"; print "<INPUT TYPE=\"submit\" NAME=\"submit\" VALUE=\" Valmis \">\n"; print "</TD></TR>\n</TABLE>\n</FORM>\n</CENTER>\n<BR>\n<BR>\n"; Kun käyttäjä painaa lomakkeen Valmis -painiketta, hänen lomakekenttiin täyttämät tietonsa ohjautuvat bmi2.pl-nimiselle CGI-sovellukselle. Käyttäjä voi halutessaan myös navigoida Terveyskuntopassin pääsivulle painamalla passin kuvaa, jolloin hänet ohjataan aiemmin esitetylle CGI-ohjelmalle käyttäjätunnus siirtyy ohjelmalle piilokentässä. print "<FORM METHOD=POST ACTION=\"$cgi_path/passport.pl\">\n"; print "<INPUT TYPE=HIDDEN NAME=\"password\" VALUE=\"$salasana\">\n"; print "<INPUT TYPE=IMAGE SRC=\"$img_path/passi.jpg\">\n"; print "</FORM>\n"; Edellä olevissa esimerkeissä määritellyt skalaarimuuttujat $cgi_path ja $img_path ovat ohjelman alussa alustetut osoittamaan tiettyihin URL-osoitteisiin. Tämä on koettu hyväksi tavaksi, jos CGI-sovelluksen domain-nimi muuttuu tai sitä halutaan käyttää muussa yhteydessä. Tällöin riittää muuttaa ainoastaan CGI-

284 sovellusten ja kuvatiedostojen osoitteisiin muuttunut polku yhteen kohtaan ohjelman alkuun eikä moneen eri kohtaan sovelluskoodiin. Käyttäjä on saapunut painoindeksin tietojen syöttöön tarkoitetulle sivulle. Lomakkeessa on oletusarvoisesti hänen aiemmat saman testin tuloksensa. Käyttäjän syötettyä tietonsa lomakekenttiin ja painettua Valmis -painiketta siirrytään siis CGI-ohjelmaan bmi2.pl, jonka aluksi taas luetaan tuttuun tapaan lomakekenttiin täytetyt tiedot sekä piilokentässä oleva käyttäjätunnus. Pituus- ja paino-kenttiin syötettyjen arvojen perusteella pystytään laskemaan käyttäjän painoindeksi. Kannattaa huomata, että esimerkiksi painoindeksi lasketaan jakamalla paino (kg) pituuden (m) neliöllä, jolloin ohjelmassa tulee varmistaa, ettei pituus ole saanut arvoa 0, koska sillä jakaminen on kiellettyä ja saattaa keskeyttää ohjelman suorituksen. Henkilön nimi-, ikä- ja sukupuolitiedot haetaan tiedostosta, jos hänen arvojaan halutaan vertailla saman ikäisiin saman sukupuolen edustajiin. Tämän jälkeen mikäli syötetyissä arvoissa ei ole virheitä käyttäjän tiedot viedään painoindeksien tiedostoon yhdessä käyttäjätunnuksen kanssa, jolloin tiedot ovat jälleen myöhemmin yhdistettävissä tähän samaan käyttäjään. Tiedosto avataan kirjoittamista varten siten, että uudet tiedot lisätään olemassa olevan tiedoston

285 loppuun, jolloin tiedostoa avattaessa sen nimen eteen liitetään kaksi suurempi kuin merkkiä (>>). open(tiedosto,">>./data/painoindeksi.dat"); print TIEDOSTO $password, ":"; print TIEDOSTO $pvm, ":"; print TIEDOSTO $pituus, ":"; print TIEDOSTO $paino, ":"; print TIEDOSTO $bmi, ":"; print TIEDOSTO "\n"; close (TIEDOSTO); Tämän jälkeen luodaan tavalliseen tapaan uusi HTML-sivu. Sivulle tulostetaan käyttäjän painoindeksi, ja lasketaan kaikkien painoindeksitiedostossa olevien henkilöiden keskiarvo, joka myös tulostetaan näytölle. Käyttäjän painoindeksi on tulostettu selainohjelmalle yhdessä kaikkien käyttäjien keskiarvon kanssa. Samalla kun kaikkien painoindeksien keskiarvoa on laskettu, ohjelma on myös luokitellut painoindeksiaineiston ja piirtänyt niistä valmiin pylväsdiagrammin. Yksinkertaisen pylväsdiagrammin tekeminen aineistosta onnistuu yllättävän helposti ilman että tarvitsee edes käyttää Perlille vapaasti saatavissa olevia grafiikkakirjastoja. Mikäli haluttaisiin tehdä monimutkaisempaa dynaamista grafiikkaa, joka päivittyy tietokannan tietojen perusteella, sekin onnistuisi esim. GD-kirjaston avulla kohtuullisen pienellä vaivalla. Tässä esimerkissä on kuitenkin tehty vieläkin helpompi ratkaisu. Pylväsdiagrammi muodostuu oikeastaan yhdestä kuvasta: sinisestä neliöstä, jonka korkeus on 10 ja leveys niinikään 10 pikseliä. Kun tiedostosta on haettu tietoa, on kunkin luokkaan kuuluvan painoindeksin frekvenssi lisätty luokan frekvenssiin. Pylvään leveydeksi on määritelty 40, mutta pylvään korkeudeksi tulee kunkin luokan frekvenssi. Tosin tässä esimerkissä aineisto on niin suuri (yli 13000 painoindeksin laskijaa), että kuvan korkeutta on jouduttu skaalaamaan jakamalla se 8:lla. Näin yksittäisen

286 pylvään korkeus ei pääse ylittämään 3500 pikseliä, minkä se tekisi ilman skaalausta. Pylväsdiagrammin saa esille painamalla sivulla olevaa naaman kuvaa. print "<IMG BORDER=0 WIDTH=40 HEIGHT=$l1\n"; print " SRC=\"$img_path/taso.jpg\">\n"; Terveyskuntopassin käyttäjien syöttämistä painoindeksitiedoista saadaan helposti luotua pylväsdiagrammi. CGI-lomakesovelluksilla saa siis luotua monimutkaisiakin rakennelmia mikäli sisällöntuottajalla riittää vain intoa ja mielikuvitusta uusien asioiden keksimiseen. Toteuttaminen onnistuu yleensä käyttäen tässä luvussa esille tulleita asioita hyvin harvoin joudutaan tätä monimutkaisempien asioiden kanssa tekemisiin.

287 Katsotaan kuitenkin vielä lyhyesti yksi hyödyllinen esimerkki siitä, mitä lomakkeiden avulla voi tehdä nopeasti ja melko vaivattomasti. Kyseessä on editori, jonka avulla voi tehdä WAP-sovelluksia, eli kirjoittaa WML- ja WMLSCRIPT-tiedostoja. Kyseinen editori on toteutettu alunperin HTMLeditorina. Se on tehty tilanteeseen, jossa www-sivut ovat Unix-palvelinkoneella ja niiden päivittäminen ja ylläpitäminen Telnet-yhteyttä ja Unixin työkaluja käyttäen voi olla sisällöntuottajalle joka ei välttämättä osaa eikä tahdokaan osata Unix-komentoja vaikeaa tai jopa ylitsepääsemätöntä. Sen sijaan tiedostojen editointi onnistuu helposti, jos niitä varten on luotu www-selaimessa toimiva editori. Tällöin suuri osa käyttöjärjestelmän toiminnoista (esim. Copy ja Paste) ovat valmiiksi käytettävissä ja yleensä käyttäjälle tutumpia kuin Unixkomennot. WAP-editori on tehty vastaavalla tavalla vastaavaan tarpeeseen. WAP-editorin etusivulle kirjoitetaan salasana ja valitaan tiedosto, jota halutaan editoida. Editorin periaate on hyvin yksinkertainen. Käyttäjä kirjoittaa salasana-kenttään salasanan ja valitsee tiedoston, jota hän haluaa editoida. Ohjelma lukee käyttäjän kirjoittaman salasanan ja tarkastaa ympäristömuuttujasta REMOTE_ADDR, että IPosoite on sen henkilön, jolle salasana on myönnetty. Mikäli nämä ovat oikein, luodaan uusi HTML-sivu.

288 Editoriin on haluttu luoda joitakin kehittyneen tekstieditorin piirteitä. Osa niistä tulee valmiiksi käyttöjärjestelmän toimintojen mukana (esim. Leikepöydän toiminnot), mutta osa taas ei ole käytettävissä <TEXTAREA>lomakekomponenteissa. Esimerkiksi Hae- ja Korvaa-toiminnot eivät ole käytettävissä, mutta niitä varten CGI-ohjelma tulostaa JavaScript-koodinpätkän, jonka avulla tekstiin voidaan tehdä korvauksia. print "<SCRIPT LANGUAGE=\"JavaScript\">\n"; print "<!--\n\n"; print "function korvaus(hakusana, korvaava) {\n"; print " var kaikki = document.lomake.kaikki[0].checked;\n"; print " var teksti = document.lomake.teksti.value;\n"; print " this.hakusana = hakusana;\n"; print " this.korvaava = korvaava;\n"; print " var found = teksti.indexof(hakusana);\n"; print " if (found!= -1) {\n"; print " var alkuteksti = new String(teksti.substring(0,found));\n"; print " var lopputeksti = new String(teksti.substring((found+hakusana.length),teksti.length));\n"; print " document.lomake.teksti.value = alkuteksti + korvaava + lopputeksti;\n"; print " if (kaikki) {\n"; print " korvaus(hakusana, korvaava);\n"; print " \n"; print " \n"; print "\n"; print "\n"; print "//-->\n"; print "</SCRIPT>\n"; Edellä on siis CGI-ohjelman tulostamaa JavaScript-koodia. Niiden lisäksi tarvitaan tekstikentät, joihin käyttäjä syöttää korvattavat ja korvaavat sanat sekä painike, joka käynnistää sanojen korvauksen eli kutsuu JavaScript-funktiota. Lisäksi luodaan kaksi valintapainiketta, joista käyttäjä valitsee, korvaako hän yhden sanan vai kaikki tietyt sanat. Nämäkin luonnollisesti tulostetaan CGIohjelmasta. print "<TABLE BORDER=1 BGCOLOR=\"#ABCDEF\">\n"; print "<TH COLSPAN=2>FIND / REPLACE</TH>\n"; print "<TR><TD>Korvattava sana:</td>\n"; print "<TD><INPUT TYPE=TEXT SIZE=20 NAME=\"hakusana\" \n"; print "VALUE=\"$hakusana\"></TD></TR>\n"; print "<TR><TD>Korvaava sana:</td>\n"; print "<TD><INPUT TYPE=TEXT SIZE=20 NAME=\"korvaava\" \n"; print "VALUE=\"$korvaava\"></TD></TR>\n"; print "<TR><TD><INPUT TYPE=RADIO NAME=\"kaikki\" VALUE=\"yes\"> \n"; print "Korvaa kaikki</td>\n"; print " <TD><INPUT TYPE=RADIO NAME=\"kaikki\" VALUE=\"no\"> \n"; print "Korvaa eka</td></tr>\n"; print "<TR><TD ALIGN=CENTER COLSPAN=2><INPUT TYPE=BUTTON \n"; print "NAME=\"nappi\" VALUE=\" * Korvaa * \" print "onclick=\"korvaus(hakusana.value, korvaava.value)\">\n"; print "</TD></TR>\n"; print "</TABLE>\n";

289 HTML-sivulle luodaan myös lomake, jossa on yksi <TEXTAREA>-komponentti. Komponentin sisälle tulostetaan editoitavan tiedoston sisältö. Se onnistuu Perlkielessä muutamalla koodirivillä: open(f,$file); while (<F>) { print $_; close (F); Tämän jälkeen tiedosto on valmis käyttäjän editoitavaksi <TEXTAREA>komponentissa. WAP-editorissa tiedostoa voi muokkailla Textarea-komponentin sisällä. Kun käyttäjä painaa Tallenna muutokset nappia, se ohjaa tähän samaan CGIohjelmaan, jossa hyvin yksinkertaisella tavalla tutkitaan, pitääkö tiedosto tallentaa: Jos TEXTAREA-elementin teksti on tyhjä, ei tallenneta, muussa tapauksessa TEXTAREA-elementissä on tekstiä, jotka tallennetaan siihen tiedostoon,