<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0"><channel><title>Anant Garg | on web development</title> <link>http://anantgarg.com</link> <description>on web development</description> <lastBuildDate>Mon, 17 Jun 2013 08:30:34 +0000</lastBuildDate> <language>en-US</language> <sy:updatePeriod>hourly</sy:updatePeriod> <sy:updateFrequency>1</sy:updateFrequency> <generator>http://wordpress.org/?v=3.5.1</generator> <atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.feedburner.com/anantgarg" /><feedburner:info uri="anantgarg" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><feedburner:emailServiceId>anantgarg</feedburner:emailServiceId><feedburner:feedburnerHostname>http://feedburner.google.com</feedburner:feedburnerHostname><item><title>Amazon Beanstalk PHP hosting &amp; database versioning for SaaS</title><link>http://feedproxy.google.com/~r/anantgarg/~3/15oxg1iSfR0/</link> <comments>http://anantgarg.com/2013/06/17/amazon-beanstalk-php-hosting-database-versioning-for-saas/#comments</comments> <pubDate>Mon, 17 Jun 2013 08:30:34 +0000</pubDate> <dc:creator>Anant Garg</dc:creator> <category><![CDATA[PHP]]></category> <category><![CDATA[SaaS]]></category><guid isPermaLink="false">http://anantgarg.com/?p=510</guid> <description><![CDATA[This tutorial will help you setup database versioning and use Amazon Beanstalk, RDS &#038; Route 53 to host your app.]]></description> <content:encoded><![CDATA[<p>This is part two of 3 posts that I intend to write about getting a SaaS app + site built from scratch. If you haven&#8217;t already, then you should read part 1 about <a
href="http://anantgarg.com/2013/06/10/build-a-php-saas-app-from-scratch/">creating a PHP SaaS app</a>. In this part we will talk about 2 things:</p><p>1. Database versioning<br
/> 2. Hosting, Database &amp; DNS</p><hr
/><h3>Part 1: Database versioning</h3><p>In the first part, we discuss about creating separate databases for every client. Now the problem is that these databases are empty and we need to fill them with some basic data + schema. The problem of creating this in the site code, is when you update your schema, you will have to manually update all the databases. This can be extremely tedious and difficult to achieve.</p><p>Thus to solve this problem, we assume our DB is empty. Then we create our version 1 of the schema and save it as db/1.sql. This will be our base i.e. when the user first visits the client&#8217;s site, it will run this SQL to create the require schema. Next version will be 2.sql (which may add/drop tables etc.) and so on.</p><p>In our app configuration file, we will add a variable (somewhere at the top) and set it to the latest schema version.</p><pre class="brush: php; title: ; notranslate">
$currentdbversion = 5;
</pre><p>Then after the DB connection, we will add the following code:</p><pre class="brush: php; title: ; notranslate">
...

mysql_selectdb(DB_NAME,$dbh);

// Success! Now we can continue to use the app as-is!

$sql = (&quot;select * from cometchat_settings where name = 'dbversion'&quot;);
$appsettings = mysql_fetch_row($sql);

$dbversion = 0;

if (!empty($appsettings['value'])) {
   $dbversion = $appsettings['value'];
}

while ($dbversion &lt; $currentdbversion) {
    $dbversion++;
    if (file_exists(dirname(__FILE__).'/db/'.($dbversion).'.php')) {
        include_once(dirname(__FILE__).'/db/'.($dbversion).'.php');
    }
}

</pre><p>In 1.sql, you can add your SQL queries, and add the bottom you will add:</p><pre class="brush: php; title: ; notranslate">
&lt;?php

$sql = (&quot;CREATE TABLE  `app_settings` ( `name` varchar(255) NOT NULL,  `value` text NOT NULL, PRIMARY KEY  USING BTREE (`name`)) ENGINE=InnoDB DEFAULT CHARSET=utf8;&quot;);
$query = mysql_query($sql);

// Add other SQL queries

$sql = (&quot;REPLACE INTO `app_settings` (name,value) values ('dbversion','1')&quot;);
$query = mysql_query($sql);

</pre><p>In 2.sql and all other SQL files, it will be:</p><pre class="brush: php; title: ; notranslate">
&lt;?php

// Add other SQL queries

$sql = (&quot;REPLACE INTO `app_settings` (name,value) values ('dbversion','2')&quot;);
$query = mysql_query($sql);

