source: OWR/Model/Streams.php @ 47c42d

Revision 47c42d, 56.6 KB checked in by pierre-alain <pierre-alain@…>, 4 years ago (diff)

adding support for plugin calls

  • Property mode set to 100644
Line 
1<?php
2/**
3 * Model for 'streams' object
4 *
5 * PHP 5
6 *
7 * OWR - OpenWebReader
8 *
9 * Copyright (c) 2009, Pierre-Alain Mignot
10 *
11 * Home page: http://openwebreader.org
12 *
13 * E-Mail: contact@openwebreader.org
14 *
15 * All Rights Reserved
16 *
17 * This program is free software; you can redistribute it and/or modify
18 * it under the terms of the GNU General Public License as published by
19 * the Free Software Foundation; either version 2 of the License, or
20 * (at your option) any later version.
21 *
22 * This program is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
25 * GNU General Public License for more details.
26 *
27 * You should have received a copy of the GNU General Public License
28 * along with this program; if not, write to the Free Software
29 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
30 *
31 * @author Pierre-Alain Mignot <contact@openwebreader.org>
32 * @copyright Copyright (c) 2009, Pierre-Alain Mignot
33 * @license http://www.gnu.org/copyleft/gpl.html
34 * @package OWR
35 * @subpackage Model
36 */
37namespace OWR\Model;
38use OWR\Model,
39    OWR\Request,
40    OWR\Exception,
41    OWR\DAO,
42    OWR\Stream\Reader as StreamReader,
43    OWR\Stream\Parser as StreamParser,
44    OWR\Logs,
45    OWR\cURLWrapper,
46    OWR\Cron,
47    OWR\User,
48    OWR\OPML\Parser as OPMLParser,
49    OWR\Upload,
50    OWR\Config,
51    OWR\Threads,
52    OWR\Plugins;
53/**
54 * This class is used to add/edit/delete stream and his related tables
55 * @package OWR
56 * @subpackage Model
57 * @uses OWR\Model extends the base class
58 * @uses OWR\Request the request
59 * @uses OWR\Exception the exception handler
60 * @uses OWR\DAO the DAO
61 * @uses OWR\Streams\Reader the stream reader
62 * @uses OWR\Stream\Parser the stream parser
63 * @uses OWR\Logs the logs object
64 * @uses OWR\Cron add/modify crontab
65 * @uses OWR\Plugins Plugins manager
66 * @subpackage Model
67 */
68class Streams extends Model
69{
70    /**
71     * Adds/Edits a stream
72     *
73     * @access public
74     * @param mixed $request the Request instance
75     * @return $this
76     */
77    public function edit(Request $request)
78    {
79        Plugins::pretrigger($request);
80        if(empty($request->url))
81        {
82            $request->setResponse(new Response(array(
83                'do'        => 'error',
84                'error'     => 'Missing url',
85                'status'    => Exception::E_OWR_BAD_REQUEST
86            )));
87            return $this;
88        }
89
90        parent::getCachedModel('streams_groups')->checkGroupById($request); // fill gid and gname
91
92        $request->url = html_entity_decode($request->url, ENT_COMPAT, 'UTF-8');
93        $hash = md5($request->url);
94
95        $ids = array();
96        $id = (int) $request->id;
97        if(!empty($id))
98        {
99            if('streams' !== DAO::getType($id))
100            {
101                $id = 0;
102            }
103        }
104
105        $cron = Cron::iGet();
106        $streams = $this->_dao->get(array('hash' => $hash));
107        if(!empty($streams))
108        { // stream exists
109            unset($hash);
110            $request->id = (int) $streams->id;
111            $request->ttl = $streams->ttl;
112
113            $streams_relation = DAO::getCachedDAO('streams_relations')->get(array('rssid' => $streams->id));
114            if($streams_relation)
115            { // user already have this stream !
116                $request->setResponse(new Response);
117                return $this;
118            }
119
120            $streams_relation = DAO::getDAO('streams_relations');
121            $streams_relation->rssid = $streams->id;
122            $streams_relation->gid = $request->gid;
123
124            $this->_db->beginTransaction();
125            try
126            {
127                $streams_relation->save(); // save
128            }
129            catch(Exception $e)
130            {
131                $this->_db->rollback();
132                throw new Exception($e->getContent(), $e->getCode());
133            }
134            unset($streams_relation);
135
136            if(!empty($id) && $id !== $streams->id)
137            { // user changed the url of one of his own stream
138                $r = clone($request);
139                $r->id = $id;
140                $this->delete($r);
141                $response = $r->getResponse();
142                if('error' === $response->getNext())
143                    Logs::iGet()->log($response->getError(), $response->getStatus());
144                unset($r);
145            }
146
147            $news = DAO::getCachedDAO('news')->get(array('rssid' => $streams->id), 'id', 'pubDate DESC, lastupd DESC');
148            if(!empty($news))
149            {
150                $r = clone($request);
151                $r->current = true; // we add a news_relations only for the current user
152                $r->streamid = $streams->id;
153                if(is_array($news))
154                {
155                    $model = parent::getCachedModel('news');
156
157                    foreach($news as $new)
158                    {
159                        $r->id = $new->id;
160                        $model->insertNewsRelations($r);
161                        $response = $r->getResponse();
162                        if('error' === $response->getNext())
163                            Logs::iGet()->log($response->getError(), $response->getStatus());
164                        else $ids[] = $r->id;
165                    }
166                }
167                else
168                {
169                    $r->id = $news->id;
170                    parent::getCachedModel('news')->insertNewsRelations($r);
171                    $response = $r->getResponse();
172                    if('error' === $response->getNext())
173                        Logs::iGet()->log($response->getError(), $response->getStatus());
174                    else $ids[] = $r->id;
175                }
176            }
177            unset($news, $r);
178
179            if(empty($request->name))
180            {
181                $reader = new StreamReader(array(
182                    'channel' => unserialize(DAO::getCachedDAO('streams_contents')->get(array('rssid'=>$streams->id), 'contents')->contents)
183                ));
184                $request->name = $reader->get('title');
185                unset($reader);
186            }
187
188            $streams_name = DAO::getDAO('streams_relations_name');
189            $streams_name->name = $request->name;
190            $streams_name->rssid = $streams->id;
191            unset($streams);
192
193            try
194            {
195                $streams_name->save(); // save title of the stream for the user
196            }
197            catch(Exception $e)
198            {
199                $this->_db->rollback();
200                throw new Exception($e->getContent(), $e->getCode());
201            }
202            $this->_db->commit();
203
204            unset($streams_name);
205
206            if(empty($request->escape) && empty($request->escapeNews))
207            {
208                $request->setResponse(new Response(array(
209                    'do'        => 'ok',
210                    'tpl'       => 'stream',
211                    'datas'     => array('id'=>$request->id, 'gid'=>$request->gid, 'name'=>$request->gname, 'ids' => $ids),
212                    'status'    => 201
213                )));
214            }
215            else
216            {
217                $request->setResponse(new Response(array(
218                    'datas' => array('id' => $request->id, 'ids' => $ids)
219                )));
220            }
221
222            $request->new = true;
223
224            return $this;
225        }
226
227        if('' === ($stream = $this->_parse($request->url)))
228        {
229            // should NOT arrive
230            // if we come here, that means that cURLWrapper got a 304 http response
231            // but the stream does not exists in DB ? surely a bug or DB not up to date
232            // TODO : fix it by forcing cURLWrapper to fetch datas
233            $request->setResponse(new Response(array(
234                'do'        => 'error',
235                'error'     => 'Can\'t parse the stream'
236            )));
237            return $this;
238        }
239        elseif(false === $stream)
240        {
241            // no stream(s) detected
242            // try auto-discovery
243            $index = cURLWrapper::get($request->url, array(), false, true);
244            $nb = $nbErr = 0;
245            if(!empty($index))
246            {
247                if($hrefs = $this->_extractHREF(array(
248                        'rel'=>array('subscriptions', 'alternate', 'related'),
249                        'type'=>array(
250                            'application/rss+xml',
251                            'application/atom+xml',
252                            'application/rdf+xml'
253                        )
254                    ), $index))
255                { // streams
256                    $nb += count($hrefs);
257                    $r = clone($request);
258
259                    foreach($hrefs as $href)
260                    {
261                        if('/' === mb_substr($href, 0, 1, 'UTF-8'))
262                            $href = $request->url . $href;
263
264                        try
265                        {
266                            $r->url = $href;
267                            $this->edit($r);
268                            $ids[] = $r->id;
269                        }
270                        catch(Exception $e)
271                        {
272                            ++$nbErr;
273                            Logs::iGet()->log($e->getContent(), $e->getCode());
274                        }
275                    }
276                }
277                unset($r, $hrefs, $href);
278
279                if($hrefs = $this->_extractHREF(array(
280                        'rel'=>array('subscriptions', 'alternate', 'related'),
281                        'type'=>array('text/x-opml')), $index))
282                { // opml
283                    $nb += count($hrefs);
284                    $r = clone($request);
285
286                    foreach($hrefs as $href)
287                    {
288                        if('/' === mb_substr($href, 0, 1, 'UTF-8'))
289                            $href = $request->url . $href;
290
291                        try
292                        {
293                            $r->url = $href;
294                            $this->editOPML($r);
295                            $ids[] = $r->ids;
296                            $ids[] = $r->id;
297                        }
298                        catch(Exception $e)
299                        {
300                            ++$nbErr;
301                            Logs::iGet()->log($e->getContent(), $e->getCode());
302                        }
303                    }
304                }
305            }
306            unset($index, $hrefs, $href, $r);
307            if($nb === $nbErr)
308            {
309                $request->setResponse(new Response(array(
310                    'do'        => 'error',
311                    'error'     => 'Can\'t parse the stream'
312                )));
313                return $this;
314            }
315            else
316            {
317                $request->new = true;
318                if(empty($request->escape) && empty($request->escapeNews))
319                {
320                    $request->setResponse(new Response(array(
321                        'do'        => 'redirect',
322                        'status'    => 201,
323                        'datas'     => array('ids' => $ids)
324                    )));
325                }
326                return $this;
327            }
328        }
329
330        $ttl = $stream->get('ttl');
331        $streams = DAO::getDAO('streams');
332        $streams->url = $request->url;
333        $streams->hash = $hash;
334        $streams->lastupd = (int) $request->begintime;
335        $streams->ttl = $ttl;
336        $streams->status = 0;
337        $streams->id = $id ?: null;
338        unset($hash);
339
340        $this->_db->beginTransaction();
341        try
342        {
343            $request->id = $streams->save(); // save stream
344        }
345        catch(Exception $e)
346        {
347            $this->_db->rollback();
348            throw new Exception($e->getContent(), $e->getCode());
349        }
350
351        unset($streams);
352
353        $streams_contents = DAO::getDAO('streams_contents');
354        $streams_contents->src = $stream->get('src');
355        $streams_contents->rssid = $request->id;
356        $streams_contents->contents = serialize($stream->get('channel'));
357
358        try
359        {
360            $streams_contents->save(); // save stream parsed contents and src
361        }
362        catch(Exception $e)
363        {
364            $this->_db->rollback();
365            throw new Exception($e->getContent(), $e->getCode());
366        }
367
368        $this->_db->commit();
369        unset($streams_contents);
370        // ok stream is fully saved, we try to find the favicon, in background
371        Threads::iGet()->add(array('do' => 'managefavicons', 'id' => $request->id));
372
373        // save user relations
374        $streams_relation = DAO::getDAO('streams_relations');
375        $streams_relation->rssid = $request->id;
376        $streams_relation->gid = $request->gid;
377
378        $this->_db->beginTransaction();
379        try
380        {
381            $streams_relation->save(); // save
382        }
383        catch(Exception $e)
384        {
385            $this->_db->rollback();
386            throw new Exception($e->getContent(), $e->getCode());
387        }
388
389        unset($streams_relation);
390
391        if(empty($request->name))
392        { // user does not fill the title field, we try to get it from stream
393            $request->name = $stream->get('title');
394        }
395        $streams_name = DAO::getDAO('streams_relations_name');
396        $streams_name->name = $request->name;
397        $streams_name->rssid = $request->id;
398
399        try
400        {
401            $streams_name->save(); // save title of the stream for the user
402        }
403        catch(Exception $e)
404        {
405            $this->_db->rollback();
406            throw new Exception($e->getContent(), $e->getCode());
407        }
408        $this->_db->commit();
409        unset($streams_name);
410
411        if(empty($request->escapeNews))
412        { // save the news
413            $model = parent::getCachedModel('news');
414            $r = clone($request);
415            $r->streamid = $request->id;
416            $r->item = array();
417            while($r->item = $stream->get('item'))
418            {
419                try
420                {
421                    $model->edit($r);
422                    $response = $r->getResponse();
423                    if('error' === $response->getNext())
424                        Logs::iGet()->log($response->getError(), $response->getStatus());
425                    else $ids[] = $r->id;
426                }
427                catch(Exception $e)
428                {
429                    switch($e->getCode())
430                    {
431                        case Exception::E_OWR_NOTICE:
432                        case Exception::E_OWR_WARNING:
433                            Logs::iGet()->log($e->getContent(), $e->getCode());
434                            break;
435                        default: throw new Exception($e->getContent(), $e->getCode());
436                            break;
437                    }
438                }
439            }
440        }
441
442        unset($stream);
443
444        try
445        {
446            if(!$request->escape)
447            {
448                $cron->manage(array('type'=>'managefavicons'));
449                $cron->manage(array('type'=>'checkstreamsavailability'));
450            }
451            $cron->manage(array('type'=>'refreshstream', 'ttl'=>$ttl));
452        }
453        catch(Exception $e)
454        {
455            switch($e->getCode())
456            {
457                case Exception::E_OWR_NOTICE:
458                case Exception::E_OWR_WARNING:
459                    Logs::iGet()->log($e->getContent(), $e->getCode());
460                    break;
461                default: throw new Exception($e->getContent(), $e->getCode());
462                    break;
463            }
464        }
465
466        Plugins::trigger($request);
467
468        if(empty($request->escape) && empty($request->escapeNews))
469        {
470            $request->setResponse(new Response(array(
471                'do'        => 'ok',
472                'tpl'       => 'stream',
473                'datas'     => array('id'=>$request->id, 'gid'=>$request->gid, 'name'=>$request->gname, 'ids' => $ids),
474                'status'    => 201
475            )));
476        }
477        else
478        {
479            $request->setResponse(new Response(array(
480                'datas' => array('id' => $request->id, 'ids' => $ids)
481            )));
482        }
483
484        $request->new = true;
485        Plugins::posttrigger($request);
486
487        return $this;
488    }
489
490    /**
491     * Gets datas to render a stream
492     *
493     * @access public
494     * @author Pierre-Alain Mignot <contact@openwebreader.org>
495     * @param mixed $request the Request instance
496     * @param array $args additional arguments, optionnal
497     * @param string $order the order clause
498     * @param string $groupby the groupby clause
499     * @param string $limit the limit clause
500     * @return $this
501     */
502    public function view(Request $request, array $args = array(), $order = '', $groupby = '', $limit = '')
503    {
504        Plugins::pretrigger($request);
505        $args['FETCH_TYPE'] = 'assoc';
506        $multiple = false;
507
508        if(!empty($request->ids))
509        {
510            $args['id'] = $request->ids;
511            $limit = count($request->ids);
512        }
513        elseif(!empty($request->id))
514        {
515            $args['id'] = $request->id;
516            $limit = 1;
517        }
518
519        $datas = $this->_dao->get($args, 'id,streams_relations_name.name,url,ttl,lastupd,favicon,status,gid,streams_groups.name AS gname'.(!isset($request->getContents) || $request->getContents ? ',contents' : ''), $order, $groupby, $limit);
520        if(empty($datas))
521        {
522            $request->setResponse(new Response(array(
523                'status'    => 204
524            )));
525            return $this;
526        }
527
528        if(!isset($datas['id']))
529        {
530            $multiple = true;
531            if(!isset($request->getContents) || $request->getContents)
532            {
533                foreach($datas as $k => $data)
534                    $datas[$k]['contents'] = unserialize($data['contents']);
535            }
536        }
537        elseif(!isset($request->getContents) || $request->getContents)
538        {
539            $datas['contents'] = unserialize($datas['contents']);
540        }
541
542        $this->_setUserTimestamp($datas);
543
544        Plugins::trigger($request);
545
546        $request->setResponse(new Response(array(
547            'datas'        => $datas,
548            'multiple'     => $multiple
549        )));
550        Plugins::posttrigger($request);
551        return $this;
552    }
553
554    /**
555     * Deletes a stream
556     *
557     * @access public
558     * @param mixed $request the Request instance
559     * @return $this
560     */
561    public function delete(Request $request)
562    {
563        Plugins::pretrigger($request);
564        if(empty($request->id))
565        {
566            $request->setResponse(new Response(array(
567                'do'        => 'error',
568                'error'     => 'Missing id',
569                'status'    => Exception::E_OWR_BAD_REQUEST
570            )));
571            return $this;
572        }
573
574        $type = DAO::getType($request->id);
575        if('streams' !== $type)
576        {
577            $request->setResponse(new Response(array(
578                'do'        => 'error',
579                'error'     => 'Invalid id',
580                'status'    => Exception::E_OWR_BAD_REQUEST
581            )));
582            return $this;
583        }
584
585        $this->_db->beginTransaction();
586        try
587        {
588            DAO::getCachedDAO('streams_relations')->delete(array('rssid' => $request->id));
589            DAO::getCachedDAO('news_relations')->delete(array('rssid' => $request->id));
590        }
591        catch(Exception $e)
592        {
593            $this->_db->rollback();
594            throw new Exception($e->getContent(), $e->getCode());
595        }
596        $this->_db->commit();
597        Plugins::trigger($request);
598        $request->setResponse(new Response);
599        Plugins::posttrigger($request);
600        return $this;
601    }
602
603    /**
604     * Moves a stream into another category
605     *
606     * @author Pierre-Alain Mignot <contact@openwebreader.org>
607     * @access protected
608     * @param mixed $request the Request instance
609     */
610    public function move(Request $request)
611    {
612        Plugins::pretrigger($request);
613        if(empty($request->id))
614        {
615            $request->setResponse(new Response(array(
616                'do'        => 'error',
617                'error'     => 'Missing id',
618                'status'    => Exception::E_OWR_BAD_REQUEST
619            )));
620            return $this;
621        }
622
623        $stream = DAO::getCachedDAO('streams_relations')->get(array('rssid' => $request->id), 'rssid');
624        if(empty($stream))
625        {
626            $request->setResponse(new Response(array(
627                'do'        => 'error',
628                'error'     => 'Invalid id',
629                'status'    => Exception::E_OWR_BAD_REQUEST
630            )));
631            return $this;
632        }
633
634        parent::getCachedModel('streams_groups')->checkGroupById($request);
635
636        $stream->gid = $request->gid;
637        $stream->save();
638
639        unset($stream);
640        Plugins::trigger($request);
641        $request->setResponse(new Response);
642        Plugins::posttrigger($request);
643        return $this;
644    }
645
646    /**
647     * Updates a stream
648     *
649     * @author Pierre-Alain Mignot <contact@openwebreader.org>
650     * @param mixed $request the Request instance
651     * @return boolean true on success
652     * @access public
653     */
654    public function update(Request $request)
655    {
656        Plugins::pretrigger($request);
657        if(empty($request->id))
658        {
659            $request->setResponse(new Response(array(
660                'do'        => 'error',
661                'error'     => 'Missing id',
662                'status'    => Exception::E_OWR_BAD_REQUEST
663            )));
664            return $this;
665        }
666
667        $streams = $this->_dao->get($request->id, 'id,url,hash');
668        if(empty($streams))
669        {
670            $request->setResponse(new Response(array(
671                'do'        => 'error',
672                'error'     => 'Invalid id',
673                'status'    => Exception::E_OWR_BAD_REQUEST
674            )));
675            return $this;
676        }
677
678        $streams_contents = DAO::getCachedDAO('streams_contents')->get(array('rssid'=>$request->id));
679        $streams->lastupd = (int)$request->begintime;
680
681        $cron = Cron::iGet();
682        if('' === ($stream = $this->_parse($streams->url, $streams_contents->src)))
683        { // 304 not changed
684            $reader = new StreamReader(array('channel' => unserialize($streams_contents->contents)));
685            $streams->status = 0;
686            $streams->ttl = $reader->get('ttl');
687            unset($reader);
688            $streams->save();
689            try
690            {
691                $cron->manage(array('type'=>'refreshstream','ttl'=>$streams->ttl));
692            }
693            catch(Exception $e)
694            {
695                switch($e->getCode())
696                {
697                    case Exception::E_OWR_NOTICE:
698                    case Exception::E_OWR_WARNING:
699                        Logs::iGet()->log($e->getContent(), $e->getCode());
700                        break;
701                    default: throw new Exception($e->getContent(), $e->getCode());
702                        break;
703                }
704            }
705            $request->setResponse(new Response);
706            return $this;
707        }
708        elseif(false === $stream)
709        {
710            $request->setResponse(new Response(array(
711                'do'    => 'error',
712                'error' => 'Can\'t parse the stream'
713            )));
714            return $this;
715        }
716
717        $streams->status = 0;
718        $streams->ttl = $stream->get('ttl');
719
720        $this->_db->beginTransaction();
721        try
722        {
723            $streams->save();
724        }
725        catch(Exception $e)
726        {
727            $this->_db->rollback();
728            throw new Exception($e->getContent(), $e->getCode());
729        }
730
731        $ttl = $streams->ttl;
732        unset($streams);
733        $streams_contents->src = $stream->get('src');
734        $streams_contents->contents = serialize($stream->get('channel'));
735        try
736        {
737            $streams_contents->save();
738        }
739        catch(Exception $e)
740        {
741            $this->_db->rollback();
742            throw new Exception($e->getContent(), $e->getCode());
743        }
744        $this->_db->commit();
745
746        unset($streams_contents);
747
748        $r = clone($request);
749        $model = parent::getCachedModel('news');
750        $ids = array();
751        while($r->item = $stream->get('item'))
752        {
753            try
754            {
755                $r->streamid = $request->id;
756                $model->edit($r);
757                $ids[] = $r->id;
758                $response = $r->getResponse();
759                if('error' === $response->getNext())
760                    Logs::iGet()->log($response->getError(), $response->getStatus());
761                else $ids[] = $r->id;
762            }
763            catch(Exception $e)
764            {
765                switch($e->getCode())
766                {
767                    case Exception::E_OWR_NOTICE:
768                    case Exception::E_OWR_WARNING:
769                        Logs::iGet()->log($e->getContent(), $e->getCode());
770                        break;
771                    default: throw new Exception($e->getContent(), $e->getCode());
772                        break;
773                }
774            }
775        }
776        unset($stream, $r);
777
778        try
779        {
780            $cron->manage(array('type'=>'refreshstream','ttl'=>$ttl));
781        }
782        catch(Exception $e)
783        {
784            switch($e->getCode())
785            {
786                case Exception::E_OWR_NOTICE:
787                case Exception::E_OWR_WARNING:
788                    Logs::iGet()->log($e->getContent(), $e->getCode());
789                    break;
790                default: throw new Exception($e->getContent(), $e->getCode());
791                    break;
792            }
793        }
794        Plugins::trigger($request);
795        $request->setResponse(new Response(array(
796            'datas' => array('ids' => $ids)
797        )));
798        Plugins::posttrigger($request);
799        return $this;
800    }
801
802    /**
803     * Clear a stream of all the news
804     *
805     * @author Pierre-Alain Mignot <contact@openwebreader.org>
806     * @param mixed $request the Request instance
807     * @return boolean true on success
808     * @access public
809     */
810    public function clear(Request $request)
811    {
812        Plugins::pretrigger($request);
813        if(empty($request->id))
814        {
815            DAO::getCachedDAO('news_relations')->delete();
816        }
817        else
818        {
819            $table = DAO::getType($request->id);
820
821            switch($table)
822            {
823                case 'streams':
824                    DAO::getCachedDAO('news_relations')->delete(array('rssid' => $request->id));
825                    break;
826
827                case 'streams_groups':
828                    $query = '
829    DELETE nr FROM news_relations nr
830        JOIN streams_relations sr ON (nr.rssid=sr.rssid)
831        WHERE gid='.$request->id.' AND nr.uid='.User::iGet()->getUid().' AND sr.uid='.User::iGet()->getUid();
832
833                    $this->_db->set($query);
834                    break;
835
836                case 'news_tags':
837                    DAO::getCachedDAO('news_relations_tags')->delete(array('tid' => $request->id));
838                    break;
839
840                default:
841                    $request->setResponse(new Response(array(
842                        'do'        => 'error',
843                        'error'     => 'Invalid id',
844                        'status'    => Exception::E_OWR_BAD_REQUEST
845                    )));
846                    return $this;
847                    break;
848            }
849        }
850
851        Plugins::trigger($request);
852
853        if($request->currentid === $request->id || 0 === $request->currentid)
854        {
855            $request->setResponse(new Response(array(
856                'tpl'   => 'posts',
857                'datas' => array(
858                    'id' => $request->currentid,
859                    'offset' => $request->offset,
860                    'sort' => $request->sort,
861                    'dir' => $request->dir
862            ))));
863        }
864        else $request->setResponse(new Response);
865        Plugins::posttrigger($request);
866        return $this;
867    }
868
869    /**
870     * Renames a stream
871     *
872     * @author Pierre-Alain Mignot <contact@openwebreader.org>
873     * @access public
874     * @param mixed $request the Request instance
875     * @return $this
876     */
877    public function rename(Request $request)
878    {
879        Plugins::pretrigger($request);
880        if(empty($request->id))
881        {
882            $request->setResponse(new Response(array(
883                'do'        => 'error',
884                'error'     => 'Missing id',
885                'status'    => Exception::E_OWR_BAD_REQUEST
886            )));
887            return $this;
888        }
889
890        if(empty($request->name))
891        {
892            $request->setResponse(new Response(array(
893                'do'        => 'error',
894                'error'     => 'Missing name',
895                'status'    => Exception::E_OWR_BAD_REQUEST
896            )));
897            return $this;
898        }
899
900        $stream = DAO::getCachedDAO('streams_relations_name')->get(array('rssid' => $request->id), 'rssid, uid');
901        if(empty($stream))
902        {
903            $request->setResponse(new Response(array(
904                'do'        => 'error',
905                'error'     => 'Invalid id',
906                'status'    => Exception::E_OWR_BAD_REQUEST
907            )));
908            return $this;
909        }
910
911        $stream->name = $request->name;
912        $stream->save();
913        Plugins::trigger($request);
914        $request->setResponse(new Response);
915        Plugins::posttrigger($request);
916        return $this;
917    }
918
919    /**
920     * Checks for dead streams
921     *
922     * @author Pierre-Alain Mignot <contact@openwebreader.org>
923     * @access public
924     * @param mixed $request the Request instance
925     * @return $this
926     */
927    public function checkAvailability(Request $request)
928    {
929        Plugins::pretrigger($request);
930        if(empty($request->id))
931        {
932            $streams = $this->_db->execute('
933    SELECT id,url
934        FROM streams
935        WHERE status > 0');
936
937            if($streams->count())
938            {
939                $threads = Threads::iGet();
940                while($streams->next())
941                {
942                    $threads->add(array('do'=>'checkstreamsavailability', 'id'=>$streams->id));
943                }
944
945                $request->setResponse(new Response(array(
946                    'status'    => 202
947                )));
948            }
949
950            $request->setResponse(new Response);
951            return $this;
952        }
953
954        $streams = $this->_db->execute('
955    SELECT id,url
956        FROM streams
957        WHERE id='.$request->id.' AND status > 0');
958
959        if($streams->count())
960        {
961            $dao = DAO::getDAO('streams');
962            while($streams->next())
963            {
964                try
965                {
966                    if(false !== cURLWrapper::get($streams->url))
967                    {
968                        $dao->id = $streams->id;
969                        $dao->status = 0; // available, else it's a timestamp of downtime
970                        $dao->ttl = 0; // to be refreshed on next cron processing
971                        $dao->save();
972                    }
973                }
974                catch(Exception $e)
975                {
976                    switch($e->getCode())
977                    {
978                        case Exception::E_OWR_NOTICE:
979                        case Exception::E_OWR_WARNING:
980                            Logs::iGet()->log($e->getContent(), $e->getCode());
981                            break;
982                        default:
983                            throw new Exception($e->getContent(), $e->getCode());
984                            break;
985                    }
986                }
987            }
988        }
989        Plugins::trigger($request);
990        $request->setResponse(new Response);
991        Plugins::posttrigger($request);
992        return $this;
993    }
994
995    /**
996     * Tries to get streams favicons
997     * If you have Imagick extension installed, it will also try to validate the icon
998     *
999     * @author Pierre-Alain Mignot <contact@openwebreader.org>
1000     * @param mixed $request the Request instance
1001     * @access protected
1002     * @return $this
1003     */
1004    public function manageFavicons(Request $request)
1005    {
1006        Plugins::pretrigger($request);
1007        if(empty($request->id))
1008        {
1009            $streams = $this->_dao->get(array(), 'id, url');
1010            if(empty($streams))
1011            {
1012                $request->setResponse(new Response);
1013                return $this;
1014            }
1015
1016            $threads = Threads::iGet();
1017
1018            foreach($streams as $stream)
1019            {
1020                $threads->add(array('do'=>'managefavicons', 'id'=>$stream->id));
1021            }
1022
1023            $request->setResponse(new Response(array(
1024                'status'    => 202
1025            )));
1026            return $this;
1027        }
1028
1029        $stream = $this->_dao->get(array('id'=>$request->id), 'id, url, favicon');
1030        $currentFavicon = $stream->favicon;
1031        $streamContents = DAO::getCachedDAO('streams_contents')->get(array('rssid'=>$request->id), 'contents');
1032        if(empty($stream))
1033        {
1034            $request->setResponse(new Response(array(
1035                'do'        => 'error',
1036                'error'     => 'Invalid id',
1037                'status'    => Exception::E_OWR_BAD_REQUEST
1038            )));
1039            return $this;
1040        }
1041
1042        $streamContents = DAO::getCachedDAO('streams_contents')->get(array('rssid'=>$stream->id));
1043        $reader = new StreamReader(array('channel'=>unserialize($streamContents->contents)));
1044        unset($streamContents);
1045        $favicons = $indexes = array();
1046
1047        if(!empty($stream->favicon))
1048            $favicons[] = $stream->favicon;
1049
1050        $url = $reader->get('realurl');
1051        unset($reader);
1052        if(!empty($url))
1053        {
1054            $values = @parse_url($url);
1055            if(false !== $values && isset($values['scheme']) && isset($values['host']) && 'file' !== $values['scheme'])
1056            {
1057                $favicons[] = $values['scheme'].'://'.$values['host'].'/favicon.ico';
1058                $indexes[] = $values['scheme'].'://'.$values['host'];
1059                $indexes[] = $url;
1060            }
1061        }
1062
1063        $favicon = '';
1064        $values = @parse_url($stream->url);
1065        if(false === $values || !isset($values['scheme']) || 'file' === $values['scheme'])
1066        {
1067            $request->setResponse(new Response(array(
1068                'do'        => 'error',
1069                'error'     => 'Invalid url',
1070                'status'    => Exception::E_OWR_UNAVAILABLE
1071            )));
1072            return $this;
1073        }
1074        else
1075        {
1076            $base = $values['scheme'].'://'.$values['host'];
1077            // we check the base of the domain first
1078            // some blogs are responding at url like http://blog.com/feeds/(favicon.ico|something)
1079            // with stream contents burk, we would /require/ imagick to check..
1080            $favicons[] = $base.'/favicon.ico';
1081            $indexes[] = $base;
1082            if(isset($values['path']) && '/' !== ($path = dirname($values['path'])))
1083            {
1084                $favicons[] = $base.$path.'/favicon.ico';
1085                $indexes[] = $base.$path;
1086            }
1087        }
1088
1089        $favicons = array_unique($favicons);
1090       
1091        foreach($favicons as $fav)
1092        {
1093            try
1094            {
1095                $headers = array();
1096                $icon = cURLWrapper::get($fav, array(), false, true, $headers);
1097            }
1098            catch(Exception $e)
1099            {
1100                // is it really usefull to log here, surely not, only for debug
1101                if(DEBUG) Logs::iGet()->log($e->getContent(), $e->getCode());
1102            }
1103
1104            if(empty($icon) || false == strpos($headers['Content-Type'], 'image')) continue;
1105           
1106            if(class_exists('Imagick', false))
1107            {
1108                try
1109                {
1110                    $image = new \Imagick();
1111                    $image->setFormat('ico');
1112                    if(@$image->readImageBlob($icon))
1113                    {
1114                        $image->destroy();
1115                        unset($image);
1116                        $favicon = $fav;
1117                        break;
1118                    }
1119                }
1120                catch(Exception $e)
1121                { // is it really usefull to log here, surely not
1122                    if(DEBUG) Logs::iGet()->log($e->getContent(), $e->getCode());
1123                }
1124
1125                unset($image);
1126            }
1127            else
1128            {
1129                if(@imagecreatefromstring($icon))
1130                {
1131                    $favicon = $fav;
1132                    break;
1133                }
1134                elseif('ico' === pathinfo($fav, PATHINFO_EXTENSION))
1135                {
1136                    $favicon = $fav;
1137                }
1138            }
1139        }
1140       
1141        unset($favicons, $icon);
1142
1143        if(empty($favicon))
1144        {
1145            $indexes = array_unique($indexes);
1146            foreach($indexes as $index)
1147            {
1148                try
1149                {
1150                    $page = cURLWrapper::get($index, array(), false, true, $headers);
1151                }
1152                catch(Exception $e)
1153                {
1154                    unset($page);
1155                    // is it really usefull to log here, surely not, only for debug
1156                    if(DEBUG) Logs::iGet()->log($e->getContent(), $e->getCode());
1157                    continue;
1158                }
1159
1160                if(empty($page) || !($hrefs = $this->_extractHREF(array('rel' => array('icon', 'shortcut icon')), $page)))
1161                {
1162                    unset($page);
1163                    continue;
1164                }
1165
1166                unset($page);
1167
1168                $icon = array();
1169                foreach($hrefs as $href)
1170                {
1171                    $url = @parse_url($href);
1172                    if(!$url) continue;
1173
1174                    if((!isset($url['scheme']) || 'file' === $url['scheme']))
1175                    {
1176                        if(!isset($url['path'])) continue;
1177
1178                        if('/' !== mb_substr($index, -1, 1, 'UTF-8'))
1179                            $index .= '/';
1180
1181                        $url = @parse_url($index.$url['path']);
1182                        if(!$url || !isset($url['path']) || !isset($url['scheme']) || !isset($url['host'])) continue;
1183
1184                        // try to resolve relative paths
1185                        // can't use realpath() because it only resolves local path
1186                        $realpath = array();
1187                        $path = explode('/', preg_replace(array('/\/+/', '/\/\.\//'), '/', $url['path']));
1188                        foreach($path as $part)
1189                        {
1190                            if('..' === $part)
1191                            {
1192                                array_pop($realpath);
1193                            }
1194                            elseif('' !== $part)
1195                            {
1196                                $realpath[] = $part;
1197                            }
1198                        }
1199
1200                        if(empty($realpath)) continue;
1201                        $href = $url['scheme'].'://'.$url['host'].'/'.join('/', $realpath);
1202                    }
1203
1204                    try
1205                    {
1206                        $headers = array();
1207                        $icon = cURLWrapper::get($href, array(), false, true, $headers);
1208                    }
1209                    catch(Exception $e)
1210                    {
1211                        unset($icon);
1212                        // is it really usefull to log here, surely not, only for debug
1213                        if(DEBUG) Logs::iGet()->log($e->getContent(), $e->getCode());
1214                        continue;
1215                    }
1216
1217                    if(empty($icon) /*|| false === strpos($headers['Content-Type'], 'image')*/) continue;
1218
1219                    if(class_exists('Imagick', false))
1220                    {
1221                        try
1222                        {
1223                            $image = new \Imagick();
1224                            $image->setFormat('ico');
1225                            if(@$image->readImageBlob($icon))
1226                            {
1227                                $image->destroy();
1228                                unset($image);
1229                                $favicon = $href;
1230                                break 2;
1231                            }
1232                        }
1233                        catch(Exception $e)
1234                        { // is it really usefull to log here, surely not
1235                            if(DEBUG) Logs::iGet()->log($e->getContent(), $e->getCode());
1236                        }
1237
1238                        unset($image);
1239                    }
1240                    else
1241                    {
1242                        if(@imagecreatefromstring($icon))
1243                        {
1244                            $favicon = $href;
1245                            break 2;
1246                        }
1247                        elseif('ico' === pathinfo($href, PATHINFO_EXTENSION))
1248                        { // TODO : to be enhanced
1249                            $favicon = $href;
1250                            break 2;
1251                        }
1252                    }
1253                }
1254            }
1255            unset($indexes, $index, $page);
1256        }
1257       
1258        if((string) $currentFavicon !== (string) $favicon)
1259        {
1260            $stream->favicon = (string) $favicon;
1261            $stream->url = null;
1262            $stream->save();
1263        }
1264        Plugins::trigger($request);
1265        $request->setResponse(new Response);
1266        Plugins::posttrigger($request);
1267        return $this;
1268    }
1269
1270    /**
1271     * Tries to refresh stream(s)
1272     *
1273     * @author Pierre-Alain Mignot <contact@openwebreader.org>
1274     * @access protected
1275     * @param mixed $request the Request instance
1276     * @return $this
1277     */
1278    public function refresh(Request $request)
1279    {
1280        Plugins::pretrigger($request);
1281        if(empty($request->id))
1282        {
1283            $query = "
1284    SELECT r.id
1285        FROM streams_relations rel
1286        JOIN streams r ON (rel.rssid=r.id)
1287        WHERE rel.uid=".User::iGet()->getUid().' AND (r.lastupd + (r.ttl * 60)) <= UNIX_TIMESTAMP()';
1288
1289            $rss = $this->_db->getAll($query);
1290            if($rss->count())
1291            {
1292                $threads = Threads::iGet();
1293                while($rss->next())
1294                {
1295                    $threads->add(array('do'=>'refreshstream', 'id'=>$rss->id));
1296                }
1297            }
1298
1299            unset($rss);
1300            Plugins::trigger($request);
1301            $request->setResponse(new Response(array(
1302                'status'    => 202
1303            )));
1304            return $this;
1305        }
1306        else
1307        {
1308            $table = DAO::getType($request->id);
1309
1310            if('streams' === $table)
1311            {
1312                $this->update($request);
1313            }
1314            elseif('streams_groups' === $table)
1315            {
1316                $query = '
1317    SELECT r.id
1318        FROM streams r
1319        JOIN streams_relations rel ON (r.id=rel.rssid)
1320        WHERE rel.gid='.$request->id.' AND rel.uid='.User::iGet()->getUid().'
1321        AND (lastupd + (ttl * 60)) <= UNIX_TIMESTAMP()
1322        GROUP BY r.id';
1323
1324                $rss = $this->_db->getAll($query);
1325                if($rss->count())
1326                {
1327                    $threads = Threads::iGet();
1328                    while($rss->next())
1329                    {
1330                        $threads->add(array('do'=>'refreshstream', 'id'=>$rss->id));
1331                    }
1332                }
1333
1334                unset($rss);
1335                Plugins::trigger($request);
1336                $request->setResponse(new Response(array(
1337                    'status'    => 202
1338                )));
1339                Plugins::posttrigger($request);
1340            }
1341            else
1342            {
1343                Plugins::trigger($request);
1344                $request->setResponse(new Response(array(
1345                    'do'        => 'error',
1346                    'error'     => 'Invalid id',
1347                    'status'    => Exception::E_OWR_BAD_REQUEST
1348                )));
1349                Plugins::posttrigger($request);
1350            }
1351        }
1352
1353        return $this;
1354    }
1355
1356    /**
1357     * Tries to refresh stream(s)
1358     *
1359     * @author Pierre-Alain Mignot <contact@openwebreader.org>
1360     * @access protected
1361     * @param mixed $request the Request instance
1362     * @return $this
1363     */
1364    public function refreshAll(Request $request)
1365    { // in cli, we refresh for all users
1366        Plugins::pretrigger($request);
1367        if(empty($request->id))
1368        {
1369            // status = 0 means stream is alive
1370            // seems obvious but in the other case it will be a timestamp of down time
1371            $query = '
1372    SELECT r.id
1373        FROM streams r
1374        WHERE (lastupd + (ttl * 60)) <= UNIX_TIMESTAMP() AND status=0';
1375
1376            $streams = $this->_db->getAll($query);
1377            if($streams->count())
1378            {
1379                $threads = Threads::iGet();
1380                while($streams->next())
1381                {
1382                    $threads->add(array('do'=>'refreshstream', 'id'=>$streams->id));
1383                }
1384                Plugins::trigger($request);
1385                $request->setResponse(new Response(array(
1386                    'status'    => 202
1387                )));
1388                Plugins::posttrigger($request);
1389                return $this;
1390            }
1391        }
1392        else
1393        {
1394            $table = DAO::getType($request->id);
1395
1396            if('streams' === $table)
1397            {
1398                $streams = $this->_db->getOne('
1399    SELECT r.id, uid
1400        FROM streams r
1401        JOIN streams_relations rel ON (r.id=rel.rssid)
1402        WHERE r.id='.$request->id.' AND (lastupd + (ttl * 60)) <= UNIX_TIMESTAMP()
1403        GROUP BY r.id');
1404                if($streams->next())
1405                {
1406                    User::iGet()->setUid($streams->uid);
1407                    try
1408                    {
1409                        $this->update($request);
1410                    }
1411                    catch(Exception $e)
1412                    {
1413                        switch($e->getCode())
1414                        {
1415                            case Exception::E_OWR_NOTICE:
1416                            case Exception::E_OWR_WARNING:
1417                                Logs::iGet()->log($e->getContent(), $e->getCode());
1418                                break;
1419                            default: throw new Exception($e->getContent(), $e->getCode());
1420                                break;
1421                        }
1422                    }
1423                }
1424                unset($streams);
1425            }
1426            elseif('streams_groups' === $table)
1427            {
1428                $query = '
1429    SELECT r.id
1430        FROM streams r
1431        JOIN streams_relations rel ON (r.id=rel.rssid)
1432        WHERE rel.gid='.$request->id.' AND (lastupd + (ttl * 60)) <= UNIX_TIMESTAMP()
1433        GROUP BY r.id';
1434
1435                $streams = $this->_db->getAll($query);
1436                if($streams->count())
1437                {
1438                    $threads = Threads::iGet();
1439                    while($streams->next())
1440                    {
1441                        $threads->add(array('do'=>'refreshstream', 'id'=>$streams->id));
1442                    }
1443                    Plugins::trigger($request);
1444                    $request->setResponse(new Response(array(
1445                        'status'    => 202
1446                    )));
1447                    Plugins::posttrigger($request);
1448                    return $this;
1449                }
1450
1451                unset($streams);
1452            }
1453        }
1454        Plugins::trigger($request);
1455        $request->setResponse(new Response);
1456        Plugins::posttrigger($request);
1457        return $this;
1458    }
1459
1460    /**
1461     * Adds/Edits a stream
1462     *
1463     * @access public
1464     * @param mixed $request the Request instance
1465     * @return $this
1466     */
1467    public function editOPML(Request $request)
1468    {
1469        Plugins::pretrigger($request);
1470        if(empty($request->escape) && empty($_POST) && empty($_FILES['opml']['tmp_name']))
1471        {
1472            $request->setResponse(new Response(array(
1473                'tpl'        => 'upload'
1474            )));
1475
1476            return $this;
1477        }
1478
1479        User::iGet()->checkToken();
1480
1481        parent::getCachedModel('streams_groups')->checkGroupById($request);
1482        $erase = false;
1483
1484        if(empty($request->escape) && !empty($_FILES['opml']['tmp_name']))
1485        {
1486            $upload = new Upload('opml', array(
1487                'isArray'       => false,
1488                'mime'          => array('text/x-opml+xml', 'text/xml'),
1489                'finfo_mime'    => 'application/xml',
1490                'maxFileSize'   => Config::iGet()->get('maxUploadFileSize'),
1491                'ext'           => array('opml', 'xml')
1492            ));
1493
1494            try
1495            {
1496                $request->url = $upload->get();
1497            }
1498            catch(Exception $e)
1499            {
1500                $request->setResponse(new Response(array(
1501                    'do'        => 'error',
1502                    'error'     => $e->getContent(),
1503                    'status'    => $e->getCode()
1504                )));
1505
1506                return $this;
1507            }
1508        }
1509
1510        if(empty($request->url))
1511        {
1512            $request->setResponse(new Response(array(
1513                'do'        => 'error',
1514                'error'     => 'Missing url',
1515                'status'    => Exception::E_OWR_BAD_REQUEST
1516            )));
1517
1518            return $this;
1519        }
1520
1521        $reader = new OPMLParser();
1522
1523        $reader->parse($request->url, isset($upload));
1524
1525        if(isset($upload))
1526        {
1527            unlink($request->url);
1528            unset($upload);
1529        }
1530
1531        $streams = $reader->export($request->url);
1532
1533        $r = clone($request);
1534        $r->gid = 0;
1535        parent::getCachedModel('streams_groups')->checkGroupById($r);
1536        $gidRoot = $r->gid;
1537        unset($r);
1538
1539        $currentGroup = array();
1540
1541        $ids = array();
1542
1543        $streamsGroupsModel = parent::getCachedModel('streams_groups');
1544        $r = clone($request);
1545        $sr = clone($request);
1546        $sr->delay = true;
1547
1548        $gid = (0 !== $request->gid && ($gidRoot !== $request->gid)) ? $request->gid : 0;
1549
1550        foreach($streams['item'] as $stream)
1551        {
1552            $url = isset($stream['xmlUrl']) ? $stream['xmlUrl'] :
1553                (isset($stream['htmlUrl']) ? $stream['htmlUrl'] : null);
1554
1555            if(empty($url))
1556            {
1557                Logs::iGet()->log('Passing stream, missing url', Exception::E_OWR_WARNING);
1558                continue;
1559            }
1560
1561            $folderId = null;
1562            $sr->gid = $sr->id = 0;
1563
1564            if($gid)
1565                $sr->gid = $gid;
1566            elseif(isset($stream['folder']))
1567            {
1568                if(!isset($currentGroup[$stream['folder']]))
1569                {
1570                    $folderId = DAO::getCachedDAO('streams_groups')->get(array('name' => $stream['folder']), 'id');
1571                    if(!$folderId)
1572                    {
1573                        try
1574                        {
1575                            $r->id = 0;
1576                            $r->name = $stream['folder'];
1577                            $streamsGroupsModel->edit($r);
1578                            $response = $r->getResponse();
1579                            if('error' === $response->getNext())
1580                            {
1581                                Logs::iGet()->log($response->getError(), $response->getStatus());
1582                                $folderId = $gidRoot;
1583                            }
1584                            else
1585                            {
1586                                $folderId = $r->id;
1587                                $ids[] = $r->id;
1588                            }
1589                        }
1590                        catch(Exception $e)
1591                        {
1592                            switch($e->getCode())
1593                            {
1594                                case Exception::E_OWR_NOTICE:
1595                                case Exception::E_OWR_WARNING:
1596                                    Logs::iGet()->log($e->getContent(), $e->getCode());
1597                                    break;
1598                                default:
1599                                    throw new Exception($e->getContent(), $e->getCode());
1600                                    break;
1601                            }
1602                            continue;
1603                        }
1604                    }
1605                    else $folderId = $folderId->id;
1606
1607                    $currentGroup[$stream['folder']] = $folderId;
1608                    unset($folderId);
1609                }
1610
1611                $sr->gid = $currentGroup[$stream['folder']];
1612            }
1613            else $sr->gid = $gidRoot;
1614
1615            $sr->name = !empty($stream['title']) ? $stream['title'] : (
1616                        !empty($stream['text']) ? $stream['text'] : 'No title');
1617            try
1618            {
1619                $sr->url = $url;
1620                $this->edit($sr);
1621                $response = $sr->getResponse();
1622                if('error' === $response->getNext())
1623                    Logs::iGet()->log($response->getError(), $response->getStatus());
1624                else
1625                {
1626                    if($sr->new) $request->new = true;
1627                    $ids[] = $sr->id;
1628                }
1629            }
1630            catch(Exception $e)
1631            {
1632                switch($e->getCode())
1633                {
1634                    case Exception::E_OWR_NOTICE:
1635                    case Exception::E_OWR_WARNING:
1636                        Logs::iGet()->log($e->getContent(), $e->getCode());
1637                        break;
1638                    default:
1639                        throw new Exception($e->getContent(), $e->getCode());
1640                        break;
1641                }
1642            }
1643        }
1644        unset($gidRoot, $r, $sr);
1645        Plugins::trigger($request);
1646        $request->setResponse(new Response(array(
1647            'status'    => 201,
1648            'datas'     => array('ids' => $ids)
1649        )));
1650        Plugins::posttrigger($request);
1651        return $this;
1652    }
1653
1654    /**
1655     * Tries to parse a stream
1656     *
1657     * @author Pierre-Alain Mignot <contact@openwebreader.org>
1658     * @param string $url the url to parse
1659     * @param string $src the original source, optionnal
1660     * @return mixed StreamReader on success, '' if stream has not changed, false on error
1661     * @access protected
1662     */
1663    protected function _parse($url, $src='')
1664    {
1665        $url = (string) $url;
1666        isset($this->_streamParser) || $this->_streamParser = new StreamParser();
1667
1668        try
1669        {
1670            if($src)
1671            {
1672                $csrc = (string)$this->_streamParser->getSrc($url);
1673                if('' === $csrc || trim($csrc) === trim($src)) return ''; // stream has not changed
1674
1675                $stream =  (!$this->_streamParser->parse($url, $csrc) ? false : $this->_streamParser->export());
1676            }
1677            else $stream = (!$this->_streamParser->parse($url) ? false : $this->_streamParser->export());
1678        }
1679        catch(Exception $e)
1680        {
1681            switch($e->getCode())
1682            {
1683                case Exception::E_OWR_NOTICE:
1684                case Exception::E_OWR_WARNING:
1685                    Logs::iGet()->log($e->getContent(), $e->getCode());
1686                    break;
1687                default: throw new Exception($e->getContent(), $e->getCode());
1688                    break;
1689            }
1690            return false;
1691        }
1692
1693        return $stream;
1694    }
1695
1696    /**
1697     * Try to get href from a specific &lt;link&gt; tag
1698     *
1699     * @author Pierre-Alain Mignot <contact@openwebreader.org>
1700     * @access protected
1701     * @param array $requestedParams the requested parameters
1702     * @param string $src the source to search
1703     * @return array found href
1704     */
1705    protected function _extractHREF(array $requestedParams, $src)
1706    {
1707        $hrefs = array();
1708
1709        if(!preg_match_all('/<link\b((\s+[a-z]+\s*=\s*(["\'])[^\\3]+?\\3)+)+\s*\/?>/is', $src, $tags))
1710            return $hrefs;
1711
1712        foreach($tags[1] as $tag)
1713        {
1714            if(!preg_match_all('/([a-z]+)\s*=\s*(["\'])([^\\2]+?)\\2/i', $tag, $params))
1715                continue;
1716
1717            $rel = $href = $type = null;
1718
1719            foreach($params[1] as $k => $param)
1720            {
1721                $param = strtolower($param);
1722                if('rel' === $param)
1723                    $rel = strtolower($params[3][$k]);
1724                elseif('href' === $param)
1725                    $href = $params[3][$k];
1726                elseif(isset($requestedParams['type']) && 'type' === $param)
1727                    $type = strtolower($params[3][$k]);
1728            }
1729
1730            unset($params);
1731
1732            if((isset($requestedParams['type']) && !isset($type)) || !$rel || !$href)
1733                continue;
1734
1735            foreach($requestedParams['rel'] as $k=>$r)
1736            {
1737                if($rel !== $r) continue;
1738
1739                if(isset($requestedParams['type']))
1740                {
1741                    foreach($requestedParams['type'] as $t)
1742                    {
1743                        if($t === $type) $hrefs[] = $href;
1744                    }
1745                }
1746                else
1747                {
1748                    $hrefs[] = $href;
1749                }
1750            }
1751        }
1752
1753        return $hrefs;
1754    }
1755}
Note: See TracBrowser for help on using the repository browser.