]> git.mxchange.org Git - quix0rs-gnu-social.git/blob - plugins/SubMirror/classes/SubMirror.php
XSS vulnerability when remote-subscribing
[quix0rs-gnu-social.git] / plugins / SubMirror / classes / SubMirror.php
1 <?php
2 /*
3  * StatusNet - the distributed open-source microblogging tool
4  * Copyright (C) 2010, StatusNet, Inc.
5  *
6  * This program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU Affero General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU Affero General Public License for more details.
15  *
16  * You should have received a copy of the GNU Affero General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 /**
21  * @package SubMirrorPlugin
22  * @maintainer Brion Vibber <brion@status.net>
23  */
24
25 class SubMirror extends Managed_DataObject
26 {
27     public $__table = 'submirror';
28
29     public $subscriber;
30     public $subscribed;
31
32     public $style;
33
34     public $created;
35     public $modified;
36
37     /**
38      * return table definition for DB_DataObject
39      *
40      * DB_DataObject needs to know something about the table to manipulate
41      * instances. This method provides all the DB_DataObject needs to know.
42      *
43      * @return array array of column definitions
44      */
45
46     function table()
47     {
48         return array('subscriber' =>  DB_DATAOBJECT_INT + DB_DATAOBJECT_NOTNULL,
49                      'subscribed' => DB_DATAOBJECT_INT + DB_DATAOBJECT_NOTNULL,
50
51                      'style' => DB_DATAOBJECT_STR,
52
53                      'created' => DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME + DB_DATAOBJECT_NOTNULL,
54                      'modified' => DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME + DB_DATAOBJECT_NOTNULL);
55     }
56
57     static function schemaDef()
58     {
59         // @fixme need a reverse key on (subscribed, subscriber) as well
60         return array(new ColumnDef('subscriber', 'integer',
61                                    null, false, 'PRI'),
62                      new ColumnDef('subscribed', 'integer',
63                                    null, false, 'PRI'),
64
65                      new ColumnDef('style', 'varchar',
66                                    16, true),
67
68                      new ColumnDef('created', 'datetime',
69                                    null, false),
70                      new ColumnDef('modified', 'datetime',
71                                    null, false));
72     }
73
74     /**
75      * Temporary hack to set up the compound index, since we can't do
76      * it yet through regular Schema interface. (Coming for 1.0...)
77      *
78      * @param Schema $schema
79      * @return void
80      */
81     static function fixIndexes($schema)
82     {
83         try {
84             $schema->createIndex('submirror', array('subscribed', 'subscriber'));
85         } catch (Exception $e) {
86             common_log(LOG_ERR, __METHOD__ . ': ' . $e->getMessage());
87         }
88     }
89
90     /**
91      * return key definitions for DB_DataObject
92      *
93      * DB_DataObject needs to know about keys that the table has; this function
94      * defines them.
95      *
96      * @return array key definitions
97      */
98
99     function keys()
100     {
101         return array_keys($this->keyTypes());
102     }
103
104     /**
105      * return key definitions for Memcached_DataObject
106      *
107      * Our caching system uses the same key definitions, but uses a different
108      * method to get them.
109      *
110      * @return array key definitions
111      */
112
113     function keyTypes()
114     {
115         // @todo FIXME keys
116         // need a sane key for reverse lookup too
117         return array('subscriber' => 'K', 'subscribed' => 'K');
118     }
119
120     function sequenceKey()
121     {
122         return array(false, false, false);
123     }
124
125     /**
126      * @param Profile $subscribed
127      * @param Profile $subscribed
128      * @return SubMirror
129      * @throws ServerException
130      */
131     public static function saveMirror($subscriber, $subscribed, $style='repeat')
132     {
133         // @fixme make sure they're subscribed!
134         $mirror = new SubMirror();
135
136         $mirror->subscriber = $subscriber->id;
137         $mirror->subscribed = $subscribed->id;
138         $mirror->style = $style;
139
140         $mirror->created = common_sql_now();
141         $mirror->modified = common_sql_now();
142         $mirror->insert();
143
144         return $mirror;
145     }
146
147     /**
148      * @param Notice $notice
149      * @return mixed Notice on successful mirroring, boolean if not
150      */
151     public function mirrorNotice(Notice $notice)
152     {
153         $profile = Profile::getKV('id', $this->subscriber);
154         if (!($profile instanceof Profile)) {
155             common_log(LOG_ERR, "SubMirror plugin skipping auto-repeat of notice $notice->id for missing user $profile->id");
156             return false;
157         }
158
159         if ($this->style == 'copy') {
160             return $this->copyNotice($profile, $notice);
161         } else { // default to repeat mode
162             return $this->repeatNotice($profile, $notice);
163         }
164     }
165
166     /**
167      * Mirror a notice using StatusNet's repeat functionality.
168      * This retains attribution within the site, and other nice things,
169      * but currently ends up looking like 'RT @foobar bla bla' when
170      * bridged out over OStatus or TwitterBridge.
171      *
172      * @param Notice $notice
173      * @return mixed Notice on successful repeat, true if already repeated, false on failure
174      */
175     protected function repeatNotice(Profile $profile, Notice $notice)
176     {
177         if($profile->hasRepeated($notice)) {
178             common_log(LOG_INFO, "SubMirror plugin skipping auto-repeat of notice $notice->id for user $profile->id; already repeated.");
179             return true;
180         } else {
181             common_log(LOG_INFO, "SubMirror plugin auto-repeating notice $notice->id for $profile->id");
182             return $notice->repeat($profile, 'mirror');
183         }
184     }
185
186     /**
187      * Mirror a notice by emitting a new notice with the same contents.
188      * Kind of dirty, but if pulling an external data feed into an account
189      * that may be what you want.
190      *
191      * @param Notice $notice
192      * @return mixed Notice on successful repeat, true if already repeated, false on failure
193      */
194     protected function copyNotice($profile, $notice)
195     {
196         $options = array('is_local' => Notice::LOCAL_PUBLIC,
197                          'url' => $notice->getUrl(), // pass through the foreign link...
198                          'rendered' => $notice->rendered);
199
200         $saved = Notice::saveNew($profile->id,
201                                  $notice->content,
202                                  'feed',
203                                  $options);
204         return $saved;
205     }
206
207     /**
208      * Get the mirroring setting for a pair of profiles, if existing.
209      *
210      * @param Profile $subscriber
211      * @param Profile $subscribed
212      * @return mixed Profile or empty
213      */
214     public static function getMirror($subscriber, $subscribed)
215     {
216         return self::pkeyGet(array('subscriber' => $subscriber->id,
217                                    'subscribed' => $subscribed->id));
218     }
219 }