</pre><p>So the workflow is as follows:</p><p>1. The site code only creates the DB. It does not populate the DB with any data/schema.<br
/> 2. When the client visits the app for the first time, $dbversion is 0, so 1.sql is executed.<br
/> 3. When you make changes to your SQL DB, you simply update $currentdbversion &#038; add the $currentdbversion.sql file to the db folder<br
/> 4. When the app is run the next time, it will check if the SQL dbversion is lower than $currentdbversion; if yes, it will run all the SQL files till it reaches the latest version.</p><hr
/><h3>Part 2: Hosting on Elastic Beanstalk</h3><p>Hosting on Amazon&#8217;s Elastic Beanstalk is fairly simple.</p><p><strong>Step 1</strong></p><p>Signup for Amazon AWS and then go to &#8220;Elastic Beanstalk&#8221; and create a new application. Zip all your PHP code (for your app) and then attach it while creating:</p> <figure><a
href="http://i2.wp.com/anantgarg.com/wp-content/uploads/2013/06/hosting1.png"><img
src="http://i1.wp.com/anantgarg.com/wp-content/uploads/2013/06/hosting1.png?resize=617%2C445" alt="Amazon Elastic Beanstalk - Create new application" class="alignnone size-full wp-image-537" data-recalc-dims="1" /></a></figure><p><strong>Step 2</strong></p><p>Enter a URL for your app; I have used &#8220;mydummyapp&#8221;.</p> <figure><a
href="http://i0.wp.com/anantgarg.com/wp-content/uploads/2013/06/host2.png"><img
src="http://i0.wp.com/anantgarg.com/wp-content/uploads/2013/06/host2.png?resize=618%2C429" alt="Amazon Elastic Beanstalk - Enter a URL" class="alignnone size-full wp-image-535" data-recalc-dims="1" /></a></figure><p><strong>Step 3</strong></p><p>t1.micro is sufficient to begin with and will keep your costs low. Select it and then proceed with creation.</p> <figure><a
href="http://i0.wp.com/anantgarg.com/wp-content/uploads/2013/06/host3.png"><img
src="http://i0.wp.com/anantgarg.com/wp-content/uploads/2013/06/host3.png?resize=617%2C459" alt="Amazon Elastic Beanstalk - Choose server size" class="alignnone size-full wp-image-536" data-recalc-dims="1" /></a></figure><p><strong>Step 4</strong></p><p>Go to &#8220;RDS&#8221;, &#8220;Launch new DB instance&#8221; and then create a new instance.</p> <figure><a
href="http://i2.wp.com/anantgarg.com/wp-content/uploads/2013/06/db1.png"><img
src="http://i0.wp.com/anantgarg.com/wp-content/uploads/2013/06/db1.png?resize=655%2C545" alt="Amazon RDS - Create new instance" class="alignnone size-full wp-image-539" data-recalc-dims="1" /></a></figure><p><strong>Step 5</strong></p><p>Create a DB for your site (which will be used to store all clients info). The client specific DBs will be created on the fly (in the same instance).</p> <figure><a
href="http://i1.wp.com/anantgarg.com/wp-content/uploads/2013/06/db2.png"><img
src="http://i0.wp.com/anantgarg.com/wp-content/uploads/2013/06/db2.png?resize=657%2C498" alt="Amazon RDS - Create Site DB" class="alignnone size-full wp-image-540" data-recalc-dims="1" /></a></figure><p>Once completed, add the connection details to your app and test if the connection is working fine. If you are unable to connect, then edit the &#8220;default&#8221; Security Group and add &#8220;CIDR/IP: 0.0.0.0/0&#8243;. This will allow you to connect to the instance from any IP. Later, you can modify this to only certain IPs.</p><p><strong>Step 6</strong></p><p>Go to &#8220;Route 53&#8243; and &#8220;Create Hosted Zone&#8221; and enter your domain URL. Then double click on the listing and then &#8220;Create Record Set&#8221;.</p> <figure><a
href="http://i0.wp.com/anantgarg.com/wp-content/uploads/2013/06/dns1.png"><img
src="http://i1.wp.com/anantgarg.com/wp-content/uploads/2013/06/dns1.png?resize=773%2C401" alt="Amazon Route 53 - Create new Record Set" class="alignnone size-full wp-image-541" data-recalc-dims="1" /></a></figure><p>Once done, get the server IP for your site hosting (from Asmallorange/Rackspace/site-host) and two <strong>A</strong> records:</p> <figure><a
href="http://i0.wp.com/anantgarg.com/wp-content/uploads/2013/06/dns2.png"><img
src="http://i1.wp.com/anantgarg.com/wp-content/uploads/2013/06/dns2.png?resize=616%2C89" alt="Amazon Route 53" class="alignnone size-full wp-image-542" data-recalc-dims="1" /></a></figure><p>Finally, update your nameservers to point to those mentioned by Amazon.</p><hr
/><p>Once this is done, you can visit http://mydummyapp.elasticbeanstalk.com and check your app. You can use any third-party host like <a
href="http://www.asmallorange.com" target="_blank">Asmallorange</a> or <a
href="http://www.rackspace.com" target="_blank">RackSpace</a> to host your SaaS site. You do not need to use Amazon Beanstalk to host your SaaS site.</p><p>That&#8217;s all folks! Now you have successfully setup your server (Beanstalk), your DBs (RDS) and your DNS (Route 53). You should be now ready to start testing your app along with your site.</p><hr
/><h3>Where do we go from here?</h3><p>We have achieved a lot using this tutorial (and <a
href="http://anantgarg.com/2013/06/10/build-a-php-saas-app-from-scratch/">part 1</a>). We have setup our app, site and have configured Amazon&#8217;s services to host our app. In the next part, we will talk about scaling and other tips &amp; tricks.</p><hr
/><h3>Discuss, Share &amp; Subscribe</h3><p>Do let me know your suggestions on how I can improve this tutorial. If you like what you are reading, then please help spread the word by re-tweeting, blogging and sharing this tutorial. Thank you.</p><p>To get a notification when the next part is ready, please subscribe using the form at the bottom of this page.</p> <img src="http://feeds.feedburner.com/~r/anantgarg/~4/15oxg1iSfR0" height="1" width="1"/>]]></content:encoded> <wfw:commentRss>http://anantgarg.com/2013/06/17/amazon-beanstalk-php-hosting-database-versioning-for-saas/feed/</wfw:commentRss> <slash:comments>0</slash:comments> <feedburner:origLink>http://anantgarg.com/2013/06/17/amazon-beanstalk-php-hosting-database-versioning-for-saas/</feedburner:origLink></item> <item><title>Build a PHP SaaS app from scratch</title><link>http://feedproxy.google.com/~r/anantgarg/~3/wE6ucq3lyuo/</link> <comments>http://anantgarg.com/2013/06/10/build-a-php-saas-app-from-scratch/#comments</comments> <pubDate>Mon, 10 Jun 2013 14:47:31 +0000</pubDate> <dc:creator>Anant Garg</dc:creator> <category><![CDATA[PHP]]></category> <category><![CDATA[SaaS]]></category><guid isPermaLink="false">http://anantgarg.com/?p=384</guid> <description><![CDATA[This tutorial will teach you how to create your first PHP SaaS app from scratch. If your app is ready, then you can learn how to convert it to a SaaS application.]]></description> <content:encoded><![CDATA[<p><strong>This is the first of 3 posts which will talk about launching your own SaaS product (or converting your existing single tenant app to a SaaS app). We will cover various aspects from developing the app to building a SaaS site (to enable your clients to signup for it)</p> <figure><img
src="http://i2.wp.com/anantgarg.com/wp-content/uploads/2013/06/myimage-1.png?resize=630%2C269" alt="Typical SaaS Application" title="Typical SaaS Application" class="alignnone size-full wp-image-395" data-recalc-dims="1" /></figure><h3>We will be looking at&#8230;</h3><p><strong>1. Creating your app</strong></p><p>The app is the product that you are going to sell. If you already have this app ready, you will only have to modify it so that it is compatible for multiple clients. The app is the front-end for your client&#8217;s end-users.</p><p><strong>2. Creating your SaaS site</strong></p><p>The site is the front-end for your clients. They will be using your site to signup for the app.</p><p><strong>3. Hosting et al.</strong></p><p>We will talk about how to host your app + site, how to ensure scaling, setting up DNS &amp; SSL, database versioning etc.</p><hr
/><h3>Part 1: Create your app</h3><p>Before you begin with selling your product, you need to have a product. Let us first try and understand the SaaS architecture. Every app consists of two parts: code &#038; data. Now, every client has end-users; the data is shared between these end-users and (mostly) not with other clients&#8217; end-users. So every set of end-users (of each client) should access a different set of data. In most cases, the code used by all the clients can be the same.</p><p>Now there are various ways in which a SaaS app can be designed:</p><hr
/><p><small></p><p><strong>Option 1 &#8211; Single database, single code-base</strong></p><p>Use a single database and store all client data in a single database. You will have to add a field like &#8220;clientid&#8221; in all your tables and modify all your SQL queries to select the correct &#8220;clientid&#8221;. If you already have a single tenant app, then heavy modification is required in the code.</p><p><strong>Advantages:</strong> Easy to manage database & updates; no duplication.<br
/> <strong>Disadvantages:</strong> If you miss out a &#8220;clientid&#8221; in the SQL query, it may result in issues with another client.</p><p><strong>Option 2 &#8211; Multiple database, single code-base</strong></p><p>Create a separate database for every client. No modification is required to your existing single-tenant codebase. You will only have to modify the configuration file to select a database depending on the client.</p><p><strong>Advantages:</strong> Very little codebase modification, easy to ensure client data does not get mixed.<br
/> <strong>Disadvantages:</strong> Multiple databases to manage so in the event of DB schema updates, you will have to update each and every database.</p><p><strong>Option 3 &#8211; Multiple database, multiple code-base</strong></p><p>Create a separate database &#038; duplicate the complete code for every client. No modification required at all.</p><p><strong>Advantages:</strong> No codebase modification, easy to ensure client data does not get mixed, easy to perform client specific modifications.<br
/> <strong>Disadvantages:</strong> Multiple databases/code to manage so in the event of DB schema/code updates, you will have to manually update for each client.</p><p></small></p><hr
/><p>There are a <a
href="http://stackoverflow.com/questions/13325383/multi-tenant-php-saas-seperate-dbs-for-each-client-or-group-them" target="_blank">number</a> <a
href="http://programmers.stackexchange.com/questions/141261/multi-tenancy-single-database-vs-multiple-database" target="_blank">of</a> <a
href="http://stackoverflow.com/questions/255616/should-i-use-a-single-or-multiple-database-setup-for-a-multi-client-application" target="_blank">articles</a> <a
href="http://stackoverflow.com/questions/69128/saas-database-design-multiple-databases-split" target="_blank">discussing</a> which approach is the best but option 2 helps us develop a scalable SaaS solution in the quickest possible way. So we will stick to option 2 for this tutorial.</p><p>If you already have an app then you will only have to modify the configuration file as below. If you do not, then make sure your configuration file looks something like below and the rest of the app can be however you like it.</p><p>Let us begin with the PHP part. You should be ideally using a framework like <a
href="http://symfony.com/">Symfony</a> or <a
href="http://www.zend.com/">Zend</a> to create your app. To simplify, I am only going to be talking about config.php:</p><p>This is how a simple standard config.php file looks: (usually DB connection is not part of this, but for a clearer explanation, I have added it)</p><pre class="brush: php; title: ; notranslate">
&lt;?php

define('DB_SERVER','localhost');
define('DB_PORT','3306');
define('DB_USERNAME','root');
define('DB_PASSWORD','password');
define('DB_NAME','dummyapp');

