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