]> git.mxchange.org Git - friendica.git/blob - include/items.php
6c438f463962bff56161192b0b6b5f2d4da972d2
[friendica.git] / include / items.php
1 <?php
2
3 require_once('include/bbcode.php');
4 require_once('include/oembed.php');
5 require_once('include/salmon.php');
6 require_once('include/crypto.php');
7 require_once('include/Photo.php');
8 require_once('include/tags.php');
9 require_once('include/text.php');
10 require_once('include/email.php');
11 require_once('include/ostatus_conversation.php');
12 require_once('include/threads.php');
13
14 function get_feed_for(&$a, $dfrn_id, $owner_nick, $last_update, $direction = 0) {
15
16
17         $sitefeed    = ((strlen($owner_nick)) ? false : true); // not yet implemented, need to rewrite huge chunks of following logic
18         $public_feed = (($dfrn_id) ? false : true);
19         $starred     = false;   // not yet implemented, possible security issues
20         $converse    = false;
21
22         if($public_feed && $a->argc > 2) {
23                 for($x = 2; $x < $a->argc; $x++) {
24                         if($a->argv[$x] == 'converse')
25                                 $converse = true;
26                         if($a->argv[$x] == 'starred')
27                                 $starred = true;
28                         if($a->argv[$x] === 'category' && $a->argc > ($x + 1) && strlen($a->argv[$x+1]))
29                                 $category = $a->argv[$x+1];
30                 }
31         }
32
33
34
35         // default permissions - anonymous user
36
37         $sql_extra = " AND `allow_cid` = '' AND `allow_gid` = '' AND `deny_cid`  = '' AND `deny_gid`  = '' ";
38
39         $r = q("SELECT `contact`.*, `user`.`uid` AS `user_uid`, `user`.`nickname`, `user`.`timezone`, `user`.`page-flags`
40                 FROM `contact` INNER JOIN `user` ON `user`.`uid` = `contact`.`uid`
41                 WHERE `contact`.`self` = 1 AND `user`.`nickname` = '%s' LIMIT 1",
42                 dbesc($owner_nick)
43         );
44
45         if(! count($r))
46                 killme();
47
48         $owner = $r[0];
49         $owner_id = $owner['user_uid'];
50         $owner_nick = $owner['nickname'];
51
52         $birthday = feed_birthday($owner_id,$owner['timezone']);
53
54         if(! $public_feed) {
55
56                 $sql_extra = '';
57                 switch($direction) {
58                         case (-1):
59                                 $sql_extra = sprintf(" AND `issued-id` = '%s' ", dbesc($dfrn_id));
60                                 $my_id = $dfrn_id;
61                                 break;
62                         case 0:
63                                 $sql_extra = sprintf(" AND `issued-id` = '%s' AND `duplex` = 1 ", dbesc($dfrn_id));
64                                 $my_id = '1:' . $dfrn_id;
65                                 break;
66                         case 1:
67                                 $sql_extra = sprintf(" AND `dfrn-id` = '%s' AND `duplex` = 1 ", dbesc($dfrn_id));
68                                 $my_id = '0:' . $dfrn_id;
69                                 break;
70                         default:
71                                 return false;
72                                 break; // NOTREACHED
73                 }
74
75                 $r = q("SELECT * FROM `contact` WHERE `blocked` = 0 AND `pending` = 0 AND `contact`.`uid` = %d $sql_extra LIMIT 1",
76                         intval($owner_id)
77                 );
78
79                 if(! count($r))
80                         killme();
81
82                 $contact = $r[0];
83                 require_once('include/security.php');
84                 $groups = init_groups_visitor($contact['id']);
85
86                 if(count($groups)) {
87                         for($x = 0; $x < count($groups); $x ++) 
88                                 $groups[$x] = '<' . intval($groups[$x]) . '>' ;
89                         $gs = implode('|', $groups);
90                 }
91                 else
92                         $gs = '<<>>' ; // Impossible to match 
93
94                 $sql_extra = sprintf(" 
95                         AND ( `allow_cid` = '' OR     `allow_cid` REGEXP '<%d>' ) 
96                         AND ( `deny_cid`  = '' OR NOT `deny_cid`  REGEXP '<%d>' ) 
97                         AND ( `allow_gid` = '' OR     `allow_gid` REGEXP '%s' )
98                         AND ( `deny_gid`  = '' OR NOT `deny_gid`  REGEXP '%s') 
99                 ",
100                         intval($contact['id']),
101                         intval($contact['id']),
102                         dbesc($gs),
103                         dbesc($gs)
104                 );
105         }
106
107         if($public_feed)
108                 $sort = 'DESC';
109         else
110                 $sort = 'ASC';
111
112         if(! strlen($last_update))
113                 $last_update = 'now -30 days';
114
115         if(isset($category)) {
116                 $sql_extra .= file_tag_file_query('item',$category,'category');
117         }
118
119         if($public_feed) {
120                 if(! $converse)
121                         $sql_extra .= " AND `contact`.`self` = 1 ";
122         }
123
124         $check_date = datetime_convert('UTC','UTC',$last_update,'Y-m-d H:i:s');
125
126         $r = q("SELECT `item`.*, `item`.`id` AS `item_id`,
127                 `contact`.`name`, `contact`.`network`, `contact`.`photo`, `contact`.`url`,
128                 `contact`.`name-date`, `contact`.`uri-date`, `contact`.`avatar-date`,
129                 `contact`.`thumb`, `contact`.`dfrn-id`, `contact`.`self`,
130                 `contact`.`id` AS `contact-id`, `contact`.`uid` AS `contact-uid`,
131                 `sign`.`signed_text`, `sign`.`signature`, `sign`.`signer`
132                 FROM `item` INNER JOIN `contact` ON `contact`.`id` = `item`.`contact-id` AND `contact`.`uid` = `item`.`uid`
133                 LEFT JOIN `sign` ON `sign`.`iid` = `item`.`id`
134                 WHERE `item`.`uid` = %d AND `item`.`visible` = 1 and `item`.`moderated` = 0 AND `item`.`parent` != 0
135                 AND `item`.`wall` = 1 AND `contact`.`blocked` = 0 AND `contact`.`pending` = 0
136                 AND ( `item`.`edited` > '%s' OR `item`.`changed` > '%s' )
137                 $sql_extra
138                 ORDER BY `parent` %s, `created` ASC LIMIT 0, 300",
139                 intval($owner_id),
140                 dbesc($check_date),
141                 dbesc($check_date),
142                 dbesc($sort)
143         );
144
145         // Will check further below if this actually returned results.
146         // We will provide an empty feed if that is the case.
147
148         $items = $r;
149
150         $feed_template = get_markup_template(($dfrn_id) ? 'atom_feed_dfrn.tpl' : 'atom_feed.tpl');
151
152         $atom = '';
153
154         $hubxml = feed_hublinks();
155
156         $salmon = feed_salmonlinks($owner_nick);
157
158         $atom .= replace_macros($feed_template, array(
159                 '$version'      => xmlify(FRIENDICA_VERSION),
160                 '$feed_id'      => xmlify($a->get_baseurl() . '/profile/' . $owner_nick),
161                 '$feed_title'   => xmlify($owner['name']),
162                 '$feed_updated' => xmlify(datetime_convert('UTC', 'UTC', 'now' , ATOM_TIME)) ,
163                 '$hub'          => $hubxml,
164                 '$salmon'       => $salmon,
165                 '$name'         => xmlify($owner['name']),
166                 '$profile_page' => xmlify($owner['url']),
167                 '$photo'        => xmlify($owner['photo']),
168                 '$thumb'        => xmlify($owner['thumb']),
169                 '$picdate'      => xmlify(datetime_convert('UTC','UTC',$owner['avatar-date'] . '+00:00' , ATOM_TIME)) ,
170                 '$uridate'      => xmlify(datetime_convert('UTC','UTC',$owner['uri-date']    . '+00:00' , ATOM_TIME)) ,
171                 '$namdate'      => xmlify(datetime_convert('UTC','UTC',$owner['name-date']   . '+00:00' , ATOM_TIME)) , 
172                 '$birthday'     => ((strlen($birthday)) ? '<dfrn:birthday>' . xmlify($birthday) . '</dfrn:birthday>' : ''),
173                 '$community'    => (($owner['page-flags'] == PAGE_COMMUNITY) ? '<dfrn:community>1</dfrn:community>' : '')
174         ));
175
176         call_hooks('atom_feed', $atom);
177
178         if(! count($items)) {
179
180                 call_hooks('atom_feed_end', $atom);
181
182                 $atom .= '</feed>' . "\r\n";
183                 return $atom;
184         }
185
186         foreach($items as $item) {
187
188                 // prevent private email from leaking.
189                 if($item['network'] === NETWORK_MAIL)
190                         continue;
191
192                 // public feeds get html, our own nodes use bbcode
193
194                 if($public_feed) {
195                         $type = 'html';
196                         // catch any email that's in a public conversation and make sure it doesn't leak
197                         if($item['private'])
198                                 continue;
199                 }
200                 else {
201                         $type = 'text';
202                 }
203
204                 $atom .= atom_entry($item,$type,null,$owner,true);
205         }
206
207         call_hooks('atom_feed_end', $atom);
208
209         $atom .= '</feed>' . "\r\n";
210
211         return $atom;
212 }
213
214
215 function construct_verb($item) {
216         if($item['verb'])
217                 return $item['verb'];
218         return ACTIVITY_POST;
219 }
220
221 function construct_activity_object($item) {
222
223         if($item['object']) {
224                 $o = '<as:object>' . "\r\n";
225                 $r = parse_xml_string($item['object'],false);
226
227
228                 if(! $r)
229                         return '';
230                 if($r->type)
231                         $o .= '<as:object-type>' . xmlify($r->type) . '</as:object-type>' . "\r\n";
232                 if($r->id)
233                         $o .= '<id>' . xmlify($r->id) . '</id>' . "\r\n";
234                 if($r->title)
235                         $o .= '<title>' . xmlify($r->title) . '</title>' . "\r\n";
236                 if($r->link) {
237                         if(substr($r->link,0,1) === '<') {
238                                 // patch up some facebook "like" activity objects that got stored incorrectly
239                                 // for a couple of months prior to 9-Jun-2011 and generated bad XML.
240                                 // we can probably remove this hack here and in the following function in a few months time.
241                                 if(strstr($r->link,'&') && (! strstr($r->link,'&amp;')))
242                                         $r->link = str_replace('&','&amp;', $r->link);
243                                 $r->link = preg_replace('/\<link(.*?)\"\>/','<link$1"/>',$r->link);
244                                 $o .= $r->link;
245                         }
246                         else
247                                 $o .= '<link rel="alternate" type="text/html" href="' . xmlify($r->link) . '" />' . "\r\n";
248                 }
249                 if($r->content)
250                         $o .= '<content type="html" >' . xmlify(bbcode($r->content)) . '</content>' . "\r\n";
251                 $o .= '</as:object>' . "\r\n";
252                 return $o;
253         }
254
255         return '';
256
257
258 function construct_activity_target($item) {
259
260         if($item['target']) {
261                 $o = '<as:target>' . "\r\n";
262                 $r = parse_xml_string($item['target'],false);
263                 if(! $r)
264                         return '';
265                 if($r->type)
266                         $o .= '<as:object-type>' . xmlify($r->type) . '</as:object-type>' . "\r\n";
267                 if($r->id)
268                         $o .= '<id>' . xmlify($r->id) . '</id>' . "\r\n";
269                 if($r->title)
270                         $o .= '<title>' . xmlify($r->title) . '</title>' . "\r\n";
271                 if($r->link) {
272                         if(substr($r->link,0,1) === '<') {
273                                 if(strstr($r->link,'&') && (! strstr($r->link,'&amp;')))
274                                         $r->link = str_replace('&','&amp;', $r->link);
275                                 $r->link = preg_replace('/\<link(.*?)\"\>/','<link$1"/>',$r->link);
276                                 $o .= $r->link;
277                         }
278                         else
279                                 $o .= '<link rel="alternate" type="text/html" href="' . xmlify($r->link) . '" />' . "\r\n";
280                 }
281                 if($r->content)
282                         $o .= '<content type="html" >' . xmlify(bbcode($r->content)) . '</content>' . "\r\n";
283                 $o .= '</as:target>' . "\r\n";
284                 return $o;
285         }
286
287         return '';
288 }
289
290 /* limit_body_size()
291  *
292  *              The purpose of this function is to apply system message length limits to
293  *              imported messages without including any embedded photos in the length
294  */
295 if(! function_exists('limit_body_size')) {
296 function limit_body_size($body) {
297
298 //      logger('limit_body_size: start', LOGGER_DEBUG);
299
300         $maxlen = get_max_import_size();
301
302         // If the length of the body, including the embedded images, is smaller
303         // than the maximum, then don't waste time looking for the images
304         if($maxlen && (strlen($body) > $maxlen)) {
305
306                 logger('limit_body_size: the total body length exceeds the limit', LOGGER_DEBUG);
307
308                 $orig_body = $body;
309                 $new_body = '';
310                 $textlen = 0;
311                 $max_found = false;
312
313                 $img_start = strpos($orig_body, '[img');
314                 $img_st_close = ($img_start !== false ? strpos(substr($orig_body, $img_start), ']') : false);
315                 $img_end = ($img_start !== false ? strpos(substr($orig_body, $img_start), '[/img]') : false);
316                 while(($img_st_close !== false) && ($img_end !== false)) {
317
318                         $img_st_close++; // make it point to AFTER the closing bracket
319                         $img_end += $img_start;
320                         $img_end += strlen('[/img]');
321
322                         if(! strcmp(substr($orig_body, $img_start + $img_st_close, 5), 'data:')) {
323                                 // This is an embedded image
324
325                                 if( ($textlen + $img_start) > $maxlen ) {
326                                         if($textlen < $maxlen) {
327                                                 logger('limit_body_size: the limit happens before an embedded image', LOGGER_DEBUG);
328                                                 $new_body = $new_body . substr($orig_body, 0, $maxlen - $textlen);
329                                                 $textlen = $maxlen;
330                                         }
331                                 }
332                                 else {
333                                         $new_body = $new_body . substr($orig_body, 0, $img_start);
334                                         $textlen += $img_start;
335                                 }
336
337                                 $new_body = $new_body . substr($orig_body, $img_start, $img_end - $img_start);
338                         }
339                         else {
340
341                                 if( ($textlen + $img_end) > $maxlen ) {
342                                         if($textlen < $maxlen) {
343                                                 logger('limit_body_size: the limit happens before the end of a non-embedded image', LOGGER_DEBUG);
344                                                 $new_body = $new_body . substr($orig_body, 0, $maxlen - $textlen);
345                                                 $textlen = $maxlen;
346                                         }
347                                 }
348                                 else {
349                                         $new_body = $new_body . substr($orig_body, 0, $img_end);
350                                         $textlen += $img_end;
351                                 }
352                         }
353                         $orig_body = substr($orig_body, $img_end);
354
355                         if($orig_body === false) // in case the body ends on a closing image tag
356                                 $orig_body = '';
357
358                         $img_start = strpos($orig_body, '[img');
359                         $img_st_close = ($img_start !== false ? strpos(substr($orig_body, $img_start), ']') : false);
360                         $img_end = ($img_start !== false ? strpos(substr($orig_body, $img_start), '[/img]') : false);
361                 }
362
363                 if( ($textlen + strlen($orig_body)) > $maxlen) {
364                         if($textlen < $maxlen) {
365                                 logger('limit_body_size: the limit happens after the end of the last image', LOGGER_DEBUG);
366                                 $new_body = $new_body . substr($orig_body, 0, $maxlen - $textlen);
367                                 $textlen = $maxlen;
368                         }
369                 }
370                 else {
371                         logger('limit_body_size: the text size with embedded images extracted did not violate the limit', LOGGER_DEBUG);
372                         $new_body = $new_body . $orig_body;
373                         $textlen += strlen($orig_body);
374                 }
375
376                 return $new_body;
377         }
378         else
379                 return $body;
380 }}
381
382 function title_is_body($title, $body) {
383
384         $title = strip_tags($title);
385         $title = trim($title);
386         $title = html_entity_decode($title, ENT_QUOTES, 'UTF-8');
387         $title = str_replace(array("\n", "\r", "\t", " "), array("","","",""), $title);
388
389         $body = strip_tags($body);
390         $body = trim($body);
391         $body = html_entity_decode($body, ENT_QUOTES, 'UTF-8');
392         $body = str_replace(array("\n", "\r", "\t", " "), array("","","",""), $body);
393
394         if (strlen($title) < strlen($body))
395                 $body = substr($body, 0, strlen($title));
396
397         if (($title != $body) and (substr($title, -3) == "...")) {
398                 $pos = strrpos($title, "...");
399                 if ($pos > 0) {
400                         $title = substr($title, 0, $pos);
401                         $body = substr($body, 0, $pos);
402                 }
403         }
404
405         return($title == $body);
406 }
407
408
409
410 function get_atom_elements($feed, $item, $contact = array()) {
411
412         require_once('library/HTMLPurifier.auto.php');
413         require_once('include/html2bbcode.php');
414
415         $best_photo = array();
416
417         $res = array();
418
419         $author = $item->get_author();
420         if($author) { 
421                 $res['author-name'] = unxmlify($author->get_name());
422                 $res['author-link'] = unxmlify($author->get_link());
423         }
424         else {
425                 $res['author-name'] = unxmlify($feed->get_title());
426                 $res['author-link'] = unxmlify($feed->get_permalink());
427         }
428         $res['uri'] = unxmlify($item->get_id());
429         $res['title'] = unxmlify($item->get_title());
430         $res['body'] = unxmlify($item->get_content());
431         $res['plink'] = unxmlify($item->get_link(0));
432
433         // removing the content of the title if its identically to the body
434         // This helps with auto generated titles e.g. from tumblr
435         if (title_is_body($res["title"], $res["body"]))
436                 $res['title'] = "";
437
438         if($res['plink'])
439                 $base_url = implode('/', array_slice(explode('/',$res['plink']),0,3));
440         else
441                 $base_url = '';
442
443         // look for a photo. We should check media size and find the best one,
444         // but for now let's just find any author photo
445
446         $rawauthor = $item->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10,'author');
447
448         if($rawauthor && $rawauthor[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link']) {
449                 $base = $rawauthor[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link'];
450                 foreach($base as $link) {
451                         if(!x($res, 'author-avatar') || !$res['author-avatar']) {
452                                 if($link['attribs']['']['rel'] === 'photo' || $link['attribs']['']['rel'] === 'avatar')
453                                         $res['author-avatar'] = unxmlify($link['attribs']['']['href']);
454                         }
455                 }
456         }
457
458         $rawactor = $item->get_item_tags(NAMESPACE_ACTIVITY, 'actor');
459
460         if($rawactor && activity_match($rawactor[0]['child'][NAMESPACE_ACTIVITY]['object-type'][0]['data'],ACTIVITY_OBJ_PERSON)) {
461                 $base = $rawactor[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link'];
462                 if($base && count($base)) {
463                         foreach($base as $link) {
464                                 if($link['attribs']['']['rel'] === 'alternate' && (! $res['author-link']))
465                                         $res['author-link'] = unxmlify($link['attribs']['']['href']);
466                                 if(!x($res, 'author-avatar') || !$res['author-avatar']) {
467                                         if($link['attribs']['']['rel'] === 'avatar' || $link['attribs']['']['rel'] === 'photo')
468                                                 $res['author-avatar'] = unxmlify($link['attribs']['']['href']);
469                                 }
470                         }
471                 }
472         }
473
474         // No photo/profile-link on the item - look at the feed level
475
476         if((! (x($res,'author-link'))) || (! (x($res,'author-avatar')))) {
477                 $rawauthor = $feed->get_feed_tags(SIMPLEPIE_NAMESPACE_ATOM_10,'author');
478                 if($rawauthor && $rawauthor[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link']) {
479                         $base = $rawauthor[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link'];
480                         foreach($base as $link) {
481                                 if($link['attribs']['']['rel'] === 'alternate' && (! $res['author-link']))
482                                         $res['author-link'] = unxmlify($link['attribs']['']['href']);
483                                 if(! $res['author-avatar']) {
484                                         if($link['attribs']['']['rel'] === 'photo' || $link['attribs']['']['rel'] === 'avatar')
485                                                 $res['author-avatar'] = unxmlify($link['attribs']['']['href']);
486                                 }
487                         }
488                 }
489
490                 $rawactor = $feed->get_feed_tags(NAMESPACE_ACTIVITY, 'subject');
491
492                 if($rawactor && activity_match($rawactor[0]['child'][NAMESPACE_ACTIVITY]['object-type'][0]['data'],ACTIVITY_OBJ_PERSON)) {
493                         $base = $rawactor[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link'];
494
495                         if($base && count($base)) {
496                                 foreach($base as $link) {
497                                         if($link['attribs']['']['rel'] === 'alternate' && (! $res['author-link']))
498                                                 $res['author-link'] = unxmlify($link['attribs']['']['href']);
499                                         if(! (x($res,'author-avatar'))) {
500                                                 if($link['attribs']['']['rel'] === 'avatar' || $link['attribs']['']['rel'] === 'photo')
501                                                         $res['author-avatar'] = unxmlify($link['attribs']['']['href']);
502                                         }
503                                 }
504                         }
505                 }
506         }
507
508         $apps = $item->get_item_tags(NAMESPACE_STATUSNET,'notice_info');
509         if($apps && $apps[0]['attribs']['']['source']) {
510                 $res['app'] = strip_tags(unxmlify($apps[0]['attribs']['']['source']));
511                 if($res['app'] === 'web')
512                         $res['app'] = 'OStatus';
513         }
514
515         // base64 encoded json structure representing Diaspora signature
516
517         $dsig = $item->get_item_tags(NAMESPACE_DFRN,'diaspora_signature');
518         if($dsig) {
519                 $res['dsprsig'] = unxmlify($dsig[0]['data']);
520         }
521
522         $dguid = $item->get_item_tags(NAMESPACE_DFRN,'diaspora_guid');
523         if($dguid)
524                 $res['guid'] = unxmlify($dguid[0]['data']);
525
526         $bm = $item->get_item_tags(NAMESPACE_DFRN,'bookmark');
527         if($bm)
528                 $res['bookmark'] = ((unxmlify($bm[0]['data']) === 'true') ? 1 : 0);
529
530
531         /**
532          * If there's a copy of the body content which is guaranteed to have survived mangling in transit, use it.
533          */
534
535         $have_real_body = false;
536
537         $rawenv = $item->get_item_tags(NAMESPACE_DFRN, 'env');
538         if($rawenv) {
539                 $have_real_body = true;
540                 $res['body'] = $rawenv[0]['data'];
541                 $res['body'] = str_replace(array(' ',"\t","\r","\n"), array('','','',''),$res['body']);
542                 // make sure nobody is trying to sneak some html tags by us
543                 $res['body'] = notags(base64url_decode($res['body']));
544         }
545
546
547         $res['body'] = limit_body_size($res['body']);
548
549         // It isn't certain at this point whether our content is plaintext or html and we'd be foolish to trust 
550         // the content type. Our own network only emits text normally, though it might have been converted to 
551         // html if we used a pubsubhubbub transport. But if we see even one html tag in our text, we will
552         // have to assume it is all html and needs to be purified.
553
554         // It doesn't matter all that much security wise - because before this content is used anywhere, we are 
555         // going to escape any tags we find regardless, but this lets us import a limited subset of html from 
556         // the wild, by sanitising it and converting supported tags to bbcode before we rip out any remaining 
557         // html.
558
559         if((strpos($res['body'],'<') !== false) && (strpos($res['body'],'>') !== false)) {
560
561                 $res['body'] = reltoabs($res['body'],$base_url);
562
563                 $res['body'] = html2bb_video($res['body']);
564
565                 $res['body'] = oembed_html2bbcode($res['body']);
566
567                 $config = HTMLPurifier_Config::createDefault();
568                 $config->set('Cache.DefinitionImpl', null);
569
570                 // we shouldn't need a whitelist, because the bbcode converter
571                 // will strip out any unsupported tags.
572
573                 $purifier = new HTMLPurifier($config);
574                 $res['body'] = $purifier->purify($res['body']);
575
576                 $res['body'] = @html2bbcode($res['body']);
577
578
579         }
580         elseif(! $have_real_body) {
581
582                 // it's not one of our messages and it has no tags
583                 // so it's probably just text. We'll escape it just to be safe.
584
585                 $res['body'] = escape_tags($res['body']);
586         }
587
588
589         // this tag is obsolete but we keep it for really old sites
590
591         $allow = $item->get_item_tags(NAMESPACE_DFRN,'comment-allow');
592         if($allow && $allow[0]['data'] == 1)
593                 $res['last-child'] = 1;
594         else
595                 $res['last-child'] = 0;
596
597         $private = $item->get_item_tags(NAMESPACE_DFRN,'private');
598         if($private && intval($private[0]['data']) > 0)
599                 $res['private'] = intval($private[0]['data']);
600         else
601                 $res['private'] = 0;
602
603         $extid = $item->get_item_tags(NAMESPACE_DFRN,'extid');
604         if($extid && $extid[0]['data'])
605                 $res['extid'] = $extid[0]['data'];
606
607         $rawlocation = $item->get_item_tags(NAMESPACE_DFRN, 'location');
608         if($rawlocation)
609                 $res['location'] = unxmlify($rawlocation[0]['data']);
610
611
612         $rawcreated = $item->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10,'published');
613         if($rawcreated)
614                 $res['created'] = unxmlify($rawcreated[0]['data']);
615
616
617         $rawedited = $item->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10,'updated');
618         if($rawedited)
619                 $res['edited'] = unxmlify($rawedited[0]['data']);
620
621         if((x($res,'edited')) && (! (x($res,'created'))))
622                 $res['created'] = $res['edited']; 
623
624         if(! $res['created'])
625                 $res['created'] = $item->get_date('c');
626
627         if(! $res['edited'])
628                 $res['edited'] = $item->get_date('c');
629
630
631         // Disallow time travelling posts
632
633         $d1 = strtotime($res['created']);
634         $d2 = strtotime($res['edited']);
635         $d3 = strtotime('now');
636
637         if($d1 > $d3)
638                 $res['created'] = datetime_convert();
639         if($d2 > $d3)
640                 $res['edited'] = datetime_convert();
641
642         $rawowner = $item->get_item_tags(NAMESPACE_DFRN, 'owner');
643         if($rawowner[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'])
644                 $res['owner-name'] = unxmlify($rawowner[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data']);
645         elseif($rawowner[0]['child'][NAMESPACE_DFRN]['name'][0]['data'])
646                 $res['owner-name'] = unxmlify($rawowner[0]['child'][NAMESPACE_DFRN]['name'][0]['data']);
647         if($rawowner[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'])
648                 $res['owner-link'] = unxmlify($rawowner[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data']);
649         elseif($rawowner[0]['child'][NAMESPACE_DFRN]['uri'][0]['data'])
650                 $res['owner-link'] = unxmlify($rawowner[0]['child'][NAMESPACE_DFRN]['uri'][0]['data']);
651
652         if($rawowner[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link']) {
653                 $base = $rawowner[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link'];
654
655                 foreach($base as $link) {
656                         if(!x($res, 'owner-avatar') || !$res['owner-avatar']) {
657                                 if($link['attribs']['']['rel'] === 'photo' || $link['attribs']['']['rel'] === 'avatar')
658                                         $res['owner-avatar'] = unxmlify($link['attribs']['']['href']);
659                         }
660                 }
661         }
662
663         $rawgeo = $item->get_item_tags(NAMESPACE_GEORSS,'point');
664         if($rawgeo)
665                 $res['coord'] = unxmlify($rawgeo[0]['data']);
666
667
668         $rawverb = $item->get_item_tags(NAMESPACE_ACTIVITY, 'verb');
669
670         // select between supported verbs
671
672         if($rawverb) {
673                 $res['verb'] = unxmlify($rawverb[0]['data']);
674         }
675
676         // translate OStatus unfollow to activity streams if it happened to get selected
677
678         if((x($res,'verb')) && ($res['verb'] === 'http://ostatus.org/schema/1.0/unfollow'))
679                 $res['verb'] = ACTIVITY_UNFOLLOW;
680
681         $cats = $item->get_categories();
682         if($cats) {
683                 $tag_arr = array();
684                 foreach($cats as $cat) {
685                         $term = $cat->get_term();
686                         if(! $term)
687                                 $term = $cat->get_label();
688                         $scheme = $cat->get_scheme();
689                         if($scheme && $term && stristr($scheme,'X-DFRN:'))
690                                 $tag_arr[] = substr($scheme,7,1) . '[url=' . unxmlify(substr($scheme,9)) . ']' . unxmlify($term) . '[/url]';
691                         elseif($term)
692                                 $tag_arr[] = notags(trim($term));
693                 }
694                 $res['tag'] =  implode(',', $tag_arr);
695         }
696
697         $attach = $item->get_enclosures();
698         if($attach) {
699                 $att_arr = array();
700                 foreach($attach as $att) {
701                         $len   = intval($att->get_length());
702                         $link  = str_replace(array(',','"'),array('%2D','%22'),notags(trim(unxmlify($att->get_link()))));
703                         $title = str_replace(array(',','"'),array('%2D','%22'),notags(trim(unxmlify($att->get_title()))));
704                         $type  = str_replace(array(',','"'),array('%2D','%22'),notags(trim(unxmlify($att->get_type()))));
705                         if(strpos($type,';'))
706                                 $type = substr($type,0,strpos($type,';'));
707                         if((! $link) || (strpos($link,'http') !== 0))
708                                 continue;
709
710                         if(! $title)
711                                 $title = ' ';
712                         if(! $type)
713                                 $type = 'application/octet-stream';
714
715                         $att_arr[] = '[attach]href="' . $link . '" length="' . $len . '" type="' . $type . '" title="' . $title . '"[/attach]'; 
716                 }
717                 $res['attach'] = implode(',', $att_arr);
718         }
719
720         $rawobj = $item->get_item_tags(NAMESPACE_ACTIVITY, 'object');
721
722         if($rawobj) {
723                 $res['object'] = '<object>' . "\n";
724                 $child = $rawobj[0]['child'];
725                 if($child[NAMESPACE_ACTIVITY]['object-type'][0]['data']) {
726                         $res['object-type'] = $child[NAMESPACE_ACTIVITY]['object-type'][0]['data'];
727                         $res['object'] .= '<type>' . $child[NAMESPACE_ACTIVITY]['object-type'][0]['data'] . '</type>' . "\n";
728                 }
729                 if(x($child[SIMPLEPIE_NAMESPACE_ATOM_10], 'id') && $child[SIMPLEPIE_NAMESPACE_ATOM_10]['id'][0]['data'])
730                         $res['object'] .= '<id>' . $child[SIMPLEPIE_NAMESPACE_ATOM_10]['id'][0]['data'] . '</id>' . "\n";
731                 if(x($child[SIMPLEPIE_NAMESPACE_ATOM_10], 'link') && $child[SIMPLEPIE_NAMESPACE_ATOM_10]['link'])
732                         $res['object'] .= '<link>' . encode_rel_links($child[SIMPLEPIE_NAMESPACE_ATOM_10]['link']) . '</link>' . "\n";
733                 if(x($child[SIMPLEPIE_NAMESPACE_ATOM_10], 'title') && $child[SIMPLEPIE_NAMESPACE_ATOM_10]['title'][0]['data'])
734                         $res['object'] .= '<title>' . $child[SIMPLEPIE_NAMESPACE_ATOM_10]['title'][0]['data'] . '</title>' . "\n";
735                 if(x($child[SIMPLEPIE_NAMESPACE_ATOM_10], 'content') && $child[SIMPLEPIE_NAMESPACE_ATOM_10]['content'][0]['data']) {
736                         $body = $child[SIMPLEPIE_NAMESPACE_ATOM_10]['content'][0]['data'];
737                         if(! $body)
738                                 $body = $child[SIMPLEPIE_NAMESPACE_ATOM_10]['summary'][0]['data'];
739                         // preserve a copy of the original body content in case we later need to parse out any microformat information, e.g. events
740                         $res['object'] .= '<orig>' . xmlify($body) . '</orig>' . "\n";
741                         if((strpos($body,'<') !== false) || (strpos($body,'>') !== false)) {
742
743                                 $body = html2bb_video($body);
744
745                                 $config = HTMLPurifier_Config::createDefault();
746                                 $config->set('Cache.DefinitionImpl', null);
747
748                                 $purifier = new HTMLPurifier($config);
749                                 $body = $purifier->purify($body);
750                                 $body = html2bbcode($body);
751                         }
752
753                         $res['object'] .= '<content>' . $body . '</content>' . "\n";
754                 }
755
756                 $res['object'] .= '</object>' . "\n";
757         }
758
759         $rawobj = $item->get_item_tags(NAMESPACE_ACTIVITY, 'target');
760
761         if($rawobj) {
762                 $res['target'] = '<target>' . "\n";
763                 $child = $rawobj[0]['child'];
764                 if($child[NAMESPACE_ACTIVITY]['object-type'][0]['data']) {
765                         $res['target'] .= '<type>' . $child[NAMESPACE_ACTIVITY]['object-type'][0]['data'] . '</type>' . "\n";
766                 }
767                 if(x($child[SIMPLEPIE_NAMESPACE_ATOM_10], 'id') && $child[SIMPLEPIE_NAMESPACE_ATOM_10]['id'][0]['data'])
768                         $res['target'] .= '<id>' . $child[SIMPLEPIE_NAMESPACE_ATOM_10]['id'][0]['data'] . '</id>' . "\n";
769                 if(x($child[SIMPLEPIE_NAMESPACE_ATOM_10], 'link') && $child[SIMPLEPIE_NAMESPACE_ATOM_10]['link'])
770                         $res['target'] .= '<link>' . encode_rel_links($child[SIMPLEPIE_NAMESPACE_ATOM_10]['link']) . '</link>' . "\n";
771                 if(x($child[SIMPLEPIE_NAMESPACE_ATOM_10], 'data') && $child[SIMPLEPIE_NAMESPACE_ATOM_10]['title'][0]['data'])
772                         $res['target'] .= '<title>' . $child[SIMPLEPIE_NAMESPACE_ATOM_10]['title'][0]['data'] . '</title>' . "\n";
773                 if(x($child[SIMPLEPIE_NAMESPACE_ATOM_10], 'data') && $child[SIMPLEPIE_NAMESPACE_ATOM_10]['content'][0]['data']) {
774                         $body = $child[SIMPLEPIE_NAMESPACE_ATOM_10]['content'][0]['data'];
775                         if(! $body)
776                                 $body = $child[SIMPLEPIE_NAMESPACE_ATOM_10]['summary'][0]['data'];
777                         // preserve a copy of the original body content in case we later need to parse out any microformat information, e.g. events
778                         $res['target'] .= '<orig>' . xmlify($body) . '</orig>' . "\n";
779                         if((strpos($body,'<') !== false) || (strpos($body,'>') !== false)) {
780
781                                 $body = html2bb_video($body);
782
783                                 $config = HTMLPurifier_Config::createDefault();
784                                 $config->set('Cache.DefinitionImpl', null);
785
786                                 $purifier = new HTMLPurifier($config);
787                                 $body = $purifier->purify($body);
788                                 $body = html2bbcode($body);
789                         }
790
791                         $res['target'] .= '<content>' . $body . '</content>' . "\n";
792                 }
793
794                 $res['target'] .= '</target>' . "\n";
795         }
796
797         // This is some experimental stuff. By now retweets are shown with "RT:"
798         // But: There is data so that the message could be shown similar to native retweets
799         // There is some better way to parse this array - but it didn't worked for me.
800         $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"];
801         if (is_array($child)) {
802                 logger('get_atom_elements: Looking for status.net repeated message');
803
804                 $message = $child["http://activitystrea.ms/spec/1.0/"]["object"][0]["child"][SIMPLEPIE_NAMESPACE_ATOM_10]["content"][0]["data"];
805                 $orig_uri = $child["http://activitystrea.ms/spec/1.0/"]["object"][0]["child"][SIMPLEPIE_NAMESPACE_ATOM_10]["id"][0]["data"];
806                 $author = $child[SIMPLEPIE_NAMESPACE_ATOM_10]["author"][0]["child"][SIMPLEPIE_NAMESPACE_ATOM_10];
807                 $uri = $author["uri"][0]["data"];
808                 $name = $author["name"][0]["data"];
809                 $avatar = @array_shift($author["link"][2]["attribs"]);
810                 $avatar = $avatar["href"];
811
812                 if (($name != "") and ($uri != "") and ($avatar != "") and ($message != "")) {
813                         logger('get_atom_elements: fixing sender of repeated message.');
814
815                         if (!intval(get_config('system','wall-to-wall_share'))) {
816                                 $prefix = "[share author='".str_replace("'", "&#039;",$name).
817                                                 "' profile='".$uri.
818                                                 "' avatar='".$avatar.
819                                                 "' link='".$orig_uri."']";
820
821                                 $res["body"] = $prefix.html2bbcode($message)."[/share]";
822                         } else {
823                                 $res["owner-name"] = $res["author-name"];
824                                 $res["owner-link"] = $res["author-link"];
825                                 $res["owner-avatar"] = $res["author-avatar"];
826
827                                 $res["author-name"] = $name;
828                                 $res["author-link"] = $uri;
829                                 $res["author-avatar"] = $avatar;
830
831                                 $res["body"] = html2bbcode($message);
832                         }
833                 }
834         }
835
836         // Search for ostatus conversation url
837         $links = $item->feed->data["child"][SIMPLEPIE_NAMESPACE_ATOM_10]["feed"][0]["child"][SIMPLEPIE_NAMESPACE_ATOM_10]["entry"][0]["child"]["http://www.w3.org/2005/Atom"]["link"];
838
839         if (is_array($links)) {
840                 foreach ($links as $link) {
841                         $conversation = array_shift($link["attribs"]);
842
843                         if ($conversation["rel"] == "ostatus:conversation") {
844                                 $res["ostatus_conversation"] = $conversation["href"];
845                                 logger('get_atom_elements: found conversation url '.$res["ostatus_conversation"]);
846                         }
847                 };
848         }
849
850         if (isset($contact["network"]) AND ($contact["network"] == NETWORK_FEED) AND $contact['fetch_further_information']) {
851                 $res["body"] = $res["title"]."\n\n[class=type-link]".fetch_siteinfo($res['plink'])."[/class]";
852                 $res["title"] = "";
853         }
854
855         $arr = array('feed' => $feed, 'item' => $item, 'result' => $res);
856
857         call_hooks('parse_atom', $arr);
858
859         //if (($res["title"] != "") or (strpos($res["body"], "RT @") > 0)) {
860         //if (strpos($res["body"], "RT @") !== false) {
861         /*if (strpos($res["body"], "@") !== false) {
862                 $debugfile = tempnam("/var/www/virtual/pirati.ca/phptmp/", "item-res2-");
863                 file_put_contents($debugfile, serialize($arr));
864         }*/
865
866         return $res;
867 }
868
869 function fetch_siteinfo($url) {
870         require_once("mod/parse_url.php");
871
872         // Fetch site infos - but only from the meta data
873         $data = parseurl_getsiteinfo($url, true);
874
875         $text = "";
876
877         if (!is_string($data["text"]) AND (sizeof($data["images"]) == 0) AND ($data["title"] == $url))
878                 return("");
879
880         if (is_string($data["title"]))
881                 $text .= "[bookmark=".$url."]".trim($data["title"])."[/bookmark]\n";
882
883         if (sizeof($data["images"]) > 0) {
884                 $imagedata = $data["images"][0];
885                 $text .= '[img='.$imagedata["width"].'x'.$imagedata["height"].']'.$imagedata["src"].'[/img]' . "\n";
886         }
887
888         if (is_string($data["text"]))
889                 $text .= "[quote]".$data["text"]."[/quote]";
890
891         return($text);
892 }
893
894 function encode_rel_links($links) {
895         $o = '';
896         if(! ((is_array($links)) && (count($links))))
897                 return $o;
898         foreach($links as $link) {
899                 $o .= '<link ';
900                 if($link['attribs']['']['rel'])
901                         $o .= 'rel="' . $link['attribs']['']['rel'] . '" ';
902                 if($link['attribs']['']['type'])
903                         $o .= 'type="' . $link['attribs']['']['type'] . '" ';
904                 if($link['attribs']['']['href'])
905                         $o .= 'href="' . $link['attribs']['']['href'] . '" ';
906                 if( (x($link['attribs'],NAMESPACE_MEDIA)) && $link['attribs'][NAMESPACE_MEDIA]['width'])
907                         $o .= 'media:width="' . $link['attribs'][NAMESPACE_MEDIA]['width'] . '" ';
908                 if( (x($link['attribs'],NAMESPACE_MEDIA)) && $link['attribs'][NAMESPACE_MEDIA]['height'])
909                         $o .= 'media:height="' . $link['attribs'][NAMESPACE_MEDIA]['height'] . '" ';
910                 $o .= ' />' . "\n" ;
911         }
912         return xmlify($o);
913 }
914
915
916
917 function item_store($arr,$force_parent = false) {
918
919         // If a Diaspora signature structure was passed in, pull it out of the
920         // item array and set it aside for later storage.
921
922         $dsprsig = null;
923         if(x($arr,'dsprsig')) {
924                 $dsprsig = json_decode(base64_decode($arr['dsprsig']));
925                 unset($arr['dsprsig']);
926         }
927
928         // if an OStatus conversation url was passed in, it is stored and then
929         // removed from the array.
930         $ostatus_conversation = null;
931
932         if (isset($arr["ostatus_conversation"])) {
933                 $ostatus_conversation = $arr["ostatus_conversation"];
934                 unset($arr["ostatus_conversation"]);
935         }
936
937         if(x($arr, 'gravity'))
938                 $arr['gravity'] = intval($arr['gravity']);
939         elseif($arr['parent-uri'] === $arr['uri'])
940                 $arr['gravity'] = 0;
941         elseif(activity_match($arr['verb'],ACTIVITY_POST))
942                 $arr['gravity'] = 6;
943         else
944                 $arr['gravity'] = 6;   // extensible catchall
945
946         if(! x($arr,'type'))
947                 $arr['type']      = 'remote';
948
949         // Shouldn't happen but we want to make absolutely sure it doesn't leak from a plugin.
950
951         if((strpos($arr['body'],'<') !== false) || (strpos($arr['body'],'>') !== false))
952                 $arr['body'] = strip_tags($arr['body']);
953
954
955         if (version_compare(PHP_VERSION, '5.3.0', '>=')) {
956                 require_once('library/langdet/Text/LanguageDetect.php');
957                 $naked_body = preg_replace('/\[(.+?)\]/','',$arr['body']);
958                 $l = new Text_LanguageDetect;
959                 //$lng = $l->detectConfidence($naked_body);
960                 //$arr['postopts'] = (($lng['language']) ? 'lang=' . $lng['language'] . ';' . $lng['confidence'] : '');
961                 $lng = $l->detect($naked_body, 3);
962
963                 if (sizeof($lng) > 0) {
964                         $postopts = "";
965
966                         foreach ($lng as $language => $score) {
967                                 if ($postopts == "")
968                                         $postopts = "lang=";
969                                 else
970                                         $postopts .= ":";
971
972                                 $postopts .= $language.";".$score;
973                         }
974                         $arr['postopts'] = $postopts;
975                 }
976         }
977
978         $arr['wall']          = ((x($arr,'wall'))          ? intval($arr['wall'])                : 0);
979         $arr['uri']           = ((x($arr,'uri'))           ? notags(trim($arr['uri']))           : random_string());
980         $arr['extid']         = ((x($arr,'extid'))         ? notags(trim($arr['extid']))         : '');
981         $arr['author-name']   = ((x($arr,'author-name'))   ? notags(trim($arr['author-name']))   : '');
982         $arr['author-link']   = ((x($arr,'author-link'))   ? notags(trim($arr['author-link']))   : '');
983         $arr['author-avatar'] = ((x($arr,'author-avatar')) ? notags(trim($arr['author-avatar'])) : '');
984         $arr['owner-name']    = ((x($arr,'owner-name'))    ? notags(trim($arr['owner-name']))    : '');
985         $arr['owner-link']    = ((x($arr,'owner-link'))    ? notags(trim($arr['owner-link']))    : '');
986         $arr['owner-avatar']  = ((x($arr,'owner-avatar'))  ? notags(trim($arr['owner-avatar']))  : '');
987         $arr['created']       = ((x($arr,'created') !== false) ? datetime_convert('UTC','UTC',$arr['created']) : datetime_convert());
988         $arr['edited']        = ((x($arr,'edited')  !== false) ? datetime_convert('UTC','UTC',$arr['edited'])  : datetime_convert());
989         $arr['commented']     = datetime_convert();
990         $arr['received']      = datetime_convert();
991         $arr['changed']       = datetime_convert();
992         $arr['title']         = ((x($arr,'title'))         ? notags(trim($arr['title']))         : '');
993         $arr['location']      = ((x($arr,'location'))      ? notags(trim($arr['location']))      : '');
994         $arr['coord']         = ((x($arr,'coord'))         ? notags(trim($arr['coord']))         : '');
995         $arr['last-child']    = ((x($arr,'last-child'))    ? intval($arr['last-child'])          : 0 );
996         $arr['visible']       = ((x($arr,'visible') !== false) ? intval($arr['visible'])         : 1 );
997         $arr['deleted']       = 0;
998         $arr['parent-uri']    = ((x($arr,'parent-uri'))    ? notags(trim($arr['parent-uri']))    : '');
999         $arr['verb']          = ((x($arr,'verb'))          ? notags(trim($arr['verb']))          : '');
1000         $arr['object-type']   = ((x($arr,'object-type'))   ? notags(trim($arr['object-type']))   : '');
1001         $arr['object']        = ((x($arr,'object'))        ? trim($arr['object'])                : '');
1002         $arr['target-type']   = ((x($arr,'target-type'))   ? notags(trim($arr['target-type']))   : '');
1003         $arr['target']        = ((x($arr,'target'))        ? trim($arr['target'])                : '');
1004         $arr['plink']         = ((x($arr,'plink'))         ? notags(trim($arr['plink']))         : '');
1005         $arr['allow_cid']     = ((x($arr,'allow_cid'))     ? trim($arr['allow_cid'])             : '');
1006         $arr['allow_gid']     = ((x($arr,'allow_gid'))     ? trim($arr['allow_gid'])             : '');
1007         $arr['deny_cid']      = ((x($arr,'deny_cid'))      ? trim($arr['deny_cid'])              : '');
1008         $arr['deny_gid']      = ((x($arr,'deny_gid'))      ? trim($arr['deny_gid'])              : '');
1009         $arr['private']       = ((x($arr,'private'))       ? intval($arr['private'])             : 0 );
1010         $arr['bookmark']      = ((x($arr,'bookmark'))      ? intval($arr['bookmark'])            : 0 );
1011         $arr['body']          = ((x($arr,'body'))          ? trim($arr['body'])                  : '');
1012         $arr['tag']           = ((x($arr,'tag'))           ? notags(trim($arr['tag']))           : '');
1013         $arr['attach']        = ((x($arr,'attach'))        ? notags(trim($arr['attach']))        : '');
1014         $arr['app']           = ((x($arr,'app'))           ? notags(trim($arr['app']))           : '');
1015         $arr['origin']        = ((x($arr,'origin'))        ? intval($arr['origin'])              : 0 );
1016         $arr['guid']          = ((x($arr,'guid'))          ? notags(trim($arr['guid']))          : get_guid());
1017         $arr['network']       = ((x($arr,'network'))       ? trim($arr['network'])               : '');
1018
1019         if ($arr['network'] == "") {
1020                 $r = q("SELECT `network` FROM `contact` WHERE `id` = %d AND `uid` = %d LIMIT 1",
1021                         intval($arr['contact-id']),
1022                         intval($arr['uid'])
1023                 );
1024
1025                 if(count($r))
1026                         $arr['network'] = $r[0]["network"];
1027
1028                 // Fallback to friendica (why is it empty in some cases?)
1029                 if ($arr['network'] == "")
1030                         $arr['network'] = NETWORK_DFRN;
1031
1032                 logger("item_store: Set network to ".$arr["network"]." for ".$arr["uri"], LOGGER_DEBUG);
1033         }
1034
1035         $arr['thr-parent'] = $arr['parent-uri'];
1036         if($arr['parent-uri'] === $arr['uri']) {
1037                 $parent_id = 0;
1038                 $parent_deleted = 0;
1039                 $allow_cid = $arr['allow_cid'];
1040                 $allow_gid = $arr['allow_gid'];
1041                 $deny_cid  = $arr['deny_cid'];
1042                 $deny_gid  = $arr['deny_gid'];
1043         }
1044         else {
1045
1046                 // find the parent and snarf the item id and ACLs
1047                 // and anything else we need to inherit
1048
1049                 $r = q("SELECT * FROM `item` WHERE `uri` = '%s' AND `uid` = %d ORDER BY `id` ASC LIMIT 1",
1050                         dbesc($arr['parent-uri']),
1051                         intval($arr['uid'])
1052                 );
1053
1054                 if(count($r)) {
1055
1056                         // is the new message multi-level threaded?
1057                         // even though we don't support it now, preserve the info
1058                         // and re-attach to the conversation parent.
1059
1060                         if($r[0]['uri'] != $r[0]['parent-uri']) {
1061                                 $arr['parent-uri'] = $r[0]['parent-uri'];
1062                                 $z = q("SELECT * FROM `item` WHERE `uri` = '%s' AND `parent-uri` = '%s' AND `uid` = %d
1063                                         ORDER BY `id` ASC LIMIT 1",
1064                                         dbesc($r[0]['parent-uri']),
1065                                         dbesc($r[0]['parent-uri']),
1066                                         intval($arr['uid'])
1067                                 );
1068                                 if($z && count($z))
1069                                         $r = $z;
1070                         }
1071
1072                         $parent_id      = $r[0]['id'];
1073                         $parent_deleted = $r[0]['deleted'];
1074                         $allow_cid      = $r[0]['allow_cid'];
1075                         $allow_gid      = $r[0]['allow_gid'];
1076                         $deny_cid       = $r[0]['deny_cid'];
1077                         $deny_gid       = $r[0]['deny_gid'];
1078                         $arr['wall']    = $r[0]['wall'];
1079
1080                         // if the parent is private, force privacy for the entire conversation
1081                         // This differs from the above settings as it subtly allows comments from
1082                         // email correspondents to be private even if the overall thread is not.
1083
1084                         if($r[0]['private'])
1085                                 $arr['private'] = $r[0]['private'];
1086
1087                         // Edge case. We host a public forum that was originally posted to privately.
1088                         // The original author commented, but as this is a comment, the permissions
1089                         // weren't fixed up so it will still show the comment as private unless we fix it here.
1090
1091                         if((intval($r[0]['forum_mode']) == 1) && (! $r[0]['private']))
1092                                 $arr['private'] = 0;
1093
1094
1095                         // If its a post from myself then tag the thread as "mention"
1096                         logger("item_store: Checking if parent ".$parent_id." has to be tagged as mention for user ".$arr['uid'], LOGGER_DEBUG);
1097                         $u = q("select * from user where uid = %d limit 1", intval($arr['uid']));
1098                         if(count($u)) {
1099                                 $a = get_app();
1100                                 $self = normalise_link($a->get_baseurl() . '/profile/' . $u[0]['nickname']);
1101                                 logger("item_store: 'myself' is ".$self." for parent ".$parent_id." checking against ".$arr['author-link']." and ".$arr['owner-link'], LOGGER_DEBUG);
1102                                 if ((normalise_link($arr['author-link']) == $self) OR (normalise_link($arr['owner-link']) == $self)) {
1103                                         q("UPDATE `thread` SET `mention` = 1 WHERE `iid` = %d", intval($parent_id));
1104                                         logger("item_store: tagged thread ".$parent_id." as mention for user ".$self, LOGGER_DEBUG);
1105                                 }
1106                         }
1107                 }
1108                 else {
1109
1110                         // Allow one to see reply tweets from status.net even when
1111                         // we don't have or can't see the original post.
1112
1113                         if($force_parent) {
1114                                 logger('item_store: $force_parent=true, reply converted to top-level post.');
1115                                 $parent_id = 0;
1116                                 $arr['parent-uri'] = $arr['uri'];
1117                                 $arr['gravity'] = 0;
1118                         }
1119                         else {
1120                                 logger('item_store: item parent was not found - ignoring item');
1121                                 return 0;
1122                         }
1123
1124                         $parent_deleted = 0;
1125                 }
1126         }
1127
1128         $r = q("SELECT `id` FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1",
1129                 dbesc($arr['uri']),
1130                 intval($arr['uid'])
1131         );
1132         if($r && count($r)) {
1133                 logger('item-store: duplicate item ignored. ' . print_r($arr,true));
1134                 return 0;
1135         }
1136
1137         call_hooks('post_remote',$arr);
1138
1139         if(x($arr,'cancel')) {
1140                 logger('item_store: post cancelled by plugin.');
1141                 return 0;
1142         }
1143
1144         dbesc_array($arr);
1145
1146         logger('item_store: ' . print_r($arr,true), LOGGER_DATA);
1147
1148         $r = dbq("INSERT INTO `item` (`"
1149                         . implode("`, `", array_keys($arr))
1150                         . "`) VALUES ('"
1151                         . implode("', '", array_values($arr))
1152                         . "')" );
1153
1154         // find the item we just created
1155
1156         $r = q("SELECT `id` FROM `item` WHERE `uri` = '%s' AND `uid` = %d ORDER BY `id` ASC ",
1157                 $arr['uri'],           // already dbesc'd
1158                 intval($arr['uid'])
1159         );
1160
1161         if(count($r)) {
1162                 $current_post = $r[0]['id'];
1163                 logger('item_store: created item ' . $current_post);
1164                 create_tags_from_item($r[0]['id']);
1165
1166                 // Only check for notifications on start posts
1167                 if ($arr['parent-uri'] === $arr['uri']) {
1168                         add_thread($r[0]['id']);
1169                         logger('item_store: Check notification for contact '.$arr['contact-id'].' and post '.$current_post, LOGGER_DEBUG);
1170
1171                         // Send a notification for every new post?
1172                         $r = q("SELECT `notify_new_posts` FROM `contact` WHERE `id` = %d AND `uid` = %d AND `notify_new_posts` LIMIT 1",
1173                                 intval($arr['contact-id']),
1174                                 intval($arr['uid'])
1175                         );
1176
1177                         if(count($r)) {
1178                                 logger('item_store: Send notification for contact '.$arr['contact-id'].' and post '.$current_post, LOGGER_DEBUG);
1179                                 $u = q("SELECT * FROM user WHERE uid = %d LIMIT 1",
1180                                         intval($arr['uid']));
1181
1182                                 $item = q("SELECT * FROM `item` WHERE `id` = %d AND `uid` = %d",
1183                                         intval($current_post),
1184                                         intval($arr['uid'])
1185                                 );
1186
1187                                 $a = get_app();
1188
1189                                 require_once('include/enotify.php');
1190                                 notification(array(
1191                                         'type'         => NOTIFY_SHARE,
1192                                         'notify_flags' => $u[0]['notify-flags'],
1193                                         'language'     => $u[0]['language'],
1194                                         'to_name'      => $u[0]['username'],
1195                                         'to_email'     => $u[0]['email'],
1196                                         'uid'          => $u[0]['uid'],
1197                                         'item'         => $item[0],
1198                                         'link'         => $a->get_baseurl().'/display/'.$u[0]['nickname'].'/'.$current_post,
1199                                         'source_name'  => $item[0]['author-name'],
1200                                         'source_link'  => $item[0]['author-link'],
1201                                         'source_photo' => $item[0]['author-avatar'],
1202                                         'verb'         => ACTIVITY_TAG,
1203                                         'otype'        => 'item'
1204                                 ));
1205                                 logger('item_store: Notification sent for contact '.$arr['contact-id'].' and post '.$current_post, LOGGER_DEBUG);
1206                         }
1207                 }
1208
1209         } else {
1210                 logger('item_store: could not locate created item');
1211                 return 0;
1212         }
1213         if(count($r) > 1) {
1214                 logger('item_store: duplicated post occurred. Removing duplicates.');
1215                 q("DELETE FROM `item` WHERE `uri` = '%s' AND `uid` = %d AND `id` != %d ",
1216                         $arr['uri'],
1217                         intval($arr['uid']),
1218                         intval($current_post)
1219                 );
1220         }
1221
1222         if((! $parent_id) || ($arr['parent-uri'] === $arr['uri']))
1223                 $parent_id = $current_post;
1224
1225         if(strlen($allow_cid) || strlen($allow_gid) || strlen($deny_cid) || strlen($deny_gid))
1226                 $private = 1;
1227         else
1228                 $private = $arr['private'];
1229
1230         // Set parent id - and also make sure to inherit the parent's ACLs.
1231
1232         $r = q("UPDATE `item` SET `parent` = %d, `allow_cid` = '%s', `allow_gid` = '%s',
1233                 `deny_cid` = '%s', `deny_gid` = '%s', `private` = %d, `deleted` = %d WHERE `id` = %d",
1234                 intval($parent_id),
1235                 dbesc($allow_cid),
1236                 dbesc($allow_gid),
1237                 dbesc($deny_cid),
1238                 dbesc($deny_gid),
1239                 intval($private),
1240                 intval($parent_deleted),
1241                 intval($current_post)
1242         );
1243         create_tags_from_item($current_post);
1244
1245         // Complete ostatus threads
1246         if ($ostatus_conversation)
1247                 complete_conversation($current_post, $ostatus_conversation);
1248
1249         $arr['id'] = $current_post;
1250         $arr['parent'] = $parent_id;
1251         $arr['allow_cid'] = $allow_cid;
1252         $arr['allow_gid'] = $allow_gid;
1253         $arr['deny_cid'] = $deny_cid;
1254         $arr['deny_gid'] = $deny_gid;
1255         $arr['private'] = $private;
1256         $arr['deleted'] = $parent_deleted;
1257
1258         // update the commented timestamp on the parent
1259
1260         q("UPDATE `item` set `commented` = '%s', `changed` = '%s' WHERE `id` = %d",
1261                 dbesc(datetime_convert()),
1262                 dbesc(datetime_convert()),
1263                 intval($parent_id)
1264         );
1265         update_thread($parent_id);
1266
1267         if($dsprsig) {
1268                 q("insert into sign (`iid`,`signed_text`,`signature`,`signer`) values (%d,'%s','%s','%s') ",
1269                         intval($current_post),
1270                         dbesc($dsprsig->signed_text),
1271                         dbesc($dsprsig->signature),
1272                         dbesc($dsprsig->signer)
1273                 );
1274         }
1275
1276
1277         /**
1278          * If this is now the last-child, force all _other_ children of this parent to *not* be last-child
1279          */
1280
1281         if($arr['last-child']) {
1282                 $r = q("UPDATE `item` SET `last-child` = 0 WHERE `parent-uri` = '%s' AND `uid` = %d AND `id` != %d",
1283                         dbesc($arr['uri']),
1284                         intval($arr['uid']),
1285                         intval($current_post)
1286                 );
1287         }
1288
1289         $deleted = tag_deliver($arr['uid'],$current_post);
1290
1291         // current post can be deleted if is for a communuty page and no mention are
1292         // in it.
1293         if (!$deleted) {
1294
1295                 // Store the fresh generated item into the cache
1296                 $cachefile = get_cachefile($arr["guid"]."-".hash("md5", $arr['body']));
1297
1298                 if (($cachefile != '') AND !file_exists($cachefile)) {
1299                         $s = prepare_text($arr['body']);
1300                         $a = get_app();
1301                         $stamp1 = microtime(true);
1302                         file_put_contents($cachefile, $s);
1303                         $a->save_timestamp($stamp1, "file");
1304                         logger('item_store: put item '.$current_post.' into cachefile '.$cachefile);
1305                 }
1306
1307         $r = q('SELECT * FROM `item` WHERE id = %d', intval($current_post));
1308         if (count($r) == 1) {
1309             call_hooks('post_remote_end', $r[0]);
1310         }
1311         else {
1312             logger('item_store: new item not found in DB, id ' . $current_post);
1313         }
1314         }
1315         return $current_post;
1316 }
1317
1318 function get_item_contact($item,$contacts) {
1319         if(! count($contacts) || (! is_array($item)))
1320                 return false;
1321         foreach($contacts as $contact) {
1322                 if($contact['id'] == $item['contact-id']) {
1323                         return $contact;
1324                         break; // NOTREACHED
1325                 }
1326         }
1327         return false;
1328 }
1329
1330 /**
1331  * look for mention tags and setup a second delivery chain for forum/community posts if appropriate
1332  * @param int $uid
1333  * @param int $item_id
1334  * @return bool true if item was deleted, else false
1335  */
1336 function tag_deliver($uid,$item_id) {
1337
1338         //
1339
1340         $a = get_app();
1341
1342         $mention = false;
1343
1344         $u = q("select * from user where uid = %d limit 1",
1345                 intval($uid)
1346         );
1347         if(! count($u))
1348                 return;
1349
1350         $community_page = (($u[0]['page-flags'] == PAGE_COMMUNITY) ? true : false);
1351         $prvgroup = (($u[0]['page-flags'] == PAGE_PRVGROUP) ? true : false);
1352
1353
1354         $i = q("select * from item where id = %d and uid = %d limit 1",
1355                 intval($item_id),
1356                 intval($uid)
1357         );
1358         if(! count($i))
1359                 return;
1360
1361         $item = $i[0];
1362
1363         $link = normalise_link($a->get_baseurl() . '/profile/' . $u[0]['nickname']);
1364
1365         // Diaspora uses their own hardwired link URL in @-tags
1366         // instead of the one we supply with webfinger
1367
1368         $dlink = normalise_link($a->get_baseurl() . '/u/' . $u[0]['nickname']);
1369
1370         $cnt = preg_match_all('/[\@\!]\[url\=(.*?)\](.*?)\[\/url\]/ism',$item['body'],$matches,PREG_SET_ORDER);
1371         if($cnt) {
1372                 foreach($matches as $mtch) {
1373                         if(link_compare($link,$mtch[1]) || link_compare($dlink,$mtch[1])) {
1374                                 $mention = true;
1375                                 logger('tag_deliver: mention found: ' . $mtch[2]);
1376                         }
1377                 }
1378         }
1379
1380         if(! $mention){
1381                 if ( ($community_page || $prvgroup) &&
1382                           (!$item['wall']) && (!$item['origin']) && ($item['id'] == $item['parent'])){
1383                         // mmh.. no mention.. community page or private group... no wall.. no origin.. top-post (not a comment)
1384                         // delete it!
1385                         logger("tag_deliver: no-mention top-level post to communuty or private group. delete.");
1386                         q("DELETE FROM item WHERE id = %d and uid = %d",
1387                                 intval($item_id),
1388                                 intval($uid)
1389                         );
1390                         return true;
1391                 }
1392                 return;
1393         }
1394
1395
1396         // send a notification
1397
1398         // use a local photo if we have one
1399
1400         $r = q("select * from contact where uid = %d and nurl = '%s' limit 1",
1401                 intval($u[0]['uid']),
1402                 dbesc(normalise_link($item['author-link']))
1403         );
1404         $photo = (($r && count($r)) ? $r[0]['thumb'] : $item['author-avatar']);
1405
1406
1407         require_once('include/enotify.php');
1408         notification(array(
1409                 'type'         => NOTIFY_TAGSELF,
1410                 'notify_flags' => $u[0]['notify-flags'],
1411                 'language'     => $u[0]['language'],
1412                 'to_name'      => $u[0]['username'],
1413                 'to_email'     => $u[0]['email'],
1414                 'uid'          => $u[0]['uid'],
1415                 'item'         => $item,
1416                 'link'         => $a->get_baseurl() . '/display/' . $u[0]['nickname'] . '/' . $item['id'],
1417                 'source_name'  => $item['author-name'],
1418                 'source_link'  => $item['author-link'],
1419                 'source_photo' => $photo,
1420                 'verb'         => ACTIVITY_TAG,
1421                 'otype'        => 'item'
1422         ));
1423
1424
1425         $arr = array('item' => $item, 'user' => $u[0], 'contact' => $r[0]);
1426
1427         call_hooks('tagged', $arr);
1428
1429         if((! $community_page) && (! $prvgroup))
1430                 return;
1431
1432
1433         // tgroup delivery - setup a second delivery chain
1434         // prevent delivery looping - only proceed
1435         // if the message originated elsewhere and is a top-level post
1436
1437         if(($item['wall']) || ($item['origin']) || ($item['id'] != $item['parent']))
1438                 return;
1439
1440         // now change this copy of the post to a forum head message and deliver to all the tgroup members
1441
1442
1443         $c = q("select name, url, thumb from contact where self = 1 and uid = %d limit 1",
1444                 intval($u[0]['uid'])
1445         );
1446         if(! count($c))
1447                 return;
1448
1449         // also reset all the privacy bits to the forum default permissions
1450
1451         $private = ($u[0]['allow_cid'] || $u[0]['allow_gid'] || $u[0]['deny_cid'] || $u[0]['deny_gid']) ? 1 : 0;
1452
1453         $forum_mode = (($prvgroup) ? 2 : 1);
1454
1455         q("update item set wall = 1, origin = 1, forum_mode = %d, `owner-name` = '%s', `owner-link` = '%s', `owner-avatar` = '%s',
1456                 `private` = %d, `allow_cid` = '%s', `allow_gid` = '%s', `deny_cid` = '%s', `deny_gid` = '%s'  where id = %d",
1457                 intval($forum_mode),
1458                 dbesc($c[0]['name']),
1459                 dbesc($c[0]['url']),
1460                 dbesc($c[0]['thumb']),
1461                 intval($private),
1462                 dbesc($u[0]['allow_cid']),
1463                 dbesc($u[0]['allow_gid']),
1464                 dbesc($u[0]['deny_cid']),
1465                 dbesc($u[0]['deny_gid']),
1466                 intval($item_id)
1467         );
1468         update_thread($item_id);
1469
1470         proc_run('php','include/notifier.php','tgroup',$item_id);
1471
1472 }
1473
1474
1475
1476 function tgroup_check($uid,$item) {
1477
1478         $a = get_app();
1479
1480         $mention = false;
1481
1482         // check that the message originated elsewhere and is a top-level post
1483
1484         if(($item['wall']) || ($item['origin']) || ($item['uri'] != $item['parent-uri']))
1485                 return false;
1486
1487
1488         $u = q("select * from user where uid = %d limit 1",
1489                 intval($uid)
1490         );
1491         if(! count($u))
1492                 return false;
1493
1494         $community_page = (($u[0]['page-flags'] == PAGE_COMMUNITY) ? true : false);
1495         $prvgroup = (($u[0]['page-flags'] == PAGE_PRVGROUP) ? true : false);
1496
1497
1498         $link = normalise_link($a->get_baseurl() . '/profile/' . $u[0]['nickname']);
1499
1500         // Diaspora uses their own hardwired link URL in @-tags
1501         // instead of the one we supply with webfinger
1502
1503         $dlink = normalise_link($a->get_baseurl() . '/u/' . $u[0]['nickname']);
1504
1505         $cnt = preg_match_all('/[\@\!]\[url\=(.*?)\](.*?)\[\/url\]/ism',$item['body'],$matches,PREG_SET_ORDER);
1506         if($cnt) {
1507                 foreach($matches as $mtch) {
1508                         if(link_compare($link,$mtch[1]) || link_compare($dlink,$mtch[1])) {
1509                                 $mention = true;
1510                                 logger('tgroup_check: mention found: ' . $mtch[2]);
1511                         }
1512                 }
1513         }
1514
1515         if(! $mention)
1516                 return false;
1517
1518         if((! $community_page) && (! $prvgroup))
1519                 return false;
1520
1521
1522
1523         return true;
1524
1525 }
1526
1527
1528
1529
1530
1531
1532 function dfrn_deliver($owner,$contact,$atom, $dissolve = false) {
1533
1534         $a = get_app();
1535
1536         $idtosend = $orig_id = (($contact['dfrn-id']) ? $contact['dfrn-id'] : $contact['issued-id']);
1537
1538         if($contact['duplex'] && $contact['dfrn-id'])
1539                 $idtosend = '0:' . $orig_id;
1540         if($contact['duplex'] && $contact['issued-id'])
1541                 $idtosend = '1:' . $orig_id;
1542
1543         $rino = ((function_exists('mcrypt_encrypt')) ? 1 : 0);
1544
1545         $rino_enable = get_config('system','rino_encrypt');
1546
1547         if(! $rino_enable)
1548                 $rino = 0;
1549
1550         $ssl_val = intval(get_config('system','ssl_policy'));
1551         $ssl_policy = '';
1552
1553         switch($ssl_val){
1554                 case SSL_POLICY_FULL:
1555                         $ssl_policy = 'full';
1556                         break;
1557                 case SSL_POLICY_SELFSIGN:
1558                         $ssl_policy = 'self';
1559                         break;
1560                 case SSL_POLICY_NONE:
1561                 default:
1562                         $ssl_policy = 'none';
1563                         break;
1564         }
1565
1566         $url = $contact['notify'] . '&dfrn_id=' . $idtosend . '&dfrn_version=' . DFRN_PROTOCOL_VERSION . (($rino) ? '&rino=1' : '');
1567
1568         logger('dfrn_deliver: ' . $url);
1569
1570         $xml = fetch_url($url);
1571
1572         $curl_stat = $a->get_curl_code();
1573         if(! $curl_stat)
1574                 return(-1); // timed out
1575
1576         logger('dfrn_deliver: ' . $xml, LOGGER_DATA);
1577
1578         if(! $xml)
1579                 return 3;
1580
1581         if(strpos($xml,'<?xml') === false) {
1582                 logger('dfrn_deliver: no valid XML returned');
1583                 logger('dfrn_deliver: returned XML: ' . $xml, LOGGER_DATA);
1584                 return 3;
1585         }
1586
1587         $res = parse_xml_string($xml);
1588
1589         if((intval($res->status) != 0) || (! strlen($res->challenge)) || (! strlen($res->dfrn_id)))
1590                 return (($res->status) ? $res->status : 3);
1591
1592         $postvars     = array();
1593         $sent_dfrn_id = hex2bin((string) $res->dfrn_id);
1594         $challenge    = hex2bin((string) $res->challenge);
1595         $perm         = (($res->perm) ? $res->perm : null);
1596         $dfrn_version = (float) (($res->dfrn_version) ? $res->dfrn_version : 2.0);
1597         $rino_allowed = ((intval($res->rino) === 1) ? 1 : 0);
1598         $page         = (($owner['page-flags'] == PAGE_COMMUNITY) ? 1 : 0);
1599
1600         if($owner['page-flags'] == PAGE_PRVGROUP)
1601                 $page = 2;
1602
1603         $final_dfrn_id = '';
1604
1605         if($perm) {
1606                 if((($perm == 'rw') && (! intval($contact['writable']))) 
1607                 || (($perm == 'r') && (intval($contact['writable'])))) {
1608                         q("update contact set writable = %d where id = %d",
1609                                 intval(($perm == 'rw') ? 1 : 0),
1610                                 intval($contact['id'])
1611                         );
1612                         $contact['writable'] = (string) 1 - intval($contact['writable']);
1613                 }
1614         }
1615
1616         if(($contact['duplex'] && strlen($contact['pubkey'])) 
1617                 || ($owner['page-flags'] == PAGE_COMMUNITY && strlen($contact['pubkey']))
1618                 || ($contact['rel'] == CONTACT_IS_SHARING && strlen($contact['pubkey']))) {
1619                 openssl_public_decrypt($sent_dfrn_id,$final_dfrn_id,$contact['pubkey']);
1620                 openssl_public_decrypt($challenge,$postvars['challenge'],$contact['pubkey']);
1621         }
1622         else {
1623                 openssl_private_decrypt($sent_dfrn_id,$final_dfrn_id,$contact['prvkey']);
1624                 openssl_private_decrypt($challenge,$postvars['challenge'],$contact['prvkey']);
1625         }
1626
1627         $final_dfrn_id = substr($final_dfrn_id, 0, strpos($final_dfrn_id, '.'));
1628
1629         if(strpos($final_dfrn_id,':') == 1)
1630                 $final_dfrn_id = substr($final_dfrn_id,2);
1631
1632         if($final_dfrn_id != $orig_id) {
1633                 logger('dfrn_deliver: wrong dfrn_id.');
1634                 // did not decode properly - cannot trust this site 
1635                 return 3;
1636         }
1637
1638         $postvars['dfrn_id']      = $idtosend;
1639         $postvars['dfrn_version'] = DFRN_PROTOCOL_VERSION;
1640         if($dissolve)
1641                 $postvars['dissolve'] = '1';
1642
1643
1644         if((($contact['rel']) && ($contact['rel'] != CONTACT_IS_SHARING) && (! $contact['blocked'])) || ($owner['page-flags'] == PAGE_COMMUNITY)) {
1645                 $postvars['data'] = $atom;
1646                 $postvars['perm'] = 'rw';
1647         }
1648         else {
1649                 $postvars['data'] = str_replace('<dfrn:comment-allow>1','<dfrn:comment-allow>0',$atom);
1650                 $postvars['perm'] = 'r';
1651         }
1652
1653         $postvars['ssl_policy'] = $ssl_policy;
1654
1655         if($page)
1656                 $postvars['page'] = $page;
1657         
1658         if($rino && $rino_allowed && (! $dissolve)) {
1659                 $key = substr(random_string(),0,16);
1660                 $data = bin2hex(aes_encrypt($postvars['data'],$key));
1661                 $postvars['data'] = $data;
1662                 logger('rino: sent key = ' . $key, LOGGER_DEBUG);       
1663
1664
1665                 if($dfrn_version >= 2.1) {      
1666                         if(($contact['duplex'] && strlen($contact['pubkey'])) 
1667                                 || ($owner['page-flags'] == PAGE_COMMUNITY && strlen($contact['pubkey']))
1668                                 || ($contact['rel'] == CONTACT_IS_SHARING && strlen($contact['pubkey']))) {
1669
1670                                 openssl_public_encrypt($key,$postvars['key'],$contact['pubkey']);
1671                         }
1672                         else {
1673                                 openssl_private_encrypt($key,$postvars['key'],$contact['prvkey']);
1674                         }
1675                 }
1676                 else {
1677                         if(($contact['duplex'] && strlen($contact['prvkey'])) || ($owner['page-flags'] == PAGE_COMMUNITY)) {
1678                                 openssl_private_encrypt($key,$postvars['key'],$contact['prvkey']);
1679                         }
1680                         else {
1681                                 openssl_public_encrypt($key,$postvars['key'],$contact['pubkey']);
1682                         }
1683                 }
1684
1685                 logger('md5 rawkey ' . md5($postvars['key']));
1686
1687                 $postvars['key'] = bin2hex($postvars['key']);
1688         }
1689
1690         logger('dfrn_deliver: ' . "SENDING: " . print_r($postvars,true), LOGGER_DATA);
1691
1692         $xml = post_url($contact['notify'],$postvars);
1693
1694         logger('dfrn_deliver: ' . "RECEIVED: " . $xml, LOGGER_DATA);
1695
1696         $curl_stat = $a->get_curl_code();
1697         if((! $curl_stat) || (! strlen($xml)))
1698                 return(-1); // timed out
1699
1700         if(($curl_stat == 503) && (stristr($a->get_curl_headers(),'retry-after')))
1701                 return(-1);
1702
1703         if(strpos($xml,'<?xml') === false) {
1704                 logger('dfrn_deliver: phase 2: no valid XML returned');
1705                 logger('dfrn_deliver: phase 2: returned XML: ' . $xml, LOGGER_DATA);
1706                 return 3;
1707         }
1708
1709         if($contact['term-date'] != '0000-00-00 00:00:00') {
1710                 logger("dfrn_deliver: $url back from the dead - removing mark for death");
1711                 require_once('include/Contact.php');
1712                 unmark_for_death($contact);
1713         }
1714
1715         $res = parse_xml_string($xml);
1716
1717         return $res->status; 
1718 }
1719
1720
1721 /*
1722   This function returns true if $update has an edited timestamp newer
1723   than $existing, i.e. $update contains new data which should override
1724   what's already there.  If there is no timestamp yet, the update is
1725   assumed to be newer.  If the update has no timestamp, the existing
1726   item is assumed to be up-to-date.  If the timestamps are equal it
1727   assumes the update has been seen before and should be ignored.
1728   */
1729 function edited_timestamp_is_newer($existing, $update) {
1730     if (!x($existing,'edited') || !$existing['edited']) {
1731         return true;
1732     }
1733     if (!x($update,'edited') || !$update['edited']) {
1734         return false;
1735     }
1736     $existing_edited = datetime_convert('UTC', 'UTC', $existing['edited']);
1737     $update_edited = datetime_convert('UTC', 'UTC', $update['edited']);
1738     return (strcmp($existing_edited, $update_edited) < 0);
1739 }
1740
1741 /**
1742  *
1743  * consume_feed - process atom feed and update anything/everything we might need to update
1744  *
1745  * $xml = the (atom) feed to consume - RSS isn't as fully supported but may work for simple feeds.
1746  *
1747  * $importer = the contact_record (joined to user_record) of the local user who owns this relationship.
1748  *             It is this person's stuff that is going to be updated.
1749  * $contact =  the person who is sending us stuff. If not set, we MAY be processing a "follow" activity
1750  *             from an external network and MAY create an appropriate contact record. Otherwise, we MUST 
1751  *             have a contact record.
1752  * $hub = should we find a hub declation in the feed, pass it back to our calling process, who might (or 
1753  *        might not) try and subscribe to it.
1754  * $datedir sorts in reverse order
1755  * $pass - by default ($pass = 0) we cannot guarantee that a parent item has been 
1756  *      imported prior to its children being seen in the stream unless we are certain
1757  *      of how the feed is arranged/ordered.
1758  * With $pass = 1, we only pull parent items out of the stream.
1759  * With $pass = 2, we only pull children (comments/likes).
1760  *
1761  * So running this twice, first with pass 1 and then with pass 2 will do the right
1762  * thing regardless of feed ordering. This won't be adequate in a fully-threaded
1763  * model where comments can have sub-threads. That would require some massive sorting
1764  * to get all the feed items into a mostly linear ordering, and might still require
1765  * recursion.  
1766  */
1767
1768 function consume_feed($xml,$importer,&$contact, &$hub, $datedir = 0, $pass = 0) {
1769
1770         require_once('library/simplepie/simplepie.inc');
1771
1772         if(! strlen($xml)) {
1773                 logger('consume_feed: empty input');
1774                 return;
1775         }
1776
1777         $feed = new SimplePie();
1778         $feed->set_raw_data($xml);
1779         if($datedir)
1780                 $feed->enable_order_by_date(true);
1781         else
1782                 $feed->enable_order_by_date(false);
1783         $feed->init();
1784
1785         if($feed->error())
1786                 logger('consume_feed: Error parsing XML: ' . $feed->error());
1787
1788         $permalink = $feed->get_permalink();
1789
1790         // Check at the feed level for updated contact name and/or photo
1791
1792         $name_updated  = '';
1793         $new_name = '';
1794         $photo_timestamp = '';
1795         $photo_url = '';
1796         $birthday = '';
1797
1798         $hubs = $feed->get_links('hub');
1799         logger('consume_feed: hubs: ' . print_r($hubs,true), LOGGER_DATA);
1800
1801         if(count($hubs))
1802                 $hub = implode(',', $hubs);
1803
1804         $rawtags = $feed->get_feed_tags( NAMESPACE_DFRN, 'owner');
1805         if(! $rawtags)
1806                 $rawtags = $feed->get_feed_tags( SIMPLEPIE_NAMESPACE_ATOM_10, 'author');
1807         if($rawtags) {
1808                 $elems = $rawtags[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10];
1809                 if($elems['name'][0]['attribs'][NAMESPACE_DFRN]['updated']) {
1810                         $name_updated = $elems['name'][0]['attribs'][NAMESPACE_DFRN]['updated'];
1811                         $new_name = $elems['name'][0]['data'];
1812                 }
1813                 if((x($elems,'link')) && ($elems['link'][0]['attribs']['']['rel'] === 'photo') && ($elems['link'][0]['attribs'][NAMESPACE_DFRN]['updated'])) {
1814                         $photo_timestamp = datetime_convert('UTC','UTC',$elems['link'][0]['attribs'][NAMESPACE_DFRN]['updated']);
1815                         $photo_url = $elems['link'][0]['attribs']['']['href'];
1816                 }
1817
1818                 if((x($rawtags[0]['child'], NAMESPACE_DFRN)) && (x($rawtags[0]['child'][NAMESPACE_DFRN],'birthday'))) {
1819                         $birthday = datetime_convert('UTC','UTC', $rawtags[0]['child'][NAMESPACE_DFRN]['birthday'][0]['data']);
1820                 }
1821         }
1822
1823         if((is_array($contact)) && ($photo_timestamp) && (strlen($photo_url)) && ($photo_timestamp > $contact['avatar-date'])) {
1824                 logger('consume_feed: Updating photo for ' . $contact['name']);
1825                 require_once("include/Photo.php");
1826                 $photo_failure = false;
1827                 $have_photo = false;
1828
1829                 $r = q("SELECT `resource-id` FROM `photo` WHERE `contact-id` = %d AND `uid` = %d LIMIT 1",
1830                         intval($contact['id']),
1831                         intval($contact['uid'])
1832                 );
1833                 if(count($r)) {
1834                         $resource_id = $r[0]['resource-id'];
1835                         $have_photo = true;
1836                 }
1837                 else {
1838                         $resource_id = photo_new_resource();
1839                 }
1840
1841                 $img_str = fetch_url($photo_url,true);
1842                 // guess mimetype from headers or filename
1843                 $type = guess_image_type($photo_url,true);
1844
1845
1846                 $img = new Photo($img_str, $type);
1847                 if($img->is_valid()) {
1848                         if($have_photo) {
1849                                 q("DELETE FROM `photo` WHERE `resource-id` = '%s' AND `contact-id` = %d AND `uid` = %d",
1850                                         dbesc($resource_id),
1851                                         intval($contact['id']),
1852                                         intval($contact['uid'])
1853                                 );
1854                         }
1855
1856                         $img->scaleImageSquare(175);
1857
1858                         $hash = $resource_id;
1859                         $r = $img->store($contact['uid'], $contact['id'], $hash, basename($photo_url), 'Contact Photos', 4);
1860
1861                         $img->scaleImage(80);
1862                         $r = $img->store($contact['uid'], $contact['id'], $hash, basename($photo_url), 'Contact Photos', 5);
1863
1864                         $img->scaleImage(48);
1865                         $r = $img->store($contact['uid'], $contact['id'], $hash, basename($photo_url), 'Contact Photos', 6);
1866
1867                         $a = get_app();
1868
1869                         q("UPDATE `contact` SET `avatar-date` = '%s', `photo` = '%s', `thumb` = '%s', `micro` = '%s'
1870                                 WHERE `uid` = %d AND `id` = %d",
1871                                 dbesc(datetime_convert()),
1872                                 dbesc($a->get_baseurl() . '/photo/' . $hash . '-4.'.$img->getExt()),
1873                                 dbesc($a->get_baseurl() . '/photo/' . $hash . '-5.'.$img->getExt()),
1874                                 dbesc($a->get_baseurl() . '/photo/' . $hash . '-6.'.$img->getExt()),
1875                                 intval($contact['uid']),
1876                                 intval($contact['id'])
1877                         );
1878                 }
1879         }
1880
1881         if((is_array($contact)) && ($name_updated) && (strlen($new_name)) && ($name_updated > $contact['name-date'])) {
1882                 $r = q("select * from contact where uid = %d and id = %d limit 1",
1883                         intval($contact['uid']),
1884                         intval($contact['id'])
1885                 );
1886
1887                 $x = q("UPDATE `contact` SET `name` = '%s', `name-date` = '%s' WHERE `uid` = %d AND `id` = %d",
1888                         dbesc(notags(trim($new_name))),
1889                         dbesc(datetime_convert()),
1890                         intval($contact['uid']),
1891                         intval($contact['id'])
1892                 );
1893
1894                 // do our best to update the name on content items
1895
1896                 if(count($r)) {
1897                         q("update item set `author-name` = '%s' where `author-name` = '%s' and `author-link` = '%s' and uid = %d",
1898                                 dbesc(notags(trim($new_name))),
1899                                 dbesc($r[0]['name']),
1900                                 dbesc($r[0]['url']),
1901                                 intval($contact['uid'])
1902                         );
1903                 }
1904         }
1905
1906         if(strlen($birthday)) {
1907                 if(substr($birthday,0,4) != $contact['bdyear']) {
1908                         logger('consume_feed: updating birthday: ' . $birthday);
1909
1910                         /**
1911                          *
1912                          * Add new birthday event for this person
1913                          *
1914                          * $bdtext is just a readable placeholder in case the event is shared
1915                          * with others. We will replace it during presentation to our $importer
1916                          * to contain a sparkle link and perhaps a photo. 
1917                          *
1918                          */
1919
1920                         $bdtext = sprintf( t('%s\'s birthday'), $contact['name']);
1921                         $bdtext2 = sprintf( t('Happy Birthday %s'), ' [url=' . $contact['url'] . ']' . $contact['name'] . '[/url]' ) ;
1922
1923
1924                         $r = q("INSERT INTO `event` (`uid`,`cid`,`created`,`edited`,`start`,`finish`,`summary`,`desc`,`type`)
1925                                 VALUES ( %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s' ) ",
1926                                 intval($contact['uid']),
1927                                 intval($contact['id']),
1928                                 dbesc(datetime_convert()),
1929                                 dbesc(datetime_convert()),
1930                                 dbesc(datetime_convert('UTC','UTC', $birthday)),
1931                                 dbesc(datetime_convert('UTC','UTC', $birthday . ' + 1 day ')),
1932                                 dbesc($bdtext),
1933                                 dbesc($bdtext2),
1934                                 dbesc('birthday')
1935                         );
1936
1937
1938                         // update bdyear
1939
1940                         q("UPDATE `contact` SET `bdyear` = '%s' WHERE `uid` = %d AND `id` = %d",
1941                                 dbesc(substr($birthday,0,4)),
1942                                 intval($contact['uid']),
1943                                 intval($contact['id'])
1944                         );
1945
1946                         // This function is called twice without reloading the contact
1947                         // Make sure we only create one event. This is why &$contact 
1948                         // is a reference var in this function
1949
1950                         $contact['bdyear'] = substr($birthday,0,4);
1951                 }
1952
1953         }
1954
1955         $community_page = 0;
1956         $rawtags = $feed->get_feed_tags( NAMESPACE_DFRN, 'community');
1957         if($rawtags) {
1958                 $community_page = intval($rawtags[0]['data']);
1959         }
1960         if(is_array($contact) && intval($contact['forum']) != $community_page) {
1961                 q("update contact set forum = %d where id = %d",
1962                         intval($community_page),
1963                         intval($contact['id'])
1964                 );
1965                 $contact['forum'] = (string) $community_page;
1966         }
1967
1968
1969         // process any deleted entries
1970
1971         $del_entries = $feed->get_feed_tags(NAMESPACE_TOMB, 'deleted-entry');
1972         if(is_array($del_entries) && count($del_entries) && $pass != 2) {
1973                 foreach($del_entries as $dentry) {
1974                         $deleted = false;
1975                         if(isset($dentry['attribs']['']['ref'])) {
1976                                 $uri = $dentry['attribs']['']['ref'];
1977                                 $deleted = true;
1978                                 if(isset($dentry['attribs']['']['when'])) {
1979                                         $when = $dentry['attribs']['']['when'];
1980                                         $when = datetime_convert('UTC','UTC', $when, 'Y-m-d H:i:s');
1981                                 }
1982                                 else
1983                                         $when = datetime_convert('UTC','UTC','now','Y-m-d H:i:s');
1984                         }
1985                         if($deleted && is_array($contact)) {
1986                                 $r = q("SELECT `item`.*, `contact`.`self` FROM `item` INNER JOIN `contact` on `item`.`contact-id` = `contact`.`id` 
1987                                         WHERE `uri` = '%s' AND `item`.`uid` = %d AND `contact-id` = %d AND NOT `item`.`file` LIKE '%%[%%' LIMIT 1",
1988                                         dbesc($uri),
1989                                         intval($importer['uid']),
1990                                         intval($contact['id'])
1991                                 );
1992                                 if(count($r)) {
1993                                         $item = $r[0];
1994
1995                                         if(! $item['deleted'])
1996                                                 logger('consume_feed: deleting item ' . $item['id'] . ' uri=' . $item['uri'], LOGGER_DEBUG);
1997
1998                                         if(($item['verb'] === ACTIVITY_TAG) && ($item['object-type'] === ACTIVITY_OBJ_TAGTERM)) {
1999                                                 $xo = parse_xml_string($item['object'],false);
2000                                                 $xt = parse_xml_string($item['target'],false);
2001                                                 if($xt->type === ACTIVITY_OBJ_NOTE) {
2002                                                         $i = q("select * from `item` where uri = '%s' and uid = %d limit 1",
2003                                                                 dbesc($xt->id),
2004                                                                 intval($importer['importer_uid'])
2005                                                         );
2006                                                         if(count($i)) {
2007
2008                                                                 // For tags, the owner cannot remove the tag on the author's copy of the post.
2009
2010                                                                 $owner_remove = (($item['contact-id'] == $i[0]['contact-id']) ? true: false);
2011                                                                 $author_remove = (($item['origin'] && $item['self']) ? true : false);
2012                                                                 $author_copy = (($item['origin']) ? true : false);
2013
2014                                                                 if($owner_remove && $author_copy)
2015                                                                         continue;
2016                                                                 if($author_remove || $owner_remove) {
2017                                                                         $tags = explode(',',$i[0]['tag']);
2018                                                                         $newtags = array();
2019                                                                         if(count($tags)) {
2020                                                                                 foreach($tags as $tag)
2021                                                                                         if(trim($tag) !== trim($xo->body))
2022                                                                                                 $newtags[] = trim($tag);
2023                                                                         }
2024                                                                         q("update item set tag = '%s' where id = %d",
2025                                                                                 dbesc(implode(',',$newtags)),
2026                                                                                 intval($i[0]['id'])
2027                                                                         );
2028                                                                         create_tags_from_item($i[0]['id']);
2029                                                                 }
2030                                                         }
2031                                                 }
2032                                         }
2033
2034                                         if($item['uri'] == $item['parent-uri']) {
2035                                                 $r = q("UPDATE `item` SET `deleted` = 1, `edited` = '%s', `changed` = '%s',
2036                                                         `body` = '', `title` = ''
2037                                                         WHERE `parent-uri` = '%s' AND `uid` = %d",
2038                                                         dbesc($when),
2039                                                         dbesc(datetime_convert()),
2040                                                         dbesc($item['uri']),
2041                                                         intval($importer['uid'])
2042                                                 );
2043                                                 create_tags_from_itemuri($item['uri'], $importer['uid']);
2044                                                 update_thread_uri($item['uri'], $importer['uid']);
2045                                         }
2046                                         else {
2047                                                 $r = q("UPDATE `item` SET `deleted` = 1, `edited` = '%s', `changed` = '%s',
2048                                                         `body` = '', `title` = ''
2049                                                         WHERE `uri` = '%s' AND `uid` = %d",
2050                                                         dbesc($when),
2051                                                         dbesc(datetime_convert()),
2052                                                         dbesc($uri),
2053                                                         intval($importer['uid'])
2054                                                 );
2055                                                 create_tags_from_itemuri($uri, $importer['uid']);
2056                                                 if($item['last-child']) {
2057                                                         // ensure that last-child is set in case the comment that had it just got wiped.
2058                                                         q("UPDATE `item` SET `last-child` = 0, `changed` = '%s' WHERE `parent-uri` = '%s' AND `uid` = %d ",
2059                                                                 dbesc(datetime_convert()),
2060                                                                 dbesc($item['parent-uri']),
2061                                                                 intval($item['uid'])
2062                                                         );
2063                                                         // who is the last child now?
2064                                                         $r = q("SELECT `id` FROM `item` WHERE `parent-uri` = '%s' AND `type` != 'activity' AND `deleted` = 0 AND `moderated` = 0 AND `uid` = %d
2065                                                                 ORDER BY `created` DESC LIMIT 1",
2066                                                                         dbesc($item['parent-uri']),
2067                                                                         intval($importer['uid'])
2068                                                         );
2069                                                         if(count($r)) {
2070                                                                 q("UPDATE `item` SET `last-child` = 1 WHERE `id` = %d",
2071                                                                         intval($r[0]['id'])
2072                                                                 );
2073                                                         }
2074                                                 }
2075                                         }
2076                                 }
2077                         }
2078                 }
2079         }
2080
2081         // Now process the feed
2082
2083         if($feed->get_item_quantity()) {
2084
2085                 logger('consume_feed: feed item count = ' . $feed->get_item_quantity());
2086
2087         // in inverse date order
2088                 if ($datedir)
2089                         $items = array_reverse($feed->get_items());
2090                 else
2091                         $items = $feed->get_items();
2092
2093
2094                 foreach($items as $item) {
2095
2096                         $is_reply = false;
2097                         $item_id = $item->get_id();
2098                         $rawthread = $item->get_item_tags( NAMESPACE_THREAD,'in-reply-to');
2099                         if(isset($rawthread[0]['attribs']['']['ref'])) {
2100                                 $is_reply = true;
2101                                 $parent_uri = $rawthread[0]['attribs']['']['ref'];
2102                         }
2103
2104                         if(($is_reply) && is_array($contact)) {
2105
2106                                 if($pass == 1)
2107                                         continue;
2108
2109                                 // not allowed to post
2110
2111                                 if($contact['rel'] == CONTACT_IS_FOLLOWER)
2112                                         continue;
2113
2114
2115                                 // Have we seen it? If not, import it.
2116
2117                                 $item_id  = $item->get_id();
2118                                 $datarray = get_atom_elements($feed, $item, $contact);
2119
2120                                 if((! x($datarray,'author-name')) && ($contact['network'] != NETWORK_DFRN))
2121                                         $datarray['author-name'] = $contact['name'];
2122                                 if((! x($datarray,'author-link')) && ($contact['network'] != NETWORK_DFRN))
2123                                         $datarray['author-link'] = $contact['url'];
2124                                 if((! x($datarray,'author-avatar')) && ($contact['network'] != NETWORK_DFRN))
2125                                         $datarray['author-avatar'] = $contact['thumb'];
2126
2127                                 if((! x($datarray,'author-name')) || (! x($datarray,'author-link'))) {
2128                                         logger('consume_feed: no author information! ' . print_r($datarray,true));
2129                                         continue;
2130                                 }
2131
2132                                 $force_parent = false;
2133                                 if($contact['network'] === NETWORK_OSTATUS || stristr($contact['url'],'twitter.com')) {
2134                                         if($contact['network'] === NETWORK_OSTATUS)
2135                                                 $force_parent = true;
2136                                         if(strlen($datarray['title']))
2137                                                 unset($datarray['title']);
2138                                         $r = q("UPDATE `item` SET `last-child` = 0, `changed` = '%s' WHERE `parent-uri` = '%s' AND `uid` = %d",
2139                                                 dbesc(datetime_convert()),
2140                                                 dbesc($parent_uri),
2141                                                 intval($importer['uid'])
2142                                         );
2143                                         $datarray['last-child'] = 1;
2144                                         update_thread_uri($parent_uri, $importer['uid']);
2145                                 }
2146
2147
2148                                 $r = q("SELECT `uid`, `last-child`, `edited`, `body` FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1",
2149                                         dbesc($item_id),
2150                                         intval($importer['uid'])
2151                                 );
2152
2153                                 // Update content if 'updated' changes
2154
2155                                 if(count($r)) {
2156                                         if (edited_timestamp_is_newer($r[0], $datarray)) {
2157
2158                                                 // do not accept (ignore) an earlier edit than one we currently have.
2159                                                 if(datetime_convert('UTC','UTC',$datarray['edited']) < $r[0]['edited'])
2160                                                         continue;
2161
2162                                                 $r = q("UPDATE `item` SET `title` = '%s', `body` = '%s', `tag` = '%s', `edited` = '%s' WHERE `uri` = '%s' AND `uid` = %d",
2163                                                         dbesc($datarray['title']),
2164                                                         dbesc($datarray['body']),
2165                                                         dbesc($datarray['tag']),
2166                                                         dbesc(datetime_convert('UTC','UTC',$datarray['edited'])),
2167                                                         dbesc($item_id),
2168                                                         intval($importer['uid'])
2169                                                 );
2170                                                 create_tags_from_itemuri($item_id, $importer['uid']);
2171                                                 update_thread_uri($item_id, $importer['uid']);
2172                                         }
2173
2174                                         // update last-child if it changes
2175
2176                                         $allow = $item->get_item_tags( NAMESPACE_DFRN, 'comment-allow');
2177                                         if(($allow) && ($allow[0]['data'] != $r[0]['last-child'])) {
2178                                                 $r = q("UPDATE `item` SET `last-child` = 0, `changed` = '%s' WHERE `parent-uri` = '%s' AND `uid` = %d",
2179                                                         dbesc(datetime_convert()),
2180                                                         dbesc($parent_uri),
2181                                                         intval($importer['uid'])
2182                                                 );
2183                                                 $r = q("UPDATE `item` SET `last-child` = %d , `changed` = '%s'  WHERE `uri` = '%s' AND `uid` = %d",
2184                                                         intval($allow[0]['data']),
2185                                                         dbesc(datetime_convert()),
2186                                                         dbesc($item_id),
2187                                                         intval($importer['uid'])
2188                                                 );
2189                                                 update_thread_uri($item_id, $importer['uid']);
2190                                         }
2191                                         continue;
2192                                 }
2193
2194
2195                                 if(($contact['network'] === NETWORK_FEED) || (! strlen($contact['notify']))) {
2196                                         // one way feed - no remote comment ability
2197                                         $datarray['last-child'] = 0;
2198                                 }
2199                                 $datarray['parent-uri'] = $parent_uri;
2200                                 $datarray['uid'] = $importer['uid'];
2201                                 $datarray['contact-id'] = $contact['id'];
2202                                 if((activity_match($datarray['verb'],ACTIVITY_LIKE)) || (activity_match($datarray['verb'],ACTIVITY_DISLIKE))) {
2203                                         $datarray['type'] = 'activity';
2204                                         $datarray['gravity'] = GRAVITY_LIKE;
2205                                         // only one like or dislike per person
2206                                         // splitted into two queries for performance issues
2207                                         $r = q("select id from item where uid = %d and `contact-id` = %d and verb ='%s' and deleted = 0 and (`parent-uri` = '%s') limit 1",
2208                                                 intval($datarray['uid']),
2209                                                 intval($datarray['contact-id']),
2210                                                 dbesc($datarray['verb']),
2211                                                 dbesc($parent_uri)
2212                                         );
2213                                         if($r && count($r))
2214                                                 continue;
2215
2216                                         $r = q("select id from item where uid = %d and `contact-id` = %d and verb ='%s' and deleted = 0 and (`thr-parent` = '%s') limit 1",
2217                                                 intval($datarray['uid']),
2218                                                 intval($datarray['contact-id']),
2219                                                 dbesc($datarray['verb']),
2220                                                 dbesc($parent_uri)
2221                                         );
2222                                         if($r && count($r))
2223                                                 continue;
2224                                 }
2225
2226                                 if(($datarray['verb'] === ACTIVITY_TAG) && ($datarray['object-type'] === ACTIVITY_OBJ_TAGTERM)) {
2227                                         $xo = parse_xml_string($datarray['object'],false);
2228                                         $xt = parse_xml_string($datarray['target'],false);
2229
2230                                         if($xt->type == ACTIVITY_OBJ_NOTE) {
2231                                                 $r = q("select * from item where `uri` = '%s' AND `uid` = %d limit 1",
2232                                                         dbesc($xt->id),
2233                                                         intval($importer['importer_uid'])
2234                                                 );
2235                                                 if(! count($r))
2236                                                         continue;
2237
2238                                                 // extract tag, if not duplicate, add to parent item
2239                                                 if($xo->id && $xo->content) {
2240                                                         $newtag = '#[url=' . $xo->id . ']'. $xo->content . '[/url]';
2241                                                         if(! (stristr($r[0]['tag'],$newtag))) {
2242                                                                 q("UPDATE item SET tag = '%s' WHERE id = %d",
2243                                                                         dbesc($r[0]['tag'] . (strlen($r[0]['tag']) ? ',' : '') . $newtag),
2244                                                                         intval($r[0]['id'])
2245                                                                 );
2246                                                                 create_tags_from_item($r[0]['id']);
2247                                                         }
2248                                                 }
2249                                         }
2250                                 }
2251
2252                                 $r = item_store($datarray,$force_parent);
2253                                 continue;
2254                         }
2255
2256                         else {
2257
2258                                 // Head post of a conversation. Have we seen it? If not, import it.
2259
2260                                 $item_id  = $item->get_id();
2261
2262                                 $datarray = get_atom_elements($feed, $item, $contact);
2263
2264                                 if(is_array($contact)) {
2265                                         if((! x($datarray,'author-name')) && ($contact['network'] != NETWORK_DFRN))
2266                                                 $datarray['author-name'] = $contact['name'];
2267                                         if((! x($datarray,'author-link')) && ($contact['network'] != NETWORK_DFRN))
2268                                                 $datarray['author-link'] = $contact['url'];
2269                                         if((! x($datarray,'author-avatar')) && ($contact['network'] != NETWORK_DFRN))
2270                                                 $datarray['author-avatar'] = $contact['thumb'];
2271                                 }
2272
2273                                 if((! x($datarray,'author-name')) || (! x($datarray,'author-link'))) {
2274                                         logger('consume_feed: no author information! ' . print_r($datarray,true));
2275                                         continue;
2276                                 }
2277
2278                                 // special handling for events
2279
2280                                 if((x($datarray,'object-type')) && ($datarray['object-type'] === ACTIVITY_OBJ_EVENT)) {
2281                                         $ev = bbtoevent($datarray['body']);
2282                                         if(x($ev,'desc') && x($ev,'start')) {
2283                                                 $ev['uid'] = $importer['uid'];
2284                                                 $ev['uri'] = $item_id;
2285                                                 $ev['edited'] = $datarray['edited'];
2286                                                 $ev['private'] = $datarray['private'];
2287
2288                                                 if(is_array($contact))
2289                                                         $ev['cid'] = $contact['id'];
2290                                                 $r = q("SELECT * FROM `event` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1",
2291                                                         dbesc($item_id),
2292                                                         intval($importer['uid'])
2293                                                 );
2294                                                 if(count($r))
2295                                                         $ev['id'] = $r[0]['id'];
2296                                                 $xyz = event_store($ev);
2297                                                 continue;
2298                                         }
2299                                 }
2300
2301                                 if($contact['network'] === NETWORK_OSTATUS || stristr($contact['url'],'twitter.com')) {
2302                                         if(strlen($datarray['title']))
2303                                                 unset($datarray['title']);
2304                                         $datarray['last-child'] = 1;
2305                                 }
2306
2307
2308                                 $r = q("SELECT `uid`, `last-child`, `edited`, `body` FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1",
2309                                         dbesc($item_id),
2310                                         intval($importer['uid'])
2311                                 );
2312
2313                                 // Update content if 'updated' changes
2314
2315                                 if(count($r)) {
2316                                         if (edited_timestamp_is_newer($r[0], $datarray)) {
2317
2318                                                 // do not accept (ignore) an earlier edit than one we currently have.
2319                                                 if(datetime_convert('UTC','UTC',$datarray['edited']) < $r[0]['edited'])
2320                                                         continue;
2321
2322                                                 $r = q("UPDATE `item` SET `title` = '%s', `body` = '%s', `tag` = '%s', `edited` = '%s' WHERE `uri` = '%s' AND `uid` = %d",
2323                                                         dbesc($datarray['title']),
2324                                                         dbesc($datarray['body']),
2325                                                         dbesc($datarray['tag']),
2326                                                         dbesc(datetime_convert('UTC','UTC',$datarray['edited'])),
2327                                                         dbesc($item_id),
2328                                                         intval($importer['uid'])
2329                                                 );
2330                                                 create_tags_from_itemuri($item_id, $importer['uid']);
2331                                                 update_thread_uri($item_id, $importer['uid']);
2332                                         }
2333
2334                                         // update last-child if it changes
2335
2336                                         $allow = $item->get_item_tags( NAMESPACE_DFRN, 'comment-allow');
2337                                         if($allow && $allow[0]['data'] != $r[0]['last-child']) {
2338                                                 $r = q("UPDATE `item` SET `last-child` = %d , `changed` = '%s' WHERE `uri` = '%s' AND `uid` = %d",
2339                                                         intval($allow[0]['data']),
2340                                                         dbesc(datetime_convert()),
2341                                                         dbesc($item_id),
2342                                                         intval($importer['uid'])
2343                                                 );
2344                                                 update_thread_uri($item_id, $importer['uid']);
2345                                         }
2346                                         continue;
2347                                 }
2348
2349                                 if(activity_match($datarray['verb'],ACTIVITY_FOLLOW)) {
2350                                         logger('consume-feed: New follower');
2351                                         new_follower($importer,$contact,$datarray,$item);
2352                                         return;
2353                                 }
2354                                 if(activity_match($datarray['verb'],ACTIVITY_UNFOLLOW))  {
2355                                         lose_follower($importer,$contact,$datarray,$item);
2356                                         return;
2357                                 }
2358
2359                                 if(activity_match($datarray['verb'],ACTIVITY_REQ_FRIEND)) {
2360                                         logger('consume-feed: New friend request');
2361                                         new_follower($importer,$contact,$datarray,$item,true);
2362                                         return;
2363                                 }
2364                                 if(activity_match($datarray['verb'],ACTIVITY_UNFRIEND))  {
2365                                         lose_sharer($importer,$contact,$datarray,$item);
2366                                         return;
2367                                 }
2368
2369
2370                                 if(! is_array($contact))
2371                                         return;
2372
2373
2374                                 if(($contact['network'] === NETWORK_FEED) || (! strlen($contact['notify']))) {
2375                                                 // one way feed - no remote comment ability
2376                                                 $datarray['last-child'] = 0;
2377                                 }
2378                                 if($contact['network'] === NETWORK_FEED)
2379                                         $datarray['private'] = 2;
2380
2381                                 // This is my contact on another system, but it's really me.
2382                                 // Turn this into a wall post.
2383
2384                                 if($contact['remote_self']) {
2385                                         $datarray['wall'] = 1;
2386                                         if($contact['network'] === NETWORK_FEED) {
2387                                                 $datarray['private'] = 0;
2388                                         }
2389                                 }
2390
2391                                 $datarray['parent-uri'] = $item_id;
2392                                 $datarray['uid'] = $importer['uid'];
2393                                 $datarray['contact-id'] = $contact['id'];
2394
2395                                 if(! link_compare($datarray['owner-link'],$contact['url'])) {
2396                                         // The item owner info is not our contact. It's OK and is to be expected if this is a tgroup delivery, 
2397                                         // but otherwise there's a possible data mixup on the sender's system.
2398                                         // the tgroup delivery code called from item_store will correct it if it's a forum,
2399                                         // but we're going to unconditionally correct it here so that the post will always be owned by our contact. 
2400                                         logger('consume_feed: Correcting item owner.', LOGGER_DEBUG);
2401                                         $datarray['owner-name']   = $contact['name'];
2402                                         $datarray['owner-link']   = $contact['url'];
2403                                         $datarray['owner-avatar'] = $contact['thumb'];
2404                                 }
2405
2406                                 // We've allowed "followers" to reach this point so we can decide if they are 
2407                                 // posting an @-tag delivery, which followers are allowed to do for certain
2408                                 // page types. Now that we've parsed the post, let's check if it is legit. Otherwise ignore it. 
2409
2410                                 if(($contact['rel'] == CONTACT_IS_FOLLOWER) && (! tgroup_check($importer['uid'],$datarray)))
2411                                         continue;
2412
2413
2414                                 $r = item_store($datarray);
2415                                 continue;
2416
2417                         }
2418                 }
2419         }
2420 }
2421
2422 function local_delivery($importer,$data) {
2423         $a = get_app();
2424
2425     logger(__function__, LOGGER_TRACE);
2426
2427         if($importer['readonly']) {
2428                 // We aren't receiving stuff from this person. But we will quietly ignore them
2429                 // rather than a blatant "go away" message.
2430                 logger('local_delivery: ignoring');
2431                 return 0;
2432                 //NOTREACHED
2433         }
2434
2435         // Consume notification feed. This may differ from consuming a public feed in several ways
2436         // - might contain email or friend suggestions
2437         // - might contain remote followup to our message
2438         //              - in which case we need to accept it and then notify other conversants
2439         // - we may need to send various email notifications
2440
2441         $feed = new SimplePie();
2442         $feed->set_raw_data($data);
2443         $feed->enable_order_by_date(false);
2444         $feed->init();
2445
2446
2447         if($feed->error())
2448                 logger('local_delivery: Error parsing XML: ' . $feed->error());
2449
2450
2451         // Check at the feed level for updated contact name and/or photo
2452
2453         $name_updated  = '';
2454         $new_name = '';
2455         $photo_timestamp = '';
2456         $photo_url = '';
2457
2458
2459         $rawtags = $feed->get_feed_tags( NAMESPACE_DFRN, 'owner');
2460
2461 // Fallback should not be needed here. If it isn't DFRN it won't have DFRN updated tags
2462 //      if(! $rawtags)
2463 //              $rawtags = $feed->get_feed_tags( SIMPLEPIE_NAMESPACE_ATOM_10, 'author');
2464
2465         if($rawtags) {
2466                 $elems = $rawtags[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10];
2467                 if($elems['name'][0]['attribs'][NAMESPACE_DFRN]['updated']) {
2468                         $name_updated = $elems['name'][0]['attribs'][NAMESPACE_DFRN]['updated'];
2469                         $new_name = $elems['name'][0]['data'];
2470                 }
2471                 if((x($elems,'link')) && ($elems['link'][0]['attribs']['']['rel'] === 'photo') && ($elems['link'][0]['attribs'][NAMESPACE_DFRN]['updated'])) {
2472                         $photo_timestamp = datetime_convert('UTC','UTC',$elems['link'][0]['attribs'][NAMESPACE_DFRN]['updated']);
2473                         $photo_url = $elems['link'][0]['attribs']['']['href'];
2474                 }
2475         }
2476
2477         if(($photo_timestamp) && (strlen($photo_url)) && ($photo_timestamp > $importer['avatar-date'])) {
2478                 logger('local_delivery: Updating photo for ' . $importer['name']);
2479                 require_once("include/Photo.php");
2480                 $photo_failure = false;
2481                 $have_photo = false;
2482
2483                 $r = q("SELECT `resource-id` FROM `photo` WHERE `contact-id` = %d AND `uid` = %d LIMIT 1",
2484                         intval($importer['id']),
2485                         intval($importer['importer_uid'])
2486                 );
2487                 if(count($r)) {
2488                         $resource_id = $r[0]['resource-id'];
2489                         $have_photo = true;
2490                 }
2491                 else {
2492                         $resource_id = photo_new_resource();
2493                 }
2494
2495                 $img_str = fetch_url($photo_url,true);
2496                 // guess mimetype from headers or filename
2497                 $type = guess_image_type($photo_url,true);
2498
2499
2500                 $img = new Photo($img_str, $type);
2501                 if($img->is_valid()) {
2502                         if($have_photo) {
2503                                 q("DELETE FROM `photo` WHERE `resource-id` = '%s' AND `contact-id` = %d AND `uid` = %d",
2504                                         dbesc($resource_id),
2505                                         intval($importer['id']),
2506                                         intval($importer['importer_uid'])
2507                                 );
2508                         }
2509
2510                         $img->scaleImageSquare(175);
2511
2512                         $hash = $resource_id;
2513                         $r = $img->store($importer['importer_uid'], $importer['id'], $hash, basename($photo_url), 'Contact Photos', 4);
2514
2515                         $img->scaleImage(80);
2516                         $r = $img->store($importer['importer_uid'], $importer['id'], $hash, basename($photo_url), 'Contact Photos', 5);
2517
2518                         $img->scaleImage(48);
2519                         $r = $img->store($importer['importer_uid'], $importer['id'], $hash, basename($photo_url), 'Contact Photos', 6);
2520
2521                         $a = get_app();
2522
2523                         q("UPDATE `contact` SET `avatar-date` = '%s', `photo` = '%s', `thumb` = '%s', `micro` = '%s'
2524                                 WHERE `uid` = %d AND `id` = %d",
2525                                 dbesc(datetime_convert()),
2526                                 dbesc($a->get_baseurl() . '/photo/' . $hash . '-4.'.$img->getExt()),
2527                                 dbesc($a->get_baseurl() . '/photo/' . $hash . '-5.'.$img->getExt()),
2528                                 dbesc($a->get_baseurl() . '/photo/' . $hash . '-6.'.$img->getExt()),
2529                                 intval($importer['importer_uid']),
2530                                 intval($importer['id'])
2531                         );
2532                 }
2533         }
2534
2535         if(($name_updated) && (strlen($new_name)) && ($name_updated > $importer['name-date'])) {
2536                 $r = q("select * from contact where uid = %d and id = %d limit 1",
2537                         intval($importer['importer_uid']),
2538                         intval($importer['id'])
2539                 );
2540
2541                 $x = q("UPDATE `contact` SET `name` = '%s', `name-date` = '%s' WHERE `uid` = %d AND `id` = %d",
2542                         dbesc(notags(trim($new_name))),
2543                         dbesc(datetime_convert()),
2544                         intval($importer['importer_uid']),
2545                         intval($importer['id'])
2546                 );
2547
2548                 // do our best to update the name on content items
2549
2550                 if(count($r)) {
2551                         q("update item set `author-name` = '%s' where `author-name` = '%s' and `author-link` = '%s' and uid = %d",
2552                                 dbesc(notags(trim($new_name))),
2553                                 dbesc($r[0]['name']),
2554                                 dbesc($r[0]['url']),
2555                                 intval($importer['importer_uid'])
2556                         );
2557                 }
2558         }
2559
2560
2561
2562         // Currently unsupported - needs a lot of work
2563         $reloc = $feed->get_feed_tags( NAMESPACE_DFRN, 'relocate' );
2564         if(isset($reloc[0]['child'][NAMESPACE_DFRN])) {
2565                 $base = $reloc[0]['child'][NAMESPACE_DFRN];
2566                 $newloc = array();
2567                 $newloc['uid'] = $importer['importer_uid'];
2568                 $newloc['cid'] = $importer['id'];
2569                 $newloc['name'] = notags(unxmlify($base['name'][0]['data']));
2570                 $newloc['photo'] = notags(unxmlify($base['photo'][0]['data']));
2571                 $newloc['thumb'] = notags(unxmlify($base['thumb'][0]['data']));
2572                 $newloc['micro'] = notags(unxmlify($base['micro'][0]['data']));
2573                 $newloc['url'] = notags(unxmlify($base['url'][0]['data']));
2574                 $newloc['request'] = notags(unxmlify($base['request'][0]['data']));
2575                 $newloc['confirm'] = notags(unxmlify($base['confirm'][0]['data']));
2576                 $newloc['notify'] = notags(unxmlify($base['notify'][0]['data']));
2577                 $newloc['poll'] = notags(unxmlify($base['poll'][0]['data']));
2578                 $newloc['sitepubkey'] = notags(unxmlify($base['sitepubkey'][0]['data']));
2579                 /** relocated user must have original key pair */
2580                 /*$newloc['pubkey'] = notags(unxmlify($base['pubkey'][0]['data']));
2581                 $newloc['prvkey'] = notags(unxmlify($base['prvkey'][0]['data']));*/
2582
2583                 logger("items:relocate contact ".print_r($newloc, true).print_r($importer, true), LOGGER_DEBUG);
2584
2585                 // update contact
2586                 $r = q("SELECT photo, url FROM contact WHERE id=%d AND uid=%d;",
2587                         intval($importer['id']),
2588                         intval($importer['importer_uid']));
2589                 if ($r === false)
2590                         return 1;
2591                 $old = $r[0];
2592
2593                 $x = q("UPDATE contact SET
2594                                         name = '%s',
2595                                         photo = '%s',
2596                                         thumb = '%s',
2597                                         micro = '%s',
2598                                         url = '%s',
2599                                         request = '%s',
2600                                         confirm = '%s',
2601                                         notify = '%s',
2602                                         poll = '%s',
2603                                         `site-pubkey` = '%s'
2604                         WHERE id=%d AND uid=%d;",
2605                                         dbesc($newloc['name']),
2606                                         dbesc($newloc['photo']),
2607                                         dbesc($newloc['thumb']),
2608                                         dbesc($newloc['micro']),
2609                                         dbesc($newloc['url']),
2610                                         dbesc($newloc['request']),
2611                                         dbesc($newloc['confirm']),
2612                                         dbesc($newloc['notify']),
2613                                         dbesc($newloc['poll']),
2614                                         dbesc($newloc['sitepubkey']),
2615                                         intval($importer['id']),
2616                                         intval($importer['importer_uid']));
2617
2618                 if ($x === false)
2619                         return 1;
2620                 // update items
2621                 $fields = array(
2622                         'owner-link' => array($old['url'], $newloc['url']),
2623                         'author-link' => array($old['url'], $newloc['url']),
2624                         'owner-avatar' => array($old['photo'], $newloc['photo']),
2625                         'author-avatar' => array($old['photo'], $newloc['photo']),
2626                         );
2627                 foreach ($fields as $n=>$f){
2628                         $x = q("UPDATE `item` SET `%s`='%s' WHERE `%s`='%s' AND uid=%d",
2629                                         $n, dbesc($f[1]),
2630                                         $n, dbesc($f[0]),
2631                                         intval($importer['importer_uid']));
2632                                 if ($x === false)
2633                                         return 1;
2634                         }
2635
2636                 // TODO
2637                 // merge with current record, current contents have priority
2638                 // update record, set url-updated
2639                 // update profile photos
2640                 // schedule a scan?
2641                 return 0;
2642         }
2643
2644
2645         // handle friend suggestion notification
2646
2647         $sugg = $feed->get_feed_tags( NAMESPACE_DFRN, 'suggest' );
2648         if(isset($sugg[0]['child'][NAMESPACE_DFRN])) {
2649                 $base = $sugg[0]['child'][NAMESPACE_DFRN];
2650                 $fsugg = array();
2651                 $fsugg['uid'] = $importer['importer_uid'];
2652                 $fsugg['cid'] = $importer['id'];
2653                 $fsugg['name'] = notags(unxmlify($base['name'][0]['data']));
2654                 $fsugg['photo'] = notags(unxmlify($base['photo'][0]['data']));
2655                 $fsugg['url'] = notags(unxmlify($base['url'][0]['data']));
2656                 $fsugg['request'] = notags(unxmlify($base['request'][0]['data']));
2657                 $fsugg['body'] = escape_tags(unxmlify($base['note'][0]['data']));
2658
2659                 // Does our member already have a friend matching this description?
2660
2661                 $r = q("SELECT * FROM `contact` WHERE `name` = '%s' AND `nurl` = '%s' AND `uid` = %d LIMIT 1",
2662                         dbesc($fsugg['name']),
2663                         dbesc(normalise_link($fsugg['url'])),
2664                         intval($fsugg['uid'])
2665                 );
2666                 if(count($r))
2667                         return 0;
2668
2669                 // Do we already have an fcontact record for this person?
2670
2671                 $fid = 0;
2672                 $r = q("SELECT * FROM `fcontact` WHERE `url` = '%s' AND `name` = '%s' AND `request` = '%s' LIMIT 1",
2673                         dbesc($fsugg['url']),
2674                         dbesc($fsugg['name']),
2675                         dbesc($fsugg['request'])
2676                 );
2677                 if(count($r)) {
2678                         $fid = $r[0]['id'];
2679
2680                         // OK, we do. Do we already have an introduction for this person ?
2681                         $r = q("select id from intro where uid = %d and fid = %d limit 1",
2682                                 intval($fsugg['uid']),
2683                                 intval($fid)
2684                         );
2685                         if(count($r))
2686                                 return 0;
2687                 }
2688                 if(! $fid)
2689                         $r = q("INSERT INTO `fcontact` ( `name`,`url`,`photo`,`request` ) VALUES ( '%s', '%s', '%s', '%s' ) ",
2690                         dbesc($fsugg['name']),
2691                         dbesc($fsugg['url']),
2692                         dbesc($fsugg['photo']),
2693                         dbesc($fsugg['request'])
2694                 );
2695                 $r = q("SELECT * FROM `fcontact` WHERE `url` = '%s' AND `name` = '%s' AND `request` = '%s' LIMIT 1",
2696                         dbesc($fsugg['url']),
2697                         dbesc($fsugg['name']),
2698                         dbesc($fsugg['request'])
2699                 );
2700                 if(count($r)) {
2701                         $fid = $r[0]['id'];
2702                 }
2703                 // database record did not get created. Quietly give up.
2704                 else
2705                         return 0;
2706
2707
2708                 $hash = random_string();
2709
2710                 $r = q("INSERT INTO `intro` ( `uid`, `fid`, `contact-id`, `note`, `hash`, `datetime`, `blocked` )
2711                         VALUES( %d, %d, %d, '%s', '%s', '%s', %d )",
2712                         intval($fsugg['uid']),
2713                         intval($fid),
2714                         intval($fsugg['cid']),
2715                         dbesc($fsugg['body']),
2716                         dbesc($hash),
2717                         dbesc(datetime_convert()),
2718                         intval(0)
2719                 );
2720
2721                 notification(array(
2722                         'type'         => NOTIFY_SUGGEST,
2723                         'notify_flags' => $importer['notify-flags'],
2724                         'language'     => $importer['language'],
2725                         'to_name'      => $importer['username'],
2726                         'to_email'     => $importer['email'],
2727                         'uid'          => $importer['importer_uid'],
2728                         'item'         => $fsugg,
2729                         'link'         => $a->get_baseurl() . '/notifications/intros',
2730                         'source_name'  => $importer['name'],
2731                         'source_link'  => $importer['url'],
2732                         'source_photo' => $importer['photo'],
2733                         'verb'         => ACTIVITY_REQ_FRIEND,
2734                         'otype'        => 'intro'
2735                 ));
2736
2737                 return 0;
2738         }
2739
2740         $ismail = false;
2741
2742         $rawmail = $feed->get_feed_tags( NAMESPACE_DFRN, 'mail' );
2743         if(isset($rawmail[0]['child'][NAMESPACE_DFRN])) {
2744
2745                 logger('local_delivery: private message received');
2746
2747                 $ismail = true;
2748                 $base = $rawmail[0]['child'][NAMESPACE_DFRN];
2749
2750                 $msg = array();
2751                 $msg['uid'] = $importer['importer_uid'];
2752                 $msg['from-name'] = notags(unxmlify($base['sender'][0]['child'][NAMESPACE_DFRN]['name'][0]['data']));
2753                 $msg['from-photo'] = notags(unxmlify($base['sender'][0]['child'][NAMESPACE_DFRN]['avatar'][0]['data']));
2754                 $msg['from-url'] = notags(unxmlify($base['sender'][0]['child'][NAMESPACE_DFRN]['uri'][0]['data']));
2755                 $msg['contact-id'] = $importer['id'];
2756                 $msg['title'] = notags(unxmlify($base['subject'][0]['data']));
2757                 $msg['body'] = escape_tags(unxmlify($base['content'][0]['data']));
2758                 $msg['seen'] = 0;
2759                 $msg['replied'] = 0;
2760                 $msg['uri'] = notags(unxmlify($base['id'][0]['data']));
2761                 $msg['parent-uri'] = notags(unxmlify($base['in-reply-to'][0]['data']));
2762                 $msg['created'] = datetime_convert(notags(unxmlify('UTC','UTC',$base['sentdate'][0]['data'])));
2763
2764                 dbesc_array($msg);
2765
2766                 $r = dbq("INSERT INTO `mail` (`" . implode("`, `", array_keys($msg)) 
2767                         . "`) VALUES ('" . implode("', '", array_values($msg)) . "')" );
2768
2769                 // send notifications.
2770
2771                 require_once('include/enotify.php');
2772
2773                 $notif_params = array(
2774                         'type' => NOTIFY_MAIL,
2775                         'notify_flags' => $importer['notify-flags'],
2776                         'language' => $importer['language'],
2777                         'to_name' => $importer['username'],
2778                         'to_email' => $importer['email'],
2779                         'uid' => $importer['importer_uid'],
2780                         'item' => $msg,
2781                         'source_name' => $msg['from-name'],
2782                         'source_link' => $importer['url'],
2783                         'source_photo' => $importer['thumb'],
2784                         'verb' => ACTIVITY_POST,
2785                         'otype' => 'mail'
2786                 );
2787
2788                 notification($notif_params);
2789                 return 0;
2790
2791                 // NOTREACHED
2792         }
2793
2794         $community_page = 0;
2795         $rawtags = $feed->get_feed_tags( NAMESPACE_DFRN, 'community');
2796         if($rawtags) {
2797                 $community_page = intval($rawtags[0]['data']);
2798         }
2799         if(intval($importer['forum']) != $community_page) {
2800                 q("update contact set forum = %d where id = %d",
2801                         intval($community_page),
2802                         intval($importer['id'])
2803                 );
2804                 $importer['forum'] = (string) $community_page;
2805         }
2806
2807         logger('local_delivery: feed item count = ' . $feed->get_item_quantity());
2808
2809         // process any deleted entries
2810
2811         $del_entries = $feed->get_feed_tags(NAMESPACE_TOMB, 'deleted-entry');
2812         if(is_array($del_entries) && count($del_entries)) {
2813                 foreach($del_entries as $dentry) {
2814                         $deleted = false;
2815                         if(isset($dentry['attribs']['']['ref'])) {
2816                                 $uri = $dentry['attribs']['']['ref'];
2817                                 $deleted = true;
2818                                 if(isset($dentry['attribs']['']['when'])) {
2819                                         $when = $dentry['attribs']['']['when'];
2820                                         $when = datetime_convert('UTC','UTC', $when, 'Y-m-d H:i:s');
2821                                 }
2822                                 else
2823                                         $when = datetime_convert('UTC','UTC','now','Y-m-d H:i:s');
2824                         }
2825                         if($deleted) {
2826
2827                                 // check for relayed deletes to our conversation
2828
2829                                 $is_reply = false;
2830                                 $r = q("select * from item where uri = '%s' and uid = %d limit 1",
2831                                         dbesc($uri),
2832                                         intval($importer['importer_uid'])
2833                                 );
2834                                 if(count($r)) {
2835                                         $parent_uri = $r[0]['parent-uri'];
2836                                         if($r[0]['id'] != $r[0]['parent'])
2837                                                 $is_reply = true;
2838                                 }
2839
2840                                 if($is_reply) {
2841                                         $community = false;
2842
2843                                         if($importer['page-flags'] == PAGE_COMMUNITY || $importer['page-flags'] == PAGE_PRVGROUP ) {
2844                                                 $sql_extra = '';
2845                                                 $community = true;
2846                                                 logger('local_delivery: possible community delete');
2847                                         }
2848                                         else
2849                                                 $sql_extra = " and contact.self = 1 and item.wall = 1 ";
2850  
2851                                         // was the top-level post for this reply written by somebody on this site? 
2852                                         // Specifically, the recipient? 
2853
2854                                         $is_a_remote_delete = false;
2855
2856                                         // POSSIBLE CLEANUP --> Why select so many fields when only forum_mode and wall are used?
2857                                         $r = q("select `item`.`id`, `item`.`uri`, `item`.`tag`, `item`.`forum_mode`,`item`.`origin`,`item`.`wall`, 
2858                                                 `contact`.`name`, `contact`.`url`, `contact`.`thumb` from `item` 
2859                                                 INNER JOIN `contact` ON `contact`.`id` = `item`.`contact-id` 
2860                                                 WHERE `item`.`uri` = '%s' AND (`item`.`parent-uri` = '%s' or `item`.`thr-parent` = '%s')
2861                                                 AND `item`.`uid` = %d 
2862                                                 $sql_extra
2863                                                 LIMIT 1",
2864                                                 dbesc($parent_uri),
2865                                                 dbesc($parent_uri),
2866                                                 dbesc($parent_uri),
2867                                                 intval($importer['importer_uid'])
2868                                         );
2869                                         if($r && count($r))
2870                                                 $is_a_remote_delete = true;
2871
2872                                         // Does this have the characteristics of a community or private group comment?
2873                                         // If it's a reply to a wall post on a community/prvgroup page it's a 
2874                                         // valid community comment. Also forum_mode makes it valid for sure. 
2875                                         // If neither, it's not.
2876
2877                                         if($is_a_remote_delete && $community) {
2878                                                 if((! $r[0]['forum_mode']) && (! $r[0]['wall'])) {
2879                                                         $is_a_remote_delete = false;
2880                                                         logger('local_delivery: not a community delete');
2881                                                 }
2882                                         }
2883
2884                                         if($is_a_remote_delete) {
2885                                                 logger('local_delivery: received remote delete');
2886                                         }
2887                                 }
2888
2889                                 $r = q("SELECT `item`.*, `contact`.`self` FROM `item` INNER JOIN contact on `item`.`contact-id` = `contact`.`id`
2890                                         WHERE `uri` = '%s' AND `item`.`uid` = %d AND `contact-id` = %d AND NOT `item`.`file` LIKE '%%[%%' LIMIT 1",
2891                                         dbesc($uri),
2892                                         intval($importer['importer_uid']),
2893                                         intval($importer['id'])
2894                                 );
2895
2896                                 if(count($r)) {
2897                                         $item = $r[0];
2898
2899                                         if($item['deleted'])
2900                                                 continue;
2901
2902                                         logger('local_delivery: deleting item ' . $item['id'] . ' uri=' . $item['uri'], LOGGER_DEBUG);
2903
2904                                         if(($item['verb'] === ACTIVITY_TAG) && ($item['object-type'] === ACTIVITY_OBJ_TAGTERM)) {
2905                                                 $xo = parse_xml_string($item['object'],false);
2906                                                 $xt = parse_xml_string($item['target'],false);
2907
2908                                                 if($xt->type === ACTIVITY_OBJ_NOTE) {
2909                                                         $i = q("select * from `item` where uri = '%s' and uid = %d limit 1",
2910                                                                 dbesc($xt->id),
2911                                                                 intval($importer['importer_uid'])
2912                                                         );
2913                                                         if(count($i)) {
2914
2915                                                                 // For tags, the owner cannot remove the tag on the author's copy of the post.
2916
2917                                                                 $owner_remove = (($item['contact-id'] == $i[0]['contact-id']) ? true: false);
2918                                                                 $author_remove = (($item['origin'] && $item['self']) ? true : false);
2919                                                                 $author_copy = (($item['origin']) ? true : false);
2920
2921                                                                 if($owner_remove && $author_copy)
2922                                                                         continue;
2923                                                                 if($author_remove || $owner_remove) {
2924                                                                         $tags = explode(',',$i[0]['tag']);
2925                                                                         $newtags = array();
2926                                                                         if(count($tags)) {
2927                                                                                 foreach($tags as $tag)
2928                                                                                         if(trim($tag) !== trim($xo->body))
2929                                                                                                 $newtags[] = trim($tag);
2930                                                                         }
2931                                                                         q("update item set tag = '%s' where id = %d",
2932                                                                                 dbesc(implode(',',$newtags)),
2933                                                                                 intval($i[0]['id'])
2934                                                                         );
2935                                                                         create_tags_from_item($i[0]['id']);
2936                                                                 }
2937                                                         }
2938                                                 }
2939                                         }
2940
2941                                         if($item['uri'] == $item['parent-uri']) {
2942                                                 $r = q("UPDATE `item` SET `deleted` = 1, `edited` = '%s', `changed` = '%s',
2943                                                         `body` = '', `title` = ''
2944                                                         WHERE `parent-uri` = '%s' AND `uid` = %d",
2945                                                         dbesc($when),
2946                                                         dbesc(datetime_convert()),
2947                                                         dbesc($item['uri']),
2948                                                         intval($importer['importer_uid'])
2949                                                 );
2950                                                 create_tags_from_itemuri($item['uri'], $importer['importer_uid']);
2951                                                 update_thread_uri($item['uri'], $importer['importer_uid']);
2952                                         }
2953                                         else {
2954                                                 $r = q("UPDATE `item` SET `deleted` = 1, `edited` = '%s', `changed` = '%s',
2955                                                         `body` = '', `title` = ''
2956                                                         WHERE `uri` = '%s' AND `uid` = %d",
2957                                                         dbesc($when),
2958                                                         dbesc(datetime_convert()),
2959                                                         dbesc($uri),
2960                                                         intval($importer['importer_uid'])
2961                                                 );
2962                                                 create_tags_from_itemuri($uri, $importer['importer_uid']);
2963                                                 update_thread_uri($uri, $importer['importer_uid']);
2964                                                 if($item['last-child']) {
2965                                                         // ensure that last-child is set in case the comment that had it just got wiped.
2966                                                         q("UPDATE `item` SET `last-child` = 0, `changed` = '%s' WHERE `parent-uri` = '%s' AND `uid` = %d ",
2967                                                                 dbesc(datetime_convert()),
2968                                                                 dbesc($item['parent-uri']),
2969                                                                 intval($item['uid'])
2970                                                         );
2971                                                         // who is the last child now?
2972                                                         $r = q("SELECT `id` FROM `item` WHERE `parent-uri` = '%s' AND `type` != 'activity' AND `deleted` = 0 AND `uid` = %d
2973                                                                 ORDER BY `created` DESC LIMIT 1",
2974                                                                         dbesc($item['parent-uri']),
2975                                                                         intval($importer['importer_uid'])
2976                                                         );
2977                                                         if(count($r)) {
2978                                                                 q("UPDATE `item` SET `last-child` = 1 WHERE `id` = %d",
2979                                                                         intval($r[0]['id'])
2980                                                                 );
2981                                                         }
2982                                                 }
2983                                                 // if this is a relayed delete, propagate it to other recipients
2984
2985                                                 if($is_a_remote_delete)
2986                                                         proc_run('php',"include/notifier.php","drop",$item['id']);
2987                                         }
2988                                 }
2989                         }
2990                 }
2991         }
2992
2993
2994         foreach($feed->get_items() as $item) {
2995
2996                 $is_reply = false;
2997                 $item_id = $item->get_id();
2998                 $rawthread = $item->get_item_tags( NAMESPACE_THREAD, 'in-reply-to');
2999                 if(isset($rawthread[0]['attribs']['']['ref'])) {
3000                         $is_reply = true;
3001                         $parent_uri = $rawthread[0]['attribs']['']['ref'];
3002                 }
3003
3004                 if($is_reply) {
3005                         $community = false;
3006
3007                         if($importer['page-flags'] == PAGE_COMMUNITY || $importer['page-flags'] == PAGE_PRVGROUP ) {
3008                                 $sql_extra = '';
3009                                 $community = true;
3010                                 logger('local_delivery: possible community reply');
3011                         }
3012                         else
3013                                 $sql_extra = " and contact.self = 1 and item.wall = 1 ";
3014
3015                         // was the top-level post for this reply written by somebody on this site?
3016                         // Specifically, the recipient?
3017
3018                         $is_a_remote_comment = false;
3019                         $top_uri = $parent_uri;
3020
3021                         $r = q("select `item`.`parent-uri` from `item`
3022                                 WHERE `item`.`uri` = '%s'
3023                                 LIMIT 1",
3024                                 dbesc($parent_uri)
3025                         );
3026                         if($r && count($r)) {
3027                                 $top_uri = $r[0]['parent-uri'];
3028
3029                                 // POSSIBLE CLEANUP --> Why select so many fields when only forum_mode and wall are used?
3030                                 $r = q("select `item`.`id`, `item`.`uri`, `item`.`tag`, `item`.`forum_mode`,`item`.`origin`,`item`.`wall`,
3031                                         `contact`.`name`, `contact`.`url`, `contact`.`thumb` from `item`
3032                                         INNER JOIN `contact` ON `contact`.`id` = `item`.`contact-id`
3033                                         WHERE `item`.`uri` = '%s' AND (`item`.`parent-uri` = '%s' or `item`.`thr-parent` = '%s')
3034                                         AND `item`.`uid` = %d
3035                                         $sql_extra
3036                                         LIMIT 1",
3037                                         dbesc($top_uri),
3038                                         dbesc($top_uri),
3039                                         dbesc($top_uri),
3040                                         intval($importer['importer_uid'])
3041                                 );
3042                                 if($r && count($r))
3043                                         $is_a_remote_comment = true;
3044                         }
3045
3046                         // Does this have the characteristics of a community or private group comment?
3047                         // If it's a reply to a wall post on a community/prvgroup page it's a 
3048                         // valid community comment. Also forum_mode makes it valid for sure. 
3049                         // If neither, it's not.
3050
3051                         if($is_a_remote_comment && $community) {
3052                                 if((! $r[0]['forum_mode']) && (! $r[0]['wall'])) {
3053                                         $is_a_remote_comment = false;
3054                                         logger('local_delivery: not a community reply');
3055                                 }
3056                         }
3057
3058                         if($is_a_remote_comment) {
3059                                 logger('local_delivery: received remote comment');
3060                                 $is_like = false;
3061                                 // remote reply to our post. Import and then notify everybody else.
3062
3063                                 $datarray = get_atom_elements($feed, $item);
3064
3065                                 $r = q("SELECT `id`, `uid`, `last-child`, `edited`, `body`  FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1",
3066                                         dbesc($item_id),
3067                                         intval($importer['importer_uid'])
3068                                 );
3069
3070                                 // Update content if 'updated' changes
3071
3072                                 if(count($r)) {
3073                                         $iid = $r[0]['id'];
3074                                         if (edited_timestamp_is_newer($r[0], $datarray)) {
3075
3076                                                 // do not accept (ignore) an earlier edit than one we currently have.
3077                                                 if(datetime_convert('UTC','UTC',$datarray['edited']) < $r[0]['edited'])
3078                                                         continue;
3079
3080                                                 logger('received updated comment' , LOGGER_DEBUG);
3081                                                 $r = q("UPDATE `item` SET `title` = '%s', `body` = '%s', `tag` = '%s', `edited` = '%s' WHERE `uri` = '%s' AND `uid` = %d",
3082                                                         dbesc($datarray['title']),
3083                                                         dbesc($datarray['body']),
3084                                                         dbesc($datarray['tag']),
3085                                                         dbesc(datetime_convert('UTC','UTC',$datarray['edited'])),
3086                                                         dbesc($item_id),
3087                                                         intval($importer['importer_uid'])
3088                                                 );
3089                                                 create_tags_from_itemuri($item_id, $importer['importer_uid']);
3090
3091                                                 proc_run('php',"include/notifier.php","comment-import",$iid);
3092
3093                                         }
3094
3095                                         continue;
3096                                 }
3097
3098
3099
3100                                 $own = q("select name,url,thumb from contact where uid = %d and self = 1 limit 1",
3101                                         intval($importer['importer_uid'])
3102                                 );
3103
3104
3105                                 $datarray['type'] = 'remote-comment';
3106                                 $datarray['wall'] = 1;
3107                                 $datarray['parent-uri'] = $parent_uri;
3108                                 $datarray['uid'] = $importer['importer_uid'];
3109                                 $datarray['owner-name'] = $own[0]['name'];
3110                                 $datarray['owner-link'] = $own[0]['url'];
3111                                 $datarray['owner-avatar'] = $own[0]['thumb'];
3112                                 $datarray['contact-id'] = $importer['id'];
3113
3114                                 if(($datarray['verb'] === ACTIVITY_LIKE) || ($datarray['verb'] === ACTIVITY_DISLIKE)) {
3115                                         $is_like = true;
3116                                         $datarray['type'] = 'activity';
3117                                         $datarray['gravity'] = GRAVITY_LIKE;
3118                                         $datarray['last-child'] = 0;
3119                                         // only one like or dislike per person
3120                                         // splitted into two queries for performance issues
3121                                         $r = q("select id from item where uid = %d and `contact-id` = %d and verb = '%s' and (`parent-uri` = '%s') and deleted = 0 limit 1",
3122                                                 intval($datarray['uid']),
3123                                                 intval($datarray['contact-id']),
3124                                                 dbesc($datarray['verb']),
3125                                                 dbesc($datarray['parent-uri'])
3126
3127                                         );
3128                                         if($r && count($r))
3129                                                 continue;
3130
3131                                         $r = q("select id from item where uid = %d and `contact-id` = %d and verb = '%s' and (`thr-parent` = '%s') and deleted = 0 limit 1",
3132                                                 intval($datarray['uid']),
3133                                                 intval($datarray['contact-id']),
3134                                                 dbesc($datarray['verb']),
3135                                                 dbesc($datarray['parent-uri'])
3136
3137                                         );
3138                                         if($r && count($r))
3139                                                 continue;
3140                                 }
3141
3142                                 if(($datarray['verb'] === ACTIVITY_TAG) && ($datarray['object-type'] === ACTIVITY_OBJ_TAGTERM)) {
3143
3144                                         $xo = parse_xml_string($datarray['object'],false);
3145                                         $xt = parse_xml_string($datarray['target'],false);
3146
3147                                         if(($xt->type == ACTIVITY_OBJ_NOTE) && ($xt->id)) {
3148
3149                                                 // fetch the parent item
3150
3151                                                 $tagp = q("select * from item where uri = '%s' and uid = %d limit 1",
3152                                                         dbesc($xt->id),
3153                                                         intval($importer['importer_uid'])
3154                                                 );
3155                                                 if(! count($tagp))
3156                                                         continue;
3157
3158                                                 // extract tag, if not duplicate, and this user allows tags, add to parent item
3159
3160                                                 if($xo->id && $xo->content) {
3161                                                         $newtag = '#[url=' . $xo->id . ']'. $xo->content . '[/url]';
3162                                                         if(! (stristr($tagp[0]['tag'],$newtag))) {
3163                                                                 $i = q("SELECT `blocktags` FROM `user` where `uid` = %d LIMIT 1",
3164                                                                         intval($importer['importer_uid'])
3165                                                                 );
3166                                                                 if(count($i) && ! intval($i[0]['blocktags'])) {
3167                                                                         q("UPDATE item SET tag = '%s', `edited` = '%s' WHERE id = %d",
3168                                                                                 dbesc($tagp[0]['tag'] . (strlen($tagp[0]['tag']) ? ',' : '') . $newtag),
3169                                                                                 intval($tagp[0]['id']),
3170                                                                                 dbesc(datetime_convert())
3171                                                                         );
3172                                                                         create_tags_from_item($tagp[0]['id']);
3173                                                                 }
3174                                                         }
3175                                                 }
3176                                         }
3177                                 }
3178
3179
3180                                 $posted_id = item_store($datarray);
3181                                 $parent = 0;
3182
3183                                 if($posted_id) {
3184                                         $r = q("SELECT `parent`, `parent-uri` FROM `item` WHERE `id` = %d AND `uid` = %d LIMIT 1",
3185                                                 intval($posted_id),
3186                                                 intval($importer['importer_uid'])
3187                                         );
3188                                         if(count($r)) {
3189                                                 $parent = $r[0]['parent'];
3190                                                 $parent_uri = $r[0]['parent-uri'];
3191                                         }
3192
3193                                         if(! $is_like) {
3194                                                 $r1 = q("UPDATE `item` SET `last-child` = 0, `changed` = '%s' WHERE `uid` = %d AND `parent` = %d",
3195                                                         dbesc(datetime_convert()),
3196                                                         intval($importer['importer_uid']),
3197                                                         intval($r[0]['parent'])
3198                                                 );
3199
3200                                                 $r2 = q("UPDATE `item` SET `last-child` = 1, `changed` = '%s' WHERE `uid` = %d AND `id` = %d",
3201                                                         dbesc(datetime_convert()),
3202                                                         intval($importer['importer_uid']),
3203                                                         intval($posted_id)
3204                                                 );
3205                                         }
3206
3207                                         if($posted_id && $parent) {
3208
3209                                                 proc_run('php',"include/notifier.php","comment-import","$posted_id");
3210
3211                                                 if((! $is_like) && (! $importer['self'])) {
3212
3213                                                         require_once('include/enotify.php');
3214
3215                                                         notification(array(
3216                                                                 'type'         => NOTIFY_COMMENT,
3217                                                                 'notify_flags' => $importer['notify-flags'],
3218                                                                 'language'     => $importer['language'],
3219                                                                 'to_name'      => $importer['username'],
3220                                                                 'to_email'     => $importer['email'],
3221                                                                 'uid'          => $importer['importer_uid'],
3222                                                                 'item'         => $datarray,
3223                                                                 'link'             => $a->get_baseurl() . '/display/' . $importer['nickname'] . '/' . $posted_id,
3224                                                                 'source_name'  => stripslashes($datarray['author-name']),
3225                                                                 'source_link'  => $datarray['author-link'],
3226                                                                 'source_photo' => ((link_compare($datarray['author-link'],$importer['url'])) 
3227                                                                         ? $importer['thumb'] : $datarray['author-avatar']),
3228                                                                 'verb'         => ACTIVITY_POST,
3229                                                                 'otype'        => 'item',
3230                                                                 'parent'       => $parent,
3231                                                                 'parent_uri'   => $parent_uri,
3232                                                         ));
3233
3234                                                 }
3235                                         }
3236
3237                                         return 0;
3238                                         // NOTREACHED
3239                                 }
3240                         }
3241                         else {
3242
3243                                 // regular comment that is part of this total conversation. Have we seen it? If not, import it.
3244
3245                                 $item_id  = $item->get_id();
3246                                 $datarray = get_atom_elements($feed,$item);
3247
3248                                 if($importer['rel'] == CONTACT_IS_FOLLOWER)
3249                                         continue;
3250
3251                                 $r = q("SELECT `uid`, `last-child`, `edited`, `body` FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1",
3252                                         dbesc($item_id),
3253                                         intval($importer['importer_uid'])
3254                                 );
3255
3256                                 // Update content if 'updated' changes
3257
3258                                 if(count($r)) {
3259                                         if (edited_timestamp_is_newer($r[0], $datarray)) {
3260
3261                                                 // do not accept (ignore) an earlier edit than one we currently have.
3262                                                 if(datetime_convert('UTC','UTC',$datarray['edited']) < $r[0]['edited'])
3263                                                         continue;
3264
3265                                                 $r = q("UPDATE `item` SET `title` = '%s', `body` = '%s', `tag` = '%s', `edited` = '%s' WHERE `uri` = '%s' AND `uid` = %d",
3266                                                         dbesc($datarray['title']),
3267                                                         dbesc($datarray['body']),
3268                                                         dbesc($datarray['tag']),
3269                                                         dbesc(datetime_convert('UTC','UTC',$datarray['edited'])),
3270                                                         dbesc($item_id),
3271                                                         intval($importer['importer_uid'])
3272                                                 );
3273                                                 create_tags_from_itemuri($item_id, $importer['importer_uid']);
3274                                         }
3275
3276                                         // update last-child if it changes
3277
3278                                         $allow = $item->get_item_tags( NAMESPACE_DFRN, 'comment-allow');
3279                                         if(($allow) && ($allow[0]['data'] != $r[0]['last-child'])) {
3280                                                 $r = q("UPDATE `item` SET `last-child` = 0, `changed` = '%s' WHERE `parent-uri` = '%s' AND `uid` = %d",
3281                                                         dbesc(datetime_convert()),
3282                                                         dbesc($parent_uri),
3283                                                         intval($importer['importer_uid'])
3284                                                 );
3285                                                 $r = q("UPDATE `item` SET `last-child` = %d , `changed` = '%s'  WHERE `uri` = '%s' AND `uid` = %d",
3286                                                         intval($allow[0]['data']),
3287                                                         dbesc(datetime_convert()),
3288                                                         dbesc($item_id),
3289                                                         intval($importer['importer_uid'])
3290                                                 );
3291                                         }
3292                                         continue;
3293                                 }
3294
3295                                 $datarray['parent-uri'] = $parent_uri;
3296                                 $datarray['uid'] = $importer['importer_uid'];
3297                                 $datarray['contact-id'] = $importer['id'];
3298                                 if(($datarray['verb'] == ACTIVITY_LIKE) || ($datarray['verb'] == ACTIVITY_DISLIKE)) {
3299                                         $datarray['type'] = 'activity';
3300                                         $datarray['gravity'] = GRAVITY_LIKE;
3301                                         // only one like or dislike per person
3302                                         // splitted into two queries for performance issues
3303                                         $r = q("select id from item where uid = %d and `contact-id` = %d and verb ='%s' and deleted = 0 and (`parent-uri` = '%s') limit 1",
3304                                                 intval($datarray['uid']),
3305                                                 intval($datarray['contact-id']),
3306                                                 dbesc($datarray['verb']),
3307                                                 dbesc($parent_uri)
3308                                         );
3309                                         if($r && count($r))
3310                                                 continue;
3311
3312                                         $r = q("select id from item where uid = %d and `contact-id` = %d and verb ='%s' and deleted = 0 and (`thr-parent` = '%s') limit 1",
3313                                                 intval($datarray['uid']),
3314                                                 intval($datarray['contact-id']),
3315                                                 dbesc($datarray['verb']),
3316                                                 dbesc($parent_uri)
3317                                         );
3318                                         if($r && count($r))
3319                                                 continue;
3320
3321                                 }
3322
3323                                 if(($datarray['verb'] === ACTIVITY_TAG) && ($datarray['object-type'] === ACTIVITY_OBJ_TAGTERM)) {
3324
3325                                         $xo = parse_xml_string($datarray['object'],false);
3326                                         $xt = parse_xml_string($datarray['target'],false);
3327
3328                                         if($xt->type == ACTIVITY_OBJ_NOTE) {
3329                                                 $r = q("select * from item where `uri` = '%s' AND `uid` = %d limit 1",
3330                                                         dbesc($xt->id),
3331                                                         intval($importer['importer_uid'])
3332                                                 );
3333                                                 if(! count($r))
3334                                                         continue;
3335
3336                                                 // extract tag, if not duplicate, add to parent item
3337                                                 if($xo->content) {
3338                                                         if(! (stristr($r[0]['tag'],trim($xo->content)))) {
3339                                                                 q("UPDATE item SET tag = '%s' WHERE id = %d",
3340                                                                         dbesc($r[0]['tag'] . (strlen($r[0]['tag']) ? ',' : '') . '#[url=' . $xo->id . ']'. $xo->content . '[/url]'),
3341                                                                         intval($r[0]['id'])
3342                                                                 );
3343                                                                 create_tags_from_item($r[0]['id']);
3344                                                         }
3345                                                 }
3346                                         }
3347                                 }
3348
3349                                 $posted_id = item_store($datarray);
3350
3351                                 // find out if our user is involved in this conversation and wants to be notified.
3352
3353                                 if(!x($datarray['type']) || $datarray['type'] != 'activity') {
3354
3355                                         $myconv = q("SELECT `author-link`, `author-avatar`, `parent` FROM `item` WHERE `parent-uri` = '%s' AND `uid` = %d AND `parent` != 0 AND `deleted` = 0",
3356                                                 dbesc($top_uri),
3357                                                 intval($importer['importer_uid'])
3358                                         );
3359
3360                                         if(count($myconv)) {
3361                                                 $importer_url = $a->get_baseurl() . '/profile/' . $importer['nickname'];
3362
3363                                                 // first make sure this isn't our own post coming back to us from a wall-to-wall event
3364                                                 if(! link_compare($datarray['author-link'],$importer_url)) {
3365
3366
3367                                                         foreach($myconv as $conv) {
3368
3369                                                                 // now if we find a match, it means we're in this conversation
3370
3371                                                                 if(! link_compare($conv['author-link'],$importer_url))
3372                                                                         continue;
3373
3374                                                                 require_once('include/enotify.php');
3375
3376                                                                 $conv_parent = $conv['parent'];
3377
3378                                                                 notification(array(
3379                                                                         'type'         => NOTIFY_COMMENT,
3380                                                                         'notify_flags' => $importer['notify-flags'],
3381                                                                         'language'     => $importer['language'],
3382                                                                         'to_name'      => $importer['username'],
3383                                                                         'to_email'     => $importer['email'],
3384                                                                         'uid'          => $importer['importer_uid'],
3385                                                                         'item'         => $datarray,
3386                                                                         'link'             => $a->get_baseurl() . '/display/' . $importer['nickname'] . '/' . $posted_id,
3387                                                                         'source_name'  => stripslashes($datarray['author-name']),
3388                                                                         'source_link'  => $datarray['author-link'],
3389                                                                         'source_photo' => ((link_compare($datarray['author-link'],$importer['url'])) 
3390                                                                                 ? $importer['thumb'] : $datarray['author-avatar']),
3391                                                                         'verb'         => ACTIVITY_POST,
3392                                                                         'otype'        => 'item',
3393                                                                         'parent'       => $conv_parent,
3394                                                                         'parent_uri'   => $parent_uri
3395
3396                                                                 ));
3397
3398                                                                 // only send one notification
3399                                                                 break;
3400                                                         }
3401                                                 }
3402                                         }
3403                                 }
3404                                 continue;
3405                         }
3406                 }
3407
3408                 else {
3409
3410                         // Head post of a conversation. Have we seen it? If not, import it.
3411
3412
3413                         $item_id  = $item->get_id();
3414                         $datarray = get_atom_elements($feed,$item);
3415
3416                         if((x($datarray,'object-type')) && ($datarray['object-type'] === ACTIVITY_OBJ_EVENT)) {
3417                                 $ev = bbtoevent($datarray['body']);
3418                                 if(x($ev,'desc') && x($ev,'start')) {
3419                                         $ev['cid'] = $importer['id'];
3420                                         $ev['uid'] = $importer['uid'];
3421                                         $ev['uri'] = $item_id;
3422                                         $ev['edited'] = $datarray['edited'];
3423                                         $ev['private'] = $datarray['private'];
3424
3425                                         $r = q("SELECT * FROM `event` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1",
3426                                                 dbesc($item_id),
3427                                                 intval($importer['uid'])
3428                                         );
3429                                         if(count($r))
3430                                                 $ev['id'] = $r[0]['id'];
3431                                         $xyz = event_store($ev);
3432                                         continue;
3433                                 }
3434                         }
3435
3436                         $r = q("SELECT `uid`, `last-child`, `edited`, `body` FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1",
3437                                 dbesc($item_id),
3438                                 intval($importer['importer_uid'])
3439                         );
3440
3441                         // Update content if 'updated' changes
3442
3443                         if(count($r)) {
3444                                 if (edited_timestamp_is_newer($r[0], $datarray)) {
3445
3446                                         // do not accept (ignore) an earlier edit than one we currently have.
3447                                         if(datetime_convert('UTC','UTC',$datarray['edited']) < $r[0]['edited'])
3448                                                 continue;
3449
3450                                         $r = q("UPDATE `item` SET `title` = '%s', `body` = '%s', `tag` = '%s', `edited` = '%s' WHERE `uri` = '%s' AND `uid` = %d",
3451                                                 dbesc($datarray['title']),
3452                                                 dbesc($datarray['body']),
3453                                                 dbesc($datarray['tag']),
3454                                                 dbesc(datetime_convert('UTC','UTC',$datarray['edited'])),
3455                                                 dbesc($item_id),
3456                                                 intval($importer['importer_uid'])
3457                                         );
3458                                         create_tags_from_itemuri($item_id, $importer['importer_uid']);
3459                                         update_thread_uri($item_id, $importer['importer_uid']);
3460                                 }
3461
3462                                 // update last-child if it changes
3463
3464                                 $allow = $item->get_item_tags( NAMESPACE_DFRN, 'comment-allow');
3465                                 if($allow && $allow[0]['data'] != $r[0]['last-child']) {
3466                                         $r = q("UPDATE `item` SET `last-child` = %d , `changed` = '%s' WHERE `uri` = '%s' AND `uid` = %d",
3467                                                 intval($allow[0]['data']),
3468                                                 dbesc(datetime_convert()),
3469                                                 dbesc($item_id),
3470                                                 intval($importer['importer_uid'])
3471                                         );
3472                                 }
3473                                 continue;
3474                         }
3475
3476                         // This is my contact on another system, but it's really me.
3477                         // Turn this into a wall post.
3478
3479                         if($importer['remote_self'])
3480                                 $datarray['wall'] = 1;
3481
3482                         $datarray['parent-uri'] = $item_id;
3483                         $datarray['uid'] = $importer['importer_uid'];
3484                         $datarray['contact-id'] = $importer['id'];
3485
3486
3487                         if(! link_compare($datarray['owner-link'],$importer['url'])) {
3488                                 // The item owner info is not our contact. It's OK and is to be expected if this is a tgroup delivery, 
3489                                 // but otherwise there's a possible data mixup on the sender's system.
3490                                 // the tgroup delivery code called from item_store will correct it if it's a forum,
3491                                 // but we're going to unconditionally correct it here so that the post will always be owned by our contact. 
3492                                 logger('local_delivery: Correcting item owner.', LOGGER_DEBUG);
3493                                 $datarray['owner-name']   = $importer['senderName'];
3494                                 $datarray['owner-link']   = $importer['url'];
3495                                 $datarray['owner-avatar'] = $importer['thumb'];
3496                         }
3497
3498                         if(($importer['rel'] == CONTACT_IS_FOLLOWER) && (! tgroup_check($importer['importer_uid'],$datarray)))
3499                                 continue;
3500
3501                         $posted_id = item_store($datarray);
3502
3503                         if(stristr($datarray['verb'],ACTIVITY_POKE)) {
3504                                 $verb = urldecode(substr($datarray['verb'],strpos($datarray['verb'],'#')+1));
3505                                 if(! $verb)
3506                                         continue;
3507                                 $xo = parse_xml_string($datarray['object'],false);
3508
3509                                 if(($xo->type == ACTIVITY_OBJ_PERSON) && ($xo->id)) {
3510
3511                                         // somebody was poked/prodded. Was it me?
3512
3513                                         $links = parse_xml_string("<links>".unxmlify($xo->link)."</links>",false);
3514
3515                                 foreach($links->link as $l) {
3516                                 $atts = $l->attributes();
3517                                 switch($atts['rel']) {
3518                                         case "alternate": 
3519                                                                 $Blink = $atts['href'];
3520                                                                 break;
3521                                                         default:
3522                                                                 break;
3523                                     }
3524                                 }
3525                                         if($Blink && link_compare($Blink,$a->get_baseurl() . '/profile/' . $importer['nickname'])) {
3526
3527                                                 // send a notification
3528                                                 require_once('include/enotify.php');
3529
3530                                                 notification(array(
3531                                                         'type'         => NOTIFY_POKE,
3532                                                         'notify_flags' => $importer['notify-flags'],
3533                                                         'language'     => $importer['language'],
3534                                                         'to_name'      => $importer['username'],
3535                                                         'to_email'     => $importer['email'],
3536                                                         'uid'          => $importer['importer_uid'],
3537                                                         'item'         => $datarray,
3538                                                         'link'             => $a->get_baseurl() . '/display/' . $importer['nickname'] . '/' . $posted_id,
3539                                                         'source_name'  => stripslashes($datarray['author-name']),
3540                                                         'source_link'  => $datarray['author-link'],
3541                                                         'source_photo' => ((link_compare($datarray['author-link'],$importer['url'])) 
3542                                                                 ? $importer['thumb'] : $datarray['author-avatar']),
3543                                                         'verb'         => $datarray['verb'],
3544                                                         'otype'        => 'person',
3545                                                         'activity'     => $verb,
3546
3547                                                 ));
3548                                         }
3549                                 }
3550                         }                       
3551
3552                         continue;
3553                 }
3554         }
3555
3556         return 0;
3557         // NOTREACHED
3558
3559 }
3560
3561
3562 function new_follower($importer,$contact,$datarray,$item,$sharing = false) {
3563         $url = notags(trim($datarray['author-link']));
3564         $name = notags(trim($datarray['author-name']));
3565         $photo = notags(trim($datarray['author-avatar']));
3566
3567         $rawtag = $item->get_item_tags(NAMESPACE_ACTIVITY,'actor');
3568         if($rawtag && $rawtag[0]['child'][NAMESPACE_POCO]['preferredUsername'][0]['data'])
3569                 $nick = $rawtag[0]['child'][NAMESPACE_POCO]['preferredUsername'][0]['data'];
3570
3571         if(is_array($contact)) {
3572                 if(($contact['network'] == NETWORK_OSTATUS && $contact['rel'] == CONTACT_IS_SHARING)
3573                         || ($sharing && $contact['rel'] == CONTACT_IS_FOLLOWER)) {
3574                         $r = q("UPDATE `contact` SET `rel` = %d, `writable` = 1 WHERE `id` = %d AND `uid` = %d",
3575                                 intval(CONTACT_IS_FRIEND),
3576                                 intval($contact['id']),
3577                                 intval($importer['uid'])
3578                         );
3579                 }
3580                 // send email notification to owner?
3581         }
3582         else {
3583
3584                 // create contact record
3585
3586                 $r = q("INSERT INTO `contact` ( `uid`, `created`, `url`, `nurl`, `name`, `nick`, `photo`, `network`, `rel`, 
3587                         `blocked`, `readonly`, `pending`, `writable` )
3588                         VALUES ( %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, 0, 0, 1, 1 ) ",
3589                         intval($importer['uid']),
3590                         dbesc(datetime_convert()),
3591                         dbesc($url),
3592                         dbesc(normalise_link($url)),
3593                         dbesc($name),
3594                         dbesc($nick),
3595                         dbesc($photo),
3596                         dbesc(($sharing) ? NETWORK_ZOT : NETWORK_OSTATUS),
3597                         intval(($sharing) ? CONTACT_IS_SHARING : CONTACT_IS_FOLLOWER)
3598                 );
3599                 $r = q("SELECT `id` FROM `contact` WHERE `uid` = %d AND `url` = '%s' AND `pending` = 1 LIMIT 1",
3600                                 intval($importer['uid']),
3601                                 dbesc($url)
3602                 );
3603                 if(count($r))
3604                                 $contact_record = $r[0];
3605
3606                 // create notification
3607                 $hash = random_string();
3608
3609                 if(is_array($contact_record)) {
3610                         $ret = q("INSERT INTO `intro` ( `uid`, `contact-id`, `blocked`, `knowyou`, `hash`, `datetime`)
3611                                 VALUES ( %d, %d, 0, 0, '%s', '%s' )",
3612                                 intval($importer['uid']),
3613                                 intval($contact_record['id']),
3614                                 dbesc($hash),
3615                                 dbesc(datetime_convert())
3616                         );
3617                 }
3618                 $r = q("SELECT * FROM `user` WHERE `uid` = %d LIMIT 1",
3619                         intval($importer['uid'])
3620                 );
3621                 $a = get_app();
3622                 if(count($r)) {
3623
3624                         if(intval($r[0]['def_gid'])) {
3625                                 require_once('include/group.php');
3626                                 group_add_member($r[0]['uid'],'',$contact_record['id'],$r[0]['def_gid']);
3627                         }
3628
3629                         if(($r[0]['notify-flags'] & NOTIFY_INTRO) && ($r[0]['page-flags'] == PAGE_NORMAL)) {
3630                                 $email_tpl = get_intltext_template('follow_notify_eml.tpl');
3631                                 $email = replace_macros($email_tpl, array(
3632                                         '$requestor' => ((strlen($name)) ? $name : t('[Name Withheld]')),
3633                                         '$url' => $url,
3634                                         '$myname' => $r[0]['username'],
3635                                         '$siteurl' => $a->get_baseurl(),
3636                                         '$sitename' => $a->config['sitename']
3637                                 ));
3638                                 $res = mail($r[0]['email'], 
3639                                         email_header_encode((($sharing) ? t('A new person is sharing with you at ') : t("You have a new follower at ")) . $a->config['sitename'],'UTF-8'),
3640                                         $email,
3641                                         'From: ' . 'Administrator' . '@' . $_SERVER['SERVER_NAME'] . "\n"
3642                                         . 'Content-type: text/plain; charset=UTF-8' . "\n"
3643                                         . 'Content-transfer-encoding: 8bit' );
3644
3645                         }
3646                 }
3647         }
3648 }
3649
3650 function lose_follower($importer,$contact,$datarray,$item) {
3651
3652         if(($contact['rel'] == CONTACT_IS_FRIEND) || ($contact['rel'] == CONTACT_IS_SHARING)) {
3653                 q("UPDATE `contact` SET `rel` = %d WHERE `id` = %d",
3654                         intval(CONTACT_IS_SHARING),
3655                         intval($contact['id'])
3656                 );
3657         }
3658         else {
3659                 contact_remove($contact['id']);
3660         }
3661 }
3662
3663 function lose_sharer($importer,$contact,$datarray,$item) {
3664
3665         if(($contact['rel'] == CONTACT_IS_FRIEND) || ($contact['rel'] == CONTACT_IS_FOLLOWER)) {
3666                 q("UPDATE `contact` SET `rel` = %d WHERE `id` = %d",
3667                         intval(CONTACT_IS_FOLLOWER),
3668                         intval($contact['id'])
3669                 );
3670         }
3671         else {
3672                 contact_remove($contact['id']);
3673         }
3674 }
3675
3676
3677 function subscribe_to_hub($url,$importer,$contact,$hubmode = 'subscribe') {
3678
3679         $a = get_app();
3680
3681         if(is_array($importer)) {
3682                 $r = q("SELECT `nickname` FROM `user` WHERE `uid` = %d LIMIT 1",
3683                         intval($importer['uid'])
3684                 );
3685         }
3686
3687         // Diaspora has different message-ids in feeds than they do 
3688         // through the direct Diaspora protocol. If we try and use
3689         // the feed, we'll get duplicates. So don't.
3690
3691         if((! count($r)) || $contact['network'] === NETWORK_DIASPORA)
3692                 return;
3693
3694         $push_url = get_config('system','url') . '/pubsub/' . $r[0]['nickname'] . '/' . $contact['id'];
3695
3696         // Use a single verify token, even if multiple hubs
3697
3698         $verify_token = ((strlen($contact['hub-verify'])) ? $contact['hub-verify'] : random_string());
3699
3700         $params= 'hub.mode=' . $hubmode . '&hub.callback=' . urlencode($push_url) . '&hub.topic=' . urlencode($contact['poll']) . '&hub.verify=async&hub.verify_token=' . $verify_token;
3701
3702         logger('subscribe_to_hub: ' . $hubmode . ' ' . $contact['name'] . ' to hub ' . $url . ' endpoint: '  . $push_url . ' with verifier ' . $verify_token);
3703
3704         if(! strlen($contact['hub-verify'])) {
3705                 $r = q("UPDATE `contact` SET `hub-verify` = '%s' WHERE `id` = %d",
3706                         dbesc($verify_token),
3707                         intval($contact['id'])
3708                 );
3709         }
3710
3711         post_url($url,$params);
3712
3713         logger('subscribe_to_hub: returns: ' . $a->get_curl_code(), LOGGER_DEBUG);
3714
3715         return;
3716
3717 }
3718
3719
3720 function atom_author($tag,$name,$uri,$h,$w,$photo) {
3721         $o = '';
3722         if(! $tag)
3723                 return $o;
3724         $name = xmlify($name);
3725         $uri = xmlify($uri);
3726         $h = intval($h);
3727         $w = intval($w);
3728         $photo = xmlify($photo);
3729
3730
3731         $o .= "<$tag>\r\n";
3732         $o .= "<name>$name</name>\r\n";
3733         $o .= "<uri>$uri</uri>\r\n";
3734         $o .= '<link rel="photo"  type="image/jpeg" media:width="' . $w . '" media:height="' . $h . '" href="' . $photo . '" />' . "\r\n";
3735         $o .= '<link rel="avatar" type="image/jpeg" media:width="' . $w . '" media:height="' . $h . '" href="' . $photo . '" />' . "\r\n";
3736
3737         call_hooks('atom_author', $o);
3738
3739         $o .= "</$tag>\r\n";
3740         return $o;
3741 }
3742
3743 function atom_entry($item,$type,$author,$owner,$comment = false,$cid = 0) {
3744
3745         $a = get_app();
3746
3747         if(! $item['parent'])
3748                 return;
3749
3750         if($item['deleted'])
3751                 return '<at:deleted-entry ref="' . xmlify($item['uri']) . '" when="' . xmlify(datetime_convert('UTC','UTC',$item['edited'] . '+00:00',ATOM_TIME)) . '" />' . "\r\n";
3752
3753
3754         if($item['allow_cid'] || $item['allow_gid'] || $item['deny_cid'] || $item['deny_gid'])
3755                 $body = fix_private_photos($item['body'],$owner['uid'],$item,$cid);
3756         else
3757                 $body = $item['body'];
3758
3759         $o = "\r\n\r\n<entry>\r\n";
3760
3761         if(is_array($author))
3762                 $o .= atom_author('author',$author['name'],$author['url'],80,80,$author['thumb']);
3763         else
3764                 $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']));
3765         if(strlen($item['owner-name']))
3766                 $o .= atom_author('dfrn:owner',$item['owner-name'],$item['owner-link'],80,80,$item['owner-avatar']);
3767
3768         if(($item['parent'] != $item['id']) || ($item['parent-uri'] !== $item['uri']) || (($item['thr-parent'] !== '') && ($item['thr-parent'] !== $item['uri']))) {
3769                 $parent_item = (($item['thr-parent']) ? $item['thr-parent'] : $item['parent-uri']);
3770                 $o .= '<thr:in-reply-to ref="' . xmlify($parent_item) . '" type="text/html" href="' .  xmlify($a->get_baseurl() . '/display/' . $owner['nickname'] . '/' . $item['parent']) . '" />' . "\r\n";
3771         }
3772
3773         $o .= '<id>' . xmlify($item['uri']) . '</id>' . "\r\n";
3774         $o .= '<title>' . xmlify($item['title']) . '</title>' . "\r\n";
3775         $o .= '<published>' . xmlify(datetime_convert('UTC','UTC',$item['created'] . '+00:00',ATOM_TIME)) . '</published>' . "\r\n";
3776         $o .= '<updated>' . xmlify(datetime_convert('UTC','UTC',$item['edited'] . '+00:00',ATOM_TIME)) . '</updated>' . "\r\n";
3777         $o .= '<dfrn:env>' . base64url_encode($body, true) . '</dfrn:env>' . "\r\n";
3778         $o .= '<content type="' . $type . '" >' . xmlify((($type === 'html') ? bbcode($body) : $body)) . '</content>' . "\r\n";
3779         $o .= '<link rel="alternate" type="text/html" href="' . xmlify($a->get_baseurl() . '/display/' . $owner['nickname'] . '/' . $item['id']) . '" />' . "\r\n";
3780         if($comment)
3781                 $o .= '<dfrn:comment-allow>' . intval($item['last-child']) . '</dfrn:comment-allow>' . "\r\n";
3782
3783         if($item['location']) {
3784                 $o .= '<dfrn:location>' . xmlify($item['location']) . '</dfrn:location>' . "\r\n";
3785                 $o .= '<poco:address><poco:formatted>' . xmlify($item['location']) . '</poco:formatted></poco:address>' . "\r\n";
3786         }
3787
3788         if($item['coord'])
3789                 $o .= '<georss:point>' . xmlify($item['coord']) . '</georss:point>' . "\r\n";
3790
3791         if(($item['private']) || strlen($item['allow_cid']) || strlen($item['allow_gid']) || strlen($item['deny_cid']) || strlen($item['deny_gid']))
3792                 $o .= '<dfrn:private>' . (($item['private']) ? $item['private'] : 1) . '</dfrn:private>' . "\r\n";
3793
3794         if($item['extid'])
3795                 $o .= '<dfrn:extid>' . xmlify($item['extid']) . '</dfrn:extid>' . "\r\n";
3796         if($item['bookmark'])
3797                 $o .= '<dfrn:bookmark>true</dfrn:bookmark>' . "\r\n";
3798
3799         if($item['app'])
3800                 $o .= '<statusnet:notice_info local_id="' . $item['id'] . '" source="' . xmlify($item['app']) . '" ></statusnet:notice_info>' . "\r\n";
3801
3802         if($item['guid'])
3803                 $o .= '<dfrn:diaspora_guid>' . $item['guid'] . '</dfrn:diaspora_guid>' . "\r\n";
3804
3805         if($item['signed_text']) {
3806                 $sign = base64_encode(json_encode(array('signed_text' => $item['signed_text'],'signature' => $item['signature'],'signer' => $item['signer'])));
3807                 $o .= '<dfrn:diaspora_signature>' . xmlify($sign) . '</dfrn:diaspora_signature>' . "\r\n";
3808         }
3809
3810         $verb = construct_verb($item);
3811         $o .= '<as:verb>' . xmlify($verb) . '</as:verb>' . "\r\n";
3812         $actobj = construct_activity_object($item);
3813         if(strlen($actobj))
3814                 $o .= $actobj;
3815         $actarg = construct_activity_target($item);
3816         if(strlen($actarg))
3817                 $o .= $actarg;
3818
3819         $tags = item_getfeedtags($item);
3820         if(count($tags)) {
3821                 foreach($tags as $t) {
3822                         $o .= '<category scheme="X-DFRN:' . xmlify($t[0]) . ':' . xmlify($t[1]) . '" term="' . xmlify($t[2]) . '" />' . "\r\n";
3823                 }
3824         }
3825
3826         $o .= item_getfeedattach($item);
3827
3828         $mentioned = get_mentions($item);
3829         if($mentioned)
3830                 $o .= $mentioned;
3831         
3832         call_hooks('atom_entry', $o);
3833
3834         $o .= '</entry>' . "\r\n";
3835         
3836         return $o;
3837 }
3838
3839 function fix_private_photos($s, $uid, $item = null, $cid = 0) {
3840
3841         if(get_config('system','disable_embedded'))
3842                 return $s;
3843
3844         $a = get_app();
3845
3846         logger('fix_private_photos: check for photos', LOGGER_DEBUG);
3847         $site = substr($a->get_baseurl(),strpos($a->get_baseurl(),'://'));
3848
3849         $orig_body = $s;
3850         $new_body = '';
3851
3852         $img_start = strpos($orig_body, '[img');
3853         $img_st_close = ($img_start !== false ? strpos(substr($orig_body, $img_start), ']') : false);
3854         $img_len = ($img_start !== false ? strpos(substr($orig_body, $img_start + $img_st_close + 1), '[/img]') : false);
3855         while( ($img_st_close !== false) && ($img_len !== false) ) {
3856
3857                 $img_st_close++; // make it point to AFTER the closing bracket
3858                 $image = substr($orig_body, $img_start + $img_st_close, $img_len);
3859
3860                 logger('fix_private_photos: found photo ' . $image, LOGGER_DEBUG);
3861
3862
3863                 if(stristr($image , $site . '/photo/')) {
3864                         // Only embed locally hosted photos
3865                         $replace = false;
3866                         $i = basename($image);
3867                         $i = str_replace(array('.jpg','.png','.gif'),array('','',''),$i);
3868                         $x = strpos($i,'-');
3869
3870                         if($x) {
3871                                 $res = substr($i,$x+1);
3872                                 $i = substr($i,0,$x);
3873                                 $r = q("SELECT * FROM `photo` WHERE `resource-id` = '%s' AND `scale` = %d AND `uid` = %d",
3874                                         dbesc($i),
3875                                         intval($res),
3876                                         intval($uid)
3877                                 );
3878                                 if($r) {
3879
3880                                         // Check to see if we should replace this photo link with an embedded image
3881                                         // 1. No need to do so if the photo is public
3882                                         // 2. If there's a contact-id provided, see if they're in the access list
3883                                         //    for the photo. If so, embed it. 
3884                                         // 3. Otherwise, if we have an item, see if the item permissions match the photo
3885                                         //    permissions, regardless of order but first check to see if they're an exact
3886                                         //    match to save some processing overhead.
3887
3888                                         if(has_permissions($r[0])) {
3889                                                 if($cid) {
3890                                                         $recips = enumerate_permissions($r[0]);
3891                                                         if(in_array($cid, $recips)) {
3892                                                                 $replace = true;        
3893                                                         }
3894                                                 }
3895                                                 elseif($item) {
3896                                                         if(compare_permissions($item,$r[0]))
3897                                                                 $replace = true;
3898                                                 }
3899                                         }
3900                                         if($replace) {
3901                                                 $data = $r[0]['data'];
3902                                                 $type = $r[0]['type'];
3903
3904                                                 // If a custom width and height were specified, apply before embedding
3905                                                 if(preg_match("/\[img\=([0-9]*)x([0-9]*)\]/is", substr($orig_body, $img_start, $img_st_close), $match)) {
3906                                                         logger('fix_private_photos: scaling photo', LOGGER_DEBUG);
3907
3908                                                         $width = intval($match[1]);
3909                                                         $height = intval($match[2]);
3910
3911                                                         $ph = new Photo($data, $type);
3912                                                         if($ph->is_valid()) {
3913                                                                 $ph->scaleImage(max($width, $height));
3914                                                                 $data = $ph->imageString();
3915                                                                 $type = $ph->getType();
3916                                                         }
3917                                                 }
3918
3919                                                 logger('fix_private_photos: replacing photo', LOGGER_DEBUG);
3920                                                 $image = 'data:' . $type . ';base64,' . base64_encode($data);
3921                                                 logger('fix_private_photos: replaced: ' . $image, LOGGER_DATA);
3922                                         }
3923                                 }
3924                         }
3925                 }       
3926
3927                 $new_body = $new_body . substr($orig_body, 0, $img_start + $img_st_close) . $image . '[/img]';
3928                 $orig_body = substr($orig_body, $img_start + $img_st_close + $img_len + strlen('[/img]'));
3929                 if($orig_body === false)
3930                         $orig_body = '';
3931
3932                 $img_start = strpos($orig_body, '[img');
3933                 $img_st_close = ($img_start !== false ? strpos(substr($orig_body, $img_start), ']') : false);
3934                 $img_len = ($img_start !== false ? strpos(substr($orig_body, $img_start + $img_st_close + 1), '[/img]') : false);
3935         }
3936
3937         $new_body = $new_body . $orig_body;
3938
3939         return($new_body);
3940 }
3941
3942
3943 function has_permissions($obj) {
3944         if(($obj['allow_cid'] != '') || ($obj['allow_gid'] != '') || ($obj['deny_cid'] != '') || ($obj['deny_gid'] != ''))
3945                 return true;
3946         return false;
3947 }
3948
3949 function compare_permissions($obj1,$obj2) {
3950         // first part is easy. Check that these are exactly the same. 
3951         if(($obj1['allow_cid'] == $obj2['allow_cid'])
3952                 && ($obj1['allow_gid'] == $obj2['allow_gid'])
3953                 && ($obj1['deny_cid'] == $obj2['deny_cid'])
3954                 && ($obj1['deny_gid'] == $obj2['deny_gid']))
3955                 return true;
3956
3957         // This is harder. Parse all the permissions and compare the resulting set.
3958
3959         $recipients1 = enumerate_permissions($obj1);
3960         $recipients2 = enumerate_permissions($obj2);
3961         sort($recipients1);
3962         sort($recipients2);
3963         if($recipients1 == $recipients2)
3964                 return true;
3965         return false;
3966 }
3967
3968 // returns an array of contact-ids that are allowed to see this object
3969
3970 function enumerate_permissions($obj) {
3971         require_once('include/group.php');
3972         $allow_people = expand_acl($obj['allow_cid']);
3973         $allow_groups = expand_groups(expand_acl($obj['allow_gid']));
3974         $deny_people  = expand_acl($obj['deny_cid']);
3975         $deny_groups  = expand_groups(expand_acl($obj['deny_gid']));
3976         $recipients   = array_unique(array_merge($allow_people,$allow_groups));
3977         $deny         = array_unique(array_merge($deny_people,$deny_groups));
3978         $recipients   = array_diff($recipients,$deny);
3979         return $recipients;
3980 }
3981
3982 function item_getfeedtags($item) {
3983         $ret = array();
3984         $matches = false;
3985         $cnt = preg_match_all('|\#\[url\=(.*?)\](.*?)\[\/url\]|',$item['tag'],$matches);
3986         if($cnt) {
3987                 for($x = 0; $x < $cnt; $x ++) {
3988                         if($matches[1][$x])
3989                                 $ret[] = array('#',$matches[1][$x], $matches[2][$x]);
3990                 }
3991         }
3992         $matches = false; 
3993         $cnt = preg_match_all('|\@\[url\=(.*?)\](.*?)\[\/url\]|',$item['tag'],$matches);
3994         if($cnt) {
3995                 for($x = 0; $x < $cnt; $x ++) {
3996                         if($matches[1][$x])
3997                                 $ret[] = array('@',$matches[1][$x], $matches[2][$x]);
3998                 }
3999         } 
4000         return $ret;
4001 }
4002
4003 function item_getfeedattach($item) {
4004         $ret = '';
4005         $arr = explode('[/attach],',$item['attach']);
4006         if(count($arr)) {
4007                 foreach($arr as $r) {
4008                         $matches = false;
4009                         $cnt = preg_match('|\[attach\]href=\"(.*?)\" length=\"(.*?)\" type=\"(.*?)\" title=\"(.*?)\"|',$r,$matches);
4010                         if($cnt) {
4011                                 $ret .= '<link rel="enclosure" href="' . xmlify($matches[1]) . '" type="' . xmlify($matches[3]) . '" ';
4012                                 if(intval($matches[2]))
4013                                         $ret .= 'length="' . intval($matches[2]) . '" ';
4014                                 if($matches[4] !== ' ')
4015                                         $ret .= 'title="' . xmlify(trim($matches[4])) . '" ';
4016                                 $ret .= ' />' . "\r\n";
4017                         }
4018                 }
4019         }
4020         return $ret;
4021 }
4022
4023
4024         
4025 function item_expire($uid,$days) {
4026
4027         if((! $uid) || ($days < 1))
4028                 return;
4029
4030         // $expire_network_only = save your own wall posts
4031         // and just expire conversations started by others
4032
4033         $expire_network_only = get_pconfig($uid,'expire','network_only');
4034         $sql_extra = ((intval($expire_network_only)) ? " AND wall = 0 " : "");
4035
4036         $r = q("SELECT * FROM `item` 
4037                 WHERE `uid` = %d 
4038                 AND `created` < UTC_TIMESTAMP() - INTERVAL %d DAY 
4039                 AND `id` = `parent` 
4040                 $sql_extra
4041                 AND `deleted` = 0",
4042                 intval($uid),
4043                 intval($days)
4044         );
4045
4046         if(! count($r))
4047                 return;
4048
4049         $expire_items = get_pconfig($uid, 'expire','items');
4050         $expire_items = (($expire_items===false)?1:intval($expire_items)); // default if not set: 1
4051
4052         $expire_notes = get_pconfig($uid, 'expire','notes');
4053         $expire_notes = (($expire_notes===false)?1:intval($expire_notes)); // default if not set: 1
4054
4055         $expire_starred = get_pconfig($uid, 'expire','starred');
4056         $expire_starred = (($expire_starred===false)?1:intval($expire_starred)); // default if not set: 1
4057
4058         $expire_photos = get_pconfig($uid, 'expire','photos');
4059         $expire_photos = (($expire_photos===false)?0:intval($expire_photos)); // default if not set: 0
4060
4061         logger('expire: # items=' . count($r). "; expire items: $expire_items, expire notes: $expire_notes, expire starred: $expire_starred, expire photos: $expire_photos");
4062
4063         foreach($r as $item) {
4064
4065                 // don't expire filed items
4066
4067                 if(strpos($item['file'],'[') !== false)
4068                         continue;
4069
4070                 // Only expire posts, not photos and photo comments
4071
4072                 if($expire_photos==0 && strlen($item['resource-id']))
4073                         continue;
4074                 if($expire_starred==0 && intval($item['starred']))
4075                         continue;
4076                 if($expire_notes==0 && $item['type']=='note')
4077                         continue;
4078                 if($expire_items==0 && $item['type']!='note')
4079                         continue;
4080
4081                 drop_item($item['id'],false);
4082         }
4083
4084         proc_run('php',"include/notifier.php","expire","$uid");
4085         
4086 }
4087
4088
4089 function drop_items($items) {
4090         $uid = 0;
4091
4092         if(! local_user() && ! remote_user())
4093                 return;
4094
4095         if(count($items)) {
4096                 foreach($items as $item) {
4097                         $owner = drop_item($item,false);
4098                         if($owner && ! $uid)
4099                                 $uid = $owner;
4100                 }
4101         }
4102
4103         // multiple threads may have been deleted, send an expire notification
4104
4105         if($uid)
4106                 proc_run('php',"include/notifier.php","expire","$uid");
4107 }
4108
4109
4110 function drop_item($id,$interactive = true) {
4111
4112         $a = get_app();
4113
4114         // locate item to be deleted
4115
4116         $r = q("SELECT * FROM `item` WHERE `id` = %d LIMIT 1",
4117                 intval($id)
4118         );
4119
4120         if(! count($r)) {
4121                 if(! $interactive)
4122                         return 0;
4123                 notice( t('Item not found.') . EOL);
4124                 goaway($a->get_baseurl() . '/' . $_SESSION['return_url']);
4125         }
4126
4127         $item = $r[0];
4128
4129         $owner = $item['uid'];
4130
4131         $cid = 0;
4132
4133         // check if logged in user is either the author or owner of this item
4134
4135         if(is_array($_SESSION['remote'])) {
4136                 foreach($_SESSION['remote'] as $visitor) {
4137                         if($visitor['uid'] == $item['uid'] && $visitor['cid'] == $item['contact-id']) {
4138                                 $cid = $visitor['cid'];
4139                                 break;
4140                         }
4141                 }
4142         }
4143
4144
4145         if((local_user() == $item['uid']) || ($cid) || (! $interactive)) {
4146
4147                 // Check if we should do HTML-based delete confirmation
4148                 if($_REQUEST['confirm']) {
4149                         // <form> can't take arguments in its "action" parameter
4150                         // so add any arguments as hidden inputs
4151                         $query = explode_querystring($a->query_string);
4152                         $inputs = array();
4153                         foreach($query['args'] as $arg) {
4154                                 if(strpos($arg, 'confirm=') === false) {
4155                                         $arg_parts = explode('=', $arg);
4156                                         $inputs[] = array('name' => $arg_parts[0], 'value' => $arg_parts[1]);
4157                                 }
4158                         }
4159
4160                         return replace_macros(get_markup_template('confirm.tpl'), array(
4161                                 '$method' => 'get',
4162                                 '$message' => t('Do you really want to delete this item?'),
4163                                 '$extra_inputs' => $inputs,
4164                                 '$confirm' => t('Yes'),
4165                                 '$confirm_url' => $query['base'],
4166                                 '$confirm_name' => 'confirmed',
4167                                 '$cancel' => t('Cancel'),
4168                         ));
4169                 }
4170                 // Now check how the user responded to the confirmation query
4171                 if($_REQUEST['canceled']) {
4172                         goaway($a->get_baseurl() . '/' . $_SESSION['return_url']);
4173                 }
4174
4175                 logger('delete item: ' . $item['id'], LOGGER_DEBUG);
4176                 // delete the item
4177
4178                 $r = q("UPDATE `item` SET `deleted` = 1, `title` = '', `body` = '', `edited` = '%s', `changed` = '%s' WHERE `id` = %d",
4179                         dbesc(datetime_convert()),
4180                         dbesc(datetime_convert()),
4181                         intval($item['id'])
4182                 );
4183                 create_tags_from_item($item['id']);
4184                 delete_thread($item['id']);
4185
4186                 // clean up categories and tags so they don't end up as orphans
4187
4188                 $matches = false;
4189                 $cnt = preg_match_all('/<(.*?)>/',$item['file'],$matches,PREG_SET_ORDER);
4190                 if($cnt) {
4191                         foreach($matches as $mtch) {
4192                                 file_tag_unsave_file($item['uid'],$item['id'],$mtch[1],true);
4193                         }
4194                 }
4195
4196                 $matches = false;
4197
4198                 $cnt = preg_match_all('/\[(.*?)\]/',$item['file'],$matches,PREG_SET_ORDER);
4199                 if($cnt) {
4200                         foreach($matches as $mtch) {
4201                                 file_tag_unsave_file($item['uid'],$item['id'],$mtch[1],false);
4202                         }
4203                 }
4204
4205                 // If item is a link to a photo resource, nuke all the associated photos 
4206                 // (visitors will not have photo resources)
4207                 // This only applies to photos uploaded from the photos page. Photos inserted into a post do not
4208                 // generate a resource-id and therefore aren't intimately linked to the item. 
4209
4210                 if(strlen($item['resource-id'])) {
4211                         q("DELETE FROM `photo` WHERE `resource-id` = '%s' AND `uid` = %d ",
4212                                 dbesc($item['resource-id']),
4213                                 intval($item['uid'])
4214                         );
4215                         // ignore the result
4216                 }
4217
4218                 // If item is a link to an event, nuke the event record.
4219
4220                 if(intval($item['event-id'])) {
4221                         q("DELETE FROM `event` WHERE `id` = %d AND `uid` = %d",
4222                                 intval($item['event-id']),
4223                                 intval($item['uid'])
4224                         );
4225                         // ignore the result
4226                 }
4227
4228                 // clean up item_id and sign meta-data tables
4229
4230                 /*
4231                 // Old code - caused very long queries and warning entries in the mysql logfiles:
4232
4233                 $r = q("DELETE FROM item_id where iid in (select id from item where parent = %d and uid = %d)",
4234                         intval($item['id']),
4235                         intval($item['uid'])
4236                 );
4237
4238                 $r = q("DELETE FROM sign where iid in (select id from item where parent = %d and uid = %d)",
4239                         intval($item['id']),
4240                         intval($item['uid'])
4241                 );
4242                 */
4243
4244                 // The new code splits the queries since the mysql optimizer really has bad problems with subqueries
4245
4246                 // Creating list of parents
4247                 $r = q("select id from item where parent = %d and uid = %d",
4248                         intval($item['id']),
4249                         intval($item['uid'])
4250                 );
4251
4252                 $parentid = "";
4253
4254                 foreach ($r AS $row) {
4255                         if ($parentid != "")
4256                                 $parentid .= ", ";
4257
4258                         $parentid .= $row["id"];
4259                 }
4260
4261                 // Now delete them
4262                 if ($parentid != "") {
4263                         $r = q("DELETE FROM item_id where iid in (%s)", dbesc($parentid));
4264
4265                         $r = q("DELETE FROM sign where iid in (%s)", dbesc($parentid));
4266                 }
4267
4268                 // If it's the parent of a comment thread, kill all the kids
4269
4270                 if($item['uri'] == $item['parent-uri']) {
4271                         $r = q("UPDATE `item` SET `deleted` = 1, `edited` = '%s', `changed` = '%s', `body` = '' , `title` = ''
4272                                 WHERE `parent-uri` = '%s' AND `uid` = %d ",
4273                                 dbesc(datetime_convert()),
4274                                 dbesc(datetime_convert()),
4275                                 dbesc($item['parent-uri']),
4276                                 intval($item['uid'])
4277                         );
4278                         create_tags_from_item($item['parent-uri'], $item['uid']);
4279                         delete_thread_uri($item['parent-uri'], $item['uid']);
4280                         // ignore the result
4281                 }
4282                 else {
4283                         // ensure that last-child is set in case the comment that had it just got wiped.
4284                         q("UPDATE `item` SET `last-child` = 0, `changed` = '%s' WHERE `parent-uri` = '%s' AND `uid` = %d ",
4285                                 dbesc(datetime_convert()),
4286                                 dbesc($item['parent-uri']),
4287                                 intval($item['uid'])
4288                         );
4289                         // who is the last child now?
4290                         $r = q("SELECT `id` FROM `item` WHERE `parent-uri` = '%s' AND `type` != 'activity' AND `deleted` = 0 AND `uid` = %d ORDER BY `edited` DESC LIMIT 1",
4291                                 dbesc($item['parent-uri']),
4292                                 intval($item['uid'])
4293                         );
4294                         if(count($r)) {
4295                                 q("UPDATE `item` SET `last-child` = 1 WHERE `id` = %d",
4296                                         intval($r[0]['id'])
4297                                 );
4298                         }
4299
4300                         // Add a relayable_retraction signature for Diaspora.
4301                         store_diaspora_retract_sig($item, $a->user, $a->get_baseurl());
4302                 }
4303                 $drop_id = intval($item['id']);
4304
4305                 // send the notification upstream/downstream as the case may be
4306
4307                 proc_run('php',"include/notifier.php","drop","$drop_id");
4308
4309                 if(! $interactive)
4310                         return $owner;
4311                 goaway($a->get_baseurl() . '/' . $_SESSION['return_url']);
4312                 //NOTREACHED
4313         }
4314         else {
4315                 if(! $interactive)
4316                         return 0;
4317                 notice( t('Permission denied.') . EOL);
4318                 goaway($a->get_baseurl() . '/' . $_SESSION['return_url']);
4319                 //NOTREACHED
4320         }
4321
4322 }
4323
4324
4325 function first_post_date($uid,$wall = false) {
4326         $r = q("select id, created from item
4327                 where uid = %d and wall = %d and deleted = 0 and visible = 1 AND moderated = 0
4328                 and id = parent
4329                 order by created asc limit 1",
4330                 intval($uid),
4331                 intval($wall ? 1 : 0)
4332         );
4333         if(count($r)) {
4334 //              logger('first_post_date: ' . $r[0]['id'] . ' ' . $r[0]['created'], LOGGER_DATA);
4335                 return substr(datetime_convert('',date_default_timezone_get(),$r[0]['created']),0,10);
4336         }
4337         return false;
4338 }
4339
4340 function posted_dates($uid,$wall) {
4341         $dnow = datetime_convert('',date_default_timezone_get(),'now','Y-m-d');
4342
4343         $dthen = first_post_date($uid,$wall);
4344         if(! $dthen)
4345                 return array();
4346
4347         // If it's near the end of a long month, backup to the 28th so that in 
4348         // consecutive loops we'll always get a whole month difference.
4349
4350         if(intval(substr($dnow,8)) > 28)
4351                 $dnow = substr($dnow,0,8) . '28';
4352         if(intval(substr($dthen,8)) > 28)
4353                 $dnow = substr($dthen,0,8) . '28';
4354
4355         $ret = array();
4356         // Starting with the current month, get the first and last days of every
4357         // month down to and including the month of the first post
4358         while(substr($dnow, 0, 7) >= substr($dthen, 0, 7)) {
4359                 $dstart = substr($dnow,0,8) . '01';
4360                 $dend = substr($dnow,0,8) . get_dim(intval($dnow),intval(substr($dnow,5)));
4361                 $start_month = datetime_convert('','',$dstart,'Y-m-d');
4362                 $end_month = datetime_convert('','',$dend,'Y-m-d');
4363                 $str = day_translate(datetime_convert('','',$dnow,'F Y'));
4364                 $ret[] = array($str,$end_month,$start_month);
4365                 $dnow = datetime_convert('','',$dnow . ' -1 month', 'Y-m-d');
4366         }
4367         return $ret;
4368 }
4369
4370
4371 function posted_date_widget($url,$uid,$wall) {
4372         $o = '';
4373
4374         if(! feature_enabled($uid,'archives'))
4375                 return $o;
4376
4377         // For former Facebook folks that left because of "timeline"
4378
4379 /*      if($wall && intval(get_pconfig($uid,'system','no_wall_archive_widget')))
4380                 return $o;*/
4381
4382         $ret = posted_dates($uid,$wall);
4383         if(! count($ret))
4384                 return $o;
4385
4386         $o = replace_macros(get_markup_template('posted_date_widget.tpl'),array(
4387                 '$title' => t('Archives'),
4388                 '$size' => ((count($ret) > 6) ? 6 : count($ret)),
4389                 '$url' => $url,
4390                 '$dates' => $ret
4391         ));
4392         return $o;
4393 }
4394
4395 function store_diaspora_retract_sig($item, $user, $baseurl) {
4396         // Note that we can't add a target_author_signature
4397         // if the comment was deleted by a remote user. That should be ok, because if a remote user is deleting
4398         // the comment, that means we're the home of the post, and Diaspora will only
4399         // check the parent_author_signature of retractions that it doesn't have to relay further
4400         //
4401         // I don't think this function gets called for an "unlike," but I'll check anyway
4402
4403         $enabled = intval(get_config('system','diaspora_enabled'));
4404         if(! $enabled) {
4405                 logger('drop_item: diaspora support disabled, not storing retraction signature', LOGGER_DEBUG);
4406                 return;
4407         }
4408
4409         logger('drop_item: storing diaspora retraction signature');
4410
4411         $signed_text = $item['guid'] . ';' . ( ($item['verb'] === ACTIVITY_LIKE) ? 'Like' : 'Comment');
4412
4413         if(local_user() == $item['uid']) {
4414
4415                 $handle = $user['nickname'] . '@' . substr($baseurl, strpos($baseurl,'://') + 3);
4416                 $authorsig = base64_encode(rsa_sign($signed_text,$user['prvkey'],'sha256'));
4417         }
4418         else {
4419                 $r = q("SELECT `nick`, `url` FROM `contact` WHERE `id` = '%d' LIMIT 1",
4420                         $item['contact-id'] // If this function gets called, drop_item() has already checked remote_user() == $item['contact-id']
4421                 );
4422                 if(count($r)) {
4423                         // The below handle only works for NETWORK_DFRN. I think that's ok, because this function
4424                         // only handles DFRN deletes
4425                         $handle_baseurl_start = strpos($r['url'],'://') + 3;
4426                         $handle_baseurl_length = strpos($r['url'],'/profile') - $handle_baseurl_start;
4427                         $handle = $r['nick'] . '@' . substr($r['url'], $handle_baseurl_start, $handle_baseurl_length);
4428                         $authorsig = '';
4429                 }
4430         }
4431
4432         if(isset($handle))
4433                 q("insert into sign (`retract_iid`,`signed_text`,`signature`,`signer`) values (%d,'%s','%s','%s') ",
4434                         intval($item['id']),
4435                         dbesc($signed_text),
4436                         dbesc($authorsig),
4437                         dbesc($handle)
4438                 );
4439
4440         return;
4441 }