filesystem.py 24.9 KB
Newer Older
Marcel's avatar
Marcel committed
1
# -*- coding: utf-8 -*-
2

Marcel's avatar
Marcel committed
3
# imports
4
5
from StringIO import StringIO
from distutils.spawn import find_executable
6
7
from mimetypes import guess_type
from zipfile import ZipFile
8
from threading import Lock
9
import ConfigParser
10
import json
11
12
13
14
import logging
import os
import re
import shutil
Gero Müller's avatar
Gero Müller committed
15
import stat
16
import subprocess
17
18
import fsmonitor
import vispa
19
20
21

try:
    import Image
Gero Müller's avatar
Gero Müller committed
22
    import ImageFilter
23
24
25
26
27
28
29
30
31
32
    HAVE_PIL = True
except:
    HAVE_PIL = False

if not HAVE_PIL:
    convert_executable = find_executable('convert')
    if convert_executable and os.path.isfile(convert_executable):
        HAVE_CONVERT = True
    else:
        HAVE_CONVERT = False
33

34
35
logger = logging.getLogger(__name__)

36

37
38
class FileSystem(object):

39
40
    FILE_EXTENSIONS = ["png", "jpg", "jpeg", "bmp", "ps", "eps", "pdf",
                       "txt", "xml", "py", "c", "cpp", "root", "pxlio"]
Marcel's avatar
Marcel committed
41
42
43
    BROWSER_EXTENSIONS = ["png", "jpg", "jpeg", "bmp"]
    ADDITIONAL_MIMES = {
        "pxlio": "text/plain",
44
        "root": "text/plain"
Marcel's avatar
Marcel committed
45
    }
46
47
    PRIVATE_WORKSPACE_CONF = "~/.vispa/workspace.ini"
    GLOBAL_WORKSPACE_CONF = "/etc/vispa/workspace.ini"
48

49
    def __init__(self, userid, workspaceid):
50
51
        # allowed extensions
        self.allowed_extensions = FileSystem.FILE_EXTENSIONS
52
53
        self._userid = userid
        self._workspaceid = workspaceid
54
55
56
57
        self._monitor_thread = fsmonitor.FSMonitorThread(self._monitor_callback)
        self._monitor_watches = {}
        self._monitor_listener = {}
        self._monitor_lock = Lock()
58
        self._monitor_watch_workspaceini = None
59
60
61
    
    def __del__(self):
        self.close()
62

63
    def setup(self, basedir=None):
Marcel's avatar
Marcel committed
64
65
        if basedir is None:
            basedir = self.expand('~/')
murban's avatar
murban committed
66
        if not os.path.isdir(basedir):
Marcel's avatar
Marcel committed
67
            raise Exception("Basedir '%s' does not exist!" % basedir)
murban's avatar
murban committed
68
69
        # the basedir
        self.basedir = os.path.join(basedir, ".vispa")
70
71
        if os.path.isdir(self.basedir):
            return "Basedir already exists"
72
        else:
Gero Müller's avatar
Gero Müller committed
73
            os.makedirs(self.basedir, 0o700)
74
            return "Basedir now exists"
75

76
77
78
79
80
    def close(self):
        if self._monitor_thread:
            self._monitor_thread.remove_all_watches()
            self._monitor_thread.running = False
    
murban's avatar
murban committed
81
    def get_mime_type(self, filepath):
Marcel's avatar
Marcel committed
82
        filepath = self.expand(filepath)
83
84
85
86
        mime, encoding = guess_type(filepath)
        if mime is not None:
            return mime
        ext = filepath.split(".")[-1]
Gero Müller's avatar
Gero Müller committed
87
88
        if ext is not None and ext != "" and ext.lower(
        ) in FileSystem.ADDITIONAL_MIMES.keys():
89
90
91
            return FileSystem.ADDITIONAL_MIMES[ext]
        return None

murban's avatar
murban committed
92
    def check_file_extension(self, path, extensions=[]):
Marcel's avatar
Marcel committed
93
        path = self.expand(path)
