Fix store property backup ownership
Debian Trixie package bundle / packages (push) Successful in 22m6s

This commit is contained in:
Mario Fetka
2026-07-23 14:13:07 +02:00
parent 5685de2dd7
commit 261ee8b69e
4 changed files with 65 additions and 30 deletions
+13 -4
View File
@@ -1066,17 +1066,26 @@ StoreObjectIterProperties(StoreClient *client, StoreObject *document)
while ((ccode = MsgSQLResults(client->storedb, &stmt)) > 0) {
StorePropInfo prop;
char *storedName = NULL;
char *storedValue = NULL;
memset(&prop, 0, sizeof(StorePropInfo));
prop.type = MsgSQLResultInt(&stmt, 0);
MsgSQLResultTextPtr(&stmt, 1, &prop.name);
MsgSQLResultTextPtr(&stmt, 2, &prop.value);
MsgSQLResultTextPtr(&stmt, 1, &storedName);
MsgSQLResultTextPtr(&stmt, 2, &storedValue);
prop.name = storedName;
prop.value = storedValue;
/*
* Known integer properties are stored without a name. The fixup
* replaces that NULL pointer with a static StorePropTable string,
* so retain the database-owned pointers for cleanup.
*/
StorePropertyFixup(&prop);
StoreOutputProperty(client, prop.type, prop.name, prop.value);
MemFree(prop.name);
MemFree(prop.value);
MemFree(storedName);
MemFree(storedValue);
}
MsgSQLFinalize(&stmt);
@@ -51,6 +51,14 @@ def _event_filename(event):
return "import-" + hashlib.sha256(uid).hexdigest()
def _json_component_value(component, *names):
for name in names:
field = component.get(name)
if isinstance(field, dict):
return field.get("value")
return None
class CalendarsCommand(Command):
log = logging.getLogger("Bongo.StoreTool")
@@ -108,17 +116,9 @@ class CalendarEventsCommand(Command):
jsob = simplejson.loads(event.props["nmap.document"].strip())
comp = jsob["components"][0]
summary = comp.get("summary")
if summary is not None:
summary = summary.get("value")
start = comp.get("start")
if start is not None:
start = start.get("value")
end = comp.get("end")
if end is not None:
end = end.get("value")
summary = _json_component_value(comp, "summary")
start = _json_component_value(comp, "dtstart", "start")
end = _json_component_value(comp, "dtend", "end")
rows.append((summary, start, end))
finally:
@@ -32,7 +32,8 @@ from bongo.external.simpletal import simpleTAL, simpleTALES
from bongo.store.StoreClient import CalendarACL, StoreClient
from bongo.storetool.CalendarCommands import (_collection_name,
_event_filename,
_event_payload)
_event_payload,
_json_component_value)
from bongo.storetool.Connection import connect_store
from bongo.storetool.ContactCommands import AddressbookContactsCommand
from bongo.storetool.ExportMailbox import MaildirMailbox, MboxMailbox
@@ -45,8 +46,9 @@ class _Response:
class _Stream:
def __init__(self, responses):
def __init__(self, responses, reads=None):
self.responses = list(responses)
self.reads = list(reads or [])
self.lines = []
self.raw = []
@@ -59,6 +61,11 @@ class _Stream:
def GetResponse(self):
return self.responses.pop(0)
def Read(self, length):
value = self.reads.pop(0)
self.assert_read_length = length
return value
class Python3RuntimeTests(unittest.TestCase):
@mock.patch("bongo.storetool.Connection.StoreClient")
@@ -89,6 +96,13 @@ class Python3RuntimeTests(unittest.TestCase):
self.assertIn("BEGIN:VEVENT", payload)
self.assertIn("UID:event@example.test", payload)
self.assertRegex(_event_filename(event), r"^import-[0-9a-f]{64}$")
component = {
"dtstart": {"value": "20260724T080000Z"},
"start": {"value": "legacy"},
}
self.assertEqual(
_json_component_value(component, "dtstart", "start"),
"20260724T080000Z")
def test_contact_listing_reads_document_bodies(self):
addressbook = SimpleNamespace(uid="addressbook-guid")
@@ -151,6 +165,21 @@ class Python3RuntimeTests(unittest.TestCase):
client.stream = _Stream([_Response(4224, "Document doesn't exist")])
self.assertIsNone(client.Info("/events/new-event"))
def test_store_propget_does_not_request_imap_uid_for_non_mail(self):
client = object.__new__(StoreClient)
client.stream = _Stream(
[_Response(2001, "nmap.type 1"), _Response(1000)],
[b"3", b"\r\n"])
self.assertEqual(client.PropGet("event-guid"), {"nmap.type": "3"})
self.assertEqual(client.stream.lines, ["PROPGET event-guid"])
def test_store_propget_ignores_missing_optional_property_response(self):
client = object.__new__(StoreClient)
client.stream = _Stream([_Response(3245), _Response(1000)])
self.assertEqual(client.PropGet("event-guid"), {})
def test_store_write_links_event_after_successful_write(self):
client = object.__new__(StoreClient)
client.stream = _Stream([
+10 -13
View File
@@ -707,8 +707,8 @@ class StoreClient:
r = self.stream.GetResponse()
while r.code == 2001 or r.code == 3245:
if r.code == 3245:
# set empty properties to None
props[key] = None
# A missing optional property has no value to consume. In
# particular, do not reuse the key from a previous response.
r = self.stream.GetResponse()
continue
@@ -721,17 +721,14 @@ class StoreClient:
# eat the \r\n afterward
self.stream.Read(2)
r = self.stream.GetResponse()
# automatically pull out imap_uid from Bongo 0.3.
if name is None and "nmap.mail.imapuid" not in props:
self.stream.Write("PROPGET %s nmap.mail.imapuid" % (doc))
r = self.stream.GetResponse()
if r.code != 2001:
raise CommandError(r)
(key, length) = r.message.split(" ", 2)
raw_data = self.stream.Read(int(length))
self.stream.Read(2)
props["nmap.mail.imapuid"] = self.force_utf8(raw_data)
if r.code != 1000:
raise CommandError(r)
# Current stores include nmap.mail.imapuid in the complete property
# response for mail and folder objects. Contacts and events do not
# have an IMAP UID, so the old Bongo 0.3 compatibility query must not
# be issued for every document.
return props
def PropSet(self, doc, name, value=None):