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
|
// Step 1: resolve principal URL
|
||||||
val principalUrl = resolvePrincipalUrl(normalizedBase, credentials, username)
|
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
|
// Step 2: find calendar home
|
||||||
val calendarHome = resolveCalendarHome(principalUrl, credentials)
|
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
|
// Step 3: list VTODO-capable calendars
|
||||||
val calendars = listCalendars(calendarHome, credentials, username, baseUrl)
|
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 caldavType = detectType(normalizedBase)
|
||||||
val now = LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME)
|
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? {
|
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 = """
|
val body = """
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<propfind xmlns="DAV:">
|
<propfind xmlns="DAV:">
|
||||||
@@ -75,7 +79,10 @@ class CalDavDiscovery @Inject constructor(private val client: CalDavClient) {
|
|||||||
if (href != null) return resolveUrl(baseUrl, href)
|
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/"
|
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> {
|
private fun parseCalendarList(xml: String, baseUrl: String): List<CalendarInfo> {
|
||||||
val results = mutableListOf<CalendarInfo>()
|
val results = mutableListOf<CalendarInfo>()
|
||||||
runCatching {
|
runCatching {
|
||||||
val factory = XmlPullParserFactory.newInstance()
|
val factory = XmlPullParserFactory.newInstance().also { it.isNamespaceAware = true }
|
||||||
val parser = factory.newPullParser()
|
val parser = factory.newPullParser()
|
||||||
parser.setInput(StringReader(xml))
|
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 {
|
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()
|
val parser = factory.newPullParser()
|
||||||
parser.setInput(StringReader(xml))
|
parser.setInput(StringReader(xml))
|
||||||
var inTarget = false
|
var inTarget = false
|
||||||
|
|||||||
@@ -220,7 +220,7 @@ class CalDavSyncManager @Inject constructor(
|
|||||||
private fun parseMultiStatus(xml: String, baseUrl: String): List<MultiStatusItem> {
|
private fun parseMultiStatus(xml: String, baseUrl: String): List<MultiStatusItem> {
|
||||||
val results = mutableListOf<MultiStatusItem>()
|
val results = mutableListOf<MultiStatusItem>()
|
||||||
runCatching {
|
runCatching {
|
||||||
val factory = XmlPullParserFactory.newInstance()
|
val factory = XmlPullParserFactory.newInstance().also { it.isNamespaceAware = true }
|
||||||
val parser = factory.newPullParser()
|
val parser = factory.newPullParser()
|
||||||
parser.setInput(StringReader(xml))
|
parser.setInput(StringReader(xml))
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user