$dbh = mysql_connect(DB_SERVER.':'.DB_PORT,DB_USERNAME,DB_PASSWORD);
if (!$dbh) {
	echo &quot;Oops! Something went horribly wrong.&quot;;
	exit();
}
mysql_selectdb(DB_NAME,$dbh);

</pre><p>In the above code, when the user visits a PHP page, it calls config.php, which stores the database configuration &#038; connects the database. Then the remaining PHP files, calling this configuration file, can connect to the DB.</p><p>The problem in the above code is that it is built for a single client (with a single set of end-users). We need to modify this code so that for each client, a different database is used. So depending on how the app is called (i.e. depending on the URL), we will connect to a different database. If you look at SaaS apps online, you will find that they assign a unique sub-domain to every client. This technique will come in handy.</p><p>So if your site is mydummyapp.com, then when a client registers, he will be assigned a URL like anantgarg.mydummyapp.com, yourname.mydummyapp.com etc. Now, all end-users of the client will connect to the same sub-domain.</p><p>To make our app SaaS friendly we need to check the sub-domain and then accordingly connect to a database. We will perform the following checks:<br
/> <small><br
/> 1. Let the end-user visit the app using *.mydummyapp.com (DNS configuration in next post)<br
/> 2. Check a central table if the sub-domain is registered; if yes, to whom? Then fetch the DB details<br
/> 3. Connect the end-user to the client&#8217;s DB<br
/> </small></p><p>Here is how a modified config.php will look:</p><pre class="brush: php; title: ; notranslate">
&lt;?php

$data = explode('.',$_SERVER['SERVER_NAME']); // Get the sub-domain here

// Add some sanitization for $data

if (!empty($data[0])) {
    $subdomain = $data[0]; // The * of *.mydummyapp.com will be now stored in $subdomain
}

// Contact a central database (which stores information about all clients

define('SITE_DB_SERVER','localhost');
define('SITE_DB_PORT','3306');
define('SITE_DB_USERNAME','root');
define('SITE_DB_PASSWORD','password');
define('SITE_DB_NAME','dummysite');

$sitedbh = mysql_connect(SITE_DB_SERVER.':'.SITE_DB_PORT,SITE_DB_USERNAME,SITE_DB_PASSWORD);

// Get client's DB details (make sure $subdomain is already sanitized)