94
95
96
97
98
99
100
101
        if (len(extensions) == 0):
            return True
        for elem in extensions:
            elem = elem if elem.startswith(".") else "." + elem
            if path.lower().endswith(elem.lower()):
                return True
        return False

Marcel's avatar
Marcel committed
102
103
    def exists(self, path, type=None):
        # type may be 'f' or 'd'
Marcel's avatar
Marcel committed
104
        path = self.expand(path)
105
106
        # path exists physically?
        if not os.path.exists(path):
Marcel's avatar
Marcel committed
107
108
            return None
        # type correct?
109
110
111
112
113
114
115
        target_type = 'd' if os.path.isdir(path) else 'f'
        if not type:
            return target_type
        type = type.lower()
        if type not in ['f', 'd']:
            return None
        return target_type if target_type == type else None
116

117
118
119
120
121
122
123
    def get_file_count(self, path, window_id=None, view_id=None, watch_id=None):
        # inline watch
        if window_id and view_id and watch_id:
            if self.watch(path, window_id, view_id, watch_id):
                pass
                # return -2 # don't fail atm since it would emit the wrong error message
        # actual function
124
125
126
127
128
129
        path = self.expand(path)
        if os.path.exists(path):
            return len(os.listdir(path))
        else:
            return -1

Gero Müller's avatar
Gero Müller committed
130
131
    def get_file_list(self, path, deep=False,
                      filter=None, reverse=False,
132
133
134
135
136
137
138
139
                      hide_hidden=True, encode_json=True,
                      window_id=None, view_id=None, watch_id=None):
        # inline watch
        if window_id and view_id and watch_id:
            if self.watch(path, window_id, view_id, watch_id):
                pass
                # return "" # don't fail atm since it would not be caught on the client side
        # actual function
140
        filter = re.compile(filter) if filter else None
141
        filelist = []
Marcel's avatar
Marcel committed
142
        path_expand = self.expand(path)
143
144
145
146
147
148
149
        try:
            for elem in os.listdir(path_expand):
                # hide hidden files?
                if elem.startswith('.') and hide_hidden:
                    continue

                # excluded by filters?
150
                if filter and bool(filter.search(elem)) != reverse:
151
                    continue
152
                
Gero Müller's avatar
Gero Müller committed
153
154
155
                info = {
                    'name': elem
                }
156

Gero Müller's avatar
Gero Müller committed
157
                fullpath = os.path.join(path_expand, elem)
Gero Müller's avatar
Gero Müller committed
158
                stats = os.lstat(fullpath)
Gero Müller's avatar
Gero Müller committed
159
                is_symlink = stat.S_ISLNK(stats.st_mode)
Gero Müller's avatar
Gero Müller committed
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
                if is_symlink:
                    realfullpath = os.path.realpath(fullpath)
                    info.update({'symlink': True, 'realpath': realfullpath})
                    if os.path.exists(realfullpath):
                        stats = os.stat(realfullpath)

                info.update({
                    'size': stats.st_size,
                    'mtime': stats.st_mtime,
                    'type': 'd' if stat.S_ISDIR(stats.st_mode) else 'f'
                })

                filelist.append(info)
                if deep:
                    filelist.extend(self.get_file_list(fullpath, deep,
                                                       filter, reverse))
        except:
            pass
178
179

        # Determine the parent
Gero Müller's avatar
Gero Müller committed
180
        # parentpath = path_expand[:-1] if path_expand.endswith(os.sep) and
Gero Müller's avatar
Gero Müller committed
181
        #    path_expand != os.sep else path_expand
182
        parentpath = os.path.dirname(path_expand)
183
184
185
        data = {'filelist': filelist, 'parentpath': parentpath}
        if encode_json:
            return json.dumps(data)
186
187
        else:
            return data
188

Gero Müller's avatar
Gero Müller committed
189
190
    def get_suggestions(
            self, path, length=1, append_hidden=True, encode_json=True):
Marcel's avatar
Marcel committed
191
192
193
        suggestions = []
        source, filter = None, None
        # does the path exist?
