+# -*- coding: utf-8 -*-
+
+"""
+Dboxswitch dropbox profile switcher
+
+license: Modified BSD License
+
+Copyright (c) 2012, <stack@inventati.org>
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the <organization> nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""
+import platform
+import re
+import shutil
+import os
+import errno
+import signal
+
+from apperror import AppError
+from settings import appconf
+
+class ProfHandler():
+
+ def __init__(self):
+
+ #create profile directory if not exists
+ try:
+ os.makedirs(self.getProfileFolder())
+ except OSError, e:
+ if e.errno != errno.EEXIST:
+ raise
+
+ #compile regular expression for validating profile names
+ self.reg = re.compile("[a-zA-Z0-9_-]+")
+
+ #patch symlink on windows
+ if platform.system() is 'Windows':
+ os.symlink = winsymlink
+
+ def getProfilesList(self):
+ """ Generate and returns the profiles
+ it assumes that self.pdir is defined """
+ #this is generated every time to handle the case of the user renaming the directories by hand
+ return sorted([os.path.join(self.pdir, f) for f in os.listdir(self.pdir)])
+
+ def getProfileFolder(self):
+ """ Generates, in a os dependant way, the local folder where all profiles are stored """
+ try:
+ #directory path is cached
+ return self.pdir
+ except AttributeError:
+ pl = platform.system()
+ if pl == "Linux":
+ try:
+ from xdg.BaseDirectory import xdf_data_home
+ self.pdir = os.path.join(xdg_data_home, appconf.appname)
+ except:
+ self.pdir = os.path.join(os.path.expanduser('~'),".local/share",appconf.appname)
+ elif pl == 'Windows':
+ self.pdir = os.path.join(os.getenv("APPDATA"), appconf.appname)
+ elif pl == 'Darwin':
+ self.pdir = os.path.join(os.path.expanduser('~'),"."+appconf.appname)
+ elif pl == None:
+ raise AppError('Operative system NOT supported.')
+
+ return self.pdir
+
+ def addProfile(self, profileName):
+ """ Create a profile """
+
+ print("Creating a new profile")
+ if self.isValidProfileName(profileName):
+ try:
+ os.makedirs(os.path.join(self.getProfileFolder(), profileName))
+ except OSError,e:
+ if e.errno == errno.EEXIST:
+ raise AppError("Profile exists.")
+ else:
+ raise AppError(str(e))
+ else:
+ raise AppError('Profile Name not valid.\nAllowed only ascii characters.')
+ print("Profile "+profileName+" created.")
+
+ def delProfile(self, profileName):
+ """ Delete a profile """
+
+ print("Deleting profile")
+ if self.isValidProfileName(profileName):
+ try:
+ #recursively delete the profile directory
+ shutil.rmtree(os.path.join(self.pdir, profileName))
+ except:
+ raise AppError('Profile Name does not exists')
+ else:
+ raise AppError('Profile Name not valid')
+ print("Profile "+profileName+" deleted.")
+
+ def isCurrentProfile(self, ppath):
+ """ Returns true if the current profile path is currently activated """
+
+ pl = platform.system()
+ if pl in ('Linux','Darwin'):
+ if os.path.exists(self.getDropboxDirectory()):
+ return True if os.readlink(self.getDropboxDirectory()) == ppath else False
+ else:
+ return False
+
+ def isValidProfileName(self, pname):
+
+ if self.reg.match(pname) is not None:
+ return True
+ else:
+ return False
+
+ def activateProfile(self, ppath):
+ pl = platform.system()
+ if ppath in self.getProfilesList():
+ self.stopDropbox()
+ try:
+ if pl in ('Linux','Darwin'):
+ if os.path.exists(self.getDropboxDirectory()):
+ os.unlink(self.getDropboxDirectory())
+ os.symlink(ppath, self.getDropboxDirectory())
+ else:
+ raise NotImplementedError, "Not implemented yet."
+ except IOError as e:
+ raise AppError('Error on activating Profile: '+ self.getBaseProfileName(ppath))
+ self.startDropbox()
+ else:
+ raise AppError("Trying to acrivate non existant profile")
+
+ def getBaseProfileName(self, ppath):
+ """ Returns the base name given a profile returned by getProfilesList """
+
+ return os.path.basename(ppath)
+
+ def getDropboxDirectory(self):
+ pl = platform.system()
+ if pl in ('Linux', 'Darwin'):
+ return os.path.join(os.path.expanduser('~'),".dropbox")
+ elif pl == 'Windows':
+ assert os.environ.has_key('APPDATA'), Exception('APPDATA env variable not found')
+ return os.path.join(os.environ['APPDATA'],'Dropbox')
+ else:
+ raise NotImplementedError, "Not implemented yet."
+
+ def stopDropbox(self):
+ """ Stop dropbox Daemon """
+ pl = platform.system()
+ if pl == 'Linux':
+ os.system("dropbox stop")
+ if pl in ('Linux','Darwin'):
+ pidfile = os.path.expanduser("~/.dropbox/dropbox.pid")
+ try:
+ with open(pidfile, "r") as f:
+ pid = int(f.read())
+ os.kill(pid, signal.SIGTERM)
+ except:
+ pass
+
+ def startDropbox(self):
+ """ Sart dropbox Daemon """
+
+ pl = platform.system()
+ if pl == 'Linux':
+ try:
+ os.system("dropbox start -i")
+ except:
+ raise AppError(u"Could not start dropbox.")
+ elif pl == 'Darwin':
+ os.system("/Applications/Dropbox.app/Contents/MacOS/Dropbox &")
+
+
+__CSL = None
+def winsymlink(source, link_name):
+ '''symlink(source, link_name)
+ Creates a symbolic link pointing to source named link_name.
+ Used to patch the nonexistant version on windows for python 2.6'''
+ global __CSL
+ if __CSL is None:
+ import ctypes
+ csl = ctypes.windll.kernel32.CreateSymbolicLinkW
+ csl.argtypes = (ctypes.c_wchar_p, ctypes.c_wchar_p, ctypes.c_uint32)
+ csl.restype = ctypes.c_ubyte
+ __CSL = csl
+ flags = 0
+ if source is not None and os.path.isdir(source):
+ flags = 1
+ if __CSL(link_name, source, flags) == 0:
+ raise ctypes.WinError()
+