5ef71e553da1216116b9a537f52aebb76d6f9908
[stack/code/dboxswitch.git] / profhandler.py
1 # -*- coding: utf-8 -*-
2
3 """
4 Dboxswitch dropbox profile switcher
5
6 license: Modified BSD License
7
8 Copyright (c) 2012,  <stack@inventati.org>
9 All rights reserved.
10
11 Redistribution and use in source and binary forms, with or without
12 modification, are permitted provided that the following conditions are met:
13     * Redistributions of source code must retain the above copyright
14       notice, this list of conditions and the following disclaimer.
15     * Redistributions in binary form must reproduce the above copyright
16       notice, this list of conditions and the following disclaimer in the
17       documentation and/or other materials provided with the distribution.
18     * Neither the name of the <organization> nor the
19       names of its contributors may be used to endorse or promote products
20       derived from this software without specific prior written permission.
21
22 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
23 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
24 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
25 DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
26 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
27 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
29 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
31 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32
33 """
34 import platform
35 import re
36 import shutil
37 import os
38 import errno
39
40 from apperror import AppError
41 from settings import appconf
42
43 class ProfHandler():
44
45     def __init__(self):
46
47         #create profile directory if not exists
48         try:
49             os.makedirs(self.getProfileFolder())
50         except OSError, e:
51             if e.errno != errno.EEXIST:
52                 raise
53
54         #compile regular expression for validating profile names
55         self.reg = re.compile("[a-zA-Z0-9_-]+")
56
57         #patch symlink on windows
58         if platform.system() is 'Windows':
59             os.symlink = winsymlink
60
61     def getProfilesList(self):
62         """ Generate and returns the profiles 
63             it assumes that self.pdir is defined """
64         #this is generated every time to handle the case of the user renaming the directories by hand
65         return [os.path.join(self.pdir, f) for f in os.listdir(self.pdir)]
66
67     def getProfileFolder(self):
68         """ Generates, in a os dependant way, the local folder where all profiles are stored """
69         try:
70             #directory path is cached
71             return self.pdir
72         except AttributeError:
73             pl = platform.system()
74             if pl == "Linux":
75                 try:
76                     from xdg.BaseDirectory import xdf_data_home
77                     self.pdir = os.path.join(xdg_data_home, appconf.appname)
78                 except:
79                     self.pdir = os.path.join(os.path.expanduser('~'),".local/share",appconf.appname)
80             elif pl == 'Windows':
81                 self.pdir = os.path.join(os.getenv("APPDATA"), appconf.appname)
82             elif pl == 'Darwin':
83                 self.pdir =  os.path.join(os.path.expanduser('~'),"."+appconf.appname)
84             elif pl == None:
85                 raise AppError('Operative system NOT supported.')
86
87             return self.pdir
88
89     def addProfile(self, profileName):
90         """ Create a profile """
91
92         print("Creating a new profile")
93         if self.isValidProfileName(profileName):
94             os.makedirs(os.path.join(self.getProfileFolder(), profileName)) 
95         else:
96             raise AppError('Profile Name not valid.\nAllowed only ascii characters.')
97         print("Profile "+profileName+" created.")
98
99     def delProfile(self, profileName):
100         """ Delete a profile """
101
102         print("Deleting profile")
103         if self.isValidProfileName(profileName):
104             try:
105                 #recursively delete the profile directory
106                 shutil.rmtree(os.path.join(self.pdir, profileName))
107             except:
108                 raise AppError('Profile Name does not exists')
109         else:
110             raise AppError('Profile Name not valid')
111         print("Profile "+profileName+" created.")
112
113     def isValidProfileName(self, pname):
114         if self.reg.match(pname) is not None:
115             return True
116         else:
117             return False
118
119     def activateProfile(self, ppath):
120         if ppath in self.getProfilesList():
121             self.stopDropbox()
122             try:
123                 with open(ppath) as pdir:
124                     os.unlink(self.getDropboxDirectory())
125                     os.symlink(ppath, self.getDropboxDirectory())
126             except IOError as e:
127                 raise AppError('Error on activating Profile: '+ppath)
128             self.startDropbox()
129         else:
130             raise AppError("Trying to acrivate non existant profile")
131
132     def getDropboxDirectory(self):
133         pl = platform.system()
134         if pl in ('Linux', 'Darwin'):
135             return os.path.join(os.path.expanduser('~'),".dropbox")
136         elif pl == 'Windows':
137             assert os.environ.has_key('APPDATA'), Exception('APPDATA env variable not found')
138             return os.path.join(os.environ['APPDATA'],'Dropbox')
139         else:
140             raise NotImplementedError, "Not implemented yet."
141
142             
143 __CSL = None
144 def winsymlink(source, link_name):
145     '''symlink(source, link_name)
146        Creates a symbolic link pointing to source named link_name.
147         Used to patch the nonexistant version on windows for python 2.6'''
148     global __CSL
149     if __CSL is None:
150         import ctypes
151         csl = ctypes.windll.kernel32.CreateSymbolicLinkW
152         csl.argtypes = (ctypes.c_wchar_p, ctypes.c_wchar_p, ctypes.c_uint32)
153         csl.restype = ctypes.c_ubyte
154         __CSL = csl
155     flags = 0
156     if source is not None and os.path.isdir(source):
157         flags = 1
158     if __CSL(link_name, source, flags) == 0:
159         raise ctypes.WinError()
160