Marcel's avatar
Marcel committed
194
195
        path_expanded = self.expand(path)
        if os.path.exists(path_expanded):
Marcel's avatar
Marcel committed
196
            # dir case
Marcel's avatar
Marcel committed
197
            if os.path.isdir(path_expanded):
Marcel's avatar
Marcel committed
198
199
200
201
202
203
204
205
206
207
208
                if path.endswith('/'):
                    source = path
                else:
                    suggestions.append(path + os.sep)
                    return suggestions
            # file case
            else:
                return suggestions
        else:
            # try to estimate source and filter
            head, tail = os.path.split(path)
Marcel's avatar
Marcel committed
209
            if os.path.isdir(os.path.expanduser(os.path.expandvars(head))):
Marcel's avatar
Marcel committed
210
211
212
213
214
215
216
                source = head
                filter = tail

        # return empty suggestions when source is not set
        if not source:
            return suggestions

Marcel's avatar
Marcel committed
217
        files = os.listdir(os.path.expanduser(os.path.expandvars(source)))
Marcel's avatar
Marcel committed
218
219
        # resort?
        if append_hidden:
Gero Müller's avatar
Gero Müller committed
220
221
            files = sorted(
                map(lambda f: str(f), files), cmp=file_compare, key=str.lower)
222
        while (len(suggestions) < length or length == 0) and len(files):
Marcel's avatar
Marcel committed
223
224
225
226
            file = files.pop(0)
            if filter and not file.startswith(filter):
                continue
            suggestion = os.path.join(source, file)
Gero Müller's avatar
Gero Müller committed
227
228
            if not suggestion.endswith(
                    '/') and os.path.isdir(os.path.expanduser(os.path.expandvars(suggestion))):
Marcel's avatar
Marcel committed
229
230
231
                suggestion += '/'
            suggestions.append(suggestion)

232
        return suggestions if not encode_json else json.dumps(suggestions)
233

murban's avatar
murban committed
234
    def cut_slashs(self, path):
Marcel's avatar
Marcel committed
235
        path = self.expand(path)
murban's avatar
murban committed
236
        path = path[1:] if path.startswith(os.sep) else path
237
238
        if path == "":
            return path
murban's avatar
murban committed
239
        path = path[:-1] if path.endswith(os.sep) else path
240
241
        return path

murban's avatar
murban committed
242
    def create_folder(self, path, name):
Marcel's avatar
Marcel committed
243
        path = self.expand(path)
Gero Müller's avatar
Gero Müller committed
244
245
        name = self.expand(name)

246
        # folder with the same name existent?
247
        name = self.handle_file_name_collision(name, path)
Marcel's avatar
Marcel committed
248
        fullpath = os.path.join(path, name)
249
250
251
        try:
            os.mkdir(fullpath)
        except Exception as e:
252
            # raise Exception("You don't have the permission to create this folder!")
253
254
            raise Exception(str(e))

murban's avatar
murban committed
255
    def create_file(self, path, name):
Marcel's avatar
Marcel committed
256
        path = self.expand(path)
Gero Müller's avatar
Gero Müller committed
257
258
        name = self.expand(name)

259
        # file with the same name existent?
260
        name = self.handle_file_name_collision(name, path)
Marcel's avatar
Marcel committed
261
        fullpath = os.path.join(path, name)
262
263
264
265
266
267
        try:
            f = file(fullpath, "w")
            f.close()
        except Exception as e:
            raise Exception(str(e))

268
    def rename(self, path, name, new_name, force=False):
Gero Müller's avatar
Gero Müller committed
269
270
271
272
        path = self.expand(path)
        name = self.expand(name)
        new_name = self.expand(new_name)

273
274
275
276
277
278
279
        try:
            if force == False:
                new_name = self.handle_file_name_collision(new_name, path)
            name = os.path.join(path, name)
            new_name = os.path.join(path, new_name)
            os.renames(name, new_name)
            return
Gero Müller's avatar
Gero Müller committed
280
        except Exception as e:
281
            return str(e)
282

murban's avatar
murban committed
283
284
    def remove(self, path):
        if isinstance(path, list):
