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