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