source: OWR/Model/Streams.php @ 712cca

Revision 712cca, 54.8 KB checked in by pierre-alain <pierre-alain@…>, 4 years ago (diff)

some people dont send good content-type when serving .ico, it may result in good favicon uri not beeing treated as well. we let imagick (or GD) do the job

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