285
286
287
            for p in path:
                self.remove(p)
        else:
Marcel's avatar
Marcel committed
288
289
290
291
292
            path = self.expand(path)
            if os.path.isdir(path):
                shutil.rmtree(path)
            else:
                os.remove(path)
293

294
295
296
297
298
299
    def move(self, source, destination):
        source = self.expand(source)
        destination = self.expand(destination)
        if os.path.isdir(destination):
            shutil.move(source, destination)

300
    def compress(self, path, paths, name):
Gero Müller's avatar
Gero Müller committed
301
302
        # paths has to be a list of strings
        paths = paths if isinstance(paths, (list, tuple)) else [paths]
Gero Müller's avatar
Gero Müller committed
303
        paths = [self.expand(p) for p in paths]
Gero Müller's avatar
Gero Müller committed
304
305
306

        path = path if not path.endswith(os.sep) else path[:-1]
        path = self.expand(path)
Martin Urban's avatar
Martin Urban committed
307

Gero Müller's avatar
Gero Müller committed
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
        name = name if name.endswith(".zip") else name + ".zip"
        name = self.handle_file_name_collision(name, path)

        fullpath = os.path.join(path, name)

        with ZipFile(fullpath, "w") as archive:
            i = 0
            while i < len(paths):
                if not paths[i]:
                    i += 1
                    continue
                p = self.expand(paths[i])
                i += 1

                if os.path.isdir(p):
                    for elem in os.listdir(p):
                        fullp = os.path.join(p, elem)
                        if os.path.isdir(fullp):
                            paths.append(fullp)
                        else:
Gero Müller's avatar
Gero Müller committed
328
329
                            ap = fullp[
                                len(path):] if fullp.startswith(path) else fullp
Gero Müller's avatar
Gero Müller committed
330
331
332
                            logger.debug(fullp)
                            archive.write(fullp, ap)
                else:
Martin Urban's avatar
Martin Urban committed
333
                    ap = p[len(path):] if p.startswith(path) else p
Gero Müller's avatar
Gero Müller committed
334
                    logger.debug(p)
Martin Urban's avatar
Martin Urban committed
335
                    archive.write(p, ap)
336

337
    def paste(self, path, fullsrc, cut):
Marcel's avatar
Marcel committed
338
        # TODO
339
340
341
342
        path = self.expand(path)
        if isinstance(fullsrc, (list, tuple)):
            for p in fullsrc:
                p = self.expand(p)
murban's avatar
murban committed
343
                self.paste(path, p, cut)
344
345
            return True

346
347
        # fulltarget = os.path.join(path, fullsrc.split(os.sep)[-1])
        logger.error("")
Gero Müller's avatar
Gero Müller committed
348
349
        target = self.handle_file_name_collision(
            fullsrc.split(os.sep)[-1], path)
350
        fulltarget = os.path.join(path, target)
351

352
353
        if os.path.isdir(fullsrc):
            shutil.copytree(fullsrc, fulltarget)
354
            if cut:
355
                shutil.rmtree(fullsrc)
356
        else:
357
            shutil.copy2(fullsrc, fulltarget)
358
            if cut:
359
                os.remove(fullsrc)
360

361
362
    def save_file(self, path, content, force=True, binary=False,
                   utf8=False, window_id=None, view_id=None, watch_id=None):
Marcel's avatar
Marcel committed
363
        path = self.expand(path)
Marcel's avatar
Marcel committed
364
        # check if file already exists
murban's avatar
murban committed
365
        if os.path.exists(path) and not force:
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
            return json.dumps({
                "mtime": 0,
                "success": False,
                "watch_error": ""
            })
        if utf8:
            content = content.encode('utf8')
        try:
            with open(path, "wb" if binary else "w") as f:
                f.write(content)
            mtime = os.path.getmtime(path)
        except Exception as e:
            mtime  = 0
        
        # inline watch
        if window_id and view_id and watch_id:
            watch_error = self.watch(path, window_id, view_id, watch_id)
