Index

Bestanden uploaden

Veronderstel dat je een forum opgestart hebt (met al de uitleg dat je tot hiertoe gekregen hebt kan dat niet moeilijk zijn...) en mensen de mogelijkheid wilt bieden foto's te uploaden.

Dit is de minimale HTML-kode dat nodig is om een bestand te uploaden:


<form method="post" enctype="multipart/form-data">
   <input type="hidden" value="abc" name="record">
   <input type="file" name="123.dat"><br>
   <textarea name="text-1" rows=8 cols=60>Enter description here</textarea><br>
   <input type="submit" value="send file">
</form>

In de praktijk ziet de formulier er dan zo uit:



De enctype="multipart/form-data" is belangrijk: het zegt hoe de verschillende velden naar de server gestuurd moeten worden. Het formaat is vergelijkbaar hetgeen gebruikt wordt bij het versturen van bijlagen via mail.

Bij het programmeren mag je er niet van uitgaan dat de elementen doorgestuurd zullen worden in de volgorde waarop ze op de html-pagina voorkomen. Ook is het mogelijk dat er extra elementen of te weinig elementen doorgestuurd worden. Het is belangrijk dat je script niet vastloopt als het continu probeert gegevens in te lezen en te verwerken, wachtend op een bepaald element. Powerbasic genereert geen foutconditie als je voorbij het einde van een bestand probeert te lezen. Het beste is te controleren op een lege input.

De kode dat het formulier verwerk ziet er zo uit:
DEFWRD a-z
FUNCTION chop$(a$, b$)                                 ' Met de chop-funktie knippen we een string in tween:
    chop$ = EXTRACT$(a$, b$)                           ' het deel voor de scheidingsstring
    a$ = REMAIN$(a$, b$)                               ' en het deel na de scheidingsstring.
END FUNCTION
FUNCTION knip$(a$, b$)                                 ' Met de knip-funktie halen we een veld uit een string
    knip$ = EXTRACT$(REMAIN$(a$, b$ + "=" + $DQ), $DQ)
END FUNCTION