$sitemysql = mysql_query('select id,dbusername,dbpassword from clients where subdomain = '.$subdomain.');
$appdata = mysql_fetch_row($sitemysql);

// You can add caching code here to skip the above DB call

if (empty($appdata['id'])) {
    // If the client does not exist i.e. the end-user types the wrong sub-domain

    echo &quot;Oops! Sorry, we are unable to find you! Please email us at support@mydummyapp.com&quot;;
    exit();
}

define('DB_SERVER','localhost');
define('DB_PORT','3306');
define('DB_USERNAME',$appdata['dbusername']);
define('DB_PASSWORD',$appdata['dbpassword']);
define('DB_NAME','dummyapp_'.$appdata['id']);

$dbh = mysql_connect(DB_SERVER.':'.DB_PORT,DB_USERNAME,DB_PASSWORD);
if (!$dbh) { exit(); }

mysql_selectdb(DB_NAME,$dbh);

// Success! Now we can continue to use the app as-is!

</pre><p>If you are modifying your existing app, then you only need to modify your configuration file and make it similar to the above. The rest of the app should not need any modification (as long as you are not saving any files). If you have files, then you will have to modify the file upload code and add a folder e.g. /uploads/$subdomain/$filename.</p><p>If you are testing on localhost, you can simply add something like the following to your hosts file:</p><pre class="brush: plain; title: ; notranslate">
127.0.0.1      test.mydummyapp.com
127.0.0.1      test2.mydummyapp.com
</pre><hr
/><h3>Part 2: Create your SaaS site</h3><p>I assume you can create a simple SaaS site with user registration. On your registration page, you will need to have something like:</p><pre class="brush: plain; title: ; notranslate">
Username: _ _ _ _ _ _ _ _
Password: _ _ _ _ _ _ _ _
Subdomain: _ _ _ _ _ _ _ _ (.mydummyapp.com): 
</pre><p>You will need to create a common DB (e.g. dummysite) with a table called &#8220;clients&#8221;. In your real site, you will also need other tables like &#8220;subscriptions&#8221; which will hold payment information.</p><pre class="brush: sql; title: ; notranslate">
create table clients (
  id int default 0 auto_increment,
  username varchar(50),
  password varchar(255),
  subdomain varchar(50),
  dbusername varchar(50),
  dbpassword varchar(50)
)
</pre><p>Now for every client who signs up on your site, you will have to add an entry into the above table. You will also have to create random dbusername &#038; dbpassword. And finally, you need to create a new DB for this client.</p><p>The PHP code for that will be:</p><pre class="brush: php; title: ; notranslate">
// Connect to database instance (which stores all app DBs)
$dbh = mysql_connect(DB_SERVER.':'.DB_PORT,DB_MASTERUSERNAME,DB_MASTERPASSWORD);

// Create DB ($id will have value of client's inserted ID)
mysql_query(&quot;CREATE DATABASE mydummyapp_&quot;.$id.&quot;&quot;,$dbh) or die(mysql_error());

// Create user and grant all privileges for the above DB
mysql_query(&quot;GRANT ALL ON mydummyapp_&quot;.$id.&quot;.* to  &quot;.$dbusername.&quot; identified by '&quot;.$dbpassword.&quot;'&quot;,$dbh) or die(mysql_error());
</pre><p>Once this is done, you should be able to put all the pieces together and get your first SaaS app up and running.</p><hr
/><h3>Part 3: Putting it all together</h3><p>The site structure will be as follows-</p><p>www.mydummyapp.com: SaaS site (where the client registers)<br
/> *.mydummyapp.com: SaaS app (catch-all points to the actual app)</p><p>The workflow is as follows:</p><p>1. The client visits www.mydummyapp.com<br
/> 2. The client registers on the site<br
/> 3. The site creates the a DB (and user) for the client (and adds an entry in the site&#8217;s DB)<br
/> 4. The client then uses the new URL (i.e. clientsubdomain.mydummyapp.com) for himself &#038; his end-users<br
/> 5. The end-users directly visit clientsubdomain.mydummyapp.com which is basically the app using the client&#8217;s DB</p><hr
/><h3>Where do we go from here?</h3><p>We have achieved quite a bit using this tutorial. We have setup our app and site and can now allow clients to register and use the app. In the next parts will we discuss about managing the databases, setting up DNS, handling hosting, scaling and other tips &amp; tricks.</p><hr
/><h3>Discuss, Share &amp; Subscribe</h3><p>Do let me know your suggestions on how I can improve this tutorial. If you like what you are reading, then please help spread the word by re-tweeting, blogging and sharing this tutorial. Thank you.</p><p>To get a notification when the next part is ready, please subscribe using the form at the bottom of this page.</p> <img src="http://feeds.feedburner.com/~r/anantgarg/~4/wE6ucq3lyuo" height="1" width="1"/>]]></content:encoded> <wfw:commentRss>http://anantgarg.com/2013/06/10/build-a-php-saas-app-from-scratch/feed/</wfw:commentRss> <slash:comments>2</slash:comments> <feedburner:origLink>http://anantgarg.com/2013/06/10/build-a-php-saas-app-from-scratch/</feedburner:origLink></item> <item><title>Busting the cookies and privacy myth</title><link>http://feedproxy.google.com/~r/anantgarg/~3/0nOULh2Ee0g/</link> <comments>http://anantgarg.com/2012/02/18/busting-the-cookies-and-privacy-myth/#comments</comments> <pubDate>Sat, 18 Feb 2012 10:10:45 +0000</pubDate> <dc:creator>Anant Garg</dc:creator> <category><![CDATA[News]]></category><guid isPermaLink="false">http://anantgarg.com/?p=345</guid> <description><![CDATA[A lot of misinformation has been floating around since the Wall Street Journal article was published. Here is a simplified explanation of what really happened and how this affects you.]]></description> <content:encoded><![CDATA[<p>I am going to try and make today&#8217;s post as simple as possible. Note some explanations maybe a little too simple for my technical audience.</p><p>I was referenced in the WSJ article- <a
href="http://j.mp/ag-wsj" target="_blank">Google&#8217;s iPhone tracking</a> regarding my <a
href="http://anantgarg.com/2010/02/18/cross-domain-cookies-in-safari/">cross-domain cookies</a> post.</p><p>For my technical audience, here is a <a
href="http://webpolicy.org/2012/02/17/safari-trackers/" target="_blank">detailed version of how my code was actually used</a>.</p><p><strong>So let us begin from scratch&#8230; what are cookies?</strong></p><p>Cookies (data on your computer used to identify you) are stored whenever you visit a site. Usually, this data is used to keep you logged into a site or to figure out if you are a repeat visitor.</p><p>This is fine and in-order for the web to function in it&#8217;s current state, in many ways, essential.</p> <figure><img
src="http://i0.wp.com/anantgarg.com/wp-content/uploads/2012/02/1.jpg?resize=604%2C234" alt="Site's store cookies" title="Site's store cookies" class="alignnone size-full wp-image-350" data-recalc-dims="1" /></figure><p>In the above diagram, when you visit siteA.com, a cookie is set for siteA.com on your computer.</p><p>When you visit siteB.com, a cookie is set for siteB.com on your computer.</p><p>But siteB.com, <strong>CANNOT</strong> read the data from the cookies set in siteA.com and vice-versa. Thus siteB.com does not know if you ever visited siteA.com or what you did there.</p><p><strong>Let&#8217;s introduce the ad-companies</strong></p><p>Ad-companies started displaying ads on sites depending on the content of the page. So, if you are a webmaster, you add a line of JavaScript code and a neat looking advertisement is displayed on your site page.</p> <figure><img
src="http://i1.wp.com/anantgarg.com/wp-content/uploads/2012/02/2.jpg?resize=224%2C224" alt="Ads are displayed" title="Ads are displayed" class="alignnone size-full wp-image-353" data-recalc-dims="1" /></figure><p>But the information on the page is not enough to generate targeted ads. So the ad-companies wanted to know where you&#8217;ve been before.</p><p>Now here is where the privacy issue unfolds.</p><p><strong>Tracking your moves</strong></p><p>Inorder to track you, as you moved from one site to another (both displaying sites ads from the same ad-company), the companies need to store cookies.</p> <figure><img
src="http://i1.wp.com/anantgarg.com/wp-content/uploads/2012/02/3.jpg?resize=603%2C231" alt="Cookies of one site cannot be accessed by another" title="Cookies of one site cannot be accessed by another" class="alignnone size-full wp-image-354" data-recalc-dims="1" /></figure><p><span
style="color:#000">The problem is that cookies set on one site CANNOT be read by another site.</span> In other words, cookies set on siteA.com cannot be read by siteB.com.</p><p>So the work-around used is to store a third-party cookie (i.e. a cross-domain cookie). When a user visits siteA.com, a cookie is not only set for siteA.com but also for a <strong>common domain</strong> which is accessed by all sites that use the ad-company&#8217;s code.</p> <figure><img
src="http://i2.wp.com/anantgarg.com/wp-content/uploads/2012/02/4.jpg?resize=599%2C298" alt="siteA.com and siteB.com set cookies for their domain as well as for adsite.com" title="siteA.com and siteB.com set cookies for their domain as well as for adsite.com" class="alignnone size-full wp-image-355" data-recalc-dims="1" /></figure><p>Thus, siteA.com, siteB.com and all other sites displaying ads from adsite.com, store and access the same third-party cookie.</p><p>So when you visit siteA.com, a third-party cookie is created on adsite.com. When you visit siteB.com, the <strong>same</strong> cookie is read.</p> <figure><img
src="http://i2.wp.com/anantgarg.com/wp-content/uploads/2012/02/5.jpg?resize=522%2C492" alt="siteA.com and siteB.com access their own site cookies and the cookies of adsite.com" title="siteA.com and siteB.com access their own site cookies and the cookies of adsite.com" class="alignnone size-full wp-image-366" data-recalc-dims="1" /></figure><p>Thus adsite.com now knows that you have been to siteA.com before going to siteB.com. On the basis of this knowledge, a targeted ad can be shown. For an ad-company which provides ads to only two sites, this is not very useful. But imagine if you have 70% of the sites using your code.</p><p>Then, nearly most of the times, the same cookie can be read and your movements can be tracked as you browse through the internet. So now, the ad-company knows exact what kind of sites you are visiting and serve ads accordingly.</p><p><strong>But why does the WSJ article only talk about Safari?</strong></p><p>Safari, by default, <strong>does not allow third-party cookies</strong> to be created.</p><p><em>However, most major browsers- Firefox, Internet Explorer (using the correct header tags), Chrome etc. do ALLOW setting third-party cookies.</em></p><p><strong>Where does your technique come in?</strong></p><p>The technique <a
href="http://anantgarg.com/2010/02/18/cross-domain-cookies-in-safari/">I posted</a> two years ago, allows setting third-party cookies for Safari as well. This helps in offering a consistent user experience irrespective of the browser used.</p><p>Note that other browsers already allow creation of third-party cookies. So Safari is the only exception.</p><p><strong>How was this discovered?</strong></p><p>A Stanford researcher, <a
href="http://stanford.edu/~jmayer" target="_blank">Jonathan Mayer</a> found that many large companies like Google, MIG, PointRoll etc. used a variation of my technique to circumvent Safari&#8217;s policy.</p><p>Infact, Facebook&#8217;s official <a
href="https://developers.facebook.com/docs/best-practices/" target="_blank">developer best practices</a> article linked to my post (the page has since been removed).</p><p>You must remember that this issue affects Safari users ONLY.</p><p>Other browsers already allow third-party cookies and thus they CAN track you <em>as they do NOT have any such policy</em>.</p><p><strong>How do I protect myself? Should I disable cookies?</strong></p><p>NO. Do not disable cookies altogether. This will cause most of your sites (which have a login system) to stop working.</p><p>Third-party cookies also have a number of other uses like logging in users to third-party sites and are NOT always used for tracking. They cannot steal your data (a myth noted by some users in comments).</p><p>If you want, you can disable third-party cookies in browsers. Dennis O&#8217;Reilly has written a guide on <a
href="http://howto.cnet.com/8301-11310_39-20042703-285/disable-third-party-cookies-in-ie-firefox-and-google-chrome/" target="_blank">how to disable third-party cookies</a> for major browsers.</p><p>You may also want to use incognito/private browsing modes when using a public PC.</p><p><strong>Questions/Suggestions?</strong></p><p>If you have any questions about how this affects you or need further explanation, feel free to post a comment or contact me.</p> <img src="http://feeds.feedburner.com/~r/anantgarg/~4/0nOULh2Ee0g" height="1" width="1"/>]]></content:encoded> <wfw:commentRss>http://anantgarg.com/2012/02/18/busting-the-cookies-and-privacy-myth/feed/</wfw:commentRss> <slash:comments>8</slash:comments> <feedburner:origLink>http://anantgarg.com/2012/02/18/busting-the-cookies-and-privacy-myth/</feedburner:origLink></item> <item><title>Exploit Scanner</title><link>http://feedproxy.google.com/~r/anantgarg/~3/XY54Grbr6xQ/</link> <comments>http://anantgarg.com/2010/09/15/exploit-scanner/#comments</comments> <pubDate>Wed, 15 Sep 2010 07:26:16 +0000</pubDate> <dc:creator>Anant Garg</dc:creator> <category><![CDATA[PHP]]></category> <category><![CDATA[security]]></category><guid isPermaLink="false">http://anantgarg.com/?p=338</guid> <description><![CDATA[Exploit Scanner is a PHP script which checks for exploits in software like Joomla, Drupal, WordPress, phpBB, vBulletin etc.]]></description> <content:encoded><![CDATA[<p><strong>The Problem</strong></p><p>As your site grows, so do your chances of getting hacked. Most of these hacks are due to known exploits in existing open/closed source software. One of the biggest worries is a user getting full access to your site using a simple PHP upload.</p><p><strong>So what&#8217;s the solution?</strong></p><p>This is no silver bullet but if a user uploads any malicious files to your server, you should be able to quickly check what files have been modified/added.</p><p><strong>PHP Exploit Scanner</strong></p><p>The Exploit Scanner is a single PHP file which generates MD5 hash for all files of a particular software and then allows you to compare that with software you think has been modified.</p><p><strong>Sample Usage</strong></p><p>Lets say you want to check for any exploits in your WordPress installation</p><p>1. Download a fresh copy of WordPress from the official site (make sure you download correct version)<br
/> 2. Upload WordPress to your server/localhost<br
/> 3. Run <em>exploitscanner.php?action=generate</em> in the new WordPress folder (you do not need to install WordPress)<br
/> 4. Check filehashes.php and verify output is correct (i.e. an array of files with their hashes)<br
/> 5. Now upload exploitscanner.php and filehashes.php to your live WordPress folder<br
/> 6. Run <em>exploitscanner.php?action=scan</em> to generate a list of modified/added files<br
/> 7. Check output of scanresults.txt</p><p><strong>Generating Hashes</strong></p><p>Upload exploitscanner.php to the folder for which you want to generate file hashes and then point your browser to:</p><p>http://www.yoursite.com/untouchedsoftware/exploitscanner.php?action=generate</p><p>Only output is filehashes.php</p><p>Note: Be sure that the software is not already exploited. Ideally use a fresh copy from the software creators (make sure you check the version)</p><p><strong>Scanning For Exploits</strong></p><p>Upload exploitscanner.php to the folder for which you want to check file hashes and then point your browser to:</p><p>http://www.yoursite.com/hackedsoftware/exploitscanner.php?action=scan</p><p>Only output is scanresults.txt<br
/> <em>Legend-</em> F: New file | M: Modified file</p><p>First search for all .php files to see what is changed. If the file is tagged M, you can use a difference tool like <a
href="http://winmerge.org">WinMerge</a> to find out what has changed.</p><p><strong>Download</strong></p><p>Download <a
href="http://www.anantgarg.com/exploit-scanner.zip">PHP Exploit Scanner</a> free</p><p><strong>License</strong></p><p>Exploit Scanner is licensed under MIT license. Let me know if you make any interesting use of the script.</p><p><strong>Comments/Suggestions?</strong></p><p>If you would like to assist in creating a community where users can quickly download filehashes.php for their software then feel free to contact me using the form below.</p><p>Do let me know your suggestions on how we can improve this code or any other features you would like to add.</p><p><strong>Future Updates</strong></p><p>1. Improve recursive function to avoid time out on shared servers<br
/> 2. Create a community where users can upload hashes for known software like phpBB, vBulletin etc.<br
/> 3. Add GUI<br
/> 4. Database integrity<br
/> 5. Malicious code insertion in template files</p><p><strong>Spread The Word</strong></p><p>If you like what you are reading, then please help spread the word by re-tweeting, blogging or using the ShareThis button below. Thank you.</p> <img src="http://feeds.feedburner.com/~r/anantgarg/~4/XY54Grbr6xQ" height="1" width="1"/>]]></content:encoded> <wfw:commentRss>http://anantgarg.com/2010/09/15/exploit-scanner/feed/</wfw:commentRss> <slash:comments>2</slash:comments> <feedburner:origLink>http://anantgarg.com/2010/09/15/exploit-scanner/</feedburner:origLink></item> <item><title>Cross-domain cookies/sessions in Safari and all other browsers</title><link>http://feedproxy.google.com/~r/anantgarg/~3/QTb4FOnkAgg/</link> <comments>http://anantgarg.com/2010/02/18/cross-domain-cookies-in-safari/#comments</comments> <pubDate>Thu, 18 Feb 2010 12:20:49 +0000</pubDate> <dc:creator>Anant Garg</dc:creator> <category><![CDATA[Javascript]]></category> <category><![CDATA[Web Development]]></category><guid isPermaLink="false">http://anantgarg.com/?p=336</guid> <description><![CDATA[Safari does not allow cross-domain cookies. In other words, if on X.com, you load an iFrame with contents of Y.com and set a cookie in the iFrame, Safari will not save the cookie. This post contains a possible solution to this problem.]]></description> <content:encoded><![CDATA[<div
style="border: 1px solid #E2C822;background:#FFF9D7;padding:10px;margin-bottom:20px;">Want to read a simplified version of this article? <a
href="http://anantgarg.com/2012/02/18/busting-the-cookies-and-privacy-myth/" style="color:#333;font-weight:bold">Read my follow-up article regarding Google&#8217;s iPhone Tracking</a></div><p><strong>The Problem</strong></p><p>Safari does not allow cross-domain cookies. In other words, if on X.com, you load an iFrame with contents of Y.com and set a cookie in the iFrame, Safari will not save the cookie. This problem also occurs in IE6/7 but can be resolved by sending a P3P header.</p><p><strong>The Solution</strong></p><p>I am not entirely sure why this works, but the cookie gets saved if a post is made to the iFrame. This solution relies of jQuery but can be adapted to any other.</p><p>Here is the code that goes on the remote domain:</p><pre class="brush: plain; title: ; notranslate">
&lt;script src=&quot;http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js&quot; type=&quot;text/javascript&quot;&gt;&lt;/script&gt;
&lt;script&gt;
var isSafari = (/Safari/.test(navigator.userAgent));
var firstTimeSession = 0;

