fix: parsing XML CalDAV namespace-aware + fallback principal Nextcloud (principals/users/)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -29,14 +29,17 @@ class CalDavDiscovery @Inject constructor(private val client: CalDavClient) {
|
||||
|
||||
// Step 1: resolve principal URL
|
||||
val principalUrl = resolvePrincipalUrl(normalizedBase, credentials, username)
|
||||
?: return@withContext DiscoveryResult.Failure("Impossible de trouver le principal CalDAV")
|
||||
?: return@withContext DiscoveryResult.Failure("Étape 1 échouée : principal CalDAV introuvable pour $username sur $normalizedBase")
|
||||
|
||||
// Step 2: find calendar home
|
||||
val calendarHome = resolveCalendarHome(principalUrl, credentials)
|
||||
?: return@withContext DiscoveryResult.Failure("Impossible de trouver le calendar home")
|
||||
?: return@withContext DiscoveryResult.Failure("Étape 2 échouée : calendar-home-set introuvable sur $principalUrl")
|
||||
|
||||
// Step 3: list VTODO-capable calendars
|
||||
val calendars = listCalendars(calendarHome, credentials, username, baseUrl)
|
||||
if (calendars.isEmpty()) {
|
||||
return@withContext DiscoveryResult.Failure("Étape 3 : aucun calendrier VTODO trouvé sur $calendarHome")
|
||||
}
|
||||
|
||||
val caldavType = detectType(normalizedBase)
|
||||
val now = LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME)
|
||||
@@ -60,7 +63,8 @@ class CalDavDiscovery @Inject constructor(private val client: CalDavClient) {
|
||||
}
|
||||
|
||||
private fun resolvePrincipalUrl(baseUrl: String, credentials: String, username: String): String? {
|
||||
val wellKnown = "$baseUrl/.well-known/caldav"
|
||||
val origin = baseUrl.substringBefore("://") + "://" + baseUrl.substringAfter("://").substringBefore("/")
|
||||
val wellKnown = "$origin/.well-known/caldav"
|
||||
val body = """
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<propfind xmlns="DAV:">
|
||||
@@ -75,7 +79,10 @@ class CalDavDiscovery @Inject constructor(private val client: CalDavClient) {
|
||||
if (href != null) return resolveUrl(baseUrl, href)
|
||||
}
|
||||
}
|
||||
// Fallback: guess principal path
|
||||
// Nextcloud uses principals/users/<username>, fallback to that first
|
||||
val nextcloudFallback = "$baseUrl/principals/users/$username/"
|
||||
val resp = client.propfind(nextcloudFallback, credentials, "0", body)
|
||||
if (resp.isSuccess) return nextcloudFallback
|
||||
return "$baseUrl/principals/$username/"
|
||||
}
|
||||
|
||||
@@ -115,7 +122,7 @@ class CalDavDiscovery @Inject constructor(private val client: CalDavClient) {
|
||||
private fun parseCalendarList(xml: String, baseUrl: String): List<CalendarInfo> {
|
||||
val results = mutableListOf<CalendarInfo>()
|
||||
runCatching {
|
||||
val factory = XmlPullParserFactory.newInstance()
|
||||
val factory = XmlPullParserFactory.newInstance().also { it.isNamespaceAware = true }
|
||||
val parser = factory.newPullParser()
|
||||
parser.setInput(StringReader(xml))
|
||||
|
||||
@@ -160,7 +167,7 @@ class CalDavDiscovery @Inject constructor(private val client: CalDavClient) {
|
||||
}
|
||||
|
||||
private fun extractHref(xml: String, parentTag: String): String? = runCatching {
|
||||
val factory = XmlPullParserFactory.newInstance()
|
||||
val factory = XmlPullParserFactory.newInstance().also { it.isNamespaceAware = true }
|
||||
val parser = factory.newPullParser()
|
||||
parser.setInput(StringReader(xml))
|
||||
var inTarget = false
|
||||
|
||||
@@ -220,7 +220,7 @@ class CalDavSyncManager @Inject constructor(
|
||||
private fun parseMultiStatus(xml: String, baseUrl: String): List<MultiStatusItem> {
|
||||
val results = mutableListOf<MultiStatusItem>()
|
||||
runCatching {
|
||||
val factory = XmlPullParserFactory.newInstance()
|
||||
val factory = XmlPullParserFactory.newInstance().also { it.isNamespaceAware = true }
|
||||
val parser = factory.newPullParser()
|
||||
parser.setInput(StringReader(xml))
|
||||
|
||||
|
||||
Reference in New Issue
Block a user