################################################################################
# PMP Server
# Copyright (C) 2006 David Voswinkel
# 
# Homepage: http://optixx.org
# E-mail:   david@optixx.org
# 
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.

# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
################################################################################

#################################################################################
#$LastChangedDate: 2006-02-18 20:16:19 +0100 (Sa, 18 Feb 2006) $
#$LastChangedRevision: 6 $
#$LastChangedBy: david $
#$HeadURL: http://127.0.0.1/svn/repos/pmp_mod/trunk/pmp_server.py $
#$Id: pmp_server.py 6 2006-02-18 19:16:19Z david $
#################################################################################

# curl "http://localhost:3333/open?filename=movie.avi&flags=O_RDONLY" 
# curl "http://localhost:3333/read?fd=1&size=100" 
# curl "http://localhost:3333/lseek?fd=1&offset=1000&whence=1"
# curl "http://localhost:3333/close?fd=1"




import re
import os
import sys
import string
import linecache
import traceback
import BaseHTTPServer
import threading
import urllib
import urlparse
import types
import urllib
import time 
import platform

   
if platform.system() in ['Windows','Microsoft']:
    ROOT="d:\Music"
else:
    ROOT="/movies"

 

class App(object):
    
    def __new__(cls, *args, **kwds):
        gui  = args[0]
	if gui:
	    obj = GuiApp.__new__(GuiApp)
	else:
	    obj = CliApp.__new__(CliApp)
	obj.__init__()
        return obj 
  
class CliApp(object):

    def __init__(self):
	pass

    def run(self):
	try:
	    httpd = HTTPServer(('',3333),FileIOServer)
	    httpd.register(self) 
	    while True: 
		httpd.handle_request()
	except Exception, argument:
	    et, ev, tb = sys.exc_info()
	    sys.stderr.write('#' * 60 + '\n')
	    sys.stderr.write('Exception:%s %s\n' % (et,ev))
	    while tb:
		co = tb.tb_frame.f_code
		filename = co.co_filename
		linenum =  traceback.tb_lineno(tb),
		line = linecache.getline(filename,linenum[0])
		line = re.compile('[\n\t]+|[ ]{2,1000}').sub(' ',line)
		sys.stderr.write("File: %s Line: %6s Code: '%s' \n" % (filename,linenum[0],line))
		tb = tb.tb_next
	    sys.stderr.write('#' * 60 + '\n')
	return

    def log(self,s):
	ts = time.strftime("%Y-%m-%d %H:%M:%S",time.localtime())
	sys.stdout.write("[%s] %s" % (ts,s));

    


def banner():
    sys.stdout.write("#"*80 +"\n")
    sys.stdout.write("# PMP Server by www.optixx.org\n")
    sys.stdout.write("#"*80 +"\n")
    sys.stdout.write("ROOT='%s'\n" % ROOT)
     
class HTTPServer(BaseHTTPServer.HTTPServer):

    def __init__(self,addr,handler):
        BaseHTTPServer.HTTPServer.__init__(self,addr,handler)
        self.RequestHandlerClass.subjects = {}
	self.fds = {}
    
    def register(self,p):
	self.RequestHandlerClass.p = p


class FileIOObject:

    def __init__(self,fd,name):
	self.name = name
	self.mutex = threading.Semaphore(1)
	self.fd = fd