383
        else:
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
            watch_error = ""
        
        return json.dumps({
            "mtime": os.path.getmtime(path),
            "success": mtime > 0,
            "watch_error": watch_error
        })
    
    def get_file(self, path, binary=False,
                  utf8=False, window_id=None, view_id=None, watch_id=None):
        # inline watch
        if window_id and view_id and watch_id:
            watch_error = self.watch(path, window_id, view_id, watch_id)
        else:
            watch_error = ""
        
        # actual function
        path = self.expand(path)
        try:
            with open(path, "rb" if binary else "r") as f:
                content = f.read()
            if utf8:
                content = content.decode('utf8')
            mtime = os.path.getmtime(path)
        except Exception as e:
            mtime  = 0
            content = ""
        
        return json.dumps({
            "content": content,
            "mtime": mtime,
            "success": mtime > 0,
            "watch_error": watch_error
        })
418

Gero Müller's avatar
Gero Müller committed
419
    def save_file_content(self, filename, content,
Gero Müller's avatar
Gero Müller committed
420
                          path=None, force=True, append=False):
421
422
        # check write permissions
        # if not self.checkPermission(username, vPath, 'w'):
Gero Müller's avatar
Gero Müller committed
423
424
425
426
427
428
        #    return False, "Permission denied!"

        if path:
            filename = os.path.join(path, filename)
        filename = self.expand(filename)

429
        # check if file already exists
Gero Müller's avatar
Gero Müller committed
430
431
432
433
434
435
436
437
438
        if os.path.exists(filename) and not force:
            return False, "The file '%s' already exists!" % filename

        out = open(filename, "ab+" if append else "wb")
        out.write(content)
        out.close()

        return True, "File saved!"

murban's avatar
murban committed
439
    def get_file_content(self, path):
Marcel's avatar
Marcel committed
440
        path = self.expand(path)
Gero Müller's avatar
Gero Müller committed
441
        f = open(path, "rb")
442
443
        content = f.read()
        f.close()
Marcel's avatar
Marcel committed
444
        return content
445

Marcel's avatar
Marcel committed
446
    def get_mtime(self, path):
Marcel's avatar
Marcel committed
447
        path = self.expand(path)
Marcel's avatar
Marcel committed
448
449
        return os.path.getmtime(path)

murban's avatar
murban committed
450
    def is_browser_file(self, path):
Marcel's avatar
Marcel committed
451
        path = self.expand(path)
452
        extension = path.split(".")[-1]
453
        return extension.lower() in FileSystem.BROWSER_EXTENSIONS
454

murban's avatar
murban committed
455
    def handle_file_name_collision(self, name, path):
456
        # collision?
murban's avatar
murban committed
457
        files = os.listdir(path)
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
        if name not in files:
            return name

        # when this line is reached, there is a collision!

        # cut the file extension
        extension = name.split(".")[-1]
        prename = None
        if name == extension:
            extension = ""
            prename = name
        else:
            prename = name.split("." + extension)[0]

        # has the name already a counter at its end?
        hasCounter = False
        preprename = None
        counter = prename.split("_")[-1]

        if counter != prename:
            try:
                counter = int(counter)
                hasCounter = True
                preprename = "_".join(prename.split("_")[:-1])
            except:
                pass

        if hasCounter:
            # increment and try again
            counter += 1
Gero Müller's avatar
Gero Müller committed
488
489
490
            newname = "%s_%d%s" % (preprename,
                                   counter,
                                   "" if extension == "" else "." + extension)
491
        else:
Gero Müller's avatar
Gero Müller committed
492
493
            newname = "%s_1%s" % (
                prename, "" if extension == "" else "." + extension)
494
495

        # return
murban's avatar
murban committed
496
        return self.handle_file_name_collision(newname, path)
Marcel's avatar
Marcel committed
497

Marcel's avatar
Marcel committed
498
499
    def expand(self, path):
        return os.path.expanduser(os.path.expandvars(path))
Marcel's avatar
Marcel committed
500

Gero Müller's avatar
Gero Müller committed
501
    def thumbnail(self, path, width=100, height=100, sharpen=True):