function submitSessionForm() {
	if (firstTimeSession == 0) {
		firstTimeSession = 1;
		$(&quot;#sessionform&quot;).submit();
		setTimeout(processApplication(),2000);
  	}	
}

if (isSafari) {
	$(&quot;body&quot;).append('&lt;iframe id=&quot;sessionframe&quot; name=&quot;sessionframe&quot; onload=&quot;submitSessionForm()&quot; src=&quot;http://www.yourdomain.com/blank.php&quot; style=&quot;display:none;&quot;&gt;&lt;/iframe&gt;&lt;form id=&quot;sessionform&quot; enctype=&quot;application/x-www-form-urlencoded&quot; action=&quot;http://www.yourdomain.com/startsession.php&quot; target=&quot;sessionframe&quot; action=&quot;post&quot;&gt;&lt;/form&gt;');
} else {
	processApplication();
}

function processApplication() {
	alert('Session has been set. Now you can start your application!');
}
&lt;/script&gt;
</pre><p>The contents for startsession.php would be as simple as:</p><pre class="brush: plain; title: ; notranslate">
&lt;?php
header('P3P: CP=&quot;IDC DSP COR CURa ADMa OUR IND PHY ONL COM STA&quot;'); 
session_start();
</pre><p>To make sure that your site is compatible with IE6/7, always output the following header:</p><pre class="brush: plain; title: ; notranslate">
&lt;?php
header('P3P: CP=&quot;IDC DSP COR CURa ADMa OUR IND PHY ONL COM STA&quot;'); 
</pre><p>Unfortunately, there is no way to find out when the browser has completed submitting the form. Thus, I have placed a delay of 2 seconds. This form needs to be submitted only once per user session. Then you can set your session data in PHP and it will be picked up by your remote domain. You can adapt the same example if you want to set cookies instead of starting a PHP session.</p><p><strong>Comments/Suggestions?</strong><br
/> Do let me know your suggestions on how we can improve this code.</p><p><strong>Spread The Word</strong><br
/> If you like what you are reading, then please help spread the word by re-tweeting, blogging or using the ShareThis button below. Thank you.</p> <img src="http://feeds.feedburner.com/~r/anantgarg/~4/QTb4FOnkAgg" height="1" width="1"/>]]></content:encoded> <wfw:commentRss>http://anantgarg.com/2010/02/18/cross-domain-cookies-in-safari/feed/</wfw:commentRss> <slash:comments>30</slash:comments> <feedburner:origLink>http://anantgarg.com/2010/02/18/cross-domain-cookies-in-safari/</feedburner:origLink></item> <item><title>SQLBuddyLite – phpMyAdmin alternative</title><link>http://feedproxy.google.com/~r/anantgarg/~3/PfZUj-eViDs/</link> <comments>http://anantgarg.com/2009/12/11/sqlbuddylite-phpmyadmin-alternative/#comments</comments> <pubDate>Fri, 11 Dec 2009 09:54:15 +0000</pubDate> <dc:creator>Anant Garg</dc:creator> <category><![CDATA[PHP]]></category> <category><![CDATA[mySQL]]></category><guid isPermaLink="false">http://anantgarg.com/?p=325</guid> <description><![CDATA[SQLBuddyLite is a lighter version of the excellent SQLBuddy available from http://www.sqlbuddy.com. It combines the entire package into only 4 files.]]></description> <content:encoded><![CDATA[<p>SQLBuddyLite is a lighter version of the excellent SQLBuddy available from http://www.sqlbuddy.com</p><p>The lite version is an exact copy of the original version but all the files have been combined into only 4 files.</p><p><strong>Acknowledgments</strong></p><p>SQLBuddyLite is a lighter version of <a
href="http://www.sqlbuddy.com">SQLBuddy</a> by Calvin Lough</p><p><strong>Why do you need a lite version?</strong></p><p>A typical situation is where you have limited access to the user&#8217;s site say only FTP access and you need to verify the database structures. The easiest way to tackle the problem is to upload SQLBuddy and view the data structures.</p><p>The lite version makes it quicker to upload files. The lite version does not compromise on the functions; it only reduces the files drastically to only 4 files, thus speeding up remote site management.</p><p>Only English language is supported in the latest release.</p><p><strong>Demonstration</strong></p><p>For security reasons, there is no demonstration version available.</p><p><strong>Screenshot</strong></p> <figure><a
href="http://i1.wp.com/anantgarg.com/wp-content/uploads/2009/12/sqlbuddy.jpg" rel="attachment wp-att-326"><img
src="http://i1.wp.com/anantgarg.com/wp-content/uploads/2009/12/sqlbuddy.jpg?resize=300%2C156" alt="sqlbuddy" title="sqlbuddy" class="alignnone size-medium wp-image-326" data-recalc-dims="1" /></a></figure><p><strong>Requirements</strong></p><p>1. PHP<br
/> 2. mySQL<br
/> 3. Apache mod-rewrite</p><p><strong>Getting Started</strong></p><p>Download and upload all the sqlbuddylite folder to your server</p><p><strong>Download</strong></p><p><a
href="http://code.google.com/p/sqlbuddylite/">SQLBuddyLite is hosted on Google Code</a><br
/> <a
href="http://sqlbuddylite.googlecode.com/files/sqlbuddylite%201.1.zip">Direct download link for SQLBuddyLite</a></p><p><strong>License</strong></p><p>MIT-style license</p><p><strong>Comments/Suggestions?</strong></p><p>Do let me know your suggestions on how we can improve this code or any other features you would like to add.</p><p><strong>Spread The Word</strong></p><p>If you like what you are reading, then please help spread the word by re-tweeting, blogging or using the ShareThis button below. Thank you.</p> <img src="http://feeds.feedburner.com/~r/anantgarg/~4/PfZUj-eViDs" height="1" width="1"/>]]></content:encoded> <wfw:commentRss>http://anantgarg.com/2009/12/11/sqlbuddylite-phpmyadmin-alternative/feed/</wfw:commentRss> <slash:comments>1</slash:comments> <feedburner:origLink>http://anantgarg.com/2009/12/11/sqlbuddylite-phpmyadmin-alternative/</feedburner:origLink></item> <item><title>PHP StackOverflow Clone</title><link>http://feedproxy.google.com/~r/anantgarg/~3/wBEs53X3FKA/</link> <comments>http://anantgarg.com/2009/12/09/php-stackoverflow-clone/#comments</comments> <pubDate>Wed, 09 Dec 2009 09:37:01 +0000</pubDate> <dc:creator>Anant Garg</dc:creator> <category><![CDATA[PHP]]></category><guid isPermaLink="false">http://anantgarg.com/?p=317</guid> <description><![CDATA[A PHP/mySQL based Stack Overflow clone. Features point system, beautiful CSS layout and many unique features.]]></description> <content:encoded><![CDATA[<p>Qwench is a PHP/mySQL based StackOverflow clone.</p><p><strong>Features</strong><br
/> 1. Allow users to post questions and answers<br
/> 2. Points system similar to StackOverflow<br
/> 3. Ability to post an article as a knowledge-base (for corporates)<br
/> 4. Ability to lock site to registered users only<br
/> 5. Clean CSS layout<br
/> 6. Works in all major browsers</p><p><strong>Demonstration</strong><br
/> <a
href="/qwench" target="_blank">Launch Qwench &#8211; StackOverflow Clone Demo</a></p><p><strong>Screenshot</strong></p> <figure><a
href="http://i2.wp.com/anantgarg.com/wp-content/uploads/2009/12/qwench.jpg" rel="attachment wp-att-318"><img
src="http://i1.wp.com/anantgarg.com/wp-content/uploads/2009/12/qwench.jpg?resize=300%2C144" alt="Qwench StackOverflow Clone" title="Qwench StackOverflow Clone" class="alignnone size-medium wp-image-318" data-recalc-dims="1" /></a></figure><p><strong>Requirements</strong><br
/> 1. PHP 4+<br
/> 2. mySQL</p><p><strong>Getting Started</strong><br
/> Download and follow the instructions in README.txt file</p><p><strong>Download</strong><br
/> <a
href="http://github.com/anantgarg/Qwench">Qwench is hosted at GitHub</a></p><p><strong>License</strong><br
/> Qwench licence can be found in the LICENSE.txt file.</p><p><strong>Comments/Suggestions?</strong><br
/> Do let me know your suggestions on how we can improve this code or any other features you would like to add.</p><p><strong>Spread The Word</strong><br
/> If you like what you are reading, then please help spread the word by re-tweeting, blogging or using the ShareThis button below. Thank you.</p> <img src="http://feeds.feedburner.com/~r/anantgarg/~4/wBEs53X3FKA" height="1" width="1"/>]]></content:encoded> <wfw:commentRss>http://anantgarg.com/2009/12/09/php-stackoverflow-clone/feed/</wfw:commentRss> <slash:comments>38</slash:comments> <feedburner:origLink>http://anantgarg.com/2009/12/09/php-stackoverflow-clone/</feedburner:origLink></item> <item><title>jQuery Fancy Gestures</title><link>http://feedproxy.google.com/~r/anantgarg/~3/qYEQ3cq_35c/</link> <comments>http://anantgarg.com/2009/05/21/jquery-fancy-gestures/#comments</comments> <pubDate>Thu, 21 May 2009 16:26:31 +0000</pubDate> <dc:creator>Anant Garg</dc:creator> <category><![CDATA[Javascript]]></category><guid isPermaLink="false">http://anantgarg.com/?p=296</guid> <description><![CDATA[Enable complex mouse gestures (letters, alphabets, symbols and your own custom gestures) on your website.]]></description> <content:encoded><![CDATA[<p>Enable complex mouse gestures (letters, alphabets, symbols and your own custom gestures) on your website!</p><p><strong>Features</strong><br
/> 1. Create your own custom gestures<br
/> 2. Create multiple areas where you can accept gestures<br
/> 3. Visual feedback<br
/> 4. Works in all major browsers</p><p><strong>Acknowledgment</strong><br
/> This script is a port of the <a
href="http://www.bytearray.org/?p=91">mouse gesture recognition action script</a> by Didier Brun.</p><p><strong>Demonstration</strong><br
/> <a
href="/fancygestures" target="_blank">Launch Fancy Gestures Demo</a></p><p><strong>Screenshot</strong></p> <figure><a
href="http://i1.wp.com/anantgarg.com/wp-content/uploads/2009/05/screenshot.jpg" rel="attachment wp-att-302"><img
style="border:1px solid #666666" src="http://i2.wp.com/anantgarg.com/wp-content/uploads/2009/05/screenshot.jpg?resize=300%2C165" alt="jQuery Fancy Gestures Screenshot" title="jQuery Fancy Gestures Screenshot" class="alignnone size-medium wp-image-302" data-recalc-dims="1" /></a></figure><p><strong>Requirements</strong><br
/> 1. <a
href="http://www.jquery.com">jQuery</a><br
/> 2. <a
href="http://www.walterzorn.com/jsgraphics/jsgraphics_e.htm#download">Walter Zorn&#8217;s VectorGraphics Library</a></p><p><strong>Getting Started</strong></p><p>Create a new html file with the following code. The function will return data i.e. the recognized character/symbol/name:</p><pre class="brush: plain; title: ; notranslate">
&lt;div id=&quot;sample&quot; style=&quot;border:1px solid black;position:relative;height:150px;width:150px;&quot;&gt;&lt;/div&gt;

