From b763c810fb02ba322e9232bfd23da3a04ecd271e Mon Sep 17 00:00:00 2001 From: Michael Vogel Date: Fri, 29 May 2015 10:35:13 +0200 Subject: [PATCH 1/6] OStatus: Trying to deliver a comment to all contacts - not only the thread owner. (Not working by now) --- include/delivery.php | 47 ++++++++++++++++++++++---------------------- include/notifier.php | 32 ++++++++++++++++++++---------- include/salmon.php | 2 +- include/text.php | 8 +++++++- 4 files changed, 54 insertions(+), 35 deletions(-) diff --git a/include/delivery.php b/include/delivery.php index a913e13170..e3e477bf64 100644 --- a/include/delivery.php +++ b/include/delivery.php @@ -88,25 +88,23 @@ function delivery_run(&$argv, &$argc){ if($cmd === 'expire') { $normal_mode = false; $expire = true; - $items = q("SELECT * FROM `item` WHERE `uid` = %d AND `wall` = 1 + $items = q("SELECT * FROM `item` WHERE `uid` = %d AND `wall` = 1 AND `deleted` = 1 AND `changed` > UTC_TIMESTAMP() - INTERVAL 30 MINUTE", intval($item_id) ); $uid = $item_id; $item_id = 0; if(! count($items)) - continue; + continue; } else { - // find ancestors $r = q("SELECT * FROM `item` WHERE `id` = %d and visible = 1 and moderated = 0 LIMIT 1", intval($item_id) ); - if((! count($r)) || (! intval($r[0]['parent']))) { + if((! count($r)) || (! intval($r[0]['parent']))) continue; - } $target_item = $r[0]; $parent_id = intval($r[0]['parent']); @@ -118,14 +116,13 @@ function delivery_run(&$argv, &$argc){ continue; - $items = q("SELECT `item`.*, `sign`.`signed_text`,`sign`.`signature`,`sign`.`signer` + $items = q("SELECT `item`.*, `sign`.`signed_text`,`sign`.`signature`,`sign`.`signer` FROM `item` LEFT JOIN `sign` ON `sign`.`iid` = `item`.`id` WHERE `parent` = %d and visible = 1 and moderated = 0 ORDER BY `id` ASC", intval($parent_id) ); - if(! count($items)) { + if(! count($items)) continue; - } $icontacts = null; $contacts_arr = array(); @@ -133,8 +130,8 @@ function delivery_run(&$argv, &$argc){ if(! in_array($item['contact-id'],$contacts_arr)) $contacts_arr[] = intval($item['contact-id']); if(count($contacts_arr)) { - $str_contacts = implode(',',$contacts_arr); - $icontacts = q("SELECT * FROM `contact` + $str_contacts = implode(',',$contacts_arr); + $icontacts = q("SELECT * FROM `contact` WHERE `id` IN ( $str_contacts ) " ); } @@ -154,10 +151,10 @@ function delivery_run(&$argv, &$argc){ } } - $r = q("SELECT `contact`.*, `user`.`pubkey` AS `upubkey`, `user`.`prvkey` AS `uprvkey`, - `user`.`timezone`, `user`.`nickname`, `user`.`sprvkey`, `user`.`spubkey`, + $r = q("SELECT `contact`.*, `user`.`pubkey` AS `upubkey`, `user`.`prvkey` AS `uprvkey`, + `user`.`timezone`, `user`.`nickname`, `user`.`sprvkey`, `user`.`spubkey`, `user`.`page-flags`, `user`.`prvnets` - FROM `contact` INNER JOIN `user` ON `user`.`uid` = `contact`.`uid` + FROM `contact` INNER JOIN `user` ON `user`.`uid` = `contact`.`uid` WHERE `contact`.`uid` = %d AND `contact`.`self` = 1 LIMIT 1", intval($uid) ); @@ -201,6 +198,13 @@ function delivery_run(&$argv, &$argc){ if(strpos($localhost,':')) $localhost = substr($localhost,0,strpos($localhost,':')); + $r = q("SELECT * FROM `contact` WHERE `id` = %d AND `blocked` = 0 AND `pending` = 0", + intval($contact_id) + ); + + if(count($r)) + $contact = $r[0]; + /** * * Be VERY CAREFUL if you make any changes to the following line. Seemingly innocuous changes @@ -209,7 +213,7 @@ function delivery_run(&$argv, &$argc){ * */ - if((! $top_level) && ($parent['wall'] == 0) && (! $expire) && (stristr($target_item['uri'],$localhost))) { + if(!$top_level && ($parent["network"] != NETWORK_OSTATUS) && ($parent['wall'] == 0) && (! $expire) && (stristr($target_item['uri'],$localhost))) { logger('relay denied for delivery agent.'); /* no relay allowed for direct contact delivery */ @@ -223,13 +227,6 @@ function delivery_run(&$argv, &$argc){ $public_message = false; // private recipients, not public } - $r = q("SELECT * FROM `contact` WHERE `id` = %d AND `blocked` = 0 AND `pending` = 0", - intval($contact_id) - ); - - if(count($r)) - $contact = $r[0]; - $hubxml = feed_hublinks(); logger('notifier: slaps: ' . print_r($slaps,true), LOGGER_DATA); @@ -390,11 +387,15 @@ function delivery_run(&$argv, &$argc){ if(! $item_contact) continue; - if(($top_level) && ($public_message) && ($item['author-link'] === $item['owner-link']) && (! $expire)) + // For OStatus don't notify all contacts in the thread + if (!$top_level AND ($parent["network"] == NETWORK_OSTATUS) AND ($item["id"] != $item["parent"])) + continue; + + if(($top_level OR ($parent["network"] == NETWORK_OSTATUS)) && ($public_message) && ($item['author-link'] === $item['owner-link']) && (! $expire)) $slaps[] = atom_entry($item,'html',null,$owner,true); } - logger('notifier: slapdelivery: ' . $contact['name']); + logger('slapdelivery item '.$item_id.' to ' . $contact['name']); foreach($slaps as $slappy) { if($contact['notify']) { if(! was_recently_delayed($contact['id'])) diff --git a/include/notifier.php b/include/notifier.php index 067251b429..9084bcb475 100644 --- a/include/notifier.php +++ b/include/notifier.php @@ -114,7 +114,7 @@ function notifier_run(&$argv, &$argc){ elseif($cmd === 'expire') { $normal_mode = false; $expire = true; - $items = q("SELECT * FROM `item` WHERE `uid` = %d AND `wall` = 1 + $items = q("SELECT * FROM `item` WHERE `uid` = %d AND `wall` = 1 AND `deleted` = 1 AND `changed` > UTC_TIMESTAMP() - INTERVAL 10 MINUTE", intval($item_id) ); @@ -178,7 +178,7 @@ function notifier_run(&$argv, &$argc){ if(! $parent_id) return; - $items = q("SELECT `item`.*, `sign`.`signed_text`,`sign`.`signature`,`sign`.`signer` + $items = q("SELECT `item`.*, `sign`.`signed_text`,`sign`.`signature`,`sign`.`signer` FROM `item` LEFT JOIN `sign` ON `sign`.`iid` = `item`.`id` WHERE `parent` = %d and visible = 1 and moderated = 0 ORDER BY `id` ASC", intval($parent_id) ); @@ -224,6 +224,9 @@ function notifier_run(&$argv, &$argc){ // fill this in with a single salmon slap if applicable $slap = ''; + // List of OStatus receiptians of follow up messages + $ostatus_recip_str = ""; + if(! ($mail || $fsuggest || $relocate)) { require_once('include/group.php'); @@ -284,8 +287,6 @@ function notifier_run(&$argv, &$argc){ if($parent['origin']) $relay_to_owner = false; - - if($relay_to_owner) { logger('notifier: followup', LOGGER_DEBUG); // local followup to remote post @@ -295,6 +296,15 @@ function notifier_run(&$argv, &$argc){ $recipients = array($parent['contact-id']); if ($parent['network'] == NETWORK_OSTATUS) { + $ostatus_recipients = array(); + + $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `network` = '%s'", intval($uid), dbesc(NETWORK_OSTATUS)); + if(count($r)) { + foreach($r as $rr) + $ostatus_recipients[] = $rr['id']; + + $ostatus_recip_str = ", ".implode(', ', $ostatus_recipients); + } // Check if the recipient isn't in your contact list $r = q("SELECT `url` FROM `contact` WHERE `id` = %d", $parent['contact-id']); @@ -553,7 +563,7 @@ function notifier_run(&$argv, &$argc){ } if($followup) - $recip_str = $parent['contact-id']; + $recip_str = $parent['contact-id'].$ostatus_recip_str; else $recip_str = implode(', ', $recipients); @@ -574,7 +584,7 @@ function notifier_run(&$argv, &$argc){ if(count($r)) { foreach($r as $contact) { - if((! $mail) && (! $fsuggest) && (! $followup) && (!$relocate) && (! $contact['self'])) { + if((! $mail) && (! $fsuggest) && (!$followup OR ($parent['contact-id'] != $contact['id'])) && (!$relocate) && (! $contact['self'])) { if(($contact['network'] === NETWORK_DIASPORA) && ($public_message)) continue; q("insert into deliverq ( `cmd`,`item`,`contact` ) values ('%s', %d, %d )", @@ -608,10 +618,12 @@ function notifier_run(&$argv, &$argc){ if($contact['self']) continue; + logger("Deliver to ".$contact['url'], LOGGER_DEBUG); + // potentially more than one recipient. Start a new process and space them out a bit. // we will deliver single recipient types of message and email recipients here. - if((! $mail) && (! $fsuggest) && (!$relocate) && (! $followup)) { + if((! $mail) && (! $fsuggest) && (!$relocate) && (!$followup OR ($parent['contact-id'] != $contact['id']))) { $this_batch[] = $contact['id']; @@ -713,7 +725,7 @@ function notifier_run(&$argv, &$argc){ break; if($followup && $contact['notify']) { - logger('notifier: slapdelivery: ' . $contact['name']); + logger('slapdelivery followup item '.$item_id.' to ' . $contact['name']); $deliver_status = slapper($owner,$contact['notify'],$slap); if($deliver_status == (-1)) { @@ -726,7 +738,7 @@ function notifier_run(&$argv, &$argc){ // a public hub, it's ok to send a salmon if((count($slaps)) && ($public_message) && (! $expire)) { - logger('notifier: slapdelivery: ' . $contact['name']); + logger('slapdelivery item '.$item_id.' to ' . $contact['name']); foreach($slaps as $slappy) { if($contact['notify']) { $deliver_status = slapper($owner,$contact['notify'],$slappy); @@ -938,7 +950,7 @@ function notifier_run(&$argv, &$argc){ // throw everything into the queue in case we get killed foreach($r as $rr) { - if((! $mail) && (! $fsuggest) && (! $followup)) { + if((! $mail) && (! $fsuggest) && (!$followup OR ($parent['contact-id'] != $contact['id']))) { q("insert into deliverq ( `cmd`,`item`,`contact` ) values ('%s', %d, %d )", dbesc($cmd), intval($item_id), diff --git a/include/salmon.php b/include/salmon.php index 3d525f51ad..aeb14adc8c 100644 --- a/include/salmon.php +++ b/include/salmon.php @@ -66,7 +66,7 @@ function get_salmon_key($uri,$keyhash) { function slapper($owner,$url,$slap) { - logger('slapper called. Data: ' . $slap); + logger('slapper called for '.$owner['url'].' to '.$url.' . Data: ' . $slap); // does contact have a salmon endpoint? diff --git a/include/text.php b/include/text.php index 0f6c83234b..f8bbfcee24 100644 --- a/include/text.php +++ b/include/text.php @@ -835,10 +835,16 @@ function get_mentions($item) { foreach($arr as $x) { $matches = null; if(preg_match('/@\[url=([^\]]*)\]/',$x,$matches)) { - $o .= "\t\t" . '' . "\r\n"; $o .= "\t\t" . '' . "\r\n"; + $o .= "\t\t" . '' . "\r\n"; } } + + if (!$item['private']) { + $o .= "\t\t".''."\r\n"; + $o .= "\t\t".''."\r\n"; + } + return $o; }} From e66489d67ffd10c2c47a82308b2f34da2d362684 Mon Sep 17 00:00:00 2001 From: Michael Vogel Date: Sat, 30 May 2015 20:47:53 +0200 Subject: [PATCH 2/6] PuSH: Publishing moved to a new process. OStatus comments are now published to all subscribers. --- include/delivery.php | 2 +- include/items.php | 20 +++++-- include/notifier.php | 57 ++++++++++++------- include/pubsubpublish.php | 114 ++++++++++++++++++++++++++++++++++++++ include/queue.php | 65 ++-------------------- include/salmon.php | 26 ++++----- 6 files changed, 184 insertions(+), 100 deletions(-) create mode 100644 include/pubsubpublish.php diff --git a/include/delivery.php b/include/delivery.php index e3e477bf64..fbbf7843f6 100644 --- a/include/delivery.php +++ b/include/delivery.php @@ -388,7 +388,7 @@ function delivery_run(&$argv, &$argc){ continue; // For OStatus don't notify all contacts in the thread - if (!$top_level AND ($parent["network"] == NETWORK_OSTATUS) AND ($item["id"] != $item["parent"])) + if (!$top_level AND ($parent["network"] == NETWORK_OSTATUS) AND ($item["id"] != $item_id)) continue; if(($top_level OR ($parent["network"] == NETWORK_OSTATUS)) && ($public_message) && ($item['author-link'] === $item['owner-link']) && (! $expire)) diff --git a/include/items.php b/include/items.php index b463e6ed17..44eb18ccd1 100644 --- a/include/items.php +++ b/include/items.php @@ -14,7 +14,7 @@ require_once('include/threads.php'); require_once('include/socgraph.php'); require_once('mod/share.php'); -function get_feed_for(&$a, $dfrn_id, $owner_nick, $last_update, $direction = 0) { +function get_feed_for(&$a, $dfrn_id, $owner_nick, $last_update, $direction = 0, $forpubsub = false) { $sitefeed = ((strlen($owner_nick)) ? false : true); // not yet implemented, need to rewrite huge chunks of following logic @@ -55,6 +55,7 @@ function get_feed_for(&$a, $dfrn_id, $owner_nick, $last_update, $direction = 0) $birthday = feed_birthday($owner_id,$owner['timezone']); $sql_post_table = ""; + $visibility = ""; if(! $public_feed) { @@ -114,6 +115,17 @@ function get_feed_for(&$a, $dfrn_id, $owner_nick, $last_update, $direction = 0) else $sort = 'ASC'; + // Include answers to status.net posts in public feeds + if($forpubsub) { + $sql_post_table = "INNER JOIN `thread` ON `thread`.`iid` = `item`.`parent` "; + $visibility = "OR (`item`.`network` = 'dfrn' AND `thread`.`network`='stat')"; + $date_field = "`received`"; + $sql_order = "`item`.`received` DESC"; + } else { + $date_field = "`changed`"; + $sql_order = "`item`.`parent` ".$sort.", `item`.`created` ASC"; + } + if(! strlen($last_update)) $last_update = 'now -30 days'; @@ -133,7 +145,7 @@ function get_feed_for(&$a, $dfrn_id, $owner_nick, $last_update, $direction = 0) // AND ( `item`.`edited` > '%s' OR `item`.`changed` > '%s' ) // dbesc($check_date), - $r = q("SELECT `item`.*, `item`.`id` AS `item_id`, + $r = q("SELECT STRAIGHT_JOIN `item`.*, `item`.`id` AS `item_id`, `contact`.`name`, `contact`.`network`, `contact`.`photo`, `contact`.`url`, `contact`.`name-date`, `contact`.`uri-date`, `contact`.`avatar-date`, `contact`.`thumb`, `contact`.`dfrn-id`, `contact`.`self`, @@ -144,9 +156,9 @@ function get_feed_for(&$a, $dfrn_id, $owner_nick, $last_update, $direction = 0) AND `contact`.`blocked` = 0 AND `contact`.`pending` = 0 LEFT JOIN `sign` ON `sign`.`iid` = `item`.`id` WHERE `item`.`uid` = %d AND `item`.`visible` = 1 and `item`.`moderated` = 0 AND `item`.`parent` != 0 - AND `item`.`wall` = 1 AND `item`.`changed` > '%s' + AND ((`item`.`wall` = 1) $visibility) AND `item`.$date_field > '%s' $sql_extra - ORDER BY `parent` %s, `created` ASC LIMIT 0, 300", + ORDER BY $sql_order LIMIT 0, 300", intval($owner_id), dbesc($check_date), dbesc($sort) diff --git a/include/notifier.php b/include/notifier.php index 9084bcb475..962090326f 100644 --- a/include/notifier.php +++ b/include/notifier.php @@ -221,6 +221,9 @@ function notifier_run(&$argv, &$argc){ // If this is a public conversation, notify the feed hub $public_message = true; + // Do a PuSH + $push_notify = false; + // fill this in with a single salmon slap if applicable $slap = ''; @@ -296,7 +299,11 @@ function notifier_run(&$argv, &$argc){ $recipients = array($parent['contact-id']); if ($parent['network'] == NETWORK_OSTATUS) { - $ostatus_recipients = array(); + logger('Parent is OStatus', LOGGER_DEBUG); + + $push_notify = true; + +/* $ostatus_recipients = array(); $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `network` = '%s'", intval($uid), dbesc(NETWORK_OSTATUS)); if(count($r)) { @@ -305,7 +312,7 @@ function notifier_run(&$argv, &$argc){ $ostatus_recip_str = ", ".implode(', ', $ostatus_recipients); } - +*/ // Check if the recipient isn't in your contact list $r = q("SELECT `url` FROM `contact` WHERE `id` = %d", $parent['contact-id']); if (count($r)) { @@ -978,32 +985,40 @@ function notifier_run(&$argv, &$argc){ } } + $push_notify = true; - if(strlen($hub)) { - $hubs = explode(',', $hub); - if(count($hubs)) { - foreach($hubs as $h) { - $h = trim($h); - if(! strlen($h)) - continue; + } - if ($h === '[internal]') { - // Set push flag for PuSH subscribers to this topic, - // they will be notified in queue.php - q("UPDATE `push_subscriber` SET `push` = 1 " . - "WHERE `nickname` = '%s'", dbesc($owner['nickname'])); - } else { - $params = 'hub.mode=publish&hub.url=' . urlencode( $a->get_baseurl() . '/dfrn_poll/' . $owner['nickname'] ); - post_url($h,$params); - logger('pubsub: publish: ' . $h . ' ' . $params . ' returned ' . $a->get_curl_code()); - } - if(count($hubs) > 1) - sleep(7); // try and avoid multiple hubs responding at precisely the same time + if($push_notify AND strlen($hub)) { + $hubs = explode(',', $hub); + if(count($hubs)) { + foreach($hubs as $h) { + $h = trim($h); + if(! strlen($h)) + continue; + + if ($h === '[internal]') { + // Set push flag for PuSH subscribers to this topic, + // they will be notified in queue.php + q("UPDATE `push_subscriber` SET `push` = 1 " . + "WHERE `nickname` = '%s'", dbesc($owner['nickname'])); + + logger('Activating internal PuSH for item '.$item_id, LOGGER_DEBUG); + + } else { + + $params = 'hub.mode=publish&hub.url=' . urlencode( $a->get_baseurl() . '/dfrn_poll/' . $owner['nickname'] ); + post_url($h,$params); + logger('publish for item '.$item_id.' ' . $h . ' ' . $params . ' returned ' . $a->get_curl_code()); } + if(count($hubs) > 1) + sleep(7); // try and avoid multiple hubs responding at precisely the same time } } + // Handling the pubsubhubbub requests + proc_run('php','include/pubsubpublish.php'); } // If the item was deleted, clean up the `sign` table diff --git a/include/pubsubpublish.php b/include/pubsubpublish.php new file mode 100644 index 0000000000..60bb2b3de0 --- /dev/null +++ b/include/pubsubpublish.php @@ -0,0 +1,114 @@ + 0, + // i.e. there has been an update (set in notifier.php). + + $r = q("SELECT * FROM `push_subscriber` WHERE `push` > 0"); + + foreach($r as $rr) { + $params = get_feed_for($a, '', $rr['nickname'], $rr['last_update'], 0, true); + $hmac_sig = hash_hmac("sha1", $params, $rr['secret']); + + $headers = array("Content-type: application/atom+xml", + sprintf("Link: <%s>;rel=hub," . + "<%s>;rel=self", + $a->get_baseurl() . '/pubsubhubbub', + $rr['topic']), + "X-Hub-Signature: sha1=" . $hmac_sig); + + logger('POST '. print_r($headers, true)."\n".$params, LOGGER_DEBUG); + + post_url($rr['callback_url'], $params, $headers); + $ret = $a->get_curl_code(); + + if ($ret >= 200 && $ret <= 299) { + logger('successfully pushed to '.$rr['callback_url']); + + // set last_update to "now", and reset push=0 + $date_now = datetime_convert('UTC','UTC','now','Y-m-d H:i:s'); + q("UPDATE `push_subscriber` SET `push` = 0, last_update = '%s' WHERE id = %d", + dbesc($date_now), + intval($rr['id'])); + + } else { + logger('error when pushing to '.$rr['callback_url'].' HTTP: '.$ret); + + // we use the push variable also as a counter, if we failed we + // increment this until some upper limit where we give up + $new_push = intval($rr['push']) + 1; + + if ($new_push > 30) // OK, let's give up + $new_push = 0; + + q("UPDATE `push_subscriber` SET `push` = %d, last_update = '%s' WHERE id = %d", + $new_push, + dbesc($date_now), + intval($rr['id'])); + } + } + + logger('done'); +} + + +function pubsubpublish_run(&$argv, &$argc){ + global $a, $db; + + if(is_null($a)){ + $a = new App; + } + + if(is_null($db)){ + @include(".htconfig.php"); + require_once("include/dba.php"); + $db = new dba($db_host, $db_user, $db_pass, $db_data); + unset($db_host, $db_user, $db_pass, $db_data); + }; + + require_once('include/items.php'); + require_once('include/pidfile.php'); + + load_config('config'); + load_config('system'); + + $lockpath = get_lockpath(); + if ($lockpath != '') { + $pidfile = new pidfile($lockpath, 'pubsubpublish'); + if($pidfile->is_already_running()) { + logger("Already running"); + if ($pidfile->running_time() > 9*60) { + $pidfile->kill(); + logger("killed stale process"); + // Calling a new instance + proc_run('php',"include/pubsubpublish.php"); + } + return; + } + } + + $a->set_baseurl(get_config('system','url')); + + load_hooks(); + + if($argc > 1) + $pubsubpublish_id = intval($argv[1]); + else + $pubsubpublish_id = 0; + + handle_pubsubhubbub(); + + return; + +} + +if (array_search(__file__,get_included_files())===0){ + pubsubpublish_run($_SERVER["argv"],$_SERVER["argc"]); + killme(); +} + diff --git a/include/queue.php b/include/queue.php index 128dfcba57..0edd64fdb1 100644 --- a/include/queue.php +++ b/include/queue.php @@ -2,64 +2,6 @@ require_once("boot.php"); require_once('include/queue_fn.php'); -function handle_pubsubhubbub() { - global $a, $db; - - logger('queue [pubsubhubbub]: start'); - - // We'll push to each subscriber that has push > 0, - // i.e. there has been an update (set in notifier.php). - - $r = q("SELECT * FROM `push_subscriber` WHERE `push` > 0"); - - foreach($r as $rr) { - $params = get_feed_for($a, '', $rr['nickname'], $rr['last_update']); - $hmac_sig = hash_hmac("sha1", $params, $rr['secret']); - - $headers = array("Content-type: application/atom+xml", - sprintf("Link: <%s>;rel=hub," . - "<%s>;rel=self", - $a->get_baseurl() . '/pubsubhubbub', - $rr['topic']), - "X-Hub-Signature: sha1=" . $hmac_sig); - - logger('queue [pubsubhubbub]: POST', $headers); - - post_url($rr['callback_url'], $params, $headers); - $ret = $a->get_curl_code(); - - if ($ret >= 200 && $ret <= 299) { - logger('queue [pubsubhubbub]: successfully pushed to ' . - $rr['callback_url']); - - // set last_update to "now", and reset push=0 - $date_now = datetime_convert('UTC','UTC','now','Y-m-d H:i:s'); - q("UPDATE `push_subscriber` SET `push` = 0, last_update = '%s' " . - "WHERE id = %d", - dbesc($date_now), - intval($rr['id'])); - - } else { - logger('queue [pubsubhubbub]: error when pushing to ' . - $rr['callback_url'] . 'HTTP: ', $ret); - - // we use the push variable also as a counter, if we failed we - // increment this until some upper limit where we give up - $new_push = intval($rr['push']) + 1; - - if ($new_push > 30) // OK, let's give up - $new_push = 0; - - q("UPDATE `push_subscriber` SET `push` = %d, last_update = '%s' " . - "WHERE id = %d", - $new_push, - dbesc($date_now), - intval($rr['id'])); - } - } -} - - function queue_run(&$argv, &$argc){ global $a, $db; @@ -112,7 +54,8 @@ function queue_run(&$argv, &$argc){ logger('queue: start'); - handle_pubsubhubbub(); + // Handling the pubsubhubbub requests + proc_run('php','include/pubsubpublish.php'); $interval = ((get_config('system','delivery_interval') === false) ? 2 : intval(get_config('system','delivery_interval'))); @@ -126,8 +69,8 @@ function queue_run(&$argv, &$argc){ } } - $r = q("SELECT `queue`.*, `contact`.`name`, `contact`.`uid` FROM `queue` - INNER JOIN `contact` ON `queue`.`cid` = `contact`.`id` + $r = q("SELECT `queue`.*, `contact`.`name`, `contact`.`uid` FROM `queue` + INNER JOIN `contact` ON `queue`.`cid` = `contact`.`id` WHERE `queue`.`created` < UTC_TIMESTAMP() - INTERVAL 3 DAY"); if($r) { foreach($r as $rr) { diff --git a/include/salmon.php b/include/salmon.php index aeb14adc8c..ed4ed7ad62 100644 --- a/include/salmon.php +++ b/include/salmon.php @@ -62,13 +62,13 @@ function get_salmon_key($uri,$keyhash) { return ''; } - - + + function slapper($owner,$url,$slap) { logger('slapper called for '.$owner['url'].' to '.$url.' . Data: ' . $slap); - // does contact have a salmon endpoint? + // does contact have a salmon endpoint? if(! strlen($url)) return; @@ -87,16 +87,16 @@ $namespaces = <<< EOT xmlns:thr="http://purl.org/syndication/thread/1.0" xmlns:at="http://purl.org/atompub/tombstones/1.0" xmlns:media="http://purl.org/syndication/atommedia" - xmlns:dfrn="http://purl.org/macgirvin/dfrn/1.0" + xmlns:dfrn="http://purl.org/macgirvin/dfrn/1.0" xmlns:as="http://activitystrea.ms/spec/1.0/" - xmlns:georss="http://www.georss.org/georss" - xmlns:poco="http://portablecontacts.net/spec/1.0" - xmlns:ostatus="http://ostatus.org/schema/1.0" + xmlns:georss="http://www.georss.org/georss" + xmlns:poco="http://portablecontacts.net/spec/1.0" + xmlns:ostatus="http://ostatus.org/schema/1.0" xmlns:statusnet="http://status.net/schema/api/1/" > > EOT; $slap = str_replace('',$namespaces,$slap); - + // create a magic envelope $data = base64url_encode($slap); @@ -125,7 +125,7 @@ EOT; '$signature' => $signature )); - // slap them + // slap them post_url($url,$salmon, array( 'Content-type: application/magic-envelope+xml', 'Content-length: ' . strlen($salmon) @@ -152,7 +152,7 @@ EOT; '$signature' => $signature2 )); - // slap them + // slap them post_url($url,$salmon, array( 'Content-type: application/magic-envelope+xml', 'Content-length: ' . strlen($salmon) @@ -166,7 +166,7 @@ EOT; // Entirely likely that their salmon implementation is // non-compliant. Let's try once more, this time only signing - // the data, without the precomputed blob + // the data, without the precomputed blob $salmon = replace_macros($salmon_tpl,array( '$data' => $data, @@ -176,7 +176,7 @@ EOT; '$signature' => $signature3 )); - // slap them + // slap them post_url($url,$salmon, array( 'Content-type: application/magic-envelope+xml', 'Content-length: ' . strlen($salmon) @@ -184,7 +184,7 @@ EOT; $return_code = $a->get_curl_code(); } } - logger('slapper returned ' . $return_code); + logger('slapper returned ' . $return_code); if(! $return_code) return(-1); if(($return_code == 503) && (stristr($a->get_curl_headers(),'retry-after'))) From 4750775752ed54bb936a302156fec05f2a69dec3 Mon Sep 17 00:00:00 2001 From: Michael Vogel Date: Sat, 30 May 2015 21:00:38 +0200 Subject: [PATCH 3/6] Cleaning the code. --- include/delivery.php | 47 ++++++++++++++++++++++---------------------- include/notifier.php | 24 ++++++---------------- 2 files changed, 29 insertions(+), 42 deletions(-) diff --git a/include/delivery.php b/include/delivery.php index fbbf7843f6..a913e13170 100644 --- a/include/delivery.php +++ b/include/delivery.php @@ -88,23 +88,25 @@ function delivery_run(&$argv, &$argc){ if($cmd === 'expire') { $normal_mode = false; $expire = true; - $items = q("SELECT * FROM `item` WHERE `uid` = %d AND `wall` = 1 + $items = q("SELECT * FROM `item` WHERE `uid` = %d AND `wall` = 1 AND `deleted` = 1 AND `changed` > UTC_TIMESTAMP() - INTERVAL 30 MINUTE", intval($item_id) ); $uid = $item_id; $item_id = 0; if(! count($items)) - continue; + continue; } else { + // find ancestors $r = q("SELECT * FROM `item` WHERE `id` = %d and visible = 1 and moderated = 0 LIMIT 1", intval($item_id) ); - if((! count($r)) || (! intval($r[0]['parent']))) + if((! count($r)) || (! intval($r[0]['parent']))) { continue; + } $target_item = $r[0]; $parent_id = intval($r[0]['parent']); @@ -116,13 +118,14 @@ function delivery_run(&$argv, &$argc){ continue; - $items = q("SELECT `item`.*, `sign`.`signed_text`,`sign`.`signature`,`sign`.`signer` + $items = q("SELECT `item`.*, `sign`.`signed_text`,`sign`.`signature`,`sign`.`signer` FROM `item` LEFT JOIN `sign` ON `sign`.`iid` = `item`.`id` WHERE `parent` = %d and visible = 1 and moderated = 0 ORDER BY `id` ASC", intval($parent_id) ); - if(! count($items)) + if(! count($items)) { continue; + } $icontacts = null; $contacts_arr = array(); @@ -130,8 +133,8 @@ function delivery_run(&$argv, &$argc){ if(! in_array($item['contact-id'],$contacts_arr)) $contacts_arr[] = intval($item['contact-id']); if(count($contacts_arr)) { - $str_contacts = implode(',',$contacts_arr); - $icontacts = q("SELECT * FROM `contact` + $str_contacts = implode(',',$contacts_arr); + $icontacts = q("SELECT * FROM `contact` WHERE `id` IN ( $str_contacts ) " ); } @@ -151,10 +154,10 @@ function delivery_run(&$argv, &$argc){ } } - $r = q("SELECT `contact`.*, `user`.`pubkey` AS `upubkey`, `user`.`prvkey` AS `uprvkey`, - `user`.`timezone`, `user`.`nickname`, `user`.`sprvkey`, `user`.`spubkey`, + $r = q("SELECT `contact`.*, `user`.`pubkey` AS `upubkey`, `user`.`prvkey` AS `uprvkey`, + `user`.`timezone`, `user`.`nickname`, `user`.`sprvkey`, `user`.`spubkey`, `user`.`page-flags`, `user`.`prvnets` - FROM `contact` INNER JOIN `user` ON `user`.`uid` = `contact`.`uid` + FROM `contact` INNER JOIN `user` ON `user`.`uid` = `contact`.`uid` WHERE `contact`.`uid` = %d AND `contact`.`self` = 1 LIMIT 1", intval($uid) ); @@ -198,13 +201,6 @@ function delivery_run(&$argv, &$argc){ if(strpos($localhost,':')) $localhost = substr($localhost,0,strpos($localhost,':')); - $r = q("SELECT * FROM `contact` WHERE `id` = %d AND `blocked` = 0 AND `pending` = 0", - intval($contact_id) - ); - - if(count($r)) - $contact = $r[0]; - /** * * Be VERY CAREFUL if you make any changes to the following line. Seemingly innocuous changes @@ -213,7 +209,7 @@ function delivery_run(&$argv, &$argc){ * */ - if(!$top_level && ($parent["network"] != NETWORK_OSTATUS) && ($parent['wall'] == 0) && (! $expire) && (stristr($target_item['uri'],$localhost))) { + if((! $top_level) && ($parent['wall'] == 0) && (! $expire) && (stristr($target_item['uri'],$localhost))) { logger('relay denied for delivery agent.'); /* no relay allowed for direct contact delivery */ @@ -227,6 +223,13 @@ function delivery_run(&$argv, &$argc){ $public_message = false; // private recipients, not public } + $r = q("SELECT * FROM `contact` WHERE `id` = %d AND `blocked` = 0 AND `pending` = 0", + intval($contact_id) + ); + + if(count($r)) + $contact = $r[0]; + $hubxml = feed_hublinks(); logger('notifier: slaps: ' . print_r($slaps,true), LOGGER_DATA); @@ -387,15 +390,11 @@ function delivery_run(&$argv, &$argc){ if(! $item_contact) continue; - // For OStatus don't notify all contacts in the thread - if (!$top_level AND ($parent["network"] == NETWORK_OSTATUS) AND ($item["id"] != $item_id)) - continue; - - if(($top_level OR ($parent["network"] == NETWORK_OSTATUS)) && ($public_message) && ($item['author-link'] === $item['owner-link']) && (! $expire)) + if(($top_level) && ($public_message) && ($item['author-link'] === $item['owner-link']) && (! $expire)) $slaps[] = atom_entry($item,'html',null,$owner,true); } - logger('slapdelivery item '.$item_id.' to ' . $contact['name']); + logger('notifier: slapdelivery: ' . $contact['name']); foreach($slaps as $slappy) { if($contact['notify']) { if(! was_recently_delayed($contact['id'])) diff --git a/include/notifier.php b/include/notifier.php index 962090326f..318e0eab25 100644 --- a/include/notifier.php +++ b/include/notifier.php @@ -227,9 +227,6 @@ function notifier_run(&$argv, &$argc){ // fill this in with a single salmon slap if applicable $slap = ''; - // List of OStatus receiptians of follow up messages - $ostatus_recip_str = ""; - if(! ($mail || $fsuggest || $relocate)) { require_once('include/group.php'); @@ -303,17 +300,8 @@ function notifier_run(&$argv, &$argc){ $push_notify = true; -/* $ostatus_recipients = array(); - - $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `network` = '%s'", intval($uid), dbesc(NETWORK_OSTATUS)); - if(count($r)) { - foreach($r as $rr) - $ostatus_recipients[] = $rr['id']; - - $ostatus_recip_str = ", ".implode(', ', $ostatus_recipients); - } -*/ - // Check if the recipient isn't in your contact list + // Check if the recipient isn't in your contact list, try to slap it + // This doesn't seem to work correctly by now $r = q("SELECT `url` FROM `contact` WHERE `id` = %d", $parent['contact-id']); if (count($r)) { $url_recipients = array(); @@ -570,7 +558,7 @@ function notifier_run(&$argv, &$argc){ } if($followup) - $recip_str = $parent['contact-id'].$ostatus_recip_str; + $recip_str = $parent['contact-id']; else $recip_str = implode(', ', $recipients); @@ -591,7 +579,7 @@ function notifier_run(&$argv, &$argc){ if(count($r)) { foreach($r as $contact) { - if((! $mail) && (! $fsuggest) && (!$followup OR ($parent['contact-id'] != $contact['id'])) && (!$relocate) && (! $contact['self'])) { + if((! $mail) && (! $fsuggest) && (! $followup) && (!$relocate) && (! $contact['self'])) { if(($contact['network'] === NETWORK_DIASPORA) && ($public_message)) continue; q("insert into deliverq ( `cmd`,`item`,`contact` ) values ('%s', %d, %d )", @@ -630,7 +618,7 @@ function notifier_run(&$argv, &$argc){ // potentially more than one recipient. Start a new process and space them out a bit. // we will deliver single recipient types of message and email recipients here. - if((! $mail) && (! $fsuggest) && (!$relocate) && (!$followup OR ($parent['contact-id'] != $contact['id']))) { + if((! $mail) && (! $fsuggest) && (!$relocate) && (! $followup)) { $this_batch[] = $contact['id']; @@ -957,7 +945,7 @@ function notifier_run(&$argv, &$argc){ // throw everything into the queue in case we get killed foreach($r as $rr) { - if((! $mail) && (! $fsuggest) && (!$followup OR ($parent['contact-id'] != $contact['id']))) { + if((! $mail) && (! $fsuggest) && (! $followup)) { q("insert into deliverq ( `cmd`,`item`,`contact` ) values ('%s', %d, %d )", dbesc($cmd), intval($item_id), From a3118e0a6edc2da6711404acc0aa9fc447e90624 Mon Sep 17 00:00:00 2001 From: Michael Vogel Date: Sat, 30 May 2015 21:08:24 +0200 Subject: [PATCH 4/6] Cleaning the code from several tests. --- include/items.php | 2 +- include/pubsubpublish.php | 3 +-- include/salmon.php | 28 ++++++++++++++-------------- 3 files changed, 16 insertions(+), 17 deletions(-) diff --git a/include/items.php b/include/items.php index 44eb18ccd1..2c4ae9e5ca 100644 --- a/include/items.php +++ b/include/items.php @@ -115,7 +115,7 @@ function get_feed_for(&$a, $dfrn_id, $owner_nick, $last_update, $direction = 0, else $sort = 'ASC'; - // Include answers to status.net posts in public feeds + // Include answers to status.net posts in pubsub feeds if($forpubsub) { $sql_post_table = "INNER JOIN `thread` ON `thread`.`iid` = `item`.`parent` "; $visibility = "OR (`item`.`network` = 'dfrn' AND `thread`.`network`='stat')"; diff --git a/include/pubsubpublish.php b/include/pubsubpublish.php index 60bb2b3de0..bc81fd7868 100644 --- a/include/pubsubpublish.php +++ b/include/pubsubpublish.php @@ -46,9 +46,8 @@ function handle_pubsubhubbub() { if ($new_push > 30) // OK, let's give up $new_push = 0; - q("UPDATE `push_subscriber` SET `push` = %d, last_update = '%s' WHERE id = %d", + q("UPDATE `push_subscriber` SET `push` = %d WHERE id = %d", $new_push, - dbesc($date_now), intval($rr['id'])); } } diff --git a/include/salmon.php b/include/salmon.php index ed4ed7ad62..3d525f51ad 100644 --- a/include/salmon.php +++ b/include/salmon.php @@ -62,13 +62,13 @@ function get_salmon_key($uri,$keyhash) { return ''; } - - + + function slapper($owner,$url,$slap) { - logger('slapper called for '.$owner['url'].' to '.$url.' . Data: ' . $slap); + logger('slapper called. Data: ' . $slap); - // does contact have a salmon endpoint? + // does contact have a salmon endpoint? if(! strlen($url)) return; @@ -87,16 +87,16 @@ $namespaces = <<< EOT xmlns:thr="http://purl.org/syndication/thread/1.0" xmlns:at="http://purl.org/atompub/tombstones/1.0" xmlns:media="http://purl.org/syndication/atommedia" - xmlns:dfrn="http://purl.org/macgirvin/dfrn/1.0" + xmlns:dfrn="http://purl.org/macgirvin/dfrn/1.0" xmlns:as="http://activitystrea.ms/spec/1.0/" - xmlns:georss="http://www.georss.org/georss" - xmlns:poco="http://portablecontacts.net/spec/1.0" - xmlns:ostatus="http://ostatus.org/schema/1.0" + xmlns:georss="http://www.georss.org/georss" + xmlns:poco="http://portablecontacts.net/spec/1.0" + xmlns:ostatus="http://ostatus.org/schema/1.0" xmlns:statusnet="http://status.net/schema/api/1/" > > EOT; $slap = str_replace('',$namespaces,$slap); - + // create a magic envelope $data = base64url_encode($slap); @@ -125,7 +125,7 @@ EOT; '$signature' => $signature )); - // slap them + // slap them post_url($url,$salmon, array( 'Content-type: application/magic-envelope+xml', 'Content-length: ' . strlen($salmon) @@ -152,7 +152,7 @@ EOT; '$signature' => $signature2 )); - // slap them + // slap them post_url($url,$salmon, array( 'Content-type: application/magic-envelope+xml', 'Content-length: ' . strlen($salmon) @@ -166,7 +166,7 @@ EOT; // Entirely likely that their salmon implementation is // non-compliant. Let's try once more, this time only signing - // the data, without the precomputed blob + // the data, without the precomputed blob $salmon = replace_macros($salmon_tpl,array( '$data' => $data, @@ -176,7 +176,7 @@ EOT; '$signature' => $signature3 )); - // slap them + // slap them post_url($url,$salmon, array( 'Content-type: application/magic-envelope+xml', 'Content-length: ' . strlen($salmon) @@ -184,7 +184,7 @@ EOT; $return_code = $a->get_curl_code(); } } - logger('slapper returned ' . $return_code); + logger('slapper returned ' . $return_code); if(! $return_code) return(-1); if(($return_code == 503) && (stristr($a->get_curl_headers(),'retry-after'))) From 788d3cf1abb11147e334bfa4819b748acc2f7800 Mon Sep 17 00:00:00 2001 From: Michael Vogel Date: Sat, 30 May 2015 21:45:43 +0200 Subject: [PATCH 5/6] Ostatus: The automatical adding of @-replies now uses the correct contact. --- mod/item.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mod/item.php b/mod/item.php index ec6f81f6bf..aecfc1bb8c 100644 --- a/mod/item.php +++ b/mod/item.php @@ -570,9 +570,9 @@ function item_post(&$a) { */ if(($parent_contact) && ($parent_contact['network'] === NETWORK_OSTATUS) - && ($parent_contact['nick']) && (! in_array('@' . $parent_contact['nick'],$tags))) { - $body = '@' . $parent_contact['nick'] . ' ' . $body; - $tags[] = '@' . $parent_contact['nick']; + && ($parent_contact['nick']) && (!in_array('@'.$parent_contact['nick'].'+'.$parent_contact['id'],$tags))) { + $body = '@'.$parent_contact['nick'].'+'.$parent_contact['id'].' '.$body; + $tags[] = '@'.$parent_contact['nick'].'+'.$parent_contact['id']; } $tagged = array(); From d732d9d1a812b1992a6dbdf6de8bdf20ff60fdac Mon Sep 17 00:00:00 2001 From: Michael Vogel Date: Sun, 31 May 2015 00:26:11 +0200 Subject: [PATCH 6/6] OStatus replies should now work even if the thread starter isn't in the mentions. --- include/bbcode.php | 22 +++++++++++++--------- mod/item.php | 31 +++++++++++++++++++++++++++---- 2 files changed, 40 insertions(+), 13 deletions(-) diff --git a/include/bbcode.php b/include/bbcode.php index b47a514320..01a1419567 100644 --- a/include/bbcode.php +++ b/include/bbcode.php @@ -828,7 +828,6 @@ function bbcode($Text,$preserve_nl = false, $tryoembed = true, $simplehtml = fal $ev = bbtoevent($Text); - // Replace any html brackets with HTML Entities to prevent executing HTML or script // Don't use strip_tags here because it breaks [url] search by replacing & with amp @@ -885,8 +884,13 @@ function bbcode($Text,$preserve_nl = false, $tryoembed = true, $simplehtml = fal $MAILSearchString = $URLSearchString; // Remove all hashtag addresses - if (!$tryoembed OR $simplehtml) + if ((!$tryoembed OR $simplehtml) AND ($simplehtml != 7)) $Text = preg_replace("/([#@])\[url\=([$URLSearchString]*)\](.*?)\[\/url\]/ism", '$1$3', $Text); + elseif ($simplehtml == 7) + $Text = preg_replace("/([@])\[url\=([$URLSearchString]*)\](.*?)\[\/url\]/ism", + '$1$3', + $Text); + // Bookmarks in red - will be converted to bookmarks in friendica $Text = preg_replace("/#\^\[url\]([$URLSearchString]*)\[\/url\]/ism", '[bookmark=$1]$1[/bookmark]', $Text); @@ -937,13 +941,13 @@ function bbcode($Text,$preserve_nl = false, $tryoembed = true, $simplehtml = fal // we may need to restrict this further if it picks up too many strays // link acct:user@host to a webfinger profile redirector - $Text = preg_replace('/acct:(.*?)@(.*?)([ ,])/', 'acct:' . "$1@$2$3" . '',$Text); // Perform MAIL Search $Text = preg_replace("/\[mail\]([$MAILSearchString]*)\[\/mail\]/", '$1', $Text); $Text = preg_replace("/\[mail\=([$MAILSearchString]*)\](.*?)\[\/mail\]/", '$2', $Text); - + // leave open the posibility of [map=something] // this is replaced in prepare_body() which has knowledge of the item location @@ -955,8 +959,8 @@ function bbcode($Text,$preserve_nl = false, $tryoembed = true, $simplehtml = fal } if (strpos($Text,'[map]') !== false) { $Text = preg_replace("/\[map\]/", '
', $Text); - } - + } + // Check for headers $Text = preg_replace("(\[h1\](.*?)\[\/h1\])ism",'

$1

',$Text); $Text = preg_replace("(\[h2\](.*?)\[\/h2\])ism",'

$1

',$Text); @@ -1004,8 +1008,8 @@ function bbcode($Text,$preserve_nl = false, $tryoembed = true, $simplehtml = fal $endlessloop = 0; while ((((strpos($Text, "[/list]") !== false) && (strpos($Text, "[list") !== false)) || - ((strpos($Text, "[/ol]") !== false) && (strpos($Text, "[ol]") !== false)) || - ((strpos($Text, "[/ul]") !== false) && (strpos($Text, "[ul]") !== false)) || + ((strpos($Text, "[/ol]") !== false) && (strpos($Text, "[ol]") !== false)) || + ((strpos($Text, "[/ul]") !== false) && (strpos($Text, "[ul]") !== false)) || ((strpos($Text, "[/li]") !== false) && (strpos($Text, "[li]") !== false))) && (++$endlessloop < 20)) { $Text = preg_replace("/\[list\](.*?)\[\/list\]/ism", '
    $1
' ,$Text); $Text = preg_replace("/\[list=\](.*?)\[\/list\]/ism", '
    $1
' ,$Text); @@ -1149,7 +1153,7 @@ function bbcode($Text,$preserve_nl = false, $tryoembed = true, $simplehtml = fal $Text = preg_replace_callback("/\[vimeo\](https?:\/\/vimeo.com\/[0-9]+).*?\[\/vimeo\]/ism",'tryoembed',$Text); } - $Text = preg_replace("/\[vimeo\]https?:\/\/player.vimeo.com\/video\/([0-9]+)(.*?)\[\/vimeo\]/ism",'[vimeo]$1[/vimeo]',$Text); + $Text = preg_replace("/\[vimeo\]https?:\/\/player.vimeo.com\/video\/([0-9]+)(.*?)\[\/vimeo\]/ism",'[vimeo]$1[/vimeo]',$Text); $Text = preg_replace("/\[vimeo\]https?:\/\/vimeo.com\/([0-9]+)(.*?)\[\/vimeo\]/ism",'[vimeo]$1[/vimeo]',$Text); if ($tryoembed) diff --git a/mod/item.php b/mod/item.php index aecfc1bb8c..27bd5108d2 100644 --- a/mod/item.php +++ b/mod/item.php @@ -144,6 +144,7 @@ function item_post(&$a) { $parent_contact["nurl"] = normalise_link($probed_contact["url"]); $parent_contact["thumb"] = $probed_contact["photo"]; $parent_contact["micro"] = $probed_contact["photo"]; + $parent_contact["addr"] = $probed_contact["addr"]; } logger('parent contact: '.print_r($parent_contact, true), LOGGER_DEBUG); } else @@ -569,10 +570,32 @@ function item_post(&$a) { * and we are replying, and there isn't one already */ - if(($parent_contact) && ($parent_contact['network'] === NETWORK_OSTATUS) - && ($parent_contact['nick']) && (!in_array('@'.$parent_contact['nick'].'+'.$parent_contact['id'],$tags))) { - $body = '@'.$parent_contact['nick'].'+'.$parent_contact['id'].' '.$body; - $tags[] = '@'.$parent_contact['nick'].'+'.$parent_contact['id']; + if ($parent_contact['id'] != "") + $contact = '@'.$parent_contact['nick'].'+'.$parent_contact['id']; + //elseif ($parent_contact['addr'] != "") + // $contact = '@'.$parent_contact['addr']; + else + $contact = '@[url='.$parent_contact['url'].']'.$parent_contact['nick'].'[/url]'; + + if ($parent_contact && ($parent_contact['network'] === NETWORK_OSTATUS)) { + if (($parent_contact['nick']) && (!in_array($contact,$tags))) { + $body = $contact.' '.$body; + $tags[] = $contact; + } + + $toplevel_contact = ""; + $toplevel_parent = q("SELECT `contact`.* FROM `contact` INNER JOIN `item` ON `item`.`contact-id` = `contact`.`id` + WHERE `item`.`id` = `item`.`parent` AND `item`.`parent` = %d", intval($parent)); + if ($toplevel_parent) + $toplevel_contact = '@'.$toplevel_parent[0]['nick'].'+'.$toplevel_parent[0]['id']; + else { + $toplevel_parent = q("SELECT `author-link`, `author-name` FROM `item` WHERE `id` = `parent` AND `parent` = %d", intval($parent)); + $toplevel_contact = '@[url='.$toplevel_parent[0]['author-link'].']'.$toplevel_parent[0]['author-name'].'[/url]'; + } + + if ($toplevel_contact != "") + if (!in_array($toplevel_contact,$tags)) + $tags[] = $toplevel_contact; } $tagged = array();