[[ノート>ノート/ノート]] ~
訪問者数 &counter();      最終更新 &lastmod();

**単純なCGI (出力のみ) [#q64aaf21]
 #!/usr/bin/env python
 # -*- coding: utf-8 -*-
 import sys
 import codecs
 import cgitb
 cgitb.enable()
 
 sys.stdout = codecs.getwriter('utf_8')(sys.stdout)
 print ('Content-type: text/html; charset=UTF-8')
 print("\r\n\r\n")
 
 print("あいう")
ポイントは、
+先頭にpythonを実行させるための#!/usr/bin/env pythonを書く
+漢字を使うなら coding: utf-8 を書く
+漢字を使うなら sysとcodecsをimportしておき、utf_8のwriterをstdoutにかましておく
+cgitbをimportし、メソッドcgitb.enable()を実行しておく。これによりデバッグ情報がホームページ上に表示される。営業運転時には外す必要あり。
+ホームページとして出力するためには、先頭に'Content-type: text/html; charset=UTF-8'を入れる。charsetはブラウザが漢字コードをUTF-8とする指示。~
この直後に必ず空行を1行以上入れる(ヘッダー区別のため)
+このファイルをセーブする時、UTF-8コードを指定(に変換)する~
更に、ファイルの実行可能ビットを立てる(chmod 755 ファイル名)

このファイルを、cgi-binディレクトリ下に置けば、呼び出せる。

**フォームのページの場合 [#ld24a882]
例 (ここでは、formを書き出すhtml部分も同じプログラム中でprintで出力している)
 #!/usr/bin/env python
 # -*- coding: utf-8 -*-
 # coding: utf-8
 import sys
 import codecs
 import cgi
 import cgitb
 cgitb.enable()
 
 sys.stdout = codecs.getwriter('utf_8')(sys.stdout)
 print ('Content-type: text/html; charset=UTF-8')
 print ("\r\n\r\n")
 
 form = cgi.FieldStorage()
 laptops = form.getvalue('laptops','0')
 desktops = form.getvalue('desktops','0')
 
 print """
 <html>
 <body>
 <form action='mytest.py'>
 How many laptops do you own
 <input type='radio' checked name='laptops' value='0' />0 
 <input type='radio' name='laptops' value='1' />1 
 <input type='radio' name='laptops' value='2' />2
 <p>
 How many desktops do you own 
 <input type='radio' checked name='desktops' value='0' />0 
 <input type='radio' name='desktops' value='1' />1 
 <input type='radio' name='desktops' value='2' />2
 <p>
 <input type='submit' value='送る' />
 <p>
 You own %d computers.
 </form>
 </body>
 </html>""" % (int(laptops)+int(desktops))
この例では、次のようなことに注目する。
+cgiライブラリ中のFieldStorageを使ってフォーム入力用の場所を確保する。getvalueメソッドで実際に値を取り込む。
+<html>から</html>までは、print """ ... """ によってまとめて書き出している。~
もちろん、1行ずつprintで書いていってもいいのだろう。~
また、そのprint文中の%dで書かれる値を、print ""..."" % int(laptops)+int(desktops) で計算・指定している。



**Python-CGIで複数ファイルを一発でアップロードする [#kcb88b64]
+複数ファイルをアップロードする件(HTML-5にて新規導入の規格)~
ファイルアップロードの際に、ファイル選択画面で複数ファイルを選択し(WindowsならばCTRLキーを押しながらマウス左クリックして、複数ファイルを選択できる)、それを同時にアップロードする。~
HTML-5から導入された新しい規格で、HTML-5に対応するFireFoxなどのみで使うことができる。~
サーバー側CGIは、たとえばapache+PHPやapache+Pythonで使える。多分他の言語でもOKと思うが、未確認(「使い方」=API=上の差異があるはず)。
+Pythonの場合、

>例
 #!/usr/bin/env python
 # -*- coding: utf-8 -*-
 # coding: utf-8
 import sys
 import codecs
 import cgi
 import cgitb
 cgitb.enable()
 
 sys.stdout = codecs.getwriter('utf_8')(sys.stdout)
 print ('Content-type: text/html; charset=UTF-8')
 print ("\r\n\r\n")
 
 form = cgi.FieldStorage()
 print("タイプは" + str(form.type) + "<br>")
 fileitem = form['userfile[]']
 
 if (isinstance(fileitem, list)):
   # Multiple files
   print("  複数ファイルです<br>")
   print("  ファイル数は" + str(len(fileitem)) + "<br>")
   for i in range(0, len(fileitem)):
     print(str(fileitem[i].filename) + "<br>")
     print(fileitem[i].value)
     print("<br>=======================<br>")
 
 else:
   print ("  単数ファイルです<br>")
   print(str(fileitem.filename) + "<br>")
   print(fileitem.value)

>ポイントは、 fileitem = form['userfile[]'] で、'userfile'にせず、'userfile[]'にすることがミソである。~
これによって、複数ファイル(リストで戻ってくる)を扱える。(こうしないとエラーになる)

>この fileitem が、ファイルを1つだけしかアップロードしない時(リストにならない)と、複数ファイルをアップロードした時(リストになる)で、形が違う。~
従って、取り出す時に区別する必要がある。

>複数ファイルの時は、 fileitem[i] のように、どのファイルかを区別する。また、len(filename)によって、ファイルの個数を知ることができる。単一ファイルの時にlen(filename)を行うと(単一オブジェクトのlenを取ることになるので)エラーになる。

>fileitemの属性は、元のファイルの名前が filename、ファイルの内容が valueである。

**SELinuxでの(アップロード用ディレクトリの)書込み許可を出す [#f5072c23]
SELinux下では、httpdによる書き込みは一般には止められる。(Permission Denied)
特定のディレクトリに対して書き込みを許可するには、
 %semanage fcontext -a -t httpd_sys_content_rw_t "/var/www/html/fcs20/FILE"
 %semanage fcontext -a -t httpd_sys_content_rw_t "/var/www/html/fcs20/FILE/*"
 %restorecon -R -v /var/www/html/fcs20/FILE
 restorecon reset /var/www/html/fcs20/FILE context system_u:object_r:httpd_sys_content_t:s0->system_u:object_r:httpd_sys_content_rw_t:s0
この辺も参照
-[[ノート/SELinux]]
-http://d.hatena.ne.jp/kgbu/20090209/1234146121
-http://fedoraproject.org/wiki/SELinux/apache
-http://yam-web.net/selinux/index.html
-http://forums.fedoraforum.org/showthread.php?t=198271
-http://forums.fedoraforum.org/showthread.php?t=209664

**上記のSELinuxでの書込み許可が出た上で、スクリプトからアップロード用ディレクトリへファイルを書く (このファイルの名前をmultifile2.pyとする) [#j8ea072a]
 #!/usr/bin/env python
 # -*- coding: utf-8 -*-
 # coding: utf-8
 import sys
 import codecs
 import cgi
 import cgitb
 cgitb.enable()
 
 sys.stdout = codecs.getwriter('utf_8')(sys.stdout)
 print ('Content-type: text/html; charset=UTF-8')
 print ("\r\n\r\n")
 
 form = cgi.FieldStorage()
 print("タイプは" + str(form.type) + "<br>")
 if (form.type != "application/x-www-form-urlencoded"):
   fileitem = form['userfile[]']
 
   if (isinstance(fileitem, list)):
     # Multiple files
     print("  複数ファイルです<br>")
     print("  ファイル数は" + str(len(fileitem)) + "<br>")
     for i in range(0, len(fileitem)):
       print(str(fileitem[i].filename) + "<br>")
       fp = open("/var/www/html/fcs20/FILE/"+str(fileitem[i].filename), "w+")
       fp.write(fileitem[i].value)
   else:
     print ("  単数ファイルです<br>")
     print(str(fileitem.filename) + "<br>")
     fp = open("/var/www/html/fcs20/FILE/"+str(fileitem.filename), "w+")
     fp.write(fileitem.value)
 
 print"""
 <html>
 <body>
 <form action="multifile2.py" method="post" enctype="multipart/form-data">
   Send these files:<br />
   <input type="hidden" name="MAX_FILE_SIZE" value="300000" />
   <input name="userfile[]" type="file" multiple/><br />
   <input type="submit" value="Send files" />
 </form>
 </body>
 </html>
 """
**もう少し複雑なページを考える。2つsubmitボタンがある場合 [#wc5b4969]
気をつけるのは、同じnameの下に複数の値が返される時、リストで返されるが、単一の時はリストでないので、その区別をすること。⇒ 参考 [[マニュアル:http://www.python.jp/doc/2.5/lib/node561.html]]

 #!/usr/bin/env python
 # -*- coding: utf-8 -*-
 # coding: utf-8
 import sys
 import codecs
 import os
 import cgi
 import cgitb
 cgitb.enable()
 
 sys.stdout = codecs.getwriter('utf_8')(sys.stdout)
 print ('Content-type: text/html; charset=UTF-8')
 print ("\r\n\r\n")
 
 datadir = "/var/www/html/fcs20/FILE/"
 form = cgi.FieldStorage()
 print("nameは" + str(form.name) + "<br>")
 print("valueは" + str(form.getvalue("submit")) + "<br>")
 print("タイプは" + str(form.type) + "<br>")
 
 if (form.getvalue("submit") == "消去"):
   print(str(form) + "<br>")
   if ("fnum" in form) :
     selectitem = form['fnum']
     print(str(selectitem) + "<br>")
     if (isinstance(selectitem,list)) :
       for i in range(0, len(selectitem)):
         print(str(selectitem[i].value))
     else:
       print(str(selectitem.value))
   else:
     print('no checks')
 
 else:
   if (form.type == "multipart/form-data"):
     fileitem = form['userfile[]']
 
     if (isinstance(fileitem, list)):
       # Multiple files
       print("  複数ファイルです<br>")
       print("  ファイル数は" + str(len(fileitem)) + "<br>")
       for i in range(0, len(fileitem)):
         print(str(fileitem[i].filename) + "<br>")
         #print(fileitem[i].value)
         #print("<br>=======================<br>")
         fp = open(datadir+str(fileitem[i].filename), "w+")
         # fp = open("/tmp/"+str(fileitem[i].filename), "r+")
         fp.write(fileitem[i].value)
     else:
       print ("  単数ファイルです<br>")
       print(str(fileitem.filename) + "<br>")
       #print(fileitem.value)
       fp = open(datadir+str(fileitem.filename), "w+")
       # fp = open("/tmp/"+str(fileitem.filename), "r+")
       fp.write(fileitem.value)
  
 print"""
 <html>
 <head>
 <META http-equiv="Content-Type" content="text/html;charset=utf-8">
 <title> FCS20 </title>
 </head>
 <body>
 <table>
 <tr><td width=300>
 """
 
 i = 0
 print("作業フォルダ内のファイル一覧<br>")
 print('<form action="fcs20.py" method="post" enctype="multipart/form-data">')
 print("<table><tr><td>")
 for f in (os.listdir(datadir)):
   print('<input type="checkbox" name=fnum value=' + str(i) +"> " + str(f) +  "</td></tr><tr><td>")
   i=i+1
 print("</tr></td></table>")
 print('<input type="submit" name="submit" value="消去" />')
 print("</form>")
 
 print"""
 </td><td>
 追加ファイルの転送<br>
 <form action="fcs20.py" method="post" enctype="multipart/form-data">
   <input type="hidden" name="MAX_FILE_SIZE" value="30000000" />
   <input width=300 name="userfile[]" type="file" multiple/><br />
   <input type="submit" name="submit" value="転送" />
 </form>
 </td></tr></table>
 </body>
 </html>
 """

*SELinuxで、public_htmlディレクトリの読み出しアクセス許可を出す [#d288bd68]
-ユーザのホーム /home/foo の許可ビットを少なくとも711以上にする(root権限必要)
-Webページのデータになるファイル(たとえば/home/foo/public_html/index.html)の許可ビットは、少なくとも755にする
-SELinuxのコマンド /sbin/restorecon -R /home/foo/public_html によって、
ファイル属性を変更する(root権限必要)。-Rはリカーシブ指定で、指定されたディレクトリの下を次々に繰り返し掘っていって変更する。
-ls -alZ /home/foo/public_html でファイルの属性を確認する。restorecon以前は~
 drwxr-xr-x. foo user unconfined_u:object_r:user_home_t:s0 public_html~
であったものが、restoreconによって~
 drwxr-xr-x. foo user unconfined_u:object_r:httpd_user_content_t:s0 public_html~
のように変化したはずである。
-python scriptをcgiで実行できるためには、追加で、~
 chcon -R -t httpd_sys_script_exec_t /home/foo/public_html/cgi-bin~
が必要で、その結果を ls -alZ /home/foo/public_html/ で見ると~
 -rwxr-xr-x. foo user unconfined_u:object_r:httpd_sys_script_exec_t:s0 mytest.py~
になった。これでOKらしい。

*SELinuxで、public_html/cgi-binディレクトリ以下のスクリプトでネットアクセスを許す [#k0622772]
-setsebool -P httpd_can_network_connect 1

なお、SELinuxのいろいろな許可ビットの一覧を見るには、
-getsebool -a | less


[[ノート>ノート/ノート]] ~
訪問者数 &counter();      最終更新 &lastmod();

**単純なCGI (出力のみ) [#q64aaf21]
 #!/usr/bin/env python
 # -*- coding: utf-8 -*-
 import sys
 import codecs
 import cgitb
 cgitb.enable()
 
 sys.stdout = codecs.getwriter('utf_8')(sys.stdout)
 print ('Content-type: text/html; charset=UTF-8')
 print("\r\n\r\n")
 
 print("あいう")
 print(u"あいう")
ポイントは、
+先頭にpythonを実行させるための#!/usr/bin/env pythonを書く
+漢字を使うなら coding: utf-8 を書く
+漢字を使うなら sysとcodecsをimportしておき、utf_8のwriterをstdoutにかましておく
+cgitbをimportし、メソッドcgitb.enable()を実行しておく。これによりデバッグ情報がホームページ上に表示される。営業運転時には外す必要あり。
+ホームページとして出力するためには、先頭に'Content-type: text/html; charset=UTF-8'を入れる。charsetはブラウザが漢字コードをUTF-8とする指示。~
この直後に必ず空行を1行以上入れる(ヘッダー区別のため)
+このファイルをセーブする時、UTF-8コードを指定(に変換)する~
更に、ファイルの実行可能ビットを立てる(chmod 755 ファイル名)

このファイルを、cgi-binディレクトリ下に置けば、呼び出せる。

**フォームのページの場合 [#ld24a882]
例 (ここでは、formを書き出すhtml部分も同じプログラム中でprintで出力している)
 #!/usr/bin/env python
 # -*- coding: utf-8 -*-
 # coding: utf-8
 import sys
 import codecs
 import cgi
 import cgitb
 cgitb.enable()
 
 sys.stdout = codecs.getwriter('utf_8')(sys.stdout)
 print ('Content-type: text/html; charset=UTF-8')
 print ("\r\n\r\n")
 
 form = cgi.FieldStorage()
 laptops = form.getvalue('laptops','0')
 desktops = form.getvalue('desktops','0')
 
 print """
 <html>
 <body>
 <form action='mytest.py'>
 How many laptops do you own
 <input type='radio' checked name='laptops' value='0' />0 
 <input type='radio' name='laptops' value='1' />1 
 <input type='radio' name='laptops' value='2' />2
 <p>
 How many desktops do you own 
 <input type='radio' checked name='desktops' value='0' />0 
 <input type='radio' name='desktops' value='1' />1 
 <input type='radio' name='desktops' value='2' />2
 <p>
 <input type='submit' value='送る' />
 <p>
 You own %d computers.
 </form>
 </body>
 </html>""" % (int(laptops)+int(desktops))
この例では、次のようなことに注目する。
+cgiライブラリ中のFieldStorageを使ってフォーム入力用の場所を確保する。getvalueメソッドで実際に値を取り込む。
+<html>から</html>までは、print """ ... """ によってまとめて書き出している。~
もちろん、1行ずつprintで書いていってもいいのだろう。~
また、そのprint文中の%dで書かれる値を、print ""..."" % int(laptops)+int(desktops) で計算・指定している。



**Python-CGIで複数ファイルを一発でアップロードする [#kcb88b64]
+複数ファイルをアップロードする件(HTML-5にて新規導入の規格)~
ファイルアップロードの際に、ファイル選択画面で複数ファイルを選択し(WindowsならばCTRLキーを押しながらマウス左クリックして、複数ファイルを選択できる)、それを同時にアップロードする。~
HTML-5から導入された新しい規格で、HTML-5に対応するFireFoxなどのみで使うことができる。~
サーバー側CGIは、たとえばapache+PHPやapache+Pythonで使える。多分他の言語でもOKと思うが、未確認(「使い方」=API=上の差異があるはず)。
+Pythonの場合、

>例
 #!/usr/bin/env python
 # -*- coding: utf-8 -*-
 # coding: utf-8
 import sys
 import codecs
 import cgi
 import cgitb
 cgitb.enable()
 
 sys.stdout = codecs.getwriter('utf_8')(sys.stdout)
 print ('Content-type: text/html; charset=UTF-8')
 print ("\r\n\r\n")
 
 form = cgi.FieldStorage()
 print("タイプは" + str(form.type) + "<br>")
 fileitem = form['userfile[]']
 
 if (isinstance(fileitem, list)):
   # Multiple files
   print("  複数ファイルです<br>")
   print("  ファイル数は" + str(len(fileitem)) + "<br>")
   for i in range(0, len(fileitem)):
     print(str(fileitem[i].filename) + "<br>")
     print(fileitem[i].value)
     print("<br>=======================<br>")
 
 else:
   print ("  単数ファイルです<br>")
   print(str(fileitem.filename) + "<br>")
   print(fileitem.value)

>ポイントは、 fileitem = form['userfile[]'] で、'userfile'にせず、'userfile[]'にすることがミソである。~
これによって、複数ファイル(リストで戻ってくる)を扱える。(こうしないとエラーになる)

>この fileitem が、ファイルを1つだけしかアップロードしない時(リストにならない)と、複数ファイルをアップロードした時(リストになる)で、形が違う。~
従って、取り出す時に区別する必要がある。

>複数ファイルの時は、 fileitem[i] のように、どのファイルかを区別する。また、len(filename)によって、ファイルの個数を知ることができる。単一ファイルの時にlen(filename)を行うと(単一オブジェクトのlenを取ることになるので)エラーになる。

>fileitemの属性は、元のファイルの名前が filename、ファイルの内容が valueである。

**SELinuxでの(アップロード用ディレクトリの)書込み許可を出す [#f5072c23]
SELinux下では、httpdによる書き込みは一般には止められる。(Permission Denied)
特定のディレクトリに対して書き込みを許可するには、
 %semanage fcontext -a -t httpd_sys_content_rw_t "/var/www/html/fcs20/FILE"
 %semanage fcontext -a -t httpd_sys_content_rw_t "/var/www/html/fcs20/FILE/*"
 %restorecon -R -v /var/www/html/fcs20/FILE
 restorecon reset /var/www/html/fcs20/FILE context system_u:object_r:httpd_sys_content_t:s0->system_u:object_r:httpd_sys_content_rw_t:s0
この辺も参照
-[[ノート/SELinux]]
-http://d.hatena.ne.jp/kgbu/20090209/1234146121
-http://fedoraproject.org/wiki/SELinux/apache
-http://yam-web.net/selinux/index.html
-http://forums.fedoraforum.org/showthread.php?t=198271
-http://forums.fedoraforum.org/showthread.php?t=209664

**上記のSELinuxでの書込み許可が出た上で、スクリプトからアップロード用ディレクトリへファイルを書く (このファイルの名前をmultifile2.pyとする) [#j8ea072a]
 #!/usr/bin/env python
 # -*- coding: utf-8 -*-
 # coding: utf-8
 import sys
 import codecs
 import cgi
 import cgitb
 cgitb.enable()
 
 sys.stdout = codecs.getwriter('utf_8')(sys.stdout)
 print ('Content-type: text/html; charset=UTF-8')
 print ("\r\n\r\n")
 
 form = cgi.FieldStorage()
 print("タイプは" + str(form.type) + "<br>")
 if (form.type != "application/x-www-form-urlencoded"):
   fileitem = form['userfile[]']
 
   if (isinstance(fileitem, list)):
     # Multiple files
     print("  複数ファイルです<br>")
     print("  ファイル数は" + str(len(fileitem)) + "<br>")
     for i in range(0, len(fileitem)):
       print(str(fileitem[i].filename) + "<br>")
       fp = open("/var/www/html/fcs20/FILE/"+str(fileitem[i].filename), "w+")
       fp.write(fileitem[i].value)
   else:
     print ("  単数ファイルです<br>")
     print(str(fileitem.filename) + "<br>")
     fp = open("/var/www/html/fcs20/FILE/"+str(fileitem.filename), "w+")
     fp.write(fileitem.value)
 
 print"""
 <html>
 <body>
 <form action="multifile2.py" method="post" enctype="multipart/form-data">
   Send these files:<br />
   <input type="hidden" name="MAX_FILE_SIZE" value="300000" />
   <input name="userfile[]" type="file" multiple/><br />
   <input type="submit" value="Send files" />
 </form>
 </body>
 </html>
 """
**もう少し複雑なページを考える。2つsubmitボタンがある場合 [#wc5b4969]
気をつけるのは、同じnameの下に複数の値が返される時、リストで返されるが、単一の時はリストでないので、その区別をすること。⇒ 参考 [[マニュアル:http://www.python.jp/doc/2.5/lib/node561.html]]

 #!/usr/bin/env python
 # -*- coding: utf-8 -*-
 # coding: utf-8
 import sys
 import codecs
 import os
 import cgi
 import cgitb
 cgitb.enable()
 
 sys.stdout = codecs.getwriter('utf_8')(sys.stdout)
 print ('Content-type: text/html; charset=UTF-8')
 print ("\r\n\r\n")
 
 datadir = "/var/www/html/fcs20/FILE/"
 form = cgi.FieldStorage()
 print("nameは" + str(form.name) + "<br>")
 print("valueは" + str(form.getvalue("submit")) + "<br>")
 print("タイプは" + str(form.type) + "<br>")
 
 if (form.getvalue("submit") == "消去"):
   print(str(form) + "<br>")
   if ("fnum" in form) :
     selectitem = form['fnum']
     print(str(selectitem) + "<br>")
     if (isinstance(selectitem,list)) :
       for i in range(0, len(selectitem)):
         print(str(selectitem[i].value))
     else:
       print(str(selectitem.value))
   else:
     print('no checks')
 
 else:
   if (form.type == "multipart/form-data"):
     fileitem = form['userfile[]']
 
     if (isinstance(fileitem, list)):
       # Multiple files
       print("  複数ファイルです<br>")
       print("  ファイル数は" + str(len(fileitem)) + "<br>")
       for i in range(0, len(fileitem)):
         print(str(fileitem[i].filename) + "<br>")
         #print(fileitem[i].value)
         #print("<br>=======================<br>")
         fp = open(datadir+str(fileitem[i].filename), "w+")
         # fp = open("/tmp/"+str(fileitem[i].filename), "r+")
         fp.write(fileitem[i].value)
     else:
       print ("  単数ファイルです<br>")
       print(str(fileitem.filename) + "<br>")
       #print(fileitem.value)
       fp = open(datadir+str(fileitem.filename), "w+")
       # fp = open("/tmp/"+str(fileitem.filename), "r+")
       fp.write(fileitem.value)
  
 print"""
 <html>
 <head>
 <META http-equiv="Content-Type" content="text/html;charset=utf-8">
 <title> FCS20 </title>
 </head>
 <body>
 <table>
 <tr><td width=300>
 """
 
 i = 0
 print("作業フォルダ内のファイル一覧<br>")
 print('<form action="fcs20.py" method="post" enctype="multipart/form-data">')
 print("<table><tr><td>")
 for f in (os.listdir(datadir)):
   print('<input type="checkbox" name=fnum value=' + str(i) +"> " + str(f) +  "</td></tr><tr><td>")
   i=i+1
 print("</tr></td></table>")
 print('<input type="submit" name="submit" value="消去" />')
 print("</form>")
 
 print"""
 </td><td>
 追加ファイルの転送<br>
 <form action="fcs20.py" method="post" enctype="multipart/form-data">
   <input type="hidden" name="MAX_FILE_SIZE" value="30000000" />
   <input width=300 name="userfile[]" type="file" multiple/><br />
   <input type="submit" name="submit" value="転送" />
 </form>
 </td></tr></table>
 </body>
 </html>
 """

*SELinuxで、public_htmlディレクトリの読み出しアクセス許可を出す [#d288bd68]
-ユーザのホーム /home/foo の許可ビットを少なくとも711以上にする(root権限必要)
-Webページのデータになるファイル(たとえば/home/foo/public_html/index.html)の許可ビットは、少なくとも755にする
-SELinuxのコマンド /sbin/restorecon -R /home/foo/public_html によって、
ファイル属性を変更する(root権限必要)。-Rはリカーシブ指定で、指定されたディレクトリの下を次々に繰り返し掘っていって変更する。
-ls -alZ /home/foo/public_html でファイルの属性を確認する。restorecon以前は~
 drwxr-xr-x. foo user unconfined_u:object_r:user_home_t:s0 public_html~
であったものが、restoreconによって~
 drwxr-xr-x. foo user unconfined_u:object_r:httpd_user_content_t:s0 public_html~
のように変化したはずである。
-python scriptをcgiで実行できるためには、追加で、~
 chcon -R -t httpd_sys_script_exec_t /home/foo/public_html/cgi-bin~
が必要で、その結果を ls -alZ /home/foo/public_html/ で見ると~
 -rwxr-xr-x. foo user unconfined_u:object_r:httpd_sys_script_exec_t:s0 mytest.py~
になった。これでOKらしい。

*SELinuxで、public_html/cgi-binディレクトリ以下のスクリプトでネットアクセスを許す [#k0622772]
-setsebool -P httpd_can_network_connect 1

なお、SELinuxのいろいろな許可ビットの一覧を見るには、
-getsebool -a | less
[[ノート>ノート/ノート]] ~
訪問者数 &counter();      最終更新 &lastmod();

**単純なCGI (出力のみ) [#q64aaf21]
 #!/usr/bin/env python
 # -*- coding: utf-8 -*-
 import sys
 import codecs
 import cgitb
 cgitb.enable()
 
 sys.stdout = codecs.getwriter('utf_8')(sys.stdout)
 print ('Content-type: text/html; charset=UTF-8')
 print("\r\n\r\n")
 
 print(u"あいう")
ポイントは、
+先頭にpythonを実行させるための#!/usr/bin/env pythonを書く
+漢字を使うなら coding: utf-8 を書く
+漢字を使うなら sysとcodecsをimportしておき、utf_8のwriterをstdoutにかましておく
+cgitbをimportし、メソッドcgitb.enable()を実行しておく。これによりデバッグ情報がホームページ上に表示される。営業運転時には外す必要あり。
+ホームページとして出力するためには、先頭に'Content-type: text/html; charset=UTF-8'を入れる。charsetはブラウザが漢字コードをUTF-8とする指示。~
この直後に必ず空行を1行以上入れる(ヘッダー区別のため)
+このファイルをセーブする時、UTF-8コードを指定(に変換)する~
更に、ファイルの実行可能ビットを立てる(chmod 755 ファイル名)

このファイルを、cgi-binディレクトリ下に置けば、呼び出せる。

**フォームのページの場合 [#ld24a882]
例 (ここでは、formを書き出すhtml部分も同じプログラム中でprintで出力している)
 #!/usr/bin/env python
 # -*- coding: utf-8 -*-
 # coding: utf-8
 import sys
 import codecs
 import cgi
 import cgitb
 cgitb.enable()
 
 sys.stdout = codecs.getwriter('utf_8')(sys.stdout)
 print ('Content-type: text/html; charset=UTF-8')
 print ("\r\n\r\n")
 
 form = cgi.FieldStorage()
 laptops = form.getvalue('laptops','0')
 desktops = form.getvalue('desktops','0')
 
 print """
 <html>
 <body>
 <form action='mytest.py'>
 How many laptops do you own
 <input type='radio' checked name='laptops' value='0' />0 
 <input type='radio' name='laptops' value='1' />1 
 <input type='radio' name='laptops' value='2' />2
 <p>
 How many desktops do you own 
 <input type='radio' checked name='desktops' value='0' />0 
 <input type='radio' name='desktops' value='1' />1 
 <input type='radio' name='desktops' value='2' />2
 <p>
 <input type='submit' value='送る' />
 <p>
 You own %d computers.
 </form>
 </body>
 </html>""" % (int(laptops)+int(desktops))
この例では、次のようなことに注目する。
+cgiライブラリ中のFieldStorageを使ってフォーム入力用の場所を確保する。getvalueメソッドで実際に値を取り込む。
+<html>から</html>までは、print """ ... """ によってまとめて書き出している。~
もちろん、1行ずつprintで書いていってもいいのだろう。~
また、そのprint文中の%dで書かれる値を、print ""..."" % int(laptops)+int(desktops) で計算・指定している。



**Python-CGIで複数ファイルを一発でアップロードする [#kcb88b64]
+複数ファイルをアップロードする件(HTML-5にて新規導入の規格)~
ファイルアップロードの際に、ファイル選択画面で複数ファイルを選択し(WindowsならばCTRLキーを押しながらマウス左クリックして、複数ファイルを選択できる)、それを同時にアップロードする。~
HTML-5から導入された新しい規格で、HTML-5に対応するFireFoxなどのみで使うことができる。~
サーバー側CGIは、たとえばapache+PHPやapache+Pythonで使える。多分他の言語でもOKと思うが、未確認(「使い方」=API=上の差異があるはず)。
+Pythonの場合、

>例
 #!/usr/bin/env python
 #!/usr/local/bin/python
 # -*- coding: utf-8 -*-
 # coding: utf-8
 import sys
 import codecs
 import sys,os,codecs
 import cgi
 import cgitb
 cgitb.enable()
 
 sys.stdout = codecs.getwriter('utf_8')(sys.stdout)
 print ('Content-type: text/html; charset=UTF-8')
 print ("\r\n\r\n")
 
 html = u'''
 <html>
 <head>
   <meta http-equiv="Content-Type" content="text/html" charset="UTF-8" />
   <title>ファイルアップロード</title>
 </head>
 <body>
 <form action="upload.py" method="post" enctype="multipart/form-data">
   <input type="file" name="file" multiple/>
   <input type="submit" />
 </form>
 </body>
 </html>
 '''
 
 result = ''
 form = cgi.FieldStorage()
 print("タイプは" + str(form.type) + "<br>")
 fileitem = form['userfile[]']
 if form.has_key("file"):
     fileitem = form['file']
     if (isinstance(fileitem, list)):
        # Multi-File
        for i in range(0, len(fileitem)):
           fout = file(os.path.join('/tmp', fileitem[i].filename), 'wb')
           while True:
               chunk = fileitem[i].file.read(1000000)
               if not chunk:
                   break
               fout.write(chunk)
           fout.close()
 
 if (isinstance(fileitem, list)):
   # Multiple files
   print("  複数ファイルです<br>")
   print("  ファイル数は" + str(len(fileitem)) + "<br>")
   for i in range(0, len(fileitem)):
     print(str(fileitem[i].filename) + "<br>")
     print(fileitem[i].value)
     print("<br>=======================<br>")
     else:
        # Single-File
        fout = file(os.path.join('/tmp', fileitem.filename), 'wb')
        while True:
           chunk = fileitem.file.read(1000000)
           if not chunk:
               break
           fout.write(chunk)
        fout.close()
 
 else:
   print ("  単数ファイルです<br>")
   print(str(fileitem.filename) + "<br>")
   print(fileitem.value)
 print html + result

>ポイントは、 fileitem = form['userfile[]'] で、'userfile'にせず、'userfile[]'にすることがミソである。~
これによって、複数ファイル(リストで戻ってくる)を扱える。(こうしないとエラーになる)

>ポイントは、 fileitem = form['file'] で返ってくる値が、複数ファイルの時はリストになって、複数のファイルを扱える。

>この fileitem が、ファイルを1つだけしかアップロードしない時(リストにならない)と、複数ファイルをアップロードした時(リストになる)で、形が違う。~
従って、取り出す時に区別する必要がある。

>複数ファイルの時は、 fileitem[i] のように、どのファイルかを区別する。また、len(filename)によって、ファイルの個数を知ることができる。単一ファイルの時にlen(filename)を行うと(単一オブジェクトのlenを取ることになるので)エラーになる。

>fileitemの属性は、元のファイルの名前が filename、ファイルの内容が valueである。

**SELinuxでの(アップロード用ディレクトリの)書込み許可を出す [#f5072c23]
SELinux下では、httpdによる書き込みは一般には止められる。(Permission Denied)
特定のディレクトリに対して書き込みを許可するには、
 %semanage fcontext -a -t httpd_sys_content_rw_t "/var/www/html/fcs20/FILE"
 %semanage fcontext -a -t httpd_sys_content_rw_t "/var/www/html/fcs20/FILE/*"
 %restorecon -R -v /var/www/html/fcs20/FILE
 restorecon reset /var/www/html/fcs20/FILE context system_u:object_r:httpd_sys_content_t:s0->system_u:object_r:httpd_sys_content_rw_t:s0
この辺も参照
-[[ノート/SELinux]]
-http://d.hatena.ne.jp/kgbu/20090209/1234146121
-http://fedoraproject.org/wiki/SELinux/apache
-http://yam-web.net/selinux/index.html
-http://forums.fedoraforum.org/showthread.php?t=198271
-http://forums.fedoraforum.org/showthread.php?t=209664

**上記のSELinuxでの書込み許可が出た上で、スクリプトからアップロード用ディレクトリへファイルを書く (このファイルの名前をmultifile2.pyとする) [#j8ea072a]
 #!/usr/bin/env python
 # -*- coding: utf-8 -*-
 # coding: utf-8
 import sys
 import codecs
 import cgi
 import cgitb
 cgitb.enable()
 
 sys.stdout = codecs.getwriter('utf_8')(sys.stdout)
 print ('Content-type: text/html; charset=UTF-8')
 print ("\r\n\r\n")
 
 form = cgi.FieldStorage()
 print("タイプは" + str(form.type) + "<br>")
 if (form.type != "application/x-www-form-urlencoded"):
   fileitem = form['userfile[]']
 
   if (isinstance(fileitem, list)):
     # Multiple files
     print("  複数ファイルです<br>")
     print("  ファイル数は" + str(len(fileitem)) + "<br>")
     for i in range(0, len(fileitem)):
       print(str(fileitem[i].filename) + "<br>")
       fp = open("/var/www/html/fcs20/FILE/"+str(fileitem[i].filename), "w+")
       fp.write(fileitem[i].value)
   else:
     print ("  単数ファイルです<br>")
     print(str(fileitem.filename) + "<br>")
     fp = open("/var/www/html/fcs20/FILE/"+str(fileitem.filename), "w+")
     fp.write(fileitem.value)
 
 print"""
 <html>
 <body>
 <form action="multifile2.py" method="post" enctype="multipart/form-data">
   Send these files:<br />
   <input type="hidden" name="MAX_FILE_SIZE" value="300000" />
   <input name="userfile[]" type="file" multiple/><br />
   <input type="submit" value="Send files" />
 </form>
 </body>
 </html>
 """
**もう少し複雑なページを考える。2つsubmitボタンがある場合 [#wc5b4969]
気をつけるのは、同じnameの下に複数の値が返される時、リストで返されるが、単一の時はリストでないので、その区別をすること。⇒ 参考 [[マニュアル:http://www.python.jp/doc/2.5/lib/node561.html]]

 #!/usr/bin/env python
 # -*- coding: utf-8 -*-
 # coding: utf-8
 import sys
 import codecs
 import os
 import cgi
 import cgitb
 cgitb.enable()
 
 sys.stdout = codecs.getwriter('utf_8')(sys.stdout)
 print ('Content-type: text/html; charset=UTF-8')
 print ("\r\n\r\n")
 
 datadir = "/var/www/html/fcs20/FILE/"
 form = cgi.FieldStorage()
 print("nameは" + str(form.name) + "<br>")
 print("valueは" + str(form.getvalue("submit")) + "<br>")
 print("タイプは" + str(form.type) + "<br>")
 
 if (form.getvalue("submit") == "消去"):
   print(str(form) + "<br>")
   if ("fnum" in form) :
     selectitem = form['fnum']
     print(str(selectitem) + "<br>")
     if (isinstance(selectitem,list)) :
       for i in range(0, len(selectitem)):
         print(str(selectitem[i].value))
     else:
       print(str(selectitem.value))
   else:
     print('no checks')
 
 else:
   if (form.type == "multipart/form-data"):
     fileitem = form['userfile[]']
 
     if (isinstance(fileitem, list)):
       # Multiple files
       print("  複数ファイルです<br>")
       print("  ファイル数は" + str(len(fileitem)) + "<br>")
       for i in range(0, len(fileitem)):
         print(str(fileitem[i].filename) + "<br>")
         #print(fileitem[i].value)
         #print("<br>=======================<br>")
         fp = open(datadir+str(fileitem[i].filename), "w+")
         # fp = open("/tmp/"+str(fileitem[i].filename), "r+")
         fp.write(fileitem[i].value)
     else:
       print ("  単数ファイルです<br>")
       print(str(fileitem.filename) + "<br>")
       #print(fileitem.value)
       fp = open(datadir+str(fileitem.filename), "w+")
       # fp = open("/tmp/"+str(fileitem.filename), "r+")
       fp.write(fileitem.value)
  
 print"""
 <html>
 <head>
 <META http-equiv="Content-Type" content="text/html;charset=utf-8">
 <title> FCS20 </title>
 </head>
 <body>
 <table>
 <tr><td width=300>
 """
 
 i = 0
 print("作業フォルダ内のファイル一覧<br>")
 print('<form action="fcs20.py" method="post" enctype="multipart/form-data">')
 print("<table><tr><td>")
 for f in (os.listdir(datadir)):
   print('<input type="checkbox" name=fnum value=' + str(i) +"> " + str(f) +  "</td></tr><tr><td>")
   i=i+1
 print("</tr></td></table>")
 print('<input type="submit" name="submit" value="消去" />')
 print("</form>")
 
 print"""
 </td><td>
 追加ファイルの転送<br>
 <form action="fcs20.py" method="post" enctype="multipart/form-data">
   <input type="hidden" name="MAX_FILE_SIZE" value="30000000" />
   <input width=300 name="userfile[]" type="file" multiple/><br />
   <input type="submit" name="submit" value="転送" />
 </form>
 </td></tr></table>
 </body>
 </html>
 """

*SELinuxで、public_htmlディレクトリの読み出しアクセス許可を出す [#d288bd68]
-ユーザのホーム /home/foo の許可ビットを少なくとも711以上にする(root権限必要)
-Webページのデータになるファイル(たとえば/home/foo/public_html/index.html)の許可ビットは、少なくとも755にする
-SELinuxのコマンド /sbin/restorecon -R /home/foo/public_html によって、
ファイル属性を変更する(root権限必要)。-Rはリカーシブ指定で、指定されたディレクトリの下を次々に繰り返し掘っていって変更する。
-ls -alZ /home/foo/public_html でファイルの属性を確認する。restorecon以前は~
 drwxr-xr-x. foo user unconfined_u:object_r:user_home_t:s0 public_html~
であったものが、restoreconによって~
 drwxr-xr-x. foo user unconfined_u:object_r:httpd_user_content_t:s0 public_html~
のように変化したはずである。
-python scriptをcgiで実行できるためには、追加で、~
 chcon -R -t httpd_sys_script_exec_t /home/foo/public_html/cgi-bin~
が必要で、その結果を ls -alZ /home/foo/public_html/ で見ると~
 -rwxr-xr-x. foo user unconfined_u:object_r:httpd_sys_script_exec_t:s0 mytest.py~
になった。これでOKらしい。

*SELinuxで、public_html/cgi-binディレクトリ以下のスクリプトでネットアクセスを許す [#k0622772]
-setsebool -P httpd_can_network_connect 1

なお、SELinuxのいろいろな許可ビットの一覧を見るには、
-getsebool -a | less


トップ   編集 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS