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