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