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