Fix store property backup ownership
Debian Trixie package bundle / packages (push) Successful in 22m6s
Debian Trixie package bundle / packages (push) Successful in 22m6s
This commit is contained in:
@@ -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([
|
||||
|
||||
@@ -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):
|
||||
|
||||
Reference in New Issue
Block a user