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