|  | @@ -220,3 +220,130 @@ class RadicaleDaemon(Daemon):
 | 
	
		
			
			| 220 | 220 |          :param callback: callback to execute in daemon
 | 
	
		
			
			| 221 | 221 |          """
 | 
	
		
			
			| 222 | 222 |          self._server.append_thread_callback(callback)
 | 
	
		
			
			|  | 223 | +
 | 
	
		
			
			|  | 224 | +
 | 
	
		
			
			|  | 225 | +# TODO : webdav deamon, make it clean !
 | 
	
		
			
			|  | 226 | +
 | 
	
		
			
			|  | 227 | +import sys, os
 | 
	
		
			
			|  | 228 | +from wsgidav.wsgidav_app import DEFAULT_CONFIG
 | 
	
		
			
			|  | 229 | +from wsgidav.xml_tools import useLxml
 | 
	
		
			
			|  | 230 | +from wsgidav.wsgidav_app import WsgiDAVApp
 | 
	
		
			
			|  | 231 | +from wsgidav._version import __version__
 | 
	
		
			
			|  | 232 | +
 | 
	
		
			
			|  | 233 | +from inspect import isfunction
 | 
	
		
			
			|  | 234 | +import traceback
 | 
	
		
			
			|  | 235 | +
 | 
	
		
			
			|  | 236 | +DEFAULT_CONFIG_FILE = "wsgidav.conf"
 | 
	
		
			
			|  | 237 | +PYTHON_VERSION = "%s.%s.%s" % (sys.version_info[0], sys.version_info[1], sys.version_info[2])
 | 
	
		
			
			|  | 238 | +
 | 
	
		
			
			|  | 239 | +class WsgiDavDaemon(Daemon):
 | 
	
		
			
			|  | 240 | +
 | 
	
		
			
			|  | 241 | +    def __init__(self, *args, **kwargs):
 | 
	
		
			
			|  | 242 | +        super().__init__(*args, **kwargs)
 | 
	
		
			
			|  | 243 | +        self.config = self._initConfig()
 | 
	
		
			
			|  | 244 | +        self._server = None
 | 
	
		
			
			|  | 245 | +
 | 
	
		
			
			|  | 246 | +    def _initConfig(self):
 | 
	
		
			
			|  | 247 | +        """Setup configuration dictionary from default, command line and configuration file."""
 | 
	
		
			
			|  | 248 | +
 | 
	
		
			
			|  | 249 | +        # Set config defaults
 | 
	
		
			
			|  | 250 | +        config = DEFAULT_CONFIG.copy()
 | 
	
		
			
			|  | 251 | +        temp_verbose = config["verbose"]
 | 
	
		
			
			|  | 252 | +
 | 
	
		
			
			|  | 253 | +        # Configuration file overrides defaults
 | 
	
		
			
			|  | 254 | +        config_file = os.path.abspath(DEFAULT_CONFIG_FILE)
 | 
	
		
			
			|  | 255 | +        if config_file:
 | 
	
		
			
			|  | 256 | +            fileConf = self._readConfigFile(config_file, temp_verbose)
 | 
	
		
			
			|  | 257 | +            config.update(fileConf)
 | 
	
		
			
			|  | 258 | +        else:
 | 
	
		
			
			|  | 259 | +            if temp_verbose >= 2:
 | 
	
		
			
			|  | 260 | +                print("Running without configuration file.")
 | 
	
		
			
			|  | 261 | +
 | 
	
		
			
			|  | 262 | +        if not useLxml and config["verbose"] >= 1:
 | 
	
		
			
			|  | 263 | +            print(
 | 
	
		
			
			|  | 264 | +                "WARNING: Could not import lxml: using xml instead (slower). Consider installing lxml from http://codespeak.net/lxml/.")
 | 
	
		
			
			|  | 265 | +
 | 
	
		
			
			|  | 266 | +        if not config["provider_mapping"]:
 | 
	
		
			
			|  | 267 | +            print("ERROR: No DAV provider defined. Try --help option.", file=sys.stderr)
 | 
	
		
			
			|  | 268 | +            sys.exit(-1)
 | 
	
		
			
			|  | 269 | +
 | 
	
		
			
			|  | 270 | +        return config
 | 
	
		
			
			|  | 271 | +
 | 
	
		
			
			|  | 272 | +    def _readConfigFile(self, config_file, verbose):
 | 
	
		
			
			|  | 273 | +        """Read configuration file options into a dictionary."""
 | 
	
		
			
			|  | 274 | +
 | 
	
		
			
			|  | 275 | +        if not os.path.exists(config_file):
 | 
	
		
			
			|  | 276 | +            raise RuntimeError("Couldn't open configuration file '%s'." % config_file)
 | 
	
		
			
			|  | 277 | +
 | 
	
		
			
			|  | 278 | +        try:
 | 
	
		
			
			|  | 279 | +            import imp
 | 
	
		
			
			|  | 280 | +            conf = {}
 | 
	
		
			
			|  | 281 | +            configmodule = imp.load_source("configuration_module", config_file)
 | 
	
		
			
			|  | 282 | +
 | 
	
		
			
			|  | 283 | +            for k, v in vars(configmodule).items():
 | 
	
		
			
			|  | 284 | +                if k.startswith("__"):
 | 
	
		
			
			|  | 285 | +                    continue
 | 
	
		
			
			|  | 286 | +                elif isfunction(v):
 | 
	
		
			
			|  | 287 | +                    continue
 | 
	
		
			
			|  | 288 | +                conf[k] = v
 | 
	
		
			
			|  | 289 | +        except Exception as e:
 | 
	
		
			
			|  | 290 | +            exceptioninfo = traceback.format_exception_only(sys.exc_type, sys.exc_value)  # @UndefinedVariable
 | 
	
		
			
			|  | 291 | +            exceptiontext = ""
 | 
	
		
			
			|  | 292 | +            for einfo in exceptioninfo:
 | 
	
		
			
			|  | 293 | +                exceptiontext += einfo + "\n"
 | 
	
		
			
			|  | 294 | +
 | 
	
		
			
			|  | 295 | +            print("Failed to read configuration file: " + config_file + "\nDue to " + exceptiontext, file=sys.stderr)
 | 
	
		
			
			|  | 296 | +            raise
 | 
	
		
			
			|  | 297 | +
 | 
	
		
			
			|  | 298 | +        return conf
 | 
	
		
			
			|  | 299 | +
 | 
	
		
			
			|  | 300 | +    def run(self):
 | 
	
		
			
			|  | 301 | +        app = WsgiDAVApp(self.config)
 | 
	
		
			
			|  | 302 | +
 | 
	
		
			
			|  | 303 | +        # Try running WsgiDAV inside the following external servers:
 | 
	
		
			
			|  | 304 | +        self._runCherryPy(app, self.config, "cherrypy-bundled")
 | 
	
		
			
			|  | 305 | +
 | 
	
		
			
			|  | 306 | +    def _runCherryPy(self, app, config, mode):
 | 
	
		
			
			|  | 307 | +        """Run WsgiDAV using cherrypy.wsgiserver, if CherryPy is installed."""
 | 
	
		
			
			|  | 308 | +        assert mode in ("cherrypy", "cherrypy-bundled")
 | 
	
		
			
			|  | 309 | +
 | 
	
		
			
			|  | 310 | +        try:
 | 
	
		
			
			|  | 311 | +            from wsgidav.server.cherrypy import wsgiserver
 | 
	
		
			
			|  | 312 | +
 | 
	
		
			
			|  | 313 | +            version = "WsgiDAV/%s %s Python/%s" % (
 | 
	
		
			
			|  | 314 | +                __version__,
 | 
	
		
			
			|  | 315 | +                wsgiserver.CherryPyWSGIServer.version,
 | 
	
		
			
			|  | 316 | +                PYTHON_VERSION)
 | 
	
		
			
			|  | 317 | +
 | 
	
		
			
			|  | 318 | +            wsgiserver.CherryPyWSGIServer.version = version
 | 
	
		
			
			|  | 319 | +            protocol = "http"
 | 
	
		
			
			|  | 320 | +
 | 
	
		
			
			|  | 321 | +            if config["verbose"] >= 1:
 | 
	
		
			
			|  | 322 | +                print("Running %s" % version)
 | 
	
		
			
			|  | 323 | +                print("Listening on %s://%s:%s ..." % (protocol, config["host"], config["port"]))
 | 
	
		
			
			|  | 324 | +            self._server = wsgiserver.CherryPyWSGIServer(
 | 
	
		
			
			|  | 325 | +                (config["host"], config["port"]),
 | 
	
		
			
			|  | 326 | +                app,
 | 
	
		
			
			|  | 327 | +                server_name=version,
 | 
	
		
			
			|  | 328 | +            )
 | 
	
		
			
			|  | 329 | +
 | 
	
		
			
			|  | 330 | +            self._server.start()
 | 
	
		
			
			|  | 331 | +        except ImportError as e:
 | 
	
		
			
			|  | 332 | +            if config["verbose"] >= 1:
 | 
	
		
			
			|  | 333 | +                print("Could not import wsgiserver.CherryPyWSGIServer.")
 | 
	
		
			
			|  | 334 | +            return False
 | 
	
		
			
			|  | 335 | +        return True
 | 
	
		
			
			|  | 336 | +
 | 
	
		
			
			|  | 337 | +    def stop(self):
 | 
	
		
			
			|  | 338 | +        self._server.stop()
 | 
	
		
			
			|  | 339 | +        print("WsgiDav : je m'arrête")
 | 
	
		
			
			|  | 340 | +
 | 
	
		
			
			|  | 341 | +    def append_thread_callback(self, callback: collections.Callable) -> None:
 | 
	
		
			
			|  | 342 | +        """
 | 
	
		
			
			|  | 343 | +        Place here the logic who permit to execute a callback in your daemon.
 | 
	
		
			
			|  | 344 | +        To get an exemple of that, take a look at
 | 
	
		
			
			|  | 345 | +        socketserver.BaseServer#service_actions  and how we use it in
 | 
	
		
			
			|  | 346 | +        tracim.lib.daemons.TracimSocketServerMixin#service_actions .
 | 
	
		
			
			|  | 347 | +        :param callback: callback to execute in your thread.
 | 
	
		
			
			|  | 348 | +        """
 | 
	
		
			
			|  | 349 | +        raise NotImplementedError()
 |