Being Hacked: Part V & VI: Epilogue?

Parte V: One against the other

In this third part of  Being Hacked  I made a small list of words to search in all account:

backdoor
fx0
//rare to find
get_current_user
//too many matches
gzinflate

base64_decode

gzuncompress

secureroot 

After cleaning all the webshells I found, I restarted the search. To my surprise, I found another new files installed less than one  minute ago!. Believe it or not, the hacker was spreading new webshell copies along the account… and this time he wasn’t very careful at all. He started overwriting wordpress files all over, such admin.php (if you log into wordpress you will go directly to this file (!)) Apparently he had noticed the ‘disappearance’ of the webshells installed and taking one of the last remaining, he started ‘farming’ again.

Finally, I solved the problem with three steps:

  • Deactivating the plugin which started the problem
  • Deleting all the webshells
  • Updating all WordPress sites.
After few revisions, the hacker got out of the system.

Part VI: Epilogue(?)

Checking the log of that day, I could see that in some moment, I was repairing the account while the hacker was using the backdoor. I was lucky: the hacker wasn’t a destructor activist, he simply accessed the account to take advantage without being noticed. But from the moment he installed their phishing sites, it was detected by Google (who sent me an email) and Site5.

The very next day, there were six attempts to register in the site where the vulnerable plugins was running (first deactivated, later updated). That confirmed the entry point who started the problem.

I think I was lucky, but it would probably change in the future. That’s why you have to prevent this problems.

Site5 sent me a step list to better protection to the site, what is welcome of course. (that would be a matter for another post)

What I learned:

  • You can follow all the security recommendations and your site will as strong as your weakest link: a vulnerable plugin.
  • Without knowing the real problem, the best solution could be useless (except radical solutions like ‘formatting’ your account…)
  • Sometimes is good to know if your plugins have a known vulnerability checkin http://securityvulns.ru/ or http://www.exploit-db.com/
Go back from start?: You have been hacked

Being Hacked: Part III y IV – Cleaning the house

 

Part III: cleaning up the mess

From the first part of Being Hacked, one of the problems was to find out what steps to take. If I get in a sinking boat, drain the water does not solve the problem, while the gaps are still not covered. One of my concerns was to find out a complete phishing site installed (used to capture credit card data) inside on my own sites. The hacker had managed to upload a zip with the website , unzip it and installed it. That lead to a several questions that initially couldn’t answer:

  • How did the hacker manage to enter the site?
  • Where was the vulnerability?
  • How to protect yourself from something that is not known?
When an account is compromised, all the sites in the account are. You cannot simply take down your blog to solve the problem, that is a blind way to fix things and if you ignore where the problem is, your are delaying the inevitable, because in some way or other you will have to deal with similar problems in the future.
First of all, I started cleaning the account. Every unused site or file for testing purposes was deleted one by one. That implied an meticulous examination of everything inside the account to alleviate the task of dealing with the problem. After a thoroughly revision at leas 10 test sites were deleted.

This cleaning task leaded me to a disturbing discovering. Every site included a file signature gh.html and inside of it the hacker signature, dated 3 weeks ago. It meant the account was compromised time enough to do whatever the hacker wanted to. In the other hand, how to detect something when you don’t have a clue what are you looking for? Finally a starting point: in the logs I detected a repeated access to a album.php file in an uncommon place. What was in that file?: Un webshell en una de sus muchas variantes

A webshell in one of its many variations

This discovery uncover why the hacker could modify and install everything he wanted. The webshells are such wonderful tool who allows you to do virtually anything you want with files. Well managed can provide magic. Evidently black magic is what is usually his target. That explained how the hacker installed the scripts, but it didn’t explain how he managed to get into the account.

Parte IV: Unraveling the tangle

First of all, I had to find the webshells around the account. Since the hacker had access to all the sites, it could just install the file everywhere he wanted. I started connecting the dots: the unusual activity in one site, the phishing in another…the problems  were related. Probably the hacker used one site to get into the account and then install the phishing site in another.

Back to the problem the shells are usually encripted in the form:

echo(gzinflate(base64_decode(‘FZrHj……..

So if we search for something as ‘gzinflate‘ or base64_decode, there are good probabilities to find out any webshell, although we also have many ‘false positives’. After some research, I made this search from de the command line:

grep -H -r “base64_decode” . | cut -d: -f1

In short, this command combination makes a global search in every file in the account containing the word base64_decode and listed for location. There is some interesting information about how to look for web shell in this link and also in the following links you’ll find interesting info about find/grep commands:

http://www.thegeekstuff.com/2009/03/15-practical-linux-find-command-examples/ http://www.thegeekstuff.com/2009/06/15-practical-unix-linux-find-command-examples-part-2/ http://en.wikipedia.org/wiki/Find

It is true I got many ‘false positives’ but also I got all the webshells inside the host. There was four or five of them, in different places, different names and different versions. What really draw my attention was to find a couple of them in the following folders:

/xxxxxxx/wp-content/forum-avatars/album.php

/xxxxxxx/wp-content/forum-avatars/smc.php

The very interesting thing about them was that the webshell were inside a images folder from a wordpress forum plugin. And then I realized where was the genius of the hacker: an indocumented  vulnerability in the plugin allowed the user to upload files instead images. So I disabled the plugin to close the security gap.

(Later, I checked the plugin support and there was not records about this vulnerability. Anyway, the plugin was already updated an its vulnerability wasn’t there anymore.). Due the plugin accepted a limited image size, the hacker managed to upload a smaller webshell and with it , uploaded another more complex webshell. The rest is story.

What I learned:

  • How to make global searches in my account.
  • Webshells can be a useful tools in the proper hands.
  • Most of WordPress vulnerabilities are coming from its plugins, instead his core.
  • Keeping updated a WordPress site is useless if you have faulty plugins or it has vulnerabilities.
  • Pay attention to vulnerabilities that allows uploading arbitrary files…now you know the reason

You can continue reading this story : Epílogue? or going back to the first part: You have been hacked

Being Hacked: Part I & II – You have been hacked

This is the beginning of a story, based on real events. Due its extension, I decided to divide it in small digestible parts. I hope what I wrote down here could be useful for many WordPress users, and eventhe  hackers will have a little fun with his exploits.

Compared with many Web veterans, my early times building sites and blogging has been very recent. My first sites were about 2005 (using a personal server) but I took the matter seriously in 2008 renting space on a host. In the country I’m living, the Internet connection was close to be a luxury and the idea to run a server proved to be expensive and prone to live with connection downtime on daily basis.

I started my first web site in mid 2008, and it was (and it is) about triatlhon , a sport that involves swimming, cycling and running. My first post (a test by the way) was done in mid-July 2008 and since then I ‘ve been making improvements, learning WordPress and expanding the installed sites when I got some customers. To sum up, has been three years and half from the starting point. Depending on the point of view, it may seem like a lot of time or very little, but if we speak about learning, I can assure you it is a very long time.

From the start I picked Site5 as hosting provider. You may think this is an ad (and maybe it is) but we’ll see that this provider has an important part on this story. I’ve been working with Site5 all this time and it was a rewarding experience. They have an excellent technical support and when there was a problem, they solved at once. The support is very good, prices are reasonable…yeah I’m happy with that, and that is hard to get. You see, I’m very critic with myself and if you are facing toward excellence, finding out a hosting provider who keeps its quality of service along the years it is very difficult to see.

Back to the story: while the number of sites were growing, I perceived the reality of being hacked as diffuse, distant and small,  nothing to worry about. Sometimes I think ignorance and lack of foresight can provide a sense of security.

I mean: a false sense of security.

What could be the odds that someone could hack a bunch of WordPress sites that were always up-to-date?

That kind of thoughts are not very forward-thinking.

And also the hackers are very clever…

Part I: Symptoms ignored because they were never well understood.

A month before what would be the main event, I received a call from a client telling that the activity of his site was unusually high. the client had a WordPress site with  Statpress Reloaded plugin installed so he could easily review the activity.

I checked the statistics, finding these numbers:

Inusual Activity | Actividad Inusual

Any blogger would be happy to know that your site has an increasing traffic, but if the site is a starting corporate profile, without updates, a dramatic increase of visitors is something suspicious. Also, a sudden change from 10-20 daily visitors  to  300-400 daily visitors in a few days is at least unusual.

The Statpress  plugin is nearly useless in this case. It will give you a clue only in the last 10 activities and in this case, all of them pointed to a the same page:  Camera just installed detects carjacking.  This was the only update in months and there was nothing in there. The real problem stayed  behind the curtains but I couldn’t realize it until much later. If we only get the last 10 pages accessed and we have at least 1000 pageviews, we’re missing the complete picture here.

Finally I concluded that there was some sort of script that recorded comments on that page, (there were hundreds of spam comments also), so I made sure that Akismet plugin was working, I added a captcha plugin to avoid such problems and decided to close the problem.

To be honest, I was far from over.

Part II: your account was suspended

Few days later Site5 detected a serious phishing problem in other site on my account. They sent me an alert by email three times. The problem was that every email alert were filtered as spam , and my account was deactivated 48 hours later without even know that!

This is the worst way to be aware about a problem.

 

Phishing en la cuenta (Datos enmascarados para respetar al remitente)
Phishing (data masked to respect the service)

 

So I had a weird traffic problem in one site and a phishing problem in other. Were they related? I didn’t make the connection at that moment. I contacted with Site5 and they reactivated my account. An account suspension means that every site in your account will be down. That was a serious impact.

And the phishing implication was disturbing. It meant that someone had found a way to upload files to my hosting. Somebody had privileges enough.

My account was compromised.

What I learned:

  • Check spam from time to time. The filters are not 100% accurate. Update the filters if some right email goes to spam.
  • Is good to have a tool to check logs. (More on this topic in future posts)
  • It is good also to have a record log. Even there was a way to activate a record log, by default the server recorded only the last 24 hours. After that I turned on a continuous recording mode.
  • Don’t overlook the problem. Sometimes the rabbit hole is more deeper that we think.

You can continue reading this story : Cleaning the house and Epílogue?

Google Trends on WordPress

A asked myself a question: How does WordPress position itself  in relation to other CMS systems?. For this I turned to Google Trends which compared three popular CMS systems: WordPress, Joomla and Drupal.

The results are interesting. It shows that at some point in the year 2009, the constantly raising WordPress exceeded Joomla trend.  Joomla in turn has started a slight decline, while Drupal although slightly below, maintains a continuous growth curve, far from its rivals.

Reversing engineering to a malware wordpress theme

I was reading the malware concept inside wordpress themes (Anatomy of a theme malware (a brillant post)) and also in Weblog Tools Collection with a excellent video . ¿What about the themes I downloaded? Maybe some of them have a monster in his chest, ready to blow up…

Something's going on on this wordpress theme...

Something's going on on this wordpress theme...

 

To my concern, a few of them (downloaded from not-so-respectable places) had something like this in the footer.php:

<?php $_F=__FILE__;$_X='Pz48IS0tIGI1ZzRuIGYyMnQ1ciAtLT4NCg0KPGQ0diBzdHlsNT0iY2w1MXI6Yj
J0aDsiPjwvZDR2Pg0KDQo8ZDR2IDRkPSJmMjJ0NXIiPg0KDQoJPHA+QzJweXI0Z2h0ICZjMnB5OyBhMDA4ICZt
NGRkMnQ7IEFsbCBSNGdodHMgUjVzNXJ2NWQgJm00ZGQydDsgPDEgaHI1Zj0iaHR0cDovL3d3dy5jcDEtbjV0dz
Jyay4ycmciID5DUEEgTjV0dzJyazwvMT4gdGg1bTUgYnkgPDEgaHI1Zj0iaHR0cDovL3d3dy5jcDEtbjV0dzJy
a3MubjV0IiA+Q1BBIE41dHcycmtzPC8xPiAmbTRkZDJ0OyBQMnc1cjVkIGJ5IDwxIGhyNWY9Imh0dHA6Ly93d3
cudzJyZHByNXNzLjJyZy8iPlcycmRQcjVzczwvMT4gJm00ZGQydDsgPD9waHAgd3BfbDJnNG4yM3QoKTsgPz48
L3A+DQoNCjwvZDR2Pg0KDQo8P3BocCBkMl8xY3Q0Mm4oJ3dwX2YyMnQ1cicpOyA/Pg0KDQo8L2Q0dj4NCg0KPC
9iMmR5Pg0KPC9odG1sPg==';eval(base64_decode('JF9YPWJhc2U2NF9kZWNvZGUoJF9YKTskX1g9c3RydH
IoJF9YLCcxMjM0NTZhb3VpZScsJ2FvdWllMTIzNDU2Jyk7JF9SPWVyZWdfcmVwbGFjZSgnX19GSUxFX18nLCIn
Ii4kX0YuIiciLCRfWCk7ZXZhbCgkX1IpOyRfUj0wOyRfWD0wOw=='));?>

You will see a few variables $_F y $_X and a  base64_decode function beginning the long sentence.

I used the  decode to base 64 page to see what is hidden in the following evaluation:

eval(base64_decode('JF9YPWJhc2U2NF9kZWNvZGUoJF9YKTskX1g9c3RydHIoJF9YLCcxMjM0NTZhb3VpZS
csJ2FvdWllMTIzNDU2Jyk7JF9SPWVyZWdfcmVwbGFjZSgnX19GSUxFX18nLCInIi4kX0YuIiciLCRfWCk7ZXZh
bCgkX1IpOyRfUj0wOyRfWD0wOw=='));

Getting:

$_X=base64_decode($_X);
$_X=strtr($_X,'123456aouie','aouie123456');
$_R=ereg_replace('__FILE__',"'".$_F."'",$_X);
eval($_R);
$_R=0;
$_X=0;

In short, decode the $_X and then evaluate.I did a little modification to render the code harmless:

<?php $_F=__FILE__;$_X='Pz4JPCEtLSBGT09URVIgU1RBUlRTIC0tPg0KCTxkNHYgNGQ9ImYyMnQ1ci0yM3
QiPg0KCTxkNHYgNGQ9ImYyMnQ1ciIgY2wxc3M9IndyMXAiPg0KICAgICAgICANCgkJPGQ0diBjbDFzcz0idzRk
ZzV0IGJsMmNrIj4NCgkJCTw/cGhwIGR5bjFtNGNfczRkNWIxcihhKSA/Pg0KCQk8L2Q0dj4NCgkJPGQ0diBjbD
Fzcz0idzRkZzV0IGJsMmNrIj4NCgkJCTw/cGhwIGR5bjFtNGNfczRkNWIxcihvKSA/Pg0KCQk8L2Q0dj4NCgkJ
PGQ0diBjbDFzcz0idzRkZzV0IGJsMmNrIGwxc3QiPg0KCQkJPD9waHAgZHluMW00Y19zNGQ1YjFyKHUpID8+DQ
oJCTwvZDR2Pg0KCTwvZDR2Pg0KCTwvZDR2Pg0KCTwhLS0gRk9PVEVSIEVORFMgLS0+DQoJPGQ0diA0ZD0iYzJw
eXI0Z2h0LTIzdCI+DQoJPGQ0diA0ZD0iYzJweXI0Z2h0IiBjbDFzcz0id3IxcCI+DQoJCTxkNHYgY2wxc3M9Im
MybC1sNWZ0Ij4NCgkJCTwzbD4NCgkJCQk8P3BocCA0ZiAoNHNfcDFnNSgpKSB7ICRoNGdobDRnaHQgPSAicDFn
NV80dDVtIjsgfSA1bHM1IHskaDRnaGw0Z2h0ID0gInAxZzVfNHQ1bSBjM3JyNW50X3AxZzVfNHQ1bSI7IH0gPz
4NCgkJCQk8bDQgY2wxc3M9Ijw/cGhwIDVjaDIgJGg0Z2hsNGdodDsgPz4gZjRyc3QiPjwxIGhyNWY9Ijw/cGhw
IGJsMmc0bmYyKCczcmwnKTsgPz4iPkgybTU8LzE+PC9sND4NCgkJCQk8P3BocCB3cF9sNHN0X3AxZzVzKCdzMn
J0X2MybDNtbj1tNW4zXzJyZDVyJmQ1cHRoPTYmdDR0bDVfbDQ9Jyk7ID8+DQoJCQk8LzNsPg0KCQk8cD4mYzJw
eTsgQzJweXI0Z2h0IDwxIGhyNWY9Imh0dHA6Ly9qNTN4ZDUtYzFzNG4yLmMybS8iIHQ0dGw1PSJqNTN4IGQ1IG
MxczRuMiI+ajUzeCBkNSBjMXM0bjI8LzE+LiBBbGwgUjRnaHRzIFI1czVydjVkLjwvcD4NCgkJPC9kNHY+DQoJ
CTxkNHYgY2wxc3M9ImMybC1yNGdodCI+DQoJCQk8MSBocjVmPSIjIj48NG1nIHNyYz0iPD9waHAgYmwyZzRuZj
IoJ3Q1bXBsMXQ1X2Q0cjVjdDJyeScpOyA/Pi80bTFnNXMvNG1nX3QycC5nNGYiIHc0ZHRoPSJvdSIgaDU0Z2h0
PSJhdSIgMWx0PSJCMWNrIDJuIFQycCIgLz48LzE+DQoJCTwvZDR2Pg0KCTwvZDR2Pg0KCTwvZDR2Pg0KPD9waH
Agd3BfZjIydDVyKCk7ID8+DQoNCjw/cGhwIDRmICggZzV0XzJwdDQybigndzIyX2cyMmdsNV8xbjFseXQ0Y3Mn
KSA8PiAiIiApIHsgNWNoMiBzdHI0cHNsMXNoNXMoZzV0XzJwdDQybigndzIyX2cyMmdsNV8xbjFseXQ0Y3MnKS
k7IH0gPz4NCjwvYjJkeT4NCjwvaHRtbD4=';

$_X=base64_decode($_X);
$_X=strtr($_X,'123456aouie','aouie123456');
$_R=ereg_replace('__FILE__',"'".$_F."'",$_X);
echo "<pre>";//added
echo ($_R); //changed to echo
echo "</pre>";//added
$_R=0;
$_X=0;
?>

To finally get:

<pre>?>	<!-- FOOTER STARTS -->
	<div id="footer-out">
	<div id="footer" class="wrap">

		<div class="widget block">
			<?php dynamic_sidebar(2) ?>
		</div>
		<div class="widget block">
			<?php dynamic_sidebar(3) ?>
		</div>

		<div class="widget block last">
			<?php dynamic_sidebar(4) ?>
		</div>
	</div>
	</div>
	<!-- FOOTER ENDS -->
	<div id="copyright-out">
	<div id="copyright" class="wrap">
		<div class="col-left">

			<ul>
				<?php if (is_page()) { $highlight = "page_item"; } else
                                {$highlight = "page_item current_page_item"; } ?>
				<li class="<?php echo $highlight; ?> first">
                                <a href="<?php bloginfo('url'); ?>">Home</a></li>
				<?php wp_list_pages('sort_column=menu_order&depth=1&title_li='); ?>
			</ul>
		<p>© Copyright <a href="http://jeuxde-casino.com/"
                    title="jeux de casino">jeux de casino</a>. All Rights Reserved.</p>

		</div>
		<div class="col-right">
			<a href="#"><img src="<?php bloginfo('template_directory'); ?>
                       /images/img_top.gif" width="34" height="24" alt="Back on Top" /></a>
		</div>
	</div>
	</div>
<?php wp_footer(); ?>

<?php if ( get_option('woo_google_analytics') <> "" )
                { echo stripslashes(get_option('woo_google_analytics')); } ?>
</body>

</html></pre>

Fortunately, no malware code inside, although some advertisement exists. There are a few tools who do this encoding task, like PHP Free Encoder. There are no smart WordPress, just smart people , for good & for bad.

Goal: Not to be Acquired (assimilated)

Many of us are trying to grow and others have reached a point to mantain a low profile:

Matt Mullenweg and Toni Schneider both whom are in attendance for the LeWeb 10 conference were recently interviewed by TechCrunch reporter, Alexia Tsotsis. The interview doesn’t go into much detail but we do get a glimpse as to how things are going for Automattic as a company. WordPress.com is getting about 300 million unique pageviews a month from 30 million publishers that make up 10% of the websites on the web. In terms of revenue, Automattic is breaking even but as a company, they make a little under $1 million per month with all services combined. TechCrunch figures that this equates to $10 million a year. Perhaps it’s just me but if that is correct, that number seems pretty low considering how large WordPress.com is.

However, the best part of the interview comes down to the final question regarding any potential exit potential for the company. Their response: “Our goal’s not to be acquired“.

Fuente: Goal: Not To Be Acquired « Weblog Tools Collection

They grew too much, the temptation could be big…but some reasons are just scaping to me. Resistence ‘could be’ futile(?!).

PD: above picture from Top 15 Hot Babes That Would Kick Your Ass

What are you looking for? Statpress has an answer

The interesting part for a plugin like Statpress is the chance to play with the data stored to see how the traffic is flowing on your site.

For one side, you can explore what the visitors want and where they go. For other part, it allows to detect potential problems (like a incorrect title o find a way to enhance the post content/title) and allow us to think a correct way to create posts. It is SEO on the run.

How to do that

  1. Install  Statpress o or some variant that I previously commented. The sql query will be similar on thos plugins (although you will have to see any different scenarios)
  2. Create a WordPress Template with this code:
  3.          <?php
    /*
    Template name: visitors-search
    Template return visitors search based on Statpress plugin
    */
    ?>
    <?php get_header() ?>
    
    <div id="container">
    <div id="content">
    <?php
    //Query to return the most recent searches
    global $wpdb;
    $fecha=date('Ymd');
    $sql="Select search,urlrequested,date,time from wp_statpress where search<>'' order by timestamp desc limit 1000";
    
    $searches_result= $wpdb->get_results($sql, ARRAY_A);
    echo "<table>";
    $counter=1;
    foreach($searches_result as $search)
    {
    display_search($search,$counter);
    $counter++;
    }
    echo "</table>";
    
    function display_search($search,$counter)
    {
    echo "<tr>";
    echo "<td>$counter</td><td>" . $search['search'] . "</td><td>" . $search['urlrequested']. "</td><td>" . $search['date']. "</td><td>" . $search['time']. "</td>" ;
    echo "</tr>";
    }
    ?>
    
    </div><!-- #content -->
    </div><!-- #container -->
    
    <?php get_footer() ?>
  4. Add a page on your site using this template.
  5. Test the page.

Super simple. It will return last thousand searches from the visitors who reached your site.

The code can be improved a lot. Think this code as a proof of concept to study traffic on your site. Good luck.

Flock

Flock , the browser built around Gecko engine has suprising characteristics. It has a nice integration with social networks  (Facebook, YouTube, Twitter, Flickr, Blogger, Gmail, Yahoo Mail) , light and with many integrated tools.

Y was surprised when I found that it already has a integrated tool similar to Firebug right out of the box  (Ctrl-Shift-I).

It worth to take a look specially when Flock launched its 3.5 version. Cool.

WP to Twitter extension for qTranslate

In the previous post I commented the fact that WordPressready is using the qTranslate plugin to handle english and spanish languages. For the other hand the  WP to Twitter plugin sends messages in every post. Both plugins are very good indeed.

There is a small problem though. Wp to Twitter compose the message from the post title, which is ok except when the qTranslate plugin is installed. qTranslate uses a custom tag to separate languages inside the post title , for example:

<!--:en-->This is an post title<!--:--><!--:es-->Este es el título de una entrada<!--:-->

WP to Twitter is doing the best it can to interpreting the title, supressing the customized tags to finally get:

This is an post titleEste es el título de una entrada

what is pretty ugly.

It could be nice if there is a simple way to solve it , don’t you think? Some kind of filter/action to handle this situation…

Ok, i’m still working on it. For the moment, I know where to put some filters, but remember this is a work in progress.

For the moment I did some code modification to WP to Twitter code so it will send a twitter message for every language set in the blog. This modification will work only when a post is created/updated (it won’t work when a page is created or when I post from xmlrpc). As I said, is a work in progress.

Wp to Twitter: some details

This plugin solves every aspect of messaging to Twitter isolating every case: if you create a post, if you create a page, if you use xmlrpc, if you create a link, etc. etc.

jd_twit is a function to create a twit when a post is made. I modified the method to take in count the qTranslate format. So before the changes:

function jd_twit( $post_ID ) {
	$jd_tweet_this = get_post_meta( $post_ID, '_jd_tweet_this', TRUE);
	if ( $jd_tweet_this != "no" ) {
		$jd_post_info = jd_post_info( $post_ID );
	    $sentence = '';
		$customTweet = stripcslashes( trim( $_POST['_jd_twitter'] ) );
		if ( ( $jd_post_info['postStatus'] == 'publish' || $_POST['publish'] == 'Publish') && ($_POST['prev_status'] == 'draft' || $_POST['original_post_status'] == 'draft' || $_POST['prev_status'] == 'pending' || $_POST['original_post_status'] =='pending' || $_POST['original_post_status'] == 'auto-draft' ) ) {
		// publish new post
				if ( get_option( 'newpost-published-update' ) == '1' ) {
					$nptext = stripcslashes( get_option( 'newpost-published-text' ) );
					$newpost = true;
				}
		} else if ( (( $_POST['originalaction'] == "editpost" ) && ( ( $_POST['prev_status'] == 'publish' ) || ($_POST['original_post_status'] == 'publish') ) ) && $jd_post_info['postStatus'] == 'publish') {
				// if this is an old post and editing updates are enabled
				if ( get_option( 'oldpost-edited-update') == '1' ) {
				    $nptext = stripcslashes( get_option( 'oldpost-edited-text' ) );
					$oldpost = true;
				}
		}
		if ($newpost || $oldpost) {
			$sentence = ( $customTweet != "" ) ? $customTweet : $nptext;
			if ($jd_post_info['shortUrl'] != '') {
				$shrink = $jd_post_info['shortUrl'];
			} else {
				$shrink = jd_shorten_link( $jd_post_info['postLink'], $jd_post_info['postTitle'], $post_ID );
				store_url( $post_ID, $shrink );
			}
			$sentence = custom_shortcodes( $sentence, $post_ID );
			$sentence = jd_truncate_tweet( $sentence, $jd_post_info['postTitle'], $jd_post_info['blogTitle'], $jd_post_info['postExcerpt'], $shrink, $jd_post_info['category'], $jd_post_info['postDate'], $post_ID, $jd_post_info['authId'] );
		}

		if ( $sentence != '' ) {
			if ( get_option('limit_categories') == '0' || in_allowed_category( $jd_post_info['categoryIds'] ) ) {
				$sendToTwitter = ( get_option( 'x_jd_api_post_status' ) == '' )?jd_doTwitterAPIPost( $sentence ):jd_doUnknownAPIPost( $sentence, $jd_post_info['authId']  );
				if ( $sendToTwitter == false ) {
					update_post_meta( $post_ID,'_jd_wp_twitter',urldecode( $sentence ) );
					update_option( 'wp_twitter_failure','1' );
				}
			}
		}
	}
	return $post_ID;
}

and now:

function jd_twit( $post_ID ) {
	$jd_tweet_this = get_post_meta( $post_ID, '_jd_tweet_this', TRUE);
	if ( $jd_tweet_this != "no" ) {
		$jd_post_info = jd_post_info( $post_ID );
	    $sentence = '';
		$customTweet = stripcslashes( trim( $_POST['_jd_twitter'] ) );
		if ( ( $jd_post_info['postStatus'] == 'publish' || $_POST['publish'] == 'Publish') && ($_POST['prev_status'] == 'draft' || $_POST['original_post_status'] == 'draft' || $_POST['prev_status'] == 'pending' || $_POST['original_post_status'] =='pending' || $_POST['original_post_status'] == 'auto-draft' ) ) {
		// publish new post
				if ( get_option( 'newpost-published-update' ) == '1' ) {
					$nptext = stripcslashes( get_option( 'newpost-published-text' ) );
					$newpost = true;
				}
		} else if ( (( $_POST['originalaction'] == "editpost" ) && ( ( $_POST['prev_status'] == 'publish' ) || ($_POST['original_post_status'] == 'publish') ) ) && $jd_post_info['postStatus'] == 'publish') {
				// if this is an old post and editing updates are enabled
				if ( get_option( 'oldpost-edited-update') == '1' ) {
				    $nptext = stripcslashes( get_option( 'oldpost-edited-text' ) );
					$oldpost = true;
				}
		}
		if ($newpost || $oldpost) {
			$sentence = ( $customTweet != "" ) ? $customTweet : $nptext;
			if ($jd_post_info['shortUrl'] != '') {
				$shrink = $jd_post_info['shortUrl'];
			} else {
				$shrink = jd_shorten_link( $jd_post_info['postLink'], $jd_post_info['postTitle'], $post_ID );
				store_url( $post_ID, $shrink );
			}
			$sentence = custom_shortcodes( $sentence, $post_ID );
            $mybuffer= $sentence; //remember this data, we'll use it later					

            /* Begin Changes|Comienzo de Cambios**********************************/
            //pattern for qTranslate|Expresión regular para los patrones de qTranslate
            $regexp="/<!--:[a-z]+-->(.*)<!--:-->/U";
            //Return languages in the posttitle in array | Devolver todos los lenguajes en un array
            preg_match_all($regexp,$jd_post_info['postTitle'],$matches,PREG_PATTERN_ORDER);
            $counter=0;
            if (empty($matches[0])) //no matches maybe plugin deactivated| sin coincidencias tal vez plugin desactivado
            {
               //Proceed as usual | Proceder como es usual
                $sentence = jd_truncate_tweet( $sentence, $jd_post_info['postTitle'], $jd_post_info['blogTitle'], $jd_post_info['postExcerpt'], $shrink, $jd_post_info['category'], $jd_post_info['postDate'], $post_ID, $jd_post_info['authId'] );
        		if ( $sentence != '' ) {
        			if ( get_option('limit_categories') == '0' || in_allowed_category( $jd_post_info['categoryIds'] ) ) {
        				$sendToTwitter = ( get_option( 'x_jd_api_post_status' ) == '' )?jd_doTwitterAPIPost( $sentence ):jd_doUnknownAPIPost( $sentence, $jd_post_info['authId']  );
        				if ( $sendToTwitter == false ) {
        					update_post_meta( $post_ID,'_jd_wp_twitter',urldecode( $sentence ) );
        					update_option( 'wp_twitter_failure','1' );
        				}
        			}
        		}
            }
            else  //We have a match| Tenemos coincidencia
            {
                //Loop for all languages| Recorrer todos los lenguajes
                while ($counter<count($matches[0]))
                {
                    $sentence = $mybuffer;
                    $text=$matches[1][$counter];
                    $sentence = jd_truncate_tweet( $sentence, $text, $jd_post_info['blogTitle'], $jd_post_info['postExcerpt'], $shrink, $jd_post_info['category'], $jd_post_info['postDate'], $post_ID, $jd_post_info['authId'] );
            		if ( $sentence != '' ) {
            			if ( get_option('limit_categories') == '0' || in_allowed_category( $jd_post_info['categoryIds'] ) ) {
            				$sendToTwitter = ( get_option( 'x_jd_api_post_status' ) == '' )?jd_doTwitterAPIPost( $sentence ):jd_doUnknownAPIPost( $sentence, $jd_post_info['authId']  );
            				if ( $sendToTwitter == false ) {
            					update_post_meta( $post_ID,'_jd_wp_twitter',urldecode( $sentence ) );
            					update_option( 'wp_twitter_failure','1' );
            				}
            			}
            		}
                    $counter +=1;
                }
            }

	}
        /*      This is before|esto era antes
		if ( $sentence != '' ) {
			if ( get_option('limit_categories') == '0' || in_allowed_category( $jd_post_info['categoryIds'] ) ) {
				$sendToTwitter = ( get_option( 'x_jd_api_post_status' ) == '' )?jd_doTwitterAPIPost( $sentence ):jd_doUnknownAPIPost( $sentence, $jd_post_info['authId']  );
				if ( $sendToTwitter == false ) {
					update_post_meta( $post_ID,'_jd_wp_twitter',urldecode( $sentence ) );
					update_option( 'wp_twitter_failure','1' );
				}
			}
		}
        */
	}
	return $post_ID;
}

When i thought I was finishing, I realise that ‘something’ was prefiltering the custom qTranslate tags, deleting them.  So I didn’t finish yet… :(

After some revision, I found that jd_post_info function was doing the prefilter task. I had to deactivate this behavior and for the other to be sure that the text in every language was correctly processed (get rid of html tags). That is the mission of qTranslateStripTags.

Before:

function jd_post_info( $post_ID ) {
	$get_post_info = get_post( $post_ID );
	$values = array();
	// get post author
	$values['authId'] = $get_post_info->post_author;
		$postdate = $get_post_info->post_date;
		$dateformat = (get_option('jd_date_format')=='')?get_option('date_format'):get_option('jd_date_format');
		$thisdate = mysql2date( $dateformat,$postdate );
	$values['postDate'] = $thisdate;
	// get first category
		$category = null;
		$categories = get_the_category( $post_ID );
		if ( $categories > 0 ) {
			$category = $categories[0]->cat_name;
		}
		foreach ($categories AS $cat) {
			$category_ids[] = $cat->term_id;
		}
	$values['categoryIds'] = $category_ids;
	$values['category'] = $category;
		$excerpt_length = get_option( 'jd_post_excerpt' );
	$values['postExcerpt'] = ( trim( $get_post_info->post_excerpt ) == "" )?@mb_substr( strip_tags($get_post_info->post_content), 0, $excerpt_length ):@mb_substr( strip_tags($get_post_info->post_excerpt), 0, $excerpt_length );
	$thisposttitle =  stripcslashes( strip_tags( $get_post_info->post_title ) );
		if ($thisposttitle == "") {
			$thisposttitle =  stripcslashes( strip_tags( $_POST['title'] ) );
		}
	$values['postTitle'] = $thisposttitle;
	$values['postLink'] = external_or_permalink( $post_ID );
	$values['blogTitle'] = get_bloginfo( 'name' );
	$values['shortUrl'] = get_post_meta( $post_ID, '_wp_jd_clig', TRUE );
	$values['postStatus'] = $get_post_info->post_status;
	return $values;
}

After:

function jd_post_info( $post_ID ) {
	$get_post_info = get_post( $post_ID );
	$values = array();
	// get post author
	$values['authId'] = $get_post_info->post_author;
		$postdate = $get_post_info->post_date;
		$dateformat = (get_option('jd_date_format')=='')?get_option('date_format'):get_option('jd_date_format');
		$thisdate = mysql2date( $dateformat,$postdate );
	$values['postDate'] = $thisdate;
	// get first category
		$category = null;
		$categories = get_the_category( $post_ID );
		if ( $categories > 0 ) {
			$category = $categories[0]->cat_name;
		}
		foreach ($categories AS $cat) {
			$category_ids[] = $cat->term_id;
		}
	$values['categoryIds'] = $category_ids;
	$values['category'] = $category;
		$excerpt_length = get_option( 'jd_post_excerpt' );
	$values['postExcerpt'] = ( trim( $get_post_info->post_excerpt ) == "" )?@mb_substr( strip_tags($get_post_info->post_content), 0, $excerpt_length ):@mb_substr( strip_tags($get_post_info->post_excerpt), 0, $excerpt_length );
/*
	$thisposttitle =  stripcslashes( strip_tags( $get_post_info->post_title ) );
		if ($thisposttitle == "") {
			$thisposttitle =  stripcslashes( strip_tags( $_POST['title'] ) );
		}
*/
	$thisposttitle =  qTranslateStripTags( $get_post_info->post_title);
		if ($thisposttitle == "") {
			$thisposttitle =  qTranslateStripTags( $_POST['title'] ) ;
		}

	$values['postTitle'] = $thisposttitle;
	$values['postLink'] = external_or_permalink( $post_ID );
	$values['blogTitle'] = get_bloginfo( 'name' );
	$values['shortUrl'] = get_post_meta( $post_ID, '_wp_jd_clig', TRUE );
	$values['postStatus'] = $get_post_info->post_status;
	return $values;
}

function qTranslateStripTags($twit)
{
     $regexp='/(<!--:[a-z]+-->)(.*)(<!--:-->)/U';
    preg_match_all($regexp,$twit,$matches,PREG_PATTERN_ORDER);
    $counter=0;
    $result="";
    if (empty($matches[0]))
    {
       return $twit;
    }
    while ($counter<count($matches[0]))
    {
           $result .= $matches[1][$counter] .   stripcslashes( strip_tags( $matches[2][$counter] ) ) . $matches[3][$counter];
           //$result .= "%!%" .   stripcslashes( strip_tags( $matches[2][$counter] ) ) . "%¡%";
           $counter++;
    }
    return $result;
}

This last task was very boring, specially because I do little PHP programming.

If you have interest in the code you can download the wp-to-twitter.php(zip) with the changes. This file replace the original in the wp to twitter plugin. (2.2.3 version)

Poor man’s message broadcast

Problem:

A client organizes a long duration sport event (8-10 hours or more) and he wishes to have a page where he sends the news while the event goes on. The page has to auto-refresh itself.

A good example could be IronmanLive.com when a event is running.

¿Do you have a simple solution? I found two ideas about it:

  1. Live blogging for wordpress. Outstanding plugin, also allowing Meteor technology. It can work alone too.
  2. Other solution: A page using  javascript, and then to use Twitter as broadcast system.

This solution could be used in every site (different than WordPress). I will use  a small javascript library with jQuery to get Twitter messages from an account.

Integrating the above solution with WordPress.

This is another quick & dirty solution to integrate to a WordPress page:

Broadcast demo: here

Instructions:

1-Download the template page

2-Install the template over the WordPress theme

3-Change the page code and select the account you desire. You can select more than one account.

3-Create a simple page on WordPress. Select the new template (Broadcast-demo)

4-Test the page.

By the way, the qTranslate – Twitter combination sucks. All the messages merge together in one twit. This is something what I will work in the next posts.

Language
Twitter