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