"cgi" --- wsparcie dla wspólnego sprzęgu bramki - z ang. - Common Gateway Interface
***********************************************************************************

**Source code:** Lib/cgi.py

======================================================================

Wspierający moduł dla skryptów sprzęgu wspólnej bramki (z ang. CGI).

Ten moduł definiuje pewną liczbę użyteczności dla użycia przez skrypty
CGI napisane w języku pytonowskim.


Wprowadzenie
============

Skrypt CGI jest wywoływany przez serwer HTTP, zwykle aby przetworzyć
wejście użytkownika dostarczone przez HTML "<FORM>" lub element
"<ISINDEX>".

Najczęściej, Skrypty CGI przebywają w katalogu specjalnym serwera
"cgi-bin". Serwer HTTP umieszcza wszelkiego rodzaju informacje o
zapytaniu (takie jak nazwa hosta klienta, oczekiwany adres URL, ciąg
zapytania, i wiele innych dobrych rzeczy) w środowisku powłoki
skryptu, wykonuje skrypt i wysyła wyjście skryptu z powrotem do
klienta.

Wejście skryptu jest połączone z klientem także, i czasami dane
formularza są wczytywane tą drogą także; innym razem dane formularza
są przekazywane przez "ciąg zapytania" jako część adresu URL. Ten
moduł jest przeznaczony do zajmowania się różnymi przypadkami i
dostarczenia prostszego sprzęgu dla skryptu języka pytonowskiego. To
także dostarcza pewną liczbę użyteczności które pomagają w
odpluskwianiu skryptów zaś najnowszym dodatkiem jest wsparcie dla
załadowywania plików przez formularz (jeśli twoja przeglądarka to
wspomaga).

Wyjście skryptu CGI powinno składać się z dwóch sekcji, oddzielonych
pustą linią. Pierwszy rozdział zawiera liczbę nagłówków, mówiąc
klientowi jakiego rodzaju dane nastąpią potem. Kod języka
pytonowskiego aby wytworzyć minimalny rozdział nagłówkowy wygląda
następująco:

   print("Content-Type: text/html")    # HTML is following
   print()                             # blank line, end of headers

Drugi rozdziałem jest zwykle HTML, który pozwala oprogramowaniu
klienta wyświetlić ładnie sformatowany tekst z nagłówkiem, obrazkami w
-jednej-linii itp. Tu jest kod języka pytonowskiego który drukuje
prosty kawałek HTML-a:

   print("<TITLE>CGI script output</TITLE>")
   print("<H1>This is my first CGI script</H1>")
   print("Hello, world!")


Użycie modułu cgi
=================

Zacznij przez napisanie "import cgi".

Gdy piszesz nowy skrypt, rozważ dodanie tych linii:

   import cgitb
   cgitb.enable()

To aktywuje specjalną obsługę błędów, która będzie wyświetlać
szczegółowe raporty o błędach w przeglądarce sieciowej jeśli nastąpią
jakiekolwiek błędy. Jeśli wolisz raczej nie pokazywać wnętrzności
swojego programu dla użytkowników twojego skryptu możesz mieć raporty
zapisane do pliku zamiast tego, za pomocą kodu takiego jak ten:

   import cgitb
   cgitb.enable(display=0, logdir="/path/to/logdir")

Jest bardzo przydatnym użycie tej właściwości w czasie rozwijania
skryptu. Raporty produkowane przez "cgitb" dostarczają informację
która może oszczędzić Ci wiele czasu w śledzeniu błędów. Możesz zawsze
usunąć "cgitb" linię później gdy przetestowałeś swój skrypt i jesteś
pewien że działa on prawidłowo.

To get at submitted form data, use the "FieldStorage" class. If the
form contains non-ASCII characters, use the *encoding* keyword
parameter set to the value of the encoding defined for the document.
It is usually contained in the META tag in the HEAD section of the
HTML document or by the *Content-Type* header).  This reads the form
contents from the standard input or the environment (depending on the
value of various environment variables set according to the CGI
standard).  Since it may consume standard input, it should be
instantiated only once.

