1
1
Fork 0

Merge pull request #9664 from annando/delete-contacts

Delete removed contacts
This commit is contained in:
Hypolite Petovan 2020-12-16 11:28:56 -05:00 committed by GitHub
commit 1414d43597
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 58 additions and 14 deletions

View file

@ -147,14 +147,25 @@ class APContact
$url = $apcontact['url']; $url = $apcontact['url'];
} }
$data = ActivityPub::fetchContent($url); $curlResult = HTTPSignature::fetchRaw($url);
if (empty($data)) { $failed = empty($curlResult) || empty($curlResult->getBody()) ||
(!$curlResult->isSuccess() && ($curlResult->getReturnCode() != 410));
if (!$failed) {
$data = json_decode($curlResult->getBody(), true);
$failed = empty($data) || !is_array($data);
}
if (!$failed && ($curlResult->getReturnCode() == 410)) {
$data = ['@context' => ActivityPub::CONTEXT, 'id' => $url, 'type' => 'Tombstone'];
}
if ($failed) {
self::markForArchival($fetched_contact ?: []); self::markForArchival($fetched_contact ?: []);
return $fetched_contact; return $fetched_contact;
} }
$compacted = JsonLD::compact($data); $compacted = JsonLD::compact($data);
if (empty($compacted['@id'])) { if (empty($compacted['@id'])) {
return $fetched_contact; return $fetched_contact;
} }
@ -207,8 +218,11 @@ class APContact
} }
// Quit if none of the basic values are set // Quit if none of the basic values are set
if (empty($apcontact['url']) || empty($apcontact['inbox']) || empty($apcontact['type'])) { if (empty($apcontact['url']) || empty($apcontact['type']) || (($apcontact['type'] != 'Tombstone') && empty($apcontact['inbox']))) {
return $fetched_contact; return $fetched_contact;
} elseif ($apcontact['type'] == 'Tombstone') {
// The "inbox" field must have a content
$apcontact['inbox'] = '';
} }
// Quit if this doesn't seem to be an account at all // Quit if this doesn't seem to be an account at all

View file

@ -724,7 +724,7 @@ class Contact
{ {
// We want just to make sure that we don't delete our "self" contact // We want just to make sure that we don't delete our "self" contact
$contact = DBA::selectFirst('contact', ['uid'], ['id' => $id, 'self' => false]); $contact = DBA::selectFirst('contact', ['uid'], ['id' => $id, 'self' => false]);
if (!DBA::isResult($contact) || !intval($contact['uid'])) { if (!DBA::isResult($contact)) {
return; return;
} }
@ -1762,6 +1762,17 @@ class Contact
DBA::update('contact', $fields, ['id' => $cids]); DBA::update('contact', $fields, ['id' => $cids]);
} }
public static function deleteContactByUrl(string $url)
{
// Update contact data for all users
$condition = ['self' => false, 'nurl' => Strings::normaliseLink($url)];
$contacts = DBA::select('contact', ['id', 'uid'], $condition);
while ($contact = DBA::fetch($contacts)) {
Logger::info('Deleting contact', ['id' => $contact['id'], 'uid' => $contact['uid'], 'url' => $url]);
self::remove($contact['id']);
}
}
/** /**
* Helper function for "updateFromProbe". Updates personal and public contact * Helper function for "updateFromProbe". Updates personal and public contact
* *
@ -1914,6 +1925,15 @@ class Contact
return false; return false;
} }
if (!empty($ret['account-type']) && $ret['account-type'] == User::ACCOUNT_TYPE_DELETED) {
Logger::info('Deleted account', ['id' => $id, 'url' => $ret['url'], 'ret' => $ret]);
self::remove($id);
// Delete all contacts with the same URL
self::deleteContactByUrl($ret['url']);
return true;
}
$uid = $contact['uid']; $uid = $contact['uid'];
unset($contact['uid']); unset($contact['uid']);

View file

@ -97,6 +97,7 @@ class User
const ACCOUNT_TYPE_NEWS = 2; const ACCOUNT_TYPE_NEWS = 2;
const ACCOUNT_TYPE_COMMUNITY = 3; const ACCOUNT_TYPE_COMMUNITY = 3;
const ACCOUNT_TYPE_RELAY = 4; const ACCOUNT_TYPE_RELAY = 4;
const ACCOUNT_TYPE_DELETED = 127;
/** /**
* @} * @}
*/ */

View file

