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