streams/include/connections.php

1027 lines
29 KiB
PHP
Raw Normal View History

2021-12-03 03:01:39 +00:00
<?php
2010-09-09 03:14:17 +00:00
2021-12-03 03:01:39 +00:00
/** @file */
2021-12-02 22:33:36 +00:00
use Sabre\VObject\Component\VCard;
use Sabre\VObject\Reader;
2022-02-16 04:08:28 +00:00
use Code\Daemon\Run;
use Code\Lib\Libsync;
use Code\Lib\Channel;
use Code\Extend\Hook;
use Code\Render\Theme;
2022-02-12 20:43:29 +00:00
2012-09-10 04:17:06 +00:00
2021-12-03 03:01:39 +00:00
function abook_store_lowlevel($arr)
{
$store = [
'abook_account' => ((array_key_exists('abook_account', $arr)) ? $arr['abook_account'] : 0),
'abook_channel' => ((array_key_exists('abook_channel', $arr)) ? $arr['abook_channel'] : 0),
'abook_xchan' => ((array_key_exists('abook_xchan', $arr)) ? $arr['abook_xchan'] : ''),
'abook_alias' => ((array_key_exists('abook_alias', $arr)) ? $arr['abook_alias'] : ''),
'abook_my_perms' => ((array_key_exists('abook_my_perms', $arr)) ? $arr['abook_my_perms'] : 0),
'abook_their_perms' => ((array_key_exists('abook_their_perms', $arr)) ? $arr['abook_their_perms'] : 0),
'abook_closeness' => ((array_key_exists('abook_closeness', $arr)) ? $arr['abook_closeness'] : 99),
'abook_created' => ((array_key_exists('abook_created', $arr)) ? $arr['abook_created'] : NULL_DATE),
'abook_updated' => ((array_key_exists('abook_updated', $arr)) ? $arr['abook_updated'] : NULL_DATE),
'abook_connected' => ((array_key_exists('abook_connected', $arr)) ? $arr['abook_connected'] : NULL_DATE),
'abook_dob' => ((array_key_exists('abook_dob', $arr)) ? $arr['abook_dob'] : NULL_DATE),
'abook_flags' => ((array_key_exists('abook_flags', $arr)) ? $arr['abook_flags'] : 0),
'abook_censor' => ((array_key_exists('abook_censor', $arr)) ? $arr['abook_censor'] : 0),
'abook_blocked' => ((array_key_exists('abook_blocked', $arr)) ? $arr['abook_blocked'] : 0),
'abook_ignored' => ((array_key_exists('abook_ignored', $arr)) ? $arr['abook_ignored'] : 0),
'abook_hidden' => ((array_key_exists('abook_hidden', $arr)) ? $arr['abook_hidden'] : 0),
'abook_archived' => ((array_key_exists('abook_archived', $arr)) ? $arr['abook_archived'] : 0),
'abook_pending' => ((array_key_exists('abook_pending', $arr)) ? $arr['abook_pending'] : 0),
'abook_unconnected' => ((array_key_exists('abook_unconnected', $arr)) ? $arr['abook_unconnected'] : 0),
'abook_self' => ((array_key_exists('abook_self', $arr)) ? $arr['abook_self'] : 0),
'abook_feed' => ((array_key_exists('abook_feed', $arr)) ? $arr['abook_feed'] : 0),
'abook_not_here' => ((array_key_exists('abook_not_here', $arr)) ? $arr['abook_not_here'] : 0),
'abook_profile' => ((array_key_exists('abook_profile', $arr)) ? $arr['abook_profile'] : ''),
'abook_incl' => ((array_key_exists('abook_incl', $arr)) ? $arr['abook_incl'] : ''),
'abook_excl' => ((array_key_exists('abook_excl', $arr)) ? $arr['abook_excl'] : ''),
'abook_instance' => ((array_key_exists('abook_instance', $arr)) ? $arr['abook_instance'] : '')
];
return create_table_from_array('abook', $store);
}
2021-12-03 03:01:39 +00:00
function rconnect_url($channel_id, $xchan)
{
2021-12-03 03:01:39 +00:00
if (! $xchan) {
return z_root() . '/fedi_id/' . $channel_id;
}
2021-12-03 03:01:39 +00:00
$r = q(
"select abook_id from abook where abook_channel = %d and abook_xchan = '%s' limit 1",
intval($channel_id),
dbesc($xchan)
);
2021-12-03 03:01:39 +00:00
// Already connected
if ($r) {
return EMPTY_STR;
}
2021-12-03 03:01:39 +00:00
$r = q(
"select * from xchan where xchan_hash = '%s' limit 1",
dbesc($xchan)
);
if (($r) && ($r[0]['xchan_follow'])) {
return $r[0]['xchan_follow'];
}
2021-12-03 03:01:39 +00:00
$r = q(
"select hubloc_url from hubloc where hubloc_hash = '%s' and hubloc_primary = 1 limit 1",
dbesc($xchan)
);
2021-12-03 03:01:39 +00:00
if ($r) {
return $r[0]['hubloc_url'] . '/follow?f=&url=%s';
}
return EMPTY_STR;
}
2021-12-03 03:01:39 +00:00
function abook_connections($channel_id, $sql_conditions = '')
{
$r = q(
"select * from abook left join xchan on abook_xchan = xchan_hash where abook_channel = %d
2015-06-15 04:08:00 +00:00
and abook_self = 0 $sql_conditions",
2021-12-03 03:01:39 +00:00
intval($channel_id)
);
return(($r) ? $r : []);
}
2019-04-17 06:31:36 +00:00
2021-12-03 03:01:39 +00:00
function abook_by_hash($channel_id, $hash)
{
$r = q(
"select * from abook left join xchan on abook_xchan = xchan_hash where abook_channel = %d
2019-04-17 06:31:36 +00:00
and abook_self = 0 and abook_xchan = '%s'",
2021-12-03 03:01:39 +00:00
intval($channel_id),
dbesc($hash)
);
return(($r) ? array_shift($r) : false);
}
2019-04-17 06:31:36 +00:00
2021-12-03 03:01:39 +00:00
function abook_self($channel_id)
{
$r = q(
"select * from abook left join xchan on abook_xchan = xchan_hash where abook_channel = %d
2015-06-15 04:08:00 +00:00
and abook_self = 1 limit 1",
2021-12-03 03:01:39 +00:00
intval($channel_id)
);
return(($r) ? $r[0] : []);
2012-10-31 04:37:58 +00:00
}
2021-12-03 03:01:39 +00:00
function vcard_from_xchan($xchan, $observer = null, $mode = '')
{
if (! $xchan) {
if (App::$poi) {
$xchan = App::$poi;
} elseif (is_array(App::$profile) && App::$profile['channel_hash']) {
$r = q(
"select * from xchan where xchan_hash = '%s' limit 1",
dbesc(App::$profile['channel_hash'])
);
if ($r) {
$xchan = $r[0];
}
}
}
if (! $xchan) {
return;
}
$connect = false;
if (local_channel()) {
$r = q(
"select * from abook where abook_xchan = '%s' and abook_channel = %d limit 1",
dbesc($xchan['xchan_hash']),
intval(local_channel())
);
if (! $r) {
$connect = t('Connect');
}
}
// don't provide a connect button for transient or one-way identities
if (in_array($xchan['xchan_network'], [ 'rss','anon','token','unknown' ])) {
$connect = false;
}
if (array_key_exists('channel_id', $xchan)) {
App::$profile_uid = $xchan['channel_id'];
}
$url = (($observer)
? z_root() . '/magic?f=&owa=1&bdest=' . bin2hex($xchan['xchan_url']) . '&addr=' . $xchan['xchan_addr']
: $xchan['xchan_url']
);
2021-12-25 22:45:01 +00:00
$profdm = EMPTY_STR;
$profdm_url = EMPTY_STR;
if (local_channel()) {
$can_dm = their_perms_contains(local_channel(),$xchan['xchan_hash'],'post_mail') && $xchan['xchan_type'] !== XCHAN_TYPE_GROUP;
if ($can_dm) {
$profdm = t('Direct Message');
$profdm_url = z_root() . '/rpost?f='
. '&to='
. urlencode($xchan['xchan_hash'])
. '&body='
. urlencode('@!{' . $xchan['xchan_addr'] ? $xchan['xchan_addr'] : $xchan['xchan_url'] . '}');
}
}
2022-02-12 20:43:29 +00:00
return replace_macros(Theme::get_template('xchan_vcard.tpl'), [
2021-12-03 03:01:39 +00:00
'$name' => $xchan['xchan_name'],
'$photo' => ((is_array(App::$profile) && array_key_exists('photo', App::$profile)) ? App::$profile['photo'] : $xchan['xchan_photo_l']),
'$follow' => urlencode(($xchan['xchan_addr']) ? $xchan['xchan_addr'] : $xchan['xchan_url']),
'$link' => zid($xchan['xchan_url']),
'$connect' => $connect,
2021-12-25 22:45:01 +00:00
'$profdm' => $profdm,
'$profdm_url' => $profdm_url,
2021-12-03 03:01:39 +00:00
'$newwin' => (($mode === 'chanview') ? t('New window') : EMPTY_STR),
'$newtit' => t('Open the selected location in a different window or browser tab'),
'$url' => $url,
]);
}
2021-12-03 03:01:39 +00:00
function abook_toggle_flag($abook, $flag)
{
$field = EMPTY_STR;
switch ($flag) {
case ABOOK_FLAG_BLOCKED:
$field = 'abook_blocked';
break;
case ABOOK_FLAG_IGNORED:
$field = 'abook_ignored';
break;
case ABOOK_FLAG_CENSORED:
$field = 'abook_censor';
break;
case ABOOK_FLAG_HIDDEN:
$field = 'abook_hidden';
break;
case ABOOK_FLAG_ARCHIVED:
$field = 'abook_archived';
break;
case ABOOK_FLAG_PENDING:
$field = 'abook_pending';
break;
case ABOOK_FLAG_UNCONNECTED:
$field = 'abook_unconnected';
break;
case ABOOK_FLAG_SELF:
$field = 'abook_self';
break;
case ABOOK_FLAG_FEED:
$field = 'abook_feed';
break;
default:
break;
}
if (! $field) {
return;
}
$r = q(
"UPDATE abook set $field = (1 - $field) where abook_id = %d and abook_channel = %d",
intval($abook['abook_id']),
intval($abook['abook_channel'])
);
// if unsetting the archive bit, update the timestamps so we'll try to connect for an additional 30 days.
if (($flag === ABOOK_FLAG_ARCHIVED) && (intval($abook['abook_archived']))) {
$r = q(
"update abook set abook_connected = '%s', abook_updated = '%s'
PostgreSQL support initial commit There were 11 main types of changes: - UPDATE's and DELETE's sometimes had LIMIT 1 at the end of them. This is not only non-compliant but it would certainly not do what whoever wrote it thought it would. It is likely this mistake was just copied from Friendica. All of these instances, the LIMIT 1 was simply removed. - Bitwise operations (and even some non-zero int checks) erroneously rely on MySQL implicit integer-boolean conversion in the WHERE clauses. This is non-compliant (and bad programming practice to boot). Proper explicit boolean conversions were added. New queries should use proper conventions. - MySQL has a different operator for bitwise XOR than postgres. Rather than add yet another dba_ func, I converted them to "& ~" ("AND NOT") when turning off, and "|" ("OR") when turning on. There were no true toggles (XOR). New queries should refrain from using XOR when not necessary. - There are several fields which the schema has marked as NOT NULL, but the inserts don't specify them. The reason this works is because mysql totally ignores the constraint and adds an empty text default automatically. Again, non-compliant, obviously. In these cases a default of empty text was added. - Several statements rely on a non-standard MySQL feature (http://dev.mysql.com/doc/refman/5.5/en/group-by-handling.html). These queries can all be rewritten to be standards compliant. Interestingly enough, the newly rewritten standards compliant queries run a zillion times faster, even on MySQL. - A couple of function/operator name translations were needed (RAND/RANDOM, GROUP_CONCAT/STRING_AGG, UTC_NOW, REGEXP/~, ^/#) -- assist functions added in the dba_ - INTERVALs: postgres requires quotes around the value, mysql requires that there are not quotes around the value -- assist functions added in the dba_ - NULL_DATE's -- Postgres does not allow the invalid date '0000-00-00 00:00:00' (there is no such thing as year 0 or month 0 or day 0). We use '0001-01-01 00:00:00' for postgres. Conversions are handled in Zot/item packets automagically by quoting all dates with dbescdate(). - char(##) specifications in the schema creates fields with blank spaces that aren't trimmed in the code. MySQL apparently treats char(##) as varchar(##), again, non-compliant. Since postgres works better with text fields anyway, this ball of bugs was simply side-stepped by using 'text' datatype for all text fields in the postgres schema. varchar was used in a couple of places where it actually seemed appropriate (size constraint), but without rigorously vetting that all of the PHP code actually validates data, new bugs might come out from under the rug. - postgres doesn't store nul bytes and a few other non-printables in text fields, even when quoted. bytea fields were used when storing binary data (photo.data, attach.data). A new dbescbin() function was added to handle this transparently. - postgres does not support LIMIT #,# syntax. All databases support LIMIT # OFFSET # syntax. Statements were updated to be standard. These changes require corresponding changes in the coding standards. Please review those before adding any code going forward. Still on my TODO list: - remove quotes from non-reserved identifiers and make reserved identifiers use dba func for quoting - Rewrite search queries for better results (both MySQL and Postgres)
2014-11-13 20:21:58 +00:00
where abook_id = %d and abook_channel = %d",
2021-12-03 03:01:39 +00:00
dbesc(datetime_convert()),
dbesc(datetime_convert()),
intval($abook['abook_id']),
intval($abook['abook_channel'])
);
}
return $r;
2012-10-31 04:37:58 +00:00
}
2012-09-10 04:17:06 +00:00
2010-09-09 03:14:17 +00:00
/**
* mark any hubs "offline" that haven't been heard from in more than 30 days
* Allow them to redeem themselves if they come back later.
* Then go through all those that are newly marked and see if any other hubs
* are attached to the controlling xchan that are still alive.
* If not, they're dead (although they could come back some day).
*/
2021-12-03 03:01:39 +00:00
function mark_orphan_hubsxchans()
{
2021-12-03 03:01:39 +00:00
return;
2021-02-19 05:40:22 +00:00
2021-12-03 03:01:39 +00:00
$dirmode = intval(get_config('system', 'directory_mode'));
if ($dirmode == DIRECTORY_MODE_NORMAL) {
return;
}
2019-04-12 03:26:38 +00:00
$r = q("update hubloc set hubloc_deleted = 1 where hubloc_deleted = 0
and hubloc_network in ('nomad','zot6') and hubloc_connected < %s - interval %s",
db_utcnow(), db_quoteinterval('36 day')
);
2021-12-03 03:01:39 +00:00
$r = q("select hubloc_id, hubloc_hash from hubloc where hubloc_deleted = 1 and hubloc_orphancheck = 0");
if ($r) {
foreach ($r as $rr) {
// see if any other hublocs are still alive for this channel
$x = q(
"select * from hubloc where hubloc_hash = '%s' and hubloc_deleted = 0",
dbesc($rr['hubloc_hash'])
);
if ($x) {
// yes - if the xchan was marked as an orphan, undo it
$y = q(
"update xchan set xchan_orphan = 0 where xchan_orphan = 1 and xchan_hash = '%s'",
dbesc($rr['hubloc_hash'])
);
} else {
// nope - mark the xchan as an orphan
$y = q(
"update xchan set xchan_orphan = 1 where xchan_hash = '%s'",
dbesc($rr['hubloc_hash'])
);
}
// mark that we've checked this entry so we don't need to do it again
$y = q(
"update hubloc set hubloc_orphancheck = 1 where hubloc_id = %d",
dbesc($rr['hubloc_id'])
);
}
}
}
2021-12-03 03:01:39 +00:00
function remove_all_xchan_resources($xchan, $channel_id = 0)
{
// $channel_id is reserved for future use.
if (intval($channel_id) === 0) {
if (! $xchan) {
return;
}
// this function is only to be executed on remote servers where only the xchan exists and there is no associated channel.
$c = q(
"select channel_id from channel where channel_hash = '%s'",
dbesc($xchan)
);
if ($c) {
return;
}
$dirmode = intval(get_config('system', 'directory_mode'));
// note: we will not remove "guest" submitted files/photos this xchan created in the file spaces of others.
// We will however remove all their posts and comments.
$r = q(
"select id from item where ( author_xchan = '%s' or owner_xchan = '%s' ) ",
dbesc($xchan),
dbesc($xchan)
);
if ($r) {
foreach ($r as $rr) {
drop_item($rr['id'], false);
}
}
$r = q(
"delete from event where event_xchan = '%s'",
dbesc($xchan)
);
$r = q(
"delete from pgrp_member where xchan = '%s'",
dbesc($xchan)
);
$r = q(
"delete from xlink where ( xlink_xchan = '%s' or xlink_link = '%s' )",
dbesc($xchan),
dbesc($xchan)
);
$r = q(
"delete from abook where abook_xchan = '%s'",
dbesc($xchan)
);
$r = q(
"delete from abconfig where xchan = '%s'",
dbesc($xchan)
);
$r = q(
"update hubloc set hubloc_deleted = 1 where hubloc_hash = '%s'",
dbesc($xchan)
);
2021-12-03 03:01:39 +00:00
$r = q(
"update xchan set xchan_deleted = 1 where xchan_hash = '%s'",
dbesc($xchan)
);
$r = q(
"delete from xprof where xprof_hash = '%s'",
dbesc($xchan)
);
2021-12-03 03:01:39 +00:00
}
}
2013-02-18 23:15:55 +00:00
2019-06-06 06:08:17 +00:00
2013-02-18 23:15:55 +00:00
2021-12-03 03:01:39 +00:00
function contact_remove($channel_id, $abook_id, $atoken_sync = false)
{
2021-12-03 03:01:39 +00:00
if ((! $channel_id) || (! $abook_id)) {
return false;
}
2013-02-18 23:15:55 +00:00
2021-12-03 03:01:39 +00:00
logger('removing connection ' . $abook_id . ' for channel ' . $channel_id, LOGGER_DEBUG);
2020-08-26 06:10:25 +00:00
2021-12-03 03:01:39 +00:00
$x = [
'channel_id' => $channel_id,
'abook_id' => $abook_id
];
Hook::call('connection_remove', $x);
2013-02-18 23:15:55 +00:00
2021-12-03 03:01:39 +00:00
$archive = get_pconfig($channel_id, 'system', 'archive_removed_contacts');
if ($archive) {
q(
"update abook set abook_archived = 1 where abook_id = %d and abook_channel = %d",
intval($abook_id),
intval($channel_id)
);
return true;
}
2021-12-03 03:01:39 +00:00
$r = q(
"select * from abook where abook_id = %d and abook_channel = %d limit 1",
intval($abook_id),
intval($channel_id)
);
2013-02-18 23:15:55 +00:00
2021-12-03 03:01:39 +00:00
if (! $r) {
return false;
}
2013-02-18 23:15:55 +00:00
2021-12-03 03:01:39 +00:00
$abook = $r[0];
2018-08-23 06:04:37 +00:00
2021-12-03 03:01:39 +00:00
if (intval($abook['abook_self'])) {
return false;
}
2018-08-23 06:04:37 +00:00
2021-12-03 03:01:39 +00:00
// if this is an atoken, delete the atoken record
2018-08-23 06:04:37 +00:00
2021-12-03 03:01:39 +00:00
if ($atoken_sync) {
$xchan = q(
"select * from xchan where xchan_hash = '%s'",
dbesc($abook['abook_xchan'])
);
if ($xchan && strpos($xchan[0]['xchan_addr'], 'guest:') === 0 && strpos($abook['abook_xchan'], '.')) {
$atoken_guid = substr($abook['abook_xchan'], strrpos($abook['abook_xchan'], '.') + 1);
if ($atoken_guid) {
2022-01-25 01:26:12 +00:00
Channel::atoken_delete_and_sync($channel_id, $atoken_guid);
2021-12-03 03:01:39 +00:00
}
}
}
2021-12-03 03:01:39 +00:00
// remove items in the background as this can take some time
2021-12-03 03:01:39 +00:00
Run::Summon([ 'Delxitems', $channel_id, $abook['abook_xchan'] ]);
2021-12-03 03:01:39 +00:00
$r = q(
"delete from abook where abook_id = %d and abook_channel = %d",
intval($abook['abook_id']),
intval($channel_id)
);
2021-12-03 03:01:39 +00:00
$r = q(
"delete from event where event_xchan = '%s' and uid = %d",
dbesc($abook['abook_xchan']),
intval($channel_id)
);
2021-12-03 03:01:39 +00:00
$r = q(
"delete from pgrp_member where xchan = '%s' and uid = %d",
dbesc($abook['abook_xchan']),
intval($channel_id)
);
2021-12-03 03:01:39 +00:00
$r = q(
"delete from mail where ( from_xchan = '%s' or to_xchan = '%s' ) and channel_id = %d ",
dbesc($abook['abook_xchan']),
dbesc($abook['abook_xchan']),
intval($channel_id)
);
2021-12-03 03:01:39 +00:00
$r = q(
"delete from abconfig where chan = %d and xchan = '%s'",
intval($channel_id),
dbesc($abook['abook_xchan'])
);
2021-12-03 03:01:39 +00:00
return true;
2010-09-09 03:14:17 +00:00
}
2021-12-03 03:01:39 +00:00
function remove_abook_items($channel_id, $xchan_hash)
{
$r = q(
"select id, parent from item where (owner_xchan = '%s' or author_xchan = '%s') and uid = %d and item_retained = 0 and item_starred = 0",
dbesc($xchan_hash),
dbesc($xchan_hash),
intval($channel_id)
);
if (! $r) {
return;
}
$already_saved = [];
foreach ($r as $rr) {
$w = $x = $y = null;
// optimise so we only process newly seen parent items
if (in_array($rr['parent'], $already_saved)) {
continue;
}
// if this isn't the parent, fetch the parent's item_retained and item_starred to see if the conversation
// should be retained
if ($rr['id'] != $rr['parent']) {
$w = q(
"select id, item_retained, item_starred from item where id = %d",
intval($rr['parent'])
);
if ($w) {
// see if the conversation was filed
$x = q(
"select uid from term where otype = %d and oid = %d and ttype = %d limit 1",
intval(TERM_OBJ_POST),
intval($w[0]['id']),
intval(TERM_FILE)
);
if (intval($w[0]['item_retained']) || intval($w[0]['item_starred']) || $x) {
$already_saved[] = $rr['parent'];
continue;
}
}
}
// see if this item was filed
$y = q(
"select uid from term where otype = %d and oid = %d and ttype = %d limit 1",
intval(TERM_OBJ_POST),
intval($rr['id']),
intval(TERM_FILE)
);
if ($y) {
continue;
}
drop_item($rr['id'], false);
}
}
2010-09-09 03:14:17 +00:00
2021-12-03 03:01:39 +00:00
function random_profile()
{
$randfunc = db_getfunc('rand');
2015-01-30 22:30:37 +00:00
2021-12-03 03:01:39 +00:00
$checkrandom = get_config('randprofile', 'check'); // False by default
$retryrandom = intval(get_config('randprofile', 'retry'));
if ($retryrandom == 0) {
$retryrandom = 5;
}
2015-01-30 22:30:37 +00:00
2021-12-03 03:01:39 +00:00
for ($i = 0; $i < $retryrandom; $i++) {
$r = q("select xchan_url, xchan_hash from xchan left join hubloc on hubloc_hash = xchan_hash where
xchan_hidden = 0 and xchan_network in ('nomad','zot6') and xchan_deleted = 0
and hubloc_connected > %s - interval %s order by $randfunc limit 1",
2021-12-03 03:01:39 +00:00
db_utcnow(),
db_quoteinterval('30 day')
);
if (!$r) {
return EMPTY_STR; // Couldn't get a random channel
}
if ($checkrandom) {
$x = z_fetch_url($r[0]['xchan_url']);
if ($x['success']) {
return $r[0]['xchan_hash'];
} else {
logger('Random channel turned out to be bad.');
}
} else {
return $r[0]['xchan_hash'];
}
}
return EMPTY_STR;
2012-04-12 14:48:28 +00:00
}
2021-12-03 03:01:39 +00:00
function update_vcard($arr, $vcard = null)
{
// logger('update_vcard: ' . print_r($arr,true));
$fn = $arr['fn'];
// This isn't strictly correct and could be a cause for concern.
// 'N' => array_reverse(explode(' ', $fn))
// What we really want is
// 'N' => Adams;John;Quincy;Reverend,Dr.;III
// which is a very difficult parsing problem especially if you allow
// the surname to contain spaces. The only way to be sure to get it
// right is to provide a form to input all the various fields and not
// try to extract it from the FN.
if (! $vcard) {
$vcard = new VCard([
'FN' => $fn,
'N' => array_reverse(explode(' ', $fn))
]);
} else {
$vcard->FN = $fn;
$vcard->N = array_reverse(explode(' ', $fn));
}
$org = $arr['org'];
if ($org) {
$vcard->ORG = $org;
}
$title = $arr['title'];
if ($title) {
$vcard->TITLE = $title;
}
$tel = $arr['tel'];
$tel_type = $arr['tel_type'];
if ($tel) {
$i = 0;
foreach ($tel as $item) {
if ($item) {
$vcard->add('TEL', $item, ['type' => $tel_type[$i]]);
}
$i++;
}
}
$email = $arr['email'];
$email_type = $arr['email_type'];
if ($email) {
$i = 0;
foreach ($email as $item) {
if ($item) {
$vcard->add('EMAIL', $item, ['type' => $email_type[$i]]);
}
$i++;
}
}
$impp = $arr['impp'];
$impp_type = $arr['impp_type'];
if ($impp) {
$i = 0;
foreach ($impp as $item) {
if ($item) {
$vcard->add('IMPP', $item, ['type' => $impp_type[$i]]);
}
$i++;
}
}
$url = $arr['url'];
$url_type = $arr['url_type'];
if ($url) {
$i = 0;
foreach ($url as $item) {
if ($item) {
$vcard->add('URL', $item, ['type' => $url_type[$i]]);
}
$i++;
}
}
$adr = $arr['adr'];
$adr_type = $arr['adr_type'];
if ($adr) {
$i = 0;
foreach ($adr as $item) {
if ($item) {
$vcard->add('ADR', $item, ['type' => $adr_type[$i]]);
}
$i++;
}
}
$note = $arr['note'];
if ($note) {
$vcard->NOTE = $note;
}
return $vcard->serialize();
2016-12-21 00:45:12 +00:00
}
2021-12-03 03:01:39 +00:00
function get_vcard_array($vc, $id)
{
$photo = '';
if ($vc->PHOTO) {
$photo_value = strtolower($vc->PHOTO->getValueType()); // binary or uri
if ($photo_value === 'binary') {
$photo_type = strtolower($vc->PHOTO['TYPE']); // mime jpeg, png or gif
$photo = 'data:image/' . $photo_type . ';base64,' . base64_encode((string)$vc->PHOTO);
} else {
$url = parse_url((string)$vc->PHOTO);
$photo = 'data:' . $url['path'];
}
}
$fn = '';
if ($vc->FN) {
$fn = (string) escape_tags($vc->FN);
}
$org = '';
if ($vc->ORG) {
$org = (string) escape_tags($vc->ORG);
}
$title = '';
if ($vc->TITLE) {
$title = (string) escape_tags($vc->TITLE);
}
$tels = [];
if ($vc->TEL) {
foreach ($vc->TEL as $tel) {
$type = (($tel['TYPE']) ? vcard_translate_type((string)$tel['TYPE']) : '');
$tels[] = [
'type' => $type,
'nr' => (string) escape_tags($tel)
];
}
}
$emails = [];
if ($vc->EMAIL) {
foreach ($vc->EMAIL as $email) {
$type = (($email['TYPE']) ? vcard_translate_type((string)$email['TYPE']) : '');
$emails[] = [
'type' => $type,
'address' => (string) escape_tags($email)
];
}
}
$impps = [];
if ($vc->IMPP) {
foreach ($vc->IMPP as $impp) {
$type = (($impp['TYPE']) ? vcard_translate_type((string)$impp['TYPE']) : '');
$impps[] = [
'type' => $type,
'address' => (string) escape_tags($impp)
];
}
}
$urls = [];
if ($vc->URL) {
foreach ($vc->URL as $url) {
$type = (($url['TYPE']) ? vcard_translate_type((string)$url['TYPE']) : '');
$urls[] = [
'type' => $type,
'address' => (string) escape_tags($url)
];
}
}
$adrs = [];
if ($vc->ADR) {
foreach ($vc->ADR as $adr) {
$type = (($adr['TYPE']) ? vcard_translate_type((string)$adr['TYPE']) : '');
$entry = [
'type' => $type,
'address' => $adr->getParts()
];
if (is_array($entry['address'])) {
array_walk($entry['address'], 'array_escape_tags');
} else {
$entry['address'] = (string) escape_tags($entry['address']);
}
$adrs[] = $entry;
}
}
$note = '';
if ($vc->NOTE) {
$note = (string) escape_tags($vc->NOTE);
}
$card = [
'id' => $id,
'photo' => $photo,
'fn' => $fn,
'org' => $org,
'title' => $title,
'tels' => $tels,
'emails' => $emails,
'impps' => $impps,
'urls' => $urls,
'adrs' => $adrs,
'note' => $note
];
return $card;
2016-12-21 00:45:12 +00:00
}
2021-12-03 03:01:39 +00:00
function vcard_translate_type($type)
{
2016-12-21 00:45:12 +00:00
2021-12-03 03:01:39 +00:00
if (!$type) {
return;
}
2016-12-21 00:45:12 +00:00
2021-12-03 03:01:39 +00:00
$type = strtoupper($type);
2016-12-21 00:45:12 +00:00
2021-12-03 03:01:39 +00:00
$map = [
'CELL' => t('Mobile'),
'HOME' => t('Home'),
'HOME,VOICE' => t('Home, Voice'),
'HOME,FAX' => t('Home, Fax'),
'WORK' => t('Work'),
'WORK,VOICE' => t('Work, Voice'),
'WORK,FAX' => t('Work, Fax'),
'OTHER' => t('Other')
];
2016-12-21 00:45:12 +00:00
2021-12-03 03:01:39 +00:00
if (array_key_exists($type, $map)) {
return [$type, $map[$type]];
} else {
return [$type, t('Other') . ' (' . $type . ')'];
}
2016-12-21 00:45:12 +00:00
}
2021-12-03 03:01:39 +00:00
function vcard_query(&$r)
{
$arr = [];
if ($r && is_array($r) && count($r)) {
$uid = $r[0]['abook_channel'];
foreach ($r as $rv) {
if ($rv['abook_xchan'] && (! in_array("'" . dbesc($rv['abook_xchan']) . "'", $arr))) {
$arr[] = "'" . dbesc($rv['abook_xchan']) . "'";
}
}
}
if ($arr) {
$a = q(
"select * from abconfig where chan = %d and xchan in (" . protect_sprintf(implode(',', $arr)) . ") and cat = 'system' and k = 'vcard'",
intval($uid)
);
if ($a) {
foreach ($a as $av) {
for ($x = 0; $x < count($r); $x++) {
if ($r[$x]['abook_xchan'] == $av['xchan']) {
$vctmp = Reader::read($av['v']);
$r[$x]['vcard'] = (($vctmp) ? get_vcard_array($vctmp, $r[$x]['abook_id']) : [] );
}
}
}
}
}
}
2019-01-18 21:04:05 +00:00
2021-12-03 03:01:39 +00:00
function contact_profile_assign($current)
{
2019-01-18 21:04:05 +00:00
2021-12-03 03:01:39 +00:00
$o = '';
2019-01-18 21:04:05 +00:00
2021-12-03 03:01:39 +00:00
$o .= "<select id=\"contact-profile-selector\" name=\"profile_assign\" class=\"form-control\"/>\r\n";
2019-01-18 21:04:05 +00:00
2021-12-03 03:01:39 +00:00
$r = q(
"SELECT profile_guid, profile_name FROM profile WHERE uid = %d",
intval($_SESSION['uid'])
);
2019-01-18 21:04:05 +00:00
2021-12-03 03:01:39 +00:00
if ($r) {
foreach ($r as $rr) {
$selected = (($rr['profile_guid'] == $current) ? " selected=\"selected\" " : "");
$o .= "<option value=\"{$rr['profile_guid']}\" $selected >{$rr['profile_name']}</option>\r\n";
}
}
$o .= "</select>\r\n";
return $o;
2019-01-18 21:04:05 +00:00
}
2021-12-03 03:01:39 +00:00
function contact_block()
{
$o = '';
2019-07-11 01:43:20 +00:00
2021-12-03 03:01:39 +00:00
if (! App::$profile['uid']) {
return;
}
2019-07-11 01:43:20 +00:00
2021-12-03 03:01:39 +00:00
if (! perm_is_allowed(App::$profile['uid'], get_observer_hash(), 'view_contacts')) {
return;
}
2019-07-11 01:43:20 +00:00
2021-12-03 03:01:39 +00:00
$shown = get_pconfig(App::$profile['uid'], 'system', 'display_friend_count');
2019-07-11 01:43:20 +00:00
2021-12-03 03:01:39 +00:00
if ($shown === false) {
$shown = 25;
}
if ($shown == 0) {
return;
}
2019-07-11 01:43:20 +00:00
2021-12-03 03:01:39 +00:00
$is_owner = ((local_channel() && local_channel() == App::$profile['uid']) ? true : false);
$sql_extra = '';
2019-07-11 01:43:20 +00:00
2021-12-03 03:01:39 +00:00
$abook_flags = " and abook_pending = 0 and abook_self = 0 ";
2019-07-11 01:43:20 +00:00
2021-12-03 03:01:39 +00:00
if (! $is_owner) {
$abook_flags .= " and abook_hidden = 0 ";
$sql_extra = " and xchan_hidden = 0 ";
}
2019-07-11 01:43:20 +00:00
2021-12-03 03:01:39 +00:00
if ((! is_array(App::$profile)) || (App::$profile['hide_friends'])) {
return $o;
}
2019-07-11 01:43:20 +00:00
2021-12-03 03:01:39 +00:00
$r = q(
"SELECT COUNT(abook_id) AS total FROM abook left join xchan on abook_xchan = xchan_hash WHERE abook_channel = %d
2019-07-11 01:43:20 +00:00
$abook_flags and xchan_orphan = 0 and xchan_deleted = 0 $sql_extra",
2021-12-03 03:01:39 +00:00
intval(App::$profile['uid'])
);
if (count($r)) {
$total = intval($r[0]['total']);
}
if (! $total) {
$contacts = t('No connections');
$micropro = null;
} else {
$randfunc = db_getfunc('RAND');
$r = q(
"SELECT abook.*, xchan.* FROM abook left join xchan on abook.abook_xchan = xchan.xchan_hash WHERE abook_channel = %d $abook_flags and abook_archived = 0 and xchan_orphan = 0 and xchan_deleted = 0 $sql_extra ORDER BY $randfunc LIMIT %d",
intval(App::$profile['uid']),
intval($shown)
);
if (count($r)) {
$contacts = t('Connections');
$micropro = array();
foreach ($r as $rr) {
// There is no setting to discover if you are bi-directionally connected
// Use the ability to post comments as an indication that this relationship is more
// than wishful thinking; even though soapbox channels and feeds will disable it.
if (! their_perms_contains(App::$profile['uid'], $rr['xchan_hash'], 'post_comments')) {
$rr['oneway'] = true;
}
$micropro[] = micropro($rr, true, 'mpfriend');
}
}
}
2022-02-12 20:43:29 +00:00
$tpl = Theme::get_template('contact_block.tpl');
2021-12-03 03:01:39 +00:00
$o = replace_macros($tpl, array(
'$contacts' => $contacts,
'$nickname' => App::$profile['channel_address'],
'$viewconnections' => (($total > $shown) ? sprintf(t('View all %s connections'), $total) : ''),
'$micropro' => $micropro,
));
$arr = array('contacts' => $r, 'output' => $o);
Hook::call('contact_block_end', $arr);
2021-12-03 03:01:39 +00:00
return $o;
2019-07-11 01:43:20 +00:00
}
2021-12-03 03:01:39 +00:00
function micropro($contact, $redirect = false, $class = '', $mode = false)
{
if ($contact['click']) {
$url = '#';
} else {
$url = chanlink_hash($contact['xchan_hash']);
}
$tpl = 'micropro_img.tpl';
if ($mode === true) {
$tpl = 'micropro_txt.tpl';
}
if ($mode === 'card') {
$tpl = 'micropro_card.tpl';
}
2022-02-12 20:43:29 +00:00
return replace_macros(Theme::get_template($tpl), array(
2021-12-03 03:01:39 +00:00
'$click' => (($contact['click']) ? $contact['click'] : ''),
'$class' => $class . (($contact['archived']) ? ' archived' : ''),
'$oneway' => (($contact['oneway']) ? true : false),
'$url' => $url,
'$photo' => $contact['xchan_photo_s'],
'$name' => $contact['xchan_name'],
'$addr' => $contact['xchan_addr'],
'$title' => $contact['xchan_name'] . ' [' . $contact['xchan_addr'] . ']',
2021-12-21 22:03:17 +00:00
'$network' => sprintf(t('Network: %s'), network_to_name($contact['xchan_network']))
2021-12-03 03:01:39 +00:00
));
2019-07-11 01:43:20 +00:00
}