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