root/trackback/files/lib/util/TrackbackUtil.class.php @ 154

Revision 154, 12.6 kB (checked in by d0nut, 5 years ago)

releaded version 0.2 of trackback plugin

  • acp exists to view incoming/outgoing pings
  • some acp pages are missing
  • timestamps of incoming pings are saved
Line 
1<?php
2// import seorewriter
3require_once(WCF_DIR.'lib/util/StringUtil.class.php');
4require_once(WCF_DIR.'lib/page/PublicSEORewriter.class.php');
5require_once(WCF_DIR.'lib/util/FileUtil.class.php');
6
7/**
8 * trackback util
9 *
10 * @author      Torben Brodt
11 * @package     de.easy-coding.wbb.trackback
12 * @license     GNU General Public License <http://opensource.org/licenses/gpl-3.0.html>
13 */
14class TrackbackUtil {
15        public static $agent = 'WBB Trackback Mod';
16        protected static $timeout_bytes = 51200;
17
18        /**
19         * fetchs necessery postdata
20         * @param postIDs       int[]           list of postids with possible trackback data
21         * @return              mixed[]         including all threaddata + threadurl
22         */
23        public static function getPost($postIDs) {
24                if(!is_array($postIDs))
25                        $postIDs = array($postIDs);
26       
27                // fetch thread data
28                $sql = "SELECT          thread.*
29                        FROM            wbb".WBB_N."_post post
30                        LEFT JOIN       wbb".WBB_N."_thread thread
31                        ON              (thread.threadID=post.threadID)
32                        WHERE           postID IN (".implode(',', $postIDs).") ";
33
34                $row = WCF::getDB()->getFirstRow($sql);
35                               
36                $rewriter = new PublicSEORewriter();
37               
38                // public seo rewriter: cache
39                $rewriter->publicCacheThreads($row['threadID'], $row);
40               
41                // public seo rewriter use
42                $row['url'] = StringUtil::decodeHTML(FileUtil::addTrailingSlash(PAGE_URL).$rewriter->publicParseThreadURLs($row['threadID'], ''));
43               
44                return $row;
45        }
46       
47        /**
48         * returns rdf string
49         * @param postPermalink         string          permalink
50         * @param postID                int             postid
51         * @param postTopic             string          posttopic
52         * @param postPreview           string          postpreview
53         * @param postUsername          string          postusername
54         * @param postTime              int             posttime as unix timestamp
55         */
56        public static function getRDF($postPermalink, $postID, $postTopic, $postPreview, $postUsername, $postTime) {
57                $postPermalink = StringUtil::decodeHTML($postPermalink);
58                $page_url = FileUtil::addTrailingSlash(PAGE_URL);
59                $postPreview = str_replace(array("\n","\r"), ' ', $postPreview);
60                $postPreview = str_replace(array("    ","   ","  "), ' ', $postPreview);
61                $postPreview = substr($postPreview, 0, 255);
62                return '<link rel="pingback" href="'.$page_url.'index.php?action=Pingback" />
63<!--
64<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
65        xmlns:dc="http://purl.org/dc/elements/1.1/"
66        xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/">
67<rdf:Description
68        rdf:about="'.$postPermalink.'"
69        dc:identifier="'.$postPermalink.'"
70        trackback:ping="'.$page_url.'index.php?action=Trackback&postID='.$postID.'"
71        dc:title="'.$postTopic.'"
72        dc:subject="TrackBack"
73        dc:description="'.$postPreview.'"
74        dc:creator="'.$postUsername.'"
75        dc:date="'.date('r', $postTime).'" />
76</rdf:RDF>
77-->';
78        }
79       
80        /**
81         * saves incoming ping/trackback
82         * @param postID                int             postid
83         * @param postURL               string          post-/thread-url
84         * @param title                 string          title
85         * @param excerpt               string          excerpt
86         * @param url                   string          url
87         * @param blog_name             string          blog_name
88         */
89        public static function save($postID, $postURL, $title, $excerpt, $url, $blog_name) {
90                // akismet check spam
91                $isSpam = self::isSpam($title, $excerpt, $url, $blog_name);
92               
93                // escape html characters
94                $postURL = StringUtil::encodeHTML($postURL);
95                $title = StringUtil::encodeHTML($title);
96                $excerpt = StringUtil::encodeHTML($excerpt);
97                $url = StringUtil::encodeHTML($url);
98                $blog_name = StringUtil::encodeHTML($blog_name);
99       
100                // insert trackback data
101                $sql = "INSERT INTO     wbb".WBB_N."_trackback
102                        (
103                                        postID,
104                                        postURL,
105                                        title,
106                                        excerpt,
107                                        url,
108                                        blog_name,
109                                        timestamp,
110                                        isSpam
111                        ) VALUES (
112                                        {$postID},
113                                        '".escapeString($postURL)."',
114                                        '".escapeString($title)."',
115                                        '".escapeString($excerpt)."',
116                                        '".escapeString($url)."',
117                                        '".escapeString($blog_name)."',
118                                        ".time().",
119                                        ".intval($isSpam)."
120                        );";
121
122                WBBCore::getDB()->sendQuery($sql);
123        }
124       
125        /**
126         * does track and ping operations on alien urls
127         * @param postID                int             postid
128         * @param alienurl              string          url in content - ping and trackback here
129         * @param page_title            string          own forum title
130         * @param author                string          own author
131         * @param url                   string          own url
132         * @param title                 string          own post title
133         * @param excerpt               string          own excerpt
134         */
135        public static function trackAndPing($postID, $alienurl, $page_title, $author, $url, $title, $excerpt) {
136                $content = ""; // hold as reference
137
138                // auto discovery
139                $pingbacks = self::discover_pingback_uri($alienurl, $content);
140                $trackbacks = count($pingbacks) > 0 ? array() : self::discover_trackback_uri($content, $alienurl);
141               
142                // merge pingbacks with public pingback services
143                $pingbacks = array_merge($pingbacks, explode("\n", StringUtil::unifyNewlines(MESSAGE_PINGBACK)));
144                $pingbacks = array_filter($pingbacks, create_function('$a','return !empty($a);'));
145               
146                // send pingbacks
147                foreach($pingbacks as $pingbackurl) {
148                        if(self::send_pingback($alienurl, $pingbackurl, $url)) {
149                                self::save_log($postID, $alienurl);
150                        }
151                }
152
153                // send trackbacks
154                foreach($trackbacks as $trackbackurl) {
155                        if(self::send_trackback($trackbackurl, $page_title, $author, $url, $title, $excerpt)) {
156                                self::save_log($postID, $alienurl);
157                        }
158                }
159        }
160       
161        /**
162         * akismet check
163         * @param title                 string          incoming title
164         * @param excerpt               string          incoming excerpt
165         * @param url                   string          incoming url
166         * @param blog_name             string          incoming blog_name
167         * @return                      boolean         true, if spam
168         */     
169        protected static function isSpam($title, $excerpt, $url, $blog_name) {
170                if(AKISMET_API_KEY == '')
171                        return false;
172               
173                // WCF includes
174                require_once(WCF_DIR.'lib/util/AkismetUtil.class.php');
175
176                $akismet = new AkismetUtil(AKISMET_API_KEY, PAGE_URL);
177                $akismet->setCommentType('trackback');
178                $akismet->setPermalink($myurl); //absolute
179               
180                // assign post data
181                $akismet->setCommentAuthor($title);
182                $akismet->setCommentAuthorURL($url);
183                $akismet->setCommentContent($excerpt);
184
185                return $akismet->isCommentSpam();
186        }
187       
188        /**
189         * discover pingback uri
190         * @param url                   string          the alien url
191         * @param contents (reference)  string          empty variable - but the content of the alien url will be returned here
192         * @return pingbacks            string[]        the pingbacks of the alien url (should be just one)
193         */
194        protected static function discover_pingback_uri($url, &$contents) {
195                $pingbacks = array(); // return var
196               
197                $byte_count = 0;
198                $contents = '';
199                $headers = '';
200                $pingback_str_dquote = 'rel="pingback"';
201                $pingback_str_squote = 'rel=\'pingback\'';
202                $x_pingback_str = 'x-pingback: ';
203                $pingback_href_original_pos = 27;
204
205                extract(parse_url($url), EXTR_SKIP);
206
207                if ( !isset($host) ) // Not an URL. This should never happen.
208                return false;
209
210                $path  = ( !isset($path) ) ? '/'          : $path;
211                $path .= ( isset($query) ) ? '?' . $query : '';
212                $port  = ( isset($port)  ) ? $port        : 80;
213
214                // Try to connect to the server at $host
215                $fp = @fsockopen($host, $port, $errno, $errstr, 2);
216                if ( !$fp ) // Couldn't open a connection to $host
217                        return false;
218
219                // Send the GET request
220                $request = "GET $path HTTP/1.1\r\nHost: $host\r\nUser-Agent: ".self::$agent." \r\n\r\n";
221                // ob_end_flush();
222                fputs($fp, $request);
223
224                // Let's check for an X-Pingback header first
225                while ( !feof($fp) ) {
226                        $line = fgets($fp, 512);
227                        if ( trim($line) == '' )
228                                break;
229                        $headers .= trim($line)."\n";
230                        $x_pingback_header_offset = strpos(strtolower($headers), $x_pingback_str);
231                        if ( $x_pingback_header_offset ) {
232                                // We got it!
233                                preg_match('#x-pingback: (.+)#is', $headers, $matches);
234                                $pingbacks[] = trim($matches[1]);
235                                return $pingbacks;
236                        }
237                        if ( strpos(strtolower($headers), 'content-type: ') ) {
238                                preg_match('#content-type: (.+)#is', $headers, $matches);
239                                $content_type = trim($matches[1]);
240                        }
241                }
242
243                if ( preg_match('#(image|audio|video|model)/#is', $content_type) ) // Not an (x)html, sgml, or xml page, no use going further
244                        return false;
245
246                while ( !feof($fp) ) {
247                        $line = fgets($fp, 1024);
248                        $contents .= trim($line);
249                        $pingback_link_offset_dquote = strpos($contents, $pingback_str_dquote);
250                        $pingback_link_offset_squote = strpos($contents, $pingback_str_squote);
251                        if ( $pingback_link_offset_dquote || $pingback_link_offset_squote ) {
252                        $quote = ($pingback_link_offset_dquote) ? '"' : '\'';
253                        $pingback_link_offset = ($quote=='"') ? $pingback_link_offset_dquote : $pingback_link_offset_squote;
254                        $pingback_href_pos = @strpos($contents, 'href=', $pingback_link_offset);
255                        $pingback_href_start = $pingback_href_pos+6;
256                        $pingback_href_end = @strpos($contents, $quote, $pingback_href_start);
257                        $pingback_server_url_len = $pingback_href_end - $pingback_href_start;
258                        $pingback_server_url = substr($contents, $pingback_href_start, $pingback_server_url_len);
259                        // We may find rel="pingback" but an incomplete pingback URL
260                        if ( $pingback_server_url_len > 0 ) // We got it!
261                                $pingbacks[] = $pingback_server_url;
262                                break;
263                        }
264                        $byte_count += strlen($line);
265                        if ( $byte_count > self::$timeout_bytes ) {
266                                // It's no use going further, there probably isn't any pingback
267                                // server to find in this file. (Prevents loading large files.)
268                                break;
269                        }
270                }
271               
272                return $pingbacks;
273        }
274       
275        /**
276         * discover trackback uri
277         * @param contents              string          html content to parse
278         * @param url                   string          the url, which should allow trackback:ping
279         * @return trackbacks           string[]        the trackbacks of the alien url (should be just one)
280         */
281        protected static function discover_trackback_uri($contents, $url) {
282                $rdf = array(); // <- holds list of RDF segments
283
284                if ($contents) {
285                        preg_match_all('/(<rdf:RDF.*?<\/rdf:RDF>)/sm', $contents, $link_rdf, PREG_SET_ORDER);
286
287                        // Loop through all rdf segments
288                        foreach ($link_rdf as $seg) {
289                                if (preg_match('|dc:identifier="' . preg_quote($url) . '"|ms', $seg[1])) {
290                                        $rdf[] = trim($seg[1]);
291                                } 
292                        } 
293                }
294                // Loop through the RDFs array and extract trackback URIs
295                $trackbacks = array(); // <- holds list of trackback URIs
296                foreach($rdf as $rdf_url) {
297                        if (preg_match('/trackback:ping="([^"]+)"/', $rdf_url, $array)) {
298                                $trackbacks[] = trim($array[1]);
299                        } 
300                } 
301
302                return $trackbacks;
303        }
304
305
306        /**
307         * Send a Pingback
308         * @param alienurl              string          the url, we want to ping
309         * @param pingbackurl           string          the pingback url from the destination site (e.g. xmlrpc.php)
310         * @param url                   string          our own url
311         * @return                      boolean         true if success
312         */
313        protected static function send_pingback($alienurl, $pingbackurl, $url) {
314                require_once(WBB_DIR.'lib/util/IXR.class.php');
315                extract(parse_url($pingbackurl), EXTR_SKIP);
316               
317                if ( !isset($host) ) // Not an URL. This should never happen.
318                        return false;
319
320                $path  = ( !isset($path) ) ? '/'          : $path;
321                $path .= ( isset($query) ) ? '?' . $query : '';
322                $port  = ( isset($port)  ) ? $port        : 80;
323
324                // using a timeout of 3 seconds should be enough to cover slow servers
325                $client = new IXR_Client($host, $path);
326                $client->timeout = 3;
327                $client->useragent = self::$agent;
328
329                // when set to true, this outputs debug messages by itself
330                $client->debug = false;
331
332                // Already registered
333                return $client->query('pingback.ping', $url, $alienurl) || (isset($client->error->code) && 48 == $client->error->code);
334        }
335
336        /**
337         * Send a Trackback
338         * @param trackbackurl          string          the url, we want to trackback
339         * @param page_title            string          own forum title
340         * @param author                string          own author (ignored)
341         * @param url                   string          own url
342         * @param title                 string          own post title
343         * @param excerpt               string          own excerpt
344         * @return                      resource        pointer on socket
345         */
346        protected static function send_trackback($trackbackurl, $page_title, $author, $url, $title, $excerpt) {
347                $blog_title = urlencode($page_title);
348                $url = urlencode($url);
349                $title = urlencode($title);
350                $excerpt = urlencode($excerpt);
351
352                $query_string = "title={$title}&url={$url}&blog_name={$blog_title}&excerpt={$excerpt}";
353
354                $parse = parse_url($trackbackurl);
355                $http_request = 'POST ' . $parse['path'] . (isset($parse['query']) ? '?'.$parse['query'] : '') . " HTTP/1.0\r\n";
356                $http_request .= 'Host: '.$parse['host']."\r\n";
357                $http_request .= 'Content-Type: application/x-www-form-urlencoded; charset='.CHARSET."\r\n";
358                $http_request .= 'Content-Length: '.strlen($query_string)."\r\n";
359                $http_request .= "User-Agent: {self::agent}";
360                $http_request .= "\r\n\r\n";
361                $http_request .= $query_string;
362
363                $fs = @fsockopen($parse['host'], isset($parse['port'])?$parse['port']:80, $errno, $errstr, 4);
364                @fputs($fs, $http_request);
365                @fclose($fs);
366               
367                return $fs;
368        }
369       
370        /**
371         * saves log
372         * @param postID                int             postID
373         * @param alienURL              string          alienURL
374         */
375        protected static function save_log($postID, $alienurl) {
376                $sql = "INSERT INTO     wbb".WBB_N."_trackbackLog
377                        (
378                                        postID,
379                                        alienURL,
380                                        timestamp
381                        ) VALUES (
382                                        {$postID},
383                                        '".escapeString($alienurl)."',
384                                        ".time()."
385                        );";
386
387                @WBBCore::getDB()->sendQuery($sql); // ignore error messages...
388        }
389} 
390?>
Note: See TracBrowser for help on using the browser.