diff options
Diffstat (limited to 'include')
76 files changed, 5866 insertions, 5792 deletions
diff --git a/include/BaseObject.php b/include/BaseObject.php index 200831e15..4bfac5fa0 100644 --- a/include/BaseObject.php +++ b/include/BaseObject.php @@ -1,4 +1,4 @@ -<?php +<?php /** @file */ if(class_exists('BaseObject')) return; diff --git a/include/Contact.php b/include/Contact.php index 36f4cbcf7..b9ad1e4cb 100644 --- a/include/Contact.php +++ b/include/Contact.php @@ -1,4 +1,4 @@ -<?php +<?php /** @file */ function abook_connections($channel_id, $sql_conditions = '') { @@ -43,6 +43,8 @@ function channelx_by_n($id) { function vcard_from_xchan($xchan, $observer = null, $mode = '') { + $a = get_app(); + $connect = false; if(local_user()) { $r = q("select * from abook where abook_xchan = '%s' and abook_channel = %d limit 1", @@ -53,6 +55,9 @@ function vcard_from_xchan($xchan, $observer = null, $mode = '') { $connect = t('Connect'); } + if(array_key_exists('channel_id',$xchan)) + $a->profile_uid = $xchan['channel_id']; + $url = (($observer) ? z_root() . '/magic?f=&dest=' . $xchan['xchan_url'] . '&addr=' . $xchan['xchan_addr'] : $xchan['xchan_url'] @@ -76,6 +81,9 @@ function abook_toggle_flag($abook,$flag) { intval($abook['abook_id']), intval($abook['abook_channel']) ); + $a = get_app(); + if($a->data['abook']) + $a->data['abook']['abook_flags'] = $a->data['abook']['abook_flags'] ^ $flag; return $r; } @@ -107,6 +115,49 @@ function user_remove($uid) { } +function account_remove($account_id) { + + if(! intval($account_id)) { + logger('account_remove: no account.'); + return false; + } + + // Don't let anybody nuke the only admin account. + + $r = q("select account_id from account where (account_roles & %d)", + intval(ACCOUNT_ROLE_ADMIN) + ); + + if($r !== false && count($r) == 1 && $r[0]['account_id'] == $account_id) { + logger("Unable to remove the only remaining admin account"); + return false; + } + + $r = q("select * from account where account_id = %d limit 1", + intval($account_id) + ); + + if(! $r) { + logger('account_remove: No account with id: ' . $account_id); + return false; + } + + $x = q("select channel_id from channel where channel_account_id = %d", + intval($account_id) + ); + if($x) { + foreach($x as $xx) { + channel_remove($xx['channel_id']); + } + } + + $r = q("delete from account where account_id = %d limit 1", + intval($account_id) + ); + + return $r; + +} function channel_remove($channel_id) { @@ -212,8 +263,6 @@ function remove_all_xchan_resources($xchan, $channel_id = 0) { } - - function contact_remove($channel_id, $abook_id) { if((! $channel_id) || (! $abook_id)) @@ -242,31 +291,37 @@ function contact_remove($channel_id, $abook_id) { if($abook['abook_flags'] & ABOOK_FLAG_SELF) return false; - q("delete from item where author_xchan = '%s' and uid = %d", + + $r = q("select * from item where author_xchan = '%s' and uid = %d", dbesc($abook['abook_xchan']), intval($channel_id) ); + if($r) { + foreach($r as $rr) { + drop_item($rr,false); + } + } - q("delete from abook where abook_id = %d and channel_id = %d limit 1", + q("delete from abook where abook_id = %d and abook_channel = %d limit 1", intval($abook['abook_id']), intval($channel_id) ); -/* -// FIXME - q("DELETE FROM `photo` WHERE `contact-id` = %d ", - intval($id) - ); - q("DELETE FROM `mail` WHERE `contact-id` = %d ", - intval($id) + $r = q("delete from event where event_xchan = '%s' and uid = %d", + dbesc($abook['abook_xchan']), + intval($channel_id) ); - q("DELETE FROM `event` WHERE `cid` = %d ", - intval($id) + + $r = q("delete from group_member where xchan = '%s' and uid = %d", + dbesc($abook['abook_xchan']), + intval($channel_id) ); - q("DELETE FROM `queue` WHERE `cid` = %d ", - intval($id) + + $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) ); -*/ return true; } @@ -397,8 +452,8 @@ function contact_photo_menu($contact) { function random_profile() { - $r = q("select xchan_url from xchan where xchan_network = 'zot' order by rand() limit 1"); - if($r && count($r)) + $r = q("select xchan_url from xchan where 1 order by rand() limit 1"); + if($r) return $r[0]['xchan_url']; return ''; } diff --git a/include/ConversationObject.php b/include/ConversationObject.php index 37633369e..30026e908 100644 --- a/include/ConversationObject.php +++ b/include/ConversationObject.php @@ -1,4 +1,5 @@ -<?php +<?php /** @file */ + if(class_exists('Conversation')) return; @@ -10,12 +11,14 @@ require_once('include/text.php'); /** * A list of threads * - * We should think about making this a SPL Iterator */ + class Conversation extends BaseObject { private $threads = array(); private $mode = null; + private $observer = null; private $writable = false; + private $commentable = false; private $profile_owner = 0; private $preview = false; @@ -33,8 +36,8 @@ class Conversation extends BaseObject { $a = $this->get_app(); - $observer = $a->get_observer(); - $ob_hash = (($observer) ? $observer['xchan_hash'] : ''); + $this->observer = $a->get_observer(); + $ob_hash = (($this->observer) ? $this->observer['xchan_hash'] : ''); switch($mode) { case 'network': @@ -46,7 +49,10 @@ class Conversation extends BaseObject { $this->writable = perm_is_allowed($this->profile_owner,$ob_hash,'post_comments'); break; case 'display': - $this->profile_owner = $a->profile['uid']; + // in this mode we set profile_owner after initialisation (from conversation()) and then + // pull some trickery which allows us to re-invoke this function afterward + // it's an ugly hack so FIXME +// $this->profile_owner = $a->profile['uid']; $this->writable = perm_is_allowed($this->profile_owner,$ob_hash,'post_comments'); break; case 'page': @@ -75,6 +81,10 @@ class Conversation extends BaseObject { return $this->writable; } + public function is_commentable() { + return $this->commentable; + } + /** * Check if page is a preview */ @@ -89,6 +99,18 @@ class Conversation extends BaseObject { return $this->profile_owner; } + public function set_profile_owner($uid) { + $this->profile_owner = $uid; + $mode = $this->get_mode(); + $this->mode = null; + $this->set_mode($mode); + } + + public function get_observer() { + return $this->observer; + } + + /** * Add a thread to the conversation * @@ -108,12 +130,30 @@ class Conversation extends BaseObject { } /* - * Only add will be displayed + * Only add things that will be displayed */ - if(activity_match($item->get_data_value('verb'),ACTIVITY_LIKE) || activity_match($item->get_data_value('verb'),ACTIVITY_DISLIKE)) { + + if(($item->get_data_value('id') != $item->get_data_value('parent')) && (activity_match($item->get_data_value('verb'),ACTIVITY_LIKE) || activity_match($item->get_data_value('verb'),ACTIVITY_DISLIKE))) { return false; } + + if(local_user() && $item->get_data_value('uid') == local_user()) + $this->commentable = true; + + if($this->writable) + $this->commentable = true; + + if($item->get_data_value('item_flags') & ITEM_NOCOMMENT) { + $this->commentable = false; + } + elseif(($this->observer) && (! $this->writable)) { + $this->commentable = can_comment_on_post($this->observer['xchan_hash'],$item->data); + } + + + + $item->set_conversation($this); $this->threads[] = $item; return end($this->threads); diff --git a/include/ITemplateEngine.php b/include/ITemplateEngine.php new file mode 100755 index 000000000..53c1845f4 --- /dev/null +++ b/include/ITemplateEngine.php @@ -0,0 +1,11 @@ +<?php
+require_once 'boot.php';
+
+
+/**
+ * Interface for template engines
+ */
+interface ITemplateEngine {
+ public function replace_macros($s,$v);
+ public function get_markup_template($file, $root='');
+}
diff --git a/include/ItemObject.php b/include/ItemObject.php index afa34abb1..8c8c0ee2a 100644 --- a/include/ItemObject.php +++ b/include/ItemObject.php @@ -1,8 +1,9 @@ -<?php +<?php /** @file */ + if(class_exists('Item')) return; -require_once('object/BaseObject.php'); +require_once('include/BaseObject.php'); require_once('include/text.php'); require_once('boot.php'); @@ -10,12 +11,10 @@ require_once('boot.php'); * An item */ class Item extends BaseObject { - private $data = array(); + public $data = array(); private $template = 'conv_item.tpl'; private $comment_box_template = 'comment_item.tpl'; private $toplevel = false; - private $writable = false; - private $commentable = false; private $children = array(); private $parent = null; private $conversation = null; @@ -26,31 +25,15 @@ class Item extends BaseObject { private $wall_to_wall = false; private $threaded = false; private $visiting = false; - private $observer = null; private $channel = null; public function __construct($data) { $a = $this->get_app(); $this->data = $data; - $this->channel = $a->get_channel(); - $this->observer = $a->get_observer(); - $this->toplevel = ($this->get_id() == $this->get_data_value('parent')); - $this->writable = (((local_user()) && ($this->channel['channel_hash'] === $this->data['owner_xchan'])) ? true : false); - $this->commentable = $this->writable; - - if(($this->observer) && (! $this->writable)) { - $this->commentable = perm_is_allowed($this->data['uid'],$this->observer['xchan_hash'],'post_comments'); - } - -// logger('writable: ' . $this->writable); -// logger('commentable: ' . $this->commentable . ' uid=' . $this->data['uid'] . ' observer=' . $this->observer['xchan_hash']); -// if(get_config('system','thread_allow') && $a->theme_thread_allow && !$this->is_toplevel()) -// $this->threaded = true; - // Prepare the children if(count($data['children'])) { foreach($data['children'] as $item) { @@ -84,7 +67,6 @@ class Item extends BaseObject { $result = array(); $a = $this->get_app(); - $observer = $this->observer; $item = $this->get_data(); $commentww = ''; @@ -98,6 +80,7 @@ class Item extends BaseObject { $total_children = $this->count_descendants(); $conv = $this->get_conversation(); + $observer = $conv->get_observer(); $lock = ((($item['item_private'] == 1) || (($item['uid'] == local_user()) && (strlen($item['allow_cid']) || strlen($item['allow_gid']) || strlen($item['deny_cid']) || strlen($item['deny_gid'])))) @@ -110,10 +93,9 @@ class Item extends BaseObject { else $edpost = false; -// FIXME - this is wrong. -// if(($this->get_data_value('uid') == local_user()) || $this->is_visiting()) - - if($this->get_data_value('uid') == local_user()) + if($observer['xchan_hash'] == $this->get_data_value('author_xchan') + || $observer['xchan_hash'] == $this->get_data_value('owner_xchan') + || $this->get_data_value('uid') == local_user()) $dropping = true; if($dropping) { @@ -122,7 +104,7 @@ class Item extends BaseObject { 'delete' => t('Delete'), ); } - +// FIXME if($observer_is_pageowner) { $multidrop = array( 'select' => t('Select'), @@ -138,9 +120,9 @@ class Item extends BaseObject { $location = format_location($item); - $showlike = ((x($alike,$item['uri'])) ? format_like($alike[$item['uri']],$alike[$item['uri'] . '-l'],'like',$item['uri']) : ''); - $showdislike = ((x($dlike,$item['uri']) && feature_enabled($conv->get_profile_owner(),'dislike')) - ? format_like($dlike[$item['uri']],$dlike[$item['uri'] . '-l'],'dislike',$item['uri']) : ''); + $showlike = ((x($alike,$item['mid'])) ? format_like($alike[$item['mid']],$alike[$item['mid'] . '-l'],'like',$item['mid']) : ''); + $showdislike = ((x($dlike,$item['mid']) && feature_enabled($conv->get_profile_owner(),'dislike')) + ? format_like($dlike[$item['mid']],$dlike[$item['mid'] . '-l'],'dislike',$item['mid']) : ''); /* * We should avoid doing this all the time, but it depends on the conversation mode @@ -151,6 +133,7 @@ class Item extends BaseObject { $this->check_wall_to_wall(); if($this->is_toplevel()) { + // FIXME check this permission if($conv->get_profile_owner() == local_user()) { // FIXME we don't need all this stuff, some can be done in the template @@ -165,16 +148,20 @@ class Item extends BaseObject { 'starred' => t('starred'), ); - $tagger = array( - 'tagit' => t("add tag"), - 'classtagger' => "", - ); } } else { $indent = 'comment'; } - if($this->is_writable()) { + // FIXME - check this permission + if($conv->get_profile_owner() == local_user()) { + $tagger = array( + 'tagit' => t("add tag"), + 'classtagger' => "", + ); + } + + if($conv->is_commentable()) { $like = array( t("I like this \x28toggle\x29"), t("like")); $dislike = array( t("I don't like this \x28toggle\x29"), t("dislike")); if ($shareable) @@ -214,8 +201,11 @@ class Item extends BaseObject { 'osparkle' => $osparkle, 'sparkle' => $sparkle, 'title' => $item['title'], + 'ago' => relative_date($item['created']), + 'app' => $item['app'], + 'str_app' => sprintf( t(' from %s'), $item['app']), + 'isotime' => datetime_convert('UTC', date_default_timezone_get(), $item['created'], 'c'), 'localtime' => datetime_convert('UTC', date_default_timezone_get(), $item['created'], 'r'), - 'ago' => (($item['app']) ? sprintf( t('%s from %s'),relative_date($item['created']),$item['app']) : relative_date($item['created'])), 'lock' => $lock, 'location' => $location, 'indent' => $indent, @@ -272,7 +262,7 @@ class Item extends BaseObject { } } - $result['private'] = $item['private']; + $result['private'] = $item['item_private']; $result['toplevel'] = ($this->is_toplevel() ? 'toplevel_item' : ''); if($this->is_threaded()) { @@ -452,28 +442,6 @@ class Item extends BaseObject { } /** - * Check if this is writable - */ - private function is_writable() { - - return $this->writable; - -// $conv = $this->get_conversation(); - -// return true; - -// if($conv) { - // This will allow us to comment on wall-to-wall items owned by our friends - // and community forums even if somebody else wrote the post. -// return ($this->writable || ($this->is_visiting() && $conv->get_mode() == 'channel')); -// } - } - - private function is_commentable() { - return $this->commentable; - } - - /** * Count the total of our descendants */ private function count_descendants() { @@ -510,44 +478,43 @@ class Item extends BaseObject { $comment_box = ''; $conv = $this->get_conversation(); - if(! $this->is_commentable()) + if(! $conv->is_commentable()) return; - if($conv->is_writable() || $this->is_writable()) { - $template = get_markup_template($this->get_comment_box_template()); - - $a = $this->get_app(); - - $qc = ((local_user()) ? get_pconfig(local_user(),'system','qcomment') : null); - $qcomment = (($qc) ? explode("\n",$qc) : null); - - $comment_box = replace_macros($template,array( - '$return_path' => '', - '$threaded' => $this->is_threaded(), - '$jsreload' => (($conv->get_mode() === 'display') ? $_SESSION['return_url'] : ''), - '$type' => (($conv->get_mode() === 'channel') ? 'wall-comment' : 'net-comment'), - '$id' => $this->get_id(), - '$parent' => $this->get_id(), - '$qcomment' => $qcomment, - '$profile_uid' => $conv->get_profile_owner(), - '$mylink' => $this->observer['xchan_url'], - '$mytitle' => t('This is you'), - '$myphoto' => $this->observer['xchan_photo_s'], - '$comment' => t('Comment'), - '$submit' => t('Submit'), - '$edbold' => t('Bold'), - '$editalic' => t('Italic'), - '$eduline' => t('Underline'), - '$edquote' => t('Quote'), - '$edcode' => t('Code'), - '$edimg' => t('Image'), - '$edurl' => t('Link'), - '$edvideo' => t('Video'), - '$preview' => ((feature_enabled($conv->get_profile_owner(),'preview')) ? t('Preview') : ''), - '$indent' => $indent, - '$sourceapp' => get_app()->sourcename - )); - } + $template = get_markup_template($this->get_comment_box_template()); + + $a = $this->get_app(); + $observer = $conv->get_observer(); + + $qc = ((local_user()) ? get_pconfig(local_user(),'system','qcomment') : null); + $qcomment = (($qc) ? explode("\n",$qc) : null); + + $comment_box = replace_macros($template,array( + '$return_path' => '', + '$threaded' => $this->is_threaded(), + '$jsreload' => (($conv->get_mode() === 'display') ? $_SESSION['return_url'] : ''), + '$type' => (($conv->get_mode() === 'channel') ? 'wall-comment' : 'net-comment'), + '$id' => $this->get_id(), + '$parent' => $this->get_id(), + '$qcomment' => $qcomment, + '$profile_uid' => $conv->get_profile_owner(), + '$mylink' => $observer['xchan_url'], + '$mytitle' => t('This is you'), + '$myphoto' => $observer['xchan_photo_s'], + '$comment' => t('Comment'), + '$submit' => t('Submit'), + '$edbold' => t('Bold'), + '$editalic' => t('Italic'), + '$eduline' => t('Underline'), + '$edquote' => t('Quote'), + '$edcode' => t('Code'), + '$edimg' => t('Image'), + '$edurl' => t('Link'), + '$edvideo' => t('Video'), + '$preview' => ((feature_enabled($conv->get_profile_owner(),'preview')) ? t('Preview') : ''), + '$indent' => $indent, + '$sourceapp' => get_app()->sourcename + )); return $comment_box; } diff --git a/include/Photo.php b/include/Photo.php deleted file mode 100644 index de3d30c39..000000000 --- a/include/Photo.php +++ /dev/null @@ -1,768 +0,0 @@ -<?php - -if(! class_exists("Photo")) { -class Photo { - - private $image; - - /** - * Put back gd stuff, not everybody have Imagick - */ - private $imagick; - private $width; - private $height; - private $valid; - private $type; - private $types; - - /** - * supported mimetypes and corresponding file extensions - */ - static function supportedTypes() { - if(class_exists('Imagick')) { - /** - * Imagick::queryFormats won't help us a lot there... - * At least, not yet, other parts of friendica uses this array - */ - $t = array( - 'image/jpeg' => 'jpg', - 'image/png' => 'png', - 'image/gif' => 'gif' - ); - } else { - $t = array(); - $t['image/jpeg'] ='jpg'; - if (imagetypes() & IMG_PNG) $t['image/png'] = 'png'; - } - - return $t; - } - - public function __construct($data, $type=null) { - $this->imagick = class_exists('Imagick'); - $this->types = $this->supportedTypes(); - if (!array_key_exists($type,$this->types)){ - $type='image/jpeg'; - } - $this->type = $type; - - if($this->is_imagick()) { - $this->image = new Imagick(); - $this->image->readImageBlob($data); - - /** - * Setup the image to the format it will be saved to - */ - $map = $this->get_FormatsMap(); - $format = $map[$type]; - $this->image->setFormat($format); - - // Always coalesce, if it is not a multi-frame image it won't hurt anyway - $this->image = $this->image->coalesceImages(); - - /** - * setup the compression here, so we'll do it only once - */ - switch($this->getType()){ - case "image/png": - $quality = get_config('system','png_quality'); - if((! $quality) || ($quality > 9)) - $quality = PNG_QUALITY; - /** - * From http://www.imagemagick.org/script/command-line-options.php#quality: - * - * 'For the MNG and PNG image formats, the quality value sets - * the zlib compression level (quality / 10) and filter-type (quality % 10). - * The default PNG "quality" is 75, which means compression level 7 with adaptive PNG filtering, - * unless the image has a color map, in which case it means compression level 7 with no PNG filtering' - */ - $quality = $quality * 10; - $this->image->setCompressionQuality($quality); - break; - case "image/jpeg": - $quality = get_config('system','jpeg_quality'); - if((! $quality) || ($quality > 100)) - $quality = JPEG_QUALITY; - $this->image->setCompressionQuality($quality); - } - } else { - $this->valid = false; - $this->image = @imagecreatefromstring($data); - if($this->image !== FALSE) { - $this->width = imagesx($this->image); - $this->height = imagesy($this->image); - $this->valid = true; - imagealphablending($this->image, false); - imagesavealpha($this->image, true); - } - } - } - - public function __destruct() { - if($this->image) { - if($this->is_imagick()) { - $this->image->clear(); - $this->image->destroy(); - return; - } - imagedestroy($this->image); - } - } - - public function is_imagick() { - return $this->imagick; - } - - /** - * Maps Mime types to Imagick formats - */ - public function get_FormatsMap() { - $m = array( - 'image/jpeg' => 'JPG', - 'image/png' => 'PNG', - 'image/gif' => 'GIF' - ); - return $m; - } - - public function is_valid() { - if($this->is_imagick()) - return ($this->image !== FALSE); - return $this->valid; - } - - public function getWidth() { - if(!$this->is_valid()) - return FALSE; - - if($this->is_imagick()) - return $this->image->getImageWidth(); - return $this->width; - } - - public function getHeight() { - if(!$this->is_valid()) - return FALSE; - - if($this->is_imagick()) - return $this->image->getImageHeight(); - return $this->height; - } - - public function getImage() { - if(!$this->is_valid()) - return FALSE; - - if($this->is_imagick()) { - /* Clean it */ - $this->image = $this->image->deconstructImages(); - return $this->image; - } - return $this->image; - } - - public function getType() { - if(!$this->is_valid()) - return FALSE; - - return $this->type; - } - - public function getExt() { - if(!$this->is_valid()) - return FALSE; - - return $this->types[$this->getType()]; - } - - public function scaleImage($max) { - if(!$this->is_valid()) - return FALSE; - - $width = $this->width; - $height = $this->height; - - $dest_width = $dest_height = 0; - - if((! $width)|| (! $height)) - return FALSE; - - if($width > $max && $height > $max) { - - // very tall image (greater than 16:9) - // constrain the width - let the height float. - - if((($height * 9) / 16) > $width) { - $dest_width = $max; - $dest_height = intval(( $height * $max ) / $width); - } - - // else constrain both dimensions - - elseif($width > $height) { - $dest_width = $max; - $dest_height = intval(( $height * $max ) / $width); - } - else { - $dest_width = intval(( $width * $max ) / $height); - $dest_height = $max; - } - } - else { - if( $width > $max ) { - $dest_width = $max; - $dest_height = intval(( $height * $max ) / $width); - } - else { - if( $height > $max ) { - - // very tall image (greater than 16:9) - // but width is OK - don't do anything - - if((($height * 9) / 16) > $width) { - $dest_width = $width; - $dest_height = $height; - } - else { - $dest_width = intval(( $width * $max ) / $height); - $dest_height = $max; - } - } - else { - $dest_width = $width; - $dest_height = $height; - } - } - } - - - if($this->is_imagick()) { - /** - * If it is not animated, there will be only one iteration here, - * so don't bother checking - */ - // Don't forget to go back to the first frame - $this->image->setFirstIterator(); - do { - - // FIXME - implement horizantal bias for scaling as in followin GD functions - // to allow very tall images to be constrained only horizontally. - - $this->image->scaleImage($dest_width, $dest_height); - } while ($this->image->nextImage()); - - // FIXME - also we need to copy the new dimensions to $this->height, $this->width as other functions - // may rely on it. - - return; - } - - - $dest = imagecreatetruecolor( $dest_width, $dest_height ); - imagealphablending($dest, false); - imagesavealpha($dest, true); - if ($this->type=='image/png') imagefill($dest, 0, 0, imagecolorallocatealpha($dest, 0, 0, 0, 127)); // fill with alpha - imagecopyresampled($dest, $this->image, 0, 0, 0, 0, $dest_width, $dest_height, $width, $height); - if($this->image) - imagedestroy($this->image); - $this->image = $dest; - $this->width = imagesx($this->image); - $this->height = imagesy($this->image); - } - - public function rotate($degrees) { - if(!$this->is_valid()) - return FALSE; - - if($this->is_imagick()) { - $this->image->setFirstIterator(); - do { - $this->image->rotateImage(new ImagickPixel(), -$degrees); // ImageMagick rotates in the opposite direction of imagerotate() - } while ($this->image->nextImage()); - return; - } - - $this->image = imagerotate($this->image,$degrees,0); - $this->width = imagesx($this->image); - $this->height = imagesy($this->image); - } - - public function flip($horiz = true, $vert = false) { - if(!$this->is_valid()) - return FALSE; - - if($this->is_imagick()) { - $this->image->setFirstIterator(); - do { - if($horiz) $this->image->flipImage(); - if($vert) $this->image->flopImage(); - } while ($this->image->nextImage()); - return; - } - - $w = imagesx($this->image); - $h = imagesy($this->image); - $flipped = imagecreate($w, $h); - if($horiz) { - for ($x = 0; $x < $w; $x++) { - imagecopy($flipped, $this->image, $x, 0, $w - $x - 1, 0, 1, $h); - } - } - if($vert) { - for ($y = 0; $y < $h; $y++) { - imagecopy($flipped, $this->image, 0, $y, 0, $h - $y - 1, $w, 1); - } - } - $this->image = $flipped; - } - - public function orient($filename) { - // based off comment on http://php.net/manual/en/function.imagerotate.php - - if(!$this->is_valid()) - return FALSE; - - if( (! function_exists('exif_read_data')) || ($this->getType() !== 'image/jpeg') ) - return; - - $exif = @exif_read_data($filename); - if($exif) { - $ort = $exif['Orientation']; - - switch($ort) - { - case 1: // nothing - break; - - case 2: // horizontal flip - $this->flip(); - break; - - case 3: // 180 rotate left - $this->rotate(180); - break; - - case 4: // vertical flip - $this->flip(false, true); - break; - - case 5: // vertical flip + 90 rotate right - $this->flip(false, true); - $this->rotate(-90); - break; - - case 6: // 90 rotate right - $this->rotate(-90); - break; - - case 7: // horizontal flip + 90 rotate right - $this->flip(); - $this->rotate(-90); - break; - - case 8: // 90 rotate left - $this->rotate(90); - break; - } - } - } - - - - public function scaleImageUp($min) { - if(!$this->is_valid()) - return FALSE; - - - $width = $this->width; - $height = $this->height; - - $dest_width = $dest_height = 0; - - if((! $width)|| (! $height)) - return FALSE; - - if($width < $min && $height < $min) { - if($width > $height) { - $dest_width = $min; - $dest_height = intval(( $height * $min ) / $width); - } - else { - $dest_width = intval(( $width * $min ) / $height); - $dest_height = $min; - } - } - else { - if( $width < $min ) { - $dest_width = $min; - $dest_height = intval(( $height * $min ) / $width); - } - else { - if( $height < $min ) { - $dest_width = intval(( $width * $min ) / $height); - $dest_height = $min; - } - else { - $dest_width = $width; - $dest_height = $height; - } - } - } - - if($this->is_imagick()) - return $this->scaleImage($dest_width,$dest_height); - - $dest = imagecreatetruecolor( $dest_width, $dest_height ); - imagealphablending($dest, false); - imagesavealpha($dest, true); - if ($this->type=='image/png') imagefill($dest, 0, 0, imagecolorallocatealpha($dest, 0, 0, 0, 127)); // fill with alpha - imagecopyresampled($dest, $this->image, 0, 0, 0, 0, $dest_width, $dest_height, $width, $height); - if($this->image) - imagedestroy($this->image); - $this->image = $dest; - $this->width = imagesx($this->image); - $this->height = imagesy($this->image); - } - - - - public function scaleImageSquare($dim) { - if(!$this->is_valid()) - return FALSE; - - if($this->is_imagick()) { - $this->image->setFirstIterator(); - do { - $this->image->scaleImage($dim, $dim); - } while ($this->image->nextImage()); - return; - } - - $dest = imagecreatetruecolor( $dim, $dim ); - imagealphablending($dest, false); - imagesavealpha($dest, true); - if ($this->type=='image/png') imagefill($dest, 0, 0, imagecolorallocatealpha($dest, 0, 0, 0, 127)); // fill with alpha - imagecopyresampled($dest, $this->image, 0, 0, 0, 0, $dim, $dim, $this->width, $this->height); - if($this->image) - imagedestroy($this->image); - $this->image = $dest; - $this->width = imagesx($this->image); - $this->height = imagesy($this->image); - } - - - public function cropImage($max,$x,$y,$w,$h) { - if(!$this->is_valid()) - return FALSE; - - if($this->is_imagick()) { - $this->image->setFirstIterator(); - do { - $this->image->cropImage($w, $h, $x, $y); - /** - * We need to remove the canva, - * or the image is not resized to the crop: - * http://php.net/manual/en/imagick.cropimage.php#97232 - */ - $this->image->setImagePage(0, 0, 0, 0); - } while ($this->image->nextImage()); - return $this->scaleImage($max); - } - - $dest = imagecreatetruecolor( $max, $max ); - imagealphablending($dest, false); - imagesavealpha($dest, true); - if ($this->type=='image/png') imagefill($dest, 0, 0, imagecolorallocatealpha($dest, 0, 0, 0, 127)); // fill with alpha - imagecopyresampled($dest, $this->image, 0, 0, $x, $y, $max, $max, $w, $h); - if($this->image) - imagedestroy($this->image); - $this->image = $dest; - $this->width = imagesx($this->image); - $this->height = imagesy($this->image); - } - - public function saveImage($path) { - if(!$this->is_valid()) - return FALSE; - - $string = $this->imageString(); - file_put_contents($path, $string); - } - - public function imageString() { - if(!$this->is_valid()) - return FALSE; - - if($this->is_imagick()) { - /* Clean it */ - $this->image = $this->image->deconstructImages(); - $string = $this->image->getImagesBlob(); - return $string; - } - - $quality = FALSE; - - ob_start(); - - switch($this->getType()){ - case "image/png": - $quality = get_config('system','png_quality'); - if((! $quality) || ($quality > 9)) - $quality = PNG_QUALITY; - imagepng($this->image,NULL, $quality); - break; - case "image/jpeg": - $quality = get_config('system','jpeg_quality'); - if((! $quality) || ($quality > 100)) - $quality = JPEG_QUALITY; - imagejpeg($this->image,NULL,$quality); - } - $string = ob_get_contents(); - ob_end_clean(); - - return $string; - } - - - - public function store($aid, $uid, $xchan, $rid, $filename, $album, $scale, $profile = 0, $allow_cid = '', $allow_gid = '', $deny_cid = '', $deny_gid = '') { - - $x = q("select id from photo where `resource_id` = '%s' and uid = %d and `xchan` = '%s' and `scale` = %d limit 1", - dbesc($rid), - intval($uid), - dbesc($xchan), - intval($scale) - ); - if(count($x)) { - $r = q("UPDATE `photo` - set `aid` = %d, - `uid` = %d, - `xchan` = '%s', - `resource_id` = '%s', - `created` = '%s', - `edited` = '%s', - `filename` = '%s', - `type` = '%s', - `album` = '%s', - `height` = %d, - `width` = %d, - `data` = '%s', - `size` = %d, - `scale` = %d, - `profile` = %d, - `allow_cid` = '%s', - `allow_gid` = '%s', - `deny_cid` = '%s', - `deny_gid` = '%s' - where id = %d limit 1", - - intval($aid), - intval($uid), - dbesc($xchan), - dbesc($rid), - dbesc(datetime_convert()), - dbesc(datetime_convert()), - dbesc(basename($filename)), - dbesc($this->getType()), - dbesc($album), - intval($this->getHeight()), - intval($this->getWidth()), - dbesc($this->imageString()), - intval(strlen($this->imageString())), - intval($scale), - intval($profile), - dbesc($allow_cid), - dbesc($allow_gid), - dbesc($deny_cid), - dbesc($deny_gid), - intval($x[0]['id']) - ); - } - else { - $r = q("INSERT INTO `photo` - ( `aid`, `uid`, `xchan`, `resource_id`, `created`, `edited`, `filename`, type, `album`, `height`, `width`, `data`, `size`, `scale`, `profile`, `allow_cid`, `allow_gid`, `deny_cid`, `deny_gid` ) - VALUES ( %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, '%s', %d, %d, %d, '%s', '%s', '%s', '%s' )", - intval($aid), - intval($uid), - dbesc($xchan), - dbesc($rid), - dbesc(datetime_convert()), - dbesc(datetime_convert()), - dbesc(basename($filename)), - dbesc($this->getType()), - dbesc($album), - intval($this->getHeight()), - intval($this->getWidth()), - dbesc($this->imageString()), - intval(strlen($this->imageString())), - intval($scale), - intval($profile), - dbesc($allow_cid), - dbesc($allow_gid), - dbesc($deny_cid), - dbesc($deny_gid) - ); - } - return $r; - } -}} - - -/** - * Guess image mimetype from filename or from Content-Type header - * - * @arg $filename string Image filename - * @arg $fromcurl boolean Check Content-Type header from curl request - */ -function guess_image_type($filename, $fromcurl=false) { - logger('Photo: guess_image_type: '.$filename . ($fromcurl?' from curl headers':''), LOGGER_DEBUG); - $type = null; - if ($fromcurl) { - $a = get_app(); - $headers=array(); - $h = explode("\n",$a->get_curl_headers()); - foreach ($h as $l) { - list($k,$v) = array_map("trim", explode(":", trim($l), 2)); - $headers[$k] = $v; - } - if (array_key_exists('Content-Type', $headers)) - $type = $headers['Content-Type']; - } - if (is_null($type)){ - // Guessing from extension? Isn't that... dangerous? - if(class_exists('Imagick') && file_exists($filename) && is_readable($filename)) { - /** - * Well, this not much better, - * but at least it comes from the data inside the image, - * we won't be tricked by a manipulated extension - */ - $image = new Imagick($filename); - $type = $image->getImageMimeType(); - } else { - $ext = pathinfo($filename, PATHINFO_EXTENSION); - $types = Photo::supportedTypes(); - $type = "image/jpeg"; - foreach ($types as $m=>$e){ - if ($ext==$e) $type = $m; - } - } - } - logger('Photo: guess_image_type: type='.$type, LOGGER_DEBUG); - return $type; - -} - -function import_profile_photo($photo,$xchan) { - - $a = get_app(); - - logger('import_profile_photo: updating channel photo from ' . $photo . ' for ' . $xchan, LOGGER_DEBUG); - - $r = q("select resource_id from photo where xchan = '%s' and scale = 4 limit 1", - dbesc($xchan) - ); - if($r) { - $hash = $r[0]['resource_id']; - } - else { - $hash = photo_new_resource(); - } - - $photo_failure = false; - - - $filename = basename($photo); - $img_str = fetch_url($photo,true); - - $type = guess_image_type($photo,true); - $img = new Photo($img_str, $type); - if($img->is_valid()) { - - $img->scaleImageSquare(175); - - $r = $img->store(0, 0, $xchan, $hash, $filename, 'Contact Photos', 4 ); - - if($r === false) - $photo_failure = true; - - $img->scaleImage(80); - - $r = $img->store(0, 0, $xchan, $hash, $filename, 'Contact Photos', 5 ); - - if($r === false) - $photo_failure = true; - - $img->scaleImage(48); - - $r = $img->store(0, 0, $xchan, $hash, $filename, 'Contact Photos', 6 ); - - if($r === false) - $photo_failure = true; - - $photo = $a->get_baseurl() . '/photo/' . $hash . '-4'; - $thumb = $a->get_baseurl() . '/photo/' . $hash . '-5'; - $micro = $a->get_baseurl() . '/photo/' . $hash . '-6'; - } - else { - logger('import_profile_photo: invalid image from ' . $photo); - $photo_failure = true; - } - if($photo_failure) { - $photo = $a->get_baseurl() . '/images/person-175.jpg'; - $thumb = $a->get_baseurl() . '/images/person-80.jpg'; - $micro = $a->get_baseurl() . '/images/person-48.jpg'; - $type = 'image/jpeg'; - } - - return(array($photo,$thumb,$micro,$type)); - -} - - - -function import_channel_photo($photo,$type,$aid,$uid) { - - $a = get_app(); - - logger('import_channel_photo: importing channel photo for ' . $uid, LOGGER_DEBUG); - - $hash = photo_new_resource(); - - $photo_failure = false; - - - $filename = $hash; - - $img = new Photo($photo, $type); - if($img->is_valid()) { - - $img->scaleImageSquare(175); - - $r = $img->store($aid,$uid,'', $hash, $filename, t('Profile Photos'), 4, true); - - if($r === false) - $photo_failure = true; - - $img->scaleImage(80); - - $r = $img->store($aid,$uid,'', $hash, $filename, t('Profile Photos'), 5, true); - - if($r === false) - $photo_failure = true; - - $img->scaleImage(48); - - $r = $img->store($aid,$uid,'', $hash, $filename, t('Profile Photos'), 6, true); - - if($r === false) - $photo_failure = true; - - } - else { - logger('import_channel_photo: invalid image.'); - $photo_failure = true; - } - - return(($photo_failure)? false : true); - -} diff --git a/include/ProtoDriver.php b/include/ProtoDriver.php index a39881e9a..7585a0135 100644 --- a/include/ProtoDriver.php +++ b/include/ProtoDriver.php @@ -1,4 +1,4 @@ -<?php +<?php /** @file */ /* * Abstraction class for dealing with alternate networks (which of course do not exist, hence the abstraction) diff --git a/include/Scrape.php b/include/Scrape.php deleted file mode 100644 index 806106ef1..000000000 --- a/include/Scrape.php +++ /dev/null @@ -1,712 +0,0 @@ -<?php - -require_once('library/HTML5/Parser.php'); -require_once('include/crypto.php'); - -if(! function_exists('scrape_dfrn')) { -function scrape_dfrn($url) { - - $a = get_app(); - - $ret = array(); - - logger('scrape_dfrn: url=' . $url); - - $s = fetch_url($url); - - if(! $s) - return $ret; - - $headers = $a->get_curl_headers(); - logger('scrape_dfrn: headers=' . $headers, LOGGER_DEBUG); - - - $lines = explode("\n",$headers); - if(count($lines)) { - foreach($lines as $line) { - // don't try and run feeds through the html5 parser - if(stristr($line,'content-type:') && ((stristr($line,'application/atom+xml')) || (stristr($line,'application/rss+xml')))) - return ret; - } - } - - try { - $dom = HTML5_Parser::parse($s); - } catch (DOMException $e) { - logger('scrape_dfrn: parse error: ' . $e); - } - - if(! $dom) - return $ret; - - $items = $dom->getElementsByTagName('link'); - - // get DFRN link elements - - foreach($items as $item) { - $x = $item->getAttribute('rel'); - if(($x === 'alternate') && ($item->getAttribute('type') === 'application/atom+xml')) - $ret['feed_atom'] = $item->getAttribute('href'); - if(substr($x,0,5) == "dfrn-") { - $ret[$x] = $item->getAttribute('href'); - } - if($x === 'lrdd') { - $decoded = urldecode($item->getAttribute('href')); - if(preg_match('/acct:([^@]*)@/',$decoded,$matches)) - $ret['nick'] = $matches[1]; - } - } - - // Pull out hCard profile elements - - $largest_photo = 0; - - $items = $dom->getElementsByTagName('*'); - foreach($items as $item) { - if(attribute_contains($item->getAttribute('class'), 'vcard')) { - $level2 = $item->getElementsByTagName('*'); - foreach($level2 as $x) { - if(attribute_contains($x->getAttribute('class'),'fn')) { - $ret['fn'] = $x->textContent; - } - if((attribute_contains($x->getAttribute('class'),'photo')) - || (attribute_contains($x->getAttribute('class'),'avatar'))) { - $size = intval($x->getAttribute('width')); - // dfrn prefers 175, so if we find this, we set largest_size so it can't be topped. - if(($size > $largest_photo) || ($size == 175) || (! $largest_photo)) { - $ret['photo'] = $x->getAttribute('src'); - $largest_photo = (($size == 175) ? 9999 : $size); - } - } - if(attribute_contains($x->getAttribute('class'),'key')) { - $ret['key'] = $x->textContent; - } - } - } - } - - return $ret; -}} - - - - - - -if(! function_exists('validate_dfrn')) { -function validate_dfrn($a) { - $errors = 0; - if(! x($a,'key')) - $errors ++; - if(! x($a,'dfrn-request')) - $errors ++; - if(! x($a,'dfrn-confirm')) - $errors ++; - if(! x($a,'dfrn-notify')) - $errors ++; - if(! x($a,'dfrn-poll')) - $errors ++; - return $errors; -}} - -if(! function_exists('scrape_meta')) { -function scrape_meta($url) { - - $a = get_app(); - - $ret = array(); - - logger('scrape_meta: url=' . $url); - - $s = fetch_url($url); - - if(! $s) - return $ret; - - $headers = $a->get_curl_headers(); - logger('scrape_meta: headers=' . $headers, LOGGER_DEBUG); - - $lines = explode("\n",$headers); - if(count($lines)) { - foreach($lines as $line) { - // don't try and run feeds through the html5 parser - if(stristr($line,'content-type:') && ((stristr($line,'application/atom+xml')) || (stristr($line,'application/rss+xml')))) - return ret; - } - } - - try { - $dom = HTML5_Parser::parse($s); - } catch (DOMException $e) { - logger('scrape_meta: parse error: ' . $e); - } - - if(! $dom) - return $ret; - - $items = $dom->getElementsByTagName('meta'); - - // get DFRN link elements - - foreach($items as $item) { - $x = $item->getAttribute('name'); - if(substr($x,0,5) == "dfrn-") - $ret[$x] = $item->getAttribute('content'); - } - - return $ret; -}} - - -if(! function_exists('scrape_vcard')) { -function scrape_vcard($url) { - - $a = get_app(); - - $ret = array(); - - logger('scrape_vcard: url=' . $url); - - $s = fetch_url($url); - - if(! $s) - return $ret; - - $headers = $a->get_curl_headers(); - $lines = explode("\n",$headers); - if(count($lines)) { - foreach($lines as $line) { - // don't try and run feeds through the html5 parser - if(stristr($line,'content-type:') && ((stristr($line,'application/atom+xml')) || (stristr($line,'application/rss+xml')))) - return ret; - } - } - - try { - $dom = HTML5_Parser::parse($s); - } catch (DOMException $e) { - logger('scrape_vcard: parse error: ' . $e); - } - - if(! $dom) - return $ret; - - // Pull out hCard profile elements - - $largest_photo = 0; - - $items = $dom->getElementsByTagName('*'); - foreach($items as $item) { - if(attribute_contains($item->getAttribute('class'), 'vcard')) { - $level2 = $item->getElementsByTagName('*'); - foreach($level2 as $x) { - if(attribute_contains($x->getAttribute('class'),'fn')) - $ret['fn'] = $x->textContent; - if((attribute_contains($x->getAttribute('class'),'photo')) - || (attribute_contains($x->getAttribute('class'),'avatar'))) { - $size = intval($x->getAttribute('width')); - if(($size > $largest_photo) || (! $largest_photo)) { - $ret['photo'] = $x->getAttribute('src'); - $largest_photo = $size; - } - } - if((attribute_contains($x->getAttribute('class'),'nickname')) - || (attribute_contains($x->getAttribute('class'),'uid'))) { - $ret['nick'] = $x->textContent; - } - } - } - } - - return $ret; -}} - - -if(! function_exists('scrape_feed')) { -function scrape_feed($url) { - - $a = get_app(); - - $ret = array(); - $s = fetch_url($url); - - $headers = $a->get_curl_headers(); - $code = $a->get_curl_code(); - - logger('scrape_feed: returns: ' . $code . ' headers=' . $headers, LOGGER_DEBUG); - - if(! $s) { - logger('scrape_feed: no data returned for ' . $url); - return $ret; - } - - - $lines = explode("\n",$headers); - if(count($lines)) { - foreach($lines as $line) { - if(stristr($line,'content-type:')) { - if(stristr($line,'application/atom+xml') || stristr($s,'<feed')) { - $ret['feed_atom'] = $url; - return $ret; - } - if(stristr($line,'application/rss+xml') || stristr($s,'<rss')) { - $ret['feed_rss'] = $url; - return $ret; - } - } - } - // perhaps an RSS version 1 feed with a generic or incorrect content-type? - if(stristr($s,'</item>')) { - $ret['feed_rss'] = $url; - return $ret; - } - } - - try { - $dom = HTML5_Parser::parse($s); - } catch (DOMException $e) { - logger('scrape_feed: parse error: ' . $e); - } - - if(! $dom) { - logger('scrape_feed: failed to parse.'); - return $ret; - } - - - $head = $dom->getElementsByTagName('base'); - if($head) { - foreach($head as $head0) { - $basename = $head0->getAttribute('href'); - break; - } - } - if(! $basename) - $basename = implode('/', array_slice(explode('/',$url),0,3)) . '/'; - - $items = $dom->getElementsByTagName('link'); - - // get Atom/RSS link elements, take the first one of either. - - if($items) { - foreach($items as $item) { - $x = $item->getAttribute('rel'); - if(($x === 'alternate') && ($item->getAttribute('type') === 'application/atom+xml')) { - if(! x($ret,'feed_atom')) - $ret['feed_atom'] = $item->getAttribute('href'); - } - if(($x === 'alternate') && ($item->getAttribute('type') === 'application/rss+xml')) { - if(! x($ret,'feed_rss')) - $ret['feed_rss'] = $item->getAttribute('href'); - } - } - } - - // Drupal and perhaps others only provide relative URL's. Turn them into absolute. - - if(x($ret,'feed_atom') && (! strstr($ret['feed_atom'],'://'))) - $ret['feed_atom'] = $basename . $ret['feed_atom']; - if(x($ret,'feed_rss') && (! strstr($ret['feed_rss'],'://'))) - $ret['feed_rss'] = $basename . $ret['feed_rss']; - - return $ret; -}} - - -/** - * - * Probe a network address to discover what kind of protocols we need to communicate with it. - * - * Warning: this function is a bit touchy and there are some subtle dependencies within the logic flow. - * Edit with care. - * - */ - -/** - * - * PROBE_DIASPORA has a bias towards returning Diaspora information - * while PROBE_NORMAL has a bias towards dfrn/zot - in the case where - * an address (such as a Friendica address) supports more than one type - * of network. - * - */ - - -define ( 'PROBE_NORMAL', 0); - -function probe_url($url, $mode = PROBE_NORMAL) { - require_once('include/email.php'); - - $result = array(); - - if(! $url) - return $result; - - $network = null; - $has_lrdd = false; - $email_conversant = false; - - $twitter = ((strpos($url,'twitter.com') !== false) ? true : false); - $lastfm = ((strpos($url,'last.fm/user') !== false) ? true : false); - - $at_addr = ((strpos($url,'@') !== false) ? true : false); - - if((! $twitter) && (! $lastfm)) { - - if(strpos($url,'mailto:') !== false && $at_addr) { - $url = str_replace('mailto:','',$url); - $links = array(); - } - else - $links = lrdd($url); - - if(count($links)) { - $has_lrdd = true; - - logger('probe_url: found lrdd links: ' . print_r($links,true), LOGGER_DATA); - foreach($links as $link) { - if($link['@attributes']['rel'] === NAMESPACE_ZOT) - $zot = unamp($link['@attributes']['href']); - if($link['@attributes']['rel'] === NAMESPACE_DFRN) - $dfrn = unamp($link['@attributes']['href']); - if($link['@attributes']['rel'] === 'salmon') - $notify = unamp($link['@attributes']['href']); - if($link['@attributes']['rel'] === NAMESPACE_FEED) - $poll = unamp($link['@attributes']['href']); - if($link['@attributes']['rel'] === 'http://microformats.org/profile/hcard') - $hcard = unamp($link['@attributes']['href']); - if($link['@attributes']['rel'] === 'http://webfinger.net/rel/profile-page') - $profile = unamp($link['@attributes']['href']); - if($link['@attributes']['rel'] === 'http://portablecontacts.net/spec/1.0') - $poco = unamp($link['@attributes']['href']); - - } - - // Status.Net can have more than one profile URL. We need to match the profile URL - // to a contact on incoming messages to prevent spam, and we won't know which one - // to match. So in case of two, one of them is stored as an alias. Only store URL's - // and not webfinger user@host aliases. If they've got more than two non-email style - // aliases, let's hope we're lucky and get one that matches the feed author-uri because - // otherwise we're screwed. - - foreach($links as $link) { - if($link['@attributes']['rel'] === 'alias') { - if(strpos($link['@attributes']['href'],'@') === false) { - if(isset($profile)) { - if($link['@attributes']['href'] !== $profile) - $alias = unamp($link['@attributes']['href']); - } - else - $profile = unamp($link['@attributes']['href']); - } - } - } - } - elseif($mode == PROBE_NORMAL) { - - // Check email - - $orig_url = $url; - if((strpos($orig_url,'@')) && validate_email($orig_url)) { - $x = q("SELECT `prvkey` FROM `user` WHERE `uid` = %d LIMIT 1", - intval(local_user()) - ); - $r = q("SELECT * FROM `mailacct` WHERE `uid` = %d AND `server` != '' LIMIT 1", - intval(local_user()) - ); - if(count($x) && count($r)) { - $mailbox = construct_mailbox_name($r[0]); - $password = ''; - openssl_private_decrypt(hex2bin($r[0]['pass']),$password,$x[0]['prvkey']); - $mbox = email_connect($mailbox,$r[0]['user'],$password); - if(! $mbox) - logger('probe_url: email_connect failed.'); - unset($password); - } - if($mbox) { - $msgs = email_poll($mbox,$orig_url); - logger('probe_url: searching ' . $orig_url . ', ' . count($msgs) . ' messages found.', LOGGER_DEBUG); - if(count($msgs)) { - $addr = $orig_url; - $network = NETWORK_MAIL; - $name = substr($url,0,strpos($url,'@')); - $phost = substr($url,strpos($url,'@')+1); - $profile = 'http://' . $phost; - // fix nick character range - $vcard = array('fn' => $name, 'nick' => $name, 'photo' => avatar_img($url)); - $notify = 'smtp ' . random_string(); - $poll = 'email ' . random_string(); - $priority = 0; - $x = email_msg_meta($mbox,$msgs[0]); - if(stristr($x[0]->from,$orig_url)) - $adr = imap_rfc822_parse_adrlist($x[0]->from,''); - elseif(stristr($x[0]->to,$orig_url)) - $adr = imap_rfc822_parse_adrlist($x[0]->to,''); - if(isset($adr)) { - foreach($adr as $feadr) { - if((strcasecmp($feadr->mailbox,$name) == 0) - &&(strcasecmp($feadr->host,$phost) == 0) - && (strlen($feadr->personal))) { - - $personal = imap_mime_header_decode($feadr->personal); - $vcard['fn'] = ""; - foreach($personal as $perspart) - if ($perspart->charset != "default") - $vcard['fn'] .= iconv($perspart->charset, 'UTF-8//IGNORE', $perspart->text); - else - $vcard['fn'] .= $perspart->text; - - $vcard['fn'] = notags($vcard['fn']); - } - } - } - } - imap_close($mbox); - } - } - } - } - - if($mode == PROBE_NORMAL) { - if(strlen($zot)) { - $s = fetch_url($zot); - if($s) { - $j = json_decode($s); - if($j) { - $network = NETWORK_ZOT; - $vcard = array( - 'fn' => $j->fullname, - 'nick' => $j->nickname, - 'photo' => $j->photo - ); - $profile = $j->url; - $notify = $j->post; - $pubkey = $j->pubkey; - $poll = 'N/A'; - } - } - } - - if(strlen($dfrn)) { - $ret = scrape_dfrn(($hcard) ? $hcard : $dfrn); - if(is_array($ret) && x($ret,'dfrn-request')) { - $network = NETWORK_DFRN; - $request = $ret['dfrn-request']; - $confirm = $ret['dfrn-confirm']; - $notify = $ret['dfrn-notify']; - $poll = $ret['dfrn-poll']; - - $vcard = array(); - $vcard['fn'] = $ret['fn']; - $vcard['nick'] = $ret['nick']; - $vcard['photo'] = $ret['photo']; - } - } - } - - if($network !== NETWORK_ZOT && $network !== NETWORK_DFRN && $network !== NETWORK_MAIL) { - if($has_lrdd) - $network = NETWORK_OSTATUS; - $priority = 0; - - if($hcard && ! $vcard) { - $vcard = scrape_vcard($hcard); - - // Google doesn't use absolute url in profile photos - - if((x($vcard,'photo')) && substr($vcard['photo'],0,1) == '/') { - $h = @parse_url($hcard); - if($h) - $vcard['photo'] = $h['scheme'] . '://' . $h['host'] . $vcard['photo']; - } - - logger('probe_url: scrape_vcard: ' . print_r($vcard,true), LOGGER_DATA); - } - - if($diaspora && $addr) { - // Diaspora returns the name as the nick. As the nick will never be updated, - // let's use the Diaspora nickname (the first part of the handle) as the nick instead - $addr_parts = explode('@', $addr); - $vcard['nick'] = $addr_parts[0]; - } - - if($twitter) { - logger('twitter: setup'); - $tid = basename($url); - $tapi = 'https://api.twitter.com/1/statuses/user_timeline.rss'; - if(intval($tid)) - $poll = $tapi . '?user_id=' . $tid; - else - $poll = $tapi . '?screen_name=' . $tid; - $profile = 'http://twitter.com/#!/' . $tid; - //$vcard['photo'] = 'https://api.twitter.com/1/users/profile_image/' . $tid; - $vcard['photo'] = 'https://api.twitter.com/1/users/profile_image?screen_name=' . $tid . '&size=bigger'; - $vcard['nick'] = $tid; - $vcard['fn'] = $tid; - } - - if($lastfm) { - $profile = $url; - $poll = str_replace(array('www.','last.fm/'),array('','ws.audioscrobbler.com/1.0/'),$url) . '/recenttracks.rss'; - $vcard['nick'] = basename($url); - $vcard['fn'] = $vcard['nick'] . t(' on Last.fm'); - $network = NETWORK_FEED; - } - - if(! x($vcard,'fn')) - if(x($vcard,'nick')) - $vcard['fn'] = $vcard['nick']; - - $check_feed = false; - - if($twitter || ! $poll) - $check_feed = true; - if((! isset($vcard)) || (! x($vcard,'fn')) || (! $profile)) - $check_feed = true; - if(($at_addr) && (! count($links))) - $check_feed = false; - - if($check_feed) { - - $feedret = scrape_feed(($poll) ? $poll : $url); - logger('probe_url: scrape_feed ' . (($poll)? $poll : $url) . ' returns: ' . print_r($feedret,true), LOGGER_DATA); - if(count($feedret) && ($feedret['feed_atom'] || $feedret['feed_rss'])) { - $poll = ((x($feedret,'feed_atom')) ? unamp($feedret['feed_atom']) : unamp($feedret['feed_rss'])); - if(! x($vcard)) - $vcard = array(); - } - - if(x($feedret,'photo') && (! x($vcard,'photo'))) - $vcard['photo'] = $feedret['photo']; - require_once('library/simplepie/simplepie.inc'); - $feed = new SimplePie(); - $xml = fetch_url($poll); - - logger('probe_url: fetch feed: ' . $poll . ' returns: ' . $xml, LOGGER_DATA); - $a = get_app(); - - logger('probe_url: scrape_feed: headers: ' . $a->get_curl_headers(), LOGGER_DATA); - - $feed->set_raw_data($xml); - - $feed->init(); - if($feed->error()) - logger('probe_url: scrape_feed: Error parsing XML: ' . $feed->error()); - - - if(! x($vcard,'photo')) - $vcard['photo'] = $feed->get_image_url(); - $author = $feed->get_author(); - - if($author) { - $vcard['fn'] = unxmlify(trim($author->get_name())); - if(! $vcard['fn']) - $vcard['fn'] = trim(unxmlify($author->get_email())); - if(strpos($vcard['fn'],'@') !== false) - $vcard['fn'] = substr($vcard['fn'],0,strpos($vcard['fn'],'@')); - $email = unxmlify($author->get_email()); - if(! $profile && $author->get_link()) - $profile = trim(unxmlify($author->get_link())); - if(! $vcard['photo']) { - $rawtags = $feed->get_feed_tags( SIMPLEPIE_NAMESPACE_ATOM_10, 'author'); - if($rawtags) { - $elems = $rawtags[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]; - if((x($elems,'link')) && ($elems['link'][0]['attribs']['']['rel'] === 'photo')) - $vcard['photo'] = $elems['link'][0]['attribs']['']['href']; - } - } - } - else { - $item = $feed->get_item(0); - if($item) { - $author = $item->get_author(); - if($author) { - $vcard['fn'] = trim(unxmlify($author->get_name())); - if(! $vcard['fn']) - $vcard['fn'] = trim(unxmlify($author->get_email())); - if(strpos($vcard['fn'],'@') !== false) - $vcard['fn'] = substr($vcard['fn'],0,strpos($vcard['fn'],'@')); - $email = unxmlify($author->get_email()); - if(! $profile && $author->get_link()) - $profile = trim(unxmlify($author->get_link())); - } - if(! $vcard['photo']) { - $rawmedia = $item->get_item_tags('http://search.yahoo.com/mrss/','thumbnail'); - if($rawmedia && $rawmedia[0]['attribs']['']['url']) - $vcard['photo'] = unxmlify($rawmedia[0]['attribs']['']['url']); - } - if(! $vcard['photo']) { - $rawtags = $item->get_item_tags( SIMPLEPIE_NAMESPACE_ATOM_10, 'author'); - if($rawtags) { - $elems = $rawtags[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]; - if((x($elems,'link')) && ($elems['link'][0]['attribs']['']['rel'] === 'photo')) - $vcard['photo'] = $elems['link'][0]['attribs']['']['href']; - } - } - } - } - - if((! $vcard['photo']) && strlen($email)) - $vcard['photo'] = avatar_img($email); - if($poll === $profile) - $lnk = $feed->get_permalink(); - if(isset($lnk) && strlen($lnk)) - $profile = $lnk; - - if(! (x($vcard,'fn'))) - $vcard['fn'] = notags($feed->get_title()); - if(! (x($vcard,'fn'))) - $vcard['fn'] = notags($feed->get_description()); - - if(strpos($vcard['fn'],'Twitter / ') !== false) { - $vcard['fn'] = substr($vcard['fn'],strpos($vcard['fn'],'/')+1); - $vcard['fn'] = trim($vcard['fn']); - } - if(! x($vcard,'nick')) { - $vcard['nick'] = strtolower(notags(unxmlify($vcard['fn']))); - if(strpos($vcard['nick'],' ')) - $vcard['nick'] = trim(substr($vcard['nick'],0,strpos($vcard['nick'],' '))); - } - if(! $network) - $network = NETWORK_FEED; - if(! $priority) - $priority = 2; - } - } - - if(! x($vcard,'photo')) { - $a = get_app(); - $vcard['photo'] = $a->get_baseurl() . '/images/person-175.jpg' ; - } - - if(! $profile) - $profile = $url; - - // No human could be associated with this link, use the URL as the contact name - - if(($network === NETWORK_FEED) && ($poll) && (! x($vcard,'fn'))) - $vcard['fn'] = $url; - - $vcard['fn'] = notags($vcard['fn']); - $vcard['nick'] = str_replace(' ','',notags($vcard['nick'])); - - $result['name'] = $vcard['fn']; - $result['nick'] = $vcard['nick']; - $result['url'] = $profile; - $result['addr'] = $addr; - $result['batch'] = $batch; - $result['notify'] = $notify; - $result['poll'] = $poll; - $result['request'] = $request; - $result['confirm'] = $confirm; - $result['poco'] = $poco; - $result['photo'] = $vcard['photo']; - $result['priority'] = $priority; - $result['network'] = $network; - $result['alias'] = $alias; - $result['pubkey'] = $pubkey; - - logger('probe_url: ' . print_r($result,true), LOGGER_DEBUG); - - return $result; -} diff --git a/include/account.php b/include/account.php index a31dbba4b..b7fd3ef28 100644 --- a/include/account.php +++ b/include/account.php @@ -1,4 +1,4 @@ -<?php +<?php /** @file */ require_once('include/config.php'); require_once('include/network.php'); @@ -26,7 +26,7 @@ function check_account_email($email) { $r = q("select account_email from account where account_email = '%s' limit 1", dbesc($email) ); - if(count($r)) { + if($r) { $result['message'] .= t('Your email address is already registered at this site.'); } } @@ -80,14 +80,14 @@ function check_account_invite($invite_code) { function check_account_admin($arr) { if(is_site_admin()) return true; - $admin_mail = trim(get_config('system','admin_email')); + $admin_email = trim(get_config('system','admin_email')); if(strlen($admin_email) && $admin_email === trim($arr['email'])) return true; return false; } function account_total() { - $r = q("select account_id from account where 1"); + $r = q("select account_id from account where true"); if(is_array($r)) return count($r); return false; @@ -107,7 +107,8 @@ function create_account($arr) { $parent = ((x($arr,'parent')) ? intval($arr['parent']) : 0 ); $flags = ((x($arr,'account_flags')) ? intval($arr['account_flags']) : ACCOUNT_OK); $roles = ((x($arr,'account_roles')) ? intval($arr['account_roles']) : 0 ); - + $expires = ((x($arr,'expires')) ? intval($arr['expires']) : '0000-00-00 00:00:00'); + $default_service_class = get_config('system','default_service_class'); if($default_service_class === false) $default_service_class = ''; @@ -129,7 +130,7 @@ function create_account($arr) { // allow the admin_email account to be admin, but only if it's the first account. $c = account_total(); - if((c === 0) && (check_account_admin($arr))) + if(($c === 0) && (check_account_admin($arr))) $roles |= ACCOUNT_ROLE_ADMIN; @@ -281,7 +282,7 @@ function send_reg_approval_email($arr) { function send_verification_email($email,$password) { $email_msg = replace_macros(get_intltext_template('register_open_eml.tpl'), array( - '$sitename' => get_config('config','sitename'), + '$sitename' => get_config('system','sitename'), '$siteurl' => z_root(), '$email' => $email, '$password' => t('your registration password'), diff --git a/include/acl_selectors.php b/include/acl_selectors.php index f4dacb74a..033186151 100644 --- a/include/acl_selectors.php +++ b/include/acl_selectors.php @@ -1,4 +1,4 @@ -<?php +<?php /** @file */ /** * */ @@ -229,41 +229,6 @@ function populate_acl($user = null,$celeb = false) { array_walk($deny_cid,'fixacl'); array_walk($deny_gid,'fixacl'); } - - /*$o = ''; - $o .= '<div id="acl-wrapper">'; - $o .= '<div id="acl-permit-outer-wrapper">'; - $o .= '<div id="acl-permit-text">' . t('Visible To:') . '</div><div id="jot-public">' . t('everybody') . '</div>'; - $o .= '<div id="acl-permit-text-end"></div>'; - $o .= '<div id="acl-permit-wrapper">'; - $o .= '<div id="group_allow_wrapper">'; - $o .= '<label id="acl-allow-group-label" for="group_allow" >' . t('Groups') . '</label>'; - $o .= group_select('group_allow','group_allow',$allow_gid); - $o .= '</div>'; - $o .= '<div id="contact_allow_wrapper">'; - $o .= '<label id="acl-allow-contact-label" for="contact_allow" >' . t('Contacts') . '</label>'; - $o .= contact_select('contact_allow','contact_allow',$allow_cid,4,false,$celeb,true); - $o .= '</div>'; - $o .= '</div>' . "\r\n"; - $o .= '<div id="acl-allow-end"></div>' . "\r\n"; - $o .= '</div>'; - $o .= '<div id="acl-deny-outer-wrapper">'; - $o .= '<div id="acl-deny-text">' . t('Except For:') . '</div>'; - $o .= '<div id="acl-deny-text-end"></div>'; - $o .= '<div id="acl-deny-wrapper">'; - $o .= '<div id="group_deny_wrapper" >'; - $o .= '<label id="acl-deny-group-label" for="group_deny" >' . t('Groups') . '</label>'; - $o .= group_select('group_deny','group_deny', $deny_gid); - $o .= '</div>'; - $o .= '<div id="contact_deny_wrapper" >'; - $o .= '<label id="acl-deny-contact-label" for="contact_deny" >' . t('Contacts') . '</label>'; - $o .= contact_select('contact_deny','contact_deny', $deny_cid,4,false, $celeb,true); - $o .= '</div>'; - $o .= '</div>' . "\r\n"; - $o .= '<div id="acl-deny-end"></div>' . "\r\n"; - $o .= '</div>'; - $o .= '</div>' . "\r\n"; - $o .= '<div id="acl-wrapper-end"></div>' . "\r\n";*/ $tpl = get_markup_template("acl_selector.tpl"); $o = replace_macros($tpl, array( diff --git a/include/activities.php b/include/activities.php index ced9f3d18..10a01792f 100644 --- a/include/activities.php +++ b/include/activities.php @@ -1,4 +1,4 @@ -<?php +<?php /** @file */ function profile_activity($changed, $value) { $a = get_app(); @@ -17,7 +17,7 @@ function profile_activity($changed, $value) { return; $arr = array(); - $arr['uri'] = $arr['parent_uri'] = item_message_id(); + $arr['mid'] = $arr['parent_mid'] = item_message_id(); $arr['uid'] = local_user(); $arr['aid'] = $self['channel_account_id']; $arr['owner_xchan'] = $arr['author_xchan'] = $self['xchan_hash']; diff --git a/include/api.php b/include/api.php index 4d74eb298..a49258d18 100644 --- a/include/api.php +++ b/include/api.php @@ -1,4 +1,4 @@ -<?php +<?php /** @file */ require_once("bbcode.php"); require_once("datetime.php"); @@ -6,6 +6,7 @@ require_once("conversation.php"); require_once("oauth.php"); require_once("html2plain.php"); require_once('include/security.php'); +require_once('include/photos.php'); /* * @@ -96,6 +97,16 @@ require_once('include/security.php'); } } + if(x($_SERVER,'HTTP_AUTHORIZATION')) { + $userpass = base64_decode(substr($_SERVER["HTTP_AUTHORIZATION"],6)) ; + if(strlen($userpass)) { + list($name, $password) = explode(':', $userpass); + $_SERVER['PHP_AUTH_USER'] = $name; + $_SERVER['PHP_AUTH_PW'] = $password; + } + } + + if (!isset($_SERVER['PHP_AUTH_USER'])) { logger('API_login: ' . print_r($_SERVER,true), LOGGER_DEBUG); header('WWW-Authenticate: Basic realm="Red"'); @@ -220,7 +231,7 @@ require_once('include/security.php'); 'updated' => api_date(null), 'atom_updated' => datetime_convert('UTC','UTC','now',ATOM_TIME), 'language' => $user_info['language'], - 'logo' => $a->get_baseurl()."/images/friendica-32.png", + 'logo' => $a->get_baseurl()."/images/rhash-64.png", ); return $arr; @@ -515,6 +526,40 @@ require_once('include/security.php'); json_return_and_die(identity_basic_export(api_user())); } api_register_func('api/export/basic','api_export_basic', true); + api_register_func('api/red/channel/export/basic','api_export_basic', true); + + + function api_channel_stream(&$a, $type) { + if(api_user() === false) { + logger('api_channel_stream: no user'); + return false; + } + + if($_SERVER['REQUEST_METHOD'] == 'POST') { + json_return_and_die(post_activity_item($_REQUEST)); + } + else { + // fetch stream + + } + } + api_register_func('api/red/channel/stream','api_channel_stream', true); + + + function api_albums(&$a,$type) { + json_return_and_die(photos_albums_list($a->get_channel(),$a->get_observer())); + } + api_register_func('api/red/albums','api_albums', true); + + function api_photos(&$a,$type) { + $album = $_REQUEST['album']; + json_return_and_die(photos_list_photos($a->get_channel(),$a->get_observer()),$album); + } + api_register_func('api/red/photos','api_photos', true); + + + + @@ -565,6 +610,15 @@ require_once('include/security.php'); return false; } + logger('api_statuses_update: REQUEST ' . print_r($_REQUEST,true)); + logger('api_statuses_update: FILES ' . print_r($_FILES,true)); + + + // set this so that the item_post() function is quiet and doesn't redirect or emit json + + $_REQUEST['api_source'] = true; + + $user_info = api_get_user($a); // convert $_POST array items to the form we use for web posts. @@ -599,7 +653,7 @@ require_once('include/security.php'); if(ctype_digit($parent)) $_REQUEST['parent'] = $parent; else - $_REQUEST['parent_uri'] = $parent; + $_REQUEST['parent_mid'] = $parent; if(requestdata('lat') && requestdata('long')) $_REQUEST['coord'] = sprintf("%s %s",requestdata('lat'),requestdata('long')); @@ -610,7 +664,9 @@ require_once('include/security.php'); $_REQUEST['type'] = 'net-comment'; else { $_REQUEST['type'] = 'wall'; + if(x($_FILES,'media')) { + $_FILES['userfile'] = $_FILES['media']; // upload the image if we have one $_REQUEST['silent']='1'; //tell wall_upload function to return img info instead of echo require_once('mod/wall_upload.php'); @@ -620,9 +676,6 @@ require_once('include/security.php'); } } - // set this so that the item_post() function is quiet and doesn't redirect or emit json - - $_REQUEST['api_source'] = true; // call out normal post function @@ -892,7 +945,7 @@ require_once('include/security.php'); and item_private = 0 and uid in ( " . stream_perms_api_uids() . " ) $sql_extra - AND id > %d group by uri + AND id > %d group by mid order by received desc LIMIT %d, %d ", intval($since_id), intval($start), @@ -991,7 +1044,7 @@ require_once('include/security.php'); if(perm_is_allowed($r[0]['uid'],$observer['xchan_hash'],'view_stream')) { if ($r[0]['body'] != "") { - $_REQUEST['body'] = html_entity_decode("♲ ", ENT_QUOTES, 'UTF-8')."[url=".$r[0]['reply_url']."]".$r[0]['reply_author']."[/url] \n".$r[0]['body']; + $_REQUEST['body'] = html_entity_decode("♲ ", ENT_QUOTES, 'UTF-8')."[zrl=".$r[0]['reply_url']."]".$r[0]['reply_author']."[/zrl] \n".$r[0]['body']; $_REQUEST['profile_uid'] = api_user(); $_REQUEST['type'] = 'wall'; $_REQUEST['api_source'] = true; @@ -1404,7 +1457,7 @@ require_once('include/security.php'); $status2 = array( 'updated' => api_date($item['edited']), 'published' => api_date($item['created']), - 'message_id' => $item['uri'], + 'message_id' => $item['mid'], 'url' => $item['plink'], 'coordinates' => $item['coord'], 'place' => $item['location'], @@ -1452,7 +1505,7 @@ require_once('include/security.php'); return api_apply_template('test', $type, array('$ok' => $ok)); } - api_register_func('api/help/test','api_help_test',true); + api_register_func('api/help/test','api_help_test',false); /** * https://dev.twitter.com/docs/api/1/get/statuses/friends @@ -1483,17 +1536,17 @@ require_once('include/security.php'); // For Red, the closest thing we can do to figure out if you're friends is if both of you are sending each other your streams. // This won't work if either of you send your stream to everybody on the network if($qtype == 'friends') - $sql_extra = sprintf(" AND ( their_perms & %d ) and ( my_perms & %d ) ", intval(PERMS_W_STREAM), intval(PERMS_W_STREAM)); + $sql_extra = sprintf(" AND ( abook_their_perms & %d ) and ( abook_my_perms & %d ) ", intval(PERMS_W_STREAM), intval(PERMS_W_STREAM)); if($qtype == 'followers') - $sql_extra = sprintf(" AND ( my_perms & %d ) and not ( their_perms & %d ) ", intval(PERMS_W_STREAM), intval(PERMS_W_STREAM)); + $sql_extra = sprintf(" AND ( abook_my_perms & %d ) and not ( abook_their_perms & %d ) ", intval(PERMS_W_STREAM), intval(PERMS_W_STREAM)); - $r = q("SELECT id FROM abook where abook_flags = 0 and abook_channel = %d $sql_extra", + $r = q("SELECT abook_id FROM abook where abook_flags = 0 and abook_channel = %d $sql_extra", intval(api_user()) ); $ret = array(); foreach($r as $cid){ - $ret[] = api_get_user($a, $cid['id']); + $ret[] = api_get_user($a, $cid['abook_id']); } @@ -1524,7 +1577,7 @@ require_once('include/security.php'); $name = get_config('system','sitename'); $server = $a->get_hostname(); - $logo = $a->get_baseurl() . '/images/fred-64.png'; + $logo = $a->get_baseurl() . '/images/rhash-64.png'; $email = get_config('system','admin_email'); $closed = ((get_config('system','register_policy') == REGISTER_CLOSED) ? 'true' : 'false'); $private = ((get_config('system','block_public')) ? 'true' : 'false'); @@ -1541,8 +1594,8 @@ require_once('include/security.php'); 'private' => $private, 'textlimit' => $textlimit, 'sslserver' => $sslserver, 'ssl' => $ssl, 'shorturllength' => '30', 'friendica' => array( - 'FRIENDICA_PLATFORM' => FRIENDICA_PLATFORM, - 'FRIENDICA_VERSION' => FRIENDICA_VERSION, + 'RED_PLATFORM' => RED_PLATFORM, + 'RED_VERSION' => RED_VERSION, 'ZOT_REVISION' => ZOT_REVISION, 'DB_UPDATE_VERSION' => DB_UPDATE_VERSION ) @@ -1577,12 +1630,12 @@ require_once('include/security.php'); if($type === 'xml') { header("Content-type: application/xml"); - echo '<?xml version="1.0" encoding="UTF-8"?>' . "\r\n" . '<version>' . FRIENDICA_VERSION . '</version>' . "\r\n"; + echo '<?xml version="1.0" encoding="UTF-8"?>' . "\r\n" . '<version>' . RED_VERSION . '</version>' . "\r\n"; killme(); } elseif($type === 'json') { header("Content-type: application/json"); - echo '"' . FRIENDICA_VERSION . '"'; + echo '"' . RED_VERSION . '"'; killme(); } } @@ -1599,11 +1652,11 @@ require_once('include/security.php'); // This won't work if either of you send your stream to everybody on the network if($qtype == 'friends') - $sql_extra = sprintf(" AND ( their_perms & %d ) and ( my_perms & %d ) ", intval(PERMS_W_STREAM), intval(PERMS_W_STREAM)); + $sql_extra = sprintf(" AND ( abook_their_perms & %d ) and ( abook_my_perms & %d ) ", intval(PERMS_W_STREAM), intval(PERMS_W_STREAM)); if($qtype == 'followers') - $sql_extra = sprintf(" AND ( my_perms & %d ) and not ( their_perms & %d ) ", intval(PERMS_W_STREAM), intval(PERMS_W_STREAM)); + $sql_extra = sprintf(" AND ( abook_my_perms & %d ) and not ( abook_their_perms & %d ) ", intval(PERMS_W_STREAM), intval(PERMS_W_STREAM)); - $r = q("SELECT id FROM abook where abook_flags = 0 and abook_channel = %d $sql_extra", + $r = q("SELECT abook_id FROM abook where abook_flags = 0 and abook_channel = %d $sql_extra", intval(api_user()) ); @@ -1612,14 +1665,14 @@ require_once('include/security.php'); header("Content-type: application/xml"); echo '<?xml version="1.0" encoding="UTF-8"?>' . "\r\n" . '<ids>' . "\r\n"; foreach($r as $rr) - echo '<id>' . $rr['id'] . '</id>' . "\r\n"; + echo '<id>' . $rr['abook_id'] . '</id>' . "\r\n"; echo '</ids>' . "\r\n"; killme(); } elseif($type === 'json') { $ret = array(); header("Content-type: application/json"); - foreach($r as $rr) $ret[] = $rr['id']; + foreach($r as $rr) $ret[] = $rr['abook_id']; echo json_encode($ret); killme(); } @@ -1653,10 +1706,10 @@ require_once('include/security.php'); $replyto = ''; $sub = ''; if (x($_REQUEST,'replyto')) { - $r = q('SELECT `parent_uri`, `title` FROM `mail` WHERE `uid`=%d AND `id`=%d', + $r = q('SELECT `parent_mid`, `title` FROM `mail` WHERE `uid`=%d AND `id`=%d', intval(api_user()), intval($_REQUEST['replyto'])); - $replyto = $r[0]['parent_uri']; + $replyto = $r[0]['parent_mid']; $sub = $r[0]['title']; } else { @@ -1708,7 +1761,7 @@ require_once('include/security.php'); $sql_extra = "`from-url`='".dbesc( $profile_url )."'"; } elseif ($box=="conversation") { - $sql_extra = "`parent_uri`='".dbesc( $_GET["uri"] ) ."'"; + $sql_extra = "`parent_mid`='".dbesc( $_GET["uri"] ) ."'"; } elseif ($box=="all") { $sql_extra = "true"; diff --git a/include/attach.php b/include/attach.php index 64d6a1689..46d406f4b 100644 --- a/include/attach.php +++ b/include/attach.php @@ -1,4 +1,4 @@ -<?php +<?php /** @file */ /* * File/attach API with the potential for revision control. @@ -10,6 +10,7 @@ */ require_once('include/permissions.php'); +require_once('include/security.php'); function z_mime_content_type($filename) { @@ -192,13 +193,13 @@ function attach_by_hash($hash,$rev = 0) { $sql_extra = permissions_sql($r[0]['uid']); // Now we'll see if we can access the attachment - +dbg(1); $r = q("SELECT * FROM attach WHERE hash = '%s' and uid = %d $sql_extra LIMIT 1", dbesc($hash), intval($r[0]['uid']) ); - +dbg(0); if(! $r) { $ret['message'] = t('Permission denied.'); return $ret; @@ -439,4 +440,64 @@ function attach_store($channel,$observer_hash,$options = '',$arr = null) { $ret['success'] = true; $ret['data'] = $r[0]; return $ret; +} + + +/** + * Read a virtual directory and return contents, checking permissions of all parent components. + * @function z_readdir + * @param integer $channel_id + * @param string $observer_hash + * @param string $pathname + * @param string $parent_hash (optional) + * + * @returns array $ret + * $ret['success'] = boolean true or false + * $ret['message'] = error message if success is false + * $ret['data'] = array of attach DB entries without data component + */ + +function z_readdir($channel_id,$observer_hash,$pathname, $parent_hash = '') { + + $ret = array('success' => false); + if(! perm_is_allowed($r[0]['uid'],get_observer_hash(),'view_storage')) { + $ret['message'] = t('Permission denied.'); + return $ret; + } + + + if(strpos($pathname,'/')) { + $paths = explode('/',$pathname); + if(count($paths) > 1) { + $curpath = array_shift($paths); + + $r = q("select hash, id from attach where uid = %d and filename = '%s' and (flags & %d ) " . permissions_sql($channel_id) . " limit 1", + intval($channel_id), + dbesc($curpath), + intval(ATTACH_FLAG_DIR) + ); + if(! $r) { + $ret['message'] = t('Path not available.'); + return $ret; + } + + return z_readdir($channel_id,$observer_hash,implode('/',$paths),$r[0]['hash']); + } + } + else + $paths = array($pathname); + + $r = q("select id, aid, uid, hash, filename, filetype, filesize, revision, folder, flags, created, edited, allow_cid, allow_gid, deny_cid, deny_gid from attach where id = %d and folder = '%s' and filename = '%s' and (flags & %d ) " . permissions_sql($channel_id), + intval($channel_id), + dbesc($parent_hash), + dbesc($paths[0]), + intval(ATTACH_FLAG_DIR) + ); + if(! $r) { + $ret['message'] = t('Path not available.'); + return $ret; + } + $ret['success'] = true; + $ret['data'] = $r; + return $ret; }
\ No newline at end of file diff --git a/include/auth.php b/include/auth.php index 14751f5a2..c86a54fb1 100644 --- a/include/auth.php +++ b/include/auth.php @@ -1,4 +1,4 @@ -<?php +<?php /** @file */ require_once('include/security.php'); @@ -67,6 +67,21 @@ if((isset($_SESSION)) && (x($_SESSION,'authenticated')) && ((! (x($_POST,'auth-p } if(x($_SESSION,'visitor_id') && (! x($_SESSION,'uid'))) { + // if our authenticated guest is allowed to take control of the admin channel, make it so. + $admins = get_config('system','remote_admin'); + if($admins && is_array($admins) && in_array($_SESSION['visitor_id'],$admins)) { + $x = q("select * from account where account_email = '%s' and account_email != '' and ( account_flags & %d ) limit 1", + dbesc(get_config('system','admin_email')), + intval(ACCOUNT_ROLE_ADMIN) + ); + if($x) { + new_cookie(60*60*24); // one day + $_SESSION['last_login_date'] = datetime_convert(); + unset($_SESSION['visitor_id']); // no longer a visitor + authenticate_success($x[0], true, true); + } + } + $r = q("select * from hubloc left join xchan on xchan_hash = hubloc_hash where hubloc_hash = '%s' limit 1", dbesc($_SESSION['visitor_id']) ); @@ -198,13 +213,3 @@ else { authenticate_success($record, true, true); } } - - -function new_cookie($time) { - $old_sid = session_id(); - session_set_cookie_params("$time"); - session_regenerate_id(false); - - q("UPDATE session SET sid = '%s' WHERE sid = '%s'", dbesc(session_id()), dbesc($old_sid)); -} - diff --git a/include/bb2diaspora.php b/include/bb2diaspora.php index ee070b4fe..8d3089a29 100644 --- a/include/bb2diaspora.php +++ b/include/bb2diaspora.php @@ -1,4 +1,4 @@ -<?php +<?php /** @file */ require_once("include/oembed.php"); require_once("include/event.php"); diff --git a/include/bbcode.php b/include/bbcode.php index d43929ef5..a0a53a310 100644 --- a/include/bbcode.php +++ b/include/bbcode.php @@ -1,4 +1,4 @@ -<?php +<?php /** @file */ require_once("include/oembed.php"); require_once('include/event.php'); @@ -91,7 +91,7 @@ function bb_replace_images($body, $images) { // We're depending on the property of 'foreach' (specified on the PHP website) that // it loops over the array starting from the first element and going sequentially // to the last element - $newbody = str_replace('[$#saved_image' . $cnt . '#$]', '<img src="' . $image .'" alt="' . t('Image/photo') . '" />', $newbody); + $newbody = str_replace('[$#saved_image' . $cnt . '#$]', '<img class="zrl" src="' . $image .'" alt="' . t('Image/photo') . '" />', $newbody); $cnt++; } @@ -99,13 +99,110 @@ function bb_replace_images($body, $images) { }} +function bb_ShareAttributes($match) { + + $attributes = $match[1]; + + $author = ""; + preg_match("/author='(.*?)'/ism", $attributes, $matches); + if ($matches[1] != "") + $author = html_entity_decode($matches[1],ENT_QUOTES,'UTF-8'); + + preg_match('/author="(.*?)"/ism', $attributes, $matches); + if ($matches[1] != "") + $author = $matches[1]; + + $link = ""; + preg_match("/link='(.*?)'/ism", $attributes, $matches); + if ($matches[1] != "") + $link = $matches[1]; + + preg_match('/link="(.*?)"/ism', $attributes, $matches); + if ($matches[1] != "") + $link = $matches[1]; + + $avatar = ""; + preg_match("/avatar='(.*?)'/ism", $attributes, $matches); + if ($matches[1] != "") + $avatar = $matches[1]; + + preg_match('/avatar="(.*?)"/ism', $attributes, $matches); + if ($matches[1] != "") + $avatar = $matches[1]; + + $profile = ""; + preg_match("/profile='(.*?)'/ism", $attributes, $matches); + if ($matches[1] != "") + $profile = $matches[1]; + + preg_match('/profile="(.*?)"/ism', $attributes, $matches); + if ($matches[1] != "") + $profile = $matches[1]; + + $posted = ""; + preg_match("/posted='(.*?)'/ism", $attributes, $matches); + if ($matches[1] != "") + $posted = $matches[1]; + + preg_match('/posted="(.*?)"/ism', $attributes, $matches); + if ($matches[1] != "") + $posted = $matches[1]; + + // FIXME - this should really be a wall-item-ago so it will get updated on the client + $reldate = (($posted) ? relative_date($posted) : ''); + + $headline = '<div class="shared_header">'; + + if ($avatar != "") + $headline .= '<img src="' . $avatar . '" alt="' . $author . '" height="32" width="32" />'; + + // Bob Smith wrote the following post 2 hours ago + + $fmt = sprintf( t('%1$s wrote the following %2$s %3$s'), + '<a href="' . zid($profile) . '" >' . $author . '</a>', + '<a href="' . zid($link) . '" >' . t('post') . '</a>', + $reldate + ); + + $headline .= '<span>' . $fmt . '</span></div>'; + + $text = $headline . '<div class="reshared-content">' . trim($match[2]) . '</div>'; + + return($text); +} + +function bb_ShareAttributesSimple($match) { + + $attributes = $match[1]; + + $author = ""; + preg_match("/author='(.*?)'/ism", $attributes, $matches); + if ($matches[1] != "") + $author = html_entity_decode($matches[1],ENT_QUOTES,'UTF-8'); + + preg_match('/author="(.*?)"/ism', $attributes, $matches); + if ($matches[1] != "") + $author = $matches[1]; + + $profile = ""; + preg_match("/profile='(.*?)'/ism", $attributes, $matches); + if ($matches[1] != "") + $profile = $matches[1]; + + preg_match('/profile="(.*?)"/ism', $attributes, $matches); + if ($matches[1] != "") + $profile = $matches[1]; + + $text = "<br />".html_entity_decode("♲ ", ENT_QUOTES, 'UTF-8').' <a href="'.$profile.'">'.$author."</a>: div class=\"reshared-content\">" .$match[2]."</div>"; + + return($text); +} // BBcode 2 HTML was written by WAY2WEB.net - // extended to work with Mistpark/Friendica - Mike Macgirvin + // extended to work with Mistpark/Friendica/Red - Mike Macgirvin function bbcode($Text,$preserve_nl = false, $tryoembed = true) { - $a = get_app(); // Extract the private images which use data url's since preg has issues with @@ -122,10 +219,15 @@ function bbcode($Text,$preserve_nl = false, $tryoembed = true) { $Text = preg_replace("/(\s*)\[\/(\w*)\]/ism", '[/$2]$1', $Text); // Hide all [noparse] contained bbtags by spacefying them - - $Text = preg_replace_callback("/\[noparse\](.*?)\[\/noparse\]/ism", 'bb_spacefy',$Text); - $Text = preg_replace_callback("/\[nobb\](.*?)\[\/nobb\]/ism", 'bb_spacefy',$Text); - $Text = preg_replace_callback("/\[pre\](.*?)\[\/pre\]/ism", 'bb_spacefy',$Text); + if (strpos($Text,'[noparse]') !== false) { + $Text = preg_replace_callback("/\[noparse\](.*?)\[\/noparse\]/ism", 'bb_spacefy',$Text); + } + if (strpos($Text,'[nobb]') !== false) { + $Text = preg_replace_callback("/\[nobb\](.*?)\[\/nobb\]/ism", 'bb_spacefy',$Text); + } + if (strpos($Text,'[pre]') !== false) { + $Text = preg_replace_callback("/\[pre\](.*?)\[\/pre\]/ism", 'bb_spacefy',$Text); + } @@ -142,9 +244,6 @@ function bbcode($Text,$preserve_nl = false, $tryoembed = true) { $Text = str_replace("<", "<", $Text); $Text = str_replace(">", ">", $Text); - // This only matters when looking for tags - otherwise has no meaning - - $Text = str_replace(array('[share]','[/share]'), array('',''), $Text); // Convert new line chars to html <br /> tags @@ -169,66 +268,84 @@ function bbcode($Text,$preserve_nl = false, $tryoembed = true) { // Perform URL Search - $Text = preg_replace("/([^\]\=]|^)(https?\:\/\/[a-zA-Z0-9\:\/\-\?\&\;\.\=\_\~\#\%\$\!\+\,]+)/ism", '$1<a href="$2" >$2</a>', $Text); - - if ($tryoembed) - $Text = preg_replace_callback("/\[bookmark\=([^\]]*)\].*?\[\/bookmark\]/ism",'tryoembed',$Text); - - $Text = preg_replace("/\[bookmark\=([^\]]*)\](.*?)\[\/bookmark\]/ism",'[url=$1]$2[/url]',$Text); - - if ($tryoembed) - $Text = preg_replace_callback("/\[url\]([$URLSearchString]*)\[\/url\]/ism",'tryoembed',$Text); - - $Text = preg_replace("/\[url\]([$URLSearchString]*)\[\/url\]/ism", '<a href="$1" >$1</a>', $Text); - $Text = preg_replace("/\[url\=([$URLSearchString]*)\](.*?)\[\/url\]/ism", '<a href="$1" >$2</a>', $Text); - //$Text = preg_replace("/\[url\=([$URLSearchString]*)\]([$URLSearchString]*)\[\/url\]/ism", '<a href="$1" target="_blank">$2</a>', $Text); + $urlchars = '[a-zA-Z0-9\:\/\-\?\&\;\.\=\@\_\~\#\%\$\!\+\,]'; + if (strpos($Text,'http') !== false) { + $Text = preg_replace("/([^\]\='".'"'."]|^)(https?\:\/\/$urlchars+)/ism", '$1<a href="$2" >$2</a>', $Text); + } + if (strpos($Text,'[/share]') !== false) { + $Text = preg_replace_callback("/\[share(.*?)\](.*?)\[\/share\]/ism","bb_ShareAttributes",$Text); + } + if($tryoembed) { + if (strpos($Text,'[/url]') !== false) { + $Text = preg_replace_callback("/\[url\]([$URLSearchString]*)\[\/url\]/ism",'tryoembed',$Text); + } + } + if (strpos($Text,'[/url]') !== false) { + $Text = preg_replace("/\[url\]([$URLSearchString]*)\[\/url\]/ism", '<a href="$1" >$1</a>', $Text); + $Text = preg_replace("/\[url\=([$URLSearchString]*)\](.*?)\[\/url\]/ism", '<a href="$1" >$2</a>', $Text); + } + if (strpos($Text,'[/zrl]') !== false) { + $Text = preg_replace("/\[zrl\]([$URLSearchString]*)\[\/zrl\]/ism", '<a class="zrl" href="$1" >$1</a>', $Text); + $Text = preg_replace("/\[zrl\=([$URLSearchString]*)\](.*?)\[\/zrl\]/ism", '<a class="zrl" href="$1" >$2</a>', $Text); + } // Perform MAIL Search - $Text = preg_replace("/\[mail\]([$MAILSearchString]*)\[\/mail\]/", '<a href="mailto:$1">$1</a>', $Text); - $Text = preg_replace("/\[mail\=([$MAILSearchString]*)\](.*?)\[\/mail\]/", '<a href="mailto:$1">$2</a>', $Text); - + if (strpos($Text,'[/mail]') !== false) { + $Text = preg_replace("/\[mail\]([$MAILSearchString]*)\[\/mail\]/", '<a href="mailto:$1">$1</a>', $Text); + $Text = preg_replace("/\[mail\=([$MAILSearchString]*)\](.*?)\[\/mail\]/", '<a href="mailto:$1">$2</a>', $Text); + } // Check for bold text - $Text = preg_replace("(\[b\](.*?)\[\/b\])ism",'<strong>$1</strong>',$Text); - + if (strpos($Text,'[b]') !== false) { + $Text = preg_replace("(\[b\](.*?)\[\/b\])ism",'<strong>$1</strong>',$Text); + } // Check for Italics text - $Text = preg_replace("(\[i\](.*?)\[\/i\])ism",'<em>$1</em>',$Text); - + if (strpos($Text,'[i]') !== false) { + $Text = preg_replace("(\[i\](.*?)\[\/i\])ism",'<em>$1</em>',$Text); + } // Check for Underline text - $Text = preg_replace("(\[u\](.*?)\[\/u\])ism",'<u>$1</u>',$Text); - + if (strpos($Text,'[u]') !== false) { + $Text = preg_replace("(\[u\](.*?)\[\/u\])ism",'<u>$1</u>',$Text); + } // Check for strike-through text - $Text = preg_replace("(\[s\](.*?)\[\/s\])ism",'<strike>$1</strike>',$Text); - + if (strpos($Text,'[s]') !== false) { + $Text = preg_replace("(\[s\](.*?)\[\/s\])ism",'<strike>$1</strike>',$Text); + } // Check for over-line text - $Text = preg_replace("(\[o\](.*?)\[\/o\])ism",'<span class="overline">$1</span>',$Text); - + if (strpos($Text,'[o]') !== false) { + $Text = preg_replace("(\[o\](.*?)\[\/o\])ism",'<span class="overline">$1</span>',$Text); + } // Check for colored text - $Text = preg_replace("(\[color=(.*?)\](.*?)\[\/color\])ism","<span style=\"color: $1;\">$2</span>",$Text); - + if (strpos($Text,'[/color]') !== false) { + $Text = preg_replace("(\[color=(.*?)\](.*?)\[\/color\])ism","<span style=\"color: $1;\">$2</span>",$Text); + } // Check for sized text - // [size=50] --> font-size: 50px (with the unit). - $Text = preg_replace("(\[size=(\d*?)\](.*?)\[\/size\])ism","<span style=\"font-size: $1px;\">$2</span>",$Text); - $Text = preg_replace("(\[size=(.*?)\](.*?)\[\/size\])ism","<span style=\"font-size: $1;\">$2</span>",$Text); - + // [size=50] --> font-size: 50px (with the unit). + if (strpos($Text,'[/size]') !== false) { + $Text = preg_replace("(\[size=(\d*?)\](.*?)\[\/size\])ism","<span style=\"font-size: $1px;\">$2</span>",$Text); + $Text = preg_replace("(\[size=(.*?)\](.*?)\[\/size\])ism","<span style=\"font-size: $1;\">$2</span>",$Text); + } // Check for centered text + if (strpos($Text,'[/center]') !== false) { $Text = preg_replace("(\[center\](.*?)\[\/center\])ism","<div style=\"text-align:center;\">$1</div>",$Text); - + } // Check for list text $Text = str_replace("[*]", "<li>", $Text); // Check for style sheet commands - $Text = preg_replace("(\[style=(.*?)\](.*?)\[\/style\])ism","<span style=\"$1;\">$2</span>",$Text); - + if (strpos($Text,'[/style]') !== false) { + $Text = preg_replace("(\[style=(.*?)\](.*?)\[\/style\])ism","<span style=\"$1;\">$2</span>",$Text); + } // Check for CSS classes - $Text = preg_replace("(\[class=(.*?)\](.*?)\[\/class\])ism","<span class=\"$1\">$2</span>",$Text); - + if (strpos($Text,'[/class]') !== false) { + $Text = preg_replace("(\[class=(.*?)\](.*?)\[\/class\])ism","<span class=\"$1\">$2</span>",$Text); + } // handle nested lists $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, "[/li]") !== false) && (strpos($Text, "[li]") !== false))) && (++$endlessloop < 20)) { + ((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", '<ul class="listbullet" style="list-style-type: circle;">$1</ul>' ,$Text); $Text = preg_replace("/\[list=\](.*?)\[\/list\]/ism", '<ul class="listnone" style="list-style-type: none;">$1</ul>' ,$Text); $Text = preg_replace("/\[list=1\](.*?)\[\/list\]/ism", '<ul class="listdecimal" style="list-style-type: decimal;">$1</ul>' ,$Text); @@ -240,15 +357,20 @@ function bbcode($Text,$preserve_nl = false, $tryoembed = true) { $Text = preg_replace("/\[ol\](.*?)\[\/ol\]/ism", '<ul class="listdecimal" style="list-style-type: decimal;">$1</ul>' ,$Text); $Text = preg_replace("/\[li\](.*?)\[\/li\]/ism", '<li>$1</li>' ,$Text); } - - $Text = preg_replace("/\[th\](.*?)\[\/th\]/sm", '<th>$1</th>' ,$Text); - $Text = preg_replace("/\[td\](.*?)\[\/td\]/sm", '<td>$1</td>' ,$Text); - $Text = preg_replace("/\[tr\](.*?)\[\/tr\]/sm", '<tr>$1</tr>' ,$Text); - $Text = preg_replace("/\[table\](.*?)\[\/table\]/sm", '<table>$1</table>' ,$Text); - - $Text = preg_replace("/\[table border=1\](.*?)\[\/table\]/sm", '<table border="1" >$1</table>' ,$Text); - $Text = preg_replace("/\[table border=0\](.*?)\[\/table\]/sm", '<table border="0" >$1</table>' ,$Text); - + if (strpos($Text,'[th]') !== false) { + $Text = preg_replace("/\[th\](.*?)\[\/th\]/sm", '<th>$1</th>' ,$Text); + } + if (strpos($Text,'[td]') !== false) { + $Text = preg_replace("/\[td\](.*?)\[\/td\]/sm", '<td>$1</td>' ,$Text); + } + if (strpos($Text,'[tr]') !== false) { + $Text = preg_replace("/\[tr\](.*?)\[\/tr\]/sm", '<tr>$1</tr>' ,$Text); + } + if (strpos($Text,'[table]') !== false) { + $Text = preg_replace("/\[table\](.*?)\[\/table\]/sm", '<table>$1</table>' ,$Text); + $Text = preg_replace("/\[table border=1\](.*?)\[\/table\]/sm", '<table border="1" >$1</table>' ,$Text); + $Text = preg_replace("/\[table border=0\](.*?)\[\/table\]/sm", '<table border="0" >$1</table>' ,$Text); + } $Text = str_replace('[hr]','<hr />', $Text); // This is actually executed in prepare_body() @@ -256,14 +378,16 @@ function bbcode($Text,$preserve_nl = false, $tryoembed = true) { $Text = str_replace('[nosmile]','',$Text); // Check for font change text - $Text = preg_replace("/\[font=(.*?)\](.*?)\[\/font\]/sm","<span style=\"font-family: $1;\">$2</span>",$Text); - + if (strpos($Text,'[/font]') !== false) { + $Text = preg_replace("/\[font=(.*?)\](.*?)\[\/font\]/sm","<span style=\"font-family: $1;\">$2</span>",$Text); + } // Declare the format for [code] layout $CodeLayout = '<code>$1</code>'; // Check for [code] text - $Text = preg_replace("/\[code\](.*?)\[\/code\]/ism","$CodeLayout", $Text); - + if (strpos($Text,'[code]') !== false) { + $Text = preg_replace("/\[code\](.*?)\[\/code\]/ism","$CodeLayout", $Text); + } // Declare the format for [spoiler] layout $SpoilerLayout = '<blockquote class="spoiler">$1</blockquote>'; @@ -281,8 +405,8 @@ function bbcode($Text,$preserve_nl = false, $tryoembed = true) { $endlessloop = 0; while ((strpos($Text, "[/spoiler]")!== false) and (strpos($Text, "[spoiler=") !== false) and (++$endlessloop < 20)) $Text = preg_replace("/\[spoiler=[\"\']*(.*?)[\"\']*\](.*?)\[\/spoiler\]/ism", - "<br /><strong class=".'"spoiler"'.">" . $t_wrote . "</strong><blockquote class=".'"spoiler"'.">$2</blockquote>", - $Text); + "<br /><strong class=".'"spoiler"'.">" . $t_wrote . "</strong><blockquote class=".'"spoiler"'.">$2</blockquote>", + $Text); // Declare the format for [quote] layout $QuoteLayout = '<blockquote>$1</blockquote>'; @@ -301,71 +425,98 @@ function bbcode($Text,$preserve_nl = false, $tryoembed = true) { $endlessloop = 0; while ((strpos($Text, "[/quote]")!== false) and (strpos($Text, "[quote=") !== false) and (++$endlessloop < 20)) $Text = preg_replace("/\[quote=[\"\']*(.*?)[\"\']*\](.*?)\[\/quote\]/ism", - "<br /><strong class=".'"author"'.">" . $t_wrote . "</strong><blockquote>$2</blockquote>", - $Text); + "<br /><strong class=".'"author"'.">" . $t_wrote . "</strong><blockquote>$2</blockquote>", + $Text); // [img=widthxheight]image source[/img] //$Text = preg_replace("/\[img\=([0-9]*)x([0-9]*)\](.*?)\[\/img\]/ism", '<img src="$3" style="height: $2px; width: $1px;" >', $Text); - $Text = preg_replace("/\[img\=([0-9]*)x([0-9]*)\](.*?)\[\/img\]/ism", '<img src="$3" style="width: $1px;" >', $Text); - + if (strpos($Text,'[/img]') !== false) { + $Text = preg_replace("/\[img\=([0-9]*)x([0-9]*)\](.*?)\[\/img\]/ism", '<img src="$3" style="width: $1px;" >', $Text); + } + if (strpos($Text,'[/zmg]') !== false) { + $Text = preg_replace("/\[zmg\=([0-9]*)x([0-9]*)\](.*?)\[\/zmg\]/ism", '<img class="zrl" src="$3" style="width: $1px;" >', $Text); + } // Images // [img]pathtoimage[/img] - $Text = preg_replace("/\[img\](.*?)\[\/img\]/ism", '<img src="$1" alt="' . t('Image/photo') . '" />', $Text); - - - $Text = preg_replace("/\[crypt\](.*?)\[\/crypt\]/ism",'<br/><img src="' .$a->get_baseurl() . '/images/lock_icon.gif" alt="' . t('Encrypted content') . '" title="' . t('Encrypted content') . '" /><br />', $Text); - $Text = preg_replace("/\[crypt=(.*?)\](.*?)\[\/crypt\]/ism",'<br/><img src="' .$a->get_baseurl() . '/images/lock_icon.gif" alt="' . t('Encrypted content') . '" title="' . '$1' . ' ' . t('Encrypted content') . '" /><br />', $Text); + if (strpos($Text,'[/img]') !== false) { + $Text = preg_replace("/\[img\](.*?)\[\/img\]/ism", '<img src="$1" alt="' . t('Image/photo') . '" />', $Text); + } + if (strpos($Text,'[/zmg]') !== false) { + $Text = preg_replace("/\[zmg\](.*?)\[\/zmg\]/ism", '<img class="zrl" src="$1" alt="' . t('Image/photo') . '" />', $Text); + } + if (strpos($Text,'[crypt]') !== false) { + $Text = preg_replace("/\[crypt\](.*?)\[\/crypt\]/ism",'<br/><img src="' .$a->get_baseurl() . '/images/lock_icon.gif" alt="' . t('Encrypted content') . '" title="' . t('Encrypted content') . '" /><br />', $Text); + $Text = preg_replace("/\[crypt=(.*?)\](.*?)\[\/crypt\]/ism",'<br/><img src="' .$a->get_baseurl() . '/images/lock_icon.gif" alt="' . t('Encrypted content') . '" title="' . '$1' . ' ' . t('Encrypted content') . '" /><br />', $Text); + } // Try to Oembed if ($tryoembed) { - $Text = preg_replace("/\[video\](.*?\.(ogg|ogv|oga|ogm|webm|mp4))\[\/video\]/ism", '<video src="$1" controls="controls" width="' . $a->videowidth . '" height="' . $a->videoheight . '"><a href="$1">$1</a></video>', $Text); - $Text = preg_replace("/\[audio\](.*?\.(ogg|ogv|oga|ogm|webm|mp4|mp3))\[\/audio\]/ism", '<audio src="$1" controls="controls"><a href="$1">$1</a></audio>', $Text); + if (strpos($Text,'[/video]') !== false) { + $Text = preg_replace("/\[video\](.*?\.(ogg|ogv|oga|ogm|webm|mp4))\[\/video\]/ism", '<video src="$1" controls="controls" width="' . $a->videowidth . '" height="' . $a->videoheight . '"><a href="$1">$1</a></video>', $Text); + } + if (strpos($Text,'[/audio]') !== false) { + $Text = preg_replace("/\[audio\](.*?\.(ogg|ogv|oga|ogm|webm|mp4|mp3))\[\/audio\]/ism", '<audio src="$1" controls="controls"><a href="$1">$1</a></audio>', $Text); + } + if (strpos($Text,'[/video]') !== false) { + $Text = preg_replace_callback("/\[video\](.*?)\[\/video\]/ism", 'tryoembed', $Text); + } + if (strpos($Text,'[/audio]') !== false) { + $Text = preg_replace_callback("/\[audio\](.*?)\[\/audio\]/ism", 'tryoembed', $Text); + } + } - $Text = preg_replace_callback("/\[video\](.*?)\[\/video\]/ism", 'tryoembed', $Text); - $Text = preg_replace_callback("/\[audio\](.*?)\[\/audio\]/ism", 'tryoembed', $Text); - } else { - $Text = preg_replace("/\[video\](.*?)\[\/video\]/", '$1', $Text); - $Text = preg_replace("/\[audio\](.*?)\[\/audio\]/", '$1', $Text); + // if video couldn't be embedded, link to it instead. + if (strpos($Text,'[/video]') !== false) { + $Text = preg_replace("/\[video\](.*?)\[\/video\]/", '<a href="$1">$1</a>', $Text); + } + if (strpos($Text,'[/audio]') !== false) { + $Text = preg_replace("/\[audio\](.*?)\[\/audio\]/", '<a href="$1">$1</a>', $Text); } - // html5 video and audio + // html5 video and audio - if ($tryoembed) - $Text = preg_replace("/\[iframe\](.*?)\[\/iframe\]/ism", '<iframe src="$1" width="' . $a->videowidth . '" height="' . $a->videoheight . '"><a href="$1">$1</a></iframe>', $Text); - else - $Text = preg_replace("/\[iframe\](.*?)\[\/iframe\]/ism", '<a href="$1">$1</a>', $Text); + if ($tryoembed){ + if (strpos($Text,'[/iframe]') !== false) { + $Text = preg_replace("/\[iframe\](.*?)\[\/iframe\]/ism", '<iframe src="$1" width="' . $a->videowidth . '" height="' . $a->videoheight . '"><a href="$1">$1</a></iframe>', $Text); + } + } + else { + if (strpos($Text,'[/iframe]') !== false) { + $Text = preg_replace("/\[iframe\](.*?)\[\/iframe\]/ism", '<a href="$1">$1</a>', $Text); + } + } // Youtube extensions - if ($tryoembed) { - $Text = preg_replace_callback("/\[youtube\](https?:\/\/www.youtube.com\/watch\?v\=.*?)\[\/youtube\]/ism", 'tryoembed', $Text); - $Text = preg_replace_callback("/\[youtube\](www.youtube.com\/watch\?v\=.*?)\[\/youtube\]/ism", 'tryoembed', $Text); - $Text = preg_replace_callback("/\[youtube\](https?:\/\/youtu.be\/.*?)\[\/youtube\]/ism",'tryoembed',$Text); + if (strpos($Text,'[youtube]') !== false) { + if ($tryoembed) { + $Text = preg_replace_callback("/\[youtube\](https?:\/\/www.youtube.com\/watch\?v\=.*?)\[\/youtube\]/ism", 'tryoembed', $Text); + $Text = preg_replace_callback("/\[youtube\](www.youtube.com\/watch\?v\=.*?)\[\/youtube\]/ism", 'tryoembed', $Text); + $Text = preg_replace_callback("/\[youtube\](https?:\/\/youtu.be\/.*?)\[\/youtube\]/ism",'tryoembed',$Text); + } + $Text = preg_replace("/\[youtube\]https?:\/\/www.youtube.com\/watch\?v\=(.*?)\[\/youtube\]/ism",'[youtube]$1[/youtube]',$Text); + $Text = preg_replace("/\[youtube\]https?:\/\/www.youtube.com\/embed\/(.*?)\[\/youtube\]/ism",'[youtube]$1[/youtube]',$Text); + $Text = preg_replace("/\[youtube\]https?:\/\/youtu.be\/(.*?)\[\/youtube\]/ism",'[youtube]$1[/youtube]',$Text); + + if ($tryoembed) + $Text = preg_replace("/\[youtube\]([A-Za-z0-9\-_=]+)(.*?)\[\/youtube\]/ism", '<iframe width="' . $a->videowidth . '" height="' . $a->videoheight . '" src="http://www.youtube.com/embed/$1" frameborder="0" ></iframe>', $Text); + else + $Text = preg_replace("/\[youtube\]([A-Za-z0-9\-_=]+)(.*?)\[\/youtube\]/ism", "http://www.youtube.com/watch?v=$1", $Text); } + if (strpos($Text,'[vimeo]') !== false) { + if ($tryoembed) { + $Text = preg_replace_callback("/\[vimeo\](https?:\/\/player.vimeo.com\/video\/[0-9]+).*?\[\/vimeo\]/ism",'tryoembed',$Text); + $Text = preg_replace_callback("/\[vimeo\](https?:\/\/vimeo.com\/[0-9]+).*?\[\/vimeo\]/ism",'tryoembed',$Text); + } - $Text = preg_replace("/\[youtube\]https?:\/\/www.youtube.com\/watch\?v\=(.*?)\[\/youtube\]/ism",'[youtube]$1[/youtube]',$Text); - $Text = preg_replace("/\[youtube\]https?:\/\/www.youtube.com\/embed\/(.*?)\[\/youtube\]/ism",'[youtube]$1[/youtube]',$Text); - $Text = preg_replace("/\[youtube\]https?:\/\/youtu.be\/(.*?)\[\/youtube\]/ism",'[youtube]$1[/youtube]',$Text); - - if ($tryoembed) - $Text = preg_replace("/\[youtube\]([A-Za-z0-9\-_=]+)(.*?)\[\/youtube\]/ism", '<iframe width="' . $a->videowidth . '" height="' . $a->videoheight . '" src="http://www.youtube.com/embed/$1" frameborder="0" ></iframe>', $Text); - else - $Text = preg_replace("/\[youtube\]([A-Za-z0-9\-_=]+)(.*?)\[\/youtube\]/ism", "http://www.youtube.com/watch?v=$1", $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) { - $Text = preg_replace_callback("/\[vimeo\](https?:\/\/player.vimeo.com\/video\/[0-9]+).*?\[\/vimeo\]/ism",'tryoembed',$Text); - $Text = preg_replace_callback("/\[vimeo\](https?:\/\/vimeo.com\/[0-9]+).*?\[\/vimeo\]/ism",'tryoembed',$Text); + if ($tryoembed) + $Text = preg_replace("/\[vimeo\]([0-9]+)(.*?)\[\/vimeo\]/ism", '<iframe width="' . $a->videowidth . '" height="' . $a->videoheight . '" src="http://player.vimeo.com/video/$1" frameborder="0" ></iframe>', $Text); + else + $Text = preg_replace("/\[vimeo\]([0-9]+)(.*?)\[\/vimeo\]/ism", "http://vimeo.com/$1", $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) - $Text = preg_replace("/\[vimeo\]([0-9]+)(.*?)\[\/vimeo\]/ism", '<iframe width="' . $a->videowidth . '" height="' . $a->videoheight . '" src="http://player.vimeo.com/video/$1" frameborder="0" ></iframe>', $Text); - else - $Text = preg_replace("/\[vimeo\]([0-9]+)(.*?)\[\/vimeo\]/ism", "http://vimeo.com/$1", $Text); - // $Text = preg_replace("/\[youtube\](.*?)\[\/youtube\]/", '<object width="425" height="350" type="application/x-shockwave-flash" data="http://www.youtube.com/v/$1" ><param name="movie" value="http://www.youtube.com/v/$1"></param><!--[if IE]><embed src="http://www.youtube.com/v/$1" type="application/x-shockwave-flash" width="425" height="350" /><![endif]--></object>', $Text); @@ -393,11 +544,15 @@ function bbcode($Text,$preserve_nl = false, $tryoembed = true) { // Unhide all [noparse] contained bbtags unspacefying them // and triming the [noparse] tag. - - $Text = preg_replace_callback("/\[noparse\](.*?)\[\/noparse\]/ism", 'bb_unspacefy_and_trim',$Text); - $Text = preg_replace_callback("/\[nobb\](.*?)\[\/nobb\]/ism", 'bb_unspacefy_and_trim',$Text); - $Text = preg_replace_callback("/\[pre\](.*?)\[\/pre\]/ism", 'bb_unspacefy_and_trim',$Text); - + if (strpos($Text,'[noparse]') !== false) { + $Text = preg_replace_callback("/\[noparse\](.*?)\[\/noparse\]/ism", 'bb_unspacefy_and_trim',$Text); + } + if (strpos($Text,'[nobb]') !== false) { + $Text = preg_replace_callback("/\[nobb\](.*?)\[\/nobb\]/ism", 'bb_unspacefy_and_trim',$Text); + } + if (strpos($Text,'[pre]') !== false) { + $Text = preg_replace_callback("/\[pre\](.*?)\[\/pre\]/ism", 'bb_unspacefy_and_trim',$Text); + } $Text = preg_replace('/\[\&\;([#a-z0-9]+)\;\]/','&$1;',$Text); @@ -411,22 +566,22 @@ function bbcode($Text,$preserve_nl = false, $tryoembed = true) { // Clean up the HTML by loading and saving the HTML with the DOM // Only do it when it has to be done - for performance reasons - if (!$tryoembed) { - $doc = new DOMDocument(); - $doc->preserveWhiteSpace = false; +// if (!$tryoembed) {// +// $doc = new DOMDocument(); +// $doc->preserveWhiteSpace = false; - $Text = mb_convert_encoding($Text, 'HTML-ENTITIES', "UTF-8"); +// $Text = mb_convert_encoding($Text, 'HTML-ENTITIES', "UTF-8"); - $doctype = '<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">'; - @$doc->loadHTML($doctype."<html><body>".$Text."</body></html>"); +// $doctype = '<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">'; +// @$doc->loadHTML($doctype."<html><body>".$Text."</body></html>"); - $Text = $doc->saveHTML(); - $Text = str_replace(array("<html><body>", "</body></html>", $doctype), array("", "", ""), $Text); +// $Text = $doc->saveHTML(); +// $Text = str_replace(array("<html><body>", "</body></html>", $doctype), array("", "", ""), $Text); - $Text = str_replace('<br></li>','</li>', $Text); +// $Text = str_replace('<br></li>','</li>', $Text); - $Text = mb_convert_encoding($Text, "UTF-8", 'HTML-ENTITIES'); - } +// $Text = mb_convert_encoding($Text, "UTF-8", 'HTML-ENTITIES'); +// } call_hooks('bbcode',$Text); diff --git a/include/cache.php b/include/cache.php index 567046f7a..b546cd0e9 100644 --- a/include/cache.php +++ b/include/cache.php @@ -1,4 +1,4 @@ -<?php +<?php /** @file */ /** * cache api diff --git a/include/cli_startup.php b/include/cli_startup.php index d9f6ecb8e..6bd4e7520 100644 --- a/include/cli_startup.php +++ b/include/cli_startup.php @@ -1,4 +1,4 @@ -<?php +<?php /** @file */ require_once('boot.php'); @@ -14,14 +14,13 @@ function cli_startup() { if(is_null($db)) { @include(".htconfig.php"); - require_once("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/dba/dba_driver.php'); + $db = dba_factory($db_host, $db_port, $db_user, $db_pass, $db_data); + unset($db_host, $db_port, $db_user, $db_pass, $db_data); }; require_once('include/session.php'); - load_config('config'); load_config('system'); $a->set_baseurl(get_config('system','baseurl')); diff --git a/include/cli_suggest.php b/include/cli_suggest.php new file mode 100644 index 000000000..321ffd2e0 --- /dev/null +++ b/include/cli_suggest.php @@ -0,0 +1,22 @@ +<?php /** @file */ + +require_once('boot.php'); +require_once('include/cli_startup.php'); +require_once('include/socgraph.php'); + + +function cli_suggest_run($argv, $argc){ + + cli_startup(); + + $a = get_app(); + + update_suggestions(); + +} + +if (array_search(__file__,get_included_files())===0){ + cli_suggest_run($argv,$argc); + killme(); +} + diff --git a/include/config.php b/include/config.php index 44606e329..38840f5e4 100644 --- a/include/config.php +++ b/include/config.php @@ -1,4 +1,4 @@ -<?php +<?php /** @file */ /** * @@ -15,24 +15,26 @@ // retrieve a "family" of config variables from database to cached storage -if(! function_exists('load_config')) { function load_config($family) { global $a; - $r = q("SELECT * FROM `config` WHERE `cat` = '%s'", dbesc($family)); - if(count($r)) { - foreach($r as $rr) { - $k = $rr['k']; - if ($family === 'config') { - $a->config[$k] = $rr['v']; - } else { - $a->config[$family][$k] = $rr['v']; + + if(! array_key_exists($family,$a->config)) + $a->config[$family] = array(); + + if(! array_key_exists('config_loaded',$a->config[$family])) { + + $r = q("SELECT * FROM config WHERE cat = '%s'", dbesc($family)); + if($r !== false) { + if($r) { + foreach($r as $rr) { + $k = $rr['k']; + $a->config[$family][$k] = $rr['v']; + } } + $a->config[$family]['config_loaded'] = true; } - } else if ($family != 'config') { - // Negative caching - $a->config[$family] = "!<unset>!"; - } -}} + } +} // get a particular config variable given the family name // and key. Returns false if not set. @@ -42,55 +44,50 @@ function load_config($family) { // local config cache, pull it into the cache so we don't have // to hit the DB again for this item. -if(! function_exists('get_config')) { -function get_config($family, $key, $instore = false) { + +function get_config($family, $key) { global $a; - if(! $instore) { - // Looking if the whole family isn't set - if(isset($a->config[$family])) { - if($a->config[$family] === '!<unset>!') { - return false; - } - } + if((! array_key_exists($family,$a->config)) || (! array_key_exists('config_loaded',$a->config[$family]))) + load_config($family); - if(isset($a->config[$family][$key])) { - if($a->config[$family][$key] === '!<unset>!') { - return false; - } - return $a->config[$family][$key]; + if(array_key_exists('config_loaded',$a->config[$family])) { + if(! array_key_exists($key,$a->config[$family])) { + return false; } + return ((preg_match('|^a:[0-9]+:{.*}$|s', $a->config[$family][$key])) + ? unserialize($a->config[$family][$key]) + : $a->config[$family][$key] + ); } - $ret = q("SELECT `v` FROM `config` WHERE `cat` = '%s' AND `k` = '%s' LIMIT 1", + return false; +} + +function get_config_from_storage($family,$key) { + $ret = q("select * from config where cat = '%s' and k = '%s' limit 1", dbesc($family), dbesc($key) ); - if(count($ret)) { - // manage array value - $val = (preg_match("|^a:[0-9]+:{.*}$|s", $ret[0]['v'])?unserialize( $ret[0]['v']):$ret[0]['v']); - $a->config[$family][$key] = $val; - return $val; - } - else { - $a->config[$family][$key] = '!<unset>!'; - } - return false; -}} + return $ret; +} + + // Store a config value ($value) in the category ($family) // under the key ($key) // Return the value, or false if the database update failed -if(! function_exists('set_config')) { function set_config($family,$key,$value) { global $a; // manage array value - $dbvalue = (is_array($value)?serialize($value):$value); - $dbvalue = (is_bool($dbvalue) ? intval($dbvalue) : $dbvalue); - if(get_config($family,$key,true) === false) { + $dbvalue = ((is_array($value)) ? serialize($value) : $value); + $dbvalue = ((is_bool($dbvalue)) ? intval($dbvalue) : $dbvalue); + + if(get_config($family,$key) === false || (! get_config_from_storage($family,$key))) { $a->config[$family][$key] = $value; - $ret = q("INSERT INTO `config` ( `cat`, `k`, `v` ) VALUES ( '%s', '%s', '%s' ) ", + + $ret = q("INSERT INTO config ( cat, k, v ) VALUES ( '%s', '%s', '%s' ) ", dbesc($family), dbesc($key), dbesc($dbvalue) @@ -100,7 +97,7 @@ function set_config($family,$key,$value) { return $ret; } - $ret = q("UPDATE `config` SET `v` = '%s' WHERE `cat` = '%s' AND `k` = '%s' LIMIT 1", + $ret = q("UPDATE config SET v = '%s' WHERE cat = '%s' AND k = '%s' LIMIT 1", dbesc($dbvalue), dbesc($family), dbesc($key) @@ -111,129 +108,255 @@ function set_config($family,$key,$value) { if($ret) return $value; return $ret; -}} - +} -if(! function_exists('load_pconfig')) { -function load_pconfig($uid,$family) { +function del_config($family,$key) { global $a; - $r = q("SELECT * FROM `pconfig` WHERE `cat` = '%s' AND `uid` = %d", + if(array_key_exists($family,$a->config) && array_key_exists($key,$a->config[$family])) + unset($a->config[$family][$key]); + $ret = q("DELETE FROM config WHERE cat = '%s' AND k = '%s' LIMIT 1", dbesc($family), + dbesc($key) + ); + return $ret; +} + + +function load_pconfig($uid,$family = '') { + global $a; + + if($uid === false) + return false; + + if(! array_key_exists($uid,$a->config)) + $a->config[$uid] = array(); + + // family is no longer used - load entire user config + + $r = q("SELECT * FROM `pconfig` WHERE `uid` = %d", intval($uid) ); - if(count($r)) { + + if($r) { foreach($r as $rr) { $k = $rr['k']; - $a->config[$uid][$family][$k] = $rr['v']; + $c = $rr['cat']; + if(! array_key_exists($c,$a->config[$uid])) { + $a->config[$uid][$c] = array(); + $a->config[$uid][$c]['config_loaded'] = true; + } + $a->config[$uid][$c][$k] = $rr['v']; } - } else if ($family != 'config') { - // Negative caching - $a->config[$uid][$family] = "!<unset>!"; - } -}} + } +} + -if(! function_exists('get_pconfig')) { function get_pconfig($uid,$family, $key, $instore = false) { global $a; - if(! $instore) { - // Looking if the whole family isn't set - if(isset($a->config[$uid][$family])) { - if($a->config[$uid][$family] === '!<unset>!') { - return false; - } - } + if($uid === false) + return false; - if(isset($a->config[$uid][$family][$key])) { - if($a->config[$uid][$family][$key] === '!<unset>!') { - return false; - } - return $a->config[$uid][$family][$key]; - } + if(! array_key_exists($uid,$a->config)) + load_pconfig($uid); + + if((! array_key_exists($family,$a->config[$uid])) || (! array_key_exists($key,$a->config[$uid][$family]))) + return false; + + return ((preg_match('|^a:[0-9]+:{.*}$|s', $a->config[$uid][$family][$key])) + ? unserialize($a->config[$uid][$family][$key]) + : $a->config[$uid][$family][$key] + ); +} + +function set_pconfig($uid,$family,$key,$value) { + + global $a; + + + // manage array value + $dbvalue = ((is_array($value)) ? serialize($value) : $value); + $dbvalue = ((is_bool($dbvalue)) ? intval($dbvalue) : $dbvalue); + + if(get_pconfig($uid,$family,$key) === false) { + if(! array_key_exists($uid,$a->config)) + $a->config[$uid] = array(); + if(! array_key_exists($family,$a->config[$uid])) + $a->config[$uid][$family] = array(); + + // keep a separate copy for all variables which were + // set in the life of this page. We need this to + // synchronise channel clones. + + if(! array_key_exists('transient',$a->config[$uid])) + $a->config[$uid]['transient'] = array(); + if(! array_key_exists($family,$a->config[$uid]['transient'])) + $a->config[$uid]['transient'][$family] = array(); + + $a->config[$uid][$family][$key] = $value; + $a->config[$uid]['transient'][$family][$key] = $value; + + $ret = q("INSERT INTO pconfig ( uid, cat, k, v ) VALUES ( %d, '%s', '%s', '%s' ) ", + intval($uid), + dbesc($family), + dbesc($key), + dbesc($dbvalue) + ); + if($ret) + return $value; + return $ret; } - $ret = q("SELECT `v` FROM `pconfig` WHERE `uid` = %d AND `cat` = '%s' AND `k` = '%s' LIMIT 1", + $ret = q("UPDATE pconfig SET v = '%s' WHERE uid = %d and cat = '%s' AND k = '%s' LIMIT 1", + dbesc($dbvalue), intval($uid), dbesc($family), dbesc($key) ); - if(count($ret)) { - $val = (preg_match("|^a:[0-9]+:{.*}$|s", $ret[0]['v'])?unserialize( $ret[0]['v']):$ret[0]['v']); - $a->config[$uid][$family][$key] = $val; - return $val; - } - else { - $a->config[$uid][$family][$key] = '!<unset>!'; - } - return false; -}} + // keep a separate copy for all variables which were + // set in the life of this page. We need this to + // synchronise channel clones. -if(! function_exists('del_config')) { -function del_config($family,$key) { + if(! array_key_exists('transient',$a->config[$uid])) + $a->config[$uid]['transient'] = array(); + if(! array_key_exists($family,$a->config[$uid]['transient'])) + $a->config[$uid]['transient'][$family] = array(); + + $a->config[$uid][$family][$key] = $value; + $a->config[$uid]['transient'][$family][$key] = $value; + + if($ret) + return $value; + return $ret; +} + + +function del_pconfig($uid,$family,$key) { global $a; - if(x($a->config[$family],$key)) - unset($a->config[$family][$key]); - $ret = q("DELETE FROM `config` WHERE `cat` = '%s' AND `k` = '%s' LIMIT 1", + if(x($a->config[$uid][$family],$key)) + unset($a->config[$uid][$family][$key]); + $ret = q("DELETE FROM pconfig WHERE uid = %d AND cat = '%s' AND k = '%s' LIMIT 1", + intval($uid), dbesc($family), dbesc($key) ); return $ret; -}} +} + +function load_xconfig($xchan,$family = '') { + global $a; -// Same as above functions except these are for personal config storage and take an -// additional $uid argument. + if(! $xchan) + return false; -if(! function_exists('set_pconfig')) { -function set_pconfig($uid,$family,$key,$value) { + if(! array_key_exists($xchan,$a->config)) + $a->config[$xchan] = array(); + + // family is no longer used. Entire config is loaded + + $r = q("SELECT * FROM `xconfig` WHERE `xchan` = '%s'", + dbesc($xchan) + ); + + if($r) { + foreach($r as $rr) { + $k = $rr['k']; + $c = $rr['cat']; + if(! array_key_exists($c,$a->config[$xchan])) { + $a->config[$xchan][$c] = array(); + $a->config[$xchan][$c]['config_loaded'] = true; + } + $a->config[$xchan][$c][$k] = $rr['v']; + } + } + +} + + + + +function get_xconfig($xchan,$family, $key) { global $a; - // manage array value - $dbvalue = (is_array($value)?serialize($value):$value); + if(! $xchan) + return false; - if(get_pconfig($uid,$family,$key,true) === false) { - $a->config[$uid][$family][$key] = $value; - $ret = q("INSERT INTO `pconfig` ( `uid`, `cat`, `k`, `v` ) VALUES ( %d, '%s', '%s', '%s' ) ", - intval($uid), + if(! array_key_exists($xchan,$a->config)) + load_xconfig($xchan); + + if((! array_key_exists($family,$a->config[$xchan])) || (! array_key_exists($key,$a->config[$xchan][$family]))) + return false; + + return ((preg_match('|^a:[0-9]+:{.*}$|s', $a->config[$xchan][$family][$key])) + ? unserialize($a->config[$xchan][$family][$key]) + : $a->config[$xchan][$family][$key] + ); + +} + + +function set_xconfig($xchan,$family,$key,$value) { + + global $a; + + // manage array value + $dbvalue = ((is_array($value)) ? serialize($value) : $value); + $dbvalue = ((is_bool($dbvalue)) ? intval($dbvalue) : $dbvalue); + + if(get_xconfig($xchan,$family,$key) === false) { + if(! array_key_exists($xchan,$a->config)) + $a->config[$xchan] = array(); + if(! array_key_exists($family,$a->config[$xchan])) + $a->config[$xchan][$family] = array(); + + $a->config[$xchan][$family][$key] = $value; + $ret = q("INSERT INTO xconfig ( xchan, cat, k, v ) VALUES ( '%s', '%s', '%s', '%s' ) ", + dbesc($xchan), dbesc($family), dbesc($key), dbesc($dbvalue) ); - if($ret) + if($ret) return $value; return $ret; } - $ret = q("UPDATE `pconfig` SET `v` = '%s' WHERE `uid` = %d AND `cat` = '%s' AND `k` = '%s' LIMIT 1", + + $ret = q("UPDATE xconfig SET v = '%s' WHERE xchan = '%s' and cat = '%s' AND k = '%s' LIMIT 1", dbesc($dbvalue), - intval($uid), + dbesc($xchan), dbesc($family), dbesc($key) ); - $a->config[$uid][$family][$key] = $value; + $a->config[$xchan][$family][$key] = $value; if($ret) return $value; return $ret; -}} -if(! function_exists('del_pconfig')) { -function del_pconfig($uid,$family,$key) { +} + + +function del_xconfig($xchan,$family,$key) { global $a; - if(x($a->config[$uid][$family],$key)) - unset($a->config[$uid][$family][$key]); - $ret = q("DELETE FROM `pconfig` WHERE `uid` = %d AND `cat` = '%s' AND `k` = '%s' LIMIT 1", - intval($uid), + if(x($a->config[$xchan][$family],$key)) + unset($a->config[$xchan][$family][$key]); + $ret = q("DELETE FROM `xconfig` WHERE `xchan` = '%s' AND `cat` = '%s' AND `k` = '%s' LIMIT 1", + dbesc($xchan), dbesc($family), dbesc($key) ); return $ret; -}} +} + + + diff --git a/include/contact_selectors.php b/include/contact_selectors.php index 76d005305..b56a77937 100644 --- a/include/contact_selectors.php +++ b/include/contact_selectors.php @@ -1,21 +1,19 @@ -<?php +<?php /** @file */ -function contact_profile_assign($current,$foreign_net) { +function contact_profile_assign($current) { $o = ''; - $disabled = (($foreign_net) ? ' disabled="true" ' : ''); + $o .= "<select id=\"contact-profile-selector\" name=\"profile-assign\" />\r\n"; - $o .= "<select id=\"contact-profile-selector\" $disabled name=\"profile-assign\" />\r\n"; + $r = q("SELECT profile_guid, profile_name FROM `profile` WHERE `uid` = %d", + intval($_SESSION['uid'])); - $r = q("SELECT `id`, `profile_name` FROM `profile` WHERE `uid` = %d", - intval($_SESSION['uid'])); - - if(count($r)) { + if($r) { foreach($r as $rr) { - $selected = (($rr['id'] == $current) ? " selected=\"selected\" " : ""); - $o .= "<option value=\"{$rr['id']}\" $selected >{$rr['profile_name']}</option>\r\n"; + $selected = (($rr['profile_guid'] == $current) ? " selected=\"selected\" " : ""); + $o .= "<option value=\"{$rr['profile_guid']}\" $selected >{$rr['profile_name']}</option>\r\n"; } } $o .= "</select>\r\n"; diff --git a/include/contact_widgets.php b/include/contact_widgets.php index 54e2ad967..ca212796f 100644 --- a/include/contact_widgets.php +++ b/include/contact_widgets.php @@ -1,4 +1,4 @@ -<?php +<?php /** @file */ function follow_widget() { diff --git a/include/conversation.php b/include/conversation.php index 368a0b0df..d80ff2cc7 100644 --- a/include/conversation.php +++ b/include/conversation.php @@ -1,4 +1,4 @@ -<?php +<?php /** @file */ require_once('include/items.php'); @@ -6,7 +6,7 @@ require_once('include/items.php'); // is identical to the code in mod/message.php for 'item_extract_images' and // 'item_redir_and_replace_images' -if(! function_exists('item_extract_images')) { + function item_extract_images($body) { $saved_image = array(); @@ -46,9 +46,9 @@ function item_extract_images($body) { $new_body = $new_body . $orig_body; return array('body' => $new_body, 'images' => $saved_image); -}} +} + -if(! function_exists('item_redir_and_replace_images')) { function item_redir_and_replace_images($body, $images, $cid) { $origbody = $body; @@ -81,7 +81,7 @@ function item_redir_and_replace_images($body, $images, $cid) { } return $newbody; -}} +} @@ -91,13 +91,9 @@ function item_redir_and_replace_images($body, $images, $cid) { function localize_item(&$item){ - $extracted = item_extract_images($item['body']); - if($extracted['images']) - $item['body'] = item_redir_and_replace_images($extracted['body'], $extracted['images'], $item['contact-id']); - if (activity_match($item['verb'],ACTIVITY_LIKE) || activity_match($item['verb'],ACTIVITY_DISLIKE)){ - - $obj= json_decode($item['object'],true); + + $obj = json_decode_plus($item['object']); if($obj['author'] && $obj['author']['link']) $author_link = get_rel_link($obj['author']['link'],'alternate'); @@ -108,32 +104,52 @@ function localize_item(&$item){ $item_url = get_rel_link($obj['link'],'alternate'); + $Bphoto = ''; + + switch($obj['type']) { + case ACTIVITY_OBJ_PHOTO: + $post_type = t('photo'); + break; + case ACTIVITY_OBJ_EVENT: + $post_type = t('event'); + break; + case ACTIVITY_OBJ_PERSON: + $post_type = t('channel'); + $author_name = $obj['title']; + if($obj['link']) { + $author_link = get_rel_link($obj['link'],'alternate'); + $Bphoto = get_rel_link($obj['link'],'photo'); + } + break; + case ACTIVITY_OBJ_THING: + $post_type = $obj['title']; + if($obj['owner']) { + if(array_key_exists('name',$obj['owner'])) + $obj['owner']['name']; + if(array_key_exists('link',$obj['owner'])) + $author_link = get_rel_link($obj['owner']['link'],'alternate'); + } + if($obj['link']) { + $Bphoto = get_rel_link($obj['link'],'photo'); + } + break; + + case ACTIVITY_OBJ_NOTE: + default: + $post_type = t('status'); + if($obj->id != $item['mid']) + $post_type = t('comment'); + break; + } // If we couldn't parse something useful, don't bother translating. // We need something better than zid here, probably magic_link(), but it needs writing if($author_link && $author_name && $item_url) { - - $author = '[url=' . chanlink_url($item['author']['xchan_url']) . ']' . $item['author']['xchan_name'] . '[/url]'; - $objauthor = '[url=' . chanlink_url($author_link) . ']' . $author_name . '[/url]'; + $author = '[zrl=' . chanlink_url($item['author']['xchan_url']) . ']' . $item['author']['xchan_name'] . '[/zrl]'; + $objauthor = '[zrl=' . chanlink_url($author_link) . ']' . $author_name . '[/zrl]'; - switch($obj->type) { - case ACTIVITY_OBJ_PHOTO: - $post_type = t('photo'); - break; - case ACTIVITY_OBJ_EVENT: - $post_type = t('event'); - break; - case ACTIVITY_OBJ_NOTE: - default: - if(! ($item_flags & ITEM_THREAD_TOP)) - $post_type = t('comment'); - else - $post_type = t('status'); - break; - } - - $plink = '[url=' . zid($item_url) . ']' . $post_type . '[/url]'; + $plink = '[zrl=' . zid($item_url) . ']' . $post_type . '[/zrl]'; if(activity_match($item['verb'],ACTIVITY_LIKE)) { $bodyverb = t('%1$s likes %2$s\'s %3$s'); @@ -142,6 +158,8 @@ function localize_item(&$item){ $bodyverb = t('%1$s doesn\'t like %2$s\'s %3$s'); } $item['body'] = $item['localize'] = sprintf($bodyverb, $author, $objauthor, $plink); + if($Bphoto != "") + $item['body'] .= "\n\n\n" . '[zrl=' . chanlink_url($author_link) . '][zmg=80x80]' . $Bphoto . '[/zmg][/zrl]'; } @@ -149,13 +167,14 @@ function localize_item(&$item){ if (activity_match($item['verb'],ACTIVITY_FRIEND)) { - if ($item['obj_type']=="" || $item['obj_type']!== ACTIVITY_OBJ_PERSON) return; + +// if ($item['obj_type']=="" || $item['obj_type']!== ACTIVITY_OBJ_PERSON) return; $Aname = $item['author']['xchan_name']; $Alink = $item['author']['xchan_url']; - $obj= json_decode($item['object'],true); + $obj= json_decode_plus($item['object']); $Blink = $Bphoto = ''; @@ -166,9 +185,9 @@ function localize_item(&$item){ $Bname = $obj['title']; - $A = '[url=' . chanlink_url($Alink) . ']' . $Aname . '[/url]'; - $B = '[url=' . chanlink_url($Blink) . ']' . $Bname . '[/url]'; - if ($Bphoto!="") $Bphoto = '[url=' . chanlink_url($Blink) . '][img=80x80]' . $Bphoto . '[/img][/url]'; + $A = '[zrl=' . chanlink_url($Alink) . ']' . $Aname . '[/zrl]'; + $B = '[zrl=' . chanlink_url($Blink) . ']' . $Bname . '[/zrl]'; + if ($Bphoto!="") $Bphoto = '[zrl=' . chanlink_url($Blink) . '][zmg=80x80]' . $Bphoto . '[/zmg][/zrl]'; $item['body'] = $item['localize'] = sprintf( t('%1$s is now connected with %2$s'), $A, $B); $item['body'] .= "\n\n\n" . $Bphoto; @@ -184,7 +203,7 @@ function localize_item(&$item){ $Alink = $item['author']['xchan_url']; - $obj= json_decode($item['object'],true); + $obj= json_decode_plus($item['object']); $Blink = $Bphoto = ''; @@ -194,9 +213,9 @@ function localize_item(&$item){ } $Bname = $obj['title']; - $A = '[url=' . chanlink_url($Alink) . ']' . $Aname . '[/url]'; - $B = '[url=' . chanlink_url($Blink) . ']' . $Bname . '[/url]'; - if ($Bphoto!="") $Bphoto = '[url=' . chanlink_url($Blink) . '][img=80x80]' . $Bphoto . '[/img][/url]'; + $A = '[zrl=' . chanlink_url($Alink) . ']' . $Aname . '[/zrl]'; + $B = '[zrl=' . chanlink_url($Blink) . ']' . $Bname . '[/zrl]'; + if ($Bphoto!="") $Bphoto = '[zrl=' . chanlink_url($Blink) . '][zmg=80x80]' . $Bphoto . '[/zmg][/zrl]'; // we can't have a translation string with three positions but no distinguishable text // So here is the translate string. @@ -221,7 +240,7 @@ function localize_item(&$item){ $Aname = $item['author']['xchan_name']; $Alink = $item['author']['xchan_url']; - $A = '[url=' . chanlink_url($Alink) . ']' . $Aname . '[/url]'; + $A = '[zrl=' . chanlink_url($Alink) . ']' . $Aname . '[/zrl]'; $txt = t('%1$s is currently %2$s'); @@ -233,13 +252,13 @@ function localize_item(&$item){ if (activity_match($item['verb'],ACTIVITY_TAG)) { $r = q("SELECT * from `item`,`contact` WHERE - `item`.`contact-id`=`contact`.`id` AND `item`.`uri`='%s';", - dbesc($item['parent_uri'])); + `item`.`contact-id`=`contact`.`id` AND `item`.`mid`='%s';", + dbesc($item['parent_mid'])); if(count($r)==0) return; $obj=$r[0]; - $author = '[url=' . zid($item['author-link']) . ']' . $item['author-name'] . '[/url]'; - $objauthor = '[url=' . zid($obj['author-link']) . ']' . $obj['author-name'] . '[/url]'; + $author = '[zrl=' . zid($item['author-link']) . ']' . $item['author-name'] . '[/zrl]'; + $objauthor = '[zrl=' . zid($obj['author-link']) . ']' . $obj['author-name'] . '[/zrl]'; switch($obj['verb']){ case ACTIVITY_POST: @@ -254,17 +273,17 @@ function localize_item(&$item){ default: if($obj['resource_id']){ $post_type = t('photo'); - $m=array(); preg_match("/\[url=([^]]*)\]/", $obj['body'], $m); + $m=array(); preg_match("/\[[zu]rl=([^]]*)\]/", $obj['body'], $m); $rr['plink'] = $m[1]; } else { $post_type = t('status'); } } - $plink = '[url=' . $obj['plink'] . ']' . $post_type . '[/url]'; + $plink = '[zrl=' . $obj['plink'] . ']' . $post_type . '[/zrl]'; $parsedobj = parse_xml_string($xmlhead.$item['object']); - $tag = sprintf('#[url=%s]%s[/url]', $parsedobj->id, $parsedobj->content); + $tag = sprintf('#[zrl=%s]%s[/zrl]', $parsedobj->id, $parsedobj->content); $item['body'] = sprintf( t('%1$s tagged %2$s\'s %3$s with %4$s'), $author, $objauthor, $plink, $tag ); } @@ -281,7 +300,7 @@ function localize_item(&$item){ $obj = parse_xml_string($xmlhead.$item['object']); if(strlen($obj->id)) { - $r = q("select * from item where uri = '%s' and uid = %d limit 1", + $r = q("select * from item where mid = '%s' and uid = %d limit 1", dbesc($obj->id), intval($item['uid']) ); @@ -289,9 +308,9 @@ function localize_item(&$item){ $target = $r[0]; $Bname = $target['author-name']; $Blink = $target['author-link']; - $A = '[url=' . zid($Alink) . ']' . $Aname . '[/url]'; - $B = '[url=' . zid($Blink) . ']' . $Bname . '[/url]'; - $P = '[url=' . $target['plink'] . ']' . t('post/item') . '[/url]'; + $A = '[zrl=' . zid($Alink) . ']' . $Aname . '[/zrl]'; + $B = '[zrl=' . zid($Blink) . ']' . $Bname . '[/zrl]'; + $P = '[zrl=' . $target['plink'] . ']' . t('post/item') . '[/zrl]'; $item['body'] = sprintf( t('%1$s marked %2$s\'s %3$s as favorite'), $A, $B, $P)."\n"; } @@ -299,25 +318,26 @@ function localize_item(&$item){ } */ +/* $matches = null; - if(strpos($item['body'],'[url') !== false) { - if(preg_match_all('/@\[url=(.*?)\]/is',$item['body'],$matches,PREG_SET_ORDER)) { + if(strpos($item['body'],'[zrl') !== false) { + if(preg_match_all('/@\[zrl=(.*?)\]/is',$item['body'],$matches,PREG_SET_ORDER)) { foreach($matches as $mtch) { if(! strpos($mtch[1],'zid=')) - $item['body'] = str_replace($mtch[0],'@[url=' . zid($mtch[1]). ']',$item['body']); + $item['body'] = str_replace($mtch[0],'@[zrl=' . zid($mtch[1]). ']',$item['body']); } } } - if(strpos($item['body'],'[img') !== false) { + if(strpos($item['body'],'[zmg') !== false) { // add zid's to public images - if(preg_match_all('/\[url=(.*?)\/photos\/(.*?)\/image\/(.*?)\]\[img(.*?)\]h(.*?)\[\/img\]\[\/url\]/is',$item['body'],$matches,PREG_SET_ORDER)) { + if(preg_match_all('/\[zrl=(.*?)\/photos\/(.*?)\/image\/(.*?)\]\[zmg(.*?)\]h(.*?)\[\/zmg\]\[\/zrl\]/is',$item['body'],$matches,PREG_SET_ORDER)) { foreach($matches as $mtch) { - $item['body'] = str_replace($mtch[0],'[url=' . zid( $mtch[1] . '/photos/' . $mtch[2] . '/image/' . $mtch[3]) . '][img' . $mtch[4] . ']h' . $mtch[5] . '[/img][/url]',$item['body']); + $item['body'] = str_replace($mtch[0],'[zrl=' . zid( $mtch[1] . '/photos/' . $mtch[2] . '/image/' . $mtch[3]) . '][zmg' . $mtch[4] . ']h' . $mtch[5] . '[/zmg][/zrl]',$item['body']); } } } - +*/ // add sparkle links to appropriate permalinks // $x = stristr($item['plink'],'/display/'); @@ -349,7 +369,9 @@ function count_descendants($item) { function visible_activity($item) { - if(activity_match($item['verb'],ACTIVITY_LIKE) || activity_match($item['verb'],ACTIVITY_DISLIKE)) + // likes can apply to other things besides posts. Check if they are post children, in which case we handle them specially + + if((activity_match($item['verb'],ACTIVITY_LIKE) || activity_match($item['verb'],ACTIVITY_DISLIKE)) && ($item['mid'] != $item['parent_mid'])) return false; return true; } @@ -365,7 +387,7 @@ function visible_activity($item) { * */ -if(!function_exists('conversation')) { + function conversation(&$a, $items, $mode, $update, $page_mode = 'traditional') { $tstart = dba_timer(); @@ -374,6 +396,22 @@ function conversation(&$a, $items, $mode, $update, $page_mode = 'traditional') { $ssl_state = ((local_user()) ? true : false); + if(local_user()) + load_pconfig(local_user(),''); + + $arr_blocked = null; + + if(local_user()) { + $str_blocked = get_pconfig(local_user(),'system','blocked'); + if($str_blocked) { + $arr_blocked = explode(',',$str_blocked); + for($x = 0; $x < count($arr_blocked); $x ++) + $arr_blocked[$x] = trim($arr_blocked[$x]); + } + + } + + $profile_owner = 0; $page_writeable = false; $live_update_div = ''; @@ -496,10 +534,24 @@ function conversation(&$a, $items, $mode, $update, $page_mode = 'traditional') { // "New Item View" on network page or search page results // - just loop through the items and format them minimally for display + //$tpl = get_markup_template('search_item.tpl'); $tpl = 'search_item.tpl'; foreach($items as $item) { + + if($arr_blocked) { + $blocked = false; + foreach($arr_blocked as $b) { + if(($b) && ($item['author_xchan'] == $b)) { + $blocked = true; + break; + } + } + if($blocked) + continue; + } + $threadsid++; $comment = ''; @@ -526,17 +578,6 @@ function conversation(&$a, $items, $mode, $update, $page_mode = 'traditional') { $tags=array(); $hashtags = array(); $mentions = array(); - foreach(explode(',',$item['tag']) as $tag){ - $tag = trim($tag); - if ($tag!="") { - $t = bbcode($tag); - $tags[] = $t; - if($t[0] == '#') - $hashtags[] = $t; - elseif($t[0] == '@') - $mentions[] = $t; - } - } $sp = false; $profile_link = best_link_url($item,$sp); @@ -592,7 +633,6 @@ function conversation(&$a, $items, $mode, $update, $page_mode = 'traditional') { $tmp_item = array( 'template' => $tpl, 'toplevel' => 'toplevel_item', - 'tags' => $tags, 'id' => (($preview) ? 'P0' : $item['item_id']), 'linktitle' => sprintf( t('View %s\'s profile @ %s'), $profile_name, $profile_url), 'profile_url' => $profile_link, @@ -617,7 +657,8 @@ function conversation(&$a, $items, $mode, $update, $page_mode = 'traditional') { 'ago' => relative_date($item['created']), 'app' => $item['app'], 'str_app' => sprintf( t(' from %s'), $item['app']), - 'localtime' => datetime_convert('UTC', date_default_timezone_get(), $item['created'], 'c'), + 'isotime' => datetime_convert('UTC', date_default_timezone_get(), $item['created'], 'c'), + 'localtime' => datetime_convert('UTC', date_default_timezone_get(), $item['created'], 'r'), 'location' => $location, 'indent' => '', 'owner_name' => $owner_name, @@ -632,7 +673,7 @@ function conversation(&$a, $items, $mode, $update, $page_mode = 'traditional') { 'like' => '', 'dislike' => '', 'comment' => '', - 'conv' => (($preview) ? '' : array('href'=> z_root() . '/display/' . $item['uri'], 'title'=> t('View in context'))), + 'conv' => (($preview) ? '' : array('href'=> z_root() . '/display/' . $item['mid'], 'title'=> t('View in context'))), 'previewing' => $previewing, 'wait' => t('Please wait'), 'thread_level' => 1, @@ -649,13 +690,21 @@ function conversation(&$a, $items, $mode, $update, $page_mode = 'traditional') { } else { + // Normal View +// logger('conv: items: ' . print_r($items,true)); require_once('include/ConversationObject.php'); require_once('include/ItemObject.php'); $conv = new Conversation($mode, $preview); + // In the display mode we don't have a profile owner. + + if($mode === 'display' && $items) + $conv->set_profile_owner($items[0]['uid']); + + // get all the topmost parents // this shouldn't be needed, as we should have only them in our array // But for now, this array respects the old style, just in case @@ -663,6 +712,18 @@ function conversation(&$a, $items, $mode, $update, $page_mode = 'traditional') { $threads = array(); foreach($items as $item) { + if($arr_blocked) { + $blocked = false; + foreach($arr_blocked as $b) { + if(($b) && ($item['author_xchan'] == $b)) { + $blocked = true; + break; + } + } + if($blocked) + continue; + } + // Can we put this after the visibility check? like_puller($a,$item,$alike,'like'); @@ -742,7 +803,7 @@ function conversation(&$a, $items, $mode, $update, $page_mode = 'traditional') { return $o; -}} +} function best_link_url($item) { @@ -775,7 +836,7 @@ function best_link_url($item) { } -if(! function_exists('item_photo_menu')){ + function item_photo_menu($item){ $a = get_app(); $contact = null; @@ -842,9 +903,9 @@ function item_photo_menu($item){ elseif ($v!="") $o .= "<li><a href=\"$v\">$k</a></li>\n"; } return $o; -}} +} + -if(! function_exists('like_puller')) { function like_puller($a,$item,&$arr,$mode) { $url = ''; @@ -861,7 +922,7 @@ function like_puller($a,$item,&$arr,$mode) { $url = zid($url); if(! $item['thr_parent']) - $item['thr_parent'] = $item['parent_uri']; + $item['thr_parent'] = $item['parent_mid']; if(! ((isset($arr[$item['thr_parent'] . '-l'])) && (is_array($arr[$item['thr_parent'] . '-l'])))) $arr[$item['thr_parent'] . '-l'] = array(); @@ -872,7 +933,7 @@ function like_puller($a,$item,&$arr,$mode) { $arr[$item['thr_parent'] . '-l'][] = '<a href="'. $url . '"'. $sparkle .'>' . $item['author']['xchan_name'] . '</a>'; } return; -}} +} // Format the like/dislike text for a profile item // $cnt = number of people who like/dislike the item @@ -881,7 +942,7 @@ function like_puller($a,$item,&$arr,$mode) { // $id = item id // returns formatted text -if(! function_exists('format_like')) { + function format_like($cnt,$arr,$type,$id) { $o = ''; if($cnt == 1) @@ -905,7 +966,7 @@ function format_like($cnt,$arr,$type,$id) { $o .= "\t" . '<div id="' . $type . 'list-' . $id . '" style="display: none;" >' . $str . '</div>'; } return $o; -}} +} function status_editor($a,$x,$popup=false) { @@ -948,6 +1009,9 @@ function status_editor($a,$x,$popup=false) { '$return_path' => $a->query_string, '$action' => $a->get_baseurl(true) . '/item', '$share' => (x($x,'button') ? $x['button'] : t('Share')), + '$webpage' => (x($x,'webpage') ? '1' : ''), + '$placeholdpagetitle' => t('Page link title'), + '$pagetitle' => (x($x,'pagetitle') ? $x['pagetitle'] : ''), '$upload' => t('Upload photo'), '$shortupload' => t('upload photo'), '$attach' => t('Attach file'), @@ -982,6 +1046,7 @@ function status_editor($a,$x,$popup=false) { '$emtitle' => t('Example: bob@example.com, mary@example.com'), '$lockstate' => $x['lockstate'], '$acl' => $x['acl'], + '$showacl' => ((array_key_exists('showacl',$x)) ? $x['showacl'] : 'yes'), '$bang' => $x['bang'], '$profile_uid' => $x['profile_uid'], '$preview' => ((feature_enabled($x['profile_uid'],'preview')) ? t('Preview') : ''), @@ -1004,12 +1069,12 @@ function get_item_children($arr, $parent) { foreach($arr as $item) { if($item['id'] != $item['parent']) { if(get_config('system','thread_allow')) { - // Fallback to parent_uri if thr_parent is not set + // Fallback to parent_mid if thr_parent is not set $thr_parent = $item['thr_parent']; if($thr_parent == '') - $thr_parent = $item['parent_uri']; + $thr_parent = $item['parent_mid']; - if($thr_parent == $parent['uri']) { + if($thr_parent == $parent['mid']) { $item['children'] = get_item_children($arr, $item); $children[] = $item; } @@ -1057,6 +1122,8 @@ function conv_sort($arr,$order) { usort($parents,'sort_thr_created'); elseif(stristr($order,'commented')) usort($parents,'sort_thr_commented'); + elseif(stristr($order,'ascending')) + usort($parents,'sort_thr_created_rev'); if(count($parents)) foreach($parents as $i=>$_x) diff --git a/include/cronhooks.php b/include/cronhooks.php index 9ff8141c4..a314593d2 100644 --- a/include/cronhooks.php +++ b/include/cronhooks.php @@ -1,4 +1,4 @@ -<?php +<?php /** @file */ require_once('boot.php'); require_once('include/cli_startup.php'); diff --git a/include/crypto.php b/include/crypto.php index a646910a1..a0268ef93 100644 --- a/include/crypto.php +++ b/include/crypto.php @@ -1,7 +1,4 @@ -<?php - -require_once('library/ASNValue.class.php'); -require_once('library/asn1.php'); +<?php /** @file */ function rsa_sign($data,$key,$alg = 'sha256') { if(! $key) @@ -20,164 +17,6 @@ function rsa_verify($data,$sig,$key,$alg = 'sha256') { return $verify; } - -function DerToPem($Der, $Private=false) -{ - //Encode: - $Der = base64_encode($Der); - //Split lines: - $lines = str_split($Der, 65); - $body = implode("\n", $lines); - //Get title: - $title = $Private? 'RSA PRIVATE KEY' : 'PUBLIC KEY'; - //Add wrapping: - $result = "-----BEGIN {$title}-----\n"; - $result .= $body . "\n"; - $result .= "-----END {$title}-----\n"; - - return $result; -} - -function DerToRsa($Der) -{ - //Encode: - $Der = base64_encode($Der); - //Split lines: - $lines = str_split($Der, 64); - $body = implode("\n", $lines); - //Get title: - $title = 'RSA PUBLIC KEY'; - //Add wrapping: - $result = "-----BEGIN {$title}-----\n"; - $result .= $body . "\n"; - $result .= "-----END {$title}-----\n"; - - return $result; -} - - -function pkcs8_encode($Modulus,$PublicExponent) { - //Encode key sequence - $modulus = new ASNValue(ASNValue::TAG_INTEGER); - $modulus->SetIntBuffer($Modulus); - $publicExponent = new ASNValue(ASNValue::TAG_INTEGER); - $publicExponent->SetIntBuffer($PublicExponent); - $keySequenceItems = array($modulus, $publicExponent); - $keySequence = new ASNValue(ASNValue::TAG_SEQUENCE); - $keySequence->SetSequence($keySequenceItems); - //Encode bit string - $bitStringValue = $keySequence->Encode(); - $bitStringValue = chr(0x00) . $bitStringValue; //Add unused bits byte - $bitString = new ASNValue(ASNValue::TAG_BITSTRING); - $bitString->Value = $bitStringValue; - //Encode body - $bodyValue = "\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01\x05\x00" . $bitString->Encode(); - $body = new ASNValue(ASNValue::TAG_SEQUENCE); - $body->Value = $bodyValue; - //Get DER encoded public key: - $PublicDER = $body->Encode(); - return $PublicDER; -} - - -function pkcs1_encode($Modulus,$PublicExponent) { - //Encode key sequence - $modulus = new ASNValue(ASNValue::TAG_INTEGER); - $modulus->SetIntBuffer($Modulus); - $publicExponent = new ASNValue(ASNValue::TAG_INTEGER); - $publicExponent->SetIntBuffer($PublicExponent); - $keySequenceItems = array($modulus, $publicExponent); - $keySequence = new ASNValue(ASNValue::TAG_SEQUENCE); - $keySequence->SetSequence($keySequenceItems); - //Encode bit string - $bitStringValue = $keySequence->Encode(); - return $bitStringValue; -} - - -function metopem($m,$e) { - $der = pkcs8_encode($m,$e); - $key = DerToPem($der,false); - return $key; -} - - -function pubrsatome($key,&$m,&$e) { - require_once('library/asn1.php'); - - $lines = explode("\n",$key); - unset($lines[0]); - unset($lines[count($lines)]); - $x = base64_decode(implode('',$lines)); - - $r = ASN_BASE::parseASNString($x); - - $m = base64url_decode($r[0]->asnData[0]->asnData); - $e = base64url_decode($r[0]->asnData[1]->asnData); -} - - -function rsatopem($key) { - pubrsatome($key,$m,$e); - return(metopem($m,$e)); -} - -function pemtorsa($key) { - pemtome($key,$m,$e); - return(metorsa($m,$e)); -} - -function pemtome($key,&$m,&$e) { - $lines = explode("\n",$key); - unset($lines[0]); - unset($lines[count($lines)]); - $x = base64_decode(implode('',$lines)); - - $r = ASN_BASE::parseASNString($x); - - $m = base64url_decode($r[0]->asnData[1]->asnData[0]->asnData[0]->asnData); - $e = base64url_decode($r[0]->asnData[1]->asnData[0]->asnData[1]->asnData); -} - -function metorsa($m,$e) { - $der = pkcs1_encode($m,$e); - $key = DerToRsa($der); - return $key; -} - -function salmon_key($pubkey) { - pemtome($pubkey,$m,$e); - return 'RSA' . '.' . base64url_encode($m,true) . '.' . base64url_encode($e,true) ; -} - - - -if(! function_exists('aes_decrypt')) { -function aes_decrypt($val,$ky) -{ - $key="\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; - for($a=0;$a<strlen($ky);$a++) - $key[$a%16]=chr(ord($key[$a%16]) ^ ord($ky[$a])); - $mode = MCRYPT_MODE_ECB; - $enc = MCRYPT_RIJNDAEL_128; - $dec = @mcrypt_decrypt($enc, $key, $val, $mode, @mcrypt_create_iv( @mcrypt_get_iv_size($enc, $mode), MCRYPT_DEV_URANDOM ) ); - return rtrim($dec,(( ord(substr($dec,strlen($dec)-1,1))>=0 and ord(substr($dec, strlen($dec)-1,1))<=16)? chr(ord( substr($dec,strlen($dec)-1,1))):null)); -}} - - -if(! function_exists('aes_encrypt')) { -function aes_encrypt($val,$ky) -{ - $key="\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; - for($a=0;$a<strlen($ky);$a++) - $key[$a%16]=chr(ord($key[$a%16]) ^ ord($ky[$a])); - $mode=MCRYPT_MODE_ECB; - $enc=MCRYPT_RIJNDAEL_128; - $val=str_pad($val, (16*(floor(strlen($val) / 16)+(strlen($val) % 16==0?2:1))), chr(16-(strlen($val) % 16))); - return mcrypt_encrypt($enc, $key, $val, $mode, mcrypt_create_iv( mcrypt_get_iv_size($enc, $mode), MCRYPT_DEV_URANDOM)); -}} - - function pkcs5_pad ($text, $blocksize) { $pad = $blocksize - (strlen($text) % $blocksize); @@ -211,11 +50,17 @@ function AES256CBC_decrypt($data,$key,$iv) { } function aes_encapsulate($data,$pubkey) { + if(! $pubkey) + logger('aes_encapsulate: no key. data: ' . $data); $key = random_string(32,RANDOM_STRING_TEXT); $iv = random_string(16,RANDOM_STRING_TEXT); $result['data'] = base64url_encode(AES256CBC_encrypt($data,$key,$iv),true); - openssl_public_encrypt($key,$k,$pubkey); - $result['key'] = base64url_encode($k,true); + // log the offending call so we can track it down + if(! openssl_public_encrypt($key,$k,$pubkey)) { + $x = debug_backtrace(); + logger('aes_encapsulate: RSA failed. ' . print_r($x[0],true)); + } + $result['key'] = base64url_encode($k,true); openssl_public_encrypt($iv,$i,$pubkey); $result['iv'] = base64url_encode($i,true); return $result; diff --git a/include/datetime.php b/include/datetime.php index a573e43a2..94c2e4f1c 100644 --- a/include/datetime.php +++ b/include/datetime.php @@ -1,8 +1,8 @@ -<?php +<?php /** @file */ // two-level sort for timezones. -if(! function_exists('timezone_cmp')) { + function timezone_cmp($a, $b) { if(strstr($a,'/') && strstr($b,'/')) { if ( t($a) == t($b)) return 0; @@ -12,10 +12,10 @@ function timezone_cmp($a, $b) { if(strstr($b,'/')) return 1; if ( t($a) == t($b)) return 0; return ( t($a) < t($b)) ? -1 : 1; -}} +} // emit a timezone selector grouped (primarily) by continent -if(! function_exists('select_timezone')) { + function select_timezone($current = 'America/Los_Angeles') { $timezone_identifiers = DateTimeZone::listIdentifiers(); @@ -52,13 +52,13 @@ function select_timezone($current = 'America/Los_Angeles') { } $o .= '</optgroup></select>'; return $o; -}} +} // return a select using 'field_select_raw' template, with timezones // groupped (primarily) by continent // arguments follow convetion as other field_* template array: // 'name', 'label', $value, 'help' -if (!function_exists('field_timezone')){ + function field_timezone($name='timezone', $label='', $current = 'America/Los_Angeles', $help){ $options = select_timezone($current); $options = str_replace('<select id="timezone_select" name="timezone">','', $options); @@ -69,7 +69,7 @@ function field_timezone($name='timezone', $label='', $current = 'America/Los_Ang '$field' => array($name, $label, $current, $help, $options), )); -}} +} // General purpose date parse/convert function. // $from = source timezone @@ -77,7 +77,7 @@ function field_timezone($name='timezone', $label='', $current = 'America/Los_Ang // $s = some parseable date/time string // $fmt = output format -if(! function_exists('datetime_convert')) { + function datetime_convert($from = 'UTC', $to = 'UTC', $s = 'now', $fmt = "Y-m-d H:i:s") { // Defaults to UTC if nothing is set, but throws an exception if set to empty string. @@ -124,7 +124,7 @@ function datetime_convert($from = 'UTC', $to = 'UTC', $s = 'now', $fmt = "Y-m-d $d->setTimeZone($to_obj); return($d->format($fmt)); -}} +} // wrapper for date selector, tailored for use in birthday fields @@ -180,7 +180,7 @@ function datesel_format($f) { // $m = already selected month // $d = already selected day -if(! function_exists('datesel')) { + function datesel($f,$pre,$ymin,$ymax,$allow_blank,$y,$m,$d) { $o = ''; @@ -231,9 +231,9 @@ function datesel($f,$pre,$ymin,$ymax,$allow_blank,$y,$m,$d) { $o .= "</select>"; return $o; -}} +} + -if(! function_exists('timesel')) { function timesel($pre,$h,$m) { $o = ''; @@ -250,7 +250,7 @@ function timesel($pre,$h,$m) { $o .= "</select>"; return $o; -}} +} @@ -264,7 +264,7 @@ function timesel($pre,$h,$m) { // Results relative to current timezone // Limited to range of timestamps -if(! function_exists('relative_date')) { + function relative_date($posted_date,$format = null) { $localtime = datetime_convert('UTC',date_default_timezone_get(),$posted_date); @@ -300,7 +300,7 @@ function relative_date($posted_date,$format = null) { return sprintf( $format,$r, (($r == 1) ? $str[0] : $str[1])); } } -}} +} @@ -341,7 +341,7 @@ function age($dob,$owner_tz = '',$viewer_tz = '') { // $month[1] = 'January'; // to match human usage. -if(! function_exists('get_dim')) { + function get_dim($y,$m) { $dim = array( 0, @@ -353,7 +353,7 @@ function get_dim($y,$m) { if(((($y % 4) == 0) && (($y % 100) != 0)) || (($y % 400) == 0)) return 29; return $dim[2]; -}} +} // Returns the first day in month for a given month, year @@ -361,11 +361,11 @@ function get_dim($y,$m) { // returns 0 = Sunday through 6 = Saturday // Months start at 1. -if(! function_exists('get_first_dim')) { + function get_first_dim($y,$m) { $d = sprintf('%04d-%02d-01 00:00', intval($y), intval($m)); return datetime_convert('UTC','UTC',$d,'w'); -}} +} // output a calendar for the given month, year. // if $links are provided (array), e.g. $links[12] => 'http://mylink' , @@ -376,8 +376,6 @@ function get_first_dim($y,$m) { // TODO: provide (prev,next) links, define class variations for different size calendars - -if(! function_exists('cal')) { function cal($y = 0,$m = 0, $links = false, $class='') { @@ -442,6 +440,6 @@ function cal($y = 0,$m = 0, $links = false, $class='') { $o .= '</tr></table>'."\r\n"; return $o; -}} +} diff --git a/include/dba.php b/include/dba.php deleted file mode 100644 index d1502af12..000000000 --- a/include/dba.php +++ /dev/null @@ -1,289 +0,0 @@ -<?php - -require_once('include/datetime.php'); - -/** - * - * MySQL database class - * - * For debugging, insert 'dbg(1);' anywhere in the program flow. - * dbg(0); will turn it off. Logging is performed at LOGGER_DATA level. - * When logging, all binary info is converted to text and html entities are escaped so that - * the debugging stream is safe to view within both terminals and web pages. - * - */ - -if(! class_exists('dba')) { -class dba { - - private $debug = 0; - private $db; - public $mysqli = true; - public $connected = false; - public $error = false; - - function __construct($server,$user,$pass,$db,$install = false) { - - $server = trim($server); - $user = trim($user); - $pass = trim($pass); - $db = trim($db); - - if (!(strlen($server) && strlen($user))){ - $this->connected = false; - $this->db = null; - return; - } - - if($install) { - if(strlen($server) && ($server !== 'localhost') && ($server !== '127.0.0.1')) { - if(! dns_get_record($server, DNS_A + DNS_CNAME + DNS_PTR)) { - $this->error = sprintf( t('Cannot locate DNS info for database server \'%s\''), $server); - $this->connected = false; - $this->db = null; - return; - } - } - } - - if(class_exists('mysqli')) { - $this->db = @new mysqli($server,$user,$pass,$db); - if(! mysqli_connect_errno()) { - $this->connected = true; - } - } - else { - $this->mysqli = false; - $this->db = mysql_connect($server,$user,$pass); - if($this->db && mysql_select_db($db,$this->db)) { - $this->connected = true; - } - } - if(! $this->connected) { - $this->db = null; - if(! $install) - system_unavailable(); - } - } - - public function getdb() { - return $this->db; - } - - public function q($sql) { - global $a; - - if((! $this->db) || (! $this->connected)) - return false; - - $this->error = ''; - - if(x($a->config,'system') && x($a->config['system'],'db_log')) - $stamp1 = microtime(true); - - if($this->mysqli) - $result = @$this->db->query($sql); - else - $result = @mysql_query($sql,$this->db); - - if(x($a->config,'system') && x($a->config['system'],'db_log')) { - $stamp2 = microtime(true); - $duration = round($stamp2-$stamp1, 3); - if ($duration > $a->config["system"]["db_loglimit"]) { - $backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); - @file_put_contents($a->config["system"]["db_log"], $duration."\t". - basename($backtrace[1]["file"])."\t". - $backtrace[1]["line"]."\t".$backtrace[2]["function"]."\t". - substr($sql, 0, 2000)."\n", FILE_APPEND); - } - } - - if($this->mysqli) { - if($this->db->errno) - $this->error = $this->db->error; - } - elseif(mysql_errno($this->db)) - $this->error = mysql_error($this->db); - - if(strlen($this->error)) { - logger('dba: ' . $this->error); - } - - if($this->debug) { - - $mesg = ''; - - if($result === false) - $mesg = 'false'; - elseif($result === true) - $mesg = 'true'; - else { - if($this->mysqli) - $mesg = $result->num_rows . ' results' . EOL; - else - $mesg = mysql_num_rows($result) . ' results' . EOL; - } - - $str = 'SQL = ' . printable($sql) . EOL . 'SQL returned ' . $mesg - . (($this->error) ? ' error: ' . $this->error : '') - . EOL; - - logger('dba: ' . $str ); - } - - /** - * If dbfail.out exists, we will write any failed calls directly to it, - * regardless of any logging that may or may nor be in effect. - * These usually indicate SQL syntax errors that need to be resolved. - */ - - if($result === false) { - logger('dba: ' . printable($sql) . ' returned false.' . "\n" . $this->error); - if(file_exists('dbfail.out')) - file_put_contents('dbfail.out', datetime_convert() . "\n" . printable($sql) . ' returned false' . "\n" . $this->error . "\n", FILE_APPEND); - } - - if(($result === true) || ($result === false)) - return $result; - - $r = array(); - if($this->mysqli) { - if($result->num_rows) { - while($x = $result->fetch_array(MYSQLI_ASSOC)) - $r[] = $x; - $result->free_result(); - } - } - else { - if(mysql_num_rows($result)) { - while($x = mysql_fetch_array($result, MYSQL_ASSOC)) - $r[] = $x; - mysql_free_result($result); - } - } - - - if($this->debug) - logger('dba: ' . printable(print_r($r, true))); - return($r); - } - - public function dbg($dbg) { - $this->debug = $dbg; - } - - public function escape($str) { - if($this->db && $this->connected) { - if($this->mysqli) - return @$this->db->real_escape_string($str); - else - return @mysql_real_escape_string($str,$this->db); - } - } - - function __destruct() { - if ($this->db) - if($this->mysqli) - $this->db->close(); - else - mysql_close($this->db); - } -}} - -if(! function_exists('printable')) { -function printable($s) { - $s = preg_replace("~([\x01-\x08\x0E-\x0F\x10-\x1F\x7F-\xFF])~",".", $s); - $s = str_replace("\x00",'.',$s); - if(x($_SERVER,'SERVER_NAME')) - $s = escape_tags($s); - return $s; -}} - -// Procedural functions -if(! function_exists('dbg')) { -function dbg($state) { - global $db; - if($db) - $db->dbg($state); -}} - -if(! function_exists('dbesc')) { -function dbesc($str) { - global $db; - if($db && $db->connected) - return($db->escape($str)); - else - return(str_replace("'","\\'",$str)); -}} - - - -// Function: q($sql,$args); -// Description: execute SQL query with printf style args. -// Example: $r = q("SELECT * FROM `%s` WHERE `uid` = %d", -// 'user', 1); - -if(! function_exists('q')) { -function q($sql) { - - global $db; - $args = func_get_args(); - unset($args[0]); - - if($db && $db->connected) { - $stmt = vsprintf($sql,$args); - if($stmt === false) - logger('dba: vsprintf error: ' . print_r(debug_backtrace(),true)); - return $db->q($stmt); - } - - /** - * - * This will happen occasionally trying to store the - * session data after abnormal program termination - * - */ - logger('dba: no database: ' . print_r($args,true)); - return false; - -}} - -/** - * - * Raw db query, no arguments - * - */ - -if(! function_exists('dbq')) { -function dbq($sql) { - - global $db; - if($db && $db->connected) - $ret = $db->q($sql); - else - $ret = false; - return $ret; -}} - - -// Caller is responsible for ensuring that any integer arguments to -// dbesc_array are actually integers and not malformed strings containing -// SQL injection vectors. All integer array elements should be specifically -// cast to int to avoid trouble. - - -if(! function_exists('dbesc_array_cb')) { -function dbesc_array_cb(&$item, $key) { - if(is_string($item)) - $item = dbesc($item); -}} - - -if(! function_exists('dbesc_array')) { -function dbesc_array(&$arr) { - if(is_array($arr) && count($arr)) { - array_walk($arr,'dbesc_array_cb'); - } -}} - - diff --git a/include/dba/dba_driver.php b/include/dba/dba_driver.php new file mode 100755 index 000000000..c829c3714 --- /dev/null +++ b/include/dba/dba_driver.php @@ -0,0 +1,167 @@ +<?php /** @file */ + +function dba_factory($server, $port,$user,$pass,$db,$install = false) { + $dba = null; + + if(class_exists('mysqli')) { + if (is_null($port)) $port = ini_get("mysqli.default_port"); + require_once('include/dba/dba_mysqli.php'); + $dba = new dba_mysqli($server, $port,$user,$pass,$db,$install); + } + else { + if (is_null($port)) $port = "3306"; + require_once('include/dba/dba_mysql.php'); + $dba = new dba_mysql($server, $port,$user,$pass,$db,$install); + } + + return $dba; +} + + +abstract class dba_driver { + + protected $debug = 0; + protected $db; + public $connected = false; + public $error = false; + + abstract function connect($server, $port, $user,$pass,$db); + abstract function q($sql); + abstract function escape($str); + abstract function close(); + + function __construct($server, $port, $user,$pass,$db,$install = false) { + if(($install) && (! $this->install($server, $port, $user,$pass,$db))) { + return; + } + $this->connect($server, $port, $user,$pass,$db); + } + + + function install($server,$user,$pass,$db) { + if (!(strlen($server) && strlen($user))){ + $this->connected = false; + $this->db = null; + return false; + } + + if(strlen($server) && ($server !== 'localhost') && ($server !== '127.0.0.1')) { + if(! dns_get_record($server, DNS_A + DNS_CNAME + DNS_PTR)) { + $this->error = sprintf( t('Cannot locate DNS info for database server \'%s\''), $server); + $this->connected = false; + $this->db = null; + return false; + } + } + return true; + } + + + function dbg($dbg) { + $this->debug = $dbg; + } + + function __destruct() { + if($this->db && $this->connected) { + $this->close(); + } + } + +} + + + +function printable($s) { + $s = preg_replace("~([\x01-\x08\x0E-\x0F\x10-\x1F\x7F-\xFF])~",".", $s); + $s = str_replace("\x00",'.',$s); + if(x($_SERVER,'SERVER_NAME')) + $s = escape_tags($s); + return $s; +} + +// Procedural functions + +function dbg($state) { + global $db; + if($db) + $db->dbg($state); +} + + +function dbesc($str) { + global $db; + if($db && $db->connected) + return($db->escape($str)); + else + return(str_replace("'","\\'",$str)); +} + + + +// Function: q($sql,$args); +// Description: execute SQL query with printf style args. +// Example: $r = q("SELECT * FROM `%s` WHERE `uid` = %d", +// 'user', 1); + + +function q($sql) { + + global $db; + $args = func_get_args(); + unset($args[0]); + + if($db && $db->connected) { + $stmt = vsprintf($sql,$args); + if($stmt === false) + logger('dba: vsprintf error: ' . print_r(debug_backtrace(),true)); + return $db->q($stmt); + } + + /** + * + * This will happen occasionally trying to store the + * session data after abnormal program termination + * + */ + logger('dba: no database: ' . print_r($args,true)); + return false; + +} + +/** + * + * Raw db query, no arguments + * + */ + + +function dbq($sql) { + + global $db; + if($db && $db->connected) + $ret = $db->q($sql); + else + $ret = false; + return $ret; +} + + +// Caller is responsible for ensuring that any integer arguments to +// dbesc_array are actually integers and not malformed strings containing +// SQL injection vectors. All integer array elements should be specifically +// cast to int to avoid trouble. + + + +function dbesc_array_cb(&$item, $key) { + if(is_string($item)) + $item = dbesc($item); +} + + + +function dbesc_array(&$arr) { + if(is_array($arr) && count($arr)) { + array_walk($arr,'dbesc_array_cb'); + } +} diff --git a/include/dba/dba_mysql.php b/include/dba/dba_mysql.php new file mode 100755 index 000000000..f5a2a47ba --- /dev/null +++ b/include/dba/dba_mysql.php @@ -0,0 +1,63 @@ +<?php + +require_once('include/dba/dba_driver.php'); + + +class dba_mysql extends dba_driver { + + function connect($server, $port, $user,$pass,$db) { + $this->db = mysql_connect($server.":".$port,$user,$pass); + if($this->db && mysql_select_db($db,$this->db)) { + $this->connected = true; + } + if($this->connected) { + return true; + } + return false; + } + + + function q($sql) { + if((! $this->db) || (! $this->connected)) + return false; + + $this->error = ''; + $result = @mysql_query($sql,$this->db); + + + if(mysql_errno($this->db)) + $this->error = mysql_error($this->db); + + if($result === false || $this->error) { + logger('dba_mysql: ' . printable($sql) . ' returned false.' . "\n" . $this->error); + if(file_exists('dbfail.out')) + file_put_contents('dbfail.out', datetime_convert() . "\n" . printable($sql) . ' returned false' . "\n" . $this->error . "\n", FILE_APPEND); + } + + if(($result === true) || ($result === false)) + return $result; + + $r = array(); + if(mysql_num_rows($result)) { + while($x = mysql_fetch_array($result,MYSQL_ASSOC)) + $r[] = $x; + mysql_free_result($result); + if($this->debug) + logger('dba_mysql: ' . printable(print_r($r,true))); + } + return $r; + } + + function escape($str) { + if($this->db && $this->connected) { + return @mysql_real_escape_string($str,$this->db); + } + } + + function close() { + if($this->db) + mysql_close($this->db); + $this->connected = false; + } + +} diff --git a/include/dba/dba_mysqli.php b/include/dba/dba_mysqli.php new file mode 100755 index 000000000..f1a50cc3f --- /dev/null +++ b/include/dba/dba_mysqli.php @@ -0,0 +1,76 @@ +<?php /** @file */ + +require_once('include/dba/dba_driver.php'); + +class dba_mysqli extends dba_driver { + + function connect($server, $port, $user,$pass,$db) { + if($port) + $this->db = new mysqli($server,$user,$pass,$db, $port); + else + $this->db = new mysqli($server,$user,$pass,$db); + + if(! mysqli_connect_errno()) { + $this->connected = true; + } + if($this->connected) { + return true; + } + $this->error = $this->db->connect_error; + return false; + } + + function q($sql) { + if((! $this->db) || (! $this->connected)) + return false; + + $this->error = ''; + $result = $this->db->query($sql); + + if($this->db->errno) + $this->error = $this->db->error; + + + if($this->error) { + logger('dba_mysqli: ERROR: ' . printable($sql) . "\n" . $this->error); + if(file_exists('dbfail.out')) { + file_put_contents('dbfail.out', datetime_convert() . "\n" . printable($sql) . "\n" . $this->error . "\n", FILE_APPEND); + } + } + + if(($result === true) || ($result === false)) { + if($this->debug) { + logger('dba_mysqli: DEBUG: returns ' . (($result) ? 'true' : 'false')); + } + return $result; + } + + if($this->debug) { + logger('dba_mysqli: DEBUG: ' . printable($sql) . ' returned ' . $result->num_rows . ' results.'); + } + + $r = array(); + if($result->num_rows) { + while($x = $result->fetch_array(MYSQLI_ASSOC)) + $r[] = $x; + $result->free_result(); + if($this->debug) { + logger('dba_mysqli: ' . printable(print_r($r,true))); + } + } + return $r; + } + + function escape($str) { + if($this->db && $this->connected) { + return @$this->db->real_escape_string($str); + } + } + + function close() { + if($this->db) + $this->db->close(); + $this->connected = flase; + } + +}
\ No newline at end of file diff --git a/include/deliver.php b/include/deliver.php index 471ea580d..547d009cc 100644 --- a/include/deliver.php +++ b/include/deliver.php @@ -1,4 +1,4 @@ -<?php +<?php /** @file */ require_once('include/cli_startup.php'); require_once('include/zot.php'); @@ -23,11 +23,14 @@ function deliver_run($argv, $argc) { if($r[0]['outq_posturl'] === z_root() . '/post') { // local delivery // we should probably batch these and save a few delivery processes - $msg = array('body' => json_encode(array('pickup' => array(array('notify' => json_decode($r[0]['outq_notify'],true),'message' => json_decode($r[0]['outq_msg'],true)))))); - zot_import($msg); - $r = q("delete from outq where outq_hash = '%s' limit 1", - dbesc($argv[$x]) - ); + // If there is no outq_msg, this is a refresh_all message which does not require local handling + if($r[0]['outq_msg']) { + $msg = array('body' => json_encode(array('pickup' => array(array('notify' => json_decode($r[0]['outq_notify'],true),'message' => json_decode($r[0]['outq_msg'],true)))))); + zot_import($msg); + $r = q("delete from outq where outq_hash = '%s' limit 1", + dbesc($argv[$x]) + ); + } } else { $result = zot_zot($r[0]['outq_posturl'],$r[0]['outq_notify']); diff --git a/include/dir_fns.php b/include/dir_fns.php index be1bcd503..0b678fd91 100644 --- a/include/dir_fns.php +++ b/include/dir_fns.php @@ -1,4 +1,4 @@ -<?php +<?php /** @file */ require_once('include/permissions.php'); @@ -6,11 +6,54 @@ function find_upstream_directory($dirmode) { return ''; } + +function sync_directories($dirmode) { + + if($dirmode == DIRECTORY_MODE_STANDALONE || $dirmode == DIRECTORY_MODE_NORMAL) + return; + + $r = q("select * from site where (site_flags & %d) and site_url != '%s'", + intval(DIRECTORY_MODE_PRIMARY), + dbesc(z_root()) + ); + + // If there are no directory servers, setup the fallback master + + if((! $r) && (z_root() != DIRECTORY_FALLBACK_MASTER)) { + $r = array( + 'site_url' => DIRECTORY_FALLBACK_MASTER, + 'site_flags' => DIRECTORY_MODE_PRIMARY, + 'site_update' => '0000-00-00 00:00:00', + 'site_directory' => DIRECTORY_FALLBACK_MASTER . '/dirsearch' + ); + $x = q("insert into site ( site_url, site_flags, site_update, site_directory ) + values ( '%s', %d', '%s', '%s' ) ", + dbesc($r[0]['site_url']), + intval($r[0]['site_flags']), + dbesc($r[0]['site_update']), + dbesc($r[0]['site_directory']) + ); + + } + + + + + + +} + + + + + + + function syncdirs($uid) { logger('syncdirs', LOGGER_DEBUG); - $p = q("select channel.channel_hash, profile.* from profile left join channel on channel_id = uid where uid = %d and is_default = 1", + $p = q("select channel.channel_hash, channel_timezone, profile.* from profile left join channel on channel_id = uid where uid = %d and is_default = 1", intval($uid) ); @@ -21,6 +64,9 @@ function syncdirs($uid) { $profile['description'] = $p[0]['pdesc']; $profile['birthday'] = $p[0]['dob']; + if($age = age($p[0]['dob'],$p[0]['channel_timezone'],'')) + $profile['age'] = $age; + $profile['gender'] = $p[0]['gender']; $profile['marital'] = $p[0]['marital']; $profile['sexual'] = $p[0]['sexual']; @@ -39,6 +85,30 @@ function syncdirs($uid) { $profile['keywords'] = $tags; } + $hidden = (1 - intval($p[0]['publish'])); + + logger('hidden: ' . $hidden); + + $r = q("select xchan_flags from xchan where xchan_hash = '%s' limit 1", + dbesc($p[0]['channel_hash']) + ); + + // Be careful - XCHAN_FLAGS_HIDDEN should evaluate to 1 + if(($r[0]['xchan_flags'] & XCHAN_FLAGS_HIDDEN) != $hidden) + $new_flags = $r[0]['xchan_flags'] ^ XCHAN_FLAGS_HIDDEN; + else + $new_flags = $r[0]['xchan_flags']; + + if($new_flags != $r[0]['xchan_flags']) { + + $r = q("update xchan set xchan_flags = %d where xchan_hash = '%s' limit 1", + intval($new_flags), + dbesc($p[0]['channel_hash']) + ); + + } + + if(perm_is_allowed($uid,'','view_profile')) { import_directory_profile($hash,$profile); diff --git a/include/directory.php b/include/directory.php index b0b975358..c0a8928c0 100644 --- a/include/directory.php +++ b/include/directory.php @@ -1,4 +1,5 @@ -g<?php +<?php /** @file */ + require_once('boot.php'); require_once('include/zot.php'); require_once('include/cli_startup.php'); @@ -18,11 +19,6 @@ function directory_run($argv, $argc){ if($dirmode === false) $dirmode = DIRECTORY_MODE_NORMAL; - if(($dirmode == DIRECTORY_MODE_PRIMARY) || ($dirmode == DIRECTORY_MODE_STANDALONE)) { - syncdirs($argv[1]); - return; - } - $x = q("select * from channel where channel_id = %d limit 1", intval($argv[1]) ); @@ -31,26 +27,34 @@ function directory_run($argv, $argc){ $channel = $x[0]; - // is channel profile visible to the public? - // FIXME - remove dir entry if permission is revoked - if(! perm_is_allowed($channel['channel_id'],null,'view_profile')) + if(($dirmode == DIRECTORY_MODE_PRIMARY) || ($dirmode == DIRECTORY_MODE_STANDALONE)) { + syncdirs($argv[1]); + + // Now update all the connections + proc_run('php','include/notifier.php','refresh_all',$channel['channel_id']); return; + } $directory = find_upstream_directory($dirmode); if($directory) { - $url = $directory['url']; + $url = $directory['url'] . '/post'; } else { $url = DIRECTORY_FALLBACK_MASTER . '/post'; } + // ensure the upstream directory is updated + $packet = zot_build_packet($channel,'refresh'); $z = zot_zot($url,$packet); - // re-queue if unsuccessful + // Now update all the connections + + proc_run('php','include/notifier.php','refresh_all',$channel['channel_id']); + } if (array_search(__file__,get_included_files())===0){ diff --git a/include/enotify.php b/include/enotify.php index 32f4cc2e7..a15e42b73 100644 --- a/include/enotify.php +++ b/include/enotify.php @@ -1,4 +1,4 @@ -<?php +<?php /** @file */ function notification($params) { @@ -33,8 +33,8 @@ function notification($params) { push_lang($recip['account_language']); // should probably have a channel language - $banner = t('Red Notification'); - $product = FRIENDICA_PLATFORM; + $banner = t('Red Matrix Notification'); + $product = RED_PLATFORM; $siteurl = $a->get_baseurl(true); $thanks = t('Thank You,'); $sitename = get_config('system','sitename'); @@ -51,13 +51,26 @@ function notification($params) { $additional_mail_header = ""; if(array_key_exists('item',$params)) { - $title = $params['item']['title']; - $body = $params['item']['body']; + // if it's a normal item... + if(array_key_exists('verb',$params['item'])) { + require_once('include/conversation.php'); + // localize_item() alters the original item so make a copy first + $i = $params['item']; + logger('calling localize'); + localize_item($i); + $title = $i['title']; + $body = $i['body']; + } + else { + $title = $params['item']['title']; + $body = $params['item']['body']; + } } else { $title = $body = ''; } + // e.g. "your post", "David's photo", etc. $possess_desc = t('%s <!item_type!>'); @@ -66,7 +79,7 @@ function notification($params) { $subject = sprintf( t('[Red:Notify] New mail received at %s'),$sitename); $preamble = sprintf( t('%1$s sent you a new private message at %2$s.'),$sender['xchan_name'],$sitename); - $epreamble = sprintf( t('%1$s sent you %2$s.'),'[url=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/url]', '[url=$itemlink]' . t('a private message') . '[/url]'); + $epreamble = sprintf( t('%1$s sent you %2$s.'),'[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]', '[zrl=$itemlink]' . t('a private message') . '[/zrl]'); $sitelink = t('Please visit %s to view and/or reply to your private messages.'); $tsitelink = sprintf( $sitelink, $siteurl . '/message/' . $params['item']['id'] ); $hsitelink = sprintf( $sitelink, '<a href="' . $siteurl . '/message/' . $params['item']['id'] . '">' . $sitename . '</a>'); @@ -87,7 +100,7 @@ function notification($params) { intval($recip['channel_id']) ); if($p) { - logger('notification comment already notified'); + logger('notification: comment already notified'); pop_lang(); return; } @@ -108,26 +121,28 @@ function notification($params) { $item_post_type = item_post_type($p[0]); + $private = $p[0]['item_private']; + //$possess_desc = str_replace('<!item_type!>',$possess_desc); // "a post" - $dest_str = sprintf(t('%1$s commented on [url=%2$s]a %3$s[/url]'), - '[url=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/url]', + $dest_str = sprintf(t('%1$s commented on [zrl=%2$s]a %3$s[/zrl]'), + '[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]', $itemlink, $item_post_type); // "George Bull's post" if($p) - $dest_str = sprintf(t('%1$s commented on [url=%2$s]%3$s\'s %4$s[/url]'), - '[url=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/url]', + $dest_str = sprintf(t('%1$s commented on [zrl=%2$s]%3$s\'s %4$s[/zrl]'), + '[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]', $itemlink, $p[0]['author']['xchan_name'], $item_post_type); // "your post" if($p[0]['owner']['xchan_name'] == $p[0]['author']['xchan_name'] && ($p[0]['item_flags'] & ITEM_WALL)) - $dest_str = sprintf(t('%1$s commented on [url=%2$s]your %3$s[/url]'), - '[url=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/url]', + $dest_str = sprintf(t('%1$s commented on [zrl=%2$s]your %3$s[/zrl]'), + '[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]', $itemlink, $item_post_type); @@ -151,9 +166,11 @@ function notification($params) { $preamble = sprintf( t('%1$s posted to your profile wall at %2$s') , $sender['xchan_name'], $sitename); - $epreamble = sprintf( t('%1$s posted to [url=%2$s]your wall[/url]') , - '[url=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/url]', + $epreamble = sprintf( t('%1$s posted to [zrl=%2$s]your wall[/zrl]') , + '[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]', $params['link']); + // FIXME - check the item privacy + $private = false; $sitelink = t('Please visit %s to view and/or reply to the conversation.'); $tsitelink = sprintf( $sitelink, $siteurl ); @@ -162,10 +179,23 @@ function notification($params) { } if($params['type'] == NOTIFY_TAGSELF) { + + $p = null; + $p = q("select id from notify where link = '%s' and uid = %d limit 1", + dbesc($params['link']), + intval($recip['channel_id']) + ); + if($p) { + logger('enotify: tag: already notified about this post'); + pop_lang(); + return; + } + + $subject = sprintf( t('[Red:Notify] %s tagged you') , $sender['xchan_name']); $preamble = sprintf( t('%1$s tagged you at %2$s') , $sender['xchan_name'], $sitename); - $epreamble = sprintf( t('%1$s [url=%2$s]tagged you[/url].') , - '[url=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/url]', + $epreamble = sprintf( t('%1$s [zrl=%2$s]tagged you[/zrl].') , + '[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]', $params['link']); $sitelink = t('Please visit %s to view and/or reply to the conversation.'); @@ -178,8 +208,8 @@ function notification($params) { $subject = sprintf( t('[Red:Notify] %1$s poked you') , $sender['xchan_name']); $preamble = sprintf( t('%1$s poked you at %2$s') , $sender['xchan_name'], $sitename); - $epreamble = sprintf( t('%1$s [url=%2$s]poked you[/url].') , - '[url=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/url]', + $epreamble = sprintf( t('%1$s [zrl=%2$s]poked you[/zrl].') , + '[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]', $params['link']); $subject = str_replace('poked', t($params['activity']), $subject); @@ -195,8 +225,8 @@ function notification($params) { if($params['type'] == NOTIFY_TAGSHARE) { $subject = sprintf( t('[Red:Notify] %s tagged your post') , $sender['xchan_name']); $preamble = sprintf( t('%1$s tagged your post at %2$s') , $sender['xchan_name'], $sitename); - $epreamble = sprintf( t('%1$s tagged [url=%2$s]your post[/url]') , - '[url=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/url]', + $epreamble = sprintf( t('%1$s tagged [zrl=%2$s]your post[/zrl]') , + '[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]', $itemlink); $sitelink = t('Please visit %s to view and/or reply to the conversation.'); @@ -208,9 +238,9 @@ function notification($params) { if($params['type'] == NOTIFY_INTRO) { $subject = sprintf( t('[Red:Notify] Introduction received')); $preamble = sprintf( t('You\'ve received an introduction from \'%1$s\' at %2$s'), $sender['xchan_name'], $sitename); - $epreamble = sprintf( t('You\'ve received [url=%1$s]an introduction[/url] from %2$s.'), + $epreamble = sprintf( t('You\'ve received [zrl=%1$s]an introduction[/zrl] from %2$s.'), $itemlink, - '[url=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/url]'); + '[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]'); $body = sprintf( t('You may visit their profile at %s'),$sender['xchan_url']); $sitelink = t('Please visit %s to approve or reject the introduction.'); @@ -222,10 +252,10 @@ function notification($params) { if($params['type'] == NOTIFY_SUGGEST) { $subject = sprintf( t('[Red:Notify] Friend suggestion received')); $preamble = sprintf( t('You\'ve received a friend suggestion from \'%1$s\' at %2$s'), $sender['xchan_name'], $sitename); - $epreamble = sprintf( t('You\'ve received [url=%1$s]a friend suggestion[/url] for %2$s from %3$s.'), + $epreamble = sprintf( t('You\'ve received [zrl=%1$s]a friend suggestion[/zrl] for %2$s from %3$s.'), $itemlink, - '[url=' . $params['item']['url'] . ']' . $params['item']['name'] . '[/url]', - '[url=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/url]'); + '[zrl=' . $params['item']['url'] . ']' . $params['item']['name'] . '[/zrl]', + '[zrl=' . $sender['xchan_url'] . ']' . $sender['xchan_name'] . '[/zrl]'); $body = t('Name:') . ' ' . $params['item']['name'] . "\n"; $body .= t('Photo:') . ' ' . $params['item']['photo'] . "\n"; @@ -293,9 +323,16 @@ function notification($params) { $datarray['type'] = $params['type']; $datarray['verb'] = $params['verb']; $datarray['otype'] = $params['otype']; + $datarray['abort'] = false; call_hooks('enotify_store', $datarray); + if($datarray['abort']) { + pop_lang(); + return; + } + + // create notification entry in DB $r = q("insert into notify (hash,name,url,photo,date,uid,link,parent,type,verb,otype) @@ -341,10 +378,28 @@ function notification($params) { logger('notification: sending notification email'); + $textversion = strip_tags(html_entity_decode(bbcode(stripslashes(str_replace(array("\\r\\n", "\\r", "\\n"), "\n", $body))),ENT_QUOTES,'UTF-8')); + $htmlversion = html_entity_decode(bbcode(stripslashes(str_replace(array("\\r\\n", "\\r","\\n\\n" ,"\\n"), - "<br />\n",$body)))); + "<br />\n",$body))), ENT_QUOTES,'UTF-8'); + + + // use $_SESSION['zid_override'] to force zid() to use + // the recipient address instead of the current observer + + $_SESSION['zid_override'] = $recip['channel_address'] . '@' . get_app()->get_hostname(); + $_SESSION['zrl_override'] = z_root() . '/channel/' . $recip['channel_address']; + + $textversion = zidify_links($textversion); + $htmlversion = zidify_links($htmlversion); + + // unset when done to revert to normal behaviour + + unset($_SESSION['zid_override']); + unset($_SESSION['zrl_override']); + $datarray = array(); $datarray['banner'] = $banner; @@ -370,9 +425,28 @@ function notification($params) { $datarray['textversion'] = $textversion; $datarray['subject'] = $subject; $datarray['headers'] = $additional_mail_header; + $datarray['email_secure'] = false; call_hooks('enotify_mail', $datarray); + // Default to private - don't disclose message contents over insecure channels (such as email) + // Might be interesting to use GPG,PGP,S/MIME encryption instead + // but we'll save that for a clever plugin developer to implement + + if(! $datarray['email_secure']) { + switch($params['type']) { + case NOTIFY_WALL: + case NOTIFY_COMMENT: + if(! $private) + break; + case NOTIFY_MAIL: + $datarray['textversion'] = $datarray['htmlversion'] = $datarray['title'] = ''; + break; + default: + break; + } + } + // load the template for private message notifications $tpl = get_markup_template('email_notify_html.tpl'); $email_html_body = replace_macros($tpl,array( diff --git a/include/event.php b/include/event.php index 8bf65016f..29ada2e96 100644 --- a/include/event.php +++ b/include/event.php @@ -1,4 +1,4 @@ -<?php +<?php /** @file */ function format_event_html($ev) { @@ -45,74 +45,6 @@ function format_event_html($ev) { return $o; } -/* -function parse_event($h) { - - require_once('include/Scrape.php'); - require_once('library/HTMLPurifier.auto.php'); - require_once('include/html2bbcode'); - - $h = '<html><body>' . $h . '</body></html>'; - - $ret = array(); - - - try { - $dom = HTML5_Parser::parse($h); - } catch (DOMException $e) { - logger('parse_event: parse error: ' . $e); - } - - if(! $dom) - return $ret; - - $items = $dom->getElementsByTagName('*'); - - foreach($items as $item) { - if(attribute_contains($item->getAttribute('class'), 'vevent')) { - $level2 = $item->getElementsByTagName('*'); - foreach($level2 as $x) { - if(attribute_contains($x->getAttribute('class'),'dtstart') && $x->getAttribute('title')) { - $ret['start'] = $x->getAttribute('title'); - if(! strpos($ret['start'],'Z')) - $ret['adjust'] = true; - } - if(attribute_contains($x->getAttribute('class'),'dtend') && $x->getAttribute('title')) - $ret['finish'] = $x->getAttribute('title'); - - if(attribute_contains($x->getAttribute('class'),'description')) - $ret['desc'] = $x->textContent; - if(attribute_contains($x->getAttribute('class'),'location')) - $ret['location'] = $x->textContent; - } - } - } - - // sanitise - - if((x($ret,'desc')) && ((strpos($ret['desc'],'<') !== false) || (strpos($ret['desc'],'>') !== false))) { - $config = HTMLPurifier_Config::createDefault(); - $config->set('Cache.DefinitionImpl', null); - $purifier = new HTMLPurifier($config); - $ret['desc'] = html2bbcode($purifier->purify($ret['desc'])); - } - - if((x($ret,'location')) && ((strpos($ret['location'],'<') !== false) || (strpos($ret['location'],'>') !== false))) { - $config = HTMLPurifier_Config::createDefault(); - $config->set('Cache.DefinitionImpl', null); - $purifier = new HTMLPurifier($config); - $ret['location'] = html2bbcode($purifier->purify($ret['location'])); - } - - if(x($ret,'start')) - $ret['start'] = datetime_convert('UTC','UTC',$ret['start']); - if(x($ret,'finish')) - $ret['finish'] = datetime_convert('UTC','UTC',$ret['finish']); - - return $ret; -} -*/ - function format_event_bbcode($ev) { $o = ''; @@ -237,6 +169,8 @@ function event_store($arr) { return $r[0]['id']; } + $event_hash = $r[0]['event_hash']; + // The event changed. Update it. $r = q("UPDATE `event` SET @@ -273,7 +207,7 @@ function event_store($arr) { ); $r = q("SELECT * FROM item left join xchan on author_xchan = xchan_hash WHERE resource_id = '%s' AND resource_type = 'event' and uid = %d LIMIT 1", - intval($r[0]['event_hash']), + dbesc($event_hash), intval($arr['uid']) ); @@ -328,11 +262,11 @@ function event_store($arr) { $hash = random_string(); - if(! $arr['uri']) - $arr['uri'] = item_message_id(); + if(! $arr['mid']) + $arr['mid'] = item_message_id(); - $r = q("INSERT INTO event ( uid,aid,event_xchan,event_hash,created,edited,start,finish,summary, desc,location,type, + $r = q("INSERT INTO event ( uid,aid,event_xchan,event_hash,created,edited,start,finish,summary, `desc`,location,type, adjust,nofinish,allow_cid,allow_gid,deny_cid,deny_gid) VALUES ( %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, '%s', '%s', '%s', '%s' ) ", intval($arr['uid']), @@ -382,8 +316,8 @@ function event_store($arr) { $item_arr['uid'] = $arr['uid']; $item_arr['author_xchan'] = $arr['event_xchan']; - $item_arr['uri'] = $arr['uri']; - $item_arr['parent_uri'] = $arr['uri']; + $item_arr['mid'] = $arr['mid']; + $item_arr['parent_mid'] = $arr['mid']; $item_arr['item_flags'] = $item_flags; diff --git a/include/expire.php b/include/expire.php index 3a914a41d..f1002b890 100644 --- a/include/expire.php +++ b/include/expire.php @@ -1,4 +1,4 @@ -<?php +<?php /** @file */ require_once('boot.php'); require_once('include/cli_startup.php'); diff --git a/include/fcontact.php b/include/fcontact.php deleted file mode 100644 index 8821a985f..000000000 --- a/include/fcontact.php +++ /dev/null @@ -1,41 +0,0 @@ -<?php - - - -function fcontact_store($url,$name,$photo) { - - $nurl = str_replace(array('https:','//www.'), array('http:','//'), $url); - - $r = q("SELECT `id` FROM `fcontact` WHERE `url` = '%s' LIMIT 1", - dbesc($nurl) - ); - - if(count($r)) - return $r[0]['id']; - - $r = q("INSERT INTO `fcontact` ( `url`, `name`, `photo` ) VALUES ( '%s', '%s', '%s' ) ", - dbesc($nurl), - dbesc($name), - dbesc($photo) - ); - - if($r) { - $r = q("SELECT `id` FROM `fcontact` WHERE `url` = '%s' LIMIT 1", - dbesc($nurl) - ); - if(count($r)) - return $r[0]['id']; - } - - return 0; -} - -function ffinder_store($uid,$cid,$fid) { - $r = q("INSERT INTO `ffinder` ( `uid`, `cid`, `fid` ) VALUES ( %d, %d, %d ) ", - intval($uid), - intval($cid), - intval($fid) - ); - return $r; -} - diff --git a/include/features.php b/include/features.php index 094e96c78..da1322a14 100644 --- a/include/features.php +++ b/include/features.php @@ -1,4 +1,4 @@ -<?php +<?php /** @file */ /* * Features management @@ -22,6 +22,9 @@ function get_features() { array('expire', t('Content Expiration'), t('Remove old posts/comments after a period of time')), array('multi_profiles', t('Multiple Profiles'), t('Ability to create multiple profiles')), array('webpages', t('Web Pages'), t('Provide managed web pages on your channel')), + array('prettyphoto', t('Enhanced Photo Albums'), t('Enable photo album with enhanced features')), + //FIXME - needs a description, but how the hell do we explain this to normals? + array('sendzid', t('Extended Identity Sharing'), t(' ')), array('expert', t('Expert Mode'), t('Enable Expert Mode to provide advanced configuration options')), @@ -55,6 +58,7 @@ function get_features() { array('filing', t('Saved Folders'), t('Ability to file posts under folders')), array('dislike', t('Dislike Posts'), t('Ability to dislike posts/comments')), array('star_posts', t('Star Posts'), t('Ability to mark special posts with a star indicator')), + array('tagadelic', t('Tag Cloud'), t('Provide a personal tag cloud on your channel page')), ), ); diff --git a/include/follow.php b/include/follow.php index a094a979f..6b192234c 100644 --- a/include/follow.php +++ b/include/follow.php @@ -1,4 +1,4 @@ -<?php +<?php /** @file */ // @@ -111,7 +111,10 @@ function new_contact($uid,$url,$channel,$interactive = false) { if((local_user()) && $uid == local_user()) { $aid = get_account_id(); - $hash = $a->observer['xchan_hash']; + $hash = get_observer_hash(); + $ch = $a->get_channel(); + $default_group = $ch['channel_default_group']; + } else { $r = q("select * from channel where channel_id = %d limit 1", @@ -123,6 +126,7 @@ function new_contact($uid,$url,$channel,$interactive = false) { } $aid = $r[0]['channel_account_id']; $hash = $r[0]['channel_hash']; + $default_group = $r[0]['channel_default_group']; } if($hash == $xchan_hash) { @@ -160,62 +164,21 @@ function new_contact($uid,$url,$channel,$interactive = false) { dbesc($xchan_hash), intval($uid) ); - if($r) + if($r) { $result['abook'] = $r[0]; - - // Then send a ping/message to the other side - - -/* - - $r = q("INSERT INTO `contact` ( `uid`, `created`, `url`, `nurl`, `addr`, `alias`, `batch`, `notify`, `poll`, `poco`, `name`, `nick`, `photo`, `network`, `pubkey`, `rel`, `priority`, - `writable`, `hidden`, `blocked`, `readonly`, `pending` ) - VALUES ( %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, %d, %d, 0, 0, 0) ", - intval($uid), - dbesc(datetime_convert()), - dbesc($ret['url']), - dbesc(normalise_link($ret['url'])), - dbesc($ret['addr']), - dbesc($ret['alias']), - dbesc($ret['batch']), - dbesc($ret['notify']), - dbesc($ret['poll']), - dbesc($ret['poco']), - dbesc($ret['name']), - dbesc($ret['nick']), - dbesc($ret['photo']), - dbesc($ret['network']), - dbesc($ret['pubkey']), - intval($new_relation), - intval($ret['priority']), - intval($writeable), - intval($hidden) - ); + proc_run('php', 'include/notifier.php', 'permission_update', $result['abook']['abook_id']); } - $r = q("SELECT * FROM `contact` WHERE `url` = '%s' AND `uid` = %d LIMIT 1", - dbesc($ret['url']), - intval($uid) - ); - - if(! count($r)) { - $result['message'] .= t('Unable to retrieve contact information.') . EOL; - return $result; - } + /** If there is a default group for this channel, add this member to it */ - $contact = $r[0]; - $contact_id = $r[0]['id']; - - - $g = q("select def_gid from user where uid = %d limit 1", - intval($uid) - ); - if($g && intval($g[0]['def_gid'])) { + if($default_group) { require_once('include/group.php'); - group_add_member($uid,'',$contact_id,$g[0]['def_gid']); + $g = group_rec_byhash($uid,$default_group); + if($g) + group_add_member($uid,'',$xchan_hash,$g['id']); } -*/ + // Then send a ping/message to the other side $result['success'] = true; diff --git a/include/friendica_smarty.php b/include/friendica_smarty.php index 2f4694c58..1e8ef5406 100644..100755 --- a/include/friendica_smarty.php +++ b/include/friendica_smarty.php @@ -1,7 +1,8 @@ -<?php - +<?php /** @file */ +require_once 'include/ITemplateEngine.php'; require_once("library/Smarty/libs/Smarty.class.php"); + class FriendicaSmarty extends Smarty { public $filename; @@ -14,15 +15,17 @@ class FriendicaSmarty extends Smarty { // setTemplateDir can be set to an array, which Smarty will parse in order. // The order is thus very important here - $template_dirs = array('theme' => "view/theme/$theme/tpl/smarty3/"); + $template_dirs = array('theme' => "view/theme/$theme/tpl/"); if( x($a->theme_info,"extends") ) - $template_dirs = $template_dirs + array('extends' => "view/theme/".$a->theme_info["extends"]."/tpl/smarty3/"); - $template_dirs = $template_dirs + array('base' => 'view/tpl/smarty3/'); + $template_dirs = $template_dirs + array('extends' => "view/theme/".$a->theme_info["extends"]."/tpl/"); + $template_dirs = $template_dirs + array('base' => 'view/tpl/'); $this->setTemplateDir($template_dirs); - $this->setCompileDir('view/tpl/smarty3/compiled/'); - $this->setConfigDir('view/tpl/smarty3/config/'); - $this->setCacheDir('view/tpl/smarty3/cache/'); + $basecompiledir = $a->config['system']['smarty3_folder']; + + $this->setCompileDir($basecompiledir.'/compiled/'); + $this->setConfigDir($basecompiledir.'/config/'); + $this->setCacheDir($basecompiledir.'/cache/'); $this->left_delimiter = $a->get_template_ldelim('smarty3'); $this->right_delimiter = $a->get_template_rdelim('smarty3'); @@ -41,3 +44,67 @@ class FriendicaSmarty extends Smarty { +class FriendicaSmartyEngine implements ITemplateEngine { + static $name ="smarty3"; + + public function __construct(){ + $a = get_app(); + $basecompiledir = $a->config['system']['smarty3_folder']; + if (!$basecompiledir) $basecompiledir = dirname(__dir__)."/view/tpl/smarty3"; + if (!is_dir($basecompiledir)) { + echo "<b>ERROR:</b> folder <tt>$basecompiledir</tt> does not exist."; killme(); + } + if(!is_writable($basecompiledir)){ + echo "<b>ERROR:</b> folder <tt>$basecompiledir</tt> must be writable by webserver."; killme(); + } + $a->config['system']['smarty3_folder'] = $basecompiledir; + } + + // ITemplateEngine interface + public function replace_macros($s, $r) { + $template = ''; + if(gettype($s) === 'string') { + $template = $s; + $s = new FriendicaSmarty(); + } + foreach($r as $key=>$value) { + if($key[0] === '$') { + $key = substr($key, 1); + } + $s->assign($key, $value); + } + return $s->parsed($template); + } + + public function get_markup_template($file, $root=''){ + $template_file = theme_include($file, $root); + if($template_file) { + $template = new FriendicaSmarty(); + $template->filename = $template_file; + + return $template; + } + return ""; + } + + public function get_intltext_template($file, $root='') { + $a = get_app(); + + if(file_exists("view/{$a->language}/$file")) + $template_file = "view/{$a->language}/$file"; + elseif(file_exists("view/en/$file")) + $template_file = "view/en/$file"; + else + $template_file = theme_include($file,$root); + if($template_file) { + $template = new FriendicaSmarty(); + $template->filename = $template_file; + + return $template; + } + return ""; + } + + + +} diff --git a/include/gprobe.php b/include/gprobe.php index 2cc87d149..48c1c8e14 100644 --- a/include/gprobe.php +++ b/include/gprobe.php @@ -1,4 +1,4 @@ -<?php +<?php /** @file */ require_once('include/cli_startup.php'); require_once('include/zot.php'); diff --git a/include/group.php b/include/group.php index 6042acbb4..7ba14a49d 100644 --- a/include/group.php +++ b/include/group.php @@ -1,7 +1,7 @@ -<?php +<?php /** @file */ -function group_add($uid,$name) { +function group_add($uid,$name,$public = 0) { $ret = false; if(x($uid) && x($name)) { @@ -37,10 +37,11 @@ function group_add($uid,$name) { } while($dups == true); - $r = q("INSERT INTO `group` ( hash, uid, name ) - VALUES( '%s', %d, '%s' ) ", + $r = q("INSERT INTO `group` ( hash, uid, visible, name ) + VALUES( '%s', %d, %d, '%s' ) ", dbesc($hash), intval($uid), + intval($public), dbesc($name) ); $ret = $r; @@ -52,40 +53,43 @@ function group_add($uid,$name) { function group_rmv($uid,$name) { $ret = false; if(x($uid) && x($name)) { - $r = q("SELECT id FROM `group` WHERE `uid` = %d AND `name` = '%s' LIMIT 1", + $r = q("SELECT id, hash FROM `group` WHERE `uid` = %d AND `name` = '%s' LIMIT 1", intval($uid), dbesc($name) ); - if(count($r)) + if($r) { $group_id = $r[0]['id']; + $group_hash = $r[0]['hash']; + } + if(! $group_id) return false; // remove group from default posting lists - $r = q("SELECT channel_default_gid, channel_allow_gid, channel_deny_gid FROM channel WHERE channel_id = %d LIMIT 1", + $r = q("SELECT channel_default_group, channel_allow_gid, channel_deny_gid FROM channel WHERE channel_id = %d LIMIT 1", intval($uid) ); if($r) { $user_info = $r[0]; $change = false; - if($user_info['channel_default_gid'] == $group_id) { - $user_info['channel_default_gid'] = 0; + if($user_info['channel_default_group'] == $group_hash) { + $user_info['channel_default_group'] = ''; $change = true; } if(strpos($user_info['channel_allow_gid'], '<' . $group_id . '>') !== false) { - $user_info['channel_allow_gid'] = str_replace('<' . $group_id . '>', '', $user_info['channel_allow_gid']); + $user_info['channel_allow_gid'] = str_replace('<' . $group_hash . '>', '', $user_info['channel_allow_gid']); $change = true; } if(strpos($user_info['channel_deny_gid'], '<' . $group_id . '>') !== false) { - $user_info['channel_deny_gid'] = str_replace('<' . $group_id . '>', '', $user_info['channel_deny_gid']); + $user_info['channel_deny_gid'] = str_replace('<' . $group_hash . '>', '', $user_info['channel_deny_gid']); $change = true; } if($change) { - q("UPDATE channel SET channel_default_gid = %d, channel_allow_gid = '%s', channel_deny_gid = '%s' + q("UPDATE channel SET channel_default_group = '%s', channel_allow_gid = '%s', channel_deny_gid = '%s' WHERE channel_id = %d", - intval($user_info['channel_default_gid']), + intval($user_info['channel_default_group']), dbesc($user_info['channel_allow_gid']), dbesc($user_info['channel_deny_gid']), intval($uid) @@ -124,6 +128,19 @@ function group_byname($uid,$name) { return false; } + +function group_rec_byhash($uid,$hash) { + if((! $uid) || (! strlen($hash))) + return false; + $r = q("SELECT * FROM `group` WHERE `uid` = %d AND `hash` = '%s' LIMIT 1", + intval($uid), + dbesc($hash) + ); + if($r) + return $r[0]; + return false; +} + function group_rmv_member($uid,$name,$member) { $gid = group_byname($uid,$name); if(! $gid) @@ -171,9 +188,10 @@ function group_get_members($gid) { if(intval($gid)) { $r = q("SELECT * FROM `group_member` LEFT JOIN abook ON abook_xchan = `group_member`.`xchan` left join xchan on xchan_hash = abook_xchan - WHERE `gid` = %d AND `group_member`.`uid` = %d and not ( abook_flags & %d ) and not ( abook_flags & %d ) and not ( abook_flags & %d ) ORDER BY xchan_name ASC ", + WHERE `gid` = %d AND abook_channel = %d and `group_member`.`uid` = %d and not ( abook_flags & %d ) and not ( abook_flags & %d ) and not ( abook_flags & %d ) ORDER BY xchan_name ASC ", intval($gid), intval(local_user()), + intval(local_user()), intval(ABOOK_FLAG_SELF), intval(ABOOK_FLAG_BLOCKED), intval(ABOOK_FLAG_PENDING) @@ -221,7 +239,7 @@ function group_side($every="contacts",$each="group",$edit = false, $group_id = 0 $groups = array(); $groups[] = array( - 'text' => t('All Connections'), + 'text' => t('All Channels'), 'id' => 0, 'selected' => (($group_id == 0) ? 'group-selected' : ''), 'href' => $every, @@ -279,12 +297,14 @@ function group_side($every="contacts",$each="group",$edit = false, $group_id = 0 function expand_groups($a) { if(! (is_array($a) && count($a))) return array(); - stringify_array_elms($groups); - $groups = implode(',', $a); + $x = $a; + stringify_array_elms($x); + $groups = implode(',', $x); $groups = dbesc($groups); - $r = q("SELECT xchan FROM `group_member` WHERE `gid` IN ( $groups )"); + if($groups) + $r = q("SELECT xchan FROM group_member WHERE gid IN ( $groups )"); $ret = array(); - if(count($r)) + if($r) foreach($r as $rr) $ret[] = $rr['xchan']; return $ret; diff --git a/include/html2bbcode.php b/include/html2bbcode.php index 985c36eaa..9bb755da5 100644 --- a/include/html2bbcode.php +++ b/include/html2bbcode.php @@ -1,4 +1,4 @@ -<?php +<?php /** @file */ /* html2bbcode.php Converter for HTML to BBCode diff --git a/include/html2plain.php b/include/html2plain.php index b8c9c440d..2f5be7f69 100644 --- a/include/html2plain.php +++ b/include/html2plain.php @@ -1,4 +1,4 @@ -<?php +<?php /** @file */ require_once "html2bbcode.php"; function breaklines($line, $level, $wraplength = 75) @@ -219,4 +219,4 @@ function html2plain($html, $wraplength = 75, $compact = false) return(trim($message)); } -?> + diff --git a/include/identity.php b/include/identity.php index 62092443e..8f8a71fee 100644 --- a/include/identity.php +++ b/include/identity.php @@ -1,4 +1,4 @@ -<?php +<?php /** @file */ require_once('include/zot.php'); require_once('include/crypto.php'); @@ -44,6 +44,29 @@ function validate_channelname($name) { } +// Create the system channel for directory synchronisation - this has no account attached + + +function create_dir_account() { + create_account(array( + 'account_id' => 'xxx', // This will create an identity with an (integer) account_id of 0, but account_id is required + 'nickname' => 'dir', + 'name' => 'Directory', + 'pageflags' => PAGE_DIRECTORY_CHANNEL|PAGE_HIDDEN, + 'publish' => 0 + )); +} + + +function channel_total() { + $r = q("select channel_id from channel where true"); + if(is_array($r)) + return count($r); + return false; +} + + + // Required: name, nickname, account_id // optional: pageflags @@ -107,7 +130,7 @@ function create_identity($arr) { dbesc($hash), dbesc($key['prvkey']), dbesc($key['pubkey']), - intval(PAGE_NORMAL) + intval($pageflags) ); $r = q("select * from channel where channel_account_id = %d @@ -123,7 +146,8 @@ function create_identity($arr) { $ret['channel'] = $r[0]; - set_default_login_identity($arr['account_id'],$ret['channel']['channel_id'],false); + if(intval($arr['account_id'])) + set_default_login_identity($arr['account_id'],$ret['channel']['channel_id'],false); // Ensure that there is a host keypair. @@ -200,16 +224,18 @@ function create_identity($arr) { intval(ABOOK_FLAG_SELF) ); + if(intval($ret['channel']['channel_account_id'])) { - // Create a group with no members. This allows somebody to use it - // right away as a default group for new contacts. + // Create a group with no members. This allows somebody to use it + // right away as a default group for new contacts. - require_once('include/group.php'); - group_add($newuid, t('Friends')); + require_once('include/group.php'); + group_add($newuid, t('Friends')); - call_hooks('register_account', $newuid); - - proc_run('php','include/directory.php', $ret['channel']['channel_id']); + call_hooks('register_account', $newuid); + + proc_run('php','include/directory.php', $ret['channel']['channel_id']); + } $ret['success'] = true; return $ret; @@ -223,11 +249,13 @@ function set_default_login_identity($account_id,$channel_id,$force = true) { $r = q("select account_default_channel from account where account_id = %d limit 1", intval($account_id) ); - if(($r) && (count($r)) && ((! intval($r[0]['account_default_channel'])) || $force)) { - $r = q("update account set account_default_channel = %d where account_id = %d limit 1", - intval($channel_id), - intval($account_id) - ); + if($r) { + if((intval($r[0]['account_default_channel']) == 0) || ($force)) { + $r = q("update account set account_default_channel = %d where account_id = %d limit 1", + intval($channel_id), + intval($account_id) + ); + } } } @@ -239,7 +267,7 @@ function identity_basic_export($channel_id) { $ret = array(); - $ret['compatibility'] = array('project' => FRIENDICA_PLATFORM, 'version' => FRIENDICA_VERSION, 'database' => DB_UPDATE_VERSION); + $ret['compatibility'] = array('project' => RED_PLATFORM, 'version' => RED_VERSION, 'database' => DB_UPDATE_VERSION); $r = q("select * from channel where channel_id = %d limit 1", intval($channel_id) @@ -275,6 +303,26 @@ function identity_basic_export($channel_id) { $ret['hubloc'] = $r; } + $r = q("select * from `group` where uid = %d ", + intval($channel_id) + ); + + if($r) + $ret['group'] = $r; + + $r = q("select * from group_member where uid = %d ", + intval($channel_id) + ); + if($r) + $ret['group_member'] = $r; + + $r = q("select * from pconfig where uid = %d", + intval($channel_id) + ); + if($r) + $ret['config'] = $r; + + $r = q("select type, data from photo where scale = 4 and profile = 1 and uid = %d limit 1", intval($channel_id) ); @@ -334,4 +382,4 @@ function identity_basic_import($arr, $seize_primary = false) { return $ret; -}
\ No newline at end of file +} diff --git a/include/iquery.php b/include/iquery.php deleted file mode 100644 index 0d51134a4..000000000 --- a/include/iquery.php +++ /dev/null @@ -1,139 +0,0 @@ -<?php - - -function network_query($a,$arr) { - - - $parent_options = ''; - $child_options = ''; - - $ordering = (($arr['order'] === 'post') ? "`created`" : "`commented`") . " DESC "; - - $itemspage = get_pconfig($arr['uid'],'system','itemspage'); - $a->set_pager_itemspage(((intval($itemspage)) ? $itemspage : 40)); - - $pager_sql = ((intval($arr['update'])) ? '' : sprintf(" LIMIT %d, %d ",intval($a->pager['start']), intval($a->pager['itemspage']))); - - $arr['cmin'] = ((x($arr,'cmin')) ? $arr['cmin'] : 0); - $arr['cmax'] = ((x($arr,'cmax')) ? $arr['cmax'] : 0); - - $simple_update = (($arr['update']) ? " and `item`.`unseen` = 1 " : ''); - - if($arr['new']) { - - // "New Item View" - show all items unthreaded in reverse created date order - - $items = q("SELECT `item`.*, `item`.`id` AS `item_id`, - `contact`.`name`, `contact`.`photo`, `contact`.`url`, `contact`.`rel`, `contact`.`writable`, - `contact`.`network`, `contact`.`thumb`, `contact`.`dfrn_id`, `contact`.`self`, - `contact`.`id` AS `cid`, `contact`.`uid` AS `contact-uid` - FROM `item`, `contact` - WHERE `item`.`uid` = %d AND `item`.`visible` = 1 - AND `item`.`deleted` = 0 and `item`.`moderated` = 0 - $simple_update - AND `contact`.`closeness` >= %d and `contact`.`closeness` <= %d - AND `contact`.`id` = `item`.`contact-id` - AND `contact`.`blocked` = 0 AND `contact`.`pending` = 0 - $sql_extra $sql_nets - ORDER BY `item`.`received` DESC $pager_sql ", - intval($arr['uid']), - intval($arr['cmin']), - intval($arr['cmax']) - - ); - - $items = fetch_post_tags($items); - return $items; - - } - if($update) { - $r = q("SELECT `parent` AS `item_id`, `contact`.`uid` AS `contact_uid` - FROM `item` LEFT JOIN `contact` ON `contact`.`id` = `item`.`contact-id` - WHERE `item`.`uid` = %d AND `item`.`visible` = 1 AND - `contact`.`closeness` >= %d and `contact`.`closeness` <= %d - (`item`.`deleted` = 0 OR item.verb = '" . ACTIVITY_LIKE ."' OR item.verb = '" . ACTIVITY_DISLIKE . "') - and `item`.`moderated` = 0 $simple_update - AND `contact`.`blocked` = 0 AND `contact`.`pending` = 0 - $sql_extra3 $sql_extra $sql_nets ", - intval($arr['uid']), - intval($arr['cmin']), - intval($arr['cmax']) - ); - } - else { - $r = q("SELECT `item`.`id` AS `item_id`, `contact`.`uid` AS `contact_uid` - FROM `item` LEFT JOIN `contact` ON `contact`.`id` = `item`.`contact-id` - WHERE `item`.`uid` = %d AND `item`.`visible` = 1 AND `item`.`deleted` = 0 - AND `item`.`moderated` = 0 AND `contact`.`blocked` = 0 AND `contact`.`pending` = 0 - AND `contact`.`closeness` >= %d and `contact`.`closeness` <= %d - AND `item`.`parent` = `item`.`id` - $sql_extra3 $sql_extra $sql_nets - ORDER BY `item`.$ordering $pager_sql ", - intval($arr['uid']), - intval($arr['cmin']), - intval($arr['cmax']) - ); - } - - // Then fetch all the children of the parents that are on this page - - $parents_arr = array(); - $parents_str = ''; - - if(count($r)) { - foreach($r as $rr) - if(! in_array($rr['item_id'],$parents_arr)) - $parents_arr[] = $rr['item_id']; - $parents_str = implode(', ', $parents_arr); - - $items = q("SELECT `item`.*, `item`.`id` AS `item_id`, - `contact`.`name`, `contact`.`photo`, `contact`.`url`, `contact`.`alias`, - `contact`.`rel`, `contact`.`writable`, - `contact`.`network`, `contact`.`thumb`, `contact`.`self`, - `contact`.`id` AS `cid`, `contact`.`uid` AS `contact-uid` - FROM `item`, `contact` - WHERE `item`.`uid` = %d AND `item`.`visible` = 1 AND `item`.`deleted` = 0 - AND `item`.`moderated` = 0 AND `contact`.`id` = `item`.`contact-id` - AND `contact`.`blocked` = 0 AND `contact`.`pending` = 0 - AND `item`.`parent` IN ( %s ) - $sql_extra ", - intval($arr['uid']), - dbesc($parents_str) - ); - - $items = fetch_post_tags($items); - - $items = conv_sort($items,$ordering); - } - else { - $items = array(); - } - - return $items; -} - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/include/items.php b/include/items.php index 3fd43ca4e..e71fd0350 100755 --- a/include/items.php +++ b/include/items.php @@ -1,9 +1,9 @@ -<?php +<?php /** @file */ require_once('include/bbcode.php'); require_once('include/oembed.php'); require_once('include/crypto.php'); -require_once('include/Photo.php'); +require_once('include/photo/photo_driver.php'); require_once('include/permissions.php'); @@ -49,237 +49,254 @@ function collect_recipients($item,&$private) { $recipients = check_list_permissions($item['uid'],$recipients,'view_stream'); + // add ourself just in case we have nomadic clones that need to get a copy. + + $recipients[] = $item['author_xchan']; + if($item['owner_xchan'] != $item['author_xchan']) + $recipients[] = $item['owner_xchan']; return $recipients; } -function get_public_feed($channel,$params) { - - $type = 'xml'; - $begin = '0000-00-00 00:00:00'; - $end = ''; - $start = 0; - $records = 40; - $direction = 'desc'; - - if(is_array($params)) { - $type = ((x($params,'type')) ? $params['type'] : $type); - $begin = ((x($params,'begin')) ? $params['begin'] : $begin); - $end = ((x($params,'end')) ? $params['end'] : $end); - $start = ((x($params,'start')) ? $params['start'] : $start); - $records = ((x($params,'records')) ? $params['records'] : $records); - $direction = ((x($params,'direction')) ? $params['direction'] : $direction); - } - - switch($type) { - case 'json': - header("Content-type: application/atom+json"); +function can_comment_on_post($observer_xchan,$item) { + if(! $observer_xchan) + return false; + if($item['comment_policy'] === 'none') + return false; + switch($item['comment_policy']) { + case 'self': + if($observer_xchan === $item['author_xchan'] || $observer_xchan === $item['owner_xchan']) + return true; + break; + case 'public': + return false; + break; + case 'contacts': + case '': + if(($item['owner']['abook_xchan']) && ($item['owner']['abook_their_perms'] & PERMS_W_COMMENT)) + return true; break; - case 'xml': default: - header("Content-type: application/atom+xml"); break; } + if(strstr($item['comment_policy'],'network:') && strstr($item['comment_policy'],'red')) + return true; + if(strstr($item['comment_policy'],'site:') && strstr($item['comment_policy'],get_app()->get_hostname())) + return true; + + return false; +} +/** + * @function red_zrl_callback + * preg_match function when fixing 'naked' links in mod item.php + * Check if we've got a hubloc for the site and use a zrl if we do, a url if we don't. + * + */ - +function red_zrl_callback($matches) { + $m = @parse_url($matches[2]); + $zrl = false; + if($m['host']) { + $r = q("select hubloc_url from hubloc where hubloc_host = '%s' limit 1", + dbesc($m['host']) + ); + if($r) + $zrl = true; + } + if($zrl) + return $matches[1] . '[zrl=' . $matches[2] . ']' . $matches[2] . '[/zrl]'; + return $matches[0]; } -function get_feed_for(&$a, $dfrn_id, $owner_nick, $last_update, $direction = 0) { - $sitefeed = ((strlen($owner_nick)) ? false : true); // not yet implemented, need to rewrite huge chunks of following logic - $public_feed = (($dfrn_id) ? false : true); - $starred = false; // not yet implemented, possible security issues - $converse = false; +/** + * @function post_activity_item($arr) + * + * post an activity + * + * @param array $arr + * + * In its simplest form one needs only to set $arr['body'] to post a note to the logged in channel's wall. + * Much more complex activities can be created. Permissions are checked. No filtering, tag expansion + * or other processing is performed. + * + * @returns array + * 'success' => true or false + * 'activity' => the resulting activity if successful + */ - if($public_feed && $a->argc > 2) { - for($x = 2; $x < $a->argc; $x++) { - if($a->argv[$x] == 'converse') - $converse = true; - if($a->argv[$x] == 'starred') - $starred = true; - if($a->argv[$x] === 'category' && $a->argc > ($x + 1) && strlen($a->argv[$x+1])) - $category = $a->argv[$x+1]; - } - } +function post_activity_item($arr) { - + $ret = array('success' => false); - // default permissions - anonymous user + $is_comment = false; + if((($arr['parent']) && $arr['parent'] != $arr['id']) || (($arr['parent_mid']) && $arr['parent_mid'] != $arr['mid'])) + $is_comment = true; - $sql_extra = " AND `allow_cid` = '' AND `allow_gid` = '' AND `deny_cid` = '' AND `deny_gid` = '' "; + if(! x($arr,'item_flags')) { + if($is_comment) + $arr['item_flags'] = ITEM_ORIGIN; + else + $arr['item_flags'] = ITEM_ORIGIN | ITEM_WALL | ITEM_THREAD_TOP; + } - $r = q("SELECT `contact`.*, `user`.`uid` AS `user_uid`, `user`.`nickname`, `user`.`timezone`, `user`.`page-flags` - FROM `contact` LEFT JOIN `user` ON `user`.`uid` = `contact`.`uid` - WHERE `contact`.`self` = 1 AND `user`.`nickname` = '%s' LIMIT 1", - dbesc($owner_nick) - ); - if(! count($r)) - killme(); + $channel = get_app()->get_channel(); + $observer = get_app()->get_observer(); - $owner = $r[0]; - $owner_id = $owner['user_uid']; - $owner_nick = $owner['nickname']; + $arr['aid'] = ((x($arr,'aid')) ? $arr['aid'] : $channel['channel_account_id']); + $arr['uid'] = ((x($arr,'uid')) ? $arr['uid'] : $channel['channel_id']); - $birthday = feed_birthday($owner_id,$owner['timezone']); + if(! perm_is_allowed($arr['uid'],$observer['xchan_hash'],(($is_comment) ? 'post_comments' : 'post_wall'))) { + $ret['message'] = t('Permission denied'); + return $ret; + } - if(! $public_feed) { + if(array_key_exists('content_type',$arr) && $arr['content_type'] == 'text/html') + $arr['body'] = purify_html($arr['body']); + else + $arr['body'] = escape_tags($arr['body']); - $sql_extra = ''; - switch($direction) { - case (-1): - $sql_extra = sprintf(" AND `issued_id` = '%s' ", dbesc($dfrn_id)); - $my_id = $dfrn_id; - break; - case 0: - $sql_extra = sprintf(" AND `issued_id` = '%s' AND `duplex` = 1 ", dbesc($dfrn_id)); - $my_id = '1:' . $dfrn_id; - break; - case 1: - $sql_extra = sprintf(" AND `dfrn_id` = '%s' AND `duplex` = 1 ", dbesc($dfrn_id)); - $my_id = '0:' . $dfrn_id; - break; - default: - return false; - break; // NOTREACHED - } - $r = q("SELECT * FROM `contact` WHERE `blocked` = 0 AND `pending` = 0 AND `contact`.`uid` = %d $sql_extra LIMIT 1", - intval($owner_id) - ); + $arr['mid'] = ((x($arr,'mid')) ? $arr['mid'] : item_message_id()); + $arr['parent_mid'] = ((x($arr,'parent_mid')) ? $arr['parent_mid'] : $arr['mid']); + $arr['thr_parent'] = ((x($arr,'thr_parent')) ? $arr['thr_parent'] : $arr['mid']); - if(! count($r)) - killme(); + $arr['owner_xchan'] = ((x($arr,'owner_xchan')) ? $arr['owner_xchan'] : $channel['channel_hash']); + $arr['author_xchan'] = ((x($arr,'author_xchan')) ? $arr['author_xchan'] : $observer['xchan_hash']); - $contact = $r[0]; - require_once('include/security.php'); - $groups = init_groups_visitor($contact['id']); + $arr['verb'] = ((x($arr,'verb')) ? $arr['verb'] : ACTIVITY_POST); + $arr['obj_type'] = ((x($arr,'obj_type')) ? $arr['obj_type'] : ACTIVITY_OBJ_NOTE); - if(count($groups)) { - for($x = 0; $x < count($groups); $x ++) - $groups[$x] = '<' . intval($groups[$x]) . '>' ; - $gs = implode('|', $groups); - } - else - $gs = '<<>>' ; // Impossible to match - - $sql_extra = sprintf(" - AND ( `allow_cid` = '' OR `allow_cid` REGEXP '<%d>' ) - AND ( `deny_cid` = '' OR NOT `deny_cid` REGEXP '<%d>' ) - AND ( `allow_gid` = '' OR `allow_gid` REGEXP '%s' ) - AND ( `deny_gid` = '' OR NOT `deny_gid` REGEXP '%s') - ", - intval($contact['id']), - intval($contact['id']), - dbesc($gs), - dbesc($gs) - ); - } + $arr['allow_cid'] = ((x($arr,'allow_cid')) ? $arr['allow_cid'] : $channel['channel_allow_cid']); + $arr['allow_gid'] = ((x($arr,'allow_gid')) ? $arr['allow_gid'] : $channel['channel_allow_gid']); + $arr['deny_cid'] = ((x($arr,'deny_cid')) ? $arr['deny_cid'] : $channel['channel_deny_cid']); + $arr['deny_gid'] = ((x($arr,'deny_gid')) ? $arr['deny_gid'] : $channel['channel_deny_gid']); - if($public_feed) - $sort = 'DESC'; - else - $sort = 'ASC'; + $arr['comment_policy'] = map_scope($channel['channel_w_comment']); - if(! strlen($last_update)) - $last_update = 'now -30 days'; + // for the benefit of plugins, we will behave as if this is an API call rather than a normal online post - if(isset($category)) { - $sql_extra .= file_tag_file_query('item',$category,'category'); + $_REQUEST['api_source'] = 1; + + call_hooks('post_local',$arr); + + if(x($arr,'cancel')) { + logger('post_activity_item: post cancelled by plugin.'); + return $ret; } - if($public_feed) { - if(! $converse) - $sql_extra .= " AND `contact`.`self` = 1 "; + + $post_id = item_store($arr); + + if($post_id) { + $arr['id'] = $post_id; + call_hooks('post_local_end', $arr); + proc_run('php','include/notifier.php','activity',$post_id); + $ret['success'] = true; + $r = q("select * from item where id = %d limit 1", + intval($post_id) + ); + if($r) + $ret['activity'] = $r[0]; } - $check_date = datetime_convert('UTC','UTC',$last_update,'Y-m-d H:i:s'); - - $r = q("SELECT `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`, - `contact`.`id` AS `contact-id`, `contact`.`uid` AS `contact-uid`, - `sign`.`signed_text`, `sign`.`signature`, `sign`.`signer` - FROM `item` LEFT JOIN `contact` ON `contact`.`id` = `item`.`contact-id` - 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 `contact`.`blocked` = 0 AND `contact`.`pending` = 0 - AND ( `item`.`edited` > '%s' OR `item`.`changed` > '%s' ) - $sql_extra - ORDER BY `parent` %s, `created` ASC LIMIT 0, 300", - intval($owner_id), - dbesc($check_date), - dbesc($check_date), - dbesc($sort) - ); + return $ret; - // Will check further below if this actually returned results. - // We will provide an empty feed if that is the case. +} - $items = $r; - $items = fetch_post_tags($items); - $feed_template = get_markup_template(($dfrn_id) ? 'atom_feed_dfrn.tpl' : 'atom_feed.tpl'); +function get_public_feed($channel,$params) { - $atom = ''; + $type = 'xml'; + $begin = '0000-00-00 00:00:00'; + $end = ''; + $start = 0; + $records = 40; + $direction = 'desc'; - $hubxml = feed_hublinks(); + if(! $params) + $params = array(); - $salmon = feed_salmonlinks($owner_nick); + $params['type'] = ((x($params,'type')) ? $params['type'] : 'xml'); + $params['begin'] = ((x($params,'begin')) ? $params['begin'] : '0000-00-00 00:00:00'); + $params['end'] = ((x($params,'end')) ? $params['end'] : datetime_convert('UTC','UTC','now')); + $params['start'] = ((x($params,'start')) ? $params['start'] : 0); + $params['records'] = ((x($params,'records')) ? $params['records'] : 40); + $params['direction'] = ((x($params,'direction')) ? $params['direction'] : 'desc'); + + switch($params['type']) { + case 'json': + header("Content-type: application/atom+json"); + break; + case 'xml': + default: + header("Content-type: application/atom+xml"); + break; + } - $atom .= replace_macros($feed_template, array( - '$version' => xmlify(FRIENDICA_VERSION), - '$feed_id' => xmlify($a->get_baseurl() . '/channel/' . $owner_nick), - '$feed_title' => xmlify($owner['name']), - '$feed_updated' => xmlify(datetime_convert('UTC', 'UTC', 'now' , ATOM_TIME)) , - '$hub' => $hubxml, - '$salmon' => $salmon, - '$name' => xmlify($owner['name']), - '$profile_page' => xmlify($owner['url']), - '$photo' => xmlify($owner['photo']), - '$thumb' => xmlify($owner['thumb']), - '$picdate' => xmlify(datetime_convert('UTC','UTC',$owner['avatar_date'] . '+00:00' , ATOM_TIME)) , - '$uridate' => xmlify(datetime_convert('UTC','UTC',$owner['uri_date'] . '+00:00' , ATOM_TIME)) , - '$namdate' => xmlify(datetime_convert('UTC','UTC',$owner['name_date'] . '+00:00' , ATOM_TIME)) , - '$birthday' => ((strlen($birthday)) ? '<dfrn:birthday>' . xmlify($birthday) . '</dfrn:birthday>' : ''), - '$community' => (($owner['page-flags'] == PAGE_COMMUNITY) ? '<dfrn:community>1</dfrn:community>' : '') - )); + + return get_feed_for($channel,get_observer_hash(),$params); +} - call_hooks('atom_feed', $atom); +function get_feed_for($channel, $observer_hash, $params) { - if(! count($items)) { + if(! channel) + http_status_exit(401); - call_hooks('atom_feed_end', $atom); + if(! perm_is_allowed($channel['channel_id'],$observer_hash,'view_stream')) + http_status_exit(403); - $atom .= '</feed>' . "\r\n"; - return $atom; - } + $items = items_fetch(array( + 'wall' => '1', + 'datequery' => $params['begin'], + 'datequery2' => $params['end'], + 'start' => $params['start'], // FIXME + 'records' => $params['records'], // FIXME + 'direction' => $params['direction'], // FIXME + 'order' => 'post' + ), $channel, $observer_hash, CLIENT_MODE_NORMAL, get_app()->module); - foreach($items as $item) { - // prevent private email from leaking. - if($item['network'] === NETWORK_MAIL) - continue; + $feed_template = get_markup_template('atom_feed.tpl'); - // public feeds get html, our own nodes use bbcode + $atom = ''; - if($public_feed) { - $type = 'html'; - // catch any email that's in a public conversation and make sure it doesn't leak - if($item['private']) + $atom .= replace_macros($feed_template, array( + '$version' => xmlify(RED_VERSION), + '$red' => xmlify(RED_PLATFORM), + '$feed_id' => xmlify($channel['channel_url']), + '$feed_title' => xmlify($channel['channel_name']), + '$feed_updated' => xmlify(datetime_convert('UTC', 'UTC', 'now' , ATOM_TIME)) , + '$hub' => '', // feed_hublinks(), + '$salmon' => '', // feed_salmonlinks($channel['channel_address']), + '$name' => xmlify($channel['channel_name']), + '$profile_page' => xmlify($channel['channel_url']), + '$mimephoto' => xmlify($channel['xchan_photo_mimetype']), + '$photo' => xmlify($channel['xchan_photo_l']), + '$thumb' => xmlify($channel['xchan_photo_m']), + '$picdate' => '', + '$uridate' => '', + '$namdate' => '', + '$birthday' => '', + '$community' => '', + )); + + call_hooks('atom_feed', $atom); + + if($items) { + $type = 'html'; + foreach($items as $item) { + if($item['item_private']) continue; - } - else { - $type = 'text'; - } - $atom .= atom_entry($item,$type,null,$owner,true); + $atom .= atom_entry($item,$type,null,$owner,true); + } } call_hooks('atom_feed_end', $atom); @@ -300,8 +317,7 @@ function construct_activity_object($item) { if($item['object']) { $o = '<as:object>' . "\r\n"; - $r = parse_xml_string($item['object'],false); - + $r = json_decode($item['object'],false); if(! $r) return ''; @@ -311,7 +327,8 @@ function construct_activity_object($item) { $o .= '<id>' . xmlify($r->id) . '</id>' . "\r\n"; if($r->title) $o .= '<title>' . xmlify($r->title) . '</title>' . "\r\n"; - if($r->link) { + if($r->links) { + // FIXME!! if(substr($r->link,0,1) === '<') { $r->link = preg_replace('/\<link(.*?)\"\>/','<link$1"/>',$r->link); $o .= $r->link; @@ -332,7 +349,7 @@ function construct_activity_target($item) { if($item['target']) { $o = '<as:target>' . "\r\n"; - $r = parse_xml_string($item['target'],false); + $r = json_decode($item['target'],false); if(! $r) return ''; if($r->type) @@ -341,7 +358,8 @@ function construct_activity_target($item) { $o .= '<id>' . xmlify($r->id) . '</id>' . "\r\n"; if($r->title) $o .= '<title>' . xmlify($r->title) . '</title>' . "\r\n"; - if($r->link) { + if($r->links) { + // FIXME !!! if(substr($r->link,0,1) === '<') { if(strstr($r->link,'&') && (! strstr($r->link,'&'))) $r->link = str_replace('&','&', $r->link); @@ -365,7 +383,7 @@ function construct_activity_target($item) { * The purpose of this function is to apply system message length limits to * imported messages without including any embedded photos in the length */ -if(! function_exists('limit_body_size')) { + function limit_body_size($body) { $maxlen = get_max_import_size(); @@ -443,7 +461,7 @@ function limit_body_size($body) { } else return $body; -}} +} function title_is_body($title, $body) { @@ -473,7 +491,6 @@ function title_is_body($title, $body) { function get_item_elements($x) { $arr = array(); - $arr['body'] = (($x['body']) ? htmlentities($x['body'],ENT_COMPAT,'UTF-8',false) : ''); $arr['created'] = datetime_convert('UTC','UTC',$x['created']); @@ -488,9 +505,14 @@ function get_item_elements($x) { $arr['edited'] = datetime_convert(); $arr['title'] = (($x['title']) ? htmlentities($x['title'], ENT_COMPAT,'UTF-8',false) : ''); + + if(mb_strlen($arr['title']) > 255) + $arr['title'] = mb_substr($arr['title'],0,255); + + $arr['app'] = (($x['app']) ? htmlentities($x['app'], ENT_COMPAT,'UTF-8',false) : ''); - $arr['uri'] = (($x['message_id']) ? htmlentities($x['message_id'], ENT_COMPAT,'UTF-8',false) : ''); - $arr['parent_uri'] = (($x['message_top']) ? htmlentities($x['message_top'], ENT_COMPAT,'UTF-8',false) : ''); + $arr['mid'] = (($x['message_id']) ? htmlentities($x['message_id'], ENT_COMPAT,'UTF-8',false) : ''); + $arr['parent_mid'] = (($x['message_top']) ? htmlentities($x['message_top'], ENT_COMPAT,'UTF-8',false) : ''); $arr['thr_parent'] = (($x['message_parent']) ? htmlentities($x['message_parent'], ENT_COMPAT,'UTF-8',false) : ''); $arr['plink'] = (($x['permalink']) ? htmlentities($x['permalink'], ENT_COMPAT,'UTF-8',false) : ''); @@ -500,7 +522,8 @@ function get_item_elements($x) { $arr['mimetype'] = (($x['mimetype']) ? htmlentities($x['mimetype'], ENT_COMPAT,'UTF-8',false) : ''); $arr['obj_type'] = (($x['object_type']) ? htmlentities($x['object_type'], ENT_COMPAT,'UTF-8',false) : ''); $arr['tgt_type'] = (($x['target_type']) ? htmlentities($x['target_type'], ENT_COMPAT,'UTF-8',false) : ''); - + $arr['comment_policy'] = (($x['comment_scope']) ? htmlentities($x['comment_scope'], ENT_COMPAT,'UTF-8',false) : 'contacts'); + $arr['object'] = activity_sanitise($x['object']); $arr['target'] = activity_sanitise($x['target']); @@ -509,6 +532,21 @@ function get_item_elements($x) { $arr['item_private'] = ((array_key_exists('flags',$x) && is_array($x['flags']) && in_array('private',$x['flags'])) ? 1 : 0); + $arr['item_flags'] = 0; + + // if it's a private post, encrypt it in the DB. + // We have to do that here because we need to cleanse the input and prevent bad stuff from getting in, + // and we need plaintext to do that. + + if(intval($arr['item_private'])) { + $arr['item_flags'] = $arr['item_flags'] | ITEM_OBSCURED; + $key = get_config('system','pubkey'); + if($arr['title']) + $arr['title'] = json_encode(aes_encapsulate($arr['title'],$key)); + if($arr['body']) + $arr['body'] = json_encode(aes_encapsulate($arr['body'],$key)); + } + if(array_key_exists('flags',$x) && in_array('deleted',$x['flags'])) $arr['item_restrict'] = ITEM_DELETED; @@ -564,20 +602,32 @@ function encode_item($item) { logger('encode_item: ' . print_r($item,true)); - $r = q("select channel_r_stream from channel where channel_id = %d limit 1", + $r = q("select channel_r_stream, channel_w_comment from channel where channel_id = %d limit 1", intval($item['uid']) ); - if($r) + if($r) { $public_scope = $r[0]['channel_r_stream']; - else + $comment_scope = $r[0]['channel_w_comment']; + } + else { $public_scope = 0; + $comment_scope = 0; + } $scope = map_scope($public_scope); + $c_scope = map_scope($comment_scope); + if(array_key_exists('item_flags',$item) && ($item['item_flags'] & ITEM_OBSCURED)) { + $key = get_config('system','prvkey'); + if($item['title']) + $item['title'] = aes_unencapsulate(json_decode_plus($item['title']),$key); + if($item['body']) + $item['body'] = aes_unencapsulate(json_decode_plus($item['body']),$key); + } if($item['item_restrict'] & ITEM_DELETED) { - $x['message_id'] = $item['uri']; + $x['message_id'] = $item['mid']; $x['created'] = $item['created']; $x['flags'] = array('deleted'); $x['owner'] = encode_item_xchan($item['owner']); @@ -585,8 +635,8 @@ function encode_item($item) { return $x; } - $x['message_id'] = $item['uri']; - $x['message_top'] = $item['parent_uri']; + $x['message_id'] = $item['mid']; + $x['message_top'] = $item['parent_mid']; $x['message_parent'] = $item['thr_parent']; $x['created'] = $item['created']; $x['edited'] = $item['edited']; @@ -605,17 +655,22 @@ function encode_item($item) { $x['owner'] = encode_item_xchan($item['owner']); $x['author'] = encode_item_xchan($item['author']); if($item['object']) - $x['object'] = json_decode($item['object'],true); + $x['object'] = json_decode_plus($item['object']); if($item['target']) - $x['target'] = json_decode($item['target'],true); + $x['target'] = json_decode_plus($item['target']); if($item['attach']) - $x['attach'] = json_decode($item['attach'],true); + $x['attach'] = json_decode_plus($item['attach']); if($y = encode_item_flags($item)) $x['flags'] = $y; if(! in_array('private',$y)) $x['public_scope'] = $scope; + if($item['item_flags'] & ITEM_NOCOMMENT) + $x['comment_scope'] = 'none'; + else + $x['comment_scope'] = $c_scope; + if($item['term']) $x['tags'] = encode_item_terms($item['term']); @@ -763,16 +818,30 @@ function encode_mail($item) { $x = array(); $x['type'] = 'mail'; - logger('encode_mail: ' . print_r($item,true)); + if(array_key_exists('mail_flags',$item) && ($item['mail_flags'] & MAIL_OBSCURED)) { + $key = get_config('system','prvkey'); + if($item['title']) + $item['title'] = aes_unencapsulate(json_decode_plus($item['title']),$key); + if($item['body']) + $item['body'] = aes_unencapsulate(json_decode_plus($item['body']),$key); + } - $x['message_id'] = $item['uri']; - $x['message_parent'] = $item['parent_uri']; + $x['message_id'] = $item['mid']; + $x['message_parent'] = $item['parent_mid']; $x['created'] = $item['created']; $x['title'] = $item['title']; $x['body'] = $item['body']; $x['from'] = encode_item_xchan($item['from']); $x['to'] = encode_item_xchan($item['to']); + $x['flags'] = array(); + + if($item['mail_flags'] & MAIL_RECALLED) { + $x['flags'][] = 'recalled'; + $x['title'] = ''; + $x['body'] = ''; + } + return $x; } @@ -782,16 +851,33 @@ function get_mail_elements($x) { $arr = array(); - $arr['body'] = (($x['body']) ? htmlentities($x['body'],ENT_COMPAT,'UTF-8',false) : ''); + $arr['body'] = (($x['body']) ? htmlentities($x['body'], ENT_COMPAT,'UTF-8',false) : ''); + $arr['title'] = (($x['title'])? htmlentities($x['title'],ENT_COMPAT,'UTF-8',false) : ''); $arr['created'] = datetime_convert('UTC','UTC',$x['created']); + $arr['mail_flags'] = 0; + + if($x['flags'] && is_array($x['flags'])) { + if(in_array('recalled',$x['flags'])) { + $arr['mail_flags'] |= MAIL_RECALLED; + } + } + + $key = get_config('system','pubkey'); + $arr['mail_flags'] |= MAIL_OBSCURED; + $arr['body'] = htmlentities($arr['body'],ENT_COMPAT,'UTF-8',false); + if($arr['body']) + $arr['body'] = json_encode(aes_encapsulate($arr['body'],$key)); + $arr['title'] = htmlentities($arr['title'],ENT_COMPAT,'UTF-8',false); + if($arr['title']) + $arr['title'] = json_encode(aes_encapsulate($arr['title'],$key)); + if($arr['created'] > datetime_convert()) $arr['created'] = datetime_convert(); - $arr['title'] = (($x['title']) ? htmlentities($x['title'], ENT_COMPAT,'UTF-8',false) : ''); - $arr['uri'] = (($x['message_id']) ? htmlentities($x['message_id'], ENT_COMPAT,'UTF-8',false) : ''); - $arr['parent_uri'] = (($x['message_parent']) ? htmlentities($x['message_parent'], ENT_COMPAT,'UTF-8',false) : ''); + $arr['mid'] = (($x['message_id']) ? htmlentities($x['message_id'], ENT_COMPAT,'UTF-8',false) : ''); + $arr['parent_mid'] = (($x['message_parent']) ? htmlentities($x['message_parent'], ENT_COMPAT,'UTF-8',false) : ''); if(import_author_xchan($x['from'])) @@ -822,6 +908,7 @@ function get_profile_elements($x) { $arr['desc'] = (($x['title']) ? htmlentities($x['title'],ENT_COMPAT,'UTF-8',false) : ''); $arr['dob'] = datetime_convert('UTC','UTC',$x['birthday'],'Y-m-d'); + $arr['age'] = (($x['age']) ? intval($x['age']) : 0); $arr['gender'] = (($x['gender']) ? htmlentities($x['gender'], ENT_COMPAT,'UTF-8',false) : ''); $arr['marital'] = (($x['marital']) ? htmlentities($x['marital'], ENT_COMPAT,'UTF-8',false) : ''); @@ -841,8 +928,6 @@ function get_profile_elements($x) { function get_atom_elements($feed,$item) { - require_once('library/HTMLPurifier.auto.php'); - require_once('include/html2bbcode.php'); $best_photo = array(); @@ -857,13 +942,14 @@ function get_atom_elements($feed,$item) { $res['author-name'] = unxmlify($feed->get_title()); $res['author-link'] = unxmlify($feed->get_permalink()); } - $res['uri'] = unxmlify($item->get_id()); + $res['mid'] = unxmlify($item->get_id()); $res['title'] = unxmlify($item->get_title()); $res['body'] = unxmlify($item->get_content()); $res['plink'] = unxmlify($item->get_link(0)); // removing the content of the title if its identically to the body // This helps with auto generated titles e.g. from tumblr + if (title_is_body($res["title"], $res["body"])) $res['title'] = ""; @@ -978,14 +1064,7 @@ function get_atom_elements($feed,$item) { $res['body'] = oembed_html2bbcode($res['body']); - $config = HTMLPurifier_Config::createDefault(); - $config->set('Cache.DefinitionImpl', null); - - // we shouldn't need a whitelist, because the bbcode converter - // will strip out any unsupported tags. - - $purifier = new HTMLPurifier($config); - $res['body'] = $purifier->purify($res['body']); + $res['body'] = purify_html($res['body']); $res['body'] = @html2bbcode($res['body']); @@ -1212,6 +1291,7 @@ function get_atom_elements($feed,$item) { // This is some experimental stuff. By now retweets are shown with "RT:" // But: There is data so that the message could be shown similar to native retweets // There is some better way to parse this array - but it didn't worked for me. + $child = $item->feed->data["child"][SIMPLEPIE_NAMESPACE_ATOM_10]["feed"][0]["child"][SIMPLEPIE_NAMESPACE_ATOM_10]["entry"][0]["child"]["http://activitystrea.ms/spec/1.0/"][object][0]["child"]; if (is_array($child)) { $message = $child["http://activitystrea.ms/spec/1.0/"]["object"][0]["child"][SIMPLEPIE_NAMESPACE_ATOM_10]["content"][0]["data"]; @@ -1238,12 +1318,6 @@ function get_atom_elements($feed,$item) { call_hooks('parse_atom', $arr); - //if (($res["title"] != "") or (strpos($res["body"], "RT @") > 0)) { - //if (strpos($res["body"], "RT @") !== false) { - // $debugfile = tempnam("/home/ike/log", "item-res2-"); - // file_put_contents($debugfile, serialize($arr)); - //} - return $res; } @@ -1275,24 +1349,63 @@ function item_store($arr,$force_parent = false) { return 0; } - $arr['lang'] = detect_language($arr['body']); + // Don't let anybody set these, either intentionally or accidentally - $allowed_languages = get_pconfig($arr['uid'],'system','allowed_languages'); + if(array_key_exists('id',$arr)) + unset($arr['id']); + if(array_key_exists('parent',$arr)) + unset($arr['parent']); + + $arr['mimetype'] = ((x($arr,'mimetype')) ? notags(trim($arr['mimetype'])) : 'text/bbcode'); + $arr['title'] = ((x($arr,'title')) ? notags(trim($arr['title'])) : ''); + $arr['body'] = ((x($arr,'body')) ? trim($arr['body']) : ''); + + $arr['allow_cid'] = ((x($arr,'allow_cid')) ? trim($arr['allow_cid']) : ''); + $arr['allow_gid'] = ((x($arr,'allow_gid')) ? trim($arr['allow_gid']) : ''); + $arr['deny_cid'] = ((x($arr,'deny_cid')) ? trim($arr['deny_cid']) : ''); + $arr['deny_gid'] = ((x($arr,'deny_gid')) ? trim($arr['deny_gid']) : ''); + $arr['item_private'] = ((x($arr,'item_private')) ? intval($arr['item_private']) : 0 ); + $arr['item_flags'] = ((x($arr,'item_flags')) ? intval($arr['item_flags']) : 0 ); - if((is_array($allowed_languages)) && ($arr['lang']) && (! array_key_exists($arr['lang'],$allowed_languages))) { - $translate = array('item' => $arr, 'from' => $arr['lang'], 'to' => $allowed_languages, 'translated' => false); - call_hooks('item_translate', $translate); - if((! $translate['translated']) && (intval(get_pconfig($arr['uid'],'system','reject_disallowed_languages')))) { - logger('item_store: language ' . $arr['lang'] . ' not accepted for uid ' . $arr['uid']); - return; + // this is a bit messy - we really need an input filter chain that temporarily undoes obscuring + + if($arr['mimetype'] != 'text/html') { + if((strpos($arr['body'],'<') !== false) || (strpos($arr['body'],'>') !== false)) + $arr['body'] = escape_tags($arr['body']); + if((strpos($arr['title'],'<') !== false) || (strpos($arr['title'],'>') !== false)) + $arr['title'] = escape_tags($arr['title']); + } + + // only detect language if we have text content, and if the post is private but not yet + // obscured, make it so. + + if(! ($arr['item_flags'] & ITEM_OBSCURED)) { + $arr['lang'] = detect_language($arr['body']); + + $allowed_languages = get_pconfig($arr['uid'],'system','allowed_languages'); + + if((is_array($allowed_languages)) && ($arr['lang']) && (! array_key_exists($arr['lang'],$allowed_languages))) { + $translate = array('item' => $arr, 'from' => $arr['lang'], 'to' => $allowed_languages, 'translated' => false); + call_hooks('item_translate', $translate); + if((! $translate['translated']) && (intval(get_pconfig($arr['uid'],'system','reject_disallowed_languages')))) { + logger('item_store: language ' . $arr['lang'] . ' not accepted for uid ' . $arr['uid']); + return; + } + $arr = $translate['item']; } - $arr = $translate['item']; + if($arr['item_private']) { + $key = get_config('system','pubkey'); + $arr['item_flags'] = $arr['item_flags'] | ITEM_OBSCURED; + if($arr['title']) + $arr['title'] = json_encode(aes_encapsulate($arr['title'],$key)); + if($arr['body']) + $arr['body'] = json_encode(aes_encapsulate($arr['body'],$key)); + } + } - // Shouldn't happen but we want to make absolutely sure it doesn't leak from a plugin. - if((strpos($arr['body'],'<') !== false) || (strpos($arr['body'],'>') !== false)) - $arr['body'] = escape_tags($arr['body']); + if((x($arr,'object')) && is_array($arr['object'])) { activity_sanitise($arr['object']); @@ -1310,7 +1423,7 @@ function item_store($arr,$force_parent = false) { } $arr['aid'] = ((x($arr,'aid')) ? intval($arr['aid']) : 0); - $arr['uri'] = ((x($arr,'uri')) ? notags(trim($arr['uri'])) : random_string()); + $arr['mid'] = ((x($arr,'mid')) ? notags(trim($arr['mid'])) : random_string()); $arr['author_xchan'] = ((x($arr,'author_xchan')) ? notags(trim($arr['author_xchan'])) : ''); $arr['owner_xchan'] = ((x($arr,'owner_xchan')) ? notags(trim($arr['owner_xchan'])) : ''); $arr['created'] = ((x($arr,'created') !== false) ? datetime_convert('UTC','UTC',$arr['created']) : datetime_convert()); @@ -1319,38 +1432,44 @@ function item_store($arr,$force_parent = false) { $arr['commented'] = datetime_convert(); $arr['received'] = datetime_convert(); $arr['changed'] = datetime_convert(); - $arr['mimetype'] = ((x($arr,'mimetype')) ? notags(trim($arr['mimetype'])) : 'text/bbcode'); - $arr['title'] = ((x($arr,'title')) ? notags(trim($arr['title'])) : ''); $arr['location'] = ((x($arr,'location')) ? notags(trim($arr['location'])) : ''); $arr['coord'] = ((x($arr,'coord')) ? notags(trim($arr['coord'])) : ''); - $arr['parent_uri'] = ((x($arr,'parent_uri')) ? notags(trim($arr['parent_uri'])) : ''); + $arr['parent_mid'] = ((x($arr,'parent_mid')) ? notags(trim($arr['parent_mid'])) : ''); + $arr['thr_parent'] = ((x($arr,'thr_parent')) ? notags(trim($arr['thr_parent'])) : $arr['parent_mid']); $arr['verb'] = ((x($arr,'verb')) ? notags(trim($arr['verb'])) : ''); $arr['obj_type'] = ((x($arr,'obj_type')) ? notags(trim($arr['obj_type'])) : ''); $arr['object'] = ((x($arr,'object')) ? trim($arr['object']) : ''); $arr['tgt_type'] = ((x($arr,'tgt_type')) ? notags(trim($arr['tgt_type'])) : ''); $arr['target'] = ((x($arr,'target')) ? trim($arr['target']) : ''); $arr['plink'] = ((x($arr,'plink')) ? notags(trim($arr['plink'])) : ''); - $arr['allow_cid'] = ((x($arr,'allow_cid')) ? trim($arr['allow_cid']) : ''); - $arr['allow_gid'] = ((x($arr,'allow_gid')) ? trim($arr['allow_gid']) : ''); - $arr['deny_cid'] = ((x($arr,'deny_cid')) ? trim($arr['deny_cid']) : ''); - $arr['deny_gid'] = ((x($arr,'deny_gid')) ? trim($arr['deny_gid']) : ''); - $arr['item_private'] = ((x($arr,'item_private')) ? intval($arr['item_private']) : 0 ); - $arr['body'] = ((x($arr,'body')) ? trim($arr['body']) : ''); $arr['attach'] = ((x($arr,'attach')) ? notags(trim($arr['attach'])) : ''); $arr['app'] = ((x($arr,'app')) ? notags(trim($arr['app'])) : ''); $arr['item_restrict'] = ((x($arr,'item_restrict')) ? intval($arr['item_restrict']) : 0 ); - $arr['item_flags'] = ((x($arr,'item_flags')) ? intval($arr['item_flags']) : 0 ); + + $arr['comment_policy'] = ((x($arr,'comment_policy')) ? notags(trim($arr['comment_policy'])) : 'contacts' ); + $arr['item_flags'] = $arr['item_flags'] | ITEM_UNSEEN; - - $arr['thr_parent'] = $arr['parent_uri']; - $arr['llink'] = z_root() . '/display/' . $arr['uri']; + if($arr['comment_policy'] == 'none') + $arr['item_flags'] = $arr['item_flags'] | ITEM_NOCOMMENT; + + + + // handle time travelers + // Allow a bit of fudge in case somebody just has a slightly slow/fast clock + + $d1 = new DateTime('now +10 minutes', new DateTimeZone('UTC')); + $d2 = new DateTime($arr['created'] . '+00:00'); + if($d2 > $d1) + $arr['item_restrict'] = $arr['item_restrict'] | ITEM_DELAYED_PUBLISH; + + $arr['llink'] = z_root() . '/display/' . $arr['mid']; if(! $arr['plink']) $arr['plink'] = $arr['llink']; - if($arr['parent_uri'] === $arr['uri']) { + if($arr['parent_mid'] === $arr['mid']) { $parent_id = 0; $parent_deleted = 0; $allow_cid = $arr['allow_cid']; @@ -1364,23 +1483,23 @@ function item_store($arr,$force_parent = false) { // find the parent and snarf the item id and ACL's // and anything else we need to inherit - $r = q("SELECT * FROM `item` WHERE `uri` = '%s' AND `uid` = %d ORDER BY `id` ASC LIMIT 1", - dbesc($arr['parent_uri']), + $r = q("SELECT * FROM `item` WHERE `mid` = '%s' AND `uid` = %d ORDER BY `id` ASC LIMIT 1", + dbesc($arr['parent_mid']), intval($arr['uid']) ); - if(count($r)) { + if($r) { // is the new message multi-level threaded? // even though we don't support it now, preserve the info // and re-attach to the conversation parent. - if($r[0]['uri'] != $r[0]['parent_uri']) { - $arr['parent_uri'] = $r[0]['parent_uri']; - $z = q("SELECT * FROM `item` WHERE `uri` = '%s' AND `parent_uri` = '%s' AND `uid` = %d + if($r[0]['mid'] != $r[0]['parent_mid']) { + $arr['parent_mid'] = $r[0]['parent_mid']; + $z = q("SELECT * FROM `item` WHERE `mid` = '%s' AND `parent_mid` = '%s' AND `uid` = %d ORDER BY `id` ASC LIMIT 1", - dbesc($r[0]['parent_uri']), - dbesc($r[0]['parent_uri']), + dbesc($r[0]['parent_mid']), + dbesc($r[0]['parent_mid']), intval($arr['uid']) ); if($z && count($z)) @@ -1412,22 +1531,8 @@ function item_store($arr,$force_parent = false) { $arr['item_private'] = 0; } else { - - // Allow one to see reply tweets from status.net even when - // we don't have or can't see the original post. - - if($force_parent) { - logger('item_store: $force_parent=true, reply converted to top-level post.'); - $parent_id = 0; - $arr['parent_uri'] = $arr['uri']; - $arr['flags'] = $arr['flags'] | ITEM_THREAD_TOP; - } - else { - logger('item_store: item parent was not found - ignoring item'); - return 0; - } - - $parent_deleted = 0; + logger('item_store: item parent was not found - ignoring item'); + return 0; } } @@ -1435,8 +1540,8 @@ function item_store($arr,$force_parent = false) { $arr['item_restrict'] = $arr['item_restrict'] | ITEM_DELETED; - $r = q("SELECT `id` FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1", - dbesc($arr['uri']), + $r = q("SELECT `id` FROM `item` WHERE `mid` = '%s' AND `uid` = %d LIMIT 1", + dbesc($arr['mid']), intval($arr['uid']) ); if($r) { @@ -1471,8 +1576,8 @@ function item_store($arr,$force_parent = false) { // find the item we just created - $r = q("SELECT `id` FROM `item` WHERE `uri` = '%s' AND `uid` = %d ORDER BY `id` ASC ", - $arr['uri'], // already dbesc'd + $r = q("SELECT `id` FROM `item` WHERE `mid` = '%s' AND `uid` = %d ORDER BY `id` ASC ", + $arr['mid'], // already dbesc'd intval($arr['uid']) ); @@ -1486,25 +1591,25 @@ function item_store($arr,$force_parent = false) { } if(count($r) > 1) { logger('item_store: duplicated post occurred. Removing duplicates.'); - q("DELETE FROM `item` WHERE `uri` = '%s' AND `uid` = %d AND `id` != %d ", - $arr['uri'], + q("DELETE FROM `item` WHERE `mid` = '%s' AND `uid` = %d AND `id` != %d ", + $arr['mid'], intval($arr['uid']), intval($current_post) ); } - if((! $parent_id) || ($arr['parent_uri'] === $arr['uri'])) + if((! $parent_id) || ($arr['parent_mid'] === $arr['mid'])) $parent_id = $current_post; if(strlen($allow_cid) || strlen($allow_gid) || strlen($deny_cid) || strlen($deny_gid)) $private = 1; else - $private = $arr['private']; + $private = $arr['item_private']; // Set parent id - and also make sure to inherit the parent's ACL's. - $r = q("UPDATE `item` SET `parent` = %d, `allow_cid` = '%s', `allow_gid` = '%s', - `deny_cid` = '%s', `deny_gid` = '%s', `item_private` = %d WHERE `id` = %d LIMIT 1", + $r = q("UPDATE item SET parent = %d, allow_cid = '%s', allow_gid = '%s', + deny_cid = '%s', deny_gid = '%s', item_private = %d WHERE id = %d LIMIT 1", intval($parent_id), dbesc($allow_cid), dbesc($allow_gid), @@ -1520,7 +1625,7 @@ function item_store($arr,$force_parent = false) { $arr['allow_gid'] = $allow_gid; $arr['deny_cid'] = $deny_cid; $arr['deny_gid'] = $deny_gid; - $arr['private'] = $private; + $arr['item_private'] = $private; // Store taxonomy @@ -1544,7 +1649,7 @@ function item_store($arr,$force_parent = false) { // update the commented timestamp on the parent - q("UPDATE `item` set `commented` = '%s', `changed` = '%s' WHERE `id` = %d LIMIT 1", + q("UPDATE item set commented = '%s', changed = '%s' WHERE id = %d LIMIT 1", dbesc(datetime_convert()), dbesc(datetime_convert()), intval($parent_id) @@ -1559,6 +1664,172 @@ function item_store($arr,$force_parent = false) { } + +function item_store_update($arr,$force_parent = false) { + + if(! intval($arr['uid'])) { + logger('item_store_update: no uid'); + return 0; + } + if(! intval($arr['id'])) { + logger('item_store_update: no id'); + return 0; + } + + $orig_post_id = $arr['id']; + unset($arr['id']); + $uid = $arr['uid']; + unset($arr['uid']); + + + $arr['lang'] = detect_language($arr['body']); + + $allowed_languages = get_pconfig($arr['uid'],'system','allowed_languages'); + + if((is_array($allowed_languages)) && ($arr['lang']) && (! array_key_exists($arr['lang'],$allowed_languages))) { + $translate = array('item' => $arr, 'from' => $arr['lang'], 'to' => $allowed_languages, 'translated' => false); + call_hooks('item_translate', $translate); + if((! $translate['translated']) && (intval(get_pconfig($arr['uid'],'system','reject_disallowed_languages')))) { + logger('item_store: language ' . $arr['lang'] . ' not accepted for uid ' . $arr['uid']); + return; + } + $arr = $translate['item']; + } + + // Shouldn't happen but we want to make absolutely sure it doesn't leak from a plugin. + + if((strpos($arr['body'],'<') !== false) || (strpos($arr['body'],'>') !== false)) + $arr['body'] = escape_tags($arr['body']); + + if((x($arr,'object')) && is_array($arr['object'])) { + activity_sanitise($arr['object']); + $arr['object'] = json_encode($arr['object']); + } + + if((x($arr,'target')) && is_array($arr['target'])) { + activity_sanitise($arr['target']); + $arr['target'] = json_encode($arr['target']); + } + + if((x($arr,'attach')) && is_array($arr['attach'])) { + activity_sanitise($arr['attach']); + $arr['attach'] = json_encode($arr['attach']); + } + + $orig = q("select * from item where id = %d and uid = %d limit 1", + intval($orig_post_id), + intval($uid) + ); + if(! $orig) { + logger('item_store_update: original post not found: ' . $orig_post_id); + return 0; + } + + unset($arr['aid']); + unset($arr['mid']); + unset($arr['parent']); + unset($arr['parent_mid']); + unset($arr['created']); + unset($arr['author_xchan']); + unset($arr['owner_xchan']); + unset($arr['thr_parent']); + unset($arr['llink']); + + $arr['edited'] = ((x($arr,'edited') !== false) ? datetime_convert('UTC','UTC',$arr['edited']) : datetime_convert()); + $arr['expires'] = ((x($arr,'expires') !== false) ? datetime_convert('UTC','UTC',$arr['expires']) : $orig[0]['expires']); + $arr['commented'] = datetime_convert(); + $arr['received'] = datetime_convert(); + $arr['changed'] = datetime_convert(); + $arr['mimetype'] = ((x($arr,'mimetype')) ? notags(trim($arr['mimetype'])) : 'text/bbcode'); + $arr['title'] = ((x($arr,'title')) ? notags(trim($arr['title'])) : ''); + $arr['location'] = ((x($arr,'location')) ? notags(trim($arr['location'])) : ''); + $arr['coord'] = ((x($arr,'coord')) ? notags(trim($arr['coord'])) : ''); + $arr['verb'] = ((x($arr,'verb')) ? notags(trim($arr['verb'])) : ''); + $arr['obj_type'] = ((x($arr,'obj_type')) ? notags(trim($arr['obj_type'])) : ''); + $arr['object'] = ((x($arr,'object')) ? trim($arr['object']) : ''); + $arr['tgt_type'] = ((x($arr,'tgt_type')) ? notags(trim($arr['tgt_type'])) : ''); + $arr['target'] = ((x($arr,'target')) ? trim($arr['target']) : ''); + $arr['plink'] = ((x($arr,'plink')) ? notags(trim($arr['plink'])) : $orig[0]['plink']); + $arr['allow_cid'] = ((x($arr,'allow_cid')) ? trim($arr['allow_cid']) : $orig[0]['allow_cid']); + $arr['allow_gid'] = ((x($arr,'allow_gid')) ? trim($arr['allow_gid']) : $orig[0]['allow_gid']); + $arr['deny_cid'] = ((x($arr,'deny_cid')) ? trim($arr['deny_cid']) : $orig[0]['deny_cid']); + $arr['deny_gid'] = ((x($arr,'deny_gid')) ? trim($arr['deny_gid']) : $orig[0]['deny_gid']); + $arr['item_private'] = ((x($arr,'item_private')) ? intval($arr['item_private']) : $orig[0]['item_private']); + $arr['body'] = ((x($arr,'body')) ? trim($arr['body']) : ''); + $arr['attach'] = ((x($arr,'attach')) ? notags(trim($arr['attach'])) : ''); + $arr['app'] = ((x($arr,'app')) ? notags(trim($arr['app'])) : ''); + $arr['item_restrict'] = ((x($arr,'item_restrict')) ? intval($arr['item_restrict']) : $orig[0]['item_restrict'] ); + $arr['item_flags'] = ((x($arr,'item_flags')) ? intval($arr['item_flags']) : $orig[0]['item_flags'] ); + + + call_hooks('post_remote_update',$arr); + + if(x($arr,'cancel')) { + logger('item_store_update: post cancelled by plugin.'); + return 0; + } + + // pull out all the taxonomy stuff for separate storage + + $terms = null; + if(array_key_exists('term',$arr)) { + $terms = $arr['term']; + unset($arr['term']); + } + + dbesc_array($arr); + + logger('item_store_update: ' . print_r($arr,true), LOGGER_DATA); + + $str = ''; + foreach($arr as $k => $v) { + if($str) + $str .= ","; + $str .= " `" . $k . "` = '" . $v . "' "; + } + + $r = dbq("update `item` set " . $str . " where id = " . $orig_post_id . " limit 1"); + + if($r) + logger('item_store_update: updated item ' . $orig_post_id, LOGGER_DEBUG); + else { + logger('item_store_update: could not update item'); + return 0; + } + + $r = q("delete from term where oid = %d and otype = %d", + intval($orig_post_id), + intval(TERM_OBJ_POST) + ); + + if(($terms) && (is_array($terms))) { + foreach($terms as $t) { + q("insert into term (uid,oid,otype,type,term,url) + values(%d,%d,%d,%d,'%s','%s') ", + intval($uid), + intval($orig_post_id), + intval(TERM_OBJ_POST), + intval($t['type']), + dbesc($t['term']), + dbesc($t['url']) + ); + } + + $arr['term'] = $terms; + } + + call_hooks('post_remote_update_end',$arr); + + send_status_notifications($orig_post_id,$arr); + + tag_deliver($uid,$orig_post_id); + + return $orig_post_id; +} + + + + function send_status_notifications($post_id,$item) { $notify = false; @@ -1581,8 +1852,8 @@ function send_status_notifications($post_id,$item) { // Was I involved in this conversation? - $x = q("select * from item where parent_uri = '%s' and uid = %d", - dbesc($item['parent_uri']), + $x = q("select * from item where parent_mid = '%s' and uid = %d", + dbesc($item['parent_mid']), intval($item['uid']) ); if($x) { @@ -1604,11 +1875,11 @@ function send_status_notifications($post_id,$item) { 'from_xchan' => $item['author_xchan'], 'to_xchan' => $r[0]['channel_hash'], 'item' => $item, - 'link' => get_app()->get_baseurl() . '/display/' . $item['uri'], + 'link' => get_app()->get_baseurl() . '/display/' . $item['mid'], 'verb' => ACTIVITY_POST, 'otype' => 'item', 'parent' => $parent, - 'parent_uri' => $item['parent_uri'] + 'parent_mid' => $item['parent_mid'] )); return; } @@ -1633,7 +1904,9 @@ function get_item_contact($item,$contacts) { function tag_deliver($uid,$item_id) { - // look for mention tags and setup a second delivery chain for forum/community posts if appropriate + // Called when we deliver things that might be tagged in ways that require delivery processing. + // Handles community tagging of posts and also look for mention tags + // and sets up a second delivery chain if appropriate $a = get_app(); @@ -1649,16 +1922,87 @@ function tag_deliver($uid,$item_id) { intval($item_id), intval($uid) ); - if(! count($i)) + if(! $i) return; $i = fetch_post_tags($i); $item = $i[0]; + + if($item['obj_type'] === ACTIVITY_OBJ_TAGTERM) { + + // We received a community tag activity for a post. + // See if we are the owner of the parent item and have given permission to tag our posts. + // If so tag the parent post. + + logger('tag_deliver: community tag activity received'); + + if(($item['owner_xchan'] === $u[0]['channel_hash']) && (! get_pconfig($u[0]['channel_id'],'system','blocktags'))) { + $j_tgt = json_decode_plus($item['target']); + if($j_tgt && $j_tgt['id']) { + $p = q("select * from item where mid = '%s' and uid = %d limit 1", + dbesc($j_tgt['id']), + intval($u[0]['channel_id']) + ); + if($p) { + $j_obj = json_decode_plus($item['object']); + logger('tag_deliver: tag object: ' . print_r($j_obj,true), LOGGER_DATA); + if($j_obj && $j_obj['id'] && $j_obj['title']) { + if(is_array($j_obj['link'])) + $taglink = get_rel_link($j_obj['link'],'alternate'); + store_item_tag($u[0]['channel_id'],$p[0]['id'],TERM_OBJ_POST,TERM_HASHTAG,$j_obj['title'],$j_obj['id']); + proc_run('php','include/notifier.php','edit_post',$p[0]['id']); + } + } + } + } + else + logger('tag_deliver: tag permission denied for ' . $u[0]['channel_address']); + } + + // This might be a followup by the original post author to a tagged forum + // If so setup a second delivery chain + + $r = null; + + if( ! ($item['item_flags'] & ITEM_THREAD_TOP)) { + $x = q("select * from item where id = parent and parent = %d and uid = %d limit 1", + intval($item['parent']), + intval($uid) + ); + if(($x) && ($x[0]['item_flags'] & ITEM_UPLINK) && ($x[0]['author_xchan'] == $item['author_xchan'])) { + logger('tag_deliver: creating second delivery chain for owner comment.'); + + // now change this copy of the post to a forum head message and deliver to all the tgroup members + // also reset all the privacy bits to the forum default permissions + + $private = (($u[0]['allow_cid'] || $u[0]['allow_gid'] || $u[0]['deny_cid'] || $u[0]['deny_gid']) ? 1 : 0); + + $flag_bits = ITEM_WALL|ITEM_ORIGIN; + + $r = q("update item set item_flags = ( item_flags | %d ), owner_xchan = '%s', allow_cid = '%s', allow_gid = '%s', + deny_cid = '%s', deny_gid = '%s', item_private = %d where id = %d limit 1", + intval($flag_bits), + dbesc($u[0]['channel_hash']), + dbesc($u[0]['allow_cid']), + dbesc($u[0]['allow_gid']), + dbesc($u[0]['deny_cid']), + dbesc($u[0]['deny_gid']), + intval($private), + intval($item_id) + ); + if($r) + proc_run('php','include/notifier.php','tgroup',$item_id); + else + logger('tag_deliver: failed to update item'); + } + } + $terms = get_terms_oftype($item['term'],TERM_MENTION); - logger('tag_deliver: post mentions: ' . print_r($terms,true), LOGGER_DATA); + if($terms) + logger('tag_deliver: post mentions: ' . print_r($terms,true), LOGGER_DATA); $link = normalise_link($a->get_baseurl() . '/channel/' . $u[0]['channel_address']); @@ -1687,7 +2031,7 @@ function tag_deliver($uid,$item_id) { $body = preg_replace('/\[share(.*?)\[\/share\]/','',$item['body']); - $pattern = '/@\[url\=' . preg_quote($term['url'],'/') . '\]' . preg_quote($u[0]['channel_name'],'/') . '\[\/url\]/'; + $pattern = '/@\[zrl\=' . preg_quote($term['url'],'/') . '\]' . preg_quote($u[0]['channel_name'],'/') . '\[\/zrl\]/'; if(! preg_match($pattern,$body,$matches)) { logger('tag_deliver: mention was in a reshare - ignoring'); @@ -1710,16 +2054,20 @@ function tag_deliver($uid,$item_id) { )); - if(! perm_is_allowed($uid,$item['author_xchan'],'tag_deliver')) + if(! perm_is_allowed($uid,$item['author_xchan'],'tag_deliver')) { + logger('tag_delivery denied for uid ' . $uid . ' and xchan ' . $item['author_xchan']); return; + } // tgroup delivery - setup a second delivery chain // prevent delivery looping - only proceed // if the message originated elsewhere and is a top-level post - if(($item['item_flags'] & ITEM_WALL) || ($item['item_flags'] & ITEM_ORIGIN) || (!($item['item_flags'] & ITEM_THREAD_TOP)) || ($item['id'] != $item['parent'])) + if(($item['item_flags'] & ITEM_WALL) || ($item['item_flags'] & ITEM_ORIGIN) || (!($item['item_flags'] & ITEM_THREAD_TOP)) || ($item['id'] != $item['parent'])) { + logger('tag_deliver: item was local or a comment. rejected.'); return; + } logger('tag_deliver: creating second delivery chain.'); @@ -1757,46 +2105,60 @@ function tgroup_check($uid,$item) { $mention = false; // check that the message originated elsewhere and is a top-level post + // or is a followup and we have already accepted the top level post - if(($item['wall']) || ($item['origin']) || ($item['uri'] != $item['parent-uri'])) + if($item['mid'] != $item['parent_mid']) { + $r = q("select id from item where mid = '%s' and uid = %d limit 1", + dbesc($item['parent_mid']), + intval($uid) + ); + if($r) + return true; + return false; + } + if(! perm_is_allowed($uid,$item['author_xchan'],'tag_deliver')) return false; - - $u = q("select * from user where uid = %d limit 1", + $u = q("select * from channel where channel_id = %d limit 1", intval($uid) ); - if(! count($u)) - return false; - - $community_page = (($u[0]['page-flags'] == PAGE_COMMUNITY) ? true : false); - $prvgroup = (($u[0]['page-flags'] == PAGE_PRVGROUP) ? true : false); + if(! $u) + return false; - $link = normalise_link($a->get_baseurl() . '/channel/' . $u[0]['nickname']); + $terms = get_terms_oftype($item['term'],TERM_MENTION); - // Diaspora uses their own hardwired link URL in @-tags - // instead of the one we supply with webfinger + if($terms) + logger('tgroup_check: post mentions: ' . print_r($terms,true), LOGGER_DATA); - $dlink = normalise_link($a->get_baseurl() . '/u/' . $u[0]['nickname']); + $link = normalise_link($a->get_baseurl() . '/channel/' . $u[0]['channel_address']); - $body = preg_replace("/\[share\](.*?)\[\/share\]/ism", '', $item['body']); - - $cnt = preg_match_all('/[\@\!]\[url\=(.*?)\](.*?)\[\/url\]/ism',$body,$matches,PREG_SET_ORDER); - if($cnt) { - foreach($matches as $mtch) { - if(link_compare($link,$mtch[1]) || link_compare($dlink,$mtch[1])) { + if($terms) { + foreach($terms as $term) { + if(($term['term'] == $u[0]['channel_name']) && link_compare($term['url'],$link)) { $mention = true; - logger('tgroup_check: mention found: ' . $mtch[2]); + break; } } - } + } - if(! $mention) + if($mention) { + logger('tgroup_check: mention found for ' . $u[0]['channel_name']); + } + else return false; - if((! $community_page) && (! $prvgroup)) - return false; + // At this point we've determined that the person receiving this post was mentioned in it. + // Now let's check if this mention was inside a reshare so we don't spam a forum + $body = preg_replace('/\[share(.*?)\[\/share\]/','',$item['body']); + + $pattern = '/@\[zrl\=' . preg_quote($term['url'],'/') . '\]' . preg_quote($u[0]['channel_name'],'/') . '\[\/zrl\]/'; + + if(! preg_match($pattern,$body,$matches)) { + logger('tgroup_check: mention was in a reshare - ignoring'); + return false; + } return true; @@ -1815,23 +2177,23 @@ function mail_store($arr) { $arr['body'] = escape_tags($arr['body']); $arr['account_id'] = ((x($arr,'account_id')) ? intval($arr['account_id']) : 0); - $arr['uri'] = ((x($arr,'uri')) ? notags(trim($arr['uri'])) : random_string()); + $arr['mid'] = ((x($arr,'mid')) ? notags(trim($arr['mid'])) : random_string()); $arr['from_xchan'] = ((x($arr,'from_xchan')) ? notags(trim($arr['from_xchan'])) : ''); $arr['to_xchan'] = ((x($arr,'to_xchan')) ? notags(trim($arr['to_xchan'])) : ''); $arr['created'] = ((x($arr,'created') !== false) ? datetime_convert('UTC','UTC',$arr['created']) : datetime_convert()); $arr['title'] = ((x($arr,'title')) ? notags(trim($arr['title'])) : ''); - $arr['parent_uri'] = ((x($arr,'parent_uri')) ? notags(trim($arr['parent_uri'])) : ''); + $arr['parent_mid'] = ((x($arr,'parent_mid')) ? notags(trim($arr['parent_mid'])) : ''); $arr['body'] = ((x($arr,'body')) ? trim($arr['body']) : ''); $arr['mail_flags'] = ((x($arr,'mail_flags')) ? intval($arr['mail_flags']) : 0 ); - if(! $arr['parent_uri']) { + if(! $arr['parent_mid']) { logger('mail_store: missing parent'); - $arr['parent_uri'] = $arr['uri']; + $arr['parent_mid'] = $arr['mid']; } - $r = q("SELECT `id` FROM mail WHERE `uri` = '%s' AND channel_id = %d LIMIT 1", - dbesc($arr['uri']), + $r = q("SELECT `id` FROM mail WHERE `mid` = '%s' AND channel_id = %d LIMIT 1", + dbesc($arr['mid']), intval($arr['channel_id']) ); if($r) { @@ -1858,8 +2220,8 @@ function mail_store($arr) { // find the item we just created - $r = q("SELECT `id` FROM mail WHERE `uri` = '%s' AND `channel_id` = %d ORDER BY `id` ASC ", - $arr['uri'], // already dbesc'd + $r = q("SELECT `id` FROM mail WHERE `mid` = '%s' AND `channel_id` = %d ORDER BY `id` ASC ", + $arr['mid'], // already dbesc'd intval($arr['channel_id']) ); @@ -1874,8 +2236,8 @@ function mail_store($arr) { } if(count($r) > 1) { logger('mail_store: duplicated post occurred. Removing duplicates.'); - q("DELETE FROM mail WHERE `uri` = '%s' AND `channel_id` = %d AND `id` != %d ", - $arr['uri'], + q("DELETE FROM mail WHERE `mid` = '%s' AND `channel_id` = %d AND `id` != %d ", + $arr['mid'], intval($arr['channel_id']), intval($current_post) ); @@ -2150,176 +2512,6 @@ function consume_feed($xml,$importer,&$contact, &$hub, $datedir = 0, $pass = 0) $photo_url = ''; $birthday = ''; - $hubs = $feed->get_links('hub'); - logger('consume_feed: hubs: ' . print_r($hubs,true), LOGGER_DATA); - - if(count($hubs)) - $hub = implode(',', $hubs); - - $rawtags = $feed->get_feed_tags( NAMESPACE_DFRN, 'owner'); - if(! $rawtags) - $rawtags = $feed->get_feed_tags( SIMPLEPIE_NAMESPACE_ATOM_10, 'author'); - if($rawtags) { - $elems = $rawtags[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]; - if($elems['name'][0]['attribs'][NAMESPACE_DFRN]['updated']) { - $name_updated = $elems['name'][0]['attribs'][NAMESPACE_DFRN]['updated']; - $new_name = $elems['name'][0]['data']; - } - if((x($elems,'link')) && ($elems['link'][0]['attribs']['']['rel'] === 'photo') && ($elems['link'][0]['attribs'][NAMESPACE_DFRN]['updated'])) { - $photo_timestamp = datetime_convert('UTC','UTC',$elems['link'][0]['attribs'][NAMESPACE_DFRN]['updated']); - $photo_url = $elems['link'][0]['attribs']['']['href']; - } - - if((x($rawtags[0]['child'], NAMESPACE_DFRN)) && (x($rawtags[0]['child'][NAMESPACE_DFRN],'birthday'))) { - $birthday = datetime_convert('UTC','UTC', $rawtags[0]['child'][NAMESPACE_DFRN]['birthday'][0]['data']); - } - } - - if((is_array($contact)) && ($photo_timestamp) && (strlen($photo_url)) && ($photo_timestamp > $contact['avatar_date'])) { - logger('consume_feed: Updating photo for ' . $contact['name']); - require_once("Photo.php"); - $photo_failure = false; - $have_photo = false; - - $r = q("SELECT `resource_id` FROM `photo` WHERE `contact-id` = %d AND `uid` = %d LIMIT 1", - intval($contact['id']), - intval($contact['uid']) - ); - if(count($r)) { - $resource_id = $r[0]['resource_id']; - $have_photo = true; - } - else { - $resource_id = photo_new_resource(); - } - - $img_str = fetch_url($photo_url,true); - // guess mimetype from headers or filename - $type = guess_image_type($photo_url,true); - - - $img = new Photo($img_str, $type); - if($img->is_valid()) { - if($have_photo) { - q("DELETE FROM `photo` WHERE `resource_id` = '%s' AND `contact-id` = %d AND `uid` = %d", - dbesc($resource_id), - intval($contact['id']), - intval($contact['uid']) - ); - } - - $img->scaleImageSquare(175); - - $hash = $resource_id; - $r = $img->store(0, $contact['uid'], $contact['id'], $hash, basename($photo_url), 'Contact Photos', 4); - - $img->scaleImage(80); - $r = $img->store(0, $contact['uid'], $contact['id'], $hash, basename($photo_url), 'Contact Photos', 5); - - $img->scaleImage(48); - $r = $img->store(0, $contact['uid'], $contact['id'], $hash, basename($photo_url), 'Contact Photos', 6); - - $a = get_app(); - - q("UPDATE `contact` SET `avatar_date` = '%s', `photo` = '%s', `thumb` = '%s', `micro` = '%s' - WHERE `uid` = %d AND `id` = %d LIMIT 1", - dbesc(datetime_convert()), - dbesc($a->get_baseurl() . '/photo/' . $hash . '-4.'.$img->getExt()), - dbesc($a->get_baseurl() . '/photo/' . $hash . '-5.'.$img->getExt()), - dbesc($a->get_baseurl() . '/photo/' . $hash . '-6.'.$img->getExt()), - intval($contact['uid']), - intval($contact['id']) - ); - } - } - - if((is_array($contact)) && ($name_updated) && (strlen($new_name)) && ($name_updated > $contact['name_date'])) { - $r = q("select * from contact where uid = %d and id = %d limit 1", - intval($contact['uid']), - intval($contact['id']) - ); - - $x = q("UPDATE `contact` SET `name` = '%s', `name_date` = '%s' WHERE `uid` = %d AND `id` = %d LIMIT 1", - dbesc(notags(trim($new_name))), - dbesc(datetime_convert()), - intval($contact['uid']), - intval($contact['id']) - ); - - // do our best to update the name on content items - - if(count($r)) { - q("update item set `author-name` = '%s' where `author-name` = '%s' and `author-link` = '%s' and uid = %d", - dbesc(notags(trim($new_name))), - dbesc($r[0]['name']), - dbesc($r[0]['url']), - intval($contact['uid']) - ); - } - } - - if(strlen($birthday)) { - if(substr($birthday,0,4) != $contact['bdyear']) { - logger('consume_feed: updating birthday: ' . $birthday); - - /** - * - * Add new birthday event for this person - * - * $bdtext is just a readable placeholder in case the event is shared - * with others. We will replace it during presentation to our $importer - * to contain a sparkle link and perhaps a photo. - * - */ - - $bdtext = sprintf( t('%s\'s birthday'), $contact['name']); - $bdtext2 = sprintf( t('Happy Birthday %s'), ' [url=' . $contact['url'] . ']' . $contact['name'] . '[/url]' ) ; - - - $r = q("INSERT INTO `event` (`uid`,`cid`,`created`,`edited`,`start`,`finish`,`summary`,`desc`,`type`) - VALUES ( %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s' ) ", - intval($contact['uid']), - intval($contact['id']), - dbesc(datetime_convert()), - dbesc(datetime_convert()), - dbesc(datetime_convert('UTC','UTC', $birthday)), - dbesc(datetime_convert('UTC','UTC', $birthday . ' + 1 day ')), - dbesc($bdtext), - dbesc($bdtext2), - dbesc('birthday') - ); - - - // update bdyear - - q("UPDATE `contact` SET `bdyear` = '%s' WHERE `uid` = %d AND `id` = %d LIMIT 1", - dbesc(substr($birthday,0,4)), - intval($contact['uid']), - intval($contact['id']) - ); - - // This function is called twice without reloading the contact - // Make sure we only create one event. This is why &$contact - // is a reference var in this function - - $contact['bdyear'] = substr($birthday,0,4); - } - - } - - $community_page = 0; - $rawtags = $feed->get_feed_tags( NAMESPACE_DFRN, 'community'); - if($rawtags) { - $community_page = intval($rawtags[0]['data']); - } - if(is_array($contact) && intval($contact['forum']) != $community_page) { - q("update contact set forum = %d where id = %d limit 1", - intval($community_page), - intval($contact['id']) - ); - $contact['forum'] = (string) $community_page; - } - // process any deleted entries @@ -2328,7 +2520,7 @@ function consume_feed($xml,$importer,&$contact, &$hub, $datedir = 0, $pass = 0) foreach($del_entries as $dentry) { $deleted = false; if(isset($dentry['attribs']['']['ref'])) { - $uri = $dentry['attribs']['']['ref']; + $mid = $dentry['attribs']['']['ref']; $deleted = true; if(isset($dentry['attribs']['']['when'])) { $when = $dentry['attribs']['']['when']; @@ -2338,70 +2530,38 @@ function consume_feed($xml,$importer,&$contact, &$hub, $datedir = 0, $pass = 0) $when = datetime_convert('UTC','UTC','now','Y-m-d H:i:s'); } if($deleted && is_array($contact)) { - $r = q("SELECT `item`.*, `contact`.`self` FROM `item` left join `contact` on `item`.`contact-id` = `contact`.`id` - WHERE `uri` = '%s' AND `item`.`uid` = %d AND `contact-id` = %d AND NOT `item`.`file` LIKE '%%[%%' LIMIT 1", - dbesc($uri), +/* $r = q("SELECT `item`.*, `contact`.`self` FROM `item` left join `contact` on `item`.`contact-id` = `contact`.`id` + WHERE `mid` = '%s' AND `item`.`uid` = %d AND `contact-id` = %d AND NOT `item`.`file` LIKE '%%[%%' LIMIT 1", + dbesc($mid), intval($importer['uid']), intval($contact['id']) ); +*/ if(count($r)) { $item = $r[0]; if(! $item['deleted']) - logger('consume_feed: deleting item ' . $item['id'] . ' uri=' . $item['uri'], LOGGER_DEBUG); - - if(($item['verb'] === ACTIVITY_TAG) && ($item['obj_type'] === ACTIVITY_OBJ_TAGTERM)) { - $xo = parse_xml_string($item['object'],false); - $xt = parse_xml_string($item['target'],false); - if($xt->type === ACTIVITY_OBJ_NOTE) { - $i = q("select * from `item` where uri = '%s' and uid = %d limit 1", - dbesc($xt->id), - intval($importer['importer_uid']) - ); - if(count($i)) { - - // For tags, the owner cannot remove the tag on the author's copy of the post. - - $owner_remove = (($item['contact-id'] == $i[0]['contact-id']) ? true: false); - $author_remove = (($item['origin'] && $item['self']) ? true : false); - $author_copy = (($item['origin']) ? true : false); - - if($owner_remove && $author_copy) - continue; - if($author_remove || $owner_remove) { - $tags = explode(',',$i[0]['tag']); - $newtags = array(); - if(count($tags)) { - foreach($tags as $tag) - if(trim($tag) !== trim($xo->body)) - $newtags[] = trim($tag); - } - q("update item set tag = '%s' where id = %d limit 1", - dbesc(implode(',',$newtags)), - intval($i[0]['id']) - ); - } - } - } - } + logger('consume_feed: deleting item ' . $item['id'] . ' mid=' . $item['mid'], LOGGER_DEBUG); - if($item['uri'] == $item['parent_uri']) { - $r = q("UPDATE `item` SET `deleted` = 1, `edited` = '%s', `changed` = '%s', + if($item['mid'] == $item['parent_mid']) { + $r = q("UPDATE `item` SET item_restrict = (item_restrict | %d), `edited` = '%s', `changed` = '%s', `body` = '', `title` = '' - WHERE `parent_uri` = '%s' AND `uid` = %d", + WHERE `parent_mid` = '%s' AND `uid` = %d", + intval(ITEM_DELETED), dbesc($when), dbesc(datetime_convert()), - dbesc($item['uri']), + dbesc($item['mid']), intval($importer['uid']) ); } else { - $r = q("UPDATE `item` SET `deleted` = 1, `edited` = '%s', `changed` = '%s', + $r = q("UPDATE `item` SET item_restrict = ( item_restrict | %d ), `edited` = '%s', `changed` = '%s', `body` = '', `title` = '' - WHERE `uri` = '%s' AND `uid` = %d LIMIT 1", + WHERE `mid` = '%s' AND `uid` = %d LIMIT 1", + intval(ITEM_DELETED), dbesc($when), dbesc(datetime_convert()), - dbesc($uri), + dbesc($mid), intval($importer['uid']) ); } @@ -2430,18 +2590,18 @@ function consume_feed($xml,$importer,&$contact, &$hub, $datedir = 0, $pass = 0) $rawthread = $item->get_item_tags( NAMESPACE_THREAD,'in-reply-to'); if(isset($rawthread[0]['attribs']['']['ref'])) { $is_reply = true; - $parent_uri = $rawthread[0]['attribs']['']['ref']; + $parent_mid = $rawthread[0]['attribs']['']['ref']; } - if(($is_reply) && is_array($contact)) { + if($is_reply) { if($pass == 1) continue; // not allowed to post - - if($contact['rel'] == CONTACT_IS_FOLLOWER) - continue; +// FIXME - check permissions +// if($contact['rel'] == CONTACT_IS_FOLLOWER) +// continue; // Have we seen it? If not, import it. @@ -2462,7 +2622,7 @@ function consume_feed($xml,$importer,&$contact, &$hub, $datedir = 0, $pass = 0) } - $r = q("SELECT `uid`, `edited`, `body` FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1", + $r = q("SELECT `uid`, `edited`, `body` FROM `item` WHERE `mid` = '%s' AND `uid` = %d LIMIT 1", dbesc($item_id), intval($importer['uid']) ); @@ -2476,7 +2636,7 @@ function consume_feed($xml,$importer,&$contact, &$hub, $datedir = 0, $pass = 0) if(datetime_convert('UTC','UTC',$datarray['edited']) < $r[0]['edited']) continue; - $r = q("UPDATE `item` SET `title` = '%s', `body` = '%s', `edited` = '%s' WHERE `uri` = '%s' AND `uid` = %d LIMIT 1", + $r = q("UPDATE `item` SET `title` = '%s', `body` = '%s', `edited` = '%s' WHERE `mid` = '%s' AND `uid` = %d LIMIT 1", dbesc($datarray['title']), dbesc($datarray['body']), dbesc(datetime_convert('UTC','UTC',$datarray['edited'])), @@ -2489,19 +2649,19 @@ function consume_feed($xml,$importer,&$contact, &$hub, $datedir = 0, $pass = 0) } - $datarray['parent_uri'] = $parent_uri; + $datarray['parent_mid'] = $parent_mid; $datarray['uid'] = $importer['uid']; $datarray['contact-id'] = $contact['id']; if((activity_match($datarray['verb'],ACTIVITY_LIKE)) || (activity_match($datarray['verb'],ACTIVITY_DISLIKE))) { $datarray['type'] = 'activity'; $datarray['gravity'] = GRAVITY_LIKE; // only one like or dislike per person - $r = q("select id from item where uid = %d and `contact-id` = %d and verb ='%s' and deleted = 0 and (`parent_uri` = '%s' OR `thr_parent` = '%s') limit 1", + $r = q("select id from item where uid = %d and `contact-id` = %d and verb ='%s' and deleted = 0 and (`parent_mid` = '%s' OR `thr_parent` = '%s') limit 1", intval($datarray['uid']), intval($datarray['contact-id']), dbesc($datarray['verb']), - dbesc($parent_uri), - dbesc($parent_uri) + dbesc($parent_mid), + dbesc($parent_mid) ); if($r && count($r)) continue; @@ -2512,7 +2672,7 @@ function consume_feed($xml,$importer,&$contact, &$hub, $datedir = 0, $pass = 0) $xt = parse_xml_string($datarray['target'],false); if($xt->type == ACTIVITY_OBJ_NOTE) { - $r = q("select * from item where `uri` = '%s' AND `uid` = %d limit 1", + $r = q("select * from item where `mid` = '%s' AND `uid` = %d limit 1", dbesc($xt->id), intval($importer['importer_uid']) ); @@ -2521,7 +2681,7 @@ function consume_feed($xml,$importer,&$contact, &$hub, $datedir = 0, $pass = 0) // extract tag, if not duplicate, add to parent item if($xo->id && $xo->content) { - $newtag = '#[url=' . $xo->id . ']'. $xo->content . '[/url]'; + $newtag = '#[zrl=' . $xo->id . ']'. $xo->content . '[/zrl]'; if(! (stristr($r[0]['tag'],$newtag))) { q("UPDATE item SET tag = '%s' WHERE id = %d LIMIT 1", dbesc($r[0]['tag'] . (strlen($r[0]['tag']) ? ',' : '') . $newtag), @@ -2564,13 +2724,13 @@ function consume_feed($xml,$importer,&$contact, &$hub, $datedir = 0, $pass = 0) $ev = bbtoevent($datarray['body']); if(x($ev,'desc') && x($ev,'start')) { $ev['uid'] = $importer['uid']; - $ev['uri'] = $item_id; + $ev['mid'] = $item_id; $ev['edited'] = $datarray['edited']; $ev['private'] = $datarray['private']; if(is_array($contact)) $ev['cid'] = $contact['id']; - $r = q("SELECT * FROM `event` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1", + $r = q("SELECT * FROM `event` WHERE `mid` = '%s' AND `uid` = %d LIMIT 1", dbesc($item_id), intval($importer['uid']) ); @@ -2581,7 +2741,7 @@ function consume_feed($xml,$importer,&$contact, &$hub, $datedir = 0, $pass = 0) } } - $r = q("SELECT `uid`, `edited`, `body` FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1", + $r = q("SELECT `uid`, `edited`, `body` FROM `item` WHERE `mid` = '%s' AND `uid` = %d LIMIT 1", dbesc($item_id), intval($importer['uid']) ); @@ -2595,7 +2755,7 @@ function consume_feed($xml,$importer,&$contact, &$hub, $datedir = 0, $pass = 0) if(datetime_convert('UTC','UTC',$datarray['edited']) < $r[0]['edited']) continue; - $r = q("UPDATE `item` SET `title` = '%s', `body` = '%s', `edited` = '%s' WHERE `uri` = '%s' AND `uid` = %d LIMIT 1", + $r = q("UPDATE `item` SET `title` = '%s', `body` = '%s', `edited` = '%s' WHERE `mid` = '%s' AND `uid` = %d LIMIT 1", dbesc($datarray['title']), dbesc($datarray['body']), dbesc(datetime_convert('UTC','UTC',$datarray['edited'])), @@ -2639,7 +2799,7 @@ function consume_feed($xml,$importer,&$contact, &$hub, $datedir = 0, $pass = 0) $datarray['wall'] = 1; } - $datarray['parent_uri'] = $item_id; + $datarray['parent_mid'] = $item_id; $datarray['uid'] = $importer['uid']; $datarray['contact-id'] = $contact['id']; @@ -2670,1117 +2830,9 @@ function consume_feed($xml,$importer,&$contact, &$hub, $datedir = 0, $pass = 0) } } -function local_delivery($importer,$data) { - - $a = get_app(); - - if($importer['readonly']) { - // We aren't receiving stuff from this person. But we will quietly ignore them - // rather than a blatant "go away" message. - logger('local_delivery: ignoring'); - return 0; - //NOTREACHED - } - - // Consume notification feed. This may differ from consuming a public feed in several ways - // - might contain email or friend suggestions - // - might contain remote followup to our message - // - in which case we need to accept it and then notify other conversants - // - we may need to send various email notifications - - $feed = new SimplePie(); - $feed->set_raw_data($data); - $feed->enable_order_by_date(false); - $feed->init(); - - - if($feed->error()) - logger('local_delivery: Error parsing XML: ' . $feed->error()); - - - // Check at the feed level for updated contact name and/or photo - - $name_updated = ''; - $new_name = ''; - $photo_timestamp = ''; - $photo_url = ''; - - - $rawtags = $feed->get_feed_tags( NAMESPACE_DFRN, 'owner'); - if(! $rawtags) - $rawtags = $feed->get_feed_tags( SIMPLEPIE_NAMESPACE_ATOM_10, 'author'); - if($rawtags) { - $elems = $rawtags[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]; - if($elems['name'][0]['attribs'][NAMESPACE_DFRN]['updated']) { - $name_updated = $elems['name'][0]['attribs'][NAMESPACE_DFRN]['updated']; - $new_name = $elems['name'][0]['data']; - } - if((x($elems,'link')) && ($elems['link'][0]['attribs']['']['rel'] === 'photo') && ($elems['link'][0]['attribs'][NAMESPACE_DFRN]['updated'])) { - $photo_timestamp = datetime_convert('UTC','UTC',$elems['link'][0]['attribs'][NAMESPACE_DFRN]['updated']); - $photo_url = $elems['link'][0]['attribs']['']['href']; - } - } - - if(($photo_timestamp) && (strlen($photo_url)) && ($photo_timestamp > $importer['avatar_date'])) { - logger('local_delivery: Updating photo for ' . $importer['name']); - require_once("Photo.php"); - $photo_failure = false; - $have_photo = false; - - $r = q("SELECT `resource_id` FROM `photo` WHERE `contact-id` = %d AND `uid` = %d LIMIT 1", - intval($importer['id']), - intval($importer['importer_uid']) - ); - if(count($r)) { - $resource_id = $r[0]['resource_id']; - $have_photo = true; - } - else { - $resource_id = photo_new_resource(); - } - - $img_str = fetch_url($photo_url,true); - // guess mimetype from headers or filename - $type = guess_image_type($photo_url,true); - - - $img = new Photo($img_str, $type); - if($img->is_valid()) { - if($have_photo) { - q("DELETE FROM `photo` WHERE `resource_id` = '%s' AND `contact-id` = %d AND `uid` = %d", - dbesc($resource_id), - intval($importer['id']), - intval($importer['importer_uid']) - ); - } - - $img->scaleImageSquare(175); - - $hash = $resource_id; - $r = $img->store($importer['importer_uid'], $importer['id'], $hash, basename($photo_url), 'Contact Photos', 4); - - $img->scaleImage(80); - $r = $img->store($importer['importer_uid'], $importer['id'], $hash, basename($photo_url), 'Contact Photos', 5); - - $img->scaleImage(48); - $r = $img->store($importer['importer_uid'], $importer['id'], $hash, basename($photo_url), 'Contact Photos', 6); - - $a = get_app(); - - q("UPDATE `contact` SET `avatar_date` = '%s', `photo` = '%s', `thumb` = '%s', `micro` = '%s' - WHERE `uid` = %d AND `id` = %d LIMIT 1", - dbesc(datetime_convert()), - dbesc($a->get_baseurl() . '/photo/' . $hash . '-4.'.$img->getExt()), - dbesc($a->get_baseurl() . '/photo/' . $hash . '-5.'.$img->getExt()), - dbesc($a->get_baseurl() . '/photo/' . $hash . '-6.'.$img->getExt()), - intval($importer['importer_uid']), - intval($importer['id']) - ); - } - } - - if(($name_updated) && (strlen($new_name)) && ($name_updated > $importer['name_date'])) { - $r = q("select * from contact where uid = %d and id = %d limit 1", - intval($importer['importer_uid']), - intval($importer['id']) - ); - - $x = q("UPDATE `contact` SET `name` = '%s', `name_date` = '%s' WHERE `uid` = %d AND `id` = %d LIMIT 1", - dbesc(notags(trim($new_name))), - dbesc(datetime_convert()), - intval($importer['importer_uid']), - intval($importer['id']) - ); - - // do our best to update the name on content items - - if(count($r)) { - q("update item set `author-name` = '%s' where `author-name` = '%s' and `author-link` = '%s' and uid = %d", - dbesc(notags(trim($new_name))), - dbesc($r[0]['name']), - dbesc($r[0]['url']), - intval($importer['importer_uid']) - ); - } - } - - -/* - // Currently unsupported - needs a lot of work - $reloc = $feed->get_feed_tags( NAMESPACE_DFRN, 'relocate' ); - if(isset($reloc[0]['child'][NAMESPACE_DFRN])) { - $base = $reloc[0]['child'][NAMESPACE_DFRN]; - $newloc = array(); - $newloc['uid'] = $importer['importer_uid']; - $newloc['cid'] = $importer['id']; - $newloc['name'] = notags(unxmlify($base['name'][0]['data'])); - $newloc['photo'] = notags(unxmlify($base['photo'][0]['data'])); - $newloc['url'] = notags(unxmlify($base['url'][0]['data'])); - $newloc['request'] = notags(unxmlify($base['request'][0]['data'])); - $newloc['confirm'] = notags(unxmlify($base['confirm'][0]['data'])); - $newloc['notify'] = notags(unxmlify($base['notify'][0]['data'])); - $newloc['poll'] = notags(unxmlify($base['poll'][0]['data'])); - $newloc['site_pubkey'] = notags(unxmlify($base['site_pubkey'][0]['data'])); - $newloc['pubkey'] = notags(unxmlify($base['pubkey'][0]['data'])); - $newloc['prvkey'] = notags(unxmlify($base['prvkey'][0]['data'])); - - // TODO - // merge with current record, current contents have priority - // update record, set url-updated - // update profile photos - // schedule a scan? - - } -*/ - - // handle friend suggestion notification - - $sugg = $feed->get_feed_tags( NAMESPACE_DFRN, 'suggest' ); - if(isset($sugg[0]['child'][NAMESPACE_DFRN])) { - $base = $sugg[0]['child'][NAMESPACE_DFRN]; - $fsugg = array(); - $fsugg['uid'] = $importer['importer_uid']; - $fsugg['cid'] = $importer['id']; - $fsugg['name'] = notags(unxmlify($base['name'][0]['data'])); - $fsugg['photo'] = notags(unxmlify($base['photo'][0]['data'])); - $fsugg['url'] = notags(unxmlify($base['url'][0]['data'])); - $fsugg['request'] = notags(unxmlify($base['request'][0]['data'])); - $fsugg['body'] = escape_tags(unxmlify($base['note'][0]['data'])); - - // Does our member already have a friend matching this description? - - $r = q("SELECT * FROM `contact` WHERE `name` = '%s' AND `nurl` = '%s' AND `uid` = %d LIMIT 1", - dbesc($fsugg['name']), - dbesc(normalise_link($fsugg['url'])), - intval($fsugg['uid']) - ); - if(count($r)) - return 0; - - // Do we already have an fcontact record for this person? - - $fid = 0; - $r = q("SELECT * FROM `fcontact` WHERE `url` = '%s' AND `name` = '%s' AND `request` = '%s' LIMIT 1", - dbesc($fsugg['url']), - dbesc($fsugg['name']), - dbesc($fsugg['request']) - ); - if(count($r)) { - $fid = $r[0]['id']; - - // OK, we do. Do we already have an introduction for this person ? - $r = q("select id from intro where uid = %d and fid = %d limit 1", - intval($fsugg['uid']), - intval($fid) - ); - if(count($r)) - return 0; - } - if(! $fid) - $r = q("INSERT INTO `fcontact` ( `name`,`url`,`photo`,`request` ) VALUES ( '%s', '%s', '%s', '%s' ) ", - dbesc($fsugg['name']), - dbesc($fsugg['url']), - dbesc($fsugg['photo']), - dbesc($fsugg['request']) - ); - $r = q("SELECT * FROM `fcontact` WHERE `url` = '%s' AND `name` = '%s' AND `request` = '%s' LIMIT 1", - dbesc($fsugg['url']), - dbesc($fsugg['name']), - dbesc($fsugg['request']) - ); - if(count($r)) { - $fid = $r[0]['id']; - } - // database record did not get created. Quietly give up. - else - return 0; - - - $hash = random_string(); - - $r = q("INSERT INTO `intro` ( `uid`, `fid`, `contact-id`, `note`, `hash`, `datetime`, `blocked` ) - VALUES( %d, %d, %d, '%s', '%s', '%s', %d )", - intval($fsugg['uid']), - intval($fid), - intval($fsugg['cid']), - dbesc($fsugg['body']), - dbesc($hash), - dbesc(datetime_convert()), - intval(0) - ); - - notification(array( - 'type' => NOTIFY_SUGGEST, - 'notify_flags' => $importer['notify-flags'], - 'language' => $importer['language'], - 'to_name' => $importer['username'], - 'to_email' => $importer['email'], - 'uid' => $importer['importer_uid'], - 'item' => $fsugg, - 'link' => $a->get_baseurl() . '/notifications/intros', - 'source_name' => $importer['name'], - 'source_link' => $importer['url'], - 'source_photo' => $importer['photo'], - 'verb' => ACTIVITY_REQ_FRIEND, - 'otype' => 'intro' - )); - - return 0; - } - - $ismail = false; - - $rawmail = $feed->get_feed_tags( NAMESPACE_DFRN, 'mail' ); - if(isset($rawmail[0]['child'][NAMESPACE_DFRN])) { - - logger('local_delivery: private message received'); - - $ismail = true; - $base = $rawmail[0]['child'][NAMESPACE_DFRN]; - - $msg = array(); - $msg['uid'] = $importer['importer_uid']; - $msg['from-name'] = notags(unxmlify($base['sender'][0]['child'][NAMESPACE_DFRN]['name'][0]['data'])); - $msg['from-photo'] = notags(unxmlify($base['sender'][0]['child'][NAMESPACE_DFRN]['avatar'][0]['data'])); - $msg['from-url'] = notags(unxmlify($base['sender'][0]['child'][NAMESPACE_DFRN]['uri'][0]['data'])); - $msg['contact-id'] = $importer['id']; - $msg['title'] = notags(unxmlify($base['subject'][0]['data'])); - $msg['body'] = escape_tags(unxmlify($base['content'][0]['data'])); - $msg['seen'] = 0; - $msg['replied'] = 0; - $msg['uri'] = notags(unxmlify($base['id'][0]['data'])); - $msg['parent_uri'] = notags(unxmlify($base['in-reply-to'][0]['data'])); - $msg['created'] = datetime_convert(notags(unxmlify('UTC','UTC',$base['sentdate'][0]['data']))); - - dbesc_array($msg); - - $r = dbq("INSERT INTO `mail` (`" . implode("`, `", array_keys($msg)) - . "`) VALUES ('" . implode("', '", array_values($msg)) . "')" ); - - // send notifications. - - require_once('include/enotify.php'); - - $notif_params = array( - 'type' => NOTIFY_MAIL, - 'notify_flags' => $importer['notify-flags'], - 'language' => $importer['language'], - 'to_name' => $importer['username'], - 'to_email' => $importer['email'], - 'uid' => $importer['importer_uid'], - 'item' => $msg, - 'source_name' => $msg['from-name'], - 'source_link' => $importer['url'], - 'source_photo' => $importer['thumb'], - 'verb' => ACTIVITY_POST, - 'otype' => 'mail' - ); - - notification($notif_params); - return 0; - - // NOTREACHED - } - - $community_page = 0; - $rawtags = $feed->get_feed_tags( NAMESPACE_DFRN, 'community'); - if($rawtags) { - $community_page = intval($rawtags[0]['data']); - } - if(intval($importer['forum']) != $community_page) { - q("update contact set forum = %d where id = %d limit 1", - intval($community_page), - intval($importer['id']) - ); - $importer['forum'] = (string) $community_page; - } - - logger('local_delivery: feed item count = ' . $feed->get_item_quantity()); - - // process any deleted entries - - $del_entries = $feed->get_feed_tags(NAMESPACE_TOMB, 'deleted-entry'); - if(is_array($del_entries) && count($del_entries)) { - foreach($del_entries as $dentry) { - $deleted = false; - if(isset($dentry['attribs']['']['ref'])) { - $uri = $dentry['attribs']['']['ref']; - $deleted = true; - if(isset($dentry['attribs']['']['when'])) { - $when = $dentry['attribs']['']['when']; - $when = datetime_convert('UTC','UTC', $when, 'Y-m-d H:i:s'); - } - else - $when = datetime_convert('UTC','UTC','now','Y-m-d H:i:s'); - } - if($deleted) { - - // check for relayed deletes to our conversation - - $is_reply = false; - $r = q("select * from item where uri = '%s' and uid = %d limit 1", - dbesc($uri), - intval($importer['importer_uid']) - ); - if(count($r)) { - $parent_uri = $r[0]['parent_uri']; - if($r[0]['id'] != $r[0]['parent']) - $is_reply = true; - } - - if($is_reply) { - $community = false; - - if($importer['page-flags'] == PAGE_COMMUNITY || $importer['page-flags'] == PAGE_PRVGROUP ) { - $sql_extra = ''; - $community = true; - logger('local_delivery: possible community delete'); - } - else - $sql_extra = " and contact.self = 1 and item.wall = 1 "; - - // was the top-level post for this reply written by somebody on this site? - // Specifically, the recipient? - - $is_a_remote_delete = false; - - $r = q("select `item`.`id`, `item`.`uri`, `item`.`forum_mode`,`item`.`origin`,`item`.`wall`, - `contact`.`name`, `contact`.`url`, `contact`.`thumb` from `item` - LEFT JOIN `contact` ON `contact`.`id` = `item`.`contact-id` - WHERE `item`.`uri` = '%s' AND (`item`.`parent_uri` = '%s' or `item`.`thr_parent` = '%s') - AND `item`.`uid` = %d - $sql_extra - LIMIT 1", - dbesc($parent_uri), - dbesc($parent_uri), - dbesc($parent_uri), - intval($importer['importer_uid']) - ); - if($r && count($r)) - $is_a_remote_delete = true; - - // Does this have the characteristics of a community or private group comment? - // If it's a reply to a wall post on a community/prvgroup page it's a - // valid community comment. Also forum_mode makes it valid for sure. - // If neither, it's not. - - if($is_a_remote_delete && $community) { - if((! $r[0]['forum_mode']) && (! $r[0]['wall'])) { - $is_a_remote_delete = false; - logger('local_delivery: not a community delete'); - } - } - - if($is_a_remote_delete) { - logger('local_delivery: received remote delete'); - } - } - - $r = q("SELECT `item`.*, `contact`.`self` FROM `item` left join contact on `item`.`contact-id` = `contact`.`id` - WHERE `uri` = '%s' AND `item`.`uid` = %d AND `contact-id` = %d AND NOT `item`.`file` LIKE '%%[%%' LIMIT 1", - dbesc($uri), - intval($importer['importer_uid']), - intval($importer['id']) - ); - - if(count($r)) { - $item = $r[0]; - - if($item['deleted']) - continue; - - logger('local_delivery: deleting item ' . $item['id'] . ' uri=' . $item['uri'], LOGGER_DEBUG); - - if(($item['verb'] === ACTIVITY_TAG) && ($item['obj_type'] === ACTIVITY_OBJ_TAGTERM)) { - $xo = parse_xml_string($item['object'],false); - $xt = parse_xml_string($item['target'],false); - - if($xt->type === ACTIVITY_OBJ_NOTE) { - $i = q("select * from `item` where uri = '%s' and uid = %d limit 1", - dbesc($xt->id), - intval($importer['importer_uid']) - ); - if(count($i)) { - - // For tags, the owner cannot remove the tag on the author's copy of the post. - - $owner_remove = (($item['contact-id'] == $i[0]['contact-id']) ? true: false); - $author_remove = (($item['origin'] && $item['self']) ? true : false); - $author_copy = (($item['origin']) ? true : false); - - if($owner_remove && $author_copy) - continue; - if($author_remove || $owner_remove) { -//FIXME - $tags = explode(',',$i[0]['tag']); - $newtags = array(); - if(count($tags)) { - foreach($tags as $tag) - if(trim($tag) !== trim($xo->body)) - $newtags[] = trim($tag); - } - q("update item set tag = '%s' where id = %d limit 1", - dbesc(implode(',',$newtags)), - intval($i[0]['id']) - ); - } - } - } - } - - if($item['uri'] == $item['parent_uri']) { - $r = q("UPDATE `item` SET `deleted` = 1, `edited` = '%s', `changed` = '%s', - `body` = '', `title` = '' - WHERE `parent_uri` = '%s' AND `uid` = %d", - dbesc($when), - dbesc(datetime_convert()), - dbesc($item['uri']), - intval($importer['importer_uid']) - ); - } - else { - $r = q("UPDATE `item` SET `deleted` = 1, `edited` = '%s', `changed` = '%s', - `body` = '', `title` = '' - WHERE `uri` = '%s' AND `uid` = %d LIMIT 1", - dbesc($when), - dbesc(datetime_convert()), - dbesc($uri), - intval($importer['importer_uid']) - ); - - // if this is a relayed delete, propagate it to other recipients - - if($is_a_remote_delete) - proc_run('php',"include/notifier.php","drop",$item['id']); - } - } - } - } - } - - - foreach($feed->get_items() as $item) { - - $is_reply = false; - $item_id = $item->get_id(); - $rawthread = $item->get_item_tags( NAMESPACE_THREAD, 'in-reply-to'); - if(isset($rawthread[0]['attribs']['']['ref'])) { - $is_reply = true; - $parent_uri = $rawthread[0]['attribs']['']['ref']; - } - - if($is_reply) { - $community = false; - - if($importer['page-flags'] == PAGE_COMMUNITY || $importer['page-flags'] == PAGE_PRVGROUP ) { - $sql_extra = ''; - $community = true; - logger('local_delivery: possible community reply'); - } - else - $sql_extra = " and contact.self = 1 and item.wall = 1 "; - - // was the top-level post for this reply written by somebody on this site? - // Specifically, the recipient? - - $is_a_remote_comment = false; - - // POSSIBLE CLEANUP --> Why select so many fields when only forum_mode and wall are used? - $r = q("select `item`.`id`, `item`.`uri`, `item`.`forum_mode`,`item`.`origin`,`item`.`wall`, - `contact`.`name`, `contact`.`url`, `contact`.`thumb` from `item` - LEFT JOIN `contact` ON `contact`.`id` = `item`.`contact-id` - WHERE `item`.`uri` = '%s' AND (`item`.`parent_uri` = '%s' or `item`.`thr_parent` = '%s') - AND `item`.`uid` = %d - $sql_extra - LIMIT 1", - dbesc($parent_uri), - dbesc($parent_uri), - dbesc($parent_uri), - intval($importer['importer_uid']) - ); - if($r && count($r)) - $is_a_remote_comment = true; - - // Does this have the characteristics of a community or private group comment? - // If it's a reply to a wall post on a community/prvgroup page it's a - // valid community comment. Also forum_mode makes it valid for sure. - // If neither, it's not. - - if($is_a_remote_comment && $community) { - if((! $r[0]['forum_mode']) && (! $r[0]['wall'])) { - $is_a_remote_comment = false; - logger('local_delivery: not a community reply'); - } - } - - if($is_a_remote_comment) { - logger('local_delivery: received remote comment'); - $is_like = false; - // remote reply to our post. Import and then notify everybody else. - - $datarray = get_atom_elements($feed,$item); - - $r = q("SELECT `id`, `uid`, `edited`, `body` FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1", - dbesc($item_id), - intval($importer['importer_uid']) - ); - - // Update content if 'updated' changes - - if(count($r)) { - $iid = $r[0]['id']; - if((x($datarray,'edited') !== false) && (datetime_convert('UTC','UTC',$datarray['edited']) !== $r[0]['edited'])) { - - // do not accept (ignore) an earlier edit than one we currently have. - if(datetime_convert('UTC','UTC',$datarray['edited']) < $r[0]['edited']) - continue; - - logger('received updated comment' , LOGGER_DEBUG); - $r = q("UPDATE `item` SET `title` = '%s', `body` = '%s', `edited` = '%s' WHERE `uri` = '%s' AND `uid` = %d LIMIT 1", - dbesc($datarray['title']), - dbesc($datarray['body']), - dbesc(datetime_convert('UTC','UTC',$datarray['edited'])), - dbesc($item_id), - intval($importer['importer_uid']) - ); - - proc_run('php',"include/notifier.php","comment-import",$iid); - - } - - continue; - } - - - - $own = q("select name,url,thumb from contact where uid = %d and self = 1 limit 1", - intval($importer['importer_uid']) - ); - - - $datarray['type'] = 'remote-comment'; - $datarray['wall'] = 1; - $datarray['parent_uri'] = $parent_uri; - $datarray['uid'] = $importer['importer_uid']; - $datarray['owner-name'] = $own[0]['name']; - $datarray['owner-link'] = $own[0]['url']; - $datarray['owner-avatar'] = $own[0]['thumb']; - $datarray['contact-id'] = $importer['id']; - - if(($datarray['verb'] === ACTIVITY_LIKE) || ($datarray['verb'] === ACTIVITY_DISLIKE)) { - $is_like = true; - $datarray['type'] = 'activity'; - $datarray['gravity'] = GRAVITY_LIKE; - - // only one like or dislike per person - $r = q("select id from item where uid = %d and `contact-id` = %d and verb = '%s' and (`thr_parent` = '%s' or `parent_uri` = '%s') and deleted = 0 limit 1", - intval($datarray['uid']), - intval($datarray['contact-id']), - dbesc($datarray['verb']), - dbesc($datarray['parent_uri']), - dbesc($datarray['parent_uri']) - - ); - if($r && count($r)) - continue; - } - - if(($datarray['verb'] === ACTIVITY_TAG) && ($datarray['obj_type'] === ACTIVITY_OBJ_TAGTERM)) { - - $xo = parse_xml_string($datarray['object'],false); - $xt = parse_xml_string($datarray['target'],false); - - if(($xt->type == ACTIVITY_OBJ_NOTE) && ($xt->id)) { - - // fetch the parent item - - $tagp = q("select * from item where uri = '%s' and uid = %d limit 1", - dbesc($xt->id), - intval($importer['importer_uid']) - ); - if(! count($tagp)) - continue; - - // extract tag, if not duplicate, and this user allows tags, add to parent item -//FIXME - if($xo->id && $xo->content) { - $newtag = '#[url=' . $xo->id . ']'. $xo->content . '[/url]'; - if(! (stristr($tagp[0]['tag'],$newtag))) { - $i = q("SELECT `blocktags` FROM `user` where `uid` = %d LIMIT 1", - intval($importer['importer_uid']) - ); - if(count($i) && ! intval($i[0]['blocktags'])) { - q("UPDATE item SET tag = '%s', `edited` = '%s' WHERE id = %d LIMIT 1", - dbesc($tagp[0]['tag'] . (strlen($tagp[0]['tag']) ? ',' : '') . $newtag), - intval($tagp[0]['id']), - dbesc(datetime_convert()) - ); - } - } - } - } - } - - - $posted_id = item_store($datarray); - $parent = 0; - - if($posted_id) { - $r = q("SELECT `parent`, `parent_uri` FROM `item` WHERE `id` = %d AND `uid` = %d LIMIT 1", - intval($posted_id), - intval($importer['importer_uid']) - ); - if(count($r)) { - $parent = $r[0]['parent']; - $parent_uri = $r[0]['parent_uri']; - } - - if(! $is_like) { - $r1 = q("UPDATE `item` SET `changed` = '%s' WHERE `uid` = %d AND `parent` = %d", - dbesc(datetime_convert()), - intval($importer['importer_uid']), - intval($r[0]['parent']) - ); - - $r2 = q("UPDATE `item` SET `changed` = '%s' WHERE `uid` = %d AND `id` = %d LIMIT 1", - dbesc(datetime_convert()), - intval($importer['importer_uid']), - intval($posted_id) - ); - } - - if($posted_id && $parent) { - - proc_run('php',"include/notifier.php","comment-import","$posted_id"); - - if((! $is_like) && (! $importer['self'])) { - - require_once('include/enotify.php'); - - notification(array( - 'type' => NOTIFY_COMMENT, - 'notify_flags' => $importer['notify-flags'], - 'language' => $importer['language'], - 'to_name' => $importer['username'], - 'to_email' => $importer['email'], - 'uid' => $importer['importer_uid'], - 'item' => $datarray, - 'link' => $a->get_baseurl() . '/display/' . $importer['nickname'] . '/' . $posted_id, - 'source_name' => stripslashes($datarray['author-name']), - 'source_link' => $datarray['author-link'], - 'source_photo' => ((link_compare($datarray['author-link'],$importer['url'])) - ? $importer['thumb'] : $datarray['author-avatar']), - 'verb' => ACTIVITY_POST, - 'otype' => 'item', - 'parent' => $parent, - 'parent_uri' => $parent_uri, - )); - - } - } - - return 0; - // NOTREACHED - } - } - else { - - // regular comment that is part of this total conversation. Have we seen it? If not, import it. - - $item_id = $item->get_id(); - $datarray = get_atom_elements($feed,$item); - - if($importer['rel'] == CONTACT_IS_FOLLOWER) - continue; - - - $r = q("SELECT `uid`, `edited`, `body` FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1", - dbesc($item_id), - intval($importer['importer_uid']) - ); - - // Update content if 'updated' changes - - if(count($r)) { - if((x($datarray,'edited') !== false) && (datetime_convert('UTC','UTC',$datarray['edited']) !== $r[0]['edited'])) { - - // do not accept (ignore) an earlier edit than one we currently have. - if(datetime_convert('UTC','UTC',$datarray['edited']) < $r[0]['edited']) - continue; - - $r = q("UPDATE `item` SET `title` = '%s', `body` = '%s', `edited` = '%s' WHERE `uri` = '%s' AND `uid` = %d LIMIT 1", - dbesc($datarray['title']), - dbesc($datarray['body']), - dbesc(datetime_convert('UTC','UTC',$datarray['edited'])), - dbesc($item_id), - intval($importer['importer_uid']) - ); - } - - continue; - } - - $datarray['parent_uri'] = $parent_uri; - $datarray['uid'] = $importer['importer_uid']; - $datarray['contact-id'] = $importer['id']; - if(($datarray['verb'] == ACTIVITY_LIKE) || ($datarray['verb'] == ACTIVITY_DISLIKE)) { - $datarray['type'] = 'activity'; - $datarray['gravity'] = GRAVITY_LIKE; - // only one like or dislike per person - $r = q("select id from item where uid = %d and `contact-id` = %d and verb ='%s' and deleted = 0 and (`parent_uri` = '%s' OR `thr_parent` = '%s') limit 1", - intval($datarray['uid']), - intval($datarray['contact-id']), - dbesc($datarray['verb']), - dbesc($parent_uri), - dbesc($parent_uri) - ); - if($r && count($r)) - continue; - - } - - if(($datarray['verb'] === ACTIVITY_TAG) && ($datarray['obj_type'] === ACTIVITY_OBJ_TAGTERM)) { - - $xo = parse_xml_string($datarray['object'],false); - $xt = parse_xml_string($datarray['target'],false); - - if($xt->type == ACTIVITY_OBJ_NOTE) { - $r = q("select * from item where `uri` = '%s' AND `uid` = %d limit 1", - dbesc($xt->id), - intval($importer['importer_uid']) - ); - if(! count($r)) - continue; - - // extract tag, if not duplicate, add to parent item - if($xo->content) { - if(! (stristr($r[0]['tag'],trim($xo->content)))) { - q("UPDATE item SET tag = '%s' WHERE id = %d LIMIT 1", - dbesc($r[0]['tag'] . (strlen($r[0]['tag']) ? ',' : '') . '#[url=' . $xo->id . ']'. $xo->content . '[/url]'), - intval($r[0]['id']) - ); - } - } - } - } - - $posted_id = item_store($datarray); - - // find out if our user is involved in this conversation and wants to be notified. - - if(!x($datarray['type']) || $datarray['type'] != 'activity') { - - $myconv = q("SELECT `author-link`, `author-avatar`, `parent` FROM `item` WHERE `parent_uri` = '%s' AND `uid` = %d AND `parent` != 0 AND `deleted` = 0", - dbesc($parent_uri), - intval($importer['importer_uid']) - ); - - if(count($myconv)) { - $importer_url = $a->get_baseurl() . '/channel/' . $importer['nickname']; - - // first make sure this isn't our own post coming back to us from a wall-to-wall event - if(! link_compare($datarray['author-link'],$importer_url)) { - - - foreach($myconv as $conv) { - - // now if we find a match, it means we're in this conversation - - if(! link_compare($conv['author-link'],$importer_url)) - continue; - - require_once('include/enotify.php'); - - $conv_parent = $conv['parent']; - - notification(array( - 'type' => NOTIFY_COMMENT, - 'notify_flags' => $importer['notify-flags'], - 'language' => $importer['language'], - 'to_name' => $importer['username'], - 'to_email' => $importer['email'], - 'uid' => $importer['importer_uid'], - 'item' => $datarray, - 'link' => $a->get_baseurl() . '/display/' . $importer['nickname'] . '/' . $posted_id, - 'source_name' => stripslashes($datarray['author-name']), - 'source_link' => $datarray['author-link'], - 'source_photo' => ((link_compare($datarray['author-link'],$importer['url'])) - ? $importer['thumb'] : $datarray['author-avatar']), - 'verb' => ACTIVITY_POST, - 'otype' => 'item', - 'parent' => $conv_parent, - 'parent_uri' => $parent_uri - - )); - - // only send one notification - break; - } - } - } - } - continue; - } - } - - else { - - // Head post of a conversation. Have we seen it? If not, import it. - - - $item_id = $item->get_id(); - $datarray = get_atom_elements($feed,$item); - - if((x($datarray,'obj_type')) && ($datarray['obj_type'] === ACTIVITY_OBJ_EVENT)) { - $ev = bbtoevent($datarray['body']); - if(x($ev,'desc') && x($ev,'start')) { - $ev['cid'] = $importer['id']; - $ev['uid'] = $importer['uid']; - $ev['uri'] = $item_id; - $ev['edited'] = $datarray['edited']; - $ev['private'] = $datarray['private']; - - $r = q("SELECT * FROM `event` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1", - dbesc($item_id), - intval($importer['uid']) - ); - if(count($r)) - $ev['id'] = $r[0]['id']; - $xyz = event_store($ev); - continue; - } - } - - $r = q("SELECT `uid`, `edited`, `body` FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1", - dbesc($item_id), - intval($importer['importer_uid']) - ); - - // Update content if 'updated' changes - - if(count($r)) { - if((x($datarray,'edited') !== false) && (datetime_convert('UTC','UTC',$datarray['edited']) !== $r[0]['edited'])) { - - // do not accept (ignore) an earlier edit than one we currently have. - if(datetime_convert('UTC','UTC',$datarray['edited']) < $r[0]['edited']) - continue; - - $r = q("UPDATE `item` SET `title` = '%s', `body` = '%s', `edited` = '%s' WHERE `uri` = '%s' AND `uid` = %d LIMIT 1", - dbesc($datarray['title']), - dbesc($datarray['body']), - dbesc(datetime_convert('UTC','UTC',$datarray['edited'])), - dbesc($item_id), - intval($importer['importer_uid']) - ); - } - - continue; - } - - // This is my contact on another system, but it's really me. - // Turn this into a wall post. - - if($importer['remote_self']) - $datarray['wall'] = 1; - - $datarray['parent_uri'] = $item_id; - $datarray['uid'] = $importer['importer_uid']; - $datarray['contact-id'] = $importer['id']; - - - if(! link_compare($datarray['owner-link'],$importer['url'])) { - // The item owner info is not our contact. It's OK and is to be expected if this is a tgroup delivery, - // but otherwise there's a possible data mixup on the sender's system. - // the tgroup delivery code called from item_store will correct it if it's a forum, - // but we're going to unconditionally correct it here so that the post will always be owned by our contact. - logger('local_delivery: Correcting item owner.', LOGGER_DEBUG); - $datarray['owner-name'] = $importer['senderName']; - $datarray['owner-link'] = $importer['url']; - $datarray['owner-avatar'] = $importer['thumb']; - } - - if(($importer['rel'] == CONTACT_IS_FOLLOWER) && (! tgroup_check($importer['importer_uid'],$datarray))) - continue; - - $posted_id = item_store($datarray); - - if(stristr($datarray['verb'],ACTIVITY_POKE)) { - $verb = urldecode(substr($datarray['verb'],strpos($datarray['verb'],'#')+1)); - if(! $verb) - continue; - $xo = parse_xml_string($datarray['object'],false); - - if(($xo->type == ACTIVITY_OBJ_PERSON) && ($xo->id)) { - - // somebody was poked/prodded. Was it me? - - $links = parse_xml_string("<links>".unxmlify($xo->link)."</links>",false); - - foreach($links->link as $l) { - $atts = $l->attributes(); - switch($atts['rel']) { - case "alternate": - $Blink = $atts['href']; - break; - default: - break; - } - } - if($Blink && link_compare($Blink,$a->get_baseurl() . '/channel/' . $importer['nickname'])) { - - // send a notification - require_once('include/enotify.php'); - - notification(array( - 'type' => NOTIFY_POKE, - 'notify_flags' => $importer['notify-flags'], - 'language' => $importer['language'], - 'to_name' => $importer['username'], - 'to_email' => $importer['email'], - 'uid' => $importer['importer_uid'], - 'item' => $datarray, - 'link' => $a->get_baseurl() . '/display/' . $importer['nickname'] . '/' . $posted_id, - 'source_name' => stripslashes($datarray['author-name']), - 'source_link' => $datarray['author-link'], - 'source_photo' => ((link_compare($datarray['author-link'],$importer['url'])) - ? $importer['thumb'] : $datarray['author-avatar']), - 'verb' => $datarray['verb'], - 'otype' => 'person', - 'activity' => $verb, - - )); - } - } - } - - continue; - } - } - - return 0; - // NOTREACHED - -} - - -function new_follower($importer,$contact,$datarray,$item,$sharing = false) { - $url = notags(trim($datarray['author-link'])); - $name = notags(trim($datarray['author-name'])); - $photo = notags(trim($datarray['author-avatar'])); - $rawtag = $item->get_item_tags(NAMESPACE_ACTIVITY,'actor'); - if($rawtag && $rawtag[0]['child'][NAMESPACE_POCO]['preferredUsername'][0]['data']) - $nick = $rawtag[0]['child'][NAMESPACE_POCO]['preferredUsername'][0]['data']; - if(is_array($contact)) { - if(($contact['network'] == NETWORK_OSTATUS && $contact['rel'] == CONTACT_IS_SHARING) - || ($sharing && $contact['rel'] == CONTACT_IS_FOLLOWER)) { - $r = q("UPDATE `contact` SET `rel` = %d WHERE `id` = %d AND `uid` = %d LIMIT 1", - intval(CONTACT_IS_FRIEND), - intval($contact['id']), - intval($importer['uid']) - ); - } - // send email notification to owner? - } - else { - - // create contact record - - $r = q("INSERT INTO `contact` ( `uid`, `created`, `url`, `nurl`, `name`, `nick`, `photo`, `network`, `rel`, - `blocked`, `readonly`, `pending`, `writable` ) - VALUES ( %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, 0, 0, 1, 1 ) ", - intval($importer['uid']), - dbesc(datetime_convert()), - dbesc($url), - dbesc(normalise_link($url)), - dbesc($name), - dbesc($nick), - dbesc($photo), - dbesc(($sharing) ? NETWORK_ZOT : NETWORK_OSTATUS), - intval(($sharing) ? CONTACT_IS_SHARING : CONTACT_IS_FOLLOWER) - ); - $r = q("SELECT `id` FROM `contact` WHERE `uid` = %d AND `url` = '%s' AND `pending` = 1 LIMIT 1", - intval($importer['uid']), - dbesc($url) - ); - if(count($r)) - $contact_record = $r[0]; - - // create notification - $hash = random_string(); - - if(is_array($contact_record)) { - $ret = q("INSERT INTO `intro` ( `uid`, `contact-id`, `blocked`, `knowyou`, `hash`, `datetime`) - VALUES ( %d, %d, 0, 0, '%s', '%s' )", - intval($importer['uid']), - intval($contact_record['id']), - dbesc($hash), - dbesc(datetime_convert()) - ); - } - $r = q("SELECT * FROM `user` WHERE `uid` = %d LIMIT 1", - intval($importer['uid']) - ); - $a = get_app(); - if(count($r)) { - - if(intval($r[0]['def_gid'])) { - require_once('include/group.php'); - group_add_member($r[0]['uid'],'',$contact_record['id'],$r[0]['def_gid']); - } - - if(($r[0]['notify-flags'] & NOTIFY_INTRO) && ($r[0]['page-flags'] == PAGE_NORMAL)) { - - $email_tpl = get_intltext_template('follow_notify_eml.tpl'); - $email = replace_macros($email_tpl, array( - '$requestor' => ((strlen($name)) ? $name : t('[Name Withheld]')), - '$url' => $url, - '$myname' => $r[0]['username'], - '$siteurl' => $a->get_baseurl(), - '$sitename' => $a->config['sitename'] - )); - - $res = mail($r[0]['email'], - (($sharing) ? t('A new person is sharing with you at ') : t("You have a new follower at ")) . $a->config['sitename'], - $email, - 'From: ' . t('Administrator') . '@' . $_SERVER['SERVER_NAME'] . "\n" - . 'Content-type: text/plain; charset=UTF-8' . "\n" - . 'Content-transfer-encoding: 8bit' ); - - } - } - } -} - -function lose_follower($importer,$contact,$datarray,$item) { - - if(($contact['rel'] == CONTACT_IS_FRIEND) || ($contact['rel'] == CONTACT_IS_SHARING)) { - q("UPDATE `contact` SET `rel` = %d WHERE `id` = %d LIMIT 1", - intval(CONTACT_IS_SHARING), - intval($contact['id']) - ); - } - else { -// contact_remove($contact['id']); - } -} - -function lose_sharer($importer,$contact,$datarray,$item) { - - if(($contact['rel'] == CONTACT_IS_FRIEND) || ($contact['rel'] == CONTACT_IS_FOLLOWER)) { - q("UPDATE `contact` SET `rel` = %d WHERE `id` = %d LIMIT 1", - intval(CONTACT_IS_FOLLOWER), - intval($contact['id']) - ); - } - else { -// contact_remove($contact['id']); - } -} - - -function atom_author($tag,$name,$uri,$h,$w,$photo) { +function atom_author($tag,$name,$uri,$h,$w,$type,$photo) { $o = ''; if(! $tag) return $o; @@ -3794,8 +2846,8 @@ function atom_author($tag,$name,$uri,$h,$w,$photo) { $o .= "<$tag>\r\n"; $o .= "<name>$name</name>\r\n"; $o .= "<uri>$uri</uri>\r\n"; - $o .= '<link rel="photo" type="image/jpeg" media:width="' . $w . '" media:height="' . $h . '" href="' . $photo . '" />' . "\r\n"; - $o .= '<link rel="avatar" type="image/jpeg" media:width="' . $w . '" media:height="' . $h . '" href="' . $photo . '" />' . "\r\n"; + $o .= '<link rel="photo" type="' . $type . '" media:width="' . $w . '" media:height="' . $h . '" href="' . $photo . '" />' . "\r\n"; + $o .= '<link rel="avatar" type="' . $type . '" media:width="' . $w . '" media:height="' . $h . '" href="' . $photo . '" />' . "\r\n"; call_hooks('atom_author', $o); @@ -3811,7 +2863,7 @@ function atom_entry($item,$type,$author,$owner,$comment = false,$cid = 0) { return; if($item['deleted']) - return '<at:deleted-entry ref="' . xmlify($item['uri']) . '" when="' . xmlify(datetime_convert('UTC','UTC',$item['edited'] . '+00:00',ATOM_TIME)) . '" />' . "\r\n"; + return '<at:deleted-entry ref="' . xmlify($item['mid']) . '" when="' . xmlify(datetime_convert('UTC','UTC',$item['edited'] . '+00:00',ATOM_TIME)) . '" />' . "\r\n"; if($item['allow_cid'] || $item['allow_gid'] || $item['deny_cid'] || $item['deny_gid']) @@ -3823,35 +2875,36 @@ function atom_entry($item,$type,$author,$owner,$comment = false,$cid = 0) { $o = "\r\n\r\n<entry>\r\n"; if(is_array($author)) - $o .= atom_author('author',$author['name'],$author['url'],80,80,$author['thumb']); + $o .= atom_author('author',$author['xchan_name'],$author['xchan_url'],80,80,$author['xchan_photo_mimetype'],$author['xchan_photo_m']); else - $o .= atom_author('author',(($item['author-name']) ? $item['author-name'] : $item['name']),(($item['author-link']) ? $item['author-link'] : $item['url']),80,80,(($item['author-avatar']) ? $item['author-avatar'] : $item['thumb'])); - if(strlen($item['owner-name'])) - $o .= atom_author('dfrn:owner',$item['owner-name'],$item['owner-link'],80,80,$item['owner-avatar']); + $o .= atom_author('author',$item['author']['xchan_name'],$item['author']['xchan_url'],80,80,$item['author']['xchan_photo_mimetype'], $item['author']['xchan_photo_m']); + + $o .= atom_author('zot:owner',$item['owner']['xchan_name'],$item['owner']['xchan_url'],80,80,$item['owner']['xchan_photo_mimetype'],$item['owner']['xchan_photo_m']); - if(($item['parent'] != $item['id']) || ($item['parent_uri'] !== $item['uri']) || (($item['thr_parent'] !== '') && ($item['thr_parent'] !== $item['uri']))) { - $parent_item = (($item['thr_parent']) ? $item['thr_parent'] : $item['parent_uri']); - $o .= '<thr:in-reply-to ref="' . xmlify($parent_item) . '" type="text/html" href="' . xmlify($a->get_baseurl() . '/display/' . $owner['nickname'] . '/' . $item['parent']) . '" />' . "\r\n"; + if(($item['parent'] != $item['id']) || ($item['parent_mid'] !== $item['mid']) || (($item['thr_parent'] !== '') && ($item['thr_parent'] !== $item['mid']))) { + $parent_item = (($item['thr_parent']) ? $item['thr_parent'] : $item['parent_mid']); + $o .= '<thr:in-reply-to ref="' . xmlify($parent_item) . '" type="text/html" href="' . xmlify($item['plink']) . '" />' . "\r\n"; } - $o .= '<id>' . xmlify($item['uri']) . '</id>' . "\r\n"; + $o .= '<id>' . xmlify($item['mid']) . '</id>' . "\r\n"; $o .= '<title>' . xmlify($item['title']) . '</title>' . "\r\n"; $o .= '<published>' . xmlify(datetime_convert('UTC','UTC',$item['created'] . '+00:00',ATOM_TIME)) . '</published>' . "\r\n"; $o .= '<updated>' . xmlify(datetime_convert('UTC','UTC',$item['edited'] . '+00:00',ATOM_TIME)) . '</updated>' . "\r\n"; - $o .= '<dfrn:env>' . base64url_encode($body, true) . '</dfrn:env>' . "\r\n"; + $o .= '<zot:env>' . base64url_encode($body, true) . '</zot:env>' . "\r\n"; + // FIXME for other content types $o .= '<content type="' . $type . '" >' . xmlify((($type === 'html') ? bbcode($body) : $body)) . '</content>' . "\r\n"; - $o .= '<link rel="alternate" type="text/html" href="' . xmlify($a->get_baseurl() . '/display/' . $owner['nickname'] . '/' . $item['id']) . '" />' . "\r\n"; + $o .= '<link rel="alternate" type="text/html" href="' . xmlify($item['plink']) . '" />' . "\r\n"; if($item['location']) { - $o .= '<dfrn:location>' . xmlify($item['location']) . '</dfrn:location>' . "\r\n"; + $o .= '<zot:location>' . xmlify($item['location']) . '</zot:location>' . "\r\n"; $o .= '<poco:address><poco:formatted>' . xmlify($item['location']) . '</poco:formatted></poco:address>' . "\r\n"; } if($item['coord']) $o .= '<georss:point>' . xmlify($item['coord']) . '</georss:point>' . "\r\n"; - if(($item['private']) || strlen($item['allow_cid']) || strlen($item['allow_gid']) || strlen($item['deny_cid']) || strlen($item['deny_gid'])) - $o .= '<dfrn:private>' . (($item['private']) ? $item['private'] : 1) . '</dfrn:private>' . "\r\n"; + if(($item['item_private']) || strlen($item['allow_cid']) || strlen($item['allow_gid']) || strlen($item['deny_cid']) || strlen($item['deny_gid'])) + $o .= '<zot:private>' . (($item['item_private']) ? $item['item_private'] : 1) . '</zot:private>' . "\r\n"; if($item['app']) @@ -3867,18 +2920,20 @@ function atom_entry($item,$type,$author,$owner,$comment = false,$cid = 0) { if(strlen($actarg)) $o .= $actarg; - $tags = item_getfeedtags($item); - if(count($tags)) { - foreach($tags as $t) { - $o .= '<category scheme="X-DFRN:' . xmlify($t[0]) . ':' . xmlify($t[1]) . '" term="' . xmlify($t[2]) . '" />' . "\r\n"; - } - } + // FIXME +// $tags = item_getfeedtags($item); +// if(count($tags)) { +// foreach($tags as $t) { +// $o .= '<category scheme="X-DFRN:' . xmlify($t[0]) . ':' . xmlify($t[1]) . '" term="' . xmlify($t[2]) . '" />' . "\r\n"; +// } +// } - $o .= item_getfeedattach($item); +// FIXME +// $o .= item_getfeedattach($item); - $mentioned = get_mentions($item,$tags); - if($mentioned) - $o .= $mentioned; +// $mentioned = get_mentions($item,$tags); +// if($mentioned) +// $o .= $mentioned; call_hooks('atom_entry', $o); @@ -3896,9 +2951,9 @@ function fix_private_photos($s, $uid, $item = null, $cid = 0) { $orig_body = $s; $new_body = ''; - $img_start = strpos($orig_body, '[img'); + $img_start = strpos($orig_body, '[zmg'); $img_st_close = ($img_start !== false ? strpos(substr($orig_body, $img_start), ']') : false); - $img_len = ($img_start !== false ? strpos(substr($orig_body, $img_start + $img_st_close + 1), '[/img]') : false); + $img_len = ($img_start !== false ? strpos(substr($orig_body, $img_start + $img_st_close + 1), '[/zmg]') : false); while( ($img_st_close !== false) && ($img_len !== false) ) { $img_st_close++; // make it point to AFTER the closing bracket @@ -3948,13 +3003,13 @@ function fix_private_photos($s, $uid, $item = null, $cid = 0) { $type = $r[0]['type']; // If a custom width and height were specified, apply before embedding - if(preg_match("/\[img\=([0-9]*)x([0-9]*)\]/is", substr($orig_body, $img_start, $img_st_close), $match)) { + if(preg_match("/\[zmg\=([0-9]*)x([0-9]*)\]/is", substr($orig_body, $img_start, $img_st_close), $match)) { logger('fix_private_photos: scaling photo', LOGGER_DEBUG); $width = intval($match[1]); $height = intval($match[2]); - $ph = new Photo($data, $type); + $ph = photo_factory($data, $type); if($ph->is_valid()) { $ph->scaleImage(max($width, $height)); $data = $ph->imageString(); @@ -3970,14 +3025,14 @@ function fix_private_photos($s, $uid, $item = null, $cid = 0) { } } - $new_body = $new_body . substr($orig_body, 0, $img_start + $img_st_close) . $image . '[/img]'; - $orig_body = substr($orig_body, $img_start + $img_st_close + $img_len + strlen('[/img]')); + $new_body = $new_body . substr($orig_body, 0, $img_start + $img_st_close) . $image . '[/zmg]'; + $orig_body = substr($orig_body, $img_start + $img_st_close + $img_len + strlen('[/zmg]')); if($orig_body === false) $orig_body = ''; - $img_start = strpos($orig_body, '[img'); + $img_start = strpos($orig_body, '[zmg'); $img_st_close = ($img_start !== false ? strpos(substr($orig_body, $img_start), ']') : false); - $img_len = ($img_start !== false ? strpos(substr($orig_body, $img_start + $img_st_close + 1), '[/img]') : false); + $img_len = ($img_start !== false ? strpos(substr($orig_body, $img_start + $img_st_close + 1), '[/zmg]') : false); } $new_body = $new_body . $orig_body; @@ -4409,39 +3464,52 @@ function zot_feed($uid,$observer_xchan,$mindate) { if(! $mindate) $mindate = '0000-00-00 00:00:00'; + $mindate = dbesc($mindate); + if(! perm_is_allowed($uid,$observer_xchan,'view_stream')) { return $result; } -// FIXME - $sql_extra = item_permissions_sql($uid,$remote_contact,$groups); + $sql_extra = item_permissions_sql($uid); - if($mindate != '0000-00-00 00:00:00') + if($mindate != '0000-00-00 00:00:00') { $sql_extra .= " and created > '$mindate' "; + $limit = ""; + } + else + $limit = " limit 0, 50 "; + $items = array(); -// FIXME - // We probably should use two queries and pick up total conversations. - // For now get a chunk of raw posts in ascending created order so that - // hopefully the parent is imported before we see the kids. - // This will fail if there are more than $limit kids and you didn't - // receive the parent via direct delivery - - $limit = 200; - - $items = q("SELECT item.* from item - WHERE uid = %d AND item_restrict = 0 + $r = q("SELECT item.*, item.id as item_id from item + WHERE uid = %d AND item_restrict = 0 and id = parent AND (item_flags & %d) - $sql_extra ORDER BY created ASC limit 0, $limit", + $sql_extra ORDER BY created ASC $limit", intval($uid), intval(ITEM_WALL) ); + if($r) { + + $parents_str = ids_to_querystr($r,'id'); + + $items = q("SELECT `item`.*, `item`.`id` AS `item_id` FROM `item` + WHERE `item`.`uid` = %d AND `item`.`item_restrict` = 0 + AND `item`.`parent` IN ( %s ) ", + intval($uid), + dbesc($parents_str) + ); + + } + if($items) { xchan_query($items); $items = fetch_post_tags($items); - } else { - $items = array(); + require_once('include/conversation.php'); + $items = conv_sort($items,'ascending'); + } + else + $items = array(); foreach($items as $item) $result[] = encode_item($item); @@ -4449,3 +3517,235 @@ function zot_feed($uid,$observer_xchan,$mindate) { return $result; } + + + +function items_fetch($arr,$channel = null,$observer_hash = null,$client_mode = CLIENT_MODE_NORMAL,$module = 'network') { + + $result = array('success' => false); + + $a = get_app(); + + $sql_extra = ''; + $sql_nets = ''; + $sql_options = ''; + $sql_extra2 = ''; + $sql_extra3 = ''; + $item_uids = ' true '; + + if($channel) { + $uid = $channel['channel_id']; + $uidhash = $channel['channel_hash']; + $item_uids = " item.uid = " . intval($uid) . " "; + } + + if($arr['star']) + $sql_options .= " and (item_flags & " . intval(ITEM_STARRED) . ") "; + + if($arr['wall']) + $sql_options .= " and (item_flags & " . intval(ITEM_WALL) . ") "; + + $sql_extra = " AND item.parent IN ( SELECT parent FROM item WHERE (item_flags & " . intval(ITEM_THREAD_TOP) . ") $sql_options ) "; + + if($arr['group'] && $uid) { + $r = q("SELECT * FROM `group` WHERE id = %d AND uid = %d LIMIT 1", + intval($arr['group']), + intval($uid) + ); + if(! $r) { + $result['message'] = t('Collection not found.'); + return $result; + } + + $contacts = expand_groups(array($arr['group'])); + if((is_array($contacts)) && count($contacts)) { + $contact_str = implode(',',$contacts); + } + else { + $contact_str = ' 0 '; + $result['message'] = t('Collection has no members.'); + } + + $sql_extra = " AND item.parent IN ( SELECT DISTINCT parent FROM item WHERE true $sql_options AND ( author_xchan IN ( $contact_str ) OR owner_xchan in ( $contact_str) or allow_gid like '" . protect_sprintf('%<' . dbesc($r[0]['hash']) . '>%') . "' ) and item_restrict = 0 ) "; + + } + elseif($arr['cid'] && $uid) { + + $r = q("SELECT * from abook where abook_id = %d and abook_channel = %d and not ( abook_flags & " . intval(ABOOK_FLAG_BLOCKED) . ") limit 1", + intval($arr['cid']), + intval($uid) + ); + if($r) { + $sql_extra = " AND item.parent IN ( SELECT DISTINCT parent FROM item WHERE true $sql_options AND uid = " . intval($arr['uid']) . " AND ( author_xchan = '" . dbesc($r[0]['abook_xchan']) . "' or owner_xchan = '" . dbesc($r[0]['abook_xchan']) . "' ) and item_restrict = 0 ) "; + } + else { + $result['message'] = t('Connection not found.'); + return $result; + } + } + + if($arr['datequery']) { + $sql_extra3 .= protect_sprintf(sprintf(" AND item.created <= '%s' ", dbesc(datetime_convert(date_default_timezone_get(),'',$arr['datequery'])))); + } + if($arr['datequery2']) { + $sql_extra3 .= protect_sprintf(sprintf(" AND item.created >= '%s' ", dbesc(datetime_convert(date_default_timezone_get(),'',$arr['datequery2'])))); + } + + if(! array_key_exists('nouveau',$arr)) { + $sql_extra2 = " AND item.parent = item.id "; + $sql_extra3 = ''; + } + + if($arr['search']) { + if(strpos($arr['search'],'#') === 0) + $sql_extra .= term_query('item',substr($arr['search'],1),TERM_HASHTAG); + else + $sql_extra .= sprintf(" AND item.body like '%s' ", + dbesc(protect_sprintf('%' . $arr['search'] . '%')) + ); + } + + if(strlen($arr['file'])) { + $sql_extra .= term_query('item',$arr['files'],TERM_FILE); + } + + if($arr['conv'] && $channel) { + $sql_extra .= sprintf(" AND parent IN (SELECT distinct parent from item where ( author_xchan like '%s' or ( item_flags & %d ))) ", + dbesc(protect_sprintf($uidhash)), + intval(ITEM_MENTIONSME) + ); + } + + if(($client_mode & CLIENT_MODE_UPDATE) && (! ($client_mode & CLIENT_MODE_LOAD))) { + + // only setup pagination on initial page view + $pager_sql = ''; + + } + else { + $itemspage = (($channel) ? get_pconfig($uid,'system','itemspage') : 20); + $a->set_pager_itemspage(((intval($itemspage)) ? $itemspage : 20)); + $pager_sql = sprintf(" LIMIT %d, %d ",intval(get_app()->pager['start']), intval(get_app()->pager['itemspage'])); + } + + + if(($arr['cmin'] != 0) || ($arr['cmax'] != 99)) { + + // Not everybody who shows up in the network stream will be in your address book. + // By default those that aren't are assumed to have closeness = 99; but this isn't + // recorded anywhere. So if cmax is 99, we'll open the search up to anybody in + // the stream with a NULL address book entry. + + $sql_nets .= " AND "; + + if($arr['cmax'] == 99) + $sql_nets .= " ( "; + + $sql_nets .= "( abook.abook_closeness >= " . intval($arr['cmin']) . " "; + $sql_nets .= " AND abook.abook_closeness <= " . intval($arr['cmax']) . " ) "; + if($cmax == 99) + $sql_nets .= " OR abook.abook_closeness IS NULL ) "; + } + + $simple_update = (($client_mode & CLIENT_MODE_UPDATE) ? " and ( item.item_flags & " . intval(ITEM_UNSEEN) . " ) " : ''); + if($client_mode & CLIENT_MODE_LOAD) + $simple_update = ''; + + $start = dba_timer(); + + require_once('include/security.php'); + $sql_extra .= item_permissions_sql($channel['channel_id']); + + if($arr['nouveau'] && ($client_mode & CLIENT_MODELOAD) && $channel) { + // "New Item View" - show all items unthreaded in reverse created date order + + $items = q("SELECT item.*, item.id AS item_id FROM item + WHERE $item_uids AND item_restrict = 0 + $simple_update + $sql_extra $sql_nets + ORDER BY item.received DESC $pager_sql " + ); + + require_once('include/items.php'); + + xchan_query($items); + + $items = fetch_post_tags($items,true); + } + else { + + // Normal conversation view + + if($arr['order'] === 'post') + $ordering = "created"; + else + $ordering = "commented"; + + if(($client_mode & CLIENT_MODE_LOAD) || ($client_mode & CLIENT_MODE_NORMAL)) { + + // Fetch a page full of parent items for this page + + $r = q("SELECT distinct item.id AS item_id FROM item + left join abook on item.author_xchan = abook.abook_xchan + WHERE $item_uids AND item.item_restrict = 0 + AND item.parent = item.id + and ((abook.abook_flags & %d) = 0 or abook.abook_flags is null) + $sql_extra3 $sql_extra $sql_nets + ORDER BY item.$ordering DESC $pager_sql ", + intval(ABOOK_FLAG_BLOCKED) + ); + + } + else { + // update + $r = q("SELECT item.parent AS item_id FROM item + left join abook on item.author_xchan = abook.abook_xchan + WHERE $item_uids AND item.item_restrict = 0 $simple_update + and ((abook.abook_flags & %d) = 0 or abook.abook_flags is null) + $sql_extra3 $sql_extra $sql_nets ", + intval(ABOOK_FLAG_BLOCKED) + ); + } + + $first = dba_timer(); + + // Then fetch all the children of the parents that are on this page + + if($r) { + + $parents_str = ids_to_querystr($r,'item_id'); + + $items = q("SELECT item.*, item.id AS item_id FROM item + WHERE $item_uids AND item.item_restrict = 0 + AND item.parent IN ( %s ) + $sql_extra ", + dbesc($parents_str) + ); + + $second = dba_timer(); + + xchan_query($items); + + $third = dba_timer(); + + $items = fetch_post_tags($items,true); + + $fourth = dba_timer(); + + require_once('include/conversation.php'); + $items = conv_sort($items,$ordering); + + //logger('items: ' . print_r($items,true)); + + } + else { + $items = array(); + } + + if($parents_str && $arr['mark_seen']) + $update_unseen = ' AND parent IN ( ' . dbesc($parents_str) . ' )'; + // FIXME finish mark unseen sql + } + + return $items; +}
\ No newline at end of file diff --git a/include/js_strings.php b/include/js_strings.php index 60a3e1a86..2e4f70774 100644 --- a/include/js_strings.php +++ b/include/js_strings.php @@ -1,4 +1,4 @@ -<?php +<?php /** @file */ function js_strings() { return replace_macros(get_markup_template('js_strings.tpl'), array( diff --git a/include/language.php b/include/language.php index 56d5f1cf4..2e7ad5ff1 100644 --- a/include/language.php +++ b/include/language.php @@ -1,4 +1,4 @@ -<?php +<?php /** @file */ /** @@ -15,35 +15,33 @@ * */ - -if(! function_exists('get_browser_language')) { function get_browser_language() { $langs = array(); if (x($_SERVER,'HTTP_ACCEPT_LANGUAGE')) { - // break up string into pieces (languages and q factors) - preg_match_all('/([a-z]{1,8}(-[a-z]{1,8})?)\s*(;\s*q\s*=\s*(1|0\.[0-9]+))?/i', + // break up string into pieces (languages and q factors) + preg_match_all('/([a-z]{1,8}(-[a-z]{1,8})?)\s*(;\s*q\s*=\s*(1|0\.[0-9]+))?/i', $_SERVER['HTTP_ACCEPT_LANGUAGE'], $lang_parse); - if (count($lang_parse[1])) { - // create a list like "en" => 0.8 - $langs = array_combine($lang_parse[1], $lang_parse[4]); - - // set default to 1 for any without q factor - foreach ($langs as $lang => $val) { - if ($val === '') $langs[$lang] = 1; - } - - // sort list based on value - arsort($langs, SORT_NUMERIC); - } + if (count($lang_parse[1])) { + // create a list like "en" => 0.8 + $langs = array_combine($lang_parse[1], $lang_parse[4]); + + // set default to 1 for any without q factor + foreach ($langs as $lang => $val) { + if ($val === '') $langs[$lang] = 1; + } + + // sort list based on value + arsort($langs, SORT_NUMERIC); + } } else $langs['en'] = 1; return $langs; -}} +} function get_best_language() { @@ -62,7 +60,7 @@ function get_best_language() { if(isset($preferred)) return $preferred; - $a = get_app(); + $a = get_app(); return ((isset($a->config['system']['language'])) ? $a->config['system']['language'] : 'en'); } @@ -101,15 +99,25 @@ function pop_lang() { // load string translation table for alternate language -if(! function_exists('load_translation_table')) { -function load_translation_table($lang) { +function load_translation_table($lang, $install = false) { global $a; + $a->strings = array(); if(file_exists("view/$lang/strings.php")) { include("view/$lang/strings.php"); } - else - $a->strings = array(); + + if(! $install) { + $plugins = q("SELECT name FROM addon WHERE installed=1;"); + if ($plugins!==false) { + foreach($plugins as $p) { + $name = $p['name']; + if(file_exists("addon/$name/lang/$lang/strings.php")) { + include("addon/$name/lang/$lang/strings.php"); + } + } + } + } // Allow individual strings to be over-ridden on this site // Either for the default language or for all languages @@ -118,11 +126,10 @@ function load_translation_table($lang) { include("view/local-$lang/strings.php"); } -}} +} // translate string if translation exists -if(! function_exists('t')) { function t($s) { global $a; @@ -132,9 +139,9 @@ function t($s) { return is_array($t)?$t[0]:$t; } return $s; -}} +} + -if(! function_exists('tt')){ function tt($singular, $plural, $count){ $a = get_app(); @@ -152,15 +159,14 @@ function tt($singular, $plural, $count){ } else { return $singular; } -}} +} // provide a fallback which will not collide with // a function defined in any language file -if(! function_exists('string_plural_select_default')) { function string_plural_select_default($n) { return ($n != 1); -}} +} diff --git a/include/message.php b/include/message.php index 00cf30512..fc0d5f2b3 100644 --- a/include/message.php +++ b/include/message.php @@ -1,7 +1,8 @@ -<?php +<?php /** @file */ /* Private Message backend API */ +require_once('include/crypto.php'); // send a private message @@ -43,37 +44,60 @@ function send_message($uid = 0, $recipient='', $body='', $subject='', $replyto=' $dups = false; $hash = random_string(); - $uri = $hash . '@' . get_app()->get_hostname(); + $mid = $hash . '@' . get_app()->get_hostname(); - $r = q("SELECT id FROM mail WHERE uri = '%s' LIMIT 1", - dbesc($uri)); + $r = q("SELECT id FROM mail WHERE mid = '%s' LIMIT 1", + dbesc($mid)); if(count($r)) $dups = true; } while($dups == true); if(! strlen($replyto)) { - $replyto = $uri; + $replyto = $mid; } + /** + * + * When a photo was uploaded into the message using the (profile wall) ajax + * uploader, The permissions are initially set to disallow anybody but the + * owner from seeing it. This is because the permissions may not yet have been + * set for the post. If it's private, the photo permissions should be set + * appropriately. But we didn't know the final permissions on the post until + * now. So now we'll look for links of uploaded messages that are in the + * post and set them to the same permissions as the post itself. + * + */ + + $match = null; + $images = null; + if(preg_match_all("/\[img\](.*?)\[\/img\]/",$body,$match)) + $images = $match[1]; + + $key = get_config('system','pubkey'); + if($subject) + $subject = json_encode(aes_encapsulate($subject,$key)); + if($body) + $body = json_encode(aes_encapsulate($body,$key)); - $r = q("INSERT INTO mail ( account_id, channel_id, from_xchan, to_xchan, title, body, uri, parent_uri, created ) - VALUES ( %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s' )", + $r = q("INSERT INTO mail ( account_id, mail_flags, channel_id, from_xchan, to_xchan, title, body, mid, parent_mid, created ) + VALUES ( %d, %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s' )", intval($channel['channel_account_id']), + intval(MAIL_OBSCURED), intval($channel['channel_id']), dbesc($channel['channel_hash']), dbesc($recipient), dbesc($subject), dbesc($body), - dbesc($uri), + dbesc($mid), dbesc($replyto), dbesc(datetime_convert()) ); // verify the save - $r = q("SELECT * FROM mail WHERE uri = '%s' and channel_id = %d LIMIT 1", - dbesc($uri), + $r = q("SELECT * FROM mail WHERE mid = '%s' and channel_id = %d LIMIT 1", + dbesc($mid), intval($channel['channel_id']) ); if($r) @@ -83,35 +107,18 @@ function send_message($uid = 0, $recipient='', $body='', $subject='', $replyto=' return $ret; } - /** - * - * When a photo was uploaded into the message using the (profile wall) ajax - * uploader, The permissions are initially set to disallow anybody but the - * owner from seeing it. This is because the permissions may not yet have been - * set for the post. If it's private, the photo permissions should be set - * appropriately. But we didn't know the final permissions on the post until - * now. So now we'll look for links of uploaded messages that are in the - * post and set them to the same permissions as the post itself. - * - */ - - $match = null; - - if(preg_match_all("/\[img\](.*?)\[\/img\]/",$body,$match)) { - $images = $match[1]; - if(count($images)) { - foreach($images as $image) { - if(! stristr($image,$a->get_baseurl() . '/photo/')) - continue; - $image_uri = substr($image,strrpos($image,'/') + 1); - $image_uri = substr($image_uri,0, strpos($image_uri,'-')); - $r = q("UPDATE photo SET allow_cid = '%s' WHERE resource_id = '%s' AND uid = %d and allow_cid = '%s'", - dbesc('<' . $recipient . '>'), - dbesc($image_uri), - intval($channel['channel_id']), - dbesc('<' . $channel['channel_hash'] . '>') - ); - } + if(count($images)) { + foreach($images as $image) { + if(! stristr($image,$a->get_baseurl() . '/photo/')) + continue; + $image_uri = substr($image,strrpos($image,'/') + 1); + $image_uri = substr($image_uri,0, strpos($image_uri,'-')); + $r = q("UPDATE photo SET allow_cid = '%s' WHERE resource_id = '%s' AND uid = %d and allow_cid = '%s'", + dbesc('<' . $recipient . '>'), + dbesc($image_uri), + intval($channel['channel_id']), + dbesc('<' . $channel['channel_hash'] . '>') + ); } } @@ -169,6 +176,15 @@ function private_messages_list($uid, $mailbox = '', $start = 0, $numitems = 0) { $r[$k]['from'] = find_xchan_in_array($rr['from_xchan'],$c); $r[$k]['to'] = find_xchan_in_array($rr['to_xchan'],$c); $r[$k]['seen'] = (($rr['mail_flags'] & MAIL_SEEN) ? 1 : 0); + if($r[$k]['mail_flags'] & MAIL_OBSCURED) { + logger('unencrypting'); + $key = get_config('system','prvkey'); + + if($r[$k]['title']) + $r[$k]['title'] = aes_unencapsulate(json_decode_plus($r[$k]['title']),$key); + if($r[$k]['body']) + $r[$k]['body'] = aes_unencapsulate(json_decode_plus($r[$k]['body']),$key); + } } return $r; @@ -201,6 +217,13 @@ function private_messages_fetch_message($channel_id, $messageitem_id, $updatesee foreach($messages as $k => $message) { $messages[$k]['from'] = find_xchan_in_array($message['from_xchan'],$c); $messages[$k]['to'] = find_xchan_in_array($message['to_xchan'],$c); + if($messages[$k]['mail_flags'] & MAIL_OBSCURED) { + $key = get_config('system','prvkey'); + if($messages[$k]['title']) + $messages[$k]['title'] = aes_unencapsulate(json_decode_plus($messages[$k]['title']),$key); + if($messages[$k]['body']) + $messages[$k]['body'] = aes_unencapsulate(json_decode_plus($messages[$k]['body']),$key); + } } if($updateseen) { @@ -221,13 +244,13 @@ function private_messages_drop($channel_id, $messageitem_id, $drop_conversation if($drop_conversation) { // find the parent_id - $p = q("SELECT parent_uri FROM mail WHERE id = %d AND channel_id = %d LIMIT 1", + $p = q("SELECT parent_mid FROM mail WHERE id = %d AND channel_id = %d LIMIT 1", intval($messageitem_id), intval($channel_id) ); if($p) { - $r = q("DELETE FROM mail WHERE parent_uri = '%s' AND channel_id = %d ", - dbesc($p[0]['parent_uri']), + $r = q("DELETE FROM mail WHERE parent_mid = '%s' AND channel_id = %d ", + dbesc($p[0]['parent_mid']), intval($channel_id) ); if($r) @@ -248,9 +271,9 @@ function private_messages_drop($channel_id, $messageitem_id, $drop_conversation function private_messages_fetch_conversation($channel_id, $messageitem_id, $updateseen = false) { - // find the parent_uri of the message being requested + // find the parent_mid of the message being requested - $r = q("SELECT parent_uri from mail WHERE channel_id = %d and id = %d limit 1", + $r = q("SELECT parent_mid from mail WHERE channel_id = %d and id = %d limit 1", intval($channel_id), intval($messageitem_id) ); @@ -258,8 +281,8 @@ function private_messages_fetch_conversation($channel_id, $messageitem_id, $upda if(! $r) return array(); - $messages = q("select * from mail where parent_uri = '%s' and channel_id = %d order by created asc", - dbesc($r[0]['parent_uri']), + $messages = q("select * from mail where parent_mid = '%s' and channel_id = %d order by created asc", + dbesc($r[0]['parent_mid']), intval($channel_id) ); @@ -282,14 +305,21 @@ function private_messages_fetch_conversation($channel_id, $messageitem_id, $upda foreach($messages as $k => $message) { $messages[$k]['from'] = find_xchan_in_array($message['from_xchan'],$c); $messages[$k]['to'] = find_xchan_in_array($message['to_xchan'],$c); + if($messages[$k]['mail_flags'] & MAIL_OBSCURED) { + $key = get_config('system','prvkey'); + if($messages[$k]['title']) + $messages[$k]['title'] = aes_unencapsulate(json_decode_plus($messages[$k]['title']),$key); + if($messages[$k]['body']) + $messages[$k]['body'] = aes_unencapsulate(json_decode_plus($messages[$k]['body']),$key); + } } if($updateseen) { - $r = q("UPDATE `mail` SET mail_flags = (mail_flags ^ %d) where not (mail_flags & %d) and parent_uri = '%s' AND channel_id = %d", + $r = q("UPDATE `mail` SET mail_flags = (mail_flags ^ %d) where not (mail_flags & %d) and parent_mid = '%s' AND channel_id = %d", intval(MAIL_SEEN), intval(MAIL_SEEN), - dbesc($r[0]['parent_uri']), + dbesc($r[0]['parent_mid']), intval($channel_id) ); } diff --git a/include/nav.php b/include/nav.php index 5490d6cd4..626caf981 100644 --- a/include/nav.php +++ b/include/nav.php @@ -1,4 +1,4 @@ -<?php +<?php /** @file */ function nav(&$a) { @@ -71,7 +71,7 @@ EOT; $nav['logout'] = Array('logout',t('Logout'), "", t('End this session')); // user menu - $nav['usermenu'][] = Array('channel/' . $channel['channel_address'], t('Status'), "", t('Your posts and conversations')); + $nav['usermenu'][] = Array('channel/' . $channel['channel_address'], t('Home'), "", t('Your posts and conversations')); $nav['usermenu'][] = Array('profile/' . $channel['channel_address'], t('View Profile'), "", t('Your profile page')); if(feature_enabled(local_user(),'multi_profiles')) $nav['usermenu'][] = Array('profiles', t('Edit Profiles'),"", t('Manage/Edit Profiles')); @@ -107,8 +107,10 @@ EOT; */ $homelink = get_my_url(); - if(! $homelink) - $homelink = ((x($_SESSION,'visitor_home')) ? $_SESSION['visitor_home'] : ''); + if(! $homelink) { + $observer = $a->get_observer(); + $homelink = (($observer) ? $observer['xchan_url'] : ''); + } if(($a->module != 'home') && (! (local_user()))) $nav['home'] = array($homelink, t('Home'), "", t('Home Page')); @@ -128,7 +130,7 @@ EOT; $nav['search'] = array('search', t('Search'), "", t('Search site content')); - $nav['directory'] = array('directory', t('Channel Directory'), "", t('Channel Locator')); + $nav['directory'] = array('directory', t('Directory'), "", t('Channel Locator')); /** @@ -139,24 +141,24 @@ EOT; if(local_user()) { - $nav['network'] = array('network', t('Network'), "", t('Conversations from your friends')); - $nav['network']['all']=array('notifications/network', t('See all network notifications'), "", ""); - $nav['network']['mark'] = array('', t('Mark all network notifications seen'), '',''); + $nav['network'] = array('network', t('Matrix'), "", t('Conversations from your grid')); + $nav['network']['all']=array('notifications/network', t('See all matrix notifications'), "", ""); + $nav['network']['mark'] = array('', t('Mark all matrix notifications seen'), '',''); $nav['home'] = array('channel/' . $channel['channel_address'], t('Home'), "", t('Your posts and conversations')); $nav['home']['all']=array('notifications/channel', t('See all channel notifications'), "", ""); $nav['home']['mark'] = array('', t('Mark all channel notifications seen'), '',''); - $nav['intros'] = array('connections/pending', t('Introductions'), "", t('New Connections')); + $nav['intros'] = array('connections/pending', t('Intros'), "", t('New Connections')); $nav['intros']['all']=array('intro', t('See all channel introductions'), "", ""); - $nav['notifications'] = array('notifications/system', t('Notifications'), "", t('Notifications')); + $nav['notifications'] = array('notifications/system', t('Notices'), "", t('Notifications')); $nav['notifications']['all']=array('notifications/system', t('See all notifications'), "", ""); $nav['notifications']['mark'] = array('', t('Mark all system notifications seen'), '',''); - $nav['messages'] = array('message', t('Messages'), "", t('Private mail')); + $nav['messages'] = array('message', t('Mail'), "", t('Private mail')); $nav['messages']['all']=array('message', t('See all private messages'), "", ""); $nav['messages']['mark'] = array('', t('Mark all private messages seen'), '',''); $nav['messages']['inbox'] = array('message', t('Inbox'), "", t('Inbox')); @@ -192,7 +194,6 @@ EOT; $banner = get_config('system','banner'); if($banner === false) -// $banner .= '<a href="http://friendica.com"><img id="logo-img" src="images/fred-32.png" alt="logo" /></a>'; $banner = 'red'; $tpl = get_markup_template('nav.tpl'); diff --git a/include/network.php b/include/network.php index 555e76802..8b9a8a6a6 100644 --- a/include/network.php +++ b/include/network.php @@ -1,4 +1,4 @@ -<?php +<?php /** @file */ @@ -6,10 +6,20 @@ function get_capath() { return appdirpath() . '/library/cacert.pem'; } + + // curl wrapper. If binary flag is true, return binary // results. -if(! function_exists('fetch_url')) { +/** + * fetch_url is deprecated and being replaced by the more capable z_fetch_url + * please use that function instead. + * Once all occurrences of fetch_url are removed from the codebase we will + * remove this function and perhaps rename z_fetch_url back to fetch_url + */ + + + function fetch_url($url,$binary = false, &$redirects = 0, $timeout = 0, $accept_content=Null) { $a = get_app(); @@ -28,8 +38,7 @@ function fetch_url($url,$binary = false, &$redirects = 0, $timeout = 0, $accept_ } @curl_setopt($ch, CURLOPT_RETURNTRANSFER,true); - //@curl_setopt($ch, CURLOPT_USERAGENT, "Friendica"); - @curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/5.0 (compatible; Friendica)"); + @curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/5.0 (compatible; Red)"); if(intval($timeout)) { @@ -98,11 +107,11 @@ function fetch_url($url,$binary = false, &$redirects = 0, $timeout = 0, $accept_ $a->set_curl_headers($header); @curl_close($ch); return($body); -}} +} // post request to $url. $params is an array of post variables. -if(! function_exists('post_url')) { + function post_url($url,$params, $headers = null, &$redirects = 0, $timeout = 0) { $a = get_app(); $ch = curl_init($url); @@ -114,7 +123,7 @@ function post_url($url,$params, $headers = null, &$redirects = 0, $timeout = 0) curl_setopt($ch, CURLOPT_RETURNTRANSFER,true); curl_setopt($ch, CURLOPT_POST,1); curl_setopt($ch, CURLOPT_POSTFIELDS,$params); - curl_setopt($ch, CURLOPT_USERAGENT, "Friendica"); + curl_setopt($ch, CURLOPT_USERAGENT, "Red"); if(intval($timeout)) { curl_setopt($ch, CURLOPT_TIMEOUT, $timeout); @@ -193,9 +202,29 @@ function post_url($url,$params, $headers = null, &$redirects = 0, $timeout = 0) curl_close($ch); return($body); -}} +} + +/** + * @function z_fetch_url + * @param string $url + * URL to fetch + * @param boolean $binary = false + * TRUE if asked to return binary results (file download) + * @param int $redirects = 0 + * internal use, recursion counter + * @param array $opts (optional parameters) + * 'accept_content' => supply Accept: header with 'accept_content' as the value + * 'timeout' => int seconds, default system config value or 60 seconds + * 'http_auth' => username:password + * 'novalidate' => do not validate SSL certs, default is to validate using our CA list + * + * @returns array + * 'return_code' => HTTP return code or 0 if timeout or failure + * 'success' => boolean true (if HTTP 2xx result) or false + * 'header' => HTTP headers + * 'body' => fetched content + */ -if(! function_exists('z_fetch_url')) { function z_fetch_url($url, $binary = false, $redirects = 0, $opts = array()) { $ret = array('return_code' => 0, 'success' => false, 'header' => "", 'body' => ""); @@ -210,7 +239,7 @@ function z_fetch_url($url, $binary = false, $redirects = 0, $opts = array()) { @curl_setopt($ch, CURLOPT_CAINFO, get_capath()); @curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true); @curl_setopt($ch, CURLOPT_RETURNTRANSFER,true); - @curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/5.0 (compatible; Friendica Red)"); + @curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/5.0 (compatible; Red)"); if (x($opts,'accept_content')){ curl_setopt($ch,CURLOPT_HTTPHEADER, array ( @@ -231,6 +260,10 @@ function z_fetch_url($url, $binary = false, $redirects = 0, $opts = array()) { @curl_setopt($ch, CURLOPT_USERPWD, $opts['http_auth']); } + @curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, + ((x($opts,'novalidate') && intval($opts['novalidate'])) ? false : true)); + + $prx = get_config('system','proxy'); if(strlen($prx)) { @curl_setopt($ch, CURLOPT_HTTPPROXYTUNNEL, 1); @@ -283,12 +316,12 @@ function z_fetch_url($url, $binary = false, $redirects = 0, $opts = array()) { $ret['header'] = $header; @curl_close($ch); return($ret); -}} +} + -if(! function_exists('z_post_url')) { -function z_post_url($url,$params, $headers = null, $redirects = 0, $timeout = 0) { +function z_post_url($url,$params, $redirects = 0, $opts = array()) { $ret = array('return_code' => 0, 'success' => false, 'header' => "", 'body' => ""); @@ -301,30 +334,30 @@ function z_post_url($url,$params, $headers = null, $redirects = 0, $timeout = 0) curl_setopt($ch, CURLOPT_RETURNTRANSFER,true); curl_setopt($ch, CURLOPT_POST,1); curl_setopt($ch, CURLOPT_POSTFIELDS,$params); - curl_setopt($ch, CURLOPT_USERAGENT, "Friendica"); + curl_setopt($ch, CURLOPT_USERAGENT, "Red"); - if(intval($timeout)) { - curl_setopt($ch, CURLOPT_TIMEOUT, $timeout); + + if (x($opts,'accept_content')){ + curl_setopt($ch,CURLOPT_HTTPHEADER, array ( + "Accept: " . $opts['accept_content'] + )); + } + + if(x($opts,'timeout') && intval($opts['timeout'])) { + @curl_setopt($ch, CURLOPT_TIMEOUT, $opts['timeout']); } else { $curl_time = intval(get_config('system','curl_timeout')); - curl_setopt($ch, CURLOPT_TIMEOUT, (($curl_time !== false) ? $curl_time : 60)); + @curl_setopt($ch, CURLOPT_TIMEOUT, (($curl_time !== false) ? $curl_time : 60)); } - if(defined('LIGHTTPD')) { - if(!is_array($headers)) { - $headers = array('Expect:'); - } else { - if(!in_array('Expect:', $headers)) { - array_push($headers, 'Expect:'); - } - } + if(x($opts,'http_auth')) { + // "username" . ':' . "password" + @curl_setopt($ch, CURLOPT_USERPWD, $opts['http_auth']); } - if($headers) - curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); - - curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true); + @curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, + ((x($opts,'novalidate') && intval($opts['novalidate'])) ? false : true)); $prx = get_config('system','proxy'); if(strlen($prx)) { @@ -365,9 +398,9 @@ function z_post_url($url,$params, $headers = null, $redirects = 0, $timeout = 0) if (isset($url_parsed)) { curl_close($ch); if($http_code == 303) { - return z_fetch_url($newurl,false,$headers,$redirects++,$timeout); + return z_fetch_url($newurl,false,$redirects++,$opts); } else { - return z_post_url($newurl,$params,$headers,$redirects++,$timeout); + return z_post_url($newurl,$params,$redirects++,$opts); } } } @@ -378,7 +411,7 @@ function z_post_url($url,$params, $headers = null, $redirects = 0, $timeout = 0) $ret['header'] = $header; curl_close($ch); return($ret); -}} +} @@ -394,7 +427,7 @@ function json_return_and_die($x) { // Outputs a basic dfrn XML status structure to STDOUT, with a <status> variable // of $st and an optional text <message> of $message and terminates the current process. -if(! function_exists('xml_status')) { + function xml_status($st, $message = '') { $xml_message = ((strlen($message)) ? "\t<message>" . xmlify($message) . "</message>\r\n" : ''); @@ -406,29 +439,37 @@ function xml_status($st, $message = '') { echo '<?xml version="1.0" encoding="UTF-8"?>'."\r\n"; echo "<result>\r\n\t<status>$st</status>\r\n$xml_message</result>\r\n"; killme(); -}} - +} -if(! function_exists('http_status_exit')) { -function http_status_exit($val) { +/** + * @function http_status_exit + * + * Send HTTP status header and exit + * @param int $val + * integer HTTP status result value + * @param string $msg + * optional message + * @returns (does not return, process is terminated) + */ + +function http_status_exit($val,$msg = '') { $err = ''; if($val >= 400) - $err = 'Error'; + $msg = (($msg) ? $msg : 'Error'); if($val >= 200 && $val < 300) - $err = 'OK'; + $msg = (($msg) ? $msg : 'OK'); - logger('http_status_exit ' . $val); - header($_SERVER["SERVER_PROTOCOL"] . ' ' . $val . ' ' . $err); + logger('http_status_exit ' . $val . ' ' . $msg); + header($_SERVER['SERVER_PROTOCOL'] . ' ' . $val . ' ' . $msg); killme(); - -}} +} // convert an XML document to a normalised, case-corrected array // used by webfinger -if(! function_exists('convert_xml_element_to_array')) { + function convert_xml_element_to_array($xml_element, &$recursion_depth=0) { // If we're getting too deep, bail out @@ -468,7 +509,7 @@ function convert_xml_element_to_array($xml_element, &$recursion_depth=0) { } else { return (trim(strval($xml_element))); } -}} +} // Given an email style address, perform webfinger lookup and // return the resulting DFRN profile URL, or if no DFRN profile URL @@ -482,7 +523,7 @@ function convert_xml_element_to_array($xml_element, &$recursion_depth=0) { // amended 7/9/2011 to return an hcard which could save potentially loading // a lengthy content page to scrape dfrn attributes -if(! function_exists('webfinger_dfrn')) { + function webfinger_dfrn($s,&$hcard) { if(! strstr($s,'@')) { return $s; @@ -502,14 +543,14 @@ function webfinger_dfrn($s,&$hcard) { } } return $profile_link; -}} +} // Given an email style address, perform webfinger lookup and // return the array of link attributes from the personal XRD file. // On error/failure return an empty array. -if(! function_exists('webfinger')) { + function webfinger($s, $debug = false) { $host = ''; if(strstr($s,'@')) { @@ -532,9 +573,9 @@ function webfinger($s, $debug = false) { } } return array(); -}} +} + -if(! function_exists('lrdd')) { function lrdd($uri, $debug = false) { $a = get_app(); @@ -702,7 +743,7 @@ function lrdd($uri, $debug = false) { return array(); -}} +} @@ -710,7 +751,7 @@ function lrdd($uri, $debug = false) { // host. Returns the LRDD template or an empty string on // error/failure. -if(! function_exists('fetch_lrdd_template')) { + function fetch_lrdd_template($host) { $tpl = ''; @@ -732,13 +773,13 @@ function fetch_lrdd_template($host) { if(! strpos($tpl,'{uri}')) $tpl = ''; return $tpl; -}} +} // Given a URL, retrieve the page as an XRD document. // Return an array of links. // on error/failure return empty array. -if(! function_exists('fetch_xrd_links')) { + function fetch_xrd_links($url) { $xrd_timeout = intval(get_config('system','xrd_timeout')); @@ -783,14 +824,14 @@ function fetch_xrd_links($url) { return $links; -}} +} // Take a URL from the wild, prepend http:// if necessary // and check DNS to see if it's real (or check if is a valid IP address) // return true if it's OK, false if something is wrong with it -if(! function_exists('validate_url')) { + function validate_url(&$url) { // no naked subdomains (allow localhost for tests) @@ -804,11 +845,11 @@ function validate_url(&$url) { return true; } return false; -}} +} // checks that email is an actual resolvable internet address -if(! function_exists('validate_email')) { + function validate_email($addr) { if(get_config('system','disable_email_validation')) @@ -822,14 +863,14 @@ function validate_email($addr) { return true; } return false; -}} +} // Check $url against our list of allowed sites, // wildcards allowed. If allowed_sites is unset return true; // If url is allowed, return true. // otherwise, return false -if(! function_exists('allowed_url')) { + function allowed_url($url) { $h = @parse_url($url); @@ -864,14 +905,14 @@ function allowed_url($url) { } } return $found; -}} +} // check if email address is allowed to register here. // Compare against our list (wildcards allowed). // Returns false if not allowed, true if allowed or if // allowed list is not configured. -if(! function_exists('allowed_email')) { + function allowed_email($email) { @@ -898,10 +939,10 @@ function allowed_email($email) { } } return $found; -}} +} + -if(! function_exists('avatar_img')) { function avatar_img($email) { $a = get_app(); @@ -918,10 +959,10 @@ function avatar_img($email) { logger('Avatar: ' . $avatar['email'] . ' ' . $avatar['url'], LOGGER_DEBUG); return $avatar['url']; -}} +} + -if(! function_exists('parse_xml_string')) { function parse_xml_string($s,$strict = true) { if($strict) { if(! strstr($s,'<?xml')) @@ -940,7 +981,7 @@ function parse_xml_string($s,$strict = true) { libxml_clear_errors(); } return $x; -}} +} function add_fcontact($arr,$update = false) { @@ -1008,14 +1049,21 @@ function scale_external_images($s, $include_link = true, $scale_replace = false) $s = htmlspecialchars_decode($s); $matches = null; - $c = preg_match_all('/\[img.*?\](.*?)\[\/img\]/ism',$s,$matches,PREG_SET_ORDER); + $c = preg_match_all('/\[img(.*?)\](.*?)\[\/img\]/ism',$s,$matches,PREG_SET_ORDER); if($c) { - require_once('include/Photo.php'); + require_once('include/photo/photo_driver.php'); + foreach($matches as $mtch) { - logger('scale_external_image: ' . $mtch[1]); + logger('scale_external_image: ' . $mtch[1] . ' ' . $mtch[2]); + + if(substr($mtch[1],0,1) == '=') { + $owidth = intval(substr($mtch[1],1)); + if(intval($owidth) > 0 && intval($owidth) < 640) + continue; + } $hostname = str_replace('www.','',substr($a->get_baseurl(),strpos($a->get_baseurl(),'://')+3)); - if(stristr($mtch[1],$hostname)) + if(stristr($mtch[2],$hostname)) continue; // $scale_replace, if passed, is an array of two elements. The @@ -1024,9 +1072,9 @@ function scale_external_images($s, $include_link = true, $scale_replace = false) // This allows Friendica to display the smaller remote image if // one exists, while still linking to the full-size image if($scale_replace) - $scaled = str_replace($scale_replace[0], $scale_replace[1], $mtch[1]); + $scaled = str_replace($scale_replace[0], $scale_replace[1], $mtch[2]); else - $scaled = $mtch[1]; + $scaled = $mtch[2]; $i = fetch_url($scaled); $cache = get_config('system','itemcache'); @@ -1036,10 +1084,10 @@ function scale_external_images($s, $include_link = true, $scale_replace = false) } // guess mimetype from headers or filename - $type = guess_image_type($mtch[1],true); + $type = guess_image_type($mtch[2],true); if($i) { - $ph = new Photo($i, $type); + $ph = photo_factory($i, $type); if($ph->is_valid()) { $orig_width = $ph->getWidth(); $orig_height = $ph->getHeight(); @@ -1052,7 +1100,7 @@ function scale_external_images($s, $include_link = true, $scale_replace = false) logger('scale_external_images: ' . $orig_width . '->' . $new_width . 'w ' . $orig_height . '->' . $new_height . 'h' . ' match: ' . $mtch[0], LOGGER_DEBUG); $s = str_replace($mtch[0],'[img=' . $new_width . 'x' . $new_height. ']' . $scaled . '[/img]' . "\n" . (($include_link) - ? '[url=' . $mtch[1] . ']' . t('view full size') . '[/url]' . "\n" + ? '[zrl=' . $mtch[2] . ']' . t('view full size') . '[/zrl]' . "\n" : ''),$s); logger('scale_external_images: new string: ' . $s, LOGGER_DEBUG); } diff --git a/include/notifier.php b/include/notifier.php index b22f77d91..dea9d6072 100644 --- a/include/notifier.php +++ b/include/notifier.php @@ -1,4 +1,4 @@ -<?php +<?php /** @file */ require_once("boot.php"); require_once('include/queue_fn.php'); @@ -53,6 +53,7 @@ require_once('include/html2plain.php'); * * ZOT * permission_update abook_id + * refresh_all channel_id * relay item_id (item was relayed to owner, we will deliver it as owner) * */ @@ -136,6 +137,7 @@ function notifier_run($argv, $argc){ $recipients = array(); $url_recipients = array(); $normal_mode = true; + $packet_type = 'undefined'; if($cmd === 'mail') { $normal_mode = false; @@ -186,6 +188,27 @@ function notifier_run($argv, $argc){ $recipients[] = $suggest[0]['cid']; $item = $suggest[0]; } + elseif($cmd === 'refresh_all') { + logger('notifier: refresh_all: ' . $item_id); + $s = q("select * from channel where channel_id = %d limit 1", + intval($item_id) + ); + if($s) + $channel = $s[0]; + $uid = $item_id; + $recipients = array(); + $r = q("select * from abook where abook_channel = %d and not (abook_flags & %d)", + intval($item_id), + intval(ABOOK_FLAG_SELF) + ); + if($r) { + foreach($r as $rr) { + $recipients[] = $rr['abook_xchan']; + } + } + $private = false; + $packet_type = 'refresh'; + } else { // Normal items @@ -208,6 +231,16 @@ function notifier_run($argv, $argc){ if($target_item['item_restrict'] & ITEM_DELETED) logger('notifier: target item ITEM_DELETED', LOGGER_DEBUG); + if($target_item['item_restrict'] & ITEM_DELAYED_PUBLISH) { + logger('notifier: target item ITEM_DELAYED_PUBLISH', LOGGER_DEBUG); + return; + } + + if($target_item['item_restrict'] & ITEM_WEBPAGE) { + logger('notifier: target item ITEM_WEBPAGE', LOGGER_DEBUG); + return; + } + $s = q("select * from channel where channel_id = %d limit 1", intval($target_item['uid']) @@ -295,24 +328,35 @@ function notifier_run($argv, $argc){ // Generic delivery section, we have an encoded item and recipients // Now start the delivery process - logger('notifier: encoded item: ' . print_r($encoded_item,true)); + $x = $encoded_item; + $x['title'] = 'private'; + $x['body'] = 'private'; + logger('notifier: encoded item: ' . print_r($x,true), LOGGER_DATA); stringify_array_elms($recipients); if(! $recipients) return; - logger('notifier: recipients: ' . print_r($recipients,true)); +// logger('notifier: recipients: ' . print_r($recipients,true)); - $env_recips = null; - if($private) { - $r = q("select xchan_guid, xchan_guid_sig from xchan where xchan_hash in (" . implode(',',$recipients) . ")"); - if($r) { - $env_recips = array(); - foreach($r as $rr) - $env_recips[] = array('guid' => $rr['xchan_guid'],'guid_sig' => $rr['xchan_guid_sig']); + $env_recips = (($private) ? array() : null); + + $details = q("select xchan_hash, xchan_addr, xchan_guid, xchan_guid_sig from xchan where xchan_hash in (" . implode(',',$recipients) . ")"); + + $recip_list = array(); + + if($details) { + foreach($details as $d) { + $recip_list[] = $d['xchan_addr'] . ' (' . $d['xchan_hash'] . ')'; + if($private) + $env_recips[] = array('guid' => $d['xchan_guid'],'guid_sig' => $d['xchan_guid_sig']); } } + + logger('notifier: recipients (may be delivered to more if public): ' . print_r($recip_list,true), LOGGER_DEBUG); + + // Now we have collected recipients (except for external mentions, FIXME) // Let's reduce this to a set of hubs. @@ -321,14 +365,19 @@ function notifier_run($argv, $argc){ $sql_extra = (($private) ? "" : " or hubloc_url = '" . z_root() . "' "); - $r = q("select distinct(hubloc_callback),hubloc_host,hubloc_sitekey from hubloc - where hubloc_hash in (" . implode(',',$recipients) . ") $sql_extra group by hubloc_callback"); + $r = q("select distinct hubloc_sitekey, hubloc_callback, hubloc_host from hubloc + where hubloc_hash in (" . implode(',',$recipients) . ") $sql_extra group by hubloc_sitekey"); if(! $r) { logger('notifier: no hubs'); return; } $hubs = $r; + $hublist = array(); + foreach($hubs as $hub) + $hublist[] = $hub['hubloc_host']; + + logger('notifier: will notify/deliver to these hubs: ' . print_r($hublist,true), LOGGER_DEBUG); $interval = ((get_config('system','delivery_interval') !== false) ? intval(get_config('system','delivery_interval')) : 2 ); @@ -342,18 +391,34 @@ function notifier_run($argv, $argc){ foreach($hubs as $hub) { $hash = random_string(); - $n = zot_build_packet($channel,'notify',$env_recips,(($private) ? $hub['hubloc_sitekey'] : null),$hash); - q("insert into outq ( outq_hash, outq_account, outq_channel, outq_posturl, outq_async, outq_created, outq_updated, outq_notify, outq_msg ) values ( '%s', %d, %d, '%s', %d, '%s', '%s', '%s', '%s' )", - dbesc($hash), - intval($target_item['aid']), - intval($target_item['uid']), - dbesc($hub['hubloc_callback']), - intval(1), - dbesc(datetime_convert()), - dbesc(datetime_convert()), - dbesc($n), - dbesc(json_encode($encoded_item)) - ); + if($packet_type === 'refresh') { + $n = zot_build_packet($channel,'refresh'); + q("insert into outq ( outq_hash, outq_account, outq_channel, outq_posturl, outq_async, outq_created, outq_updated, outq_notify, outq_msg ) values ( '%s', %d, %d, '%s', %d, '%s', '%s', '%s', '%s' )", + dbesc($hash), + intval($channel['channel_account']), + intval($channel['channel_id']), + dbesc($hub['hubloc_callback']), + intval(1), + dbesc(datetime_convert()), + dbesc(datetime_convert()), + dbesc($n), + dbesc('') + ); + } + else { + $n = zot_build_packet($channel,'notify',$env_recips,(($private) ? $hub['hubloc_sitekey'] : null),$hash); + q("insert into outq ( outq_hash, outq_account, outq_channel, outq_posturl, outq_async, outq_created, outq_updated, outq_notify, outq_msg ) values ( '%s', %d, %d, '%s', %d, '%s', '%s', '%s', '%s' )", + dbesc($hash), + intval($target_item['aid']), + intval($target_item['uid']), + dbesc($hub['hubloc_callback']), + intval(1), + dbesc(datetime_convert()), + dbesc(datetime_convert()), + dbesc($n), + dbesc(json_encode($encoded_item)) + ); + } $deliver[] = $hash; if(count($deliver) >= $deliveries_per_process) { @@ -369,12 +434,16 @@ function notifier_run($argv, $argc){ if(count($deliver)) { proc_run('php','include/deliver.php',$deliver); } + + logger('notifier: basic loop complete.', LOGGER_DEBUG); if($normal_mode) call_hooks('notifier_normal',$target_item); + call_hooks('notifier_end',$target_item); + logger('notifer: complete.'); return; } diff --git a/include/notify.php b/include/notify.php index 3bfd1e58c..aa96fa279 100644 --- a/include/notify.php +++ b/include/notify.php @@ -1,4 +1,4 @@ -<?php +<?php /** @file */ function format_notification($item) { diff --git a/include/oauth.php b/include/oauth.php index 99fc16eef..2f70f21fb 100644 --- a/include/oauth.php +++ b/include/oauth.php @@ -1,4 +1,4 @@ -<?php +<?php /** @file */ /** * OAuth server * Based on oauth2-php <http://code.google.com/p/oauth2-php/> diff --git a/include/oembed.php b/include/oembed.php index 6fc4c5371..04a40f6ee 100755 --- a/include/oembed.php +++ b/include/oembed.php @@ -1,4 +1,4 @@ -<?php +<?php /** @file */ function oembed_replacecb($matches){ // logger('oembedcb'); $embedurl=$matches[1]; @@ -29,7 +29,11 @@ function oembed_fetch_url($embedurl){ if (!in_array($ext, $noexts)){ // try oembed autodiscovery $redirects = 0; - $html_text = fetch_url($embedurl, false, $redirects, 15, "text/*"); + + $result = z_fetch_url($embedurl, false, $redirects, array('timeout' => 15, 'accept_content' => "text/*", 'novalidate' => true )); + if($result['success']) + $html_text = $result['body']; + if($html_text){ $dom = @DOMDocument::loadHTML($html_text); if ($dom){ @@ -47,10 +51,17 @@ function oembed_fetch_url($embedurl){ } } - if ($txt==false || $txt==""){ + if ($txt==false || $txt=="") { + $x = array('url' => $embedurl,'videowidth' => $a->videowidth); + call_hooks('oembed_probe',$x); + if(array_key_exists('embed',$x)) + $txt = $x['embed']; + // try oohembed service - $ourl = "http://oohembed.com/oohembed/?url=".urlencode($embedurl).'&maxwidth=' . $a->videowidth; - $txt = fetch_url($ourl); +// $ourl = "http://oohembed.com/oohembed/?url=".urlencode($embedurl).'&maxwidth=' . $a->videowidth; +// $result = z_fetch_url($ourl); +// if($result['success']) +// $txt = $result['body']; } $txt=trim($txt); diff --git a/include/onepoll.php b/include/onepoll.php index 8b0c5211b..a225edfd8 100644 --- a/include/onepoll.php +++ b/include/onepoll.php @@ -1,4 +1,4 @@ -<?php +<?php /** @file */ require_once('boot.php'); require_once('include/cli_startup.php'); diff --git a/include/permissions.php b/include/permissions.php index 72e002ace..e7f50ceeb 100644 --- a/include/permissions.php +++ b/include/permissions.php @@ -1,4 +1,4 @@ -<?php +<?php /** @file */ function get_perms() { @@ -23,7 +23,7 @@ function get_perms() { 'post_comments' => array('channel_w_comment', intval(PERMS_W_COMMENT), false, t('Can comment on my posts'), ''), 'post_mail' => array('channel_w_mail', intval(PERMS_W_MAIL), false, t('Can send me private mail messages'), ''), 'post_photos' => array('channel_w_photos', intval(PERMS_W_PHOTOS), false, t('Can post photos to my photo albums'), ''), - 'tag_deliver' => array('channel_w_tagwall', intval(PERMS_W_TAGWALL), false, t('Can forward to all my channel contacts via post tags'), t('Advanced - useful for creating group forum channels')), + 'tag_deliver' => array('channel_w_tagwall', intval(PERMS_W_TAGWALL), false, t('Can forward to all my channel contacts via post @mentions'), t('Advanced - useful for creating group forum channels')), 'chat' => array('channel_w_chat', intval(PERMS_W_CHAT), false, t('Can chat with me (when available)'), t('Requires compatible chat plugin')), 'write_storage' => array('channel_w_storage', intval(PERMS_W_STORAGE), false, t('Can write to my "public" file storage'), ''), 'write_pages' => array('channel_w_pages', intval(PERMS_W_PAGES), false, t('Can edit my "public" pages'), ''), @@ -168,6 +168,13 @@ function get_all_perms($uid,$observer_xchan,$internal_use = true) { $ret[$perm_name] = false; continue; } + + // They are in your address book, but haven't been approved + + if($x[0]['abook_flags'] & ABOOK_FLAG_PENDING) { + $ret[$perm_name] = false; + continue; + } if(($r) && ($r[0][$channel_perm] & PERMS_CONTACTS)) { @@ -192,6 +199,8 @@ function get_all_perms($uid,$observer_xchan,$internal_use = true) { continue; } + + $arr = array( 'channel_id' => $uid, 'observer_hash' => $observer_xchan, @@ -282,6 +291,10 @@ function perm_is_allowed($uid,$observer_xchan,$permission) { return false; } + if($x[0]['abook_flags'] & ABOOK_FLAG_PENDING) { + return false; + } + if($r[0][$channel_perm] & PERMS_CONTACTS) { return true; } diff --git a/include/photo/photo_driver.php b/include/photo/photo_driver.php new file mode 100644 index 000000000..38210ba26 --- /dev/null +++ b/include/photo/photo_driver.php @@ -0,0 +1,640 @@ +<?php /** @file */ + +function photo_factory($data, $type = null) { + $ph = null; + + if(class_exists('Imagick')) { + require_once('include/photo/photo_imagick.php'); + $ph = new photo_imagick($data,$type); + } + else { + require_once('include/photo/photo_gd.php'); + $ph = new photo_gd($data,$type); + } + + return $ph; +} + + + + +abstract class photo_driver { + + protected $image; + protected $width; + protected $height; + protected $valid; + protected $type; + protected $types; + + abstract function supportedTypes(); + + abstract function load($data,$type); + + abstract function destroy(); + + abstract function setDimensions(); + + abstract function getImage(); + + abstract function doScaleImage($new_width,$new_height); + + abstract function rotate($degrees); + + abstract function flip($horiz = true, $vert = false); + + abstract function cropImage($max,$x,$y,$w,$h); + + abstract function imageString(); + + + public function __construct($data, $type='') { + $this->types = $this->supportedTypes(); + if (! array_key_exists($type,$this->types)){ + $type='image/jpeg'; + } + $this->type = $type; + $this->valid = false; + $this->load($data,$type); + } + + public function __destruct() { + if($this->is_valid()) + $this->destroy(); + } + + public function is_valid() { + return $this->valid; + } + + public function getWidth() { + if(!$this->is_valid()) + return FALSE; + return $this->width; + } + + public function getHeight() { + if(!$this->is_valid()) + return FALSE; + return $this->height; + } + + + public function saveImage($path) { + if(!$this->is_valid()) + return FALSE; + file_put_contents($path, $this->imageString()); + } + + + public function getType() { + if(!$this->is_valid()) + return FALSE; + + return $this->type; + } + + public function getExt() { + if(!$this->is_valid()) + return FALSE; + + return $this->types[$this->getType()]; + } + + public function scaleImage($max) { + if(!$this->is_valid()) + return FALSE; + + $width = $this->width; + $height = $this->height; + + $dest_width = $dest_height = 0; + + if((! $width)|| (! $height)) + return FALSE; + + if($width > $max && $height > $max) { + + // very tall image (greater than 16:9) + // constrain the width - let the height float. + + if((($height * 9) / 16) > $width) { + $dest_width = $max; + $dest_height = intval(( $height * $max ) / $width); + } + + // else constrain both dimensions + + elseif($width > $height) { + $dest_width = $max; + $dest_height = intval(( $height * $max ) / $width); + } + else { + $dest_width = intval(( $width * $max ) / $height); + $dest_height = $max; + } + } + else { + if( $width > $max ) { + $dest_width = $max; + $dest_height = intval(( $height * $max ) / $width); + } + else { + if( $height > $max ) { + + // very tall image (greater than 16:9) + // but width is OK - don't do anything + + if((($height * 9) / 16) > $width) { + $dest_width = $width; + $dest_height = $height; + } + else { + $dest_width = intval(( $width * $max ) / $height); + $dest_height = $max; + } + } + else { + $dest_width = $width; + $dest_height = $height; + } + } + } + $this->doScaleImage($dest_width,$dest_height); + } + + public function scaleImageUp($min) { + if(!$this->is_valid()) + return FALSE; + + + $width = $this->width; + $height = $this->height; + + $dest_width = $dest_height = 0; + + if((! $width)|| (! $height)) + return FALSE; + + if($width < $min && $height < $min) { + if($width > $height) { + $dest_width = $min; + $dest_height = intval(( $height * $min ) / $width); + } + else { + $dest_width = intval(( $width * $min ) / $height); + $dest_height = $min; + } + } + else { + if( $width < $min ) { + $dest_width = $min; + $dest_height = intval(( $height * $min ) / $width); + } + else { + if( $height < $min ) { + $dest_width = intval(( $width * $min ) / $height); + $dest_height = $min; + } + else { + $dest_width = $width; + $dest_height = $height; + } + } + } + $this->doScaleImage($dest_width,$dest_height); + } + + public function scaleImageSquare($dim) { + if(!$this->is_valid()) + return FALSE; + $this->doScaleImage($dim,$dim); + } + + + + + public function orient($filename) { + + /** + * This function is a bit unusual, because it is operating on a file, but you must + * first create an image from that file to initialise the type and check validity + * of the image. + */ + + if(! $this->is_valid()) + return FALSE; + + if((! function_exists('exif_read_data')) || ($this->getType() !== 'image/jpeg')) + return; + + $exif = @exif_read_data($filename); + if($exif) { + $ort = $exif['Orientation']; + + switch($ort) + { + case 1: // nothing + break; + + case 2: // horizontal flip + $this->flip(); + break; + + case 3: // 180 rotate left + $this->rotate(180); + break; + + case 4: // vertical flip + $this->flip(false, true); + break; + + case 5: // vertical flip + 90 rotate right + $this->flip(false, true); + $this->rotate(-90); + break; + + case 6: // 90 rotate right + $this->rotate(-90); + break; + + case 7: // horizontal flip + 90 rotate right + $this->flip(); + $this->rotate(-90); + break; + + case 8: // 90 rotate left + $this->rotate(90); + break; + } + } + } + + + public function save($arr) { + + $p = array(); + + $p['aid'] = ((intval($arr['aid'])) ? intval($arr['aid']) : 0); + $p['uid'] = ((intval($arr['uid'])) ? intval($arr['uid']) : 0); + $p['xchan'] = (($arr['xchan']) ? $arr['xchan'] : ''); + $p['resource_id'] = (($arr['resource_id']) ? $arr['resource_id'] : ''); + $p['filename'] = (($arr['filename']) ? $arr['filename'] : ''); + $p['album'] = (($arr['album']) ? $arr['album'] : ''); + $p['scale'] = ((intval($arr['scale'])) ? intval($arr['scale']) : 0); + $p['photo_flags'] = ((intval($arr['photo_flags'])) ? intval($arr['photo_flags']) : 0); + $p['allow_cid'] = (($arr['allow_cid']) ? $arr['allow_cid'] : ''); + $p['allow_gid'] = (($arr['allow_gid']) ? $arr['allow_gid'] : ''); + $p['deny_cid'] = (($arr['deny_cid']) ? $arr['deny_cid'] : ''); + $p['deny_gid'] = (($arr['deny_gid']) ? $arr['deny_gid'] : ''); + + // temporary until we get rid of photo['profile'] and just use photo['photo_flags'] + // but this will require updating all existing photos in the DB. + + $p['profile'] = (($p['photo_flags'] & PHOTO_PROFILE) ? 1 : 0); + + + $x = q("select id from photo where resource_id = '%s' and uid = %d and xchan = '%s' and `scale` = %d limit 1", + dbesc($p['resource_id']), + intval($p['uid']), + dbesc($p['xchan']), + intval($p['scale']) + ); + if($x) { + $r = q("UPDATE `photo` set + `aid` = %d, + `uid` = %d, + `xchan` = '%s', + `resource_id` = '%s', + `created` = '%s', + `edited` = '%s', + `filename` = '%s', + `type` = '%s', + `album` = '%s', + `height` = %d, + `width` = %d, + `data` = '%s', + `size` = %d, + `scale` = %d, + `profile` = %d, + `photo_flags` = %d, + `allow_cid` = '%s', + `allow_gid` = '%s', + `deny_cid` = '%s', + `deny_gid` = '%s' + where id = %d limit 1", + + intval($p['aid']), + intval($p['uid']), + dbesc($p['xchan']), + dbesc($p['resource_id']), + dbesc(datetime_convert()), + dbesc(datetime_convert()), + dbesc(basename($p['filename'])), + dbesc($this->getType()), + dbesc($p['album']), + intval($this->getHeight()), + intval($this->getWidth()), + dbesc($this->imageString()), + intval(strlen($this->imageString())), + intval($p['scale']), + intval($p['profile']), + intval($p['photo_flags']), + dbesc($p['allow_cid']), + dbesc($p['allow_gid']), + dbesc($p['deny_cid']), + dbesc($p['deny_gid']), + intval($x[0]['id']) + ); + } + else { + $r = q("INSERT INTO `photo` + ( `aid`, `uid`, `xchan`, `resource_id`, `created`, `edited`, `filename`, type, `album`, `height`, `width`, `data`, `size`, `scale`, `profile`, `photo_flags`, `allow_cid`, `allow_gid`, `deny_cid`, `deny_gid` ) + VALUES ( %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, '%s', %d, %d, %d, %d, '%s', '%s', '%s', '%s' )", + intval($p['aid']), + intval($p['uid']), + dbesc($p['xchan']), + dbesc($p['resource_id']), + dbesc(datetime_convert()), + dbesc(datetime_convert()), + dbesc(basename($filename)), + dbesc($this->getType()), + dbesc($p['album']), + intval($this->getHeight()), + intval($this->getWidth()), + dbesc($this->imageString()), + intval(strlen($this->imageString())), + intval($p['scale']), + intval($p['profile']), + intval($p['photo_flags']), + dbesc($p['allow_cid']), + dbesc($p['allow_gid']), + dbesc($p['deny_cid']), + dbesc($p['deny_gid']) + ); + } + return $r; + } + + public function store($aid, $uid, $xchan, $rid, $filename, $album, $scale, $profile = 0, $allow_cid = '', $allow_gid = '', $deny_cid = '', $deny_gid = '') { + + $x = q("select id from photo where `resource_id` = '%s' and uid = %d and `xchan` = '%s' and `scale` = %d limit 1", + dbesc($rid), + intval($uid), + dbesc($xchan), + intval($scale) + ); + if(count($x)) { + $r = q("UPDATE `photo` + set `aid` = %d, + `uid` = %d, + `xchan` = '%s', + `resource_id` = '%s', + `created` = '%s', + `edited` = '%s', + `filename` = '%s', + `type` = '%s', + `album` = '%s', + `height` = %d, + `width` = %d, + `data` = '%s', + `size` = %d, + `scale` = %d, + `profile` = %d, + `allow_cid` = '%s', + `allow_gid` = '%s', + `deny_cid` = '%s', + `deny_gid` = '%s' + where id = %d limit 1", + + intval($aid), + intval($uid), + dbesc($xchan), + dbesc($rid), + dbesc(datetime_convert()), + dbesc(datetime_convert()), + dbesc(basename($filename)), + dbesc($this->getType()), + dbesc($album), + intval($this->getHeight()), + intval($this->getWidth()), + dbesc($this->imageString()), + intval(strlen($this->imageString())), + intval($scale), + intval($profile), + dbesc($allow_cid), + dbesc($allow_gid), + dbesc($deny_cid), + dbesc($deny_gid), + intval($x[0]['id']) + ); + } + else { + $r = q("INSERT INTO `photo` + ( `aid`, `uid`, `xchan`, `resource_id`, `created`, `edited`, `filename`, type, `album`, `height`, `width`, `data`, `size`, `scale`, `profile`, `allow_cid`, `allow_gid`, `deny_cid`, `deny_gid` ) + VALUES ( %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, '%s', %d, %d, %d, '%s', '%s', '%s', '%s' )", + intval($aid), + intval($uid), + dbesc($xchan), + dbesc($rid), + dbesc(datetime_convert()), + dbesc(datetime_convert()), + dbesc(basename($filename)), + dbesc($this->getType()), + dbesc($album), + intval($this->getHeight()), + intval($this->getWidth()), + dbesc($this->imageString()), + intval(strlen($this->imageString())), + intval($scale), + intval($profile), + dbesc($allow_cid), + dbesc($allow_gid), + dbesc($deny_cid), + dbesc($deny_gid) + ); + } + return $r; + } + +} + + + + + + + + +/** + * Guess image mimetype from filename or from Content-Type header + * + * @arg $filename string Image filename + * @arg $fromcurl boolean Check Content-Type header from curl request + */ + +function guess_image_type($filename, $fromcurl=false) { + logger('Photo: guess_image_type: '.$filename . ($fromcurl?' from curl headers':''), LOGGER_DEBUG); + $type = null; + if ($fromcurl) { + $a = get_app(); + $headers=array(); + $h = explode("\n",$a->get_curl_headers()); + foreach ($h as $l) { + list($k,$v) = array_map("trim", explode(":", trim($l), 2)); + $headers[$k] = $v; + } + if (array_key_exists('Content-Type', $headers)) + $type = $headers['Content-Type']; + } + if (is_null($type)){ +// FIXME!!!! + // Guessing from extension? Isn't that... dangerous? + if(class_exists('Imagick') && file_exists($filename) && is_readable($filename)) { + /** + * Well, this not much better, + * but at least it comes from the data inside the image, + * we won't be tricked by a manipulated extension + */ + $image = new Imagick($filename); + $type = $image->getImageMimeType(); + } else { + $ext = pathinfo($filename, PATHINFO_EXTENSION); + $ph = photo_factory(''); + $types = $ph->supportedTypes(); + $type = "image/jpeg"; + foreach ($types as $m=>$e){ + if ($ext==$e) $type = $m; + } + } + } + logger('Photo: guess_image_type: type='.$type, LOGGER_DEBUG); + return $type; + +} + +function import_profile_photo($photo,$xchan) { + + $a = get_app(); + + logger('import_profile_photo: updating channel photo from ' . $photo . ' for ' . $xchan, LOGGER_DEBUG); + + $r = q("select resource_id from photo where xchan = '%s' and scale = 4 limit 1", + dbesc($xchan) + ); + if($r) { + $hash = $r[0]['resource_id']; + } + else { + $hash = photo_new_resource(); + } + + $photo_failure = false; + + + $filename = basename($photo); + $type = guess_image_type($photo,true); + $result = z_fetch_url($photo,true); + + if($result['success']) + $img_str = $result['body']; + + $img = photo_factory($img_str, $type); + if($img->is_valid()) { + + $img->scaleImageSquare(175); + + $p = array('xchan' => $xchan,'resource_id' => $hash, 'filename' => 'Contact Photos', 'photo_flags' => PHOTO_XCHAN, 'scale' => 4); + + $r = $img->save($p); + + if($r === false) + $photo_failure = true; + + $img->scaleImage(80); + $p['scale'] = 5; + + $r = $img->save($p); + + if($r === false) + $photo_failure = true; + + $img->scaleImage(48); + $p['scale'] = 6; + + $r = $img->save($p); + + if($r === false) + $photo_failure = true; + + $photo = $a->get_baseurl() . '/photo/' . $hash . '-4'; + $thumb = $a->get_baseurl() . '/photo/' . $hash . '-5'; + $micro = $a->get_baseurl() . '/photo/' . $hash . '-6'; + } + else { + logger('import_profile_photo: invalid image from ' . $photo); + $photo_failure = true; + } + if($photo_failure) { + $photo = $a->get_baseurl() . '/images/person-175.jpg'; + $thumb = $a->get_baseurl() . '/images/person-80.jpg'; + $micro = $a->get_baseurl() . '/images/person-48.jpg'; + $type = 'image/jpeg'; + } + + return(array($photo,$thumb,$micro,$type)); + +} + + + +function import_channel_photo($photo,$type,$aid,$uid) { + + $a = get_app(); + + logger('import_channel_photo: importing channel photo for ' . $uid, LOGGER_DEBUG); + + $hash = photo_new_resource(); + + $photo_failure = false; + + + $filename = $hash; + + $img = photo_factory($photo, $type); + if($img->is_valid()) { + + $img->scaleImageSquare(175); + + $p = array('aid' => $aid, 'uid' => $uid, 'resource_id' => $hash, 'filename' => $filename, 'album' => t('Profile Photos'), 'photo_flags' => PHOTO_PROFILE, 'scale' => 4); + + $r = $img->save($p); + + if($r === false) + $photo_failure = true; + + $img->scaleImage(80); + $p['scale'] = 5; + + $r = $img->save($p); + + if($r === false) + $photo_failure = true; + + $img->scaleImage(48); + $p['scale'] = 6; + + $r = $img->save($p); + + if($r === false) + $photo_failure = true; + + } + else { + logger('import_channel_photo: invalid image.'); + $photo_failure = true; + } + + return(($photo_failure)? false : true); + +} diff --git a/include/photo/photo_gd.php b/include/photo/photo_gd.php new file mode 100644 index 000000000..466f8c23a --- /dev/null +++ b/include/photo/photo_gd.php @@ -0,0 +1,140 @@ +<?php /** @file */ + + +require_once('include/photo/photo_driver.php'); + + +class photo_gd extends photo_driver { + + function supportedTypes() { + $t = array(); + $t['image/jpeg'] ='jpg'; + if (imagetypes() & IMG_PNG) $t['image/png'] = 'png'; + + return $t; + + } + + function load($data, $type) { + $this->valid = false; + if(! $data) + return; + + $this->image = @imagecreatefromstring($data); + if($this->image !== FALSE) { + $this->valid = true; + $this->setDimensions(); + imagealphablending($this->image, false); + imagesavealpha($this->image, true); + } + } + + function setDimensions() { + $this->width = imagesx($this->image); + $this->height = imagesy($this->image); + } + + + public function destroy() { + if($this->is_valid()) { + imagedestroy($this->image); + } + } + + public function getImage() { + if(!$this->is_valid()) + return FALSE; + + return $this->image; + } + + public function doScaleImage($dest_width,$dest_height) { + + $dest = imagecreatetruecolor( $dest_width, $dest_height ); + $width = imagesx($this->image); + $height = imagesy($this->image); + + imagealphablending($dest, false); + imagesavealpha($dest, true); + if ($this->type=='image/png') imagefill($dest, 0, 0, imagecolorallocatealpha($dest, 0, 0, 0, 127)); // fill with alpha + imagecopyresampled($dest, $this->image, 0, 0, 0, 0, $dest_width, $dest_height, $width, $height); + if($this->image) + imagedestroy($this->image); + $this->image = $dest; + $this->setDimensions(); + } + + public function rotate($degrees) { + if(!$this->is_valid()) + return FALSE; + + $this->image = imagerotate($this->image,$degrees,0); + $this->setDimensions(); + } + + public function flip($horiz = true, $vert = false) { + if(!$this->is_valid()) + return FALSE; + + $w = imagesx($this->image); + $h = imagesy($this->image); + $flipped = imagecreate($w, $h); + if($horiz) { + for ($x = 0; $x < $w; $x++) { + imagecopy($flipped, $this->image, $x, 0, $w - $x - 1, 0, 1, $h); + } + } + if($vert) { + for ($y = 0; $y < $h; $y++) { + imagecopy($flipped, $this->image, 0, $y, 0, $h - $y - 1, $w, 1); + } + } + $this->image = $flipped; + $this->setDimensions(); // Shouldn't really be necessary + } + + public function cropImage($max,$x,$y,$w,$h) { + if(!$this->is_valid()) + return FALSE; + + $dest = imagecreatetruecolor( $max, $max ); + imagealphablending($dest, false); + imagesavealpha($dest, true); + if ($this->type=='image/png') imagefill($dest, 0, 0, imagecolorallocatealpha($dest, 0, 0, 0, 127)); // fill with alpha + imagecopyresampled($dest, $this->image, 0, 0, $x, $y, $max, $max, $w, $h); + if($this->image) + imagedestroy($this->image); + $this->image = $dest; + $this->setDimensions(); + } + + public function imageString() { + if(!$this->is_valid()) + return FALSE; + + $quality = FALSE; + + ob_start(); + + switch($this->getType()){ + case "image/png": + $quality = get_config('system','png_quality'); + if((! $quality) || ($quality > 9)) + $quality = PNG_QUALITY; + imagepng($this->image,NULL, $quality); + break; + case "image/jpeg": + default: + $quality = get_config('system','jpeg_quality'); + if((! $quality) || ($quality > 100)) + $quality = JPEG_QUALITY; + imagejpeg($this->image,NULL,$quality); + break; + } + $string = ob_get_contents(); + ob_end_clean(); + + return $string; + } + +}
\ No newline at end of file diff --git a/include/photo/photo_imagick.php b/include/photo/photo_imagick.php new file mode 100644 index 000000000..3f84fd06c --- /dev/null +++ b/include/photo/photo_imagick.php @@ -0,0 +1,177 @@ +<?php /** @file */ + + +require_once('include/photo/photo_driver.php'); + + +class photo_imagick extends photo_driver { + + + function supportedTypes() { + return array( + 'image/jpeg' => 'jpg', + 'image/png' => 'png', + 'image/gif' => 'gif' + ); + } + + public function get_FormatsMap() { + return array( + 'image/jpeg' => 'JPG', + 'image/png' => 'PNG', + 'image/gif' => 'GIF' + ); + } + + + function load($data, $type) { + $this->valid = false; + $this->image = new Imagick(); + + if(! $data) + return; + + $this->image->readImageBlob($data); + + + /** + * Setup the image to the format it will be saved to + */ + + $map = $this->get_FormatsMap(); + $format = $map[$type]; + + if($this->image) { + $this->image->setFormat($format); + + // Always coalesce, if it is not a multi-frame image it won't hurt anyway + $this->image = $this->image->coalesceImages(); + + + $this->valid = true; + $this->setDimensions(); + + /** + * setup the compression here, so we'll do it only once + */ + switch($this->getType()) { + case "image/png": + $quality = get_config('system','png_quality'); + if((! $quality) || ($quality > 9)) + $quality = PNG_QUALITY; + /** + * From http://www.imagemagick.org/script/command-line-options.php#quality: + * + * 'For the MNG and PNG image formats, the quality value sets + * the zlib compression level (quality / 10) and filter-type (quality % 10). + * The default PNG "quality" is 75, which means compression level 7 with adaptive PNG filtering, + * unless the image has a color map, in which case it means compression level 7 with no PNG filtering' + */ + $quality = $quality * 10; + $this->image->setCompressionQuality($quality); + break; + case "image/jpeg": + $quality = get_config('system','jpeg_quality'); + if((! $quality) || ($quality > 100)) + $quality = JPEG_QUALITY; + $this->image->setCompressionQuality($quality); + default: + break; + + } + } + } + + public function destroy() { + if($this->is_valid()) { + $this->image->clear(); + $this->image->destroy(); + } + } + + + public function setDimensions() { + $this->width = $this->image->getImageWidth(); + $this->height = $this->image->getImageHeight(); + } + + + public function getImage() { + if(!$this->is_valid()) + return FALSE; + + $this->image = $this->image->deconstructImages(); + return $this->image; + } + + public function doScaleImage($dest_width,$dest_height) { + + /** + * If it is not animated, there will be only one iteration here, + * so don't bother checking + */ + // Don't forget to go back to the first frame + $this->image->setFirstIterator(); + do { + $this->image->scaleImage($dest_width, $dest_height); + } while ($this->image->nextImage()); + + $this->setDimensions(); + } + + public function rotate($degrees) { + if(!$this->is_valid()) + return FALSE; + + $this->image->setFirstIterator(); + do { + // ImageMagick rotates in the opposite direction of imagerotate() + $this->image->rotateImage(new ImagickPixel(), -$degrees); + } while ($this->image->nextImage()); + + $this->setDimensions(); + } + + public function flip($horiz = true, $vert = false) { + if(!$this->is_valid()) + return FALSE; + + $this->image->setFirstIterator(); + do { + if($horiz) $this->image->flipImage(); + if($vert) $this->image->flopImage(); + } while ($this->image->nextImage()); + + $this->setDimensions(); // Shouldn't really be necessary + } + + public function cropImage($max,$x,$y,$w,$h) { + if(!$this->is_valid()) + return FALSE; + + $this->image->setFirstIterator(); + do { + $this->image->cropImage($w, $h, $x, $y); + /** + * We need to remove the canvas, + * or the image is not resized to the crop: + * http://php.net/manual/en/imagick.cropimage.php#97232 + */ + $this->image->setImagePage(0, 0, 0, 0); + } while ($this->image->nextImage()); + + $this->doScaleImage($max,$max); + } + + public function imageString() { + if(!$this->is_valid()) + return FALSE; + + /* Clean it */ + $this->image = $this->image->deconstructImages(); + return $this->image->getImagesBlob(); + } + + + +}
\ No newline at end of file diff --git a/include/photos.php b/include/photos.php index 885d2f958..c670bd90c 100644 --- a/include/photos.php +++ b/include/photos.php @@ -1,7 +1,9 @@ -<?php +<?php /** @file */ require_once('include/permissions.php'); require_once('include/items.php'); +require_once('include/photo/photo_driver.php'); + function photo_upload($channel, $observer, $args) { @@ -116,13 +118,13 @@ function photo_upload($channel, $observer, $args) { } - $ph = new Photo($imagedata, $type); + $ph = photo_factory($imagedata, $type); if(! $ph->is_valid()) { $ret['message'] = t('Unable to process image'); logger('photo_upload: unable to process image'); @unlink($src); - call_hooks('photo_post_end',$ret); + call_hooks('photo_upload_end',$ret); return $ret; } @@ -148,13 +150,20 @@ function photo_upload($channel, $observer, $args) { $errors = false; - $r1 = $ph->store($account_id, $channel_id, $visitor, $photo_hash, $filename, $album, 0 , 0, $str_contact_allow, $str_group_allow, $str_contact_deny, $str_group_deny); + $p = array('aid' => $account_id, 'uid' => $channel_id, 'xchan' => $visitor, 'resource_id' => $photo_hash, + 'filename' => $filename, 'album' => $album, 'scale' => 0, 'photo_flags' => PHOTO_NORMAL, + 'allow_cid' => $str_contact_allow, 'allow_gid' => $str_group_allow, + 'deny_cid' => $str_contact_deny, 'deny_gid' => $str_group_deny + ); + + $r1 = $ph->save($p); if(! $r1) $errors = true; if(($width > 640 || $height > 640) && (! $errors)) { $ph->scaleImage(640); - $r2 = $ph->store($account_id, $channel_id, $visitor, $photo_hash, $filename, $album, 1, 0, $str_contact_allow, $str_group_allow, $str_contact_deny, $str_group_deny); + $p['scale'] = 1; + $r2 = $ph->save($p); $smallest = 1; if(! $r2) $errors = true; @@ -162,7 +171,8 @@ function photo_upload($channel, $observer, $args) { if(($width > 320 || $height > 320) && (! $errors)) { $ph->scaleImage(320); - $r3 = $ph->store($account_id, $channel_id, $visitor, $photo_hash, $filename, $album, 2, 0, $str_contact_allow, $str_group_allow, $str_contact_deny, $str_group_deny); + $p['scale'] = 2; + $r3 = $ph->save($p); $smallest = 2; if(! $r3) $errors = true; @@ -175,26 +185,26 @@ function photo_upload($channel, $observer, $args) { ); $ret['message'] = t('Photo storage failed.'); logger('photo_upload: photo store failed.'); - call_hooks('photo_post_end',$ret); + call_hooks('photo_upload_end',$ret); return $ret; } $basename = basename($filename); - $uri = item_message_id(); + $mid = item_message_id(); // Create item container $item_flags = ITEM_WALL|ITEM_ORIGIN|ITEM_THREAD_TOP; $item_restrict = (($visible) ? ITEM_VISIBLE : ITEM_HIDDEN); $title = ''; - $uri = item_message_id(); + $mid = item_message_id(); $arr = array(); $arr['aid'] = $account_id; $arr['uid'] = $channel_id; - $arr['uri'] = $uri; - $arr['parent_uri'] = $uri; + $arr['mid'] = $mid; + $arr['parent_mid'] = $mid; $arr['item_flags'] = $item_flags; $arr['item_restrict'] = $item_restrict; $arr['resource_type'] = 'photo'; @@ -208,9 +218,9 @@ function photo_upload($channel, $observer, $args) { $arr['deny_gid'] = $str_group_deny; - $arr['body'] = '[url=' . z_root() . '/photos/' . $channel['channel_address'] . '/image/' . $photo_hash . ']' - . '[img]' . z_root() . "/photo/{$photo_hash}-{$smallest}.".$ph->getExt() . '[/img]' - . '[/url]'; + $arr['body'] = '[zrl=' . z_root() . '/photos/' . $channel['channel_address'] . '/image/' . $photo_hash . ']' + . '[zmg]' . z_root() . "/photo/{$photo_hash}-{$smallest}.".$ph->getExt() . '[/zmg]' + . '[/zrl]'; $item_id = item_store($arr); @@ -222,7 +232,7 @@ function photo_upload($channel, $observer, $args) { $ret['resource_id'] = $photo_hash; $ret['photoitem_id'] = $item_id; - call_hooks('photo_post_end',$ret); + call_hooks('photo_upload_end',$ret); return $ret; } @@ -242,19 +252,25 @@ function photos_albums_list($channel,$observer) { $sql_extra = permissions_sql($channel_id); - $albums = q("SELECT distinct album from photo where uid = %d $sql_extra order by created desc", - intval($channel_id) + $albums = q("SELECT distinct album from photo where uid = %d and ( photo_flags = %d or photo_flags = %d ) $sql_extra order by created desc", + intval($channel_id), + intval(PHOTO_NORMAL), + intval(PHOTO_PROFILE) + ); // add various encodings to the array so we can just loop through and pick them out in a template + $ret = array('success' => false); + if($albums) { + $ret['success'] = true; foreach($albums as $k => $album) { - $albums[$k]['urlencode'] = urlencode($album['album']); - $albums[$k]['bin2hex'] = bin2hex($album['album']); + $entry = array('text' => $album['album'], 'urlencode' => urlencode($album['album']),'bin2hex' => bin2hex($album['album'])); + $ret[] = $entry; } } - return $albums; + return $ret; } @@ -278,6 +294,38 @@ function photos_album_widget($channelx,$observer,$albums = null) { return $o; } + +function photos_list_photos($channel,$observer,$album = '') { + + $channel_id = $channel['channel_id']; + $observer_xchan = (($observer) ? $observer['xchan_hash'] : ''); + + if(! perm_is_allowed($channel_id,$observer_xchan,'view_photos')) + return false; + + $sql_extra = permissions_sql($channel_id); + + if($album) + $sql_extra .= " and album = '" . protect_sprintf(dbesc($album)) . "' "; + + $ret = array('success' => false); + + $r = q("select resource_id, created, edited, title, `desc`, album, filename, `type`, height, width, `size`, `scale`, profile, photo_flags, allow_cid, allow_gid, deny_cid, deny_gid from photo where uid = %d and ( photo_flags = %d or photo_flags = %d ) $sql_extra ", + intval($channel_id), + intval(PHOTO_NORMAL), + intval(PHOTO_PROFILE) + ); + + if($r) { + $ret['success'] = true; + $ret['photos'] = $r; + } + + return $ret; +} + + + function photos_album_exists($channel_id,$album) { $r = q("SELECT id from photo where album = '%s' and uid = %d limit 1", dbesc($album), @@ -330,14 +378,14 @@ function photos_create_item($channel, $creator_hash, $photo, $visible = false) { $item_restrict = (($visible) ? ITEM_HIDDEN : ITEM_VISIBLE); $title = ''; - $uri = item_message_id(); + $mid = item_message_id(); $arr = array(); $arr['aid'] = $channel['channel_account_id']; $arr['uid'] = $channel['channel_id']; - $arr['uri'] = $uri; - $arr['parent_uri'] = $uri; + $arr['mid'] = $mid; + $arr['parent_mid'] = $mid; $arr['item_flags'] = $item_flags; $arr['item_restrict'] = $item_restrict; $arr['resource_type'] = 'photo'; @@ -350,9 +398,9 @@ function photos_create_item($channel, $creator_hash, $photo, $visible = false) { $arr['deny_cid'] = $photo['deny_cid']; $arr['deny_gid'] = $photo['deny_gid']; - $arr['body'] = '[url=' . z_root() . '/photos/' . $channel['channel_address'] . '/image/' . $photo['resource_id'] . ']' - . '[img]' . z_root() . '/photo/' . $photo['resource_id'] . '-' . $photo['scale'] . '[/img]' - . '[/url]'; + $arr['body'] = '[zrl=' . z_root() . '/photos/' . $channel['channel_address'] . '/image/' . $photo['resource_id'] . ']' + . '[zmg]' . z_root() . '/photo/' . $photo['resource_id'] . '-' . $photo['scale'] . '[/zmg]' + . '[/zrl]'; $item_id = item_store($arr); return $item_id; diff --git a/include/plugin.php b/include/plugin.php index 6660fba59..01ee99786 100644..100755 --- a/include/plugin.php +++ b/include/plugin.php @@ -1,26 +1,43 @@ -<?php +<?php /** @file */ require_once("include/friendica_smarty.php"); // install and uninstall plugin -if (! function_exists('uninstall_plugin')){ -function uninstall_plugin($plugin){ - logger("Addons: uninstalling " . $plugin, LOGGER_DEBUG); - q("DELETE FROM `addon` WHERE `name` = '%s' ", - dbesc($plugin) - ); + +function unload_plugin($plugin){ + logger("Addons: unloading " . $plugin, LOGGER_DEBUG); @include_once('addon/' . $plugin . '/' . $plugin . '.php'); + if(function_exists($plugin . '_unload')) { + $func = $plugin . '_unload'; + $func(); + } +} + + + +function uninstall_plugin($plugin) { + + unload_plugin($plugin); + + if(! file_exists('addon/' . $plugin . '/' . $plugin . '.php')) + return false; + + logger("Addons: uninstalling " . $plugin); + $t = @filemtime('addon/' . $plugin . '/' . $plugin . '.php'); + @include_once('addon/' . $plugin . '/' . $plugin . '.php'); if(function_exists($plugin . '_uninstall')) { $func = $plugin . '_uninstall'; $func(); } -}} -if (! function_exists('install_plugin')){ -function install_plugin($plugin) { - // silently fail if plugin was removed + q("DELETE FROM `addon` WHERE `name` = '%s' ", + dbesc($plugin) + ); +} + +function install_plugin($plugin) { if(! file_exists('addon/' . $plugin . '/' . $plugin . '.php')) return false; @@ -30,14 +47,34 @@ function install_plugin($plugin) { if(function_exists($plugin . '_install')) { $func = $plugin . '_install'; $func(); + } + + $plugin_admin = (function_exists($plugin . "_plugin_admin") ? 1 : 0); - $plugin_admin = (function_exists($plugin . "_plugin_admin") ? 1 : 0); + $r = q("INSERT INTO `addon` (`name`, `installed`, `timestamp`, `plugin_admin`) VALUES ( '%s', 1, %d , %d ) ", + dbesc($plugin), + intval($t), + $plugin_admin + ); + + load_plugin($plugin); + +} + + +function load_plugin($plugin) { + // silently fail if plugin was removed + + if(! file_exists('addon/' . $plugin . '/' . $plugin . '.php')) + return false; + + logger("Addons: loading " . $plugin); + $t = @filemtime('addon/' . $plugin . '/' . $plugin . '.php'); + @include_once('addon/' . $plugin . '/' . $plugin . '.php'); + if(function_exists($plugin . '_load')) { + $func = $plugin . '_load'; + $func(); - $r = q("INSERT INTO `addon` (`name`, `installed`, `timestamp`, `plugin_admin`) VALUES ( '%s', 1, %d , %d ) ", - dbesc($plugin), - intval($t), - $plugin_admin - ); // we can add the following with the previous SQL // once most site tables have been updated. @@ -51,15 +88,14 @@ function install_plugin($plugin) { return true; } else { - logger("Addons: FAILED installing " . $plugin); + logger("Addons: FAILED loading " . $plugin); return false; } -}} +} // reload all updated plugins -if(! function_exists('reload_plugins')) { function reload_plugins() { $plugins = get_config('system','addon'); if(strlen($plugins)) { @@ -86,12 +122,12 @@ function reload_plugins() { logger('Reloading plugin: ' . $i['name']); @include_once($fname); - if(function_exists($pl . '_uninstall')) { - $func = $pl . '_uninstall'; + if(function_exists($pl . '_unload')) { + $func = $pl . '_unload'; $func(); } - if(function_exists($pl . '_install')) { - $func = $pl . '_install'; + if(function_exists($pl . '_load')) { + $func = $pl . '_load'; $func(); } q("UPDATE `addon` SET `timestamp` = %d WHERE `id` = %d LIMIT 1", @@ -104,14 +140,13 @@ function reload_plugins() { } } } - -}} +} -if(! function_exists('register_hook')) { + function register_hook($hook,$file,$function,$priority=0) { $r = q("SELECT * FROM `hook` WHERE `hook` = '%s' AND `file` = '%s' AND `function` = '%s' LIMIT 1", @@ -129,18 +164,18 @@ function register_hook($hook,$file,$function,$priority=0) { dbesc($priority) ); return $r; -}} +} + -if(! function_exists('unregister_hook')) { function unregister_hook($hook,$file,$function) { - $r = q("DELETE FROM `hook` WHERE `hook` = '%s' AND `file` = '%s' AND `function` = '%s' LIMIT 1", + $r = q("DELETE FROM hook WHERE hook = '%s' AND `file` = '%s' AND `function` = '%s' LIMIT 1", dbesc($hook), dbesc($file), dbesc($function) ); return $r; -}} +} // @@ -148,22 +183,22 @@ function unregister_hook($hook,$file,$function) { // array in their theme_init() and use this to customise the app behaviour. // -if(! function_exists('load_hooks')) { + function load_hooks() { $a = get_app(); $a->hooks = array(); - $r = q("SELECT * FROM `hook` WHERE 1 ORDER BY `priority` DESC"); - if(count($r)) { + $r = q("SELECT * FROM hook WHERE true ORDER BY priority DESC"); + if($r) { foreach($r as $rr) { if(! array_key_exists($rr['hook'],$a->hooks)) $a->hooks[$rr['hook']] = array(); $a->hooks[$rr['hook']][] = array($rr['file'],$rr['function']); } } -}} +} + -if(! function_exists('call_hooks')) { function call_hooks($name, &$data = null) { $a = get_app(); @@ -185,7 +220,7 @@ function call_hooks($name, &$data = null) { } } -}} +} /* @@ -201,7 +236,7 @@ function call_hooks($name, &$data = null) { * * */ -if (! function_exists('get_plugin_info')){ + function get_plugin_info($plugin){ $info=Array( 'name' => $plugin, @@ -241,7 +276,7 @@ function get_plugin_info($plugin){ } return $info; -}} +} /* @@ -257,7 +292,7 @@ function get_plugin_info($plugin){ * * */ -if (! function_exists('get_theme_info')){ + function get_theme_info($theme){ $info=Array( 'name' => $theme, @@ -316,7 +351,7 @@ function get_theme_info($theme){ } return $info; -}} +} function get_theme_screenshot($theme) { @@ -397,7 +432,7 @@ function upgrade_link($bbcode = false) { if(! $l) return ''; if($bbcode) - $t = sprintf('[url=%s]' . t('Click here to upgrade.') . '[/url]', $l); + $t = sprintf('[zrl=%s]' . t('Click here to upgrade.') . '[/zrl]', $l); else $t = sprintf('<a href="%s">' . t('Click here to upgrade.') . '</div>', $l); return $t; @@ -503,55 +538,21 @@ function theme_include($file, $root = '') { -if(! function_exists('get_intltext_template')) { -function get_intltext_template($s) { - global $a; - if(! isset($a->language)) - $a->language = 'en'; +function get_intltext_template($s, $root = '') { + $a = get_app(); + $t = $a->template_engine(); - $engine = ''; - if($a->get_template_engine() === 'smarty3') - $engine = "/smarty3"; + $template = $t->get_intltext_template($s, $root); + return $template; - $file = ''; - if(file_exists("view/{$a->language}$engine/$s")) - $file = "view/{$a->language}$engine/$s"; - elseif(file_exists("view/en$engine/$s")) - $file = "view/en$engine/$s"; - else - $file = "view/tpl/$engine/$s"; - if($engine === '/smarty3') { - $template = new FriendicaSmarty(); - $template->filename = $file; - - return $template; - } - else - return file_get_contents($file); +} -}} -if(! function_exists('get_markup_template')) { function get_markup_template($s, $root = '') { - $a = get_app(); - - $template_eng = $a->get_template_engine(); - if($template_eng === 'internal') { - $template_file = theme_include($s, $root); - if($template_file) - return file_get_contents($template_file); - } - else { - $template_file = theme_include("$template_eng/$s", $root); - - if($template_file) { - $template = new FriendicaSmarty(); - $template->filename = $template_file; - - return $template; - } - } -}} + $t = $a->template_engine(); + $template = $t->get_markup_template($s, $root); + return $template; +} diff --git a/include/poller.php b/include/poller.php index ef4b93fe7..00914a712 100644 --- a/include/poller.php +++ b/include/poller.php @@ -1,4 +1,4 @@ -<?php +<?php /** @file */ require_once('boot.php'); require_once('include/cli_startup.php'); @@ -30,14 +30,32 @@ function poller_run($argv, $argc){ // expire any expired accounts q("UPDATE account - SET account_flags = account_flags | %d - where not account_flags & %d + SET account_flags = (account_flags | %d) + where not (account_flags & %d) and account_expires != '0000-00-00 00:00:00' and account_expires < UTC_TIMESTAMP() ", intval(ACCOUNT_EXPIRED), intval(ACCOUNT_EXPIRED) ); + // publish any applicable items that were set to be published in the future + // (time travel posts) + + $r = q("select id from item where ( item_restrict & %d ) and created <= UTC_TIMESTAMP() ", + intval(ITEM_DELAYED_PUBLISH) + ); + if($r) { + foreach($r as $rr) { + $x = q("update item set item_restrict = ( item_restrict ^ %d ) where id = %d limit 1", + intval(ITEM_DELAYED_PUBLISH), + intval($rr['id']) + ); + if($x) { + proc_run('php','include/notifer.php','wall-new',$rr['id']); + } + } + } + $abandon_days = intval(get_config('system','account_abandon_days')); if($abandon_days < 1) $abandon_days = 0; @@ -45,15 +63,51 @@ function poller_run($argv, $argc){ // once daily run birthday_updates and then expire in background + // FIXME: add birthday updates, both locally and for xprof for use + // by directory servers + $d1 = get_config('system','last_expire_day'); $d2 = intval(datetime_convert('UTC','UTC','now','d')); if($d2 != intval($d1)) { -// update_suggestions(); + // If this is a directory server, request a sync with an upstream + // directory at least once a day, up to once every poll interval. + // Pull remote changes and push local changes. + // potential issue: how do we keep from creating an endless update loop? + + $dirmode = get_config('system','directory_mode'); + if($dirmode == DIRECTORY_MODE_SECONDARY || $dirmode == DIRECTORY_MODE_PRIMARY) { + require_once('include/dir_fns.php'); + sync_directories($dirmode); + } + set_config('system','last_expire_day',$d2); proc_run('php','include/expire.php'); + + proc_run('php','include/cli_suggest.php'); + + } + + // update any photos which didn't get imported properly + // This should be rare + + $r = q("select xchan_photo_l, xchan_hash from xchan where xchan_photo_l != '' and xchan_photo_m = '' + and xchan_photo_date < UTC_TIMESTAMP() - INTERVAL 1 DAY"); + if($r) { + require_once('include/photo/photo_driver.php'); + foreach($r as $rr) { + $photos = import_profile_photo($rr['xchan_photo_l'],$rr['xchan_hash']); + $x = q("update xchan set xchan_photo_l = '%s', xchan_photo_m = '%s', xchan_photo_s = '%s', xchan_photo_mimetype = '%s' + where xchan_hash = '%s' limit 1", + dbesc($photos[0]), + dbesc($photos[1]), + dbesc($photos[2]), + dbesc($photos[3]), + dbesc($rr['xchan_hash']) + ); + } } diff --git a/include/profile_advanced.php b/include/profile_advanced.php index 749c79a3b..21606185d 100644 --- a/include/profile_advanced.php +++ b/include/profile_advanced.php @@ -1,4 +1,4 @@ -<?php +<?php /** @file */ function advanced_profile(&$a) { @@ -80,9 +80,44 @@ function advanced_profile(&$a) { if($txt = prepare_text($a->profile['education'])) $profile['education'] = array( t('School/education:'), $txt ); + $r = q("select * from obj left join term on obj_obj = term_hash where term_hash != '' and obj_page = '%s' and uid = %d and obj_type = %d + order by obj_verb, term", + dbesc($a->profile['profile_guid']), + intval($a->profile['profile_uid']), + intval(TERM_OBJ_THING) + ); + + $things = null; + + if($r) { + $things = array(); + + // Use the system obj_verbs array as a sort key, since we don't really + // want an alphabetic sort. To change the order, use a plugin to + // alter the obj_verbs() array or alter it in code. Unknown verbs come + // after the known ones - in no particular order. + + $v = obj_verbs(); + foreach($v as $k => $foo) + $things[$k] = null; + foreach($r as $rr) { + if(! $things[$rr['obj_verb']]) + $things[$rr['obj_verb']] = array(); + $things[$rr['obj_verb']][] = array('term' => $rr['term'],'url' => $rr['url'],'img' => $rr['imgurl']); + } + $sorted_things = array(); + if($things) + foreach($things as $k => $v) + if(is_array($things[$k])) + $sorted_things[$k] = $v; + } + + logger('mod_profile: things: ' . print_r($sorted_things,true), LOGGER_DATA); + return replace_macros($tpl, array( '$title' => t('Profile'), '$profile' => $profile, + '$things' => $sorted_things )); } diff --git a/include/profile_selectors.php b/include/profile_selectors.php index 8d29fd099..1ffcd49be 100644 --- a/include/profile_selectors.php +++ b/include/profile_selectors.php @@ -1,4 +1,4 @@ -<?php +<?php /** @file */ function gender_selector($current="",$suffix="") { diff --git a/include/queue.php b/include/queue.php index c74a08ac9..ec7246cb2 100644 --- a/include/queue.php +++ b/include/queue.php @@ -1,4 +1,4 @@ -<?php +<?php /** @file */ require_once("boot.php"); require_once('include/cli_startup.php'); require_once('include/queue_fn.php'); @@ -40,14 +40,14 @@ function queue_run($argv, $argc){ return; foreach($r as $rr) { - if(in_array($rr['outq_hub'],$deadguys)) + if(in_array($rr['outq_posturl'],$deadguys)) continue; $result = zot_zot($rr['outq_posturl'],$rr['outq_notify']); if($result['success']) { zot_process_response($rr['outq_posturl'],$result, $rr); } else { - $deadguys[] = $rr['outq_hub']; + $deadguys[] = $rr['outq_posturl']; $y = q("update outq set outq_updated = '%s' where outq_hash = '%s' limit 1", dbesc(datetime_convert()), dbesc($rr['outq_hash']) diff --git a/include/queue_fn.php b/include/queue_fn.php index c9782b939..512edb531 100644 --- a/include/queue_fn.php +++ b/include/queue_fn.php @@ -1,4 +1,4 @@ -<?php +<?php /** @file */ function update_queue_time($id) { logger('queue: requeue item ' . $id); diff --git a/include/security.php b/include/security.php index e691939fb..ef4d5a313 100644 --- a/include/security.php +++ b/include/security.php @@ -1,4 +1,4 @@ -<?php +<?php /** @file */ function authenticate_success($user_record, $login_initial = false, $interactive = false,$return = false,$update_lastlog = false) { @@ -205,26 +205,29 @@ function permissions_sql($owner_id,$remote_verified = false,$groups = null) { else { - $observer = get_app()->get_observer(); - $groups = init_groups_visitor($remote_user); - - $gs = '<<>>'; // should be impossible to match - - if(is_array($groups) && count($groups)) { - foreach($groups as $g) - $gs .= '|<' . $g . '>'; - } - $sql = sprintf( - " AND ( NOT (deny_cid like '%s' OR deny_gid REGEXP '%s') - AND ( allow_cid like '%s' OR allow_gid REGEXP '%s' OR ( allow_cid = '' AND allow_gid = '') ) - ) - ", - dbesc(protect_sprintf( '%<' . $remote_user . '>%')), - dbesc($gs), - dbesc(protect_sprintf( '%<' . $remote_user . '>%')), - dbesc($gs) - ); + $observer = get_observer_hash(); + if($observer) { + $groups = init_groups_visitor($observer); + + $gs = '<<>>'; // should be impossible to match + + if(is_array($groups) && count($groups)) { + foreach($groups as $g) + $gs .= '|<' . $g . '>'; + } + $sql = sprintf( + " AND ( NOT (deny_cid like '%s' OR deny_gid REGEXP '%s') + AND ( allow_cid like '%s' OR allow_gid REGEXP '%s' OR ( allow_cid = '' AND allow_gid = '') ) + ) + ", + dbesc(protect_sprintf( '%<' . $observer . '>%')), + dbesc($gs), + dbesc(protect_sprintf( '%<' . $observer . '>%')), + dbesc($gs) + ); + } } + return $sql; } @@ -260,25 +263,28 @@ function item_permissions_sql($owner_id,$remote_verified = false,$groups = null) else { - $observer = get_app()->get_observer(); - $groups = init_groups_visitor($remote_user); - - $gs = '<<>>'; // should be impossible to match - - if(is_array($groups) && count($groups)) { - foreach($groups as $g) - $gs .= '|<' . $g . '>'; - } - $sql = sprintf( - " AND ( NOT (deny_cid like '%s' OR deny_gid REGEXP '%s') - AND ( allow_cid like '%s' OR allow_gid REGEXP '%s' OR ( allow_cid = '' AND allow_gid = '') ) - ) - ", - dbesc(protect_sprintf( '%<' . $remote_user . '>%')), - dbesc($gs), - dbesc(protect_sprintf( '%<' . $remote_user . '>%')), - dbesc($gs) - ); + $observer = get_observer_hash(); + + if($observer) { + $groups = init_groups_visitor($observer); + + $gs = '<<>>'; // should be impossible to match + + if(is_array($groups) && count($groups)) { + foreach($groups as $g) + $gs .= '|<' . $g . '>'; + } + $sql = sprintf( + " AND ( NOT (deny_cid like '%s' OR deny_gid REGEXP '%s') + AND ( allow_cid like '%s' OR allow_gid REGEXP '%s' OR ( allow_cid = '' AND allow_gid = '') ) + ) + ", + dbesc(protect_sprintf( '%<' . $observer . '>%')), + dbesc($gs), + dbesc(protect_sprintf( '%<' . $observer . '>%')), + dbesc($gs) + ); + } } return $sql; } diff --git a/include/session.php b/include/session.php index 6c32e299f..6072bdb33 100644 --- a/include/session.php +++ b/include/session.php @@ -1,4 +1,4 @@ -<?php +<?php /** @file */ // Session management functions. These provide database storage of PHP // session info. @@ -6,12 +6,23 @@ $session_exists = 0; $session_expire = 180000; -if(! function_exists('ref_session_open')) { + + + +function new_cookie($time) { + $old_sid = session_id(); + session_set_cookie_params("$time"); + session_regenerate_id(false); + + q("UPDATE session SET sid = '%s' WHERE sid = '%s'", dbesc(session_id()), dbesc($old_sid)); +} + + function ref_session_open ($s,$n) { return true; -}} +} + -if(! function_exists('ref_session_read')) { function ref_session_read ($id) { global $session_exists; if(x($id)) @@ -21,9 +32,9 @@ function ref_session_read ($id) { return $r[0]['data']; } return ''; -}} +} + -if(! function_exists('ref_session_write')) { function ref_session_write ($id,$data) { global $session_exists, $session_expire; if(! $id || ! $data) { @@ -44,25 +55,25 @@ function ref_session_write ($id,$data) { dbesc($id), dbesc($default_expire), dbesc($data)); return true; -}} +} + -if(! function_exists('ref_session_close')) { function ref_session_close() { return true; -}} +} + -if(! function_exists('ref_session_destroy')) { function ref_session_destroy ($id) { q("DELETE FROM `session` WHERE `sid` = '%s'", dbesc($id)); return true; -}} +} + -if(! function_exists('ref_session_gc')) { function ref_session_gc($expire) { - q("DELETE FROM `session` WHERE `expire` < %d", dbesc(time())); - q("OPTIMIZE TABLE `sess_data`"); + q("DELETE FROM session WHERE expire < %d", dbesc(time())); + q("OPTIMIZE TABLE session"); return true; -}} +} $gc_probability = 50; @@ -71,6 +82,4 @@ ini_set('session.use_only_cookies', 1); ini_set('session.cookie_httponly', 1); -session_set_save_handler ('ref_session_open', 'ref_session_close', - 'ref_session_read', 'ref_session_write', - 'ref_session_destroy', 'ref_session_gc'); +session_set_save_handler ('ref_session_open', 'ref_session_close', 'ref_session_read', 'ref_session_write', 'ref_session_destroy', 'ref_session_gc'); diff --git a/include/socgraph.php b/include/socgraph.php index 0d9ae3e6c..177ed0f3a 100644 --- a/include/socgraph.php +++ b/include/socgraph.php @@ -1,10 +1,16 @@ -<?php +<?php /** @file */ require_once('include/zot.php'); /* * poco_load * + * xchan is your connection + * We will load their friend list, and store in xlink_xchan your connection hash and xlink_link the hash for each connection + * If xchan isn't provided we will load the list of people from url who have indicated they are willing to be friends with + * new folks and add them to xlink with no xlink_xchan. + * + * Old behaviour: (documentation only): * Given a contact-id (minimum), load the PortableContacts friend list for that contact, * and add the entries to the gcontact (Global Contact) table, or update existing entries * if anything (name or photo) has changed. @@ -19,7 +25,7 @@ require_once('include/zot.php'); -function poco_load($xchan = null,$url = null) { +function poco_load($xchan = '',$url = null) { $a = get_app(); if($xchan && ! $url) { @@ -44,7 +50,12 @@ function poco_load($xchan = null,$url = null) { $s = z_fetch_url($url); if(! $s['success']) { - logger('poco_load: returns ' . print_r($s,true)); + if($s['return_code'] == 401) + logger('poco_load: protected'); + elseif($s['return_code'] == 404) + logger('poco_load: nothing found'); + else + logger('poco_load: returns ' . print_r($s,true)); return; } @@ -60,7 +71,6 @@ function poco_load($xchan = null,$url = null) { $total = 0; foreach($j['entry'] as $entry) { - $total ++; $profile_url = ''; $profile_photo = ''; $address = ''; @@ -112,30 +122,40 @@ function poco_load($xchan = null,$url = null) { if($j) import_xchan($j); } + $x = q("select xchan_hash from xchan where xchan_hash = '%s' limit 1", + dbesc($hash) + ); + if(! $x) { + continue; + } + } + else { + continue; } } + $total ++; - if($xchan) { - $r = q("select * from xlink where xlink_xchan = '%s' and xlink_link = '%s' limit 1", + + $r = q("select * from xlink where xlink_xchan = '%s' and xlink_link = '%s' limit 1", + dbesc($xchan), + dbesc($hash) + ); + + if(! $r) { + q("insert into xlink ( xlink_xchan, xlink_link, xlink_rating, xlink_updated ) values ( '%s', '%s', %d, '%s' ) ", dbesc($xchan), - dbesc($hash) + dbesc($hash), + intval($rating), + dbesc(datetime_convert()) + ); + } + else { + q("update xlink set xlink_updated = '%s', xlink_rating = %d where xlink_id = %d limit 1", + dbesc(datetime_convert()), + intval($rating), + intval($r[0]['xlink_id']) ); - if(! $r) { - q("insert into xlink ( xlink_xchan, xlink_link, xlink_rating, xlink_updated ) values ( '%s', '%s', %d, '%s' ) ", - dbesc($xchan), - dbesc($hash), - intval($rating), - dbesc(datetime_convert()) - ); - } - else { - q("update xlink set xlink_updated = '%s', rating = %d where xlink_id = %d limit 1", - dbesc(datetime_convert()), - intval($rating), - intval($r[0]['xlink_id']) - ); - } } } logger("poco_load: loaded $total entries",LOGGER_DEBUG); @@ -254,80 +274,92 @@ function all_friends($uid,$cid,$start = 0, $limit = 80) { -function suggestion_query($uid, $start = 0, $limit = 80) { +function suggestion_query($uid, $myxchan, $start = 0, $limit = 80) { - if(! $uid) + if((! $uid) || (! $myxchan)) return array(); - $r = q("SELECT count(glink.gcid) as `total`, gcontact.* from gcontact - left join glink on glink.gcid = gcontact.id - where uid = %d and not gcontact.nurl in ( select nurl from contact where uid = %d ) - and not gcontact.name in ( select name from contact where uid = %d ) - and not gcontact.id in ( select gcid from gcign where uid = %d ) - group by glink.gcid order by total desc limit %d, %d ", - intval($uid), + $r = q("SELECT count(xlink_xchan) as `total`, xchan.* from xchan + left join xlink on xlink_link = xchan_hash + where xlink_xchan in ( select abook_xchan from abook where abook_channel = %d ) + and not xlink_link in ( select abook_xchan from abook where abook_channel = %d ) + and not xlink_link in ( select xchan from xign where uid = %d ) + and xlink_xchan != '' + and not ( xchan_flags & %d ) + group by xchan_hash order by total desc limit %d, %d ", intval($uid), intval($uid), intval($uid), + intval(XCHAN_FLAGS_HIDDEN), intval($start), intval($limit) ); - if(count($r) && count($r) >= ($limit -1)) + if($r && count($r) >= ($limit -1)) return $r; - $r2 = q("SELECT gcontact.* from gcontact - left join glink on glink.gcid = gcontact.id - where glink.uid = 0 and glink.cid = 0 and glink.zcid = 0 and not gcontact.nurl in ( select nurl from contact where uid = %d ) - and not gcontact.name in ( select name from contact where uid = %d ) - and not gcontact.id in ( select gcid from gcign where uid = %d ) - order by rand() limit %d, %d ", - intval($uid), + $r2 = q("SELECT count(xlink_link) as `total`, xchan.* from xchan + left join xlink on xlink_link = xchan_hash + where xlink_xchan = '' + and not xlink_link in ( select abook_xchan from abook where abook_channel = %d ) + and not xlink_link in ( select xchan from xign where uid = %d ) + and not ( xchan_flags & %d ) + group by xchan_hash order by total desc limit %d, %d ", intval($uid), intval($uid), + intval(XCHAN_FLAGS_HIDDEN), intval($start), intval($limit) ); + if(is_array($r) && is_array($r2)) + return array_merge($r,$r2); - return array_merge($r,$r2); - + return array(); } function update_suggestions() { -// FIXME -return; $a = get_app(); - $done = array(); - - // fix this to get a json list from an upstream directory -// poco_load(0,0,0,$a->get_baseurl() . '/poco'); - -// $done[] = $a->get_baseurl() . '/poco'; - -// if(strlen(get_config('system','directory_submit_url'))) { -// $x = fetch_url('http://dir.friendica.com/pubsites'); -// if($x) { -// $j = json_decode($x); -// if($j->entries) { -// foreach($j->entries as $entry) { -// $url = $entry->url . '/poco'; -// if(! in_array($url,$done)) -// poco_load(0,0,0,$entry->url . '/poco'); -// } -// } -// } -// } - - $r = q("select distinct(xchan_connurl) as poco from xchan where xchan_network = 'zot'"); - - if($r) { - foreach($r as $rr) { - $base = substr($rr['poco'],0,strrpos($rr['poco'],'/')); - if(! in_array($base,$done)) - poco_load('',$base); + $dirmode = get_config('system','directory_mode'); + if($dirmode === false) + $dirmode = DIRECTORY_MODE_NORMAL; + + if(($dirmode == DIRECTORY_MODE_PRIMARY) || ($dirmode == DIRECTORY_MODE_STANDALONE)) { + $url = z_root() . '/sitelist'; + } + else { + $directory = find_upstream_directory($dirmode); + + if($directory) { + $url = $directory['url'] . '/sitelist'; + } + else { + $url = DIRECTORY_FALLBACK_MASTER . '/sitelist'; + } + } + if(! $url) + return; + + + + $ret = z_fetch_url($url); + + if($ret['success']) { + + // We will grab fresh data once a day via the poller. Remove anything over a week old because + // the targets may have changed their preferences and don't want to be suggested - and they + // may have simply gone away. + + $r = q("delete from xlink where xlink_xchan = '' and xlink_updated < UTC_TIMESTAMP() - INTERVAL 7 DAY"); + + + $j = json_decode($ret['body'],true); + if($j && $j['success']) { + foreach($j['entries'] as $host) { + poco_load('',$host['url'] . '/poco'); + } } } } diff --git a/include/system_unavailable.php b/include/system_unavailable.php index bd7196cdf..dfe7c5e6b 100644 --- a/include/system_unavailable.php +++ b/include/system_unavailable.php @@ -1,4 +1,4 @@ -<?php +<?php /** @file */ function system_down() { echo <<< EOT diff --git a/include/taxonomy.php b/include/taxonomy.php new file mode 100644 index 000000000..b6803743a --- /dev/null +++ b/include/taxonomy.php @@ -0,0 +1,208 @@ +<?php /** @file */ + +// post categories and "save to file" use the same item.file table for storage. +// We will differentiate the different uses by wrapping categories in angle brackets +// and save to file categories in square brackets. +// To do this we need to escape these characters if they appear in our tag. + +function file_tag_encode($s) { + return str_replace(array('<','>','[',']'),array('%3c','%3e','%5b','%5d'),$s); +} + +function file_tag_decode($s) { + return str_replace(array('%3c','%3e','%5b','%5d'),array('<','>','[',']'),$s); +} + +function file_tag_file_query($table,$s,$type = 'file') { + + if($type == 'file') + $termtype = TERM_FILE; + else + $termtype = TERM_CATEGORY; + + return sprintf(" AND " . (($table) ? dbesc($table) . '.' : '') . "id in (select term.oid from term where term.type = %d and term.term = '%s' and term.uid = " . (($table) ? dbesc($table) . '.' : '') . "uid ) ", + intval($termtype), + protect_sprintf(dbesc($s)) + ); +} + +function term_query($table,$s,$type = TERM_UNKNOWN) { + + return sprintf(" AND " . (($table) ? dbesc($table) . '.' : '') . "id in (select term.oid from term where term.type = %d and term.term = '%s' and term.uid = " . (($table) ? dbesc($table) . '.' : '') . "uid ) ", + intval($type), + protect_sprintf(dbesc($s)) + ); +} + + +function store_item_tag($uid,$iid,$otype,$type,$term,$url = '') { + if(! $term) + return false; + $r = q("select * from term + where uid = %d and oid = %d and otype = %d and type = %d + and term = '%s' and url = '%s' ", + intval($uid), + intval($iid), + intval($otype), + intval($type), + dbesc($term), + dbesc($url) + ); + if($r) + return false; + $r = q("insert into term (uid, oid, otype, type, term, url) + values( %d, %d, %d, %d, '%s', '%s') ", + intval($uid), + intval($iid), + intval($otype), + intval($type), + dbesc($term), + dbesc($url) + ); + return $r; +} + +function get_terms_oftype($arr,$type) { + $ret = array(); + if(! (is_array($arr) && count($arr))) + return $ret; + + if(! is_array($type)) + $type = array($type); + + foreach($type as $t) + foreach($arr as $x) + if($x['type'] == $t) + $ret[] = $x; + return $ret; +} + +function format_term_for_display($term) { + $s = ''; + if($term['type'] == TERM_HASHTAG) + $s .= '#'; + elseif($term['type'] == TERM_MENTION) + $s .= '@'; + else + return $s; + + if($term['url']) + $s .= '<a href="' . $term['url'] . '">' . htmlspecialchars($term['term']) . '</a>'; + else + $s .= htmlspecialchars($term['term']); + return $s; +} + +// Tag cloud functions - need to be adpated to this database format + + +function tagadelic($uid, $count = 0, $authors = '', $flags = 0, $type = TERM_HASHTAG) { + + $sql_options = ''; + + if($flags) + $sql_options .= " and ((item_flags & " . intval($flags) . ") = " . intval($flags) . ") "; + if($authors) { + if(! is_array($authors)) + $authors = array($authors); + stringify_array_elms($authors,true); + $sql_options .= " and author_xchan in (" . implode(',',$authors) . ") "; + } + + // Fetch tags + $r = q("select term, count(term) as total from term left join item on term.oid = item.id + where term.uid = %d and term.type = %d + and otype = %d and item_restrict = 0 and item_private = 0 + $sql_options + group by term order by total desc %s", + intval($uid), + intval($type), + intval(TERM_OBJ_POST), + ((intval($count)) ? "limit $count" : '') + ); + + if(! $r) + return array(); + + // Find minimum and maximum log-count. + $tags = array(); + $min = 1e9; + $max = -1e9; + + $x = 0; + foreach($r as $rr) { + $tags[$x][0] = $rr['term']; + $tags[$x][1] = log($rr['total']); + $tags[$x][2] = 0; + $min = min($min,$tags[$x][1]); + $max = max($max,$tags[$x][1]); + $x ++; + } + + usort($tags,'tags_sort'); + + $range = max(.01, $max - $min) * 1.0001; + + for($x = 0; $x < count($tags); $x ++) { + $tags[$x][2] = 1 + floor(5 * ($tags[$x][1] - $min) / $range); + } + + return $tags; +} + +function tags_sort($a,$b) { + if($a[0] == $b[0]) + return 0; + return((strtolower($a[0]) < strtolower($b[0])) ? -1 : 1); +} + + +function tagblock($link,$uid,$count = 0,$authors = '',$flags = 0,$type = TERM_HASHTAG) { + $o = ''; + $tab = 0; + $r = tagadelic($uid,$count,$authors,$flags,$type); + + if($r) { + $o = '<div class="tagblock widget"><h3>' . t('Tags') . '</h3><div class="tags" align="center">'; + foreach($r as $rr) { + $o .= '<a href="'.$link .'/' . '?f=&tag=' . urlencode($rr[0]).'" class="tag'.$rr[2].'">'.$rr[0].'</a> ' . "\r\n"; + } + $o .= '</div></div>'; + } + return $o; +} + + + /** + * verbs: [0] = first person singular, e.g. "I want", [1] = 3rd person singular, e.g. "Bill wants" + * We use the first person form when creating an activity, but the third person for use in activities + * FIXME: There is no accounting for verb gender for languages where this is significant. We may eventually + * require obj_verbs() to provide full conjugations and specify which form to use in the $_REQUEST params to this module. + */ + + + +function obj_verbs() { + $verbs = array( + 'has' => array( t('have'), t('has')), + 'wants' => array( t('want'), t('wants')), + 'likes' => array( t('like'), t('likes')), + 'dislikes' => array( t('dislike'), t('dislikes')), + ); + + $arr = array('verbs' => $verbs); + call_hooks('obj_verbs', $arr); + return $arr['verbs']; +} + + +function obj_verb_selector() { + $verbs = obj_verbs(); + $o .= '<select class="obj-verb-selector" name="verb" >'; + foreach($verbs as $k => $v) { + $o .= '<option value="' . urlencode($k) . '">' . $v[0] . '</option>'; + } + $o .= '</select>'; + return $o; + +}
\ No newline at end of file diff --git a/include/template_processor.php b/include/template_processor.php index 61526e570..74acc9c67 100644..100755 --- a/include/template_processor.php +++ b/include/template_processor.php @@ -1,7 +1,11 @@ <?php + require_once 'include/ITemplateEngine.php'; + define ("KEY_NOT_EXISTS", '^R_key_not_Exists^'); - class Template { + class Template implements ITemplateEngine { + static $name ="internal"; + var $r; var $search; var $replace; @@ -244,9 +248,13 @@ return $s; } - - public function replace($s, $r) { - $t1 = dba_timer(); + + private function replace($s,$r) { + $this->replace_macros($s, $r); + } + + // TemplateEngine interface + public function replace_macros($s, $r) { $this->r = $r; $s = $this->_build_nodes($s); @@ -265,14 +273,18 @@ $os=$s; $count++; $s = $this->var_replace($s); } - $t3 = dba_timer(); -// logger('macro timer: ' . sprintf('%01.4f %01.4f',$t3 - $t2, $t2 - $t1)); - return $s; } + + public function get_markup_template($file, $root='') { + $template_file = theme_include($file, $root); + if ($template_file) { + $content = file_get_contents($template_file); + } + return $content; + } } - $t = new Template; diff --git a/include/text.php b/include/text.php index 523970bf4..61b39cb59 100644..100755 --- a/include/text.php +++ b/include/text.php @@ -1,48 +1,25 @@ -<?php +<?php /** @file */ -// This is our template processor. -// $s is the string requiring macro substitution. -// $r is an array of key value pairs (search => replace) -// returns substituted string. -// WARNING: this is pretty basic, and doesn't properly handle search strings that are substrings of each other. -// For instance if 'test' => "foo" and 'testing' => "bar", testing could become either bar or fooing, -// depending on the order in which they were declared in the array. require_once("include/template_processor.php"); +require_once("include/friendica_smarty.php"); -if(! function_exists('replace_macros')) { +/** + * This is our template processor + * + * @param string|FriendicaSmarty $s the string requiring macro substitution, + * or an instance of FriendicaSmarty + * @param array $r key value pairs (search => replace) + * @return string substituted string + */ function replace_macros($s,$r) { - global $t; - -// $ts = microtime(); $a = get_app(); - if($a->get_template_engine() === 'smarty3') { - $output = ''; - if(gettype($s) !== 'NULL') { - $template = ''; - if(gettype($s) === 'string') { - $template = $s; - $s = new FriendicaSmarty(); - } - foreach($r as $key=>$value) { - if($key[0] === '$') { - $key = substr($key, 1); - } - $s->assign($key, $value); - } - $output = $s->parsed($template); - } - } - else { - $r = $t->replace($s,$r); + $t = $a->template_engine(); + $output = $t->replace_macros($s,$r); - $output = template_unescape($r); - } -// $tt = microtime() - $ts; -// $a->page['debug'] .= "$tt <br>\n"; return $output; -}} +} // random string, there are 86 characters max in text mode, 128 for hex @@ -51,13 +28,13 @@ function replace_macros($s,$r) { define('RANDOM_STRING_HEX', 0x00 ); define('RANDOM_STRING_TEXT', 0x01 ); -if(! function_exists('random_string')) { + function random_string($size = 64,$type = RANDOM_STRING_HEX) { // generate a bit of entropy and run it through the whirlpool $s = hash('whirlpool', (string) rand() . uniqid(rand(),true) . (string) rand(),(($type == RANDOM_STRING_TEXT) ? true : false)); $s = (($type == RANDOM_STRING_TEXT) ? str_replace("\n","",base64url_encode($s,true)) : $s); return(substr($s,0,$size)); -}} +} /** * This is our primary input filter. @@ -73,32 +50,63 @@ function random_string($size = 64,$type = RANDOM_STRING_HEX) { * They will be replaced with safer brackets. This may be filtered further * if these are not allowed either. * + * @param string $string Input string + * @return string Filtered string */ -if(! function_exists('notags')) { + function notags($string) { return(str_replace(array("<",">"), array('[',']'), $string)); // High-bit filter no longer used // return(str_replace(array("<",">","\xBA","\xBC","\xBE"), array('[',']','','',''), $string)); -}} +} // use this on "body" or "content" input where angle chars shouldn't be removed, // and allow them to be safely displayed. -if(! function_exists('escape_tags')) { + + +/** + * use this on "body" or "content" input where angle chars shouldn't be removed, + * and allow them to be safely displayed. + * @param string $string + * @return string + */ function escape_tags($string) { return(htmlspecialchars($string, ENT_COMPAT, 'UTF-8', false)); -}} +} + + +function purify_html($s) { + require_once('library/HTMLPurifier.auto.php'); + require_once('include/html2bbcode.php'); + +// FIXME this function has html output, not bbcode - so safely purify these +// $s = html2bb_video($s); +// $s = oembed_html2bbcode($s); + + $config = HTMLPurifier_Config::createDefault(); + $config->set('Cache.DefinitionImpl', null); + + $purifier = new HTMLPurifier($config); + return $purifier->purify($s); +} // generate a string that's random, but usually pronounceable. // used to generate initial passwords -if(! function_exists('autoname')) { + +/** + * generate a string that's random, but usually pronounceable. + * used to generate initial passwords + * @param int $len + * @return string + */ function autoname($len) { if($len <= 0) @@ -167,13 +175,18 @@ function autoname($len) { if(substr($word,-1) == 'q') $word = substr($word,0,-1); return $word; -}} +} // escape text ($str) for XML transport // returns escaped text. -if(! function_exists('xmlify')) { + +/** + * escape text ($str) for XML transport + * @param string $str + * @return string Escaped text. + */ function xmlify($str) { $buffer = ''; @@ -210,20 +223,22 @@ function xmlify($str) { } $buffer = trim($buffer); return($buffer); -}} +} // undo an xmlify // pass xml escaped text ($s), returns unescaped text -if(! function_exists('unxmlify')) { + function unxmlify($s) { $ret = str_replace('&','&', $s); $ret = str_replace(array('<','>','"','''),array('<','>','"',"'"),$ret); return $ret; -}} +} // convenience wrapper, reverse the operation "bin2hex" +// This is a built-in function in php >= 5.4 + if(! function_exists('hex2bin')) { function hex2bin($s) { if(! (is_string($s) && strlen($s))) @@ -236,6 +251,7 @@ function hex2bin($s) { return(pack("H*",$s)); }} + // Automatic pagination. // To use, get the count of total items. // Then call $a->set_pager_total($number_items); @@ -246,7 +262,7 @@ function hex2bin($s) { // will limit the results to the correct items for the current page. // The actual page handling is then accomplished at the application layer. -if(! function_exists('paginate')) { + function paginate(&$a) { $o = ''; $stripped = preg_replace('/(&page=[0-9]*)/','',$a->query_string); @@ -300,9 +316,9 @@ function paginate(&$a) { $o .= '</div>'."\r\n"; } return $o; -}} +} + -if(! function_exists('alt_pager')) { function alt_pager(&$a, $i, $more = '', $less = '') { $o = ''; @@ -318,25 +334,21 @@ function alt_pager(&$a, $i, $more = '', $less = '') { $pagenum = $a->pager['page']; $url = $a->get_baseurl() . '/' . $stripped; - $o .= '<div class="pager">'; - - if($a->pager['page'] > 1) - $o .= "<a href=\"$url"."&page=".($a->pager['page'] - 1).'">' . $less . '</a>'; - if($i > 0 && $i == $a->pager['itemspage']) { - if($a->pager['page']>1) - $o .= " | "; - $o .= "<a href=\"$url"."&page=".($a->pager['page'] + 1).'">' . $more . '</a>'; - } - - - $o .= '</div>'."\r\n"; + return replace_macros(get_markup_template('alt_pager.tpl'),array( + '$has_less' => (($a->pager['page'] > 1) ? true : false), + '$has_more' => (($i > 0 && $i == $a->pager['itemspage']) ? true : false), + '$less' => $less, + '$more' => $more, + '$url' => $url, + '$prevpage' => $a->pager['page'] - 1, + '$nextpage' => $a->pager['page'] + 1, + )); - return $o; -}} +} // Turn user/group ACLs stored as angle bracketed text into arrays -if(! function_exists('expand_acl')) { + function expand_acl($s) { // turn string array of angle-bracketed elements into string array @@ -353,22 +365,22 @@ function expand_acl($s) { } } return $ret; -}} +} // Used to wrap ACL elements in angle brackets for storage -if(! function_exists('sanitise_acl')) { + function sanitise_acl(&$item) { if(strlen($item)) $item = '<' . notags(trim($item)) . '>'; else unset($item); -}} +} // Convert an ACL array to a storable string -if(! function_exists('perms2str')) { + function perms2str($p) { $ret = ''; @@ -382,32 +394,32 @@ function perms2str($p) { $ret = implode('',$tmp); } return $ret; -}} +} // generate a guaranteed unique (for this domain) item ID for ATOM // safe from birthday paradox -if(! function_exists('item_message_id')) { + function item_message_id() { do { $dups = false; $hash = random_string(); - $uri = $hash . '@' . get_app()->get_hostname(); + $mid = $hash . '@' . get_app()->get_hostname(); - $r = q("SELECT `id` FROM `item` WHERE `uri` = '%s' LIMIT 1", - dbesc($uri)); + $r = q("SELECT `id` FROM `item` WHERE `mid` = '%s' LIMIT 1", + dbesc($mid)); if(count($r)) $dups = true; } while($dups == true); - return $uri; -}} + return $mid; +} // Generate a guaranteed unique photo ID. // safe from birthday paradox -if(! function_exists('photo_new_resource')) { + function photo_new_resource() { do { @@ -420,7 +432,7 @@ function photo_new_resource() { $found = true; } while($found == true); return $resource; -}} +} @@ -435,15 +447,14 @@ function photo_new_resource() { // pass the attribute string as $attr and the attribute you // are looking for as $s - returns true if found, otherwise false -if(! function_exists('attribute_contains')) { function attribute_contains($attr,$s) { $a = explode(' ', $attr); if(count($a) && in_array($s,$a)) return true; return false; -}} +} + -if(! function_exists('logger')) { function logger($msg,$level = 0) { // turn off logger in install mode global $a; @@ -460,7 +471,7 @@ function logger($msg,$level = 0) { @file_put_contents($logfile, datetime_convert() . ':' . session_id() . ' ' . $msg . "\n", FILE_APPEND); return; -}} +} // This is a special logging facility for developers. It allows one to target specific things to trace/debug @@ -469,7 +480,7 @@ function logger($msg,$level = 0) { // If you find dlogger() calls in checked in code, you are free to remove them - so as to provide a noise-free // development environment which responds to events you are targetting personally. -if(! function_exists('dlogger')) { + function dlogger($msg,$level = 0) { // turn off logger in install mode global $a; @@ -486,7 +497,7 @@ function dlogger($msg,$level = 0) { @file_put_contents($logfile, datetime_convert() . ':' . session_id() . ' ' . $msg . "\n", FILE_APPEND); return; -}} +} function profiler($t1,$t2,$label) { @@ -495,12 +506,13 @@ function profiler($t1,$t2,$label) { } -if(! function_exists('activity_match')) { + function activity_match($haystack,$needle) { if(($haystack === $needle) || ((basename($needle) === $haystack) && strstr($needle,NAMESPACE_ACTIVITY_SCHEMA))) return true; + return false; -}} +} // Pull out all #hashtags and @person tags from $s; @@ -511,7 +523,7 @@ function activity_match($haystack,$needle) { // Returns array of tags found, or empty array. -if(! function_exists('get_tags')) { + function get_tags($s) { $ret = array(); @@ -556,19 +568,19 @@ function get_tags($s) { } } return $ret; -}} +} // quick and dirty quoted_printable encoding -if(! function_exists('qp')) { + function qp($s) { return str_replace ("%","=",rawurlencode($s)); -}} +} + -if(! function_exists('get_mentions')) { function get_mentions($item,$tags) { $o = ''; @@ -582,9 +594,9 @@ function get_mentions($item,$tags) { } } return $o; -}} +} + -if(! function_exists('contact_block')) { function contact_block() { $o = ''; $a = get_app(); @@ -637,7 +649,7 @@ function contact_block() { call_hooks('contact_block_end', $arr); return $o; -}} +} function chanlink_hash($s) { @@ -661,7 +673,7 @@ function magiclink_url($observer,$myaddr,$url) { } -if(! function_exists('micropro')) { + function micropro($contact, $redirect = false, $class = '', $textmode = false) { if($contact['click']) @@ -677,11 +689,11 @@ function micropro($contact, $redirect = false, $class = '', $textmode = false) { '$name' => $contact['xchan_name'], '$title' => $contact['xchan_name'] . ' [' . $contact['xchan_addr'] . ']', )); -}} +} + -if(! function_exists('search')) { function search($s,$id='search-box',$url='/search',$save = false) { $a = get_app(); $o = '<div id="' . $id . '">'; @@ -692,9 +704,9 @@ function search($s,$id='search-box',$url='/search',$save = false) { $o .= '<input type="submit" name="save" id="search-save" value="' . t('Save') . '" />'; $o .= '</form></div>'; return $o; -}} +} + -if(! function_exists('valid_email')) { function valid_email($x){ if(get_config('system','disable_email_validation')) @@ -703,7 +715,7 @@ function valid_email($x){ if(preg_match('/^[_a-zA-Z0-9\-\+]+(\.[_a-zA-Z0-9\-\+]+)*@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)+$/',$x)) return true; return false; -}} +} /** @@ -714,12 +726,12 @@ function valid_email($x){ * */ -if(! function_exists('linkify')) { + function linkify($s) { - $s = preg_replace("/(https?\:\/\/[a-zA-Z0-9\:\/\-\?\&\;\.\=\_\~\#\'\%\$\!\+]*)/", ' <a href="$1" >$1</a>', $s); + $s = preg_replace("/(https?\:\/\/[a-zA-Z0-9\:\/\-\?\&\;\.\=\_\@\~\#\'\%\$\!\+]*)/", ' <a href="$1" >$1</a>', $s); $s = preg_replace("/\<(.*?)(src|href)=(.*?)\&\;(.*?)\>/ism",'<$1$2=$3&$4>',$s); return($s); -}} +} function get_poke_verbs() { @@ -792,7 +804,7 @@ function get_mood_verbs() { * */ -if(! function_exists('smilies')) { + function smilies($s, $sample = false) { $a = get_app(); @@ -837,7 +849,7 @@ function smilies($s, $sample = false) { ':facepalm', ':like', ':dislike', - '~friendika', + 'red#', '~friendica' ); @@ -875,7 +887,7 @@ function smilies($s, $sample = false) { '<img class="smiley" src="' . $a->get_baseurl() . '/images/smiley-facepalm.gif" alt=":facepalm" />', '<img class="smiley" src="' . $a->get_baseurl() . '/images/like.gif" alt=":like" />', '<img class="smiley" src="' . $a->get_baseurl() . '/images/dislike.gif" alt=":dislike" />', - '<a href="http://project.friendika.com">~friendika <img class="smiley" src="' . $a->get_baseurl() . '/images/friendika-16.png" alt="~friendika" /></a>', + '<a href="http://getzot.com"><img class="smiley" src="' . $a->get_baseurl() . '/images/rhash-16.png" alt="red#" /> the Red Matrix</a>', '<a href="http://friendica.com">~friendica <img class="smiley" src="' . $a->get_baseurl() . '/images/friendica-16.png" alt="~friendica" /></a>' ); @@ -898,7 +910,7 @@ function smilies($s, $sample = false) { return $s; -}} +} function smile_encode($m) { return(str_replace($m[1],base64url_encode($m[1]),$m[0])); @@ -922,7 +934,7 @@ function preg_heart($x) { } -if(! function_exists('day_translate')) { + function day_translate($s) { $ret = str_replace(array('Monday','Tuesday','Wednesday','Thursday','Friday','Saturday','Sunday'), array( t('Monday'), t('Tuesday'), t('Wednesday'), t('Thursday'), t('Friday'), t('Saturday'), t('Sunday')), @@ -933,14 +945,14 @@ function day_translate($s) { $ret); return $ret; -}} +} + -if(! function_exists('normalise_link')) { function normalise_link($url) { $ret = str_replace(array('https:','//www.'), array('http:','//'), $url); return(rtrim($ret,'/')); -}} +} /** * @@ -953,24 +965,35 @@ function normalise_link($url) { * */ -if(! function_exists('link_compare')) { + function link_compare($a,$b) { if(strcasecmp(normalise_link($a),normalise_link($b)) === 0) return true; return false; -}} +} // Given an item array, convert the body element from bbcode to html and add smilie icons. // If attach is true, also add icons for item attachments -if(! function_exists('prepare_body')) { -function prepare_body($item,$attach = false) { + +function prepare_body(&$item,$attach = false) { $a = get_app(); + + + call_hooks('prepare_body_init', $item); - $s = prepare_text($item['body']); + if(array_key_exists('item_flags',$item) && ($item['item_flags'] & ITEM_OBSCURED)) { + $key = get_config('system','prvkey'); + if($item['title']) + $item['title'] = aes_unencapsulate(json_decode_plus($item['title']),$key); + if($item['body']) + $item['body'] = aes_unencapsulate(json_decode_plus($item['body']),$key); + } + + $s = prepare_text($item['body'],$item['mimetype']); $prep_arr = array('item' => $item, 'html' => $s); call_hooks('prepare_body', $prep_arr); @@ -980,7 +1003,8 @@ function prepare_body($item,$attach = false) { return $s; } - $arr = json_decode($item['attach'],true); + + $arr = json_decode_plus($item['attach']); if(count($arr)) { $s .= '<div class="body-attach">'; foreach($arr as $r) { @@ -1011,6 +1035,27 @@ function prepare_body($item,$attach = false) { $s .= '<div class="clear"></div></div>'; } +// At some point in time, posttags were removed from the threaded conversation templates, but remained in the search_item template. +// Code to put them back was added into include/conversation.php and/or include/ItemObject.php but under new class names +// Then it was discovered that the following bits remained of the old code. +// Commented out, but we may decide to use this instead of the other version and put all the tag rendering in one place. In the other +// location it is more theme-able. +// if(is_array($item['term']) && count($item['term'])) { +// $tstr = ''; +// foreach($item['term'] as $t) { +// $t1 = format_term_for_display($t); +// if($t1) { +// if($tstr) +// $tstr .= ' '; +// $tstr .= $t1; +// } +// } +// if($tstr) +// $s .= '<br /><div class="posttags">' . $tstr . '</div>'; +// } + + $writeable = ((get_observer_hash() == $item['owner_xchan']) ? true : false); + $x = ''; $terms = get_terms_oftype($item['term'],TERM_CATEGORY); if($terms) { @@ -1018,7 +1063,7 @@ function prepare_body($item,$attach = false) { if(strlen($x)) $x .= ','; $x .= htmlspecialchars($t['term'],ENT_COMPAT,'UTF-8') - . ((local_user() == $item['uid']) ? ' <a href="' . $a->get_baseurl() . '/filerm/' . $item['id'] . '?f=&cat=' . urlencode($t['term']) . '" title="' . t('remove') . '" >' . t('[remove]') . '</a>' : ''); + . (($writeable) ? ' <a href="' . $a->get_baseurl() . '/filerm/' . $item['id'] . '?f=&cat=' . urlencode($t['term']) . '" title="' . t('remove') . '" >' . t('[remove]') . '</a>' : ''); } if(strlen($x)) $s .= '<div class="categorytags"><span>' . t('Categories:') . ' </span>' . $x . '</div>'; @@ -1073,30 +1118,108 @@ function prepare_body($item,$attach = false) { call_hooks('prepare_body_final', $prep_arr); return $prep_arr['html']; -}} +} // Given a text string, convert from bbcode to html and add smilie icons. -if(! function_exists('prepare_text')) { -function prepare_text($text) { - require_once('include/bbcode.php'); +function prepare_text($text,$content_type = 'text/bbcode') { - if(stristr($text,'[nosmile]')) - $s = bbcode($text); - else - $s = smilies(bbcode($text)); + + switch($content_type) { + + case 'text/plain': + $s = escape_tags($text); + break; + + case 'text/html': + $s = $text; + break; + + case 'text/markdown': + require_once('library/markdown.php'); + $s = Markdown($text); + break; + + case 'text/bbcode': + case '': + default: + require_once('include/bbcode.php'); + + if(stristr($text,'[nosmile]')) + $s = bbcode($text); + else + $s = smilies(bbcode($text)); + $s = zidify_links($s); + break; + } return $s; -}} +} + + +/** + * zidify_callback() and zidify_links() work together to turn any HTML a tags with class="zrl" into zid links + * These will typically be generated by a bbcode '[zrl]' tag. This is done inside prepare_text() rather than bbcode() + * because the latter is used for general purpose conversions and the former is used only when preparing text for + * immediate display. + * + * Issues: Currently the order of HTML parameters in the text is somewhat rigid and inflexible. + * We assume it looks like <a class="zrl" href="xxxxxxxxxx"> and will not work if zrl and href appear in a different order. + */ + + +function zidify_callback($match) { + if (feature_enabled(local_user(),'sendzid')) { + $replace = '<a' . $match[1] . ' href="' . zid($match[2]) . '"'; + } + else { + $replace = '<a' . $match[1] . 'class="zrl"' . $match[2] . ' href="' . zid($match[3]) . '"'; + } + + $x = str_replace($match[0],$replace,$match[0]); + return $x; +} + +function zidify_img_callback($match) { + if (feature_enabled(local_user(),'sendzid')) { + $replace = '<img' . $match[1] . ' src="' . zid($match[2]) . '"'; + } + else { + $replace = '<img' . $match[1] . ' src="' . zid($match[2]) . '"'; + } + + $x = str_replace($match[0],$replace,$match[0]); + return $x; +} + + +function zidify_links($s) { + if(feature_enabled(local_user(),'sendzid')) { + $s = preg_replace_callback('/\<a(.*?)href\=\"(.*?)\"/ism','zidify_callback',$s); + $s = preg_replace_callback('/\<img(.*?)src\=\"(.*?)\"/ism','zidify_img_callback',$s); + } + else { + $s = preg_replace_callback('/\<a(.*?)class\=\"zrl\"(.*?)href\=\"(.*?)\"/ism','zidify_callback',$s); + $s = preg_replace_callback('/\<img class\=\"zrl\"(.*?)src\=\"(.*?)\"/ism','zidify_img_callback',$s); +// FIXME - remove the following line and redo the regex for the prev line once all Red images are converted to zmg + $s = preg_replace_callback('/\<img(.*?)src\=\"(.*?)\"/ism','zidify_img_callback',$s); + } + + return $s; +} + + + + /** * return atom link elements for all of our hubs */ -if(! function_exists('feed_hublinks')) { + function feed_hublinks() { $hub = get_config('system','huburl'); @@ -1114,11 +1237,11 @@ function feed_hublinks() { } } return $hubxml; -}} +} /* return atom link elements for salmon endpoints */ -if(! function_exists('feed_salmonlinks')) { + function feed_salmonlinks($nick) { $a = get_app(); @@ -1130,12 +1253,12 @@ function feed_salmonlinks($nick) { $salmon .= ' <link rel="http://salmon-protocol.org/ns/salmon-replies" href="' . xmlify($a->get_baseurl() . '/salmon/' . $nick) . '" />' . "\n" ; $salmon .= ' <link rel="http://salmon-protocol.org/ns/salmon-mention" href="' . xmlify($a->get_baseurl() . '/salmon/' . $nick) . '" />' . "\n" ; return $salmon; -}} +} + -if(! function_exists('get_plink')) { function get_plink($item) { $a = get_app(); - if (x($item,'plink') && ($item['private'] != 1)) { + if (x($item,'plink') && ($item['item_private'] != 1)) { return array( 'href' => $item['plink'], 'title' => t('link to source'), @@ -1144,17 +1267,17 @@ function get_plink($item) { else { return false; } -}} +} + -if(! function_exists('unamp')) { function unamp($s) { return str_replace('&', '&', $s); -}} +} + -if(! function_exists('lang_selector')) { function lang_selector() { global $a; @@ -1187,10 +1310,10 @@ function lang_selector() { )); return $o; -}} +} + -if(! function_exists('return_bytes')) { function return_bytes ($size_str) { switch (substr ($size_str, -1)) { @@ -1199,7 +1322,7 @@ function return_bytes ($size_str) { case 'G': case 'g': return (int)$size_str * 1073741824; default: return $size_str; } -}} +} function generate_user_guid() { $found = true; @@ -1253,63 +1376,6 @@ function base64url_decode($s) { } -if (!function_exists('str_getcsv')) { - function str_getcsv($input, $delimiter = ',', $enclosure = '"', $escape = '\\', $eol = '\n') { - if (is_string($input) && !empty($input)) { - $output = array(); - $tmp = preg_split("/".$eol."/",$input); - if (is_array($tmp) && !empty($tmp)) { - while (list($line_num, $line) = each($tmp)) { - if (preg_match("/".$escape.$enclosure."/",$line)) { - while ($strlen = strlen($line)) { - $pos_delimiter = strpos($line,$delimiter); - $pos_enclosure_start = strpos($line,$enclosure); - if ( - is_int($pos_delimiter) && is_int($pos_enclosure_start) - && ($pos_enclosure_start < $pos_delimiter) - ) { - $enclosed_str = substr($line,1); - $pos_enclosure_end = strpos($enclosed_str,$enclosure); - $enclosed_str = substr($enclosed_str,0,$pos_enclosure_end); - $output[$line_num][] = $enclosed_str; - $offset = $pos_enclosure_end+3; - } else { - if (empty($pos_delimiter) && empty($pos_enclosure_start)) { - $output[$line_num][] = substr($line,0); - $offset = strlen($line); - } else { - $output[$line_num][] = substr($line,0,$pos_delimiter); - $offset = ( - !empty($pos_enclosure_start) - && ($pos_enclosure_start < $pos_delimiter) - ) - ?$pos_enclosure_start - :$pos_delimiter+1; - } - } - $line = substr($line,$offset); - } - } else { - $line = preg_split("/".$delimiter."/",$line); - - /* - * Validating against pesky extra line breaks creating false rows. - */ - if (is_array($line) && !empty($line[0])) { - $output[$line_num] = $line; - } - } - } - return $output; - } else { - return false; - } - } else { - return false; - } - } -} - function cleardiv() { return '<div class="clear"></div>'; } @@ -1357,7 +1423,7 @@ function array_xmlify($val){ function reltoabs($text, $base) { if (empty($base)) - return $text; + return $text; $base = rtrim($base,'/'); @@ -1387,278 +1453,28 @@ function reltoabs($text, $base) } function item_post_type($item) { - if(intval($item['event-id'])) - return t('event'); - if(strlen($item['resource_id'])) - return t('photo'); - if(strlen($item['verb']) && $item['verb'] !== ACTIVITY_POST) - return t('activity'); - if($item['id'] != $item['parent']) - return t('comment'); - return t('post'); -} - -// post categories and "save to file" use the same item.file table for storage. -// We will differentiate the different uses by wrapping categories in angle brackets -// and save to file categories in square brackets. -// To do this we need to escape these characters if they appear in our tag. - -function file_tag_encode($s) { - return str_replace(array('<','>','[',']'),array('%3c','%3e','%5b','%5d'),$s); -} - -function file_tag_decode($s) { - return str_replace(array('%3c','%3e','%5b','%5d'),array('<','>','[',']'),$s); -} - -function file_tag_file_query($table,$s,$type = 'file') { - - if($type == 'file') - $termtype = TERM_FILE; - else - $termtype = TERM_CATEGORY; - - return sprintf(" AND " . (($table) ? dbesc($table) . '.' : '') . "id in (select term.oid from term where term.type = %d and term.term = '%s' and term.uid = " . (($table) ? dbesc($table) . '.' : '') . "uid ) ", - intval($termtype), - protect_sprintf(dbesc($s)) - ); -} - -function term_query($table,$s,$type = TERM_UNKNOWN) { - - return sprintf(" AND " . (($table) ? dbesc($table) . '.' : '') . "id in (select term.oid from term where term.type = %d and term.term = '%s' and term.uid = " . (($table) ? dbesc($table) . '.' : '') . "uid ) ", - intval($type), - protect_sprintf(dbesc($s)) - ); -} - -// ex. given music,video return <music><video> or [music][video] -function file_tag_list_to_file($list,$type = 'file') { - $tag_list = ''; - if(strlen($list)) { - $list_array = explode(",",$list); - if($type == 'file') { - $lbracket = '['; - $rbracket = ']'; - } - else { - $lbracket = '<'; - $rbracket = '>'; - } - - foreach($list_array as $item) { - if(strlen($item)) { - $tag_list .= $lbracket . file_tag_encode(trim($item)) . $rbracket; - } - } - } - return $tag_list; -} - -// ex. given <music><video>[friends], return music,video or friends -function file_tag_file_to_list($file,$type = 'file') { - $matches = false; - $list = ''; - if($type == 'file') { - $cnt = preg_match_all('/\[(.*?)\]/',$file,$matches,PREG_SET_ORDER); - } - else { - $cnt = preg_match_all('/<(.*?)>/',$file,$matches,PREG_SET_ORDER); - } - if($cnt) { - foreach($matches as $mtch) { - if(strlen($list)) - $list .= ','; - $list .= file_tag_decode($mtch[1]); - } - } - - return $list; -} - -function file_tag_update_pconfig($uid,$file_old,$file_new,$type = 'file') { - // $file_old - categories previously associated with an item - // $file_new - new list of categories for an item - - if(! intval($uid)) - return false; - - if($file_old == $file_new) - return true; - - $saved = get_pconfig($uid,'system','filetags'); - if(strlen($saved)) { - if($type == 'file') { - $lbracket = '['; - $rbracket = ']'; - } - else { - $lbracket = '<'; - $rbracket = '>'; - } - - $filetags_updated = $saved; - - // check for new tags to be added as filetags in pconfig - $new_tags = array(); - $check_new_tags = explode(",",file_tag_file_to_list($file_new,$type)); - - foreach($check_new_tags as $tag) { - if(! stristr($saved,$lbracket . file_tag_encode($tag) . $rbracket)) - $new_tags[] = $tag; - } - - $filetags_updated .= file_tag_list_to_file(implode(",",$new_tags),$type); - - // check for deleted tags to be removed from filetags in pconfig - $deleted_tags = array(); - $check_deleted_tags = explode(",",file_tag_file_to_list($file_old,$type)); - - foreach($check_deleted_tags as $tag) { - if(! stristr($file_new,$lbracket . file_tag_encode($tag) . $rbracket)) - $deleted_tags[] = $tag; - } - - foreach($deleted_tags as $key => $tag) { - $r = q("select file from item where uid = %d " . file_tag_file_query('item',$tag,$type), - intval($uid) - ); - - if(count($r)) { - unset($deleted_tags[$key]); - } - else { - $filetags_updated = str_replace($lbracket . file_tag_encode($tag) . $rbracket,'',$filetags_updated); - } - } - - if($saved != $filetags_updated) { - set_pconfig($uid,'system','filetags', $filetags_updated); - } - return true; - } - else - if(strlen($file_new)) { - set_pconfig($uid,'system','filetags', $file_new); - } - return true; -} - -function store_item_tag($uid,$iid,$otype,$type,$term,$url = '') { - if(! $term) - return false; - $r = q("select * from term - where uid = %d and oid = %d and otype = %d and type = %d - and term = '%s' and url = '%s' ", - intval($uid), - intval($iid), - intval($otype), - intval($type), - dbesc($term), - dbesc($url) - ); - if(count($r)) - return false; - $r = q("insert into term (uid, oid, otype, type, term, url) - values( %d, %d, %d, %d, '%s', '%s') ", - intval($uid), - intval($iid), - intval($otype), - intval($type), - dbesc($term), - dbesc($url) - ); - return $r; -} - -function get_terms_oftype($arr,$type) { - $ret = array(); - if(! (is_array($arr) && count($arr))) - return $ret; - - if(! is_array($type)) - $type = array($type); - - foreach($type as $t) - foreach($arr as $x) - if($x['type'] == $t) - $ret[] = $x; - return $ret; -} - -function format_term_for_display($term) { - $s = ''; - if($term['type'] == TERM_HASHTAG) - $s .= '#'; - elseif($term['type'] == TERM_MENTION) - $s .= '@'; - - if($term['url']) $s .= '<a target="extlink" href="' . $term['url'] . '">' . htmlspecialchars($term['term']) . '</a>'; - else $s .= htmlspecialchars($term['term']); - return $s; -} + switch($item['resource_type']) { + case 'photo': + $post_type = t('photo'); + break; + case 'event': + $post_type = t('event'); + break; + default: + $post_type = t('status'); + if($item['mid'] != $item['parent_mid']) + $post_type = t('comment'); + break; + } -function file_tag_save_file($uid,$item,$file) { - $result = false; - if(! intval($uid)) - return false; + if(strlen($item['verb']) && (! activity_match($item['verb'],ACTIVITY_POST))) + $post_type = t('activity'); - $r = q("select file from item where id = %d and uid = %d limit 1", - intval($item), - intval($uid) - ); - if(count($r)) { - if(! stristr($r[0]['file'],'[' . file_tag_encode($file) . ']')) - q("update item set file = '%s' where id = %d and uid = %d limit 1", - dbesc($r[0]['file'] . '[' . file_tag_encode($file) . ']'), - intval($item), - intval($uid) - ); - $saved = get_pconfig($uid,'system','filetags'); - if((! strlen($saved)) || (! stristr($saved,'[' . file_tag_encode($file) . ']'))) - set_pconfig($uid,'system','filetags',$saved . '[' . file_tag_encode($file) . ']'); - info( t('Item filed') ); - } - return true; + return $post_type; } -function file_tag_unsave_file($uid,$item,$file,$cat = false) { - $result = false; - if(! intval($uid)) - return false; - - if($cat == true) - $pattern = '<' . file_tag_encode($file) . '>' ; - else - $pattern = '[' . file_tag_encode($file) . ']' ; - - - $r = q("select file from item where id = %d and uid = %d limit 1", - intval($item), - intval($uid) - ); - if(! count($r)) - return false; - - q("update item set file = '%s' where id = %d and uid = %d limit 1", - dbesc(str_replace($pattern,'',$r[0]['file'])), - intval($item), - intval($uid) - ); - - $r = q("select file from item where uid = %d and deleted = 0 " . file_tag_file_query('item',$file,(($cat) ? 'category' : 'file')), - intval($uid) - ); - - if(! count($r)) { - $saved = get_pconfig($uid,'system','filetags'); - set_pconfig($uid,'system','filetags',str_replace($pattern,'',$saved)); - - } - return true; -} function normalise_openid($s) { return trim(str_replace(array('http://','https://'),array('',''),$s),'/'); @@ -1667,7 +1483,7 @@ function normalise_openid($s) { function undo_post_tagging($s) { $matches = null; - $cnt = preg_match_all('/([@#])\[url=(.*?)\](.*?)\[\/url\]/ism',$s,$matches,PREG_SET_ORDER); + $cnt = preg_match_all('/([@#])\[zrl=(.*?)\](.*?)\[\/zrl\]/ism',$s,$matches,PREG_SET_ORDER); if($cnt) { foreach($matches as $mtch) { $s = str_replace($mtch[0], $mtch[1] . $mtch[3],$s); @@ -1757,7 +1573,7 @@ function ids_to_querystr($arr,$idx = 'id') { // author_xchan and owner_xchan. If $abook is true also include the abook info. // This is needed in the API to save extra per item lookups there. -function xchan_query(&$items,$abook = false) { +function xchan_query(&$items,$abook = true) { $arr = array(); if($items && count($items)) { foreach($items as $item) { @@ -1769,8 +1585,10 @@ function xchan_query(&$items,$abook = false) { } if(count($arr)) { if($abook) { - $chans = q("select * from xchan left join hubloc on hubloc_hash = xchan_hash left join abook on abook_xchan = xchan_hash - where xchan_hash in (" . implode(',', $arr) . ") and ( hubloc_flags & " . intval(HUBLOC_FLAGS_PRIMARY) . " )"); + $chans = q("select * from xchan left join hubloc on hubloc_hash = xchan_hash left join abook on abook_xchan = xchan_hash and abook_channel = %d + where xchan_hash in (" . implode(',', $arr) . ") and ( hubloc_flags & " . intval(HUBLOC_FLAGS_PRIMARY) . " )", + intval($item['uid']) + ); } else { $chans = q("select xchan.*,hubloc.* from xchan left join hubloc on hubloc_hash = xchan_hash @@ -1849,117 +1667,61 @@ function stringify_array_elms(&$arr,$escape = false) { */ function jindent($json) { - $result = ''; - $pos = 0; - $strLen = strlen($json); - $indentStr = ' '; - $newLine = "\n"; - $prevChar = ''; - $outOfQuotes = true; - - for ($i=0; $i<=$strLen; $i++) { - - // Grab the next character in the string. - $char = substr($json, $i, 1); - - // Are we inside a quoted string? - if ($char == '"' && $prevChar != '\\') { - $outOfQuotes = !$outOfQuotes; - - // If this character is the end of an element, - // output a new line and indent the next line. - } else if(($char == '}' || $char == ']') && $outOfQuotes) { - $result .= $newLine; - $pos --; - for ($j=0; $j<$pos; $j++) { - $result .= $indentStr; - } - } - - // Add the character to the result string. - $result .= $char; - - // If the last character was the beginning of an element, - // output a new line and indent the next line. - if (($char == ',' || $char == '{' || $char == '[') && $outOfQuotes) { - $result .= $newLine; - if ($char == '{' || $char == '[') { - $pos ++; - } - - for ($j = 0; $j < $pos; $j++) { - $result .= $indentStr; - } - } - - $prevChar = $char; - } - - return $result; -} - - -// Tag cloud functions - need to be adpated to this database format + $result = ''; + $pos = 0; + $strLen = strlen($json); + $indentStr = ' '; + $newLine = "\n"; + $prevChar = ''; + $outOfQuotes = true; + for ($i=0; $i<=$strLen; $i++) { -function tagadelic($uid, $count = 0, $type = TERM_HASHTAG) { + // Grab the next character in the string. + $char = substr($json, $i, 1); - // Fetch tags - $r = q("select term, count(term) as total from term - where uid = %d and type = %d - and otype = %d - group by term order by total desc %s", - intval($uid), - intval($type), - intval(TERM_OBJ_POST), - ((intval($count)) ? "limit $count" : '') - ); - - if(! $r) - return array(); - - // Find minimum and maximum log-count. - $tags = array(); - $min = 1e9; - $max = -1e9; - - $x = 0; - foreach($r as $rr) { - $tags[$x][0] = $rr['term']; - $tags[$x][1] = log($rr['total']); - $tags[$x][2] = 0; - $min = min($min,$tags[$x][1]); - $max = max($max,$tags[$x][1]); - $x ++; - } - - usort($tags,'tags_sort'); - - $range = max(.01, $max - $min) * 1.0001; - - for($x = 0; $x < count($tags); $x ++) { - $tags[$x][2] = 1 + floor(5 * ($tags[$x][1] - $min) / $range); + // Are we inside a quoted string? + if ($char == '"' && $prevChar != '\\') { + $outOfQuotes = !$outOfQuotes; + + // If this character is the end of an element, + // output a new line and indent the next line. + } else if(($char == '}' || $char == ']') && $outOfQuotes) { + $result .= $newLine; + $pos --; + for ($j=0; $j<$pos; $j++) { + $result .= $indentStr; + } + } + + // Add the character to the result string. + $result .= $char; + + // If the last character was the beginning of an element, + // output a new line and indent the next line. + if (($char == ',' || $char == '{' || $char == '[') && $outOfQuotes) { + $result .= $newLine; + if ($char == '{' || $char == '[') { + $pos ++; + } + + for ($j = 0; $j < $pos; $j++) { + $result .= $indentStr; + } + } + + $prevChar = $char; } - return $tags; + return $result; } -function tags_sort($a,$b) { - if($a[0] == $b[0]) - return 0; - return((strtolower($a[0]) < strtolower($b[0])) ? -1 : 1); -} +function json_decode_plus($s) { -function tagblock($link,$uid,$count = 0,$type = TERM_HASHTAG) { - $tab = 0; - $r = tagadelic($uid,$count,$type); + $x = json_decode($s,true); + if(! $x) + $x = json_decode(str_replace(array('\\"','\\\\'),array('"','\\'),$s),true); + return $x; - if($r) { - echo '<div class="tags" align="center">'; - foreach($r as $rr) { - echo '<a href="'.$link .'/' . '?f=&tag=' . urlencode($rr[0]).'" class="tag'.$rr[2].'">'.$rr[0].'</a> '; - } - echo '</div>'; - } -} +}
\ No newline at end of file diff --git a/include/zot.php b/include/zot.php index 524f958ad..d1bc03bc2 100644 --- a/include/zot.php +++ b/include/zot.php @@ -1,4 +1,4 @@ -<?php +<?php /** @file */ require_once('include/crypto.php'); require_once('include/items.php'); @@ -47,34 +47,6 @@ function zot_get_hubloc($arr,$primary = false) { } -function zot_notify($channel,$url,$type = 'notify',$recipients = null, $remote_key = null) { - - $params = array( - 'type' => $type, - 'sender' => json_encode(array( - 'guid' => $channel['channel_guid'], - 'guid_sig' => base64url_encode(rsa_sign($channel['channel_guid'],$channel['channel_prvkey'])), - 'url' => z_root(), - 'url_sig' => base64url_encode(rsa_sign(z_root(),$channel['channel_prvkey'])) - )), - 'callback' => '/post', - 'version' => ZOT_REVISION - ); - - - if($recipients) - $params['recipients'] = json_encode($recipients); - - // Hush-hush ultra top-secret mode - - if($remote_key) { - $params = aes_encapsulate($params,$remote_key); - } - - $x = z_post_url($url,$params); - return($x); -} - /* * * zot_build_packet builds a notification packet that you can either @@ -117,10 +89,33 @@ function zot_build_packet($channel,$type = 'notify',$recipients = null, $remote_ } +/** + * @function: zot_zot + * @param: string $url + * @param: array $data + * + * @returns: array => see z_post_url for returned data format + */ + + + function zot_zot($url,$data) { return z_post_url($url,array('data' => $data)); } +/** + * @function: zot_finger + * + * Look up information about channel + * @param: string $webbie + * does not have to be host qualified e.g. 'foo' is treated as 'foo@thishub' + * @param: array $channel + * (optional), if supplied permissions will be enumerated specifically for $channel + * + * @returns: array => see z_post_url and mod/zfinger.php + */ + + function zot_finger($webbie,$channel) { @@ -135,6 +130,11 @@ function zot_finger($webbie,$channel) { $xchan_addr = $address . '@' . $host; + if((! $address) || (! $xchan_addr)) { + logger('zot_finger: no address :' . $webbie); + return array('success' => false); + } + $r = q("select xchan.*, hubloc.* from xchan left join hubloc on xchan_hash = hubloc_hash where xchan_addr = '%s' and (hubloc_flags & %d) limit 1", @@ -152,7 +152,7 @@ function zot_finger($webbie,$channel) { $rhs = '/.well-known/zot-info'; $https = ((strpos($url,'https://') === 0) ? true : false); - logger('zot_finger: ' . $url, LOGGER_DEBUG); + logger('zot_finger: ' . $address . ' at ' . $url, LOGGER_DEBUG); if($channel) { $postvars = array( @@ -191,6 +191,15 @@ function zot_finger($webbie,$channel) { } +/** + * @function: zot_refresh + * + * zot_refresh is typically invoked when somebody has changed permissions of a channel and they are notified + * to fetch new permissions via a finger operation. This may result in a new connection (abook entry) being added to a local channel + * and it may result in auto-permissions being granted. + * + */ + function zot_refresh($them,$channel = null) { logger('zot_refresh: them: ' . print_r($them,true), LOGGER_DATA); @@ -329,37 +338,25 @@ function zot_refresh($them,$channel = null) { intval(ABOOK_FLAG_SELF) ); if($z) - proc_run('php','include/notifier.php','permissions_update',$z[0]['abook_id']); + proc_run('php','include/notifier.php','permission_update',$z[0]['abook_id']); } } } } - else { - - logger('zot_refresh: importing profile if available'); - - // Are we a directory server of some kind? - $dirmode = intval(get_config('system','directory_mode')); - if($dirmode != DIRECTORY_MODE_NORMAL) { - if(array_key_exists('profile',$x) && is_array($x['profile'])) { - import_directory_profile($x['hash'],$x['profile']); - } - else { - // they may have made it private - $r = q("delete from xprof where xprof_hash = '%s' limit 1", - dbesc($x['hash']) - ); - $r = q("delete from xtag where xtag_hash = '%s' limit 1", - dbesc($x['hash']) - ); - } - } - } return true; } return false; } +/** + * @function: zot_gethub + * + * A guid and a url, both signed by the sender, distinguish a known sender at a known location + * This function looks these up to see if the channel is known. If not, we will need to verify it. + * @returns: array => hubloc record + */ + + function zot_gethub($arr) { @@ -378,7 +375,7 @@ function zot_gethub($arr) { return $r[0]; } } - logger('zot_gethub: not found', LOGGER_DEBUG); + logger('zot_gethub: not found: ' . print_r($arr,true), LOGGER_DEBUG); return null; } @@ -417,6 +414,16 @@ function zot_register_hub($arr) { function import_xchan($arr) { $ret = array('success' => false); + $dirmode = intval(get_config('system','directory_mode')); + + $changed = false; + + if(! (is_array($arr) && array_key_exists('success',$arr) && $arr['success'])) { + logger('import_xchan: invalid data packet: ' . print_r($arr,true)); + $ret['message'] = t('Invalid data packet'); + return $ret; + } + $xchan_hash = base64url_encode(hash('whirlpool',$arr['guid'] . $arr['guid_sig'], true)); $import_photos = false; @@ -427,6 +434,9 @@ function import_xchan($arr) { return $ret; } + + logger('import_xchan: ' . $xchan_hash, LOGGER_DEBUG); + $r = q("select * from xchan where xchan_hash = '%s' limit 1", dbesc($xchan_hash) ); @@ -454,21 +464,47 @@ function import_xchan($arr) { $new_flags = $r[0]['xchan_flags']; - if(($r[0]['xchan_name_date'] != $arr['name_updated']) || ($r[0]['xchan_connurl'] != $arr['connections_url']) || ($r[0]['xchan_flags'] != $new_flags)) { - $r = q("update xchan set xchan_name = '%s', xchan_name_date = '%s', xchan_connurl = '%s', xchan_flags = %d where xchan_hash = '%s' limit 1", + if(($r[0]['xchan_name_date'] != $arr['name_updated']) + || ($r[0]['xchan_connurl'] != $arr['connections_url']) + || ($r[0]['xchan_flags'] != $new_flags) + || ($r[0]['xchan_addr'] != $arr['address']) + || ($r[0]['xchan_url'] != $arr['url'])) { + $r = q("update xchan set xchan_name = '%s', xchan_name_date = '%s', xchan_connurl = '%s', xchan_flags = %d, + xchan_addr = '%s', xchan_url = '%s' where xchan_hash = '%s' limit 1", dbesc($arr['name']), dbesc($arr['name_updated']), dbesc($arr['connections_url']), intval($new_flags), + dbesc($arr['address']), + dbesc($arr['url']), dbesc($xchan_hash) ); + + logger('import_xchan: existing: ' . print_r($r[0],true), LOGGER_DATA); + logger('import_xchan: new: ' . print_r($arr,true), LOGGER_DATA); + + update_modtime($xchan_hash); + $changed = true; } } else { $import_photos = true; + + + if((($arr['site']['directory_mode'] === 'standalone') || ($dirmode & DIRECTORY_MODE_STANDALONE)) +&& ($arr['site']['url'] != z_root())) + $arr['searchable'] = false; + + $hidden = (1 - intval($arr['searchable'])); + + if($hidden) + $new_flags = XCHAN_FLAGS_HIDDEN; + else + $new_flags = 0; + $x = q("insert into xchan ( xchan_hash, xchan_guid, xchan_guid_sig, xchan_pubkey, xchan_photo_mimetype, - xchan_photo_l, xchan_addr, xchan_url, xchan_connurl, xchan_name, xchan_network, xchan_photo_date, xchan_name_date) - values ( '%s', '%s', '%s', '%s' , '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s') ", + xchan_photo_l, xchan_addr, xchan_url, xchan_connurl, xchan_name, xchan_network, xchan_photo_date, xchan_name_date, xchan_flags) + values ( '%s', '%s', '%s', '%s' , '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d) ", dbesc($xchan_hash), dbesc($arr['guid']), dbesc($arr['guid_sig']), @@ -481,15 +517,18 @@ function import_xchan($arr) { dbesc($arr['name']), dbesc('zot'), dbesc($arr['photo_updated']), - dbesc($arr['name_updated']) + dbesc($arr['name_updated']), + intval($new_flags) ); + update_modtime($xchan_hash); + $changed = true; } if($import_photos) { - require_once("Photo.php"); + require_once('include/photo/photo_driver.php'); $photos = import_profile_photo($arr['photo'],$xchan_hash); $r = q("update xchan set xchan_photo_date = '%s', xchan_photo_l = '%s', xchan_photo_m = '%s', xchan_photo_s = '%s', xchan_photo_mimetype = '%s' @@ -501,9 +540,20 @@ function import_xchan($arr) { dbesc($photos[3]), dbesc($xchan_hash) ); + + update_modtime($xchan_hash); + $changed = true; } + // what we are missing for true hub independence is for any changes in the primary hub to + // get reflected not only in the hublocs, but also to update the URLs and addr in the appropriate xchan + if($arr['locations']) { + + $xisting = q("select hubloc_id, hubloc_url from hubloc where hubloc_hash = '%s'", + dbesc($xchan_hash) + ); + foreach($arr['locations'] as $location) { if(! rsa_verify($location['url'],base64url_decode($location['url_sig']),$arr['key'])) { logger('import_xchan: Unable to verify site signature for ' . $location['url']); @@ -511,20 +561,44 @@ function import_xchan($arr) { continue; } - $r = q("select * from hubloc where hubloc_hash = '%s' and hubloc_url = '%s' limit 1", + for($x = 0; $x < count($xisting); $x ++) { + if($xisting[$x]['hubloc_url'] == $location['url']) { + $xisting[$x]['updated'] = true; + } + } + + $r = q("select * from hubloc where hubloc_hash = '%s' and hubloc_guid = '%s' and hubloc_guid_sig = '%s' + and hubloc_url = '%s' and hubloc_url_sig = '%s' limit 1", dbesc($xchan_hash), - dbesc($location['url']) + dbesc($arr['guid']), + dbesc($arr['guid_sig']), + dbesc($location['url']), + dbesc($location['url_sig']) ); if($r) { - if(($r[0]['hubloc_flags'] & HUBLOC_FLAGS_PRIMARY) && (! $location['primary'])) { + logger('import_xchan: hub exists: ' . $location['url']); + if((($r[0]['hubloc_flags'] & HUBLOC_FLAGS_PRIMARY) && (! $location['primary'])) + || ((! ($r[0]['hubloc_flags'] & HUBLOC_FLAGS_PRIMARY)) && ($location['primary']))) { $r = q("update hubloc set hubloc_flags = (hubloc_flags ^ %d) where hubloc_id = %d limit 1", intval(HUBLOC_FLAGS_PRIMARY), intval($r[0]['hubloc_id']) ); } + update_modtime($xchan_hash); + $changed = true; continue; } + // new hub claiming to be primary. Make it so. + + if(intval($location['primary'])) { + $r = q("update hubloc set hubloc_flags = (hubloc_flags ^ %d) where hubloc_hash = '%s' and (hubloc_flags & %d )", + intval(HUBLOC_FLAGS_PRIMARY), + dbesc($xchan_hash), + intval(HUBLOC_FLAGS_PRIMARY) + ); + } + logger('import_xchan: new hub: ' . $location['url']); $r = q("insert into hubloc ( hubloc_guid, hubloc_guid_sig, hubloc_hash, hubloc_addr, hubloc_flags, hubloc_url, hubloc_url_sig, hubloc_host, hubloc_callback, hubloc_sitekey) values ( '%s','%s','%s','%s', %d ,'%s','%s','%s','%s','%s')", dbesc($arr['guid']), @@ -538,8 +612,61 @@ function import_xchan($arr) { dbesc($location['callback']), dbesc($location['sitekey']) ); + update_modtime($xchan_hash); + $changed = true; + } + // get rid of any hubs we have for this channel which weren't reported. + if($xisting) { + foreach($xisting as $x) { + if(! array_key_exists('updated',$x)) { + logger('import_xchan: removing unreferenced hub location ' . $x['hubloc_url']); + $r = q("delete from hubloc where hubloc_id = %d limit 1", + intval($x['hubloc_id']) + ); + update_modtime($xchan_hash); + $changed = true; + } + } + } + + } + + // Are we a directory server of some kind? + + if($dirmode != DIRECTORY_MODE_NORMAL) { + if(array_key_exists('profile',$arr) && is_array($arr['profile'])) { + $profile_changed = import_directory_profile($xchan_hash,$arr['profile']); + if($profile_changed) { + update_modtime($xchan_hash); + $changed = true; + } + } + else { + logger('import_xchan: profile not available - hiding'); + // they may have made it private + $r = q("delete from xprof where xprof_hash = '%s' limit 1", + dbesc($xchan_hash) + ); + $r = q("delete from xtag where xtag_hash = '%s' limit 1", + dbesc($xchan_hash) + ); + } + } + + if(array_key_exists('site',$arr) && is_array($arr['site'])) { + $profile_changed = import_site($arr['site'],$arr['key']); + if($profile_changed) { + update_modtime($xchan_hash); + $changed = true; } + } + + + + if($changed) { + // send out a directory mirror update packet if we're a directory server or some kind + } @@ -586,6 +713,18 @@ function zot_process_response($hub,$arr,$outq) { logger('zot_process_response: ' . print_r($x,true), LOGGER_DATA); } +/** + * @function: zot_fetch + * + * We received a notification packet (in mod/post.php) that a message is waiting for us, and we've verified the sender. + * Now send back a pickup message, using our message tracking ID ($arr['secret']), which we will sign. + * The entire pickup message is encrypted with the remote site's public key. + * If everything checks out on the remote end, we will receive back a packet containing one or more messages, + * which will be processed before returning. + * + */ + + function zot_fetch($arr) { logger('zot_fetch: ' . print_r($arr,true), LOGGER_DATA); @@ -594,7 +733,7 @@ function zot_fetch($arr) { $ret_hub = zot_gethub($arr['sender']); if(! $ret_hub) { - logger('zot_fetch: not ret_hub'); + logger('zot_fetch: no hub: ' . print_r($arr['sender'],true)); return; } @@ -620,11 +759,17 @@ function zot_fetch($arr) { return $result; } +/** + * @function zot_import + * + * Process an incoming array of messages which were obtained via pickup, and + * import, update, delete as directed. + * + * The message types handled here are 'activity' (e.g. posts), 'mail' and 'profile' + */ function zot_import($arr) { -// logger('zot_import: ' . print_r($arr,true), LOGGER_DATA); - $data = json_decode($arr['body'],true); if(! $data) { @@ -632,14 +777,10 @@ function zot_import($arr) { return array(); } -// logger('zot_import: data1: ' . print_r($data,true)); - if(array_key_exists('iv',$data)) { $data = json_decode(aes_unencapsulate($data,get_config('system','prvkey')),true); } - logger('zot_import: data' . print_r($data,true), LOGGER_DATA); - $incoming = $data['pickup']; $return = array(); @@ -692,10 +833,12 @@ function zot_import($arr) { if($i['message']) { if($i['message']['type'] === 'activity') { $arr = get_item_elements($i['message']); + if(! array_key_exists('created',$arr)) { logger('Activity rejected: probable failure to lookup author/owner. ' . print_r($i['message'],true)); continue; } + logger('Activity received: ' . print_r($arr,true), LOGGER_DATA); logger('Activity recipients: ' . print_r($deliveries,true), LOGGER_DATA); @@ -722,6 +865,16 @@ function zot_import($arr) { $result = process_profile_delivery($i['notify']['sender'],$arr,$deliveries); } + elseif($i['message']['type'] === 'channel_sync') { +// $arr = get_channelsync_elements($i['message']); + + $arr = $i['message']; + + logger('Channel sync received: ' . print_r($arr,true), LOGGER_DATA); + logger('Channel sync recipients: ' . print_r($deliveries,true), LOGGER_DATA); + + $result = process_channel_sync_delivery($i['notify']['sender'],$arr,$deliveries); + } } if($result) $return = array_merge($return,$result); @@ -780,7 +933,7 @@ function public_recips($msg) { $x = array(); $r = array_merge($r,$x); - + logger('public_recips: ' . print_r($r,true), LOGGER_DATA); return $r; } @@ -832,7 +985,7 @@ function allowed_public_recips($msg) { $condensed_recips[] = $rr['hash']; $results = array(); - $r = q("select channel_hash as hash from channel left join abook on abook_channel = channel_id where abook_hash = '%s' ", + $r = q("select channel_hash as hash from channel left join abook on abook_channel = channel_id where abook_xchan = '%s' ", dbesc($hash) ); if($r) { @@ -857,34 +1010,52 @@ function process_delivery($sender,$arr,$deliveries,$relay) { ); if(! $r) { - $result[] = array($d['hash'],'not found'); + $result[] = array($d['hash'],'recipients not found'); continue; } $channel = $r[0]; - $perm = (($arr['uri'] == $arr['parent_uri']) ? 'send_stream' : 'post_comments'); + $tag_delivery = tgroup_check($channel['channel_id'],$arr); + + $perm = (($arr['mid'] == $arr['parent_mid']) ? 'send_stream' : 'post_comments'); + + // This is our own post, possibly coming from a channel clone + + if($arr['owner_xchan'] == $d['hash']) { + $arr['item_flags'] = $arr['item_flags'] | ITEM_WALL; + } + else { + // clear the wall flag if it is set + if($arr['item_flags'] & ITEM_WALL) { + $arr['item_flags'] = ($arr['item_flags'] ^ ITEM_WALL); + } + } - if(! perm_is_allowed($channel['channel_id'],$sender['hash'],$perm)) { + if((! perm_is_allowed($channel['channel_id'],$sender['hash'],$perm)) && (! $tag_delivery)) { logger("permission denied for delivery {$channel['channel_id']}"); - $result[] = array($d['hash'],'permission denied'); + $result[] = array($d['hash'],'permission denied',$channel['channel_name'] . ' <' . $channel['channel_address'] . '@' . get_app()->get_hostname() . '>'); continue; } if($arr['item_restrict'] & ITEM_DELETED) { + + // remove_community_tag is a no-op if this isn't a community tag activity + remove_community_tag($sender,$arr,$channel['channel_id']); + $item_id = delete_imported_item($sender,$arr,$channel['channel_id']); - $result[] = array($d['hash'],'deleted'); + $result[] = array($d['hash'],'deleted',$channel['channel_name'] . ' <' . $channel['channel_address'] . '@' . get_app()->get_hostname() . '>'); if($relay && $item_id) { logger('process_delivery: invoking relay'); proc_run('php','include/notifier.php','relay',intval($item_id)); - $result[] = array($d['hash'],'relayed'); + $result[] = array($d['hash'],'relayed',$channel['channel_name'] . ' <' . $channel['channel_address'] . '@' . get_app()->get_hostname() . '>'); } continue; } - // for events, extract the event info and create and event linked to an item + // for events, extract the event info and create an event linked to an item if((x($arr,'obj_type')) && (activity_match($arr['obj_type'],ACTIVITY_OBJ_EVENT))) { require_once('include/event.php'); @@ -894,13 +1065,13 @@ function process_delivery($sender,$arr,$deliveries,$relay) { $ev['uid'] = $channel['channel_id']; $ev['account'] = $channel['channel_account_id']; $ev['edited'] = $arr['edited']; - $ev['uri'] = $arr['uri']; + $ev['mid'] = $arr['mid']; $ev['private'] = $arr['item_private']; // is this an edit? - $r = q("SELECT resource_id FROM item where uri = '%s' and uid = %d and resource_type = 'event' limit 1", - dbesc($arr['uri']), + $r = q("SELECT resource_id FROM item where mid = '%s' and uid = %d and resource_type = 'event' limit 1", + dbesc($arr['mid']), intval($channel['channel_id']) ); if($r) { @@ -909,34 +1080,37 @@ function process_delivery($sender,$arr,$deliveries,$relay) { $xyz = event_store($ev); - $result = array($d['hash'],'event processed'); + $result = array($d['hash'],'event processed',$channel['channel_name'] . ' <' . $channel['channel_address'] . '@' . get_app()->get_hostname() . '>'); continue; } } - $r = q("select id, edited from item where uri = '%s' and uid = %d limit 1", - dbesc($arr['uri']), + $r = q("select id, edited from item where mid = '%s' and uid = %d limit 1", + dbesc($arr['mid']), intval($channel['channel_id']) ); if($r) { - if($arr['edited'] > $r[0]['edited']) + if($arr['edited'] > $r[0]['edited']) { + $arr['id'] = $r[0]['id']; + $arr['uid'] = $channel['channel_id']; update_imported_item($sender,$arr,$channel['channel_id']); - $result[] = array($d['hash'],'updated'); + } + $result[] = array($d['hash'],'updated',$channel['channel_name'] . ' <' . $channel['channel_address'] . '@' . get_app()->get_hostname() . '>'); $item_id = $r[0]['id']; } else { $arr['aid'] = $channel['channel_account_id']; $arr['uid'] = $channel['channel_id']; $item_id = item_store($arr); - $result[] = array($d['hash'],'posted'); + $result[] = array($d['hash'],(($item_id) ? 'posted' : 'storage failed'),$channel['channel_name'] . ' <' . $channel['channel_address'] . '@' . get_app()->get_hostname() . '>'); } if($relay && $item_id) { logger('process_delivery: invoking relay'); proc_run('php','include/notifier.php','relay',intval($item_id)); - $result[] = array($d['hash'],'relayed'); + $result[] = array($d['hash'],'relayed',$channel['channel_name'] . ' <' . $channel['channel_address'] . '@' . get_app()->get_hostname() . '>'); } } @@ -949,8 +1123,71 @@ function process_delivery($sender,$arr,$deliveries,$relay) { } +function remove_community_tag($sender,$arr,$uid) { + + if(! (activity_match($arr['verb'],ACTIVITY_TAG) && ($arr['obj_type'] == ACTIVITY_OBJ_TAGTERM))) + return; + + logger('remove_community_tag: invoked'); + + + if(! get_pconfig($uid,'system','blocktags')) { + logger('remove_community tag: permission denied.'); + return; + } + + $r = q("select * from item where mid = '%s' and uid = %d limit 1", + dbesc($arr['mid']), + intval($uid) + ); + if(! $r) { + logger('remove_community_tag: no item'); + return; + } + + if(($sender['hash'] != $r[0]['owner_xchan']) && ($sender['hash'] != $r[0]['author_xchan'])) { + logger('remove_community_tag: sender not authorised.'); + return; + } + + $i = $r[0]; + + if($i['target']) + $i['target'] = json_decode_plus($i['target']); + if($i['object']) + $i['object'] = json_decode_plus($i['object']); + + if(! ($i['target'] && $i['object'])) { + logger('remove_community_tag: no target/object'); + return; + } + + $message_id = $i['target']['id']; + + $r = q("select id from item where mid = '%s' and uid = %d limit 1", + dbesc($message_id), + intval($uid) + ); + if(! $r) { + logger('remove_community_tag: no parent message'); + return; + } + + $x = q("delete from term where uid = %d and oid = %d and otype = %d and type = %d and term = '%s' and url = '%s' limit 1", + intval($uid), + intval($r[0]['id']), + intval(TERM_OBJ_POST), + intval(TERM_HASHTAG), + dbesc($i['object']['title']), + dbesc(get_rel_link($i['object']['link'],'alternate')) + ); + + return; +} + function update_imported_item($sender,$item,$uid) { -// FIXME + + item_store_update($item); logger('update_imported_item'); } @@ -960,10 +1197,10 @@ function delete_imported_item($sender,$item,$uid) { logger('delete_imported_item invoked',LOGGER_DEBUG); $r = q("select id from item where ( author_xchan = '%s' or owner_xchan = '%s' ) - and uri = '%s' and uid = %d limit 1", + and mid = '%s' and uid = %d limit 1", dbesc($sender['hash']), dbesc($sender['hash']), - dbesc($item['uri']), + dbesc($item['mid']), intval($uid) ); @@ -978,37 +1215,56 @@ function delete_imported_item($sender,$item,$uid) { } function process_mail_delivery($sender,$arr,$deliveries) { + + + $result = array(); foreach($deliveries as $d) { $r = q("select * from channel where channel_hash = '%s' limit 1", dbesc($d['hash']) ); - if(! $r) + if(! $r) { + $result[] = array($d['hash'],'not found'); continue; + } $channel = $r[0]; if(! perm_is_allowed($channel['channel_id'],$sender['hash'],'post_mail')) { logger("permission denied for mail delivery {$channel['channel_id']}"); + $result[] = array($d['hash'],'permission denied',$channel['channel_name']); continue; } - $r = q("select id from mail where uri = '%s' and channel_id = %d limit 1", - dbesc($arr['uri']), + $r = q("select id from mail where mid = '%s' and channel_id = %d limit 1", + dbesc($arr['mid']), intval($channel['channel_id']) ); if($r) { - logger('duplicate mail received'); + if($arr['mail_flags'] & MAIL_RECALLED) { + $x = q("delete from mail where id = %d and channel_id = %d limit 1", + intval($r[0]['id']), + intval($channel['channel_id']) + ); + $result[] = array($d['hash'],'mail recalled',$channel['channel_name']); + logger('mail_recalled'); + } + else { + $result[] = array($d['hash'],'duplicate mail received',$channel['channel_name']); + logger('duplicate mail received'); + } continue; } else { $arr['account_id'] = $channel['channel_account_id']; $arr['channel_id'] = $channel['channel_id']; $item_id = mail_store($arr); + $result[] = array($d['hash'],'mail delivered',$channel['channel_name']); } } + return $result; } function process_profile_delivery($sender,$arr,$deliveries) { @@ -1019,17 +1275,26 @@ function process_profile_delivery($sender,$arr,$deliveries) { import_directory_profile($sender['hash'],$arr); } + +/* + * @function import_directory_profile + * + * @returns boolean $updated if something changed + * + */ + function import_directory_profile($hash,$profile) { logger('import_directory_profile', LOGGER_DEBUG); if(! $hash) - return; + return false; $arr = array(); $arr['xprof_hash'] = $hash; $arr['xprof_desc'] = (($profile['description']) ? htmlentities($profile['description'], ENT_COMPAT,'UTF-8',false) : ''); $arr['xprof_dob'] = datetime_convert('','',$profile['birthday'],'Y-m-d'); // !!!! check this for 0000 year + $arr['xprof_age'] = (($profile['age']) ? intval($profile['age']) : 0); $arr['xprof_gender'] = (($profile['gender']) ? htmlentities($profile['gender'], ENT_COMPAT,'UTF-8',false) : ''); $arr['xprof_marital'] = (($profile['marital']) ? htmlentities($profile['marital'], ENT_COMPAT,'UTF-8',false) : ''); $arr['xprof_sexual'] = (($profile['sexual']) ? htmlentities($profile['sexual'], ENT_COMPAT,'UTF-8',false) : ''); @@ -1043,47 +1308,60 @@ function import_directory_profile($hash,$profile) { import_directory_keywords($hash,$profile['keywords']); foreach($profile['keywords'] as $kw) { $kw = trim(htmlentities($kw,ENT_COMPAT,'UTF-8',false)); + $kw = trim($kw,','); } $clean[] = $kw; } $arr['xprof_keywords'] = implode(' ',$clean); - $r = q("select * from xprof where xprof_hash = '%s' limit 1", dbesc($hash) ); if($r) { - $x = q("update xprof set - xprof_desc = '%s', - xprof_dob = '%s', - xprof_gender = '%s', - xprof_marital = '%s', - xprof_sexual = '%s', - xprof_locale = '%s', - xprof_region = '%s', - xprof_postcode = '%s', - xprof_country = '%s', - xprof_keywords = '%s' - where xprof_hash = '%s' limit 1", - dbesc($arr['xprof_desc']), - dbesc($arr['xprof_dob']), - dbesc($arr['xprof_gender']), - dbesc($arr['xprof_marital']), - dbesc($arr['xprof_sexual']), - dbesc($arr['xprof_locale']), - dbesc($arr['xprof_region']), - dbesc($arr['xprof_postcode']), - dbesc($arr['xprof_country']), - dbesc($arr['xprof_keywords']), - dbesc($arr['xprof_hash']) - ); + $update = false; + foreach($r[0] as $k => $v) { + if((array_key_exists($k,$arr)) && ($arr[$k] != $v)) { + $update = true; + break; + } + } + if($update) { + $x = q("update xprof set + xprof_desc = '%s', + xprof_dob = '%s', + xprof_age = %d, + xprof_gender = '%s', + xprof_marital = '%s', + xprof_sexual = '%s', + xprof_locale = '%s', + xprof_region = '%s', + xprof_postcode = '%s', + xprof_country = '%s', + xprof_keywords = '%s' + where xprof_hash = '%s' limit 1", + dbesc($arr['xprof_desc']), + dbesc($arr['xprof_dob']), + intval($arr['xprof_age']), + dbesc($arr['xprof_gender']), + dbesc($arr['xprof_marital']), + dbesc($arr['xprof_sexual']), + dbesc($arr['xprof_locale']), + dbesc($arr['xprof_region']), + dbesc($arr['xprof_postcode']), + dbesc($arr['xprof_country']), + dbesc($arr['xprof_keywords']), + dbesc($arr['xprof_hash']) + ); + } } else { - $x = q("insert into xprof (xprof_hash, xprof_desc, xprof_dob, xprof_gender, xprof_marital, xprof_sexual, xprof_locale, xprof_region, xprof_postcode, xprof_country, xprof_keywords) values ('%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s') ", + $update = true; + $x = q("insert into xprof (xprof_hash, xprof_desc, xprof_dob, xprof_age, xprof_gender, xprof_marital, xprof_sexual, xprof_locale, xprof_region, xprof_postcode, xprof_country, xprof_keywords) values ('%s', '%s', '%s', %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s') ", dbesc($arr['xprof_hash']), dbesc($arr['xprof_desc']), dbesc($arr['xprof_dob']), + intval($arr['xprof_age']), dbesc($arr['xprof_gender']), dbesc($arr['xprof_marital']), dbesc($arr['xprof_sexual']), @@ -1095,7 +1373,12 @@ function import_directory_profile($hash,$profile) { ); } - return; + $d = array('xprof' => $arr, 'profile' => $profile, 'update' => $update); + call_hooks('import_directory_profile', $d); + + if($d['update']) + update_modtime($arr['xprof_hash']); + return $d['update']; } function import_directory_keywords($hash,$keywords) { @@ -1125,9 +1408,289 @@ function import_directory_keywords($hash,$keywords) { } foreach($clean as $x) { if(! in_array($x,$existing)) - $r = q("insert int xtag ( xtag_hash, xtag_term) values ( '%s' ,'%s' )", + $r = q("insert into xtag ( xtag_hash, xtag_term) values ( '%s' ,'%s' )", dbesc($hash), dbesc($x) ); } -}
\ No newline at end of file +} + + +function update_modtime($hash) { + $r = q("select * from updates where ud_hash = '%s' limit 1", + dbesc($hash) + ); + if($r) + q("update updates set ud_date = '%s' where ud_hash = '%s' limit 1", + dbesc(datetime_convert()), + dbesc($hash) + ); + else + q("insert into updates (ud_hash, ud_date) values ( '%s', '%s' )", + dbesc($hash), + dbesc(datetime_convert()) + ); +} + + +function import_site($arr,$pubkey) { + if( (! is_array($arr)) || (! $arr['url']) || (! $arr['url_sig'])) + return false; + + if(! rsa_verify($arr['url'],base64url_decode($arr['url_sig']),$pubkey)) { + logger('import_site: bad url_sig'); + return false; + } + + $update = false; + + $r = q("select * from site where site_url = '%s' limit 1", + dbesc($arr['url']) + ); + if($r) + $update = true; + + $site_directory = 0; + if($arr['directory_mode'] == 'normal') + $site_directory = DIRECTORY_MODE_NORMAL; + + if($arr['directory_mode'] == 'primary') + $site_directory = DIRECTORY_MODE_PRIMARY; + if($arr['directory_mode'] == 'secondary') + $site_directory = DIRECTORY_MODE_SECONDARY; + if($arr['directory_mode'] == 'standalone') + $site_directory = DIRECTORY_MODE_STANDALONE; + + $register_policy = 0; + if($arr['register_policy'] == 'closed') + $register_policy = REGISTER_CLOSED; + if($arr['register_policy'] == 'open') + $register_policy = REGISTER_OPEN; + if($arr['register_policy'] == 'approve') + $register_policy = REGISTER_APPROVE; + + if($update) { + $r = q("update site set site_flags = %d, site_directory = '%s', site_register = %d, site_update = '%s' + where site_url = '%s' limit 1", + intval($site_directory), + dbesc(htmlentities($arr['directory_url'],ENT_COMPAT,'UTF-8',false)), + intval($register_policy), + dbesc(datetime_convert()), + dbesc(htmlentities($arr['url'],ENT_COMPAT,'UTF-8',false)) + ); + if(! $r) { + logger('import_site: update failed. ' . print_r($arr,true)); + } + } + else { + $r = q("insert into site ( site_url, site_flags, site_update, site_directory, site_register ) + values ( '%s', %d, '%s', '%s', %d )", + dbesc(htmlentities($arr['url'],ENT_COMPAT,'UTF-8',false)), + intval($site_directory), + dbesc(datetime_convert()), + dbesc(htmlentities($arr['directory_url'],ENT_COMPAT,'UTF-8',false)), + intval($register_policy) + ); + if(! $r) { + logger('import_site: record create failed. ' . print_r($arr,true)); + } + } + + return $r; + +} + + + +/** + * Send a zot packet to all hubs where this channel is duplicated, refreshing + * such things as personal settings, channel permissions, address book updates, etc. + */ + +function build_sync_packet($uid = 0, $packet = null) { + + $a = get_app(); + + logger('build_sync_packet'); + + if(! $uid) + $uid = local_user(); + + if(! $uid) + return; + + $r = q("select * from channel where channel_id = %d limit 1", + intval($uid) + ); + if(! $r) + return; + + $channel = $r[0]; + + $h = q("select * from hubloc where hubloc_hash = '%s'", + dbesc($channel['channel_hash']) + ); + + if(! $h) + return; + + $synchubs = array(); + + foreach($h as $x) { + if($x['hubloc_host'] == $a->get_hostname()) + continue; + $synchubs[] = $x; + } + + if(! $synchubs) + return; + + $r = q("select xchan_guid, xchan_guid_sig from xchan where xchan_hash = '%s' limit 1", + dbesc($channel['channel_hash']) + ); + if(! $r) + return; + + $env_recips = array(); + $env_recips[] = array('guid' => $r[0]['xchan_guid'],'guid_sig' => $r[0]['xchan_guid_sig']); + + $info = (($packet) ? $packet : array()); + $info['type'] = 'channel_sync'; + + if(array_key_exists($uid,$a->config) && array_key_exists('transient',$a->config[$uid])) { + $settings = $a->config[$uid]['transient']; + if($settings) { + $info['config'] = $settings; + } + } + + if($channel) { + $info['channel'] = array(); + foreach($channel as $k => $v) { + + // filter out any joined tables like xchan + + if(strpos($k,'channel_') !== 0) + continue; + + // don't pass these elements, they should not be synchronised + + $disallowed = array('channel_id','channel_account_id','channel_primary','channel_prvkey','channel_address'); + + if(in_array($k,$disallowed)) + continue; + + $info['channel'][$k] = $v; + } + } + + $interval = ((get_config('system','delivery_interval') !== false) + ? intval(get_config('system','delivery_interval')) : 2 ); + + + logger('build_sync_packet: packet: ' . print_r($info,true), LOGGER_DATA); + + foreach($synchubs as $hub) { + $hash = random_string(); + $n = zot_build_packet($channel,'notify',$env_recips,$hub['hubloc_sitekey'],$hash); + q("insert into outq ( outq_hash, outq_account, outq_channel, outq_posturl, outq_async, outq_created, outq_updated, outq_notify, outq_msg ) values ( '%s', %d, %d, '%s', %d, '%s', '%s', '%s', '%s' )", + dbesc($hash), + intval($channel['channel_account']), + intval($channel['channel_id']), + dbesc($hub['hubloc_callback']), + intval(1), + dbesc(datetime_convert()), + dbesc(datetime_convert()), + dbesc($n), + dbesc(json_encode($info)) + ); + + proc_run('php','include/deliver.php',$hash); + if($interval) + @time_sleep_until(microtime(true) + (float) $interval); + } + + +} + +function process_channel_sync_delivery($sender,$arr,$deliveries) { + +// FIXME - this will sync red structures (channel, pconfig and abook). Eventually we need to make this application agnostic. +// TODO: missing group membership changes + + $result = array(); + + foreach($deliveries as $d) { + $r = q("select * from channel where channel_hash = '%s' limit 1", + dbesc($d['hash']) + ); + + if(! $r) { + $result[] = array($d['hash'],'not found'); + continue; + } + + $channel = $r[0]; + + if($channel['channel_hash'] != $sender['hash']) { + logger('process_channel_sync_delivery: possible forgery. Sender ' . $sender['hash'] . ' is not ' . $channel['channel_hash']); + $result[] = array($d['hash'],'channel mismatch',$channel['channel_name']); + continue; + } + + if(array_key_exists('config',$arr) && is_array($arr['config']) && count($arr['config'])) { + foreach($arr['config'] as $cat => $k) { + foreach($arr['config'][$cat] as $k => $v) + set_pconfig($channel['channel_id'],$cat,$k,$v); + } + } + + if(array_key_exists('channel',$arr) && is_array($arr['channel']) && count($arr['channel'])) { + $disallowed = array('channel_id','channel_account_id','channel_primary','channel_prvkey', 'channel_address'); + + $clean = array(); + foreach($arr['channel'] as $k => $v) { + if(in_array($k,$disallowed)) + continue; + $clean[$k] = $v; + } + if(count($clean)) { + foreach($clean as $k => $v) { + $r = dbq("UPDATE channel set " . dbesc($k) . " = '" . dbesc($v) + . "' where channel_id = " . intval($channel['channel_id']) . " limit 1"); + } + } + } + + + if(array_key_exists('abook',$arr) && is_array($arr['abook']) && count($arr['abook'])) { + + $disallowed = array('abook_id','abook_account','abook_channel'); + + $clean = array(); + foreach($arr['abook'] as $abook) { + foreach($abook as $k => $v) { + if(in_array($k,$disallowed)) + continue; + $clean[$k] = $v; + } + + if(! array_key_exists('abook_xchan',$clean)) + continue; + + if(count($clean)) { + foreach($clean as $k => $v) { + $r = dbq("UPDATE abook set " . dbesc($k) . " = '" . dbesc($v) + . "' where abook_xchan = '" . dbesc($clean['abook_xchan']) . "' and abook_channel = " . intval($channel['channel_id']) + . " limit 1"); + } + } + } + } + + $result[] = array($d['hash'],'channel sync updated',$channel['channel_name']); + + + } + return $result; +} |