@ -104,7 +104,7 @@ class Proxy extends BaseModule
// It shouldn't happen but it does - spaces in URL // It shouldn't happen but it does - spaces in URL
$request['url'] = str_replace(' ', '+', $request['url']); $request['url'] = str_replace(' ', '+', $request['url']);
$fetchResult = HTTPSignature::fetchRaw($request['url'], local_user(), true, ['timeout' => 10]); $fetchResult = HTTPSignature::fetchRaw($request['url'], local_user(), ['timeout' => 10]);
$img_str = $fetchResult->getBody(); $img_str = $fetchResult->getBody();
// If there is an error then return a blank image // If there is an error then return a blank image

View file

@ -68,7 +68,7 @@ class ActivityPub
'manuallyApprovesFollowers' => 'as:manuallyApprovesFollowers', 'manuallyApprovesFollowers' => 'as:manuallyApprovesFollowers',
'sensitive' => 'as:sensitive', 'Hashtag' => 'as:Hashtag', 'sensitive' => 'as:sensitive', 'Hashtag' => 'as:Hashtag',
'directMessage' => 'litepub:directMessage']]; 'directMessage' => 'litepub:directMessage']];
const ACCOUNT_TYPES = ['Person', 'Organization', 'Service', 'Group', 'Application']; const ACCOUNT_TYPES = ['Person', 'Organization', 'Service', 'Group', 'Application', 'Tombstone'];
/** /**
* Checks if the web request is done for the AP protocol * Checks if the web request is done for the AP protocol
* *
@ -113,6 +113,9 @@ class ActivityPub
case 'Application': case 'Application':
$accounttype = User::ACCOUNT_TYPE_RELAY; $accounttype = User::ACCOUNT_TYPE_RELAY;
break; break;
case 'Tombstone':
$accounttype = User::ACCOUNT_TYPE_DELETED;
break;
} }
return $accounttype; return $accounttype;

View file

@ -376,8 +376,7 @@ class HTTPSignature
*/ */
public static function fetch($request, $uid) public static function fetch($request, $uid)
{ {
$opts = ['accept_content' => 'application/activity+json, application/ld+json']; $curlResult = self::fetchRaw($request, $uid);
$curlResult = self::fetchRaw($request, $uid, false, $opts);
if (empty($curlResult)) { if (empty($curlResult)) {
return false; return false;
@ -410,7 +409,7 @@ class HTTPSignature
* @return object CurlResult * @return object CurlResult
* @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/ */
public static function fetchRaw($request, $uid = 0, $binary = false, $opts = []) public static function fetchRaw($request, $uid = 0, $opts = ['accept_content' => 'application/activity+json, application/ld+json'])
{ {
$header = []; $header = [];

View file

@ -31,15 +31,21 @@ use Friendica\Model\Photo;
*/ */
class RemoveContact { class RemoveContact {
public static function execute($id) { public static function execute($id) {
// Only delete if the contact is to be deleted // Only delete if the contact is to be deleted
$contact = DBA::selectFirst('contact', ['uid'], ['deleted' => true, 'id' => $id]); $contact = DBA::selectFirst('contact', ['uid'], ['deleted' => true, 'id' => $id]);
if (!DBA::isResult($contact)) { if (!DBA::isResult($contact)) {
return; return;
} }
Logger::info('Start deleting contact', ['id' => $id]);
// Now we delete the contact and all depending tables // Now we delete the contact and all depending tables
$condition = ['uid' => $contact['uid'], 'contact-id' => $id]; if ($contact['uid'] == 0) {
DBA::delete('post-tag', ['cid' => $id]);
$condition = ["`author-id` = ? OR `owner-id` = ? OR `causer-id` = ? OR `contact-id` = ?",
$id, $id, $id, $id];
} else {
$condition = ['uid' => $contact['uid'], 'contact-id' => $id];
}
do { do {
$items = Item::select(['id', 'guid'], $condition, ['limit' => 100]); $items = Item::select(['id', 'guid'], $condition, ['limit' => 100]);
while ($item = Item::fetch($items)) { while ($item = Item::fetch($items)) {
@ -49,7 +55,8 @@ class RemoveContact {
DBA::close($items); DBA::close($items);
} while (Item::exists($condition)); } while (Item::exists($condition));
Photo::delete(['uid' => $contact['uid'], 'contact-id' => $id]); Photo::delete(['contact-id' => $id]);
DBA::delete('contact', ['id' => $id]); $ret = DBA::delete('contact', ['id' => $id]);
Logger::info('Deleted contact', ['id' => $id, 'result' => $ret]);
} }
} }