class FileIOServer(BaseHTTPServer.BaseHTTPRequestHandler):

    __fds      = {}
    __fd_max   = 0
    __mutex    = threading.Semaphore(1) 
    server_version = "PMPServer"
   
   
    def log_message(self, format, *args):
        sys.stdout.write("[%s] %s\n" % (self.address_string(),format%args))

    def do_POST(self):
        self.do_get()
        
    def do_GET(self):
	uri = urlparse.urlparse(self.path)
	action 	= uri[2].replace("/","")
	args    = uri[4].split("&")
	if not isinstance(args,types.ListType):
	    self.send(str(0))
	    return 
	if action not in ['open','close','lseek','read','listdir','stat','fstat']:
	   self.send(str(0))
	   return 
	kwds = {}
	for arg in args:
	    if arg.find("=")<0:
		continue
	    tmp = arg.split("=")
	    kwds[tmp[0]] = urllib.unquote(tmp[1])
	args = []
	func = getattr(self, 'FileIO' + action)
	data = func(*args,**kwds)
	self.send(data)
	
    def send(self,data):
	self.wfile.write("HTTP/1.0 200 OK\r\n")
	self.send_header("Content-type","video/mpeg")
	self.end_headers()
	self.wfile.write(data)
	self.wfile.flush()
	
    def FileIOopen(self,*args,**kwds):
	try:
	    filename = os.path.join(ROOT,urllib.unquote_plus(kwds['filename']))
	    if not kwds.has_key('flags'):
		flags = "O_RDONLY"
	    else:
		flags = kwds['flags']
	    if not hasattr(os,flags):
		flags = "O_RDONLY"
	    flags = getattr(os,flags)
	    if not os.path.isfile(filename):
		self.p.log("ERROR: no file '%s'\n" % filename)
		return "-1"
	    FileIOServer.__mutex.acquire()
	    FileIOServer.__fd_max =  FileIOServer.__fd_max +1
	    net_fd = FileIOServer.__fd_max
	    if platform.system() in  ['Windows','Microsoft']: 
		flags |= os.O_BINARY  
	    fileno = os.open(filename,flags)
	    fo = FileIOObject(fileno,filename)
	    FileIOServer.__fds[net_fd] = fo
	    self.p.log("[Open  net:%03i fd:%03i '%s']\n" % ( net_fd,fileno,filename)) 
	except:
	    et, ev, tb = sys.exc_info()
	    self.p.log("ERROR: %s %s\n" % (et,ev))
	    FileIOServer.__mutex.release() 
	    return "-1"
	FileIOServer.__mutex.release()
	return net_fd
    
    def FileIOstat(self,*args,**kwds):
	try:
	    filename = os.path.join(ROOT,urllib.unquote_plus(kwds['filename']))
	    if os.path.isfile(filename):
		ret = os.stat(filename)
		ret_buf = str()
		for val in ret:
		    ret_buf += "%i " % val
	    else:
		return "-1"
	    self.p.log("[Stat  net:000 fd:000 '%s' (%s)]\n" % ( filename,ret_buf)) 
	except:
	    et, ev, tb = sys.exc_info()
	    self.p.log("ERROR: %s %s\n" % (et,ev))
	    return "-1"
	return ret_buf
      
    def FileIOread(self,*args,**kwds):
	try:
	    fo	    = None  
	    net_fd  = int(kwds['fd'])
	    size    = int(kwds['size'])
	    FileIOServer.__mutex.acquire()
	    if not FileIOServer.__fds.has_key(net_fd):
		FileIOServer.__mutex.release()
		return "-1"
	    
	    fo = FileIOServer.__fds[net_fd]
	    fo.mutex.acquire()
	    FileIOServer.__mutex.release()
	    recv_data=os.read(fo.fd,size)
	    recv_len = len(recv_data)
	    self.p.log("[Read  net:%03i fd:%03i '%s' '%i' of '%i' bytes]\n" % ( net_fd, fo.fd,fo.name, recv_len,size))
	    fo.mutex.release()    
	except:
	    FileIOServer.__mutex.release() 
	    if fo:
		fo.mutex.release()
	    et, ev, tb = sys.exc_info()
	    self.p.log("ERROR: %s %s\n" % (et,ev))
	    return "-1"
	
	return recv_data
   
    def FileIOfstat(self,*args,**kwds):
	try:
	    fo	    = None  
	    net_fd  = int(kwds['fd'])
	    FileIOServer.__mutex.acquire()
	    if not FileIOServer.__fds.has_key(net_fd):
		FileIOServer.__mutex.release()
		return "-1"
	    
	    fo = FileIOServer.__fds[net_fd]
	    fo.mutex.acquire()
	    FileIOServer.__mutex.release()
	    ret = os.fstat(fo.fd)
	    ret_buf = str()
	    for val in ret:
		ret_buf += "%i " % val
	    self.p.log("[Fstat net:%03i fd:%03i '%s' (%s)]\n" % (net_fd, fo.fd,fo.name,ret_buf))
	    fo.mutex.release()    
	except:
	    FileIOServer.__mutex.release() 
	    if fo:
		fo.mutex.release()
	    et, ev, tb = sys.exc_info()
	    self.p.log("ERROR: %s %s\n" % (et,ev))
	    return "-1"
	
	return ret_buf
    
   
    def FileIOlseek(self,*args,**kwds):
	try:
	    fo	    = None  
	    net_fd  = int(kwds['fd'])
	    offset  = int(kwds['offset'])
	    whence  = int(kwds['whence'])
	    FileIOServer.__mutex.acquire()
	    if not FileIOServer.__fds.has_key(net_fd):
		FileIOServer.__mutex.release()
		return "-1"
	    
	    fo = FileIOServer.__fds[net_fd]
	    fo.mutex.acquire()
	    FileIOServer.__mutex.release()
	    ret  = os.lseek(fo.fd,offset,whence)
	    self.p.log("[Seek  net:%03i fd:%03i '%s' offset '%i']\n" % (net_fd, fo.fd,fo.name,ret))
	    fo.mutex.release()    
	except:
	    FileIOServer.__mutex.release() 
	    if fo:
		fo.mutex.release()
	    et, ev, tb = sys.exc_info()
	    self.p.log("ERROR: %s %s\n" % (et,ev))
	    return "-1"
	return str(ret)
    
    def FileIOclose(self,*args,**kwds):
	try:
	    fo	    = None  
	    net_fd  = int(kwds['fd'])
	    FileIOServer.__mutex.acquire()
	    if not FileIOServer.__fds.has_key(net_fd):
		FileIOServer.__mutex.release()
		return "-1"
	    
	    fo = FileIOServer.__fds[net_fd]
	    ret  = os.close(fo.fd)
	    self.p.log("[Close net:%03i fd:%03i '%s']\n" % (net_fd, fo.fd,fo.name))
	    fo.mutex.release()   
	    del  FileIOServer.__fds[net_fd]
	    FileIOServer.__mutex.release()
	except:
	    FileIOServer.__mutex.release() 
	    if fo:
		fo.mutex.release()
	    et, ev, tb = sys.exc_info()
	    self.p.log("ERROR: %s %s\n" % (et,ev))
	    return "-1"
	return str(0)
    
    def FileIOlistdir(self,*args,**kwds):
	try:
	    path = os.path.join(ROOT,kwds['path'])
	    if kwds.has_key("size"):
		size = int(kwds['size'])
	    else:
		size = 4 * 1024
	    cnt = 0
	    dlist = str()
	    d = os.listdir(path)
	    for dent in d:
		if dent.startswith("."):
		    continue
		ext = dent.split(".")
		if not len(ext):
		    continue
		if ext.pop().lower() not in ['pmp','avi','mpg','mpeg','divx','xvid', 'ts', 'asf', 'm4a', 'mp3']:
		    continue
		if os.path.isfile(os.path.join(path,dent)):
		    next = "net:/%s\n" % (dent)
		    if len(dlist) + len(next) > size:
			break 
		    dlist += next 
		    cnt +=1
	    self.p.log("[Dir   net:000 fd:000 '%s' %i files]\n" % (path,cnt))
	except:
	    return "-1"
	return dlist
      
 

if __name__ == '__main__':
    try:
	from qt import *
	try:
	    from qt import QThread
	except:
	    print "Thread support not enabled"
	    sys.exit(1)
	GUI = True
    except:
	GUI = False
 
  
    app = App(False)
    app.run()
    

    
