]> git.mxchange.org Git - friendica-addons.git/blob - twitter/twitter.php
Merge remote-tracking branch 'upstream/master'
[friendica-addons.git] / twitter / twitter.php
1 <?php
2 /**
3  * Name: Twitter Connector
4  * Description: Relay public postings to a connected Twitter account
5  * Version: 1.0.4
6  * Author: Tobias Diekershoff <https://f.diekershoff.de/profile/tobias>
7  * Author: Michael Vogel <https://pirati.ca/profile/heluecht>
8  *
9  * Copyright (c) 2011-2013 Tobias Diekershoff, Michael Vogel
10  * All rights reserved.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions are met:
14  *    * Redistributions of source code must retain the above copyright notice,
15  *     this list of conditions and the following disclaimer.
16  *    * Redistributions in binary form must reproduce the above
17  *    * copyright notice, this list of conditions and the following disclaimer in
18  *      the documentation and/or other materials provided with the distribution.
19  *    * Neither the name of the <organization> nor the names of its contributors
20  *      may be used to endorse or promote products derived from this software
21  *      without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
25  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
26  * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY DIRECT,
27  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
28  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
30  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
31  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
32  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33  *
34  */
35  
36 /*   Twitter Plugin for Friendica
37  *
38  *   Author: Tobias Diekershoff
39  *           tobias.diekershoff@gmx.net
40  *
41  *   License:3-clause BSD license
42  *
43  *   Configuration:
44  *     To use this plugin you need a OAuth Consumer key pair (key & secret)
45  *     you can get it from Twitter at https://twitter.com/apps
46  *
47  *     Register your Friendica site as "Client" application with "Read & Write" access
48  *     we do not need "Twitter as login". When you've registered the app you get the
49  *     OAuth Consumer key and secret pair for your application/site.
50  *
51  *     Add this key pair to your global .htconfig.php or use the admin panel.
52  *
53  *     $a->config['twitter']['consumerkey'] = 'your consumer_key here';
54  *     $a->config['twitter']['consumersecret'] = 'your consumer_secret here';
55  *
56  *     To activate the plugin itself add it to the $a->config['system']['addon']
57  *     setting. After this, your user can configure their Twitter account settings
58  *     from "Settings -> Plugin Settings".
59  *
60  *     Requirements: PHP5, curl [Slinky library]
61  */
62
63 define('TWITTER_DEFAULT_POLL_INTERVAL', 5); // given in minutes
64
65 function twitter_install() {
66         //  we need some hooks, for the configuration and for sending tweets
67         register_hook('connector_settings', 'addon/twitter/twitter.php', 'twitter_settings'); 
68         register_hook('connector_settings_post', 'addon/twitter/twitter.php', 'twitter_settings_post');
69         register_hook('post_local', 'addon/twitter/twitter.php', 'twitter_post_local');
70         register_hook('notifier_normal', 'addon/twitter/twitter.php', 'twitter_post_hook');
71         register_hook('jot_networks', 'addon/twitter/twitter.php', 'twitter_jot_nets');
72         register_hook('cron', 'addon/twitter/twitter.php', 'twitter_cron');
73         register_hook('queue_predeliver', 'addon/twitter/twitter.php', 'twitter_queue_hook');
74         register_hook('follow', 'addon/twitter/twitter.php', 'twitter_follow');
75         logger("installed twitter");
76 }
77
78
79 function twitter_uninstall() {
80         unregister_hook('connector_settings', 'addon/twitter/twitter.php', 'twitter_settings'); 
81         unregister_hook('connector_settings_post', 'addon/twitter/twitter.php', 'twitter_settings_post');
82         unregister_hook('post_local', 'addon/twitter/twitter.php', 'twitter_post_local');
83         unregister_hook('notifier_normal', 'addon/twitter/twitter.php', 'twitter_post_hook');
84         unregister_hook('jot_networks', 'addon/twitter/twitter.php', 'twitter_jot_nets');
85         unregister_hook('cron', 'addon/twitter/twitter.php', 'twitter_cron');
86         unregister_hook('queue_predeliver', 'addon/twitter/twitter.php', 'twitter_queue_hook');
87         unregister_hook('follow', 'addon/twitter/twitter.php', 'twitter_follow');
88
89         // old setting - remove only
90         unregister_hook('post_local_end', 'addon/twitter/twitter.php', 'twitter_post_hook');
91         unregister_hook('plugin_settings', 'addon/twitter/twitter.php', 'twitter_settings'); 
92         unregister_hook('plugin_settings_post', 'addon/twitter/twitter.php', 'twitter_settings_post');
93
94 }
95
96 function twitter_follow($a, &$contact) {
97
98         logger("twitter_follow: Check if contact is twitter contact. ".$contact["url"], LOGGER_DEBUG);
99
100         if (!strstr($contact["url"], "://twitter.com") AND !strstr($contact["url"], "@twitter.com"))
101                 return;
102
103         // contact seems to be a twitter contact, so continue
104         $nickname = preg_replace("=https?://twitter.com/(.*)=ism", "$1", $contact["url"]);
105         $nickname = str_replace("@twitter.com", "", $nickname);
106
107         $uid = $a->user["uid"];
108
109         $ckey    = get_config('twitter', 'consumerkey');
110         $csecret = get_config('twitter', 'consumersecret');
111         $otoken  = get_pconfig($uid, 'twitter', 'oauthtoken');
112         $osecret = get_pconfig($uid, 'twitter', 'oauthsecret');
113
114         require_once("addon/twitter/codebird.php");
115
116         $cb = \Codebird\Codebird::getInstance();
117         $cb->setConsumerKey($ckey, $csecret);
118         $cb->setToken($otoken, $osecret);
119
120         $parameters = array();
121         $parameters["screen_name"] = $nickname;
122
123         $user = $cb->friendships_create($parameters);
124
125         twitter_fetchuser($a, $uid, $nickname);
126
127         $r = q("SELECT name,nick,url,addr,batch,notify,poll,request,confirm,poco,photo,priority,network,alias,pubkey
128                 FROM `contact` WHERE `uid` = %d AND `nick` = '%s'",
129                                 intval($uid),
130                                 dbesc($nickname));
131         if (count($r))
132                 $contact["contact"] = $r[0];
133 }
134
135 function twitter_jot_nets(&$a,&$b) {
136         if(! local_user())
137                 return;
138
139         $tw_post = get_pconfig(local_user(),'twitter','post');
140         if(intval($tw_post) == 1) {
141                 $tw_defpost = get_pconfig(local_user(),'twitter','post_by_default');
142                 $selected = ((intval($tw_defpost) == 1) ? ' checked="checked" ' : '');
143                 $b .= '<div class="profile-jot-net"><input type="checkbox" name="twitter_enable"' . $selected . ' value="1" /> ' 
144                         . t('Post to Twitter') . '</div>';
145         }
146 }
147
148 function twitter_settings_post ($a,$post) {
149         if(! local_user())
150                 return;
151         // don't check twitter settings if twitter submit button is not clicked
152         if (!x($_POST,'twitter-submit'))
153                 return;
154
155         if (isset($_POST['twitter-disconnect'])) {
156                 /***
157                  * if the twitter-disconnect checkbox is set, clear the OAuth key/secret pair
158                  * from the user configuration
159                  */
160                 del_pconfig(local_user(), 'twitter', 'consumerkey');
161                 del_pconfig(local_user(), 'twitter', 'consumersecret');
162                 del_pconfig(local_user(), 'twitter', 'oauthtoken');
163                 del_pconfig(local_user(), 'twitter', 'oauthsecret');
164                 del_pconfig(local_user(), 'twitter', 'post');
165                 del_pconfig(local_user(), 'twitter', 'post_by_default');
166                 del_pconfig(local_user(), 'twitter', 'post_taglinks');
167                 del_pconfig(local_user(), 'twitter', 'lastid');
168                 del_pconfig(local_user(), 'twitter', 'mirror_posts');
169                 del_pconfig(local_user(), 'twitter', 'intelligent_shortening');
170                 del_pconfig(local_user(), 'twitter', 'import');
171                 del_pconfig(local_user(), 'twitter', 'create_user');
172         } 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 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 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         $has_picture = false;
946
947         require_once('mod/item.php');
948
949         require_once('library/twitteroauth.php');
950         $connection = new TwitterOAuth($ckey,$csecret,$otoken,$osecret);
951
952         $parameters = array("exclude_replies" => true, "trim_user" => false, "contributor_details" => true, "include_rts" => true);
953
954         $first_time = ($lastid == "");
955
956         if ($lastid <> "")
957                 $parameters["since_id"] = $lastid;
958
959         $items = $connection->get('statuses/user_timeline', $parameters);
960
961         if (!is_array($items))
962                 return;
963
964         $posts = array_reverse($items);
965
966         if (count($posts)) {
967             foreach ($posts as $post) {
968                 if ($post->id_str > $lastid)
969                         $lastid = $post->id_str;
970
971                 if ($first_time)
972                         continue;
973
974                 if (!strpos($post->source, $application_name)) {
975                         $_SESSION["authenticated"] = true;
976                         $_SESSION["uid"] = $uid;
977
978                         unset($_REQUEST);
979                         $_REQUEST["type"] = "wall";
980                         $_REQUEST["api_source"] = true;
981                         $_REQUEST["profile_uid"] = $uid;
982                         $_REQUEST["source"] = "Twitter";
983
984                         //$_REQUEST["date"] = $post->created_at;
985
986                         $_REQUEST["title"] = "";
987
988                         if (is_object($post->retweeted_status)) {
989
990                                 $_REQUEST['body'] = $post->retweeted_status->text;
991
992                                 // media
993                                 if (is_array($post->retweeted_status->entities->media)) {
994                                         foreach($post->retweeted_status->entities->media AS $media) {
995                                                 switch($media->type) {
996                                                         case 'photo':
997                                                                 $_REQUEST['body'] = str_replace($media->url, "\n\n[img]".$media->media_url_https."[/img]\n", $_REQUEST['body']);
998                                                                 $has_picture = true;
999                                                                 break;
1000                                                 }
1001                                         }
1002                                 }
1003
1004                                 $converted = twitter_convertmsg($a, $_REQUEST['body'], true, $has_picture);
1005                                 $_REQUEST['body'] = $converted["body"];
1006
1007                                 $_REQUEST['body'] = "[share author='".$post->retweeted_status->user->name.
1008                                         "' profile='https://twitter.com/".$post->retweeted_status->user->screen_name.
1009                                         "' avatar='".$post->retweeted_status->user->profile_image_url_https.
1010                                         "' link='https://twitter.com/".$post->retweeted_status->user->screen_name."/status/".$post->retweeted_status->id_str."']".
1011                                         $_REQUEST['body'];
1012                                 $_REQUEST['body'] .= "[/share]";
1013                         } else {
1014                                 $_REQUEST["body"] = $post->text;
1015
1016                                 if (is_array($post->entities->media)) {
1017                                         foreach($post->entities->media AS $media) {
1018                                                 switch($media->type) {
1019                                                         case 'photo':
1020                                                                 $_REQUEST['body'] = str_replace($media->url, "\n\n[img]".$media->media_url_https."[/img]\n", $_REQUEST['body']);
1021                                                                 $has_picture = true;
1022                                                                 break;
1023                                                 }
1024                                         }
1025                                 }
1026
1027                                 $converted = twitter_convertmsg($a, $_REQUEST["body"], true, $has_picture);
1028                                 $_REQUEST['body'] = $converted["body"];
1029                         }
1030
1031                         if (is_string($post->place->name))
1032                                 $_REQUEST["location"] = $post->place->name;
1033
1034                         if (is_string($post->place->full_name))
1035                                 $_REQUEST["location"] = $post->place->full_name;
1036
1037                         if (is_array($post->geo->coordinates))
1038                                 $_REQUEST["coord"] = $post->geo->coordinates[0]." ".$post->geo->coordinates[1];
1039
1040                         if (is_array($post->coordinates->coordinates))
1041                                 $_REQUEST["coord"] = $post->coordinates->coordinates[1]." ".$post->coordinates->coordinates[0];
1042
1043                         //print_r($_REQUEST);
1044                         logger('twitter: posting for user '.$uid);
1045
1046 //                      require_once('mod/item.php');
1047
1048                         item_post($a);
1049                 }
1050             }
1051         }
1052         set_pconfig($uid, 'twitter', 'lastid', $lastid);
1053 }
1054
1055 function twitter_queue_hook(&$a,&$b) {
1056
1057         $qi = q("SELECT * FROM `queue` WHERE `network` = '%s'",
1058                 dbesc(NETWORK_TWITTER)
1059                 );
1060         if(! count($qi))
1061                 return;
1062
1063         require_once('include/queue_fn.php');
1064
1065         foreach($qi as $x) {
1066                 if($x['network'] !== NETWORK_TWITTER)
1067                         continue;
1068
1069                 logger('twitter_queue: run');
1070
1071                 $r = q("SELECT `user`.* FROM `user` LEFT JOIN `contact` on `contact`.`uid` = `user`.`uid` 
1072                         WHERE `contact`.`self` = 1 AND `contact`.`id` = %d LIMIT 1",
1073                         intval($x['cid'])
1074                 );
1075                 if(! count($r))
1076                         continue;
1077
1078                 $user = $r[0];
1079
1080                 $ckey    = get_config('twitter', 'consumerkey');
1081                 $csecret = get_config('twitter', 'consumersecret');
1082                 $otoken  = get_pconfig($user['uid'], 'twitter', 'oauthtoken');
1083                 $osecret = get_pconfig($user['uid'], 'twitter', 'oauthsecret');
1084
1085                 $success = false;
1086
1087                 if ($ckey AND $csecret AND $otoken AND $osecret) {
1088
1089                         logger('twitter_queue: able to post');
1090
1091                         $z = unserialize($x['content']);
1092
1093                         require_once("addon/twitter/codebird.php");
1094
1095                         $cb = \Codebird\Codebird::getInstance();
1096                         $cb->setConsumerKey($ckey, $csecret);
1097                         $cb->setToken($otoken, $osecret);
1098
1099                         if ($z['url'] == "statuses/update")
1100                                 $result = $cb->statuses_update($z['post']);
1101
1102                         logger('twitter_queue: post result: ' . print_r($result, true), LOGGER_DEBUG);
1103
1104                         if ($result->errors)
1105                                 logger('twitter_queue: Send to Twitter failed: "' . print_r($result->errors, true) . '"');
1106                         else {
1107                                 $success = true;
1108                                 remove_queue_item($x['id']);
1109                         }
1110                 } else
1111                         logger("twitter_queue: Error getting tokens for user ".$user['uid']);
1112
1113                 if (!$success) {
1114                         logger('twitter_queue: delayed');
1115                         update_queue_time($x['id']);
1116                 }
1117         }
1118 }
1119
1120 function twitter_fetch_contact($uid, $contact, $create_user) {
1121
1122         $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `alias` = '%s' LIMIT 1",
1123                 intval($uid), dbesc("twitter::".$contact->id_str));
1124
1125         if(!count($r) AND !$create_user)
1126                 return(0);
1127
1128         if (count($r) AND ($r[0]["readonly"] OR $r[0]["blocked"])) {
1129                 logger("twitter_fetch_contact: Contact '".$r[0]["nick"]."' is blocked or readonly.", LOGGER_DEBUG);
1130                 return(-1);
1131         }
1132
1133         if(!count($r)) {
1134                 // create contact record
1135                 q("INSERT INTO `contact` ( `uid`, `created`, `url`, `nurl`, `addr`, `alias`, `notify`, `poll`,
1136                                         `name`, `nick`, `photo`, `network`, `rel`, `priority`,
1137                                         `writable`, `blocked`, `readonly`, `pending` )
1138                                         VALUES ( %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, %d, 0, 0, 0 ) ",
1139                         intval($uid),
1140                         dbesc(datetime_convert()),
1141                         dbesc("https://twitter.com/".$contact->screen_name),
1142                         dbesc(normalise_link("https://twitter.com/".$contact->screen_name)),
1143                         dbesc($contact->screen_name."@twitter.com"),
1144                         dbesc("twitter::".$contact->id_str),
1145                         dbesc(''),
1146                         dbesc("twitter::".$contact->id_str),
1147                         dbesc($contact->name),
1148                         dbesc($contact->screen_name),
1149                         dbesc($contact->profile_image_url_https),
1150                         dbesc(NETWORK_TWITTER),
1151                         intval(CONTACT_IS_FRIEND),
1152                         intval(1),
1153                         intval(1)
1154                 );
1155
1156                 $r = q("SELECT * FROM `contact` WHERE `alias` = '%s' AND `uid` = %d LIMIT 1",
1157                         dbesc("twitter::".$contact->id_str),
1158                         intval($uid)
1159                         );
1160
1161                 if(! count($r))
1162                         return(false);
1163
1164                 $contact_id  = $r[0]['id'];
1165
1166                 $g = q("SELECT def_gid FROM user WHERE uid = %d LIMIT 1",
1167                         intval($uid)
1168                 );
1169
1170                 if($g && intval($g[0]['def_gid'])) {
1171                         require_once('include/group.php');
1172                         group_add_member($uid,'',$contact_id,$g[0]['def_gid']);
1173                 }
1174
1175                 require_once("Photo.php");
1176
1177                 $photos = import_profile_photo($contact->profile_image_url_https,$uid,$contact_id);
1178
1179                 q("UPDATE `contact` SET `photo` = '%s',
1180                                         `thumb` = '%s',
1181                                         `micro` = '%s',
1182                                         `name-date` = '%s',
1183                                         `uri-date` = '%s',
1184                                         `avatar-date` = '%s'
1185                                 WHERE `id` = %d",
1186                         dbesc($photos[0]),
1187                         dbesc($photos[1]),
1188                         dbesc($photos[2]),
1189                         dbesc(datetime_convert()),
1190                         dbesc(datetime_convert()),
1191                         dbesc(datetime_convert()),
1192                         intval($contact_id)
1193                 );
1194         } else {
1195                 // update profile photos once every two weeks as we have no notification of when they change.
1196
1197                 //$update_photo = (($r[0]['avatar-date'] < datetime_convert('','','now -2 days')) ? true : false);
1198                 $update_photo = ($r[0]['avatar-date'] < datetime_convert('','','now -12 hours'));
1199
1200                 // check that we have all the photos, this has been known to fail on occasion
1201
1202                 if((! $r[0]['photo']) || (! $r[0]['thumb']) || (! $r[0]['micro']) || ($update_photo)) {
1203
1204                         logger("twitter_fetch_contact: Updating contact ".$contact->screen_name, LOGGER_DEBUG);
1205
1206                         require_once("Photo.php");
1207
1208                         $photos = import_profile_photo($contact->profile_image_url_https, $uid, $r[0]['id']);
1209
1210                         q("UPDATE `contact` SET `photo` = '%s',
1211                                                 `thumb` = '%s',
1212                                                 `micro` = '%s',
1213                                                 `name-date` = '%s',
1214                                                 `uri-date` = '%s',
1215                                                 `avatar-date` = '%s',
1216                                                 `url` = '%s',
1217                                                 `nurl` = '%s',
1218                                                 `addr` = '%s',
1219                                                 `name` = '%s',
1220                                                 `nick` = '%s'
1221                                         WHERE `id` = %d",
1222                                 dbesc($photos[0]),
1223                                 dbesc($photos[1]),
1224                                 dbesc($photos[2]),
1225                                 dbesc(datetime_convert()),
1226                                 dbesc(datetime_convert()),
1227                                 dbesc(datetime_convert()),
1228                                 dbesc("https://twitter.com/".$contact->screen_name),
1229                                 dbesc(normalise_link("https://twitter.com/".$contact->screen_name)),
1230                                 dbesc($contact->screen_name."@twitter.com"),
1231                                 dbesc($contact->name),
1232                                 dbesc($contact->screen_name),
1233                                 intval($r[0]['id'])
1234                         );
1235                 }
1236         }
1237
1238         return($r[0]["id"]);
1239 }
1240
1241 function twitter_fetchuser($a, $uid, $screen_name = "", $user_id = "") {
1242         $ckey    = get_config('twitter', 'consumerkey');
1243         $csecret = get_config('twitter', 'consumersecret');
1244         $otoken  = get_pconfig($uid, 'twitter', 'oauthtoken');
1245         $osecret = get_pconfig($uid, 'twitter', 'oauthsecret');
1246
1247         require_once("addon/twitter/codebird.php");
1248
1249         $cb = \Codebird\Codebird::getInstance();
1250         $cb->setConsumerKey($ckey, $csecret);
1251         $cb->setToken($otoken, $osecret);
1252
1253         $r = q("SELECT * FROM `contact` WHERE `self` = 1 AND `uid` = %d LIMIT 1",
1254                 intval($uid));
1255
1256         if(count($r)) {
1257                 $self = $r[0];
1258         } else
1259                 return;
1260
1261         $parameters = array();
1262
1263         if ($screen_name != "")
1264                 $parameters["screen_name"] = $screen_name;
1265
1266         if ($user_id != "")
1267                 $parameters["user_id"] = $user_id;
1268
1269         // Fetching user data
1270         $user = $cb->users_show($parameters);
1271
1272         if (!is_object($user))
1273                 return;
1274
1275         $contact_id = twitter_fetch_contact($uid, $user, true);
1276
1277         return $contact_id;
1278 }
1279
1280 function twitter_createpost($a, $uid, $post, $self, $create_user, $only_existing_contact) {
1281
1282         $has_picture = false;
1283
1284         $postarray = array();
1285         $postarray['gravity'] = 0;
1286         $postarray['uid'] = $uid;
1287         $postarray['wall'] = 0;
1288         $postarray['uri'] = "twitter::".$post->id_str;
1289
1290         $r = q("SELECT * FROM `item` WHERE `extid` = '%s' AND `uid` = %d LIMIT 1",
1291                         dbesc($postarray['uri']),
1292                         intval($uid)
1293                 );
1294
1295         if (count($r))
1296                 return(array());
1297
1298         $contactid = 0;
1299
1300         if ($post->in_reply_to_status_id_str != "") {
1301
1302                 $parent = "twitter::".$post->in_reply_to_status_id_str;
1303
1304                 $r = q("SELECT * FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1",
1305                                 dbesc($parent),
1306                                 intval($uid)
1307                         );
1308                 if (count($r)) {
1309                         $postarray['thr-parent'] = $r[0]["uri"];
1310                         $postarray['parent-uri'] = $r[0]["parent-uri"];
1311                 } else {
1312                         $r = q("SELECT * FROM `item` WHERE `extid` = '%s' AND `uid` = %d LIMIT 1",
1313                                         dbesc($parent),
1314                                         intval($uid)
1315                                 );
1316                         if (count($r)) {
1317                                 $postarray['thr-parent'] = $r[0]['uri'];
1318                                 $postarray['parent-uri'] = $r[0]['parent-uri'];
1319                         } else {
1320                                 $postarray['thr-parent'] = $postarray['uri'];
1321                                 $postarray['parent-uri'] = $postarray['uri'];
1322                         }
1323                 }
1324
1325                 // Is it me?
1326                 $own_id = get_pconfig($uid, 'twitter', 'own_id');
1327
1328                 if ($post->user->id_str == $own_id) {
1329                         $r = q("SELECT * FROM `contact` WHERE `self` = 1 AND `uid` = %d LIMIT 1",
1330                                 intval($uid));
1331
1332                         if(count($r)) {
1333                                 $contactid = $r[0]["id"];
1334
1335                                 $postarray['owner-name'] =  $r[0]["name"];
1336                                 $postarray['owner-link'] = $r[0]["url"];
1337                                 $postarray['owner-avatar'] =  $r[0]["photo"];
1338                         } else
1339                                 return(array());
1340                 }
1341         } else
1342                 $postarray['parent-uri'] = $postarray['uri'];
1343
1344         if ($contactid == 0) {
1345                 $contactid = twitter_fetch_contact($uid, $post->user, $create_user);
1346
1347                 $postarray['owner-name'] = $post->user->name;
1348                 $postarray['owner-link'] = "https://twitter.com/".$post->user->screen_name;
1349                 $postarray['owner-avatar'] = $post->user->profile_image_url_https;
1350         }
1351
1352         if(($contactid == 0) AND !$only_existing_contact)
1353                 $contactid = $self['id'];
1354         elseif ($contactid <= 0)
1355                 return(array());
1356
1357         $postarray['contact-id'] = $contactid;
1358
1359         $postarray['verb'] = ACTIVITY_POST;
1360         $postarray['author-name'] = $postarray['owner-name'];
1361         $postarray['author-link'] = $postarray['owner-link'];
1362         $postarray['author-avatar'] = $postarray['owner-avatar'];
1363         $postarray['plink'] = "https://twitter.com/".$post->user->screen_name."/status/".$post->id_str;
1364         $postarray['app'] = strip_tags($post->source);
1365
1366         if ($post->user->protected) {
1367                 $postarray['private'] = 1;
1368                 $postarray['allow_cid'] = '<' . $self['id'] . '>';
1369         }
1370
1371         $postarray['body'] = $post->text;
1372
1373         // media
1374         if (is_array($post->entities->media)) {
1375                 foreach($post->entities->media AS $media) {
1376                         switch($media->type) {
1377                                 case 'photo':
1378                                         $postarray['body'] = str_replace($media->url, "\n\n[img]".$media->media_url_https."[/img]\n", $postarray['body']);
1379                                         $has_picture = true;
1380                                         break;
1381                                 default:
1382                                         $postarray['body'] .= print_r($media, true);
1383                         }
1384                 }
1385         }
1386
1387         $converted = twitter_convertmsg($a, $postarray['body'], false, $has_picture);
1388         $postarray['body'] = $converted["body"];
1389         $postarray['tag'] = $converted["tags"];
1390
1391         $postarray['created'] = datetime_convert('UTC','UTC',$post->created_at);
1392         $postarray['edited'] = datetime_convert('UTC','UTC',$post->created_at);
1393
1394         if (is_string($post->place->name))
1395                 $postarray["location"] = $post->place->name;
1396
1397         if (is_string($post->place->full_name))
1398                 $postarray["location"] = $post->place->full_name;
1399
1400         if (is_array($post->geo->coordinates))
1401                 $postarray["coord"] = $post->geo->coordinates[0]." ".$post->geo->coordinates[1];
1402
1403         if (is_array($post->coordinates->coordinates))
1404                 $postarray["coord"] = $post->coordinates->coordinates[1]." ".$post->coordinates->coordinates[0];
1405
1406         if (is_object($post->retweeted_status)) {
1407
1408                 $postarray['body'] = $post->retweeted_status->text;
1409
1410                 // media
1411                 if (is_array($post->retweeted_status->entities->media)) {
1412                         foreach($post->retweeted_status->entities->media AS $media) {
1413                                 switch($media->type) {
1414                                         case 'photo':
1415                                                 $postarray['body'] = str_replace($media->url, "\n\n[img]".$media->media_url_https."[/img]\n", $postarray['body']);
1416                                                 $has_picture = true;
1417                                                 break;
1418                                         default:
1419                                                 $postarray['body'] .= print_r($media, true);
1420                                 }
1421                         }
1422                 }
1423
1424                 $converted = twitter_convertmsg($a, $postarray['body'], false, $has_picture);
1425                 $postarray['body'] = $converted["body"];
1426                 $postarray['tag'] = $converted["tags"];
1427
1428
1429                 // Deactivated at the moment, since there are problems with answers to retweets
1430                 if (false AND !intval(get_config('system','wall-to-wall_share'))) {
1431                         $postarray['body'] = "[share author='".$post->retweeted_status->user->name.
1432                                 "' profile='https://twitter.com/".$post->retweeted_status->user->screen_name.
1433                                 "' avatar='".$post->retweeted_status->user->profile_image_url_https.
1434                                 "' link='https://twitter.com/".$post->retweeted_status->user->screen_name."/status/".$post->retweeted_status->id_str."']".
1435                                 $postarray['body'];
1436                         $postarray['body'] .= "[/share]";
1437                 } else {
1438                         // Let retweets look like wall-to-wall posts
1439                         $postarray['author-name'] = $post->retweeted_status->user->name;
1440                         $postarray['author-link'] = "https://twitter.com/".$post->retweeted_status->user->screen_name;
1441                         $postarray['author-avatar'] = $post->retweeted_status->user->profile_image_url_https;
1442                 }
1443
1444         }
1445         return($postarray);
1446 }
1447
1448 function twitter_checknotification($a, $uid, $own_id, $top_item, $postarray) {
1449
1450         $user = q("SELECT * FROM `contact` WHERE `uid` = %d AND `self` LIMIT 1",
1451                         intval($uid)
1452                 );
1453
1454         if(!count($user))
1455                 return;
1456
1457         // Is it me?
1458         if (link_compare($user[0]["url"], $postarray['author-link']))
1459                 return;
1460
1461         $own_user = q("SELECT * FROM `contact` WHERE `uid` = %d AND `alias` = '%s' LIMIT 1",
1462                         intval($uid),
1463                         dbesc("twitter::".$own_id)
1464                 );
1465
1466         if(!count($own_user))
1467                 return;
1468
1469         // Is it me from twitter?
1470         if (link_compare($own_user[0]["url"], $postarray['author-link']))
1471                 return;
1472
1473         $myconv = q("SELECT `author-link`, `author-avatar`, `parent` FROM `item` WHERE `parent-uri` = '%s' AND `uid` = %d AND `parent` != 0 AND `deleted` = 0",
1474                         dbesc($postarray['parent-uri']),
1475                         intval($uid)
1476                         );
1477
1478         if(count($myconv)) {
1479
1480                 foreach($myconv as $conv) {
1481                         // now if we find a match, it means we're in this conversation
1482
1483                         if(!link_compare($conv['author-link'],$user[0]["url"]) AND !link_compare($conv['author-link'],$own_user[0]["url"]))
1484                                 continue;
1485
1486                         require_once('include/enotify.php');
1487
1488                         $conv_parent = $conv['parent'];
1489
1490                         notification(array(
1491                                 'type'         => NOTIFY_COMMENT,
1492                                 'notify_flags' => $user[0]['notify-flags'],
1493                                 'language'     => $user[0]['language'],
1494                                 'to_name'      => $user[0]['username'],
1495                                 'to_email'     => $user[0]['email'],
1496                                 'uid'          => $user[0]['uid'],
1497                                 'item'         => $postarray,
1498                                 'link'             => $a->get_baseurl() . '/display/' . $user[0]['nickname'] . '/' . $top_item,
1499                                 'source_name'  => $postarray['author-name'],
1500                                 'source_link'  => $postarray['author-link'],
1501                                 'source_photo' => $postarray['author-avatar'],
1502                                 'verb'         => ACTIVITY_POST,
1503                                 'otype'        => 'item',
1504                                 'parent'       => $conv_parent,
1505                         ));
1506
1507                         // only send one notification
1508                         break;
1509                 }
1510         }
1511 }
1512
1513 function twitter_fetchhometimeline($a, $uid) {
1514         $ckey    = get_config('twitter', 'consumerkey');
1515         $csecret = get_config('twitter', 'consumersecret');
1516         $otoken  = get_pconfig($uid, 'twitter', 'oauthtoken');
1517         $osecret = get_pconfig($uid, 'twitter', 'oauthsecret');
1518         $create_user = get_pconfig($uid, 'twitter', 'create_user');
1519
1520         logger("twitter_fetchhometimeline: Fetching for user ".$uid, LOGGER_DEBUG);
1521
1522         require_once('library/twitteroauth.php');
1523         require_once('include/items.php');
1524
1525         $connection = new TwitterOAuth($ckey,$csecret,$otoken,$osecret);
1526
1527         $own_contact = twitter_fetch_own_contact($a, $uid);
1528
1529         $r = q("SELECT * FROM `contact` WHERE `id` = %d AND `uid` = %d LIMIT 1",
1530                 intval($own_contact),
1531                 intval($uid));
1532
1533         if(count($r)) {
1534                 $own_id = $r[0]["nick"];
1535         } else {
1536                 logger("twitter_fetchhometimeline: Own twitter contact not found for user ".$uid, LOGGER_DEBUG);
1537                 return;
1538         }
1539
1540         $r = q("SELECT * FROM `contact` WHERE `self` = 1 AND `uid` = %d LIMIT 1",
1541                 intval($uid));
1542
1543         if(count($r)) {
1544                 $self = $r[0];
1545         } else {
1546                 logger("twitter_fetchhometimeline: Own contact not found for user ".$uid, LOGGER_DEBUG);
1547                 return;
1548         }
1549
1550         $u = q("SELECT * FROM user WHERE uid = %d LIMIT 1",
1551                 intval($uid));
1552         if(!count($u)) {
1553                 logger("twitter_fetchhometimeline: Own user not found for user ".$uid, LOGGER_DEBUG);
1554                 return;
1555         }
1556
1557         $parameters = array("exclude_replies" => false, "trim_user" => false, "contributor_details" => true, "include_rts" => true);
1558         //$parameters["count"] = 200;
1559
1560
1561         // Fetching timeline
1562         $lastid  = get_pconfig($uid, 'twitter', 'lasthometimelineid');
1563
1564         $first_time = ($lastid == "");
1565
1566         if ($lastid <> "")
1567                 $parameters["since_id"] = $lastid;
1568
1569         $items = $connection->get('statuses/home_timeline', $parameters);
1570
1571         if (!is_array($items)) {
1572                 logger("twitter_fetchhometimeline: Error fetching home timeline: ".print_r($items, true), LOGGER_DEBUG);
1573                 return;
1574         }
1575
1576         $posts = array_reverse($items);
1577
1578         logger("twitter_fetchhometimeline: Fetching timeline for user ".$uid." ".sizeof($posts)." items", LOGGER_DEBUG);
1579
1580         if (count($posts)) {
1581                 foreach ($posts as $post) {
1582                         if ($post->id_str > $lastid)
1583                                 $lastid = $post->id_str;
1584
1585                         if ($first_time)
1586                                 continue;
1587
1588                         $postarray = twitter_createpost($a, $uid, $post, $self, $create_user, true);
1589
1590                         if (trim($postarray['body']) == "")
1591                                 continue;
1592
1593                         $item = item_store($postarray);
1594
1595                         logger('twitter_fetchhometimeline: User '.$self["nick"].' posted home timeline item '.$item);
1596
1597                         if ($item != 0)
1598                                 twitter_checknotification($a, $uid, $own_id, $item, $postarray);
1599
1600                 }
1601         }
1602         set_pconfig($uid, 'twitter', 'lasthometimelineid', $lastid);
1603
1604         // Fetching mentions
1605         $lastid  = get_pconfig($uid, 'twitter', 'lastmentionid');
1606
1607         $first_time = ($lastid == "");
1608
1609         if ($lastid <> "")
1610                 $parameters["since_id"] = $lastid;
1611
1612         $items = $connection->get('statuses/mentions_timeline', $parameters);
1613
1614         if (!is_array($items)) {
1615                 logger("twitter_fetchhometimeline: Error fetching mentions: ".print_r($items, true), LOGGER_DEBUG);
1616                 return;
1617         }
1618
1619         $posts = array_reverse($items);
1620
1621         logger("twitter_fetchhometimeline: Fetching mentions for user ".$uid." ".sizeof($posts)." items", LOGGER_DEBUG);
1622
1623         if (count($posts)) {
1624                 foreach ($posts as $post) {
1625                         if ($post->id_str > $lastid)
1626                                 $lastid = $post->id_str;
1627
1628                         if ($first_time)
1629                                 continue;
1630
1631                         $postarray = twitter_createpost($a, $uid, $post, $self, false, false);
1632
1633                         if (trim($postarray['body']) == "")
1634                                 continue;
1635
1636                         $item = item_store($postarray);
1637
1638                         logger('twitter_fetchhometimeline: User '.$self["nick"].' posted mention timeline item '.$item);
1639
1640                         if ($item == 0) {
1641                                 $r = q("SELECT * FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1",
1642                                         dbesc($postarray['uri']),
1643                                         intval($uid)
1644                                 );
1645                                 if (count($r))
1646                                         $item = $r[0]['id'];
1647                         }
1648
1649                         if ($item != 0) {
1650                                 require_once('include/enotify.php');
1651                                 notification(array(
1652                                         'type'         => NOTIFY_TAGSELF,
1653                                         'notify_flags' => $u[0]['notify-flags'],
1654                                         'language'     => $u[0]['language'],
1655                                         'to_name'      => $u[0]['username'],
1656                                         'to_email'     => $u[0]['email'],
1657                                         'uid'          => $u[0]['uid'],
1658                                         'item'         => $postarray,
1659                                         'link'         => $a->get_baseurl() . '/display/' . $u[0]['nickname'] . '/' . $item,
1660                                         'source_name'  => $postarray['author-name'],
1661                                         'source_link'  => $postarray['author-link'],
1662                                         'source_photo' => $postarray['author-avatar'],
1663                                         'verb'         => ACTIVITY_TAG,
1664                                         'otype'        => 'item'
1665                                 ));
1666                         }
1667                 }
1668         }
1669
1670         set_pconfig($uid, 'twitter', 'lastmentionid', $lastid);
1671 }
1672
1673 function twitter_original_url($url, $depth=1, $fetchbody = false) {
1674         if ($depth > 10)
1675                 return($url);
1676
1677         $siteinfo = array();
1678         $ch = curl_init();
1679         curl_setopt($ch, CURLOPT_URL, $url);
1680         curl_setopt($ch, CURLOPT_HEADER, 1);
1681
1682         if ($fetchbody)
1683                 curl_setopt($ch, CURLOPT_NOBODY, 0);
1684         else
1685                 curl_setopt($ch, CURLOPT_NOBODY, 1);
1686
1687         curl_setopt($ch, CURLOPT_TIMEOUT, 10);
1688         curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
1689         curl_setopt($ch,CURLOPT_USERAGENT,'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:24.0) Gecko/20100101 Firefox/24.0');
1690
1691         $header = curl_exec($ch);
1692         $curl_info = @curl_getinfo($ch);
1693         $http_code = $curl_info['http_code'];
1694         curl_close($ch);
1695
1696         if ((($curl_info['http_code'] == "301") OR ($curl_info['http_code'] == "302"))
1697                 AND (($curl_info['redirect_url'] != "") OR ($curl_info['location'] != ""))) {
1698                 if ($curl_info['redirect_url'] != "")
1699                         return(twitter_original_url($curl_info['redirect_url'], ++$depth, $fetchbody));
1700                 else
1701                         return(twitter_original_url($curl_info['location'], ++$depth, $fetchbody));
1702         }
1703
1704         $pos = strpos($header, "\r\n\r\n");
1705
1706         if ($pos)
1707                 $body = trim(substr($header, $pos));
1708         else
1709                 $body = $header;
1710
1711         if (trim($body) == "")
1712                 return(twitter_original_url($url, ++$depth, true));
1713
1714         $doc = new DOMDocument();
1715         @$doc->loadHTML($body);
1716
1717         $xpath = new DomXPath($doc);
1718
1719         $list = $xpath->query("//meta[@content]");
1720         foreach ($list as $node) {
1721                 $attr = array();
1722                 if ($node->attributes->length)
1723                         foreach ($node->attributes as $attribute)
1724                                 $attr[$attribute->name] = $attribute->value;
1725
1726                 if (@$attr["http-equiv"] == 'refresh') {
1727                         $path = $attr["content"];
1728                         $pathinfo = explode(";", $path);
1729                         $content = "";
1730                         foreach ($pathinfo AS $value)
1731                                 if (substr(strtolower($value), 0, 4) == "url=")
1732                                         return(twitter_original_url(substr($value, 4), ++$depth));
1733                 }
1734         }
1735
1736         return($url);
1737 }
1738
1739 function twitter_siteinfo($url, $dontincludemedia) {
1740         require_once("mod/parse_url.php");
1741
1742         // Fetch site infos - but only from the meta data
1743         $data = parseurl_getsiteinfo($url, true);
1744
1745         if ($dontincludemedia)
1746                 unset($data["images"]);
1747
1748         if (!is_string($data["text"]) AND (sizeof($data["images"]) == 0) AND ($data["title"] == $url))
1749                 return("");
1750
1751         if (is_string($data["title"]))
1752                 $text .= "[bookmark=".$url."]".trim($data["title"])."[/bookmark]\n";
1753
1754         // Add a spoiler to the extra information
1755         //if ((sizeof($data["images"]) > 0) OR is_string($data["text"]))
1756         //      $text .= "[spoiler]";
1757
1758         if (sizeof($data["images"]) > 0) {
1759                 $imagedata = $data["images"][0];
1760                 $text .= '[img='.$imagedata["width"].'x'.$imagedata["height"].']'.$imagedata["src"].'[/img]' . "\n";
1761         }
1762
1763         if (is_string($data["text"]))
1764                 $text .= "[quote]".$data["text"]."[/quote]";
1765
1766         //if ((sizeof($data["images"]) > 0) OR is_string($data["text"]))
1767         //      $text .= "[/spoiler]";
1768
1769         return($text);
1770
1771 }
1772
1773 function twitter_convertmsg($a, $body, $no_tags = false, $dontincludemedia) {
1774
1775         $links = preg_match_all("/([^\]\='".'"'."]|^)(https?\:\/\/[a-zA-Z0-9\:\/\-\?\&\;\.\=\_\~\#\%\$\!\+\,]+)/ism", $body,$matches,PREG_SET_ORDER);
1776
1777         $footer = "";
1778         $footerurl = "";
1779
1780         if ($links) {
1781                 foreach ($matches AS $match) {
1782                         $expanded_url = twitter_original_url($match[2]);
1783
1784                         // To-Do:
1785                         // Twitlonger
1786
1787                         if (strstr($expanded_url, "//www.youtube.com/"))
1788                                 $body = str_replace($match[2], "\n[youtube]".$expanded_url."[/youtube]\n", $body);
1789                         elseif (strstr($expanded_url, "//player.vimeo.com/"))
1790                                 $body = str_replace($match[2], "\n[vimeo]".$expanded_url."[/vimeo]\n", $body);
1791                         elseif (strstr($expanded_url, "//twitpic.com/")) // Test
1792                                 $body = str_replace($match[2], "\n[url]".$expanded_url."[/url]\n", $body);
1793                         elseif (strstr($expanded_url, "//instagram.com/"))
1794                                 $body = str_replace($match[2], "\n[url]".$expanded_url."[/url]\n", $body);
1795                         else {
1796                                 $img_str = fetch_url($expanded_url, true, $redirects, 4);
1797
1798                                 $tempfile = tempnam(get_config("system","temppath"), "cache");
1799                                 file_put_contents($tempfile, $img_str);
1800                                 $mime = image_type_to_mime_type(exif_imagetype($tempfile));
1801                                 unlink($tempfile);
1802
1803                                 if (substr($mime, 0, 6) == "image/") {
1804                                         $body = str_replace($match[2], "[img]".$expanded_url."[/img]", $body);
1805                                         $dontincludemedia = true;
1806                                 } else {
1807                                         $footerurl = $expanded_url;
1808                                         $footerlink = "[url=".$expanded_url."]".$expanded_url."[/url]";
1809
1810                                         $body = str_replace($match[2], $footerlink, $body);
1811                                 }
1812                         }
1813                 }
1814
1815                 if ($footerurl != "")
1816                         $footer = "\n\n".twitter_siteinfo($footerurl, $dontincludemedia);
1817
1818                 if (($footerlink != "") AND (trim($footer) != "")) {
1819                         $removedlink = trim(str_replace($footerlink, "", $body));
1820
1821                         if (strstr($body, $removedlink))
1822                                 $body = $removedlink;
1823
1824                         $body .= $footer;
1825                 }
1826         }
1827
1828         if ($no_tags)
1829                 return(array("body" => $body, $tags => ""));
1830
1831         $str_tags = '';
1832
1833         $tags = get_tags($body);
1834
1835         if(count($tags)) {
1836                 foreach($tags as $tag) {
1837                         if (strstr(trim($tag), " "))
1838                                 continue;
1839
1840                         if(strpos($tag,'#') === 0) {
1841                                 if(strpos($tag,'[url='))
1842                                         continue;
1843
1844                                 // don't link tags that are already embedded in links
1845
1846                                 if(preg_match('/\[(.*?)' . preg_quote($tag,'/') . '(.*?)\]/',$body))
1847                                         continue;
1848                                 if(preg_match('/\[(.*?)\]\((.*?)' . preg_quote($tag,'/') . '(.*?)\)/',$body))
1849                                         continue;
1850
1851                                 $basetag = str_replace('_',' ',substr($tag,1));
1852                                 $body = str_replace($tag,'#[url=' . $a->get_baseurl() . '/search?tag=' . rawurlencode($basetag) . ']' . $basetag . '[/url]',$body);
1853                                 if(strlen($str_tags))
1854                                         $str_tags .= ',';
1855                                 $str_tags .= '#[url=' . $a->get_baseurl() . '/search?tag=' . rawurlencode($basetag) . ']' . $basetag . '[/url]';
1856                                 continue;
1857                         } elseif(strpos($tag,'@') === 0) {
1858                                 $basetag = substr($tag,1);
1859                                 $body = str_replace($tag,'@[url=https://twitter.com/' . rawurlencode($basetag) . ']' . $basetag . '[/url]',$body);
1860                         }
1861
1862                 }
1863         }
1864
1865         $cnt = preg_match_all('/@\[url=(.*?)\[\/url\]/ism',$body,$matches,PREG_SET_ORDER);
1866         if($cnt) {
1867                 foreach($matches as $mtch) {
1868                         if(strlen($str_tags))
1869                                 $str_tags .= ',';
1870                         $str_tags .= '@[url=' . $mtch[1] . '[/url]';
1871                 }
1872         }
1873
1874         return(array("body"=>$body, "tags"=>$str_tags));
1875
1876 }
1877
1878 function twitter_fetch_own_contact($a, $uid) {
1879         $ckey    = get_config('twitter', 'consumerkey');
1880         $csecret = get_config('twitter', 'consumersecret');
1881         $otoken  = get_pconfig($uid, 'twitter', 'oauthtoken');
1882         $osecret = get_pconfig($uid, 'twitter', 'oauthsecret');
1883
1884         $own_id = get_pconfig($uid, 'twitter', 'own_id');
1885
1886         $contact_id = 0;
1887
1888         if ($own_id == "") {
1889                 require_once('library/twitteroauth.php');
1890
1891                 $connection = new TwitterOAuth($ckey,$csecret,$otoken,$osecret);
1892
1893                 // Fetching user data
1894                 $user = $connection->get('account/verify_credentials');
1895
1896                 set_pconfig($uid, 'twitter', 'own_id', $user->id_str);
1897
1898                 $contact_id = twitter_fetch_contact($uid, $user, true);
1899
1900         } else {
1901                 $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `alias` = '%s' LIMIT 1",
1902                         intval($uid), dbesc("twitter::".$own_id));
1903                 if(count($r))
1904                         $contact_id = $r[0]["id"];
1905         }
1906
1907         return($contact_id);
1908 }
1909 ?>