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