tag:blogger.com,1999:blog-26878201851673661962024-02-19T00:00:22.781-08:00Mulligan!Viraj does it once over...Viraj Modyhttp://www.blogger.com/profile/10980120071443522974noreply@blogger.comBlogger53125tag:blogger.com,1999:blog-2687820185167366196.post-12848665432644223132012-03-02T15:35:00.000-08:002012-03-02T16:01:09.358-08:00Handling Fraudulent In-App Purchases on iOS<div dir="ltr" style="text-align: left;" trbidi="on">
The <a href="http://www.audiogalaxy.com/">Audiogalaxy</a> mobile apps for <a href="http://itunes.apple.com/app/audiogalaxy-mobile/id373357030">iOS</a> and <a href="http://market.android.com/details?id=com.audiogalaxy">Android</a> have some in-app purchases which allow users to buy add-ons to augment the free service. I've written previously about some <a href="http://crazyviraj.blogspot.com/2011/06/some-notes-on-implementing-in-app.html">interesting</a> <a href="http://crazyviraj.blogspot.com/2011/08/dont-let-apples-latency-mess-with-your.html">observations</a> about implementing each. Continuing that series, here are some more interesting things I've seen when dealing with in-app purchases on iOS, specifically related to fraudulent purchases.<br />
<br />
Apple's <a href="https://developer.apple.com/library/mac/#documentation/NetworkingInternet/Conceptual/StoreKitGuide/Introduction/Introduction.html">in-app purchases programming guide</a> describes the steps needed to <a href="https://developer.apple.com/library/mac/#documentation/NetworkingInternet/Conceptual/StoreKitGuide/VerifyingStoreReceipts/VerifyingStoreReceipts.html#//apple_ref/doc/uid/TP40008267-CH104-SW1">verify store receipts</a> once you're notified of a successful transaction in the <span style="font-family: 'Courier New', Courier, monospace;">SKRequestDelegate</span> callback. It's tempting to just unlock the feature when you get notified of a successful transaction being complete, but you <i>really</i> should do the extra work required to send over the receipt to Apple for verification, or you risk getting defrauded pretty easily. Ideally, you want to send the receipt up to your own server first (over SSL, obviously) and then verify the receipt using Apple's iTunes receipt verification service.<br />
<br />
Since releasing the in-app purchase for iOS, I've seen several fraudulent purchase attempts. For the first version, I took a conservative approach - when notified by StoreKit that a transaction was successfully completed, I unlocked the feature and then sent the receipt up to my server for verification. Additionally, I recorded verification responses from Apple's service but didn't make the device reject the add-on if it failed. The reason I did this was a combination of naivete and being unsure of how things would really work in the wild. What if my server could not be reached? What if Apple's service was down? I didn't want the user to be charged money and be left hanging because of connectivity problems. (Not knowing better, I also assumed that the 'purchase complete' callback was pretty bullet-proof and meant that the user was charged, and that verification was just added formality).<br />
<br />
Well, it turns out that StoreKit on jailbroken iOS devices can be easily fooled into believing that a local transaction completed successfully. So with my conservative approach, users with jailbroken phones could easily get in-app features unlocked without paying for them. Thankfully, I caught this before it did serious damage and pushed an app update. Because of the <a href="http://crazyviraj.blogspot.com/2011/08/dont-let-apples-latency-mess-with-your.html">dumb-client architecture of my system</a>, I was also able to easily revoke the purchases made fraudulently (need more reasons to have a server be in control?) What I had originally failed to account for is that the system is built to handle verification failures - if you legitimately fail to verify a transaction because of connectivity problems on either end, just don't mark the transaction as 'finished' and it will automatically get restored the next time StoreKit's queue is initialized (which is why it's also a good idea to write code to handle restored transactions, even if your in-app purchases are only of the <a href="https://developer.apple.com/library/mac/#documentation/NetworkingInternet/Conceptual/StoreKitGuide/APIOverview/OverviewoftheStoreKitAPI.html#//apple_ref/doc/uid/TP40008267-CH100-SW1">consumable</a> variety). This might mean that an add-on won't be available to a user for a while if they paid and you failed to verify the transaction because of networking problems, but they won't be re-charged for it and it will eventually be unlocked correctly.<br />
<br />
In case you're interested, here are some examples of fake base64 encoded receipts:<br />
<pre><span style="font-family: 'Courier New', Courier, monospace;"><span style="text-align: -webkit-auto;">Y29tLnVydXMuaWFwLjcxNDA4NTE0</span>
<span style="text-align: -webkit-auto;">Y29tLnVydXMuaWFwLjMwODEwMjk5</span>
<span style="text-align: -webkit-auto;">Y29tLnVydXMuaWFwLjk2NTQyNjQ3</span></span>
</pre>
The response from Apple, when these types of receipts are sent for verification, looks like:<br />
<pre><span style="font-family: 'Courier New', Courier, monospace; text-align: -webkit-auto;">{"status":21002, "exception":"java.lang.ClassCastException"}</span></pre>
<span style="font-family: inherit;">FWIW, Apple's documentation states that any non-0 response is considered a failure for purchases other than auto-renewing subscriptions. For those, they have a set of well-defined error codes. In a future post, I'll try to cover my experiences implementing auto-renewing subscriptions (and the non-technical, app approval overhead that goes with it).</span><br />
<div>
<div style="text-align: -webkit-auto;">
<span style="font-family: inherit;"><br /></span></div>
<div style="text-align: -webkit-auto;">
<span style="font-family: inherit;">If you're curious, here's the distribution of where fraudulent purchase attempts came from:</span></div>
<span style="font-family: inherit;">United States - 38.46%</span><br />
<span style="font-family: inherit;">Israel - 14.11%</span><br />
<span style="font-family: inherit;">Canada - 5.45%</span><br />
<span style="font-family: inherit;">United Kingdom - 4.49%</span><br />
<span style="font-family: inherit;">Saudi Arabia - 3.81%</span><br />
Netherlands - 3.53%
<br />
<span style="font-family: inherit;">Germany - 1.60%</span><br />
<span style="font-family: inherit;">Mexico - 1.60%</span><br />
<span style="font-family: inherit;">United Arab Emirates - 1.28%</span><br />
<span style="font-family: inherit;">Japan - 1.28%</span><br />
<span style="font-family: inherit;">Others - rest</span></div>
</div>Viraj Modyhttp://www.blogger.com/profile/10980120071443522974noreply@blogger.com4tag:blogger.com,1999:blog-2687820185167366196.post-23829359495698435852011-11-11T12:50:00.001-08:002011-11-11T13:32:46.908-08:00Customer Support Rule #1 - Don't be a Robot<div dir="ltr" style="text-align: left;" trbidi="on">
A recent user support interaction, pictured below, is one of many examples I've experienced where being honest and human goes a long way in winning over users, and humor doesn't hurt. You don't have to be 'nice', don't have to follow a strict template or be formal. You just have to communicate with your user like a real human being.<br />
<br />
Users love it when they realize there's a human on the other end, and not a robot that's spewing stock replies crafted by corporate types. That is, often, the start of a loyal relationship.<br />
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhBZzm4yFU5_eOu1k_AqlGK5uH05CPkyPPFgIv72ie3apgusaW-xqzHu0KBHvcm4nYyBu5WSnb44Qqw24C763tWAfd27p88zXW0_QB4EfowJFD8WsJ6XO8ieg44fAUd9X5bjJzC8tB3I3c/s1600/Support.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="272" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhBZzm4yFU5_eOu1k_AqlGK5uH05CPkyPPFgIv72ie3apgusaW-xqzHu0KBHvcm4nYyBu5WSnb44Qqw24C763tWAfd27p88zXW0_QB4EfowJFD8WsJ6XO8ieg44fAUd9X5bjJzC8tB3I3c/s640/Support.png" width="640" /></a></div>
<div>
<br /></div>
</div>Viraj Modyhttp://www.blogger.com/profile/10980120071443522974noreply@blogger.com0tag:blogger.com,1999:blog-2687820185167366196.post-2805637286778888122011-08-22T10:58:00.000-07:002011-08-23T15:06:17.000-07:00Don't Let Apple's Latency Mess With Your iOS AppLast week, I released an update for the iOS <a href="http://www.audiogalaxy.com/">Audiogalaxy</a> app which featured its first in-app purchase that allows users to <a href="http://www.audiogalaxy.com/blog/2011/08/audiogalaxy-mobile-2-0-for-ios-with-offline-playback-and-more/">'pin' songs to their devices</a>. Users can try the feature by pinning a limited number of songs for free, and are then prompted to purchase if they wished to continue pinning more songs. As suggested in Apple's SDK documentation, I built and tested the in-app purchase against the <a href="http://developer.apple.com/library/ios/#documentation/NetworkingInternet/Conceptual/StoreKitGuide/DevelopingwithStoreKit/DevelopingwithStoreKit.html">sandbox</a>. After everything checked out as I expected, I submitted both the app update and the in-app purchase for approval by Apple.<div>
<br /></div><a href="http://webkilled.tv/wp-content/uploads/2010/01/apple-fail.jpg" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 250px; height: 220px;" src="http://webkilled.tv/wp-content/uploads/2010/01/apple-fail.jpg" border="0" alt="" /></a><div>The application and the in-app purchase were concurrently approved. After verifying that the status of in-app purchase product in iTunes Connect was "Ready for Sale", I released the updated application. It usually takes about an hour for app updates to start propagating to users, so I anxiously waited to get the update on my iPhone and verify that in-app purchasing worked fine. Once I got the update, I tried to purchase the in-app product but it failed. I had written code to handle potential errors, and looking at the error message I realized that the StoreKit API wasn't able to identify the in-app product I was requesting. </div><div>
<br /></div><div>I went back and verified that the product identifier was correct, and everything still worked fine in the sandbox. Users had started hitting the same problem - not good! It's definitely not the best first impression for users to upgrade, try new features and immediately see an error. I scoured the web for clues and found a <a href="http://stackoverflow.com/questions/5753162/in-app-purchase-does-not-work-when-live">StackOverflow question</a> which suggested that it can take up to 24-36 hours for in-app products to 'go live'. <i>Uh-oh</i>! </div><div>
<br /></div><div>There is no mention of such a delay anywhere in Apple's documentation, and neither is it reflected in the status of the in-app purchase product. If I had known, I obviously would have waited to release the update. Apple doesn't provide the ability to roll back to previous versions, so basically I was SOL. If indeed it took ~24 hours for the in-app product to show up, it could be disastrous. The fallout could include:</div><div><ul><li><u>Disappointed users</u>. There is nothing more disappointing that looking at the "What's New" section of an app before upgrading, getting excited and then watching the feature just not work as expected.</li><li><u>Negative reviews/ratings</u>. App Store ratings/reviews provide a medium for users to vent their frustrations or to shower praise. For developers, it's really a matter of making sure that the people with exceedingly pleasurable experiences with your application exceed the number of people with even remotely dissatisfying experiences. Audiogalaxy was a 5-star rated app, but this snafu could potentially affect that negatively, really fast.</li><li><u>Personal disappointment</u>. I take a lot of <a href="http://crazyviraj.blogspot.com/2011/01/on-mediocre-software-where-is-love.html">pride</a> in making sure that I deliver delightful experiences to users, and this would be the exact opposite of delightful. The hurt would be tempered by the fact that this was beyond my control, but it would hurt nonetheless.</li><li><u>Lost revenue</u>. From my experience with <a href="http://www.audiogalaxy.com/blog/2011/07/offline-music-for-android/">in-app purchases on Android</a> (and now also on iOS) it was abundantly clear to me that purchase volume is very high in the first few days of a new in-app purchase being available. Users upgrade, try what's new and buy it if they like it. If they're unable to, they might never come back to buy it. </li></ul></div><div>I was really hoping that the problem was the delay-to-propagate issue and not anything more serious. Damage would be limited if the in-app purchase went live before too many users upgraded and started using the new feature, and I was secretly hoping that this was a matter of a few hours instead of 24-36 as suggested in the SO question. </div><div>
<br /></div><div>I started monitoring how many users were pulling down the update and trying this new feature (yay for building simple monitoring tools that are fully in your control) and also dumping these userIDs into a table so I could keep track - a subset of these users were (hopefully) trying to purchase the add-on, but were failing to do so. The number started growing rapidly, and I began to panic. 5-6 hours later, the in-app product still wasn't live, negative reviews had started showing up and folks had started emailing us to let us know. </div><div>
<br /></div><div>Watching user behavior, I knew something needed to be done. We decided that the best option was to make it impossible to even attempt to purchase the add-on. I updated the server to make it believe that all iOS users had already purchased the add-on - the app would then automatically hide all purchase related options. The plan was to just let everyone use the feature for free until the in-app purchase showed up for real. I wrote the code, tested it and pushed it to production. I watched it work for a bit, and then went to sleep.</div><div>
<br /></div><div>The next morning, 14 hours after the status was "Ready for Sale", I was finally able to purchase the in-app product from the app. Thankfully, the server tweaks had helped and there were no new negative reviews or support emails. I rolled back the code from the previous night and waited for purchases to start showing up. Within minutes, I could see folks buying so I knew things were working fine now. </div><div>
<br /></div><div>The last thing that needed to be done was informing users who might have previously had trouble and might potentially never try buying again. I wrote a script to run through the list of all users who had tried the feature and to send them a personal note thanking them for their support, explaining what had happened and letting them know that the in-app purchase was now available if they wished to purchase it. A surprisingly large percentage of users went ahead and purchased it, and a few also went back and updated their reviews.</div><div>
<br /></div><div>I took away three lessons from this experience -</div><div><ul><li>Design your apps/clients to be as dumb as possible and the server as much in control as possible. We've always tried to do that with Audiogalaxy, and it certainly helped in this situation (as it has on a few previous occasions).</li><li>Spamming all your users isn't a good idea, but it's perfectly OK to email a subset of users directly impacted by specific issues. Users are receptive to personally addressed emails with honest dialogue, and don't consider it spam.</li><li>Wait for 24 hours before releasing iOS app updates if you have added new in-app purchases.</li></ul></div><div>All in all, this didn't turn out as badly for us as it could have - but it shouldn't have happened. I still believe Apple is at fault for not communicating this information to developers, but that's not a rare occurrence for Apple - it's best to anticipate and prepare for the unexpected. I hope this blog post helps other who might be planning to add in-app purchases to their apps. In addition to better documenting such gotchas, it would also be awesome if Apple added the ability for developers to roll back to previous versions (or at least 1 previous version) like Google's Android Market recently did.</div>Viraj Modyhttp://www.blogger.com/profile/10980120071443522974noreply@blogger.com2tag:blogger.com,1999:blog-2687820185167366196.post-5428466579350644412011-06-22T15:01:00.000-07:002011-06-23T18:29:12.263-07:00A Capitalistic Fix for America's Broken Immigration System?<div>Immigration in America is <a href="http://www.competeamerica.org/">broken</a>. There is no way to <a href="http://techcrunch.com/2009/12/05/the-startup-visa-and-why-the-xenophobes-need-to-go-back-into-their-caves/">sugarcoat</a> it, unfortunately. Failure of the administration to separate legal and illegal immigration concerns and to treat the politics of both as distinct from each other is <a href="http://techcrunch.com/2011/03/06/why-silicon-valley-immigrant-entrepreneurs-are-returning-home/">jeopardizing</a> the <a href="http://www.politico.com/news/stories/0611/57013.html">future of America</a>. </div><div><br /></div><div>There are <a href="http://www.cfr.org/terrorism/broken-immigration-system-risks-serious-damage-us-national-interests-warns-cfr-task-force/p19743">several</a> <a href="http://immigrationvoice.org/index.php?option=com_content&task=view&id=5&Itemid=47">articles</a>, <a href="http://www.uscis.gov/portal/site/uscis/menuitem.5af9bb95919f35e66f614176543f6d1a/?vgnextoid=a294b16dedc0f210VgnVCM100000082ca60aRCRD&vgnextchannel=aa290a5659083210VgnVCM100000082ca60aRCRD">memos</a> and <a href="http://techcrunch.com/2011/03/06/why-silicon-valley-immigrant-entrepreneurs-are-returning-home/">blog posts</a> which <a href="http://www.washingtonpost.com/wp-dyn/content/article/2007/02/23/AR2007022301697.html?sub=AR">articulate what is wrong</a> in a lot of detail, so I won't repeat those. To illustrate the problem though, here is a short blurb about my own situation. </div><div><br /></div><div>I have been a legal immigrant in America since I arrived to study Computer Science at the University of Texas at Austin in 2000. I graduated with a BS in Computer Science, interned at National Instruments for many months and went on to work at Microsoft for several years. I now work at a software startup. I'm a legal resident alien, but I'm still awaiting my permanent resident card ("green card"). I've been in the US for over 11 years now. I pay my taxes (TurboTax computes that ~16% of all income I've earned over the last 10 years has gone towards taxes, and this doesn't include local sales taxes), I occasionally speed on freeways and get pulled over, I spend money to stimulate the local economy (<a href="http://www.mint.com/">Mint.com</a> says I spend ~2.5x what the average American my age spends), I eat junk food, I save money for retirement and invest it in American companies, I volunteer at a local high school, I support the <a href="http://www.texassports.com">Texas Longhorns</a> and the <a href="http://www.seahawks.com">Seattle Seahawks</a>, I drink a lot of coffee, I donate to <a href="http://liveunited.org/">local</a> and <a href="http://www.cry.org">international causes</a>, I whine about the weather... basically, I do what most average Americans do. I have a decent life here in America, and I'm thankful for it. But, barring any change in legislation, the estimated date for my green card being issued is <b>2024</b>. Yep, that's not a typo. If nothing changes, I will get my green card <b>24 years</b> after I legally entered the United States of America. Not citizenship, just permanent residence!</div><div><br /></div><div>I assume I don't need to explain to any reasonable person how ridiculous and ironic this situation is, given that American was built by immigrants. I won't get into how my personal and professional life are impacted by this silly situation. Instead, I want to propose a possible solution, one that I haven't heard mentioned previously. </div><div><br /></div><div>What if one of the criteria for issuing a green card was - if your stay in America has been legal and you have paid more in federal+local taxes than the average American pays over his/her<i style="font-weight: bold; "> entire lifetime</i> then your green card application should be fast-tracked.</div><div><br /></div><div>If this sounds too simple, it's because it is -a simple, capitalist, pragmatic approach to immigration. This rule ensures that prospective immigrants enter America using legal means and remain legal, that they are employable and have documented income that is in line with or exceeds that of average Americans, that they will not be a burden to society but an asset and that they have an incentive to contribute to the success of their adopted nation.</div><div><br /></div><div>Thoughts?</div><div><br /></div><div>Unfortunately, immigration reform is a political issue instead of a practical one, and a solution to legal immigration doesn't seem possible without a solution to illegal immigration (which just baffles me - anyone know why the two are joined at the hip?). America and Americans will eventually have to suffer the consequences of such nearsightedness, I fear. Illegal immigration is a political minefield though, so I don't have much hope for any real reform anytime soon, but then again... "hope and change", right?</div>Viraj Modyhttp://www.blogger.com/profile/10980120071443522974noreply@blogger.com2tag:blogger.com,1999:blog-2687820185167366196.post-31571810405656127572011-06-20T22:42:00.000-07:002011-06-21T15:27:00.185-07:00Some Notes on Implementing In-App Billing on AndroidAbout a year ago, I implemented <a href="http://developer.apple.com/library/ios/#documentation/NetworkingInternet/Conceptual/StoreKitGuide/Introduction/Introduction.html">in-app payments for iOS</a>, and it was painful. Technically, it wasn't too hard but there were a number of hoops to jump though to get everything working. Lack of clear documentation and undocumented error responses seemed to be the primary problem - I think a hold up with verifying tax information on the developer account resulted in strange, undocumented failure codes in my case. There were several dozen blog posts complaining about many such "magic" issues back then. It was almost comical, to be honest.<div><br /></div><div>This last weekend, I decided to add <a href="http://developer.android.com/guide/market/billing/index.html">in-app billing</a> to an app that's already shipping for Android, and I ran into a couple of hiccups that I think are worth documenting here to save others some time/pain. Overall, I think the Android folks learnt from some of the pitfalls that early developers hit with in-app payments on iOS and worked to fix those. The documentation is a lot clearer and precise compared to where iOS was a year ago. Android Market also has a nifty <a href="http://developer.android.com/guide/market/billing/billing_testing.html#billing-testing-static">static test/response facility</a> which makes it easy to test code without worrying about external influences. iOS was desperately lacking something of this sort (not sure if it's better now) - I even wrote a dummy implementation of the iOS in-app payment API and the different response conditions to let me test my code in an emulator, which was really helpful. I'm glad Android got this right (still no testing in an emulator though, which is a bummer because of the inability to test with different Android OS versions).</div><div><br /></div><div>At a high level, the in-app billing protocol on Android is extremely chatty (see the <a href="http://developer.android.com/guide/market/billing/billing_overview.html">diagrams on this page</a>). It seems particularly so when compared to <a href="https://www.x.com/community/ppx/xspaces/mobile/mep">PayPal's rival offering</a>. PayPal's in-app billing module handles payment using <a href="http://developer.android.com/reference/android/app/Activity.html#startActivityForResult(android.content.Intent, int)"><code>startActivityForResult</code></a> and <a href="http://developer.android.com/reference/android/app/Activity.html#onActivityResult(int, int, android.content.Intent)"><code>onActivityResult</code></a> methods which can be used for communicating between child/parent activities. This is very similar to how <a href="https://github.com/facebook/facebook-android-sdk/">Facebook's Android SDK</a> handles single sign on. That model is extremely simple to understand and implement for developers - you could probably have it coded up and tested faster than you can finish reading this blog post. I think part of this chattiness is to ensure a higher level of security and also to handle <a href="http://developer.android.com/guide/market/billing/billing_admin.html#billing-purchase-type">managed purchases</a>, which are both likely harder to do using PayPal's approach. I assume they had good reason to make it as chatty as it is now, but my gut says that Google loses out on a lot of developers because of this apparent complexity. Having said that, the <a href="http://developer.android.com/guide/market/billing/billing_integrate.html#billing-download">sample application</a> Google provides does a really good job of illustrating the various communications that need to occur, so studying that code makes the chattiness easier to understand (for some reason, code registers more easily in my brain than verbose documentation - good code and good documentation together are very rare, and Android seems to have gotten this right - kudos!)</div><div><br /></div><div>One of the biggest red flags with using Android's in-app billing, however, is the development teams non-responsiveness to critical bugs. <a href="http://code.google.com/p/marketbilling/issues/detail?id=14">This issue</a>, marked critical and accepted, talks about transactions failing 4-6% of the time, and has been active for over two months without an owner being assigned. An equally disturbing issue with <a href="http://code.google.com/p/marketbilling/issues/detail?id=19">unacceptably long latency</a> when handling authorization has been open for almost two months without any comment from Google or even an owner being assigned. In fact, browsing the <a href="http://code.google.com/p/marketbilling/issues/list">issues list</a> certainly doesn't inspire confidence in the platform. It would be a big mistake by Google to not take these issues seriously. In my case, I'm certainly debating the merit of jumping into this as an early adopter.</div><div><br /></div><div>Some other notes about implementing in-app billing on Android:</div><div><ul><li>It sounds obvious in hindsight, but you need to make sure that your application is signed with the release key before testing in-app billing, even the static response stuff. Developer's aren't used to doing this while debugging, so it's worth calling out in the documentation.</li></ul><ul><li>If you want valid signatures for the JSON responses as suggested by the <a href="http://developer.android.com/guide/market/billing/billing_testing.html#billing-testing-static">table here</a>, the "debuggable" attribute in the app manifest should be set to false, even if you're using a release-key-signed APK and static responses. I was a little annoyed by this un-documented restriction because it basically meant that I had to resort to using logging as the only real means of debugging my code. If there is indeed some way to debug this using a debugger (and yet receive valid signatures), it’s definitely not obvious to me. Again, not a terribly big deal once you realize that this attribute actually seems to affect signatures, but that isn't documented anywhere.</li></ul><ul><li>If you're using a server to validate signatures (<a href="http://developer.android.com/guide/market/billing/billing_best_practices.html#billing-security">as you should</a>), be aware that the public key you obtain from the Android Market profile page is a base-64 encoded string. You will almost certainly need to convert it to PEM/DER format or use the x509 certificate for it to be usable with most server openssl implementations. For instance, this command can be used to convert the base-64 encoded key into a key usable by PHP: <pre>openssl enc -base64 -d -in publickey.base64 -A | openssl rsa -inform DER -pubin > publickey.pem</pre> Also, if you're using the PHP function <a href="http://php.net/manual/en/function.openssl-verify.php"><code>openssl_verify</code></a>, remember that <code>$signature</code> needs to be the binary signature, not the base-64 encoded signature string that the Android Market app sends (I found out the long way).</li></ul><ul><li>Unavailability of subscriptions/auto-billing is a big gap in the offering currently, but I assume support for it is coming soon, since <a href="http://developer.apple.com/library/ios/#documentation/NetworkingInternet/Conceptual/StoreKitGuide/RenewableSubscriptions/RenewableSubscriptions.html#//apple_ref/doc/uid/TP40008267-CH4-SW2">iOS already supports it</a>.</li></ul><ul><li>I hit an issue when testing payments using carrier billing, where accepting the carrier ToS basically causes the transaction to get lost somewhere between the carrier and Android Market. I logged <a href="http://code.google.com/p/marketbilling/issues/detail?id=30">this bug</a> to track the issue.</li></ul></div><div>Well, I hope that the web-search spiders do their job and this post helps others out there looking at implementing in-app billing on Android. And making Google get a move on those bugs.</div><div><br /></div><div><a href="http://news.ycombinator.com/item?id=2680273">Discuss this post on Hacker News</a></div>Viraj Modyhttp://www.blogger.com/profile/10980120071443522974noreply@blogger.com5tag:blogger.com,1999:blog-2687820185167366196.post-68662679919067703192011-03-03T21:42:00.000-08:002011-03-03T22:55:01.092-08:00The India-Trip PenaltyVacations to India from America have a pretty high fixed-cost that significantly eats into the time that can 'actually' count as vacation. Here's the composition of this 'penalty':<div><ul><li>Average flying time to get to India, if you don't need a connecting flight at the destination, is 18-22 hours each way depending on where you fly from, the route and layovers. Assuming the actual flight time is an optimistic 20 hours, let's add the 3 hours that it takes to head out to the airport/check-in/security/etc each way, and another 2 hours to clear immigration/customs/etc each way. So, the time to get to and from the vacation is 50 hours.</li></ul></div><div><ul><li>When you fly half way around the world, it's hard not to get jet-lagged. Optimistically, you've got to account for at least a half day (<a href="http://cultureshock.kristiejoy.net/jet-lag-help-please/">a very optimistic assumption</a>) wasted on both ends of the trip. So you have another 24 hours spent jet-lagged.</li></ul><ul><li>A special perk of visiting India is the high probability that you'll fall ill. Over the years, I've noticed my immunity to the Indian environment (food, water, air, etc) fade. This is true for most people I know who have lived outside of India for > 5 years. During each trip, it's very likely that you'll catch a stomach flu, get food poisoning, a fever, or generally fall sick. If you're lucky, it takes a day of complete rest and another half day of extreme caution to get back on track. So that's another 36 hours of lost vacation right there.</li></ul></div><div><ul><li>Packing for a trip back from India isn't like packing for a quick flight to San Diego. A good deal of your last day is usually spent planning to pack, and then packing, and then re-packing to make sure your bags are all within weight limits. For those who don't live in metro areas, there's the additional time of travelling to metro areas. So let's throw in another 6 hours for all this.</li></ul></div><div>So the total penalty of a trip to India is, roughly, 116 hours (a little under 5 days). Regardless of how long your vacation is, you're likely to incur this penalty, give-or-take a few hours. Heck, I would consider a 5-day trip within the US, or even a quick 5-day trip to Mexico, to be a decent vacation in itself. </div><div><br /></div><div>It's pretty amazing to think that for many, the penalty of a vacation to visit friends and family is a whole other vacation. It's no wonder that most people who go to India need/want at least 3 weeks for the vacation to be worth it, and even then they feel gypped (rightly so?).</div><div><br /></div><div>This also puts into perspective things that you otherwise might not think about. How much would you pay to avoid a 6-hour layover in Amsterdam en-route to India? Would a business-class ticket be worth the time it helps you shave off with airport formalities, jet-lag and worrying about luggage limits?</div>Viraj Modyhttp://www.blogger.com/profile/10980120071443522974noreply@blogger.com2tag:blogger.com,1999:blog-2687820185167366196.post-35965712758736566612011-01-26T10:46:00.000-08:002011-01-26T12:58:54.811-08:00Hey Bing, iOS isn't Windows Phone 7The Bing app for iOS was decent, until recent updates. It looks like someone at Microsoft decided they needed to make the Bing app for iOS look and feel more like a Windows Phone 7 app. So they did, and the result is an ugly, unusable disaster.<br /><br />On Windows Phone 7, buttons look like traditional 'labels', but in lower-case font. Clickable elements are very subtly different from text, but yet it's obvious what is clickable and what isn't. The hardware 'back' button, standard on all phones, is used to dismiss modal dialogs. That's how the native interface of the phone (called 'Metro', I believe) is, and it all comes together quite beautifully. Here's what a menu on a native Windows Phone 7 app looks like:<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhDGinKexIi_JZ-90EfF4ev9-LsQdencJZ9zBO6ChRakmsWaNwDzPcyYjf2XCwMG8kNLpPmVdYJt3Z_LOsU_DH-GGbtmvFxzZV_LNczUw4DJ07V9yYS7nDltRbnqLuVgHlu-GGG1usp3mk/s1600/wp7.png"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 212px; height: 354px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhDGinKexIi_JZ-90EfF4ev9-LsQdencJZ9zBO6ChRakmsWaNwDzPcyYjf2XCwMG8kNLpPmVdYJt3Z_LOsU_DH-GGbtmvFxzZV_LNczUw4DJ07V9yYS7nDltRbnqLuVgHlu-GGG1usp3mk/s400/wp7.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5566570585550948914" /></a> <br /><br />On iOS, buttons looks like, well, buttons. Clickable elements have depth and curves and are strikingly different from text/labels. There is no hardware back button, so Apple has provided built-in buttons with system fonts and colors which are placed on the top right corner to dismiss modal dialogs. Just like Windows Phone 7, it's well thought out, coherent and comes together beautifully. Here's how menus, buttons and dialogs look on iOS.<br /><br /><div style="text-align:center;"><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhJIpW4HOFAxiaOkUUcrDaARg-T7vQ-svjo4DB-dihYQemQJSOe09Di_uidAUL_lq7UX6Ct0hu2OwITBaHqAwUHnkd_sNGbxX7yoo26ocOKTVEZXzlDeFrVpY0UtQwehcXXYC38fYuNYqA/s1600/IMG_0341.PNG"><img style="margin:0px auto 10px; padding:0 10px; text-align:center;cursor:pointer; cursor:hand;width: 250px; height: 400px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhJIpW4HOFAxiaOkUUcrDaARg-T7vQ-svjo4DB-dihYQemQJSOe09Di_uidAUL_lq7UX6Ct0hu2OwITBaHqAwUHnkd_sNGbxX7yoo26ocOKTVEZXzlDeFrVpY0UtQwehcXXYC38fYuNYqA/s400/IMG_0341.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5566586044851652098" /></a><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhRbfh8oMIBPX_ou7y7hTLoNVUl7JR3q1iGD4HY-oDrQrK4yHHG-V6qIJRGWhwMiMU8RxBZtj5Eln1IRthVYOO9mOw_KMRYaW7FudI6HRegPfGV9U0pTEQZzkcDxnM810SI_jAF2sUP8k4/s1600/IMG_0342.PNG"><img style="margin:0px auto 10px;padding:0 10px; text-align:center;cursor:pointer; cursor:hand;width: 250px; height: 400px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhRbfh8oMIBPX_ou7y7hTLoNVUl7JR3q1iGD4HY-oDrQrK4yHHG-V6qIJRGWhwMiMU8RxBZtj5Eln1IRthVYOO9mOw_KMRYaW7FudI6HRegPfGV9U0pTEQZzkcDxnM810SI_jAF2sUP8k4/s400/IMG_0342.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5566586040847167730" /></a><br /></div><br /><br />Good apps follow <a href="http://en.wikipedia.org/wiki/Human_interface_guidelines">Human Interface Guidelines</a> of the platform they're built for, which gives them consistency with the rest of the system and makes interactions with the application intuitive. For some reason, the developers/designers of the Bing iOS app decided to follow the Human Interface Guidelines for <a href="http://docs.google.com/viewer?a=v&q=cache:iax1gbnImpsJ:download.microsoft.com/download/D/8/6/D869941E-455D-4882-A6B8-0DBCAA6AF2D4/UI%2520Design%2520and%2520Interaction%2520Guide%2520for%2520Windows%2520Phone%25207%2520Series.pdf+windows+phone+7+human+interface+guidelines&hl=en&gl=us&pid=bl&srcid=ADGEESjDgT3ywDjqaUzI3V2K9jgDttjbSzj2XbLsSTzDOfFiTmCXOflF1oiRd8pI7Ld_R6HEGKb6IzDGyBEXSqrQgL5Hrsr_vzFlZ0-z0BxhywmEx4haKFrGX9r8gokB_TmsJDqG0jgc&sig=AHIEtbQswcIrqpWDl8j0wljGlixIiUj-tQ&pli=1">Windows Phone 7</a> for their iOS app, instead of following the <a href="http://developer.apple.com/library/ios/#documentation/userexperience/conceptual/mobilehig/Introduction/Introduction.html">iOS</a> HIG guidelines, and the result is just awful.<br /><br />Below is what a modal menu on the Bing app looks like on my iPhone (the rest of the app looks similar, and has the same flaws). I have been using apps on my iPhone for two years and yet it took me several minutes to figure out what I needed to do on that dialog. It wasn't obvious how to toggle options on the menu, nor was it obvious how I could dismiss the dialogs. It took me a good two minutes to realize that the 'done' label was infact a button, and that almost everything on that screen was clickable, except the tiny header that reads "MENU". The font size on that label (page header) is smaller than that on the label-buttons, which sounds inverted, and makes the page look ugly. <br /><br /><div style="text-align:center;"><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj38ZBo8ieKtJw3EXaFyNtThcWQqbcXJXwmcZBk4vj5Y2sk1ibRxQA2q-aKToyH2aYeC3g47tOBV7Qjzy_OC7TtSSkktrNcvLoTLfW26ijfKnSefvG7klj3IfYpr9AqLZZKyl3DnE8z6hY/s1600/IMG_0339.PNG"><img style="margin:0px auto 10px; padding:0 10px; text-align:center;cursor:pointer; cursor:hand;width: 250px; height: 375px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj38ZBo8ieKtJw3EXaFyNtThcWQqbcXJXwmcZBk4vj5Y2sk1ibRxQA2q-aKToyH2aYeC3g47tOBV7Qjzy_OC7TtSSkktrNcvLoTLfW26ijfKnSefvG7klj3IfYpr9AqLZZKyl3DnE8z6hY/s400/IMG_0339.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5566587904071413602" /></a><br /></div><br /><br />Compare this with the image below and notice how obvious and beautiful some of those same options are on the native iPhone mapping application. (This isn't a modal dialog on the native app - if it was, I can guarantee that there would be a blue 'Done' button in the top right corner). <br /><br /><div style="text-align:center;"><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEibCH9_4vwl_vz1sGYW4NocxHij6akxgTN1ySQN4b9g3K0wnMAaXlOAZG91yepCdsocVX_K2El4IDOs_Mg54OH-8HVoYEjSDHJseKVEJer-zY1B96YwQLpWzLRNKCuK1kMOhcnV9t6U-dI/s1600/IMG_0340.PNG"><img style="margin:0px auto 10px; padding:0 10px; text-align:center;cursor:pointer; cursor:hand;width: 250px; height: 375px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEibCH9_4vwl_vz1sGYW4NocxHij6akxgTN1ySQN4b9g3K0wnMAaXlOAZG91yepCdsocVX_K2El4IDOs_Mg54OH-8HVoYEjSDHJseKVEJer-zY1B96YwQLpWzLRNKCuK1kMOhcnV9t6U-dI/s400/IMG_0340.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5566587904566252722" /></a><br /></div><br /><br />In fact, the entire experience of using the Bing app is so jarring that I refuse to use it at all, in spite of some of its nifty features. And I would be very surprised if I'm the only one who feels that way. It literally feels like Microsoft ported an app that is likely beautiful and usable on Windows Phone 7 to iOS without putting any thought into iOS usability. In fact, my guess is that the developers spent more time trying to make those buttons and table-views looks like Windows Phone 7 than they would have if they just used what the system provided with minor tweaks.<br /><br />I cannot come up any reasonable explanation for why the designers of Bing chose to do this. If they want their application to be used, I sincerely hope they fix it soon.Viraj Modyhttp://www.blogger.com/profile/10980120071443522974noreply@blogger.com1tag:blogger.com,1999:blog-2687820185167366196.post-76951009708560138452011-01-21T15:30:00.000-08:002011-01-21T16:58:39.059-08:00On Mediocre Software - Where is the Love?<div>A recurring theme in some of the feedback we've seen for <a href="http://www.audiogalaxy.com/">Audiogalaxy</a> in reviews and direct feedback from users is how surprised they are when everything just works. A lot of the praise that the product has received goes something like this:</div><br /><div style="font-style:italic;padding:5px 20px;">"Having tried and failed with several similar products I was shocked that when I used Audiogalaxy, it worked! No long delays, no dropping back to the initial screen, no anything wrong, just the music playing for as many hours as I needed."</div><br /><div style="font-style:italic;padding:5px 20px;">"It does exactly what it's supposed to do. How often do you see that?"</div><br /><div style="font-style:italic;padding:5px 20px;">"I never write reviews because most apps usually suck, but this is... different."</div><br /><div>I won't feign modesty - we've worked pretty hard to make sure that Audiogalaxy works well. But that's not the point of this post. What statements like the ones above imply is that users are accustomed to underwhelming software. Most people, it seems, approach new software products with skepticism and wariness. Having been bitten by bad software several times in the past, they have become conditioned to assume the worst, and to be surprised when software works as it's expected to. While this might be true of other things too, in this case it points to a collective failure of the software development community. Commoditization of software developers/development and easy software distribution channels seem, on the surface, a boon to the software industry but are also a root cause for this mediocrity that people seem to now expect.</div><br /><div>A telling sign of how extensive this perception is the following email I received from a friend after he used Audiogalaxy:</div><br /><div style="font-style:italic;padding:5px 20px;">"Frankly speaking, my expectations were not very high. I was like, 'it's gonna be yet another process sucking the life out of my machine'. I was expecting to spend at least 30 minutes configuring everything, but was pretty much done in a couple of clicks and listening to music in a couple of minutes. It blew away my expectations in terms of how light weight it was (like 5 MB of memory and close to 0-1% cpu) and also super responsive in the browser to!!!"</div><br /><div>On the surface, this comment seems to fall into the same bucket at the others above. However, this friend is actually a really good software developer himself. So this default notion of 'crappy software' isn't just users' perception. It is a dirty insider secret - we know about it, we expect it and yet we just suck it up and deal with it.</div><br /><div>It's time to fix this, my fellow software geeks. You may say that people will vote with their money, and that bad software will fall by the wayside, and that the wheat will separate from the chaff, yada yada... All that may be true. But by producing crappy software, we're diluting the value of software as a whole, which eventually trickles down and affects each one of us.</div><br /><div>Now this is the part of the post where I really should offer a silver bullet that will 'fix it all' and permanently change users' perceptions. Alas, I'll leave that to folks who are smarter than I. The one thing that I've learnt in my short career, and the only advise I can offer is this:</div><br /><div style="font-style:italic;">Build software with love, nurture it with care, take pride in its successes and share blame in its failures.</div><br /><div>This mantra won't make you write bug free code or automatically make your software user friendly overnight - that's what experience, skills, iteration and good engineering practices are for. Heck, no software developer is anywhere close to writing 100% bug-free code. However, it will guide you in making decisions that lead to good, solid software which does what it says and says what it does. After all, software isn't just code - it is the culmination of every decision made in its journey from its inception to its current state.</div><br /><div>A world where bad software/technology is an anomaly, not the norm, would be a good world - and it starts with us.</div>Viraj Modyhttp://www.blogger.com/profile/10980120071443522974noreply@blogger.com0tag:blogger.com,1999:blog-2687820185167366196.post-86006742931461142192010-12-25T23:25:00.000-08:002010-12-26T11:12:41.076-08:00Off the Grid - Food, Diving, Love and Cholera in ParadiseBula! We recently took a trip to the Fiji Islands, and I took this opportunity to go completely off the grid - no cellphone, no computer, no TV, no newspapers. I was afraid I wouldn't last the week and succumb to withdrawal symptoms, but I decided I would give it my best shot. Not happy with just being off the grid, I decided to bring a book with me. If I read it, it would be <a href="http://crazyviraj.blogspot.com/2008/10/regret-not-reading-enough-books.html">the first book I would read in almost 4 years</a>. I had heard great things about <a href="http://www.amazon.com/Cholera-Penguin-Great-Books-Century/dp/0140119906">Love in the Time of Cholera</a> and it was a genre of books I hadn't ever attempted to read before, so I figured it would be ideal. As luck would have it, the <a href="http://www.kcls.org/bellevue/">King Country public library in Bellevue</a> (a most excellent library, by the way) had the book available to check out.<div><br /></div><div>Well, turns out that the world does continue to turn even if I don't read the news, write some code, catch up on blogs and scan Facebook. </div><div><br /></div><div>It was a great week full of excellent scuba diving, phenomenal tropical foods and magnificent living in resorts bathed in luxury and relaxation. We spent a few days at <a href="http://new.outrigger.com/hotels-resorts/fiji/viti-levu/outrigger-on-the-lagoon-fiji?hotel=37&">Outrigger on the Lagoon</a> near <a href="http://maps.google.com/maps?f=q&source=s_q&hl=en&geocode=&q=Sigatoka,+Western+Division,+Fiji&sll=37.020098,-95.712891&sspn=83.493485,209.53125&ie=UTF8&hq=&hnear=Sigatoka,+Nadroga-Navosa,+Western+Division,+Fiji&z=16">Sigatoka</a> on the main island, Viti Levu. It was larger than I would've preferred, but our plan here was to primarily relax and explore some of the surrounding villages/towns and interact with local Fijians and experience Fijian culture. We did 2 dives at <a href="http://www.dive-the-world.com/diving-sites-fiji-beqa-lagoon.php">Beqa Lagoon</a>, but saved the majority of our diving for the latter half of our trip, which was near the more renowned dive sites. Instead, we spent a day relaxing at the resort and enjoying a local bar and another exploring some not-so-touristed surrounding. We got invited to dinner at a local family's house (our taxi driver, who happened to be a 4th generation Fijian Indian), and it was a humbling experience. Our hosts were a warm-hearted family who lived in a modest home in a tiny village who served us lovingly prepared Fijian dishes. The fish lovo, made with a snapper that was caught the previous night, and vegetable curry made with Taro root and star fruit, were delicious.</div><div><br /></div><div>At the end of this part of the trip, I was done reading about a third of my book, and I hadn't felt the urge to get wired in at all, although internet access was readily available. I read by the pool, on the beach and in the lounge - the book was more interesting than I had imagined, and written in a style that was very refreshing.</div><div><br /></div><div>Our next stop was the <a href="http://www.namalefiji.com/">Namale Resort & Spa</a>, home to the <a href="http://www.blogger.com/Most%20Sexiest%20Villas%20in%20the%20South%20Pacific.">Sexiest Villas in the South Pacific</a>. I knew the resort was good and I had lofty expectations for the price I had paid, but it exceeded my every expectation. From the warm welcome to the teary send-off, I could not have imagined a better way celebrate love. We spent the mornings diving, after having devoured a scrumptious breakfast. We were lucky enough to dive <a href="http://www.youtube.com/watch?v=SWu4DOtkQqY">Chimneys</a> and <a href="http://www.youtube.com/watch?v=gfRYAE9Y-RQ">Grand Central</a> at the <a href="http://www.namena.org/">Namena Barrier Reef</a> on our last day of diving. I cannot imagine a better dive site than Chimneys anywhere in the world. It was magical - I think I could dive Chimneys every day for a year and still be amazed after every dive. After diving, we would partake of the phenomenal 3-course lunch served at a location of our choice (beach, deck, bure... anywhere we felt like eating) and spend the afternoon lazing on the beach, in the spa or indulging in more water activites (snorkel, water ski, etc). After that, some more R&R and drinks to whet the appetite for the gastronomical extravaganza that was dinner, accompanied by island cocktails or wine pairings and the Kava Boys. Every day, we were made to feel special with several personal touches by the staff at the resort - they really treated us like family. If there is paradise, Namale was pretty darn close to what I'd like it to be. </div><div><br /></div><div>My reading progressed surprisingly well, and I found myself carrying my book to the beach, to the spa and to the outdoor jacuzzi. Save for about a 100 pages in the middle, I was enthralled by the story and the writing. I completed the book at the airport lounge prior to departure, and it had lived up to it's hype. It was Gabriel Marquez' ode to the myriad forms of love - spurned love, unrequited love, illicit love, love at first sight, true love, love that grows and fades over time and love that cannot really be classified. I got lucky with this book - I couldn't have picked a more appropriate book for the occasion. The fact it was a literary masterpiece didn't hurt either.</div><div><br /></div><div>Anyways, all good things end, and so did our week in paradise. I hope one day to return to this lap of luxury in the midst of the <a href="http://www.interestingworldfacts.com/general/10-friendliest-countries-in-the-world.html">friendliest people in the world</a>. Until then, it's back to regularly scheduled programming.</div><div><br /></div>Viraj Modyhttp://www.blogger.com/profile/10980120071443522974noreply@blogger.com1tag:blogger.com,1999:blog-2687820185167366196.post-88885023995773519442010-09-28T18:14:00.000-07:002010-09-28T20:11:11.226-07:00Fun facts about PilotsOn a recent flight, I sat alongside a pilot who was returning home to the Seattle area. A lot of interesting things came up in my chat with the pilot, stuff I didn't really know about:<div><ul><li>Pilots fly free on all domestic routes, regardless of airlines. All airlines have an agreement to lets each others' pilots occupy empty seats.</li><li>If no seats are available, the travelling pilot can also occupy an extra seat in the cockpit that is usually empty.</li><li>Pilots retire at 65(!).</li><li>The pilot I spoke with had been flying for 30 years. His pay/benefits today are half what they were 20 years ago (adjusted for inflation).</li><li>Until ~10 years ago, most commercial pilots came from the military (air force/navy). Now, it's about 50% military and 50% from pilot schools.</li><li>For pilots, career all about seniority. If this guy (a captain currently) were to leave his airline and join another airline he would start as a flight engineer. That's one level below first officer, who is below captain. This is a result of unionization.</li><li>Flights longer than 8 hours require 3 pilots (1 captain and 2 first officers) to rotate flying duties. Flights longer than 12 hours require 4 pilots (1 captain and 3 first officers). They usually fly 3-4 hour shifts.</li><li>Pilots at traditional airlines (American, Continental) make more than pilots at startup airlines (Virgin America, Jet Blue).</li><li>Airlines like Horizon Air/American Eagle/etc serve as training grounds for pilots, who then move to the more lucrative airlines.</li><li>On average, pilots fly between 9 and 14 days a month.</li><li>Pilots train on simulators every 9 months, and typically work on hard scenarios like dual engine failures once every couple of years.</li><li>Boeing and MD planes have "steering wheel" type controls. Airbus has fly-by-wire joystick like controls.</li><li>A 737 needs about 6000 feet to land comfortably. Headwind makes a huge difference in amount of runway needed.</li><li>Airbus planes felt more "robust" to this pilot.</li><li>Bird hits are more common than passengers realize.</li><li><a href="http://www.youtube.com/watch?v=iAxAso8xSo0">Tegucigalpa, Honduras</a>, is the hardest airport that he's landed in. Overshooting the 5000 foot runway drops you off a cliff and on to a strip mall. There have been 2 crashes in the last few years, with one dead pilot.</li></ul><div><br /></div></div>Viraj Modyhttp://www.blogger.com/profile/10980120071443522974noreply@blogger.com3tag:blogger.com,1999:blog-2687820185167366196.post-41164316804823219062010-09-09T10:29:00.000-07:002010-09-09T10:37:46.108-07:00Google Instant's biggest win? Teaching users to search better<div>The biggest impact of Google Instant is that it teaches users how to search more effectively. Based on these dynamic results, users can tweak their query such that it helps them find the exact answer they were looking for, even if they started with a bad query. Teaching users how to improve their search query is huge. The immediate impact is that it improves users' perception of Google's search results. Same with speed - users will perceive Google as being much faster than anything else out there because of the instant results.</div><div><br /></div><div>Perception is reality. Speed and relevance are a search engines bread & butter. I think Google just upped the ante by a lot. </div><div><br /></div><div>Credit to Microsoft though - this probably wouldn't have happened if it weren't for Bing's recent gains. Proof that competition provides the best kick-in-the-pants needed to innovate.</div>Viraj Modyhttp://www.blogger.com/profile/10980120071443522974noreply@blogger.com1tag:blogger.com,1999:blog-2687820185167366196.post-36039625182440778622010-09-03T12:30:00.000-07:002010-09-03T16:06:12.296-07:00Integrating Core Data into your Three20 AppThe <a href="http://three20.info/">Three20 library</a> for iPhone app development is pretty cool - it take care of a lot of the 'mundane' code that you might need to write to accomplish basic tasks. Apple really should include some of the functionality of Three20 in the iPhone SDK itself, specially the Network stack.<div><br /></div><div>As I built a more advanced app using Three20, I realized that it would be great to be able to persist to a database to make loading quicker when state on the server is unchanged. I needed some pretty basic Core Data integration in my app, and it was a little annoying that Three20 didn't have a Core Data-backed implementation of TTModel built into it. It seems like a lot of people want this, but there isn't any good post about it I could find. Jeff Verkoey, who currently maintains Three20, started writing a <a href="http://github.com/jverkoey/Three20-Tutorials">Core Data based tutorial</a> but didn't get around to actually doing much with it. And there are a few threads in the Three20 Google group but nothing too comprehensive.</div><div><br /></div><div>Having built this for my app, I figured I would post some more concrete information here for others who might find it helpful. I really want to just contribute a clean cut of this code as a <code>TTCoreDataModel</code> with unit tests to the Three20 source, but I don't have the luxury to spend time on it currently.</div><div><br /></div><div>So here's a quick step-by-step on how to modify your existing Three20 based app to add basic Core Data support. The specific example I use fetches data from a server using <code>TTURLRequestModel</code> and writes it out to SQLite using Core Data entities. When displaying data to the user, it fetches data from Core Data using a <code><a href="http://developer.apple.com/iphone/library/documentation/CoreData/Reference/NSFetchedResultsController_Class/Reference/Reference.html">NSFetchedResultsController</a>,</code> which efficiently interacts with Core Data.</div><div><br /></div><div><u>Step 1:</u><br />Add CoreData.framework to your list of dependencies. Then, add the Core Data stack. Usually, you would do this in the AppDelegate. Jeff's <a href="http://github.com/jverkoey/Three20-Tutorials/tree/master/github/Classes/">AppDelegate files</a> have this implemented, if you want an example. In my case, I wanted different databases for different Models, so I had these in my TTModel implementations.</div><div><br /></div><div><u>Step 2:</u></div><div>Create your model. Add a new file of type "Data Model" in XCode (available in the 'Resource' section). Make sure the .xcdatamodel file gets added to your Target so that it's compiled into your binary. Look <a href="http://developer.apple.com/mac/library/documentation/DeveloperTools/Conceptual/XcodeCoreDataTools/Introduction/Introduction.html">here</a> for information on using the modeling tool. Once you've created your model, <a href="http://developer.apple.com/mac/library/documentation/DeveloperTools/Conceptual/XcodeCoreDataTools/Articles/xcdCodeGeneration.html#//apple_ref/doc/uid/TP40006872-SW1">import the code for the Entity objects</a> into your project. Now you're ready to start using Core Data.</div><div><br /></div><div><u>Step 3:</u></div><div>Update your TTURLRequestModel implementation. Let's assume you're simply downloading some JSON and want to write it out to Core Data as an Entity you created in Step 2. Update your <code>(void)requestDidFinishLoad:(TTURLRequest*)request</code> implementation of <code>TTURLRequestDelegate</code> as follows:</div><br /><br /><iframe src="http://pastebin.com/embed_iframe.php?i=yJebq7Mz" style="border:none;width:100%"></iframe><br /><br /><div>A few notes about the code above:</div><div><ul><li><code>DBResource</code> is my simple entity containing an "id" and a "name".</li><li>The<code> NSAutoreleasePool</code> helps clean up memory allocated in this tight loop here</li><li><code>-reset</code>ing the context makes sure that the data <a href="http://developer.apple.com/mac/library/documentation/Cocoa/Conceptual/CoreData/Articles/cdFaultingUniquing.html">faults in</a> as needed instead of staying in memory when you read it using the <code>NSFetchedResultsController</code></li></ul></div><div><u>Step 4:</u></div><div>In your <code>TTTableViewDataSource</code> implementation, create a <code>NSFetchedResultsController</code> and configure it to fetch your Entities as needed. Then, override the <code>(id)tableView:(UITableView*)tableView objectForRowAtIndexPath:(NSIndexPath*)indexPath</code> method to return a <code>TTTableItem</code> built using the Entity you fetched from Core Data</div><br /><br /><iframe src="http://pastebin.com/embed_iframe.php?i=zYXEADhb" style="border:none;width:100%"></iframe><br /><br /><div>Now, your TTTableViewDataSource is configured to read results from the DB you created and display them in your view. Remember that <code>tableViewDidLoadModel</code> can be called multiple times, so clean up any previously allocated <code>NSFetchedResultsController</code> if you need to.</div><div><br /></div><div><u>Step 5:</u></div><div>Implement your logic to delete/update your Core Data entities. Depending on how your app uses data persistence, you want to ensure that you update/delete your entities from Core Data. For instance, if you always want to fetch data from the server and load them into Core Data on first run, then update Step 3 to first delete all entities. If you wish to show local data when available but fetch when not, then update Step 3 to not hit the server before checking locally. When deleting entities, remember that Core Data only lets you delete a single entity at a time (so you must fetch all and loop through each to delete it). If you want <code>DROP TABLE ...</code> semantics you're going to need to maintain different <code>NSManagedObjectContent</code> and <code>NSPersistentStoreCoordinator</code> for each such table, and then simply delete the database file.</div><div><br /></div><div>So, this isn't too hard really. Assuming you're familiar enough with building your Three20 app, my notes above should help you get started with integrating Core Data into your app. My app uses a <code>TTSectionedDataSource</code>, and with a few tweaks to <code>NSFetchedResultsController</code> it's simple to do that too.</div>Viraj Modyhttp://www.blogger.com/profile/10980120071443522974noreply@blogger.com11tag:blogger.com,1999:blog-2687820185167366196.post-75194543765424592912010-08-07T08:31:00.000-07:002010-08-07T09:38:17.102-07:00An Apple A Day Keeps the Bugs AwayI've been working from home for the last year or so, and it has tremendously increased my productivity as a developer. Besides the obvious reasons (lack of distractions, no meetings, etc) there is one other advantage that I haven't often heard people mention before.<div><br /></div><div>I have been able to find solutions to some really hard technical problems much quicker, and I attribute it to the fact that I work from home instead of in a more office-like environment.</div><div><br /></div><div>Most software engineers will tell you that some of their best ideas have come to them when doing the most mundane of things - taking a shower, strolling across lawns, eating a snack and so on. I don't know the psychology behind this, but I know it's true for me. Something about being alone and thinking about your problem subconsciously, and yet being physically distracted with an unrelated task seems to work wonders. I have solved more tough problems while in the shower, when sitting down to eat an apple, when watering the plants or simply doing the dishes than I have sitting at a desk and just thinking. So obviously, being at home makes it much easier for me to find one of these mundane tasks that let me 'detach' and yet perform an activity that is beneficial to me both personally and professionally.</div><div><br /></div><div>It's become a pretty important weapon in my software development arsenal - if I'm stuck on something for more than 30 minutes, I get out of my workspace and go do something mundane around the house or eat a quick snack. More often than not a solution magically appears in my head. </div><div><br /></div><div>Back when I worked in an office, if I was stuck trying to solve a hard problem my best option was to find someone familiar enough with the problem space and bounce ideas off of them. This worked pretty well initially but didn't scale too well with problems that became narrower and deeper in scope - I often found myself spending a lot of time trying to catch others up to where it would be valuable to bounce ideas off them. And heading off into the office kitchen to reorganize stuff or watering random plants around the office would just make me look weird.</div><div><br /></div><div>So, the next time you're having a hard time finding a solution to a problem in the office think about taking the next day off and working from home. Oh, and make sure the dishes aren't done. ;)</div>Viraj Modyhttp://www.blogger.com/profile/10980120071443522974noreply@blogger.com0tag:blogger.com,1999:blog-2687820185167366196.post-56619008987636079982010-06-02T00:50:00.000-07:002010-06-02T10:28:20.332-07:00Slow onclick Javascript handling in Google Chrome 5.0.375.55<span class="blsp-spelling-error" id="SPELLING_ERROR_0">Google's</span> Chrome has been a pretty slick browser, and it's speed was the primary reason it became my default browser. The <a href="http://code.google.com/p/v8/">V8 Javascript engine</a> supposedly blows other engines out of the water, and it did feel noticeably faster than others - until this latest version.<div><br />Sometime last week, my version of Chrome was auto updated to 5.0.375.55, and I noticed that the website I've been developing became terribly slow. Because of <a href="http://code.google.com/p/chromium/issues/detail?id=33441">a previous Chrome bug</a>, I hadn't migrated my site from <a href="http://jquery.com/"><span class="blsp-spelling-error" id="SPELLING_ERROR_1">jQuery</span></a> 1.3 to 1.4, so one of the first things I did was verify the fix and then update my site to use the newer, faster <span class="blsp-spelling-error" id="SPELLING_ERROR_2">jQuery</span>. As I began testing, I noticed a significant reduction in performance of some features on my site.<br /><br />Initially I suspected it had something to do with <span class="blsp-spelling-error" id="SPELLING_ERROR_3">jQuery</span> upgrade. After a few hours of changing things around, I was pretty convinced the problem wasn't <span class="blsp-spelling-error" id="SPELLING_ERROR_4">jQuery</span> 1.4. I noticed that <span class="blsp-spelling-error" id="SPELLING_ERROR_5">Firefox</span> and even IE 8 didn't exhibit this latency, so now I started to suspect the new version of Chrome. I hacked up a couple of test pages, and I think I found the culprit. It looks like the <span class="blsp-spelling-error" id="SPELLING_ERROR_6">onclick</span> handler's performance degrades with the size of the HTML page, resulting in a very noticeable (1-2 second) lag once you have a table with 3000/4000 rows. <span class="blsp-spelling-error" id="SPELLING_ERROR_7">Firefox</span> and IE 8 don't seem to have any trouble handling this.</div><div><br /></div><div>I've logged <a href="http://code.google.com/p/chromium/issues/detail?id=45620">Issues 45620</a> in the Chromium project with details and a <span class="blsp-spelling-error" id="SPELLING_ERROR_8">repro</span> to track this problem. This definitely isn't good news for me - with growing adoption of Chrome and general perception that Chrome is the fastest browser, problems like these make website look faulty even when they might not be.</div><div><br /></div><div>As an aside, I have noticed that Chrome feels like it has regressions more often than other browsers (perhaps a function of shipping more often?) I've run into several issues developing for Chrome - the silver lining is that bug fixes are quicker than rival browsers, but better <span class="blsp-spelling-error" id="SPELLING_ERROR_9">QA</span> before release would certainly help developers.</div><div><br /></div><div>Other Chromium issues that I've had trouble with and have had to work around are <a href="http://code.google.com/p/chromium/issues/detail?id=7771">Issue 7771</a>, <a href="http://code.google.com/p/chromium/issues/detail?id=30693">Issue 30693</a>, <a href="http://code.google.com/p/chromium/issues/detail?id=33441">Issue 33411</a> and <a href="http://code.google.com/p/chromium/issues/detail?id=36559">Issue 36559</a>.</div><div><br /></div><div><b>Update</b>: An <a href="http://code.google.com/p/chromium/issues/detail?id=45620#c1">comment on Issue 45620</a> identifies one of the reasons for the slowdown and a workaround.</div>Viraj Modyhttp://www.blogger.com/profile/10980120071443522974noreply@blogger.com2tag:blogger.com,1999:blog-2687820185167366196.post-57146416333221828112010-05-19T01:26:00.000-07:002010-05-19T02:07:01.322-07:00Showing the Cancel button in UIActionSheets on iPadIn it's <a href="http://developer.apple.com/iphone/library/documentation/General/Conceptual/iPadHIG/Introduction/Introduction.html#//apple_ref/doc/uid/TP40009446"><span class="blsp-spelling-error" id="SPELLING_ERROR_0">iPad</span> Human Interface Guidelines</a> Apple provides several guidelines about how user experience should differ between <span class="blsp-spelling-error" id="SPELLING_ERROR_1">iPhones</span> and <span class="blsp-spelling-error" id="SPELLING_ERROR_2">iPads</span>. One such element that should behave differently is the <a href="http://developer.apple.com/iphone/library/documentation/General/Conceptual/iPadHIG/UIElements/UIElements.html#//apple_ref/doc/uid/TP40009446-CH6-DontLinkElementID_32"><span class="blsp-spelling-error" id="SPELLING_ERROR_3">UIActionSheet</span></a>.<div><br /></div><div>Apple recommends that cancel buttons not be shown for action sheets on <span class="blsp-spelling-error" id="SPELLING_ERROR_4">iPad</span>:</div><div><span class="Apple-style-span" style=" ;font-family:'Lucida Grande', Geneva, Helvetica, Arial, sans-serif;font-size:13px;"><blockquote>Because taps outside the popover dismiss the action sheet without selecting an item, this results in a default way to cancel the sheet. Including a cancel button would therefore only cause confusion.</blockquote></span></div><div>But the guidelines do allow exceptions to this rule:</div><div><span class="Apple-style-span" style=" ;font-family:'Lucida Grande', Geneva, Helvetica, Arial, sans-serif;font-size:13px;"><blockquote>However, if you have an existing popover and are displaying an action sheet on top of other content using an animation, a cancel button is still appropriate.</blockquote></span></div><div>So you would think following this guideline is as simple as not specifying a cancel button in most cases, and only specifying a cancel button when you have carefully considered the exception case, right? Wrong. Even when you do specify a cancel button, it looks like the <span class="blsp-spelling-error" id="SPELLING_ERROR_5">iPad</span> <span class="blsp-spelling-error" id="SPELLING_ERROR_6">runtime</span> <i>automatically always hides it</i>.</div><div><br /></div><div>So how do you provide a cancel button in the exception case? You could just provide another normal button that has cancel behavior, but that's not ideal because you really want the difference in appearance that <span class="blsp-spelling-error" id="SPELLING_ERROR_7">UIActionSheets</span> on iPhone provide by default. After some tinkering, the best solution I could find was to set the <span class="Apple-style-span" style=" ;font-family:Courier, Consolas, monospace;font-size:13px;"><a href="http://developer.apple.com/iphone/library/documentation/uikit/reference/UIActionSheet_Class/Reference/Reference.html#//apple_ref/occ/instp/UIActionSheet/actionSheetStyle" style="text-decoration: none; "><span class="Apple-style-span" style="color:#3366FF;"><span class="blsp-spelling-error" id="SPELLING_ERROR_8">actionSheetStyle</span></span></a><span class="Apple-style-span" style="color:#3366FF;"> </span></span>property of the <a href="http://developer.apple.com/iphone/library/documentation/uikit/reference/UIActionSheet_Class/Reference/Reference.html"><span class="blsp-spelling-error" id="SPELLING_ERROR_9">UIActionSheet</span></a> to <span class="Apple-style-span" style="color:#3366FF;"><a href="http://developer.apple.com/iphone/library/documentation/uikit/reference/UIActionSheet_Class/Reference/Reference.html#//apple_ref/doc/c_ref/UIActionSheetStyleBlackOpaque" style="text-decoration: none; "><span class="blsp-spelling-error" id="SPELLING_ERROR_10">UIActionSheetStyleBlackOpaque</span></span></a>.</div><div><br /></div><div>I can speculate why this works, but I don't know for certain so I'd rather not. If you know of a better/more correct way to do this, please do leave a comment.</div><div><br /></div><div>I am surprised that Apple was mindful enough to list the exception case, and yet careless enough to not provide documentation on how to handle this case - they even seem to have forgotten to document that the <span class="blsp-spelling-error" id="SPELLING_ERROR_11">iPad</span> automatically overrides user-provided cancel buttons by default.</div>Viraj Modyhttp://www.blogger.com/profile/10980120071443522974noreply@blogger.com2tag:blogger.com,1999:blog-2687820185167366196.post-14966695584623227772010-05-08T18:21:00.000-07:002010-05-10T09:35:21.266-07:00Notes from a Week in Paris<div>I just got back from a lovely week in Paris with my wife - it was a rejuvenating and much required holiday for both of us. Work has been crazy, for her especially. Paris was wonderful and more than lived up to my expectations. We rented a tiny little studio in <a href="http://www.parismarais.com/welcome-to-le-marais.htm">Le Marais</a> (~120 sq ft, fully equipped including a washer - extremely efficient use of space!) Spending the entire week gave us enough time to explore sights, sounds and tastes of Paris without too much of a rush.<br /><br /></div><div>There are lots of books and blogs about visiting Paris and things to do/see. We used a bunch of resources - advice from friends, <a href="http://www.lonelyplanet.com/campaigns/iphone/">Lonely Planet's excellent iPhone app</a>, the very insightful <a href="http://www.secretsofparis.com/">Secrets of Paris</a> website with lots of tips on what to see and do and <a href="http://parisbytrain.com/">Paris By Train</a> for everything related to using Paris' excellent rail network. However, there were some things I hadn't read about or was surprised by:</div><div><ol><li>Cafés, brasseries and boulangeries/patisseries often have different prices for take away (<i>emporter</i>), eating inside (<i>sallé</i>) and terrace dining. Most cafés/brasseries offer set menus (<i>Formulae</i>) and chef's recommendations which change daily (menu and price). They are almost always much more cost effective than ordering a-la-carté.<br /><br /></li><li>The French are very proper. You greet business owners when you enter their store/café and thank/greet them before you leave. Sprawling across lush, green lawns in the several Parisian parks (<i>Jardins</i>) or putting your feet up on empty train seats is frowned upon. You sit in chairs provided in the <i>Jardins</i> and sit up straight in trains.<br /><br /></li><li>Freshly squeezed orange juice (<i>jus d'orange pressé</i>) is extremely popular. You'll find orange juice machines pretty much everywhere (including Starbucks), but it's best to find a green-grocer/fruit vendor and get a half-litre jar of fresh squeezed juice each morning. Fruit vendors are everywhere.<br /><br /></li><li>By default, coffee means espresso. A cappuccino/café-au-lait at most Cafés is expensive (between 4-8 EUR) and isn't that great (for a Seattlelite, at least). A mocha at Starbucks is EUR 4.50. Little créperies or street cafés have better deals for coffee that tastes identical (1-2 EUR). Or, hit up a <a href="http://www.mccafecoffee.com/">McCafé</a>. A 'mocha' is called a 'chocochino' and isn't commonly available.<br /><br /></li><li>Sit-down créperies are totally not worth the price/experience. Get a crépe from a street vendor/corner shop and sit in a park or a street bench. The more exotic crépes (savory & sweet) are only available in nicer establishments, obviously.<br /><br /></li><li>Wine/liquor is available in grocery stores (<i>supermachés</i>). <i>Supermachés</i> open around 8:30am and shut around 8:30pm. You are expected to bag your own groceries. For late-night grocery runs, there's usually a little-store-that-sells-most-things every few blocks which stays open late (2am)<br /><br /></li><li>Bars have later Happy Hours than in the US. Happy Hour generally runs from 6pm to 9-10pm. Folks eat dinner late (most restaurants stay open until 11pm).<br /><br /></li><li>Time is specified using 24 hour notation. "What time do you shut?" is answered with "23 hours". Most people don't understand am/pm.<br /><br /></li><li>Take binoculars to fully appreciate the amazing art/architecture details.<br /><br /></li><li>Use the '<a href="http://www.velib.paris.fr/">Vélib</a>' shared bike service. Vélib stations are everywhere, the roads and drivers are biker-friendly and it is the cheapest and most convenient way to get between locations in the city. Buy a map-book that has streets sorted by districts (<i>arrondissements</i>) and also has metro/Vélib stations marked.<br /><br /></li><li>It is not common for <i>crémeries</i>/gelaterias to offer tastes.<br /><br /></li><li>On the metro, or just walking around, you'll see a significant number of people going home (presumably) with one or more fresh baguettes from the bakery. For some reason almost all of these people are men. Join the crowd - fresh baguettes are the only way to go. Bread is baked fresh twice a day (if not continuously).<br /><br /></li><li>When you pay cash, check the change you get back. Parisians are either terrible at math or prone to short-changing customers. I routinely got back incorrect change, on average twice a day.<br /><br /></li><li>Before you go to a specific place (café, boulangerie, museum, grocery store, monument) on a Saturday/Sunday/Monday check before you head out. Almost every establishment is closed on at least one or more of these days.<br /><br /></li><li>Strange, English speaking 'beggar women' holding hand-written post cards will randomly approach you at monuments/museums and ask "Speak English?". I didn't quite figure out what the scam was (I never answered "Yes") but this is most certainly some sort of scam.<br /><br /></li><li><a href="http://en.wikipedia.org/wiki/Comt%C3%A9_(cheese)">Comté</a> tastes awesome, and is the most popular cheese. <a href="http://en.wikipedia.org/wiki/Camembert">Camembert</a> is a close second. Get cheese fresh from your neighborhood <i>fromagerie</i>.<br /><br /></li><li>If you decide to cook at home, chances are good that you'll find an Italian store nearby selling freshly prepared pastas and raviolis that you can cook at home, in addition to a large number of other Italian foods (including freshly prepared Tiramisu).</li></ol></div>Viraj Modyhttp://www.blogger.com/profile/10980120071443522974noreply@blogger.com0tag:blogger.com,1999:blog-2687820185167366196.post-60402038168032632462010-04-19T06:40:00.000-07:002010-04-19T06:40:00.659-07:00Why I use Google Voice for SMSI have an iPhone with AT&T service, but I recently started using Google Voice for most of my SMS needs. SMSes sent to my Google Voice number are forwarded to my email. It works great for me:<div><ul><li>I don't want to pay AT&T any more than I have to. I use AT&T out of necessity, not choice.<br /><br /></li><li>GMail has support for Exchange ActiveSync, and is the default Exchange account on my iPhone. So I get notified of the SMS in email on the phone as soon as it is received (assuming I have data coverage, which is pretty much all of the time) giving me the equivalent of the native SMS experience.<br /><br /></li><li>The <a href="https://chrome.google.com/extensions/detail/kcnhkahnjcbndmmehfkdnkjomaanaooo">Google Voice extension for Chrome</a>. Chrome is my default browser, I'm in front of a computer for a large part of the day, and my browser is usually open. I can read, send and reply to SMSes using a full keyboard and without exiting the browser, instead of pulling out my iPhone and fiddling with the tiny keyboard to type out a message. So much more convenient!<br /><br /></li><li>All my SMSes are archived and made searchable not only by Google Voice, but also by GMail. Win.</li></ul></div>Viraj Modyhttp://www.blogger.com/profile/10980120071443522974noreply@blogger.com0tag:blogger.com,1999:blog-2687820185167366196.post-10320308667869993702010-04-18T11:07:00.000-07:002010-04-18T19:19:15.362-07:00Living Every Developer's Wet DreamBack in June I wrote about <a href="http://crazyviraj.blogspot.com/2009/06/new-technology-for-me-roundup.html">new technologies I had started working with</a> and my experiences learning those technologies. Having continued working with most of those since then, I've become incredibly comfortable and proficient with them. It's pretty normal to have a week where I write a bunch of <span class="blsp-spelling-error" id="SPELLING_ERROR_0">PHP</span>, a few <span class="blsp-spelling-error" id="SPELLING_ERROR_1">SQL</span> queries, some fancy Javascript, learn new <span class="blsp-spelling-error" id="SPELLING_ERROR_2">CSS</span> tricks, hack up features in client software using Win32/Cocoa, debug some corner-case issue using logs and a mini-dump and also write some straight up C/C++ server side code with unit tests. Initially, jumping languages and client/server boundaries came with large context-switch penalties but it's become almost second nature now, which feels very empowering and has had a dramatic impact on productivity.<div><br /></div><div>Then, there are the one-offs - new technologies that need to be learnt, integrated into the product/service (in a way that's easy to understand/change in the future) and then 'forgotten' before moving on to the next one. These tend to be client-side technologies usually - server side stuff is much more on-going and stable. Some of the 'bigger' things I've done in the last 6-9 months that would fall into this one-off category are:</div><div><ul><li><u>Fiddling around with HTML5 &<span class="blsp-spelling-error" id="SPELLING_ERROR_3">lt</span>;audio> and &<span class="blsp-spelling-error" id="SPELLING_ERROR_4">lt</span>;video> before giving up</u><br />For all the promise of new HTML5 multimedia standards, the sad reality is that we are far from any consensus. Different stakeholders have different motivations, which leaves the issue of a <a href="http://lists.whatwg.org/htdig.cgi/whatwg-whatwg.org/2009-June/020620.html">single standard <span class="blsp-spelling-error" id="SPELLING_ERROR_5">codec</span> up in the air</a>. The proprietary nature of <a href="http://www.adobe.com/products/flash/">Flash</a>, licensing issues surrounding <a href="http://en.wikipedia.org/wiki/H.264/MPEG-4_AVC">H.264</a>, Apple's opposition to including <a href="http://en.wikipedia.org/wiki/Ogg_Theora"><span class="blsp-spelling-error" id="SPELLING_ERROR_6">Ogg</span> <span class="blsp-spelling-error" id="SPELLING_ERROR_7">Theora</span></a> in <span class="blsp-spelling-error" id="SPELLING_ERROR_8">Quicktime</span> and Microsoft's silence have resulted in the current stalemate. And this shows when you experiment with the different browsers. Development for web/mobile-web is poised to become radically easier once/if this is sorted out, but there is a lot of pain to be endured before then - pain that wasn't worth it to me presently.<br /><br /></li><li><u>Flash audio support</u><br />Having decided to not want to build a flash media <span class="blsp-spelling-error" id="SPELLING_ERROR_9">plugin</span> myself, I first played around with <a href="http://flash-mp3-player.net/">this MP3 Player</a> and later switched to the much more powerful <a href="http://www.schillmania.com/projects/soundmanager2/"><span class="blsp-spelling-error" id="SPELLING_ERROR_10">SoundManager</span>2</a>. With a few tweaks, I was able to make it do exactly what I wanted.<br /><br /></li><li><a href="http://wiki.developers.facebook.com/index.php/Facebook_Connect"><span class="blsp-spelling-error" id="SPELLING_ERROR_11">Facebook</span> Connect</a><br />For single sign-on and any sort of social media integration, <span class="blsp-spelling-error" id="SPELLING_ERROR_12">Facebook</span> Connect is a must have. Having implemented <span class="blsp-spelling-error" id="SPELLING_ERROR_13">Facebook</span> Connect for my website and for <a href="http://wiki.developers.facebook.com/index.php/Facebook_iPhone_SDK">iPhone</a>, I must say that I am very pleased with the facilities offered by the platform and the consistency across client/server applications. <a href="http://wiki.developers.facebook.com/index.php/XFBML"><span class="blsp-spelling-error" id="SPELLING_ERROR_14">XFBML</span></a> and <a href="http://wiki.developers.facebook.com/index.php/FQL"><span class="blsp-spelling-error" id="SPELLING_ERROR_15">FQL</span></a> are nice touches. My only gripe is that, for a mature and widely adopted platform, documentation seems disproportionately poor as compared to the richness of the Platform <span class="blsp-spelling-error" id="SPELLING_ERROR_16">APIs</span>.<br /><br /></li><li><u>Online Payments</u><br />There's a lot you learn about how safe online transactions really are (<i>they're not) </i>when you dive into how payments really work. I developed a quick payment portal using <a href="https://www.paypal.com/cgi-bin/webscr?cmd=_payflow-pro-overview-outside"><span class="blsp-spelling-error" id="SPELLING_ERROR_17">Payflow</span> Pro</a> by <span class="blsp-spelling-error" id="SPELLING_ERROR_18">PayPal</span>. Can't really vouch for <span class="blsp-spelling-error" id="SPELLING_ERROR_19">Payflow</span> Pro yet, though. A few weeks after I finished working with <span class="blsp-spelling-error" id="SPELLING_ERROR_20">Payflow</span> Pro, <a href="https://www.x.com/index.jspa"><span class="blsp-spelling-error" id="SPELLING_ERROR_21">PayPal</span> X</a> was announced. I'll eventually get around to migrating to it, I guess.<br /><br /></li><li><a href="http://en.wikipedia.org/wiki/Identicon"><span class="blsp-spelling-error" id="SPELLING_ERROR_22">Identicons</span></a><br />Simple, cute, pretty. I use the ones provided by <a href="http://blog.gravatar.com/2008/04/22/identicons-monsterids-and-wavatars-oh-my/"><span class="blsp-spelling-error" id="SPELLING_ERROR_23">Gravatar</span></a>.<br /><br /></li><li><u>Javascript Craziness</u><br />One of the crazier things I've done, among others, with Javascript is to try and emulate a desktop-software like user experience in a website. Dragging, dropping, keyboard navigation, sorting, multi-select with <span class="blsp-spelling-error" id="SPELLING_ERROR_24">Ctrl</span>/Shift/Meta... oh boy! It look a while, but with some help from <a href="http://jqueryui.com/"><span class="blsp-spelling-error" id="SPELLING_ERROR_25">jQuery</span> <span class="blsp-spelling-error" id="SPELLING_ERROR_26">UI</span></a> (the different components work great individually, but try to also make a sortable a <span class="blsp-spelling-error" id="SPELLING_ERROR_28">draggable</span> and a selectable, and you're in a world of hurt) and a lot of long nights, I finally got a lot of this done. For <span class="blsp-spelling-error" id="SPELLING_ERROR_29">Forefox</span>. Then I spent some time making it work for Chrome. And then a whole bunch of more time making it work for IE8. It sorta works in IE7, and not at all in IE6, but honestly if you're still using those browsers you should be chastised. Someday, I want to wrap all this into my own little <span class="blsp-spelling-error" id="SPELLING_ERROR_30">plugin</span> that others can use. If you told me a year ago that I would be some kind of <span class="blsp-spelling-error" id="SPELLING_ERROR_31">JS</span> nut, I would have laughed. Ha!<br /><br /></li><li><u>iPhone web-app (before I ditched it for a full blown iPhone app)</u><br />Not wanting to deal with Apple and their strange <span class="blsp-spelling-error" id="SPELLING_ERROR_32">AppStore</span> practices, I decided to try and be really creative with native <span class="blsp-spelling-error" id="SPELLING_ERROR_33">Quicktime</span> support built into Safari on the iPhone. I almost got all the way to hacking up really neat functionality, complete with callback from <span class="blsp-spelling-error" id="SPELLING_ERROR_34">Quicktime</span> into my website, background playback and an rich interactive experience. With Flash coming soon to other mobile platforms, this would have been ideal for providing mobile experiences without needing multiple platform-specific applications. However, the iPhone web app felt clumsy and not polished, so I ditched it and went the iPhone app route. I reserve comment on my experiences with development for the iPhone - my app hasn't been submitted/approved yet. ;)<br />I also foresee myself playing with other mobile development platforms in the very near future. I think a blog post comparing my experiences with the different platforms might make for good reading.</li></ul></div><div><div>In addition to these one-offs, there are the perpetual how-will-it-scale and how-will-it-perform issues that need to be considered for every little feature. One of the harder lessons for me was to change my default mindset about how I approached design. While I wouldn't say I optimized prematurely, my design was heavily influenced by potential for optimization. (segue: This, I now realize, is a result of my years at Microsoft. At companies like Microsoft, you do things once and you do them right - rapid iteration is often not possible or not practical. So, while this approach has its merits it's not ideal for agility in <span class="blsp-spelling-error" id="SPELLING_ERROR_35">startup</span> environments). The new approach that's imbued into me now is to build it quick and make it simple first. Simplicity is the bedrock of agility. Simpler code is easier to revisit and to scale. Complexity is technical debt that you don't really need or want.</div><div><br /></div><div>What's the biggest thing I've learnt in the last year? I learnt how to learn efficiently. </div><div>You can spend hours studying/learning about something and barely scratch the surface. Or you can study to retain information for very short periods of time. Exceptional developers are able to identify when they need to deep-dive vs when they need to simply skim the surface. They can instinctively answer the "How do you learn enough to make well informed decisions and yet don't spend a disproportionate amount of time learning without producing?" question. Transient learning is only good when you never need to revisit a decision - such cases are rare. Otherwise, learning to execute quickly and leaving a trail behind you is a crucial skill. I believe I'm well on my way down this path.</div><div><br /></div><div>So yes, in many ways I am living a developer's wet-dream. And fortunately, I am able to do a little more than just development stuff. But I know this probably won't last forever. This series of blog posts is my attempt to capture my memories for when I'm not doing this everyday anymore.</div></div>Viraj Modyhttp://www.blogger.com/profile/10980120071443522974noreply@blogger.com0tag:blogger.com,1999:blog-2687820185167366196.post-39830197708761013312010-03-05T09:06:00.000-08:002010-03-05T13:31:20.358-08:00SecureCRT and Windows 7 - The Case of the Magically Disappearing Window<div>Ever since I upgraded my copy of <a href="http://www.vandyke.com/products/securecrt/index.html">SecureCRT </a>to 6.5.1 on Windows 7 x64, I started noticing really strange behavior. Occasionally, my SecureCRT window would just 'disappear'. What do I mean by 'disappear'? The SecureCRT icon on the Windows 7 taskbar, which appears like this when a window is open:</div><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj0JNBcpQXWRCUG7m85yiP0XBSG5TcCoccN9jizaX9VVJNjHKD70aFNAfR2mgws2KYOuT8TEDKnPdyy2oKGI4yHAlhkpW2OZo0z7Oh071bNAkLrYItQ9WTxlJ7M1iesZXKQjkuOZZ9-FHs/s1600-h/SecureCRT1.PNG"><img style="cursor:pointer; cursor:hand;width: 85px; height: 54px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj0JNBcpQXWRCUG7m85yiP0XBSG5TcCoccN9jizaX9VVJNjHKD70aFNAfR2mgws2KYOuT8TEDKnPdyy2oKGI4yHAlhkpW2OZo0z7Oh071bNAkLrYItQ9WTxlJ7M1iesZXKQjkuOZZ9-FHs/s320/SecureCRT1.PNG" border="0" alt="" id="BLOGGER_PHOTO_ID_5445199052878407986" /></a><br />would suddenly change to this, which is what an icon looks like when the process has no windows open:<br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiFuKe7-F66mvmjCIm25v2eFatNPJjzzntAndtJNraTNopyon7bruyxgKx_U9Xhx-mvXDEvc1lNtC1BLoZauRIwPEt1ciW8Mb0yvE1mdKSda_pm6NAesymNMsQRmwkdoq99JDytQKbUMc4/s1600-h/SecureCRT2.PNG"><img style="cursor:pointer; cursor:hand;width: 63px; height: 48px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiFuKe7-F66mvmjCIm25v2eFatNPJjzzntAndtJNraTNopyon7bruyxgKx_U9Xhx-mvXDEvc1lNtC1BLoZauRIwPEt1ciW8Mb0yvE1mdKSda_pm6NAesymNMsQRmwkdoq99JDytQKbUMc4/s320/SecureCRT2.PNG" border="0" alt="" id="BLOGGER_PHOTO_ID_5445199386887876114" /></a><br /><br /><div>I could still see SecureCRT.exe running, but didn't find any windows for it, even in the Alt+Tab menu. Nothing appeared in my system tray either except for the network connection, volume and system time information.</div><div><br /></div><div>Once this happened even after a system restart (isn't that the usual fix for everything?! ;)) I got curious. Eventually, I realized that this happened each time I minimized the window. Somehow, minimizing the window is muscle memory for me now and I don't really notice I'm doing it until I consciously try.</div><div><br /></div><div>Minimizing a windows shouldn't ever cause the window to just 'disappear' so I started looking at the different SecureCRT settings and noticed a curious "Minimize to Activator" setting that was checked, and seemed suspicious. Unchecking it was all it took to restore sanity. </div><div><br /></div><div>What is this 'Activator'? It's an icon that sits in your system tray. But Windows 7, by default, <a href="http://helpdeskgeek.com/windows-7/windows-7-system-tray/">hides away all my system tray icons</a> so I had no idea this even existed until I started investigation this issue. This is the 'Activator':</div><div><br /></div><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg6ixWyNCxKO_hEdGMAflFBcnyRlb2c9lTf-0SSMojHL31SnwlTHyBfBVtoL5DS1ifR9HCl7apKtEkQslibinlvzmhRcZPs6DSj3BokmheM_L_VRfS3uTEWBtuBM3sPQR56EiK8Ufyc7gk/s1600-h/SecureCRT3.png"><img style="cursor:pointer; cursor:hand;width: 128px; height: 174px;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg6ixWyNCxKO_hEdGMAflFBcnyRlb2c9lTf-0SSMojHL31SnwlTHyBfBVtoL5DS1ifR9HCl7apKtEkQslibinlvzmhRcZPs6DSj3BokmheM_L_VRfS3uTEWBtuBM3sPQR56EiK8Ufyc7gk/s320/SecureCRT3.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5445204388431652818" /></a><div><br /></div><div>SecureCRT removes the window and sticks it into this Activator. Without any sort of visual cue or notification bubble. By default. Ugh! To restore it, you need <i>right click</i> the icon and select the "Restore" option. Umm... isn't that the whole point of Windows' built-in minimize implementation?</div><div><br /></div><div>This seems like a poorly designed feature, especially given how Windows 7 treats tray icons (and I know SecureCRT v6.5.1 has some Windows 7 specific features so this isn't accidental). Making the process 'appear' like it's not running and sticking into into the 'Activator' (why isn't it called 'system tray icon'?) instead seems like bad design. If anything, this should definitely not be on by default. </div><div><br /></div><div><i>Ed: Title inspired by <a href="http://blogs.technet.com/markrussinovich/">Mark Russinovich's blog</a> posts</i></div><div><i><br /></i></div><div><i>Update: Just got an email response back from SecureCRT's support staff to a query about I this I had sent last night. She clarified "... the global option is **not** enabled by default. The session option by default is a "tri-state" option, meaning it uses the setting found in the global option."<br /></i><br /></div>Viraj Modyhttp://www.blogger.com/profile/10980120071443522974noreply@blogger.com0tag:blogger.com,1999:blog-2687820185167366196.post-69392154405317443922010-03-03T17:55:00.001-08:002010-03-03T18:37:05.487-08:00Discouraging Tardiness in Social Circles<div>What's the best way, if any, to incentivize punctuality in a social setting i.e. among friends and family? Alternately, is there any way to dis-incentivize/discourage <span class="blsp-spelling-error" id="SPELLING_ERROR_0">impunctuality</span>? </div><div></div><br /><div>In professional environments this isn't usually a problem. There is generally one party which has some form of authority (the boss running a meeting, the doctor who is so booked up that you got an appointment only after months, the VP who can only meet for 20 minutes before he has to run to another meeting, the senior engineer whose time really is more precious than yours) who sets an example that is expected to be followed, and can dangle a carrot or wield a stick to manage tardiness. There is also a personal incentive to be on time - you want to make a good first impression, not appear to be the one holding the team behind, or not miss out on something important if you're late.</div><div><br /></div><div><div>The social dynamics that exist among friends and family are vastly different though. Often times, no one person holds a position of authority over another, the 'meetings' aren't usually that important and outcasting the offenders as a group isn't really an option either. Hence the repercussions of tardiness are pretty mild, if any. In many ways, the punctual folk have more to lose.</div><div><br /></div><div>The ideal solution wouldn't make any individual or sub-group appear to be 'jerks' and yet make each member of the group <i>want</i> to be punctual. Poking fun at the offenders or just generally being prudish isn't going to help, I think. </div><div><br /></div><div>So, any ideas? I'm drawing blanks...</div><div><br /></div></div>Viraj Modyhttp://www.blogger.com/profile/10980120071443522974noreply@blogger.com1tag:blogger.com,1999:blog-2687820185167366196.post-26208471497791036992010-02-19T09:26:00.000-08:002010-02-19T09:36:44.708-08:00Coupon etiquette?What's the 'correct' social etiquette about using restaurant coupons purchased online (such as those from <a href="http://www.restaurant.com">restaurant.com</a> or <a href="http://www.groupon.com/">groupon.com</a>) when you go out in a group and split the check? <div><br /></div><div>For coupons you get in the mail, or basically free stuff, I assume common etiquette is to offer the coupon to the group to use and split the remainder. Does it work the same way when you've bought, say, a $50 coupon for $25 (i.e. you use the coupon to cover $25 off your tab and offer the discount value to the group)? Do you use the entire $50 to cover just your tab? Or maybe simply not using the coupon when dining as a group is the right way to go?</div>Viraj Modyhttp://www.blogger.com/profile/10980120071443522974noreply@blogger.com0tag:blogger.com,1999:blog-2687820185167366196.post-30892397543606960012010-02-09T10:27:00.000-08:002010-02-09T10:57:08.988-08:00Hey Google Buzz, I don't email my friends anymoreI just <a href="http://www.youtube.com/feb0910googleevent">saw the presentation</a> from Google about Google Buzz. The quick summary of the announcements - creating a social network using my email contacts and emailing history as the source of my social graph, and using rich geo-tagged status messages (buzz-es) to keep it updated. It comes with rich mobile apps to help this take off. They also promised enterprise versions of Buzz.<div><br /></div><div>Buzz sounds pretty exciting. Some of the geo-tagged updates sounds really slick. My first reaction - this is going to be 'public follow' thing more than a 'follow my friends' things. Because emailing my friends is like, so 2007! </div><div><br /></div><div>Fact is, I don't email my closest friends/family anymore. I either IM them, post on their Facebook page, send them SMS or call them. Email is more for work type, non-personal stuff. Having said that, it's important to note that Google does have GTalk and Google Voice so I hope they weigh activity from GTalk and Google Voice higher than GMail activity when figuring out who I must 'auto-follow'.</div><div><br /></div><div>Time for someone to write a Buzz-to-Facebook cross poster. :)</div>Viraj Modyhttp://www.blogger.com/profile/10980120071443522974noreply@blogger.com0tag:blogger.com,1999:blog-2687820185167366196.post-75822216265331929072010-01-25T15:26:00.000-08:002010-01-25T22:30:05.164-08:00Test cases for basic Facebook Connect integration<div>I recently spent a couple of days adding some basic <span class="blsp-spelling-error" id="SPELLING_ERROR_0">Facebook</span> Connect functionality to an existing site with it's own <span class="blsp-spelling-error" id="SPELLING_ERROR_1">Login</span>/<span class="blsp-spelling-error" id="SPELLING_ERROR_2">Auth</span> system. The process isn't too hard and there are several <a href="http://www.facebook.com/connectnews?v=app_7146470109">examples</a> and even a nice <a href="http://www.goldsteintech.com/facebook_connect/tutorial.php">tutorial</a> which shows you exactly how to hook this up.</div><div><br /></div><div>The tutorials and the <a href="http://wiki.developers.facebook.com/"><span class="blsp-spelling-error" id="SPELLING_ERROR_3">Facebook</span> developer wiki</a> to a good job of getting you on the right path and putting up a quick working implementation. However, there are several subtleties that need to be addressed, depending on the requirements of the existing site. I didn't really find a good compilation of these 'test cases' anywhere. <span class="blsp-spelling-error" id="SPELLING_ERROR_4">Facebook's</span> own documentation probably references several of these but they're scattered all over their <a href="http://wiki.developers.facebook.com/">wiki page</a> and are very easy to miss if your primary focus is getting this stuff up and running quickly.</div><div><br /></div><div>This blog post is my attempt to compile of list of things you need to think about when adding <span class="blsp-spelling-error" id="SPELLING_ERROR_5">Facebook</span> Connect support for sign up/<span class="blsp-spelling-error" id="SPELLING_ERROR_6">login</span> or linked accounts on an <span class="Apple-style-span" style="text-decoration: underline;">existing</span> site with its own authorization system. Here are a few characteristics (pretty standard) of the existing site:</div><div><ul><li>Authentication using a <span class="blsp-spelling-error" id="SPELLING_ERROR_7">username</span>/email address and password</li><li>Cookies to help keep users logged in</li><li>Accounts keyed by email address (i.e. 1:1 mapping between accounts and email address)</li><li>Ability to reset accounts by having a temp password send to user's email address</li><li>Fully <span class="blsp-spelling-error" id="SPELLING_ERROR_8">ajax</span> navigation (which complicates matter a little bit)</li><li><span class="blsp-spelling-error" id="SPELLING_ERROR_9">Logout</span> functionality which clears up cookies</li></ul></div><div></div><div>The goals for the <span class="blsp-spelling-error" id="SPELLING_ERROR_10">Facebook</span> Connect integration are pretty simple:</div><div><ul><li>New User Sign Up without needing to create a site-specific password (<a href="http://wiki.developers.facebook.com/index.php/Roadmap_Email">email address</a> needed)</li><li>Ability for existing users to link with their <span class="blsp-spelling-error" id="SPELLING_ERROR_11">Facebook</span> account</li><li>1:1 mapping between accounts on your site and <span class="blsp-spelling-error" id="SPELLING_ERROR_12">Facebook</span> accounts</li><li><a href="http://wiki.developers.facebook.com/index.php/Detecting_Connect_Status">Single sign-on</a> experience</li></ul></div><div></div><div>Let's call the existing site <a href="http://blogs.wsj.com/digits/2009/12/11/30-rock-introduces-youface/tab/article/"><span class="blsp-spelling-error" id="SPELLING_ERROR_13">YouFace</span></a>. I won't go into how to get stuff hooked up or explaining how to implement this - it's not too complicated and there's a bunch of information online for it. Instead, treat this as a list of a few things you probably want to test when you're done. There shouldn't really be anything ground breaking here - this was just a convenient way for me to build a list of things to test on my own site. Some of these are handled by the <span class="blsp-spelling-error" id="SPELLING_ERROR_14">Facebook</span> Connect client library, while some need your application to do the right thing. Please comment if I've missed any cases or if some just appear wrong - I would love to fix my site!</div><div><ul><li>Sign Up should fail if the user denies permission to the app <i>(category: sign up)</i></li><br /><li>Since we need access to an <a href="http://wiki.developers.facebook.com/index.php/Roadmap_Email">email address</a>, Sign Up should fail if the user provides publish permission but denies email permission <i>(category: sign up)</i></li><br /><li>If the user provides an email address that already exists in your system, fail Sign Up. Make sure no <span class="blsp-spelling-error" id="SPELLING_ERROR_15">YouFace</span> <span class="blsp-spelling-error" id="SPELLING_ERROR_16">backend</span> tables are modified <i>(category: sign up, 1:1 mapping) </i><i>PS - when this happens, I didn't find a way for you to <span class="blsp-spelling-error" id="SPELLING_ERROR_17">de</span>-authorize <span class="blsp-spelling-error" id="SPELLING_ERROR_18">YouFace</span> on the <span class="blsp-spelling-error" id="SPELLING_ERROR_19">Facebook</span> user's behalf. The user must manually do this if they wish you use the same account but provide a different email address.</i></li><br /><li>Accounts created using <span class="blsp-spelling-error" id="SPELLING_ERROR_20">Facebook</span> Connect should not be able to <span class="blsp-spelling-error" id="SPELLING_ERROR_21">login</span> using <span class="blsp-spelling-error" id="SPELLING_ERROR_22">YouFace's</span> default email/password <span class="blsp-spelling-error" id="SPELLING_ERROR_23">login</span> system <i>(category: sign in, account security). PS: Since <span class="blsp-spelling-error" id="SPELLING_ERROR_24">YouFace</span> accounts require a password and those created using <span class="blsp-spelling-error" id="SPELLING_ERROR_25">Facebook</span> Connect don't, make sure to insert a random password hash into your table to avoid silly errors</i></li><br /><li>Accounts created using <span class="blsp-spelling-error" id="SPELLING_ERROR_26">YouFace</span> should be able to sign in without requiring to be signed into <span class="blsp-spelling-error" id="SPELLING_ERROR_27">Facebook</span>, even if when a link to a <span class="blsp-spelling-error" id="SPELLING_ERROR_28">Facebook</span> accounts exists <i>(category: sign in)</i></li><br /><li>Attempting to Sign Up with a <span class="blsp-spelling-error" id="SPELLING_ERROR_29">Facebook</span> account that's already linked and authorized should result in the Sign In experience <i>(category: sign up, sign in)</i></li><br /><li>Attempting to Sign In with a <span class="blsp-spelling-error" id="SPELLING_ERROR_30">Facebook</span> account that <span class="blsp-spelling-error" id="SPELLING_ERROR_31">YouFace</span> hasn't seen before should lead to the Sign Up experience, i.e. create a new account <i>(category: sign up, sign in)</i></li><br /><li>Linking a <span class="blsp-spelling-error" id="SPELLING_ERROR_32">Facebook</span> account with an existing <span class="blsp-spelling-error" id="SPELLING_ERROR_33">YouFace</span> account should add the email address from <span class="blsp-spelling-error" id="SPELLING_ERROR_34">Facebook</span> to the list of 'used addresses' in the <span class="blsp-spelling-error" id="SPELLING_ERROR_35">YouFace</span> system <i>(category: linking, 1:1 mapping)</i></li><br /><li>When a <span class="blsp-spelling-error" id="SPELLING_ERROR_36">Facebook</span> linked account <span class="blsp-spelling-error" id="SPELLING_ERROR_37">de</span>-authorizes <span class="blsp-spelling-error" id="SPELLING_ERROR_38">YouFace</span>, your system should record this action and change state as needed <i>(category: authorization)</i></li><br /><li>Linking a <span class="blsp-spelling-error" id="SPELLING_ERROR_39">YouFace</span> account with a <span class="blsp-spelling-error" id="SPELLING_ERROR_40">Facebook</span> account that's already been linked to some other account in your system should be denied <i>(category: linking, 1:1 mapping, authorization)</i></li><br /><li>Users with linked accounts can continue to sign in using their <span class="blsp-spelling-error" id="SPELLING_ERROR_41">YouFace</span> credentials even after they <span class="blsp-spelling-error" id="SPELLING_ERROR_42">de</span>-authorize <span class="blsp-spelling-error" id="SPELLING_ERROR_43">YouFace</span> from their <span class="blsp-spelling-error" id="SPELLING_ERROR_44">Facebook</span> account <i>(category: linking, authorization)</i></li><br /><li>Linking a <span class="blsp-spelling-error" id="SPELLING_ERROR_45">Facebook</span> account different from one you originally linked to an existing <span class="blsp-spelling-error" id="SPELLING_ERROR_46">YouFace</span> account should be denied. This can happen when an existing user links a <span class="blsp-spelling-error" id="SPELLING_ERROR_47">YouFace</span> account to Foo@FB, <span class="blsp-spelling-error" id="SPELLING_ERROR_48">de</span>-authorizes the app and then tries to link Bar@FB to that account <i>(category: linking, 1:1 mapping, authorization)</i></li><br /><li>A user who Signed Up using <span class="blsp-spelling-error" id="SPELLING_ERROR_49">Facebook</span> Connect should not be allowed to link their account to another <span class="blsp-spelling-error" id="SPELLING_ERROR_50">Facebook</span> account <i>(category: sign up, 1:1 mapping)</i></li><br /><li>Attempting to Sign In or Sign Up with a <span class="blsp-spelling-error" id="SPELLING_ERROR_51">Facebook</span> account that <span class="blsp-spelling-error" id="SPELLING_ERROR_52">YouFace</span> has seen, but one that has currently not authorized the <span class="blsp-spelling-error" id="SPELLING_ERROR_53">YouFace</span> app (maybe the user revoked authorization) should ask to re-authorize before proceeding. It must log the user into the <span class="Apple-style-span" style="text-decoration: underline; ">original</span> <span class="blsp-spelling-error" id="SPELLING_ERROR_54">YouFace</span> account linked with that <span class="blsp-spelling-error" id="SPELLING_ERROR_55">Facebook</span> account, i.e. no new account should be created. If that account was originally created using <span class="blsp-spelling-error" id="SPELLING_ERROR_56">Facebook</span> Connect, the primary email address should be updated to reflect the user's current <span class="blsp-spelling-error" id="SPELLING_ERROR_57">Facebook</span> contact email address else the primary email address should not be changed from what the user provided <span class="blsp-spelling-error" id="SPELLING_ERROR_58">YouFace</span> when they signed up <i>(category: sign in, 1:1 mapping, <span class="blsp-spelling-error" id="SPELLING_ERROR_59">reauthorization</span>)</i></li><br /><li><i><span class="Apple-style-span" style="font-style: normal; ">If a user with a linked account is logged into <span class="blsp-spelling-error" id="SPELLING_ERROR_60">Facebook</span>, they should be signed into <span class="blsp-spelling-error" id="SPELLING_ERROR_61">YouFace</span> directly <i>(category: single sign in)</i></span></i></li><br /><li>Users who signed into <span class="blsp-spelling-error" id="SPELLING_ERROR_62">YouFace</span> with <span class="blsp-spelling-error" id="SPELLING_ERROR_63">Facebook</span> Connect should be logged out of <span class="blsp-spelling-error" id="SPELLING_ERROR_64">Facebook</span> if they log out of <span class="blsp-spelling-error" id="SPELLING_ERROR_65">YouFace</span> <i>(category: single sign out)</i></li><br /><li>Users who signed into <span class="blsp-spelling-error" id="SPELLING_ERROR_66">YouFace</span> with <span class="blsp-spelling-error" id="SPELLING_ERROR_67">Facebook</span> Connect should be logged out of <span class="blsp-spelling-error" id="SPELLING_ERROR_68">YouFace</span> if they log out of <span class="blsp-spelling-error" id="SPELLING_ERROR_69">Facebook</span> <i>(category: single sign out)</i></li><br /><li>Users with linked accounts who signed into <span class="blsp-spelling-error" id="SPELLING_ERROR_70">YouFace</span> directly (not using <span class="blsp-spelling-error" id="SPELLING_ERROR_71">Facebook</span> Connect) should continue to remain signed in, regardless of whether they're were originally signed into <span class="blsp-spelling-error" id="SPELLING_ERROR_72">Facebook</span>, when they sign out of <span class="blsp-spelling-error" id="SPELLING_ERROR_73">Facebook</span> <i>(category: sign in, sign out)</i></li><br /><li>Users who created their account using <span class="blsp-spelling-error" id="SPELLING_ERROR_74">Facebook</span> Connect should not be allowed to reset passwords or have temporary passwords sent to their email addresses <i>(category: account security)</i></li><br /><li>This is a kinda convoluted. Users Foo and Bar both have <span class="blsp-spelling-error" id="SPELLING_ERROR_75">YouFace</span> accounts linked to <span class="blsp-spelling-error" id="SPELLING_ERROR_76">Facebook</span>. Bar is logged into <span class="blsp-spelling-error" id="SPELLING_ERROR_77">YouFace</span> with his <span class="blsp-spelling-error" id="SPELLING_ERROR_78">YouFace</span> account, and Foo is logged into <span class="blsp-spelling-error" id="SPELLING_ERROR_79">Facebook</span>, in the same instance of the browser (Bar logged in first). When Bar logs out of <span class="blsp-spelling-error" id="SPELLING_ERROR_80">YouFace</span>, Foo should not get logged out of <span class="blsp-spelling-error" id="SPELLING_ERROR_81">Facebook</span> (<i>category: single sign out, multiple accounts ). PS: </i><i><span class="blsp-spelling-error" id="SPELLING_ERROR_82">YouFace's</span> Connect library is going to pick up a valid <span class="blsp-spelling-error" id="SPELLING_ERROR_83">Facebook</span> ID but it's that of Foo, not Bar. So you need to do more than just client-side detection of whether <span class="blsp-spelling-error" id="SPELLING_ERROR_84">someone's</span> logged into <span class="blsp-spelling-error" id="SPELLING_ERROR_85">Facebook</span>.</i></li></ul></div>Viraj Modyhttp://www.blogger.com/profile/10980120071443522974noreply@blogger.com19tag:blogger.com,1999:blog-2687820185167366196.post-13436147538761804192009-12-23T12:41:00.000-08:002009-12-23T12:55:02.455-08:00Browser history navigation bug in Chrome and SafariI've been seeing some odd behavior in Chrome - occasionally browser history would get hosed pretty badly. When I noticed the same issue navigating the website I'm working on, I ended up spending a bunch of time trying to get to the bottom of the issue. Initially I suspected it was something wrong with the site, but I was eventually able to create a stand-alone repro for Chrome (v3 and v4) as well as for Safari (v4). I suspect this might be a WebKit thing, which both browsers share, but I don't know enough to be certain.<div><br /></div><div>The problem seems to occur when you spend time moving around fully ajax sites that contain one or more iframes (I've seen this on GMail as well). In my case I worked around this issue by not using iframes but I imagine that's not always possible. Anyways, this is now <a href="http://code.google.com/p/chromium/issues/detail?id=30693">Chromium bug 30693</a>. </div>Viraj Modyhttp://www.blogger.com/profile/10980120071443522974noreply@blogger.com0tag:blogger.com,1999:blog-2687820185167366196.post-29976382613838868612009-10-09T11:08:00.000-07:002009-10-14T19:40:19.722-07:00XSRF/CSRF Vulnerability Explained in Non-Geek SpeakI recently explained what cross-site request forgery (<span class="blsp-spelling-error" id="SPELLING_ERROR_0">CSRF</span>/<span class="blsp-spelling-error" id="SPELLING_ERROR_1">XSRF</span>) is to a friend who isn't a programmer/CS person and I was pretty amazed at how quickly he understood the implications of not protecting against such attacks. With the proliferation of social interactions on the web now, such attacks can be pretty damaging to consumers of new media and e-commerce sites.<div><br /></div><div>The <a href="http://en.wikipedia.org/wiki/Cross-site_request_forgery"><span class="blsp-spelling-error" id="SPELLING_ERROR_2">Wikipedia</span> article about <span class="blsp-spelling-error" id="SPELLING_ERROR_3">CSRF</span></a> does a pretty good job of explaining the issue - most people familiar with web development should be able to understand it easily. Basically, this attack lets legitimate unsuspecting users submit changes to a site they routinely interact with, but without their knowledge. <span class="blsp-spelling-error" id="SPELLING_ERROR_4">CSRF</span> attacks leverage your own browser and the trust you associate with it to perform potentially damaging actions on your behalf. </div><div><br /></div><div>Here's an example of what such an attack might look like. Let's assume that <span class="blsp-spelling-error" id="SPELLING_ERROR_5">Facebook</span> wasn't protected against such attacks, for the purpose of this illustration. In reality, I'm sure <span class="blsp-spelling-error" id="SPELLING_ERROR_6">Facebook</span> engineers have done a good job of making sure this can't really ever happen. That's not true of every site out there, though.</div><div><br /></div><div>When you sign into <span class="blsp-spelling-error" id="SPELLING_ERROR_7">Facebook</span>, you have the option of remaining signed in. To do this, sites like <span class="blsp-spelling-error" id="SPELLING_ERROR_8">Facebook</span> set a browser cookie which identifies you to their servers. This is great because it lets you visit the site frequently and navigate the site without needing to enter your password every time. Browsers are good about securing your cookie - they make sure that no other site besides <span class="blsp-spelling-error" id="SPELLING_ERROR_9">Facebook</span> can see this cookie, so it's not possible for the author of www.hacker.com to steal your cookie and present it to <span class="blsp-spelling-error" id="SPELLING_ERROR_10">Facebook</span> and then impersonate you. However, each time you go to any URL that contains www.facebook.com, the browser diligently sends over all cookies associated with <span class="blsp-spelling-error" id="SPELLING_ERROR_11">Facebook</span>, because the receiver of those cookies is www.facebook.com.</div><div><br /></div><div>Once on <span class="blsp-spelling-error" id="SPELLING_ERROR_12">Facebook</span>, you do things like update your status or write on a friend's wall. When you do that, the browser sends your new status update to <span class="blsp-spelling-error" id="SPELLING_ERROR_13">Facebook</span> along with the cookies. By inspecting the cookie, <span class="blsp-spelling-error" id="SPELLING_ERROR_14">Facebook</span> is able to validate that it is indeed you trying to update your own status, so it proceeds to allow the action. Under the covers, your browser is sending over two pieces of information - some data (your status update text, in this case) and the cookies. The browser was triggered to do this by you clicking the "Submit" button. This sounds pretty straightforward so far.</div><div><br /></div><div>Now imagine you're browsing the web and land on this page that talks about world hunger and how you can help the cause by simply clicking a "Help the Children" button. With only altruistic intentions, you click on the button. Nothing visible happens, or maybe a "Thank you!" label replaces that button. Then you simply move on to the next thing you were going to do. Suddenly, you start receiving email notifications about friends responding to your latest status update on <span class="blsp-spelling-error" id="SPELLING_ERROR_15">Facebook</span>, except you never posted any update. You visit <span class="blsp-spelling-error" id="SPELLING_ERROR_16">Facebook</span> and sure enough your status now says "I am a loser!", updated 2 minutes ago. Huh?! You didn't set that status. You panic - maybe someone hacked your account? Or someone guessed your password? What's going on?</div><div><br /></div><div>What really happened was that the world hunger website you were on was written by a malicious attacker, and when you clicked the "Help the Children" button that website didn't really contribute 10 cents to UNICEF, but instead told your browser to send a new status update with the text "I am a loser!" to www.facebook.com. This site couldn't access your cookies directly, so the browser didn't do anything bad and stuck to its promise of not letting anyone else see cookies that shouldn't be visible to them. Remember, the attacker never looked at your cookie at all while any of this happened. However, when the attacker asked the browser to send data to www.facebook.com, the browser added the <span class="blsp-spelling-error" id="SPELLING_ERROR_17">Facebook</span> cookies to the request. So now <span class="blsp-spelling-error" id="SPELLING_ERROR_18">Facebook</span> looks at the cookies and the data, thinks that it is indeed you and goes on to update your status. Boom!</div><div><br /></div><div>So what went wrong here? The browser didn't do anything bad - it did exactly what it's supposed to. It hid your <span class="blsp-spelling-error" id="SPELLING_ERROR_19">Facebook</span> cookies from everyone except <span class="blsp-spelling-error" id="SPELLING_ERROR_20">Facebook</span> and it sent data that submitting the "Help the Children" button asked it to send. You didn't do anything bad - you simply clicked on a link that was meant to help needy kids. You did have cookies enabled (default for most browsers) and decided to check that "Log in automatically" option, but those seem like reasonable actions. </div><div><br /></div><div>The flaw here is with the website you trusted - <span class="blsp-spelling-error" id="SPELLING_ERROR_21">Facebook</span>, in this example. Sites like <span class="blsp-spelling-error" id="SPELLING_ERROR_22">Facebook</span> that rely on cookies to identify and authorize user actions need to do more things than just validate your cookie when data is presented to the site on your behalf. They need to make sure that place from which the data was sent was 'good', or that a malicious author cannot create data the same way that www.facebook.com can. They need to make sure that when you update your status, the data contain more than just your new status text - there must be some kind of secret information in there that can only be generated by <span class="blsp-spelling-error" id="SPELLING_ERROR_23">Facebook</span>, and this secret must be different for each <span class="blsp-spelling-error" id="SPELLING_ERROR_24">Facebook</span> user. They can even control how long cookies and secrets remain valid, and have ways to periodically require users to sign in and generate new unique secrets for users.</div><div><br /></div><div>Of course, this is just a silly example. I'm sure <span class="blsp-spelling-error" id="SPELLING_ERROR_25">Facebook</span> is perfectly adept and handling such attacks. However, a vulnerability like this could impact everything from online review sites to banking and e-commerce sites. It's always good to be aware of things that could go wrong online - the <span class="blsp-spelling-corrected" id="SPELLING_ERROR_26">Internet</span> is definitely not as safe as you might think it is.</div><div><br /></div><div><br /></div>Viraj Modyhttp://www.blogger.com/profile/10980120071443522974noreply@blogger.com1