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