gmyth-stream/server/0.3/lib/request_handler.py
author renatofilho
Mon Aug 13 23:03:08 2007 +0100 (2007-08-13)
branchtrunk
changeset 802 d0e8c542c38e
parent 800 2b1824e138b1
child 803 b1d0964060b3
permissions -rw-r--r--
[svn r808] fixed some security bugs
     1 #!/usr/bin/env python
     2 
     3 __author__ = "Gustavo Sverzut Barbieri / Artur Duque de Souza"
     4 __author_email__ = "barbieri@gmail.com / artur.souza@indt.org.br"
     5 __license__ = "GPL"
     6 __version__ = "0.3"
     7 
     8 import os
     9 import cgi
    10 import socket
    11 import logging
    12 import urlparse
    13 import threading
    14 import SocketServer
    15 import BaseHTTPServer
    16 import mimetypes
    17 
    18 import lib.utils as utils
    19 import lib.file_handler as files
    20 import lib.transcoder as transcoder
    21 
    22 from log import Log
    23 
    24 __all__ = ("RequestHandler")
    25 
    26 class RequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
    27     """Class that implements an HTTP request handler for our server."""
    28     log = logging.getLogger("gms.request")
    29     def_transcoder = None
    30     transcoders = utils.PluginSet(transcoder.Transcoder)
    31     transcoders_log = Log()
    32     tid_queue = []
    33 
    34     menu = {
    35         "Log": "/get_log.do",
    36         "Stop": "/stop-transcoder.do",
    37         "Status": "/status.do",
    38         "All Log": "/get_all_log.do",
    39         "Version": "/version.do",
    40         "Shutdown": "/shutdown.do"
    41         }
    42 
    43     @classmethod
    44     def load_plugins_transcoders(cls, directory):
    45         cls.transcoders.load_from_directory(directory)
    46 
    47         if cls.def_transcoder is None and cls.transcoders:
    48             cls.def_transcoder = cls.transcoders[0].name
    49     # load_plugins_transcoders()
    50 
    51 
    52     def do_dispatch(self, body):
    53         self.url = self.path
    54         pieces = urlparse.urlparse(self.path)
    55         self.path = pieces[2]
    56         self.query = cgi.parse_qs(pieces[4])
    57 
    58         url = {
    59             "/": self.serve_main,
    60             "/shutdown.do": self.serve_shutdown,
    61             "/stop-transcoder.do": self.serve_stop_transcoder,
    62             "/status.do": self.serve_status,
    63             "/version.do": self.serve_version,
    64             "/new_id.do": self.serve_new_id,
    65             "/get_log.do": self.serve_get_log,
    66             "/get_all_log.do": self.serve_get_all_log,
    67             "/stream.do": self.serve_stream,
    68             "/transcode.do": self.serve_transcode,
    69             "/list.do": self.serve_list,
    70             "/get_file_info.do": self.serve_file_info,
    71             }
    72 
    73         try:
    74             url[self.path](body)
    75         except KeyError:
    76             try:
    77                 action = self.query.get("action", None)
    78                 if action and "stream.do" in action:
    79                     self.serve_stream(body)
    80                 elif os.path.exists("html/%s" % self.path):
    81                     data = open("html/%s" % self.path)
    82                     self.wfile.write(data.read())
    83                 else:
    84                     self.send_error(404, "File not found")
    85             except Exception, e:
    86                 self.log.error(e)
    87 
    88     # do_dispatch()
    89 
    90 
    91     def do_GET(self):
    92         self.do_dispatch(True)
    93     # do_GET()
    94 
    95 
    96     def do_HEAD(self):
    97         self.do_dispatch(False)
    98     # do_HEAD()
    99 
   100 
   101     def _nav_items(self):
   102         ret = ""
   103         for name, url in self.menu.items():
   104             ret += utils.getHTML("menu", {"name": name, "url": url})
   105         return ret
   106     # _nav_items()
   107 
   108 
   109     def serve_main(self, body):
   110         self.send_response(200)
   111         self.send_header("Content-Type", "text/html")
   112         self.send_header('Connection', 'close')
   113         self.end_headers()
   114         if body:
   115             self.wfile.write(utils.getHTML("index", {"menu": self._nav_items()}))
   116     # serve_main()
   117 
   118 
   119     def serve_version(self, body):
   120         self.send_response(200)
   121         self.send_header("Content-Type", "text/html")
   122         self.send_header('Connection', 'close')
   123         self.end_headers()
   124         if body:
   125             self.wfile.write("Version: %s" %  __version__)
   126     # serve_version
   127 
   128 
   129     def serve_shutdown(self, body):
   130         self.send_response(200)
   131         self.send_header("Content-Type", "text/html")
   132         self.send_header('Connection', 'close')
   133         self.end_headers()
   134         if body:
   135             self.wfile.write(utils.getHTML("shutdown"))
   136         self.server.server_close()
   137     # serve_shutdown()
   138 
   139 
   140     def serve_list(self, body):
   141         self.send_response(200)
   142         self.send_header("Content-Type", "text/html")
   143         self.send_header('Connection', 'close')
   144         self.end_headers()
   145 
   146         if body:
   147             file_list = []
   148             files.list_media_files(".transcoded", file_list)
   149             output = files.FileList(map(lambda x, y: x+y, file_list,
   150                                         ["<br>"]*len(file_list)))
   151             self.wfile.write(output)
   152 
   153     # serve_list()
   154 
   155 
   156     def serve_stop_all_transcoders(self, body):
   157         self.send_response(200)
   158         self.send_header("Content-Type", "text/html")
   159         self.send_header('Connection', 'close')
   160         self.end_headers()
   161         if body:
   162             self.server.stop_transcoders()
   163             self.wfile.write(utils.getHTML("stop_all",
   164                                            {"menu": self._nav_items()}))
   165     # serve_stop_all_transcoders()
   166 
   167 
   168     def serve_stop_selected_transcoders(self, body, tids=[]):
   169         self.send_response(200)
   170         self.send_header("Content-Type", "text/html")
   171         self.send_header('Connection', 'close')
   172         self.end_headers()
   173         opts = ""
   174         if body:
   175             transcoders = self.server.get_transcoders()
   176 
   177             for tid in tids:
   178                 for t, r in transcoders:
   179                     if t.tid == int(tid):
   180                         try:
   181                             t.stop()
   182                         except Exception, e:
   183                             self.log.info("Plugin already stopped")
   184 
   185                         opts += utils._create_html_item("%s" % t)
   186 
   187                         break
   188 
   189                 self.wfile.write(utils.getHTML("stop_selected",
   190                                                {"menu": self._nav_items(),
   191                                                 "opts": opts}))
   192     # serve_stop_selected_transcoders()
   193 
   194 
   195     def serve_stop_transcoder(self, body):
   196         req = self.query.get("request", None)
   197         tid = self.query.get("tid", None)
   198         if req and "all" in req:
   199             self.serve_stop_all_transcoders(body)
   200         elif tid:
   201             self.serve_stop_selected_transcoders(body, tid[0].split(";"))
   202         else:
   203             self.serve_status(body)
   204     # serve_stop_transcoder()
   205 
   206 
   207     def serve_status(self, body):
   208         self.send_response(200)
   209         self.send_header("Content-Type", "text/html")
   210         self.send_header('Connection', 'close')
   211         self.end_headers()
   212         stopone = ""
   213         running = ""
   214         stopall = ""
   215 
   216         if body:
   217             tl = self.server.get_transcoders()
   218             if not tl and not self.query.get("tid", None) and \
   219                    not self.query.get("running", None):
   220                 running = "<p>No running transcoder.</p>\n"
   221 
   222             elif not tl and self.query.get("tid", None):
   223                 tids = self.query.get("tid")
   224                 for tid in tids:
   225                     stat = self.transcoders_log.get_status(int(tid))
   226                     self.wfile.write("%s<br>" % stat)
   227                 return True
   228 
   229             elif self.query.get("running", None):
   230                 for transcoder, request in tl:
   231                     outf = transcoder.params_first("outfile")
   232                     tid = transcoder.tid
   233                     self.wfile.write("%s:%s<br>" % (tid, outf))
   234                 return True
   235 
   236             elif self.query.get("tid", None):
   237                 req_tid = self.query.get("tid")
   238                 for transcoder, request in tl:
   239                     if str(transcoder.tid) in req_tid:
   240                         self.wfile.write("Status:%s:%s %%" % (\
   241                             transcoder.tid, transcoder.status))
   242                         return True
   243                 stat = self.transcoders_log.get_status(int(req_tid[0]))
   244                 self.wfile.write("%s<br>" % stat)
   245                 return True
   246 
   247             else:
   248                 running = "<p>Running transcoders:</p>\n"
   249                 stopall = utils._create_html_item("<a href='%s?request=all'>"
   250                                                  "[STOP ALL]</a>" %
   251                                                  self.menu["Stop"])
   252 
   253                 for transcoder, request in tl:
   254                     stopone += utils._create_html_item("%s;"
   255                                                        "<a href='%s?tid=%s'>"
   256                                                        " [STOP] </a>") % (
   257                         transcoder, self.menu["Stop"], transcoder.tid)
   258 
   259             self.wfile.write(utils.getHTML("status",
   260                                            {"menu": self._nav_items(),
   261                                             "running": running,
   262                                             "stopall": stopall,
   263                                             "stopone": stopone}))
   264     # serve_status()
   265 
   266 
   267     def _get_transcoder(self):
   268         # get transcoder option: mencoder is the default
   269         request_transcoders = self.query.get("transcoder", ["mencoder"])
   270 
   271         for t in request_transcoders:
   272             transcoder = self.transcoders.get(t)
   273             if transcoder:
   274                 return transcoder
   275 
   276         if not transcoder:
   277             return self.transcoders[self.def_transcoder]
   278     # _get_transcoder()
   279 
   280 
   281     def _get_new_id(self, tid):
   282         self.server.last_tid = utils.create_tid(tid)
   283         self.tid_queue.append(self.server.last_tid)
   284         return self.server.last_tid
   285     # _get_new_id()
   286 
   287 
   288     def serve_new_id(self, body):
   289         self.send_response(200)
   290         self.send_header("Content-Type", "text/html")
   291         self.send_header('Connection', 'close')
   292         self.end_headers()
   293 
   294         if body:
   295             self.wfile.write("%s" % self._get_new_id(self.server.last_tid))
   296     # serve_new_id()
   297 
   298     def serve_get_log(self, body):
   299         self.send_response(200)
   300         self.send_header("Content-Type", "text/html")
   301         self.send_header('Connection', 'close')
   302         self.end_headers()
   303 
   304         if body:
   305             if self.query.get("tid", None):
   306                 tid = int(self.query.get("tid")[0])
   307                 stat = self.transcoders_log.get_status(tid)
   308                 self.wfile.write("Status: %s" % stat)
   309             else:
   310                 stat = self.transcoders_log.get_status()
   311                 for rtid, status in stat.iteritems():
   312                     self.wfile.write("<b>%s</b>: %s<br><br>" % (rtid, status))
   313     # serve_get_log()
   314 
   315     def serve_get_all_log(self, body):
   316         self.send_response(200)
   317         self.send_header("Content-Type", "text/html")
   318         self.send_header('Connection', 'close')
   319         self.end_headers()
   320 
   321         if body:
   322             if self.query.get("tid", None):
   323                 tid = int(self.query.get("tid")[0])
   324                 stat = self.transcoders_log.get_status(tid, True)
   325                 for status in stat:
   326                     self.wfile.write("%s<br><br>" % status)
   327             else:
   328                 stat = self.transcoders_log.get_status(None, True)
   329                 for rtid, history in stat.iteritems():
   330                     for status in history:
   331                         self.wfile.write("<b>%s</b>: %s<br>" % (rtid, status))
   332                     self.wfile.write("<br><br>")
   333     # serve_get_all_log()
   334 
   335 
   336     def serve_file_info(self, body):
   337         if body:
   338 
   339             file_dat = self.query.get("file", None)
   340 
   341             if file_dat:
   342                 self.send_response(200)
   343                 self.send_header("Content-Type", "text/html")
   344                 self.send_header('Connection', 'close')
   345                 self.end_headers()
   346 
   347                 try:
   348                     opts = files.TranscodedFile(file_dat[0], self.query).opts
   349                     for key in opts.keys():
   350                         self.wfile.write("%s=%s<br>" % (key, opts.get(key, "None")[0]))
   351 
   352                 except Exception, e:
   353                     self.send_error(500, str(e))
   354                     return
   355     # serve_file_info()
   356 
   357     def serve_stream(self, body):
   358         filename = self.query.get("file", None)[0]
   359 
   360         if not filename:
   361             self.send_error(404, "File not found")
   362             return
   363 
   364         #Only stream files on .transcode dir
   365         filename = ".transcode/" + os.path.basename(filename)
   366         if not os.path.exists (filename):
   367             self.send_error(404, "File not found")
   368             return
   369 
   370         size = int(os.path.getsize(filename))
   371         self.send_response(200)
   372         self.send_header("Content-Type", mimetypes.guess_type(filename)[0])
   373         self.send_header("Cache-Control","no-cache")
   374         self.send_header("Content-Length", size)
   375         #self.send_header("Connection", "close")
   376         self.end_headers()
   377 
   378         media = open(filename)
   379         data_in = " "
   380         total_read = 0
   381 
   382         test_tid = int(self.query.get("tid", "0")[0])
   383         if test_tid == 0 or test_tid not in self.tid_queue:
   384             test_tid = self._get_new_id(self.server.last_tid)
   385 
   386         self.transcoders_log.insert(test_tid, "gms.Stream: %s" % filename)
   387 
   388         try:
   389             while data_in != "":
   390                 data_in = media.read(4096)
   391                 total_read += 4096
   392                 self.wfile.write(data_in)
   393                 status = utils.progress_bar(total_read, size, 50)
   394                 msg_status = "Status:%s:%s%%" % (test_tid, status)
   395                 self.transcoders_log._update_status(test_tid, msg_status)
   396 
   397             self.transcoders_log._update_status(test_tid, "OK: Done")
   398 
   399         except Exception, e:
   400             self.log.error("Stream error: %s" %e)
   401             self.transcoders_log._update_status(test_tid, "Error: %s" % e)
   402     # serve_stream()
   403 
   404     def serve_transcode(self, body):
   405         type = self.query.get("type", None)[0]
   406         if type.upper() == "FILE":
   407             self.send_error(404, "Transcode local files not allowed")
   408             return
   409 
   410         transcoder = self._get_transcoder()
   411         try:
   412             obj = transcoder(self.query)
   413         except Exception, e:
   414             self.send_error(500, str(e))
   415             return
   416 
   417         self.send_response(200)
   418         self.send_header("Content-Type", obj.get_mimetype())
   419         self.send_header("Cache-Control","no-cache")
   420 
   421         if (obj.name == "gmencoder"):
   422             self.send_header("Transfer-Encoding", "chunked")
   423 
   424         #self.send_header("Connection", "close")
   425         self.end_headers()
   426 
   427         if body:
   428             test_tid = int(self.query.get("tid", "0")[0])
   429             if test_tid == 0 or test_tid not in self.tid_queue:
   430                 test_tid = self._get_new_id(self.server.last_tid)
   431 
   432             if self.query.get("transcoder", None):
   433                 self.transcoders_log.insert(test_tid, "gms.%s" % obj.name)
   434                 obj.tid = test_tid
   435                 obj.log = self.transcoders_log
   436 
   437                 self.server.add_transcoders(self, obj)
   438                 obj.start(self.wfile)
   439                 self.server.del_transcoders(self, obj)
   440                 files.TranscodedFile("", self.query)
   441 
   442     # serve_stream()
   443 
   444 
   445     def log_request(self, code='-', size='-'):
   446         self.log.info('"%s" %s %s', self.requestline, str(code), str(size))
   447     # log_request()
   448 
   449 
   450     def log_error(self, format, *args):
   451         self.log.error("%s: %s" % (self.address_string(), format % args))
   452     # log_error()
   453 
   454 
   455     def log_message(self, format, *args):
   456         self.log.info("%s: %s" % (self.address_string(), format % args))
   457     # log_message()
   458 
   459 # RequestHandler