X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=include%2Fitems.php;h=7b43bf500a60c9bc55bc5053b6271841b893ca1f;hb=4b8f3a4194b8fbec19e8a085fcd16e9369fbfcd2;hp=87d6ab4794bf0d71435ecd29b6c336cabfce8418;hpb=bcca817b9ab8f0a9dc0af56d55a3a347056a4494;p=friendica.git diff --git a/include/items.php b/include/items.php index 87d6ab4794..7b43bf500a 100755 --- a/include/items.php +++ b/include/items.php @@ -4,6 +4,8 @@ require_once('include/bbcode.php'); require_once('include/oembed.php'); require_once('include/salmon.php'); require_once('include/crypto.php'); +require_once('include/Photo.php'); + function get_feed_for(&$a, $dfrn_id, $owner_nick, $last_update, $direction = 0) { @@ -278,7 +280,99 @@ function construct_activity_target($item) { } return ''; -} +} + +/* limit_body_size() + * + * 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) { + + logger('limit_body_size: start', LOGGER_DEBUG); + + $maxlen = get_max_import_size(); + + // If the length of the body, including the embedded images, is smaller + // than the maximum, then don't waste time looking for the images + if($maxlen && (strlen($body) > $maxlen)) { + + logger('limit_body_size: the total body length exceeds the limit', LOGGER_DEBUG); + + $orig_body = $body; + $new_body = ''; + $textlen = 0; + $max_found = false; + + $img_start = strpos($orig_body, '[img'); + $img_st_close = ($img_start !== false ? strpos(substr($orig_body, $img_start), ']') : false); + $img_end = ($img_start !== false ? strpos(substr($orig_body, $img_start), '[/img]') : false); + while(($img_st_close !== false) && ($img_end !== false)) { + + $img_st_close++; // make it point to AFTER the closing bracket + $img_end += $img_start; + $img_end += strlen('[/img]'); + + if(! strcmp(substr($orig_body, $img_start + $img_st_close, 5), 'data:')) { + // This is an embedded image + + if( ($textlen + $img_start) > $maxlen ) { + if($textlen < $maxlen) { + logger('limit_body_size: the limit happens before an embedded image', LOGGER_DEBUG); + $new_body = $new_body . substr($orig_body, 0, $maxlen - $textlen); + $textlen = $maxlen; + } + } + else { + $new_body = $new_body . substr($orig_body, 0, $img_start); + $textlen += $img_start; + } + + $new_body = $new_body . substr($orig_body, $img_start, $img_end - $img_start); + } + else { + + if( ($textlen + $img_end) > $maxlen ) { + if($textlen < $maxlen) { + logger('limit_body_size: the limit happens before the end of a non-embedded image', LOGGER_DEBUG); + $new_body = $new_body . substr($orig_body, 0, $maxlen - $textlen); + $textlen = $maxlen; + } + } + else { + $new_body = $new_body . substr($orig_body, 0, $img_end); + $textlen += $img_end; + } + } + $orig_body = substr($orig_body, $img_end); + + if($orig_body === false) // in case the body ends on a closing image tag + $orig_body = ''; + + $img_start = strpos($orig_body, '[img'); + $img_st_close = ($img_start !== false ? strpos(substr($orig_body, $img_start), ']') : false); + $img_end = ($img_start !== false ? strpos(substr($orig_body, $img_start), '[/img]') : false); + } + + if( ($textlen + strlen($orig_body)) > $maxlen) { + if($textlen < $maxlen) { + logger('limit_body_size: the limit happens after the end of the last image', LOGGER_DEBUG); + $new_body = $new_body . substr($orig_body, 0, $maxlen - $textlen); + $textlen = $maxlen; + } + } + else { + logger('limit_body_size: the text size with embedded images extracted did not violate the limit', LOGGER_DEBUG); + $new_body = $new_body . $orig_body; + $textlen += strlen($orig_body); + } + + return $new_body; + } + else + return $body; +}} function title_is_body($title, $body) { @@ -352,7 +446,7 @@ function get_atom_elements($feed,$item) { $res['author-avatar'] = unxmlify($link['attribs']['']['href']); } } - } + } $rawactor = $item->get_item_tags(NAMESPACE_ACTIVITY, 'actor'); @@ -384,7 +478,7 @@ function get_atom_elements($feed,$item) { $res['author-avatar'] = unxmlify($link['attribs']['']['href']); } } - } + } $rawactor = $feed->get_feed_tags(NAMESPACE_ACTIVITY, 'subject'); @@ -409,7 +503,7 @@ function get_atom_elements($feed,$item) { $res['app'] = strip_tags(unxmlify($apps[0]['attribs']['']['source'])); if($res['app'] === 'web') $res['app'] = 'OStatus'; - } + } // base64 encoded json structure representing Diaspora signature @@ -442,9 +536,8 @@ function get_atom_elements($feed,$item) { $res['body'] = notags(base64url_decode($res['body'])); } - $maxlen = get_max_import_size(); - if($maxlen && (strlen($res['body']) > $maxlen)) - $res['body'] = substr($res['body'],0, $maxlen); + + $res['body'] = limit_body_size($res['body']); // It isn't certain at this point whether our content is plaintext or html and we'd be foolish to trust // the content type. Our own network only emits text normally, though it might have been converted to @@ -485,6 +578,7 @@ function get_atom_elements($feed,$item) { $res['body'] = escape_tags($res['body']); } + // this tag is obsolete but we keep it for really old sites $allow = $item->get_item_tags(NAMESPACE_DFRN,'comment-allow'); @@ -553,7 +647,7 @@ function get_atom_elements($feed,$item) { foreach($base as $link) { if(!x($res, 'owner-avatar') || !$res['owner-avatar']) { - if($link['attribs']['']['rel'] === 'photo' || $link['attribs']['']['rel'] === 'avatar') + if($link['attribs']['']['rel'] === 'photo' || $link['attribs']['']['rel'] === 'avatar') $res['owner-avatar'] = unxmlify($link['attribs']['']['href']); } } @@ -693,10 +787,41 @@ function get_atom_elements($feed,$item) { $res['target'] .= '' . "\n"; } + // 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"]; + $author = $child[SIMPLEPIE_NAMESPACE_ATOM_10]["author"][0]["child"][SIMPLEPIE_NAMESPACE_ATOM_10]; + $uri = $author["uri"][0]["data"]; + $name = $author["name"][0]["data"]; + $avatar = @array_shift($author["link"][2]["attribs"]); + $avatar = $avatar["href"]; + + if (($name != "") and ($uri != "") and ($avatar != "") and ($message != "")) { + $res["owner-name"] = $res["author-name"]; + $res["owner-link"] = $res["author-link"]; + $res["owner-avatar"] = $res["author-avatar"]; + + $res["author-name"] = $name; + $res["author-link"] = $uri; + $res["author-avatar"] = $avatar; + + $res["body"] = html2bbcode($message); + } + } + $arr = array('feed' => $feed, 'item' => $item, 'result' => $res); 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; } @@ -752,6 +877,14 @@ function item_store($arr,$force_parent = false) { $arr['body'] = strip_tags($arr['body']); + if (version_compare(PHP_VERSION, '5.3.0', '>=')) { + require_once('Text/LanguageDetect.php'); + $naked_body = preg_replace('/\[(.+?)\]/','',$arr['body']); + $l = new Text_LanguageDetect; + $lng = $l->detectConfidence($naked_body); + $arr['postopts'] = (($lng['language']) ? 'lang=' . $lng['language'] . ';' . $lng['confidence'] : ''); + } + $arr['wall'] = ((x($arr,'wall')) ? intval($arr['wall']) : 0); $arr['uri'] = ((x($arr,'uri')) ? notags(trim($arr['uri'])) : random_string()); $arr['extid'] = ((x($arr,'extid')) ? notags(trim($arr['extid'])) : ''); @@ -792,6 +925,8 @@ function item_store($arr,$force_parent = false) { $arr['origin'] = ((x($arr,'origin')) ? intval($arr['origin']) : 0 ); $arr['guid'] = ((x($arr,'guid')) ? notags(trim($arr['guid'])) : get_guid()); + + $arr['thr-parent'] = $arr['parent-uri']; if($arr['parent-uri'] === $arr['uri']) { $parent_id = 0; $parent_deleted = 0; @@ -817,9 +952,8 @@ function item_store($arr,$force_parent = false) { // and re-attach to the conversation parent. if($r[0]['uri'] != $r[0]['parent-uri']) { - $arr['thr-parent'] = $arr['parent-uri']; $arr['parent-uri'] = $r[0]['parent-uri']; - $z = q("SELECT `id` FROM `item` WHERE `uri` = '%s' AND `parent-uri` = '%s' AND `uid` = %d + $z = q("SELECT * FROM `item` WHERE `uri` = '%s' AND `parent-uri` = '%s' AND `uid` = %d ORDER BY `id` ASC LIMIT 1", dbesc($r[0]['parent-uri']), dbesc($r[0]['parent-uri']), @@ -859,7 +993,6 @@ function item_store($arr,$force_parent = false) { if($force_parent) { logger('item_store: $force_parent=true, reply converted to top-level post.'); $parent_id = 0; - $arr['thr-parent'] = $arr['parent-uri']; $arr['parent-uri'] = $arr['uri']; $arr['gravity'] = 0; } @@ -1644,7 +1777,7 @@ function consume_feed($xml,$importer,&$contact, &$hub, $datedir = 0, $pass = 0) // Now process the feed - if($feed->get_item_quantity()) { + if($feed->get_item_quantity()) { logger('consume_feed: feed item count = ' . $feed->get_item_quantity()); @@ -1657,7 +1790,7 @@ function consume_feed($xml,$importer,&$contact, &$hub, $datedir = 0, $pass = 0) foreach($items as $item) { - $is_reply = false; + $is_reply = false; $item_id = $item->get_id(); $rawthread = $item->get_item_tags( NAMESPACE_THREAD,'in-reply-to'); if(isset($rawthread[0]['attribs']['']['ref'])) { @@ -1671,7 +1804,7 @@ function consume_feed($xml,$importer,&$contact, &$hub, $datedir = 0, $pass = 0) continue; // Have we seen it? If not, import it. - + $item_id = $item->get_id(); $datarray = get_atom_elements($feed,$item); @@ -1975,6 +2108,121 @@ function local_delivery($importer,$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'); + +// Fallback should not be needed here. If it isn't DFRN it won't have DFRN updated tags +// 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' ); @@ -2214,6 +2462,7 @@ function local_delivery($importer,$data) { $is_a_remote_delete = false; + // POSSIBLE CLEANUP --> Why select so many fields when only forum_mode and wall are used? $r = q("select `item`.`id`, `item`.`uri`, `item`.`tag`, `item`.`forum_mode`,`item`.`origin`,`item`.`wall`, `contact`.`name`, `contact`.`url`, `contact`.`thumb` from `item` LEFT JOIN `contact` ON `contact`.`id` = `item`.`contact-id` @@ -2227,7 +2476,7 @@ function local_delivery($importer,$data) { intval($importer['importer_uid']) ); if($r && count($r)) - $is_a_remote_delete = true; + $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 @@ -2298,7 +2547,8 @@ function local_delivery($importer,$data) { } if($item['uri'] == $item['parent-uri']) { - $r = q("UPDATE `item` SET `deleted` = 1, `edited` = '%s', `changed` = '%s' + $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()), @@ -2307,7 +2557,8 @@ function local_delivery($importer,$data) { ); } else { - $r = q("UPDATE `item` SET `deleted` = 1, `edited` = '%s', `changed` = '%s' + $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()), @@ -2369,22 +2620,32 @@ function local_delivery($importer,$data) { // 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`.`tag`, `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 + $top_uri = $parent_uri; + + $r = q("select `item`.`parent-uri` from `item` + WHERE `item`.`uri` = '%s' LIMIT 1", - dbesc($parent_uri), - dbesc($parent_uri), - dbesc($parent_uri), - intval($importer['importer_uid']) + dbesc($parent_uri) ); - if($r && count($r)) - $is_a_remote_comment = true; + if($r && count($r)) { + $top_uri = $r[0]['parent-uri']; + + // POSSIBLE CLEANUP --> Why select so many fields when only forum_mode and wall are used? + $r = q("select `item`.`id`, `item`.`uri`, `item`.`tag`, `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($top_uri), + dbesc($top_uri), + dbesc($top_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 @@ -2530,12 +2791,14 @@ function local_delivery($importer,$data) { $parent = 0; if($posted_id) { - $r = q("SELECT `parent` FROM `item` WHERE `id` = %d AND `uid` = %d LIMIT 1", + $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)) + if(count($r)) { $parent = $r[0]['parent']; + $parent_uri = $r[0]['parent-uri']; + } if(! $is_like) { $r1 = q("UPDATE `item` SET `last-child` = 0, `changed` = '%s' WHERE `uid` = %d AND `parent` = %d", @@ -2552,7 +2815,7 @@ function local_delivery($importer,$data) { } if($posted_id && $parent) { - + proc_run('php',"include/notifier.php","comment-import","$posted_id"); if((! $is_like) && (! $importer['self'])) { @@ -2575,7 +2838,7 @@ function local_delivery($importer,$data) { 'verb' => ACTIVITY_POST, 'otype' => 'item', 'parent' => $parent, - + 'parent_uri' => $parent_uri, )); } @@ -2686,7 +2949,7 @@ function local_delivery($importer,$data) { 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), + dbesc($top_uri), intval($importer['importer_uid']) ); @@ -2724,6 +2987,7 @@ function local_delivery($importer,$data) { 'verb' => ACTIVITY_POST, 'otype' => 'item', 'parent' => $conv_parent, + 'parent_uri' => $parent_uri )); @@ -2813,7 +3077,8 @@ function local_delivery($importer,$data) { $datarray['uid'] = $importer['importer_uid']; $datarray['contact-id'] = $importer['id']; - if(! link_compare($datarray['owner-link'],$contact['url'])) { + + 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, @@ -2824,7 +3089,57 @@ function local_delivery($importer,$data) { $datarray['owner-avatar'] = $importer['thumb']; } - $r = item_store($datarray); + $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("".unxmlify($xo->link)."",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() . '/profile/' . $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; } } @@ -3032,7 +3347,6 @@ function atom_entry($item,$type,$author,$owner,$comment = false,$cid = 0) { else $body = $item['body']; - $o = "\r\n\r\n\r\n"; if(is_array($author)) @@ -3042,7 +3356,7 @@ function atom_entry($item,$type,$author,$owner,$comment = false,$cid = 0) { if(strlen($item['owner-name'])) $o .= atom_author('dfrn:owner',$item['owner-name'],$item['owner-link'],80,80,$item['owner-avatar']); - if(($item['parent'] != $item['id']) || ($item['parent-uri'] !== $item['uri']) || ($item['thr-parent'])) { + 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 .= '' . "\r\n"; } @@ -3113,20 +3427,33 @@ function atom_entry($item,$type,$author,$owner,$comment = false,$cid = 0) { return $o; } -function fix_private_photos($s,$uid, $item = null, $cid = 0) { +function fix_private_photos($s, $uid, $item = null, $cid = 0) { $a = get_app(); logger('fix_private_photos', LOGGER_DEBUG); $site = substr($a->get_baseurl(),strpos($a->get_baseurl(),'://')); - if(preg_match("/\[img(.*?)\](.*?)\[\/img\]/is",$s,$matches)) { - $image = $matches[2]; + $orig_body = $s; + $new_body = ''; + + $img_start = strpos($orig_body, '[img'); + $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); + while( ($img_st_close !== false) && ($img_len !== false) ) { + + $img_st_close++; // make it point to AFTER the closing bracket + $image = substr($orig_body, $img_start + $img_st_close, $img_len); + logger('fix_private_photos: found photo ' . $image, LOGGER_DEBUG); + + if(stristr($image , $site . '/photo/')) { + // Only embed locally hosted photos $replace = false; $i = basename($image); $i = str_replace(array('.jpg','.png'),array('',''),$i); $x = strpos($i,'-'); + if($x) { $res = substr($i,$x+1); $i = substr($i,0,$x); @@ -3144,14 +3471,6 @@ function fix_private_photos($s,$uid, $item = null, $cid = 0) { // 3. Otherwise, if we have an item, see if the item permissions match the photo // permissions, regardless of order but first check to see if they're an exact // match to save some processing overhead. - - // Currently we only embed one private photo per message so as not to hit import - // size limits at the receiving end. - - // To embed multiples, we would need to parse out the embedded photos on message - // receipt and limit size based only on the text component. Would also need to - // ignore all photos during bbcode translation and item localisation, as these - // will hit internal regex backtrace limits. if(has_permissions($r[0])) { if($cid) { @@ -3166,15 +3485,45 @@ function fix_private_photos($s,$uid, $item = null, $cid = 0) { } } if($replace) { + $data = $r[0]['data']; + $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)) { + logger('fix_private_photos: scaling photo', LOGGER_DEBUG); + + $width = intval($match[1]); + $height = intval($match[2]); + + $ph = new Photo($data, $type); + if($ph->is_valid()) { + $ph->scaleImage(max($width, $height)); + $data = $ph->imageString(); + $type = $ph->getType(); + } + } + logger('fix_private_photos: replacing photo', LOGGER_DEBUG); - $s = str_replace($image, 'data:' . $r[0]['type'] . ';base64,' . base64_encode($r[0]['data']), $s); - logger('fix_private_photos: replaced: ' . $s, LOGGER_DATA); + $image = 'data:' . $type . ';base64,' . base64_encode($data); + logger('fix_private_photos: replaced: ' . $image, LOGGER_DATA); } } } } + + $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]')); + if($orig_body === false) + $orig_body = ''; + + $img_start = strpos($orig_body, '[img'); + $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); } - return($s); + + $new_body = $new_body . $orig_body; + + return($new_body); } @@ -3366,10 +3715,23 @@ function drop_item($id,$interactive = true) { $owner = $item['uid']; + $cid = 0; + // check if logged in user is either the author or owner of this item - if((local_user() == $item['uid']) || (remote_user() == $item['contact-id'])) { + if(is_array($_SESSION['remote'])) { + foreach($_SESSION['remote'] as $visitor) { + if($visitor['uid'] == $item['uid'] && $visitor['cid'] == $item['contact-id']) { + $cid = $visitor['cid']; + break; + } + } + } + + + if((local_user() == $item['uid']) || ($cid) || (! $interactive)) { + logger('delete item: ' . $item['id'], LOGGER_DEBUG); // delete the item $r = q("UPDATE `item` SET `deleted` = 1, `title` = '', `body` = '', `edited` = '%s', `changed` = '%s' WHERE `id` = %d LIMIT 1", @@ -3518,7 +3880,9 @@ function posted_dates($uid,$wall) { $dnow = substr($dthen,0,8) . '28'; $ret = array(); - while($dnow >= $dthen) { + // Starting with the current month, get the first and last days of every + // month down to and including the month of the first post + while(substr($dnow, 0, 7) >= substr($dthen, 0, 7)) { $dstart = substr($dnow,0,8) . '01'; $dend = substr($dnow,0,8) . get_dim(intval($dnow),intval(substr($dnow,5))); $start_month = datetime_convert('','',$dstart,'Y-m-d'); @@ -3577,7 +3941,7 @@ function store_diaspora_retract_sig($item, $user, $baseurl) { } else { $r = q("SELECT `nick`, `url` FROM `contact` WHERE `id` = '%d' LIMIT 1", - $item['contact-id'] + $item['contact-id'] // If this function gets called, drop_item() has already checked remote_user() == $item['contact-id'] ); if(count($r)) { // The below handle only works for NETWORK_DFRN. I think that's ok, because this function