|
| 1 | +# CLAUDE.md |
| 2 | + |
| 3 | +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. |
| 4 | + |
| 5 | +## Project Overview |
| 6 | + |
| 7 | +PyWebDAV3 is a Python 3.8+ implementation of a WebDAV server and library supporting WebDAV levels 1 and 2 (including LOCK/UNLOCK). The project provides both a standalone server (`davserver`) and a library for integrating WebDAV capabilities into applications. |
| 8 | + |
| 9 | +## Architecture |
| 10 | + |
| 11 | +The codebase is organized into two main packages: |
| 12 | + |
| 13 | +### pywebdav/lib/ - Core WebDAV Library |
| 14 | +- **WebDAVServer.py**: Main DAV request handler class (`DAVRequestHandler`) that inherits from `AuthServer.AuthRequestHandler` and `LockManager`. Implements HTTP methods (GET, HEAD, PUT, OPTIONS, PROPFIND, PROPPATCH, MKCOL, REPORT, LOCK, UNLOCK). |
| 15 | +- **AuthServer.py**: Provides HTTP Basic Authentication layer on top of standard HTTP server. |
| 16 | +- **iface.py**: Abstract interface class (`dav_interface`) that defines the contract for backend data sources. Any custom backend must implement this interface to handle property retrieval, data access, and resource management. |
| 17 | +- **propfind.py, report.py, davcopy.py, davmove.py, delete.py**: Separate classes for each DAV method that handle XML parsing and response generation. |
| 18 | +- **davcmd.py**: Utility functions for DAV operations (copyone, copytree, moveone, movetree, delone, deltree). |
| 19 | +- **locks.py**: `LockManager` class implementing WebDAV level 2 locking functionality. |
| 20 | +- **INI_Parse.py**: Configuration file parser for server settings. |
| 21 | +- **dbconn.py**: Database connection utilities for optional MySQL support. |
| 22 | +- **utils.py, status.py, errors.py, constants.py**: Utility functions and definitions. |
| 23 | + |
| 24 | +### pywebdav/server/ - Reference Server Implementation |
| 25 | +- **server.py**: Entry point for the standalone server. Contains `run()` function (CLI wrapper with argument parsing) and `runserver()` function (internal server startup). The `davserver` console script calls `run()`. |
| 26 | +- **fshandler.py**: `FilesystemHandler` class implements the `dav_interface` for serving files from a local filesystem. This serves as the reference implementation for custom backends. |
| 27 | +- **fileauth.py**: `DAVAuthHandler` - authentication handler implementation. |
| 28 | +- **mysqlauth.py**: `MySQLAuthHandler` - optional MySQL-based authentication. |
| 29 | +- **daemonize.py**: Unix daemon support for server. |
| 30 | + |
| 31 | +### Key Architectural Pattern |
| 32 | +The WebDAVServer class requires an interface class (implementing `dav_interface`) to connect to actual data. This separation allows backends for filesystems, databases, or other storage. The interface class is instantiated by WebDAVServer and called for property retrieval, resource creation, data access, etc. XML parsing for each DAV method is factored into dedicated classes (propfind.py, etc.) which obtain data through the interface class. |
| 33 | + |
| 34 | +## Development Commands |
| 35 | + |
| 36 | +### Build and Package |
| 37 | +```bash |
| 38 | +python -m build # Build distribution packages (requires build module) |
| 39 | +twine check dist/* # Validate package format |
| 40 | +``` |
| 41 | + |
| 42 | +### Installation |
| 43 | +```bash |
| 44 | +pip install -e . # Development installation |
| 45 | +pip install PyWebDAV3 # Install from PyPI |
| 46 | +``` |
| 47 | + |
| 48 | +### Running the Server |
| 49 | +```bash |
| 50 | +davserver -D /tmp -n -J # Run server: -D=directory, -n=no auth, -J=no lock |
| 51 | +davserver -D /path -u user -p pass # Run with authentication |
| 52 | +davserver --help # Full options list |
| 53 | +``` |
| 54 | + |
| 55 | +Common options: |
| 56 | +- `-D/--directory`: Root directory to serve (default: /tmp) |
| 57 | +- `-H/--host`: Listen host (default: localhost) |
| 58 | +- `-P/--port`: Port number (default: 8008) |
| 59 | +- `-u/--user`, `-p/--password`: Authentication credentials |
| 60 | +- `-n/--noauth`: Disable authentication |
| 61 | +- `-J/--nolock`: Disable LOCK/UNLOCK (WebDAV level 2) |
| 62 | +- `-M/--nomime`: Disable mimetype sniffing |
| 63 | +- `-T/--noiter`: Disable iterator (fixes some corruption issues) |
| 64 | +- `-v/--verbose`: Verbose output |
| 65 | +- `-l/--loglevel`: Set log level (DEBUG, INFO, WARNING, ERROR, CRITICAL) |
| 66 | +- `-d/--daemon`: Daemon mode (start|stop|restart|status) |
| 67 | + |
| 68 | +### Testing |
| 69 | +```bash |
| 70 | +python test/test_litmus.py # Run litmus WebDAV compliance tests (from repo root) |
| 71 | +``` |
| 72 | + |
| 73 | +The test suite uses litmus 0.13, a C-based WebDAV server protocol compliance test suite originally by Joe Orton. The test harness (`test/test_litmus.py`) automatically: |
| 74 | +1. Extracts and compiles litmus from `test/litmus-0.13.tar.gz` on first run (`./configure && make`) |
| 75 | +2. Starts a temporary davserver instance on port 38028 |
| 76 | +3. Runs the full litmus test battery via `make check` |
| 77 | +4. Verifies both authenticated (user: test, pass: pass) and non-authenticated modes |
| 78 | + |
| 79 | +#### Litmus Test Suites |
| 80 | +Litmus comprises 5 separate test executables that each verify different WebDAV functionality: |
| 81 | + |
| 82 | +- **basic**: Core WebDAV level 1 operations |
| 83 | + - OPTIONS (DAV: header checking) |
| 84 | + - PUT, GET with byte-level comparison |
| 85 | + - MKCOL (collection creation) |
| 86 | + - DELETE (both collections and non-collections) |
| 87 | + - put_no_parent (error handling) |
| 88 | + |
| 89 | +- **copymove**: COPY and MOVE operations testing all combinations |
| 90 | + - Overwrite true/false |
| 91 | + - Destination exists/doesn't exist |
| 92 | + - Collection/non-collection resources |
| 93 | + - Depth: 0 COPY of collections |
| 94 | + |
| 95 | +- **props**: Property manipulation and querying (PROPFIND, PROPPATCH) |
| 96 | + - Set, delete, replace properties |
| 97 | + - Dead property persistence across COPY |
| 98 | + - Namespace handling |
| 99 | + - Unicode values in property values |
| 100 | + - PROPPATCH propertyupdate evaluation order |
| 101 | + |
| 102 | +- **locks**: WebDAV level 2 locking (LOCK, UNLOCK) |
| 103 | + - Lock/unlock operations |
| 104 | + - Exclusive vs shared locks |
| 105 | + - Lock discovery |
| 106 | + - Attempts to modify locked resources (as owner vs non-owner) |
| 107 | + - PROPPATCH on locked resources |
| 108 | + - Collection locking |
| 109 | + - If: header handling with lock-tokens and etags |
| 110 | + |
| 111 | +- **http**: HTTP protocol-level tests |
| 112 | + - Conditional requests |
| 113 | + - expect100 handling |
| 114 | + - Non-ASCII characters in URIs |
| 115 | + |
| 116 | +Each test adds an `X-Litmus-One` header to requests for debugging. After running, `debug.log` contains full network traces. Tests run against a temporary directory and require ability to create a `litmus` collection at the target URL. |
| 117 | + |
| 118 | +#### Manual Litmus Testing |
| 119 | +To run litmus tests manually for debugging: |
| 120 | +```bash |
| 121 | +cd test/litmus-0.13 |
| 122 | +sh ./configure && make # Compile litmus (one-time) |
| 123 | +davserver -D /tmp/davtest -n & # Start server in background |
| 124 | +make URL=http://localhost:8008/ check # Run all test suites |
| 125 | +make URL=http://localhost:8008/ CREDS="user pass" check # With auth |
| 126 | +./basic http://localhost:8008/ # Run single test suite |
| 127 | +./locks http://localhost:8008/ user pass # Single suite with credentials |
| 128 | +``` |
| 129 | +After tests complete, examine `debug.log` for full request/response traces. |
| 130 | + |
| 131 | +### CI/CD |
| 132 | +GitHub Actions workflow (`.github/workflows/python-package.yml`) runs on push/PR to master: |
| 133 | +- Tests Python 3.8, 3.9, 3.10 |
| 134 | +- Builds package and validates with twine |
| 135 | +- Runs litmus test suite |
| 136 | + |
| 137 | +## Version Management |
| 138 | +Uses `git-versioner` for version numbers from git tags. Version is read from `pywebdav.__version__` which is populated by the versioner. |
| 139 | + |
| 140 | +## Dependencies |
| 141 | +- `six`: Python 2/3 compatibility (maintained for legacy reasons) |
| 142 | +- `git-versioner`: Version management from git tags |
| 143 | + |
| 144 | +Optional: |
| 145 | +- MySQL support requires MySQLdb (install separately: `pip install mysqlclient`) |
0 commit comments