Skip to content

[3.0] Save one recipient's copy of a personal message, not everybody's - #9460

Open
albertlast wants to merge 1 commit into
SimpleMachines:release-3.0from
albertlast:3.0/pm-received-save-scope
Open

[3.0] Save one recipient's copy of a personal message, not everybody's#9460
albertlast wants to merge 1 commit into
SimpleMachines:release-3.0from
albertlast:3.0/pm-received-save-scope

Conversation

@albertlast

Copy link
Copy Markdown
Collaborator

Description

A row in pm_recipients is one member's copy of one personal message, and (id_pm, id_member) is its primary key.

Received::save() named only the PM in its WHERE clause, and set id_member in the SET clause:

UPDATE {db_prefix}pm_recipients
SET
    id_member = {int:member},
    ...
WHERE id_pm = {int:id}

So the statement asks the database to hand every recipient's copy to the same member. On any PM with more than one recipient that is a duplicate key error, every time, for anything that saves — labelling it, marking it read, deleting it.

Reproduced on a clean install: send a PM to two members, then apply a label to it from the inbox.

Database Error: Duplicate entry '4-2' for key 'smf_pm_recipients.PRIMARY'
File: /var/www/html/Sources/PersonalMessage/Received.php
Line: 239

The member belongs in the WHERE clause rather than the SET clause — a copy's owner is its identity, not something a save changes. With that, both recipients keep their own row and their own state; the unread flag on the copy that was not being saved survives, where before the whole statement was rejected.

Labels are owned by a member too, so the same applies to the labelled messages the save clears out. It deleted every label on that PM:

DELETE FROM {db_prefix}pm_labeled_messages
WHERE id_pm = {int:current_pm}
    AND id_label NOT IN ({array_int:labels})

pm_labels rows carry an id_member, so a PM labelled by two recipients has a row per member here, and saving one member's labels threw away the other's. Fixture with (label 2, pm 4) owned by member 1 and (label 4, pm 4) owned by member 2, saving member 1's labels:

rows left
before 2-4
after 2-4, 4-4

That line is only reachable for a PM that has other recipients once the update above works, which is why the two are one change. The subquery is over a different table, so it is fine on MySQL and on PostgreSQL.

Found while sweeping the personal messages area. Two other faults sit alongside this one and are going up separately: Received::loadByPm() collapses every recipient of a PM into one, and the per-message label drop-down submits a value applyActions() no longer accepts.

Issues References (Fixes|Related|Closes)

n/a

A row in pm_recipients is one member's copy of one personal message, and
(id_pm, id_member) is its primary key. Received::save() named only the PM
in its WHERE clause and set id_member in the SET clause, so the statement
asked the database to hand every recipient's copy to the same member.

Any PM with more than one recipient therefore fails with a duplicate key
error the moment something saves it - labelling it, marking it read,
deleting it. On this install, applying a label to a PM sent to two people
gives "Duplicate entry '4-2' for key 'smf_pm_recipients.PRIMARY'".

The member goes in the WHERE clause instead of the SET clause; a copy's
owner is its identity, not something a save changes.

Labels are owned by a member too, so the same applies to the labelled
messages the save clears out: it deleted every label on that PM, including
the ones belonging to the other recipients. That line is only reachable
for a PM with other recipients now that the update above works, so both
belong in the same change.

Signed-off-by: Mathias Albert <mathiaspapealbert@hotmail.com>
Signed-off-by: albertlast <mathiaspapealbert@hotmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants