X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=include%2Fitems.php;h=733cd8048410911943cc79d867b3c306e85695ac;hb=e14ab2d369391ee9c97d5568034423f303222019;hp=f204745bb4c0946477436f9f545b6b5fbf6a2031;hpb=082909fe1a861219c4f9159f8f8df5aea7546da9;p=friendica.git diff --git a/include/items.php b/include/items.php index f204745bb4..733cd80484 100644 --- a/include/items.php +++ b/include/items.php @@ -2,80 +2,34 @@ require_once('bbcode.php'); require_once('oembed.php'); +require_once('include/salmon.php'); function get_feed_for(&$a, $dfrn_id, $owner_nick, $last_update, $direction = 0) { // default permissions - anonymous user - $sql_extra = " - AND `allow_cid` = '' - AND `allow_gid` = '' - AND `deny_cid` = '' - AND `deny_gid` = '' - "; - - if(strlen($owner_nick) && ! intval($owner_nick)) { - $r = q("SELECT `uid`, `nickname`, `timezone` FROM `user` WHERE `nickname` = '%s' LIMIT 1", - dbesc($owner_nick) - ); - if(count($r)) { - $owner_id = $r[0]['uid']; - $owner_nick = $r[0]['nickname']; - $owner_tz = $r[0]['timezone']; - } - } - - $r = q("SELECT * FROM `contact` WHERE `self` = 1 AND `uid` = %d LIMIT 1", - intval($owner_id) - ); - if(count($r)) { - $owner = $r[0]; - $owner['nickname'] = $owner_nick; - } - else + if(! strlen($owner_nick)) killme(); + $sql_extra = " AND `allow_cid` = '' AND `allow_gid` = '' AND `deny_cid` = '' AND `deny_gid` = '' "; - /** - * - * Determine the next birthday, but only if the birthday is published - * in the default profile. We _could_ also look for a private profile that the - * recipient can see, but somebody could get mad at us if they start getting - * public birthday greetings when they haven't made this info public. - * - * Assuming we are able to publish this info, we are then going to convert - * the start time from the owner's timezone to UTC. - * - * This will potentially solve the problem found with some social networks - * where birthdays are converted to the viewer's timezone and salutations from - * elsewhere in the world show up on the wrong day. We will convert it to the - * viewer's timezone also, but first we are going to convert it from the birthday - * person's timezone to GMT - so the viewer may find the birthday starting at - * 6:00PM the day before, but that will correspond to midnight to the birthday person. - * - */ + $r = q("SELECT `contact`.*, `user`.`uid` AS `user_uid`, `user`.`nickname`, `user`.`timezone` + FROM `contact` LEFT JOIN `user` ON `user`.`uid` = `contact`.`uid` + WHERE `contact`.`self` = 1 AND `user`.`nickname` = '%s' LIMIT 1", + dbesc($owner_nick) + ); - $birthday = ''; + if(! count($r)) + killme(); - $p = q("SELECT `dob` FROM `profile` WHERE `is-default` = 1 AND `uid` = %d LIMIT 1", - intval($owner_id) - ); + $owner = $r[0]; + $owner_id = $owner['user_uid']; + $owner_nick = $owner['nickname']; - if($p && count($p)) { - $tmp_dob = substr($p[0]['dob'],5); - if(intval($tmp_dob)) { - $y = datetime_convert($owner_tz,$owner_tz,'now','Y'); - $bd = $y . '-' . $tmp_dob . ' 00:00'; - $t_dob = strtotime($bd); - $now = strtotime(datetime_convert($owner_tz,$owner_tz,'now')); - if($t_dob < $now) - $bd = $y + 1 . '-' . $tmp_dob . ' 00:00'; - $birthday = datetime_convert($owner_tz,'UTC',$bd,ATOM_TIME); - } - } + $birthday = feed_birthday($owner_id,$owner['timezone']); - if($dfrn_id && $dfrn_id != '*') { + if(strlen($dfrn_id)) { $sql_extra = ''; switch($direction) { @@ -101,7 +55,7 @@ function get_feed_for(&$a, $dfrn_id, $owner_nick, $last_update, $direction = 0) ); if(! count($r)) - return false; + killme(); $contact = $r[0]; $groups = init_groups_visitor($contact['id']); @@ -143,7 +97,7 @@ function get_feed_for(&$a, $dfrn_id, $owner_nick, $last_update, $direction = 0) `contact`.`thumb`, `contact`.`dfrn-id`, `contact`.`self`, `contact`.`id` AS `contact-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 + WHERE `item`.`uid` = %d AND `item`.`visible` = 1 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 @@ -155,7 +109,7 @@ function get_feed_for(&$a, $dfrn_id, $owner_nick, $last_update, $direction = 0) ); // Will check further below if this actually returned results. - // We will provide an empty feed in any case. + // We will provide an empty feed if that is the case. $items = $r; @@ -163,25 +117,9 @@ function get_feed_for(&$a, $dfrn_id, $owner_nick, $last_update, $direction = 0) $atom = ''; - $hub = get_config('system','huburl'); - - $hubxml = ''; - if(strlen($hub)) { - $hubs = explode(',', $hub); - if(count($hubs)) { - foreach($hubs as $h) { - $h = trim($h); - if(! strlen($h)) - continue; - $hubxml .= '' . "\n" ; - } - } - } - - $salmon = '' . "\n" ; - $salmon .= '' . "\n" ; - $salmon .= '' . "\n" ; + $hubxml = feed_hublinks(); + $salmon = feed_salmonlinks($owner_nick); $atom .= replace_macros($feed_template, array( '$version' => xmlify(FRIENDIKA_VERSION), @@ -214,7 +152,7 @@ function get_feed_for(&$a, $dfrn_id, $owner_nick, $last_update, $direction = 0) // public feeds get html, our own nodes use bbcode - if($dfrn_id === '*') { + if($dfrn_id === '') { $type = 'html'; } else { @@ -242,7 +180,7 @@ function construct_activity_object($item) { if($item['object']) { $o = '' . "\r\n"; - $r = @simplexml_load_string($item['object']); + $r = parse_xml_string($item['object']); if($r->type) $o .= '' . xmlify($r->type) . '' . "\r\n"; if($r->id) @@ -268,7 +206,7 @@ function construct_activity_target($item) { if($item['target']) { $o = '' . "\r\n"; - $r = @simplexml_load_string($item['target']); + $r = parse_xml_string($item['target']); if($r->type) $o .= '' . xmlify($r->type) . '' . "\r\n"; if($r->id) @@ -303,12 +241,18 @@ function get_atom_elements($feed,$item) { $res = array(); $author = $item->get_author(); - $res['author-name'] = unxmlify($author->get_name()); - $res['author-link'] = unxmlify($author->get_link()); + if($author) { + $res['author-name'] = unxmlify($author->get_name()); + $res['author-link'] = unxmlify($author->get_link()); + } + else { + $res['author-name'] = unxmlify($feed->get_title()); + $res['author-link'] = unxmlify($feed->get_permalink()); + } $res['uri'] = unxmlify($item->get_id()); $res['title'] = unxmlify($item->get_title()); $res['body'] = unxmlify($item->get_content()); - + $res['plink'] = unxmlify($item->get_link(0)); // look for a photo. We should check media size and find the best one, // but for now let's just find any author photo @@ -376,6 +320,21 @@ function get_atom_elements($feed,$item) { } + /** + * If there's a copy of the body content which is guaranteed to have survived mangling in transit, use it. + */ + + $have_real_body = false; + + $rawenv = $item->get_item_tags(NAMESPACE_DFRN, 'env'); + if($rawenv) { + $have_real_body = true; + $res['body'] = $rawenv[0]['data']; + $res['body'] = str_replace(array(' ',"\t","\r","\n"), array('','','',''),$res['body']); + // make sure nobody is trying to sneak some html tags by us + $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); @@ -390,14 +349,13 @@ function get_atom_elements($feed,$item) { // the wild, by sanitising it and converting supported tags to bbcode before we rip out any remaining // html. - - if((strpos($res['body'],'<')) || (strpos($res['body'],'>'))) { + if((strpos($res['body'],'<') !== false) || (strpos($res['body'],'>') !== false)) { $res['body'] = preg_replace('#]+>.+?' . 'http://www.youtube.com/((?:v|cp)/[A-Za-z0-9\-_=]+).+?#s', '[youtube]$1[/youtube]', $res['body']); $res['body'] = oembed_html2bbcode($res['body']); - + $config = HTMLPurifier_Config::createDefault(); $config->set('Cache.DefinitionImpl', null); @@ -410,9 +368,6 @@ function get_atom_elements($feed,$item) { $res['body'] = html2bbcode($res['body']); } - else - $res['body'] = escape_tags($res['body']); - $allow = $item->get_item_tags(NAMESPACE_DFRN,'comment-allow'); if($allow && $allow[0]['data'] == 1) @@ -439,15 +394,28 @@ function get_atom_elements($feed,$item) { $rawedited = $item->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10,'updated'); if($rawedited) - $res['edited'] = unxmlify($rawcreated[0]['data']); + $res['edited'] = unxmlify($rawedited[0]['data']); + if((x($res,'edited')) && (! (x($res,'created')))) + $res['created'] = $res['edited']; if(! $res['created']) - $res['created'] = $item->get_date(); + $res['created'] = $item->get_date('c'); if(! $res['edited']) - $res['edited'] = $item->get_date(); + $res['edited'] = $item->get_date('c'); + + + // Disallow time travelling posts + $d1 = strtotime($res['created']); + $d2 = strtotime($res['edited']); + $d3 = strtotime('now'); + + if($d1 > $d3) + $res['created'] = datetime_convert(); + if($d2 > $d3) + $res['edited'] = datetime_convert(); $rawowner = $item->get_item_tags(NAMESPACE_DFRN, 'owner'); if($rawowner[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data']) @@ -488,7 +456,45 @@ function get_atom_elements($feed,$item) { if((x($res,'verb')) && ($res['verb'] === 'http://ostatus.org/schema/1.0/unfollow')) $res['verb'] = ACTIVITY_UNFOLLOW; - + + $cats = $item->get_categories(); + if($cats) { + $tag_arr = array(); + foreach($cats as $cat) { + $term = $cat->get_term(); + if(! $term) + $term = $cat->get_label(); + $scheme = $cat->get_scheme(); + if($scheme && $term && stristr($scheme,'X-DFRN:')) + $tag_arr[] = substr($scheme,7,1) . '[url=' . unxmlify(substr($scheme,9)) . ']' . unxmlify($term) . '[/url]'; + elseif($term) + $tag_arr[] = notags(trim($term)); + } + $res['tag'] = implode(',', $tag_arr); + } + + $attach = $item->get_enclosures(); + if($attach) { + $att_arr = array(); + foreach($attach as $att) { + $len = intval($att->get_length()); + $link = str_replace(array(',','"'),array('%2D','%22'),notags(trim(unxmlify($att->get_link())))); + $title = str_replace(array(',','"'),array('%2D','%22'),notags(trim(unxmlify($att->get_title())))); + $type = str_replace(array(',','"'),array('%2D','%22'),notags(trim(unxmlify($att->get_type())))); + if(strpos($type,';')) + $type = substr($type,0,strpos($type,';')); + if((! $link) || (strpos($link,'http') !== 0)) + continue; + + if(! $title) + $title = ' '; + if(! $type) + $type = 'application/octet-stream'; + + $att_arr[] = '[attach]href="' . $link . '" size="' . $len . '" type="' . $type . '" title="' . $title . '"[/attach]'; + } + $res['attach'] = implode(',', $att_arr); + } $rawobj = $item->get_item_tags(NAMESPACE_ACTIVITY, 'object'); @@ -510,7 +516,7 @@ function get_atom_elements($feed,$item) { $body = $rawobj[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['summary'][0]['data']; // preserve a copy of the original body content in case we later need to parse out any microformat information, e.g. events $res['object'] .= '' . xmlify($body) . '' . "\n"; - if((strpos($body,'<')) || (strpos($body,'>'))) { + if((strpos($body,'<') !== false) || (strpos($body,'>') !== false)) { $body = preg_replace('#]+>.+?' . 'http://www.youtube.com/((?:v|cp)/[A-Za-z0-9\-_=]+).+?#s', '[youtube]$1[/youtube]', $body); @@ -522,8 +528,6 @@ function get_atom_elements($feed,$item) { $body = $purifier->purify($body); $body = html2bbcode($body); } - else - $body = escape_tags($body); $res['object'] .= '' . $body . '' . "\n"; } @@ -551,7 +555,7 @@ function get_atom_elements($feed,$item) { $body = $rawobj[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['summary'][0]['data']; // preserve a copy of the original body content in case we later need to parse out any microformat information, e.g. events $res['object'] .= '' . xmlify($body) . '' . "\n"; - if((strpos($body,'<')) || (strpos($body,'>'))) { + if((strpos($body,'<') !== false) || (strpos($body,'>') !== false)) { $body = preg_replace('#]+>.+?' . 'http://www.youtube.com/((?:v|cp)/[A-Za-z0-9\-_=]+).+?#s', '[youtube]$1[/youtube]', $body); @@ -563,8 +567,6 @@ function get_atom_elements($feed,$item) { $body = $purifier->purify($body); $body = html2bbcode($body); } - else - $body = escape_tags($body); $res['target'] .= '' . $body . '' . "\n"; } @@ -600,7 +602,7 @@ function encode_rel_links($links) { return xmlify($o); } -function item_store($arr) { +function item_store($arr,$force_parent = false) { if($arr['gravity']) $arr['gravity'] = intval($arr['gravity']); @@ -613,6 +615,13 @@ function item_store($arr) { if(! x($arr,'type')) $arr['type'] = 'remote'; + + // 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'] = strip_tags($arr['body']); + + $arr['wall'] = ((x($arr,'wall')) ? intval($arr['wall']) : 0); $arr['uri'] = ((x($arr,'uri')) ? notags(trim($arr['uri'])) : random_string()); $arr['author-name'] = ((x($arr,'author-name')) ? notags(trim($arr['author-name'])) : ''); @@ -636,25 +645,15 @@ function item_store($arr) { $arr['object'] = ((x($arr,'object')) ? trim($arr['object']) : ''); $arr['target-type'] = ((x($arr,'target-type')) ? notags(trim($arr['target-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['private'] = ((x($arr,'private')) ? intval($arr['private']) : 0 ); - $arr['body'] = ((x($arr,'body')) ? escape_tags(trim($arr['body'])) : ''); - - // The content body has been through a lot of filtering and transport escaping by now. - // We don't want to skip any filters, however a side effect of all this filtering - // is that ampersands and <> may have been double encoded, depending on which filter chain - // they came through. - - $arr['body'] = str_replace( - array('&amp;', '&gt;', '&lt;', '&quot;'), - array('&' , '>' , '<', '"'), - $arr['body'] - ); - - + $arr['body'] = ((x($arr,'body')) ? trim($arr['body']) : ''); + $arr['tag'] = ((x($arr,'tag')) ? notags(trim($arr['tag'])) : ''); + $arr['attach'] = ((x($arr,'attach')) ? notags(trim($arr['attach'])) : ''); if($arr['parent-uri'] === $arr['uri']) { $parent_id = 0; @@ -666,6 +665,7 @@ function item_store($arr) { else { // 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 LIMIT 1", dbesc($arr['parent-uri']), @@ -683,15 +683,29 @@ function item_store($arr) { $arr['parent-uri'] = $r[0]['parent-uri']; } - $parent_id = $r[0]['id']; - $allow_cid = $r[0]['allow_cid']; - $allow_gid = $r[0]['allow_gid']; - $deny_cid = $r[0]['deny_cid']; - $deny_gid = $r[0]['deny_gid']; + $parent_id = $r[0]['id']; + $parent_deleted = $r[0]['deleted']; + $allow_cid = $r[0]['allow_cid']; + $allow_gid = $r[0]['allow_gid']; + $deny_cid = $r[0]['deny_cid']; + $deny_gid = $r[0]['deny_gid']; + $arr['wall'] = $r[0]['wall']; } else { - logger('item_store: item parent was not found - ignoring item'); - return 0; + + // 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['thr-parent'] = $arr['parent-uri']; + $arr['parent-uri'] = $arr['uri']; + } + else { + logger('item_store: item parent was not found - ignoring item'); + return 0; + } } } @@ -722,10 +736,10 @@ function item_store($arr) { return 0; } - if($arr['parent-uri'] === $arr['uri']) + if((! $parent_id) || ($arr['parent-uri'] === $arr['uri'])) $parent_id = $current_post; - - if(strlen($allow_cid) || strlen($allow_gid) || strlen($deny_cid) || strlen($deny_gid)) + + if(strlen($allow_cid) || strlen($allow_gid) || strlen($deny_cid) || strlen($deny_gid)) $private = 1; else $private = $arr['private']; @@ -733,16 +747,29 @@ function item_store($arr) { // 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', `private` = %d WHERE `id` = %d LIMIT 1", + `deny_cid` = '%s', `deny_gid` = '%s', `private` = %d, `deleted` = %d WHERE `id` = %d LIMIT 1", intval($parent_id), dbesc($allow_cid), dbesc($allow_gid), dbesc($deny_cid), dbesc($deny_gid), intval($private), + intval($parent_deleted), intval($current_post) ); + /** + * If this is now the last-child, force all _other_ children of this parent to *not* be last-child + */ + + if($arr['last-child']) { + $r = q("UPDATE `item` SET `last-child` = 0 WHERE `parent-uri` = '%s' AND `uid` = %d AND `id` != %d", + dbesc($arr['uri']), + intval($arr['uid']), + intval($current_post) + ); + } + return $current_post; } @@ -763,7 +790,7 @@ function dfrn_deliver($owner,$contact,$atom, $dissolve = false) { $a = get_app(); - if((! strlen($contact['dfrn-id'])) && (! $contact['duplex']) && (! ($owner['page-flags'] == PAGE_COMMUNITY))) + if((! strlen($contact['issued-id'])) && (! $contact['duplex']) && (! ($owner['page-flags'] == PAGE_COMMUNITY))) return 3; $idtosend = $orig_id = (($contact['dfrn-id']) ? $contact['dfrn-id'] : $contact['issued-id']); @@ -780,7 +807,7 @@ function dfrn_deliver($owner,$contact,$atom, $dissolve = false) { if(! $rino_enable) $rino = 0; - $url = $contact['notify'] . '?dfrn_id=' . $idtosend . '&dfrn_version=' . DFRN_PROTOCOL_VERSION . (($rino) ? '&rino=1' : ''); + $url = $contact['notify'] . '&dfrn_id=' . $idtosend . '&dfrn_version=' . DFRN_PROTOCOL_VERSION . (($rino) ? '&rino=1' : ''); logger('dfrn_deliver: ' . $url); @@ -795,7 +822,13 @@ function dfrn_deliver($owner,$contact,$atom, $dissolve = false) { if(! $xml) return 3; - $res = simplexml_load_string($xml); + if(strpos($xml,'status) != 0) || (! strlen($res->challenge)) || (! strlen($res->dfrn_id))) return (($res->status) ? $res->status : 3); @@ -803,19 +836,20 @@ function dfrn_deliver($owner,$contact,$atom, $dissolve = false) { $postvars = array(); $sent_dfrn_id = hex2bin((string) $res->dfrn_id); $challenge = hex2bin((string) $res->challenge); + $dfrn_version = (float) (($res->dfrn_version) ? $res->dfrn_version : 2.0); $rino_allowed = ((intval($res->rino) === 1) ? 1 : 0); $final_dfrn_id = ''; - if(($contact['duplex'] && strlen($contact['prvkey'])) || ($owner['page-flags'] == PAGE_COMMUNITY)) { - openssl_private_decrypt($sent_dfrn_id,$final_dfrn_id,$contact['prvkey']); - openssl_private_decrypt($challenge,$postvars['challenge'],$contact['prvkey']); - } - else { + if(($contact['duplex'] && strlen($contact['pubkey'])) || ($owner['page-flags'] == PAGE_COMMUNITY)) { openssl_public_decrypt($sent_dfrn_id,$final_dfrn_id,$contact['pubkey']); openssl_public_decrypt($challenge,$postvars['challenge'],$contact['pubkey']); } + else { + openssl_private_decrypt($sent_dfrn_id,$final_dfrn_id,$contact['prvkey']); + openssl_private_decrypt($challenge,$postvars['challenge'],$contact['prvkey']); + } $final_dfrn_id = substr($final_dfrn_id, 0, strpos($final_dfrn_id, '.')); @@ -833,14 +867,14 @@ function dfrn_deliver($owner,$contact,$atom, $dissolve = false) { if($dissolve) $postvars['dissolve'] = '1'; - if(($contact['rel']) && ($contact['rel'] != REL_FAN) && (! $contact['blocked']) && (! $contact['readonly'])) { - $postvars['data'] = $atom; - } - elseif($owner['page-flags'] == PAGE_COMMUNITY) { + + if((($contact['rel']) && ($contact['rel'] != REL_FAN) && (! $contact['blocked'])) || ($owner['page-flags'] == PAGE_COMMUNITY)) { $postvars['data'] = $atom; + $postvars['perm'] = 'rw'; } else { $postvars['data'] = str_replace('1','0',$atom); + $postvars['perm'] = 'r'; } if($rino && $rino_allowed && (! $dissolve)) { @@ -849,11 +883,22 @@ function dfrn_deliver($owner,$contact,$atom, $dissolve = false) { $postvars['data'] = $data; logger('rino: sent key = ' . $key); - if(($contact['duplex'] && strlen($contact['prvkey'])) || ($owner['page-flags'] == PAGE_COMMUNITY)) { - openssl_private_encrypt($key,$postvars['key'],$contact['prvkey']); + + if($dfrn_version >= 2.1) { + if(($contact['duplex'] && strlen($contact['pubkey'])) || ($owner['page-flags'] == PAGE_COMMUNITY)) { + openssl_public_encrypt($key,$postvars['key'],$contact['pubkey']); + } + else { + openssl_private_encrypt($key,$postvars['key'],$contact['prvkey']); + } } else { - openssl_public_encrypt($key,$postvars['key'],$contact['pubkey']); + if(($contact['duplex'] && strlen($contact['prvkey'])) || ($owner['page-flags'] == PAGE_COMMUNITY)) { + openssl_private_encrypt($key,$postvars['key'],$contact['prvkey']); + } + else { + openssl_public_encrypt($key,$postvars['key'],$contact['pubkey']); + } } logger('md5 rawkey ' . md5($postvars['key'])); @@ -871,19 +916,24 @@ function dfrn_deliver($owner,$contact,$atom, $dissolve = false) { if((! $curl_stat) || (! strlen($xml))) return(-1); // timed out - $res = simplexml_load_string($xml); + if(strpos($xml,'status; - + return $res->status; } -/* +/** * * consume_feed - process atom feed and update anything/everything we might need to update * - * $xml = the (atom) feed to consume - no RSS spoken here, it might partially work since simplepie - * handles both, but we don't claim it will work well, and are reasonably certain it won't. + * $xml = the (atom) feed to consume - RSS isn't as fully supported but may work for simple feeds. + * * $importer = the contact_record (joined to user_record) of the local user who owns this relationship. * It is this person's stuff that is going to be updated. * $contact = the person who is sending us stuff. If not set, we MAY be processing a "follow" activity @@ -894,7 +944,7 @@ function dfrn_deliver($owner,$contact,$atom, $dissolve = false) { * */ -function consume_feed($xml,$importer,&$contact, &$hub, $datedir = 0) { +function consume_feed($xml,$importer,&$contact, &$hub, $datedir = 0, $secure_feed = false) { require_once('simplepie/simplepie.inc'); @@ -909,6 +959,7 @@ function consume_feed($xml,$importer,&$contact, &$hub, $datedir = 0) { if($feed->error()) logger('consume_feed: Error parsing XML: ' . $feed->error()); + $permalink = $feed->get_permalink(); // Check at the feed level for updated contact name and/or photo @@ -1050,25 +1101,18 @@ function consume_feed($xml,$importer,&$contact, &$hub, $datedir = 0) { } - // Now process the feed - if($feed->get_item_quantity()) { - // in inverse date order - if ($datedir) - $items = array_reverse($feed->get_items()); - else - $items = $feed->get_items(); - - foreach($items as $item) { + // 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; - - $rawdelete = $item->get_item_tags( NAMESPACE_TOMB, 'deleted-entry'); - if(isset($rawdelete[0]['attribs']['']['ref'])) { - $uri = $rawthread[0]['attribs']['']['ref']; + if(isset($dentry['attribs']['']['ref'])) { + $uri = $dentry['attribs']['']['ref']; $deleted = true; - if(isset($rawdelete[0]['attribs']['']['when'])) { - $when = $rawthread[0]['attribs']['']['when']; + if(isset($dentry['attribs']['']['when'])) { + $when = $dentry['attribs']['']['when']; $when = datetime_convert('UTC','UTC', $when, 'Y-m-d H:i:s'); } else @@ -1082,6 +1126,10 @@ function consume_feed($xml,$importer,&$contact, &$hub, $datedir = 0) { ); if(count($r)) { $item = $r[0]; + + if(! $item['deleted']) + logger('consume_feed: deleting item ' . $item['id'] . ' uri=' . $item['uri'], LOGGER_DEBUG); + if($item['uri'] == $item['parent-uri']) { $r = q("UPDATE `item` SET `deleted` = 1, `edited` = '%s', `changed` = '%s', `body` = '', `title` = '' @@ -1103,7 +1151,7 @@ function consume_feed($xml,$importer,&$contact, &$hub, $datedir = 0) { ); if($item['last-child']) { // ensure that last-child is set in case the comment that had it just got wiped. - $q("UPDATE `item` SET `last-child` = 0, `changed` = '%s' WHERE `parent-uri` = '%s' AND `uid` = %d ", + q("UPDATE `item` SET `last-child` = 0, `changed` = '%s' WHERE `parent-uri` = '%s' AND `uid` = %d ", dbesc(datetime_convert()), dbesc($item['parent-uri']), intval($item['uid']) @@ -1122,9 +1170,24 @@ function consume_feed($xml,$importer,&$contact, &$hub, $datedir = 0) { } } } - continue; } + } + } + // Now process the feed + + if($feed->get_item_quantity()) { + + logger('consume_feed: feed item count = ' . $feed->get_item_quantity()); + + // in inverse date order + if ($datedir) + $items = array_reverse($feed->get_items()); + else + $items = $feed->get_items(); + + + foreach($items as $item) { $is_reply = false; $item_id = $item->get_id(); @@ -1134,19 +1197,32 @@ function consume_feed($xml,$importer,&$contact, &$hub, $datedir = 0) { $parent_uri = $rawthread[0]['attribs']['']['ref']; } - if(($is_reply) && is_array($contact)) { - + // Have we seen it? If not, import it. - $item_id = $item->get_id(); - - $r = q("SELECT `uid`, `last-child`, `edited` FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1", + $item_id = $item->get_id(); + $datarray = get_atom_elements($feed,$item); + + $r = q("SELECT `uid`, `last-child`, `edited`, `body` FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1", dbesc($item_id), intval($importer['uid']) ); - // FIXME update content if 'updated' changes + + // Update content if 'updated' changes + if(count($r)) { + if((x($datarray,'edited') !== false) && (datetime_convert('UTC','UTC',$datarray['edited']) !== $r[0]['edited'])) { + $r = q("UPDATE `item` SET `body` = '%s', `edited` = '%s' WHERE `uri` = '%s' AND `uid` = %d LIMIT 1", + dbesc($datarray['body']), + dbesc(datetime_convert('UTC','UTC',$datarray['edited'])), + dbesc($item_id), + intval($importer['uid']) + ); + } + + // update last-child if it changes + $allow = $item->get_item_tags( NAMESPACE_DFRN, 'comment-allow'); if(($allow) && ($allow[0]['data'] != $r[0]['last-child'])) { $r = q("UPDATE `item` SET `last-child` = 0, `changed` = '%s' WHERE `parent-uri` = '%s' AND `uid` = %d", @@ -1163,9 +1239,10 @@ function consume_feed($xml,$importer,&$contact, &$hub, $datedir = 0) { } continue; } - $datarray = get_atom_elements($feed,$item); + $force_parent = false; if($contact['network'] === 'stat') { + $force_parent = true; if(strlen($datarray['title'])) unset($datarray['title']); $r = q("UPDATE `item` SET `last-child` = 0, `changed` = '%s' WHERE `parent-uri` = '%s' AND `uid` = %d", @@ -1188,19 +1265,37 @@ function consume_feed($xml,$importer,&$contact, &$hub, $datedir = 0) { $datarray['gravity'] = GRAVITY_LIKE; } - $r = item_store($datarray); + $r = item_store($datarray,$force_parent); continue; } else { + // Head post of a conversation. Have we seen it? If not, import it. - $item_id = $item->get_id(); - $r = q("SELECT `uid`, `last-child`, `edited` FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1", + $item_id = $item->get_id(); + + $datarray = get_atom_elements($feed,$item); + + $r = q("SELECT `uid`, `last-child`, `edited`, `body` FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1", dbesc($item_id), intval($importer['uid']) ); + + // Update content if 'updated' changes + if(count($r)) { + if((x($datarray,'edited') !== false) && (datetime_convert('UTC','UTC',$datarray['edited']) !== $r[0]['edited'])) { + $r = q("UPDATE `item` SET `body` = '%s', `edited` = '%s' WHERE `uri` = '%s' AND `uid` = %d LIMIT 1", + dbesc($datarray['body']), + dbesc(datetime_convert('UTC','UTC',$datarray['edited'])), + dbesc($item_id), + intval($importer['uid']) + ); + } + + // update last-child if it changes + $allow = $item->get_item_tags( NAMESPACE_DFRN, 'comment-allow'); if($allow && $allow[0]['data'] != $r[0]['last-child']) { $r = q("UPDATE `item` SET `last-child` = %d , `changed` = '%s' WHERE `uri` = '%s' AND `uid` = %d LIMIT 1", @@ -1212,7 +1307,6 @@ function consume_feed($xml,$importer,&$contact, &$hub, $datedir = 0) { } continue; } - $datarray = get_atom_elements($feed,$item); if(activity_match($datarray['verb'],ACTIVITY_FOLLOW)) { logger('consume-feed: New follower'); @@ -1226,7 +1320,7 @@ function consume_feed($xml,$importer,&$contact, &$hub, $datedir = 0) { if(! is_array($contact)) return; - if($contact['network'] === 'stat') { + if($contact['network'] === 'stat' || stristr($permalink,'twitter.com')) { if(strlen($datarray['title'])) unset($datarray['title']); $datarray['last-child'] = 1; @@ -1273,8 +1367,8 @@ function new_follower($importer,$contact,$datarray,$item) { // create contact record - set to readonly $r = q("INSERT INTO `contact` ( `uid`, `created`, `url`, `name`, `nick`, `photo`, `network`, `rel`, - `blocked`, `readonly`, `pending` ) - VALUES ( %d, '%s', '%s', '%s', '%s', '%s', '%s', %d, 0, 1, 1 ) ", + `blocked`, `readonly`, `pending`, `writable` ) + VALUES ( %d, '%s', '%s', '%s', '%s', '%s', '%s', %d, 0, 1, 1, 1 ) ", intval($importer['uid']), dbesc(datetime_convert()), dbesc($url), @@ -1321,7 +1415,9 @@ function new_follower($importer,$contact,$datarray,$item) { $res = mail($r[0]['email'], t("You have a new follower at ") . $a->config['sitename'], $email, - 'From: ' . t('Administrator') . '@' . $_SERVER['SERVER_NAME'] ); + 'From: ' . t('Administrator') . '@' . $_SERVER['SERVER_NAME'] . "\n" + . 'Content-type: text/plain; charset=UTF-8' . "\n" + . 'Content-transfer-encoding: 8bit' ); } } @@ -1421,8 +1517,9 @@ function atom_entry($item,$type,$author,$owner,$comment = false) { $o .= '' . xmlify($item['title']) . '' . "\r\n"; $o .= '' . xmlify(datetime_convert('UTC','UTC',$item['created'] . '+00:00',ATOM_TIME)) . '' . "\r\n"; $o .= '' . xmlify(datetime_convert('UTC','UTC',$item['edited'] . '+00:00',ATOM_TIME)) . '' . "\r\n"; + $o .= '' . base64url_encode($item['body'], true) . '' . "\r\n"; $o .= '' . xmlify(($type === 'html') ? bbcode($item['body']) : $item['body']) . '' . "\r\n"; - $o .= '' . "\r\n"; + $o .= '' . "\r\n"; if($comment) $o .= '' . intval($item['last-child']) . '' . "\r\n"; @@ -1446,6 +1543,15 @@ function atom_entry($item,$type,$author,$owner,$comment = false) { if(strlen($actarg)) $o .= $actarg; + $tags = item_getfeedtags($item); + if(count($tags)) { + foreach($tags as $t) { + $o .= '' . "\r\n"; + } + } + + $o .= item_getfeedattach($item); + $mentioned = get_mentions($item); if($mentioned) $o .= $mentioned; @@ -1456,4 +1562,93 @@ function atom_entry($item,$type,$author,$owner,$comment = false) { return $o; } + +function item_getfeedtags($item) { + $ret = array(); + $matches = false; + $cnt = preg_match_all('|\#\[url\=(.+?)\](.+?)\[\/url\]|',$item['tag'],$matches); + if($cnt) { + for($x = 0; $x < count($matches); $x ++) { + if($matches[1][$x]) + $ret[] = array('#',$matches[1][$x], $matches[2][$x]); + } + } + $matches = false; + $cnt = preg_match_all('|\@\[url\=(.+?)\](.+?)\[\/url\]|',$item['tag'],$matches); + if($cnt) { + for($x = 0; $x < count($matches); $x ++) { + if($matches[1][$x]) + $ret[] = array('#',$matches[1][$x], $matches[2][$x]); + } + } + return $ret; +} + +function item_getfeedattach($item) { + $ret = ''; + $arr = explode(',',$item['attach']); + if(count($arr)) { + foreach($arr as $r) { + $matches = false; + $cnt = preg_match('|\[attach\]href=\"(.+?)\" size=\"(.+?)\" type=\"(.+?)\" title=\"(.+?)\"\[\/attach\]|',$r,$matches); + if($cnt) { + $ret .= '