FUNCTION PBMAIN()
    OPEN ENVIRON$("CGI_STDOUT") FOR OUTPUT AS #3
    OPEN ENVIRON$("CGI_STDIN") FOR BINARY AS #1: GET$ #1, LOF(1), a$: CLOSE #1
    bound$ = chop$(a$, $CRLF)                               ' De eerste regel is de boundary tussen de verschillende elementen.
    PRINT #3, "<html><body><pre>Boundary: " bound$          ' In dit voorbeeldprogramma printen we de boundary ook uit.			

    multi$ = chop$(a$, $CRLF + bound$ + $CRLF)              ' multi$ bevat de verschillende elementen die doorgestuurd werden.
    WHILE LEN(multi$)
        filen$ = ""
        naam$ = ""
        PRINT #3, "<font color=red>"                        ' We scannen eerst de headers van ieder element.
        lijn$ = chop$(multi$, $CRLF)                        ' lijn$ bevat iedere keer n regel.
        WHILE LEN(lijn$)
            PRINT #3, "<li>" lijn$                          ' In dit demo-programma printen we iedere regel van de header uit in het rood
            b$ = knip$(lijn$, " filename")
            IF LEN(b$) THEN                                 ' We zijn vooral op zoek naar een veld dat "filename" heet:
                filen$ = LCASE$(b$)                         ' dit is het bestand dat ons doorgestuurd wordt.
            END IF
            b$ = knip$(lijn$, " name")
            IF LEN(b$) THEN                                 ' Met het veld "name" kunnen we iedere inputveld verwerken 
                naam$ = LCASE$(b$)
            END IF
            lijn$ = chop$(multi$, $CRLF)
        WEND                                                ' Als we een lege regel tegenkomen, dan zijn alle headers ingelezen
        PRINT #3, "</font>
        i = INSTR(-1, filen$, "\")                          ' Nu worden de headers verwerkt: eerst het bestandsnaam wordt gecontroleerd.
        IF i THEN
            filen$ = MID$(filen$, i + 1)                    ' De volledige path van het bestand (bij de client)
        END IF                                              ' wordt verwijderd —die hebben we eigenlijk niet nodig

        filen$ = RETAIN$(filen$, ANY ".-_abcdefghijklmnopqrstuvwxyz0123456789")
        PRINT #3, "Filename: " filen$                       ' In ons demo-programma printen we de velden uit
        PRINT #3, "Name: " naam$
        PRINT #3, "Length: " LEN(multi$)                    ' De rest van het element (dus als alle headers ingelezen zijn) 
        IF LEN(filen$) THEN                                 ' bevat nu uitsluitend nog de data.
            OPEN "d:\xitami\webpages\uploads\" + filen$ FOR OUTPUT AS #1: PRINT #1, multi$;: CLOSE #1
        ELSE                                                ' Als er een filename aanwezig is, dan is het element een bestand
            PRINT #3, "<font color=blue>" multi$ "</font>"  ' dat opgeslagen moet worden, anders is het element een inputveld
        END IF                                              ' dat hier in het blauw uitgeprint wordt.                         
        PRINT #3, "<hr>"                              
        multi$ = MID$(chop$(a$, $CRLF + bound$), 3)         ' Hier knippen we een volgend element uit de stream.
    WEND                                                    ' Als er geen data meer zit in de elementen, dan sluiten we af.
    PRINT #3, "</body></html>
    CLOSE
END FUNCTION

De kode dat hier getoond wordt bevat demonstratiekode (het uitprinten van de velden), dat niet aanwezig zal zijn in een normale applikatie. Bestanden met eenzelfde naam wissen de oude bestanden, dus is het aangeraden bij een echte toepassing een eigen naam te genereren, en niet de naam gebruiken dat door de client gegeven is.

Zorg er wel voor dat de extention (.GIF, .JPG, .TXT,...) bewaard blijft, want in dit voorbeeld gebruiken we de Content-Type niet (we gaan ervan uit dat de client een windows-machine is dat extensies gebruikt). Eigenlijk is de Content-Type wel belangrijk want niet alle clients gebruiken extensies (sommige mensen die enkel in de windows-wereld opgegroeid zijn vergeten dit maar al te gemakkelijk!). Een Content-Type bepaalt ondubbelzinnig met welk soort bestand we te maken hebben.

Om een perfekte bestandsnaam aan te maken gebruiken we een unieke bestandsnaam, waarvan we de extensie zelf selecteren aan de hand van de Content-Type:

  • .gif voor image/gif
  • .jpg voor image/jpeg
  • .txt voor text/plain
  • .htm voor text/html
  • .png voor image/png
  • .pdf voor application/pdf
    Je kan de bestaande Content-Types terugvinden in het bestand xitami.cfg in de root directory van de server

    ascii art

    Server hacked!

    Een slim gastje had het niet beter gevonden de uploadfile index.html te noemen. Omdat een volgende 'hacker' ook zijn upload-bestand index.html gaat noemen, heb ik het bestand gesaved in een beveiligde lokatie: gesavede hack. 'k Vond het eigenlijk niet mis, en het heeft wel 3.5 seconden geduurd vooraleer ik wist wat mij overkwam. Mijn server gehakt! Mijn hart sloeg 3.5 keer over.

    Een andere hacker heeft een PHP bestand ge-upload in de hoop dat er PHP op mijn server draait: zo kan hij commandos op mijn server uitvoeren. Gelukkig draait er geen PHP op de server (PHP is nagenoeg onmogelijk om te beveiligen) en is zijn PHP bestand waardeloos. maar het bevat wel mooie ASCII art (ah waar is de tijd van de bulletin boards!), kijk naar een bestand met php-extensie in de upload directory.

    Index

    Individuele landingspage bezoekers: