]> git.mxchange.org Git - friendica-addons.git/blob - twitter/twitter.php
ef69740429b4c85a7f3d148f58aa5006376a8f81
[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/api.php");
376         require_once("include/bbcode.php");
377         require_once("include/html2plain.php");
378
379         $max_char = 140;
380
381         // Looking for the first image
382         $cleaned_body = api_clean_plain_items($b['body']);
383         $image = '';
384         if(preg_match("/\[img\=([0-9]*)x([0-9]*)\](.*?)\[\/img\]/is",$cleaned_body,$matches))
385                 $image = $matches[3];
386
387         if ($image == '')
388                 if(preg_match("/\[img\](.*?)\[\/img\]/is",$cleaned_body,$matches))
389                         $image = $matches[1];
390
391         $multipleimages = (strpos($cleaned_body, "[img") != strrpos($cleaned_body, "[img"));
392
393         // When saved into the database the content is sent through htmlspecialchars
394         // That means that we have to decode all image-urls
395         $image = htmlspecialchars_decode($image);
396
397         $body = $b["body"];
398         if ($b["title"] != "")
399                 $body = $b["title"]."\n\n".$body;
400
401         if (strpos($body, "[bookmark") !== false) {
402                 // splitting the text in two parts:
403                 // before and after the bookmark
404                 $pos = strpos($body, "[bookmark");
405                 $body1 = substr($body, 0, $pos);
406                 $body2 = substr($body, $pos);
407
408                 // Removing all quotes after the bookmark
409                 // they are mostly only the content after the bookmark.
410                 $body2 = preg_replace("/\[quote\=([^\]]*)\](.*?)\[\/quote\]/ism",'',$body2);
411                 $body2 = preg_replace("/\[quote\](.*?)\[\/quote\]/ism",'',$body2);
412                 $body = $body1.$body2;
413         }
414
415         // Add some newlines so that the message could be cut better
416         $body = str_replace(array("[quote", "[bookmark", "[/bookmark]", "[/quote]"),
417                         array("\n[quote", "\n[bookmark", "[/bookmark]\n", "[/quote]\n"), $body);
418
419         // remove the recycle signs and the names since they aren't helpful on twitter
420         // recycle 1
421         $recycle = html_entity_decode("&#x2672; ", ENT_QUOTES, 'UTF-8');
422         $body = preg_replace( '/'.$recycle.'\[url\=(\w+.*?)\](\w+.*?)\[\/url\]/i', "\n", $body);
423         // recycle 2 (Test)
424         $recycle = html_entity_decode("&#x25CC; ", ENT_QUOTES, 'UTF-8');
425         $body = preg_replace( '/'.$recycle.'\[url\=(\w+.*?)\](\w+.*?)\[\/url\]/i', "\n", $body);
426
427         // remove the share element
428         //$body = preg_replace("/\[share(.*?)\](.*?)\[\/share\]/ism","\n\n$2\n\n",$body);
429
430         // At first convert the text to html
431         $html = bbcode(api_clean_plain_items($body), false, false, 2);
432
433         // Then convert it to plain text
434         $msg = trim(html2plain($html, 0, true));
435         $msg = html_entity_decode($msg,ENT_QUOTES,'UTF-8');
436
437         // Removing multiple newlines
438         while (strpos($msg, "\n\n\n") !== false)
439                 $msg = str_replace("\n\n\n", "\n\n", $msg);
440
441         // Removing multiple spaces
442         while (strpos($msg, "  ") !== false)
443                 $msg = str_replace("  ", " ", $msg);
444
445         $origmsg = trim($msg);
446
447         // Removing URLs
448         $msg = preg_replace('/(https?\:\/\/[a-zA-Z0-9\:\/\-\?\&\;\.\=\_\~\#\%\$\!\+\,]+)/i', "", $msg);
449
450         $msg = trim($msg);
451
452         $link = '';
453         // look for bookmark-bbcode and handle it with priority
454         if(preg_match("/\[bookmark\=([^\]]*)\](.*?)\[\/bookmark\]/is",$b['body'],$matches))
455                 $link = $matches[1];
456
457         $multiplelinks = (strpos($b['body'], "[bookmark") != strrpos($b['body'], "[bookmark"));
458
459         // If there is no bookmark element then take the first link
460         if ($link == '') {
461                 $links = collecturls($html);
462
463                 foreach($links AS $singlelink) {
464                         $img_str = fetch_url($singlelink);
465
466                         $tempfile = tempnam(get_config("system","temppath"), "cache");
467                         file_put_contents($tempfile, $img_str);
468                         $mime = image_type_to_mime_type(exif_imagetype($tempfile));
469                         unlink($tempfile);
470
471                         if (substr($mime, 0, 6) == "image/") {
472                                 $image = $singlelink;
473                                 unset($links[$singlelink]);
474                         }
475                 }
476
477                 if (sizeof($links) > 0) {
478                         reset($links);
479                         $link = current($links);
480                 }
481                 $multiplelinks = (sizeof($links) > 1);
482         }
483
484         $msglink = "";
485         if ($multiplelinks)
486                 $msglink = $b["plink"];
487         else if ($link != "")
488                 $msglink = $link;
489         else if ($multipleimages)
490                 $msglink = $b["plink"];
491         else if ($image != "")
492                 $msglink = $image;
493
494         if (($msglink == "") and strlen($msg) > $max_char)
495                 $msglink = $b["plink"];
496
497         // If the message is short enough then don't modify it.
498         if ((strlen($origmsg) <= $max_char) AND ($msglink == ""))
499                 return(array("msg"=>$origmsg, "image"=>""));
500
501         // If the message is short enough and contains a picture then post the picture as well
502         if ((strlen($origmsg) <= ($max_char - 23)) AND strpos($origmsg, $msglink))
503                 return(array("msg"=>$origmsg, "image"=>$image));
504
505         // If the message is short enough and the link exists in the original message don't modify it as well
506         // -3 because of the bad shortener of twitter
507         if ((strlen($origmsg) <= ($max_char - 3)) AND strpos($origmsg, $msglink))
508                 return(array("msg"=>$origmsg, "image"=>""));
509
510         // Preserve the unshortened link
511         $orig_link = $msglink;
512
513         // Just replace the message link with a 22 character long string
514         // Twitter calculates with this length
515         if (trim($msglink) <> '')
516                 $msglink = "1234567890123456789012";
517
518         if (strlen(trim($msg." ".$msglink)) > ($max_char)) {
519                 $msg = substr($msg, 0, ($max_char) - (strlen($msglink)));
520                 $lastchar = substr($msg, -1);
521                 $msg = substr($msg, 0, -1);
522                 $pos = strrpos($msg, "\n");
523                 if ($pos > 0)
524                         $msg = substr($msg, 0, $pos);
525                 else if ($lastchar != "\n")
526                         $msg = substr($msg, 0, -3)."...";
527
528                 // if the post contains a picture and a link then the system tries to cut the post earlier.
529                 // So the link and the picture can be posted.
530                 if (($image != "") AND ($orig_link != $image)) {
531                         $msg2 = substr($msg, 0, ($max_char - 20) - (strlen($msglink)));
532                         $lastchar = substr($msg2, -1);
533                         $msg2 = substr($msg2, 0, -1);
534                         $pos = strrpos($msg2, "\n");
535                         if ($pos > 0)
536                                 $msg = substr($msg2, 0, $pos);
537                         else if ($lastchar == "\n")
538                                 $msg = trim($msg2);
539                 }
540
541         }
542         // Removing multiple spaces - again
543         while (strpos($msg, "  ") !== false)
544                 $msg = str_replace("  ", " ", $msg);
545
546         $msg = trim($msg);
547
548         // Removing multiple newlines
549         //while (strpos($msg, "\n\n") !== false)
550         //      $msg = str_replace("\n\n", "\n", $msg);
551
552         // Looking if the link points to an image
553         $img_str = fetch_url($orig_link);
554
555         $tempfile = tempnam(get_config("system","temppath"), "cache");
556         file_put_contents($tempfile, $img_str);
557         $mime = image_type_to_mime_type(exif_imagetype($tempfile));
558         unlink($tempfile);
559
560         if (($image == $orig_link) OR (substr($mime, 0, 6) == "image/"))
561                 return(array("msg"=>$msg, "image"=>$orig_link));
562         else if (($image != $orig_link) AND ($image != "") AND (strlen($msg." ".$msglink) <= ($max_char - 23))) {
563                 if ($shortlink)
564                         $orig_link = short_link($orig_link);
565
566                 return(array("msg"=>$msg." ".$orig_link, "image"=>$image));
567         } else {
568                 if ($shortlink)
569                         $orig_link = short_link($orig_link);
570
571                 return(array("msg"=>$msg." ".$orig_link, "image"=>""));
572         }
573 }
574
575 function twitter_action($a, $uid, $pid, $action) {
576
577         $ckey    = get_config('twitter', 'consumerkey');
578         $csecret = get_config('twitter', 'consumersecret');
579         $otoken  = get_pconfig($uid, 'twitter', 'oauthtoken');
580         $osecret = get_pconfig($uid, 'twitter', 'oauthsecret');
581
582         require_once("addon/twitter/codebird.php");
583
584         $cb = \Codebird\Codebird::getInstance();
585         $cb->setConsumerKey($ckey, $csecret);
586         $cb->setToken($otoken, $osecret);
587
588         $post = array('id' => $pid);
589
590         logger("twitter_action '".$action."' ID: ".$pid." data: " . print_r($post, true), LOGGER_DATA);
591
592         switch ($action) {
593                 case "delete":
594                         $result = $cb->statuses_destroy($post);
595                         break;
596                 case "like":
597                         $result = $cb->favorites_create($post);
598                         break;
599                 case "unlike":
600                         $result = $cb->favorites_destroy($post);
601                         break;
602         }
603         logger("twitter_action '".$action."' send, result: " . print_r($result, true), LOGGER_DEBUG);
604 }
605
606 function twitter_post_hook(&$a,&$b) {
607
608         /**
609          * Post to Twitter
610          */
611
612         if (!get_pconfig($b["uid"],'twitter','import')) {
613                 if($b['deleted'] || $b['private'] || ($b['created'] !== $b['edited']))
614                         return;
615         }
616
617         if($b['parent'] != $b['id']) {
618                 logger("twitter_post_hook: parameter ".print_r($b, true), LOGGER_DATA);
619
620                 // Looking if its a reply to a twitter post
621                 if ((substr($b["parent-uri"], 0, 9) != "twitter::") AND (substr($b["extid"], 0, 9) != "twitter::") AND (substr($b["thr-parent"], 0, 9) != "twitter::")) {
622                         logger("twitter_post_hook: no twitter post ".$b["parent"]);
623                         return;
624                 }
625
626                 $r = q("SELECT * FROM item WHERE item.uri = '%s' AND item.uid = %d LIMIT 1",
627                         dbesc($b["thr-parent"]),
628                         intval($b["uid"]));
629
630                 if(!count($r)) {
631                         logger("twitter_post_hook: no parent found ".$b["thr-parent"]);
632                         return;
633                 } else {
634                         $iscomment = true;
635                         $orig_post = $r[0];
636                 }
637
638                 $nickname = preg_replace("=https?://twitter.com/(.*)=ism", "$1", $orig_post["author-link"]);
639                 $nickname = "@[url=".$orig_post["author-link"]."]".$nickname."[/url]";
640
641                 logger("twitter_post_hook: comparing ".$nickname." with ".$b["body"], LOGGER_DEBUG);
642                 if (strpos($b["body"], $nickname) === false)
643                         $b["body"] = $nickname." ".$b["body"];
644
645                 logger("twitter_post_hook: parent found ".print_r($orig_post, true), LOGGER_DATA);
646         } else {
647                 $iscomment = false;
648
649                 if($b['private'] OR !strstr($b['postopts'],'twitter'))
650                         return;
651         }
652
653         if (($b['verb'] == ACTIVITY_POST) AND $b['deleted'])
654                 twitter_action($a, $b["uid"], substr($orig_post["uri"], 9), "delete");
655
656         if($b['verb'] == ACTIVITY_LIKE) {
657                 logger("twitter_post_hook: parameter 2 ".substr($b["thr-parent"], 9), LOGGER_DEBUG);
658                 if ($b['deleted'])
659                         twitter_action($a, $b["uid"], substr($b["thr-parent"], 9), "unlike");
660                 else
661                         twitter_action($a, $b["uid"], substr($b["thr-parent"], 9), "like");
662                 return;
663         }
664
665         if($b['deleted'] || ($b['created'] !== $b['edited']))
666                 return;
667
668         // if post comes from twitter don't send it back
669         if($b['app'] == "Twitter")
670                 return;
671
672         logger('twitter post invoked');
673
674
675         load_pconfig($b['uid'], 'twitter');
676
677         $ckey    = get_config('twitter', 'consumerkey');
678         $csecret = get_config('twitter', 'consumersecret');
679         $otoken  = get_pconfig($b['uid'], 'twitter', 'oauthtoken');
680         $osecret = get_pconfig($b['uid'], 'twitter', 'oauthsecret');
681         $intelligent_shortening = get_pconfig($b['uid'], 'twitter', 'intelligent_shortening');
682
683         // Global setting overrides this
684         if (get_config('twitter','intelligent_shortening'))
685                 $intelligent_shortening = get_config('twitter','intelligent_shortening');
686
687         if($ckey && $csecret && $otoken && $osecret) {
688                 logger('twitter: we have customer key and oauth stuff, going to send.', LOGGER_DEBUG);
689
690                 // If it's a repeated message from twitter then do a native retweet and exit
691                 if (twitter_is_retweet($a, $b['uid'], $b['body']))
692                         return;
693
694                 require_once('library/twitteroauth.php');
695                 require_once('include/bbcode.php');
696                 $tweet = new TwitterOAuth($ckey,$csecret,$otoken,$osecret);
697
698                 // in theory max char is 140 but T. uses t.co to make links 
699                 // longer so we give them 10 characters extra
700                 if (!$intelligent_shortening) {
701                         $max_char = 130; // max. length for a tweet
702                         // we will only work with up to two times the length of the dent 
703                         // we can later send to Twitter. This way we can "gain" some 
704                         // information during shortening of potential links but do not 
705                         // shorten all the links in a 200000 character long essay.
706                         if (! $b['title']=='') {
707                             $tmp = $b['title'] . ' : '. $b['body'];
708         //                    $tmp = substr($tmp, 0, 4*$max_char);
709                         } else {
710                             $tmp = $b['body']; // substr($b['body'], 0, 3*$max_char);
711                         }
712                         // if [url=bla][img]blub.png[/img][/url] get blub.png
713                         $tmp = preg_replace( '/\[url\=(https?\:\/\/[a-zA-Z0-9\:\/\-\?\&\;\.\=\_\~\#\%\$\!\+\,]+)\]\[img\](\\w+.*?)\\[\\/img\]\\[\\/url\]/i', '$2', $tmp);
714                         // preserve links to images, videos and audios
715                         $tmp = preg_replace( '/\[img\=([0-9]*)x([0-9]*)\](.*?)\[\/img\]/ism', '$3', $tmp);
716                         $tmp = preg_replace( '/\[\\/?img(\\s+.*?\]|\])/i', '', $tmp);
717                         $tmp = preg_replace( '/\[\\/?video(\\s+.*?\]|\])/i', '', $tmp);
718                         $tmp = preg_replace( '/\[\\/?youtube(\\s+.*?\]|\])/i', '', $tmp);
719                         $tmp = preg_replace( '/\[\\/?vimeo(\\s+.*?\]|\])/i', '', $tmp);
720                         $tmp = preg_replace( '/\[\\/?audio(\\s+.*?\]|\])/i', '', $tmp);
721                         $linksenabled = get_pconfig($b['uid'],'twitter','post_taglinks');
722                         // if a #tag is linked, don't send the [url] over to SN
723                         // that is, don't send if the option is not set in the
724                         // connector settings
725                         if ($linksenabled=='0') {
726                                 // #-tags
727                                 $tmp = preg_replace( '/#\[url\=(\w+.*?)\](\w+.*?)\[\/url\]/i', '#$2', $tmp);
728                                 // @-mentions
729                                 $tmp = preg_replace( '/@\[url\=(\w+.*?)\](\w+.*?)\[\/url\]/i', '@$2', $tmp);
730                                 // recycle 1
731                                 $recycle = html_entity_decode("&#x2672; ", ENT_QUOTES, 'UTF-8');
732                                 $tmp = preg_replace( '/'.$recycle.'\[url\=(\w+.*?)\](\w+.*?)\[\/url\]/i', $recycle.'$2', $tmp);
733                                 // recycle 2 (Test)
734                                 $recycle = html_entity_decode("&#x25CC; ", ENT_QUOTES, 'UTF-8');
735                                 $tmp = preg_replace( '/'.$recycle.'\[url\=(\w+.*?)\](\w+.*?)\[\/url\]/i', $recycle.'$2', $tmp);
736                         }
737                         $tmp = preg_replace( '/\[url\=(https?\:\/\/[a-zA-Z0-9\:\/\-\?\&\;\.\=\_\~\#\%\$\!\+\,]+)\](\w+.*?)\[\/url\]/i', '$2 $1', $tmp);
738                         $tmp = preg_replace( '/\[bookmark\=(https?\:\/\/[a-zA-Z0-9\:\/\-\?\&\;\.\=\_\~\#\%\$\!\+\,]+)\](\w+.*?)\[\/bookmark\]/i', '$2 $1', $tmp);
739                         // find all http or https links in the body of the entry and
740                         // apply the shortener if the link is longer then 20 characters
741                         if (( strlen($tmp)>$max_char ) && ( $max_char > 0 )) {
742                             preg_match_all ( '/(https?\:\/\/[a-zA-Z0-9\:\/\-\?\&\;\.\=\_\~\#\%\$\!\+\,]+)/i', $tmp, $allurls  );
743                             foreach ($allurls as $url) {
744                                 foreach ($url as $u) {
745                                     if (strlen($u)>20) {
746                                         $sl = short_link($u);
747                                         $tmp = str_replace( $u, $sl, $tmp );
748                                     }
749                                 }
750                             }
751                         }
752                         // ok, all the links we want to send out are save, now strip 
753                         // away the remaining bbcode
754                         //$msg = strip_tags(bbcode($tmp, false, false));
755                         $msg = bbcode($tmp, false, false, true);
756                         $msg = str_replace(array('<br>','<br />'),"\n",$msg);
757                         $msg = strip_tags($msg);
758
759                         // quotes not working - let's try this
760                         $msg = html_entity_decode($msg);
761                         if (( strlen($msg) > $max_char) && $max_char > 0) {
762                                 $shortlink = short_link( $b['plink'] );
763                                 // the new message will be shortened such that "... $shortlink"
764                                 // will fit into the character limit
765                                 $msg = nl2br(substr($msg, 0, $max_char-strlen($shortlink)-4));
766                                 $msg = str_replace(array('<br>','<br />'),' ',$msg);
767                                 $e = explode(' ', $msg);
768                                 //  remove the last word from the cut down message to 
769                                 //  avoid sending cut words to the MicroBlog
770                                 array_pop($e);
771                                 $msg = implode(' ', $e);
772                                 $msg .= '... ' . $shortlink;
773                         }
774
775                         $msg = trim($msg);
776                         $image = "";
777                 } else {
778                         $msgarr = twitter_shortenmsg($b);
779                         $msg = $msgarr["msg"];
780                         $image = $msgarr["image"];
781                 }
782
783                 // and now tweet it :-)
784                 if(strlen($msg) and ($image != "")) {
785                         $img_str = fetch_url($image);
786
787                         $tempfile = tempnam(get_config("system","temppath"), "cache");
788                         file_put_contents($tempfile, $img_str);
789
790                         // Twitter had changed something so that the old library doesn't work anymore
791                         // so we are using a new library for twitter
792                         // To-Do:
793                         // Switching completely to this library with all functions
794                         require_once("addon/twitter/codebird.php");
795
796                         $cb = \Codebird\Codebird::getInstance();
797                         $cb->setConsumerKey($ckey, $csecret);
798                         $cb->setToken($otoken, $osecret);
799
800                         $post = array('status' => $msg, 'media[]' => $tempfile);
801
802                         if ($iscomment)
803                                 $post["in_reply_to_status_id"] = substr($orig_post["uri"], 9);
804
805                         $result = $cb->statuses_updateWithMedia($post);
806                         unlink($tempfile);
807
808                         logger('twitter_post_with_media send, result: ' . print_r($result, true), LOGGER_DEBUG);
809                         if ($result->errors OR $result->error) {
810                                 logger('Send to Twitter failed: "' . print_r($result->errors, true) . '"');
811
812                                 // Workaround: Remove the picture link so that the post can be reposted without it
813                                 $msg .= " ".$image;
814                                 $image = "";
815                         } elseif ($iscomment) {
816                                 logger('twitter_post: Update extid '.$result->id_str." for post id ".$b['id']);
817                                 q("UPDATE `item` SET `extid` = '%s', `body` = '%s' WHERE `id` = %d",
818                                         dbesc("twitter::".$result->id_str),
819                                         dbesc($result->text),
820                                         intval($b['id'])
821                                 );
822                         }
823                 }
824
825                 if(strlen($msg) and ($image == "")) {
826                         $url = 'statuses/update';
827                         $post = array('status' => $msg);
828
829                         if ($iscomment)
830                                 $post["in_reply_to_status_id"] = substr($orig_post["uri"], 9);
831
832                         $result = $tweet->post($url, $post);
833                         logger('twitter_post send, result: ' . print_r($result, true), LOGGER_DEBUG);
834                         if ($result->errors) {
835                                 logger('Send to Twitter failed: "' . print_r($result->errors, true) . '"');
836
837                                 $r = q("SELECT `id` FROM `contact` WHERE `uid` = %d AND `self`", intval($b['uid']));
838                                 if (count($r))
839                                         $a->contact = $r[0]["id"];
840
841                                 $s = serialize(array('url' => $url, 'item' => $b['id'], 'post' => $post));
842                                 require_once('include/queue_fn.php');
843                                 add_to_queue($a->contact,NETWORK_TWITTER,$s);
844                                 notice(t('Twitter post failed. Queued for retry.').EOL);
845                         } elseif ($iscomment) {
846                                 logger('twitter_post: Update extid '.$result->id_str." for post id ".$b['id']);
847                                 q("UPDATE `item` SET `extid` = '%s' WHERE `id` = %d",
848                                         dbesc("twitter::".$result->id_str),
849                                         intval($b['id'])
850                                 );
851                                 //q("UPDATE `item` SET `extid` = '%s', `body` = '%s' WHERE `id` = %d",
852                                 //      dbesc("twitter::".$result->id_str),
853                                 //      dbesc($result->text),
854                                 //      intval($b['id'])
855                                 //);
856                         }
857                 }
858         }
859 }
860
861 function twitter_plugin_admin_post(&$a){
862         $consumerkey    =       ((x($_POST,'consumerkey'))              ? notags(trim($_POST['consumerkey']))   : '');
863         $consumersecret =       ((x($_POST,'consumersecret'))   ? notags(trim($_POST['consumersecret'])): '');
864         $applicationname = ((x($_POST, 'applicationname')) ? notags(trim($_POST['applicationname'])):'');
865         set_config('twitter','consumerkey',$consumerkey);
866         set_config('twitter','consumersecret',$consumersecret);
867         set_config('twitter','application_name',$applicationname);
868         info( t('Settings updated.'). EOL );
869 }
870 function twitter_plugin_admin(&$a, &$o){
871         $t = get_markup_template( "admin.tpl", "addon/twitter/" );
872
873         $o = replace_macros($t, array(
874                 '$submit' => t('Save Settings'),
875                                                                 // name, label, value, help, [extra values]
876                 '$consumerkey' => array('consumerkey', t('Consumer key'),  get_config('twitter', 'consumerkey' ), ''),
877                 '$consumersecret' => array('consumersecret', t('Consumer secret'),  get_config('twitter', 'consumersecret' ), ''),
878                 '$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'))
879         ));
880 }
881
882 function twitter_cron($a,$b) {
883         $last = get_config('twitter','last_poll');
884
885         $poll_interval = intval(get_config('twitter','poll_interval'));
886         if(! $poll_interval)
887                 $poll_interval = TWITTER_DEFAULT_POLL_INTERVAL;
888
889         if($last) {
890                 $next = $last + ($poll_interval * 60);
891                 if($next > time()) {
892                         logger('twitter: poll intervall not reached');
893                         return;
894                 }
895         }
896         logger('twitter: cron_start');
897
898         $r = q("SELECT * FROM `pconfig` WHERE `cat` = 'twitter' AND `k` = 'mirror_posts' AND `v` = '1' ORDER BY RAND()");
899         if(count($r)) {
900                 foreach($r as $rr) {
901                         logger('twitter: fetching for user '.$rr['uid']);
902                         twitter_fetchtimeline($a, $rr['uid']);
903                 }
904         }
905
906
907         $r = q("SELECT * FROM `pconfig` WHERE `cat` = 'twitter' AND `k` = 'import' AND `v` = '1' ORDER BY RAND()");
908         if(count($r)) {
909                 foreach($r as $rr) {
910                         logger('twitter: importing timeline from user '.$rr['uid']);
911                         twitter_fetchhometimeline($a, $rr["uid"]);
912
913 /*
914                         // To-Do
915                         // check for new contacts once a day
916                         $last_contact_check = get_pconfig($rr['uid'],'pumpio','contact_check');
917                         if($last_contact_check)
918                                 $next_contact_check = $last_contact_check + 86400;
919                         else
920                                 $next_contact_check = 0;
921
922                         if($next_contact_check <= time()) {
923                                 pumpio_getallusers($a, $rr["uid"]);
924                                 set_pconfig($rr['uid'],'pumpio','contact_check',time());
925                         }
926 */
927
928                 }
929         }
930
931         logger('twitter: cron_end');
932
933         set_config('twitter','last_poll', time());
934 }
935
936 function twitter_fetchtimeline($a, $uid) {
937         $ckey    = get_config('twitter', 'consumerkey');
938         $csecret = get_config('twitter', 'consumersecret');
939         $otoken  = get_pconfig($uid, 'twitter', 'oauthtoken');
940         $osecret = get_pconfig($uid, 'twitter', 'oauthsecret');
941         $lastid  = get_pconfig($uid, 'twitter', 'lastid');
942
943         $application_name  = get_config('twitter', 'application_name');
944
945         if ($application_name == "")
946                 $application_name = $a->get_hostname();
947
948         $has_picture = false;
949
950         require_once('mod/item.php');
951
952         require_once('library/twitteroauth.php');
953         $connection = new TwitterOAuth($ckey,$csecret,$otoken,$osecret);
954
955         $parameters = array("exclude_replies" => true, "trim_user" => false, "contributor_details" => true, "include_rts" => true);
956
957         $first_time = ($lastid == "");
958
959         if ($lastid <> "")
960                 $parameters["since_id"] = $lastid;
961
962         $items = $connection->get('statuses/user_timeline', $parameters);
963
964         if (!is_array($items))
965                 return;
966
967         $posts = array_reverse($items);
968
969         if (count($posts)) {
970             foreach ($posts as $post) {
971                 if ($post->id_str > $lastid)
972                         $lastid = $post->id_str;
973
974                 if ($first_time)
975                         continue;
976
977                 if (!strpos($post->source, $application_name)) {
978                         $_SESSION["authenticated"] = true;
979                         $_SESSION["uid"] = $uid;
980
981                         unset($_REQUEST);
982                         $_REQUEST["type"] = "wall";
983                         $_REQUEST["api_source"] = true;
984                         $_REQUEST["profile_uid"] = $uid;
985                         $_REQUEST["source"] = "Twitter";
986
987                         //$_REQUEST["date"] = $post->created_at;
988
989                         $_REQUEST["title"] = "";
990
991                         if (is_object($post->retweeted_status)) {
992
993                                 $_REQUEST['body'] = $post->retweeted_status->text;
994
995                                 // media
996                                 if (is_array($post->retweeted_status->entities->media)) {
997                                         foreach($post->retweeted_status->entities->media AS $media) {
998                                                 switch($media->type) {
999                                                         case 'photo':
1000                                                                 $_REQUEST['body'] = str_replace($media->url, "\n\n[img]".$media->media_url_https."[/img]\n", $_REQUEST['body']);
1001                                                                 $has_picture = true;
1002                                                                 break;
1003                                                 }
1004                                         }
1005                                 }
1006
1007                                 //$converted = twitter_convertmsg($a, $_REQUEST['body'], true, $has_picture);
1008                                 $converted = twitter_expand_entities($a, $_REQUEST['body'], $post->retweeted_status, true, $has_picture);
1009                                 $_REQUEST['body'] = $converted["body"];
1010
1011                                 $_REQUEST['body'] = "[share author='".$post->retweeted_status->user->name.
1012                                         "' profile='https://twitter.com/".$post->retweeted_status->user->screen_name.
1013                                         "' avatar='".$post->retweeted_status->user->profile_image_url_https.
1014                                         "' link='https://twitter.com/".$post->retweeted_status->user->screen_name."/status/".$post->retweeted_status->id_str."']".
1015                                         $_REQUEST['body'];
1016                                 $_REQUEST['body'] .= "[/share]";
1017                         } else {
1018                                 $_REQUEST["body"] = $post->text;
1019
1020                                 if (is_array($post->entities->media)) {
1021                                         foreach($post->entities->media AS $media) {
1022                                                 switch($media->type) {
1023                                                         case 'photo':
1024                                                                 $_REQUEST['body'] = str_replace($media->url, "\n\n[img]".$media->media_url_https."[/img]\n", $_REQUEST['body']);
1025                                                                 $has_picture = true;
1026                                                                 break;
1027                                                 }
1028                                         }
1029                                 }
1030
1031                                 //$converted = twitter_convertmsg($a, $_REQUEST["body"], true, $has_picture);
1032                                 $converted = twitter_expand_entities($a, $_REQUEST["body"], $post, true, $has_picture);
1033                                 $_REQUEST['body'] = $converted["body"];
1034                         }
1035
1036                         if (is_string($post->place->name))
1037                                 $_REQUEST["location"] = $post->place->name;
1038
1039                         if (is_string($post->place->full_name))
1040                                 $_REQUEST["location"] = $post->place->full_name;
1041
1042                         if (is_array($post->geo->coordinates))
1043                                 $_REQUEST["coord"] = $post->geo->coordinates[0]." ".$post->geo->coordinates[1];
1044
1045                         if (is_array($post->coordinates->coordinates))
1046                                 $_REQUEST["coord"] = $post->coordinates->coordinates[1]." ".$post->coordinates->coordinates[0];
1047
1048                         //print_r($_REQUEST);
1049                         logger('twitter: posting for user '.$uid);
1050
1051 //                      require_once('mod/item.php');
1052
1053                         item_post($a);
1054                 }
1055             }
1056         }
1057         set_pconfig($uid, 'twitter', 'lastid', $lastid);
1058 }
1059
1060 function twitter_queue_hook(&$a,&$b) {
1061
1062         $qi = q("SELECT * FROM `queue` WHERE `network` = '%s'",
1063                 dbesc(NETWORK_TWITTER)
1064                 );
1065         if(! count($qi))
1066                 return;
1067
1068         require_once('include/queue_fn.php');
1069
1070         foreach($qi as $x) {
1071                 if($x['network'] !== NETWORK_TWITTER)
1072                         continue;
1073
1074                 logger('twitter_queue: run');
1075
1076                 $r = q("SELECT `user`.* FROM `user` LEFT JOIN `contact` on `contact`.`uid` = `user`.`uid` 
1077                         WHERE `contact`.`self` = 1 AND `contact`.`id` = %d LIMIT 1",
1078                         intval($x['cid'])
1079                 );
1080                 if(! count($r))
1081                         continue;
1082
1083                 $user = $r[0];
1084
1085                 $ckey    = get_config('twitter', 'consumerkey');
1086                 $csecret = get_config('twitter', 'consumersecret');
1087                 $otoken  = get_pconfig($user['uid'], 'twitter', 'oauthtoken');
1088                 $osecret = get_pconfig($user['uid'], 'twitter', 'oauthsecret');
1089
1090                 $success = false;
1091
1092                 if ($ckey AND $csecret AND $otoken AND $osecret) {
1093
1094                         logger('twitter_queue: able to post');
1095
1096                         $z = unserialize($x['content']);
1097
1098                         require_once("addon/twitter/codebird.php");
1099
1100                         $cb = \Codebird\Codebird::getInstance();
1101                         $cb->setConsumerKey($ckey, $csecret);
1102                         $cb->setToken($otoken, $osecret);
1103
1104                         if ($z['url'] == "statuses/update")
1105                                 $result = $cb->statuses_update($z['post']);
1106
1107                         logger('twitter_queue: post result: ' . print_r($result, true), LOGGER_DEBUG);
1108
1109                         if ($result->errors)
1110                                 logger('twitter_queue: Send to Twitter failed: "' . print_r($result->errors, true) . '"');
1111                         else {
1112                                 $success = true;
1113                                 remove_queue_item($x['id']);
1114                         }
1115                 } else
1116                         logger("twitter_queue: Error getting tokens for user ".$user['uid']);
1117
1118                 if (!$success) {
1119                         logger('twitter_queue: delayed');
1120                         update_queue_time($x['id']);
1121                 }
1122         }
1123 }
1124
1125 function twitter_fetch_contact($uid, $contact, $create_user) {
1126
1127         // Check if the unique contact is existing
1128         // To-Do: only update once a while
1129          $r = q("SELECT id FROM unique_contacts WHERE url='%s' LIMIT 1",
1130                         dbesc(normalise_link("https://twitter.com/".$contact->screen_name)));
1131
1132         if (count($r) == 0)
1133                 q("INSERT INTO unique_contacts (url, name, nick, avatar) VALUES ('%s', '%s', '%s', '%s')",
1134                         dbesc(normalise_link("https://twitter.com/".$contact->screen_name)),
1135                         dbesc($contact->name),
1136                         dbesc($contact->screen_name),
1137                         dbesc($contact->profile_image_url_https));
1138         else
1139                 q("UPDATE unique_contacts SET name = '%s', nick = '%s', avatar = '%s' WHERE url = '%s'",
1140                         dbesc($contact->name),
1141                         dbesc($contact->screen_name),
1142                         dbesc($contact->profile_image_url_https),
1143                         dbesc(normalise_link("https://twitter.com/".$contact->screen_name)));
1144
1145         $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `alias` = '%s' LIMIT 1",
1146                 intval($uid), dbesc("twitter::".$contact->id_str));
1147
1148         if(!count($r) AND !$create_user)
1149                 return(0);
1150
1151         if (count($r) AND ($r[0]["readonly"] OR $r[0]["blocked"])) {
1152                 logger("twitter_fetch_contact: Contact '".$r[0]["nick"]."' is blocked or readonly.", LOGGER_DEBUG);
1153                 return(-1);
1154         }
1155
1156         if(!count($r)) {
1157                 // create contact record
1158                 q("INSERT INTO `contact` ( `uid`, `created`, `url`, `nurl`, `addr`, `alias`, `notify`, `poll`,
1159                                         `name`, `nick`, `photo`, `network`, `rel`, `priority`,
1160                                         `writable`, `blocked`, `readonly`, `pending` )
1161                                         VALUES ( %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, %d, 0, 0, 0 ) ",
1162                         intval($uid),
1163                         dbesc(datetime_convert()),
1164                         dbesc("https://twitter.com/".$contact->screen_name),
1165                         dbesc(normalise_link("https://twitter.com/".$contact->screen_name)),
1166                         dbesc($contact->screen_name."@twitter.com"),
1167                         dbesc("twitter::".$contact->id_str),
1168                         dbesc(''),
1169                         dbesc("twitter::".$contact->id_str),
1170                         dbesc($contact->name),
1171                         dbesc($contact->screen_name),
1172                         dbesc($contact->profile_image_url_https),
1173                         dbesc(NETWORK_TWITTER),
1174                         intval(CONTACT_IS_FRIEND),
1175                         intval(1),
1176                         intval(1)
1177                 );
1178
1179                 $r = q("SELECT * FROM `contact` WHERE `alias` = '%s' AND `uid` = %d LIMIT 1",
1180                         dbesc("twitter::".$contact->id_str),
1181                         intval($uid)
1182                         );
1183
1184                 if(! count($r))
1185                         return(false);
1186
1187                 $contact_id  = $r[0]['id'];
1188
1189                 $g = q("SELECT def_gid FROM user WHERE uid = %d LIMIT 1",
1190                         intval($uid)
1191                 );
1192
1193                 if($g && intval($g[0]['def_gid'])) {
1194                         require_once('include/group.php');
1195                         group_add_member($uid,'',$contact_id,$g[0]['def_gid']);
1196                 }
1197
1198                 require_once("Photo.php");
1199
1200                 $photos = import_profile_photo($contact->profile_image_url_https,$uid,$contact_id);
1201
1202                 q("UPDATE `contact` SET `photo` = '%s',
1203                                         `thumb` = '%s',
1204                                         `micro` = '%s',
1205                                         `name-date` = '%s',
1206                                         `uri-date` = '%s',
1207                                         `avatar-date` = '%s'
1208                                 WHERE `id` = %d",
1209                         dbesc($photos[0]),
1210                         dbesc($photos[1]),
1211                         dbesc($photos[2]),
1212                         dbesc(datetime_convert()),
1213                         dbesc(datetime_convert()),
1214                         dbesc(datetime_convert()),
1215                         intval($contact_id)
1216                 );
1217
1218         } else {
1219                 // update profile photos once every two weeks as we have no notification of when they change.
1220
1221                 //$update_photo = (($r[0]['avatar-date'] < datetime_convert('','','now -2 days')) ? true : false);
1222                 $update_photo = ($r[0]['avatar-date'] < datetime_convert('','','now -12 hours'));
1223
1224                 // check that we have all the photos, this has been known to fail on occasion
1225
1226                 if((! $r[0]['photo']) || (! $r[0]['thumb']) || (! $r[0]['micro']) || ($update_photo)) {
1227
1228                         logger("twitter_fetch_contact: Updating contact ".$contact->screen_name, LOGGER_DEBUG);
1229
1230                         require_once("Photo.php");
1231
1232                         $photos = import_profile_photo($contact->profile_image_url_https, $uid, $r[0]['id']);
1233
1234                         q("UPDATE `contact` SET `photo` = '%s',
1235                                                 `thumb` = '%s',
1236                                                 `micro` = '%s',
1237                                                 `name-date` = '%s',
1238                                                 `uri-date` = '%s',
1239                                                 `avatar-date` = '%s',
1240                                                 `url` = '%s',
1241                                                 `nurl` = '%s',
1242                                                 `addr` = '%s',
1243                                                 `name` = '%s',
1244                                                 `nick` = '%s'
1245                                         WHERE `id` = %d",
1246                                 dbesc($photos[0]),
1247                                 dbesc($photos[1]),
1248                                 dbesc($photos[2]),
1249                                 dbesc(datetime_convert()),
1250                                 dbesc(datetime_convert()),
1251                                 dbesc(datetime_convert()),
1252                                 dbesc("https://twitter.com/".$contact->screen_name),
1253                                 dbesc(normalise_link("https://twitter.com/".$contact->screen_name)),
1254                                 dbesc($contact->screen_name."@twitter.com"),
1255                                 dbesc($contact->name),
1256                                 dbesc($contact->screen_name),
1257                                 intval($r[0]['id'])
1258                         );
1259                 }
1260         }
1261
1262         return($r[0]["id"]);
1263 }
1264
1265 function twitter_fetchuser($a, $uid, $screen_name = "", $user_id = "") {
1266         $ckey    = get_config('twitter', 'consumerkey');
1267         $csecret = get_config('twitter', 'consumersecret');
1268         $otoken  = get_pconfig($uid, 'twitter', 'oauthtoken');
1269         $osecret = get_pconfig($uid, 'twitter', 'oauthsecret');
1270
1271         require_once("addon/twitter/codebird.php");
1272
1273         $cb = \Codebird\Codebird::getInstance();
1274         $cb->setConsumerKey($ckey, $csecret);
1275         $cb->setToken($otoken, $osecret);
1276
1277         $r = q("SELECT * FROM `contact` WHERE `self` = 1 AND `uid` = %d LIMIT 1",
1278                 intval($uid));
1279
1280         if(count($r)) {
1281                 $self = $r[0];
1282         } else
1283                 return;
1284
1285         $parameters = array();
1286
1287         if ($screen_name != "")
1288                 $parameters["screen_name"] = $screen_name;
1289
1290         if ($user_id != "")
1291                 $parameters["user_id"] = $user_id;
1292
1293         // Fetching user data
1294         $user = $cb->users_show($parameters);
1295
1296         if (!is_object($user))
1297                 return;
1298
1299         $contact_id = twitter_fetch_contact($uid, $user, true);
1300
1301         return $contact_id;
1302 }
1303
1304 function twitter_expand_entities($a, $body, $item, $no_tags = false, $dontincludemedia) {
1305         require_once("include/oembed.php");
1306
1307         $tags = "";
1308
1309         if (isset($item->entities->urls)) {
1310                 $type = "";
1311                 $footerurl = "";
1312                 $footerlink = "";
1313                 $footer = "";
1314
1315                 foreach ($item->entities->urls AS $url) {
1316                         if ($url->url AND $url->expanded_url AND $url->display_url) {
1317
1318                                 $expanded_url = twitter_original_url($url->expanded_url);
1319
1320                                 $oembed_data = oembed_fetch_url($expanded_url);
1321
1322                                 // Quickfix: Workaround for URL with "[" and "]" in it
1323                                 if (strpos($expanded_url, "[") OR strpos($expanded_url, "]"))
1324                                         $expanded_url = $url->url;
1325
1326                                 if ($type == "")
1327                                         $type = $oembed_data->type;
1328
1329                                 if ($oembed_data->type == "video") {
1330                                         $body = str_replace($url->url,
1331                                                         "[video]".$expanded_url."[/video]", $body);
1332                                         $dontincludemedia = true;
1333                                 } elseif (($oembed_data->type == "photo") AND isset($oembed_data->url) AND !$dontincludemedia) {
1334                                         $body = str_replace($url->url,
1335                                                         "[url=".$expanded_url."][img]".$oembed_data->url."[/img][/url]",
1336                                                         $body);
1337                                         $dontincludemedia = true;
1338                                 } elseif ($oembed_data->type != "link")
1339                                         $body = str_replace($url->url,
1340                                                         "[url=".$expanded_url."]".$expanded_url."[/url]",
1341                                                         $body);
1342                                                         //"[url=".$expanded_url."]".$url->display_url."[/url]",
1343                                 else {
1344                                         $img_str = fetch_url($expanded_url, true, $redirects, 4);
1345
1346                                         $tempfile = tempnam(get_config("system","temppath"), "cache");
1347                                         file_put_contents($tempfile, $img_str);
1348                                         $mime = image_type_to_mime_type(exif_imagetype($tempfile));
1349                                         unlink($tempfile);
1350
1351                                         if (substr($mime, 0, 6) == "image/") {
1352                                                 $type = "photo";
1353                                                 $body = str_replace($url->url, "[img]".$expanded_url."[/img]", $body);
1354                                                 $dontincludemedia = true;
1355                                         } else {
1356                                                 $type = $oembed_data->type;
1357                                                 $footerurl = $expanded_url;
1358                                                 $footerlink = "[url=".$expanded_url."]".$expanded_url."[/url]";
1359                                                 //$footerlink = "[url=".$expanded_url."]".$url->display_url."[/url]";
1360
1361                                                 $body = str_replace($url->url, $footerlink, $body);
1362                                         }
1363                                 }
1364                         }
1365                 }
1366
1367                 if ($footerurl != "")
1368                         $footer = twitter_siteinfo($footerurl, $dontincludemedia);
1369
1370                 if (($footerlink != "") AND (trim($footer) != "")) {
1371                         $removedlink = trim(str_replace($footerlink, "", $body));
1372
1373                         if (strstr($body, $removedlink))
1374                                 $body = $removedlink;
1375
1376                         $body .= "\n\n[class=type-".$type."]".$footer."[/class]";
1377                 }
1378
1379                 if ($no_tags)
1380                         return(array("body" => $body, "tags" => ""));
1381
1382                 $tags_arr = array();
1383
1384                 foreach ($item->entities->hashtags AS $hashtag) {
1385                         $url = "#[url=".$a->get_baseurl()."/search?tag=".rawurlencode($hashtag->text)."]".$hashtag->text."[/url]";
1386                         $tags_arr["#".$hashtag->text] = $url;
1387                         $body = str_replace("#".$hashtag->text, $url, $body);
1388                 }
1389
1390                 foreach ($item->entities->user_mentions AS $mention) {
1391                         $url = "@[url=https://twitter.com/".rawurlencode($mention->screen_name)."]".$mention->screen_name."[/url]";
1392                         $tags_arr["@".$mention->screen_name] = $url;
1393                         $body = str_replace("@".$mention->screen_name, $url, $body);
1394                 }
1395
1396                 // it seems as if the entities aren't always covering all mentions. So the rest will be checked here
1397                 $tags = get_tags($body);
1398
1399                 if(count($tags)) {
1400                         foreach($tags as $tag) {
1401                                 if (strstr(trim($tag), " "))
1402                                         continue;
1403
1404                                 if(strpos($tag,'#') === 0) {
1405                                         if(strpos($tag,'[url='))
1406                                                 continue;
1407
1408                                         // don't link tags that are already embedded in links
1409
1410                                         if(preg_match('/\[(.*?)' . preg_quote($tag,'/') . '(.*?)\]/',$body))
1411                                                 continue;
1412                                         if(preg_match('/\[(.*?)\]\((.*?)' . preg_quote($tag,'/') . '(.*?)\)/',$body))
1413                                                 continue;
1414
1415                                         $basetag = str_replace('_',' ',substr($tag,1));
1416                                         $url = '#[url='.$a->get_baseurl().'/search?tag='.rawurlencode($basetag).']'.$basetag.'[/url]';
1417                                         $body = str_replace($tag,$url,$body);
1418                                         $tags_arr["#".$basetag] = $url;
1419                                         continue;
1420                                 } elseif(strpos($tag,'@') === 0) {
1421                                         if(strpos($tag,'[url='))
1422                                                 continue;
1423
1424                                         $basetag = substr($tag,1);
1425                                         $url = '@[url=https://twitter.com/'.rawurlencode($basetag).']'.$basetag.'[/url]';
1426                                         $body = str_replace($tag,$url,$body);
1427                                         $tags_arr["@".$basetag] = $url;
1428                                 }
1429                         }
1430                 }
1431
1432
1433                 $tags = implode($tags_arr, ",");
1434
1435         }
1436         return(array("body" => $body, "tags" => $tags));
1437 }
1438
1439 function twitter_createpost($a, $uid, $post, $self, $create_user, $only_existing_contact) {
1440
1441         $has_picture = false;
1442
1443         $postarray = array();
1444         $postarray['network'] = NETWORK_TWITTER;
1445         $postarray['gravity'] = 0;
1446         $postarray['uid'] = $uid;
1447         $postarray['wall'] = 0;
1448         $postarray['uri'] = "twitter::".$post->id_str;
1449
1450         $r = q("SELECT * FROM `item` WHERE `extid` = '%s' AND `uid` = %d LIMIT 1",
1451                         dbesc($postarray['uri']),
1452                         intval($uid)
1453                 );
1454
1455         if (count($r))
1456                 return(array());
1457
1458         $contactid = 0;
1459
1460         if ($post->in_reply_to_status_id_str != "") {
1461
1462                 $parent = "twitter::".$post->in_reply_to_status_id_str;
1463
1464                 $r = q("SELECT * FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1",
1465                                 dbesc($parent),
1466                                 intval($uid)
1467                         );
1468                 if (count($r)) {
1469                         $postarray['thr-parent'] = $r[0]["uri"];
1470                         $postarray['parent-uri'] = $r[0]["parent-uri"];
1471                 } else {
1472                         $r = q("SELECT * FROM `item` WHERE `extid` = '%s' AND `uid` = %d LIMIT 1",
1473                                         dbesc($parent),
1474                                         intval($uid)
1475                                 );
1476                         if (count($r)) {
1477                                 $postarray['thr-parent'] = $r[0]['uri'];
1478                                 $postarray['parent-uri'] = $r[0]['parent-uri'];
1479                         } else {
1480                                 $postarray['thr-parent'] = $postarray['uri'];
1481                                 $postarray['parent-uri'] = $postarray['uri'];
1482                         }
1483                 }
1484
1485                 // Is it me?
1486                 $own_id = get_pconfig($uid, 'twitter', 'own_id');
1487
1488                 if ($post->user->id_str == $own_id) {
1489                         $r = q("SELECT * FROM `contact` WHERE `self` = 1 AND `uid` = %d LIMIT 1",
1490                                 intval($uid));
1491
1492                         if(count($r)) {
1493                                 $contactid = $r[0]["id"];
1494
1495                                 $postarray['owner-name'] =  $r[0]["name"];
1496                                 $postarray['owner-link'] = $r[0]["url"];
1497                                 $postarray['owner-avatar'] =  $r[0]["photo"];
1498                         } else
1499                                 return(array());
1500                 }
1501         } else
1502                 $postarray['parent-uri'] = $postarray['uri'];
1503
1504         if ($contactid == 0) {
1505                 $contactid = twitter_fetch_contact($uid, $post->user, $create_user);
1506
1507                 $postarray['owner-name'] = $post->user->name;
1508                 $postarray['owner-link'] = "https://twitter.com/".$post->user->screen_name;
1509                 $postarray['owner-avatar'] = $post->user->profile_image_url_https;
1510         }
1511
1512         if(($contactid == 0) AND !$only_existing_contact)
1513                 $contactid = $self['id'];
1514         elseif ($contactid <= 0)
1515                 return(array());
1516
1517         $postarray['contact-id'] = $contactid;
1518
1519         $postarray['verb'] = ACTIVITY_POST;
1520         $postarray['author-name'] = $postarray['owner-name'];
1521         $postarray['author-link'] = $postarray['owner-link'];
1522         $postarray['author-avatar'] = $postarray['owner-avatar'];
1523         $postarray['plink'] = "https://twitter.com/".$post->user->screen_name."/status/".$post->id_str;
1524         $postarray['app'] = strip_tags($post->source);
1525
1526         if ($post->user->protected) {
1527                 $postarray['private'] = 1;
1528                 $postarray['allow_cid'] = '<' . $self['id'] . '>';
1529         }
1530
1531         $postarray['body'] = $post->text;
1532
1533         // media
1534         if (is_array($post->entities->media)) {
1535                 foreach($post->entities->media AS $media) {
1536                         switch($media->type) {
1537                                 case 'photo':
1538                                         $postarray['body'] = str_replace($media->url, "\n\n[img]".$media->media_url_https."[/img]\n", $postarray['body']);
1539                                         $has_picture = true;
1540                                         break;
1541                                 default:
1542                                         $postarray['body'] .= print_r($media, true);
1543                         }
1544                 }
1545         }
1546
1547         //$converted = twitter_convertmsg($a, $postarray['body'], false, $has_picture);
1548         $converted = twitter_expand_entities($a, $postarray['body'], $post, false, $has_picture);
1549         $postarray['body'] = $converted["body"];
1550         $postarray['tag'] = $converted["tags"];
1551
1552         $postarray['created'] = datetime_convert('UTC','UTC',$post->created_at);
1553         $postarray['edited'] = datetime_convert('UTC','UTC',$post->created_at);
1554
1555         if (is_string($post->place->name))
1556                 $postarray["location"] = $post->place->name;
1557
1558         if (is_string($post->place->full_name))
1559                 $postarray["location"] = $post->place->full_name;
1560
1561         if (is_array($post->geo->coordinates))
1562                 $postarray["coord"] = $post->geo->coordinates[0]." ".$post->geo->coordinates[1];
1563
1564         if (is_array($post->coordinates->coordinates))
1565                 $postarray["coord"] = $post->coordinates->coordinates[1]." ".$post->coordinates->coordinates[0];
1566
1567         if (is_object($post->retweeted_status)) {
1568
1569                 $postarray['body'] = $post->retweeted_status->text;
1570
1571                 // media
1572                 if (is_array($post->retweeted_status->entities->media)) {
1573                         foreach($post->retweeted_status->entities->media AS $media) {
1574                                 switch($media->type) {
1575                                         case 'photo':
1576                                                 $postarray['body'] = str_replace($media->url, "\n\n[img]".$media->media_url_https."[/img]\n", $postarray['body']);
1577                                                 $has_picture = true;
1578                                                 break;
1579                                         default:
1580                                                 $postarray['body'] .= print_r($media, true);
1581                                 }
1582                         }
1583                 }
1584
1585                 //$converted = twitter_convertmsg($a, $postarray['body'], false, $has_picture);
1586                 $converted = twitter_expand_entities($a, $postarray['body'], $post->retweeted_status, false, $has_picture);
1587                 $postarray['body'] = $converted["body"];
1588                 $postarray['tag'] = $converted["tags"];
1589
1590                 twitter_fetch_contact($uid, $post->retweeted_status->user, false);
1591
1592                 // Deactivated at the moment, since there are problems with answers to retweets
1593                 if (false AND !intval(get_config('system','wall-to-wall_share'))) {
1594                         $postarray['body'] = "[share author='".$post->retweeted_status->user->name.
1595                                 "' profile='https://twitter.com/".$post->retweeted_status->user->screen_name.
1596                                 "' avatar='".$post->retweeted_status->user->profile_image_url_https.
1597                                 "' link='https://twitter.com/".$post->retweeted_status->user->screen_name."/status/".$post->retweeted_status->id_str."']".
1598                                 $postarray['body'];
1599                         $postarray['body'] .= "[/share]";
1600                 } else {
1601                         // Let retweets look like wall-to-wall posts
1602                         $postarray['author-name'] = $post->retweeted_status->user->name;
1603                         $postarray['author-link'] = "https://twitter.com/".$post->retweeted_status->user->screen_name;
1604                         $postarray['author-avatar'] = $post->retweeted_status->user->profile_image_url_https;
1605                         //if (($post->retweeted_status->user->screen_name != "") AND ($post->retweeted_status->id_str != "")) {
1606                         //      $postarray['plink'] = "https://twitter.com/".$post->retweeted_status->user->screen_name."/status/".$post->retweeted_status->id_str;
1607                         //      $postarray['uri'] = "twitter::".$post->retweeted_status->id_str;
1608                         //}
1609                 }
1610
1611         }
1612         return($postarray);
1613 }
1614
1615 function twitter_checknotification($a, $uid, $own_id, $top_item, $postarray) {
1616
1617         $user = q("SELECT * FROM `contact` WHERE `uid` = %d AND `self` LIMIT 1",
1618                         intval($uid)
1619                 );
1620
1621         if(!count($user))
1622                 return;
1623
1624         // Is it me?
1625         if (link_compare($user[0]["url"], $postarray['author-link']))
1626                 return;
1627
1628         $own_user = q("SELECT * FROM `contact` WHERE `uid` = %d AND `alias` = '%s' LIMIT 1",
1629                         intval($uid),
1630                         dbesc("twitter::".$own_id)
1631                 );
1632
1633         if(!count($own_user))
1634                 return;
1635
1636         // Is it me from twitter?
1637         if (link_compare($own_user[0]["url"], $postarray['author-link']))
1638                 return;
1639
1640         $myconv = q("SELECT `author-link`, `author-avatar`, `parent` FROM `item` WHERE `parent-uri` = '%s' AND `uid` = %d AND `parent` != 0 AND `deleted` = 0",
1641                         dbesc($postarray['parent-uri']),
1642                         intval($uid)
1643                         );
1644
1645         if(count($myconv)) {
1646
1647                 foreach($myconv as $conv) {
1648                         // now if we find a match, it means we're in this conversation
1649
1650                         if(!link_compare($conv['author-link'],$user[0]["url"]) AND !link_compare($conv['author-link'],$own_user[0]["url"]))
1651                                 continue;
1652
1653                         require_once('include/enotify.php');
1654
1655                         $conv_parent = $conv['parent'];
1656
1657                         notification(array(
1658                                 'type'         => NOTIFY_COMMENT,
1659                                 'notify_flags' => $user[0]['notify-flags'],
1660                                 'language'     => $user[0]['language'],
1661                                 'to_name'      => $user[0]['username'],
1662                                 'to_email'     => $user[0]['email'],
1663                                 'uid'          => $user[0]['uid'],
1664                                 'item'         => $postarray,
1665                                 'link'             => $a->get_baseurl() . '/display/' . $user[0]['nickname'] . '/' . $top_item,
1666                                 'source_name'  => $postarray['author-name'],
1667                                 'source_link'  => $postarray['author-link'],
1668                                 'source_photo' => $postarray['author-avatar'],
1669                                 'verb'         => ACTIVITY_POST,
1670                                 'otype'        => 'item',
1671                                 'parent'       => $conv_parent,
1672                         ));
1673
1674                         // only send one notification
1675                         break;
1676                 }
1677         }
1678 }
1679
1680 function twitter_fetchhometimeline($a, $uid) {
1681         $ckey    = get_config('twitter', 'consumerkey');
1682         $csecret = get_config('twitter', 'consumersecret');
1683         $otoken  = get_pconfig($uid, 'twitter', 'oauthtoken');
1684         $osecret = get_pconfig($uid, 'twitter', 'oauthsecret');
1685         $create_user = get_pconfig($uid, 'twitter', 'create_user');
1686
1687         logger("twitter_fetchhometimeline: Fetching for user ".$uid, LOGGER_DEBUG);
1688
1689         require_once('library/twitteroauth.php');
1690         require_once('include/items.php');
1691
1692         $connection = new TwitterOAuth($ckey,$csecret,$otoken,$osecret);
1693
1694         $own_contact = twitter_fetch_own_contact($a, $uid);
1695
1696         $r = q("SELECT * FROM `contact` WHERE `id` = %d AND `uid` = %d LIMIT 1",
1697                 intval($own_contact),
1698                 intval($uid));
1699
1700         if(count($r)) {
1701                 $own_id = $r[0]["nick"];
1702         } else {
1703                 logger("twitter_fetchhometimeline: Own twitter contact not found for user ".$uid, LOGGER_DEBUG);
1704                 return;
1705         }
1706
1707         $r = q("SELECT * FROM `contact` WHERE `self` = 1 AND `uid` = %d LIMIT 1",
1708                 intval($uid));
1709
1710         if(count($r)) {
1711                 $self = $r[0];
1712         } else {
1713                 logger("twitter_fetchhometimeline: Own contact not found for user ".$uid, LOGGER_DEBUG);
1714                 return;
1715         }
1716
1717         $u = q("SELECT * FROM user WHERE uid = %d LIMIT 1",
1718                 intval($uid));
1719         if(!count($u)) {
1720                 logger("twitter_fetchhometimeline: Own user not found for user ".$uid, LOGGER_DEBUG);
1721                 return;
1722         }
1723
1724         $parameters = array("exclude_replies" => false, "trim_user" => false, "contributor_details" => true, "include_rts" => true);
1725         //$parameters["count"] = 200;
1726
1727
1728         // Fetching timeline
1729         $lastid  = get_pconfig($uid, 'twitter', 'lasthometimelineid');
1730
1731         $first_time = ($lastid == "");
1732
1733         if ($lastid <> "")
1734                 $parameters["since_id"] = $lastid;
1735
1736         $items = $connection->get('statuses/home_timeline', $parameters);
1737
1738         if (!is_array($items)) {
1739                 logger("twitter_fetchhometimeline: Error fetching home timeline: ".print_r($items, true), LOGGER_DEBUG);
1740                 return;
1741         }
1742
1743         $posts = array_reverse($items);
1744
1745         logger("twitter_fetchhometimeline: Fetching timeline for user ".$uid." ".sizeof($posts)." items", LOGGER_DEBUG);
1746
1747         if (count($posts)) {
1748                 foreach ($posts as $post) {
1749                         if ($post->id_str > $lastid)
1750                                 $lastid = $post->id_str;
1751
1752                         if ($first_time)
1753                                 continue;
1754
1755                         $postarray = twitter_createpost($a, $uid, $post, $self, $create_user, true);
1756
1757                         if (trim($postarray['body']) == "")
1758                                 continue;
1759
1760                         $item = item_store($postarray);
1761
1762                         logger('twitter_fetchhometimeline: User '.$self["nick"].' posted home timeline item '.$item);
1763
1764                         if ($item != 0)
1765                                 twitter_checknotification($a, $uid, $own_id, $item, $postarray);
1766
1767                 }
1768         }
1769         set_pconfig($uid, 'twitter', 'lasthometimelineid', $lastid);
1770
1771         // Fetching mentions
1772         $lastid  = get_pconfig($uid, 'twitter', 'lastmentionid');
1773
1774         $first_time = ($lastid == "");
1775
1776         if ($lastid <> "")
1777                 $parameters["since_id"] = $lastid;
1778
1779         $items = $connection->get('statuses/mentions_timeline', $parameters);
1780
1781         if (!is_array($items)) {
1782                 logger("twitter_fetchhometimeline: Error fetching mentions: ".print_r($items, true), LOGGER_DEBUG);
1783                 return;
1784         }
1785
1786         $posts = array_reverse($items);
1787
1788         logger("twitter_fetchhometimeline: Fetching mentions for user ".$uid." ".sizeof($posts)." items", LOGGER_DEBUG);
1789
1790         if (count($posts)) {
1791                 foreach ($posts as $post) {
1792                         if ($post->id_str > $lastid)
1793                                 $lastid = $post->id_str;
1794
1795                         if ($first_time)
1796                                 continue;
1797
1798                         $postarray = twitter_createpost($a, $uid, $post, $self, false, false);
1799
1800                         if (trim($postarray['body']) == "")
1801                                 continue;
1802
1803                         $item = item_store($postarray);
1804
1805                         logger('twitter_fetchhometimeline: User '.$self["nick"].' posted mention timeline item '.$item);
1806
1807                         if ($item == 0) {
1808                                 $r = q("SELECT * FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1",
1809                                         dbesc($postarray['uri']),
1810                                         intval($uid)
1811                                 );
1812                                 if (count($r))
1813                                         $item = $r[0]['id'];
1814                         }
1815
1816                         if ($item != 0) {
1817                                 require_once('include/enotify.php');
1818                                 notification(array(
1819                                         'type'         => NOTIFY_TAGSELF,
1820                                         'notify_flags' => $u[0]['notify-flags'],
1821                                         'language'     => $u[0]['language'],
1822                                         'to_name'      => $u[0]['username'],
1823                                         'to_email'     => $u[0]['email'],
1824                                         'uid'          => $u[0]['uid'],
1825                                         'item'         => $postarray,
1826                                         'link'         => $a->get_baseurl() . '/display/' . $u[0]['nickname'] . '/' . $item,
1827                                         'source_name'  => $postarray['author-name'],
1828                                         'source_link'  => $postarray['author-link'],
1829                                         'source_photo' => $postarray['author-avatar'],
1830                                         'verb'         => ACTIVITY_TAG,
1831                                         'otype'        => 'item'
1832                                 ));
1833                         }
1834                 }
1835         }
1836
1837         set_pconfig($uid, 'twitter', 'lastmentionid', $lastid);
1838 }
1839
1840 function twitter_original_url($url, $depth=1, $fetchbody = false) {
1841         if ($depth > 10)
1842                 return($url);
1843
1844         $siteinfo = array();
1845         $ch = curl_init();
1846         curl_setopt($ch, CURLOPT_URL, $url);
1847         curl_setopt($ch, CURLOPT_HEADER, 1);
1848
1849         if ($fetchbody)
1850                 curl_setopt($ch, CURLOPT_NOBODY, 0);
1851         else
1852                 curl_setopt($ch, CURLOPT_NOBODY, 1);
1853
1854         curl_setopt($ch, CURLOPT_TIMEOUT, 10);
1855         curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
1856         curl_setopt($ch,CURLOPT_USERAGENT,'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:24.0) Gecko/20100101 Firefox/24.0');
1857
1858         $header = curl_exec($ch);
1859         $curl_info = @curl_getinfo($ch);
1860         $http_code = $curl_info['http_code'];
1861         curl_close($ch);
1862
1863         if ((($curl_info['http_code'] == "301") OR ($curl_info['http_code'] == "302"))
1864                 AND (($curl_info['redirect_url'] != "") OR ($curl_info['location'] != ""))) {
1865                 if ($curl_info['redirect_url'] != "")
1866                         return(twitter_original_url($curl_info['redirect_url'], ++$depth, $fetchbody));
1867                 else
1868                         return(twitter_original_url($curl_info['location'], ++$depth, $fetchbody));
1869         }
1870
1871         $pos = strpos($header, "\r\n\r\n");
1872
1873         if ($pos)
1874                 $body = trim(substr($header, $pos));
1875         else
1876                 $body = $header;
1877
1878         if (trim($body) == "")
1879                 return(twitter_original_url($url, ++$depth, true));
1880
1881         $doc = new DOMDocument();
1882         @$doc->loadHTML($body);
1883
1884         $xpath = new DomXPath($doc);
1885
1886         $list = $xpath->query("//meta[@content]");
1887         foreach ($list as $node) {
1888                 $attr = array();
1889                 if ($node->attributes->length)
1890                         foreach ($node->attributes as $attribute)
1891                                 $attr[$attribute->name] = $attribute->value;
1892
1893                 if (@$attr["http-equiv"] == 'refresh') {
1894                         $path = $attr["content"];
1895                         $pathinfo = explode(";", $path);
1896                         $content = "";
1897                         foreach ($pathinfo AS $value)
1898                                 if (substr(strtolower($value), 0, 4) == "url=")
1899                                         return(twitter_original_url(substr($value, 4), ++$depth));
1900                 }
1901         }
1902
1903         return($url);
1904 }
1905
1906 function twitter_siteinfo($url, $dontincludemedia) {
1907         require_once("mod/parse_url.php");
1908
1909         // Fetch site infos - but only from the meta data
1910         $data = parseurl_getsiteinfo($url, true);
1911
1912         if ($dontincludemedia)
1913                 unset($data["images"]);
1914
1915         if (!is_string($data["text"]) AND (sizeof($data["images"]) == 0) AND ($data["title"] == $url))
1916                 return("");
1917
1918         if (is_string($data["title"]))
1919                 $text .= "[bookmark=".$url."]".trim($data["title"])."[/bookmark]\n";
1920
1921         // Add a spoiler to the extra information
1922         //if ((sizeof($data["images"]) > 0) OR is_string($data["text"]))
1923         //      $text .= "[spoiler]";
1924
1925         if (sizeof($data["images"]) > 0) {
1926                 $imagedata = $data["images"][0];
1927                 //$text .= '[img='.$imagedata["width"].'x'.$imagedata["height"].']'.$imagedata["src"].'[/img]' . "\n";
1928                 $text .= '[img]'.$imagedata["src"].'[/img]'."\n";
1929         }
1930
1931         if (is_string($data["text"]))
1932                 $text .= "[quote]".$data["text"]."[/quote]";
1933
1934         //if ((sizeof($data["images"]) > 0) OR is_string($data["text"]))
1935         //      $text .= "[/spoiler]";
1936
1937         return($text);
1938
1939 }
1940
1941 function twitter_convertmsg($a, $body, $no_tags = false, $dontincludemedia) {
1942
1943         require_once("include/oembed.php");
1944
1945         $links = preg_match_all("/([^\]\='".'"'."]|^)(https?\:\/\/[a-zA-Z0-9\:\/\-\?\&\;\.\=\_\~\#\%\$\!\+\,]+)/ism", $body,$matches,PREG_SET_ORDER);
1946
1947         $footer = "";
1948         $footerurl = "";
1949         $type = "";
1950
1951         if ($links) {
1952                 foreach ($matches AS $match) {
1953                         $expanded_url = twitter_original_url($match[2]);
1954
1955                         $oembed_data = oembed_fetch_url($expanded_url);
1956
1957                         if ($type == "")
1958                                 $type = $oembed_data->type;
1959
1960                         if ($oembed_data->type != "link")
1961                                 $body = str_replace($match[2], "\n[url]".$expanded_url."[/url]\n", $body);
1962                         else {
1963                                 $img_str = fetch_url($expanded_url, true, $redirects, 4);
1964
1965                                 $tempfile = tempnam(get_config("system","temppath"), "cache");
1966                                 file_put_contents($tempfile, $img_str);
1967                                 $mime = image_type_to_mime_type(exif_imagetype($tempfile));
1968                                 unlink($tempfile);
1969
1970                                 if (substr($mime, 0, 6) == "image/") {
1971                                         $type = "photo";
1972                                         $body = str_replace($match[2], "[img]".$expanded_url."[/img]", $body);
1973                                         $dontincludemedia = true;
1974                                 } else {
1975                                         $type = $oembed_data->type;
1976                                         $footerurl = $expanded_url;
1977                                         $footerlink = "[url=".$expanded_url."]".$expanded_url."[/url]";
1978
1979                                         $body = str_replace($match[2], $footerlink, $body);
1980                                 }
1981                         }
1982                 }
1983
1984                 if ($footerurl != "")
1985                         $footer = twitter_siteinfo($footerurl, $dontincludemedia);
1986
1987                 if (($footerlink != "") AND (trim($footer) != "")) {
1988                         $removedlink = trim(str_replace($footerlink, "", $body));
1989
1990                         if (strstr($body, $removedlink))
1991                                 $body = $removedlink;
1992
1993                         $body .= "\n\n[class=type-".$type."]".$footer."[/class]";
1994                 }
1995         }
1996
1997         if ($no_tags)
1998                 return(array("body" => $body, "tags" => ""));
1999
2000         $str_tags = '';
2001
2002         $tags = get_tags($body);
2003
2004         if(count($tags)) {
2005                 foreach($tags as $tag) {
2006                         if (strstr(trim($tag), " "))
2007                                 continue;
2008
2009                         if(strpos($tag,'#') === 0) {
2010                                 if(strpos($tag,'[url='))
2011                                         continue;
2012
2013                                 // don't link tags that are already embedded in links
2014
2015                                 if(preg_match('/\[(.*?)' . preg_quote($tag,'/') . '(.*?)\]/',$body))
2016                                         continue;
2017                                 if(preg_match('/\[(.*?)\]\((.*?)' . preg_quote($tag,'/') . '(.*?)\)/',$body))
2018                                         continue;
2019
2020                                 $basetag = str_replace('_',' ',substr($tag,1));
2021                                 $body = str_replace($tag,'#[url=' . $a->get_baseurl() . '/search?tag=' . rawurlencode($basetag) . ']' . $basetag . '[/url]',$body);
2022                                 if(strlen($str_tags))
2023                                         $str_tags .= ',';
2024                                 $str_tags .= '#[url=' . $a->get_baseurl() . '/search?tag=' . rawurlencode($basetag) . ']' . $basetag . '[/url]';
2025                                 continue;
2026                         } elseif(strpos($tag,'@') === 0) {
2027                                 $basetag = substr($tag,1);
2028                                 $body = str_replace($tag,'@[url=https://twitter.com/' . rawurlencode($basetag) . ']' . $basetag . '[/url]',$body);
2029                         }
2030
2031                 }
2032         }
2033
2034         $cnt = preg_match_all('/@\[url=(.*?)\[\/url\]/ism',$body,$matches,PREG_SET_ORDER);
2035         if($cnt) {
2036                 foreach($matches as $mtch) {
2037                         if(strlen($str_tags))
2038                                 $str_tags .= ',';
2039                         $str_tags .= '@[url=' . $mtch[1] . '[/url]';
2040                 }
2041         }
2042
2043         return(array("body"=>$body, "tags"=>$str_tags));
2044
2045 }
2046
2047 function twitter_fetch_own_contact($a, $uid) {
2048         $ckey    = get_config('twitter', 'consumerkey');
2049         $csecret = get_config('twitter', 'consumersecret');
2050         $otoken  = get_pconfig($uid, 'twitter', 'oauthtoken');
2051         $osecret = get_pconfig($uid, 'twitter', 'oauthsecret');
2052
2053         $own_id = get_pconfig($uid, 'twitter', 'own_id');
2054
2055         $contact_id = 0;
2056
2057         if ($own_id == "") {
2058                 require_once('library/twitteroauth.php');
2059
2060                 $connection = new TwitterOAuth($ckey,$csecret,$otoken,$osecret);
2061
2062                 // Fetching user data
2063                 $user = $connection->get('account/verify_credentials');
2064
2065                 set_pconfig($uid, 'twitter', 'own_id', $user->id_str);
2066
2067                 $contact_id = twitter_fetch_contact($uid, $user, true);
2068
2069         } else {
2070                 $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `alias` = '%s' LIMIT 1",
2071                         intval($uid), dbesc("twitter::".$own_id));
2072                 if(count($r))
2073                         $contact_id = $r[0]["id"];
2074                 else
2075                         del_pconfig($uid, 'twitter', 'own_id');
2076
2077         }
2078
2079         return($contact_id);
2080 }
2081
2082 function twitter_is_retweet($a, $uid, $body) {
2083         $body = trim($body);
2084
2085         // Skip if it isn't a pure repeated messages
2086         // Does it start with a share?
2087         if (strpos($body, "[share") > 0)
2088                 return(false);
2089
2090         // Does it end with a share?
2091         if (strlen($body) > (strrpos($body, "[/share]") + 8))
2092                 return(false);
2093
2094         $attributes = preg_replace("/\[share(.*?)\]\s?(.*?)\s?\[\/share\]\s?/ism","$1",$body);
2095         // Skip if there is no shared message in there
2096         if ($body == $attributes)
2097                 return(false);
2098
2099         $link = "";
2100         preg_match("/link='(.*?)'/ism", $attributes, $matches);
2101         if ($matches[1] != "")
2102                 $link = $matches[1];
2103
2104         preg_match('/link="(.*?)"/ism', $attributes, $matches);
2105         if ($matches[1] != "")
2106                 $link = $matches[1];
2107
2108         $id = preg_replace("=https?://twitter.com/(.*)/status/(.*)=ism", "$2", $link);
2109         if ($id == $link)
2110                 return(false);
2111
2112         logger('twitter_is_retweet: Retweeting id '.$id.' for user '.$uid, LOGGER_DEBUG);
2113
2114         $ckey    = get_config('twitter', 'consumerkey');
2115         $csecret = get_config('twitter', 'consumersecret');
2116         $otoken  = get_pconfig($uid, 'twitter', 'oauthtoken');
2117         $osecret = get_pconfig($uid, 'twitter', 'oauthsecret');
2118
2119         require_once('library/twitteroauth.php');
2120         $connection = new TwitterOAuth($ckey,$csecret,$otoken,$osecret);
2121
2122         $result = $connection->post('statuses/retweet/'.$id);
2123
2124         logger('twitter_is_retweet: result '.print_r($result, true), LOGGER_DEBUG);
2125
2126         return(!isset($result->errors));
2127 }
2128
2129 ?>