&lt;script type=&quot;text/javascript&quot; src=&quot;wz_jsgraphics.js&quot;&gt;&lt;/script&gt;
&lt;script type=&quot;text/javascript&quot; src=&quot;jquery.js&quot;&gt;&lt;/script&gt;
&lt;script type=&quot;text/javascript&quot; src=&quot;jquery.fancygestures.js&quot;&gt;&lt;/script&gt;

&lt;script&gt;

$(document).ready(function () {   
	$('#sample').fancygestures(function (data) {
		alert(data);	
	});
});

&lt;/script&gt;
</pre><p><strong>Customization</strong></p><p>Please use the key below to generate a string of numbers for your gesture: e.g. L will be something like 2 then 0. The following letters are already mapped into the script:</p> <figure><img
src="http://i0.wp.com/anantgarg.com/wp-content/uploads/2009/05/fancygestures.gif?resize=537%2C152" alt="Fancy Gestures Customization" title="Fancy Gestures Customization" class="alignnone size-full wp-image-297" data-recalc-dims="1" /></figure><p>Suppose you want to add a &#8220;CIRCLE&#8221; symbol with the sequence &#8220;432107654&#8243;, then simply edit jquery.fancygestures.js and add the following after line 7:</p><pre class="brush: plain; title: ; notranslate">
gestures[&quot;CIRCLE&quot;] = &quot;432107654&quot;
</pre><p>The above code will return &#8220;CIRCLE&#8221; as data in the function when you make a circle. However, make sure you do not have similar gestures, or you will get unpredictable results e.g. having same gesture for zero and letter O.</p><p><strong>Download</strong><br
/> <a
href="/fancygestures.zip">Download Fancy Gestures</a></p><p><strong>License</strong><br
/> Fancy Gestures is licensed under MIT license. Let me know if you make any interesting use of the script.</p><p><strong>Comments/Suggestions?</strong><br
/> Do let me know your suggestions on how we can improve this code or any other features you would like to add.</p><p><strong>Spread The Word</strong><br
/> If you like what you are reading, then please help spread the word by re-tweeting, blogging or using the ShareThis button below. Thank you.</p> <img src="http://feeds.feedburner.com/~r/anantgarg/~4/qYEQ3cq_35c" height="1" width="1"/>]]></content:encoded> <wfw:commentRss>http://anantgarg.com/2009/05/21/jquery-fancy-gestures/feed/</wfw:commentRss> <slash:comments>11</slash:comments> <feedburner:origLink>http://anantgarg.com/2009/05/21/jquery-fancy-gestures/</feedburner:origLink></item> <item><title>Collabtweet – Multiple Users, Single Twitter Account</title><link>http://feedproxy.google.com/~r/anantgarg/~3/1R99mgS-IM0/</link> <comments>http://anantgarg.com/2009/05/20/collabtweet-multiple-users-single-twitter-account/#comments</comments> <pubDate>Wed, 20 May 2009 14:18:53 +0000</pubDate> <dc:creator>Anant Garg</dc:creator> <category><![CDATA[PHP]]></category> <category><![CDATA[Web Development]]></category><guid isPermaLink="false">http://anantgarg.com/?p=273</guid> <description><![CDATA[Enable multiple users to post to a single collaborative twitter account.]]></description> <content:encoded><![CDATA[<p>Enable multiple users to post to a single collaborative twitter account.</p><p><b>Why would you need such a program?</b><br
/> 1. You run a small company/group which you would like twitter users to follow<br
/> 2. You would like twitter users to follow a single brand (twitter account) and not separate multiple users<br
/> 3. You would like a simple <em>slick</em> interface to enable your users to post to your collaborative twitter account<br
/> 4. You would like to hide your real twitter account password from your users<br
/> 5. You would like to automatically add &#8220;-username&#8221; to all tweets by a user<br
/> 6. You would like to host this service on your own server</p><p><b>Demonstration</b><br
/> Please login with one of the following username/password combination:<br
/> johndoe/johndoe<br
/> janedoe/janedoe<br
/> babydoe/babydoe</p><p><a
href="/collabtweet/collabtweet.php" target="_blank">Click here to launch Collabtweet demo</a></p><p>For demonstration purposes, I am using a dummy twitter account- <a
href="http://www.twitter.com/collabtweet" target="_blank">http://www.twitter.com/collabtweet</a></p><p><b>Screenshots</b></p><table
style="border:0;margin:0;background-color:white" border=0 bgcolor="white" ><tr><td
style="background-color:white"><a
href="http://i0.wp.com/anantgarg.com/wp-content/uploads/2009/05/collabtweet-screen-1.jpg" rel="attachment wp-att-274"><img
style="border:1px solid black" src="http://i2.wp.com/anantgarg.com/wp-content/uploads/2009/05/collabtweet-screen-1.jpg?resize=300%2C218" alt="Collabtweet Login Screen" title="Collabtweet Login Screen" class="alignnone size-medium wp-image-274" data-recalc-dims="1" /></a></td><td
style="background-color:white"> <a
href="http://i0.wp.com/anantgarg.com/wp-content/uploads/2009/05/collabtweet-screen-2.jpg" rel="attachment wp-att-275"><img
style="border:1px solid black" src="http://i1.wp.com/anantgarg.com/wp-content/uploads/2009/05/collabtweet-screen-2.jpg?resize=300%2C215" alt="Collabtweet Tweet Screen" title="Collabtweet Tweet Screen" class="alignnone size-medium wp-image-275" data-recalc-dims="1" /></a></td></tr><tr><td
style="background-color:white"> <a
href="http://i2.wp.com/anantgarg.com/wp-content/uploads/2009/05/collabtweet-screen-3.jpg" rel="attachment wp-att-276"><img
style="border:1px solid black" src="http://i1.wp.com/anantgarg.com/wp-content/uploads/2009/05/collabtweet-screen-3.jpg?resize=300%2C168" alt="Collabtweet Tweeted!" title="Collabtweet Tweeted!" class="alignnone size-medium wp-image-276" data-recalc-dims="1" /></a></td><td
style="background-color:white"> <a
href="http://i0.wp.com/anantgarg.com/wp-content/uploads/2009/05/collabtweet-screen-4.jpg" rel="attachment wp-att-277"><img
style="border:1px solid black" src="http://i1.wp.com/anantgarg.com/wp-content/uploads/2009/05/collabtweet-screen-4.jpg?resize=300%2C181" alt="Twitter Outcome!" title="Twitter Outcome!" class="alignnone size-medium wp-image-277" data-recalc-dims="1" /></a></td></tr></table><p><b>Download</b><br
/> <a
href="/collabtweet.zip">Collabtweet &#8211; Multiple Users, Single Twitter Account</a></p><p><b>Requirements</b><br
/> Web server capable of running PHP</p><p><b>Configuration</b><br
/> Simple edit config.php and modify the users which can access your account and your twitter username and password.</p><pre class="brush: plain; title: ; notranslate">
&lt;?php

