]> git.mxchange.org Git - friendica-addons.git/blob - statusnet/statusnet.php
Some notices had been removed / expiring of item related tables in twitter (#643)
[friendica-addons.git] / statusnet / statusnet.php
1 <?php
2
3 /**
4  * Name: GNU Social Connector
5  * Description: Bidirectional (posting, relaying and reading) connector for GNU Social.
6  * Version: 1.0.5
7  * Author: Tobias Diekershoff <https://f.diekershoff.de/profile/tobias>
8  * Author: Michael Vogel <https://pirati.ca/profile/heluecht>
9  *
10  * Copyright (c) 2011-2013 Tobias Diekershoff, Michael Vogel
11  * All rights reserved.
12  *
13  * Redistribution and use in source and binary forms, with or without
14  * modification, are permitted provided that the following conditions are met:
15  *    * Redistributions of source code must retain the above copyright notice,
16  *     this list of conditions and the following disclaimer.
17  *    * Redistributions in binary form must reproduce the above
18  *    * copyright notice, this list of conditions and the following disclaimer in
19  *      the documentation and/or other materials provided with the distribution.
20  *    * Neither the name of the <organization> nor the names of its contributors
21  *      may be used to endorse or promote products derived from this software
22  *      without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
26  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
27  * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY DIRECT,
28  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
29  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
30  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
31  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
32  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
33  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34  *
35  */
36 define('STATUSNET_DEFAULT_POLL_INTERVAL', 5); // given in minutes
37
38 require_once __DIR__ . DIRECTORY_SEPARATOR . 'library' . DIRECTORY_SEPARATOR . 'statusnetoauth.php';
39 require_once 'include/enotify.php';
40
41 use CodebirdSN\CodebirdSN;
42 use Friendica\App;
43 use Friendica\Content\OEmbed;
44 use Friendica\Content\Text\HTML;
45 use Friendica\Content\Text\Plaintext;
46 use Friendica\Core\Addon;
47 use Friendica\Core\Config;
48 use Friendica\Core\L10n;
49 use Friendica\Core\PConfig;
50 use Friendica\Model\GContact;
51 use Friendica\Model\Group;
52 use Friendica\Model\Item;
53 use Friendica\Model\ItemContent;
54 use Friendica\Model\Photo;
55 use Friendica\Model\User;
56 use Friendica\Util\DateTimeFormat;
57 use Friendica\Util\Network;
58 use Friendica\Database\DBM;
59
60 function statusnet_install()
61 {
62         //  we need some hooks, for the configuration and for sending tweets
63         Addon::registerHook('connector_settings', 'addon/statusnet/statusnet.php', 'statusnet_settings');
64         Addon::registerHook('connector_settings_post', 'addon/statusnet/statusnet.php', 'statusnet_settings_post');
65         Addon::registerHook('notifier_normal', 'addon/statusnet/statusnet.php', 'statusnet_post_hook');
66         Addon::registerHook('post_local', 'addon/statusnet/statusnet.php', 'statusnet_post_local');
67         Addon::registerHook('jot_networks', 'addon/statusnet/statusnet.php', 'statusnet_jot_nets');
68         Addon::registerHook('cron', 'addon/statusnet/statusnet.php', 'statusnet_cron');
69         Addon::registerHook('prepare_body', 'addon/statusnet/statusnet.php', 'statusnet_prepare_body');
70         Addon::registerHook('check_item_notification', 'addon/statusnet/statusnet.php', 'statusnet_check_item_notification');
71         logger("installed GNU Social");
72 }
73
74 function statusnet_uninstall()
75 {
76         Addon::unregisterHook('connector_settings', 'addon/statusnet/statusnet.php', 'statusnet_settings');
77         Addon::unregisterHook('connector_settings_post', 'addon/statusnet/statusnet.php', 'statusnet_settings_post');
78         Addon::unregisterHook('notifier_normal', 'addon/statusnet/statusnet.php', 'statusnet_post_hook');
79         Addon::unregisterHook('post_local', 'addon/statusnet/statusnet.php', 'statusnet_post_local');
80         Addon::unregisterHook('jot_networks', 'addon/statusnet/statusnet.php', 'statusnet_jot_nets');
81         Addon::unregisterHook('cron', 'addon/statusnet/statusnet.php', 'statusnet_cron');
82         Addon::unregisterHook('prepare_body', 'addon/statusnet/statusnet.php', 'statusnet_prepare_body');
83         Addon::unregisterHook('check_item_notification', 'addon/statusnet/statusnet.php', 'statusnet_check_item_notification');
84
85         // old setting - remove only
86         Addon::unregisterHook('post_local_end', 'addon/statusnet/statusnet.php', 'statusnet_post_hook');
87         Addon::unregisterHook('addon_settings', 'addon/statusnet/statusnet.php', 'statusnet_settings');
88         Addon::unregisterHook('addon_settings_post', 'addon/statusnet/statusnet.php', 'statusnet_settings_post');
89 }
90
91 function statusnet_check_item_notification(App $a, &$notification_data)
92 {
93         $notification_data["profiles"][] = PConfig::get($notification_data["uid"], 'statusnet', 'own_url');
94 }
95
96 function statusnet_jot_nets(App $a, &$b)
97 {
98         if (!local_user()) {
99                 return;
100         }
101
102         $statusnet_post = PConfig::get(local_user(), 'statusnet', 'post');
103         if (intval($statusnet_post) == 1) {
104                 $statusnet_defpost = PConfig::get(local_user(), 'statusnet', 'post_by_default');
105                 $selected = ((intval($statusnet_defpost) == 1) ? ' checked="checked" ' : '');
106                 $b .= '<div class="profile-jot-net"><input type="checkbox" name="statusnet_enable"' . $selected . ' value="1" /> '
107                         . L10n::t('Post to GNU Social') . '</div>';
108         }
109 }
110
111 function statusnet_settings_post(App $a, $post)
112 {
113         if (!local_user()) {
114                 return;
115         }
116         // don't check GNU Social settings if GNU Social submit button is not clicked
117         if (!x($_POST, 'statusnet-submit')) {
118                 return;
119         }
120
121         if (isset($_POST['statusnet-disconnect'])) {
122                 /*               * *
123                  * if the GNU Social-disconnect checkbox is set, clear the GNU Social configuration
124                  */
125                 PConfig::delete(local_user(), 'statusnet', 'consumerkey');
126                 PConfig::delete(local_user(), 'statusnet', 'consumersecret');
127                 PConfig::delete(local_user(), 'statusnet', 'post');
128                 PConfig::delete(local_user(), 'statusnet', 'post_by_default');
129                 PConfig::delete(local_user(), 'statusnet', 'oauthtoken');
130                 PConfig::delete(local_user(), 'statusnet', 'oauthsecret');
131                 PConfig::delete(local_user(), 'statusnet', 'baseapi');
132                 PConfig::delete(local_user(), 'statusnet', 'lastid');
133                 PConfig::delete(local_user(), 'statusnet', 'mirror_posts');
134                 PConfig::delete(local_user(), 'statusnet', 'import');
135                 PConfig::delete(local_user(), 'statusnet', 'create_user');
136                 PConfig::delete(local_user(), 'statusnet', 'own_id');
137         } else {
138                 if (isset($_POST['statusnet-preconf-apiurl'])) {
139                         /*                       * *
140                          * If the user used one of the preconfigured GNU Social server credentials
141                          * use them. All the data are available in the global config.
142                          * Check the API Url never the less and blame the admin if it's not working ^^
143                          */
144                         $globalsn = Config::get('statusnet', 'sites');
145                         foreach ($globalsn as $asn) {
146                                 if ($asn['apiurl'] == $_POST['statusnet-preconf-apiurl']) {
147                                         $apibase = $asn['apiurl'];
148                                         $c = Network::fetchUrl($apibase . 'statusnet/version.xml');
149                                         if (strlen($c) > 0) {
150                                                 PConfig::set(local_user(), 'statusnet', 'consumerkey', $asn['consumerkey']);
151                                                 PConfig::set(local_user(), 'statusnet', 'consumersecret', $asn['consumersecret']);
152                                                 PConfig::set(local_user(), 'statusnet', 'baseapi', $asn['apiurl']);
153                                                 //PConfig::set(local_user(), 'statusnet', 'application_name', $asn['applicationname'] );
154                                         } else {
155                                                 notice(L10n::t('Please contact your site administrator.<br />The provided API URL is not valid.') . EOL . $asn['apiurl'] . EOL);
156                                         }
157                                 }
158                         }
159                         goaway('settings/connectors');
160                 } else {
161                         if (isset($_POST['statusnet-consumersecret'])) {
162                                 //  check if we can reach the API of the GNU Social server
163                                 //  we'll check the API Version for that, if we don't get one we'll try to fix the path but will
164                                 //  resign quickly after this one try to fix the path ;-)
165                                 $apibase = $_POST['statusnet-baseapi'];
166                                 $c = Network::fetchUrl($apibase . 'statusnet/version.xml');
167                                 if (strlen($c) > 0) {
168                                         //  ok the API path is correct, let's save the settings
169                                         PConfig::set(local_user(), 'statusnet', 'consumerkey', $_POST['statusnet-consumerkey']);
170                                         PConfig::set(local_user(), 'statusnet', 'consumersecret', $_POST['statusnet-consumersecret']);
171                                         PConfig::set(local_user(), 'statusnet', 'baseapi', $apibase);
172                                         //PConfig::set(local_user(), 'statusnet', 'application_name', $_POST['statusnet-applicationname'] );
173                                 } else {
174                                         //  the API path is not correct, maybe missing trailing / ?
175                                         $apibase = $apibase . '/';
176                                         $c = Network::fetchUrl($apibase . 'statusnet/version.xml');
177                                         if (strlen($c) > 0) {
178                                                 //  ok the API path is now correct, let's save the settings
179                                                 PConfig::set(local_user(), 'statusnet', 'consumerkey', $_POST['statusnet-consumerkey']);
180                                                 PConfig::set(local_user(), 'statusnet', 'consumersecret', $_POST['statusnet-consumersecret']);
181                                                 PConfig::set(local_user(), 'statusnet', 'baseapi', $apibase);
182                                         } else {
183                                                 //  still not the correct API base, let's do noting
184                                                 notice(L10n::t('We could not contact the GNU Social API with the Path you entered.') . EOL);
185                                         }
186                                 }
187                                 goaway('settings/connectors');
188                         } else {
189                                 if (isset($_POST['statusnet-pin'])) {
190                                         //  if the user supplied us with a PIN from GNU Social, let the magic of OAuth happen
191                                         $api = PConfig::get(local_user(), 'statusnet', 'baseapi');
192                                         $ckey = PConfig::get(local_user(), 'statusnet', 'consumerkey');
193                                         $csecret = PConfig::get(local_user(), 'statusnet', 'consumersecret');
194                                         //  the token and secret for which the PIN was generated were hidden in the settings
195                                         //  form as token and token2, we need a new connection to GNU Social using these token
196                                         //  and secret to request a Access Token with the PIN
197                                         $connection = new StatusNetOAuth($api, $ckey, $csecret, $_POST['statusnet-token'], $_POST['statusnet-token2']);
198                                         $token = $connection->getAccessToken($_POST['statusnet-pin']);
199                                         //  ok, now that we have the Access Token, save them in the user config
200                                         PConfig::set(local_user(), 'statusnet', 'oauthtoken', $token['oauth_token']);
201                                         PConfig::set(local_user(), 'statusnet', 'oauthsecret', $token['oauth_token_secret']);
202                                         PConfig::set(local_user(), 'statusnet', 'post', 1);
203                                         PConfig::set(local_user(), 'statusnet', 'post_taglinks', 1);
204                                         //  reload the Addon Settings page, if we don't do it see Bug #42
205                                         goaway('settings/connectors');
206                                 } else {
207                                         //  if no PIN is supplied in the POST variables, the user has changed the setting
208                                         //  to post a dent for every new __public__ posting to the wall
209                                         PConfig::set(local_user(), 'statusnet', 'post', intval($_POST['statusnet-enable']));
210                                         PConfig::set(local_user(), 'statusnet', 'post_by_default', intval($_POST['statusnet-default']));
211                                         PConfig::set(local_user(), 'statusnet', 'mirror_posts', intval($_POST['statusnet-mirror']));
212                                         PConfig::set(local_user(), 'statusnet', 'import', intval($_POST['statusnet-import']));
213                                         PConfig::set(local_user(), 'statusnet', 'create_user', intval($_POST['statusnet-create_user']));
214
215                                         if (!intval($_POST['statusnet-mirror']))
216                                                 PConfig::delete(local_user(), 'statusnet', 'lastid');
217
218                                         info(L10n::t('GNU Social settings updated.') . EOL);
219                                 }
220                         }
221                 }
222         }
223 }
224
225 function statusnet_settings(App $a, &$s)
226 {
227         if (!local_user()) {
228                 return;
229         }
230         $a->page['htmlhead'] .= '<link rel="stylesheet"  type="text/css" href="' . $a->get_baseurl() . '/addon/statusnet/statusnet.css' . '" media="all" />' . "\r\n";
231         /*       * *
232          * 1) Check that we have a base api url and a consumer key & secret
233          * 2) If no OAuthtoken & stuff is present, generate button to get some
234          *    allow the user to cancel the connection process at this step
235          * 3) Checkbox for "Send public notices (respect size limitation)
236          */
237         $api     = PConfig::get(local_user(), 'statusnet', 'baseapi');
238         $ckey    = PConfig::get(local_user(), 'statusnet', 'consumerkey');
239         $csecret = PConfig::get(local_user(), 'statusnet', 'consumersecret');
240         $otoken  = PConfig::get(local_user(), 'statusnet', 'oauthtoken');
241         $osecret = PConfig::get(local_user(), 'statusnet', 'oauthsecret');
242         $enabled = PConfig::get(local_user(), 'statusnet', 'post');
243         $checked = (($enabled) ? ' checked="checked" ' : '');
244         $defenabled = PConfig::get(local_user(), 'statusnet', 'post_by_default');
245         $defchecked = (($defenabled) ? ' checked="checked" ' : '');
246         $mirrorenabled = PConfig::get(local_user(), 'statusnet', 'mirror_posts');
247         $mirrorchecked = (($mirrorenabled) ? ' checked="checked" ' : '');
248         $import = PConfig::get(local_user(), 'statusnet', 'import');
249         $importselected = ["", "", ""];
250         $importselected[$import] = ' selected="selected"';
251         //$importenabled = PConfig::get(local_user(),'statusnet','import');
252         //$importchecked = (($importenabled) ? ' checked="checked" ' : '');
253         $create_userenabled = PConfig::get(local_user(), 'statusnet', 'create_user');
254         $create_userchecked = (($create_userenabled) ? ' checked="checked" ' : '');
255
256         $css = (($enabled) ? '' : '-disabled');
257
258         $s .= '<span id="settings_statusnet_inflated" class="settings-block fakelink" style="display: block;" onclick="openClose(\'settings_statusnet_expanded\'); openClose(\'settings_statusnet_inflated\');">';
259         $s .= '<img class="connector' . $css . '" src="images/gnusocial.png" /><h3 class="connector">' . L10n::t('GNU Social Import/Export/Mirror') . '</h3>';
260         $s .= '</span>';
261         $s .= '<div id="settings_statusnet_expanded" class="settings-block" style="display: none;">';
262         $s .= '<span class="fakelink" onclick="openClose(\'settings_statusnet_expanded\'); openClose(\'settings_statusnet_inflated\');">';
263         $s .= '<img class="connector' . $css . '" src="images/gnusocial.png" /><h3 class="connector">' . L10n::t('GNU Social Import/Export/Mirror') . '</h3>';
264         $s .= '</span>';
265
266         if ((!$ckey) && (!$csecret)) {
267                 /*               * *
268                  * no consumer keys
269                  */
270                 $globalsn = Config::get('statusnet', 'sites');
271                 /*               * *
272                  * lets check if we have one or more globally configured GNU Social
273                  * server OAuth credentials in the configuration. If so offer them
274                  * with a little explanation to the user as choice - otherwise
275                  * ignore this option entirely.
276                  */
277                 if (!$globalsn == null) {
278                         $s .= '<h4>' . L10n::t('Globally Available GNU Social OAuthKeys') . '</h4>';
279                         $s .= '<p>' . L10n::t("There are preconfigured OAuth key pairs for some GNU Social servers available. If you are using one of them, please use these credentials. If not feel free to connect to any other GNU Social instance \x28see below\x29.") . '</p>';
280                         $s .= '<div id="statusnet-preconf-wrapper">';
281                         foreach ($globalsn as $asn) {
282                                 $s .= '<input type="radio" name="statusnet-preconf-apiurl" value="' . $asn['apiurl'] . '">' . $asn['sitename'] . '<br />';
283                         }
284                         $s .= '<p></p><div class="clear"></div></div>';
285                         $s .= '<div class="settings-submit-wrapper" ><input type="submit" name="statusnet-submit" class="settings-submit" value="' . L10n::t('Save Settings') . '" /></div>';
286                 }
287                 $s .= '<h4>' . L10n::t('Provide your own OAuth Credentials') . '</h4>';
288                 $s .= '<p>' . L10n::t('No consumer key pair for GNU Social found. Register your Friendica Account as an desktop client on your GNU Social account, copy the consumer key pair here and enter the API base root.<br />Before you register your own OAuth key pair ask the administrator if there is already a key pair for this Friendica installation at your favorited GNU Social installation.') . '</p>';
289                 $s .= '<div id="statusnet-consumer-wrapper">';
290                 $s .= '<label id="statusnet-consumerkey-label" for="statusnet-consumerkey">' . L10n::t('OAuth Consumer Key') . '</label>';
291                 $s .= '<input id="statusnet-consumerkey" type="text" name="statusnet-consumerkey" size="35" /><br />';
292                 $s .= '<div class="clear"></div>';
293                 $s .= '<label id="statusnet-consumersecret-label" for="statusnet-consumersecret">' . L10n::t('OAuth Consumer Secret') . '</label>';
294                 $s .= '<input id="statusnet-consumersecret" type="text" name="statusnet-consumersecret" size="35" /><br />';
295                 $s .= '<div class="clear"></div>';
296                 $s .= '<label id="statusnet-baseapi-label" for="statusnet-baseapi">' . L10n::t("Base API Path \x28remember the trailing /\x29") . '</label>';
297                 $s .= '<input id="statusnet-baseapi" type="text" name="statusnet-baseapi" size="35" /><br />';
298                 $s .= '<div class="clear"></div>';
299                 //$s .= '<label id="statusnet-applicationname-label" for="statusnet-applicationname">'.L10n::t('GNU Socialapplication name').'</label>';
300                 //$s .= '<input id="statusnet-applicationname" type="text" name="statusnet-applicationname" size="35" /><br />';
301                 $s .= '<p></p><div class="clear"></div>';
302                 $s .= '<div class="settings-submit-wrapper" ><input type="submit" name="statusnet-submit" class="settings-submit" value="' . L10n::t('Save Settings') . '" /></div>';
303                 $s .= '</div>';
304         } else {
305                 /*               * *
306                  * ok we have a consumer key pair now look into the OAuth stuff
307                  */
308                 if ((!$otoken) && (!$osecret)) {
309                         /*                       * *
310                          * the user has not yet connected the account to GNU Social
311                          * get a temporary OAuth key/secret pair and display a button with
312                          * which the user can request a PIN to connect the account to a
313                          * account at GNU Social
314                          */
315                         $connection = new StatusNetOAuth($api, $ckey, $csecret);
316                         $request_token = $connection->getRequestToken('oob');
317                         $token = $request_token['oauth_token'];
318                         /*                       * *
319                          *  make some nice form
320                          */
321                         $s .= '<p>' . L10n::t('To connect to your GNU Social account click the button below to get a security code from GNU Social which you have to copy into the input box below and submit the form. Only your <strong>public</strong> posts will be posted to GNU Social.') . '</p>';
322                         $s .= '<a href="' . $connection->getAuthorizeURL($token, False) . '" target="_statusnet"><img src="addon/statusnet/signinwithstatusnet.png" alt="' . L10n::t('Log in with GNU Social') . '"></a>';
323                         $s .= '<div id="statusnet-pin-wrapper">';
324                         $s .= '<label id="statusnet-pin-label" for="statusnet-pin">' . L10n::t('Copy the security code from GNU Social here') . '</label>';
325                         $s .= '<input id="statusnet-pin" type="text" name="statusnet-pin" />';
326                         $s .= '<input id="statusnet-token" type="hidden" name="statusnet-token" value="' . $token . '" />';
327                         $s .= '<input id="statusnet-token2" type="hidden" name="statusnet-token2" value="' . $request_token['oauth_token_secret'] . '" />';
328                         $s .= '</div><div class="clear"></div>';
329                         $s .= '<div class="settings-submit-wrapper" ><input type="submit" name="statusnet-submit" class="settings-submit" value="' . L10n::t('Save Settings') . '" /></div>';
330                         $s .= '<h4>' . L10n::t('Cancel Connection Process') . '</h4>';
331                         $s .= '<div id="statusnet-cancel-wrapper">';
332                         $s .= '<p>' . L10n::t('Current GNU Social API is') . ': ' . $api . '</p>';
333                         $s .= '<label id="statusnet-cancel-label" for="statusnet-cancel">' . L10n::t('Cancel GNU Social Connection') . '</label>';
334                         $s .= '<input id="statusnet-cancel" type="checkbox" name="statusnet-disconnect" value="1" />';
335                         $s .= '</div><div class="clear"></div>';
336                         $s .= '<div class="settings-submit-wrapper" ><input type="submit" name="statusnet-submit" class="settings-submit" value="' . L10n::t('Save Settings') . '" /></div>';
337                 } else {
338                         /*                       * *
339                          *  we have an OAuth key / secret pair for the user
340                          *  so let's give a chance to disable the postings to GNU Social
341                          */
342                         $connection = new StatusNetOAuth($api, $ckey, $csecret, $otoken, $osecret);
343                         $details = $connection->get('account/verify_credentials');
344                         $s .= '<div id="statusnet-info" ><img id="statusnet-avatar" src="' . $details->profile_image_url . '" /><p id="statusnet-info-block">' . L10n::t('Currently connected to: ') . '<a href="' . $details->statusnet_profile_url . '" target="_statusnet">' . $details->screen_name . '</a><br /><em>' . $details->description . '</em></p></div>';
345                         $s .= '<p>' . L10n::t('If enabled all your <strong>public</strong> postings can be posted to the associated GNU Social account. You can choose to do so by default (here) or for every posting separately in the posting options when writing the entry.') . '</p>';
346                         if ($a->user['hidewall']) {
347                                 $s .= '<p>' . L10n::t('<strong>Note</strong>: Due your privacy settings (<em>Hide your profile details from unknown viewers?</em>) the link potentially included in public postings relayed to GNU Social will lead the visitor to a blank page informing the visitor that the access to your profile has been restricted.') . '</p>';
348                         }
349                         $s .= '<div id="statusnet-enable-wrapper">';
350                         $s .= '<label id="statusnet-enable-label" for="statusnet-checkbox">' . L10n::t('Allow posting to GNU Social') . '</label>';
351                         $s .= '<input id="statusnet-checkbox" type="checkbox" name="statusnet-enable" value="1" ' . $checked . '/>';
352                         $s .= '<div class="clear"></div>';
353                         $s .= '<label id="statusnet-default-label" for="statusnet-default">' . L10n::t('Send public postings to GNU Social by default') . '</label>';
354                         $s .= '<input id="statusnet-default" type="checkbox" name="statusnet-default" value="1" ' . $defchecked . '/>';
355                         $s .= '<div class="clear"></div>';
356
357                         $s .= '<label id="statusnet-mirror-label" for="statusnet-mirror">' . L10n::t('Mirror all posts from GNU Social that are no replies or repeated messages') . '</label>';
358                         $s .= '<input id="statusnet-mirror" type="checkbox" name="statusnet-mirror" value="1" ' . $mirrorchecked . '/>';
359
360                         $s .= '<div class="clear"></div>';
361                         $s .= '</div>';
362
363                         $s .= '<label id="statusnet-import-label" for="statusnet-import">' . L10n::t('Import the remote timeline') . '</label>';
364                         //$s .= '<input id="statusnet-import" type="checkbox" name="statusnet-import" value="1" '. $importchecked . '/>';
365
366                         $s .= '<select name="statusnet-import" id="statusnet-import" />';
367                         $s .= '<option value="0" ' . $importselected[0] . '>' . L10n::t("Disabled") . '</option>';
368                         $s .= '<option value="1" ' . $importselected[1] . '>' . L10n::t("Full Timeline") . '</option>';
369                         $s .= '<option value="2" ' . $importselected[2] . '>' . L10n::t("Only Mentions") . '</option>';
370                         $s .= '</select>';
371                         $s .= '<div class="clear"></div>';
372                         /*
373                           $s .= '<label id="statusnet-create_user-label" for="statusnet-create_user">'.L10n::t('Automatically create contacts').'</label>';
374                           $s .= '<input id="statusnet-create_user" type="checkbox" name="statusnet-create_user" value="1" '. $create_userchecked . '/>';
375                           $s .= '<div class="clear"></div>';
376                          */
377                         $s .= '<div id="statusnet-disconnect-wrapper">';
378                         $s .= '<label id="statusnet-disconnect-label" for="statusnet-disconnect">' . L10n::t('Clear OAuth configuration') . '</label>';
379                         $s .= '<input id="statusnet-disconnect" type="checkbox" name="statusnet-disconnect" value="1" />';
380                         $s .= '</div><div class="clear"></div>';
381                         $s .= '<div class="settings-submit-wrapper" ><input type="submit" name="statusnet-submit" class="settings-submit" value="' . L10n::t('Save Settings') . '" /></div>';
382                 }
383         }
384         $s .= '</div><div class="clear"></div>';
385 }
386
387 function statusnet_post_local(App $a, &$b)
388 {
389         if ($b['edit']) {
390                 return;
391         }
392
393         if (!local_user() || (local_user() != $b['uid'])) {
394                 return;
395         }
396
397         $statusnet_post = PConfig::get(local_user(), 'statusnet', 'post');
398         $statusnet_enable = (($statusnet_post && x($_REQUEST, 'statusnet_enable')) ? intval($_REQUEST['statusnet_enable']) : 0);
399
400         // if API is used, default to the chosen settings
401         if ($b['api_source'] && intval(PConfig::get(local_user(), 'statusnet', 'post_by_default'))) {
402                 $statusnet_enable = 1;
403         }
404
405         if (!$statusnet_enable) {
406                 return;
407         }
408
409         if (strlen($b['postopts'])) {
410                 $b['postopts'] .= ',';
411         }
412
413         $b['postopts'] .= 'statusnet';
414 }
415
416 function statusnet_action(App $a, $uid, $pid, $action)
417 {
418         $api = PConfig::get($uid, 'statusnet', 'baseapi');
419         $ckey = PConfig::get($uid, 'statusnet', 'consumerkey');
420         $csecret = PConfig::get($uid, 'statusnet', 'consumersecret');
421         $otoken = PConfig::get($uid, 'statusnet', 'oauthtoken');
422         $osecret = PConfig::get($uid, 'statusnet', 'oauthsecret');
423
424         $connection = new StatusNetOAuth($api, $ckey, $csecret, $otoken, $osecret);
425
426         logger("statusnet_action '" . $action . "' ID: " . $pid, LOGGER_DATA);
427
428         switch ($action) {
429                 case "delete":
430                         $result = $connection->post("statuses/destroy/" . $pid);
431                         break;
432                 case "like":
433                         $result = $connection->post("favorites/create/" . $pid);
434                         break;
435                 case "unlike":
436                         $result = $connection->post("favorites/destroy/" . $pid);
437                         break;
438         }
439         logger("statusnet_action '" . $action . "' send, result: " . print_r($result, true), LOGGER_DEBUG);
440 }
441
442 function statusnet_post_hook(App $a, &$b)
443 {
444         /**
445          * Post to GNU Social
446          */
447         if (!PConfig::get($b["uid"], 'statusnet', 'import')) {
448                 if ($b['deleted'] || $b['private'] || ($b['created'] !== $b['edited']))
449                         return;
450         }
451
452         $api = PConfig::get($b["uid"], 'statusnet', 'baseapi');
453         $hostname = preg_replace("=https?://([\w\.]*)/.*=ism", "$1", $api);
454
455         if ($b['parent'] != $b['id']) {
456                 logger("statusnet_post_hook: parameter " . print_r($b, true), LOGGER_DATA);
457
458                 // Looking if its a reply to a GNU Social post
459                 $hostlength = strlen($hostname) + 2;
460                 if ((substr($b["parent-uri"], 0, $hostlength) != $hostname . "::") && (substr($b["extid"], 0, $hostlength) != $hostname . "::") && (substr($b["thr-parent"], 0, $hostlength) != $hostname . "::")) {
461                         logger("statusnet_post_hook: no GNU Social post " . $b["parent"]);
462                         return;
463                 }
464
465                 $condition = ['uri' => $b["thr-parent"], 'uid' => $b["uid"]];
466                 $orig_post = Item::selectFirst(['author-link', 'uri'], $condition);
467                 if (!DBM::is_result($orig_post)) {
468                         logger("statusnet_post_hook: no parent found " . $b["thr-parent"]);
469                         return;
470                 } else {
471                         $iscomment = true;
472                 }
473
474                 $nick = preg_replace("=https?://(.*)/(.*)=ism", "$2", $orig_post["author-link"]);
475
476                 $nickname = "@[url=" . $orig_post["author-link"] . "]" . $nick . "[/url]";
477                 $nicknameplain = "@" . $nick;
478
479                 logger("statusnet_post_hook: comparing " . $nickname . " and " . $nicknameplain . " with " . $b["body"], LOGGER_DEBUG);
480                 if ((strpos($b["body"], $nickname) === false) && (strpos($b["body"], $nicknameplain) === false)) {
481                         $b["body"] = $nickname . " " . $b["body"];
482                 }
483
484                 logger("statusnet_post_hook: parent found " . print_r($orig_post, true), LOGGER_DEBUG);
485         } else {
486                 $iscomment = false;
487
488                 if ($b['private'] || !strstr($b['postopts'], 'statusnet')) {
489                         return;
490                 }
491
492                 // Dont't post if the post doesn't belong to us.
493                 // This is a check for forum postings
494                 $self = dba::selectFirst('contact', ['id'], ['uid' => $b['uid'], 'self' => true]);
495                 if ($b['contact-id'] != $self['id']) {
496                         return;
497                 }
498         }
499
500         if (($b['verb'] == ACTIVITY_POST) && $b['deleted']) {
501                 statusnet_action($a, $b["uid"], substr($orig_post["uri"], $hostlength), "delete");
502         }
503
504         if ($b['verb'] == ACTIVITY_LIKE) {
505                 logger("statusnet_post_hook: parameter 2 " . substr($b["thr-parent"], $hostlength), LOGGER_DEBUG);
506                 if ($b['deleted'])
507                         statusnet_action($a, $b["uid"], substr($b["thr-parent"], $hostlength), "unlike");
508                 else
509                         statusnet_action($a, $b["uid"], substr($b["thr-parent"], $hostlength), "like");
510                 return;
511         }
512
513         if ($b['deleted'] || ($b['created'] !== $b['edited'])) {
514                 return;
515         }
516
517         // if posts comes from GNU Social don't send it back
518         if ($b['extid'] == NETWORK_STATUSNET) {
519                 return;
520         }
521
522         if ($b['app'] == "StatusNet") {
523                 return;
524         }
525
526         logger('GNU Socialpost invoked');
527
528         PConfig::load($b['uid'], 'statusnet');
529
530         $api     = PConfig::get($b['uid'], 'statusnet', 'baseapi');
531         $ckey    = PConfig::get($b['uid'], 'statusnet', 'consumerkey');
532         $csecret = PConfig::get($b['uid'], 'statusnet', 'consumersecret');
533         $otoken  = PConfig::get($b['uid'], 'statusnet', 'oauthtoken');
534         $osecret = PConfig::get($b['uid'], 'statusnet', 'oauthsecret');
535
536         if ($ckey && $csecret && $otoken && $osecret) {
537                 // If it's a repeated message from GNU Social then do a native retweet and exit
538                 if (statusnet_is_retweet($a, $b['uid'], $b['body'])) {
539                         return;
540                 }
541
542                 $dent = new StatusNetOAuth($api, $ckey, $csecret, $otoken, $osecret);
543                 $max_char = $dent->get_maxlength(); // max. length for a dent
544
545                 PConfig::set($b['uid'], 'statusnet', 'max_char', $max_char);
546
547                 $tempfile = "";
548                 $msgarr = ItemContent::getPlaintextPost($b, $max_char, true, 7);
549                 $msg = $msgarr["text"];
550
551                 if (($msg == "") && isset($msgarr["title"]))
552                         $msg = Plaintext::shorten($msgarr["title"], $max_char - 50);
553
554                 $image = "";
555
556                 if (isset($msgarr["url"]) && ($msgarr["type"] != "photo")) {
557                         $msg .= " \n" . $msgarr["url"];
558                 } elseif (isset($msgarr["image"]) && ($msgarr["type"] != "video")) {
559                         $image = $msgarr["image"];
560                 }
561
562                 if ($image != "") {
563                         $img_str = Network::fetchUrl($image);
564                         $tempfile = tempnam(get_temppath(), "cache");
565                         file_put_contents($tempfile, $img_str);
566                         $postdata = ["status" => $msg, "media[]" => $tempfile];
567                 } else {
568                         $postdata = ["status" => $msg];
569                 }
570
571                 // and now send it :-)
572                 if (strlen($msg)) {
573                         if ($iscomment) {
574                                 $postdata["in_reply_to_status_id"] = substr($orig_post["uri"], $hostlength);
575                                 logger('statusnet_post send reply ' . print_r($postdata, true), LOGGER_DEBUG);
576                         }
577
578                         // New code that is able to post pictures
579                         require_once __DIR__ . DIRECTORY_SEPARATOR . 'library' . DIRECTORY_SEPARATOR . 'codebirdsn.php';
580                         $cb = CodebirdSN::getInstance();
581                         $cb->setAPIEndpoint($api);
582                         $cb->setConsumerKey($ckey, $csecret);
583                         $cb->setToken($otoken, $osecret);
584                         $result = $cb->statuses_update($postdata);
585                         //$result = $dent->post('statuses/update', $postdata);
586                         logger('statusnet_post send, result: ' . print_r($result, true) .
587                                 "\nmessage: " . $msg, LOGGER_DEBUG . "\nOriginal post: " . print_r($b, true) . "\nPost Data: " . print_r($postdata, true));
588
589                         if (!empty($result->source)) {
590                                 PConfig::set($b["uid"], "statusnet", "application_name", strip_tags($result->source));
591                         }
592
593                         if (!empty($result->error)) {
594                                 logger('Send to GNU Social failed: "' . $result->error . '"');
595                         } elseif ($iscomment) {
596                                 logger('statusnet_post: Update extid ' . $result->id . " for post id " . $b['id']);
597                                 Item::update(['extid' => $hostname . "::" . $result->id, 'body' => $result->text], ['id' => $b['id']]);
598                         }
599                 }
600                 if ($tempfile != "") {
601                         unlink($tempfile);
602                 }
603         }
604 }
605
606 function statusnet_addon_admin_post(App $a)
607 {
608         $sites = [];
609
610         foreach ($_POST['sitename'] as $id => $sitename) {
611                 $sitename = trim($sitename);
612                 $apiurl = trim($_POST['apiurl'][$id]);
613                 if (!(substr($apiurl, -1) == '/')) {
614                         $apiurl = $apiurl . '/';
615                 }
616                 $secret = trim($_POST['secret'][$id]);
617                 $key = trim($_POST['key'][$id]);
618                 //$applicationname = ((x($_POST, 'applicationname')) ? notags(trim($_POST['applicationname'][$id])):'');
619                 if ($sitename != "" &&
620                         $apiurl != "" &&
621                         $secret != "" &&
622                         $key != "" &&
623                         !x($_POST['delete'][$id])) {
624
625                         $sites[] = [
626                                 'sitename' => $sitename,
627                                 'apiurl' => $apiurl,
628                                 'consumersecret' => $secret,
629                                 'consumerkey' => $key,
630                                 //'applicationname' => $applicationname
631                         ];
632                 }
633         }
634
635         $sites = Config::set('statusnet', 'sites', $sites);
636 }
637
638 function statusnet_addon_admin(App $a, &$o)
639 {
640         $sites = Config::get('statusnet', 'sites');
641         $sitesform = [];
642         if (is_array($sites)) {
643                 foreach ($sites as $id => $s) {
644                         $sitesform[] = [
645                                 'sitename' => ["sitename[$id]", "Site name", $s['sitename'], ""],
646                                 'apiurl' => ["apiurl[$id]", "Api url", $s['apiurl'], L10n::t("Base API Path \x28remember the trailing /\x29")],
647                                 'secret' => ["secret[$id]", "Secret", $s['consumersecret'], ""],
648                                 'key' => ["key[$id]", "Key", $s['consumerkey'], ""],
649                                 //'applicationname' => Array("applicationname[$id]", "Application name", $s['applicationname'], ""),
650                                 'delete' => ["delete[$id]", "Delete", False, "Check to delete this preset"],
651                         ];
652                 }
653         }
654         /* empty form to add new site */
655         $id++;
656         $sitesform[] = [
657                 'sitename' => ["sitename[$id]", L10n::t("Site name"), "", ""],
658                 'apiurl' => ["apiurl[$id]", "Api url", "", L10n::t("Base API Path \x28remember the trailing /\x29")],
659                 'secret' => ["secret[$id]", L10n::t("Consumer Secret"), "", ""],
660                 'key' => ["key[$id]", L10n::t("Consumer Key"), "", ""],
661                 //'applicationname' => Array("applicationname[$id]", L10n::t("Application name"), "", ""),
662         ];
663
664         $t = get_markup_template("admin.tpl", "addon/statusnet/");
665         $o = replace_macros($t, [
666                 '$submit' => L10n::t('Save Settings'),
667                 '$sites' => $sitesform,
668         ]);
669 }
670
671 function statusnet_prepare_body(App $a, &$b)
672 {
673         if ($b["item"]["network"] != NETWORK_STATUSNET) {
674                 return;
675         }
676
677         if ($b["preview"]) {
678                 $max_char = PConfig::get(local_user(), 'statusnet', 'max_char');
679                 if (intval($max_char) == 0) {
680                         $max_char = 140;
681                 }
682
683                 $item = $b["item"];
684                 $item["plink"] = $a->get_baseurl() . "/display/" . $a->user["nickname"] . "/" . $item["parent"];
685
686                 $condition = ['uri' => $item["thr-parent"], 'uid' => local_user()];
687                 $orig_post = Item::selectFirst(['author-link', 'uri'], $condition);
688                 if (DBM::is_result($orig_post)) {
689                         $nick = preg_replace("=https?://(.*)/(.*)=ism", "$2", $orig_post["author-link"]);
690
691                         $nickname = "@[url=" . $orig_post["author-link"] . "]" . $nick . "[/url]";
692                         $nicknameplain = "@" . $nick;
693
694                         if ((strpos($item["body"], $nickname) === false) && (strpos($item["body"], $nicknameplain) === false)) {
695                                 $item["body"] = $nickname . " " . $item["body"];
696                         }
697                 }
698
699                 $msgarr = ItemContent::getPlaintextPost($item, $max_char, true, 7);
700                 $msg = $msgarr["text"];
701
702                 if (isset($msgarr["url"]) && ($msgarr["type"] != "photo")) {
703                         $msg .= " " . $msgarr["url"];
704                 }
705
706                 if (isset($msgarr["image"])) {
707                         $msg .= " " . $msgarr["image"];
708                 }
709
710                 $b['html'] = nl2br(htmlspecialchars($msg));
711         }
712 }
713
714 function statusnet_cron(App $a, $b)
715 {
716         $last = Config::get('statusnet', 'last_poll');
717
718         $poll_interval = intval(Config::get('statusnet', 'poll_interval'));
719         if (!$poll_interval) {
720                 $poll_interval = STATUSNET_DEFAULT_POLL_INTERVAL;
721         }
722
723         if ($last) {
724                 $next = $last + ($poll_interval * 60);
725                 if ($next > time()) {
726                         logger('statusnet: poll intervall not reached');
727                         return;
728                 }
729         }
730         logger('statusnet: cron_start');
731
732         $r = q("SELECT * FROM `pconfig` WHERE `cat` = 'statusnet' AND `k` = 'mirror_posts' AND `v` = '1' ORDER BY RAND() ");
733         if (DBM::is_result($r)) {
734                 foreach ($r as $rr) {
735                         logger('statusnet: fetching for user ' . $rr['uid']);
736                         statusnet_fetchtimeline($a, $rr['uid']);
737                 }
738         }
739
740         $abandon_days = intval(Config::get('system', 'account_abandon_days'));
741         if ($abandon_days < 1) {
742                 $abandon_days = 0;
743         }
744
745         $abandon_limit = date(DateTimeFormat::MYSQL, time() - $abandon_days * 86400);
746
747         $r = q("SELECT * FROM `pconfig` WHERE `cat` = 'statusnet' AND `k` = 'import' AND `v` ORDER BY RAND()");
748         if (DBM::is_result($r)) {
749                 foreach ($r as $rr) {
750                         if ($abandon_days != 0) {
751                                 $user = q("SELECT `login_date` FROM `user` WHERE uid=%d AND `login_date` >= '%s'", $rr['uid'], $abandon_limit);
752                                 if (!DBM::is_result($user)) {
753                                         logger('abandoned account: timeline from user ' . $rr['uid'] . ' will not be imported');
754                                         continue;
755                                 }
756                         }
757
758                         logger('statusnet: importing timeline from user ' . $rr['uid']);
759                         statusnet_fetchhometimeline($a, $rr["uid"], $rr["v"]);
760                 }
761         }
762
763         logger('statusnet: cron_end');
764
765         Config::set('statusnet', 'last_poll', time());
766 }
767
768 function statusnet_fetchtimeline(App $a, $uid)
769 {
770         $ckey    = PConfig::get($uid, 'statusnet', 'consumerkey');
771         $csecret = PConfig::get($uid, 'statusnet', 'consumersecret');
772         $api     = PConfig::get($uid, 'statusnet', 'baseapi');
773         $otoken  = PConfig::get($uid, 'statusnet', 'oauthtoken');
774         $osecret = PConfig::get($uid, 'statusnet', 'oauthsecret');
775         $lastid  = PConfig::get($uid, 'statusnet', 'lastid');
776
777         require_once 'mod/item.php';
778         require_once 'include/items.php';
779
780         //  get the application name for the SN app
781         //  1st try personal config, then system config and fallback to the
782         //  hostname of the node if neither one is set.
783         $application_name = PConfig::get($uid, 'statusnet', 'application_name');
784         if ($application_name == "") {
785                 $application_name = Config::get('statusnet', 'application_name');
786         }
787         if ($application_name == "") {
788                 $application_name = $a->get_hostname();
789         }
790
791         $connection = new StatusNetOAuth($api, $ckey, $csecret, $otoken, $osecret);
792
793         $parameters = ["exclude_replies" => true, "trim_user" => true, "contributor_details" => false, "include_rts" => false];
794
795         $first_time = ($lastid == "");
796
797         if ($lastid <> "") {
798                 $parameters["since_id"] = $lastid;
799         }
800
801         $items = $connection->get('statuses/user_timeline', $parameters);
802
803         if (!is_array($items)) {
804                 return;
805         }
806
807         $posts = array_reverse($items);
808
809         if (count($posts)) {
810                 foreach ($posts as $post) {
811                         if ($post->id > $lastid)
812                                 $lastid = $post->id;
813
814                         if ($first_time) {
815                                 continue;
816                         }
817
818                         if ($post->source == "activity") {
819                                 continue;
820                         }
821
822                         if (!empty($post->retweeted_status)) {
823                                 continue;
824                         }
825
826                         if ($post->in_reply_to_status_id != "") {
827                                 continue;
828                         }
829
830                         if (!stristr($post->source, $application_name)) {
831                                 $_SESSION["authenticated"] = true;
832                                 $_SESSION["uid"] = $uid;
833
834                                 unset($_REQUEST);
835                                 $_REQUEST["type"] = "wall";
836                                 $_REQUEST["api_source"] = true;
837                                 $_REQUEST["profile_uid"] = $uid;
838                                 //$_REQUEST["source"] = "StatusNet";
839                                 $_REQUEST["source"] = $post->source;
840                                 $_REQUEST["extid"] = NETWORK_STATUSNET;
841
842                                 if (isset($post->id)) {
843                                         $_REQUEST['message_id'] = Item::newURI($uid, NETWORK_STATUSNET . ":" . $post->id);
844                                 }
845
846                                 //$_REQUEST["date"] = $post->created_at;
847
848                                 $_REQUEST["title"] = "";
849
850                                 $_REQUEST["body"] = add_page_info_to_body($post->text, true);
851                                 if (is_string($post->place->name)) {
852                                         $_REQUEST["location"] = $post->place->name;
853                                 }
854
855                                 if (is_string($post->place->full_name)) {
856                                         $_REQUEST["location"] = $post->place->full_name;
857                                 }
858
859                                 if (is_array($post->geo->coordinates)) {
860                                         $_REQUEST["coord"] = $post->geo->coordinates[0] . " " . $post->geo->coordinates[1];
861                                 }
862
863                                 if (is_array($post->coordinates->coordinates)) {
864                                         $_REQUEST["coord"] = $post->coordinates->coordinates[1] . " " . $post->coordinates->coordinates[0];
865                                 }
866
867                                 //print_r($_REQUEST);
868                                 if ($_REQUEST["body"] != "") {
869                                         logger('statusnet: posting for user ' . $uid);
870
871                                         item_post($a);
872                                 }
873                         }
874                 }
875         }
876         PConfig::set($uid, 'statusnet', 'lastid', $lastid);
877 }
878
879 function statusnet_address($contact)
880 {
881         $hostname = normalise_link($contact->statusnet_profile_url);
882         $nickname = $contact->screen_name;
883
884         $hostname = preg_replace("=https?://([\w\.]*)/.*=ism", "$1", $contact->statusnet_profile_url);
885
886         $address = $contact->screen_name . "@" . $hostname;
887
888         return $address;
889 }
890
891 function statusnet_fetch_contact($uid, $contact, $create_user)
892 {
893         if ($contact->statusnet_profile_url == "") {
894                 return -1;
895         }
896
897         GContact::update(["url" => $contact->statusnet_profile_url,
898                 "network" => NETWORK_STATUSNET, "photo" => $contact->profile_image_url,
899                 "name" => $contact->name, "nick" => $contact->screen_name,
900                 "location" => $contact->location, "about" => $contact->description,
901                 "addr" => statusnet_address($contact), "generation" => 3]);
902
903         $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `alias` = '%s' AND `network` = '%s'LIMIT 1", intval($uid), dbesc(normalise_link($contact->statusnet_profile_url)), dbesc(NETWORK_STATUSNET));
904
905         if (!DBM::is_result($r) && !$create_user) {
906                 return 0;
907         }
908
909         if (DBM::is_result($r) && ($r[0]["readonly"] || $r[0]["blocked"])) {
910                 logger("statusnet_fetch_contact: Contact '" . $r[0]["nick"] . "' is blocked or readonly.", LOGGER_DEBUG);
911                 return -1;
912         }
913
914         if (!DBM::is_result($r)) {
915                 // create contact record
916                 q("INSERT INTO `contact` ( `uid`, `created`, `url`, `nurl`, `addr`, `alias`, `notify`, `poll`,
917                                         `name`, `nick`, `photo`, `network`, `rel`, `priority`,
918                                         `location`, `about`, `writable`, `blocked`, `readonly`, `pending` )
919                                         VALUES ( %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, '%s', '%s', %d, 0, 0, 0 ) ",
920                         intval($uid),
921                         dbesc(DateTimeFormat::utcNow()),
922                         dbesc($contact->statusnet_profile_url),
923                         dbesc(normalise_link($contact->statusnet_profile_url)),
924                         dbesc(statusnet_address($contact)),
925                         dbesc(normalise_link($contact->statusnet_profile_url)),
926                         dbesc(''),
927                         dbesc(''),
928                         dbesc($contact->name),
929                         dbesc($contact->screen_name),
930                         dbesc($contact->profile_image_url),
931                         dbesc(NETWORK_STATUSNET),
932                         intval(CONTACT_IS_FRIEND),
933                         intval(1),
934                         dbesc($contact->location),
935                         dbesc($contact->description),
936                         intval(1)
937                 );
938
939                 $r = q("SELECT * FROM `contact` WHERE `alias` = '%s' AND `uid` = %d AND `network` = '%s' LIMIT 1",
940                         dbesc($contact->statusnet_profile_url),
941                         intval($uid),
942                         dbesc(NETWORK_STATUSNET));
943
944                 if (!DBM::is_result($r)) {
945                         return false;
946                 }
947
948                 $contact_id = $r[0]['id'];
949
950                 Group::addMember(User::getDefaultGroup($uid), $contact_id);
951
952                 $photos = Photo::importProfilePhoto($contact->profile_image_url, $uid, $contact_id);
953
954                 q("UPDATE `contact` SET `photo` = '%s',
955                                         `thumb` = '%s',
956                                         `micro` = '%s',
957                                         `avatar-date` = '%s'
958                                 WHERE `id` = %d",
959                         dbesc($photos[0]),
960                         dbesc($photos[1]),
961                         dbesc($photos[2]),
962                         dbesc(DateTimeFormat::utcNow()),
963                         intval($contact_id)
964                 );
965         } else {
966                 // update profile photos once every two weeks as we have no notification of when they change.
967                 //$update_photo = (($r[0]['avatar-date'] < DateTimeFormat::convert('now -2 days', '', '', )) ? true : false);
968                 $update_photo = ($r[0]['avatar-date'] < DateTimeFormat::utc('now -12 hours'));
969
970                 // check that we have all the photos, this has been known to fail on occasion
971                 if ((!$r[0]['photo']) || (!$r[0]['thumb']) || (!$r[0]['micro']) || ($update_photo)) {
972                         logger("statusnet_fetch_contact: Updating contact " . $contact->screen_name, LOGGER_DEBUG);
973
974                         $photos = Photo::importProfilePhoto($contact->profile_image_url, $uid, $r[0]['id']);
975
976                         q("UPDATE `contact` SET `photo` = '%s',
977                                                 `thumb` = '%s',
978                                                 `micro` = '%s',
979                                                 `name-date` = '%s',
980                                                 `uri-date` = '%s',
981                                                 `avatar-date` = '%s',
982                                                 `url` = '%s',
983                                                 `nurl` = '%s',
984                                                 `addr` = '%s',
985                                                 `name` = '%s',
986                                                 `nick` = '%s',
987                                                 `location` = '%s',
988                                                 `about` = '%s'
989                                         WHERE `id` = %d",
990                                 dbesc($photos[0]),
991                                 dbesc($photos[1]),
992                                 dbesc($photos[2]),
993                                 dbesc(DateTimeFormat::utcNow()),
994                                 dbesc(DateTimeFormat::utcNow()),
995                                 dbesc(DateTimeFormat::utcNow()),
996                                 dbesc($contact->statusnet_profile_url),
997                                 dbesc(normalise_link($contact->statusnet_profile_url)),
998                                 dbesc(statusnet_address($contact)),
999                                 dbesc($contact->name),
1000                                 dbesc($contact->screen_name),
1001                                 dbesc($contact->location),
1002                                 dbesc($contact->description),
1003                                 intval($r[0]['id'])
1004                         );
1005                 }
1006         }
1007
1008         return $r[0]["id"];
1009 }
1010
1011 function statusnet_fetchuser(App $a, $uid, $screen_name = "", $user_id = "")
1012 {
1013         $ckey    = PConfig::get($uid, 'statusnet', 'consumerkey');
1014         $csecret = PConfig::get($uid, 'statusnet', 'consumersecret');
1015         $api     = PConfig::get($uid, 'statusnet', 'baseapi');
1016         $otoken  = PConfig::get($uid, 'statusnet', 'oauthtoken');
1017         $osecret = PConfig::get($uid, 'statusnet', 'oauthsecret');
1018
1019         require_once __DIR__ . DIRECTORY_SEPARATOR . 'library' . DIRECTORY_SEPARATOR . 'codebirdsn.php';
1020
1021         $cb = CodebirdSN::getInstance();
1022         $cb->setConsumerKey($ckey, $csecret);
1023         $cb->setToken($otoken, $osecret);
1024
1025         $r = q("SELECT * FROM `contact` WHERE `self` = 1 AND `uid` = %d LIMIT 1",
1026                 intval($uid));
1027
1028         if (DBM::is_result($r)) {
1029                 $self = $r[0];
1030         } else {
1031                 return;
1032         }
1033
1034         $parameters = [];
1035
1036         if ($screen_name != "") {
1037                 $parameters["screen_name"] = $screen_name;
1038         }
1039
1040         if ($user_id != "") {
1041                 $parameters["user_id"] = $user_id;
1042         }
1043
1044         // Fetching user data
1045         $user = $cb->users_show($parameters);
1046
1047         if (!is_object($user)) {
1048                 return;
1049         }
1050
1051         $contact_id = statusnet_fetch_contact($uid, $user, true);
1052
1053         return $contact_id;
1054 }
1055
1056 function statusnet_createpost(App $a, $uid, $post, $self, $create_user, $only_existing_contact)
1057 {
1058         logger("statusnet_createpost: start", LOGGER_DEBUG);
1059
1060         $api = PConfig::get($uid, 'statusnet', 'baseapi');
1061         $hostname = preg_replace("=https?://([\w\.]*)/.*=ism", "$1", $api);
1062
1063         $postarray = [];
1064         $postarray['network'] = NETWORK_STATUSNET;
1065         $postarray['uid'] = $uid;
1066         $postarray['wall'] = 0;
1067
1068         if (!empty($post->retweeted_status)) {
1069                 $content = $post->retweeted_status;
1070                 statusnet_fetch_contact($uid, $content->user, false);
1071         } else {
1072                 $content = $post;
1073         }
1074
1075         $postarray['uri'] = $hostname . "::" . $content->id;
1076
1077         if (dba::exists('item', ['extid' => $postarray['uri'], 'uid' => $uid])) {
1078                 return [];
1079         }
1080
1081         $contactid = 0;
1082
1083         if (!empty($content->in_reply_to_status_id)) {
1084
1085                 $parent = $hostname . "::" . $content->in_reply_to_status_id;
1086
1087                 $fields = ['uri', 'parent-uri', 'parent'];
1088                 $item = Item::selectFirst($fields, ['uri' => $parent, 'uid' => $uid]);
1089
1090                 if (!DBM::is_result($item)) {
1091                         $item = Item::selectFirst($fields, ['extid' => $parent, 'uid' => $uid]);
1092                 }
1093
1094                 if (DBM::is_result($item)) {
1095                         $postarray['thr-parent'] = $item['uri'];
1096                         $postarray['parent-uri'] = $item['parent-uri'];
1097                         $postarray['parent'] = $item['parent'];
1098                         $postarray['object-type'] = ACTIVITY_OBJ_COMMENT;
1099                 } else {
1100                         $postarray['thr-parent'] = $postarray['uri'];
1101                         $postarray['parent-uri'] = $postarray['uri'];
1102                         $postarray['object-type'] = ACTIVITY_OBJ_NOTE;
1103                 }
1104
1105                 // Is it me?
1106                 $own_url = PConfig::get($uid, 'statusnet', 'own_url');
1107
1108                 if ($content->user->id == $own_url) {
1109                         $r = q("SELECT * FROM `contact` WHERE `self` = 1 AND `uid` = %d LIMIT 1",
1110                                 intval($uid));
1111
1112                         if (DBM::is_result($r)) {
1113                                 $contactid = $r[0]["id"];
1114
1115                                 $postarray['owner-name'] = $r[0]["name"];
1116                                 $postarray['owner-link'] = $r[0]["url"];
1117                                 $postarray['owner-avatar'] = $r[0]["photo"];
1118                         } else {
1119                                 return [];
1120                         }
1121                 }
1122                 // Don't create accounts of people who just comment something
1123                 $create_user = false;
1124         } else {
1125                 $postarray['parent-uri'] = $postarray['uri'];
1126                 $postarray['object-type'] = ACTIVITY_OBJ_NOTE;
1127         }
1128
1129         if ($contactid == 0) {
1130                 $contactid = statusnet_fetch_contact($uid, $post->user, $create_user);
1131                 $postarray['owner-name'] = $post->user->name;
1132                 $postarray['owner-link'] = $post->user->statusnet_profile_url;
1133                 $postarray['owner-avatar'] = $post->user->profile_image_url;
1134         }
1135         if (($contactid == 0) && !$only_existing_contact) {
1136                 $contactid = $self['id'];
1137         } elseif ($contactid <= 0) {
1138                 return [];
1139         }
1140
1141         $postarray['contact-id'] = $contactid;
1142
1143         $postarray['verb'] = ACTIVITY_POST;
1144
1145         $postarray['author-name'] = $content->user->name;
1146         $postarray['author-link'] = $content->user->statusnet_profile_url;
1147         $postarray['author-avatar'] = $content->user->profile_image_url;
1148
1149         // To-Do: Maybe unreliable? Can the api be entered without trailing "/"?
1150         $hostname = str_replace("/api/", "/notice/", PConfig::get($uid, 'statusnet', 'baseapi'));
1151
1152         $postarray['plink'] = $hostname . $content->id;
1153         $postarray['app'] = strip_tags($content->source);
1154
1155         if ($content->user->protected) {
1156                 $postarray['private'] = 1;
1157                 $postarray['allow_cid'] = '<' . $self['id'] . '>';
1158         }
1159
1160         $postarray['body'] = HTML::toBBCode($content->statusnet_html);
1161
1162         $converted = statusnet_convertmsg($a, $postarray['body'], false);
1163         $postarray['body'] = $converted["body"];
1164         $postarray['tag'] = $converted["tags"];
1165
1166         $postarray['created'] = DateTimeFormat::utc($content->created_at);
1167         $postarray['edited'] = DateTimeFormat::utc($content->created_at);
1168
1169         if (!empty($content->place->name)) {
1170                 $postarray["location"] = $content->place->name;
1171         }
1172
1173         if (!empty($content->place->full_name)) {
1174                 $postarray["location"] = $content->place->full_name;
1175         }
1176
1177         if (!empty($content->geo->coordinates)) {
1178                 $postarray["coord"] = $content->geo->coordinates[0] . " " . $content->geo->coordinates[1];
1179         }
1180
1181         if (!empty($content->coordinates->coordinates)) {
1182                 $postarray["coord"] = $content->coordinates->coordinates[1] . " " . $content->coordinates->coordinates[0];
1183         }
1184
1185         logger("statusnet_createpost: end", LOGGER_DEBUG);
1186
1187         return $postarray;
1188 }
1189
1190 function statusnet_fetchhometimeline(App $a, $uid, $mode = 1)
1191 {
1192         $conversations = [];
1193
1194         $ckey    = PConfig::get($uid, 'statusnet', 'consumerkey');
1195         $csecret = PConfig::get($uid, 'statusnet', 'consumersecret');
1196         $api     = PConfig::get($uid, 'statusnet', 'baseapi');
1197         $otoken  = PConfig::get($uid, 'statusnet', 'oauthtoken');
1198         $osecret = PConfig::get($uid, 'statusnet', 'oauthsecret');
1199         $create_user = PConfig::get($uid, 'statusnet', 'create_user');
1200
1201         // "create_user" is deactivated, since currently you cannot add users manually by now
1202         $create_user = true;
1203
1204         logger("statusnet_fetchhometimeline: Fetching for user " . $uid, LOGGER_DEBUG);
1205
1206         require_once 'include/items.php';
1207
1208         $connection = new StatusNetOAuth($api, $ckey, $csecret, $otoken, $osecret);
1209
1210         $own_contact = statusnet_fetch_own_contact($a, $uid);
1211
1212         $r = q("SELECT * FROM `contact` WHERE `id` = %d AND `uid` = %d LIMIT 1",
1213                 intval($own_contact),
1214                 intval($uid));
1215
1216         if (DBM::is_result($r)) {
1217                 $nick = $r[0]["nick"];
1218         } else {
1219                 logger("statusnet_fetchhometimeline: Own GNU Social contact not found for user " . $uid, LOGGER_DEBUG);
1220                 return;
1221         }
1222
1223         $r = q("SELECT * FROM `contact` WHERE `self` = 1 AND `uid` = %d LIMIT 1",
1224                 intval($uid));
1225
1226         if (DBM::is_result($r)) {
1227                 $self = $r[0];
1228         } else {
1229                 logger("statusnet_fetchhometimeline: Own contact not found for user " . $uid, LOGGER_DEBUG);
1230                 return;
1231         }
1232
1233         $u = q("SELECT * FROM user WHERE uid = %d LIMIT 1",
1234                 intval($uid));
1235         if (!DBM::is_result($u)) {
1236                 logger("statusnet_fetchhometimeline: Own user not found for user " . $uid, LOGGER_DEBUG);
1237                 return;
1238         }
1239
1240         $parameters = ["exclude_replies" => false, "trim_user" => false, "contributor_details" => true, "include_rts" => true];
1241         //$parameters["count"] = 200;
1242
1243         if ($mode == 1) {
1244                 // Fetching timeline
1245                 $lastid = PConfig::get($uid, 'statusnet', 'lasthometimelineid');
1246                 //$lastid = 1;
1247
1248                 $first_time = ($lastid == "");
1249
1250                 if ($lastid != "") {
1251                         $parameters["since_id"] = $lastid;
1252                 }
1253
1254                 $items = $connection->get('statuses/home_timeline', $parameters);
1255
1256                 if (!is_array($items)) {
1257                         if (is_object($items) && isset($items->error)) {
1258                                 $errormsg = $items->error;
1259                         } elseif (is_object($items)) {
1260                                 $errormsg = print_r($items, true);
1261                         } elseif (is_string($items) || is_float($items) || is_int($items)) {
1262                                 $errormsg = $items;
1263                         } else {
1264                                 $errormsg = "Unknown error";
1265                         }
1266
1267                         logger("statusnet_fetchhometimeline: Error fetching home timeline: " . $errormsg, LOGGER_DEBUG);
1268                         return;
1269                 }
1270
1271                 $posts = array_reverse($items);
1272
1273                 logger("statusnet_fetchhometimeline: Fetching timeline for user " . $uid . " " . sizeof($posts) . " items", LOGGER_DEBUG);
1274
1275                 if (count($posts)) {
1276                         foreach ($posts as $post) {
1277
1278                                 if ($post->id > $lastid) {
1279                                         $lastid = $post->id;
1280                                 }
1281
1282                                 if ($first_time) {
1283                                         continue;
1284                                 }
1285
1286                                 if (isset($post->statusnet_conversation_id)) {
1287                                         if (!isset($conversations[$post->statusnet_conversation_id])) {
1288                                                 statusnet_complete_conversation($a, $uid, $self, $create_user, $nick, $post->statusnet_conversation_id);
1289                                                 $conversations[$post->statusnet_conversation_id] = $post->statusnet_conversation_id;
1290                                         }
1291                                 } else {
1292                                         $postarray = statusnet_createpost($a, $uid, $post, $self, $create_user, true);
1293
1294                                         if (trim($postarray['body']) == "") {
1295                                                 continue;
1296                                         }
1297
1298                                         $item = Item::insert($postarray);
1299                                         $postarray["id"] = $item;
1300
1301                                         logger('statusnet_fetchhometimeline: User ' . $self["nick"] . ' posted home timeline item ' . $item);
1302                                 }
1303                         }
1304                 }
1305                 PConfig::set($uid, 'statusnet', 'lasthometimelineid', $lastid);
1306         }
1307
1308         // Fetching mentions
1309         $lastid = PConfig::get($uid, 'statusnet', 'lastmentionid');
1310         $first_time = ($lastid == "");
1311
1312         if ($lastid != "") {
1313                 $parameters["since_id"] = $lastid;
1314         }
1315
1316         $items = $connection->get('statuses/mentions_timeline', $parameters);
1317
1318         if (!is_array($items)) {
1319                 logger("statusnet_fetchhometimeline: Error fetching mentions: " . print_r($items, true), LOGGER_DEBUG);
1320                 return;
1321         }
1322
1323         $posts = array_reverse($items);
1324
1325         logger("statusnet_fetchhometimeline: Fetching mentions for user " . $uid . " " . sizeof($posts) . " items", LOGGER_DEBUG);
1326
1327         if (count($posts)) {
1328                 foreach ($posts as $post) {
1329                         if ($post->id > $lastid) {
1330                                 $lastid = $post->id;
1331                         }
1332
1333                         if ($first_time) {
1334                                 continue;
1335                         }
1336
1337                         $postarray = statusnet_createpost($a, $uid, $post, $self, false, false);
1338
1339                         if (isset($post->statusnet_conversation_id)) {
1340                                 if (!isset($conversations[$post->statusnet_conversation_id])) {
1341                                         statusnet_complete_conversation($a, $uid, $self, $create_user, $nick, $post->statusnet_conversation_id);
1342                                         $conversations[$post->statusnet_conversation_id] = $post->statusnet_conversation_id;
1343                                 }
1344                         } else {
1345                                 if (trim($postarray['body']) == "") {
1346                                         continue;
1347                                 }
1348
1349                                 $item = Item::insert($postarray);
1350
1351                                 logger('statusnet_fetchhometimeline: User ' . $self["nick"] . ' posted mention timeline item ' . $item);
1352                         }
1353                 }
1354         }
1355
1356         PConfig::set($uid, 'statusnet', 'lastmentionid', $lastid);
1357 }
1358
1359 function statusnet_complete_conversation(App $a, $uid, $self, $create_user, $nick, $conversation)
1360 {
1361         $ckey    = PConfig::get($uid, 'statusnet', 'consumerkey');
1362         $csecret = PConfig::get($uid, 'statusnet', 'consumersecret');
1363         $api     = PConfig::get($uid, 'statusnet', 'baseapi');
1364         $otoken  = PConfig::get($uid, 'statusnet', 'oauthtoken');
1365         $osecret = PConfig::get($uid, 'statusnet', 'oauthsecret');
1366         $own_url = PConfig::get($uid, 'statusnet', 'own_url');
1367
1368         $connection = new StatusNetOAuth($api, $ckey, $csecret, $otoken, $osecret);
1369
1370         $parameters["count"] = 200;
1371
1372         $items = $connection->get('statusnet/conversation/' . $conversation, $parameters);
1373         if (is_array($items)) {
1374                 $posts = array_reverse($items);
1375
1376                 foreach ($posts as $post) {
1377                         $postarray = statusnet_createpost($a, $uid, $post, $self, false, false);
1378
1379                         if (trim($postarray['body']) == "") {
1380                                 continue;
1381                         }
1382
1383                         $item = Item::insert($postarray);
1384                         $postarray["id"] = $item;
1385
1386                         logger('statusnet_complete_conversation: User ' . $self["nick"] . ' posted home timeline item ' . $item);
1387                 }
1388         }
1389 }
1390
1391 function statusnet_convertmsg(App $a, $body, $no_tags = false)
1392 {
1393         require_once "include/items.php";
1394
1395         $body = preg_replace("=\[url\=https?://([0-9]*).([0-9]*).([0-9]*).([0-9]*)/([0-9]*)\](.*?)\[\/url\]=ism", "$1.$2.$3.$4/$5", $body);
1396
1397         $URLSearchString = "^\[\]";
1398         $links = preg_match_all("/[^!#@]\[url\=([$URLSearchString]*)\](.*?)\[\/url\]/ism", $body, $matches, PREG_SET_ORDER);
1399
1400         $footer = "";
1401         $footerurl = "";
1402         $footerlink = "";
1403         $type = "";
1404
1405         if ($links) {
1406                 foreach ($matches AS $match) {
1407                         $search = "[url=" . $match[1] . "]" . $match[2] . "[/url]";
1408
1409                         logger("statusnet_convertmsg: expanding url " . $match[1], LOGGER_DEBUG);
1410
1411                         $expanded_url = Network::finalUrl($match[1]);
1412
1413                         logger("statusnet_convertmsg: fetching data for " . $expanded_url, LOGGER_DEBUG);
1414
1415                         $oembed_data = OEmbed::fetchURL($expanded_url, true);
1416
1417                         logger("statusnet_convertmsg: fetching data: done", LOGGER_DEBUG);
1418
1419                         if ($type == "") {
1420                                 $type = $oembed_data->type;
1421                         }
1422
1423                         if ($oembed_data->type == "video") {
1424                                 //$body = str_replace($search, "[video]".$expanded_url."[/video]", $body);
1425                                 $type = $oembed_data->type;
1426                                 $footerurl = $expanded_url;
1427                                 $footerlink = "[url=" . $expanded_url . "]" . $expanded_url . "[/url]";
1428
1429                                 $body = str_replace($search, $footerlink, $body);
1430                         } elseif (($oembed_data->type == "photo") && isset($oembed_data->url) && !$dontincludemedia) {
1431                                 $body = str_replace($search, "[url=" . $expanded_url . "][img]" . $oembed_data->url . "[/img][/url]", $body);
1432                         } elseif ($oembed_data->type != "link") {
1433                                 $body = str_replace($search, "[url=" . $expanded_url . "]" . $expanded_url . "[/url]", $body);
1434                         } else {
1435                                 $img_str = Network::fetchUrl($expanded_url, true, $redirects, 4);
1436
1437                                 $tempfile = tempnam(get_temppath(), "cache");
1438                                 file_put_contents($tempfile, $img_str);
1439                                 $mime = image_type_to_mime_type(exif_imagetype($tempfile));
1440                                 unlink($tempfile);
1441
1442                                 if (substr($mime, 0, 6) == "image/") {
1443                                         $type = "photo";
1444                                         $body = str_replace($search, "[img]" . $expanded_url . "[/img]", $body);
1445                                 } else {
1446                                         $type = $oembed_data->type;
1447                                         $footerurl = $expanded_url;
1448                                         $footerlink = "[url=" . $expanded_url . "]" . $expanded_url . "[/url]";
1449
1450                                         $body = str_replace($search, $footerlink, $body);
1451                                 }
1452                         }
1453                 }
1454
1455                 if ($footerurl != "") {
1456                         $footer = add_page_info($footerurl);
1457                 }
1458
1459                 if (($footerlink != "") && (trim($footer) != "")) {
1460                         $removedlink = trim(str_replace($footerlink, "", $body));
1461
1462                         if (($removedlink == "") || strstr($body, $removedlink)) {
1463                                 $body = $removedlink;
1464                         }
1465
1466                         $body .= $footer;
1467                 }
1468         }
1469
1470         if ($no_tags) {
1471                 return ["body" => $body, "tags" => ""];
1472         }
1473
1474         $str_tags = '';
1475
1476         $cnt = preg_match_all("/([!#@])\[url\=([$URLSearchString]*)\](.*?)\[\/url\]/ism", $body, $matches, PREG_SET_ORDER);
1477         if ($cnt) {
1478                 foreach ($matches as $mtch) {
1479                         if (strlen($str_tags)) {
1480                                 $str_tags .= ',';
1481                         }
1482
1483                         if ($mtch[1] == "#") {
1484                                 // Replacing the hash tags that are directed to the GNU Social server with internal links
1485                                 $snhash = "#[url=" . $mtch[2] . "]" . $mtch[3] . "[/url]";
1486                                 $frdchash = '#[url=' . $a->get_baseurl() . '/search?tag=' . rawurlencode($mtch[3]) . ']' . $mtch[3] . '[/url]';
1487                                 $body = str_replace($snhash, $frdchash, $body);
1488
1489                                 $str_tags .= $frdchash;
1490                         } else {
1491                                 $str_tags .= "@[url=" . $mtch[2] . "]" . $mtch[3] . "[/url]";
1492                         }
1493                         // To-Do:
1494                         // There is a problem with links with to GNU Social groups, so these links are stored with "@" like friendica groups
1495                         //$str_tags .= $mtch[1]."[url=".$mtch[2]."]".$mtch[3]."[/url]";
1496                 }
1497         }
1498
1499         return ["body" => $body, "tags" => $str_tags];
1500 }
1501
1502 function statusnet_fetch_own_contact(App $a, $uid)
1503 {
1504         $ckey    = PConfig::get($uid, 'statusnet', 'consumerkey');
1505         $csecret = PConfig::get($uid, 'statusnet', 'consumersecret');
1506         $api     = PConfig::get($uid, 'statusnet', 'baseapi');
1507         $otoken  = PConfig::get($uid, 'statusnet', 'oauthtoken');
1508         $osecret = PConfig::get($uid, 'statusnet', 'oauthsecret');
1509         $own_url = PConfig::get($uid, 'statusnet', 'own_url');
1510
1511         $contact_id = 0;
1512
1513         if ($own_url == "") {
1514                 $connection = new StatusNetOAuth($api, $ckey, $csecret, $otoken, $osecret);
1515
1516                 // Fetching user data
1517                 $user = $connection->get('account/verify_credentials');
1518
1519                 PConfig::set($uid, 'statusnet', 'own_url', normalise_link($user->statusnet_profile_url));
1520
1521                 $contact_id = statusnet_fetch_contact($uid, $user, true);
1522         } else {
1523                 $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `alias` = '%s' LIMIT 1",
1524                         intval($uid), dbesc($own_url));
1525                 if (DBM::is_result($r)) {
1526                         $contact_id = $r[0]["id"];
1527                 } else {
1528                         PConfig::delete($uid, 'statusnet', 'own_url');
1529                 }
1530         }
1531         return $contact_id;
1532 }
1533
1534 function statusnet_is_retweet(App $a, $uid, $body)
1535 {
1536         $body = trim($body);
1537
1538         // Skip if it isn't a pure repeated messages
1539         // Does it start with a share?
1540         if (strpos($body, "[share") > 0) {
1541                 return false;
1542         }
1543
1544         // Does it end with a share?
1545         if (strlen($body) > (strrpos($body, "[/share]") + 8)) {
1546                 return false;
1547         }
1548
1549         $attributes = preg_replace("/\[share(.*?)\]\s?(.*?)\s?\[\/share\]\s?/ism", "$1", $body);
1550         // Skip if there is no shared message in there
1551         if ($body == $attributes) {
1552                 return false;
1553         }
1554
1555         $link = "";
1556         preg_match("/link='(.*?)'/ism", $attributes, $matches);
1557         if (!empty($matches[1])) {
1558                 $link = $matches[1];
1559         }
1560
1561         preg_match('/link="(.*?)"/ism', $attributes, $matches);
1562         if (!empty($matches[1])) {
1563                 $link = $matches[1];
1564         }
1565
1566         $ckey    = PConfig::get($uid, 'statusnet', 'consumerkey');
1567         $csecret = PConfig::get($uid, 'statusnet', 'consumersecret');
1568         $api     = PConfig::get($uid, 'statusnet', 'baseapi');
1569         $otoken  = PConfig::get($uid, 'statusnet', 'oauthtoken');
1570         $osecret = PConfig::get($uid, 'statusnet', 'oauthsecret');
1571         $hostname = preg_replace("=https?://([\w\.]*)/.*=ism", "$1", $api);
1572
1573         $id = preg_replace("=https?://" . $hostname . "/notice/(.*)=ism", "$1", $link);
1574
1575         if ($id == $link) {
1576                 return false;
1577         }
1578
1579         logger('statusnet_is_retweet: Retweeting id ' . $id . ' for user ' . $uid, LOGGER_DEBUG);
1580
1581         $connection = new StatusNetOAuth($api, $ckey, $csecret, $otoken, $osecret);
1582
1583         $result = $connection->post('statuses/retweet/' . $id);
1584
1585         logger('statusnet_is_retweet: result ' . print_r($result, true), LOGGER_DEBUG);
1586
1587         return isset($result->id);
1588 }