]> git.mxchange.org Git - quix0rs-gnu-social.git/blob - scripts/importtwitteratom.php
XSS vulnerability when remote-subscribing
[quix0rs-gnu-social.git] / scripts / importtwitteratom.php
1 #!/usr/bin/env php
2 <?php
3 /*
4  * StatusNet - the distributed open-source microblogging tool
5  * Copyright (C) 2010 StatusNet, Inc.
6  *
7  * This program is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU Affero General Public License as published by
9  * the Free Software Foundation, either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU Affero General Public License for more details.
16  *
17  * You should have received a copy of the GNU Affero General Public License
18  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
19  */
20
21 define('INSTALLDIR', realpath(dirname(__FILE__) . '/..'));
22
23 $shortoptions = 'i:n:f:';
24 $longoptions = array('id=', 'nickname=', 'file=');
25
26 $helptext = <<<END_OF_IMPORTTWITTERATOM_HELP
27 importtwitteratom.php [options]
28 import an Atom feed from Twitter as notices by a user
29
30   -i --id       ID of user to update
31   -n --nickname nickname of the user to update
32   -f --file     file to import (Atom-only for now)
33
34 END_OF_IMPORTTWITTERATOM_HELP;
35
36 require_once INSTALLDIR.'/scripts/commandline.inc';
37 require_once INSTALLDIR.'/extlib/htmLawed/htmLawed.php';
38
39 function getAtomFeedDocument()
40 {
41     $filename = get_option_value('f', 'file');
42
43     if (empty($filename)) {
44         show_help();
45         exit(1);
46     }
47
48     if (!file_exists($filename)) {
49         throw new Exception("No such file '$filename'.");
50     }
51
52     if (!is_file($filename)) {
53         throw new Exception("Not a regular file: '$filename'.");
54     }
55
56     if (!is_readable($filename)) {
57         throw new Exception("File '$filename' not readable.");
58     }
59
60     $xml = file_get_contents($filename);
61
62     $dom = DOMDocument::loadXML($xml);
63
64     if ($dom->documentElement->namespaceURI != Activity::ATOM ||
65         $dom->documentElement->localName != 'feed') {
66         throw new Exception("'$filename' is not an Atom feed.");
67     }
68
69     return $dom;
70 }
71
72 function importActivityStream($user, $doc)
73 {
74     $feed = $doc->documentElement;
75
76     $entries = $feed->getElementsByTagNameNS(Activity::ATOM, 'entry');
77
78     for ($i = $entries->length - 1; $i >= 0; $i--) {
79         $entry = $entries->item($i);
80         $activity = new Activity($entry, $feed);
81         $object = $activity->objects[0];
82         if (!have_option('q', 'quiet')) {
83             print $activity->content . "\n";
84         }
85         $html = getTweetHtml($object->link);
86
87         $config = array('safe' => 1,
88                         'deny_attribute' => 'class,rel,id,style,on*');
89
90         $html = htmLawed($html, $config);
91
92         $content = html_entity_decode(strip_tags($html), ENT_QUOTES, 'UTF-8');
93
94         $notice = Notice::saveNew($user->id,
95                                   $content,
96                                   'importtwitter',
97                                   array('uri' => $object->id,
98                                         'url' => $object->link,
99                                         'rendered' => $html,
100                                         'created' => common_sql_date($activity->time),
101                                         'replies' => array(),
102                                         'groups' => array()));
103     }
104 }
105
106 function getTweetHtml($url)
107 {
108     try {
109         $client = new HTTPClient();
110         $response = $client->get($url);
111     } catch (HTTP_Request2_Exception $e) {
112         print "ERROR: HTTP response " . $e->getMessage() . "\n";
113         return false;
114     }
115
116     if (!$response->isOk()) {
117         print "ERROR: HTTP response " . $response->getCode() . "\n";
118         return false;
119     }
120
121     $body = $response->getBody();
122
123     return tweetHtmlFromBody($body);
124 }
125
126 function tweetHtmlFromBody($body)
127 {
128     $doc = DOMDocument::loadHTML($body);
129     $xpath = new DOMXPath($doc);
130
131     $spans = $xpath->query('//span[@class="entry-content"]');
132
133     if ($spans->length == 0) {
134         print "ERROR: No content in tweet page.\n";
135         return '';
136     }
137
138     $span = $spans->item(0);
139
140     $children = $span->childNodes;
141
142     $text = '';
143
144     for ($i = 0; $i < $children->length; $i++) {
145         $child = $children->item($i);
146         if ($child instanceof DOMElement &&
147             $child->tagName == 'a' &&
148             !preg_match('#^https?://#', $child->getAttribute('href'))) {
149             $child->setAttribute('href', 'http://twitter.com' . $child->getAttribute('href'));
150         }
151         $text .= $doc->saveXML($child);
152     }
153
154     return $text;
155 }
156
157 try {
158
159     $doc = getAtomFeedDocument();
160     $user = getUser();
161
162     importActivityStream($user, $doc);
163
164 } catch (Exception $e) {
165     print $e->getMessage()."\n";
166     exit(1);
167 }
168