Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Unverified Commit 24d4ba65 authored by Ricki Hirner's avatar Ricki Hirner Committed by GitHub
Browse files

Fix NPE when `Event._ID` is larger than `Integer.MAX_VALUE` (#1661)

* Add test that reproduces NPE

* Fix NPE when Event ID is larger than Integer.MAX_VALUE
parent ae96f1ff
Loading
Loading
Loading
Loading
+31 −10
Original line number Diff line number Diff line
@@ -8,14 +8,18 @@ import android.accounts.Account
import android.content.ContentProviderClient
import android.content.ContentUris
import android.content.ContentValues
import android.content.Entity
import android.provider.CalendarContract
import android.provider.CalendarContract.ACCOUNT_TYPE_LOCAL
import android.provider.CalendarContract.Events
import androidx.core.content.contentValuesOf
import androidx.test.platform.app.InstrumentationRegistry
import at.bitfire.ical4android.Event
import at.bitfire.ical4android.util.MiscUtils.asSyncAdapter
import at.bitfire.ical4android.util.MiscUtils.closeCompat
import at.bitfire.synctools.storage.calendar.AndroidCalendar
import at.bitfire.synctools.storage.calendar.AndroidCalendarProvider
import at.bitfire.synctools.storage.calendar.AndroidEvent2
import at.bitfire.synctools.test.InitCalendarProviderRule
import dagger.hilt.android.testing.HiltAndroidRule
import dagger.hilt.android.testing.HiltAndroidTest
@@ -25,6 +29,7 @@ import net.fortuna.ical4j.model.property.RecurrenceId
import net.fortuna.ical4j.model.property.Status
import org.junit.After
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNull
import org.junit.Before
import org.junit.Rule
import org.junit.Test
@@ -44,6 +49,7 @@ class LocalCalendarTest {
    lateinit var localCalendarFactory: LocalCalendar.Factory

    private val account = Account("LocalCalendarTest", ACCOUNT_TYPE_LOCAL)
    private lateinit var androidCalendar: AndroidCalendar
    private lateinit var client: ContentProviderClient
    private lateinit var calendar: LocalCalendar

@@ -55,12 +61,13 @@ class LocalCalendarTest {
        client = context.contentResolver.acquireContentProviderClient(CalendarContract.AUTHORITY)!!

        val provider = AndroidCalendarProvider(account, client)
        calendar = localCalendarFactory.create(provider.createAndGetCalendar(ContentValues()))
        androidCalendar = provider.createAndGetCalendar(ContentValues())
        calendar = localCalendarFactory.create(androidCalendar)
    }

    @After
    fun tearDown() {
        calendar.androidCalendar.delete()
        androidCalendar.delete()
        client.closeCompat()
    }

@@ -135,24 +142,38 @@ class LocalCalendarTest {
            flags = LocalResource.FLAG_REMOTELY_PRESENT
        )
        val localEvent = calendar.findByName("filename.ics")!!
        val eventId = localEvent.id
        val eventUrl = androidCalendar.eventUri(localEvent.id)

        // set event as dirty
        client.update(ContentUris.withAppendedId(Events.CONTENT_URI.asSyncAdapter(account), eventId), ContentValues(1).apply {
            put(Events.DIRTY, 1)
        }, null, null)
        client.update(eventUrl, contentValuesOf(
            Events.DIRTY to 1
        ), null, null)

        // this method should mark the event as deleted
        calendar.deleteDirtyEventsWithoutInstances()

        // verify that event is not marked as deleted
        client.query(
            ContentUris.withAppendedId(Events.CONTENT_URI.asSyncAdapter(account), eventId),
            arrayOf(Events.DELETED), null, null, null
        )!!.use { cursor ->
        client.query(eventUrl, arrayOf(Events.DELETED), null, null, null)!!.use { cursor ->
            cursor.moveToNext()
            assertEquals(0, cursor.getInt(0))
        }
    }

    @Test
    fun testRemoveNotDirtyMarked_IdLargerThanIntMaxValue() {
        val id = androidCalendar.addEvent(Entity(contentValuesOf(
            Events.CALENDAR_ID to androidCalendar.id,
            Events._ID to Int.MAX_VALUE.toLong() + 10,
            Events.DTSTART to System.currentTimeMillis(),
            Events.DTEND to System.currentTimeMillis(),
            Events.TITLE to "Some Event",
            Events.DIRTY to 0,
            AndroidEvent2.COLUMN_FLAGS to 123
        )))

        calendar.removeNotDirtyMarked(123)

        assertNull(androidCalendar.getEvent(id))
    }

}
 No newline at end of file
+2 −2
Original line number Diff line number Diff line
@@ -119,11 +119,11 @@ class LocalCalendar @AssistedInject constructor(
            "${Events.CALENDAR_ID}=? AND NOT ${Events.DIRTY} AND ${Events.ORIGINAL_ID} IS NULL AND ${AndroidEvent2.COLUMN_FLAGS}=?",
            arrayOf(androidCalendar.id.toString(), flags.toString())
        ) { values ->
            val id = values.getAsInteger(Events._ID)
            val id = values.getAsLong(Events._ID)

            // delete event and possible exceptions (content provider doesn't delete exceptions itself)
            batch += BatchOperation.CpoBuilder
                .newDelete(Events.CONTENT_URI.asSyncAdapter(androidCalendar.account))
                .newDelete(androidCalendar.eventsUri)
                .withSelection("${Events._ID}=? OR ${Events.ORIGINAL_ID}=?", arrayOf(id.toString(), id.toString()))
        }
        return batch.commit()