1<?php 2 3/** 4 * Internal helper functions for use with the DokuWiki Linkback Plugin. 5 * 6 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 7 * @author Gina Haeussge <osd@foosel.net> 8 * @link http://wiki.foosel.net/snippets/dokuwiki/linkback 9 */ 10 11require_once (DOKU_PLUGIN . 'linkback/http.php'); 12 13class tools_plugin_linkback extends DokuWiki_Plugin { 14 15 /** 16 * Retrieves a favicon for the given page. 17 */ 18 function getFavicon($url, $page) { 19 $urlparts = parse_url($url); 20 21 $regex = '!<link rel="(shortcut )?icon" href="([^"]+)" ?/?>!'; 22 if (preg_match($regex, $page, $match)) { 23 $icon = $match[2]; 24 if (!preg_match("#^(http://)?[^/]+#i", $icon)) { 25 $icon = $urlparts['scheme'] . '://' . $urlparts['host'] . (($icon[0]) == '/' ? '' : '/') . $icon; 26 } 27 return $icon; 28 } 29 30 $icon = $urlparts['scheme'] . '://' . $urlparts['host'] . '/favicon.ico'; 31 32 $http_client = new LinkbackHTTPClient(); 33 $http_client->sendRequest($icon, array (), 'HEAD'); 34 if ($http_client->status == 200) 35 return $icon; 36 else 37 return $this->getConf('favicon_default'); 38 } 39 40 /** 41 * Retrieves $conf['range']kB of $url and returns headers and retrieved body. 42 */ 43 function getPage($url) { 44 $range = $this->getConf('range') * 1024; 45 46 $http_client = new LinkbackHTTPClient(); 47 $http_client->headers['Range'] = 'bytes=0-' . $range; 48 $http_client->max_bodysize = $range; 49 $http_client->max_bodysize_limit = true; 50 51 $retval = $http_client->get($url, true); 52 53 return array ( 54 'success' => $retval, 55 'headers' => $http_client->resp_headers, 56 'body' => $http_client->resp_body, 57 'error' => $http_client->error, 58 'status' => $http_client->status, 59 ); 60 } 61 62 /** 63 * Sends a notify mail on new linkback 64 * 65 * @param string $ID id of the wiki page for which the 66 * linkback was received 67 * @param array $linkback data array of the new linkback 68 * 69 * @author Andreas Gohr <andi@splitbrain.org> 70 * @author Esther Brunner <wikidesign@gmail.com> 71 * @author Gina Haeussge <osd@foosel.net> 72 */ 73 function notify($ID, $linkback) { 74 global $conf; 75 76 if (!$conf['notify']) 77 return; 78 $to = $conf['notify']; 79 $text = io_readFile($this->localFN('subscribermail')); 80 81 $search = array ( 82 '@PAGE@', 83 '@TITLE@', 84 '@DATE@', 85 '@URL@', 86 '@TEXT@', 87 '@UNSUBSCRIBE@', 88 '@DOKUWIKIURL@', 89 '@PAGEURL@', 90 91 ); 92 $replace = array ( 93 $ID, 94 $conf['title'], 95 strftime($conf['dformat'], $linkback['received']), 96 $linkback['url'], 97 $linkback['excerpt'], 98 wl($ID, 'do=unsubscribe', true, '&'), 99 DOKU_URL, 100 wl($ID, '', true), 101 ); 102 $text = str_replace($search, $replace, $text); 103 104 $subject = '[' . $conf['title'] . '] ' . $this->getLang('mail_newlinkback'); 105 106 mail_send($to, $subject, $text, $conf['mailfrom'], ''); 107 108 } 109 110 /** 111 * Adds an entry to the linkbacks changelog 112 * 113 * @author Esther Brunner <wikidesign@gmail.com> 114 * @author Ben Coburn <btcoburn@silicodon.net> 115 * @author Gina Haeussge <osd@foosel.net> 116 */ 117 function addLogEntry($date, $id, $type = 'cl', $summary = '', $extra = '') { 118 global $conf; 119 120 $changelog = $conf['metadir'] . '/_linkbacks.changes'; 121 122 if (!$date) { 123 $date = time(); //use current time if none supplied 124 } 125 $remote = $_SERVER['REMOTE_ADDR']; 126 $user = $_SERVER['REMOTE_USER']; 127 128 $strip = array ( 129 "\t", 130 "\n" 131 ); 132 $logline = array ( 133 'date' => $date, 134 'ip' => $remote, 135 'type' => str_replace($strip, 136 '', 137 $type 138 ), 'id' => $id, 'user' => $user, 'sum' => str_replace($strip, '', $summary), 'extra' => str_replace($strip, '', $extra)); 139 140 // add changelog line 141 $logline = implode("\t", $logline) . "\n"; 142 io_saveFile($changelog, $logline, true); //global changelog cache 143 $this->_trimRecentCommentsLog($changelog); 144 } 145 146 /** 147 * Trims the recent comments cache to the last $conf['changes_days'] recent 148 * changes or $conf['recent'] items, which ever is larger. 149 * The trimming is only done once a day. 150 * 151 * @author Ben Coburn <btcoburn@silicodon.net> 152 */ 153 function _trimRecentCommentsLog($changelog) { 154 global $conf; 155 156 if (@ file_exists($changelog) && (filectime($changelog) + 86400) < time() && !@ file_exists($changelog . 157 '_tmp')) { 158 159 io_lock($changelog); 160 $lines = file($changelog); 161 if (count($lines) < $conf['recent']) { 162 // nothing to trim 163 io_unlock($changelog); 164 return true; 165 } 166 167 io_saveFile($changelog . '_tmp', ''); // presave tmp as 2nd lock 168 $trim_time = time() - $conf['recent_days'] * 86400; 169 $out_lines = array (); 170 171 $linecount = count($lines); 172 for ($i = 0; $i < $linecount; $i++) { 173 $log = parseChangelogLine($lines[$i]); 174 if ($log === false) 175 continue; // discard junk 176 if ($log['date'] < $trim_time) { 177 $old_lines[$log['date'] . ".$i"] = $lines[$i]; // keep old lines for now (append .$i to prevent key collisions) 178 } else { 179 $out_lines[$log['date'] . ".$i"] = $lines[$i]; // definitely keep these lines 180 } 181 } 182 183 // sort the final result, it shouldn't be necessary, 184 // however the extra robustness in making the changelog cache self-correcting is worth it 185 ksort($out_lines); 186 $extra = $conf['recent'] - count($out_lines); // do we need extra lines do bring us up to minimum 187 if ($extra > 0) { 188 ksort($old_lines); 189 $out_lines = array_merge(array_slice($old_lines, - $extra), $out_lines); 190 } 191 192 // save trimmed changelog 193 io_saveFile($changelog . '_tmp', implode('', $out_lines)); 194 @ unlink($changelog); 195 if (!rename($changelog . '_tmp', $changelog)) { 196 // rename failed so try another way... 197 io_unlock($changelog); 198 io_saveFile($changelog, implode('', $out_lines)); 199 @ unlink($changelog . '_tmp'); 200 } else { 201 io_unlock($changelog); 202 } 203 return true; 204 } 205 } 206 207 function addProcessLogEntry($data) { 208 global $conf; 209 210 io_saveFile($conf['cachedir'].'/linkback.log',join("\n",$data)."\n\n",true); 211 } 212 213} 214