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