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