forked from grumpydevelop/federator

- fixed how To and CC field (recipients) are handled in general - fixed posts in database - improved some error exceptions and prevented early breaks through try-catch blocks - we now support CN-articles on our newcontent endpoint, with create and update calls
242 lines
9.3 KiB
PHP
242 lines
9.3 KiB
PHP
<?php
|
|
/**
|
|
* SPDX-FileCopyrightText: 2024 Sascha Nitsch (grumpydeveloper) https://contentnation.net/@grumpydevelop
|
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
*
|
|
* @author Yannis Vogel (vogeldevelopment)
|
|
**/
|
|
|
|
namespace Federator\DIO;
|
|
|
|
/**
|
|
* IO functions related to fedUsers
|
|
*/
|
|
class FedUser
|
|
{
|
|
/**
|
|
* add local user based on given user object received from remote service
|
|
* @param \mysqli $dbh database handle
|
|
* @param \Federator\Data\FedUser $user user object to use
|
|
* @param string $_user user/profile name
|
|
* @return void
|
|
*/
|
|
protected static function addLocalUser($dbh, $user, $_user)
|
|
{
|
|
// check if it is timed out user
|
|
$sql = 'select unix_timestamp(`validuntil`) from fedusers where id=?';
|
|
$stmt = $dbh->prepare($sql);
|
|
if ($stmt === false) {
|
|
throw new \Federator\Exceptions\ServerError("FedUser::addLocalUser Failed to prepare statement");
|
|
}
|
|
$stmt->bind_param("s", $_user);
|
|
$validuntil = 0;
|
|
$ret = $stmt->bind_result($validuntil);
|
|
$stmt->execute();
|
|
if ($ret) {
|
|
$stmt->fetch();
|
|
}
|
|
$stmt->close();
|
|
if ($validuntil == 0) {
|
|
$sql = 'insert into fedusers (id, url, name, publickey, summary, type, inboxurl, sharedinboxurl,';
|
|
$sql .= ' followersurl, followingurl, publickeyid, outboxurl, validuntil)';
|
|
$sql .= ' values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, now() + interval 1 day)';
|
|
$stmt = $dbh->prepare($sql);
|
|
if ($stmt === false) {
|
|
throw new \Federator\Exceptions\ServerError("FedUser::addLocalUser Failed to prepare create statement");
|
|
}
|
|
$stmt->bind_param(
|
|
"ssssssssssss",
|
|
$_user,
|
|
$user->actorURL,
|
|
$user->name,
|
|
$user->publicKey,
|
|
$user->summary,
|
|
$user->type,
|
|
$user->inboxURL,
|
|
$user->sharedInboxURL,
|
|
$user->followersURL,
|
|
$user->followingURL,
|
|
$user->publicKeyId,
|
|
$user->outboxURL
|
|
);
|
|
} else {
|
|
// update to existing user
|
|
$sql = 'update fedusers set validuntil=now() + interval 1 day, url=?, name=?, publickey=?, summary=?,';
|
|
$sql .= ' type=?, inboxurl=?, sharedinboxurl=?, followersurl=?, followingurl=?, publickeyid=?, outboxurl=?';
|
|
$sql .= ' where id=?';
|
|
$stmt = $dbh->prepare($sql);
|
|
if ($stmt === false) {
|
|
throw new \Federator\Exceptions\ServerError("FedUser::extendUser Failed to prepare update statement");
|
|
}
|
|
$stmt->bind_param(
|
|
"ssssssssssss",
|
|
$user->actorURL,
|
|
$user->name,
|
|
$user->publicKey,
|
|
$user->summary,
|
|
$user->type,
|
|
$user->inboxURL,
|
|
$user->sharedInboxURL,
|
|
$user->followersURL,
|
|
$user->followingURL,
|
|
$user->publicKeyId,
|
|
$user->outboxURL,
|
|
$_user
|
|
);
|
|
}
|
|
try {
|
|
$stmt->execute();
|
|
$stmt->close();
|
|
$user->id = $_user;
|
|
} catch (\mysqli_sql_exception $e) {
|
|
error_log($sql);
|
|
error_log(print_r($user, true));
|
|
error_log($e->getMessage());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* extend the given user with internal data
|
|
* @param \mysqli $dbh database handle
|
|
* @param \Federator\Data\FedUser $user user to extend
|
|
* @param string $_user user/profile name
|
|
*/
|
|
protected static function extendUser(\mysqli $dbh, \Federator\Data\FedUser $user, $_user): void
|
|
{
|
|
$sql = 'select id,unix_timestamp(`validuntil`) from fedusers where id=?';
|
|
$stmt = $dbh->prepare($sql);
|
|
if ($stmt === false) {
|
|
throw new \Federator\Exceptions\ServerError("FedUser::extendUser Failed to prepare statement");
|
|
}
|
|
$stmt->bind_param("s", $_user);
|
|
$validuntil = 0;
|
|
$ret = $stmt->bind_result($user->id, $validuntil);
|
|
$stmt->execute();
|
|
if ($ret) {
|
|
$stmt->fetch();
|
|
}
|
|
$stmt->close();
|
|
// if a new user, create own database entry with additionally needed info
|
|
if ($user->id === null || $validuntil < time()) {
|
|
self::addLocalUser($dbh, $user, $_user);
|
|
}
|
|
|
|
// no further processing for now
|
|
}
|
|
|
|
/**
|
|
* get user by name
|
|
*
|
|
* @param \mysqli $dbh
|
|
* database handle
|
|
* @param string $_name
|
|
* user name
|
|
* @param \Federator\Cache\Cache|null $cache
|
|
* optional caching service
|
|
* @return \Federator\Data\FedUser
|
|
*/
|
|
public static function getUserByName($dbh, $_name, $cache)
|
|
{
|
|
$user = false;
|
|
|
|
// ask cache
|
|
if ($cache !== null) {
|
|
$user = $cache->getRemoteFedUserByName($_name);
|
|
}
|
|
if ($user !== false) {
|
|
return $user;
|
|
}
|
|
// check our db
|
|
$sql = 'select `id`, `url`, `name`, `publickey`, `summary`, `type`, `inboxurl`, `sharedinboxurl`, `followersurl`,';
|
|
$sql .= ' `followingurl`, `publickeyid`, `outboxurl`';
|
|
$sql .= ' from fedusers where `id`=? and `validuntil`>=now()';
|
|
$stmt = $dbh->prepare($sql);
|
|
if ($stmt === false) {
|
|
throw new \Federator\Exceptions\ServerError("FedUser::getUserByName Failed to prepare statement");
|
|
}
|
|
$stmt->bind_param("s", $_name);
|
|
$user = new \Federator\Data\FedUser();
|
|
$ret = $stmt->bind_result(
|
|
$user->id,
|
|
$user->actorURL,
|
|
$user->name,
|
|
$user->publicKey,
|
|
$user->summary,
|
|
$user->type,
|
|
$user->inboxURL,
|
|
$user->sharedInboxURL,
|
|
$user->followersURL,
|
|
$user->followingURL,
|
|
$user->publicKeyId,
|
|
$user->outboxURL
|
|
);
|
|
$stmt->execute();
|
|
if ($ret) {
|
|
$stmt->fetch();
|
|
}
|
|
$stmt->close();
|
|
|
|
if ($user->id === null) {
|
|
// check if its a federated user with username@domain.ending
|
|
if (preg_match("/^([^@]+)@(.*)$/", $_name, $matches) == 1) {
|
|
// make webfinger request
|
|
$remoteURL = 'https://' . $matches[2] . '/.well-known/webfinger?resource=acct:' . urlencode($_name);
|
|
$headers = ['Accept: application/activity+json'];
|
|
[$response, $info] = \Federator\Main::getFromRemote($remoteURL, $headers);
|
|
if ($info['http_code'] != 200) {
|
|
throw new \Federator\Exceptions\ServerError("FedUser::getUserByName Failed to fetch webfinger for " . $_name);
|
|
}
|
|
$r = json_decode($response, true);
|
|
if ($r === false || $r === null || !is_array($r)) {
|
|
throw new \Federator\Exceptions\ServerError("FedUser::getUserByName Failed to decode webfinger for " . $_name);
|
|
}
|
|
// get the webwinger user url and fetch the user
|
|
if (isset($r['links'])) {
|
|
foreach ($r['links'] as $link) {
|
|
if (isset($link['rel']) && $link['rel'] === 'self') {
|
|
$remoteURL = $link['href'];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (!isset($remoteURL)) {
|
|
throw new \Federator\Exceptions\ServerError("FedUser::getUserByName Failed to find self link in webfinger for " . $_name);
|
|
}
|
|
// fetch the user
|
|
$headers = ['Accept: application/activity+json'];
|
|
[$response, $info] = \Federator\Main::getFromRemote($remoteURL, $headers);
|
|
if ($info['http_code'] != 200) {
|
|
throw new \Federator\Exceptions\ServerError("FedUser::getUserByName Failed to fetch user from remoteUrl for " . $_name);
|
|
}
|
|
$r = json_decode($response, true);
|
|
if ($r === false || $r === null || !is_array($r)) {
|
|
throw new \Federator\Exceptions\ServerError("FedUser::getUserByName Failed to decode user for " . $_name);
|
|
}
|
|
$r['publicKeyId'] = $r['publicKey']['id'];
|
|
$r['publicKey'] = $r['publicKey']['publicKeyPem'];
|
|
if (isset($r['endpoints'])) {
|
|
if (isset($r['endpoints']['sharedInbox'])) {
|
|
$r['sharedInbox'] = $r['endpoints']['sharedInbox'];
|
|
}
|
|
}
|
|
$r['actorURL'] = $remoteURL;
|
|
$data = json_encode($r);
|
|
if ($data === false) {
|
|
throw new \Federator\Exceptions\ServerError("FedUser::getUserByName Failed to encode userdata " . $_name);
|
|
}
|
|
$user = \Federator\Data\FedUser::createFromJson($data);
|
|
}
|
|
}
|
|
|
|
if ($cache !== null && $user !== false) {
|
|
if ($user->id !== null && $user->actorURL !== null) {
|
|
self::addLocalUser($dbh, $user, $_name);
|
|
}
|
|
$cache->saveRemoteFedUserByName($_name, $user);
|
|
}
|
|
if ($user === false) {
|
|
throw new \Federator\Exceptions\ServerError("FedUser::getUserByName User not found");
|
|
}
|
|
return $user;
|
|
}
|
|
}
|