]> git.mxchange.org Git - friendica-addons.git/blob - statusnet/statusnet.php
Merge pull request #630 from annando/pumpio-fix
[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 ($result->source) {
590                                 PConfig::set($b["uid"], "statusnet", "application_name", strip_tags($result->source));
591                         }
592
593                         if ($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 (is_object($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['gravity'] = 0;
1066         $postarray['uid'] = $uid;
1067         $postarray['wall'] = 0;
1068
1069         if (is_object($post->retweeted_status)) {
1070                 $content = $post->retweeted_status;
1071                 statusnet_fetch_contact($uid, $content->user, false);
1072         } else {
1073                 $content = $post;
1074         }
1075
1076         $postarray['uri'] = $hostname . "::" . $content->id;
1077
1078         if (dba::exists('item', ['extid' => $postarray['uri'], 'uid' => $uid])) {
1079                 return [];
1080         }
1081
1082         $contactid = 0;
1083
1084         if ($content->in_reply_to_status_id != "") {
1085
1086                 $parent = $hostname . "::" . $content->in_reply_to_status_id;
1087
1088                 $fields = ['uri', 'parent-uri', 'parent'];
1089                 $item = Item::selectFirst($fields, ['uri' => $parent, 'uid' => $uid]);
1090
1091                 if (!DBM::is_result($item)) {
1092                         $item = Item::selectFirst($fields, ['extid' => $parent, 'uid' => $uid]);
1093                 }
1094
1095                 if (DBM::is_result($item)) {
1096                         $postarray['thr-parent'] = $item['uri'];
1097                         $postarray['parent-uri'] = $item['parent-uri'];
1098                         $postarray['parent'] = $item['parent'];
1099                         $postarray['object-type'] = ACTIVITY_OBJ_COMMENT;
1100                 } else {
1101                         $postarray['thr-parent'] = $postarray['uri'];
1102                         $postarray['parent-uri'] = $postarray['uri'];
1103                         $postarray['object-type'] = ACTIVITY_OBJ_NOTE;
1104                 }
1105
1106                 // Is it me?
1107                 $own_url = PConfig::get($uid, 'statusnet', 'own_url');
1108
1109                 if ($content->user->id == $own_url) {
1110                         $r = q("SELECT * FROM `contact` WHERE `self` = 1 AND `uid` = %d LIMIT 1",
1111                                 intval($uid));
1112
1113                         if (DBM::is_result($r)) {
1114                                 $contactid = $r[0]["id"];
1115
1116                                 $postarray['owner-name'] = $r[0]["name"];
1117                                 $postarray['owner-link'] = $r[0]["url"];
1118                                 $postarray['owner-avatar'] = $r[0]["photo"];
1119                         } else {
1120                                 return [];
1121                         }
1122                 }
1123                 // Don't create accounts of people who just comment something
1124                 $create_user = false;
1125         } else {
1126                 $postarray['parent-uri'] = $postarray['uri'];
1127                 $postarray['object-type'] = ACTIVITY_OBJ_NOTE;
1128         }
1129
1130         if ($contactid == 0) {
1131                 $contactid = statusnet_fetch_contact($uid, $post->user, $create_user);
1132                 $postarray['owner-name'] = $post->user->name;
1133                 $postarray['owner-link'] = $post->user->statusnet_profile_url;
1134                 $postarray['owner-avatar'] = $post->user->profile_image_url;
1135         }
1136         if (($contactid == 0) && !$only_existing_contact) {
1137                 $contactid = $self['id'];
1138         } elseif ($contactid <= 0) {
1139                 return [];
1140         }
1141
1142         $postarray['contact-id'] = $contactid;
1143
1144         $postarray['verb'] = ACTIVITY_POST;
1145
1146         $postarray['author-name'] = $content->user->name;
1147         $postarray['author-link'] = $content->user->statusnet_profile_url;
1148         $postarray['author-avatar'] = $content->user->profile_image_url;
1149
1150         // To-Do: Maybe unreliable? Can the api be entered without trailing "/"?
1151         $hostname = str_replace("/api/", "/notice/", PConfig::get($uid, 'statusnet', 'baseapi'));
1152
1153         $postarray['plink'] = $hostname . $content->id;
1154         $postarray['app'] = strip_tags($content->source);
1155
1156         if ($content->user->protected) {
1157                 $postarray['private'] = 1;
1158                 $postarray['allow_cid'] = '<' . $self['id'] . '>';
1159         }
1160
1161         $postarray['body'] = HTML::toBBCode($content->statusnet_html);
1162
1163         $converted = statusnet_convertmsg($a, $postarray['body'], false);
1164         $postarray['body'] = $converted["body"];
1165         $postarray['tag'] = $converted["tags"];
1166
1167         $postarray['created'] = DateTimeFormat::utc($content->created_at);
1168         $postarray['edited'] = DateTimeFormat::utc($content->created_at);
1169
1170         if (is_string($content->place->name)) {
1171                 $postarray["location"] = $content->place->name;
1172         }
1173
1174         if (is_string($content->place->full_name)) {
1175                 $postarray["location"] = $content->place->full_name;
1176         }
1177
1178         if (is_array($content->geo->coordinates)) {
1179                 $postarray["coord"] = $content->geo->coordinates[0] . " " . $content->geo->coordinates[1];
1180         }
1181
1182         if (is_array($content->coordinates->coordinates)) {
1183                 $postarray["coord"] = $content->coordinates->coordinates[1] . " " . $content->coordinates->coordinates[0];
1184         }
1185
1186         logger("statusnet_createpost: end", LOGGER_DEBUG);
1187
1188         return $postarray;
1189 }
1190
1191 function statusnet_fetchhometimeline(App $a, $uid, $mode = 1)
1192 {
1193         $conversations = [];
1194
1195         $ckey    = PConfig::get($uid, 'statusnet', 'consumerkey');
1196         $csecret = PConfig::get($uid, 'statusnet', 'consumersecret');
1197         $api     = PConfig::get($uid, 'statusnet', 'baseapi');
1198         $otoken  = PConfig::get($uid, 'statusnet', 'oauthtoken');
1199         $osecret = PConfig::get($uid, 'statusnet', 'oauthsecret');
1200         $create_user = PConfig::get($uid, 'statusnet', 'create_user');
1201
1202         // "create_user" is deactivated, since currently you cannot add users manually by now
1203         $create_user = true;
1204
1205         logger("statusnet_fetchhometimeline: Fetching for user " . $uid, LOGGER_DEBUG);
1206
1207         require_once 'include/items.php';
1208
1209         $connection = new StatusNetOAuth($api, $ckey, $csecret, $otoken, $osecret);
1210
1211         $own_contact = statusnet_fetch_own_contact($a, $uid);
1212
1213         $r = q("SELECT * FROM `contact` WHERE `id` = %d AND `uid` = %d LIMIT 1",
1214                 intval($own_contact),
1215                 intval($uid));
1216
1217         if (DBM::is_result($r)) {
1218                 $nick = $r[0]["nick"];
1219         } else {
1220                 logger("statusnet_fetchhometimeline: Own GNU Social contact not found for user " . $uid, LOGGER_DEBUG);
1221                 return;
1222         }
1223
1224         $r = q("SELECT * FROM `contact` WHERE `self` = 1 AND `uid` = %d LIMIT 1",
1225                 intval($uid));
1226
1227         if (DBM::is_result($r)) {
1228                 $self = $r[0];
1229         } else {
1230                 logger("statusnet_fetchhometimeline: Own contact not found for user " . $uid, LOGGER_DEBUG);
1231                 return;
1232         }
1233
1234         $u = q("SELECT * FROM user WHERE uid = %d LIMIT 1",
1235                 intval($uid));
1236         if (!DBM::is_result($u)) {
1237                 logger("statusnet_fetchhometimeline: Own user not found for user " . $uid, LOGGER_DEBUG);
1238                 return;
1239         }
1240
1241         $parameters = ["exclude_replies" => false, "trim_user" => false, "contributor_details" => true, "include_rts" => true];
1242         //$parameters["count"] = 200;
1243
1244         if ($mode == 1) {
1245                 // Fetching timeline
1246                 $lastid = PConfig::get($uid, 'statusnet', 'lasthometimelineid');
1247                 //$lastid = 1;
1248
1249                 $first_time = ($lastid == "");
1250
1251                 if ($lastid != "") {
1252                         $parameters["since_id"] = $lastid;
1253                 }
1254
1255                 $items = $connection->get('statuses/home_timeline', $parameters);
1256
1257                 if (!is_array($items)) {
1258                         if (is_object($items) && isset($items->error)) {
1259                                 $errormsg = $items->error;
1260                         } elseif (is_object($items)) {
1261                                 $errormsg = print_r($items, true);
1262                         } elseif (is_string($items) || is_float($items) || is_int($items)) {
1263                                 $errormsg = $items;
1264                         } else {
1265                                 $errormsg = "Unknown error";
1266                         }
1267
1268                         logger("statusnet_fetchhometimeline: Error fetching home timeline: " . $errormsg, LOGGER_DEBUG);
1269                         return;
1270                 }
1271
1272                 $posts = array_reverse($items);
1273
1274                 logger("statusnet_fetchhometimeline: Fetching timeline for user " . $uid . " " . sizeof($posts) . " items", LOGGER_DEBUG);
1275
1276                 if (count($posts)) {
1277                         foreach ($posts as $post) {
1278
1279                                 if ($post->id > $lastid) {
1280                                         $lastid = $post->id;
1281                                 }
1282
1283                                 if ($first_time) {
1284                                         continue;
1285                                 }
1286
1287                                 if (isset($post->statusnet_conversation_id)) {
1288                                         if (!isset($conversations[$post->statusnet_conversation_id])) {
1289                                                 statusnet_complete_conversation($a, $uid, $self, $create_user, $nick, $post->statusnet_conversation_id);
1290                                                 $conversations[$post->statusnet_conversation_id] = $post->statusnet_conversation_id;
1291                                         }
1292                                 } else {
1293                                         $postarray = statusnet_createpost($a, $uid, $post, $self, $create_user, true);
1294
1295                                         if (trim($postarray['body']) == "") {
1296                                                 continue;
1297                                         }
1298
1299                                         $item = Item::insert($postarray);
1300                                         $postarray["id"] = $item;
1301
1302                                         logger('statusnet_fetchhometimeline: User ' . $self["nick"] . ' posted home timeline item ' . $item);
1303                                 }
1304                         }
1305                 }
1306                 PConfig::set($uid, 'statusnet', 'lasthometimelineid', $lastid);
1307         }
1308
1309         // Fetching mentions
1310         $lastid = PConfig::get($uid, 'statusnet', 'lastmentionid');
1311         $first_time = ($lastid == "");
1312
1313         if ($lastid != "") {
1314                 $parameters["since_id"] = $lastid;
1315         }
1316
1317         $items = $connection->get('statuses/mentions_timeline', $parameters);
1318
1319         if (!is_array($items)) {
1320                 logger("statusnet_fetchhometimeline: Error fetching mentions: " . print_r($items, true), LOGGER_DEBUG);
1321                 return;
1322         }
1323
1324         $posts = array_reverse($items);
1325
1326         logger("statusnet_fetchhometimeline: Fetching mentions for user " . $uid . " " . sizeof($posts) . " items", LOGGER_DEBUG);
1327
1328         if (count($posts)) {
1329                 foreach ($posts as $post) {
1330                         if ($post->id > $lastid) {
1331                                 $lastid = $post->id;
1332                         }
1333
1334                         if ($first_time) {
1335                                 continue;
1336                         }
1337
1338                         $postarray = statusnet_createpost($a, $uid, $post, $self, false, false);
1339
1340                         if (isset($post->statusnet_conversation_id)) {
1341                                 if (!isset($conversations[$post->statusnet_conversation_id])) {
1342                                         statusnet_complete_conversation($a, $uid, $self, $create_user, $nick, $post->statusnet_conversation_id);
1343                                         $conversations[$post->statusnet_conversation_id] = $post->statusnet_conversation_id;
1344                                 }
1345                         } else {
1346                                 if (trim($postarray['body']) == "") {
1347                                         continue;
1348                                 }
1349
1350                                 $item = Item::insert($postarray);
1351
1352                                 logger('statusnet_fetchhometimeline: User ' . $self["nick"] . ' posted mention timeline item ' . $item);
1353                         }
1354                 }
1355         }
1356
1357         PConfig::set($uid, 'statusnet', 'lastmentionid', $lastid);
1358 }
1359
1360 function statusnet_complete_conversation(App $a, $uid, $self, $create_user, $nick, $conversation)
1361 {
1362         $ckey    = PConfig::get($uid, 'statusnet', 'consumerkey');
1363         $csecret = PConfig::get($uid, 'statusnet', 'consumersecret');
1364         $api     = PConfig::get($uid, 'statusnet', 'baseapi');
1365         $otoken  = PConfig::get($uid, 'statusnet', 'oauthtoken');
1366         $osecret = PConfig::get($uid, 'statusnet', 'oauthsecret');
1367         $own_url = PConfig::get($uid, 'statusnet', 'own_url');
1368
1369         $connection = new StatusNetOAuth($api, $ckey, $csecret, $otoken, $osecret);
1370
1371         $parameters["count"] = 200;
1372
1373         $items = $connection->get('statusnet/conversation/' . $conversation, $parameters);
1374         if (is_array($items)) {
1375                 $posts = array_reverse($items);
1376
1377                 foreach ($posts as $post) {
1378                         $postarray = statusnet_createpost($a, $uid, $post, $self, false, false);
1379
1380                         if (trim($postarray['body']) == "") {
1381                                 continue;
1382                         }
1383
1384                         $item = Item::insert($postarray);
1385                         $postarray["id"] = $item;
1386
1387                         logger('statusnet_complete_conversation: User ' . $self["nick"] . ' posted home timeline item ' . $item);
1388                 }
1389         }
1390 }
1391
1392 function statusnet_convertmsg(App $a, $body, $no_tags = false)
1393 {
1394         require_once "include/items.php";
1395
1396         $body = preg_replace("=\[url\=https?://([0-9]*).([0-9]*).([0-9]*).([0-9]*)/([0-9]*)\](.*?)\[\/url\]=ism", "$1.$2.$3.$4/$5", $body);
1397
1398         $URLSearchString = "^\[\]";
1399         $links = preg_match_all("/[^!#@]\[url\=([$URLSearchString]*)\](.*?)\[\/url\]/ism", $body, $matches, PREG_SET_ORDER);
1400
1401         $footer = "";
1402         $footerurl = "";
1403         $footerlink = "";
1404         $type = "";
1405
1406         if ($links) {
1407                 foreach ($matches AS $match) {
1408                         $search = "[url=" . $match[1] . "]" . $match[2] . "[/url]";
1409
1410                         logger("statusnet_convertmsg: expanding url " . $match[1], LOGGER_DEBUG);
1411
1412                         $expanded_url = Network::finalUrl($match[1]);
1413
1414                         logger("statusnet_convertmsg: fetching data for " . $expanded_url, LOGGER_DEBUG);
1415
1416                         $oembed_data = OEmbed::fetchURL($expanded_url, true);
1417
1418                         logger("statusnet_convertmsg: fetching data: done", LOGGER_DEBUG);
1419
1420                         if ($type == "") {
1421                                 $type = $oembed_data->type;
1422                         }
1423
1424                         if ($oembed_data->type == "video") {
1425                                 //$body = str_replace($search, "[video]".$expanded_url."[/video]", $body);
1426                                 $type = $oembed_data->type;
1427                                 $footerurl = $expanded_url;
1428                                 $footerlink = "[url=" . $expanded_url . "]" . $expanded_url . "[/url]";
1429
1430                                 $body = str_replace($search, $footerlink, $body);
1431                         } elseif (($oembed_data->type == "photo") && isset($oembed_data->url) && !$dontincludemedia) {
1432                                 $body = str_replace($search, "[url=" . $expanded_url . "][img]" . $oembed_data->url . "[/img][/url]", $body);
1433                         } elseif ($oembed_data->type != "link") {
1434                                 $body = str_replace($search, "[url=" . $expanded_url . "]" . $expanded_url . "[/url]", $body);
1435                         } else {
1436                                 $img_str = Network::fetchUrl($expanded_url, true, $redirects, 4);
1437
1438                                 $tempfile = tempnam(get_temppath(), "cache");
1439                                 file_put_contents($tempfile, $img_str);
1440                                 $mime = image_type_to_mime_type(exif_imagetype($tempfile));
1441                                 unlink($tempfile);
1442
1443                                 if (substr($mime, 0, 6) == "image/") {
1444                                         $type = "photo";
1445                                         $body = str_replace($search, "[img]" . $expanded_url . "[/img]", $body);
1446                                 } else {
1447                                         $type = $oembed_data->type;
1448                                         $footerurl = $expanded_url;
1449                                         $footerlink = "[url=" . $expanded_url . "]" . $expanded_url . "[/url]";
1450
1451                                         $body = str_replace($search, $footerlink, $body);
1452                                 }
1453                         }
1454                 }
1455
1456                 if ($footerurl != "") {
1457                         $footer = add_page_info($footerurl);
1458                 }
1459
1460                 if (($footerlink != "") && (trim($footer) != "")) {
1461                         $removedlink = trim(str_replace($footerlink, "", $body));
1462
1463                         if (($removedlink == "") || strstr($body, $removedlink)) {
1464                                 $body = $removedlink;
1465                         }
1466
1467                         $body .= $footer;
1468                 }
1469         }
1470
1471         if ($no_tags) {
1472                 return ["body" => $body, "tags" => ""];
1473         }
1474
1475         $str_tags = '';
1476
1477         $cnt = preg_match_all("/([!#@])\[url\=([$URLSearchString]*)\](.*?)\[\/url\]/ism", $body, $matches, PREG_SET_ORDER);
1478         if ($cnt) {
1479                 foreach ($matches as $mtch) {
1480                         if (strlen($str_tags)) {
1481                                 $str_tags .= ',';
1482                         }
1483
1484                         if ($mtch[1] == "#") {
1485                                 // Replacing the hash tags that are directed to the GNU Social server with internal links
1486                                 $snhash = "#[url=" . $mtch[2] . "]" . $mtch[3] . "[/url]";
1487                                 $frdchash = '#[url=' . $a->get_baseurl() . '/search?tag=' . rawurlencode($mtch[3]) . ']' . $mtch[3] . '[/url]';
1488                                 $body = str_replace($snhash, $frdchash, $body);
1489
1490                                 $str_tags .= $frdchash;
1491                         } else {
1492                                 $str_tags .= "@[url=" . $mtch[2] . "]" . $mtch[3] . "[/url]";
1493                         }
1494                         // To-Do:
1495                         // There is a problem with links with to GNU Social groups, so these links are stored with "@" like friendica groups
1496                         //$str_tags .= $mtch[1]."[url=".$mtch[2]."]".$mtch[3]."[/url]";
1497                 }
1498         }
1499
1500         return ["body" => $body, "tags" => $str_tags];
1501 }
1502
1503 function statusnet_fetch_own_contact(App $a, $uid)
1504 {
1505         $ckey    = PConfig::get($uid, 'statusnet', 'consumerkey');
1506         $csecret = PConfig::get($uid, 'statusnet', 'consumersecret');
1507         $api     = PConfig::get($uid, 'statusnet', 'baseapi');
1508         $otoken  = PConfig::get($uid, 'statusnet', 'oauthtoken');
1509         $osecret = PConfig::get($uid, 'statusnet', 'oauthsecret');
1510         $own_url = PConfig::get($uid, 'statusnet', 'own_url');
1511
1512         $contact_id = 0;
1513
1514         if ($own_url == "") {
1515                 $connection = new StatusNetOAuth($api, $ckey, $csecret, $otoken, $osecret);
1516
1517                 // Fetching user data
1518                 $user = $connection->get('account/verify_credentials');
1519
1520                 PConfig::set($uid, 'statusnet', 'own_url', normalise_link($user->statusnet_profile_url));
1521
1522                 $contact_id = statusnet_fetch_contact($uid, $user, true);
1523         } else {
1524                 $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `alias` = '%s' LIMIT 1",
1525                         intval($uid), dbesc($own_url));
1526                 if (DBM::is_result($r)) {
1527                         $contact_id = $r[0]["id"];
1528                 } else {
1529                         PConfig::delete($uid, 'statusnet', 'own_url');
1530                 }
1531         }
1532         return $contact_id;
1533 }
1534
1535 function statusnet_is_retweet(App $a, $uid, $body)
1536 {
1537         $body = trim($body);
1538
1539         // Skip if it isn't a pure repeated messages
1540         // Does it start with a share?
1541         if (strpos($body, "[share") > 0) {
1542                 return false;
1543         }
1544
1545         // Does it end with a share?
1546         if (strlen($body) > (strrpos($body, "[/share]") + 8)) {
1547                 return false;
1548         }
1549
1550         $attributes = preg_replace("/\[share(.*?)\]\s?(.*?)\s?\[\/share\]\s?/ism", "$1", $body);
1551         // Skip if there is no shared message in there
1552         if ($body == $attributes) {
1553                 return false;
1554         }
1555
1556         $link = "";
1557         preg_match("/link='(.*?)'/ism", $attributes, $matches);
1558         if ($matches[1] != "") {
1559                 $link = $matches[1];
1560         }
1561
1562         preg_match('/link="(.*?)"/ism', $attributes, $matches);
1563         if ($matches[1] != "") {
1564                 $link = $matches[1];
1565         }
1566
1567         $ckey    = PConfig::get($uid, 'statusnet', 'consumerkey');
1568         $csecret = PConfig::get($uid, 'statusnet', 'consumersecret');
1569         $api     = PConfig::get($uid, 'statusnet', 'baseapi');
1570         $otoken  = PConfig::get($uid, 'statusnet', 'oauthtoken');
1571         $osecret = PConfig::get($uid, 'statusnet', 'oauthsecret');
1572         $hostname = preg_replace("=https?://([\w\.]*)/.*=ism", "$1", $api);
1573
1574         $id = preg_replace("=https?://" . $hostname . "/notice/(.*)=ism", "$1", $link);
1575
1576         if ($id == $link) {
1577                 return false;
1578         }
1579
1580         logger('statusnet_is_retweet: Retweeting id ' . $id . ' for user ' . $uid, LOGGER_DEBUG);
1581
1582         $connection = new StatusNetOAuth($api, $ckey, $csecret, $otoken, $osecret);
1583
1584         $result = $connection->post('statuses/retweet/' . $id);
1585
1586         logger('statusnet_is_retweet: result ' . print_r($result, true), LOGGER_DEBUG);
1587
1588         return isset($result->id);
1589 }