Deprecated parts removed
[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         if((local_user()) && (local_user() == $b['uid']) && (! $b['private'])) {
436
437                 $statusnet_post = get_pconfig(local_user(),'statusnet','post');
438                 $statusnet_enable = (($statusnet_post && x($_REQUEST,'statusnet_enable')) ? intval($_REQUEST['statusnet_enable']) : 0);
439
440                 // if API is used, default to the chosen settings
441                 if($_REQUEST['api_source'] && intval(get_pconfig(local_user(),'statusnet','post_by_default')))
442                         $statusnet_enable = 1;
443
444                 if(! $statusnet_enable)
445                         return;
446
447                 if(strlen($b['postopts']))
448                         $b['postopts'] .= ',';
449                 $b['postopts'] .= 'statusnet';
450         }
451 }
452
453 function statusnet_action($a, $uid, $pid, $action) {
454         $api     = get_pconfig($uid, 'statusnet', 'baseapi');
455         $ckey    = get_pconfig($uid, 'statusnet', 'consumerkey');
456         $csecret = get_pconfig($uid, 'statusnet', 'consumersecret');
457         $otoken  = get_pconfig($uid, 'statusnet', 'oauthtoken');
458         $osecret = get_pconfig($uid, 'statusnet', 'oauthsecret');
459
460         $connection = new StatusNetOAuth($api,$ckey,$csecret,$otoken,$osecret);
461
462         logger("statusnet_action '".$action."' ID: ".$pid, LOGGER_DATA);
463
464         switch ($action) {
465                 case "delete":
466                         $result = $connection->post("statuses/destroy/".$pid);
467                         break;
468                 case "like":
469                         $result = $connection->post("favorites/create/".$pid);
470                         break;
471                 case "unlike":
472                         $result = $connection->post("favorites/destroy/".$pid);
473                         break;
474         }
475         logger("statusnet_action '".$action."' send, result: " . print_r($result, true), LOGGER_DEBUG);
476 }
477
478 function statusnet_post_hook(&$a,&$b) {
479
480         /**
481          * Post to GNU Social
482          */
483
484         if (!get_pconfig($b["uid"],'statusnet','import')) {
485                 if($b['deleted'] || $b['private'] || ($b['created'] !== $b['edited']))
486                         return;
487         }
488
489         $api = get_pconfig($b["uid"], 'statusnet', 'baseapi');
490         $hostname = preg_replace("=https?://([\w\.]*)/.*=ism", "$1", $api);
491
492         if($b['parent'] != $b['id']) {
493                 logger("statusnet_post_hook: parameter ".print_r($b, true), LOGGER_DATA);
494
495                 // Looking if its a reply to a GNU Social post
496                 $hostlength = strlen($hostname) + 2;
497                 if ((substr($b["parent-uri"], 0, $hostlength) != $hostname."::") AND (substr($b["extid"], 0, $hostlength) != $hostname."::")
498                         AND (substr($b["thr-parent"], 0, $hostlength) != $hostname."::")) {
499                         logger("statusnet_post_hook: no GNU Social post ".$b["parent"]);
500                         return;
501                 }
502
503                 $r = q("SELECT `item`.`author-link`, `item`.`uri`, `contact`.`nick` AS contact_nick
504                         FROM `item` INNER JOIN `contact` ON `contact`.`id` = `item`.`contact-id`
505                         WHERE `item`.`uri` = '%s' AND `item`.`uid` = %d LIMIT 1",
506                         dbesc($b["thr-parent"]),
507                         intval($b["uid"]));
508
509                 if(!count($r)) {
510                         logger("statusnet_post_hook: no parent found ".$b["thr-parent"]);
511                         return;
512                 } else {
513                         $iscomment = true;
514                         $orig_post = $r[0];
515                 }
516
517                 //$nickname = "@[url=".$orig_post["author-link"]."]".$orig_post["contact_nick"]."[/url]";
518                 //$nicknameplain = "@".$orig_post["contact_nick"];
519
520                 $nick = preg_replace("=https?://(.*)/(.*)=ism", "$2", $orig_post["author-link"]);
521
522                 $nickname = "@[url=".$orig_post["author-link"]."]".$nick."[/url]";
523                 $nicknameplain = "@".$nick;
524
525                 logger("statusnet_post_hook: comparing ".$nickname." and ".$nicknameplain." with ".$b["body"], LOGGER_DEBUG);
526                 if ((strpos($b["body"], $nickname) === false) AND (strpos($b["body"], $nicknameplain) === false))
527                         $b["body"] = $nickname." ".$b["body"];
528
529                 logger("statusnet_post_hook: parent found ".print_r($orig_post, true), LOGGER_DEBUG);
530         } else {
531                 $iscomment = false;
532
533                 if($b['private'] OR !strstr($b['postopts'],'statusnet'))
534                         return;
535         }
536
537         if (($b['verb'] == ACTIVITY_POST) AND $b['deleted'])
538                 statusnet_action($a, $b["uid"], substr($orig_post["uri"], $hostlength), "delete");
539
540         if($b['verb'] == ACTIVITY_LIKE) {
541                 logger("statusnet_post_hook: parameter 2 ".substr($b["thr-parent"], $hostlength), LOGGER_DEBUG);
542                 if ($b['deleted'])
543                         statusnet_action($a, $b["uid"], substr($b["thr-parent"], $hostlength), "unlike");
544                 else
545                         statusnet_action($a, $b["uid"], substr($b["thr-parent"], $hostlength), "like");
546                 return;
547         }
548
549         if($b['deleted'] || ($b['created'] !== $b['edited']))
550                 return;
551
552         // if posts comes from GNU Social don't send it back
553         if($b['extid'] == NETWORK_STATUSNET)
554                 return;
555
556         if($b['app'] == "StatusNet")
557                 return;
558
559         logger('GNU Socialpost invoked');
560
561         load_pconfig($b['uid'], 'statusnet');
562
563         $api     = get_pconfig($b['uid'], 'statusnet', 'baseapi');
564         $ckey    = get_pconfig($b['uid'], 'statusnet', 'consumerkey');
565         $csecret = get_pconfig($b['uid'], 'statusnet', 'consumersecret');
566         $otoken  = get_pconfig($b['uid'], 'statusnet', 'oauthtoken');
567         $osecret = get_pconfig($b['uid'], 'statusnet', 'oauthsecret');
568
569         if($ckey && $csecret && $otoken && $osecret) {
570
571                 // If it's a repeated message from GNU Social then do a native retweet and exit
572                 if (statusnet_is_retweet($a, $b['uid'], $b['body']))
573                         return;
574
575                 require_once('include/bbcode.php');
576                 $dent = new StatusNetOAuth($api,$ckey,$csecret,$otoken,$osecret);
577                 $max_char = $dent->get_maxlength(); // max. length for a dent
578
579                 set_pconfig($b['uid'], 'statusnet', 'max_char', $max_char);
580
581                 $tempfile = "";
582                 require_once("include/plaintext.php");
583                 require_once("include/network.php");
584                 $msgarr = plaintext($a, $b, $max_char, true, 7);
585                 $msg = $msgarr["text"];
586
587                 if (($msg == "") AND isset($msgarr["title"]))
588                         $msg = shortenmsg($msgarr["title"], $max_char - 50);
589
590                 $image = "";
591
592                 if (isset($msgarr["url"]) AND ($msgarr["type"] != "photo")) {
593                         if ((strlen($msgarr["url"]) > 20) AND
594                                 ((strlen($msg." \n".$msgarr["url"]) > $max_char)))
595                                 $msg .= " \n".short_link($msgarr["url"]);
596                         else
597                                 $msg .= " \n".$msgarr["url"];
598                 } elseif (isset($msgarr["image"]) AND ($msgarr["type"] != "video"))
599                         $image = $msgarr["image"];
600
601                 if ($image != "") {
602                         $img_str = fetch_url($image);
603                         $tempfile = tempnam(get_temppath(), "cache");
604                         file_put_contents($tempfile, $img_str);
605                         $postdata = array("status" => $msg, "media[]" => $tempfile);
606                 } else
607                         $postdata = array("status"=>$msg);
608
609                 // and now dent it :-)
610                 if(strlen($msg)) {
611
612                         if ($iscomment) {
613                                 $postdata["in_reply_to_status_id"] = substr($orig_post["uri"], $hostlength);
614                                 logger('statusnet_post send reply '.print_r($postdata, true), LOGGER_DEBUG);
615                         }
616
617                         // New code that is able to post pictures
618                         require_once("addon/statusnet/codebird.php");
619                         $cb = \CodebirdSN\CodebirdSN::getInstance();
620                         $cb->setAPIEndpoint($api);
621                         $cb->setConsumerKey($ckey, $csecret);
622                         $cb->setToken($otoken, $osecret);
623                         $result = $cb->statuses_update($postdata);
624                         //$result = $dent->post('statuses/update', $postdata);
625                         logger('statusnet_post send, result: ' . print_r($result, true).
626                                 "\nmessage: ".$msg, LOGGER_DEBUG."\nOriginal post: ".print_r($b, true)."\nPost Data: ".print_r($postdata, true));
627
628                         if ($result->source)
629                                 set_pconfig($b["uid"], "statusnet", "application_name", strip_tags($result->source));
630
631                         if ($result->error) {
632                                 logger('Send to GNU Social failed: "'.$result->error.'"');
633                         } elseif ($iscomment) {
634                                 logger('statusnet_post: Update extid '.$result->id." for post id ".$b['id']);
635                                 q("UPDATE `item` SET `extid` = '%s', `body` = '%s' WHERE `id` = %d",
636                                         dbesc($hostname."::".$result->id),
637                                         dbesc($result->text),
638                                         intval($b['id'])
639                                 );
640                         }
641                 }
642                 if ($tempfile != "")
643                         unlink($tempfile);
644         }
645 }
646
647 function statusnet_plugin_admin_post(&$a){
648
649         $sites = array();
650
651         foreach($_POST['sitename'] as $id=>$sitename){
652                 $sitename=trim($sitename);
653                 $apiurl=trim($_POST['apiurl'][$id]);
654                 if (! (substr($apiurl, -1)=='/'))
655                     $apiurl=$apiurl.'/';
656                 $secret=trim($_POST['secret'][$id]);
657                 $key=trim($_POST['key'][$id]);
658                 //$applicationname = ((x($_POST, 'applicationname')) ? notags(trim($_POST['applicationname'][$id])):'');
659                 if ($sitename!="" &&
660                         $apiurl!="" &&
661                         $secret!="" &&
662                         $key!="" &&
663                         !x($_POST['delete'][$id])){
664
665                                 $sites[] = Array(
666                                         'sitename' => $sitename,
667                                         'apiurl' => $apiurl,
668                                         'consumersecret' => $secret,
669                                         'consumerkey' => $key,
670                                         //'applicationname' => $applicationname
671                                 );
672                 }
673         }
674
675         $sites = set_config('statusnet','sites', $sites);
676
677 }
678
679 function statusnet_plugin_admin(&$a, &$o){
680
681         $sites = get_config('statusnet','sites');
682         $sitesform=array();
683         if (is_array($sites)){
684                 foreach($sites as $id=>$s){
685                         $sitesform[] = Array(
686                                 'sitename' => Array("sitename[$id]", "Site name", $s['sitename'], ""),
687                                 'apiurl' => Array("apiurl[$id]", "Api url", $s['apiurl'], t("Base API Path \x28remember the trailing /\x29") ),
688                                 'secret' => Array("secret[$id]", "Secret", $s['consumersecret'], ""),
689                                 'key' => Array("key[$id]", "Key", $s['consumerkey'], ""),
690                                 //'applicationname' => Array("applicationname[$id]", "Application name", $s['applicationname'], ""),
691                                 'delete' => Array("delete[$id]", "Delete", False , "Check to delete this preset"),
692                         );
693                 }
694         }
695         /* empty form to add new site */
696         $id++;
697         $sitesform[] = Array(
698                 'sitename' => Array("sitename[$id]", t("Site name"), "", ""),
699                 'apiurl' => Array("apiurl[$id]", "Api url", "", t("Base API Path \x28remember the trailing /\x29") ),
700                 'secret' => Array("secret[$id]", t("Consumer Secret"), "", ""),
701                 'key' => Array("key[$id]", t("Consumer Key"), "", ""),
702                 //'applicationname' => Array("applicationname[$id]", t("Application name"), "", ""),
703         );
704
705         $t = get_markup_template( "admin.tpl", "addon/statusnet/" );
706         $o = replace_macros($t, array(
707                 '$submit' => t('Save Settings'),
708                 '$sites' => $sitesform,
709         ));
710 }
711
712 function statusnet_prepare_body(&$a,&$b) {
713         if ($b["item"]["network"] != NETWORK_STATUSNET)
714                 return;
715
716         if ($b["preview"]) {
717                 $max_char = get_pconfig(local_user(),'statusnet','max_char');
718                 if (intval($max_char) == 0)
719                         $max_char = 140;
720
721                 require_once("include/plaintext.php");
722                 $item = $b["item"];
723                 $item["plink"] = $a->get_baseurl()."/display/".$a->user["nickname"]."/".$item["parent"];
724
725                 $r = q("SELECT `item`.`author-link`, `item`.`uri`, `contact`.`nick` AS contact_nick
726                         FROM `item` INNER JOIN `contact` ON `contact`.`id` = `item`.`contact-id`
727                         WHERE `item`.`uri` = '%s' AND `item`.`uid` = %d LIMIT 1",
728                         dbesc($item["thr-parent"]),
729                         intval(local_user()));
730
731                 if(count($r)) {
732                         $orig_post = $r[0];
733                         //$nickname = "@[url=".$orig_post["author-link"]."]".$orig_post["contact_nick"]."[/url]";
734                         //$nicknameplain = "@".$orig_post["contact_nick"];
735
736                         $nick = preg_replace("=https?://(.*)/(.*)=ism", "$2", $orig_post["author-link"]);
737
738                         $nickname = "@[url=".$orig_post["author-link"]."]".$nick."[/url]";
739                         $nicknameplain = "@".$nick;
740
741                         if ((strpos($item["body"], $nickname) === false) AND (strpos($item["body"], $nicknameplain) === false))
742                                 $item["body"] = $nickname." ".$item["body"];
743                 }
744
745
746                 $msgarr = plaintext($a, $item, $max_char, true, 7);
747                 $msg = $msgarr["text"];
748
749                 if (isset($msgarr["url"]) AND ($msgarr["type"] != "photo"))
750                         $msg .= " ".$msgarr["url"];
751
752                 if (isset($msgarr["image"]))
753                         $msg .= " ".$msgarr["image"];
754
755                 $b['html'] = nl2br(htmlspecialchars($msg));
756         }
757 }
758
759 function statusnet_cron($a,$b) {
760         $last = get_config('statusnet','last_poll');
761
762         $poll_interval = intval(get_config('statusnet','poll_interval'));
763         if(! $poll_interval)
764                 $poll_interval = STATUSNET_DEFAULT_POLL_INTERVAL;
765
766         if($last) {
767                 $next = $last + ($poll_interval * 60);
768                 if($next > time()) {
769                         logger('statusnet: poll intervall not reached');
770                         return;
771                 }
772         }
773         logger('statusnet: cron_start');
774
775         $r = q("SELECT * FROM `pconfig` WHERE `cat` = 'statusnet' AND `k` = 'mirror_posts' AND `v` = '1' ORDER BY RAND() ");
776         if(count($r)) {
777                 foreach($r as $rr) {
778                         logger('statusnet: fetching for user '.$rr['uid']);
779                         statusnet_fetchtimeline($a, $rr['uid']);
780                 }
781         }
782
783         $abandon_days = intval(get_config('system','account_abandon_days'));
784         if ($abandon_days < 1)
785                 $abandon_days = 0;
786
787         $abandon_limit = date("Y-m-d H:i:s", time() - $abandon_days * 86400);
788
789         $r = q("SELECT * FROM `pconfig` WHERE `cat` = 'statusnet' AND `k` = 'import' AND `v` ORDER BY RAND()");
790         if(count($r)) {
791                 foreach($r as $rr) {
792                         if ($abandon_days != 0) {
793                                 $user = q("SELECT `login_date` FROM `user` WHERE uid=%d AND `login_date` >= '%s'", $rr['uid'], $abandon_limit);
794                                 if (!count($user)) {
795                                         logger('abandoned account: timeline from user '.$rr['uid'].' will not be imported');
796                                         continue;
797                                 }
798                         }
799
800                         logger('statusnet: importing timeline from user '.$rr['uid']);
801                         statusnet_fetchhometimeline($a, $rr["uid"], $rr["v"]);
802                 }
803         }
804
805         logger('statusnet: cron_end');
806
807         set_config('statusnet','last_poll', time());
808 }
809
810 function statusnet_fetchtimeline($a, $uid) {
811         $ckey    = get_pconfig($uid, 'statusnet', 'consumerkey');
812         $csecret = get_pconfig($uid, 'statusnet', 'consumersecret');
813         $api     = get_pconfig($uid, 'statusnet', 'baseapi');
814         $otoken  = get_pconfig($uid, 'statusnet', 'oauthtoken');
815         $osecret = get_pconfig($uid, 'statusnet', 'oauthsecret');
816         $lastid  = get_pconfig($uid, 'statusnet', 'lastid');
817
818         require_once('mod/item.php');
819         require_once('include/items.php');
820
821         //  get the application name for the SN app
822         //  1st try personal config, then system config and fallback to the
823         //  hostname of the node if neither one is set.
824         $application_name  = get_pconfig( $uid, 'statusnet', 'application_name');
825         if ($application_name == "")
826                 $application_name  = get_config('statusnet', 'application_name');
827         if ($application_name == "")
828                 $application_name = $a->get_hostname();
829
830         $connection = new StatusNetOAuth($api, $ckey,$csecret,$otoken,$osecret);
831
832         $parameters = array("exclude_replies" => true, "trim_user" => true, "contributor_details" => false, "include_rts" => false);
833
834         $first_time = ($lastid == "");
835
836         if ($lastid <> "")
837                 $parameters["since_id"] = $lastid;
838
839         $items = $connection->get('statuses/user_timeline', $parameters);
840
841         if (!is_array($items))
842                 return;
843
844         $posts = array_reverse($items);
845
846         if (count($posts)) {
847             foreach ($posts as $post) {
848                 if ($post->id > $lastid)
849                         $lastid = $post->id;
850
851                 if ($first_time)
852                         continue;
853
854                 if ($post->source == "activity")
855                         continue;
856
857                 if (is_object($post->retweeted_status))
858                         continue;
859
860                 if ($post->in_reply_to_status_id != "")
861                         continue;
862
863                 if (!stristr($post->source, $application_name)) {
864                         $_SESSION["authenticated"] = true;
865                         $_SESSION["uid"] = $uid;
866
867                         unset($_REQUEST);
868                         $_REQUEST["type"] = "wall";
869                         $_REQUEST["api_source"] = true;
870                         $_REQUEST["profile_uid"] = $uid;
871                         //$_REQUEST["source"] = "StatusNet";
872                         $_REQUEST["source"] = $post->source;
873                         $_REQUEST["extid"] = NETWORK_STATUSNET;
874
875                         if (isset($post->id)) {
876                                 $_REQUEST['message_id'] = item_new_uri($a->get_hostname(), $uid, NETWORK_STATUSNET.":".$post->id);
877                         }
878
879                         //$_REQUEST["date"] = $post->created_at;
880
881                         $_REQUEST["title"] = "";
882
883                         $_REQUEST["body"] = add_page_info_to_body($post->text, true);
884                         if (is_string($post->place->name))
885                                 $_REQUEST["location"] = $post->place->name;
886
887                         if (is_string($post->place->full_name))
888                                 $_REQUEST["location"] = $post->place->full_name;
889
890                         if (is_array($post->geo->coordinates))
891                                 $_REQUEST["coord"] = $post->geo->coordinates[0]." ".$post->geo->coordinates[1];
892
893                         if (is_array($post->coordinates->coordinates))
894                                 $_REQUEST["coord"] = $post->coordinates->coordinates[1]." ".$post->coordinates->coordinates[0];
895
896                         //print_r($_REQUEST);
897                         if ($_REQUEST["body"] != "") {
898                                 logger('statusnet: posting for user '.$uid);
899
900                                 item_post($a);
901                         }
902                 }
903             }
904         }
905         set_pconfig($uid, 'statusnet', 'lastid', $lastid);
906 }
907
908 function statusnet_address($contact) {
909         $hostname = normalise_link($contact->statusnet_profile_url);
910         $nickname = $contact->screen_name;
911
912         $hostname = preg_replace("=https?://([\w\.]*)/.*=ism", "$1", $contact->statusnet_profile_url);
913
914         $address = $contact->screen_name."@".$hostname;
915
916         return($address);
917 }
918
919 function statusnet_fetch_contact($uid, $contact, $create_user) {
920         if ($contact->statusnet_profile_url == "")
921                 return(-1);
922
923         update_gcontact(array("url" => $contact->statusnet_profile_url,
924                         "network" => NETWORK_STATUSNET, "photo" => $contact->profile_image_url,
925                         "name" => $contact->name, "nick" => $contact->screen_name,
926                         "location" => $contact->location, "about" => $contact->description,
927                         "addr" => statusnet_address($contact), "generation" => 3));
928
929         $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `alias` = '%s' AND `network` = '%s'LIMIT 1",
930                 intval($uid), dbesc(normalise_link($contact->statusnet_profile_url)), dbesc(NETWORK_STATUSNET));
931
932         if(!count($r) AND !$create_user)
933                 return(0);
934
935         if (count($r) AND ($r[0]["readonly"] OR $r[0]["blocked"])) {
936                 logger("statusnet_fetch_contact: Contact '".$r[0]["nick"]."' is blocked or readonly.", LOGGER_DEBUG);
937                 return(-1);
938         }
939
940         if(!count($r)) {
941                 // create contact record
942                 q("INSERT INTO `contact` ( `uid`, `created`, `url`, `nurl`, `addr`, `alias`, `notify`, `poll`,
943                                         `name`, `nick`, `photo`, `network`, `rel`, `priority`,
944                                         `location`, `about`, `writable`, `blocked`, `readonly`, `pending` )
945                                         VALUES ( %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, '%s', '%s', %d, 0, 0, 0 ) ",
946                         intval($uid),
947                         dbesc(datetime_convert()),
948                         dbesc($contact->statusnet_profile_url),
949                         dbesc(normalise_link($contact->statusnet_profile_url)),
950                         dbesc(statusnet_address($contact)),
951                         dbesc(normalise_link($contact->statusnet_profile_url)),
952                         dbesc(''),
953                         dbesc(''),
954                         dbesc($contact->name),
955                         dbesc($contact->screen_name),
956                         dbesc($contact->profile_image_url),
957                         dbesc(NETWORK_STATUSNET),
958                         intval(CONTACT_IS_FRIEND),
959                         intval(1),
960                         dbesc($contact->location),
961                         dbesc($contact->description),
962                         intval(1)
963                 );
964
965                 $r = q("SELECT * FROM `contact` WHERE `alias` = '%s' AND `uid` = %d AND `network` = '%s' LIMIT 1",
966                         dbesc($contact->statusnet_profile_url),
967                         intval($uid),
968                         dbesc(NETWORK_STATUSNET));
969
970                 if(! count($r))
971                         return(false);
972
973                 $contact_id  = $r[0]['id'];
974
975                 $g = q("SELECT def_gid FROM user WHERE uid = %d LIMIT 1",
976                         intval($uid)
977                 );
978
979                 if($g && intval($g[0]['def_gid'])) {
980                         require_once('include/group.php');
981                         group_add_member($uid,'',$contact_id,$g[0]['def_gid']);
982                 }
983
984                 require_once("Photo.php");
985
986                 $photos = import_profile_photo($contact->profile_image_url,$uid,$contact_id);
987
988                 q("UPDATE `contact` SET `photo` = '%s',
989                                         `thumb` = '%s',
990                                         `micro` = '%s',
991                                         `avatar-date` = '%s'
992                                 WHERE `id` = %d",
993                         dbesc($photos[0]),
994                         dbesc($photos[1]),
995                         dbesc($photos[2]),
996                         dbesc(datetime_convert()),
997                         intval($contact_id)
998                 );
999         } else {
1000                 // update profile photos once every two weeks as we have no notification of when they change.
1001
1002                 //$update_photo = (($r[0]['avatar-date'] < datetime_convert('','','now -2 days')) ? true : false);
1003                 $update_photo = ($r[0]['avatar-date'] < datetime_convert('','','now -12 hours'));
1004
1005                 // check that we have all the photos, this has been known to fail on occasion
1006
1007                 if((!$r[0]['photo']) || (!$r[0]['thumb']) || (!$r[0]['micro']) || ($update_photo)) {
1008
1009                         logger("statusnet_fetch_contact: Updating contact ".$contact->screen_name, LOGGER_DEBUG);
1010
1011                         require_once("Photo.php");
1012
1013                         $photos = import_profile_photo($contact->profile_image_url, $uid, $r[0]['id']);
1014
1015                         q("UPDATE `contact` SET `photo` = '%s',
1016                                                 `thumb` = '%s',
1017                                                 `micro` = '%s',
1018                                                 `name-date` = '%s',
1019                                                 `uri-date` = '%s',
1020                                                 `avatar-date` = '%s',
1021                                                 `url` = '%s',
1022                                                 `nurl` = '%s',
1023                                                 `addr` = '%s',
1024                                                 `name` = '%s',
1025                                                 `nick` = '%s',
1026                                                 `location` = '%s',
1027                                                 `about` = '%s'
1028                                         WHERE `id` = %d",
1029                                 dbesc($photos[0]),
1030                                 dbesc($photos[1]),
1031                                 dbesc($photos[2]),
1032                                 dbesc(datetime_convert()),
1033                                 dbesc(datetime_convert()),
1034                                 dbesc(datetime_convert()),
1035                                 dbesc($contact->statusnet_profile_url),
1036                                 dbesc(normalise_link($contact->statusnet_profile_url)),
1037                                 dbesc(statusnet_address($contact)),
1038                                 dbesc($contact->name),
1039                                 dbesc($contact->screen_name),
1040                                 dbesc($contact->location),
1041                                 dbesc($contact->description),
1042                                 intval($r[0]['id'])
1043                         );
1044                 }
1045         }
1046
1047         return($r[0]["id"]);
1048 }
1049
1050 function statusnet_fetchuser($a, $uid, $screen_name = "", $user_id = "") {
1051         $ckey    = get_pconfig($uid, 'statusnet', 'consumerkey');
1052         $csecret = get_pconfig($uid, 'statusnet', 'consumersecret');
1053         $api     = get_pconfig($uid, 'statusnet', 'baseapi');
1054         $otoken  = get_pconfig($uid, 'statusnet', 'oauthtoken');
1055         $osecret = get_pconfig($uid, 'statusnet', 'oauthsecret');
1056
1057         require_once("addon/statusnet/codebird.php");
1058
1059         $cb = \Codebird\Codebird::getInstance();
1060         $cb->setConsumerKey($ckey, $csecret);
1061         $cb->setToken($otoken, $osecret);
1062
1063         $r = q("SELECT * FROM `contact` WHERE `self` = 1 AND `uid` = %d LIMIT 1",
1064                 intval($uid));
1065
1066         if(count($r)) {
1067                 $self = $r[0];
1068         } else
1069                 return;
1070
1071         $parameters = array();
1072
1073         if ($screen_name != "")
1074                 $parameters["screen_name"] = $screen_name;
1075
1076         if ($user_id != "")
1077                 $parameters["user_id"] = $user_id;
1078
1079         // Fetching user data
1080         $user = $cb->users_show($parameters);
1081
1082         if (!is_object($user))
1083                 return;
1084
1085         $contact_id = statusnet_fetch_contact($uid, $user, true);
1086
1087         return $contact_id;
1088 }
1089
1090 function statusnet_createpost($a, $uid, $post, $self, $create_user, $only_existing_contact) {
1091
1092         require_once("include/html2bbcode.php");
1093
1094         logger("statusnet_createpost: start", LOGGER_DEBUG);
1095
1096         $api = get_pconfig($uid, 'statusnet', 'baseapi');
1097         $hostname = preg_replace("=https?://([\w\.]*)/.*=ism", "$1", $api);
1098
1099         $postarray = array();
1100         $postarray['network'] = NETWORK_STATUSNET;
1101         $postarray['gravity'] = 0;
1102         $postarray['uid'] = $uid;
1103         $postarray['wall'] = 0;
1104
1105         if (is_object($post->retweeted_status)) {
1106                 $content = $post->retweeted_status;
1107                 statusnet_fetch_contact($uid, $content->user, false);
1108         } else
1109                 $content = $post;
1110
1111         $postarray['uri'] = $hostname."::".$content->id;
1112
1113         $r = q("SELECT * FROM `item` WHERE `extid` = '%s' AND `uid` = %d LIMIT 1",
1114                         dbesc($postarray['uri']),
1115                         intval($uid)
1116                 );
1117
1118         if (count($r))
1119                 return(array());
1120
1121         $contactid = 0;
1122
1123         if ($content->in_reply_to_status_id != "") {
1124
1125                 $parent = $hostname."::".$content->in_reply_to_status_id;
1126
1127                 $r = q("SELECT * FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1",
1128                                 dbesc($parent),
1129                                 intval($uid)
1130                         );
1131                 if (count($r)) {
1132                         $postarray['thr-parent'] = $r[0]["uri"];
1133                         $postarray['parent-uri'] = $r[0]["parent-uri"];
1134                         $postarray['parent'] = $r[0]["parent"];
1135                         $postarray['object-type'] = ACTIVITY_OBJ_COMMENT;
1136                 } else {
1137                         $r = q("SELECT * FROM `item` WHERE `extid` = '%s' AND `uid` = %d LIMIT 1",
1138                                         dbesc($parent),
1139                                         intval($uid)
1140                                 );
1141                         if (count($r)) {
1142                                 $postarray['thr-parent'] = $r[0]['uri'];
1143                                 $postarray['parent-uri'] = $r[0]['parent-uri'];
1144                                 $postarray['parent'] = $r[0]['parent'];
1145                                 $postarray['object-type'] = ACTIVITY_OBJ_COMMENT;
1146                         } else {
1147                                 $postarray['thr-parent'] = $postarray['uri'];
1148                                 $postarray['parent-uri'] = $postarray['uri'];
1149                                 $postarray['object-type'] = ACTIVITY_OBJ_NOTE;
1150                         }
1151                 }
1152
1153                 // Is it me?
1154                 $own_url = get_pconfig($uid, 'statusnet', 'own_url');
1155
1156                 if ($content->user->id == $own_url) {
1157                         $r = q("SELECT * FROM `contact` WHERE `self` = 1 AND `uid` = %d LIMIT 1",
1158                                 intval($uid));
1159
1160                         if(count($r)) {
1161                                 $contactid = $r[0]["id"];
1162
1163                                 $postarray['owner-name'] =  $r[0]["name"];
1164                                 $postarray['owner-link'] = $r[0]["url"];
1165                                 $postarray['owner-avatar'] =  $r[0]["photo"];
1166                         } else
1167                                 return(array());
1168                 }
1169                 // Don't create accounts of people who just comment something
1170                 $create_user = false;
1171         } else {
1172                 $postarray['parent-uri'] = $postarray['uri'];
1173                 $postarray['object-type'] = ACTIVITY_OBJ_NOTE;
1174         }
1175
1176         if ($contactid == 0) {
1177                 $contactid = statusnet_fetch_contact($uid, $post->user, $create_user);
1178                 $postarray['owner-name'] = $post->user->name;
1179                 $postarray['owner-link'] = $post->user->statusnet_profile_url;
1180                 $postarray['owner-avatar'] = $post->user->profile_image_url;
1181         }
1182         if(($contactid == 0) AND !$only_existing_contact)
1183                 $contactid = $self['id'];
1184         elseif ($contactid <= 0)
1185                 return(array());
1186
1187         $postarray['contact-id'] = $contactid;
1188
1189         $postarray['verb'] = ACTIVITY_POST;
1190
1191         $postarray['author-name'] = $content->user->name;
1192         $postarray['author-link'] = $content->user->statusnet_profile_url;
1193         $postarray['author-avatar'] = $content->user->profile_image_url;
1194
1195         // To-Do: Maybe unreliable? Can the api be entered without trailing "/"?
1196         $hostname = str_replace("/api/", "/notice/", get_pconfig($uid, 'statusnet', 'baseapi'));
1197
1198         $postarray['plink'] = $hostname.$content->id;
1199         $postarray['app'] = strip_tags($content->source);
1200
1201         if ($content->user->protected) {
1202                 $postarray['private'] = 1;
1203                 $postarray['allow_cid'] = '<' . $self['id'] . '>';
1204         }
1205
1206         $postarray['body'] = html2bbcode($content->statusnet_html);
1207
1208         $converted = statusnet_convertmsg($a, $postarray['body'], false);
1209         $postarray['body'] = $converted["body"];
1210         $postarray['tag'] = $converted["tags"];
1211
1212         $postarray['created'] = datetime_convert('UTC','UTC',$content->created_at);
1213         $postarray['edited'] = datetime_convert('UTC','UTC',$content->created_at);
1214
1215         if (is_string($content->place->name))
1216                 $postarray["location"] = $content->place->name;
1217
1218         if (is_string($content->place->full_name))
1219                 $postarray["location"] = $content->place->full_name;
1220
1221         if (is_array($content->geo->coordinates))
1222                 $postarray["coord"] = $content->geo->coordinates[0]." ".$content->geo->coordinates[1];
1223
1224         if (is_array($content->coordinates->coordinates))
1225                 $postarray["coord"] = $content->coordinates->coordinates[1]." ".$content->coordinates->coordinates[0];
1226
1227         /*if (is_object($post->retweeted_status)) {
1228                 $postarray['body'] = html2bbcode($post->retweeted_status->statusnet_html);
1229
1230                 $converted = statusnet_convertmsg($a, $postarray['body'], false);
1231                 $postarray['body'] = $converted["body"];
1232                 $postarray['tag'] = $converted["tags"];
1233
1234                 statusnet_fetch_contact($uid, $post->retweeted_status->user, false);
1235
1236                 // Let retweets look like wall-to-wall posts
1237                 $postarray['author-name'] = $post->retweeted_status->user->name;
1238                 $postarray['author-link'] = $post->retweeted_status->user->statusnet_profile_url;
1239                 $postarray['author-avatar'] = $post->retweeted_status->user->profile_image_url;
1240         }*/
1241         logger("statusnet_createpost: end", LOGGER_DEBUG);
1242         return($postarray);
1243 }
1244
1245 function statusnet_checknotification($a, $uid, $own_url, $top_item, $postarray) {
1246
1247         // This function necer worked and need cleanup
1248
1249         $user = q("SELECT * FROM `contact` WHERE `uid` = %d AND `self` LIMIT 1",
1250                         intval($uid)
1251                 );
1252
1253         if(!count($user))
1254                 return;
1255
1256         // Is it me?
1257         if (link_compare($user[0]["url"], $postarray['author-link']))
1258                 return;
1259
1260         $own_user = q("SELECT * FROM `contact` WHERE `uid` = %d AND `alias` = '%s' LIMIT 1",
1261                         intval($uid),
1262                         dbesc($own_url)
1263                 );
1264
1265         if(!count($own_user))
1266                 return;
1267
1268         // Is it me from GNU Social?
1269         if (link_compare($own_user[0]["url"], $postarray['author-link']))
1270                 return;
1271
1272         $myconv = q("SELECT `author-link`, `author-avatar`, `parent` FROM `item` WHERE `parent-uri` = '%s' AND `uid` = %d AND `parent` != 0 AND `deleted` = 0",
1273                         dbesc($postarray['parent-uri']),
1274                         intval($uid)
1275                         );
1276
1277         if(count($myconv)) {
1278
1279                 foreach($myconv as $conv) {
1280                         // now if we find a match, it means we're in this conversation
1281
1282                         if(!link_compare($conv['author-link'],$user[0]["url"]) AND !link_compare($conv['author-link'],$own_user[0]["url"]))
1283                                 continue;
1284
1285                         require_once('include/enotify.php');
1286
1287                         $conv_parent = $conv['parent'];
1288
1289                         notification(array(
1290                                 'type'         => NOTIFY_COMMENT,
1291                                 'notify_flags' => $user[0]['notify-flags'],
1292                                 'language'     => $user[0]['language'],
1293                                 'to_name'      => $user[0]['username'],
1294                                 'to_email'     => $user[0]['email'],
1295                                 'uid'          => $user[0]['uid'],
1296                                 'item'         => $postarray,
1297                                 'link'         => $a->get_baseurl().'/display/'.urlencode(get_item_guid($top_item)),
1298                                 'source_name'  => $postarray['author-name'],
1299                                 'source_link'  => $postarray['author-link'],
1300                                 'source_photo' => $postarray['author-avatar'],
1301                                 'verb'         => ACTIVITY_POST,
1302                                 'otype'        => 'item',
1303                                 'parent'       => $conv_parent,
1304                         ));
1305
1306                         // only send one notification
1307                         break;
1308                 }
1309         }
1310 }
1311
1312 function statusnet_fetchhometimeline($a, $uid, $mode = 1) {
1313         $conversations = array();
1314
1315         $ckey    = get_pconfig($uid, 'statusnet', 'consumerkey');
1316         $csecret = get_pconfig($uid, 'statusnet', 'consumersecret');
1317         $api     = get_pconfig($uid, 'statusnet', 'baseapi');
1318         $otoken  = get_pconfig($uid, 'statusnet', 'oauthtoken');
1319         $osecret = get_pconfig($uid, 'statusnet', 'oauthsecret');
1320         $create_user = get_pconfig($uid, 'statusnet', 'create_user');
1321
1322         // "create_user" is deactivated, since currently you cannot add users manually by now
1323         $create_user = true;
1324
1325         logger("statusnet_fetchhometimeline: Fetching for user ".$uid, LOGGER_DEBUG);
1326
1327         require_once('library/twitteroauth.php');
1328         require_once('include/items.php');
1329
1330         $connection = new StatusNetOAuth($api, $ckey,$csecret,$otoken,$osecret);
1331
1332         $own_contact = statusnet_fetch_own_contact($a, $uid);
1333
1334         $r = q("SELECT * FROM `contact` WHERE `id` = %d AND `uid` = %d LIMIT 1",
1335                 intval($own_contact),
1336                 intval($uid));
1337
1338         if(count($r)) {
1339                 $nick = $r[0]["nick"];
1340         } else {
1341                 logger("statusnet_fetchhometimeline: Own GNU Social contact not found for user ".$uid, LOGGER_DEBUG);
1342                 return;
1343         }
1344
1345         $r = q("SELECT * FROM `contact` WHERE `self` = 1 AND `uid` = %d LIMIT 1",
1346                 intval($uid));
1347
1348         if(count($r)) {
1349                 $self = $r[0];
1350         } else {
1351                 logger("statusnet_fetchhometimeline: Own contact not found for user ".$uid, LOGGER_DEBUG);
1352                 return;
1353         }
1354
1355         $u = q("SELECT * FROM user WHERE uid = %d LIMIT 1",
1356                 intval($uid));
1357         if(!count($u)) {
1358                 logger("statusnet_fetchhometimeline: Own user not found for user ".$uid, LOGGER_DEBUG);
1359                 return;
1360         }
1361
1362         $parameters = array("exclude_replies" => false, "trim_user" => false, "contributor_details" => true, "include_rts" => true);
1363         //$parameters["count"] = 200;
1364
1365         if ($mode == 1) {
1366                 // Fetching timeline
1367                 $lastid  = get_pconfig($uid, 'statusnet', 'lasthometimelineid');
1368                 //$lastid = 1;
1369
1370                 $first_time = ($lastid == "");
1371
1372                 if ($lastid <> "")
1373                         $parameters["since_id"] = $lastid;
1374
1375                 $items = $connection->get('statuses/home_timeline', $parameters);
1376
1377                 if (!is_array($items)) {
1378                         if (is_object($items) AND isset($items->error))
1379                                 $errormsg = $items->error;
1380                         elseif (is_object($items))
1381                                 $errormsg = print_r($items, true);
1382                         elseif (is_string($items) OR is_float($items) OR is_int($items))
1383                                 $errormsg = $items;
1384                         else
1385                                 $errormsg = "Unknown error";
1386
1387                         logger("statusnet_fetchhometimeline: Error fetching home timeline: ".$errormsg, LOGGER_DEBUG);
1388                         return;
1389                 }
1390
1391                 $posts = array_reverse($items);
1392
1393                 logger("statusnet_fetchhometimeline: Fetching timeline for user ".$uid." ".sizeof($posts)." items", LOGGER_DEBUG);
1394
1395                 if (count($posts)) {
1396                         foreach ($posts as $post) {
1397
1398                                 if ($post->id > $lastid)
1399                                         $lastid = $post->id;
1400
1401                                 if ($first_time)
1402                                         continue;
1403
1404                                 if (isset($post->statusnet_conversation_id)) {
1405                                         if (!isset($conversations[$post->statusnet_conversation_id])) {
1406                                                 statusnet_complete_conversation($a, $uid, $self, $create_user, $nick, $post->statusnet_conversation_id);
1407                                                 $conversations[$post->statusnet_conversation_id] = $post->statusnet_conversation_id;
1408                                         }
1409                                 } else {
1410                                         $postarray = statusnet_createpost($a, $uid, $post, $self, $create_user, true);
1411
1412                                         if (trim($postarray['body']) == "")
1413                                                 continue;
1414
1415                                         $item = item_store($postarray);
1416                                         $postarray["id"] = $item;
1417
1418                                         logger('statusnet_fetchhometimeline: User '.$self["nick"].' posted home timeline item '.$item);
1419
1420                                         if ($item AND !function_exists("check_item_notification"))
1421                                                 statusnet_checknotification($a, $uid, $nick, $item, $postarray);
1422                                 }
1423
1424                         }
1425                 }
1426                 set_pconfig($uid, 'statusnet', 'lasthometimelineid', $lastid);
1427         }
1428
1429         // Fetching mentions
1430         $lastid  = get_pconfig($uid, 'statusnet', 'lastmentionid');
1431         $first_time = ($lastid == "");
1432
1433         if ($lastid <> "")
1434                 $parameters["since_id"] = $lastid;
1435
1436         $items = $connection->get('statuses/mentions_timeline', $parameters);
1437
1438         if (!is_array($items)) {
1439                 logger("statusnet_fetchhometimeline: Error fetching mentions: ".print_r($items, true), LOGGER_DEBUG);
1440                 return;
1441         }
1442
1443         $posts = array_reverse($items);
1444
1445         logger("statusnet_fetchhometimeline: Fetching mentions for user ".$uid." ".sizeof($posts)." items", LOGGER_DEBUG);
1446
1447         if (count($posts)) {
1448                 foreach ($posts as $post) {
1449                         if ($post->id > $lastid)
1450                                 $lastid = $post->id;
1451
1452                         if ($first_time)
1453                                 continue;
1454
1455                         $postarray = statusnet_createpost($a, $uid, $post, $self, false, false);
1456
1457                         if (isset($post->statusnet_conversation_id)) {
1458                                 if (!isset($conversations[$post->statusnet_conversation_id])) {
1459                                         statusnet_complete_conversation($a, $uid, $self, $create_user, $nick, $post->statusnet_conversation_id);
1460                                         $conversations[$post->statusnet_conversation_id] = $post->statusnet_conversation_id;
1461                                 }
1462                         } else {
1463                                 if (trim($postarray['body']) != "") {
1464                                         continue;
1465
1466                                         $item = item_store($postarray);
1467                                         $postarray["id"] = $item;
1468
1469                                         logger('statusnet_fetchhometimeline: User '.$self["nick"].' posted mention timeline item '.$item);
1470
1471                                         if ($item AND function_exists("check_item_notification"))
1472                                                 check_item_notification($item, $uid, NOTIFY_TAGSELF);
1473                                 }
1474                         }
1475
1476                         $r = q("SELECT * FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1",
1477                                 dbesc($postarray['uri']),
1478                                 intval($uid)
1479                         );
1480                         if (count($r)) {
1481                                 $item = $r[0]['id'];
1482                                 $parent_id = $r[0]['parent'];
1483                         }
1484
1485                         if (($item != 0) AND !function_exists("check_item_notification")) {
1486                                 require_once('include/enotify.php');
1487                                 notification(array(
1488                                         'type'         => NOTIFY_TAGSELF,
1489                                         'notify_flags' => $u[0]['notify-flags'],
1490                                         'language'     => $u[0]['language'],
1491                                         'to_name'      => $u[0]['username'],
1492                                         'to_email'     => $u[0]['email'],
1493                                         'uid'          => $u[0]['uid'],
1494                                         'item'         => $postarray,
1495                                         'link'         => $a->get_baseurl().'/display/'.urlencode(get_item_guid($item)),
1496                                         'source_name'  => $postarray['author-name'],
1497                                         'source_link'  => $postarray['author-link'],
1498                                         'source_photo' => $postarray['author-avatar'],
1499                                         'verb'         => ACTIVITY_TAG,
1500                                         'otype'        => 'item',
1501                                         'parent'       => $parent_id,
1502                                 ));
1503                         }
1504                 }
1505         }
1506
1507         set_pconfig($uid, 'statusnet', 'lastmentionid', $lastid);
1508 }
1509
1510 function statusnet_complete_conversation($a, $uid, $self, $create_user, $nick, $conversation) {
1511         $ckey    = get_pconfig($uid, 'statusnet', 'consumerkey');
1512         $csecret = get_pconfig($uid, 'statusnet', 'consumersecret');
1513         $api     = get_pconfig($uid, 'statusnet', 'baseapi');
1514         $otoken  = get_pconfig($uid, 'statusnet', 'oauthtoken');
1515         $osecret = get_pconfig($uid, 'statusnet', 'oauthsecret');
1516         $own_url = get_pconfig($uid, 'statusnet', 'own_url');
1517
1518         require_once('library/twitteroauth.php');
1519
1520         $connection = new StatusNetOAuth($api, $ckey,$csecret,$otoken,$osecret);
1521
1522         $parameters["count"] = 200;
1523
1524         $items = $connection->get('statusnet/conversation/'.$conversation, $parameters);
1525         if (is_array($items)) {
1526                 $posts = array_reverse($items);
1527
1528                 foreach($posts AS $post) {
1529                         $postarray = statusnet_createpost($a, $uid, $post, $self, false, false);
1530
1531                         if (trim($postarray['body']) == "")
1532                                 continue;
1533
1534                         //print_r($postarray);
1535                         $item = item_store($postarray);
1536                         $postarray["id"] = $item;
1537
1538                         logger('statusnet_complete_conversation: User '.$self["nick"].' posted home timeline item '.$item);
1539
1540                         if ($item AND !function_exists("check_item_notification"))
1541                                 statusnet_checknotification($a, $uid, $nick, $item, $postarray);
1542                 }
1543         }
1544 }
1545
1546 function statusnet_convertmsg($a, $body, $no_tags = false) {
1547
1548         require_once("include/oembed.php");
1549         require_once("include/items.php");
1550         require_once("include/network.php");
1551
1552         $body = preg_replace("=\[url\=https?://([0-9]*).([0-9]*).([0-9]*).([0-9]*)/([0-9]*)\](.*?)\[\/url\]=ism","$1.$2.$3.$4/$5",$body);
1553
1554         $URLSearchString = "^\[\]";
1555         $links = preg_match_all("/[^!#@]\[url\=([$URLSearchString]*)\](.*?)\[\/url\]/ism", $body,$matches,PREG_SET_ORDER);
1556
1557         $footer = "";
1558         $footerurl = "";
1559         $footerlink = "";
1560         $type = "";
1561
1562         if ($links) {
1563                 foreach ($matches AS $match) {
1564                         $search = "[url=".$match[1]."]".$match[2]."[/url]";
1565
1566                         logger("statusnet_convertmsg: expanding url ".$match[1], LOGGER_DEBUG);
1567
1568                         $expanded_url = original_url($match[1]);
1569
1570                         logger("statusnet_convertmsg: fetching data for ".$expanded_url, LOGGER_DEBUG);
1571
1572                         $oembed_data = oembed_fetch_url($expanded_url, true);
1573
1574                         logger("statusnet_convertmsg: fetching data: done", LOGGER_DEBUG);
1575
1576                         if ($type == "")
1577                                 $type = $oembed_data->type;
1578                         if ($oembed_data->type == "video") {
1579                                 //$body = str_replace($search, "[video]".$expanded_url."[/video]", $body);
1580                                 $type = $oembed_data->type;
1581                                 $footerurl = $expanded_url;
1582                                 $footerlink = "[url=".$expanded_url."]".$expanded_url."[/url]";
1583
1584                                 $body = str_replace($search, $footerlink, $body);
1585                         } elseif (($oembed_data->type == "photo") AND isset($oembed_data->url) AND !$dontincludemedia)
1586                                 $body = str_replace($search, "[url=".$expanded_url."][img]".$oembed_data->url."[/img][/url]", $body);
1587                         elseif ($oembed_data->type != "link")
1588                                 $body = str_replace($search,  "[url=".$expanded_url."]".$expanded_url."[/url]", $body);
1589                         else {
1590                                 $img_str = fetch_url($expanded_url, true, $redirects, 4);
1591
1592                                 $tempfile = tempnam(get_temppath(), "cache");
1593                                 file_put_contents($tempfile, $img_str);
1594                                 $mime = image_type_to_mime_type(exif_imagetype($tempfile));
1595                                 unlink($tempfile);
1596
1597                                 if (substr($mime, 0, 6) == "image/") {
1598                                         $type = "photo";
1599                                         $body = str_replace($search, "[img]".$expanded_url."[/img]", $body);
1600                                 } else {
1601                                         $type = $oembed_data->type;
1602                                         $footerurl = $expanded_url;
1603                                         $footerlink = "[url=".$expanded_url."]".$expanded_url."[/url]";
1604
1605                                         $body = str_replace($search, $footerlink, $body);
1606                                 }
1607                         }
1608                 }
1609
1610                 if ($footerurl != "")
1611                         $footer = add_page_info($footerurl);
1612
1613                 if (($footerlink != "") AND (trim($footer) != "")) {
1614                         $removedlink = trim(str_replace($footerlink, "", $body));
1615
1616                         if (($removedlink == "") OR strstr($body, $removedlink))
1617                                 $body = $removedlink;
1618
1619                         $body .= $footer;
1620                 }
1621         }
1622
1623         if ($no_tags)
1624                 return(array("body" => $body, "tags" => ""));
1625
1626         $str_tags = '';
1627
1628         $cnt = preg_match_all("/([!#@])\[url\=([$URLSearchString]*)\](.*?)\[\/url\]/ism",$body,$matches,PREG_SET_ORDER);
1629         if($cnt) {
1630                 foreach($matches as $mtch) {
1631                         if(strlen($str_tags))
1632                                 $str_tags .= ',';
1633
1634                         if ($mtch[1] == "#") {
1635                                 // Replacing the hash tags that are directed to the GNU Social server with internal links
1636                                 $snhash = "#[url=".$mtch[2]."]".$mtch[3]."[/url]";
1637                                 $frdchash = '#[url='.$a->get_baseurl().'/search?tag='.rawurlencode($mtch[3]).']'.$mtch[3].'[/url]';
1638                                 $body = str_replace($snhash, $frdchash, $body);
1639
1640                                 $str_tags .= $frdchash;
1641                         } else
1642                                 $str_tags .= "@[url=".$mtch[2]."]".$mtch[3]."[/url]";
1643                                 // To-Do:
1644                                 // There is a problem with links with to GNU Social groups, so these links are stored with "@" like friendica groups
1645                                 //$str_tags .= $mtch[1]."[url=".$mtch[2]."]".$mtch[3]."[/url]";
1646                 }
1647         }
1648
1649         return(array("body"=>$body, "tags"=>$str_tags));
1650
1651 }
1652
1653 function statusnet_fetch_own_contact($a, $uid) {
1654         $ckey    = get_pconfig($uid, 'statusnet', 'consumerkey');
1655         $csecret = get_pconfig($uid, 'statusnet', 'consumersecret');
1656         $api     = get_pconfig($uid, 'statusnet', 'baseapi');
1657         $otoken  = get_pconfig($uid, 'statusnet', 'oauthtoken');
1658         $osecret = get_pconfig($uid, 'statusnet', 'oauthsecret');
1659         $own_url = get_pconfig($uid, 'statusnet', 'own_url');
1660
1661         $contact_id = 0;
1662
1663         if ($own_url == "") {
1664                 require_once('library/twitteroauth.php');
1665
1666                 $connection = new StatusNetOAuth($api, $ckey,$csecret,$otoken,$osecret);
1667
1668                 // Fetching user data
1669                 $user = $connection->get('account/verify_credentials');
1670
1671                 set_pconfig($uid, 'statusnet', 'own_url', normalise_link($user->statusnet_profile_url));
1672
1673                 $contact_id = statusnet_fetch_contact($uid, $user, true);
1674
1675         } else {
1676                 $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `alias` = '%s' LIMIT 1",
1677                         intval($uid), dbesc($own_url));
1678                 if(count($r))
1679                         $contact_id = $r[0]["id"];
1680                 else
1681                         del_pconfig($uid, 'statusnet', 'own_url');
1682
1683         }
1684         return($contact_id);
1685 }
1686
1687 function statusnet_is_retweet($a, $uid, $body) {
1688         $body = trim($body);
1689
1690         // Skip if it isn't a pure repeated messages
1691         // Does it start with a share?
1692         if (strpos($body, "[share") > 0)
1693                 return(false);
1694
1695         // Does it end with a share?
1696         if (strlen($body) > (strrpos($body, "[/share]") + 8))
1697                 return(false);
1698
1699         $attributes = preg_replace("/\[share(.*?)\]\s?(.*?)\s?\[\/share\]\s?/ism","$1",$body);
1700         // Skip if there is no shared message in there
1701         if ($body == $attributes)
1702                 return(false);
1703
1704         $link = "";
1705         preg_match("/link='(.*?)'/ism", $attributes, $matches);
1706         if ($matches[1] != "")
1707                 $link = $matches[1];
1708
1709         preg_match('/link="(.*?)"/ism', $attributes, $matches);
1710         if ($matches[1] != "")
1711                 $link = $matches[1];
1712
1713         $ckey    = get_pconfig($uid, 'statusnet', 'consumerkey');
1714         $csecret = get_pconfig($uid, 'statusnet', 'consumersecret');
1715         $api     = get_pconfig($uid, 'statusnet', 'baseapi');
1716         $otoken  = get_pconfig($uid, 'statusnet', 'oauthtoken');
1717         $osecret = get_pconfig($uid, 'statusnet', 'oauthsecret');
1718         $hostname = preg_replace("=https?://([\w\.]*)/.*=ism", "$1", $api);
1719
1720         $id = preg_replace("=https?://".$hostname."/notice/(.*)=ism", "$1", $link);
1721
1722         if ($id == $link)
1723                 return(false);
1724
1725         logger('statusnet_is_retweet: Retweeting id '.$id.' for user '.$uid, LOGGER_DEBUG);
1726
1727         $connection = new StatusNetOAuth($api, $ckey,$csecret,$otoken,$osecret);
1728
1729         $result = $connection->post('statuses/retweet/'.$id);
1730
1731         logger('statusnet_is_retweet: result '.print_r($result, true), LOGGER_DEBUG);
1732         return(isset($result->id));
1733 }