// Username =&gt; Password
// Username will be used as nickname
$users = array(
&quot;USER1&quot; =&gt; &quot;PASS1&quot;,
&quot;USER2&quot; =&gt; &quot;PASS2&quot;,
&quot;USER3&quot; =&gt; &quot;PASS3&quot;
);


$twitterUser = &quot;YOURTWITTERUSERNAME&quot;;
$twitterPass = &quot;YOURTWITTERPASSWORD&quot;;
</pre><p><b>Future Updates</b><br
/> 1. Ability to manage users using a web interface</p><p><b>Comments/Suggestions?</b><br
/> Do let me know your suggestions on how we can improve this code or any other features you would like to add.</p><p><b>Spread The Word</b><br
/> If you like what you are reading, then please help spread the word by re-tweeting, blogging or using the ShareThis button below. Thank you.</p> <img src="http://feeds.feedburner.com/~r/anantgarg/~4/1R99mgS-IM0" height="1" width="1"/>]]></content:encoded> <wfw:commentRss>http://anantgarg.com/2009/05/20/collabtweet-multiple-users-single-twitter-account/feed/</wfw:commentRss> <slash:comments>42</slash:comments> <feedburner:origLink>http://anantgarg.com/2009/05/20/collabtweet-multiple-users-single-twitter-account/</feedburner:origLink></item> <item><title>Gmail/Facebook Style jQuery Chat</title><link>http://feedproxy.google.com/~r/anantgarg/~3/W-PpfT0sDq0/</link> <comments>http://anantgarg.com/2009/05/13/gmail-facebook-style-jquery-chat/#comments</comments> <pubDate>Wed, 13 May 2009 17:18:23 +0000</pubDate> <dc:creator>Anant Garg</dc:creator> <category><![CDATA[Javascript]]></category> <category><![CDATA[PHP]]></category> <category><![CDATA[jquery]]></category> <category><![CDATA[plugin]]></category><guid isPermaLink="false">http://anantgarg.com/?p=255</guid> <description><![CDATA[This jQuery chat module enables you to seamlessly integrate Gmail/Facebook style chat into your existing website.]]></description> <content:encoded><![CDATA[<p><strong>Introduction</strong><br
/> Everyone loves the <a
href="http://mail.google.com/mail/help/chat.html">gmail</a> and <a
href="http://blog.facebook.com/blog.php?post=12811122130">facebook</a> inline chat modules. This jQuery chat module enables you to seamlessly integrate Gmail/Facebook style chat into your existing website.</p> <figure><img
src="http://i2.wp.com/anantgarg.com/wp-content/uploads/2009/05/jquery-chat-module.jpg?resize=234%2C317" alt="jQuery Chat" title="jQuery Chat" class="alignnone size-full wp-image-256" data-recalc-dims="1" /></figure><p><strong>Features</strong><br
/> 1. Gmail style bottom right display of chat boxes<br
/> 2. Keeps chat boxes open and stores state (data) even when pages are browsed/refreshed similar to Facebook<br
/> 3. Displays &#8220;Sent at&#8230;&#8221; after 3 minutes of inactivity<br
/> 4. Displays &#8220;X says&#8230;&#8221; &#038; blinks chat boxes when window is not in focus<br
/> 5. Minimize and close chat boxes<br
/> 6. Auto-resize of text input box<br
/> 7. Auto-scrolling of chat text<br
/> 8. Auto-back-off polling policy (hits the server less-often when chat activity is low)<br
/> 9. Extremely simple to integrate into existing site</p><p><strong>Demo</strong><br
/> Please load the following links in different browsers otherwise it wont work:<br
/> (Note that due to users trying out the chat links below at the same time, it might work slightly erratically because the username is actually not supposed to be used by multiple users at the same time)</p><p><a
href="/chat/samplea.php">Sample Chat User One</a><br
/> <a
href="/chat/sampleb.php">Sample Chat User Two</a><br
/> <a
href="/chat/samplec.php">Sample Chat User Three</a></p><p><strong>Getting Started</strong><br
/> First download the module (link below)</p><p>The script does not manage rosters (i.e. which users are online etc.), it only enables users to chat with each other. I assume your current application already provides that functionality. You must also make sure that $_SESSION['username'] is being set when your website session begins. You will understand the logic better after you try the sample files that I have provided.</p><p>You must first create a mySQL table as below (or import db.txt)</p><pre class="brush: plain; title: ; notranslate">
CREATE TABLE `chat` (
  `id` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
  `from` VARCHAR(255) NOT NULL DEFAULT '',
  `to` VARCHAR(255) NOT NULL DEFAULT '',
  `message` TEXT NOT NULL,
  `sent` DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00',
  `recd` INTEGER UNSIGNED NOT NULL DEFAULT 0,
  PRIMARY KEY (`id`),
 INDEX `to` (`to`),
 INDEX `from` (`from`)
)
ENGINE = InnoDB;
</pre><p>Add the following scripts to your page template</p><pre class="brush: plain; title: ; notranslate">
&lt;script type=&quot;text/javascript&quot; src=&quot;js/jquery.js&quot;&gt;&lt;/script&gt;
&lt;script type=&quot;text/javascript&quot; src=&quot;js/chat.js&quot;&gt;&lt;/script&gt;
</pre><p>Add the following CSS to your page template</p><pre class="brush: plain; title: ; notranslate">
&lt;link type=&quot;text/css&quot; rel=&quot;stylesheet&quot; media=&quot;all&quot; href=&quot;css/chat.css&quot; /&gt;
&lt;link type=&quot;text/css&quot; rel=&quot;stylesheet&quot; media=&quot;all&quot; href=&quot;css/screen.css&quot; /&gt;