502
503
504
        path = self.expand(path)
        if HAVE_PIL:
            output = StringIO()
Gero Müller's avatar
Gero Müller committed
505
            img = Image.open(path)
Gero Müller's avatar
Gero Müller committed
506
            img.thumbnail((width, height), Image.ANTIALIAS)
Gero Müller's avatar
Gero Müller committed
507
            if sharpen:
508
                img.filter(ImageFilter.SHARPEN)
Gero Müller's avatar
Gero Müller committed
509
            img.save(output, "JPEG")
510
511
512
513
514
515
516
517
518
519
520
521
            contents = output.getvalue()
            output.close()
            return contents
        elif HAVE_CONVERT:
            cmd = ['convert', path,
                   '-thumbnail', '%dx%d' % (width, height),
                   'jpeg:-']
            p = subprocess.Popen(cmd, stdin=None, stdout=subprocess.PIPE)
            return p.communicate()[0]
        else:
            return self.get_file_content(path)

522
523
524
525
    def _monitor_callback(self, event):
        if event.action_name == 'access':
            return
        
526
527
528
529
530
        if os.path.exists(event.watch.path):
            mtime = os.path.getmtime(event.watch.path)
        else:
            mtime = -1
        
531
532
533
534
535
        if event.watch == self._monitor_watch_workspaceini:
            if mtime == -1 or event.action_name == 'modify':
                vispa.remote.send_topic('workspace.ini_modified', 
                    user_id=self._userid, data={
                        "workspaceId": self._workspaceid,
536
                        "mtime": mtime
537
                    })
538
539
540
541
542
543
544
545
            if mtime == -1:
                self._monitor_thread.remove_watch(event.watch)
                self._monitor_watch_workspaceini = None
            return
        
        else:
            with self._monitor_lock:
                # inform all listener
546
                for combined_id in event.watch.listener:
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
                    vispa.remote.send_topic(
                        "extension.%s.socket.watch" % combined_id[1],
                        window_id=combined_id[0],
                        data={
                            "path": event.watch.path,
                            "action_name": event.action_name,
                            "watch_id": combined_id[2],
                            "mtime": mtime
                        })
                
                # cleanup yourself
                if event.action_name in ['delete self','move self']:
                    for combined_id in event.watch.listener:
                        del self._monitor_listener[combined_id]
                    event.watch.listener = []
                    self._check_watch(event.watch)
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
    
    def _check_watch(self, watch):
        if len(watch.listener) == 0:
            del self._monitor_watches[watch.path]
            self._monitor_thread.remove_watch(watch)
        
    def watch(self, path, window_id, view_id, watch_id):
        # fail if there is no such fie
        path = self.expand(path)
        if not os.path.exists(path):
            return "The file does not exist"
        combined_id = (window_id, view_id, watch_id)
        
        with self._monitor_lock:
            # watch is in use
            if combined_id in self._monitor_listener:
                # has path changed?
                if self._monitor_listener[combined_id].path == path:
                    return ""
                else:
                    self._monitor_listener[combined_id].listener.remove(combined_id)
                    self._check_watch(self._monitor_listener[combined_id])
                    del self._monitor_listener[combined_id]
            
            # create or get the desired watch
            if path not in self._monitor_watches:
                if os.path.isfile(path):
                    watch = self._monitor_thread.add_file_watch(path)
                elif os.path.isdir(path):
                    watch = self._monitor_thread.add_dir_watch(path)
                else:
                    return "This kind of file can't be wachted"
                watch.listener = []
                self._monitor_watches[path] = watch
            else:
                watch = self._monitor_watches[path]
            
            if combined_id in watch.listener:
                return "This file is already watched" # this should never occur
            
            watch.listener.append(combined_id)
            self._monitor_listener[combined_id] = watch

        return ""
        
    def unwatch(self, window_id, view_id, watch_id=None):
        with self._monitor_lock:
            if watch_id is None:
                # will perform poor with lots of watches/listener
                for path, watch in self._monitor_watches.items():
                    for combined_id in watch.listener:
                        if window_id==combined_id[0] and view_id==combined_id[1]:
                            watch.listener.remove(combined_id)
                            del self._monitor_listener[combined_id]
                    
                    self._check_watch(watch)
            
            else:
                combined_id = (window_id, view_id, watch_id)
                if combined_id in self._monitor_listener:
                    self._monitor_listener[combined_id].listener.remove(combined_id)
                    self._check_watch(self._monitor_listener[combined_id])
                    del self._monitor_listener[combined_id]

        return ""

