1<?php
2/**
3 * Smarty plugin
4 *
5 * @package    Smarty
6 * @subpackage PluginsFunction
7 */
8/**
9 * Smarty {fetch} plugin
10 * Type:     function
11 * Name:     fetch
12 * Purpose:  fetch file, web or ftp data and display results
13 *
14 * @link   http://www.smarty.net/manual/en/language.function.fetch.php {fetch}
15 *         (Smarty online manual)
16 * @author Monte Ohrt <monte at ohrt dot com>
17 *
18 * @param array                    $params   parameters
19 * @param Smarty_Internal_Template $template template object
20 *
21 * @throws SmartyException
22 * @return string|null if the assign parameter is passed, Smarty assigns the result to a template variable
23 */
24function smarty_function_fetch($params, $template)
25{
26    if (empty($params[ 'file' ])) {
27        trigger_error('[plugin] fetch parameter \'file\' cannot be empty', E_USER_NOTICE);
28        return;
29    }
30    // strip file protocol
31    if (stripos($params[ 'file' ], 'file://') === 0) {
32        $params[ 'file' ] = substr($params[ 'file' ], 7);
33    }
34    $protocol = strpos($params[ 'file' ], '://');
35    if ($protocol !== false) {
36        $protocol = strtolower(substr($params[ 'file' ], 0, $protocol));
37    }
38    if (isset($template->smarty->security_policy)) {
39        if ($protocol) {
40            // remote resource (or php stream, …)
41            if (!$template->smarty->security_policy->isTrustedUri($params[ 'file' ])) {
42                return;
43            }
44        } else {
45            // local file
46            if (!$template->smarty->security_policy->isTrustedResourceDir($params[ 'file' ])) {
47                return;
48            }
49        }
50    }
51    $content = '';
52    if ($protocol === 'http') {
53        // http fetch
54        if ($uri_parts = parse_url($params[ 'file' ])) {
55            // set defaults
56            $host = $server_name = $uri_parts[ 'host' ];
57            $timeout = 30;
58            $accept = 'image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, */*';
59            $agent = 'Smarty Template Engine ' . Smarty::SMARTY_VERSION;
60            $referer = '';
61            $uri = !empty($uri_parts[ 'path' ]) ? $uri_parts[ 'path' ] : '/';
62            $uri .= !empty($uri_parts[ 'query' ]) ? '?' . $uri_parts[ 'query' ] : '';
63            $_is_proxy = false;
64            if (empty($uri_parts[ 'port' ])) {
65                $port = 80;
66            } else {
67                $port = $uri_parts[ 'port' ];
68            }
69            if (!empty($uri_parts[ 'user' ])) {
70                $user = $uri_parts[ 'user' ];
71            }
72            if (!empty($uri_parts[ 'pass' ])) {
73                $pass = $uri_parts[ 'pass' ];
74            }
75            // loop through parameters, setup headers
76            foreach ($params as $param_key => $param_value) {
77                switch ($param_key) {
78                    case 'file':
79                    case 'assign':
80                    case 'assign_headers':
81                        break;
82                    case 'user':
83                        if (!empty($param_value)) {
84                            $user = $param_value;
85                        }
86                        break;
87                    case 'pass':
88                        if (!empty($param_value)) {
89                            $pass = $param_value;
90                        }
91                        break;
92                    case 'accept':
93                        if (!empty($param_value)) {
94                            $accept = $param_value;
95                        }
96                        break;
97                    case 'header':
98                        if (!empty($param_value)) {
99                            if (!preg_match('![\w\d-]+: .+!', $param_value)) {
100                                trigger_error("[plugin] invalid header format '{$param_value}'", E_USER_NOTICE);
101                                return;
102                            } else {
103                                $extra_headers[] = $param_value;
104                            }
105                        }
106                        break;
107                    case 'proxy_host':
108                        if (!empty($param_value)) {
109                            $proxy_host = $param_value;
110                        }
111                        break;
112                    case 'proxy_port':
113                        if (!preg_match('!\D!', $param_value)) {
114                            $proxy_port = (int)$param_value;
115                        } else {
116                            trigger_error("[plugin] invalid value for attribute '{$param_key }'", E_USER_NOTICE);
117                            return;
118                        }
119                        break;
120                    case 'agent':
121                        if (!empty($param_value)) {
122                            $agent = $param_value;
123                        }
124                        break;
125                    case 'referer':
126                        if (!empty($param_value)) {
127                            $referer = $param_value;
128                        }
129                        break;
130                    case 'timeout':
131                        if (!preg_match('!\D!', $param_value)) {
132                            $timeout = (int)$param_value;
133                        } else {
134                            trigger_error("[plugin] invalid value for attribute '{$param_key}'", E_USER_NOTICE);
135                            return;
136                        }
137                        break;
138                    default:
139                        trigger_error("[plugin] unrecognized attribute '{$param_key}'", E_USER_NOTICE);
140                        return;
141                }
142            }
143            if (!empty($proxy_host) && !empty($proxy_port)) {
144                $_is_proxy = true;
145                $fp = fsockopen($proxy_host, $proxy_port, $errno, $errstr, $timeout);
146            } else {
147                $fp = fsockopen($server_name, $port, $errno, $errstr, $timeout);
148            }
149            if (!$fp) {
150                trigger_error("[plugin] unable to fetch: $errstr ($errno)", E_USER_NOTICE);
151                return;
152            } else {
153                if ($_is_proxy) {
154                    fputs($fp, 'GET ' . $params[ 'file' ] . " HTTP/1.0\r\n");
155                } else {
156                    fputs($fp, "GET $uri HTTP/1.0\r\n");
157                }
158                if (!empty($host)) {
159                    fputs($fp, "Host: $host\r\n");
160                }
161                if (!empty($accept)) {
162                    fputs($fp, "Accept: $accept\r\n");
163                }
164                if (!empty($agent)) {
165                    fputs($fp, "User-Agent: $agent\r\n");
166                }
167                if (!empty($referer)) {
168                    fputs($fp, "Referer: $referer\r\n");
169                }
170                if (isset($extra_headers) && is_array($extra_headers)) {
171                    foreach ($extra_headers as $curr_header) {
172                        fputs($fp, $curr_header . "\r\n");
173                    }
174                }
175                if (!empty($user) && !empty($pass)) {
176                    fputs($fp, 'Authorization: BASIC ' . base64_encode("$user:$pass") . "\r\n");
177                }
178                fputs($fp, "\r\n");
179                while (!feof($fp)) {
180                    $content .= fgets($fp, 4096);
181                }
182                fclose($fp);
183                $csplit = preg_split("!\r\n\r\n!", $content, 2);
184                $content = $csplit[ 1 ];
185                if (!empty($params[ 'assign_headers' ])) {
186                    $template->assign($params[ 'assign_headers' ], preg_split("!\r\n!", $csplit[ 0 ]));
187                }
188            }
189        } else {
190            trigger_error("[plugin fetch] unable to parse URL, check syntax", E_USER_NOTICE);
191            return;
192        }
193    } else {
194        $content = @file_get_contents($params[ 'file' ]);
195        if ($content === false) {
196            throw new SmartyException("{fetch} cannot read resource '" . $params[ 'file' ] . "'");
197        }
198    }
199    if (!empty($params[ 'assign' ])) {
200        $template->assign($params[ 'assign' ], $content);
201    } else {
202        return $content;
203    }
204}
205