Title: [commits] (heikki) [16065] Bug 11454, Plugin install fails if no write access to CHANDLERHOME/plugins.
Revision
16065
Author
heikki
Date
2007-12-06 12:58:23 -0800 (Thu, 06 Dec 2007)

Log Message

Bug 11454, Plugin install fails if no write access to CHANDLERHOME/plugins. Now if we can't install in that dir,
we'll install into $PROFILEDIR/plugins. That part of the patch by vajda, r=heikki.

Since this change could expose users' profile directory in chandler.log, I added code to replace profile dir
part of the log message that is done during plugin install with '$PROFILEDIR' string. r=vajda for this part.

Modified Paths

Added Paths

Diff

Modified: trunk/chandler/application/Plugins.py (16064 => 16065)

--- trunk/chandler/application/Plugins.py	2007-12-06 20:47:27 UTC (rev 16064)
+++ trunk/chandler/application/Plugins.py	2007-12-06 20:58:23 UTC (rev 16065)
@@ -80,17 +80,45 @@
 
         if archive is not None:
             options = Globals.options
-            pluginsDir = options.pluginPath[0]
-            if not os.path.exists(pluginsDir):
-                os.makedirs(pluginsDir)
 
+            # find writable path on options.pluginPath
+            # it is assumed that, unless the user changed the defaults,
+            # that pluginPath contains at least a path relative to CHANDLERHOME
+            # and another relative to the user's PROFILEDIR, in that order,
+            # so that plugins are installed in a shared directory (when
+            # CHANDLERHOME is shared) by default.
+
+            for pluginsDir in options.pluginPath:
+                try:
+                    if not os.path.exists(pluginsDir):
+                        os.makedirs(pluginsDir)
+                except OSError:
+                    continue
+                if os.access(pluginsDir, os.W_OK):
+                    break
+            else:
+                raise ValueError, ('no writable path in pluginPath',
+                                   options.pluginPath)
+
             try:
                 from setuptools.command.easy_install import main
                 from distutils.log import _global_log
-
+                from util.string_utils import nocase_replace
+                
                 # patch distutils' logger with logging's
                 # distutils logging levels happen to be one tenth of logging's
+                # Also ensure that we don't write full profileDir to log,
+                # which would be a security issue.
                 def log(level, msg, args):
+                    if msg:
+                        if wx.Platform == '__WXMSW__':
+                            msg = nocase_replace(msg,
+                                                 Globals.options.profileDir,
+                                                '$PROFILEDIR')
+                        else:
+                            msg = msg.replace(Globals.options.profileDir,
+                                              '$PROFILEDIR')
+                            
                     logger.log(level * 10, msg, *args)
 
                 _log = _global_log._log

Modified: trunk/chandler/application/Utility.py (16064 => 16065)

--- trunk/chandler/application/Utility.py	2007-12-06 20:47:27 UTC (rev 16064)
+++ trunk/chandler/application/Utility.py	2007-12-06 20:58:23 UTC (rev 16065)
@@ -17,7 +17,7 @@
 """
 
 import os, sys, logging, logging.config, logging.handlers, string, glob
-import i18n, schema
+import i18n, schema, itertools
 import M2Crypto.Rand as Rand, M2Crypto.threading as m2threading
 from optparse import OptionParser
 from configobj import ConfigObj
@@ -265,7 +265,7 @@
 # short opt, long opt, type flag, default value, env var, help text
 COMMAND_LINE_OPTIONS = {
     'parcelPath': ('-p', '--parcelPath', 's', None,  'PARCELPATH', 'Parcel search path'),
-    'pluginPath': (''  , '--pluginPath', 's', 'plugins',  None, 'Plugin search path, relative to CHANDLERHOME'),
+    'pluginPath': (''  , '--pluginPath', 's', 'plugins',  None, 'Plugin search path, relative to CHANDLERHOME and PROFILEDIR'),
     'webserver':  ('-W', '--webserver',  'v', [], 'CHANDLERWEBSERVER', 'Activate the built-in webserver'),
     'profileDir': ('-P', '--profileDir', 's', '',  'PROFILEDIR', 'location of the Chandler user profile directory (relative to CHANDLERHOME)'),
     'testScripts':('-t', '--testScripts','b', False, None, 'run all test scripts'),
@@ -462,11 +462,23 @@
                     value = prefs[name]
                 setattr(options, name, value)
 
-    # Resolve pluginPath relative to chandlerDirectory
     chandlerDirectory = locateChandlerDirectory()
-    options.pluginPath = [os.path.join(chandlerDirectory, path)
-                          for path in options.pluginPath.split(os.pathsep)]
-        
+
+    # Resolve pluginPath relative to chandlerDirectory and profileDir
+    # This means that relative paths in options.pluginPath get expanded
+    # twice into options.pluginPath, once relative to chandlerDirectory and
+    # once relative to profileDir, in this order, removing duplicates.
+    pluginPath = [os.path.expanduser(path)
+                  for path in options.pluginPath.split(os.pathsep)]
+    options.pluginPath = []
+    for path in itertools.chain((os.path.join(chandlerDirectory, path)
+                                 for path in pluginPath),
+                                (os.path.join(options.profileDir, path)
+                                 for path in pluginPath)):
+        path = os.path.abspath(os.path.normpath(path))
+        if path not in options.pluginPath:
+            options.pluginPath.append(path)
+
     # Store up the remaining args
     options.args = args
 
@@ -501,6 +513,8 @@
         options.profileDir = os.path.expanduser(profileDir)
     elif not os.path.isdir(options.profileDir):
         createProfileDir(options.profileDir)
+        
+    options.profileDir = os.path.normpath(options.profileDir)
 
 
 def loadPrefs(options):

Added: trunk/chandler/util/string_utils.py (16064 => 16065)

--- trunk/chandler/util/string_utils.py	2007-12-06 20:47:27 UTC (rev 16064)
+++ trunk/chandler/util/string_utils.py	2007-12-06 20:58:23 UTC (rev 16065)
@@ -0,0 +1,56 @@
+#   Copyright (c) 2007 Open Source Applications Foundation
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+
+def nocase_replace(msg, old, new):
+    """
+    Like str.replace, but case insensitive find.
+    
+    >>> s = 'Aabb'
+    >>> nocase_replace(s, 'a', 'cc')
+    'ccccbb'
+    >>> s = 'Hello World! The WORLD is not Enough!'
+    >>> nocase_replace(s, 'world', 'Moon')
+    'Hello Moon! The Moon is not Enough!'
+    >>> nocase_replace(s, '', 'Moon')
+    Traceback (most recent call last):
+    ...
+    ValueError: Need a non-empty string to search for
+    >>> s = ''
+    >>> nocase_replace(s, 'a', 'cc')
+    ''
+    """
+    if not old:
+        raise ValueError('Need a non-empty string to search for')
+    if not msg:
+        return msg
+    
+    s = msg.lower()
+    o = old.lower()
+    ret = []
+    i = 0
+    j = s.find(o)
+    oldLen = len(old)
+    
+    while j > -1:
+        ret.append(msg[i:j])
+        ret.append(new)
+        i = j + oldLen
+        j = s.find(o, i)
+    ret.append(msg[i:])
+
+    return ''.join(ret)
+
+if __name__ == '__main__':
+    import doctest
+    doctest.testmod(optionflags=doctest.ELLIPSIS)




_______________________________________________
Commits mailing list
[email protected]
http://lists.osafoundation.org/mailman/listinfo/commits

Reply via email to