/[drupal]/contributions/modules/embedfilter/embedfilter.module
ViewVC logotype

Contents of /contributions/modules/embedfilter/embedfilter.module

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.7 - (show annotations) (download) (as text)
Sun Sep 27 10:54:07 2009 UTC (2 months ago) by karens
Branch: MAIN
CVS Tags: HEAD
Changes since 1.6: +73 -24 lines
File MIME type: text/x-php
Port code to D6, #218518 by greg.harvey ignore sub domains, #177853 by ragaskar check object data tags too.
1 <?php
2 // $Id: embedfilter.module,v 1.6 2007/09/25 17:09:45 darthsteven Exp $
3
4 /**
5 * Enter description here...
6 *
7 * @param string $section
8 * A Drupal path
9 * @return string
10 * The help text
11 */
12 function embedfilter_help($section) {
13 switch ($section) {
14 case 'admin/help#embedfilter':
15 $output = '<p>'. t('Many great sites like <a href="http://www.youtube.com">YouTube</a>, <a href="http://www.ifilm.com">iFilm</a> and <a href="http://www.nowpublic.com">NowPublic</a> allow their media assets to be served to 3rd party websites (like this one). They usually offer snippets of code to be embedded in a web page that will then load a media object (usually Flash) from their servers. This module lets your site users embed such snippets into posts but gives you the power to decide which hosts to trust.') .'</p>';
16 $output .= '<p>'. t('Here are some examples of code snippets from the sites mentioned above:') .'</p>';
17 $output .= <<<EOF
18 <h3>YouTube.com</h3>
19 <pre>
20 &lt;object width="425" height="350">&lt;param name="movie"
21 value="http://www.youtube.com/v/BqLvBUSJucg">
22 &lt;/param>&lt;embed src="http://www.youtube.com/v/BqLvBUSJucg"
23 type="application/x-shockwave-flash" width="425" height="350">
24 &lt;/embed>&lt;/object>
25 </pre>
26 <h3>iFilm</h3>
27 <pre>
28 &lt;embed allowScriptAccess="never" width="448" height="365"
29 src="http://www.ifilm.com/efp" quality="high" bgcolor="000000"
30 name="efp" align="middle" type="application/x-shockwave-flash"
31 pluginspage="http://www.macromedia.com/go/getflashplayer"
32 flashvars="flvbaseclip=2761045" >&lt;/embed>
33 </pre>
34 <h3>NowPublic</h3>
35 <pre>
36 &lt;script language="JavaScript"
37 src="http://media.nowpublic.com/js/7ae70d3f8c890cebc5c9633f49607ae2.js">&lt;/script>
38 </pre>
39 EOF;
40
41 $output .= '<p>'. t('To use this module, start by entering a number of hosts that you wish to allow. This is done from the <a href="/?q=admin/settings/embedfilter">settings page</a>. Examples taken from the sites mentioned above would be: <ul><li>youtube.com</li><li>ifilm.com</li><li>nowpublic.com</li></ul> You can define some HTML that will be embedded before and after any tags (in case you want to be able to visually identify them on your site), as well as some filter tip instructions to your users.') .'</p>';
42 $output .= '<p>'. t('After you have configured the module from the settings page, you need to add the "Object and embed" filter to the appropriate input formats (such as "Filtered HTML") on the <a href="/?q=admin/filters">input formats</a> administration page. Configure your HTML filter to accept &lt;object>, &lt;embed>, &lt;param> and &lt;script> tags by adding them to the list of accepted tags. Configure the order of the filters so that the "Object and embed tag" filter comes directly after the "HTML" filter. After this you should be ready to go!'). '</p>';
43 return $output;
44 }
45 }
46
47 /**
48 * Implemention of hook_menu
49 *
50 */
51 function embedfilter_menu() {
52 $items = array();
53 $items['admin/settings/embedfilter'] = array(
54 'title' => t('Embed filter'),
55 'description' => t('Settings for Embed filter.'),
56 'page callback' => 'drupal_get_form',
57 'page arguments' => array('embedfilter_admin_settings'),
58 'access arguments' => array('administer filters'),
59 'type' => MENU_NORMAL_ITEM,
60 );
61 return $items;
62 }
63
64 /**
65 * Implementation of hook_settings
66 *
67 * @return a $form array
68 */
69 function embedfilter_admin_settings() {
70 $form['embedfilter_host_whitelist'] = array(
71 '#type' => 'textarea',
72 '#title' => t('Allowed hosts'),
73 '#default_value' => variable_get('embedfilter_host_whitelist', "http://www.youtube.com\nhttp://media.nowpublic.com"),
74 '#description' => t('Each host name from which &lt;object>, &lt;embed>, &lt;param> or &lt;script> tags should be accepted. One host per line. This module is patched to expect only domain.tld format and ignores subdomains. Example: youtube.com'),
75 );
76
77 $form['extramarkup'] = array(
78 '#type' => 'fieldset',
79 '#title' => t('Extra markup'),
80 '#collapsible' => true,
81 '#collapsed' => true,
82 );
83 $form['extramarkup']['embedfilter_pre_markup'] = array(
84 '#type' => 'textarea',
85 '#default_value' => variable_get('embedfilter_pre_markup', ''),
86 '#title' => t('Pre tag markup'),
87 '#description' => t('Markup that should appear directly before an &lt;object>, &lt;embed> or &lt;script> tag. This only applies to the outermost tag in the case where nested tags are found.'),
88 );
89 $form['extramarkup']['embedfilter_post_markup'] = array(
90 '#type' => 'textarea',
91 '#default_value' => variable_get('embedfilter_post_markup', ''),
92 '#title' => t('Post tag markup'),
93 '#description' => t('Markup that should appear directly after an &lt;/object>, &lt;/embed> or &lt;/script> tag. This only applies to the outermost tag in the case where nested tags are found.'),
94 );
95
96 $form['filtertip'] = array(
97 '#type' => 'fieldset',
98 '#title' => t('Filter tip'),
99 '#collapsible' => true,
100 '#collapsed' => true,
101 );
102 $form['filtertip']['embedfilter_filtertip'] = array(
103 '#type' => 'textarea',
104 '#default_value' => variable_get('embedfilter_filtertip', t('You can use &lt;object>, &lt;embed> and &lt;script> tags from the following sites to add media to your posts:')),
105 '#title' => t('Custom filter tips'),
106 '#description' => t('Instructions to your site users about how to use these tags. The whitelist will also be displayed.'),
107 );
108
109 return system_settings_form($form);
110 }
111
112 function embedfilter_get_whitelist() {
113 static $whitelist;
114 if (empty($whitelist)) {
115 $var = variable_get('embedfilter_host_whitelist', '');
116 $whitelist = array_filter(split("[\n|\r]", $var), 'trim');
117 }
118 return $whitelist;
119 }
120
121 /**
122 * Implementation of hook_filter_tips()
123 *
124 * @param int $delta
125 * Used when a module defines more than one filter
126 * @param unknown_type $format
127 * @param boolean $long
128 * Determines whether the long or the short tip version is displayed
129 * @return string
130 * The tip to be displayed
131 */
132 function embedfilter_filter_tips($delta, $format, $long = false) {
133 $output .= '<p>'. variable_get('embedfilter_filtertip', t('You can use &lt;object>, &lt;embed> and &lt;script> tags from the following sites to add media to your posts:')). "\n";
134 $output .= theme('item_list', embedfilter_get_whitelist()). "</p>\n";
135 return $output;
136 }
137
138 /**
139 * Implentation of hook_filter. Defines a filter, "Object and embed tag filter",
140 * that can be used in conjunction with the built in HTML filter to allow
141 * users to include <object>, <embed> and <script> tags within their posts,
142 * as long as any href or src elements point to trusted hosts defined on the
143 * whitelist (see admin/settings/embedfilter).
144 *
145 * @param string $op
146 * @param int $delta
147 * @param int $format
148 * @param string $text
149 * The text to be filtered
150 * @return string
151 */
152 function embedfilter_filter($op, $delta = 0, $format = -1, $text = '', $langcode = '', $cache_id = 0) {
153 switch ($op) {
154 case 'list':
155 return array(0 => t('Object and embed tag filter'));
156
157 case 'description':
158 return t('Lets users safely add <code>&lt;object></code>, <code>&lt;embed></code>, and <code>&lt;script></code> tags to posts.');
159
160 case 'process':
161 $text = embedfilter_process($text);
162 return $text;
163
164 default:
165 return $text;
166 }
167 }
168
169 /**
170 * A recursive function that finds all <embed>, <object> and <script>
171 * tags and either approves them (based on the whitelist) or removes them.
172 *
173 * No text is allowed between <script> and </script> tags.
174 *
175 * No on*= attributes are allowed (to prevent unwanted scripting)
176 *
177 * @param string or array $input
178 * @return string
179 * The processed input string.
180 */
181 function embedfilter_process($input) {
182 static $count;
183
184 // This function is called recursively to handle <embed> tags in <object> tags.
185
186 // If $input is an array, we are coming from preg_replace_callback.
187 if (is_array($input)) {
188 $count++;
189 $output = '';
190
191 // Check to see if the host is on the whitelist.
192 if (embedfilter_approve($input[2])) {
193 // The first time through we add the pre-markup.
194 if ($count === 1) {
195 $output = variable_get('embedfilter_pre_markup', '');
196 }
197 $output .= '<'. $input[1]. embedfilter_noevents(embedfilter_sanitize($input[2]));
198
199 // if there are opening and closing <script> tags, and there is
200 // anything in between them, deny.
201 if (count($input) == 4 && strtolower($input[1]) == 'script' && strlen(trim($input[3])) > 0) {
202 $count--;
203 return '';
204 }
205 // 4 part arrays have closing tags: <embed.... ></embed>
206 if (count($input) == 4) {
207 $output .= '>'. embedfilter_process($input[3]). '</'. $input[1]. '>';
208 }
209 // 3 part arrays are single, closed tags: <embed.... />
210 else if (count($input) == 3) {
211 $output .= '/>';
212 }
213 if ($count === 1) {
214 $output .= variable_get('embedfilter_post_markup', '');
215 }
216 $count--;
217 } else {
218 return '';
219 }
220 // If the embedfilter_approve step failed, we return an empty string.
221 $input = $output;
222 }
223 else {
224 // find open tag/close tag pairs.
225 $input = preg_replace_callback('@<(embed|object|script)([^>]*)>(.*?)</\1>@si', 'embedfilter_process', $input, 5);
226
227 // find single, closed tags
228 $input = preg_replace_callback('@<(embed|object|script)([^>]*)/>@si', 'embedfilter_process', $input, 5);
229 }
230
231 return embedfilter_media_resize($input, 550);
232 }
233
234 /**
235 * Checks a string for the presence of src and href attributes.
236 * If found, the URLs within those attributes are compared to the
237 * hosts in whitelist.
238 *
239 * @param string $input
240 * @return boolean
241 * true if all the hosts are on the whitelist, false if any host is not.
242 */
243 function embedfilter_approve($input) {
244 $pattern = '@(src|href|data)=([\'"])([^"]+)\\2@i';
245 $matches = array();
246 preg_match_all($pattern, $input, $matches);
247 foreach ($matches[3] as $url) {
248 $parts = parse_url($url);
249 //first check if there is no subdomain
250 if (preg_match('/^\.(com|co\.uk|tv|net|org|gov)/',strstr($parts['host'],'.'))) {
251 if (!in_array($parts['host'], embedfilter_get_whitelist())) {
252 return false;
253 }
254 //otherwise check if domain and subdomain are both missing from the permitted list
255 }
256 elseif ((!in_array(ltrim(strstr($parts['host'],'.'),'.'), embedfilter_get_whitelist())) && (!in_array($parts['host'], embedfilter_get_whitelist()))) {
257 return false;
258 }
259 }
260 return true;
261 }
262
263 /**
264 * Prevents XSS attacks on from the href or src attributes.
265 *
266 * @param string or array $input
267 * @return string
268 * A sanitized string where all href and src attributes have been run through
269 * check_url.
270 */
271 function embedfilter_sanitize($input) {
272 // arrays are the product of the preg_replace_callback function
273 if (is_array($input)) {
274 $output = $input[1]. '='. $input[2]. check_url($input[3]). $input[2];
275 }
276 else {
277 $pattern = '@(src|href|data)=([\'"])([^"]+)\\2@i';
278 $output = preg_replace_callback($pattern, 'embedfilter_sanitize', $input);
279 }
280
281 //removes any errant slashes from the end of $output
282 $last = $input[strlen($output)-1];
283 if ($last == '/') {
284 $output = substr($output, 0, -1);
285 }
286
287 return $output;
288 }
289
290 /**
291 * Removes any on* attributes so that no scripting can be done.
292 * NOTE: The presence of these attributes does not cause the whole
293 * <object>, <embed> or <script> tag to be removed; only the offending
294 * attributes are removed.
295 *
296 * @param string $input
297 * @return string
298 */
299 function embedfilter_noevents($input) {
300 $output = '';
301
302 // If $input is an array, we return an empty string because we want to
303 // get rid of whatever matched.
304 if (!is_array($input)) {
305 $pattern = '@( on[^=]+?=([\'"])([^\\2]+?)\\2)?@i';
306 $output = preg_replace_callback($pattern, 'embedfilter_noevents', $input);
307 }
308 return $output;
309 }
310
311 /**
312 * Resizes over-sized media
313 *
314 * @param $input
315 * string of HTML with width and height attributes
316 *
317 * @param $maxwidth
318 * the maximum permitted width on the website for embedded media
319 *
320 * @return
321 * replacement $input HTML string containing revised width and height
322 */
323 function embedfilter_media_resize($input = '', $maxwidth = 550) {
324 preg_match('/height="([0-9]*)"/',$input,$fetchheight);
325 preg_match('/width="([0-9]*)"/',$input,$fetchwidth);
326 if ($fetchheight && $fetchwidth) {
327 $height = $fetchheight[1];
328 $width = $fetchwidth[1];
329 }
330
331 if ($width > $maxwidth) {
332 $diff = $width-$maxwidth;
333 $ratio = $diff/$width;
334 $heightdiff = $ratio*$height;
335 $height = round($height-$heightdiff);
336 $width = $maxwidth;
337
338 $patterns[0] = '/height="([0-9]*)"/';
339 $patterns[1] = '/width="([0-9]*)"/';
340 $replacements[1] = 'height="'.$height.'"';
341 $replacements[0] = 'width="'.$width.'"';
342
343 $input = preg_replace($patterns, $replacements, $input);
344 }
345 return $input;
346 }
347

  ViewVC Help
Powered by ViewVC 1.1.2