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