The "FieldStorage" instance can be indexed like a Python dictionary.
It allows membership testing with the "in" operator, and also supports
the standard dictionary method "keys()" and the built-in function
"len()".  Form fields containing empty strings are ignored and do not
appear in the dictionary; to keep such values, provide a true value
for the optional *keep_blank_values* keyword parameter when creating
the "FieldStorage" instance.

Dla przykładu, następujący kod (który zakłada, że nagłówek *Content-
Type* i puste linie zostały już wydrukowane) sprawdza czy pola "nazwa"
i "addr" zostały oba ustawione na nie-pusty ciąg znaków:

   form = cgi.FieldStorage()
   if "name" not in form or "addr" not in form:
       print("<H1>Error</H1>")
       print("Please fill in the name and addr fields.")
       return
   print("<p>name:", form["name"].value)
   print("<p>addr:", form["addr"].value)
   ...further form processing here...

Here the fields, accessed through "form[key]", are themselves
instances of "FieldStorage" (or "MiniFieldStorage", depending on the
form encoding). The "value" attribute of the instance yields the
string value of the field.  The "getvalue()" method returns this
string value directly; it also accepts an optional second argument as
a default to return if the requested key is not present.

If the submitted form data contains more than one field with the same
name, the object retrieved by "form[key]" is not a "FieldStorage" or
"MiniFieldStorage" instance but a list of such instances.  Similarly,
in this situation, "form.getvalue(key)" would return a list of
strings. If you expect this possibility (when your HTML form contains
multiple fields with the same name), use the "getlist()" method, which
always returns a list of values (so that you do not need to special-
case the single item case).  For example, this code concatenates any
number of username fields, separated by commas:

   value = form.getlist("username")
   usernames = ",".join(value)

If a field represents an uploaded file, accessing the value via the
"value" attribute or the "getvalue()" method reads the entire file in
memory as bytes.  This may not be what you want.  You can test for an
uploaded file by testing either the "filename" attribute or the "file"
attribute.  You can then read the data from the "file" attribute
before it is automatically closed as part of the garbage collection of
the "FieldStorage" instance (the "read()" and "readline()" methods
will return bytes):

   fileitem = form["userfile"]
   if fileitem.file:
       # It's an uploaded file; count lines
       linecount = 0
       while True:
           line = fileitem.file.readline()
           if not line: break
           linecount = linecount + 1

"FieldStorage" objects also support being used in a "with" statement,
which will automatically close them when done.

If an error is encountered when obtaining the contents of an uploaded
file (for example, when the user interrupts the form submission by
clicking on a Back or Cancel button) the "done" attribute of the
object for the field will be set to the value -1.

