File Coverage

blib/lib/Mojolicious/Plugin/LinkEmbedder.pm
Criterion Covered Total %
statement 15 89 16.8
branch 0 52 0.0
condition 1 35 2.8
subroutine 5 16 31.2
pod 1 2 50.0
total 22 194 11.3


line stmt bran cond sub pod time code
1             package Mojolicious::Plugin::LinkEmbedder;
2 1     1   26525 use Mojo::Base 'Mojolicious::Plugin';
  1         7205  
  1         7  
3 1     1   1098 use Mojo::JSON;
  1         56054  
  1         46  
4 1     1   532 use Mojo::UserAgent;
  1         165520  
  1         7  
5 1     1   560 use Mojolicious::Plugin::LinkEmbedder::Link;
  1         3  
  1         6  
6 1   50 1   36 use constant DEBUG => $ENV{MOJO_LINKEMBEDDER_DEBUG} || 0;
  1         3  
  1         1242  
7              
8             our $VERSION = '0.25';
9              
10             has _ua => sub { Mojo::UserAgent->new(max_redirects => 3) };
11              
12             sub embed_link {
13 0     0 0   my ($self, $c, $url, $cb) = @_;
14              
15 0 0 0       $url = Mojo::URL->new($url || '') unless ref $url;
16              
17 0 0         if ($url =~ m!\.(?:jpg|png|gif)\b!i) {
18 0 0         return $c if $self->_new_link_object(image => $c, {url => $url}, $cb);
19             }
20 0 0         if ($url =~ m!\.(?:mpg|mpeg|mov|mp4|ogv)\b!i) {
21 0 0         return $c if $self->_new_link_object(video => $c, {url => $url}, $cb);
22             }
23 0 0         if ($url =~ m!^spotify:\w+!i) {
24 0 0         return $c if $self->_new_link_object('open.spotify' => $c, {url => $url}, $cb);
25             }
26 0 0 0       if (!$url or !$url->host) {
27 0           return $c->tap(
28             $cb,
29             Mojolicious::Plugin::LinkEmbedder::Link->new(
30             url => Mojo::URL->new,
31             error => {message => 'Invalid input', code => 400,}
32             )
33             );
34             }
35              
36             return $c->delay(
37             sub {
38 0     0     my ($delay) = @_;
39 0           $self->_ua->head($url => $delay->begin);
40             },
41             sub {
42 0     0     my ($delay, $tx) = @_;
43 0           $self->_learn($c, $tx, $cb);
44             }
45 0           );
46             }
47              
48             sub _learn {
49 0     0     my ($self, $c, $tx, $cb) = @_;
50 0   0       my $ct = $tx->res->headers->content_type || '';
51 0           my $etag = $tx->res->headers->etag;
52 0           my $url = $tx->req->url;
53              
54 0 0 0       if ($etag and $etag eq ($c->req->headers->etag // '')) {
      0        
55 0           return $c->$cb(Mojolicious::Plugin::LinkEmbedder::Link->new(_tx => $tx, etag => $etag));
56             }
57 0 0         if (my $err = $tx->error) {
58 0           return $c->$cb(Mojolicious::Plugin::LinkEmbedder::Link->new(_tx => $tx, error => $err));
59             }
60 0 0         if (my $type = lc $url->host) {
61 0           $type =~ s/^(?:www|my)\.//;
62 0           $type =~ s/\.\w+$//;
63 0 0         return if $self->_new_link_object($type => $c, {_tx => $tx}, $cb);
64             }
65              
66 0 0 0       return if $ct =~ m!^image/! and $self->_new_link_object(image => $c, {url => $url, _tx => $tx}, $cb);
67 0 0 0       return if $ct =~ m!^video/! and $self->_new_link_object(video => $c, {url => $url, _tx => $tx}, $cb);
68 0 0 0       return if $ct =~ m!^text/plain! and $self->_new_link_object(text => $c, {url => $url, _tx => $tx}, $cb);
69              
70 0 0         if ($ct =~ m!^text/html!) {
71 0 0         return if $self->_new_link_object(html => $c, {_tx => $tx}, $cb);
72             }
73              
74 0           warn "[LINK] New from $ct: Mojolicious::Plugin::LinkEmbedder::Link ($url)\n" if DEBUG;
75 0           $c->$cb(Mojolicious::Plugin::LinkEmbedder::Link->new(_tx => $tx));
76             }
77              
78             sub _new_link_object {
79 0     0     my ($self, $type, $c, $args, $cb) = @_;
80 0 0         my $class = $self->{classes}{$type} or return;
81              
82 0           warn "[LINK] New from $type: $class\n" if DEBUG;
83 0 0         eval "require $class;1" or die "Could not require $class: $@";
84 0           local $args->{ua} = $self->_ua;
85 0           my $link = $class->new($args);
86              
87             Mojo::IOLoop->delay(
88             sub {
89 0     0     my ($delay) = @_;
90 0           $link->learn($c, $delay->begin);
91             },
92             sub {
93 0     0     my ($delay) = @_;
94 0           do { local $link->{_tx}; local $link->{ua}; warn Data::Dumper::Dumper($link); } if DEBUG and 0;
95 0           $c->$cb($link);
96             },
97 0           );
98              
99 0           return $class;
100             }
101              
102             sub register {
103 0     0 1   my ($self, $app, $config) = @_;
104              
105             $self->{classes} = {
106 0           'appear' => 'Mojolicious::Plugin::LinkEmbedder::Link::Video::AppearIn',
107             '2play' => 'Mojolicious::Plugin::LinkEmbedder::Link::Game::_2play',
108             'beta.dbtv' => 'Mojolicious::Plugin::LinkEmbedder::Link::Video::Dbtv',
109             'dbtv' => 'Mojolicious::Plugin::LinkEmbedder::Link::Video::Dbtv',
110             'collegehumor' => 'Mojolicious::Plugin::LinkEmbedder::Link::Video::Collegehumor',
111             'gist.github' => 'Mojolicious::Plugin::LinkEmbedder::Link::Text::GistGithub',
112             'github' => 'Mojolicious::Plugin::LinkEmbedder::Link::Text::Github',
113             'html' => 'Mojolicious::Plugin::LinkEmbedder::Link::Text::HTML',
114             'instagram' => 'Mojolicious::Plugin::LinkEmbedder::Link::Image::Instagram',
115             'image' => 'Mojolicious::Plugin::LinkEmbedder::Link::Image',
116             'imgur' => 'Mojolicious::Plugin::LinkEmbedder::Link::Image::Imgur',
117             'ix' => 'Mojolicious::Plugin::LinkEmbedder::Link::Text::Ix',
118             'metacpan' => 'Mojolicious::Plugin::LinkEmbedder::Link::Text::Metacpan',
119             'open.spotify' => 'Mojolicious::Plugin::LinkEmbedder::Link::Music::Spotify',
120             'paste.scsys.co' => 'Mojolicious::Plugin::LinkEmbedder::Link::Text::PasteScsysCoUk',
121             'pastebin' => 'Mojolicious::Plugin::LinkEmbedder::Link::Text::Pastebin',
122             'pastie' => 'Mojolicious::Plugin::LinkEmbedder::Link::Text::Pastie',
123             'ted' => 'Mojolicious::Plugin::LinkEmbedder::Link::Video::Ted',
124             'text' => 'Mojolicious::Plugin::LinkEmbedder::Link::Text',
125             'twitter' => 'Mojolicious::Plugin::LinkEmbedder::Link::Text::Twitter',
126             'travis-ci' => 'Mojolicious::Plugin::LinkEmbedder::Link::Text::Travis',
127             'video' => 'Mojolicious::Plugin::LinkEmbedder::Link::Video',
128             'vimeo' => 'Mojolicious::Plugin::LinkEmbedder::Link::Video::Vimeo',
129             'youtube' => 'Mojolicious::Plugin::LinkEmbedder::Link::Video::Youtube',
130             'xkcd' => 'Mojolicious::Plugin::LinkEmbedder::Link::Image::Xkcd',
131             };
132              
133             $app->helper(
134             embed_link => sub {
135 0 0   0     return $self if @_ == 1;
136 0           return $self->embed_link(@_);
137             }
138 0           );
139              
140 0 0         if (my $route = $config->{route}) {
141 0           $self->_add_action($app, $route, $config);
142             }
143             }
144              
145             sub _add_action {
146 0     0     my ($self, $app, $route, $config) = @_;
147              
148 0   0       $config->{max_age} //= 60;
149 0 0         $route = $app->routes->route($route) unless ref $route;
150              
151             $route->to(
152             cb => sub {
153 0     0     my $c = shift;
154 0           my $url = $c->param('url');
155 0           my $if_none_match = $c->req->headers->if_none_match;
156              
157             $c->delay(
158             sub {
159 0           my ($delay) = @_;
160 0           $c->embed_link($url, $delay->begin);
161             },
162             sub {
163 0           my ($delay, $link) = @_;
164 0           my $err = $link->error;
165              
166 0 0 0       if ($err) {
    0          
167 0   0       $c->res->code($err->{code} || 500);
168 0   0       $c->respond_to(json => {json => $err}, any => {text => $err->{message} || 'Unknown error.'});
169             }
170             elsif ($if_none_match and $if_none_match eq $link->etag) {
171 0           $c->res->code(304);
172 0           $c->rendered;
173             }
174             else {
175 0 0         $c->res->headers->etag($link->etag) if $link->etag;
176 0 0 0       $c->res->headers->cache_control("max-age=$config->{max_age}") if !$link->etag and $config->{max_age};
177 0           $c->respond_to(json => {json => $link}, any => {text => $link->to_embed});
178             }
179             }
180 0           );
181             }
182 0           );
183             }
184              
185             1;
186              
187             =encoding utf8
188              
189             =head1 NAME
190              
191             Mojolicious::Plugin::LinkEmbedder - Deprecated
192              
193             =head1 VERSION
194              
195             0.25
196              
197             =head1 DESCRIPTION
198              
199             Deprecated in favor of L
200              
201             =head1 DISCLAIMER
202              
203             This module might embed javascript from 3rd party services.
204              
205             Any damage caused by either evil DNS takeover or malicious code inside
206             the javascript is not taken into account by this module.
207              
208             If you are aware of any security risks, then please let us know.
209              
210             =head1 COPYRIGHT AND LICENSE
211              
212             Copyright (C) 2014, Jan Henning Thorsen
213              
214             This program is free software, you can redistribute it and/or modify
215             it under the terms of the Artistic License version 2.0.
216              
217             =head1 AUTHOR
218              
219             Jan Henning Thorsen - C
220              
221             Joel Berger, jberger@cpan.org
222              
223             Marcus Ramberg - C
224              
225             =cut