--- /dev/null
+*.swp
+*.pyc
--- /dev/null
+ * systray
+ tasto sinistro systrai elenca o dice crea nuovo
+ click su profilo per switchare, stoppando e riavviando dropbox
+ ogni volta
+ tasto destro dice gestisci profili o esci
+ * gestire profili
+ - listare profili in base a dir
+ - creare dir profili e linkarla alla cosa
+ - switch profili
+ * inizializzazione applicazione se non trova la dir dove infila i profili
+
+
+Directory del profilo con nome dell'account
+ * validazione nome account che deve essere ([0-9]|[a-zA-Z_-])+
+
+Implementare custom exceptions per pigliarle dentro l'applicazione e mostrare l'errore in un messagebox
--- /dev/null
+#!/usr/bin/python
+# -*- 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 sys
+
+class AppError(Exception):
+ """ Custom exception that prints on stderr and raise the error to be catched for example in the Gui """
+ def __init__(self, value):
+ print >> sys.stderr, value
+ self.value = value
+
+ def __str__(self):
+ return repr(self.value)
--- /dev/null
+#!/usr/bin/python
+# -*- 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 sys
+import os
+from PyQt4 import QtGui
+
+from apperror import AppError
+
+class Gui(QtGui.QDialog):
+ def __init__(self, prManager):
+ self.app = QtGui.QApplication(sys.argv)
+
+ #check if system tray is avaiable on the system
+ if not QtGui.QSystemTrayIcon.isSystemTrayAvailable():
+ QtGui.QMessageBox.critical(None, "Systray",
+ "I couldn't detect any system tray on this system.")
+ sys.exit(1)
+
+ QtGui.QApplication.setQuitOnLastWindowClosed(False)
+
+ super(Gui, self).__init__()
+
+ self.createActions()
+ self.createTrayIcon()
+
+ self.trayIcon.show()
+
+ self.setWindowTitle("Profile manager / Dboxswitch - dropbox profile switcher")
+ self.resize(400, 300)
+
+ self.profileManager = prManager
+
+
+
+ def main(self):
+ sys.exit(self.app.exec_())
+
+ def closeEvent(self, event):
+ if self.trayIcon.isVisible():
+ self.hide()
+ event.ignore()
+
+ def createActions(self):
+ self.manageprofiles = QtGui.QAction("Manage &Profiles", self,
+ triggered=self.hide)
+ self.quitAction = QtGui.QAction("&Quit", self,
+ triggered=QtGui.qApp.quit)
+
+ def createTrayIcon(self):
+ self.trayIconMenu = QtGui.QMenu(self)
+ self.trayIconMenu.addAction(self.manageprofiles)
+ self.trayIconMenu.addSeparator()
+ self.trayIconMenu.addAction(self.quitAction)
+
+ self.trayIcon = QtGui.QSystemTrayIcon(self)
+ self.trayIcon.setContextMenu(self.trayIconMenu)
--- /dev/null
+#!/usr/bin/python
+# -*- 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.
+
+"""
+
+from profhandler import ProfHandler
+from gui import Gui
+from PyQt4 import QtGui
+
+from settings import appconf
+
+if __name__ == '__main__':
+ #Application init
+ prManager = ProfHandler()
+
+ window = Gui(prManager)
+ window.main()
--- /dev/null
+#!/usr/bin/python
+# -*- 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
+
+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 [os.path.join(self.pdir, f) for f in os.listdir(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 newProfile(self, profileName):
+ """ Create a profile """
+
+ print("Creating a new profile")
+ if self.isValidProfileName(profileName):
+ os.makedirs(os.path.join(self.getProfileFolder(), profileName))
+ else:
+ raise AppError('Profile Name not valid')
+ 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+" created.")
+
+ def isValidProfileName(self, pname):
+ if self.reg.match(pname) is not None:
+ return True
+ else:
+ return False
+
+ def activateProfile(self, pname):
+ if pname in self.getProfilesList():
+ self.stopDropbox()
+ try:
+ with open(os.path.join(self.getProfileFolder(), pname)) as pdir:
+ os.unlink(self.getDropboxDirectory())
+ os.symlink(os.path.join(self.getProfileFolder(), pname), self.getDropboxDirectory())
+ except IOError as e:
+ raise AppError('Error on activating Profile: '+pname)
+ self.startDropbox()
+ else:
+ raise AppError("Trying to acrivate non existant profile")
+
+ def getDropboxDirectory(self):
+ pl = platform.system()
+ if pl == 'Linux':
+ return os.path.join(os.path.expanduser('~'),".dropbox")
+ elif pl == 'Windows':
+ raise NotImplementedError, "Not implemented yet."
+ elif pl == 'Darwin':
+ raise NotImplementedError, "Not implemented yet."
+
+
+__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()
+
--- /dev/null
+#!/bin/python
+
+class Settings():
+ def __init__(self):
+ self.appname = "dboxswitch"
+
+global conf
+appconf = Settings()