&lt;!--[if lte IE 7]&gt;
&lt;link type=&quot;text/css&quot; rel=&quot;stylesheet&quot; media=&quot;all&quot; href=&quot;css/screen_ie.css&quot; /&gt;
&lt;![endif]--&gt;
</pre><p>Now in your list of users online, add &#8220;javascript:chatWith(&#8216;USERNAME&#8217;);&#8221; function where USERNAME is the username for that particular user who he/she wants to chat with.</p><p>Once that is done, edit chat.php and set your database parameters and try your website.</p><p>For better understanding, load 3 different browsers (internet explorer, firefox, safari) and point them to samplea.php, sampleb.php and samplec.php.</p><p>Click on &#8220;chat with john doe&#8221; link and watch the chat functionality come alive!</p><p>Inorder to integrate your existing website, you must place all your content between the &#8220;main_container&#8221; div tag.</p><p><strong>Browser Compatibility</strong><br
/> 1. Firefox 2+<br
/> 2. Internet Explorer 6+<br
/> 3. Safari 2+<br
/> 4. Opera 9+</p><p><strong>Licensing</strong><br
/> This chat script can be used for <strong>free</strong> under GPL-style license for <strong>non-commercial purposes</strong>. For commercial purposes, please <a
href="http://www.cometchat.com/?r=ag">purchase a license</a>.</p><p><strong>Download</strong></p><p><a
href='http://anantgarg.com/wp-content/uploads/2009/05/jquerychat.zip'>jQuery Chat Module</a>. If you would like a more <a
href='http://www.cometchat.com/?r=ag'>full featured inline chat software</a>, have a look at <b><a
href='http://www.cometchat.com/?r=ag'>CometChat</a>.</b></p><p><strong>Future Updates</strong><br
/> 1. Gmail style pop-out functionality<br
/> 2. Gmail style emoticons<br
/> 3. Gmail style video chat<br
/> 4. Comet integration<br
/> 5. Jabber integration</p><p><strong>Comments/Suggestions?</strong><br
/> Do let me know your suggestions on how we can improve this code or any other features you would like to add.</p><p><strong>Spread The Word</strong><br
/> If you like what you are reading, then please help spread the word by re-tweeting, blogging and dzone upvoting or use the ShareThis button below. Thank you.</p> <img src="http://feeds.feedburner.com/~r/anantgarg/~4/W-PpfT0sDq0" height="1" width="1"/>]]></content:encoded> <wfw:commentRss>http://anantgarg.com/2009/05/13/gmail-facebook-style-jquery-chat/feed/</wfw:commentRss> <slash:comments>519</slash:comments> <feedburner:origLink>http://anantgarg.com/2009/05/13/gmail-facebook-style-jquery-chat/</feedburner:origLink></item> </channel> </rss>