Standard ładowania pliku przewiduje możliwość załadowania wielu plików
z jednego pola (używając rekursywnego kodowania *multipart/**). Gdy to
się zdarzy, element będzie słowniko-podobnym elementem "FieldStorage".
To może być określone przez sprawdzenie właściwości "type", która
powinna być *multipart/form-data* (lub byćmoże inny typ MIME pasujący
do *multipart/**). W tym przypadku może być on iterowany rekursywnie
tak, jak przedmiot formularza nadrzędnego poziomu.

Gdy formularz jest podawany w "starym" formacie (jako ciąg zapytania
lub pojedyncza część danych typu *application/x-www-form-urlencoded*),
wszystkie elementy właściwie będą przykładami uogólnienia
"MiniFieldStorage". W tym przypadku, właściwości "list", "file", i
"filename" są zawsze "None".

Formularz podany przez sposób postępowania POST który także zawiera
ciąg zapytania będzie zawierał zarówno elementy "FieldStorage" jak i
"MiniFieldStorage".

Zmienione w wersji 3.4: The "file" attribute is automatically closed
upon the garbage collection of the creating "FieldStorage" instance.

Zmienione w wersji 3.5: Added support for the context management
protocol to the "FieldStorage" class.


Sprzęg Wyższego Poziomu (Higher Level Interface)
================================================

Poprzednia sekcja wyjaśnia jak czytać dane z formularza CGI używając
uogólnienia "FieldStorage". Ten rozdział opisuje sprzęg wyższego
rzędu, który został dodany do tego uogólnienia aby pozwolić robić to w
bardziej czytelny i intuicyjny sposób. Sprzęg nie powoduje że techniki
opisane w poprzednich rozdziałach stają się zbędne --- one są wciąż
użyteczne aby wykonywać ładowanie plików efektywnie, dla przykładu.

Sprzęg składa się z dwóch prostych sposobów postępowania. Używając
sposobów postępowania możesz przetwarzać dane w zastępczy sposób bez
konieczności martwienia się czy tylko jedna czy więcej danych zostało
opublikowanych pod daną nazwą.

W poprzednim rozdziale, dowiedziałeś się jak pisać następujący kod za
każdym razem gdy spodziewałeś się że użytkownik opublikuje więcej niż
jedną wartość pod jedną nazwą:

   item = form.getvalue("item")
   if isinstance(item, list):
       # The user is requesting more than one item.
   else:
       # The user is requesting only one item.

Ta sytuacja jest obecna dla przykładu gdy formularz zawiera grupę
wielu pól zaznaczania o tej samej nazwie:

   <input type="checkbox" name="item" value="1" />
   <input type="checkbox" name="item" value="2" />

W większości przypadków, jednakże, istnieje tylko jedna urządzenie
formularza o określonej nazwie w formularzu i wtedy oczekujesz i
potrzebujesz tylko jednej wartości powiązanej z tą nazwą. Więc piszesz
skrypt zawierający dla przykładu ten kod:

   user = form.getvalue("user").upper()

Problem z kodem polega na tym, że nigdy nie powinieneś oczekiwać, że
klient dostarczy właściwe wpisy do twoich skryptów. Dla przykładu,
jeśli ciekawy użytkownik dołoży jeszcze jedną parę "user=foo" do ciągu
zapytania, wtedy skrypt ulegnie wypadkowi, ponieważ w tej sytuacji
wywołanie sposobu postępowania "getvalue("user")" zwróci listę zamiast
ciągu znaków. Wywołanie sposobu postępowania "upper()" na liście nie
jest prawidłowe (gdyż listy nie mają sposobów postępowania o tej
nazwie) i kończy się wyjątkiem "AttributeError".

Zatem, właściwym sposobem aby wczytywać dane formularza było zawsze
używanie kodu który sprawdza czy otrzymana wartość jest pojedynczą
wartością czy listą wartości. To jest irytujące i prowadzi do mniej
czytelnych skryptów.

A more convenient approach is to use the methods "getfirst()" and
"getlist()" provided by this higher level interface.

FieldStorage.getfirst(name, default=None)

   Ten sposób postępowania zawsze zwraca tylko jedną wartość związaną
   z polem formularza *name*. Sposób postępowania zwraca tylko
   pierwszą wartość w przypadku gdy więcej wartości zostało
   opublikowanych pod taką nazwą. Proszę zauważ że kolejność w jakiej
   wartości są otrzymywane może różnić się między przeglądarkami i nie
   powinien liczyć się. [1] Jeśli żadne takie pole formularza ani
   wartość nie istnieje wtedy sposób postępowania zwraca wartość
   określoną przez opcjonalny parametr *domyślny* - z ang - *default*.
   Ten parametr domyślnie równy jest "None" jeśli nie jest określony.

FieldStorage.getlist(name)

   Ten sposób postępowania zawsze zwraca listę wartości związaną z
   polem formularza *name*. Ten sposób postępowania zwraca pustą listę
   jeśli żadne pole ani wartość nie istnieje dla *nazwy* - z ang. -
   *name*. Zwraca listę składającą się z jednego elementu jeśli tylko
   jedna taka wartość istnieje.

Używając tych sposobów postępowania możesz napisać ładny zgrabny kod:

   import cgi
   form = cgi.FieldStorage()
   user = form.getfirst("user", "").upper()    # This way it's safe.
   for item in form.getlist("item"):
       do_something(item)


Zadania
=======

Te są użyteczne jeśli chcesz więcej kontrolować, lub jeśli chcesz
zatrudnić niektóre z algorytmów wypełnionych w tym module w innych
przypadkach.

cgi.parse(fp=None, environ=os.environ, keep_blank_values=False, strict_parsing=False, separator="&")

   Parse a query in the environment or from a file (the file defaults
   to "sys.stdin").  The *keep_blank_values*, *strict_parsing* and
   *separator* parameters are passed to "urllib.parse.parse_qs()"
   unchanged.

   Zmienione w wersji 3.8.8: Added the *separator* parameter.

cgi.parse_multipart(fp, pdict, encoding="utf-8", errors="replace", separator="&")

   Parse input of type *multipart/form-data* (for  file uploads).
   Arguments are *fp* for the input file, *pdict* for a dictionary
   containing other parameters in the *Content-Type* header, and
   *encoding*, the request encoding.

   Returns a dictionary just like "urllib.parse.parse_qs()": keys are
   the field names, each value is a list of values for that field. For
   non-file fields, the value is a list of strings.

   This is easy to use but not much good if you are expecting
   megabytes to be uploaded --- in that case, use the "FieldStorage"
   class instead which is much more flexible.

   Zmienione w wersji 3.7: Added the *encoding* and *errors*
   parameters.  For non-file fields, the value is now a list of
   strings, not bytes.

   Zmienione w wersji 3.8.8: Added the *separator* parameter.

cgi.parse_header(string)

   Wczytaj nagłówek MIME (taki, jak *Content-Type*) do głównej
   wartości i słownika parametrów.

cgi.test()

   Wydajny testowy skrypt CGI, użyteczny jako program główny. Zapisuje
   minimalne nagłówki HTTP i formatuje całą informację dostarczoną dla
   skryptu w formularzu HTML.

cgi.print_environ()

   Formatuje środowisko powłoki w HTML-u

cgi.print_form(form)

   Formatuje formularz w HTML-u.

cgi.print_directory()

   Formatuje obecny katalog w HTML-u.

cgi.print_environ_usage()

   Drukuje listę użytecznych (używanych przez CGI) zmiennych
   środowiskowych w HTML-u.


Troska o bezpieczeństwo
=======================

Istnieje jedna istotna zasada: Jeśli wywołujesz zewnętrzny program
(przez zadanie "os.system()" lub "os.popen()" lub inne o podobnym
działaniu), upewnij się bardzo że nie przekazujesz dowolnych ciągów
znaków otrzymanych od klienta do powłoki. To jest bardzo znana luka
bezpieczeństwa gdzie sprytni hakerzy dowolnie w sieci mogą wykorzystać
naiwny skrypt CGI aby wywołać dowolną komendę powłoki. Nawet częściom
adresu URL lub nazwom pól nie można ufać, gdyż zapytanie może nie
pochodzić z formularza!

Aby trzymać się bezpiecznej strony, jeśli przekazujesz ciąg znaków
otrzymany z formularza do polecenia powłoki, powinieneś upewnić się,
że ciąg znaków zawiera tylko znaki alfanumeryczne, ukośniki,
podkreślenia i kropki.


Instalowanie twojego skryptu CGI na systemie Unix-owym
======================================================

Przeczytaj dokumentację dla twojego serwera HTTP i sprawdź u swojego
administratora systemu aby dowiedzieć się który katalog powinien być
użyty dla instalowania skryptów CGI; zwykle to jest katalog "cgi-bin"
w drzewie serwera.

Upewnij się że twój skrypt jest czytelny i wykonywalny przez "innych";
tryb pliku Unix-a powinien być "0o755" ósemkowo (użyj "chmod 0755
nazwa-pliku"). Upewnij się, że pierwsza linia skryptu zawiera "#!"
zaczynając w kolumnie 1 po której następuje ścieżka dostępu do pliku
programu interpretującego polecenia języka pytonowskiego, dla
przykładu:

   #!/usr/local/bin/python

Upewnij się że program interpretujący polecenia języka pytonowskiego
istnieje i jest wykonywalny przez "innych".

Upewnij się, że jakiekolwiek pliki które twój skrypt chce odczytać lub
zapisać są czytelne i możliwe do zapisania odpowiednio, przez "innych"
--- ich tryb powinien być "0o644" dla czytelnych i 0o666`` dla
możliwych do zapisania. To jest ponieważ z powodów bezpieczeństwa,
serwer HTTP wykonuje twój skrypt jako użytkownik "nikt" - z ang. -
"nobody" bez żadnych szczególnych przywilejów. Może tylko wczytywać
(zapisywać, wykonywać) pliki które wszyscy mogą czytać (zapisać,
wykonać). Bierzący katalog w czasie wykonania jest także inny (jest to
zwykle katalog serwera cgi-bin) i ustawienie zmiennych środowiskowych
jest także inne od tego które dostajesz gdy się zalogujesz. W
szczególności, nie licz na ścieżkę przeszukiwania pod kątem plików
wykonywalnych ("PATH") ani na ścieżkę przeszukiwania modułów języka
pytonowskiego ("PYTHONPATH"), że będą ustawione na cokolwiek
interesującego.

Jeśli potrzebujesz załadować moduły z katalogu który nie jest domyślną
ścieżką przeszukiwania modułów języka pytonowskiego, możesz zmienić
ścieżkę w twoim skrypcie, przez importowaniem innych modułów. Dla
przykładu:

   import sys
   sys.path.insert(0, "/usr/home/joe/lib/python")
   sys.path.insert(0, "/usr/local/lib/python")

(W ten sposób, katalog wstawiony jako ostatni będzie przeszukiwany
jako pierwszy!)

Instrukcje dla nie-Unixowych systemów będą różne; Sprawdź dokumentację
serwera HTTP (będzie zwykle miała sekcję o skryptach CGI).


Sprawdzanie twoich skryptów CGI
===============================

Niestety, skrypt CGI zwykle nie uruchomi się gdy spróbujesz go
uruchomić z wiersza poleceń i skrypt który działa dobrze z wiersza
polecenia może zawieźć nieoczekiwanie gdy uruchomiony z serwera. Jest
jeden powód dla którego wciąż powinieneś testować swój skrypt z
wiersza polecenia: jeśli zawiera błąd składniowy, program
interpretujący polecenia języka pytonowskiego nie wykona go w ogóle, a
serwer HTTP najprawdopodobniej wyśle tajemniczy komunikat o błędzie do
klienta.

Zakładając, że twój skrypt nie ma błędów składniowych, a jednak wciąż
nie działa, nie masz wyboru, tylko musisz czytać dalej.


Odpluskwianie skryptów CGI
==========================

First of all, check for trivial installation errors --- reading the
section above on installing your CGI script carefully can save you a
lot of time.  If you wonder whether you have understood the
installation procedure correctly, try installing a copy of this module
file ("cgi.py") as a CGI script.  When invoked as a script, the file
will dump its environment and the contents of the form in HTML form.
Give it the right mode etc, and send it a request.  If it's installed
in the standard "cgi-bin" directory, it should be possible to send it
a request by entering a URL into your browser of the form:

   http://yourhostname/cgi-bin/cgi.py?name=Joe+Blow&addr=At+Home

Jeśli zwróci błąd typu 404, wtedy serwer nie może znaleźć skryptu --
być może potrzebujesz zainstalować go w innych katalogu. Jeśli daje
inny błąd, istnieje problem instalacji który powinieneś naprawić zanim
spróbujesz iść dalej. Jeśli otrzymujesz ładnie sformatowany wypis
środowiska i zawartości formularza (w tym przypadku, pola powinny być
wypisane jako "addr" z wartością "At Home" i "name" z wartością "Joe
Blow"), plik skryptu "cgi.py" zostały zainstalowane poprawnie. Jeśli
wykonasz tę samą procedurę dla twojego własnego skryptu, powinieneś
móc go teraz odpluskwiać.

Następnym krokiem mogłoby być wezwanie zadania "test()" modułu "cgi" z
twojego skryptu: zamiana jej głównego kodu na pojedyncze stwierdzenie:

   cgi.test()

To powinno dać te same wyniki jak te otrzymane z zainstalowania samego
pliku "cgi.py".

Gdy zwykły skrypt języka pytonowskiego zgłasza nieobsłużony wyjątek
(dla jakiegokolwiek powodu: z powodu literówki w nazwie modułu, pliku
który nie może być otwarty, itp.), program interpretujący polecenia
języka pytonowskiego wypisuje ładny wypis i wychodzi. Podczas gdy
program interpretujący polecenia języka pytonowskiego będzie wciąż to
robił gdy twój skrypt CGI zgłosi wyjątek, najprawdopodobniej wypis
skończy w jednym z plików logów serwera HTTP, lub zostanie odrzucony
całkowicie.

Szczęśliwie, gdy już uda ci się wykonać *jakiś* kod za pomocą skryptu,
możesz łatwo wysyłać wypisy do przeglądarki używając "cgitb". Jeśli
jeszcze nie zrobiłeś tego, po prostu dodaj linie:

   import cgitb
   cgitb.enable()

na górę twojego skryptu. Wtedy spróbuj uruchomić go jeszcze raz; gdy
problem pojawi się znów, powinieneś zobaczyć szczegółowy raport, który
prawdopodobnie uczyni jasnym powód wypadku.

Jeśli podejrzewasz, że może być problem w importowaniu modułu "cgitb",
możesz użyć nawet jeszcze bardziej wydajnego podejścia (które używa
tylko modułów wbudowanych):

   import sys
   sys.stderr = sys.stdout
   print("Content-Type: text/plain")
   print()
   ...your code here...

To opiera się na programie interpretującym polecenia języka
pytonowskiego, że wydrukuje swój wypis. Typ treści wyjścia jest
ustawiony na zwykły tekst, co wyklucza całą obsługę HTML. Jeśli twój
skrypt działa, surowy HTML zostanie pokazany przez twojego klienta.
Jeśli zgłasza wyjątek, najprawdopodobniej po tym jak pierwsze linie
zostaną wypisane, wypis wsteczny zostanie wyświetlony. Ponieważ żadna
interpretacja HTML-a nie miejsca, wypis wsteczny będzie czytelny.


Typowe problemy i ich rozwiązania
=================================

* Większość serwerów HTTP przechowuje w przestrzeni wymiany wyjście ze
  skryptów CGI dopóki skrypt się nie zakończy. To oznacza, że nie jest
  możliwe wyświetlenie raportu postępu na wyświetlaczu klienta kiedy
  skrypt działa.

* Sprawdź instrukcje instalacyjne powyżej.

* Sprawdź pliki logu serwera HTTP. ("tail -f logfile" w oddzielnym
  oknie może być użyteczne!)

* Zawsze sprawdzaj skrypt dla błędów składniowych najpierw, przez
  wykonanie czegoś podobnego do "python script.py".

* Jeśli twój skrypt nie ma żadnych błędów składniowych, spróbuj dodać
  "import cgitb; cgitb.enable()" na górze skryptu.

* Gdy wywoływane są zewnętrzne programy upewnij się że mogą być
  znalezione. Zwykle to oznacza, że używanie nazw ścieżek absolutnych
  --- "PATH" nie jest zwykle ustawiana na użyteczną wartość skryptu
  CGI.

* Gdy wczytujesz lub zapisujesz zewnętrzne pliki, upewnij się że mogą
  one być wczytane lub zapisane przez userid pod którym twój skrypt
  CGI będzie działał: to jest typowo userid pod którym serwer sieci
  działa, lub pewny jawnie określony userid dla właściwości sieciowego
  serwera "suexec".

* Nie próbuj nadawać skryptowi CGI trybu set-uid. To nie działa na
  większości systemów, i jest odpowiedzialnością za bezpieczeństwo
  także.

-[ Przypisy ]-

[1] Zauważ że pewne niedawne wersje specyfikacji HTML-a określają w
    jakiej kolejności wartości pól powinny być dostarczone, ale
    wiedzieć czy zapytanie zostało otrzymane z odpowiadającej
    przeglądarki, czy też w ogóle z przeglądarki jest nużące i podatne
    na błędy.
