]> git.mxchange.org Git - friendica.git/blob - include/items.php
update DE strings
[friendica.git] / include / items.php
1 <?php
2
3 require_once('bbcode.php');
4 require_once('oembed.php');
5 require_once('include/salmon.php');
6
7 function get_feed_for(&$a, $dfrn_id, $owner_nick, $last_update, $direction = 0) {
8
9         // default permissions - anonymous user
10
11         if(! strlen($owner_nick))
12                 killme();
13
14         $sql_extra = " AND `allow_cid` = '' AND `allow_gid` = '' AND `deny_cid`  = '' AND `deny_gid`  = '' ";
15
16         $r = q("SELECT `contact`.*, `user`.`uid` AS `user_uid`, `user`.`nickname`, `user`.`timezone`
17                 FROM `contact` LEFT JOIN `user` ON `user`.`uid` = `contact`.`uid`
18                 WHERE `contact`.`self` = 1 AND `user`.`nickname` = '%s' LIMIT 1",
19                 dbesc($owner_nick)
20         );
21
22         if(! count($r))
23                 killme();
24
25         $owner = $r[0];
26         $owner_id = $owner['user_uid'];
27         $owner_nick = $owner['nickname'];
28
29         $birthday = feed_birthday($owner_id,$owner['timezone']);
30
31         if(strlen($dfrn_id)) {
32
33                 $sql_extra = '';
34                 switch($direction) {
35                         case (-1):
36                                 $sql_extra = sprintf(" AND `issued-id` = '%s' ", dbesc($dfrn_id));
37                                 $my_id = $dfrn_id;
38                                 break;
39                         case 0:
40                                 $sql_extra = sprintf(" AND `issued-id` = '%s' AND `duplex` = 1 ", dbesc($dfrn_id));
41                                 $my_id = '1:' . $dfrn_id;
42                                 break;
43                         case 1:
44                                 $sql_extra = sprintf(" AND `dfrn-id` = '%s' AND `duplex` = 1 ", dbesc($dfrn_id));
45                                 $my_id = '0:' . $dfrn_id;
46                                 break;
47                         default:
48                                 return false;
49                                 break; // NOTREACHED
50                 }
51
52                 $r = q("SELECT * FROM `contact` WHERE `blocked` = 0 AND `pending` = 0 AND `contact`.`uid` = %d $sql_extra LIMIT 1",
53                         intval($owner_id)
54                 );
55
56                 if(! count($r))
57                         killme();
58
59                 $contact = $r[0];
60                 $groups = init_groups_visitor($contact['id']);
61
62                 if(count($groups)) {
63                         for($x = 0; $x < count($groups); $x ++) 
64                                 $groups[$x] = '<' . intval($groups[$x]) . '>' ;
65                         $gs = implode('|', $groups);
66                 }
67                 else
68                         $gs = '<<>>' ; // Impossible to match 
69
70                 $sql_extra = sprintf(" 
71                         AND ( `allow_cid` = '' OR     `allow_cid` REGEXP '<%d>' ) 
72                         AND ( `deny_cid`  = '' OR NOT `deny_cid`  REGEXP '<%d>' ) 
73                         AND ( `allow_gid` = '' OR     `allow_gid` REGEXP '%s' )
74                         AND ( `deny_gid`  = '' OR NOT `deny_gid`  REGEXP '%s') 
75                 ",
76                         intval($contact['id']),
77                         intval($contact['id']),
78                         dbesc($gs),
79                         dbesc($gs)
80                 );
81         }
82
83         if($dfrn_id === '' || $dfrn_id === '*')
84                 $sort = 'DESC';
85         else
86                 $sort = 'ASC';
87
88         if(! strlen($last_update))
89                 $last_update = 'now -30 days';
90
91         $check_date = datetime_convert('UTC','UTC',$last_update,'Y-m-d H:i:s');
92
93         $r = q("SELECT `item`.*, `item`.`id` AS `item_id`, 
94                 `contact`.`name`, `contact`.`photo`, `contact`.`url`, 
95                 `contact`.`name-date`, `contact`.`uri-date`, `contact`.`avatar-date`,
96                 `contact`.`thumb`, `contact`.`dfrn-id`, `contact`.`self`, 
97                 `contact`.`id` AS `contact-id`, `contact`.`uid` AS `contact-uid`
98                 FROM `item` LEFT JOIN `contact` ON `contact`.`id` = `item`.`contact-id`
99                 WHERE `item`.`uid` = %d AND `item`.`visible` = 1 AND `item`.`parent` != 0 
100                 AND `item`.`wall` = 1 AND `contact`.`blocked` = 0 AND `contact`.`pending` = 0
101                 AND ( `item`.`edited` > '%s' OR `item`.`changed` > '%s' )
102                 $sql_extra
103                 ORDER BY `parent` %s, `created` ASC LIMIT 0, 300",
104                 intval($owner_id),
105                 dbesc($check_date),
106                 dbesc($check_date),
107                 dbesc($sort)
108         );
109
110         // Will check further below if this actually returned results.
111         // We will provide an empty feed if that is the case.
112
113         $items = $r;
114
115         $feed_template = get_markup_template(($dfrn_id) ? 'atom_feed_dfrn.tpl' : 'atom_feed.tpl');
116
117         $atom = '';
118
119         $hubxml = feed_hublinks();
120
121         $salmon = feed_salmonlinks($owner_nick);
122
123         $atom .= replace_macros($feed_template, array(
124                 '$version'      => xmlify(FRIENDIKA_VERSION),
125                 '$feed_id'      => xmlify($a->get_baseurl() . '/profile/' . $owner_nick),
126                 '$feed_title'   => xmlify($owner['name']),
127                 '$feed_updated' => xmlify(datetime_convert('UTC', 'UTC', 'now' , ATOM_TIME)) ,
128                 '$hub'          => $hubxml,
129                 '$salmon'       => $salmon,
130                 '$name'         => xmlify($owner['name']),
131                 '$profile_page' => xmlify($owner['url']),
132                 '$photo'        => xmlify($owner['photo']),
133                 '$thumb'        => xmlify($owner['thumb']),
134                 '$picdate'      => xmlify(datetime_convert('UTC','UTC',$owner['avatar-date'] . '+00:00' , ATOM_TIME)) ,
135                 '$uridate'      => xmlify(datetime_convert('UTC','UTC',$owner['uri-date']    . '+00:00' , ATOM_TIME)) ,
136                 '$namdate'      => xmlify(datetime_convert('UTC','UTC',$owner['name-date']   . '+00:00' , ATOM_TIME)) , 
137                 '$birthday'     => ((strlen($birthday)) ? '<dfrn:birthday>' . xmlify($birthday) . '</dfrn:birthday>' : '')
138         ));
139
140         call_hooks('atom_feed', $atom);
141
142         if(! count($items)) {
143
144                 call_hooks('atom_feed_end', $atom);
145
146                 $atom .= '</feed>' . "\r\n";
147                 return $atom;
148         }
149
150         foreach($items as $item) {
151
152                 // public feeds get html, our own nodes use bbcode
153
154                 if($dfrn_id === '') {
155                         $type = 'html';
156                         // catch any email that's in a public conversation and make sure it doesn't leak
157                         if($item['private'])
158                                 continue;
159                 }
160                 else {
161                         $type = 'text';
162                 }
163
164                 $atom .= atom_entry($item,$type,null,$owner,true);
165         }
166
167         call_hooks('atom_feed_end', $atom);
168
169         $atom .= '</feed>' . "\r\n";
170
171         return $atom;
172 }
173
174
175 function construct_verb($item) {
176         if($item['verb'])
177                 return $item['verb'];
178         return ACTIVITY_POST;
179 }
180
181 function construct_activity_object($item) {
182
183         if($item['object']) {
184                 $o = '<as:object>' . "\r\n";
185                 $r = parse_xml_string($item['object'],false);
186
187
188                 if(! $r)
189                         return '';
190                 if($r->type)
191                         $o .= '<as:object-type>' . xmlify($r->type) . '</as:object-type>' . "\r\n";
192                 if($r->id)
193                         $o .= '<id>' . xmlify($r->id) . '</id>' . "\r\n";
194                 if($r->title)
195                         $o .= '<title>' . xmlify($r->title) . '</title>' . "\r\n";
196                 if($r->link) {
197                         if(substr($r->link,0,1) === '<') {
198                                 // patch up some facebook "like" activity objects that got stored incorrectly
199                                 // for a couple of months prior to 9-Jun-2011 and generated bad XML.
200                                 // we can probably remove this hack here and in the following function in a few months time.
201                                 if(strstr($r->link,'&') && (! strstr($r->link,'&amp;')))
202                                         $r->link = str_replace('&','&amp;', $r->link);
203                                 $r->link = preg_replace('/\<link(.*?)\"\>/','<link$1"/>',$r->link);
204                                 $o .= $r->link;
205                         }                                       
206                         else
207                                 $o .= '<link rel="alternate" type="text/html" href="' . xmlify($r->link) . '" />' . "\r\n";
208                 }
209                 if($r->content)
210                         $o .= '<content type="html" >' . xmlify(bbcode($r->content)) . '</content>' . "\r\n";
211                 $o .= '</as:object>' . "\r\n";
212                 return $o;
213         }
214
215         return '';
216
217
218 function construct_activity_target($item) {
219
220         if($item['target']) {
221                 $o = '<as:target>' . "\r\n";
222                 $r = parse_xml_string($item['target'],false);
223                 if(! $r)
224                         return '';
225                 if($r->type)
226                         $o .= '<as:object-type>' . xmlify($r->type) . '</as:object-type>' . "\r\n";
227                 if($r->id)
228                         $o .= '<id>' . xmlify($r->id) . '</id>' . "\r\n";
229                 if($r->title)
230                         $o .= '<title>' . xmlify($r->title) . '</title>' . "\r\n";
231                 if($r->link) {
232                         if(substr($r->link,0,1) === '<') {
233                                 if(strstr($r->link,'&') && (! strstr($r->link,'&amp;')))
234                                         $r->link = str_replace('&','&amp;', $r->link);
235                                 $r->link = preg_replace('/\<link(.*?)\"\>/','<link$1"/>',$r->link);
236                                 $o .= $r->link;
237                         }                                       
238                         else
239                                 $o .= '<link rel="alternate" type="text/html" href="' . xmlify($r->link) . '" />' . "\r\n";
240                 }
241                 if($r->content)
242                         $o .= '<content type="html" >' . xmlify(bbcode($r->content)) . '</content>' . "\r\n";
243                 $o .= '</as:target>' . "\r\n";
244                 return $o;
245         }
246
247         return '';
248
249
250
251
252
253 function get_atom_elements($feed,$item) {
254
255         require_once('library/HTMLPurifier.auto.php');
256         require_once('include/html2bbcode.php');
257
258         $best_photo = array();
259
260         $res = array();
261
262         $author = $item->get_author();
263         if($author) { 
264                 $res['author-name'] = unxmlify($author->get_name());
265                 $res['author-link'] = unxmlify($author->get_link());
266         }
267         else {
268                 $res['author-name'] = unxmlify($feed->get_title());
269                 $res['author-link'] = unxmlify($feed->get_permalink());
270         }
271         $res['uri'] = unxmlify($item->get_id());
272         $res['title'] = unxmlify($item->get_title());
273         $res['body'] = unxmlify($item->get_content());
274         $res['plink'] = unxmlify($item->get_link(0));
275
276         // look for a photo. We should check media size and find the best one,
277         // but for now let's just find any author photo
278
279         $rawauthor = $item->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10,'author');
280
281         if($rawauthor && $rawauthor[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link']) {
282                 $base = $rawauthor[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link'];
283                 foreach($base as $link) {
284                         if(! $res['author-avatar']) {
285                                 if($link['attribs']['']['rel'] === 'photo' || $link['attribs']['']['rel'] === 'avatar')
286                                         $res['author-avatar'] = unxmlify($link['attribs']['']['href']);
287                         }
288                 }
289         }                       
290
291         $rawactor = $item->get_item_tags(NAMESPACE_ACTIVITY, 'actor');
292
293         if($rawactor && activity_match($rawactor[0]['child'][NAMESPACE_ACTIVITY]['object-type'][0]['data'],ACTIVITY_OBJ_PERSON)) {
294                 $base = $rawactor[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link'];
295                 if($base && count($base)) {
296                         foreach($base as $link) {
297                                 if($link['attribs']['']['rel'] === 'alternate' && (! $res['author-link']))
298                                         $res['author-link'] = unxmlify($link['attribs']['']['href']);
299                                 if(! $res['author-avatar']) {
300                                         if($link['attribs']['']['rel'] === 'avatar' || $link['attribs']['']['rel'] === 'photo')
301                                                 $res['author-avatar'] = unxmlify($link['attribs']['']['href']);
302                                 }
303                         }
304                 }
305         }
306
307         // No photo/profile-link on the item - look at the feed level
308
309         if((! (x($res,'author-link'))) || (! (x($res,'author-avatar')))) {
310                 $rawauthor = $feed->get_feed_tags(SIMPLEPIE_NAMESPACE_ATOM_10,'author');
311                 if($rawauthor && $rawauthor[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link']) {
312                         $base = $rawauthor[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link'];
313                         foreach($base as $link) {
314                                 if($link['attribs']['']['rel'] === 'alternate' && (! $res['author-link']))
315                                         $res['author-link'] = unxmlify($link['attribs']['']['href']);
316                                 if(! $res['author-avatar']) {
317                                         if($link['attribs']['']['rel'] === 'photo' || $link['attribs']['']['rel'] === 'avatar')
318                                                 $res['author-avatar'] = unxmlify($link['attribs']['']['href']);
319                                 }
320                         }
321                 }                       
322
323                 $rawactor = $feed->get_feed_tags(NAMESPACE_ACTIVITY, 'subject');
324
325                 if($rawactor && activity_match($rawactor[0]['child'][NAMESPACE_ACTIVITY]['object-type'][0]['data'],ACTIVITY_OBJ_PERSON)) {
326                         $base = $rawactor[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link'];
327
328                         if($base && count($base)) {
329                                 foreach($base as $link) {
330                                         if($link['attribs']['']['rel'] === 'alternate' && (! $res['author-link']))
331                                                 $res['author-link'] = unxmlify($link['attribs']['']['href']);
332                                         if(! (x($res,'author-avatar'))) {
333                                                 if($link['attribs']['']['rel'] === 'avatar' || $link['attribs']['']['rel'] === 'photo')
334                                                         $res['author-avatar'] = unxmlify($link['attribs']['']['href']);
335                                         }
336                                 }
337                         }
338                 }
339         }
340
341         $apps = $item->get_item_tags(NAMESPACE_STATUSNET,'notice_info');
342         if($apps && $apps[0]['attribs']['']['source']) {
343                 $res['app'] = strip_tags(unxmlify($apps[0]['attribs']['']['source']));
344                 if($res['app'] === 'web')
345                         $res['app'] = 'OStatus';
346         }                  
347
348         /**
349          * If there's a copy of the body content which is guaranteed to have survived mangling in transit, use it.
350          */
351
352         $have_real_body = false;
353
354         $rawenv = $item->get_item_tags(NAMESPACE_DFRN, 'env');
355         if($rawenv) {
356                 $have_real_body = true;
357                 $res['body'] = $rawenv[0]['data'];
358                 $res['body'] = str_replace(array(' ',"\t","\r","\n"), array('','','',''),$res['body']);
359                 // make sure nobody is trying to sneak some html tags by us
360                 $res['body'] = notags(base64url_decode($res['body']));
361         }
362
363         $maxlen = get_max_import_size();
364         if($maxlen && (strlen($res['body']) > $maxlen))
365                 $res['body'] = substr($res['body'],0, $maxlen);
366
367         // It isn't certain at this point whether our content is plaintext or html and we'd be foolish to trust 
368         // the content type. Our own network only emits text normally, though it might have been converted to 
369         // html if we used a pubsubhubbub transport. But if we see even one html tag in our text, we will
370         // have to assume it is all html and needs to be purified.
371
372         // It doesn't matter all that much security wise - because before this content is used anywhere, we are 
373         // going to escape any tags we find regardless, but this lets us import a limited subset of html from 
374         // the wild, by sanitising it and converting supported tags to bbcode before we rip out any remaining 
375         // html.
376
377         if((strpos($res['body'],'<') !== false) || (strpos($res['body'],'>') !== false)) {
378
379                 $res['body'] = preg_replace('#<object[^>]+>.+?' . 'http://www.youtube.com/((?:v|cp)/[A-Za-z0-9\-_=]+).+?</object>#s',
380                         '[youtube]$1[/youtube]', $res['body']);
381
382                 $res['body'] = preg_replace('#<iframe[^>].+?' . 'http://www.youtube.com/embed/([A-Za-z0-9\-_=]+).+?</iframe>#s',
383                         '[youtube]$1[/youtube]', $res['body']);
384
385                 $res['body'] = oembed_html2bbcode($res['body']);
386
387                 $config = HTMLPurifier_Config::createDefault();
388                 $config->set('Cache.DefinitionImpl', null);
389
390                 // we shouldn't need a whitelist, because the bbcode converter
391                 // will strip out any unsupported tags.
392                 // $config->set('HTML.Allowed', 'p,b,a[href],i'); 
393
394                 $purifier = new HTMLPurifier($config);
395                 $res['body'] = $purifier->purify($res['body']);
396
397                 $res['body'] = html2bbcode($res['body']);
398         }
399
400         $allow = $item->get_item_tags(NAMESPACE_DFRN,'comment-allow');
401         if($allow && $allow[0]['data'] == 1)
402                 $res['last-child'] = 1;
403         else
404                 $res['last-child'] = 0;
405
406         $private = $item->get_item_tags(NAMESPACE_DFRN,'private');
407         if($private && $private[0]['data'] == 1)
408                 $res['private'] = 1;
409         else
410                 $res['private'] = 0;
411
412         $extid = $item->get_item_tags(NAMESPACE_DFRN,'extid');
413         if($extid && $extid[0]['data'])
414                 $res['extid'] = $extid[0]['data'];
415
416         $rawlocation = $item->get_item_tags(NAMESPACE_DFRN, 'location');
417         if($rawlocation)
418                 $res['location'] = unxmlify($rawlocation[0]['data']);
419
420
421         $rawcreated = $item->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10,'published');
422         if($rawcreated)
423                 $res['created'] = unxmlify($rawcreated[0]['data']);
424
425
426         $rawedited = $item->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10,'updated');
427         if($rawedited)
428                 $res['edited'] = unxmlify($rawedited[0]['data']);
429
430         if((x($res,'edited')) && (! (x($res,'created'))))
431                 $res['created'] = $res['edited']; 
432
433         if(! $res['created'])
434                 $res['created'] = $item->get_date('c');
435
436         if(! $res['edited'])
437                 $res['edited'] = $item->get_date('c');
438
439
440         // Disallow time travelling posts
441
442         $d1 = strtotime($res['created']);
443         $d2 = strtotime($res['edited']);
444         $d3 = strtotime('now');
445
446         if($d1 > $d3)
447                 $res['created'] = datetime_convert();
448         if($d2 > $d3)
449                 $res['edited'] = datetime_convert();
450
451         $rawowner = $item->get_item_tags(NAMESPACE_DFRN, 'owner');
452         if($rawowner[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'])
453                 $res['owner-name'] = unxmlify($rawowner[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data']);
454         elseif($rawowner[0]['child'][NAMESPACE_DFRN]['name'][0]['data'])
455                 $res['owner-name'] = unxmlify($rawowner[0]['child'][NAMESPACE_DFRN]['name'][0]['data']);
456         if($rawowner[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'])
457                 $res['owner-link'] = unxmlify($rawowner[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data']);
458         elseif($rawowner[0]['child'][NAMESPACE_DFRN]['uri'][0]['data'])
459                 $res['owner-link'] = unxmlify($rawowner[0]['child'][NAMESPACE_DFRN]['uri'][0]['data']);
460
461         if($rawowner[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link']) {
462                 $base = $rawowner[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link'];
463
464                 foreach($base as $link) {
465                         if(! $res['owner-avatar']) {
466                                 if($link['attribs']['']['rel'] === 'photo' || $link['attribs']['']['rel'] === 'avatar')                 
467                                         $res['owner-avatar'] = unxmlify($link['attribs']['']['href']);
468                         }
469                 }
470         }
471
472         $rawgeo = $item->get_item_tags(NAMESPACE_GEORSS,'point');
473         if($rawgeo)
474                 $res['coord'] = unxmlify($rawgeo[0]['data']);
475
476
477         $rawverb = $item->get_item_tags(NAMESPACE_ACTIVITY, 'verb');
478
479         // select between supported verbs
480
481         if($rawverb) {
482                 $res['verb'] = unxmlify($rawverb[0]['data']);
483         }
484
485         // translate OStatus unfollow to activity streams if it happened to get selected
486                 
487         if((x($res,'verb')) && ($res['verb'] === 'http://ostatus.org/schema/1.0/unfollow'))
488                 $res['verb'] = ACTIVITY_UNFOLLOW;
489
490         $cats = $item->get_categories();
491         if($cats) {
492                 $tag_arr = array();
493                 foreach($cats as $cat) {
494                         $term = $cat->get_term();
495                         if(! $term)
496                                 $term = $cat->get_label();
497                         $scheme = $cat->get_scheme();
498                         if($scheme && $term && stristr($scheme,'X-DFRN:'))
499                                 $tag_arr[] = substr($scheme,7,1) . '[url=' . unxmlify(substr($scheme,9)) . ']' . unxmlify($term) . '[/url]';
500                         elseif($term)
501                                 $tag_arr[] = notags(trim($term));
502                 }
503                 $res['tag'] =  implode(',', $tag_arr);
504         }
505
506         $attach = $item->get_enclosures();
507         if($attach) {
508                 $att_arr = array();
509                 foreach($attach as $att) {
510                         $len   = intval($att->get_length());
511                         $link  = str_replace(array(',','"'),array('%2D','%22'),notags(trim(unxmlify($att->get_link()))));
512                         $title = str_replace(array(',','"'),array('%2D','%22'),notags(trim(unxmlify($att->get_title()))));
513                         $type  = str_replace(array(',','"'),array('%2D','%22'),notags(trim(unxmlify($att->get_type()))));
514                         if(strpos($type,';'))
515                                 $type = substr($type,0,strpos($type,';'));
516                         if((! $link) || (strpos($link,'http') !== 0))
517                                 continue;
518
519                         if(! $title)
520                                 $title = ' ';
521                         if(! $type)
522                                 $type = 'application/octet-stream';
523
524                         $att_arr[] = '[attach]href="' . $link . '" length="' . $len . '" type="' . $type . '" title="' . $title . '"[/attach]'; 
525                 }
526                 $res['attach'] = implode(',', $att_arr);
527         }
528
529         $rawobj = $item->get_item_tags(NAMESPACE_ACTIVITY, 'object');
530
531         if($rawobj) {
532                 $res['object'] = '<object>' . "\n";
533                 if($rawobj[0]['child'][NAMESPACE_ACTIVITY]['object-type'][0]['data']) {
534                         $res['object-type'] = $rawobj[0]['child'][NAMESPACE_ACTIVITY]['object-type'][0]['data'];
535                         $res['object'] .= '<type>' . $rawobj[0]['child'][NAMESPACE_ACTIVITY]['object-type'][0]['data'] . '</type>' . "\n";
536                 }       
537                 if($rawobj[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['id'][0]['data'])
538                         $res['object'] .= '<id>' . $rawobj[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['id'][0]['data'] . '</id>' . "\n";
539                 if($rawobj[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link'])
540                         $res['object'] .= '<link>' . encode_rel_links($rawobj[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link']) . '</link>' . "\n";
541                 if($rawobj[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['title'][0]['data'])
542                         $res['object'] .= '<title>' . $rawobj[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['title'][0]['data'] . '</title>' . "\n";
543                 if($rawobj[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['content'][0]['data']) {
544                         $body = $rawobj[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['content'][0]['data'];
545                         if(! $body)
546                                 $body = $rawobj[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['summary'][0]['data'];
547                         // preserve a copy of the original body content in case we later need to parse out any microformat information, e.g. events
548                         $res['object'] .= '<orig>' . xmlify($body) . '</orig>' . "\n";
549                         if((strpos($body,'<') !== false) || (strpos($body,'>') !== false)) {
550
551                                 $body = preg_replace('#<object[^>]+>.+?' . 'http://www.youtube.com/((?:v|cp)/[A-Za-z0-9\-_=]+).+?</object>#s',
552                                         '[youtube]$1[/youtube]', $body);
553
554                 $res['body'] = preg_replace('#<iframe[^>].+?' . 'http://www.youtube.com/embed/([A-Za-z0-9\-_=]+).+?</iframe>#s',
555                         '[youtube]$1[/youtube]', $res['body']);
556
557
558                                 $config = HTMLPurifier_Config::createDefault();
559                                 $config->set('Cache.DefinitionImpl', null);
560
561                                 $purifier = new HTMLPurifier($config);
562                                 $body = $purifier->purify($body);
563                                 $body = html2bbcode($body);
564                         }
565
566                         $res['object'] .= '<content>' . $body . '</content>' . "\n";
567                 }
568
569                 $res['object'] .= '</object>' . "\n";
570         }
571
572         $rawobj = $item->get_item_tags(NAMESPACE_ACTIVITY, 'target');
573
574         if($rawobj) {
575                 $res['target'] = '<target>' . "\n";
576                 if($rawobj[0]['child'][NAMESPACE_ACTIVITY]['object-type'][0]['data']) {
577                         $res['target'] .= '<type>' . $rawobj[0]['child'][NAMESPACE_ACTIVITY]['object-type'][0]['data'] . '</type>' . "\n";
578                 }       
579                 if($rawobj[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['id'][0]['data'])
580                         $res['target'] .= '<id>' . $rawobj[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['id'][0]['data'] . '</id>' . "\n";
581
582                 if($rawobj[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link'])
583                         $res['target'] .= '<link>' . encode_rel_links($rawobj[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link']) . '</link>' . "\n";
584                 if($rawobj[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['title'][0]['data'])
585                         $res['target'] .= '<title>' . $rawobj[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['title'][0]['data'] . '</title>' . "\n";
586                 if($rawobj[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['content'][0]['data']) {
587                         $body = $rawobj[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['content'][0]['data'];
588                         if(! $body)
589                                 $body = $rawobj[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['summary'][0]['data'];
590                         // preserve a copy of the original body content in case we later need to parse out any microformat information, e.g. events
591                         $res['object'] .= '<orig>' . xmlify($body) . '</orig>' . "\n";
592                         if((strpos($body,'<') !== false) || (strpos($body,'>') !== false)) {
593
594                                 $body = preg_replace('#<object[^>]+>.+?' . 'http://www.youtube.com/((?:v|cp)/[A-Za-z0-9\-_=]+).+?</object>#s',
595                                         '[youtube]$1[/youtube]', $body);
596
597                 $res['body'] = preg_replace('#<iframe[^>].+?' . 'http://www.youtube.com/embed/([A-Za-z0-9\-_=]+).+?</iframe>#s',
598                         '[youtube]$1[/youtube]', $res['body']);
599
600                                 $config = HTMLPurifier_Config::createDefault();
601                                 $config->set('Cache.DefinitionImpl', null);
602
603                                 $purifier = new HTMLPurifier($config);
604                                 $body = $purifier->purify($body);
605                                 $body = html2bbcode($body);
606                         }
607
608                         $res['target'] .= '<content>' . $body . '</content>' . "\n";
609                 }
610
611                 $res['target'] .= '</target>' . "\n";
612         }
613
614         $arr = array('feed' => $feed, 'item' => $item, 'result' => $res);
615
616         call_hooks('parse_atom', $arr);
617
618         return $res;
619 }
620
621 function encode_rel_links($links) {
622         $o = '';
623         if(! ((is_array($links)) && (count($links))))
624                 return $o;
625         foreach($links as $link) {
626                 $o .= '<link ';
627                 if($link['attribs']['']['rel'])
628                         $o .= 'rel="' . $link['attribs']['']['rel'] . '" ';
629                 if($link['attribs']['']['type'])
630                         $o .= 'type="' . $link['attribs']['']['type'] . '" ';
631                 if($link['attribs']['']['href'])
632                         $o .= 'href="' . $link['attribs']['']['href'] . '" ';
633                 if( (x($link['attribs'],NAMESPACE_MEDIA)) && $link['attribs'][NAMESPACE_MEDIA]['width'])
634                         $o .= 'media:width="' . $link['attribs'][NAMESPACE_MEDIA]['width'] . '" ';
635                 if( (x($link['attribs'],NAMESPACE_MEDIA)) && $link['attribs'][NAMESPACE_MEDIA]['height'])
636                         $o .= 'media:height="' . $link['attribs'][NAMESPACE_MEDIA]['height'] . '" ';
637                 $o .= ' />' . "\n" ;
638         }
639         return xmlify($o);
640 }
641
642 function item_store($arr,$force_parent = false) {
643
644         if($arr['gravity'])
645                 $arr['gravity'] = intval($arr['gravity']);
646         elseif($arr['parent-uri'] == $arr['uri'])
647                 $arr['gravity'] = 0;
648         elseif(activity_match($arr['verb'],ACTIVITY_POST))
649                 $arr['gravity'] = 6;
650         else      
651                 $arr['gravity'] = 6;   // extensible catchall
652
653         if(! x($arr,'type'))
654                 $arr['type']      = 'remote';
655
656         // Shouldn't happen but we want to make absolutely sure it doesn't leak from a plugin.
657
658         if((strpos($arr['body'],'<') !== false) || (strpos($arr['body'],'>') !== false)) 
659                 $arr['body'] = strip_tags($arr['body']);
660
661
662         $arr['wall']          = ((x($arr,'wall'))          ? intval($arr['wall'])                : 0);
663         $arr['uri']           = ((x($arr,'uri'))           ? notags(trim($arr['uri']))           : random_string());
664         $arr['extid']         = ((x($arr,'extid'))         ? notags(trim($arr['extid']))         : '');
665         $arr['author-name']   = ((x($arr,'author-name'))   ? notags(trim($arr['author-name']))   : '');
666         $arr['author-link']   = ((x($arr,'author-link'))   ? notags(trim($arr['author-link']))   : '');
667         $arr['author-avatar'] = ((x($arr,'author-avatar')) ? notags(trim($arr['author-avatar'])) : '');
668         $arr['owner-name']    = ((x($arr,'owner-name'))    ? notags(trim($arr['owner-name']))    : '');
669         $arr['owner-link']    = ((x($arr,'owner-link'))    ? notags(trim($arr['owner-link']))    : '');
670         $arr['owner-avatar']  = ((x($arr,'owner-avatar'))  ? notags(trim($arr['owner-avatar']))  : '');
671         $arr['created']       = ((x($arr,'created') !== false) ? datetime_convert('UTC','UTC',$arr['created']) : datetime_convert());
672         $arr['edited']        = ((x($arr,'edited')  !== false) ? datetime_convert('UTC','UTC',$arr['edited'])  : datetime_convert());
673         $arr['received']      = datetime_convert();
674         $arr['changed']       = datetime_convert();
675         $arr['title']         = ((x($arr,'title'))         ? notags(trim($arr['title']))         : '');
676         $arr['location']      = ((x($arr,'location'))      ? notags(trim($arr['location']))      : '');
677         $arr['coord']         = ((x($arr,'coord'))         ? notags(trim($arr['coord']))         : '');
678         $arr['last-child']    = ((x($arr,'last-child'))    ? intval($arr['last-child'])          : 0 );
679         $arr['visible']       = ((x($arr,'visible') !== false) ? intval($arr['visible'])         : 1 );
680         $arr['deleted']       = 0;
681         $arr['parent-uri']    = ((x($arr,'parent-uri'))    ? notags(trim($arr['parent-uri']))    : '');
682         $arr['verb']          = ((x($arr,'verb'))          ? notags(trim($arr['verb']))          : '');
683         $arr['object-type']   = ((x($arr,'object-type'))   ? notags(trim($arr['object-type']))   : '');
684         $arr['object']        = ((x($arr,'object'))        ? trim($arr['object'])                : '');
685         $arr['target-type']   = ((x($arr,'target-type'))   ? notags(trim($arr['target-type']))   : '');
686         $arr['target']        = ((x($arr,'target'))        ? trim($arr['target'])                : '');
687         $arr['plink']         = ((x($arr,'plink'))         ? notags(trim($arr['plink']))         : '');
688         $arr['allow_cid']     = ((x($arr,'allow_cid'))     ? trim($arr['allow_cid'])             : '');
689         $arr['allow_gid']     = ((x($arr,'allow_gid'))     ? trim($arr['allow_gid'])             : '');
690         $arr['deny_cid']      = ((x($arr,'deny_cid'))      ? trim($arr['deny_cid'])              : '');
691         $arr['deny_gid']      = ((x($arr,'deny_gid'))      ? trim($arr['deny_gid'])              : '');
692         $arr['private']       = ((x($arr,'private'))       ? intval($arr['private'])             : 0 );
693         $arr['body']          = ((x($arr,'body'))          ? trim($arr['body'])                  : '');
694         $arr['tag']           = ((x($arr,'tag'))           ? notags(trim($arr['tag']))           : '');
695         $arr['attach']        = ((x($arr,'attach'))        ? notags(trim($arr['attach']))        : '');
696         $arr['app']           = ((x($arr,'app'))           ? notags(trim($arr['app']))           : '');
697
698         if($arr['parent-uri'] === $arr['uri']) {
699                 $parent_id = 0;
700                 $allow_cid = $arr['allow_cid'];
701                 $allow_gid = $arr['allow_gid'];
702                 $deny_cid  = $arr['deny_cid'];
703                 $deny_gid  = $arr['deny_gid'];
704         }
705         else { 
706
707                 // find the parent and snarf the item id and ACL's
708                 // and anything else we need to inherit
709
710                 $r = q("SELECT * FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1",
711                         dbesc($arr['parent-uri']),
712                         intval($arr['uid'])
713                 );
714
715                 if(count($r)) {
716
717                         // is the new message multi-level threaded?
718                         // even though we don't support it now, preserve the info
719                         // and re-attach to the conversation parent.
720
721                         if($r[0]['uri'] != $r[0]['parent-uri']) {
722                                 $arr['thr-parent'] = $arr['parent-uri'];
723                                 $arr['parent-uri'] = $r[0]['parent-uri'];
724                                 $z = q("SELECT `id` FROM `item` WHERE `uri` = '%s' AND `parent-uri` = '%s' AND `uid` = %d LIMIT 1",
725                                         dbesc($r[0]['parent-uri']),
726                                         dbesc($r[0]['parent-uri']),
727                                         intval($arr['uid'])
728                                 );
729                                 if($z && count($z))
730                                         $r = $z;
731                         }
732
733                         $parent_id      = $r[0]['id'];
734                         $parent_deleted = $r[0]['deleted'];
735                         $allow_cid      = $r[0]['allow_cid'];
736                         $allow_gid      = $r[0]['allow_gid'];
737                         $deny_cid       = $r[0]['deny_cid'];
738                         $deny_gid       = $r[0]['deny_gid'];
739                         $arr['wall']    = $r[0]['wall'];
740                 }
741                 else {
742
743                         // Allow one to see reply tweets from status.net even when
744                         // we don't have or can't see the original post.
745
746                         if($force_parent) {
747                                 logger('item_store: $force_parent=true, reply converted to top-level post.');
748                                 $parent_id = 0;
749                                 $arr['thr-parent'] = $arr['parent-uri'];
750                                 $arr['parent-uri'] = $arr['uri'];
751                                 $arr['gravity'] = 0;
752                         }
753                         else {
754                                 logger('item_store: item parent was not found - ignoring item');
755                                 return 0;
756                         }
757                 }
758         }
759
760         $arr['guid'] = get_guid();
761
762         call_hooks('post_remote',$arr);
763
764         dbesc_array($arr);
765
766         logger('item_store: ' . print_r($arr,true), LOGGER_DATA);
767
768         $r = dbq("INSERT INTO `item` (`" 
769                         . implode("`, `", array_keys($arr)) 
770                         . "`) VALUES ('" 
771                         . implode("', '", array_values($arr)) 
772                         . "')" );
773
774         // find the item we just created
775
776         $r = q("SELECT `id` FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1",
777                 $arr['uri'],           // already dbesc'd
778                 intval($arr['uid'])
779         );
780         if(! count($r)) {
781                 // This is not good, but perhaps we encountered a rare race/cache condition, so back off and try again. 
782                 sleep(3);
783                 $r = q("SELECT `id` FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1",
784                         $arr['uri'],           // already dbesc'd
785                         intval($arr['uid'])
786                 );
787         }
788
789         if(count($r)) {
790                 $current_post = $r[0]['id'];
791                 logger('item_store: created item ' . $current_post);
792         }
793         else {
794                 logger('item_store: could not locate created item');
795                 return 0;
796         }
797
798         if((! $parent_id) || ($arr['parent-uri'] === $arr['uri']))      
799                 $parent_id = $current_post;
800
801         if(strlen($allow_cid) || strlen($allow_gid) || strlen($deny_cid) || strlen($deny_gid))
802                 $private = 1;
803         else
804                 $private = $arr['private']; 
805
806         // Set parent id - and also make sure to inherit the parent's ACL's.
807
808         $r = q("UPDATE `item` SET `parent` = %d, `allow_cid` = '%s', `allow_gid` = '%s',
809                 `deny_cid` = '%s', `deny_gid` = '%s', `private` = %d, `deleted` = %d WHERE `id` = %d LIMIT 1",
810                 intval($parent_id),
811                 dbesc($allow_cid),
812                 dbesc($allow_gid),
813                 dbesc($deny_cid),
814                 dbesc($deny_gid),
815                 intval($private),
816                 intval($parent_deleted),
817                 intval($current_post)
818         );
819
820         /**
821          * If this is now the last-child, force all _other_ children of this parent to *not* be last-child
822          */
823
824         if($arr['last-child']) {
825                 $r = q("UPDATE `item` SET `last-child` = 0 WHERE `parent-uri` = '%s' AND `uid` = %d AND `id` != %d",
826                         dbesc($arr['uri']),
827                         intval($arr['uid']),
828                         intval($current_post)
829                 );
830         }
831
832         return $current_post;
833 }
834
835 function get_item_contact($item,$contacts) {
836         if(! count($contacts) || (! is_array($item)))
837                 return false;
838         foreach($contacts as $contact) {
839                 if($contact['id'] == $item['contact-id']) {
840                         return $contact;
841                         break; // NOTREACHED
842                 }
843         }
844         return false;
845 }
846
847
848 function dfrn_deliver($owner,$contact,$atom, $dissolve = false) {
849
850         $a = get_app();
851
852         if((! strlen($contact['issued-id'])) && (! $contact['duplex']) && (! ($owner['page-flags'] == PAGE_COMMUNITY)))
853                 return 3;
854
855         $idtosend = $orig_id = (($contact['dfrn-id']) ? $contact['dfrn-id'] : $contact['issued-id']);
856
857         if($contact['duplex'] && $contact['dfrn-id'])
858                 $idtosend = '0:' . $orig_id;
859         if($contact['duplex'] && $contact['issued-id'])
860                 $idtosend = '1:' . $orig_id;            
861
862         $rino = ((function_exists('mcrypt_encrypt')) ? 1 : 0);
863
864         $rino_enable = get_config('system','rino_encrypt');
865
866         if(! $rino_enable)
867                 $rino = 0;
868
869         $url = $contact['notify'] . '&dfrn_id=' . $idtosend . '&dfrn_version=' . DFRN_PROTOCOL_VERSION . (($rino) ? '&rino=1' : '');
870
871         logger('dfrn_deliver: ' . $url);
872
873         $xml = fetch_url($url);
874
875         $curl_stat = $a->get_curl_code();
876         if(! $curl_stat)
877                 return(-1); // timed out
878
879         logger('dfrn_deliver: ' . $xml);
880
881         if(! $xml)
882                 return 3;
883
884         if(strpos($xml,'<?xml') === false) {
885                 logger('dfrn_deliver: no valid XML returned');
886                 logger('dfrn_deliver: returned XML: ' . $xml, LOGGER_DATA);
887                 return 3;
888         }
889
890         $res = parse_xml_string($xml);
891
892         if((intval($res->status) != 0) || (! strlen($res->challenge)) || (! strlen($res->dfrn_id)))
893                 return (($res->status) ? $res->status : 3);
894
895         $postvars     = array();
896         $sent_dfrn_id = hex2bin((string) $res->dfrn_id);
897         $challenge    = hex2bin((string) $res->challenge);
898         $dfrn_version = (float) (($res->dfrn_version) ? $res->dfrn_version : 2.0);
899         $rino_allowed = ((intval($res->rino) === 1) ? 1 : 0);
900
901         $final_dfrn_id = '';
902
903
904         if(($contact['duplex'] && strlen($contact['pubkey'])) || ($owner['page-flags'] == PAGE_COMMUNITY)) {
905                 openssl_public_decrypt($sent_dfrn_id,$final_dfrn_id,$contact['pubkey']);
906                 openssl_public_decrypt($challenge,$postvars['challenge'],$contact['pubkey']);
907         }
908         else {
909                 openssl_private_decrypt($sent_dfrn_id,$final_dfrn_id,$contact['prvkey']);
910                 openssl_private_decrypt($challenge,$postvars['challenge'],$contact['prvkey']);
911         }
912
913         $final_dfrn_id = substr($final_dfrn_id, 0, strpos($final_dfrn_id, '.'));
914
915         if(strpos($final_dfrn_id,':') == 1)
916                 $final_dfrn_id = substr($final_dfrn_id,2);
917
918         if($final_dfrn_id != $orig_id) {
919                 logger('dfrn_deliver: wrong dfrn_id.');
920                 // did not decode properly - cannot trust this site 
921                 return 3;
922         }
923
924         $postvars['dfrn_id']      = $idtosend;
925         $postvars['dfrn_version'] = DFRN_PROTOCOL_VERSION;
926         if($dissolve)
927                 $postvars['dissolve'] = '1';
928
929
930         if((($contact['rel']) && ($contact['rel'] != CONTACT_IS_SHARING) && (! $contact['blocked'])) || ($owner['page-flags'] == PAGE_COMMUNITY)) {
931                 $postvars['data'] = $atom;
932                 $postvars['perm'] = 'rw';
933         }
934         else {
935                 $postvars['data'] = str_replace('<dfrn:comment-allow>1','<dfrn:comment-allow>0',$atom);
936                 $postvars['perm'] = 'r';
937         }
938
939         if($rino && $rino_allowed && (! $dissolve)) {
940                 $key = substr(random_string(),0,16);
941                 $data = bin2hex(aes_encrypt($postvars['data'],$key));
942                 $postvars['data'] = $data;
943                 logger('rino: sent key = ' . $key);     
944
945
946                 if($dfrn_version >= 2.1) {      
947                         if(($contact['duplex'] && strlen($contact['pubkey'])) || ($owner['page-flags'] == PAGE_COMMUNITY)) {
948                                 openssl_public_encrypt($key,$postvars['key'],$contact['pubkey']);
949                         }
950                         else {
951                                 openssl_private_encrypt($key,$postvars['key'],$contact['prvkey']);
952                         }
953                 }
954                 else {
955                         if(($contact['duplex'] && strlen($contact['prvkey'])) || ($owner['page-flags'] == PAGE_COMMUNITY)) {
956                                 openssl_private_encrypt($key,$postvars['key'],$contact['prvkey']);
957                         }
958                         else {
959                                 openssl_public_encrypt($key,$postvars['key'],$contact['pubkey']);
960                         }
961                 }
962
963                 logger('md5 rawkey ' . md5($postvars['key']));
964
965                 $postvars['key'] = bin2hex($postvars['key']);
966         }
967
968         logger('dfrn_deliver: ' . "SENDING: " . print_r($postvars,true), LOGGER_DATA);
969
970         $xml = post_url($contact['notify'],$postvars);
971
972         logger('dfrn_deliver: ' . "RECEIVED: " . $xml, LOGGER_DATA);
973
974         $curl_stat = $a->get_curl_code();
975         if((! $curl_stat) || (! strlen($xml)))
976                 return(-1); // timed out
977
978         if(strpos($xml,'<?xml') === false) {
979                 logger('dfrn_deliver: phase 2: no valid XML returned');
980                 logger('dfrn_deliver: phase 2: returned XML: ' . $xml, LOGGER_DATA);
981                 return 3;
982         }
983
984         $res = parse_xml_string($xml);
985
986         return $res->status; 
987 }
988
989
990 /**
991  *
992  * consume_feed - process atom feed and update anything/everything we might need to update
993  *
994  * $xml = the (atom) feed to consume - RSS isn't as fully supported but may work for simple feeds.
995  *
996  * $importer = the contact_record (joined to user_record) of the local user who owns this relationship.
997  *             It is this person's stuff that is going to be updated.
998  * $contact =  the person who is sending us stuff. If not set, we MAY be processing a "follow" activity
999  *             from an external network and MAY create an appropriate contact record. Otherwise, we MUST 
1000  *             have a contact record.
1001  * $hub = should we find a hub declation in the feed, pass it back to our calling process, who might (or 
1002  *        might not) try and subscribe to it.
1003  *
1004  */
1005
1006 function consume_feed($xml,$importer,&$contact, &$hub, $datedir = 0, $secure_feed = false) {
1007
1008         require_once('library/simplepie/simplepie.inc');
1009
1010         if(! strlen($xml)) {
1011                 logger('consume_feed: empty input');
1012                 return;
1013         }
1014                 
1015         $feed = new SimplePie();
1016         $feed->set_raw_data($xml);
1017         if($datedir)
1018                 $feed->enable_order_by_date(true);
1019         else
1020                 $feed->enable_order_by_date(false);
1021         $feed->init();
1022
1023         if($feed->error())
1024                 logger('consume_feed: Error parsing XML: ' . $feed->error());
1025
1026         $permalink = $feed->get_permalink();
1027
1028         // Check at the feed level for updated contact name and/or photo
1029
1030         $name_updated  = '';
1031         $new_name = '';
1032         $photo_timestamp = '';
1033         $photo_url = '';
1034         $birthday = '';
1035
1036         $hubs = $feed->get_links('hub');
1037
1038         if(count($hubs))
1039                 $hub = implode(',', $hubs);
1040
1041         $rawtags = $feed->get_feed_tags( NAMESPACE_DFRN, 'owner');
1042         if(! $rawtags)
1043                 $rawtags = $feed->get_feed_tags( SIMPLEPIE_NAMESPACE_ATOM_10, 'author');
1044         if($rawtags) {
1045                 $elems = $rawtags[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10];
1046                 if($elems['name'][0]['attribs'][NAMESPACE_DFRN]['updated']) {
1047                         $name_updated = $elems['name'][0]['attribs'][NAMESPACE_DFRN]['updated'];
1048                         $new_name = $elems['name'][0]['data'];
1049                 } 
1050                 if((x($elems,'link')) && ($elems['link'][0]['attribs']['']['rel'] === 'photo') && ($elems['link'][0]['attribs'][NAMESPACE_DFRN]['updated'])) {
1051                         $photo_timestamp = datetime_convert('UTC','UTC',$elems['link'][0]['attribs'][NAMESPACE_DFRN]['updated']);
1052                         $photo_url = $elems['link'][0]['attribs']['']['href'];
1053                 }
1054
1055                 if((x($rawtags[0]['child'], NAMESPACE_DFRN)) && (x($rawtags[0]['child'][NAMESPACE_DFRN],'birthday'))) {
1056                         $birthday = datetime_convert('UTC','UTC', $rawtags[0]['child'][NAMESPACE_DFRN]['birthday'][0]['data']);
1057                 }
1058         }
1059
1060         if((is_array($contact)) && ($photo_timestamp) && (strlen($photo_url)) && ($photo_timestamp > $contact['avatar-date'])) {
1061                 logger('consume_feed: Updating photo for ' . $contact['name']);
1062                 require_once("Photo.php");
1063                 $photo_failure = false;
1064                 $have_photo = false;
1065
1066                 $r = q("SELECT `resource-id` FROM `photo` WHERE `contact-id` = %d AND `uid` = %d LIMIT 1",
1067                         intval($contact['id']),
1068                         intval($contact['uid'])
1069                 );
1070                 if(count($r)) {
1071                         $resource_id = $r[0]['resource-id'];
1072                         $have_photo = true;
1073                 }
1074                 else {
1075                         $resource_id = photo_new_resource();
1076                 }
1077                         
1078                 $img_str = fetch_url($photo_url,true);
1079                 $img = new Photo($img_str);
1080                 if($img->is_valid()) {
1081                         if($have_photo) {
1082                                 q("DELETE FROM `photo` WHERE `resource-id` = '%s' AND `contact-id` = %d AND `uid` = %d",
1083                                         dbesc($resource_id),
1084                                         intval($contact['id']),
1085                                         intval($contact['uid'])
1086                                 );
1087                         }
1088                                 
1089                         $img->scaleImageSquare(175);
1090                                 
1091                         $hash = $resource_id;
1092                         $r = $img->store($contact['uid'], $contact['id'], $hash, basename($photo_url), 'Contact Photos', 4);
1093                                 
1094                         $img->scaleImage(80);
1095                         $r = $img->store($contact['uid'], $contact['id'], $hash, basename($photo_url), 'Contact Photos', 5);
1096
1097                         $img->scaleImage(48);
1098                         $r = $img->store($contact['uid'], $contact['id'], $hash, basename($photo_url), 'Contact Photos', 6);
1099
1100                         $a = get_app();
1101
1102                         q("UPDATE `contact` SET `avatar-date` = '%s', `photo` = '%s', `thumb` = '%s', `micro` = '%s'  
1103                                 WHERE `uid` = %d AND `id` = %d LIMIT 1",
1104                                 dbesc(datetime_convert()),
1105                                 dbesc($a->get_baseurl() . '/photo/' . $hash . '-4.jpg'),
1106                                 dbesc($a->get_baseurl() . '/photo/' . $hash . '-5.jpg'),
1107                                 dbesc($a->get_baseurl() . '/photo/' . $hash . '-6.jpg'),
1108                                 intval($contact['uid']),
1109                                 intval($contact['id'])
1110                         );
1111                 }
1112         }
1113
1114         if((is_array($contact)) && ($name_updated) && (strlen($new_name)) && ($name_updated > $contact['name-date'])) {
1115                 q("UPDATE `contact` SET `name` = '%s', `name-date` = '%s' WHERE `uid` = %d AND `id` = %d LIMIT 1",
1116                         dbesc(notags(trim($new_name))),
1117                         dbesc(datetime_convert()),
1118                         intval($contact['uid']),
1119                         intval($contact['id'])
1120                 );
1121         }
1122
1123         if(strlen($birthday)) {
1124                 if(substr($birthday,0,4) != $contact['bdyear']) {
1125                         logger('consume_feed: updating birthday: ' . $birthday);
1126
1127                         /**
1128                          *
1129                          * Add new birthday event for this person
1130                          *
1131                          * $bdtext is just a readable placeholder in case the event is shared
1132                          * with others. We will replace it during presentation to our $importer
1133                          * to contain a sparkle link and perhaps a photo. 
1134                          *
1135                          */
1136                          
1137                         $bdtext = t('Birthday:') . ' [url=' . $contact['url'] . ']' . $contact['name'] . '[/url]' ;
1138
1139
1140                         $r = q("INSERT INTO `event` (`uid`,`cid`,`created`,`edited`,`start`,`finish`,`desc`,`type`)
1141                                 VALUES ( %d, %d, '%s', '%s', '%s', '%s', '%s', '%s' ) ",
1142                                 intval($contact['uid']),
1143                                 intval($contact['id']),
1144                                 dbesc(datetime_convert()),
1145                                 dbesc(datetime_convert()),
1146                                 dbesc(datetime_convert('UTC','UTC', $birthday)),
1147                                 dbesc(datetime_convert('UTC','UTC', $birthday . ' + 1 day ')),
1148                                 dbesc($bdtext),
1149                                 dbesc('birthday')
1150                         );
1151                         
1152
1153                         // update bdyear
1154
1155                         q("UPDATE `contact` SET `bdyear` = '%s' WHERE `uid` = %d AND `id` = %d LIMIT 1",
1156                                 dbesc(substr($birthday,0,4)),
1157                                 intval($contact['uid']),
1158                                 intval($contact['id'])
1159                         );
1160
1161                         // This function is called twice without reloading the contact
1162                         // Make sure we only create one event. This is why &$contact 
1163                         // is a reference var in this function
1164
1165                         $contact['bdyear'] = substr($birthday,0,4);
1166                 }
1167
1168         }
1169
1170
1171         // process any deleted entries
1172
1173         $del_entries = $feed->get_feed_tags(NAMESPACE_TOMB, 'deleted-entry');
1174         if(is_array($del_entries) && count($del_entries)) {
1175                 foreach($del_entries as $dentry) {
1176                         $deleted = false;
1177                         if(isset($dentry['attribs']['']['ref'])) {
1178                                 $uri = $dentry['attribs']['']['ref'];
1179                                 $deleted = true;
1180                                 if(isset($dentry['attribs']['']['when'])) {
1181                                         $when = $dentry['attribs']['']['when'];
1182                                         $when = datetime_convert('UTC','UTC', $when, 'Y-m-d H:i:s');
1183                                 }
1184                                 else
1185                                         $when = datetime_convert('UTC','UTC','now','Y-m-d H:i:s');
1186                         }
1187                         if($deleted && is_array($contact)) {
1188                                 $r = q("SELECT * FROM `item` WHERE `uri` = '%s' AND `uid` = %d AND `contact-id` = %d LIMIT 1",
1189                                         dbesc($uri),
1190                                         intval($importer['uid']),
1191                                         intval($contact['id'])
1192                                 );
1193                                 if(count($r)) {
1194                                         $item = $r[0];
1195
1196                                         if(! $item['deleted'])
1197                                                 logger('consume_feed: deleting item ' . $item['id'] . ' uri=' . $item['uri'], LOGGER_DEBUG);
1198
1199                                         if($item['uri'] == $item['parent-uri']) {
1200                                                 $r = q("UPDATE `item` SET `deleted` = 1, `edited` = '%s', `changed` = '%s',
1201                                                         `body` = '', `title` = ''
1202                                                         WHERE `parent-uri` = '%s' AND `uid` = %d",
1203                                                         dbesc($when),
1204                                                         dbesc(datetime_convert()),
1205                                                         dbesc($item['uri']),
1206                                                         intval($importer['uid'])
1207                                                 );
1208                                         }
1209                                         else {
1210                                                 $r = q("UPDATE `item` SET `deleted` = 1, `edited` = '%s', `changed` = '%s',
1211                                                         `body` = '', `title` = '' 
1212                                                         WHERE `uri` = '%s' AND `uid` = %d LIMIT 1",
1213                                                         dbesc($when),
1214                                                         dbesc(datetime_convert()),
1215                                                         dbesc($uri),
1216                                                         intval($importer['uid'])
1217                                                 );
1218                                                 if($item['last-child']) {
1219                                                         // ensure that last-child is set in case the comment that had it just got wiped.
1220                                                         q("UPDATE `item` SET `last-child` = 0, `changed` = '%s' WHERE `parent-uri` = '%s' AND `uid` = %d ",
1221                                                                 dbesc(datetime_convert()),
1222                                                                 dbesc($item['parent-uri']),
1223                                                                 intval($item['uid'])
1224                                                         );
1225                                                         // who is the last child now? 
1226                                                         $r = q("SELECT `id` FROM `item` WHERE `parent-uri` = '%s' AND `type` != 'activity' AND `deleted` = 0 AND `uid` = %d 
1227                                                                 ORDER BY `created` DESC LIMIT 1",
1228                                                                         dbesc($item['parent-uri']),
1229                                                                         intval($importer['uid'])
1230                                                         );
1231                                                         if(count($r)) {
1232                                                                 q("UPDATE `item` SET `last-child` = 1 WHERE `id` = %d LIMIT 1",
1233                                                                         intval($r[0]['id'])
1234                                                                 );
1235                                                         }
1236                                                 }       
1237                                         }
1238                                 }       
1239                         }
1240                 }
1241         }
1242
1243         // Now process the feed
1244
1245         if($feed->get_item_quantity()) {                
1246
1247                 logger('consume_feed: feed item count = ' . $feed->get_item_quantity());
1248
1249         // in inverse date order
1250                 if ($datedir)
1251                         $items = array_reverse($feed->get_items());
1252                 else
1253                         $items = $feed->get_items();
1254
1255
1256                 foreach($items as $item) {
1257
1258                         $is_reply = false;              
1259                         $item_id = $item->get_id();
1260                         $rawthread = $item->get_item_tags( NAMESPACE_THREAD,'in-reply-to');
1261                         if(isset($rawthread[0]['attribs']['']['ref'])) {
1262                                 $is_reply = true;
1263                                 $parent_uri = $rawthread[0]['attribs']['']['ref'];
1264                         }
1265
1266                         if(($is_reply) && is_array($contact)) {
1267
1268                                 // Have we seen it? If not, import it.
1269         
1270                                 $item_id  = $item->get_id();
1271                                 $datarray = get_atom_elements($feed,$item);
1272
1273                                 if(! x($datarray,'author-name'))
1274                                         $datarray['author-name'] = $contact['name'];
1275                                 if(! x($datarray,'author-link'))
1276                                         $datarray['author-link'] = $contact['url'];
1277                                 if(! x($datarray,'author-avatar'))
1278                                         $datarray['author-avatar'] = $contact['thumb'];
1279
1280
1281                                 $r = q("SELECT `uid`, `last-child`, `edited`, `body` FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1",
1282                                         dbesc($item_id),
1283                                         intval($importer['uid'])
1284                                 );
1285
1286                                 // Update content if 'updated' changes
1287
1288                                 if(count($r)) {
1289                                         if((x($datarray,'edited') !== false) && (datetime_convert('UTC','UTC',$datarray['edited']) !== $r[0]['edited'])) {  
1290                                                 $r = q("UPDATE `item` SET `body` = '%s', `edited` = '%s' WHERE `uri` = '%s' AND `uid` = %d LIMIT 1",
1291                                                         dbesc($datarray['body']),
1292                                                         dbesc(datetime_convert('UTC','UTC',$datarray['edited'])),
1293                                                         dbesc($item_id),
1294                                                         intval($importer['uid'])
1295                                                 );
1296                                         }
1297
1298                                         // update last-child if it changes
1299
1300                                         $allow = $item->get_item_tags( NAMESPACE_DFRN, 'comment-allow');
1301                                         if(($allow) && ($allow[0]['data'] != $r[0]['last-child'])) {
1302                                                 $r = q("UPDATE `item` SET `last-child` = 0, `changed` = '%s' WHERE `parent-uri` = '%s' AND `uid` = %d",
1303                                                         dbesc(datetime_convert()),
1304                                                         dbesc($parent_uri),
1305                                                         intval($importer['uid'])
1306                                                 );
1307                                                 $r = q("UPDATE `item` SET `last-child` = %d , `changed` = '%s'  WHERE `uri` = '%s' AND `uid` = %d LIMIT 1",
1308                                                         intval($allow[0]['data']),
1309                                                         dbesc(datetime_convert()),
1310                                                         dbesc($item_id),
1311                                                         intval($importer['uid'])
1312                                                 );
1313                                         }
1314                                         continue;
1315                                 }
1316
1317                                 $force_parent = false;
1318                                 if($contact['network'] === 'stat') {
1319                                         $force_parent = true;
1320                                         if(strlen($datarray['title']))
1321                                                 unset($datarray['title']);
1322                                         $r = q("UPDATE `item` SET `last-child` = 0, `changed` = '%s' WHERE `parent-uri` = '%s' AND `uid` = %d",
1323                                                 dbesc(datetime_convert()),
1324                                                 dbesc($parent_uri),
1325                                                 intval($importer['uid'])
1326                                         );
1327                                         $datarray['last-child'] = 1;
1328                                 }
1329
1330                                 if(($contact['network'] === 'feed') || (! strlen($contact['notify']))) {
1331                                         // one way feed - no remote comment ability
1332                                         $datarray['last-child'] = 0;
1333                                 }
1334                                 $datarray['parent-uri'] = $parent_uri;
1335                                 $datarray['uid'] = $importer['uid'];
1336                                 $datarray['contact-id'] = $contact['id'];
1337                                 if((activity_match($datarray['verb'],ACTIVITY_LIKE)) || (activity_match($datarray['verb'],ACTIVITY_DISLIKE))) {
1338                                         $datarray['type'] = 'activity';
1339                                         $datarray['gravity'] = GRAVITY_LIKE;
1340                                 }
1341
1342                                 $r = item_store($datarray,$force_parent);
1343                                 continue;
1344                         }
1345
1346                         else {
1347
1348                                 // Head post of a conversation. Have we seen it? If not, import it.
1349
1350                                 $item_id  = $item->get_id();
1351
1352                                 $datarray = get_atom_elements($feed,$item);
1353
1354                                 if(is_array($contact)) {
1355                                         if(! x($datarray,'author-name'))
1356                                                 $datarray['author-name'] = $contact['name'];
1357                                         if(! x($datarray,'author-link'))
1358                                                 $datarray['author-link'] = $contact['url'];
1359                                         if(! x($datarray,'author-avatar'))
1360                                                 $datarray['author-avatar'] = $contact['thumb'];
1361                                 }
1362
1363                                 if((x($datarray,'object-type')) && ($datarray['object-type'] === ACTIVITY_OBJ_EVENT)) {
1364                                         $ev = bbtoevent($datarray['body']);
1365                                         if(x($ev,'desc') && x($ev,'start')) {
1366                                                 $ev['uid'] = $importer['uid'];
1367                                                 $ev['uri'] = $item_id;
1368                                                 $ev['edited'] = $datarray['edited'];
1369                                                 $ev['private'] = $datarray['private'];
1370
1371                                                 if(is_array($contact))
1372                                                         $ev['cid'] = $contact['id'];
1373                                                 $r = q("SELECT * FROM `event` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1",
1374                                                         dbesc($item_id),
1375                                                         intval($importer['uid'])
1376                                                 );
1377                                                 if(count($r))
1378                                                         $ev['id'] = $r[0]['id'];
1379                                                 $xyz = event_store($ev);
1380                                                 continue;
1381                                         }
1382                                 }
1383
1384                                 $r = q("SELECT `uid`, `last-child`, `edited`, `body` FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1",
1385                                         dbesc($item_id),
1386                                         intval($importer['uid'])
1387                                 );
1388
1389                                 // Update content if 'updated' changes
1390
1391                                 if(count($r)) {
1392                                         if((x($datarray,'edited') !== false) && (datetime_convert('UTC','UTC',$datarray['edited']) !== $r[0]['edited'])) {  
1393                                                 $r = q("UPDATE `item` SET `body` = '%s', `edited` = '%s' WHERE `uri` = '%s' AND `uid` = %d LIMIT 1",
1394                                                         dbesc($datarray['body']),
1395                                                         dbesc(datetime_convert('UTC','UTC',$datarray['edited'])),
1396                                                         dbesc($item_id),
1397                                                         intval($importer['uid'])
1398                                                 );
1399                                         }
1400
1401                                         // update last-child if it changes
1402
1403                                         $allow = $item->get_item_tags( NAMESPACE_DFRN, 'comment-allow');
1404                                         if($allow && $allow[0]['data'] != $r[0]['last-child']) {
1405                                                 $r = q("UPDATE `item` SET `last-child` = %d , `changed` = '%s' WHERE `uri` = '%s' AND `uid` = %d LIMIT 1",
1406                                                         intval($allow[0]['data']),
1407                                                         dbesc(datetime_convert()),
1408                                                         dbesc($item_id),
1409                                                         intval($importer['uid'])
1410                                                 );
1411                                         }
1412                                         continue;
1413                                 }
1414
1415                                 if(activity_match($datarray['verb'],ACTIVITY_FOLLOW)) {
1416                                         logger('consume-feed: New follower');
1417                                         new_follower($importer,$contact,$datarray,$item);
1418                                         return;
1419                                 }
1420                                 if(activity_match($datarray['verb'],ACTIVITY_UNFOLLOW))  {
1421                                         lose_follower($importer,$contact,$datarray,$item);
1422                                         return;
1423                                 }
1424                                 if(! is_array($contact))
1425                                         return;
1426
1427                                 if($contact['network'] === 'stat' || stristr($permalink,'twitter.com')) {
1428                                         if(strlen($datarray['title']))
1429                                                 unset($datarray['title']);
1430                                         $datarray['last-child'] = 1;
1431                                 }
1432
1433                                 if(($contact['network'] === 'feed') || (! strlen($contact['notify']))) {
1434                                         // one way feed - no remote comment ability
1435                                         $datarray['last-child'] = 0;
1436                                 }
1437
1438                                 // This is my contact on another system, but it's really me.
1439                                 // Turn this into a wall post.
1440
1441                                 if($contact['remote_self'])
1442                                         $datarray['wall'] = 1;
1443
1444                                 $datarray['parent-uri'] = $item_id;
1445                                 $datarray['uid'] = $importer['uid'];
1446                                 $datarray['contact-id'] = $contact['id'];
1447                                 $r = item_store($datarray);
1448                                 continue;
1449
1450                         }
1451                 }
1452         }
1453 }
1454
1455 function new_follower($importer,$contact,$datarray,$item) {
1456         $url = notags(trim($datarray['author-link']));
1457         $name = notags(trim($datarray['author-name']));
1458         $photo = notags(trim($datarray['author-avatar']));
1459
1460         $rawtag = $item->get_item_tags(NAMESPACE_ACTIVITY,'actor');
1461         if($rawtag && $rawtag[0]['child'][NAMESPACE_POCO]['preferredUsername'][0]['data'])
1462                 $nick = $rawtag[0]['child'][NAMESPACE_POCO]['preferredUsername'][0]['data'];
1463
1464         if(is_array($contact)) {
1465                 if($contact['network'] == 'stat' && $contact['rel'] == CONTACT_IS_SHARING) {
1466                         $r = q("UPDATE `contact` SET `rel` = %d WHERE `id` = %d AND `uid` = %d LIMIT 1",
1467                                 intval(CONTACT_IS_FRIEND),
1468                                 intval($contact['id']),
1469                                 intval($importer['uid'])
1470                         );
1471                 }
1472
1473                 // send email notification to owner?
1474         }
1475         else {
1476         
1477                 // create contact record
1478
1479                 $r = q("INSERT INTO `contact` ( `uid`, `created`, `url`, `name`, `nick`, `photo`, `network`, `rel`, 
1480                         `blocked`, `readonly`, `pending`, `writable` )
1481                         VALUES ( %d, '%s', '%s', '%s', '%s', '%s', '%s', %d, 0, 0, 1, 1 ) ",
1482                         intval($importer['uid']),
1483                         dbesc(datetime_convert()),
1484                         dbesc($url),
1485                         dbesc($name),
1486                         dbesc($nick),
1487                         dbesc($photo),
1488                         dbesc('stat'),
1489                         intval(CONTACT_IS_FOLLOWER)
1490                 );
1491                 $r = q("SELECT `id` FROM `contact` WHERE `uid` = %d AND `url` = '%s' AND `pending` = 1 AND `rel` = %d LIMIT 1",
1492                                 intval($importer['uid']),
1493                                 dbesc($url),
1494                                 intval(CONTACT_IS_FOLLOWER)
1495                 );
1496                 if(count($r))
1497                                 $contact_record = $r[0];
1498
1499                 // create notification  
1500                 $hash = random_string();
1501
1502                 if(is_array($contact_record)) {
1503                         $ret = q("INSERT INTO `intro` ( `uid`, `contact-id`, `blocked`, `knowyou`, `hash`, `datetime`)
1504                                 VALUES ( %d, %d, 0, 0, '%s', '%s' )",
1505                                 intval($importer['uid']),
1506                                 intval($contact_record['id']),
1507                                 dbesc($hash),
1508                                 dbesc(datetime_convert())
1509                         );
1510                 }
1511                 $r = q("SELECT * FROM `user` WHERE `uid` = %d LIMIT 1",
1512                         intval($importer['uid'])
1513                 );
1514                 $a = get_app();
1515                 if(count($r)) {
1516                         if(($r[0]['notify-flags'] & NOTIFY_INTRO) && ($r[0]['page-flags'] == PAGE_NORMAL)) {
1517                                 $email_tpl = get_intltext_template('follow_notify_eml.tpl');
1518                                 $email = replace_macros($email_tpl, array(
1519                                         '$requestor' => ((strlen($name)) ? $name : t('[Name Withheld]')),
1520                                         '$url' => $url,
1521                                         '$myname' => $r[0]['username'],
1522                                         '$siteurl' => $a->get_baseurl(),
1523                                         '$sitename' => $a->config['sitename']
1524                                 ));
1525                                 $res = mail($r[0]['email'], 
1526                                         t("You have a new follower at ") . $a->config['sitename'],
1527                                         $email,
1528                                         'From: ' . t('Administrator') . '@' . $_SERVER['SERVER_NAME'] . "\n"
1529                                         . 'Content-type: text/plain; charset=UTF-8' . "\n"
1530                                         . 'Content-transfer-encoding: 8bit' );
1531                         
1532                         }
1533                 }
1534         }
1535 }
1536
1537 function lose_follower($importer,$contact,$datarray,$item) {
1538
1539         if(($contact['rel'] == CONTACT_IS_FRIEND) || ($contact['rel'] == CONTACT_IS_SHARING)) {
1540                 q("UPDATE `contact` SET `rel` = %d WHERE `id` = %d LIMIT 1",
1541                         intval(CONTACT_IS_SHARING),
1542                         intval($contact['id'])
1543                 );
1544         }
1545         else {
1546                 contact_remove($contact['id']);
1547         }
1548 }
1549
1550
1551 function subscribe_to_hub($url,$importer,$contact) {
1552
1553         if(is_array($importer)) {
1554                 $r = q("SELECT `nickname` FROM `user` WHERE `uid` = %d LIMIT 1",
1555                         intval($importer['uid'])
1556                 );
1557         }
1558         if(! count($r))
1559                 return;
1560
1561         $push_url = get_config('system','url') . '/pubsub/' . $r[0]['nickname'] . '/' . $contact['id'];
1562
1563         // Use a single verify token, even if multiple hubs
1564
1565         $verify_token = ((strlen($contact['hub-verify'])) ? $contact['hub-verify'] : random_string());
1566
1567         $params= 'hub.mode=subscribe&hub.callback=' . urlencode($push_url) . '&hub.topic=' . urlencode($contact['poll']) . '&hub.verify=async&hub.verify_token=' . $verify_token;
1568
1569         logger('subscribe_to_hub: subscribing ' . $contact['name'] . ' to hub ' . $url . ' with verifier ' . $verify_token);
1570
1571         if(! strlen($contact['hub-verify'])) {
1572                 $r = q("UPDATE `contact` SET `hub-verify` = '%s' WHERE `id` = %d LIMIT 1",
1573                         dbesc($verify_token),
1574                         intval($contact['id'])
1575                 );
1576         }
1577
1578         post_url($url,$params);                 
1579         return;
1580
1581 }
1582
1583
1584 function atom_author($tag,$name,$uri,$h,$w,$photo) {
1585         $o = '';
1586         if(! $tag)
1587                 return $o;
1588         $name = xmlify($name);
1589         $uri = xmlify($uri);
1590         $h = intval($h);
1591         $w = intval($w);
1592         $photo = xmlify($photo);
1593
1594
1595         $o .= "<$tag>\r\n";
1596         $o .= "<name>$name</name>\r\n";
1597         $o .= "<uri>$uri</uri>\r\n";
1598         $o .= '<link rel="photo"  type="image/jpeg" media:width="' . $w . '" media:height="' . $h . '" href="' . $photo . '" />' . "\r\n";
1599         $o .= '<link rel="avatar" type="image/jpeg" media:width="' . $w . '" media:height="' . $h . '" href="' . $photo . '" />' . "\r\n";
1600
1601         call_hooks('atom_author', $o);
1602
1603         $o .= "</$tag>\r\n";
1604         return $o;
1605 }
1606
1607 function atom_entry($item,$type,$author,$owner,$comment = false) {
1608
1609         $a = get_app();
1610
1611         if($item['deleted'])
1612                 return '<at:deleted-entry ref="' . xmlify($item['uri']) . '" when="' . xmlify(datetime_convert('UTC','UTC',$item['edited'] . '+00:00',ATOM_TIME)) . '" />' . "\r\n";
1613
1614
1615         if($item['allow_cid'] || $item['allow_gid'] || $item['deny_cid'] || $item['deny_gid'])
1616                 $body = fix_private_photos($item['body'],$owner['uid']);
1617         else
1618                 $body = $item['body'];
1619
1620
1621         $o = "\r\n\r\n<entry>\r\n";
1622
1623         if(is_array($author))
1624                 $o .= atom_author('author',$author['name'],$author['url'],80,80,$author['thumb']);
1625         else
1626                 $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']));
1627         if(strlen($item['owner-name']))
1628                 $o .= atom_author('dfrn:owner',$item['owner-name'],$item['owner-link'],80,80,$item['owner-avatar']);
1629
1630         if($item['parent'] != $item['id'])
1631                 $o .= '<thr:in-reply-to ref="' . xmlify($item['parent-uri']) . '" type="text/html" href="' .  xmlify($a->get_baseurl() . '/display/' . $owner['nickname'] . '/' . $item['id']) . '" />' . "\r\n";
1632
1633         $o .= '<id>' . xmlify($item['uri']) . '</id>' . "\r\n";
1634         $o .= '<title>' . xmlify($item['title']) . '</title>' . "\r\n";
1635         $o .= '<published>' . xmlify(datetime_convert('UTC','UTC',$item['created'] . '+00:00',ATOM_TIME)) . '</published>' . "\r\n";
1636         $o .= '<updated>' . xmlify(datetime_convert('UTC','UTC',$item['edited'] . '+00:00',ATOM_TIME)) . '</updated>' . "\r\n";
1637         $o .= '<dfrn:env>' . base64url_encode($body, true) . '</dfrn:env>' . "\r\n";
1638         $o .= '<content type="' . $type . '" >' . xmlify(($type === 'html') ? bbcode($body) : $body) . '</content>' . "\r\n";
1639         $o .= '<link rel="alternate" type="text/html" href="' . xmlify($a->get_baseurl() . '/display/' . $owner['nickname'] . '/' . $item['id']) . '" />' . "\r\n";
1640         if($comment)
1641                 $o .= '<dfrn:comment-allow>' . intval($item['last-child']) . '</dfrn:comment-allow>' . "\r\n";
1642
1643         if($item['location']) {
1644                 $o .= '<dfrn:location>' . xmlify($item['location']) . '</dfrn:location>' . "\r\n";
1645                 $o .= '<poco:address><poco:formatted>' . xmlify($item['location']) . '</poco:formatted></poco:address>' . "\r\n";
1646         }
1647
1648         if($item['coord'])
1649                 $o .= '<georss:point>' . xmlify($item['coord']) . '</georss:point>' . "\r\n";
1650
1651         if(($item['private']) || strlen($item['allow_cid']) || strlen($item['allow_gid']) || strlen($item['deny_cid']) || strlen($item['deny_gid']))
1652                 $o .= '<dfrn:private>1</dfrn:private>' . "\r\n";
1653
1654         if($item['extid'])
1655                 $o .= '<dfrn:extid>' . $item['extid'] . '</dfrn:extid>' . "\r\n";
1656
1657         if($item['app'])
1658                 $o .= '<statusnet:notice_info local_id="' . $item['id'] . '" source="' . $item['app'] . '" ></statusnet:notice_info>';
1659         $verb = construct_verb($item);
1660         $o .= '<as:verb>' . xmlify($verb) . '</as:verb>' . "\r\n";
1661         $actobj = construct_activity_object($item);
1662         if(strlen($actobj))
1663                 $o .= $actobj;
1664         $actarg = construct_activity_target($item);
1665         if(strlen($actarg))
1666                 $o .= $actarg;
1667
1668         $tags = item_getfeedtags($item);
1669         if(count($tags)) {
1670                 foreach($tags as $t) {
1671                         $o .= '<category scheme="X-DFRN:' . xmlify($t[0]) . ':' . xmlify($t[1]) . '" term="' . xmlify($t[2]) . '" />' . "\r\n";
1672                 }
1673         }
1674
1675         $o .= item_getfeedattach($item);
1676
1677         $mentioned = get_mentions($item);
1678         if($mentioned)
1679                 $o .= $mentioned;
1680         
1681         call_hooks('atom_entry', $o);
1682
1683         $o .= '</entry>' . "\r\n";
1684         
1685         return $o;
1686 }
1687
1688 function fix_private_photos($s,$uid) {
1689         $a = get_app();
1690         logger('fix_private_photos');
1691
1692         if(preg_match("/\[img\](.*?)\[\/img\]/is",$s,$matches)) {
1693                 $image = $matches[1];
1694                 logger('fix_private_photos: found photo ' . $image);
1695                 if(stristr($image ,$a->get_baseurl() . '/photo/')) {
1696                         $i = basename($image);
1697                         $i = str_replace('.jpg','',$i);
1698                         $x = strpos($i,'-');
1699                         if($x) {
1700                                 $res = substr($i,$x+1);
1701                                 $i = substr($i,0,$x);
1702                                 $r = q("SELECT * FROM `photo` WHERE `resource-id` = '%s' AND `scale` = %d AND `uid` = %d",
1703                                         dbesc($i),
1704                                         intval($res),
1705                                         intval($uid)
1706                                 );
1707                                 if(count($r)) {
1708                                         logger('replacing photo');
1709                                         $s = str_replace($image, 'data:image/jpg;base64,' . base64_encode($r[0]['data']), $s);
1710                                 }
1711                         }
1712                         logger('fix_private_photos: replaced: ' . $s, LOGGER_DATA);
1713                 }       
1714         }
1715         return($s);
1716 }
1717
1718
1719
1720 function item_getfeedtags($item) {
1721         $ret = array();
1722         $matches = false;
1723         $cnt = preg_match_all('|\#\[url\=(.*?)\](.*?)\[\/url\]|',$item['tag'],$matches);
1724         if($cnt) {
1725                 for($x = 0; $x < count($matches); $x ++) {
1726                         if($matches[1][$x])
1727                                 $ret[] = array('#',$matches[1][$x], $matches[2][$x]);
1728                 }
1729         }
1730         $matches = false; 
1731         $cnt = preg_match_all('|\@\[url\=(.*?)\](.*?)\[\/url\]|',$item['tag'],$matches);
1732         if($cnt) {
1733                 for($x = 0; $x < count($matches); $x ++) {
1734                         if($matches[1][$x])
1735                                 $ret[] = array('#',$matches[1][$x], $matches[2][$x]);
1736                 }
1737         } 
1738         return $ret;
1739 }
1740
1741 function item_getfeedattach($item) {
1742         $ret = '';
1743         $arr = explode(',',$item['attach']);
1744         if(count($arr)) {
1745                 foreach($arr as $r) {
1746                         $matches = false;
1747                         $cnt = preg_match('|\[attach\]href=\"(.*?)\" length=\"(.*?)\" type=\"(.*?)\" title=\"(.*?)\"\[\/attach\]|',$r,$matches);
1748                         if($cnt) {
1749                                 $ret .= '<link rel="enclosure" href="' . xmlify($matches[1]) . '" type="' . xmlify($matches[3]) . '" ';
1750                                 if(intval($matches[2]))
1751                                         $ret .= 'length="' . intval($matches[2]) . '" ';
1752                                 if($matches[4] !== ' ')
1753                                         $ret .= 'title="' . xmlify(trim($matches[4])) . '" ';
1754                                 $ret .= ' />' . "\r\n";
1755                         }
1756                 }
1757         }
1758         return $ret;
1759 }
1760
1761
1762         
1763 function item_expire($uid,$days) {
1764
1765         if((! $uid) || (! $days))
1766                 return;
1767
1768         $r = q("SELECT * FROM `item` 
1769                 WHERE `uid` = %d 
1770                 AND `created` < UTC_TIMESTAMP() - INTERVAL %d DAY 
1771                 AND `id` = `parent` 
1772                 AND `deleted` = 0",
1773                 intval($uid),
1774                 intval($days)
1775         );
1776
1777         if(! count($r))
1778                 return;
1779  
1780         logger('expire: # items=' . count($r) );
1781
1782         foreach($r as $item) {
1783
1784                 // Only expire posts, not photos and photo comments
1785
1786                 if(strlen($item['resource-id']))
1787                         continue;
1788
1789                 $r = q("UPDATE `item` SET `deleted` = 1, `edited` = '%s', `changed` = '%s' WHERE `id` = %d LIMIT 1",
1790                         dbesc(datetime_convert()),
1791                         dbesc(datetime_convert()),
1792                         intval($item['id'])
1793                 );
1794
1795                 // kill the kids
1796
1797                 $r = q("UPDATE `item` SET `deleted` = 1, `edited` = '%s', `changed` = '%s' WHERE `parent-uri` = '%s' AND `uid` = %d ",
1798                         dbesc(datetime_convert()),
1799                         dbesc(datetime_convert()),
1800                         dbesc($item['parent-uri']),
1801                         intval($item['uid'])
1802                 );
1803
1804         }
1805
1806         proc_run('php',"include/notifier.php","expire","$uid");
1807
1808 }
1809
1810
1811 function drop_items($items) {
1812         $uid = 0;
1813
1814         if(count($items)) {
1815                 foreach($items as $item) {
1816                         $owner = drop_item($item,false);
1817                         if($owner && ! $uid)
1818                                 $uid = $owner;
1819                 }
1820         }
1821
1822         // multiple threads may have been deleted, send an expire notification
1823
1824         if($uid)
1825                 proc_run('php',"include/notifier.php","expire","$uid");
1826 }
1827
1828
1829 function drop_item($id,$interactive = true) {
1830
1831         $a = get_app();
1832
1833         // locate item to be deleted
1834
1835         $r = q("SELECT * FROM `item` WHERE `id` = %d LIMIT 1",
1836                 intval($id)
1837         );
1838
1839         if(! count($r)) {
1840                 if(! $interactive)
1841                         return 0;
1842                 notice( t('Item not found.') . EOL);
1843                 goaway($a->get_baseurl() . '/' . $_SESSION['return_url']);
1844         }
1845
1846         $item = $r[0];
1847
1848         $owner = $item['uid'];
1849
1850         // check if logged in user is either the author or owner of this item
1851
1852         if((local_user() == $item['uid']) || (remote_user() == $item['contact-id'])) {
1853
1854                 // delete the item
1855
1856                 $r = q("UPDATE `item` SET `deleted` = 1, `body` = '', `edited` = '%s', `changed` = '%s' WHERE `id` = %d LIMIT 1",
1857                         dbesc(datetime_convert()),
1858                         dbesc(datetime_convert()),
1859                         intval($item['id'])
1860                 );
1861
1862                 // If item is a link to a photo resource, nuke all the associated photos 
1863                 // (visitors will not have photo resources)
1864                 // This only applies to photos uploaded from the photos page. Photos inserted into a post do not
1865                 // generate a resource-id and therefore aren't intimately linked to the item. 
1866
1867                 if(strlen($item['resource-id'])) {
1868                         q("DELETE FROM `photo` WHERE `resource-id` = '%s' AND `uid` = %d ",
1869                                 dbesc($item['resource-id']),
1870                                 intval($item['uid'])
1871                         );
1872                         // ignore the result
1873                 }
1874
1875                 // If item is a link to an event, nuke the event record.
1876
1877                 if(intval($item['event-id'])) {
1878                         q("DELETE FROM `event` WHERE `id` = %d AND `uid` = %d LIMIT 1",
1879                                 intval($item['event-id']),
1880                                 intval($item['uid'])
1881                         );
1882                         // ignore the result
1883                 }
1884
1885
1886                 // If it's the parent of a comment thread, kill all the kids
1887
1888                 if($item['uri'] == $item['parent-uri']) {
1889                         $r = q("UPDATE `item` SET `deleted` = 1, `edited` = '%s', `changed` = '%s', `body` = '' 
1890                                 WHERE `parent-uri` = '%s' AND `uid` = %d ",
1891                                 dbesc(datetime_convert()),
1892                                 dbesc(datetime_convert()),
1893                                 dbesc($item['parent-uri']),
1894                                 intval($item['uid'])
1895                         );
1896                         // ignore the result
1897                 }
1898                 else {
1899                         // ensure that last-child is set in case the comment that had it just got wiped.
1900                         q("UPDATE `item` SET `last-child` = 0, `changed` = '%s' WHERE `parent-uri` = '%s' AND `uid` = %d ",
1901                                 dbesc(datetime_convert()),
1902                                 dbesc($item['parent-uri']),
1903                                 intval($item['uid'])
1904                         );
1905                         // who is the last child now? 
1906                         $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",
1907                                 dbesc($item['parent-uri']),
1908                                 intval($item['uid'])
1909                         );
1910                         if(count($r)) {
1911                                 q("UPDATE `item` SET `last-child` = 1 WHERE `id` = %d LIMIT 1",
1912                                         intval($r[0]['id'])
1913                                 );
1914                         }       
1915                 }
1916                 $drop_id = intval($item['id']);
1917                         
1918                 // send the notification upstream/downstream as the case may be
1919
1920                 if(! $interactive)
1921                         return $owner;
1922
1923                 proc_run('php',"include/notifier.php","drop","$drop_id");
1924                 goaway($a->get_baseurl() . '/' . $_SESSION['return_url']);
1925                 //NOTREACHED
1926         }
1927         else {
1928                 if(! $interactive)
1929                         return 0;
1930                 notice( t('Permission denied.') . EOL);
1931                 goaway($a->get_baseurl() . '/' . $_SESSION['return_url']);
1932                 //NOTREACHED
1933         }
1934         
1935 }