629
    def get_workspaceini(self, request, fail_on_missing=False):
630
        try:
631
            request_dict = json.loads(request)
632
            config = ConfigParser.ConfigParser()
633
634
635
636
            config.read([FileSystem.GLOBAL_WORKSPACE_CONF,
                    self.expand(FileSystem.PRIVATE_WORKSPACE_CONF)])
            mtime = os.path.getmtime(self.expand(FileSystem.PRIVATE_WORKSPACE_CONF))
            self._watch_workspaceini() 
637
            if not isinstance(request_dict, dict):
638
                request_dict = dict.fromkeys(config.sections(), True)
639
640
641
642
643
644
            data = {}
            for section, name_list in request_dict.iteritems():
                if config.has_section(section):
                    if isinstance(name_list, basestring):
                        name_list = [name_list]
                    if not isinstance(name_list, list):
645
                        data[section] = dict(config.items(section))
646
647
648
649
650
651
652
653
654
                    else:
                        data[section] = {}
                        for name in name_list:
                            if config.has_option(section, name):
                                data[section][name] = config.get(section, name)
                            elif fail_on_missing:
                                raise Exception('workspace.ini is missing the option "%s" in section [%s] ' % (name, section))
                elif fail_on_missing:
                    raise Exception('workspace.ini is missing the section [%s]' % section)
655
            return json.dumps({
656
                "content": data,
657
658
                "success": True,
                "mtime": mtime
659
660
661
                })
        except Exception as e:
            return json.dumps({
662
663
664
                "content": "",
                "success": False,
                "error": str(e)
665
666
                })

667
    def set_workspaceini(self, request):
668
        try:
669
670
671
            request_dict = json.loads(request)
            if not isinstance(request_dict, dict):
                raise Exception('Given values to be set in workspace.ini in wrong format')
672
            config = ConfigParser.ConfigParser()
673
            config.read(self.expand(FileSystem.PRIVATE_WORKSPACE_CONF))
674
675
676
677
678
679
680
            for section, options in request_dict.iteritems():
                if not isinstance(options, dict):
                    raise Exception('Given values to be set in workspace.ini in wrong format')
                if not config.has_section(section):
                    config.add_section(section)
                for name, value in options.iteritems():
                    config.set(section, name, value)
681
682
            with open(self.expand(FileSystem.PRIVATE_WORKSPACE_CONF), 'w') as f:
                self._watch_workspaceini()
683
684
                config.write(f)
            return ""
685
        except Exception as e:
686
            return str(e)
687
688
689
690
691
692
693
694
    
    def _watch_workspaceini(self):
        if self._monitor_watch_workspaceini:
            return
        if not self.exists(FileSystem.PRIVATE_WORKSPACE_CONF, 'f'):
            return
        self._monitor_watch_workspaceini = self._monitor_thread.add_file_watch(
            self.expand(FileSystem.PRIVATE_WORKSPACE_CONF))
695

Marcel's avatar
Marcel committed
696
697
698
699
700
701
702
703
def string_compare(a, b):
    if a == b:
        return 0
    elif a > b:
        return 1
    else:
        return -1

704

Marcel's avatar
Marcel committed
705
706
707
708
709
710
711
712
713
def file_compare(a, b):
    if not a.startswith('.') and not b.startswith('.'):
        return string_compare(a, b)
    elif a.startswith('.') and b.startswith('.'):
        return string_compare(a, b)
    elif a.startswith('.') and not b.startswith('.'):
        return 1
    elif not a.startswith('.') and b.startswith('.'):
        return -1