source: orange/install-scripts/orngServer/orngServerFilesServer.py @ 6538:a5f65d7f0b2c

Revision 6538:a5f65d7f0b2c, 12.2 KB checked in by Mitar <Mitar@…>, 4 years ago (diff)

Made XPM version of the icon 32x32.

Line 
1import sys
2sys.path.insert(0,"../CherryPy-3.1.0")
3
4import cherrypy
5print "Loaded CherryPy version", cherrypy.__version__
6
7import os
8import shutil
9import re
10import hashlib
11import threading
12import cgi
13pj = os.path.join
14import datetime
15
16basedir = pj(os.getcwd(), "..", "orngServerData")
17
18userfilename = '../orngServerFilesUsers.txt'
19
20def readUserFile():
21    s = open(userfilename, 'rt').read()
22    umap = {}
23    for line in s.split('\n'):
24        try:
25            uname, passw = line.split(',')
26            umap[uname] = passw
27        except:
28            pass
29    print "USERS", umap.keys()
30    return umap
31
32def noBodyProcess():
33    """Sets cherrypy.request.process_request_body = False, giving
34    us direct control of the file upload destination. By default
35    cherrypy loads it to memory, we are directing it to disk."""
36    #cherrypy.request.process_request_body = False
37    print "noBodyProcess"
38    print "LOGIN", cherrypy.request.login
39    print "PROCESS RB", cherrypy.request.process_request_body
40    if not cherrypy.request.login:
41        cherrypy.request.process_request_body = False
42   
43cherrypy.tools.noBodyProcess = cherrypy.Tool('on_start_resource', noBodyProcess, priority=20)
44
45class FileInfo(object):
46
47    separ = '|||||'
48
49    def __init__(self, fname):
50        self.fname = fname
51        self.set()
52
53    def set(self, name=None, protection=None, datetime=None, title=None, tags=[]):
54        self.name = name
55        self.protection = protection
56        self.datetime = datetime
57        self.title = title
58        self.tags = tags
59   
60    def load(self):
61        f = open(self.fname, 'rb')
62        cont = f.read()
63        f.close()
64        #print "CONT", cont
65        name, protection, datetime, title, tags = cont.split("\n")
66        tags = tags.split(";")
67        self.set(name, protection, datetime, title, tags)
68
69    def userInfo(self):
70        return self.separ.join([\
71            str(os.stat(self.fname + ".file").st_size), \
72            str(self.datetime), \
73            self.title, \
74            ";".join(self.tags) \
75            ])
76
77    def save(self, fname=None):
78        if not fname:
79            fname = self.fname
80        f = open(fname, 'wb')
81        cont = '\n'.join([self.name, self.protection, str(self.datetime), \
82            self.title, ";".join(self.tags)])
83        #print "WRITING", cont
84        f.write(cont)
85        f.close()
86
87    def exists(self):
88        """
89        If file info already exists as a file.
90        """
91        return self.protection != None
92
93
94"""
95Only one client can edit data for now!
96FIXME: allow multiple clients changing different files.
97Try locking a specific basename!
98"""
99sem = threading.BoundedSemaphore()
100
101def lock(**kwargs):
102    #print "locking"
103    sem.acquire()
104
105def unlock(**kwargs):
106    sem.release()
107    #print "unlocking"
108
109cherrypy.tools.lock = cherrypy.Tool('on_start_resource', lock, priority=2)
110cherrypy.tools.unlock = cherrypy.Tool('on_end_request', unlock)
111"""
112End of simple locking tools
113"""
114
115rec = re.compile("[^A-Za-z0-9\-\.\_]")
116def safeFilename(s):
117    return rec.sub("", s)
118
119def hash(s):
120    """
121    May hashing function.
122    """
123    return hashlib.sha256(s).hexdigest()
124
125def baseDomain(domain):
126    domain = safeFilename(domain) #force safe domain
127    return pj(basedir, domain)
128
129def baseFilename(domain, filename):
130    """
131    Return base filename for saving on disk: composed of only
132    lowercase characters. First part are first 100 alphanumeric
133    characters from the filename, next its hash.
134    """
135    return pj(baseDomain(domain), \
136        safeFilename(filename.lower())[:100] + "." + hash(filename))
137
138def fileInfo(domain, filename):
139    """
140    Each file is saved in two files: its index and its data.
141    It is possible that multiple files get the same name. Therefore
142    enumerate them. If filename is the same also in index, then this
143    is the same file.
144    Returns file's FileInfo. If file does not exists, then its fileinfo
145    has only attribute fname.
146    """
147    basename = baseFilename(domain, filename)
148    candidate = 1
149    filei = None
150    while 1:
151        try: 
152            fileit = FileInfo(basename + "." + str(candidate))
153            fileit.load()
154            if fileit.name == filename:
155                filei = fileit
156                break
157        except IOError:
158            break # no file - file is free to be taken
159        candidate += 1
160    if not filei:
161        filei = FileInfo(basename + "." + str(candidate))
162    return filei
163
164def userFileInfo(domain, filename, protected=False):
165    fi = fileInfo(domain, filename)
166    if accessAllowed(fi, protected):
167        return fi.userInfo()
168    else:
169        return "None"
170
171def accessAllowed(fi, protected):
172    """
173    protected == access_code or True (allow everything) or False (public access)
174
175    Allow access if:
176    - there is no protection or
177    - protected == True -> administrative account
178    - fi.protection == protected and it is not "1"
179    """
180    #return fi.protection == "0" or protected == True or (fi.protection == protected)
181    return fi.protection == "0" or protected == True or (fi.protection != "1" and fi.protection == protected)
182
183def downloadFile(domain, filename, protected=False):
184    fi = fileInfo(domain, filename)
185    if accessAllowed(fi, protected):
186        return cherrypy.lib.static.serve_file(fi.fname + ".file", "application/x-download", "attachment", filename)
187    else:
188        raise cherrypy.HTTPError(500, "File not available!")
189
190def listFilesL(domain, protected=False):
191    dir = baseDomain(domain)
192    files = [ a for a in os.listdir(dir) if a[-1].isdigit() ]
193    okfiles = []
194    for file in files:
195        fi = FileInfo(pj(dir, file))
196        try:
197            fi.load() 
198            if fi.exists() and accessAllowed(fi, protected):
199                okfiles.append(fi.name)
200        except:
201            pass
202
203    return okfiles
204
205def listFiles(domain, protected=False):
206    return "|||||".join(listFilesL(domain, protected=protected))
207
208def listdomainsL():
209    dir = basedir
210    files = [ a for a in os.listdir(dir) ]
211    ok = []
212    for file in files:
213        if os.path.isdir(os.path.join(dir, file)):
214            ok.append(file)
215    return ok
216
217def listdomains():
218    return "|||||".join(listdomainsL())
219
220def allFileInfosL(domain, protected=False):
221    files = listFilesL(domain, protected=protected)
222    out = []
223    for filename in files:
224        info = userFileInfo(domain, filename, protected=protected)
225        out.append((filename, info))
226    return out
227
228def allFileInfos(domain, protected=False):
229    ai = allFileInfosL(domain, protected=protected)
230    return [ "[[[[[".join( [ a + "=====" + b for a,b in ai ] ) ]       
231
232class RootServer:
233    @cherrypy.expose
234    def index(self):
235        return """"""
236
237class PublicServer:
238
239    @cherrypy.expose
240    def index(self):
241        return """"""
242
243    @cherrypy.expose
244    def info(self, domain, filename, access_code=None):
245        return userFileInfo(domain, filename, protected=access_code)
246
247    @cherrypy.expose
248    def allinfo(self, domain, access_code=None):
249        return allFileInfos(domain, protected=access_code)
250
251    @cherrypy.expose
252    def download(self, domain, filename, access_code=None):
253        return downloadFile(domain, filename, protected=access_code)
254
255    @cherrypy.expose
256    def list(self, domain, access_code=None):
257        return listFiles(domain, protected=access_code)
258
259    @cherrypy.expose
260    def listdomains(self):
261        return listdomains()
262
263class SecureServer:
264
265    @cherrypy.expose
266    def index(self):
267        return """"""
268
269    @cherrypy.expose
270    def info(self, domain, filename):
271        return userFileInfo(domain, filename, protected=True)
272
273    @cherrypy.expose
274    def allinfo(self, domain):
275        return allFileInfos(domain, protected=True)
276
277    @cherrypy.expose
278    def download(self, domain, filename):
279        return downloadFile(domain, filename, protected=True)
280
281    @cherrypy.expose
282    def list(self, domain):
283        return listFiles(domain, protected=True)
284
285    @cherrypy.expose
286    def createdomain(self, domain):
287        """
288        Creates domain. If creation is successful, return 0, else
289        return error.
290        """
291        dir = baseDomain(domain)
292        os.mkdir(dir)
293        return "0"
294
295    @cherrypy.expose
296    def removedomain(self, domain, force=False):
297        """
298        Removes domain. If successful return 0, else
299        return error.
300        """
301        dir = baseDomain(domain)
302        if not force:
303            os.rmdir(dir)
304        else:
305            shutil.rmtree(dir)
306        return '0'
307
308    @cherrypy.expose
309    @cherrypy.tools.lock()
310    @cherrypy.tools.unlock()
311    def remove(self, domain, filename):
312        fi = fileInfo(domain, filename)
313        if fi.exists(): #valid file
314            os.remove(fi.fname)
315            os.remove(fi.fname+".file")
316            return "0"
317        else:
318            raise cherrypy.HTTPError(500, "File does not exists.")
319           
320    @cherrypy.expose
321    @cherrypy.tools.lock()
322    @cherrypy.tools.unlock()
323    def upload(self, domain, filename, title, tags, data):
324
325        fi = fileInfo(domain, filename)
326        #print data.file.name
327
328        fupl = open(fi.fname + ".uploading", 'wb')
329        shutil.copyfileobj(data.file, fupl, 1024*8) #copy with buffer
330        fupl.close()
331        #print "transfer successful?" #TODO check this - MD5?
332
333        #TODO is there any difference in those files?
334
335        fupl = open(fi.fname + ".uploading", 'rb')
336        ffin = open(fi.fname + ".file", 'wb')
337        shutil.copyfileobj(fupl, ffin) #copy with buffer
338        ffin.close()
339        fupl.close()
340
341        #remove file copy
342        os.remove(fi.fname + ".uploading")
343
344        datetime_now = str(datetime.datetime.utcnow())
345        fi.datetime = datetime_now
346        fi.name = filename
347        if fi.protection == None:
348            fi.protection = "1"  #only administrative access by default
349        fi.title = title
350        fi.tags = tags.split(";")
351
352        fi.save()
353
354    @cherrypy.expose
355    @cherrypy.tools.lock()
356    @cherrypy.tools.unlock()
357    def protect(self, domain, filename, access_code="1"):
358        fi = fileInfo(domain, filename)
359        if fi.exists():
360            fi.protection = access_code
361            fi.save()
362        else:
363            raise cherrypy.HTTPError(500, "File does not exists.")
364
365    @cherrypy.expose
366    @cherrypy.tools.lock()
367    @cherrypy.tools.unlock()
368    def unprotect(self, domain, filename):
369        #DEPRECATED
370        fi = fileInfo(domain, filename)
371        if fi.exists():
372            fi.protection = "0"
373            fi.save()
374        else:
375            raise cherrypy.HTTPError(500, "File does not exists.")
376
377    @cherrypy.expose
378    @cherrypy.tools.lock()
379    @cherrypy.tools.unlock()
380    def protection(self, domain, filename):
381        fi = fileInfo(domain, filename)
382        if fi.exists():
383            return fi.protection
384        else:
385            raise cherrypy.HTTPError(500, "File does not exists.")
386
387    @cherrypy.expose
388    def listdomains(self):
389        return listdomains()
390
391
392"""
393Tools for enforcing security measures.
394Also,
395"""
396
397def force_secure(header="Secure"):
398    secure = cherrypy.request.headers.get(header, False)
399    if not secure:
400        raise cherrypy.HTTPError(500, "Use ssl!")
401
402def force_insecure(header="Secure"):
403    secure = cherrypy.request.headers.get(header, False)
404    if secure:
405        raise cherrypy.HTTPError(500, "Do not use ssl!")
406
407cherrypy.tools.secure = cherrypy.Tool('on_start_resource', force_secure, priority=1)
408cherrypy.tools.insecure = cherrypy.Tool('on_start_resource', force_insecure, priority=1)
409
410# remove any limit on the request body size; cherrypy's default is 100MB
411cherrypy.server.max_request_body_size = 0
412
413def buildServer():
414    users = readUserFile()
415
416    conf = {'global': { 'log.screen': False,
417                        'log.access_file': 'af.log',
418                        'log.error_file': 'ef.log' },
419            '/public': { 'tools.insecure.on': True},
420            '/private': { 'tools.secure.on': True,
421                       'tools.basic_auth.on': True,
422                       'tools.basic_auth.realm': 'orngFileServer',
423                       'tools.basic_auth.users': users,
424                       'tools.basic_auth.encrypt': lambda x: x,
425                      }}
426
427    root = RootServer()
428    root.public = PublicServer()
429    root.private = SecureServer()
430
431    return root, conf
432
433if __name__ == '__main__':
434   
435    root, conf = buildServer()
436    cherrypy.tree.mount(root, '/', conf)
437    cherrypy.engine.start()
438    cherrypy.engine.block()
439
Note: See TracBrowser for help on using the repository browser.