<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-4137806788465333774</id><updated>2012-02-21T09:10:50.348-08:00</updated><category term='RabbitMQ/Celery'/><category term='Unix'/><category term='Hudson'/><category term='openid sso hudson'/><category term='SQL'/><category term='JavaScript'/><category term='CSS'/><category term='Python/Django'/><category term='Selenium'/><title type='text'>Hus to Know?</title><subtitle type='html'>Written by Roger Hu</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://hustoknow.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://hustoknow.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><link rel='next' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default?start-index=101&amp;max-results=100'/><author><name>Roger</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>275</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-4137806788465333774.post-4614923003903825574</id><published>2012-02-21T09:09:00.003-08:00</published><updated>2012-02-21T09:10:50.366-08:00</updated><title type='text'>Tricks of using the SGE2010 switch</title><content type='html'>1. Telnet to the IP address.&lt;br /&gt;&lt;br /&gt;2. Enable the SSH server.&lt;br /&gt;&lt;br /&gt;3. SSH into the switch.&lt;br /&gt;&lt;br /&gt;4. Hit Ctrl-Z.  You will then be dropped into the shell.  Type 'lcli'.&lt;br /&gt;&lt;br /&gt;5. You can now access the Cisco switch using the CLI interface.  To save config changes, type "copy running-config startup-config".&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4137806788465333774-4614923003903825574?l=hustoknow.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hustoknow.blogspot.com/feeds/4614923003903825574/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hustoknow.blogspot.com/2012/02/tricks-of-using-sge2010-switch.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/4614923003903825574'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/4614923003903825574'/><link rel='alternate' type='text/html' href='http://hustoknow.blogspot.com/2012/02/tricks-of-using-sge2010-switch.html' title='Tricks of using the SGE2010 switch'/><author><name>Roger</name><uri>http://www.blogger.com/profile/06655396766752051975</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4137806788465333774.post-8746902267667692967</id><published>2012-02-18T16:17:00.001-08:00</published><updated>2012-02-18T16:26:33.155-08:00</updated><title type='text'>X-Forwarded-Host to X_FORWARDED_HOST in mod_wsgi..</title><content type='html'>Ever wondered how the parameters in Django's request.META, such as X-Forwarded-Host or X-Forwarded-For get set in the os.environ objects?  The headers often get transformed to entire upper-case and the dashes get replaced by underscores (i.e. X-Forwarded-Host becomes X_FORWARDED_HOST). Most of the work gets done inside mod_wsgi.c using the wsgi_http2env() function.&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt;static PyObject *Auth_environ(AuthObject *self, const char *group)&lt;br /&gt;{&lt;br /&gt;            PyDict_SetItemString(vars, wsgi_http2env(r-&gt;pool, hdrs[i].key),&lt;br /&gt;                                 object);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The wsgi_http2env() function basically performs this conversion to HTTP environment variables:&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt;static char *wsgi_http2env(apr_pool_t *a, const char *w)&lt;br /&gt;{&lt;br /&gt;    char *res = (char *)apr_palloc(a, sizeof("HTTP_") + strlen(w));&lt;br /&gt;    char *cp = res;&lt;br /&gt;    char c;&lt;br /&gt;&lt;br /&gt;    *cp++ = 'H';&lt;br /&gt;    *cp++ = 'T';&lt;br /&gt;    *cp++ = 'T';&lt;br /&gt;    *cp++ = 'P';&lt;br /&gt;    *cp++ = '_';&lt;br /&gt;&lt;br /&gt;    while ((c = *w++) != 0) {&lt;br /&gt;        if (!apr_isalnum(c)) {&lt;br /&gt;            *cp++ = '_';&lt;br /&gt;        }&lt;br /&gt;        else {&lt;br /&gt;            *cp++ = apr_toupper(c);&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;    *cp = 0;&lt;br /&gt;&lt;br /&gt;    return res;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4137806788465333774-8746902267667692967?l=hustoknow.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hustoknow.blogspot.com/feeds/8746902267667692967/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hustoknow.blogspot.com/2012/02/x-forwarded-host-to-xforwardedhost-in.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/8746902267667692967'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/8746902267667692967'/><link rel='alternate' type='text/html' href='http://hustoknow.blogspot.com/2012/02/x-forwarded-host-to-xforwardedhost-in.html' title='X-Forwarded-Host to X_FORWARDED_HOST in mod_wsgi..'/><author><name>Roger</name><uri>http://www.blogger.com/profile/06655396766752051975</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4137806788465333774.post-431818746685133254</id><published>2012-02-18T16:13:00.000-08:00</published><updated>2012-02-18T16:48:12.300-08:00</updated><title type='text'>Integrating Google Apps with Apache module</title><content type='html'>Recntly, we've been using &lt;a href="https://github.com/epotocko/apache-google-apps-sso"&gt;https://github.com/epotocko/apache-google-apps-sso&lt;/a&gt; to implement single-sign-on for a lot of our Apache-based resources for Google Apps/OpenID integration.  The documentation are pretty clear and discuss how to go about protecting  certain URL paths, but what if you want to require authentication to occur for the entire site?&lt;br /&gt;&lt;br /&gt;The trick is to use a RewriteRule and mod_rewrite to basically stop Auth_memCookie from being needed for /auth/ logins, which is where the PHP code will be executed to perform the authentication.  The 'L' flag in the RewriteRule will stop the rest of the rulesets from executing.&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;RewriteEngine on&lt;br /&gt;RewriteRule ^/auth/(.*)$ /auth/$1 [L]&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;IfModule mod_auth_memcookie.c&amp;gt;&lt;br /&gt;&amp;lt;Location /&amp;gt;&lt;br /&gt;  AuthType Cookie&lt;br /&gt;.&lt;br /&gt;.&lt;br /&gt;.&lt;br /&gt;  ProxyPass http://127.0.0.1:9999/&lt;br /&gt;  ProxyPassReverse http://127.0.0.1:9999/&lt;br /&gt;&lt;br /&gt;&amp;lt;/Location&amp;gt;&lt;br /&gt;&amp;lt;/IfModule&amp;gt;&lt;br /&gt;The ProxyPass is used to proxy traffic to an internal webserver host, and the ProxyPassReverse rewrites Location: headers.  If you are using Django, an HttpResponseRedirect() function call will usually set the Location: header based on the X-Forwarded-Host but because if you are not using mod_wsgi(), the header may not be correctly set.  The ProxyPassReverse provides a way to rewrite redirection requests for this reason.&lt;br /&gt;The other thing is to make sure that your base URL is also set with this /auth/ path:&lt;br /&gt;&lt;pre class="prettyprint"&gt;// Base url to protect&lt;br /&gt;GApps_Session::$BASE_URL = 'https://myhost.com/auth/';&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4137806788465333774-431818746685133254?l=hustoknow.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hustoknow.blogspot.com/feeds/431818746685133254/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hustoknow.blogspot.com/2012/02/integrating-google-apps-with-apache.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/431818746685133254'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/431818746685133254'/><link rel='alternate' type='text/html' href='http://hustoknow.blogspot.com/2012/02/integrating-google-apps-with-apache.html' title='Integrating Google Apps with Apache module'/><author><name>Roger</name><uri>http://www.blogger.com/profile/06655396766752051975</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4137806788465333774.post-920446842006146245</id><published>2012-02-14T10:23:00.000-08:00</published><updated>2012-02-18T16:48:26.188-08:00</updated><title type='text'>How Browsers Work</title><content type='html'>&lt;a href="http://www.html5rocks.com/en/tutorials/internals/howbrowserswork/"&gt;http://www.html5rocks.com/en/tutorials/internals/howbrowserswork/&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4137806788465333774-920446842006146245?l=hustoknow.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hustoknow.blogspot.com/feeds/920446842006146245/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hustoknow.blogspot.com/2012/02/how-browsers-work.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/920446842006146245'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/920446842006146245'/><link rel='alternate' type='text/html' href='http://hustoknow.blogspot.com/2012/02/how-browsers-work.html' title='How Browsers Work'/><author><name>Roger</name><uri>http://www.blogger.com/profile/06655396766752051975</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4137806788465333774.post-5991467764807309264</id><published>2012-01-28T22:46:00.000-08:00</published><updated>2012-01-28T23:04:16.806-08:00</updated><title type='text'>Replacing the motor on a Maytag MDE3000AYW dryer...</title><content type='html'>&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;object width="320" height="266" class="BLOG_video_class" id="BLOG_video-aaced466394d7d31" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0"&gt;&lt;param name="movie" value="http://www.youtube.com/get_player"&gt;&lt;param name="bgcolor" value="#FFFFFF"&gt;&lt;param name="allowfullscreen" value="true"&gt;&lt;param name="flashvars" value="flvurl=http://v11.nonxt3.googlevideo.com/videoplayback?id%3Daaced466394d7d31%26itag%3D5%26app%3Dblogger%26ip%3D0.0.0.0%26ipbits%3D0%26expire%3D1332182510%26sparams%3Did,itag,ip,ipbits,expire%26signature%3D239C2BDCD13F3590E61476AC8C2790F74E962734.45EAE70B0C281AACDEB2D3DE4647F696404523E8%26key%3Dck1&amp;amp;iurl=http://video.google.com/ThumbnailServer2?app%3Dblogger%26contentid%3Daaced466394d7d31%26offsetms%3D5000%26itag%3Dw160%26sigh%3D81XW_hDzZ-VekglCgMDGmicDU0g&amp;amp;autoplay=0&amp;amp;ps=blogger"&gt;&lt;embed src="http://www.youtube.com/get_player" type="application/x-shockwave-flash"width="320" height="266" bgcolor="#FFFFFF"flashvars="flvurl=http://v11.nonxt3.googlevideo.com/videoplayback?id%3Daaced466394d7d31%26itag%3D5%26app%3Dblogger%26ip%3D0.0.0.0%26ipbits%3D0%26expire%3D1332182510%26sparams%3Did,itag,ip,ipbits,expire%26signature%3D239C2BDCD13F3590E61476AC8C2790F74E962734.45EAE70B0C281AACDEB2D3DE4647F696404523E8%26key%3Dck1&amp;iurl=http://video.google.com/ThumbnailServer2?app%3Dblogger%26contentid%3Daaced466394d7d31%26offsetms%3D5000%26itag%3Dw160%26sigh%3D81XW_hDzZ-VekglCgMDGmicDU0g&amp;autoplay=0&amp;ps=blogger"allowFullScreen="true" /&gt;&lt;/object&gt;&lt;/div&gt;&lt;br /&gt;About two weeks ago, one of these 1999 Maytag dryer models suddenly stopped working.  You could push the Start button but all you would get is a low-hum.  Suspecting that the motor died, I decided to open up the unit up to see if I could confirm the issue myself. &amp;nbsp;NOTE: apparently if you try to call a Maytag service person at this point, they'll actually charge you extra for being forced to figure out what you did! &amp;nbsp;It actually took at least 5+ hours to get this whole unit disassembled and reassembled, so also don't try if you really have a pressing need to clean your dirty underwear. &amp;nbsp;A technician may work faster too if you don't have all the tools are your disposal.&lt;br /&gt;&lt;br /&gt;Maytag still keeps its information about these models online, and this particular model the links are posted below. &amp;nbsp;However, you won't find any repair guide, since the company normally&amp;nbsp;recommends that you contact an authorized technician. &amp;nbsp;Nonetheless, you can get a list of parts information in case you want to get a rough estimate about the cost. &amp;nbsp;Pandora OEM, which sells off Amazon.com, can deliver your parts from Fresno, CA within a few days, and they even respond to email inquiries on weekends so double-check with them about your model before you order your parts.&lt;br /&gt;&lt;br /&gt;&lt;a href="https://www.maytag.com/digitalassets/MLPDF/Use%20and%20Care%20Guide%20-%206-3702450.pdf"&gt;https://www.maytag.com/digitalassets/MLPDF/Use%20and%20Care%20Guide%20-%206-3702450.pdf&lt;/a&gt; (Owner's Manual)&lt;br /&gt;&lt;br /&gt;&lt;a href="https://www.maytag.com/digitalassets/MLPDF/Dimension%20Guide%20-%20DNDRY.pdf"&gt;https://www.maytag.com/digitalassets/MLPDF/Dimension%20Guide%20-%20DNDRY.pdf&lt;/a&gt; (Dimensions Guide)&lt;br /&gt;&lt;br /&gt;&lt;a href="https://www.maytag.com/digitalassets/MLPDF/Repair%20Part%20List%20-%20MDE3000AYW.pdf"&gt;https://www.maytag.com/digitalassets/MLPDF/Repair%20Part%20List%20-%20MDE3000AYW.pdf&lt;/a&gt;&amp;nbsp;(Parts List)&lt;br /&gt;&lt;br /&gt;You can however find the service manual by doing a Google search for the Dryer Service Manual (16023110.pdf) for this model. &amp;nbsp;Background info: Apparently the way these models are named is that M represents the brand, DE represents an electric dryer (DG is gas dryer), 3000 represents the model no, A is the production code, Y = 240 volts, and W represents the color (white on white). &lt;br /&gt;&lt;br /&gt;If you want some context about how to disassemble dryers for Maytag models, there are several YouTube clips that provide a good overview (PartSelect.com and RepairClinic.com do an excellent job explaining the step-by-step process).   A dryer consists of a pulley system, a belt, motor, a cynlindrical tumbler for turning your clothes, a heating element, and a fan (along with a thermal fuses to keep you from burning the whole unit up).  The videos make things a lot easier than they seem, but many of the aspects of taking apart a dryer especially related to the retaining clips and clamps were fairly consistent even with this older 1999 model.&lt;br /&gt;&lt;br /&gt;&lt;iframe allowfullscreen="" frameborder="0" height="315" src="http://www.youtube.com/embed/aFssz2ZrU_c" width="560"&gt;&lt;/iframe&gt;&lt;br /&gt;&lt;br /&gt;&lt;iframe allowfullscreen="" frameborder="0" height="315" src="http://www.youtube.com/embed/wuP7o10SOG8" width="560"&gt;&lt;/iframe&gt;&lt;br /&gt;&lt;br /&gt;A brief overview of many of the tools that I ended up using:&lt;br /&gt;&lt;br /&gt;- Safety goggles and worker gloves&lt;br /&gt;- Ohm-meter (for continuity testing)&lt;br /&gt;- 5/16" socket wrench (and 7/8" if you actually want to pull off the motor pulley)&lt;br /&gt;- Screwdriver&lt;br /&gt;- Adjustable wrench&lt;br /&gt;- Snap ring pliers (for removing the snap ring on the blower wheel)&lt;br /&gt;- Vice grips (for removing the blower wheel clamp)&lt;br /&gt;- Replacement motor w/ a motor pulley&lt;br /&gt;- Optional: blower wheel &amp;amp; replacement belt (the blower wheel can be a pain to remove since it may be fused to the motor shaft after many years of use).&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-5lgcQxGLPEg/TyTblnCub-I/AAAAAAAACM0/x2iwFbscag0/s1600/bracket.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="320" src="http://1.bp.blogspot.com/-5lgcQxGLPEg/TyTblnCub-I/AAAAAAAACM0/x2iwFbscag0/s320/bracket.png" width="302" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;Disclaimer: do not try any of this work without the plug for the dryer disconnected. &amp;nbsp; You may also want to verify that your circuit breaker didn't accidentally trip and therefore is actually supplying no power to your unit (if you see a lamp on when the lid is open, chances are there is power). &amp;nbsp;In addition, verify that the thermal fuse simply needs replacing. &amp;nbsp;The thermal fuse can be seen at the bottom of the dryer, and you can use an ohm-meter to verify that there is still connectivity. &amp;nbsp;In other words, check everything before you decide to replace the motor since it's a fairly involved procedure.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-PY25TWqGpMU/TyTfS_5YrSI/AAAAAAAACNU/vfZF_v40ynE/s1600/fuse.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://2.bp.blogspot.com/-PY25TWqGpMU/TyTfS_5YrSI/AAAAAAAACNU/vfZF_v40ynE/s1600/fuse.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;In order to get to the motor, you need to first remove the hinges from the door (see Chapter 5 - Cabinet Assembly Components). &amp;nbsp;The hinges can be removed with a screwdriver, and the door can be pulled from the slots in the wall. &amp;nbsp; You can then remove the outer shell of the once these hinges and the screws attached to the door are removed.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-otDuq6PJ2t4/TyRyqBX8TaI/AAAAAAAACMk/iIID3etBL-Y/s1600/door.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="248" src="http://1.bp.blogspot.com/-otDuq6PJ2t4/TyRyqBX8TaI/AAAAAAAACMk/iIID3etBL-Y/s320/door.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Once you've removed the front door (see Section 6 on Tumbler &amp;amp; Bulkheads in the service manual) you can remove the front bulkhead by starting to remove many of the screws that you see. &amp;nbsp;What's confusing is that the instruction manual says "Remove the lowest outside screw at each corner and then the top screw on each side") in step 4. &amp;nbsp;What the manual is really saying is that there are another set of screws holding the tumbler in place, so the manual wants you to remove the screws that aren't attached directly to the tumbler. &amp;nbsp;By doing so, you'll disconnect the first layer of the dryer before you get to the tumbler.&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-oINoE2rpJdQ/TyR1RlyOUaI/AAAAAAAACMs/whmJX6nyK1U/s1600/dryer3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="291" src="http://1.bp.blogspot.com/-oINoE2rpJdQ/TyR1RlyOUaI/AAAAAAAACMs/whmJX6nyK1U/s320/dryer3.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;There are also "hold down brackets" shaped as a horse-shoe that keep you from being able to lift the lid of the dryer.  Once you unscrew the bolt from the bracket with a 5/6" socket wrench, you swing the bracket to the side (see Figure 5-5). &amp;nbsp;Chances are the bracket will fall out, so it's better just to stow them away somewhere.&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-5lgcQxGLPEg/TyTblnCub-I/AAAAAAAACM0/x2iwFbscag0/s1600/bracket.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="320" src="http://1.bp.blogspot.com/-5lgcQxGLPEg/TyTblnCub-I/AAAAAAAACM0/x2iwFbscag0/s320/bracket.png" width="302" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;/div&gt;&lt;br /&gt;The manual covers how to remove most of the front assembly, but basically you need to remove a lot of screws to even get to the tumbler and then to the motor.   You'll notice that the tumbler actually is held in the front and the moment you start removing the screws, it may start to no longer align with the back of the dryer cabinet.&lt;br /&gt;&lt;br /&gt;The tumbler itself may need to be removed, which requires you to crouch down and try to locate several pulleys in the back. &amp;nbsp;One is the motor pulley, which is attached the motor. &amp;nbsp;The other is known as the idler pulley, which is attached a spring and helps provide the tension of the belt. &amp;nbsp;You should be able to pull the idler pulley to the left, which will remove the tension from the belt and allow you to take it. &amp;nbsp;Consult the YouTube clips to see how it's done. &amp;nbsp; This model's instruction manual specifies explicitly that the belt should be ribbed-side down on the tumbler, whereas other models seem to indicate the opposite. &lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-RC0t9xzsB0M/TyThhg_zPJI/AAAAAAAACNs/cROn81bF9hU/s1600/belt.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="219" src="http://4.bp.blogspot.com/-RC0t9xzsB0M/TyThhg_zPJI/AAAAAAAACNs/cROn81bF9hU/s320/belt.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Once the tumbler is removed, you'll eventually get to the blower wheel, which amounts to a small fan at the bottom of the dryer. &amp;nbsp;You need to remove the blower wheel since it's attached to the motor shaft and the motor can't be removed until you detach this blower wheel. &amp;nbsp; Basically there are two clamps attached to the blower wheel: the retaining clip and clamp. &amp;nbsp;The retaining clamp usually requires snap-ring pliers to remove (Home Depot sells them for $20, but you have to make sure that the pliers can fit into the small holes of this retaining clip. &amp;nbsp;Orchard Supply Hardware carried a pair but the attachments were a bit too large.) &amp;nbsp;The clamp itself can be removed with vise grips. &amp;nbsp;If you squeeze the outer protruding edges, the clamp will actually expand (the video clips may show this point, but it's not exactly obvious since it happens so fast). &lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/-A5yy8Uy6kMM/TyTe0WCObbI/AAAAAAAACNM/6d8Mdo42TIg/s1600/blower.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="239" src="http://2.bp.blogspot.com/-A5yy8Uy6kMM/TyTe0WCObbI/AAAAAAAACNM/6d8Mdo42TIg/s320/blower.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Make sure to keep the retaining clip and clamp, since they can be reused when reinstalling the blower wheel. &amp;nbsp;Once you've successfully removed the retaining clip and clamp, you'll need to remove the blower wheel itself. &amp;nbsp;The instructions in the service manual say to "work blower wheel back and forth while pulling off shaft...[and] may require considerable effort to remove the blower wheel." &amp;nbsp;The wheel itself may be somewhat fused to the shaft after so many years of use, so adding some lubricant (careful not to spray it elsewhere in the dryer, since the substance may be flammable) to help remove the wheel. &amp;nbsp; A replacement blower wheel can cost $20-$30, but makes it much easier to attach to a replacement motor so I'd highly recommend replacing it too if you're also planning to change the motor.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-hHbtQpnRAo8/TyTf2V_9WSI/AAAAAAAACNc/lVu2uOOCVF0/s1600/blower2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="221" src="http://4.bp.blogspot.com/-hHbtQpnRAo8/TyTf2V_9WSI/AAAAAAAACNc/lVu2uOOCVF0/s320/blower2.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Once the tumbler blower wheel is removed, you should be able to see the motor. &amp;nbsp;There should be a plastic connector adapter that is attached to the motor that needs to be removed by pushing on both sides. &amp;nbsp;The connector is keyed to only plug-in a certain way, so you shouldn't have to remember which way to reinsert the connector when replacing the motor. &amp;nbsp;The service manual contains the resistance that you could measure from the controller panel to the terminal (i.e 3.00 Ohms from terminal 3 of the start switch to terminal 4), but I found opening another panel to be extra hassle and decided to just replace the battery to see if it would solve the problem. &amp;nbsp;There are YouTube clips that also show how you can create a test cord to verify the motor works independently, but there isn't an off-the-shell unit so you'd have to do soldering work and find a 220-volt three-prong adapter so I decided to opt against making one.&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-PY8wx5H4ewU/TyTjcAI4brI/AAAAAAAACN8/To94Ev7uZSQ/s1600/battery.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="257" src="http://3.bp.blogspot.com/-PY8wx5H4ewU/TyTjcAI4brI/AAAAAAAACN8/To94Ev7uZSQ/s320/battery.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;After the plastic connector is removed, the next step is to remove the motor. &amp;nbsp;There are two retaining clips on the two sides of the motor, which keep the motor in place. &amp;nbsp; You need to remove them by pushing down on one side of the tab, which will hopefully unlatch the tab. &amp;nbsp;Again, the YouTube links are a good place to see how to remove these retaining clips. &amp;nbsp;You shouldn't have to remove the screws attached to the base of the dryer cabinet, but it may be easier to manuever.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-radgbInA6rA/TyTiyIWqVWI/AAAAAAAACN0/5PdnXpM6U2E/s1600/motor2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://4.bp.blogspot.com/-radgbInA6rA/TyTiyIWqVWI/AAAAAAAACN0/5PdnXpM6U2E/s1600/motor2.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;At this point you should check if you have the same replacement motor as the one you purchased. &amp;nbsp;Some motors don't include the motor pulley. &amp;nbsp;If you don't have the motor pulley with the replacement motor, you either ordered the wrong motor, especially for this model. &amp;nbsp;Other MayTag units seem to require that you take it from the old motor using an Allen wrench. &amp;nbsp;(For this particular brand, if you wanted to remove the motor pulley, you could use a 7/8" socket wrench and a wrench to hold one side of the motor shaft steady you loosen the screw attached to the motor pulley. &amp;nbsp;I wouldn't recommend spending the time since the replacement motor you ordered should come with it.)&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;At this point, you should have successfully removed the motor from the dryer. &amp;nbsp; Now you have to reverse the process and reinstall the replacement one. &amp;nbsp;There are several things that are key to know. &amp;nbsp;First, you may find it easier to remove the blower wheel casing that sits adjacent to the motor before installing the motor, since the motor has two cylindrical circles that need to be aligned with the motor housing. &amp;nbsp; The retaining clips need to be added, and you may find one side easier to latch than the other.&amp;nbsp;The plastic connector on the battery terminal should then be reattached to this new motor.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Reattaching the belt also proved to be different from how the YouTube clips demonstrated, which often call for looping the belt around the idler pulley first before attaching to the motor pulley. &amp;nbsp;For this particular model, you actually want to follow the instructions specified in "Installing Drive Belt" which call for looping the belt around the motor pulley first. &amp;nbsp;You want to position the ribbed-side of the belt against the tumbler and you should loop the belt around the motor pulley first ribbed-side. &amp;nbsp; You then want to pull the idler pulley left and up (only pull to the maximum spring tension allowed) and attach the&amp;nbsp;&lt;/div&gt;&lt;div&gt;belt. &amp;nbsp;In this way, the remaining slack from the belt is taken up by the tension in the idler pulley. &amp;nbsp;You should be able to rotate the tumbler now and have everything rotated correctly.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;You shouldn't have had to remove any electrical wiring, but if you inadvertently detached a cable, you should also check that wiring connections are correct. &amp;nbsp; For the dryer lamp, there are two connectors (one ground and one power), and one plug is bigger than the other, which makes it easier to distinguish between the two. &amp;nbsp; Since the door light controls whether the start switch can be used, you actually need to reinstall most of the unit to verify that your motor is successfully work. &amp;nbsp;You can obviously take shortcuts and attach the door without screwing everything back, but do it with caution.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The rest of the installation pretty much involves reversing what you just did. &amp;nbsp;Remember that the lint is flammable, so you would be well-advised to try to clean up all the excess lint in the dryer. &amp;nbsp;&amp;nbsp;Remember that installing the clamp on the blower wheel requires that you use vice grips and squeezing the sides to expand the clamp. &amp;nbsp;In addition, adding the retaining clip on the blower wheel will require using the snap-ring pliers again. &amp;nbsp;Finally, you should verify the felt seals are resting on the outside of the tumbler before reattaching everything, which appear&amp;nbsp;to be used to help reduce the friction between the metal cabinet and the tumbler. (You can check by rotating the tumbler and watching where the felt pieces rest).&lt;br /&gt;&lt;br /&gt;You should make sure that everything works by carefully monitoring the dryer. &amp;nbsp;Although there is a circuit breaker that will trip, it's a good idea not to leave the dryer on when you're out of the house after this install process. &amp;nbsp;You may also wish to find a service technician instead, since this information should only be used as a reference. &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4137806788465333774-5991467764807309264?l=hustoknow.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hustoknow.blogspot.com/feeds/5991467764807309264/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hustoknow.blogspot.com/2012/01/replacing-motor-on-maytag-mde3000ayw.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/5991467764807309264'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/5991467764807309264'/><link rel='alternate' type='text/html' href='http://hustoknow.blogspot.com/2012/01/replacing-motor-on-maytag-mde3000ayw.html' title='Replacing the motor on a Maytag MDE3000AYW dryer...'/><author><name>Roger</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://img.youtube.com/vi/aFssz2ZrU_c/default.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4137806788465333774.post-3403716626242844361</id><published>2012-01-25T09:41:00.000-08:00</published><updated>2012-01-28T23:10:31.793-08:00</updated><title type='text'>Using PyCrypto instead of M2Crypto on Google App Engine</title><content type='html'>If you've ever tried to leverage the M2Crypto library on the Google App Engine platform, you'll notice that you're prevented from doing so.  One of the reasons is that M2Crypto depends on C-compiled openssl libraries that are not allowed to be used on the platform.   The major alternative that Google provides is the &lt;a href="http://code.google.com/appengine/docs/python/tools/libraries.html#PyCrypto"&gt;Python Cryptography Toolkit&lt;/a&gt;, or pycrypto, which is a Python-based version of various encryption algorithms. &lt;br /&gt;&lt;br /&gt;Performance wise, the pycrypto library may not run as fast since it implements the encryption algorithms primarily in Python.   Furthermore, the current version that is supported is version 2.3, which doesn't provide RSA PKCS#1 v1.5 encryption support (if you find yourself getting "Block type 01 not supported, you need to handle the padding/encoding format -- see information below) nor does it provide PEM format (you need to encode your keys as DER format, which are basically the base64-unencoded versions of the PEM).&lt;br /&gt;&lt;br /&gt;Nonetheless, you can get pycrypto working on Google App Engine.  Here's how you can do it inside your App Engine projects.  I'll also discuss some of the extra steps needed to get RSA signatures working too (using the PKCS#1 v1.5 standard).  It's not completely obvious so some of the stack traces you see will hopefully explain why these steps were necessary.&lt;br /&gt;&lt;br /&gt;1. Inside the app.yaml configuration, you need to specify a runtime of python27, and specify the pycrypto librarie.  Note: you either have to install python2.7 on your own local dev instance, or you simply have to deploy constantly to Google App Engine to test your app.  Note that you also need to specify the threadsafe: parameter too for the Python 2.7 experimental version (see the &lt;a href="http://code.google.com/appengine/docs/python/python27/newin27.html"&gt;Google App Engine Python 2.7 docs &lt;/a&gt;for more information).&lt;br /&gt;&lt;pre class="prettyprint"&gt;application: YOUR_APP_NAME_HERE&lt;br /&gt;version: 1runtime: python27&lt;br /&gt;api_version: 1&lt;br /&gt;threadsafe: false&lt;br /&gt;handlers:- url: /static  &lt;br /&gt;static_dir: static&lt;br /&gt;&lt;br /&gt;...&lt;br /&gt;libraries:&lt;br /&gt;- name: lxml  &lt;br /&gt;version: "2.3"&lt;br /&gt;- name: pycrypto  &lt;br /&gt;version: "2.3"&lt;/pre&gt;&lt;br /&gt;2. You need to generate your RSA keys with the DER format.  If you see this stack trace, chances are that you're trying to provide key formats in PEM.  The Pycrypto v2.3 version on Google App Engine  assumes you're feeding things in DER format.  In other words, these two lines will not work:&lt;br /&gt;&lt;pre class="prettyprint"&gt;&gt;&gt;&gt; from Crypto.PublicKey import RSA&lt;br /&gt;&gt;&gt;&gt; privatekey = open("privkey.pem", "r")&lt;br /&gt;&gt;&gt;&gt; m = RSA.importKey(privatekey)&lt;br /&gt;&lt;br /&gt;Stack trace:File &lt;br /&gt;"/base/python27_runtime/python27_lib/versions/third_party/pycrypto-2.3/Crypto/PublicKey/RSA.py", line 247, in importKey    return self._importKeyDER(der)  &lt;br /&gt;File "/base/python27_runtime/python27_lib/versions/third_party/pycrypto-2.3/Crypto/PublicKey/RSA.py", line 234, in _importKeyDER    raise ValueError("RSA key format is not supported")&lt;/pre&gt;&lt;br /&gt;You can use the openssl command to convert your public/private key PEM file to DER format.    You'll also need to the same if you're using a PEM certificate too:&lt;br /&gt;&lt;pre class="prettyprint"&gt;openssl rsa -outform der privkey.pem &amp;gt; privkey.der&lt;br /&gt;openssl rsa -outform der &amp;lt; cert.pem &amp;gt; cert.der&lt;/pre&gt;&lt;br /&gt;These lines should the work:&lt;br /&gt;&lt;pre class="prettyprint"&gt;&gt;&gt;&gt; from Crypto.PublicKey import RSA &lt;br /&gt;&gt;&gt;&gt; privatekey = open("privkey.der", "r")&lt;br /&gt;&gt;&gt;&gt; m = RSA.importKey(privatekey)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;If you really want to verify your keys are working correctly, you can also use m.decrypt() and m.encrypt() to encode and encode the data.  You can click &lt;a href="http://www.laurentluce.com/posts/python-and-cryptography-with-pycrypto/"&gt;this link&lt;/a&gt; for another good overview about how to use pycrypto.&lt;br /&gt;&lt;pre class="prettyprint"&gt;&gt;&gt;&gt; from Crypto.PublicKey import RSA&lt;br /&gt;&gt;&gt;&gt; from Crypto import Random&lt;br /&gt;&gt;&gt;&gt; plaintext = "abcd"&lt;br /&gt;&gt;&gt;&gt; random_generator = Random.new().read&lt;br /&gt;&gt;&gt;&gt; encrypted_data = m.encrypt(plaintext, random_generator)&lt;br /&gt;&gt;&gt;&gt; m.decrypt(encrypted_data)&lt;/pre&gt;&lt;br /&gt;3. Next, it's not completely obvious how to use pycrypto to handle RSA signature signing.  Most of the heavy lifting is done by the M2Crypto library, but if you're using pycrypto to do you may find yourself having to do some extra work to implement some of the PKCS algorithms.&lt;br /&gt;&lt;br /&gt;For PKCS#1 v1.5 (which M2Crypto seems to be using), the private key is signed by the sender using the decryption process (not encryption) while the public key is used with the encryption process  by the receiver to verify a signature, which is somewhat the opposite if you wanted to encrypt/decrypt data. (You can confirm this point by reviewing the &lt;a href="http://8-bits.googlecode.com/svn-history/r9/trunk/pycrypto-2.0.1/Doc/pycrypt.tex"&gt;Latex-based pycrypt.tex&lt;/a&gt; file).&lt;br /&gt;&lt;br /&gt;In the M2Crypto world, RSA signature can be performed with the following lines:&lt;br /&gt;&lt;pre class="prettyprint"&gt;&amp;gt;&amp;gt;&amp;gt; m = M2Crypto.RSA.load_key_string(privatekey)&amp;gt;&amp;gt;&amp;gt; digest = hashlib.sha1(plaintext).digest()&amp;gt;&amp;gt;&amp;gt; signature = m.sign(digest, "sha1")&lt;/pre&gt;In the PyCrypto world, you have to implement PKCS#1 v1.5 yourself.  If you get "block type is not 01" when using M2Crypto to verify a signature create by PyCrypto, chances are M2Crypto is expecting the data to be encoded.   You can review the &lt;a href="http://tools.ietf.org/html/rfc2313"&gt;RFC2313&lt;/a&gt; spec to see what needs to be done to convert the signature with the correct padding.  There is a padding spacing (ps) parameter that determines how many FF bytes should be added, which can be calculated by doing a number.size() call on your private key with the PyCrypto library and dividing by 8 bits (i.e. 2048 bits / 8 = 256 bytes)&lt;br /&gt;&lt;br /&gt;The code below demonstrates how you would implement PKCS#1 v1.5 padding.  Note that the private key is used to decrypt the data:&lt;br /&gt;&lt;pre class="prettyprint"&gt;import hashlib&lt;br /&gt;&lt;br /&gt;   def _emsa_pkcs1_v1_5_encode(M, emLen):&lt;br /&gt;        SHA1DER = '\x30\x21\x30\x09\x06\x05\x2b\x0e\x03\x02\x1a\x05\x00\x04\x14'&lt;br /&gt;        SHA1DERLEN = len(SHA1DER) + 0x14&lt;br /&gt;&lt;br /&gt;        H = hashlib.sha1(M).digest()&lt;br /&gt;        T = SHA1DER + H&lt;br /&gt;        if emLen &lt; (SHA1DERLEN + 11):&lt;br /&gt;            raise Exception('intended encoded message length too short (%s)' % emLen)&lt;br /&gt;        ps = '\xff' * (emLen - SHA1DERLEN - 3)&lt;br /&gt;        if len(ps) &lt; 8:&lt;br /&gt;            raise Exception('ps length too short')&lt;br /&gt;        return '\x00\x01' + ps + '\x00' + T&lt;br /&gt;&lt;/pre&gt;4. Currently, Google App Engine only supports pycrypto v2.3.  You may encounter issues with using openssl-generated keys to do RSA signature verification.  The bug reports for pycrypto v2.3 are posted here:&lt;a href="https://bugs.launchpad.net/pycrypto/+bug/898466"&gt;https://bugs.launchpad.net/pycrypto/+bug/898466&lt;/a&gt;&lt;a href="https://bugs.launchpad.net/pycrypto/+bug/702835"&gt;https://bugs.launchpad.net/pycrypto/+bug/702835&lt;/a&gt;A bug report has been issued here:&lt;a href="http://code.google.com/p/googleappengine/issues/detail?id=5302"&gt;http://code.google.com/p/googleappengine/issues/detail?id=5302&lt;/a&gt;If you find issues trying to decrypt/encrypt data, you can bypass this issue by importing your own custom RSA.py version patched from the 2.3 version.  Here is the diff that is needed to patch the v2.3 RSA.py version.&lt;pre class="prettyprint"&gt;--- a/RSA.py&lt;br /&gt;+++ b/RSA.py&lt;br /&gt;@@ -206,7 +206,7 @@ class RSAImplementation(object):&lt;br /&gt;def generate(self, bits, randfunc=None, progress_func=None):&lt;br /&gt;if bits &amp;lt; 1024 or (bits &amp;amp; 0xff) != 0:              # pubkey.getStrongPrime doesn't like anything that's not a multiple of 128 and &amp;gt; 512&lt;br /&gt;-            raise ValueError("RSA modulus length must be a multiple of 256 and &amp;gt; 1024")&lt;br /&gt;+            raise ValueError("RSA modulus length must be a multiple of 256 and &amp;gt;= 1024")&lt;br /&gt;rf = self._get_randfunc(randfunc)&lt;br /&gt;obj = _RSA.generate_py(bits, rf, progress_func)    # TODO: Don't use legacy _RSA module&lt;br /&gt;key = self._math.rsa_construct(obj.n, obj.e, obj.d, obj.p, obj.q, obj.u)&lt;br /&gt;@@ -221,7 +221,8 @@ class RSAImplementation(object):&lt;br /&gt;der.decode(externKey, True)&lt;br /&gt;if len(der)==9 and der.hasOnlyInts() and der[0]==0:&lt;br /&gt;# ASN.1 RSAPrivateKey element&lt;br /&gt;-               del der[6:8]    # Remove d mod (p-1) and d mod (q-1)&lt;br /&gt;+               del der[6:]     # Remove d mod (p-1), d mod (q-1), and q^{-1} mod p&lt;br /&gt;+                der.append(inverse(der[4],der[5])) # Add p^{-1} mod q&lt;br /&gt;del der[0]      # Remove version&lt;br /&gt;return self.construct(der[:]&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Hopefully this explains how you can get RSA encryption and signature signing working on Google App Engine without the M2Crypto library!  (The last issue was not easy to find.  In fact, it seemed very puzzling that RSA signature verification worked fine on local hosts, but never seemed to work on GAE instances.)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4137806788465333774-3403716626242844361?l=hustoknow.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hustoknow.blogspot.com/feeds/3403716626242844361/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hustoknow.blogspot.com/2012/01/using-pycrypto-instead-of-m2crypto-on.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/3403716626242844361'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/3403716626242844361'/><link rel='alternate' type='text/html' href='http://hustoknow.blogspot.com/2012/01/using-pycrypto-instead-of-m2crypto-on.html' title='Using PyCrypto instead of M2Crypto on Google App Engine'/><author><name>Roger</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4137806788465333774.post-7039320368765772572</id><published>2012-01-18T10:06:00.000-08:00</published><updated>2012-01-25T11:31:09.797-08:00</updated><title type='text'>Forcing Ubuntu 10.0.4 to renew a DHCP client after hibernate/sleep mode...</title><content type='html'>If you're on a network that has a short DHCP lease and your Ubuntu machine suspends/hibernates, you may find that you often may be IP conflicting with another system on the same network.   You can run /etc/init.d/networking but often times you just want to force the DHCP renewal yourself.&lt;br /&gt;&lt;br /&gt;One way is to modify /etc/default/acpi-support and add the "networking" module to the STOP_SERVICES declaration for the /etc/default/acpi-support file.  However, if you have multiple networking interfaces that need to be restarted on bootup, this could cause more delays after resuming.   It also seems strange that you have to taken down an entire networking module just to get things working again.&lt;br /&gt;&lt;br /&gt;Another approach is to execute dhclient -r after resuming.  As of Ubuntu 10.04, the power management modules actually use pm-utils, though there is a legacy /etc/acpi/ directory that often invokes pm-sleep, pm-hibernate and setups events depending on laptop keys pressed.  You can find learn &lt;a href="https://wiki.archlinux.org/index.php/Pm-utils#Creating_your_own_hooks"&gt;how to write your own power management hooks&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Here's how I got things to work:&lt;div&gt;&lt;br /&gt;1. sudo vi /etc/pm/sleep.d/00_network&lt;br /&gt;&lt;pre class="prettyprint"&gt;#!/bin/bash&lt;br /&gt;case $1 in&lt;br /&gt; hibernate)&lt;br /&gt;     echo "Hey guy, we are going to suspend to disk!"&lt;br /&gt;     ;;&lt;br /&gt; suspend)&lt;br /&gt;     echo "Oh, this time we are doing a suspend to RAM. Cool!"&lt;br /&gt;     ;;&lt;br /&gt; thaw)&lt;br /&gt;     /sbin/dhclient -r&lt;br /&gt;     /sbin/dhclient &lt;br /&gt;     ;;&lt;br /&gt; resume)&lt;br /&gt;     /sbin/dhclient -r&lt;br /&gt;     /sbin/dhclient &lt;br /&gt;     ;;&lt;br /&gt; *)  echo "Somebody is calling me totally wrong."&lt;br /&gt;     ;;&lt;br /&gt;esac&lt;/pre&gt;2. sudo chown root /etc/pm/sleep.d/00_network&lt;br /&gt;&lt;br /&gt;To test, run sudo pm-sleep or sudo pm-hibernate.  On bootup, take a look at tail -30 /var/log/pm-suspend.log and see if you see the 00_network script resume.   If so, you should be good to go!&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;/etc/pm/sleep.d/00_network thaw hibernate:Internet Systems Consortium DHCP Client V3.1.3&lt;br /&gt;Copyright 2004-2009 Internet Systems Consortium.&lt;br /&gt;All rights reserved.&lt;br /&gt;For info, please visit https://www.isc.org/software/dhcp/&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4137806788465333774-7039320368765772572?l=hustoknow.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hustoknow.blogspot.com/feeds/7039320368765772572/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hustoknow.blogspot.com/2012/01/forcing-ubuntu-1004-to-renew-dhcp.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/7039320368765772572'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/7039320368765772572'/><link rel='alternate' type='text/html' href='http://hustoknow.blogspot.com/2012/01/forcing-ubuntu-1004-to-renew-dhcp.html' title='Forcing Ubuntu 10.0.4 to renew a DHCP client after hibernate/sleep mode...'/><author><name>Roger</name><uri>http://www.blogger.com/profile/06655396766752051975</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4137806788465333774.post-6920874590582554350</id><published>2012-01-14T17:47:00.000-08:00</published><updated>2012-01-17T18:10:30.965-08:00</updated><title type='text'>Upgrading from jQuery v1.4 to v1.5..</title><content type='html'>Apparently jQuery v1.5+ no longer allows extra data parameters.  Instead, it's passed in as extra.data.&lt;br /&gt;&lt;pre class="prettyprint"&gt;$("#mydiv").click(my_func, {'abc' : 123});  (old)&lt;/pre&gt;&lt;pre class="prettyprint"&gt;&lt;pre class="prettyprint"&gt;my_func = function(event) { alert(event.data); }&lt;/pre&gt;&lt;/pre&gt;&lt;a href="http://code.jquery.com/jquery-1.4.2.js"&gt;http://code.jquery.com/jquery-1.4.2.js&lt;/a&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " +&lt;br /&gt;"mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +&lt;br /&gt;"change select submit keydown keypress keyup error").split(" "), function( i, name ) {&lt;br /&gt;&lt;br /&gt;// Handle event binding&lt;br /&gt;jQuery.fn[ name ] = function( fn ) {&lt;br /&gt;return fn ? this.bind( name, fn ) : this.trigger( name );&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;if ( jQuery.attrFn ) {&lt;br /&gt;jQuery.attrFn[ name ] = true;&lt;br /&gt;}&lt;br /&gt;});&lt;br /&gt;&lt;/pre&gt;Note that the data parameter is no longer passed as an extra parameter:&lt;br /&gt;&lt;a href="http://code.jquery.com/jquery-1.7.js"&gt;http://code.jquery.com/jquery-1.7.js&lt;/a&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " +&lt;br /&gt;"mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +&lt;br /&gt;"change select submit keydown keypress keyup error contextmenu").split(" "), function( i, name ) {&lt;br /&gt;&lt;br /&gt;// Handle event binding&lt;br /&gt;jQuery.fn[ name ] = function( data, fn ) {&lt;br /&gt;if ( fn == null ) {&lt;br /&gt; fn = data;&lt;br /&gt; data = null;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;return arguments.length &amp;gt; 0 ?&lt;br /&gt; this.bind( name, data, fn ) :&lt;br /&gt; this.trigger( name );&lt;br /&gt;};&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4137806788465333774-6920874590582554350?l=hustoknow.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hustoknow.blogspot.com/feeds/6920874590582554350/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hustoknow.blogspot.com/2012/01/upgrading-from-jquery-v14-to-v15.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/6920874590582554350'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/6920874590582554350'/><link rel='alternate' type='text/html' href='http://hustoknow.blogspot.com/2012/01/upgrading-from-jquery-v14-to-v15.html' title='Upgrading from jQuery v1.4 to v1.5..'/><author><name>Roger</name><uri>http://www.blogger.com/profile/06655396766752051975</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4137806788465333774.post-7474301780649823789</id><published>2012-01-13T19:10:00.000-08:00</published><updated>2012-01-13T19:11:28.499-08:00</updated><title type='text'>Port scanning for web servers on your network..</title><content type='html'>&lt;pre class="prettyprint"&gt;sudo apt-get install nmap&lt;br /&gt;sudo nmap -sS -O -p80 192.168.0.1/24&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4137806788465333774-7474301780649823789?l=hustoknow.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hustoknow.blogspot.com/feeds/7474301780649823789/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hustoknow.blogspot.com/2012/01/port-scanning-for-web-servers-on-your.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/7474301780649823789'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/7474301780649823789'/><link rel='alternate' type='text/html' href='http://hustoknow.blogspot.com/2012/01/port-scanning-for-web-servers-on-your.html' title='Port scanning for web servers on your network..'/><author><name>Roger</name><uri>http://www.blogger.com/profile/06655396766752051975</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4137806788465333774.post-5384925233031801130</id><published>2012-01-12T18:40:00.000-08:00</published><updated>2012-01-13T15:46:51.134-08:00</updated><title type='text'>Dealing with zombie Facebook cookies...</title><content type='html'>&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;Not able to logout in your Facebook Connect applications? Facebook recently pushed a change in the past day or so such that the domain= parameter is now added to your fbsr_ cookies.   If you find that your cookies are not being removed after a user attemps to logout, chances are you're experiencing the repercussions of these recent changes.&lt;br /&gt;&lt;br /&gt;In the timestamped version (1326413659,169943147), there is a section that was added to the Facebook Connect Library:&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;table style="color: rgb(34, 34, 34); font-family: arial, sans-serif; font-size: 11px; background-color: rgba(255, 255, 255, 0.917969); border-collapse: collapse; width: 853px; "&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td bgcolor="#ccc" align="right" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; text-align: right; width: 23px; background-color: rgb(204, 204, 204); "&gt;2340&lt;/td&gt;&lt;td bgcolor="#ccc" align="right" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; text-align: right; width: 23px; background-color: rgb(204, 204, 204); "&gt;2348&lt;/td&gt;&lt;td style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; "&gt;         }&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td bgcolor="#ccc" align="right" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; text-align: right; width: 23px; background-color: rgb(204, 204, 204); "&gt;2341&lt;/td&gt;&lt;td bgcolor="#ccc" align="right" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; text-align: right; width: 23px; background-color: rgb(204, 204, 204); "&gt;2349&lt;/td&gt;&lt;td style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; "&gt;         return b;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td bgcolor="#ccc" align="right" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; text-align: right; width: 23px; background-color: rgb(204, 204, 204); "&gt;2342&lt;/td&gt;&lt;td bgcolor="#ccc" align="right" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; text-align: right; width: 23px; background-color: rgb(204, 204, 204); "&gt;2350&lt;/td&gt;&lt;td style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; "&gt;     },&lt;/td&gt;&lt;/tr&gt;&lt;tr bgcolor="#dfd" style="background-color: rgb(221, 255, 221); "&gt;&lt;td bgcolor="#ccc" align="right" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; text-align: right; width: 23px; background-color: rgb(204, 204, 204); "&gt;&lt;/td&gt;&lt;td bgcolor="#ccc" align="right" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; text-align: right; width: 23px; background-color: rgb(204, 204, 204); "&gt;2351&lt;/td&gt;&lt;td style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; "&gt;     loadMeta: function() {&lt;/td&gt;&lt;/tr&gt;&lt;tr bgcolor="#dfd" style="background-color: rgb(221, 255, 221); "&gt;&lt;td bgcolor="#ccc" align="right" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; text-align: right; width: 23px; background-color: rgb(204, 204, 204); "&gt;&lt;/td&gt;&lt;td bgcolor="#ccc" align="right" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; text-align: right; width: 23px; background-color: rgb(204, 204, 204); "&gt;2352&lt;/td&gt;&lt;td style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; "&gt;         var a = document.&lt;wbr&gt;cookie.match('\\bfbm_' + FB._&lt;wbr&gt;apiKey + '="([^;]*)\\b'),&lt;/td&gt;&lt;/tr&gt;&lt;tr bgcolor="#dfd" style="background-color: rgb(221, 255, 221); "&gt;&lt;td bgcolor="#ccc" align="right" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; text-align: right; width: 23px; background-color: rgb(204, 204, 204); "&gt;&lt;/td&gt;&lt;td bgcolor="#ccc" align="right" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; text-align: right; width: 23px; background-color: rgb(204, 204, 204); "&gt;2353&lt;/td&gt;&lt;td style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; "&gt;             b;&lt;/td&gt;&lt;/tr&gt;&lt;tr bgcolor="#dfd" style="background-color: rgb(221, 255, 221); "&gt;&lt;td bgcolor="#ccc" align="right" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; text-align: right; width: 23px; background-color: rgb(204, 204, 204); "&gt;&lt;/td&gt;&lt;td bgcolor="#ccc" align="right" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; text-align: right; width: 23px; background-color: rgb(204, 204, 204); "&gt;2354&lt;/td&gt;&lt;td style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; "&gt;         if (a) {&lt;/td&gt;&lt;/tr&gt;&lt;tr bgcolor="#dfd" style="background-color: rgb(221, 255, 221); "&gt;&lt;td bgcolor="#ccc" align="right" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; text-align: right; width: 23px; background-color: rgb(204, 204, 204); "&gt;&lt;/td&gt;&lt;td bgcolor="#ccc" align="right" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; text-align: right; width: 23px; background-color: rgb(204, 204, 204); "&gt;2355&lt;/td&gt;&lt;td style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; "&gt;             b = FB.QS.decode(&lt;wbr&gt;a[1]);&lt;/td&gt;&lt;/tr&gt;&lt;tr bgcolor="#dfd" style="background-color: rgb(221, 255, 221); "&gt;&lt;td bgcolor="#ccc" align="right" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; text-align: right; width: 23px; background-color: rgb(204, 204, 204); "&gt;&lt;/td&gt;&lt;td bgcolor="#ccc" align="right" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; text-align: right; width: 23px; background-color: rgb(204, 204, 204); "&gt;2356&lt;/td&gt;&lt;td style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; "&gt;             if (!FB.Cookie._&lt;wbr&gt;domain) FB.Cookie._domain = b.&lt;wbr&gt;base_domain;&lt;/td&gt;&lt;/tr&gt;&lt;tr bgcolor="#dfd" style="background-color: rgb(221, 255, 221); "&gt;&lt;td bgcolor="#ccc" align="right" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; text-align: right; width: 23px; background-color: rgb(204, 204, 204); "&gt;&lt;/td&gt;&lt;td bgcolor="#ccc" align="right" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; text-align: right; width: 23px; background-color: rgb(204, 204, 204); "&gt;2357&lt;/td&gt;&lt;td style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; "&gt;         }&lt;/td&gt;&lt;/tr&gt;&lt;tr bgcolor="#dfd" style="background-color: rgb(221, 255, 221); "&gt;&lt;td bgcolor="#ccc" align="right" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; text-align: right; width: 23px; background-color: rgb(204, 204, 204); "&gt;&lt;/td&gt;&lt;td bgcolor="#ccc" align="right" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; text-align: right; width: 23px; background-color: rgb(204, 204, 204); "&gt;2358&lt;/td&gt;&lt;td style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; "&gt;         return b;&lt;/td&gt;&lt;/tr&gt;&lt;tr bgcolor="#dfd" style="background-color: rgb(221, 255, 221); "&gt;&lt;td bgcolor="#ccc" align="right" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; text-align: right; width: 23px; background-color: rgb(204, 204, 204); "&gt;&lt;/td&gt;&lt;td bgcolor="#ccc" align="right" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; text-align: right; width: 23px; background-color: rgb(204, 204, 204); "&gt;2359&lt;/td&gt;&lt;td style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; "&gt;     },&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td bgcolor="#ccc" align="right" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; text-align: right; width: 23px; background-color: rgb(204, 204, 204); "&gt;2343&lt;/td&gt;&lt;td bgcolor="#ccc" align="right" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; text-align: right; width: 23px; background-color: rgb(204, 204, 204); "&gt;2360&lt;/td&gt;&lt;td style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; "&gt;     loadSignedRequest: &lt;wbr&gt;function() {&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td bgcolor="#ccc" align="right" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; text-align: right; width: 23px; background-color: rgb(204, 204, 204); "&gt;2344&lt;/td&gt;&lt;td bgcolor="#ccc" align="right" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; text-align: right; width: 23px; background-color: rgb(204, 204, 204); "&gt;2361&lt;/td&gt;&lt;td style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; "&gt;         var a = document.&lt;wbr&gt;cookie.match('\\bfbsr_' + FB._&lt;wbr&gt;apiKey + '=([^;]*)\\b');&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td bgcolor="#ccc" align="right" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; text-align: right; width: 23px; background-color: rgb(204, 204, 204); "&gt;2345&lt;/td&gt;&lt;td bgcolor="#ccc" align="right" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; text-align: right; width: 23px; background-color: rgb(204, 204, 204); "&gt;2362&lt;/td&gt;&lt;td style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; "&gt;         if (!a) return null;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;The regexp apparently fails to match because there is an extra quotation mark.  Instead of:&lt;br /&gt;&lt;pre class="prettyprint"&gt;var a = document.cookie.match('\\bfbm_' + FB._apiKey + '="([^;]*)\\b'), b;&lt;/pre&gt;It should be:&lt;br /&gt;&lt;pre class="prettyprint"&gt;var a = document.cookie.match('\\bfbm_' + FB._apiKey + '=([^;]*)\\b'), b;&lt;/pre&gt;If you inspect document.cookie in a JavaScript console, you'll see no sign of how this regexp could match (i.e. the regexp would match fbm_1234="abcde" but not fm_1234=abcde).  You can also use the Chrome/Safari Web Inspector, put breakpoints on this function, and use the deminifier feature (look for the {} icon at the bottom) to double-check.&lt;br /&gt;&lt;br /&gt;Background info: Before the OAuth2 migration, the fbs_ cookie was used.  Included in the fbs_cookie was a query string that needed to be decoded and the base_domain parameter used for the domain= cookie parameter.(For more background about how to set or delete cookies in JavaScript, see: &lt;a href="http://www.quirksmode.org/js/cookies.html"&gt;http://www.quirksmode.org/js/cookies.html&lt;/a&gt;.)&lt;br /&gt;&lt;br /&gt;Cookies be cleared by setting the expiration date to 01/01/1970 GMT.  However, most browsers won't know how to delete the cookie unless the path= and domain= parameters are set correctly too.  In other words, if you had a cookie named fbsr_1234, with domain=abc.com, the browser would not be able to delete it unless you also specified this parameter.&lt;div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Until now, OAuth2 didn't include the domain= parameter in fbsr_ cookies.  But with today's recent push, it is now being used.   The result?  If you had an old cookie without this domain= parameter and attempted to logout with this new JavaScript code, you might find that you're unable to clear them.  You may also encounter strange logout issues in general and not see the fbsr_ cookie cleared correctly.&lt;br /&gt;&lt;br /&gt;When you logout, a cross-domain request gets sent to Facebook to invalidate the session. When you hit reload and invoke another FB.init() command, another request gets sent to Facebook's site -- since FB recognizes the existing cookie as invalid, it correctly deletes the cookie from your browser (the cross-domain response appears to pass in the domain= parameter correctly). Unfortunately the clearing of the cookie initially doesn't work because the fbm_ cookie isn't parsed correctly because of this regexp bug.&lt;br /&gt;&lt;br /&gt;Facebook will most likely fix this issue soon, though in the interim your users may not be able to logout of your app.  One thing we've done is to use a server-side code to instruct the browser to clear the cookie, though it may not always work unless your page invokes FB.init() properly and receives back a cross-domain request from Facebook to set the domain= properly.&lt;br /&gt;&lt;br /&gt;You can examine this code for how you can delete cookies from the server-side:&lt;br /&gt;&lt;br /&gt;&lt;a href="https://github.com/rogerhu/facebook_oauth2/blob/master/views.py"&gt;https://github.com/rogerhu/facebook_oauth2/blob/master/views.py&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Want to track Facebook Connect JavaScript changes?  Check out: &lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;a href="https://github.com/rogerhu/connect-js"&gt;https://github.com/rogerhu/connect-js&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4137806788465333774-5384925233031801130?l=hustoknow.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hustoknow.blogspot.com/feeds/5384925233031801130/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hustoknow.blogspot.com/2012/01/dealing-with-zombie-facebook-cookies.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/5384925233031801130'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/5384925233031801130'/><link rel='alternate' type='text/html' href='http://hustoknow.blogspot.com/2012/01/dealing-with-zombie-facebook-cookies.html' title='Dealing with zombie Facebook cookies...'/><author><name>Roger</name><uri>http://www.blogger.com/profile/06655396766752051975</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4137806788465333774.post-1221301115598557225</id><published>2012-01-10T23:16:00.000-08:00</published><updated>2012-01-10T23:16:13.107-08:00</updated><title type='text'>Using FTP/TLS with Python 2.6...</title><content type='html'>FTP over SSL/TLS can be supported on Python 2.6.  If you have prod machines that are running Python 2.6 and you want to avoid upgrading, you can do the following:&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;wget http://www.python.org/ftp/python/2.7.1/Python-2.7.1.tgz&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;...untar the file and grab the Lib/ftplib.py file.&lt;br /&gt;&lt;br /&gt;You can then do:&lt;br /&gt;&lt;pre class="prettyprint"&gt;from ftplib import FTP_TLS&lt;br /&gt;&lt;br /&gt;ftps = FTP_TLS('ftp.mysite.com')&lt;br /&gt;ftps.login('userid', 'pw')&lt;br /&gt;ftps.prot_p()&lt;br /&gt;ftps.retrlines('LIST')&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To download a file, you would do: &lt;br /&gt;&lt;pre class="prettyprint"&gt;ftps.retrbinary('RETR file.bin', open('output.bin', 'wb').write)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;or to upload a file, you would do something on the order of (borrowed from &lt;a href="http://www.daniweb.com/software-development/python/threads/110195"&gt;http://www.daniweb.com/software-development/python/threads/110195&lt;/a&gt;)&lt;br /&gt;&lt;pre class="prettyprint"&gt;def upload(ftp, file):&lt;br /&gt;  ext = os.path.splitext(file)[1]&lt;br /&gt;  if ext in (".txt", ".htm", ".html"):&lt;br /&gt;     ftp.storlines("STOR %s" % (file), open(file))&lt;br /&gt;  else:&lt;br /&gt;     print ftp.storbinary("STOR %s" % (file), open(file, "rb"), 1024)&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4137806788465333774-1221301115598557225?l=hustoknow.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hustoknow.blogspot.com/feeds/1221301115598557225/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hustoknow.blogspot.com/2012/01/using-ftptls-with-python-26.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/1221301115598557225'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/1221301115598557225'/><link rel='alternate' type='text/html' href='http://hustoknow.blogspot.com/2012/01/using-ftptls-with-python-26.html' title='Using FTP/TLS with Python 2.6...'/><author><name>Roger</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4137806788465333774.post-7949434454916710164</id><published>2012-01-10T15:08:00.001-08:00</published><updated>2012-01-10T15:08:52.046-08:00</updated><title type='text'>jQuery UI 1.9</title><content type='html'>Cool slide presentation about jQuery UI 1.9..&lt;br /&gt;&lt;br /&gt;&lt;a href="http://ajpiano.com/widgetfactory/"&gt;http://ajpiano.com/widgetfactory/&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4137806788465333774-7949434454916710164?l=hustoknow.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hustoknow.blogspot.com/feeds/7949434454916710164/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hustoknow.blogspot.com/2012/01/jquery-ui-19.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/7949434454916710164'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/7949434454916710164'/><link rel='alternate' type='text/html' href='http://hustoknow.blogspot.com/2012/01/jquery-ui-19.html' title='jQuery UI 1.9'/><author><name>Roger</name><uri>http://www.blogger.com/profile/06655396766752051975</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4137806788465333774.post-885830492867941980</id><published>2012-01-10T13:27:00.000-08:00</published><updated>2012-01-10T13:29:32.997-08:00</updated><title type='text'>Cisco Unified Communications 500 Series - Cisco Smart Business Communication Systems</title><content type='html'>&lt;a href="https://supportforums.cisco.com/docs/DOC-9772"&gt;https://supportforums.cisco.com/docs/DOC-9772&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Can the T1/E1 module on UC500 terminate Voice and Data?&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;The VWIC-2MFT-T1/E1 card on UC500 can only be used for Voice T1 termination. It cannot be used for terminating Data T1s. On UC500, this card does not support multiplexing of Voice and Data.&lt;/span&gt; For Data connectivity, using an ISR based T1/E1 is recommended.&lt;br /&gt;&lt;br /&gt;What are the total number of PSTN interfaces that the UC500 can support?&lt;br /&gt;Depending on the UC500 model or SKU - can have any of the below:&lt;br /&gt;4 to 12 FXO ports or 2 to 6 BRI ports (VIC slot can be used to add interfaces in certain model)&lt;br /&gt;T1/E1 VWIC interface card for use in the 8-, 16-, and 32-user UC500 SKUs (VIC slot can be used to add this T1/E1 interface card)&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;UC500 48-user SKU is also available with integrated T1/E1 interface.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;What T1 parameters are supported on the UC500 for voice termination?&lt;br /&gt;The below parameters are supported:&lt;br /&gt;T1 line coding = B8ZS&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;T1 framing = ESF&lt;br /&gt;Interface switch Type for PRI - National with FAS (User side)&lt;br /&gt;  Hunt Type = two way, descending (channel 23 to 1)&lt;br /&gt;  Switch Protocol = See ISDN PRI switch-types above&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4137806788465333774-885830492867941980?l=hustoknow.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hustoknow.blogspot.com/feeds/885830492867941980/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hustoknow.blogspot.com/2012/01/cisco-unified-communications-500-series.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/885830492867941980'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/885830492867941980'/><link rel='alternate' type='text/html' href='http://hustoknow.blogspot.com/2012/01/cisco-unified-communications-500-series.html' title='Cisco Unified Communications 500 Series - Cisco Smart Business Communication Systems'/><author><name>Roger</name><uri>http://www.blogger.com/profile/06655396766752051975</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4137806788465333774.post-2440381981866546184</id><published>2012-01-03T18:16:00.000-08:00</published><updated>2012-01-03T18:19:33.764-08:00</updated><title type='text'>Tracking the SWF file..</title><content type='html'>Facebook doesn't often change the SWF file that is used for its cross-domain handler, but sometimes it's useful to download automatically the right binary file:&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt;SWF_FILE=`grep --color=none all_deminified.js | "_swfPath\": " | sed -E "s/(.*?)swfPath\": \"rsrc.php(.*?.swf)\"/\2/" | sed 's/\\\\//g'`&lt;br /&gt;/usr/bin/wget "http://static.ak.fbcdn.net/rsrc.php/${SWF_FILE}" -O ${SWF_OUTPUT}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="https://github.com/rogerhu/connect-js/blob/master/update_fb_github.sh"&gt;https://github.com/rogerhu/connect-js/blob/master/update_fb_github.sh&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4137806788465333774-2440381981866546184?l=hustoknow.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hustoknow.blogspot.com/feeds/2440381981866546184/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hustoknow.blogspot.com/2012/01/tracking-swf-file.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/2440381981866546184'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/2440381981866546184'/><link rel='alternate' type='text/html' href='http://hustoknow.blogspot.com/2012/01/tracking-swf-file.html' title='Tracking the SWF file..'/><author><name>Roger</name><uri>http://www.blogger.com/profile/06655396766752051975</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4137806788465333774.post-2134702247841828102</id><published>2012-01-02T13:35:00.000-08:00</published><updated>2012-01-02T13:37:13.841-08:00</updated><title type='text'>Cisco routers can't do PPTP client..</title><content type='html'>&lt;a href="https://supportforums.cisco.com/thread/2010872"&gt;https://supportforums.cisco.com/thread/2010872&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt;#config t&lt;br /&gt;#vpdn enable&lt;br /&gt;#vpdn-group 1&lt;br /&gt;#accept-dialout&lt;br /&gt;#protocol ?&lt;br /&gt;  l2tp  Use L2TP&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4137806788465333774-2134702247841828102?l=hustoknow.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hustoknow.blogspot.com/feeds/2134702247841828102/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hustoknow.blogspot.com/2012/01/cisco-routers-cant-do-pptp-client.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/2134702247841828102'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/2134702247841828102'/><link rel='alternate' type='text/html' href='http://hustoknow.blogspot.com/2012/01/cisco-routers-cant-do-pptp-client.html' title='Cisco routers can&apos;t do PPTP client..'/><author><name>Roger</name><uri>http://www.blogger.com/profile/06655396766752051975</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4137806788465333774.post-102658648364495069</id><published>2011-12-23T15:31:00.000-08:00</published><updated>2011-12-23T16:10:09.537-08:00</updated><title type='text'>Changes in Facebook's swf flash handler..</title><content type='html'>Ever since Facebook introduced a change in their &lt;a href="http://hustoknow.blogspot.com/2011/06/how-facebooks-xdproxyphp-seemed-to-have.html"&gt;Facebook Connect Library&lt;/a&gt; that caused severe login issues for IE users that lasted for more than a week, we've created scripts to monitor Facebook's JavaScript Connect Library to detect for any changes that might affect our users.  Nate Frieldy first created the &lt;a href="https://github.com/nfriedly/connect-js"&gt;first version &lt;/a&gt;to monitor for diffs, and I soon forked it &lt;a href="https://github.com/rogerhu/connect-js"&gt;here&lt;/a&gt; to monitor for diffs that span more than just the changes that happen on the 1st line that indicates timestamp changes.&lt;br /&gt;&lt;br /&gt;On December 8, 2011, our diff detection scripts picked up this change:&lt;br /&gt;&lt;br /&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: arial, sans-serif; font-size: 11px; border-collapse: collapse; "&gt;&lt;table style="font-size: 11px; border-collapse: collapse; width: 898px; "&gt;&lt;tbody&gt;&lt;tr bgcolor="#fdd" style="font-size: 11px; background-color: rgb(255, 221, 221); "&gt;&lt;td bgcolor="#ccc" align="right" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; background-color: rgb(204, 204, 204); color: rgb(0, 0, 0); text-align: right; width: 23px; "&gt;1&lt;/td&gt;&lt;td bgcolor="#ccc" align="right" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; background-color: rgb(204, 204, 204); color: rgb(0, 0, 0); text-align: right; width: 23px; "&gt;&lt;/td&gt;&lt;td style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; color: rgb(0, 0, 0); "&gt; /*&lt;span style="font-size: 11px; background-color: rgb(255, 170, 170); "&gt;1323218538&lt;/span&gt;,&lt;span style="font-size: 11px; background-color: rgb(255, 170, 170); "&gt;169893481&lt;/span&gt;,JIT &lt;wbr&gt;Construction: &lt;span style="font-size: 11px; background-color: rgb(255, 170, 170); "&gt;v482006&lt;/span&gt;,en_US*/&lt;/td&gt;&lt;/tr&gt;&lt;tr bgcolor="#dfd" style="font-size: 11px; background-color: rgb(221, 255, 221); "&gt;&lt;td bgcolor="#ccc" align="right" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; background-color: rgb(204, 204, 204); color: rgb(0, 0, 0); text-align: right; width: 23px; "&gt;&lt;/td&gt;&lt;td bgcolor="#ccc" align="right" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; background-color: rgb(204, 204, 204); color: rgb(0, 0, 0); text-align: right; width: 23px; "&gt;1&lt;/td&gt;&lt;td style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; color: rgb(0, 0, 0); "&gt; /*&lt;span style="font-size: 11px; background-color: rgb(170, 255, 170); "&gt;1323305592&lt;/span&gt;,&lt;span style="font-size: 11px; background-color: rgb(170, 255, 170); "&gt;169920374&lt;/span&gt;,JIT &lt;wbr&gt;Construction: &lt;span style="font-size: 11px; background-color: rgb(170, 255, 170); "&gt;v482779&lt;/span&gt;,en_US*/&lt;/td&gt;&lt;/tr&gt;&lt;tr style="font-size: 11px; "&gt;&lt;td bgcolor="#ccc" align="right" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; background-color: rgb(204, 204, 204); color: rgb(0, 0, 0); text-align: right; width: 23px; "&gt;2&lt;/td&gt;&lt;td bgcolor="#ccc" align="right" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; background-color: rgb(204, 204, 204); color: rgb(0, 0, 0); text-align: right; width: 23px; "&gt;2&lt;/td&gt;&lt;td style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; color: rgb(0, 0, 0); "&gt; &lt;/td&gt;&lt;/tr&gt;&lt;tr style="font-size: 11px; "&gt;&lt;td bgcolor="#ccc" align="right" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; background-color: rgb(204, 204, 204); color: rgb(0, 0, 0); text-align: right; width: 23px; "&gt;3&lt;/td&gt;&lt;td bgcolor="#ccc" align="right" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; background-color: rgb(204, 204, 204); color: rgb(0, 0, 0); text-align: right; width: 23px; "&gt;3&lt;/td&gt;&lt;td style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; color: rgb(0, 0, 0); "&gt; if (!window.FB) window.FB = {&lt;/td&gt;&lt;/tr&gt;&lt;tr style="font-size: 11px; "&gt;&lt;td bgcolor="#ccc" align="right" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; background-color: rgb(204, 204, 204); color: rgb(0, 0, 0); text-align: right; width: 23px; "&gt;4&lt;/td&gt;&lt;td bgcolor="#ccc" align="right" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; background-color: rgb(204, 204, 204); color: rgb(0, 0, 0); text-align: right; width: 23px; "&gt;4&lt;/td&gt;&lt;td style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; color: rgb(0, 0, 0); "&gt;     _apiKey: null,&lt;/td&gt;&lt;/tr&gt;&lt;tr style="font-size: 11px; "&gt;&lt;td colspan="3" title="Unchanged content skipped between diff. blocks" align="center" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; border-bottom-width: 1px; border-bottom-style: solid; border-bottom-color: rgb(105, 105, 105); color: rgb(0, 0, 0); text-align: center; border-top-width: 1px; border-top-style: solid; border-top-color: rgb(105, 105, 105); "&gt;…&lt;/td&gt;&lt;/tr&gt;&lt;tr style="font-size: 11px; "&gt;&lt;td bgcolor="#ccc" align="right" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; background-color: rgb(204, 204, 204); color: rgb(0, 0, 0); text-align: right; width: 23px; "&gt;5361&lt;/td&gt;&lt;td bgcolor="#ccc" align="right" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; background-color: rgb(204, 204, 204); color: rgb(0, 0, 0); text-align: right; width: 23px; "&gt;5361&lt;/td&gt;&lt;td style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; color: rgb(0, 0, 0); "&gt;         [10, 3, 181, 34],&lt;/td&gt;&lt;/tr&gt;&lt;tr style="font-size: 11px; "&gt;&lt;td bgcolor="#ccc" align="right" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; background-color: rgb(204, 204, 204); color: rgb(0, 0, 0); text-align: right; width: 23px; "&gt;5362&lt;/td&gt;&lt;td bgcolor="#ccc" align="right" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; background-color: rgb(204, 204, 204); color: rgb(0, 0, 0); text-align: right; width: 23px; "&gt;5362&lt;/td&gt;&lt;td style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; color: rgb(0, 0, 0); "&gt;         [11, 0, 0]&lt;/td&gt;&lt;/tr&gt;&lt;tr style="font-size: 11px; "&gt;&lt;td bgcolor="#ccc" align="right" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; background-color: rgb(204, 204, 204); color: rgb(0, 0, 0); text-align: right; width: 23px; "&gt;5363&lt;/td&gt;&lt;td bgcolor="#ccc" align="right" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; background-color: rgb(204, 204, 204); color: rgb(0, 0, 0); text-align: right; width: 23px; "&gt;5363&lt;/td&gt;&lt;td style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; color: rgb(0, 0, 0); "&gt;     ],&lt;/td&gt;&lt;/tr&gt;&lt;tr bgcolor="#fdd" style="font-size: 11px; background-color: rgb(255, 221, 221); "&gt;&lt;td bgcolor="#ccc" align="right" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; background-color: rgb(204, 204, 204); color: rgb(0, 0, 0); text-align: right; width: 23px; "&gt;5364&lt;/td&gt;&lt;td bgcolor="#ccc" align="right" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; background-color: rgb(204, 204, 204); color: rgb(0, 0, 0); text-align: right; width: 23px; "&gt;&lt;/td&gt;&lt;td style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; color: rgb(0, 0, 0); "&gt;     "_swfPath": "rsrc.php\/&lt;wbr&gt;v1\/&lt;span style="font-size: 11px; background-color: rgb(255, 170, 170); "&gt;yK&lt;/span&gt;\/r\/&lt;span style="font-size: 11px; background-color: rgb(255, 170, 170); "&gt;RIxWozDt5Qq&lt;/span&gt;.swf"&lt;/td&gt;&lt;/tr&gt;&lt;tr bgcolor="#dfd" style="font-size: 11px; background-color: rgb(221, 255, 221); "&gt;&lt;td bgcolor="#ccc" align="right" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; background-color: rgb(204, 204, 204); color: rgb(0, 0, 0); text-align: right; width: 23px; "&gt;&lt;/td&gt;&lt;td bgcolor="#ccc" align="right" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; background-color: rgb(204, 204, 204); color: rgb(0, 0, 0); text-align: right; width: 23px; "&gt;5364&lt;/td&gt;&lt;td style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; color: rgb(0, 0, 0); "&gt;     "_swfPath": "rsrc.php\/&lt;wbr&gt;v1\/&lt;span style="font-size: 11px; background-color: rgb(170, 255, 170); "&gt;yD&lt;/span&gt;\/r\/&lt;span style="font-size: 11px; background-color: rgb(170, 255, 170); "&gt;GL74y29Am1r&lt;/span&gt;.swf"&lt;/td&gt;&lt;/tr&gt;&lt;tr style="font-size: 11px; "&gt;&lt;td bgcolor="#ccc" align="right" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; background-color: rgb(204, 204, 204); color: rgb(0, 0, 0); text-align: right; width: 23px; "&gt;5365&lt;/td&gt;&lt;td bgcolor="#ccc" align="right" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; background-color: rgb(204, 204, 204); color: rgb(0, 0, 0); text-align: right; width: 23px; "&gt;5365&lt;/td&gt;&lt;td style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; color: rgb(0, 0, 0); "&gt; }, true);&lt;/td&gt;&lt;/tr&gt;&lt;tr style="font-size: 11px; "&gt;&lt;td bgcolor="#ccc" align="right" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; background-color: rgb(204, 204, 204); color: rgb(0, 0, 0); text-align: right; width: 23px; "&gt;5366&lt;/td&gt;&lt;td bgcolor="#ccc" align="right" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; background-color: rgb(204, 204, 204); color: rgb(0, 0, 0); text-align: right; width: 23px; "&gt;5366&lt;/td&gt;&lt;td style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; color: rgb(0, 0, 0); "&gt; FB.provide("XD", {&lt;/td&gt;&lt;/tr&gt;&lt;tr style="font-size: 11px; "&gt;&lt;td bgcolor="#ccc" align="right" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; background-color: rgb(204, 204, 204); color: rgb(0, 0, 0); text-align: right; width: 23px; "&gt;5367&lt;/td&gt;&lt;td bgcolor="#ccc" align="right" style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; background-color: rgb(204, 204, 204); color: rgb(0, 0, 0); text-align: right; width: 23px; "&gt;5367&lt;/td&gt;&lt;td style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; color: rgb(0, 0, 0); "&gt;     "_xdProxyUrl": "connect\/&lt;wbr&gt;xd_proxy.php?version=3"&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;This SWF file is Facebook's cross-domain handler for web browsers that don't implement HTML5 but can use a Flash-based version of HTML5's postMessage() function that allows messages to be passed between different domains.  Facebook doesn't often recompile the SWF file, so this diff caught my attention.  The most reliable decompiler I've found is Sothink's SWF decompiler, which can be used to export the ActionScript files with a 30-day trial (for more context about how to decompile, see http://hustoknow.blogspot.com/2011/06/facebooks-flash-xdcomm-receiver.html).&lt;br /&gt;&lt;br /&gt;&lt;div&gt;I've decompiled the SWF file and ActionScript files from &lt;a href="http://static.ak.fbcdn.net/rsrc.php//v1//yD//r//GL74y29Am1r.swf"&gt;http://static.ak.fbcdn.net/rsrc.php\/v1\/yD\/r\/GL74y29Am1r.swf&lt;/a&gt; and reviewed the diffs between the previously decompiled SWF with this one.  If you were to compare the diff changes for the XdComm.as file, you would see:&lt;/div&gt;&lt;pre class="prettyprint"&gt;15a16,17&lt;br /&gt;&amp;gt;         private static var initialized:Boolean = false;&lt;br /&gt;&amp;gt;         private static var origin_validated:Boolean = false;&lt;br /&gt;20,21c22,29&lt;br /&gt;&amp;lt;             Security.allowDomain("*"); &amp;lt;             Security.allowInsecureDomain("*"); --- &amp;gt;             if (XdComm.initialized)&lt;br /&gt;&amp;gt;             {&lt;br /&gt;&amp;gt;                 return;&lt;br /&gt;&amp;gt;             }&lt;br /&gt;&amp;gt;             XdComm.initialized = true;&lt;br /&gt;&amp;gt;             var _loc_1:* = PostMessage.getCurrentDomain();&lt;br /&gt;&amp;gt;             Security.allowDomain(_loc_1);&lt;br /&gt;&amp;gt;             Security.allowInsecureDomain(_loc_1);&lt;br /&gt;51a60&lt;br /&gt;&amp;gt;             ExternalInterface.addCallback("postMessage_init", this.initPostMessage);&lt;br /&gt;60a70,76&lt;br /&gt;&amp;gt;         private function initPostMessage(param1:String, param2:String) : void&lt;br /&gt;&amp;gt;         {&lt;br /&gt;&amp;gt;             origin_validated = true;&lt;br /&gt;&amp;gt;             this.postMessage.init(param1, param2);&lt;br /&gt;&amp;gt;             return;&lt;br /&gt;&amp;gt;         }// end function&lt;br /&gt;&amp;gt;&lt;br /&gt;164a181,189&lt;br /&gt;&amp;gt;         public static function proxy(param1:String, param2:String) : void&lt;br /&gt;&amp;gt;         {&lt;br /&gt;&amp;gt;             if (origin_validated)&lt;br /&gt;&amp;gt;             {&lt;br /&gt;&amp;gt;                 ExternalInterface.call(param1, param2);&lt;br /&gt;&amp;gt;             }&lt;br /&gt;&amp;gt;             return;&lt;br /&gt;&amp;gt;         }// end function&lt;br /&gt;&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The changes indicate that Facebook has tightened the cross-domain security policies.  Instead of using wildcard domains to accept messages in its &lt;a href="http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/system/Security.html#allowDomain()"&gt;allowDomain() function&lt;/a&gt;, it now invokes a call to getCurrentDomain(), which is a function defined in the PostMessage.as file used to execute a call to document.domain, relying more on the browser to define the security restrictions.&lt;br /&gt;&lt;br /&gt;Most of these change should not affect your users...just wished Facebook would discuss more what's going on behind the scenes since your apps may very well be using the Facebook Connect Library without realizing these changes are happening beneath you!&lt;br /&gt;&lt;br /&gt;I've started to post the decompiled SWF files here: &lt;br /&gt;&lt;a href="https://github.com/rogerhu/connect-js/tree/master/swf"&gt;https://github.com/rogerhu/connect-js/tree/master/swf&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Note that these updates are only manually.  If someone knows of an open-source SWF decompiler, then the diffs could be much more automated!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4137806788465333774-102658648364495069?l=hustoknow.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hustoknow.blogspot.com/feeds/102658648364495069/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hustoknow.blogspot.com/2011/12/changes-in-facebooks-swf-flash-handler.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/102658648364495069'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/102658648364495069'/><link rel='alternate' type='text/html' href='http://hustoknow.blogspot.com/2011/12/changes-in-facebooks-swf-flash-handler.html' title='Changes in Facebook&apos;s swf flash handler..'/><author><name>Roger</name><uri>http://www.blogger.com/profile/06655396766752051975</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4137806788465333774.post-2589058696201130140</id><published>2011-12-14T16:36:00.000-08:00</published><updated>2011-12-20T11:28:10.244-08:00</updated><title type='text'>Setting up IPSec with racoon and a Cisco router..</title><content type='html'>&lt;b&gt;Tools on Linux v2.6&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;The Linux 2.6 kernel already comes with IPSec support (Ubuntu distributed appears to have AH/ESP support), so you need two packages to get it working. First, you need ipsec-tools and racoon. You can install them by doing apt-get install ipsec-tools and apt-get install racoon respectively. You'll need to setup /etc/ipsec-tools.conf to define what IP subnets/hosts will be connected via VPN (and whether to use ESP and/or AH tunnel or transport mode, as well as the gateway IP's that are used to bridge the connections). Racoon has other parameters about Phase 1 and Phase 2 negotiation that you need to setup too, which are defined in /etc/racoon/racoon.conf. You use the remote {} configuration parameters for Phase 1, and sainfo parameters for Phase 2.). See &lt;a href="http://lists.freebsd.org/pipermail/freebsd-net/2006-June/010975.html"&gt;http://lists.freebsd.org/pipermail/freebsd-net/2006-June/010975.html&lt;/a&gt; for more detailed info.&lt;br /&gt;&lt;br /&gt;Note the unique allows for multiple security associations to be used over the same host, See &lt;a href="http://pardini.net/blog/2008/08/21/ipsec-with-setkeyracoon-and-multiple-single-host-spds/"&gt;http://pardini.net/blog/2008/08/21/ipsec-with-setkeyracoon-and-multiple-single-host-spds/&lt;/a&gt;.  Apparently using the keyword 'unique' instead of 'require' fixes the issue:&lt;br /&gt;&lt;pre class="prettyprint"&gt;flush;spdflush;spdadd ${LOCAL_NETWORK} ${STJUST_NETWORK} any -P out ipsec esp/tunnel/${LOCAL_OUTSIDE}-${STJUST_OUTSIDE}/unique;spdadd ${STJUST_NETWORK} ${LOCAL_NETWORK} any -P in  ipsec esp/tunnel/${STJUST_OUTSIDE}-${LOCAL_OUTSIDE}/unique;&lt;/pre&gt;&lt;div&gt;More documentation here about unique in the &lt;a href="http://linux.die.net/man/8/setkey"&gt;setkey&lt;/a&gt; man page:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: arial, sans-serif; font-size: 13px; background-color: rgb(255, 255, 255); "&gt;The &lt;/span&gt;&lt;i style="font-family: arial, sans-serif; font-size: 13px; text-align: -webkit-auto; background-color: rgb(255, 255, 255); "&gt;protocol/mode/src-dst/level&lt;/i&gt;&lt;span class="Apple-style-span" style="font-family: arial, sans-serif; font-size: 13px; text-align: -webkit-auto; background-color: rgb(255, 255, 255); "&gt; part specifies the rule how to process the packet. Either ah, esp, or ipcomp must be used as &lt;/span&gt;&lt;i style="font-family: arial, sans-serif; font-size: 13px; text-align: -webkit-auto; background-color: rgb(255, 255, 255); "&gt;protocol&lt;/i&gt;&lt;span class="Apple-style-span" style="font-family: arial, sans-serif; font-size: 13px; text-align: -webkit-auto; background-color: rgb(255, 255, 255); "&gt;. &lt;/span&gt;&lt;i style="font-family: arial, sans-serif; font-size: 13px; text-align: -webkit-auto; background-color: rgb(255, 255, 255); "&gt;mode&lt;/i&gt;&lt;span class="Apple-style-span" style="font-family: arial, sans-serif; font-size: 13px; text-align: -webkit-auto; background-color: rgb(255, 255, 255); "&gt; is either transport or tunnel. If &lt;/span&gt;&lt;i style="font-family: arial, sans-serif; font-size: 13px; text-align: -webkit-auto; background-color: rgb(255, 255, 255); "&gt;mode&lt;/i&gt;&lt;span class="Apple-style-span" style="font-family: arial, sans-serif; font-size: 13px; text-align: -webkit-auto; background-color: rgb(255, 255, 255); "&gt; is tunnel, you must specify the end-point addresses of the SA as &lt;/span&gt;&lt;i style="font-family: arial, sans-serif; font-size: 13px; text-align: -webkit-auto; background-color: rgb(255, 255, 255); "&gt;src&lt;/i&gt;&lt;span class="Apple-style-span" style="font-family: arial, sans-serif; font-size: 13px; text-align: -webkit-auto; background-color: rgb(255, 255, 255); "&gt; and &lt;/span&gt;&lt;i style="font-family: arial, sans-serif; font-size: 13px; text-align: -webkit-auto; background-color: rgb(255, 255, 255); "&gt;dst&lt;/i&gt;&lt;span class="Apple-style-span" style="font-family: arial, sans-serif; font-size: 13px; text-align: -webkit-auto; background-color: rgb(255, 255, 255); "&gt; with '-' between these addresses, which is used to specify the SA to use. If &lt;/span&gt;&lt;i style="font-family: arial, sans-serif; font-size: 13px; text-align: -webkit-auto; background-color: rgb(255, 255, 255); "&gt;mode&lt;/i&gt;&lt;span class="Apple-style-span" style="font-family: arial, sans-serif; font-size: 13px; text-align: -webkit-auto; background-color: rgb(255, 255, 255); "&gt; is transport, both &lt;/span&gt;&lt;i style="font-family: arial, sans-serif; font-size: 13px; text-align: -webkit-auto; background-color: rgb(255, 255, 255); "&gt;src&lt;/i&gt;&lt;span class="Apple-style-span" style="font-family: arial, sans-serif; font-size: 13px; text-align: -webkit-auto; background-color: rgb(255, 255, 255); "&gt; and &lt;/span&gt;&lt;i style="font-family: arial, sans-serif; font-size: 13px; text-align: -webkit-auto; background-color: rgb(255, 255, 255); "&gt;dst&lt;/i&gt;&lt;span class="Apple-style-span" style="font-family: arial, sans-serif; font-size: 13px; text-align: -webkit-auto; background-color: rgb(255, 255, 255); "&gt; can be omitted. &lt;/span&gt;&lt;i style="font-family: arial, sans-serif; font-size: 13px; text-align: -webkit-auto; background-color: rgb(255, 255, 255); "&gt;level&lt;/i&gt;&lt;span class="Apple-style-span" style="font-family: arial, sans-serif; font-size: 13px; text-align: -webkit-auto; background-color: rgb(255, 255, 255); "&gt; is to be one of the following: default, use, require, or unique. If the SA is not available in every level, the kernel will ask the key exchange daemon to establish a suitable SA. default means the kernel consults the system wide default for the protocol you specified, e.g. the esp_trans_deflev sysctl variable, when the kernel processes the packet. use means that the kernel uses an SA if it's available, otherwise the kernel keeps normal operation. require means SA is required whenever the kernel sends a packet matched with the policy. unique is the same as require; in addition, it allows the policy to match the unique out-bound SA. You just specify the policy level unique, &lt;/span&gt;&lt;b style="font-weight: bold; font-family: arial, sans-serif; font-size: 13px; text-align: -webkit-auto; background-color: rgb(255, 255, 255); "&gt;&lt;a href="http://linux.die.net/man/8/racoon" style="color: rgb(102, 0, 0); "&gt;racoon&lt;/a&gt;&lt;/b&gt;&lt;span class="Apple-style-span" style="font-family: arial, sans-serif; font-size: 13px; text-align: -webkit-auto; background-color: rgb(255, 255, 255); "&gt;(8) will configure the SA for the policy.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-family: arial, sans-serif; font-size: 13px; text-align: -webkit-auto; background-color: rgb(255, 255, 255); "&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;Racoon works by basically listening for commands from the Linux kernel. The tunnels get setup the first time you attempt to negotiate a connection to a host. If you have certain routes established that are defined in /etc/ipsec-tools.conf and do a setkey -f /etc/ipsec-tools.conf, this information will be loaded as the security policy database (SPD) and the kernel will send a trigger that causes Racoon to attempt to establish the connection.&lt;br /&gt;&lt;br /&gt;The two commands you will use to initially test are:&lt;br /&gt;&lt;pre class="prettyprint"&gt;sudo setkey -f /etc/ipsec-tools.confsudo racoon -F -f /etc/racoon/racoon.conf -v -ddd -l /etc/racoon/racoon.log&lt;/pre&gt;&lt;br /&gt;The most secure (but complicated) is to use is a Internet Key Exchange (IKE) authentication approach. In this approach, both VPN client/server sides announce that they will use a pre-shared key authentication mechanism and their authentication and hash algorithm. The pre-shared key is just some hard-coded value that both sides decide before setting up the VPN connection. Once the connection is established, both sides use a Diffie-Hellman key approach to generate a public/private key so that future exchanges will be encrypted. It so happens in this approach both sides exchange a public key and are able to decode the packet using their own private key! All this negotiation happens during what's called Phase 1 negotiation.&lt;br /&gt;&lt;br /&gt;Alternate approaches to the IKE implementation call call for setting up manual keys. In other words, both sides have to know how to encrypt the data beforehand instead of this intricate key exchange approach. A lot of the Racoon documentation mention setting up security association (SA)'s within /etc/ipsec-tools.conf, but this approach is un-needed if we are going to use the IKE-based approach, which is presumably more secure. If the IKE implementation is used, then Phase 2 negotiations must also occur.&lt;br /&gt;&lt;br /&gt;A great guide to troubleshooting IPSec connections is here:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://vpn.precision-guesswork.com/vpn/ipsec_troubleshooting.pdf"&gt;http://vpn.precision-guesswork.com/vpn/ipsec_troubleshooting.pdf&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Want to know how all the nomencalture is laid out?  Click here:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.unixwiz.net/techtips/iguide-ipsec.html"&gt;http://www.unixwiz.net/techtips/iguide-ipsec.html&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Phase 1&lt;/b&gt;&lt;br /&gt;If you read the IPSec documents, you'll see there are 3 rounds of this Phase 1. You can use tshark/wireshark to watch the network dumps:&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Round 1:&lt;/b&gt; agree on authentication, encryption, hash payload algorithm&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Round 2:&lt;/b&gt; key exchanges w/ nonce values (to avoid replay attacks).&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Round 3:&lt;/b&gt; validation of hash/identification payloads using the secret keys completes successfully.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Phase 2 (IKE only)&lt;/b&gt;&lt;br /&gt;The second step, also known as Quick Mode, in the IKE approach is to negotiate a security association (SA) policy. These policies not only define what encryption/authentication algorithms should be used, what encryption keys should be used for data transferring and for what IP subnets.&lt;br /&gt;&lt;br /&gt;The ipsec-tools package comes with an /etc/ipsec-tools.conf that defines the security association (SA) policies. This policy must match against the information provided by the customer side. In the Northwestern Mutual's case, their IT department set their Cisco router with an access control list that defines that's allowed to connect. You will notice in the ISAKMP protocol during Phase 2 negotation that the packet structure for Phase 2 also includes an IDci and IDcr identity payload. You can watch Racoon and see what bits get passed through:&lt;br /&gt;&lt;br /&gt;2011-12-14 01:12:56: DEBUG: IDci:&lt;br /&gt;2011-12-14 01:12:56: DEBUG:&lt;br /&gt;04000000 &amp;lt;IP address here&amp;gt; &lt;subnet 14="" 04000000="" ip="" address="" subnet="" convert="" idci="and" idcr="" to="" ip="" addresses="" bitmaps="" and="" it="" confirms="" that="" the="" spadd="" configs="" are="" working="" correctly="" one="" is="" 0="" 16="" other="" 0="" this="" used="" for="" phase="" 2="" in="" your="" you="" use="" security="" policy="" need="" match="" up="" against="" no="" vpn="" connection="" can="" be="" b=""&gt;Data exchanges&lt;br /&gt;Assuming everything is setup correctly, you need to setup your route table for the specific IP blocks to which you are connecting. Make sure do netstat -rn and then do route add's to add the correct routes. Unless you're bridging Ethernet interfaces, you need to be sure that you are always sending packets over the same Ethernet interface.&lt;br /&gt;&lt;br /&gt;You can confirm packets that go over the wire by using either tcpdump or tshark -i eth0 not port 22 (to exclude traces from your current SSH connection from being dumped out). If you are using ESP encryption, then you should also see that the kernel encrypting packets destined for those IP's to the appropriate place). Again, the Linux kernel is handling most of the work, so long as the routes are correctly defined.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Ways to debug:&lt;/b&gt;&lt;br /&gt;Watch isakmp packets:&lt;br /&gt;&lt;br /&gt;1. sudo tshark -i eth0 udp port 500 -V or&lt;br /&gt;sudo tshark -i eth0 udp not port 22&lt;br /&gt;&lt;br /&gt;2. ssh -X &lt;vpn host="" relay=""&gt;&lt;br /&gt;sudo wireshark&lt;br /&gt;&lt;br /&gt;(X11Forwarding needs to be temporarily enabled on /etc/ssh/sshd_config, then do /etc/init.d/ssh restart &amp;amp;.  You then need to make sure X11Forwarding is setup in your /etc/ssh/ssh_config on hte client side).&lt;br /&gt;&lt;br /&gt;Wireshark can actually decrypt ESP/AH authentication assuming you provide the Security Parameter Indexes (SPI) generated on-the-fly and encryption keys. Most of this data you can observe via running Racoon in debug mode.&lt;br /&gt;&lt;br /&gt;FYI - You may also notice "next payload" in Racoon dumps. The ISAKMP standard appears to define multiple types of payloads. Often times you will see vendor ID and other data such as the following:&lt;br /&gt;&lt;pre class="prettyprint"&gt;Vendor ID: RFC 3706 Detecting Dead IKE Peers (DPD)        Next payload: Vendor ID (13)        Payload length: 20        Vendor ID: RFC 3706 Detecting Dead IKE Peers (DPD)    Vendor ID: XXXXXX        Next payload: Vendor ID (13)        Payload length: 20        Vendor ID: XXXXXX    Vendor ID: draft-beaulieu-ike-xauth-02.txt        Next payload: NONE (0)        Payload length: 12        Vendor ID: draft-beaulieu-ike-xauth-02.txt&lt;/pre&gt;&lt;/vpn&gt;&lt;/subnet&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4137806788465333774-2589058696201130140?l=hustoknow.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hustoknow.blogspot.com/feeds/2589058696201130140/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hustoknow.blogspot.com/2011/12/setting-up-ipsec-with-racoon-and-cisco.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/2589058696201130140'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/2589058696201130140'/><link rel='alternate' type='text/html' href='http://hustoknow.blogspot.com/2011/12/setting-up-ipsec-with-racoon-and-cisco.html' title='Setting up IPSec with racoon and a Cisco router..'/><author><name>Roger</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4137806788465333774.post-4063953522833667932</id><published>2011-12-11T21:53:00.000-08:00</published><updated>2011-12-11T21:53:15.520-08:00</updated><title type='text'>Setting up a VPN between two DD-WRT routers..</title><content type='html'>This setup worked for two WRT54GL routers running DD-WRT v24-sp1. &lt;br /&gt;&lt;br /&gt;&lt;b&gt;PPTP server (192.168.0.1):&lt;/b&gt;&lt;br /&gt;First, one machine needs to be setup as a PPTP server.  &lt;br /&gt;1. Go to Services-&gt;PPTP to enable the PPTP server.&lt;br /&gt;2. Set the server IP (should be a virtual lAN IP address different than your LAN IP address -- i.e. 192.168.0.2), &lt;br /&gt;3. Set the Client IP block (192.168.0.50-192.168.0.70), and then setup the CHAP Secrets (johndoe* mypassword *).  &lt;br /&gt;&lt;br /&gt;&lt;b&gt;PPTP client (192.168.1.1) :&lt;/b&gt;&lt;br /&gt;1. Enable PPTP client.&lt;br /&gt;2. Set the PPTP Server IP.&lt;br /&gt;3. Set the Remote Subnet (192.168.0.0) and Remote Subnet Mask (255.255.255.0)&lt;br /&gt;4. Set the MPPE Encryption to "mppe required".&lt;br /&gt;5. Set the MTU/MRU to be 1450.&lt;br /&gt;6. Disable NAT mdoe.&lt;br /&gt;7. Set the username and password to the CHAP secret set in the PPTP Server.&lt;br /&gt;&lt;br /&gt;You should verify the PPTP connection is established by telnetting into the PPTP client box and attempting to ping the private IP address of the PPTP server (i.e. 192.168.0.2 or 192.168.0.1). If this succeeds, then you may be able to ping the routers but other machines on the network are not able to talk with each other.  In this case, you may wish to confirm that the PPTP server has not setup a route of 192.168.1.0.  To do it automatically, you need to do the following:&lt;br /&gt;&lt;br /&gt;1. Go to Administration-&gt;Commands.&lt;br /&gt;2. Add the following firewall commands.   Usually what happens is that the /tmp/pptpd_client/ip-up script is created.  A delay is inserted before adding the route and then re-executing the ip-up bash script again.  &lt;br /&gt;&lt;pre class="prettyprint"&gt;sleep 40&lt;br /&gt;/bin/sh -c 'echo "ip route add 192.168.1.0/24 dev ppp0" &gt;&gt; /tmp/pptpd/ip-up'&lt;br /&gt;/tmp/pptpd_client/ip-up&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Click on Save-&gt;Firewall after saving.&lt;br /&gt;&lt;br /&gt;If you want to reinitiate the PPTP connection, try to click Save/Apply Settings and waiting for the VPN connection to be re-established.   If you really want to check things out, you can configure a VPN client on Ubuntu 10.04 through the Network Manager (make sure to click Point-to-Point MPPE Encryption and allow stateful encryption, send PPP echo packets to help keep the connection alive).  (Note: If you forget to enable the MPPE encryption/stateful encryption options, you may find that the VPN connection is flaky.  It seems as if there are CHAP requests/rejects that keep happening without these two options).  This VPN client will help you verify that the PPTP server is responding correctly.&lt;br /&gt;&lt;br /&gt;You should also telnet to both DD-WRT routers and verify the routes have been established between the two subnets.   You should also cat /tmp/pptpd_client/ip-up on the PPTP server to verify that the IP route was added correctly.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4137806788465333774-4063953522833667932?l=hustoknow.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hustoknow.blogspot.com/feeds/4063953522833667932/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hustoknow.blogspot.com/2011/12/setting-up-vpn-between-two-dd-wrt.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/4063953522833667932'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/4063953522833667932'/><link rel='alternate' type='text/html' href='http://hustoknow.blogspot.com/2011/12/setting-up-vpn-between-two-dd-wrt.html' title='Setting up a VPN between two DD-WRT routers..'/><author><name>Roger</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4137806788465333774.post-2099531828532494209</id><published>2011-12-10T21:35:00.000-08:00</published><updated>2011-12-11T11:32:33.357-08:00</updated><title type='text'>Upgrading a Compaq Presario C700 from Vista to Windows 7..</title><content type='html'>Recently, I upgraded an old Compaq C714NR Presario laptop that had been running Windows Vista to Windows 7.  Coffee had been spilled on the touchpad, rendering it inoperable.   The keyboard still worked but the spacebar was sort of sticky so needed to be replaced.  You may have older machines that might be worth upgrading, especially if you took advantage of Microsoft offers that allowed .edu email addresses to get a copy of Windows 7 Professional for $25.&lt;br /&gt;&lt;br /&gt;1. Although your machine may be running 32-bit Windows Vista, chances are that if the machine runs a dual-core processor, which means it can run 64-bit Windows Professional.  Most of the graphics, networks, and sound drivers already come with Windows 7, so there usually isn't a need to download extra drivers.  Windows 7 should install right out of the box.&lt;br /&gt;&lt;br /&gt;2. If you're burning a copy of Windows 7, you may encounter issues about "Required cd/dvd drive device driver is missing".  If you observe this case, chances are likely the DVD you burned actually has problems, especially if you were using a copy that hadn't been used for awhile.  Originally you may be led to think that there are some driver incompatibility issues with the 64-bit Windows 7 version, but try to reburn the DVD and see if the install works.&lt;br /&gt;&lt;br /&gt;3. The touchpad can be replaced, but you have to buy one that comes with the laptop casing too.  Since most of the casing + touchpads parts are sold off Ebay for $30+, it may easier to simply attach a USB-mouse instead.  The picture below shows an example of the touchpad + upper casing:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://i.ebayimg.com/t/HP-COMPAQ-PRESARIO-C700-PALMREST-TOUCHPAD-454936-001-/00/s/MTIwMFgxNjAw/$(KGrHqIOKnME52P1vqKOBOpU1!1Gew~~60_12.JPG" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center; cursor:pointer; cursor:hand;width: 500px; height: 375px;" src="http://i.ebayimg.com/t/HP-COMPAQ-PRESARIO-C700-PALMREST-TOUCHPAD-454936-001-/00/s/MTIwMFgxNjAw/$(KGrHqIOKnME52P1vqKOBOpU1!1Gew~~60_12.JPG" border="0" alt="" /&gt;&lt;/a&gt;&lt;br /&gt;4. There are web sites that sell spare keyboard keys (i.e. &lt;a href="http://www.blogger.com/laptopkey.com"&gt;laptopkey.com&lt;/a&gt;), but buying one part can easily cost $8 and you can usually buy the entire keyboard replacement for $12.   The HP service manual for replacing the keyboard is fairly straightforward, but there are a few key things to know.  In the case of the C700, there were 3 screws at the bottom of the laptop, each with a keyboard icon at the bottom.  One of these screws was obscured by the memory lid, so you may have to remove the lid first.&lt;br /&gt;&lt;a href="http://1.bp.blogspot.com/-rJaVYexQ0Es/TuRJKZINS6I/AAAAAAAAAAo/IxByC7wYsTs/s1600/presario.png" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 246px;" src="http://1.bp.blogspot.com/-rJaVYexQ0Es/TuRJKZINS6I/AAAAAAAAAAo/IxByC7wYsTs/s320/presario.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5684749072541240226" /&gt;&lt;/a&gt;&lt;br /&gt;Second, there is the Zero-Insertion Force (ZIF) connector that attaches to the keyboard and laptop.   What this usually means is that the sides of the connector need to be pushed out.&lt;br /&gt;&lt;a href="http://2.bp.blogspot.com/-ua1Soo3iYLQ/TuRIxt_BplI/AAAAAAAAAAc/Z1Ait1Npitw/s1600/zif_install.gif" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center; cursor:pointer; cursor:hand;width: 320px; height: 199px;" src="http://2.bp.blogspot.com/-ua1Soo3iYLQ/TuRIxt_BplI/AAAAAAAAAAc/Z1Ait1Npitw/s320/zif_install.gif" border="0" alt="" id="BLOGGER_PHOTO_ID_5684748648643143250" /&gt;&lt;/a&gt;&lt;br /&gt;You should avoid pulling the ribbon cable out until the connector is released.  The picture below shows one example of how the ZIF connector is pushed out.  You can usually use your fingers and push the connector out slightly before inserting the ribbon cable.  You should push down on the sides to fasten the ribbon cable securely.  &lt;a href="http://kyorune.com/modding/file.php?art=49&amp;amp;file=2" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 262px;" src="http://kyorune.com/modding/file.php?art=49&amp;amp;file=2" border="0" alt="" /&gt;&lt;/a&gt;&lt;br /&gt;Keep in mind that you should verify that all keys work.  If the connector is not fully fastened, you may find some keys do not respond.  You can try to boot up the computer with the keyboard installed, but be careful if any components are exposed.&lt;br /&gt;&lt;br /&gt;5. Finally, if you need to replace any keys, you first have to figure out how the keyboard mechanism works..  There are a bunch of YouTube demos for replacing the keys in HP laptops, but none of the videos I found pointed out that it's easier to attach one of the plastic hinges to the key, and the other smaller plastic hinge to the laptop.  If you were to setup the hinges on the laptop first, the plastic hinges should move up and down if you were to apply pressure to them, supplementing the spring-like action in the button.&lt;br /&gt;&lt;br /&gt;&lt;iframe width="420" style="display:block; margin:0px auto 10px; text-align:center" height="315" src="http://www.youtube.com/embed/s9K13E-N6h8" frameborder="0" allowfullscreen=""&gt;&lt;/iframe&gt;&lt;div&gt;Once you figure out the right way to place them, take the large hinge and attach it to the key before attaching the other part.   For this keyboard, I couldn't just put the key over the two plastic hinges since the pressure of the keys would cause both plastic to be pushed down without snapping into place.   You have to be careful with this part since the plastic hooks can break, so avoid trying to force the keys to attach to the plastic hinges.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4137806788465333774-2099531828532494209?l=hustoknow.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hustoknow.blogspot.com/feeds/2099531828532494209/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hustoknow.blogspot.com/2011/12/upgrading-compaq-presario-c700-from.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/2099531828532494209'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/2099531828532494209'/><link rel='alternate' type='text/html' href='http://hustoknow.blogspot.com/2011/12/upgrading-compaq-presario-c700-from.html' title='Upgrading a Compaq Presario C700 from Vista to Windows 7..'/><author><name>Roger</name><uri>http://www.blogger.com/profile/06655396766752051975</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/-rJaVYexQ0Es/TuRJKZINS6I/AAAAAAAAAAo/IxByC7wYsTs/s72-c/presario.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4137806788465333774.post-7375780348959101456</id><published>2011-12-10T13:14:00.001-08:00</published><updated>2011-12-10T15:37:32.366-08:00</updated><title type='text'>Celery 2.3+ crashes when channel.close() commands are issued...</title><content type='html'>This AMQPChannelException issue has happened for us over the last 3 weeks, so I decided to dig-in to understand why we were getting AMQPChannelException's that caused our Celery workers to simply die.  Well, it turns out this exception often appeared in our logs: &lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt;(404, u"NOT_FOUND - no exchange 'reply.celeryd.pidbox' in vhost 'myhost'", (60, 40), 'Channel.basic_publish')&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The basic problem I suspect is that we have a task that is checking for long running Celery tasks.   It relies on the celeryctl command, which is a special mechanism used by Celery to broadcast messages to all boxes running Celery (i.e. celeryctl inspect active)&lt;br /&gt;&lt;br /&gt;The celeryctl command implements a multicast protocol by leveraging the AMQP standard (see &lt;a href="http://www.rabbitmq.com/getstarted.html"&gt;RabbitMQ tutorial&lt;/a&gt;).  All Celery machines on startup bind to the exchange celery.pidbox.  When you send a celeryctl command, RabbitMQ receives this message, and then goes and delivers to all machines listening to this exchange celery.pidbox.  &lt;br /&gt;&lt;br /&gt;The machines also send back their replies on a separate exchange called reply.celery.pidbox, which is used by the main process that issued the celeryctl command to collect all the responses.   Once all program completes, it will delete the celery.pidbox since it's no longer needed.  Unfortunately, if a worker receives this command and attempts to respond but it's too late, it can trigger an exchange not found, causing RabbitMQ to issue a channel.close() command.  I suspect this happens especially during heavy loads and/or during intermittent network failures, since the problem often shows up during these times.&lt;br /&gt;&lt;br /&gt;Celery handles connection failures fine, but doesn't seem to deal with situations where the AMQP host issues a close command.   I solved it in two ways: first, allowing Celery to gracefully reset the entire connection when such an event happens (&lt;a href="https://github.com/ask/celery/pull/564"&gt;PR request to the Celery framework&lt;/a&gt;), and increasing the window in which we check for replies so the exchange isn't deleted quickly thereafter (i.e. celery inspect active --timeout=60).  The latter may be the quicker way to solve it, though the former should probably be something that would help avoid the situation altogether (although it may cause other issues).&lt;br /&gt;&lt;br /&gt;The result may appear to trap the exception and try to establish a new connection.  This approach is already being used when for Celery control commands (an error msg "Error occurred while handling control command" gets printed out but the underlying Exception gets caught).  It seems this exception occurs when the RabbitMQ sends a close() command to terminate the connection, causing the entire process to die.&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt;    def on_control(self, body, message):&lt;br /&gt;        """Process remote control command message."""&lt;br /&gt;        try:&lt;br /&gt;            self.pidbox_node.handle_message(body, message)&lt;br /&gt;        except KeyError, exc:&lt;br /&gt;            self.logger.error("No such control command: %s", exc)&lt;br /&gt;        except Exception, exc:&lt;br /&gt;            self.logger.error(&lt;br /&gt;                "Error occurred while handling control command: %r\n%r",&lt;br /&gt;                    exc, traceback.format_exc(), exc_info=sys.exc_info())&lt;br /&gt;            self.reset_pidbox_node()&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;We'll see in this pull-request whether the authors of Celery think this is a good idea...I suspect that it would be better to create a new channel than to restart the connection altogether.&lt;br /&gt;&lt;br /&gt;&lt;a href="https://github.com/ask/celery/pull/564"&gt;https://github.com/ask/celery/pull/564&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4137806788465333774-7375780348959101456?l=hustoknow.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hustoknow.blogspot.com/feeds/7375780348959101456/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hustoknow.blogspot.com/2011/12/celery-23-crashes-when.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/7375780348959101456'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/7375780348959101456'/><link rel='alternate' type='text/html' href='http://hustoknow.blogspot.com/2011/12/celery-23-crashes-when.html' title='Celery 2.3+ crashes when channel.close() commands are issued...'/><author><name>Roger</name><uri>http://www.blogger.com/profile/06655396766752051975</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4137806788465333774.post-2993768028392801438</id><published>2011-12-09T10:09:00.000-08:00</published><updated>2011-12-09T10:36:39.058-08:00</updated><title type='text'>Moving to Nose..</title><content type='html'>We've started to have our developers use &lt;a href="http://code.google.com/p/python-nose/"&gt;Nose&lt;/a&gt;, a much more powerful unit test discovery package for Python.  For one, in order to generate JUnit XML outputs for Hudson/Jenkins, the test runner comes with a --with-xunit command that lets you dump these results out.&lt;br /&gt;&lt;br /&gt;Here are a few things that might help you get adjusted to using Nose:&lt;br /&gt;&lt;br /&gt;1. As mentioned in the &lt;a href="http://webamused.wordpress.com/2010/04/07/tutorial-testing-efficiently-with-nose-nose-exclude-and-django-nose/"&gt;Testing Efficiently with Nose tutorial&lt;/a&gt;, the convention is slightly different for running tests has changed.  The format has changed:&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt;python manage.py test app.tests:YourTestCaseClass&lt;br /&gt;python manage.py test app.tests:YourTestCaseClass.your_test_method&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;One way to force the test results to use the same format is to create a class that inherits from unittest that overrides the __str__, __id__, and shortDescription methods.  The __str__() method is used by the Django test runner to display what tests are running and which ones have failed, enabling you to copy/paste the test to re-run the test.  The __id__() method is used by the XUnit plug-in to generate the test name, enabling you to swap out the class name with the Nose convention.  Finally, the shortDescription() will prevent docstrings from replacing the test name when running the tests.&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt;class BaseTestCase(unittest.TestCase):&lt;br /&gt;&lt;br /&gt;    def __str__(self):&lt;br /&gt;        # Use Nose testing format (colon to differentiate between module/class name)&lt;br /&gt;&lt;br /&gt;        if 'django_nose' in settings.INSTALLED_APPS or 'nose' in settings.TEST_RUNNER.lower():&lt;br /&gt;            return "%s:%s.%s" % (self.__module__, self.__class__.__name__, self._testMethodName)&lt;br /&gt;        else:&lt;br /&gt;            return "%s.%s.%s" % (self.__module__, self.__class__.__name__, self._testMethodName)&lt;br /&gt;&lt;br /&gt;    def id(self):  # for XUnit outputs&lt;br /&gt;        return self.__str__()&lt;br /&gt;&lt;br /&gt;    def shortDescription(self):  # do not return the docstring&lt;br /&gt;        return None&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;2. For SauceLabs jobs, you can also expose the URL of the job run in which you are running.  WebDriverException's inherit from the Exception class, and add a 'msg' property that we can use to insert the SauceLabs URL.  You want to avoid adding the URL in the id() and __str__() methods, since those routines are used to dump out the names of classes that Hudson/Jenkins may used to compare against between builds.&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt;    def get_sauce_job_url(self):&lt;br /&gt;        # Expose SauceLabs job number for easy reference if an error occurs.&lt;br /&gt;        if getattr(self, 'webdriver', None) and hasattr(self.webdriver, 'session_id') and self.webdriver.session_id:&lt;br /&gt;            session_id = "(http://saucelabs.com/jobs/%s)" % self.webdriver.session_id&lt;br /&gt;        else:&lt;br /&gt;            session_id = ''&lt;br /&gt;&lt;br /&gt;        return session_id&lt;br /&gt;&lt;br /&gt;    def __str__(self):&lt;br /&gt;        session_id = self.get_sauce_job_url()&lt;br /&gt;        return " ".join([super(SeleniumTestSuite, self).__str__(), session_id])&lt;br /&gt;&lt;br /&gt;    def _exc_info(self):&lt;br /&gt;        exc_info = super(SeleniumTestSuite, self)._exc_info()&lt;br /&gt;&lt;br /&gt;        # WebDriver exceptions have a 'msg' attribute, which gets used to be dumped out.&lt;br /&gt;        # We can take advantage of this fact and store the SauceLabs jobs URL in there too!&lt;br /&gt;        if exc_info[1] and hasattr(exc_info[1], 'msg'):&lt;br /&gt;            session_id = self.get_sauce_job_url()&lt;br /&gt;            exc_info[1].msg = session_id + "\n" + exc_info[1].msg&lt;br /&gt;        return exc_info&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;3. Nose has a bunch of nifty commands that you should try, including --with-id &amp; --failed, which lets you run your entire test suite and then run only the ones that failed.  You can also use the &lt;a href="http://readthedocs.org/docs/nose/en/latest/plugins/attrib.html"&gt;attrib&lt;/a&gt; decorator, which lets you to decorate certain test suites/test methods with various attributes, such as network-based tests or slow-running ones.   The attrib plugin seems to support a limited boolean logic, so check the documentation carefully if you intend to use the -a flag (you can use --collect-only in conjunction to verify your tests are correctly running with the right logic).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4137806788465333774-2993768028392801438?l=hustoknow.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hustoknow.blogspot.com/feeds/2993768028392801438/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hustoknow.blogspot.com/2011/12/moving-to-nose.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/2993768028392801438'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/2993768028392801438'/><link rel='alternate' type='text/html' href='http://hustoknow.blogspot.com/2011/12/moving-to-nose.html' title='Moving to Nose..'/><author><name>Roger</name><uri>http://www.blogger.com/profile/06655396766752051975</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4137806788465333774.post-1731316852030052389</id><published>2011-12-08T20:45:00.000-08:00</published><updated>2011-12-09T10:09:10.343-08:00</updated><title type='text'>Hover states with Selenium 2..</title><content type='html'>We do a lot of automated tests on Selenium 2, and recently we noticed that some of our tests that attempt to verify mouseover/hover states intermittently break.  We could see the mouseover event get fired, but then suddenly it would stop.  &lt;br /&gt;&lt;br /&gt;The phenomenon at first made us suspect there were possible race conditions in our JavaScript code.  Were we accidentally clearing/hiding elements before WebDriver could reach them?  Was the web browser loading multiple times and clearing out the original state?  Or was it an issue in Selenium 2, since it attempts to calculate the center position of the element, scroll into view, and then move the mouse cursor to the center of the element (see &lt;a href="http://www.google.com/codesearch#2tHw6m3DZzo/trunk/javascript/atoms/dom.js"&gt;http://www.google.com/codesearch#2tHw6m3DZzo/trunk/javascript/atoms/dom.js&lt;/a&gt;).  You can look at how the Windows driver's mouseMove is implemented here:&lt;br /&gt;&lt;a href="http://www.google.com/codesearch#2tHw6m3DZzo/trunk/cpp/webdriver-interactions/interactions.cpp"&gt;http://www.google.com/codesearch#2tHw6m3DZzo/trunk/cpp/webdriver-interactions/interactions.cpp&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;It turns out that hover states for Windows in Selenium 2 are just problematic right now.  You can even see inside the Selenium 2 test suite that hover states are not skipped: for Windows-based systems:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.blogger.com/%20http://www.google.com/codesearch#2tHw6m3DZzo/trunk/java/client/test/org/openqa/selenium/RenderedWebElementTest.java&amp;amp;q=2067%20package:http://selenium%5C.googlecode%5C.com"&gt;http://www.google.com/codesearch#2tHw6m3DZzo/trunk/java/client/test/org/openqa/selenium/RenderedWebElementTest.java&amp;amp;q=2067%20package:http://selenium\.googlecode\.com&lt;/a&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt;if (!supportsHover()) {       &lt;br /&gt;  System.out.println("Skipping hover test: Hover is very short-lived on Windows. Issue 2067.");       &lt;br /&gt;  return;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The full bug report is here:&lt;br /&gt;&lt;a href="http://code.google.com/p/selenium/issues/detail?id=2067"&gt;http://code.google.com/p/selenium/issues/detail?id=2067&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;In Selenium v2.15.0, even the Internet Explorer wiki was updated to remind people that hover states (using mousemove) have issues:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://code.google.com/p/selenium/source/diff?spec=svn14947&amp;amp;r=14947&amp;amp;format=side&amp;amp;path=/wiki/InternetExplorerDriver.wiki"&gt;http://code.google.com/p/selenium/source/diff?spec=svn14947&amp;amp;r=14947&amp;amp;format=side&amp;amp;path=/wiki/InternetExplorerDriver.wiki&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;The workaround seems to be generating your own synthetic events:&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt;$('#myelem').trigger('mouseenter');&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4137806788465333774-1731316852030052389?l=hustoknow.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hustoknow.blogspot.com/feeds/1731316852030052389/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hustoknow.blogspot.com/2011/12/hover-states-with-selenium-2.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/1731316852030052389'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/1731316852030052389'/><link rel='alternate' type='text/html' href='http://hustoknow.blogspot.com/2011/12/hover-states-with-selenium-2.html' title='Hover states with Selenium 2..'/><author><name>Roger</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4137806788465333774.post-3583255000116962006</id><published>2011-12-03T16:27:00.000-08:00</published><updated>2011-12-03T16:37:00.673-08:00</updated><title type='text'>AT&amp;T Uverse Internet 18Mbps solution..</title><content type='html'>You have two modem/router choices for the AT&amp;T Internet Uverse plan: Motorola NVG510 or the Motorola 2210-02-1ATT.  The NVG510 doesn't have a bridge mode, and there are a wide variety of hacks to use the IP Passthrough option for the Motorola NVG510 modem: &lt;br /&gt;&lt;br /&gt;&lt;a href="http://forums.att.com/t5/Features-and-How-To/NVG510-Bridge-Mode/td-p/2890841/page/2"&gt;http://forums.att.com/t5/Features-and-How-To/NVG510-Bridge-Mode/td-p/2890841/page/2&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;If you read through the discussions though, you'll notice that the discussion gravitates towards using the IP Passthrough with a Fixed-DHCPS set for the router's IP address: "i'm using the IP Passthrough functionality. According to its own instructions on the right-hand side of the tab, the RG is supposed to pass its public IP address through to another device, using its own DHCP - but it won't do it. I had to manually enter the information into my regular router (an Airport Extreme Base Station, aka AEBS) to use the Passthrough functionality.&lt;br /&gt;&lt;br /&gt;...it seems more better if you had a choice to go with the Motorola 2210 option.   Also, don't confuse the Motorola 2210-02-1TT model with the Motorola 2210-02-1022 model, the latter of which was an old DSL modem that you can buy off Ebay.  For one thing, the 2210-02-1022 models only speak PPPoE, and since the Uverse solution relies on authentication based on the MAC address, you won't be able to plug this modem in place of things.  You can check your diagnostics tool on the router to verify that you're connecting via IP-DSLAM, which is synonymous with the AT&amp;T Uverse Internet 18Mbps solution.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4137806788465333774-3583255000116962006?l=hustoknow.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hustoknow.blogspot.com/feeds/3583255000116962006/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hustoknow.blogspot.com/2011/12/at-uverse-internet-18mbps-solution.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/3583255000116962006'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/3583255000116962006'/><link rel='alternate' type='text/html' href='http://hustoknow.blogspot.com/2011/12/at-uverse-internet-18mbps-solution.html' title='AT&amp;T Uverse Internet 18Mbps solution..'/><author><name>Roger</name><uri>http://www.blogger.com/profile/06655396766752051975</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4137806788465333774.post-8523870982916466322</id><published>2011-12-02T23:43:00.001-08:00</published><updated>2011-12-03T00:08:26.528-08:00</updated><title type='text'>Fujitsu C2010 specs</title><content type='html'>http://implitech.com/manufacturers/fujitsu-siemens/lifebook-c2010/&lt;br /&gt;&lt;br /&gt;Intel 845MP/MZ chipset &lt;br /&gt;&lt;br /&gt;Fujitsu Siemens LifeBook C2010 Graphics - ATI Radeon Mobility M6 LY Driver&lt;br /&gt;Fujitsu Siemens LifeBook C2010 Human Interface - Belkin MI 2150 Trust Mouse Driver&lt;br /&gt;Fujitsu Siemens LifeBook C2010 IEEE 1394 - Texas Instruments TSB43AB21 IEEE-1394a-2000 Controller Driver&lt;br /&gt;Fujitsu Siemens LifeBook C2010 Modem - Intel 82801CA CAM AC97 Modem Driver&lt;br /&gt;Fujitsu Siemens LifeBook C2010 Multimedia - Intel 82801CA CAM AC97 Audio Controller Driver&lt;br /&gt;Fujitsu Siemens LifeBook C2010 Network - Realtek RTL 8139 8139C 8139C Driver&lt;br /&gt;Fujitsu Siemens LifeBook C2010 PCMCIA - Texas Instruments PCI1520 PC Card Cardbus Driver&lt;br /&gt;Fujitsu Siemens LifeBook C2010 Storage - Intel 82801CAM IDE U100 Driver&lt;br /&gt;Fujitsu Siemens LifeBook C2010 Intel Chipset Drivers&lt;br /&gt;&lt;br /&gt;Device manager for Fujitsu Siemens LifeBook C2010 Laptop&lt;br /&gt;Radeon Mobility M6 LY - ATI&lt;br /&gt;PCI\VEN_1002&amp;DEV_4C59&amp;SUBSYS_113E10CF&amp;REV_00\4&amp;34C0AFA1&amp;0&amp;0008&lt;br /&gt;MI 2150 Trust Mouse - Belkin&lt;br /&gt;USB\VID_1241&amp;PID_1166\5&amp;95D66C5&amp;0&amp;2&lt;br /&gt;TSB43AB21 IEEE-1394a-2000 Controller - Texas Instruments&lt;br /&gt;PCI\VEN_104C&amp;DEV_8026&amp;SUBSYS_116210CF&amp;REV_00\4&amp;6B54384&amp;0&amp;30F0&lt;br /&gt;82801CA CAM AC97 Modem - Intel&lt;br /&gt;PCI\VEN_8086&amp;DEV_2486&amp;SUBSYS_10D110CF&amp;REV_02\3&amp;61AAA01&amp;0&amp;FE&lt;br /&gt;82801CA CAM AC97 Audio Controller - Intel&lt;br /&gt;PCI\VEN_8086&amp;DEV_2485&amp;SUBSYS_117710CF&amp;REV_02\3&amp;61AAA01&amp;0&amp;FD&lt;br /&gt;RTL 8139 8139C 8139C - Realtek&lt;br /&gt;PCI\VEN_10EC&amp;DEV_8139&amp;SUBSYS_111C10CF&amp;REV_10\3&amp;61AAA01&amp;0&amp;68&lt;br /&gt;PCI1520 PC Card Cardbus - Texas Instruments&lt;br /&gt;PCI\VEN_104C&amp;DEV_AC55&amp;SUBSYS_116410CF&amp;REV_01\4&amp;139E449D&amp;0&amp;50F0&lt;br /&gt;82801CAM IDE U100 - Intel&lt;br /&gt;PCI\VEN_8086&amp;DEV_248A&amp;SUBSYS_113D10CF&amp;REV_02\3&amp;61AAA01&amp;0&amp;F9&lt;br /&gt;Brookdale (82845 845 Chipset Host Bridge) - Intel&lt;br /&gt;PCI\VEN_8086&amp;DEV_1A30&amp;SUBSYS_00000000&amp;REV_04\3&amp;61AAA01&amp;0&amp;00&lt;br /&gt;Brookdale (82845 845 Chipset AGP Bridge) - Intel&lt;br /&gt;PCI\VEN_8086&amp;DEV_1A31&amp;SUBSYS_00000000&amp;REV_04\3&amp;61AAA01&amp;0&amp;08&lt;br /&gt;82801CA CAM USB Controller #1 - Intel&lt;br /&gt;PCI\VEN_8086&amp;DEV_2482&amp;SUBSYS_113D10CF&amp;REV_02\3&amp;61AAA01&amp;0&amp;E8&lt;br /&gt;82801CA CAM USB Controller #2 - Intel&lt;br /&gt;PCI\VEN_8086&amp;DEV_2484&amp;SUBSYS_113D10CF&amp;REV_02\3&amp;61AAA01&amp;0&amp;E9&lt;br /&gt;82801 Mobile PCI Bridge - Intel&lt;br /&gt;PCI\VEN_8086&amp;DEV_2448&amp;SUBSYS_00000000&amp;REV_F3\3&amp;33FD14CA&amp;0&amp;F0&lt;br /&gt;82801CAM ISA Bridge (LPC) - Intel&lt;br /&gt;PCI\VEN_8086&amp;DEV_248C&amp;SUBSYS_00000000&amp;REV_02\3&amp;61AAA01&amp;0&amp;F8&lt;br /&gt;82801CA CAM SMBus Controller - Intel&lt;br /&gt;PCI\VEN_8086&amp;DEV_2483&amp;SUBSYS_113D10CF&amp;REV_02\3&amp;61AAA01&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4137806788465333774-8523870982916466322?l=hustoknow.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hustoknow.blogspot.com/feeds/8523870982916466322/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hustoknow.blogspot.com/2011/12/fujitsu-c2010-specs.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/8523870982916466322'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/8523870982916466322'/><link rel='alternate' type='text/html' href='http://hustoknow.blogspot.com/2011/12/fujitsu-c2010-specs.html' title='Fujitsu C2010 specs'/><author><name>Roger</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4137806788465333774.post-2121774176289057862</id><published>2011-11-26T14:31:00.000-08:00</published><updated>2011-11-26T15:43:23.100-08:00</updated><title type='text'>Setting up wake-on LAN in your own home</title><content type='html'>One of the drawbacks of using Slicehost or Amazon EC2 is that you're pretty much paying $16-$20/month for a VPS server even when you're not accessing the server that much.  If you have data that you also don't want being stored on a cloud, you may opt to setup your own home server to store this information.  You may want to have the ability to access this data but not have your machine turned on all the time adding to your electricity bill.&lt;br /&gt;&lt;br /&gt;First, your computer(s) needs to have wake-on-LAN enabled in the BIOS and your computer's Ethernet port must be plugged directly into a switch/router (wireless wake-on LAN NIC cards may also be possible).  For Toshiba laptops, for instance, you need to reboot, hit Esc and then F1 to enter the BIOS.  You can then enable Wake-on-LAN support and save the changes.&lt;br /&gt;&lt;br /&gt;You also need a router that can run the &lt;a href="http://www.dd-wrt.com/"&gt;DD-WRT&lt;/a&gt; or Tomoto open source firmware.  If you use the VPN version of the DD-WRT, you can also setup a PPTP server with &lt;a href="http://dyn.com/dns/"&gt;DynDNS&lt;/a&gt; so that you can VPN into your server even though you're using an ISP that does not provides static IP's.  You'll want to setup your DD-WRT server to assign a static IP address to your remote computer (inside the Services tab, look for static leases).&lt;br /&gt;&lt;br /&gt;If have one of the Windows Professional versions, you can also take advantage of the Remote Desktop servers.    You can enable Remote Desktop Sever by going to the Control Panel &gt; System &gt; Remote. You can also right-click My Computer (if the icon is shown on the desktop) and choose Properties.&lt;br /&gt;&lt;br /&gt;To verify that Wake-on-LAN works, you can use Netcat/socat to test things out.  This &lt;a href="http://code-slim-jim.blogspot.com/2011/05/quick-wake-on-lan-script-with-netcat.html"&gt;blog posting&lt;/a&gt; explains how the magic packet for Wake-on-LAN is constructed: 6 bytes of FF followed by the LAN MAC address repeated 16 times.    The bash script used to generate a hex version is listed as follows -- you just need to substitute the ETHER parameter with the MAC address of the machine that will need to be woken up:&lt;br /&gt;&lt;pre class="prettyprint"&gt;ETHER="aa:bb:cc:dd:ee:ff"&lt;br /&gt;ETHER2=`echo $ETHER | sed "s/://g"`&lt;br /&gt;ETHER3="${ETHER2}${ETHER2}${ETHER2}${ETHER2}"&lt;br /&gt;ETHER4="FFFFFFFFFFFF${ETHER3}${ETHER3}${ETHER3}${ETHER3}"&lt;br /&gt;echo ${ETHER4} | xxd -r -p &amp;gt; wake.packet&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The blog posting points out that you can use netcat to send to a router IP, but what about sending a broadcast address from within the same network?  For this purpose, the socat utility seems to work better (not sure if netcat allows sending to broadcast IP addresses?)&lt;br /&gt;&lt;pre class="prettyprint"&gt;socat - UDP-DATAGRAM:192.168.0.255:7,broadcast &amp;lt; wake.packet &lt;/pre&gt;&lt;br /&gt;You can also use DD-WRT to wake up any device (check the WoL section) too, but the above information is more background information for how it actualy works!&lt;br /&gt;&lt;br /&gt;To setup dynamic DNS, you'll need to go to the DDNS page of the DD-WRT firmware and input your username/pw.  The DynDNS service is now $20/year, though it used to be free in recent years.  The instructions for setting DynDNS are posted at: &lt;a href="http://www.dd-wrt.com/wiki/index.php/DDNS_-_How_to_setup_Custom_DDNS_settings_using_embedded_inadyn_-_HOWTO"&gt;http://www.dd-wrt.com/wiki/index.php/DDNS_-_How_to_setup_Custom_DDNS_settings_using_embedded_inadyn_-_HOWTO&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;To setup the PPTP server for VPN, go to the PPTP section and follow the instructions according to &lt;a href="http://www.dd-wrt.com/wiki/index.php/PPTP_Server_Configuration"&gt;http://www.dd-wrt.com/wiki/index.php/PPTP_Server_Configuration&lt;/a&gt;.  One thing to note is that the PPTP server IP should be different from your router's internal IP, and the CHAP secrets format is (&lt;username&gt; * &lt;password&gt; *).   Yes, there is an asterisk after username and password.&lt;br /&gt;&lt;br /&gt;Ubuntu comes with 'tsclient', which is a Remote Desktop Connection.  Once you've setup a VPN connection correctly, you can To get your Windows machine to hibernate, you can also create this batch file and add to your Desktop:&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt;%windir%\system32\rundll32.exe powrprof.dll,SetSuspendState Hibernate&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;You can also get an iPad/Android app to wake up your computer remotely -- we found &lt;a href="http://www.mochasoft.dk/iphone_wol.htm"&gt;Mocha WoL&lt;/a&gt;, a free app that lets you wake up the machine remotely.  You can also set the hostname to a DynDNS machine, and assuming you have UDP port forwarding setup to relay WoL broadcasts, it should be able to wake up the machine remotely.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4137806788465333774-2121774176289057862?l=hustoknow.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hustoknow.blogspot.com/feeds/2121774176289057862/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hustoknow.blogspot.com/2011/11/setting-up-wake-on-lan-in-your-own-home.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/2121774176289057862'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/2121774176289057862'/><link rel='alternate' type='text/html' href='http://hustoknow.blogspot.com/2011/11/setting-up-wake-on-lan-in-your-own-home.html' title='Setting up wake-on LAN in your own home'/><author><name>Roger</name><uri>http://www.blogger.com/profile/06655396766752051975</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4137806788465333774.post-2656021438404178213</id><published>2011-11-25T14:07:00.000-08:00</published><updated>2011-11-25T14:14:02.206-08:00</updated><title type='text'>WebDriverWait and SauceLabs</title><content type='html'>If you've used the WebDriverWait class with SauceLabs, you may have noticed that it actually will run beyond the designated timeout period.    I first noticed this issue when seeing that this function took 40 minutes to complete with a 20 second implicit wait time and a 60 second timeout w/ .5 poll frequency! (60 / .5 = 120  loops * 20 seconds/implicit wait = 2400 seconds total)&lt;br /&gt;&lt;br /&gt;The problem appears to be that WebDriverWait attempts to run self._timeout / self._poll times before returning a Timeout Exception:&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt;for _ in xrange(max(1, int(self._timeout/self._poll))):&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Since this code could wait until an ElementNotFound exception is found (and if the implicit wait time is increased), then the total time this routine takes is a function of the total # of loops * poll frequency * the implicit wait time.&lt;br /&gt;&lt;br /&gt;The correct patch appears to be recording the start time and looping until the maximum timeout period has been exceeded:&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt;--- a/selenium/webdriver/support/wait.py&lt;br /&gt;+++ b/selenium/webdriver/support/wait.py&lt;br /&gt;@@ -24,7 +24,7 @@ class WebDriverWait(object):&lt;br /&gt; &lt;br /&gt;     def __init__(self, driver, timeout, poll_frequency=POLL_FREQUENCY):&lt;br /&gt;         """Constructor, takes a WebDriver instance and timeout in seconds.&lt;br /&gt;-           &lt;br /&gt;+&lt;br /&gt;            :Args:&lt;br /&gt;             - driver - Instance of WebDriver (Ie, Firefox, Chrome or Remote)&lt;br /&gt;             - timeout - Number of seconds before timing out&lt;br /&gt;@@ -43,7 +43,8 @@ class WebDriverWait(object):&lt;br /&gt;     def until(self, method):&lt;br /&gt;         """Calls the method provided with the driver as an argument until the \&lt;br /&gt;         return value is not Falsy."""&lt;br /&gt;-        for _ in xrange(max(1, int(self._timeout/self._poll))):&lt;br /&gt;+        end_time = time.time() + self._timeout&lt;br /&gt;+        while(time.time() &lt; end_time):&lt;br /&gt;             try:&lt;br /&gt;                 value = method(self._driver)&lt;br /&gt;                 if value:&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The bug report is filed here:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://code.google.com/p/selenium/issues/detail?id=2939"&gt;http://code.google.com/p/selenium/issues/detail?id=2939&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4137806788465333774-2656021438404178213?l=hustoknow.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hustoknow.blogspot.com/feeds/2656021438404178213/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hustoknow.blogspot.com/2011/11/webdriverwait-and-saucelabs.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/2656021438404178213'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/2656021438404178213'/><link rel='alternate' type='text/html' href='http://hustoknow.blogspot.com/2011/11/webdriverwait-and-saucelabs.html' title='WebDriverWait and SauceLabs'/><author><name>Roger</name><uri>http://www.blogger.com/profile/06655396766752051975</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4137806788465333774.post-9112598197202204629</id><published>2011-11-12T11:53:00.000-08:00</published><updated>2011-11-12T12:14:11.292-08:00</updated><title type='text'>How the celeryctl command works in Celery..</title><content type='html'>One of the &lt;a href="http://www.hostucan.com/webmaster-tutorials/10-popular-django-apps"&gt;most popular Django apps&lt;/a&gt; out there is the &lt;a href="http://ask.github.com/celery/getting-started/introduction.html"&gt;Celery task queue&lt;/a&gt; framework.  It allows you to build your own task queue system for offline processing and provides an elegant framework for interfacing with message brokers such as AMQP, Redis, etc.  &lt;br /&gt;&lt;br /&gt;The &lt;a href="http://ask.github.com/celery/userguide/monitoring.html"&gt;celeryctl&lt;/a&gt; command in the Celery task queue is an amazing tool.  It allows you send commands to your Celery works to figure out which ones are actively processing tasks, revoke tasks that have been dispatched so that workers will skip over procesing them, and see which ones are scheduled.  We use it here to monitor long-running tasks, such as this script:&lt;br /&gt;&lt;pre class="prettyprint"&gt;from celery.bin import celeryctl&lt;br /&gt;import datetime&lt;br /&gt;&lt;br /&gt;def probe_celeryctl():&lt;br /&gt;    results = celeryctl.inspect().run("active")&lt;br /&gt;    check_old_celery_tasks(results)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;def check_old_celery_tasks(results):&lt;br /&gt;    bad_tasks = []&lt;br /&gt;&lt;br /&gt;    MAX_TIMEDELTA = {'hours': 1}&lt;br /&gt;    for host, tasks in results.items():&lt;br /&gt;        for task in tasks:&lt;br /&gt;            task_start = task.get('time_start')&lt;br /&gt;            timestamp = float(task_start)&lt;br /&gt;            task_timestamp = datetime.datetime.fromtimestamp(timestamp)&lt;br /&gt;            time_diff = abs(datetime.datetime.utcnow() - task_timestamp)&lt;br /&gt;            if time_diff &gt; datetime.timedelta(**MAX_TIMEDELTA):&lt;br /&gt;                print "Hmm..%s %s (on %s)" % (time_diff, task, host)&lt;br /&gt;                bad_tasks.append("Task %s elapsed (%s): name=%s, PID=%s, args=%s, kwargs=%s" % (time_diff, host, task.get('name'), task.get('worker_pid'), task.get('args'), task.get('kwargs')))&lt;br /&gt;&lt;br /&gt;    if len(bad_tasks) &gt; 0:&lt;br /&gt;        message = "You'd better check on these tasks...they are slowing things down.\n\n"&lt;br /&gt;        message += "\n".join(bad_tasks)&lt;br /&gt;        print message&lt;br /&gt;&lt;br /&gt;if __name__ == "__main__":&lt;br /&gt;    probe_celeryctl()&lt;br /&gt;&lt;/pre&gt;How does celeryctl work?   Assuming you're using the AMQP backend with Celery, celeryctl relies on the same concepts used in the AMQP open standard (click &lt;a href="http://www.rabbitmq.com/tutorials/amqp-concepts.html"&gt;here&lt;/a&gt; for a basic intro).   When you first startup, Celery will create an AMQP exchange called "celeryd.pidbox" on the AMQP host.  You can confirm by using rabbitmqctl to list exchanges:&lt;br /&gt;&lt;pre class="prettyprint"&gt;$ sudo rabbitmqctl -p fanmgmt_prod list_exchanges&lt;br /&gt;Listing exchanges ...&lt;br /&gt;celeryd.pidbox fanout&lt;br /&gt;.&lt;br /&gt;.&lt;br /&gt;.&lt;br /&gt;&lt;/pre&gt;You'll notice that celeryd.pidbox is created as a fanout exchange (see the AMQP intro for more details).  This way, using celeryctl on one machine will broadcast a message to this exchange.  The AMQP host will deliver messages to each queue that is bound to this celeryd.pidbox exchange.  On startup, every Celery worker will also create a queue (queue.hostname.celery.pidbox) bound to this exchange, which can be used to respond to celeryctl commands.&lt;br /&gt;&lt;br /&gt;Replies from each Celery worker are passed back via direct exchange using celeryd.reply.pidbox.  When you startup celeryctl, it sends messages to the celeryd.pidbox and listens for messages to arrive from the celeryd.reply.pidbox queue.   The timeout period to wait for replies is 1 second, so if you wish to increase the time to wait for replies from Celery workers, you can increase this number with the -t parameter (i.e. celeryctl inspect active -t &lt;seconds&gt;)&lt;br /&gt;&lt;br /&gt;Note: the reply exchange gets deleted after celeryctl command exists (usually set to auto_delete=True).  Since all the Celery workers are still bound to the other celeryd.pidbox exchange, it should still persist until you shutdown all Celery workers.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4137806788465333774-9112598197202204629?l=hustoknow.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hustoknow.blogspot.com/feeds/9112598197202204629/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hustoknow.blogspot.com/2011/11/how-celeryctl-command-works-in-celery.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/9112598197202204629'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/9112598197202204629'/><link rel='alternate' type='text/html' href='http://hustoknow.blogspot.com/2011/11/how-celeryctl-command-works-in-celery.html' title='How the celeryctl command works in Celery..'/><author><name>Roger</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4137806788465333774.post-2531753746485997153</id><published>2011-11-10T09:45:00.000-08:00</published><updated>2011-11-10T09:45:14.052-08:00</updated><title type='text'>Installing HipChat in Jenkins/Hudson..</title><content type='html'>1. git clone https://github.com/jlewallen/jenkins-hipchat-plugin&lt;br /&gt;&lt;br /&gt;2. Compile and build (See http://hustoknow.blogspot.com/2010/09/installing-java-on-ubuntu-1004.html and http://hustoknow.blogspot.com/2011/04/hudson-becomes-jenkins-compling-plugins.html)&lt;br /&gt;&lt;br /&gt;3. Copy the target/.hpi file that got generated into your hudson/plugins dir. &lt;br /&gt;&lt;br /&gt;4. Restart Hudson.&lt;br /&gt;&lt;br /&gt;5. Go into the Configure Hudson and provide the API key/conference room.&lt;br /&gt;&lt;br /&gt;6. Make sure to enable HipChat notifications in each of your build projects!  &lt;br /&gt;&lt;br /&gt;FYI - this plug-in uses the HipChat API to publish information to the respective room that you deisgnate:&lt;br /&gt;&lt;br /&gt;&lt;a href="https://github.com/jlewallen/jenkins-hipchat-plugin/blob/master/src/main/java/jenkins/plugins/hipchat/StandardHipChatService.java"&gt;https://github.com/jlewallen/jenkins-hipchat-plugin/blob/master/src/main/java/jenkins/plugins/hipchat/StandardHipChatService.java&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;   public void publish(String message, String color) {&lt;br /&gt;      for(String roomId : roomIds) {&lt;br /&gt;         logger.info("Posting: " + from + " to " + roomId + ": " + message + " " + color);&lt;br /&gt;         HttpClient client = new HttpClient();&lt;br /&gt;         String url = "https://api.hipchat.com/v1/rooms/message?auth_token=" + token;&lt;br /&gt;         PostMethod post = new PostMethod(url);&lt;br /&gt;         try {&lt;br /&gt;            post.addParameter("from", from);&lt;br /&gt;            post.addParameter("room_id", roomId);&lt;br /&gt;            post.addParameter("message", message);&lt;br /&gt;            post.addParameter("color", color);&lt;br /&gt;            client.executeMethod(post);&lt;br /&gt;         }&lt;br /&gt;         catch(HttpException e) {&lt;br /&gt;            throw new RuntimeException("Error posting to HipChat", e);&lt;br /&gt;         }&lt;br /&gt;         catch(IOException e) {&lt;br /&gt;            throw new RuntimeException("Error posting to HipChat", e);&lt;br /&gt;         }&lt;br /&gt;         finally {&lt;br /&gt;            post.releaseConnection();&lt;br /&gt;         }&lt;br /&gt;      }&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   public void rooms() {&lt;br /&gt;      HttpClient client = new HttpClient();&lt;br /&gt;      String url = "https://api.hipchat.com/v1/rooms/list?format=json&amp;auth_token=" + token;&lt;br /&gt;      GetMethod get = new GetMethod(url);&lt;br /&gt;      try {&lt;br /&gt;         client.executeMethod(get);&lt;br /&gt;         logger.info(get.getResponseBodyAsString());&lt;br /&gt;      }&lt;br /&gt;      catch(HttpException e) {&lt;br /&gt;         throw new RuntimeException("Error posting to HipChat", e);&lt;br /&gt;      }&lt;br /&gt;      catch(IOException e) {&lt;br /&gt;         throw new RuntimeException("Error posting to HipChat", e);&lt;br /&gt;      }&lt;br /&gt;      finally {&lt;br /&gt;         get.releaseConnection();&lt;br /&gt;      }&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4137806788465333774-2531753746485997153?l=hustoknow.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hustoknow.blogspot.com/feeds/2531753746485997153/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hustoknow.blogspot.com/2011/11/installing-hipchat-in-jenkinshudson.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/2531753746485997153'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/2531753746485997153'/><link rel='alternate' type='text/html' href='http://hustoknow.blogspot.com/2011/11/installing-hipchat-in-jenkinshudson.html' title='Installing HipChat in Jenkins/Hudson..'/><author><name>Roger</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4137806788465333774.post-5336310427282767784</id><published>2011-11-10T09:34:00.001-08:00</published><updated>2011-11-10T09:37:58.862-08:00</updated><title type='text'>Installing HipChat on 64-bit Linux.</title><content type='html'>The instructions for HipChat are pretty straightforward, though you do need to run th commands from root.&lt;br /&gt;&lt;br /&gt;&lt;a href="https://www.hipchat.com/help/page/how-do-i-install-hipchat-on-64-bit-linux"&gt;https://www.hipchat.com/help/page/how-do-i-install-hipchat-on-64-bit-linux&lt;/a&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt;# Adobe's instructions forget to mention ia32-libs-gtk&lt;br /&gt;$ apt-get install lib32asound2 lib32gcc1 lib32ncurses5 lib32stdc++6 \&lt;br /&gt;  lib32z1 libc6 libc6-i386 ia32-libs-gtk lib32nss-mdns&lt;br /&gt;&lt;br /&gt;$ wget http://frozenfox.freehostia.com/cappy/getlibs-all.deb&lt;br /&gt;$ dpkg -i getlibs-all.deb&lt;br /&gt;&lt;br /&gt;# incorrect in adobe's instructions (corrected)&lt;br /&gt;$ getlibs -p gnome-keyring&lt;br /&gt;$ getlibs -p libhal-storage1&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Seems like these commands were not needed on Ubuntu 10.04:&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt;$ ln -s /usr/lib32/libnss3.so.1d /usr/lib32/libnss3.so&lt;br /&gt;$ ln -s /usr/lib32/libssl3.so.1d /usr/lib32/libssl3.so&lt;br /&gt;$ ln -s /usr/lib32/libnspr4.so.0d /usr/lib32/libnspr4.so&lt;br /&gt;$ ln -s /usr/lib32/libsmime3.so.1d /usr/lib32/libsmime3.so # missing from adobe's instructions&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt;wget http://airdownload.adobe.com/air/lin/download/2.6/AdobeAIRInstaller.bin&lt;br /&gt;chmod 777 AdobeAIRInstaller.bin&lt;br /&gt;./AdobeAIRInstaller.bin&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The final step is to download the Adobe AIR client here:&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt;$ wget http://downloads.hipchat.com/hipchat.air&lt;br /&gt;$ /opt/Adobe\ AIR/Versions/1.0/Adobe\ AIR\ Application\ Installer hipchat.air&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4137806788465333774-5336310427282767784?l=hustoknow.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hustoknow.blogspot.com/feeds/5336310427282767784/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hustoknow.blogspot.com/2011/11/installing-hipchat-on-64-bit-linux.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/5336310427282767784'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/5336310427282767784'/><link rel='alternate' type='text/html' href='http://hustoknow.blogspot.com/2011/11/installing-hipchat-on-64-bit-linux.html' title='Installing HipChat on 64-bit Linux.'/><author><name>Roger</name><uri>http://www.blogger.com/profile/06655396766752051975</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4137806788465333774.post-2139122614593537205</id><published>2011-11-03T07:56:00.000-07:00</published><updated>2011-11-10T09:34:03.731-08:00</updated><title type='text'>Debugging Facebook's JavaScript code</title><content type='html'>In reviewing &lt;a href="https://github.com/facebook/connect-js/blob/master/src/core/init.js"&gt;Facebook's JavaScript code&lt;/a&gt;, there apparently is a way to enable debugging of the JavaScript.  If you set fb_debug=1, then the logging option will be enabled:&lt;br /&gt;&lt;pre class="prettyprint"&gt;if (!options.logging &amp;amp;&amp;amp;        window.location.toString().indexOf('fb_debug=1') &amp;lt; 0) {      FB._logging = false;    }&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4137806788465333774-2139122614593537205?l=hustoknow.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hustoknow.blogspot.com/feeds/2139122614593537205/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hustoknow.blogspot.com/2011/11/debugging-facebooks-javascript-code.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/2139122614593537205'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/2139122614593537205'/><link rel='alternate' type='text/html' href='http://hustoknow.blogspot.com/2011/11/debugging-facebooks-javascript-code.html' title='Debugging Facebook&apos;s JavaScript code'/><author><name>Roger</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4137806788465333774.post-2445647650299141141</id><published>2011-10-27T20:19:00.000-07:00</published><updated>2011-10-27T20:19:46.107-07:00</updated><title type='text'>Celerybeat and celerybeat-schedule</title><content type='html'>In my effort to attempt to replace /etc/init.d/celerybeat with a version that worked more reliably with fabric, one of the discoveries is that celerybeat keeps firing off all tasks from the scheduled task list because Celerybeat stores the last_run time of a scheduled task usually in a celerybeat-schedule in the default dir)...if it hasn't been run in awhile (by virtue of using an older celerybeat-schedule) then you may see a lot of "Sending due task".&lt;br /&gt;&lt;br /&gt;You can check the last_run_at apparently by using the shelve library from Python, which celerybeat uses to store all the scheduled tasks defined in your Celery configurations.  Each time you restart Celerybeat, this celerybeat-schedule gets merged with your Celery scheduled tasks.  Those that don't exist already are added.&lt;br /&gt;&lt;pre class="prettyprint"&gt;sudo python&lt;br /&gt;&gt;&gt;&gt; import shelve&lt;br /&gt;&gt;&gt;&gt; shelve.open("/var/run/celerybeat-schedule")&lt;br /&gt;&gt;&gt;&gt; a['entries']['my_task'].last_run_at&lt;br /&gt;datetime.datetime(2011, 10, 28, 2, 1, 57, 717454)&lt;br /&gt;&lt;/pre&gt;The key is to specify explicitly define the celerybeat-schedule:&lt;br /&gt;&lt;br /&gt;/etc/default/celerybeat:&lt;br /&gt;&lt;pre class="prettyprint"&gt;export CELERYBEAT_OPTS="--schedule=/var/run/celerybeat-schedule"&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4137806788465333774-2445647650299141141?l=hustoknow.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hustoknow.blogspot.com/feeds/2445647650299141141/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hustoknow.blogspot.com/2011/10/celerybeat-and-celerybeat-schedule.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/2445647650299141141'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/2445647650299141141'/><link rel='alternate' type='text/html' href='http://hustoknow.blogspot.com/2011/10/celerybeat-and-celerybeat-schedule.html' title='Celerybeat and celerybeat-schedule'/><author><name>Roger</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4137806788465333774.post-2082517793352626378</id><published>2011-10-27T16:02:00.000-07:00</published><updated>2011-10-27T16:02:08.771-07:00</updated><title type='text'>IPython: interactive Python</title><content type='html'>- who: shows you what var&lt;br /&gt;- store: stores a variable to a file (%store foo &amp;gt; a.txt)&lt;br /&gt;- reset: clears namespace&lt;br /&gt;- logstart, logon, logoff&lt;br /&gt;- lsmagic&lt;br /&gt;&lt;br /&gt;- run -d &amp;lt;file&amp;gt;: run python code step-by-step&lt;br /&gt;- run -p &amp;lt;file&amp;gt;:&lt;br /&gt;&lt;br /&gt;- xmode Context (xmode Verbose: shows the call values)&lt;br /&gt;- pdb: turns on uncaught exception&lt;br /&gt;- time (func): times run&lt;br /&gt;&lt;br /&gt;Django caches all its models:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://stackoverflow.com/questions/890924/how-do-you-reload-a-django-model-module-using-the-interactive-interpreter-via-m/903943#903943"&gt;http://stackoverflow.com/questions/890924/how-do-you-reload-a-django-model-module-using-the-interactive-interpreter-via-m/903943#903943&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4137806788465333774-2082517793352626378?l=hustoknow.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hustoknow.blogspot.com/feeds/2082517793352626378/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hustoknow.blogspot.com/2011/10/ipython-interactive-python.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/2082517793352626378'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/2082517793352626378'/><link rel='alternate' type='text/html' href='http://hustoknow.blogspot.com/2011/10/ipython-interactive-python.html' title='IPython: interactive Python'/><author><name>Roger</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4137806788465333774.post-3932627882351562717</id><published>2011-10-26T20:29:00.001-07:00</published><updated>2011-10-26T20:29:26.494-07:00</updated><title type='text'>Reasons for double-forking....</title><content type='html'>&lt;a href="http://stackoverflow.com/questions/881388/what-is-the-reason-for-performing-a-double-fork-when-creating-a-daemon"&gt;http://stackoverflow.com/questions/881388/what-is-the-reason-for-performing-a-double-fork-when-creating-a-daemon&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4137806788465333774-3932627882351562717?l=hustoknow.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hustoknow.blogspot.com/feeds/3932627882351562717/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hustoknow.blogspot.com/2011/10/reasons-for-double-forking.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/3932627882351562717'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/3932627882351562717'/><link rel='alternate' type='text/html' href='http://hustoknow.blogspot.com/2011/10/reasons-for-double-forking.html' title='Reasons for double-forking....'/><author><name>Roger</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4137806788465333774.post-4251170898201485971</id><published>2011-10-25T10:34:00.001-07:00</published><updated>2011-10-25T10:34:17.564-07:00</updated><title type='text'>Adobe Flash Player 11 for Ubuntu</title><content type='html'>Adobe finally has a 64-bit version of Adobe Flash Player for Ubuntu now!&lt;br /&gt;&lt;br /&gt;&lt;a href="http://get.adobe.com/flashplayer/completion/?installer=Flash_Player_11_for_Ubuntu_(apt)"&gt;http://get.adobe.com/flashplayer/completion/?installer=Flash_Player_11_for_Ubuntu_(apt)&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4137806788465333774-4251170898201485971?l=hustoknow.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hustoknow.blogspot.com/feeds/4251170898201485971/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hustoknow.blogspot.com/2011/10/adobe-flash-player-11-for-ubuntu.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/4251170898201485971'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/4251170898201485971'/><link rel='alternate' type='text/html' href='http://hustoknow.blogspot.com/2011/10/adobe-flash-player-11-for-ubuntu.html' title='Adobe Flash Player 11 for Ubuntu'/><author><name>Roger</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4137806788465333774.post-4754864149881591031</id><published>2011-10-22T13:55:00.000-07:00</published><updated>2011-10-27T14:45:02.994-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='openid sso hudson'/><title type='text'>Integrating OpenID Google Apps Single Sign On with Hudson/Jenkins....</title><content type='html'>A not so well-documented aspect of using Hudson is that you can integrate OpenID single-sign on (SSO) with your Google Apps domain. You could implement SSO using the &lt;a href="https://github.com/jenkinsci/crowd-plugin"&gt;Jenkins Crowd plugin&lt;/a&gt;&amp;nbsp;that comes pre-packaged with Hudson, but then you'd have to do custom integration work.  Since the Crowd protcol is all SOAP-based, just &lt;a href="http://hustoknow.blogspot.com/2011/10/crowd-and-wsdl.html"&gt;getting the SOAP bindings right&lt;/a&gt; can be a big pain.   Then you'd have to go about either setting up Crowd identity server or creating your own version via the &lt;a href="http://confluence.atlassian.com/display/CROWD012/SOAP+API"&gt;Crowd API&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;The OpenId plugin does not seem to be provided with the Hudson/Jenkins v2.1.2 release, but you can download and install it yourself.  You do need the Sun version of Java (not OpenJDK), since there seems to be some sun.com dependencies existing in the Jenkins code base (the instructions for setting up on Ubuntu are listed &lt;a href="http://hustoknow.blogspot.com/2010/09/installing-java-on-ubuntu-1004.html"&gt;here&lt;/a&gt;).  You also need to install Maven too (sudo apt-get install maven2) and &lt;a href="http://hustoknow.blogspot.com/2011/04/hudson-becomes-jenkins-compling-plugins.html"&gt;configure your ~/.m2/settings.xml.&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Once Java and maven are setup, you can clone the OpenID repo and compile:&lt;br /&gt;&lt;br /&gt;1) git clone https://github.com/jenkinsci/openid-plugin.git&lt;br /&gt;&lt;br /&gt;2) mvn&lt;br /&gt;&lt;br /&gt;If the compile was successful, the openid.hpi plugin should have been compiled into the target/ dir.  You need to copy this open.hpi into your Hudson plugins/ dir (i.e. /var/lib/hudson/plugins).  You don't appear to need to add an openid.hpi.pinned to avoid Hudson from overwriting this package, since the OpenID does come with Jenkins by defualt.&lt;br /&gt;&lt;br /&gt;3) The OpenID plugin expects that the URL that a user connects to your continuous integration ends with a trailing slash ('/').  In your Apache2 config, you may find that you need to add a rewrite rule to force connections to your server always to end with a '/'.  If your server is just http://hudson.myhost.com, the rewrite rule becomes:&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;RewriteEngine on&lt;br /&gt;  RewriteRule  ^$  /  [R]&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;(The major reason is that the getRootUrl() command in the Jenkins code base borrows from the request URL).  The OpenID plugin, when concatenates the OpenID finish callbacks, assumes that there will be a trailing slash at the end.  Without it, your OpenID authorization flows may not work):&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;src/main/java/hudson/plugins/openid/OpenIdSession.java:&lt;br /&gt;receivingurl="Hudson.getInstance().getRootUrl()+finishUrl";&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;If you notice that the OpenID callbacks (i.e federatedLoginService/openid/finish) are not prefixed with a '/', it means that you are missing this trailing slash!&lt;br /&gt;&lt;br /&gt;4) Inside the Hudson configuration screen, the OpenID SSO will be &lt;b&gt;https://www.google.com/accounts/o8/id&lt;/b&gt;.   Your permissions will be defined by the email address of the SSO.  If you do not wish anonymous users to be able to login, you should make sure that they do not have any types of permissions.&lt;br /&gt;&lt;br /&gt;5) Make sure to enable OpenID SSO support in your Google Apps domain. &amp;nbsp;The checkbox should be enabled inside "Manage this domain"-&amp;gt;"Advanced Tools"-&amp;gt;"Federated Authenticatin using OpenID".&lt;br /&gt;&lt;br /&gt;One extra bonus...if you're using the Git plugin with Hudson, you may have also noticed, depending on which version of the Git plugin, that User accounts were based either on the full name or the e-mail username of the Git committer.   If you want the user accounts associated with your Git committers to also be linked to your SSO solution, then this pull-request may also be useful.&lt;br /&gt;&lt;br /&gt;&lt;a href="https://github.com/rogerhu/git-plugin/pull/new/fix_git_email"&gt;https://github.com/rogerhu/git-plugin/pull/new/fix_git_email&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;(If you have pre-existing users, you may wish to convert their user directories from "John Doe" to jdoe@myhost.com to be consistent.)&lt;br /&gt;&lt;br /&gt;(Interesting note: the Git plugin used in the Jenkins/Hudson 2.1.2 release is located at &lt;a href="https://github.com/hudson-plugins/git-plugin"&gt;https://github.com/hudson-plugins/git-plugin&lt;/a&gt;, whereas the older v1 versions are at &lt;a href="https://github.com/jenkinsci/git-plugin"&gt;https://github.com/jenkinsci/git-plugin&lt;/a&gt;.  The code base appears to have diverged a little bit, so one commit patch incorporated in &lt;a href="https://github.com/jenkinsci/git-plugin.git%203607d2ec90f69edcf8cedfcb358ce19a980b8f1a"&gt;https://github.com/jenkinsci/git-plugin.git 3607d2ec90f69edcf8cedfcb358ce19a980b8f1a&lt;/a&gt; that attempted to create accounts based on the Git commiter's username is not included in the v2.1.2 Jenkins release.)&lt;br /&gt;&lt;br /&gt;Also, if you use automated build triggers, it appears they still work even if you turned on the OpenID SSO on too!&lt;br /&gt;&lt;br /&gt;Update: it looks like the Git plug-in will start to expose an option to use the username's entire email address as a Hudson/Jenkins option. &amp;nbsp;See the PR below:&lt;br /&gt;&lt;br /&gt;&lt;a href="https://github.com/hudson-plugins/git-plugin/pull/31/files"&gt;https://github.com/hudson-plugins/git-plugin/pull/31/files&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4137806788465333774-4754864149881591031?l=hustoknow.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hustoknow.blogspot.com/feeds/4754864149881591031/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hustoknow.blogspot.com/2011/10/integrating-google-apps-with.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/4754864149881591031'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/4754864149881591031'/><link rel='alternate' type='text/html' href='http://hustoknow.blogspot.com/2011/10/integrating-google-apps-with.html' title='Integrating OpenID Google Apps Single Sign On with Hudson/Jenkins....'/><author><name>Roger</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4137806788465333774.post-4223819705489288915</id><published>2011-10-20T23:06:00.001-07:00</published><updated>2011-10-20T23:06:34.816-07:00</updated><title type='text'>start-stop-daemon</title><content type='html'>Wondering what the internals of the start-stop-daemon source code are?&lt;br /&gt;&lt;br /&gt;&lt;a href="http://doxygen.kannel.org/d6/d8e/start-stop-daemon_8c-source.html"&gt;http://doxygen.kannel.org/d6/d8e/start-stop-daemon_8c-source.html&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4137806788465333774-4223819705489288915?l=hustoknow.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hustoknow.blogspot.com/feeds/4223819705489288915/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hustoknow.blogspot.com/2011/10/start-stop-daemon.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/4223819705489288915'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/4223819705489288915'/><link rel='alternate' type='text/html' href='http://hustoknow.blogspot.com/2011/10/start-stop-daemon.html' title='start-stop-daemon'/><author><name>Roger</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4137806788465333774.post-6144405424795421552</id><published>2011-10-20T22:47:00.000-07:00</published><updated>2011-10-21T08:49:21.910-07:00</updated><title type='text'>Crowd and WSDL</title><content type='html'>Need the latest copy of the Crowd WSDL file?&lt;br /&gt;&lt;br /&gt;1. Visit &lt;a href="http://www.atlassian.com/software/crowd/CrowdDownloadCenter.jspa"&gt;http://www.atlassian.com/software/crowd/CrowdDownloadCenter.jspa&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;2. Download a copy.&lt;br /&gt;&lt;br /&gt;3. Unpack the .jar files, and go into the atlassian-x.x.x directory.&lt;br /&gt;&lt;br /&gt;4. vi crowd-webapp/WEB-INF/classes/crowd-init.properties&lt;br /&gt;&lt;br /&gt;crowd.home=/home/myuser/projects/atlassian-crowd/data&lt;br /&gt;&lt;br /&gt;5. ./start_crowd.sh&lt;br /&gt;&lt;br /&gt;6. Go to http://localhost:8095 (or your dev server IP).&lt;br /&gt;&lt;br /&gt;7. You should be able to connect and setup the Crowd service.&lt;br /&gt;&lt;br /&gt;8. Go through the setup flow, and get a license key from Atlassian .&lt;br /&gt;&lt;br /&gt;9. wget http://yourhost.com:8095/crowd/services/SecurityServer?wsdl&lt;br /&gt;&lt;br /&gt;Need to get the WSDL working in Python?  Either use ZSI (which is Google App Engine compatible) or the Python suds library:&lt;br /&gt;&lt;br /&gt;&lt;a href="https://jira.atlassian.com/browse/CWD-159"&gt;https://jira.atlassian.com/browse/CWD-159&lt;br /&gt;&lt;/a&gt;&lt;br /&gt;The instructions below will show you how to do Crowd authentication using ZSI: &lt;br /&gt;&lt;br /&gt;&lt;a href="http://tearsoffire.org/twiki/bin/view/Projects/CrowdSoapApi"&gt;http://tearsoffire.org/twiki/bin/view/Projects/CrowdSoapApi&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;from SecurityServer_services import SecurityServerLocator, SecurityServerHttpBindingSOAP&lt;br /&gt;import SecurityServer_services as sss&lt;br /&gt;from SecurityServer_services_types import ns0&lt;br /&gt;&lt;br /&gt;loc = SecurityServerLocator()&lt;br /&gt;server = loc.getSecurityServerPortType()&lt;br /&gt;&lt;br /&gt;#build up the application authentication token&lt;br /&gt;r = ns0.ApplicationAuthenticationContext_Def('ApplicationAuthenticationContext')&lt;br /&gt;cred = ns0.PasswordCredential_Def('_credential').pyclass()&lt;br /&gt;req = sss.authenticateApplicationRequest()&lt;br /&gt;r._name='soaptest'&lt;br /&gt;cred._credential = 'passwordGoesHere'&lt;br /&gt;r._credential=cred&lt;br /&gt;req._in0 = r&lt;br /&gt;token = server.authenticateApplication( req )&lt;br /&gt;&lt;br /&gt;#Look up a principle from the 'soaptest' application&lt;br /&gt;prin = sss.findPrincipalByNameRequest()&lt;br /&gt;prin._in0 = token._out&lt;br /&gt;prin._in1 = 'cpepe'&lt;br /&gt;me = server.findPrincipalByName( prin )&lt;br /&gt;for i in me._out._attributes._SOAPAttribute:&lt;br /&gt;    print '%s: %s' % (str(i._name), str(i._values.__dict__))&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4137806788465333774-6144405424795421552?l=hustoknow.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hustoknow.blogspot.com/feeds/6144405424795421552/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hustoknow.blogspot.com/2011/10/crowd-and-wsdl.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/6144405424795421552'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/6144405424795421552'/><link rel='alternate' type='text/html' href='http://hustoknow.blogspot.com/2011/10/crowd-and-wsdl.html' title='Crowd and WSDL'/><author><name>Roger</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4137806788465333774.post-6716461033469112860</id><published>2011-10-20T20:00:00.000-07:00</published><updated>2011-10-20T20:00:05.487-07:00</updated><title type='text'>Using Fabric with sudo</title><content type='html'>If you've ever had to use &lt;a href="http://docs.fabfile.org/en/1.2.2/index.html"&gt;Fabric&lt;/a&gt;, one of the issues is that your scripts must return an error code of 0 in order for the sudo() command to assert that the command executed successfully.  Any non-zero error code will result in an error message.&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;Fatal error: sudo() encountered an error (return code 1) while executing 'sudo"...&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;If you're using bash scripts, this means that any "set -e" or "bash -e" statements that trigger an abnormal exit.  The "kill -0 &lt;pid&gt;", which allows you to test whether a process exists and can be killed, suffers from a flaw in that if you provide a PID that does not exist, it will trigger an exception and cause bash to break out if "set -e" or "bash -e" is set (normally you can use $? to check the return value).&lt;br /&gt;&lt;br /&gt;You also should check the integer value (if [ $? -eq 0 ]; then or if [ ?! -eq 1]; then) to determine which step to use.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4137806788465333774-6716461033469112860?l=hustoknow.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hustoknow.blogspot.com/feeds/6716461033469112860/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hustoknow.blogspot.com/2011/10/using-fabric-with-sudo.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/6716461033469112860'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/6716461033469112860'/><link rel='alternate' type='text/html' href='http://hustoknow.blogspot.com/2011/10/using-fabric-with-sudo.html' title='Using Fabric with sudo'/><author><name>Roger</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4137806788465333774.post-6000383852535924542</id><published>2011-10-19T14:50:00.000-07:00</published><updated>2011-10-19T14:50:23.311-07:00</updated><title type='text'>Minus sign</title><content type='html'>The minus sign is the default value if the variable isn't set.  This line has two sets of fallbacks:&lt;br /&gt;&lt;br /&gt;CELERYBEAT_PID_FILE=${CELERYBEAT_PID_FILE:-${CELERYBEAT_PIDFILE:-$DEFAULT_PID_FILE}}&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4137806788465333774-6000383852535924542?l=hustoknow.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hustoknow.blogspot.com/feeds/6000383852535924542/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hustoknow.blogspot.com/2011/10/minus-sign.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/6000383852535924542'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/6000383852535924542'/><link rel='alternate' type='text/html' href='http://hustoknow.blogspot.com/2011/10/minus-sign.html' title='Minus sign'/><author><name>Roger</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4137806788465333774.post-3587691157579906874</id><published>2011-10-19T09:34:00.001-07:00</published><updated>2011-10-19T09:34:40.945-07:00</updated><title type='text'>value_for_platform</title><content type='html'>&lt;a href="http://wiki.opscode.com/display/chef/Recipes#Recipes-valueforplatform"&gt;http://wiki.opscode.com/display/chef/Recipes#Recipes-valueforplatform&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4137806788465333774-3587691157579906874?l=hustoknow.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hustoknow.blogspot.com/feeds/3587691157579906874/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hustoknow.blogspot.com/2011/10/valueforplatform.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/3587691157579906874'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/3587691157579906874'/><link rel='alternate' type='text/html' href='http://hustoknow.blogspot.com/2011/10/valueforplatform.html' title='value_for_platform'/><author><name>Roger</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4137806788465333774.post-29315084906199893</id><published>2011-10-19T09:22:00.001-07:00</published><updated>2011-10-19T09:34:25.893-07:00</updated><title type='text'>Chef template specificity</title><content type='html'>&lt;a href="http://wiki.opscode.com/display/chef/Templates"&gt;http://wiki.opscode.com/display/chef/Templates&lt;/a&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="color: #333333; font-family: Arial, Helvetica, FreeSans, sans-serif; font-size: 13px; line-height: 17px;"&gt;&lt;/span&gt;&lt;br /&gt;&lt;h1 style="border-bottom-color: rgb(145, 150, 153); border-bottom-style: solid; border-bottom-width: 1px; color: black; font-size: 1.8em; font-weight: bold; line-height: normal; margin-bottom: 0.5em; margin-left: 0px; margin-right: 0px; margin-top: 2em; padding-bottom: 2px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;Template Location Specificity&lt;/h1&gt;&lt;div style="background-attachment: initial; background-clip: initial; background-color: initial; background-image: none; background-origin: initial; background-position: initial initial; background-repeat: initial initial; color: #333333; font-size: 10pt; font-weight: normal; line-height: 13pt; margin-bottom: 10px; margin-left: 0px; margin-right: 0px; margin-top: 10px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;&lt;a href="http://wiki.opscode.com/display/chef/Cookbooks" style="color: #326ca6; outline-color: initial; outline-style: none; outline-width: initial; text-decoration: none;" title="Cookbooks"&gt;Cookbooks&lt;/a&gt;&amp;nbsp;are often designed to work on a variety of hosts and platforms. Templates often need to differ depending on the platform, host, or function of the node. When the differences are minor, they can be handled with a small amount of logic within the template itself.&amp;nbsp;&lt;span style="color: #f7681a;"&gt;When templates differ dramatically, you can define multiple templates for the same file.&lt;/span&gt;&amp;nbsp;Chef will decide which template to render based on the following rules.&lt;/div&gt;&lt;div style="background-attachment: initial; background-clip: initial; background-color: initial; background-image: none; background-origin: initial; background-position: initial initial; background-repeat: initial initial; color: #333333; font-size: 10pt; font-weight: normal; line-height: 13pt; margin-bottom: 10px; margin-left: 0px; margin-right: 0px; margin-top: 10px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;Within a Cookbook's template directory, you might find a directory structure like this:&lt;/div&gt;&lt;ul style="font-size: 10pt; line-height: 13pt; list-style-type: disc;"&gt;&lt;li style="font-size: 10pt; line-height: 13pt; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;templates&lt;ul style="font-size: 10pt; line-height: 13pt; list-style-type: disc;"&gt;&lt;li style="font-size: 10pt; line-height: 13pt; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;host-foo.example.com&lt;/li&gt;&lt;li style="font-size: 10pt; line-height: 13pt; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;ubuntu-8.04&lt;/li&gt;&lt;li style="font-size: 10pt; line-height: 13pt; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;ubuntu&lt;/li&gt;&lt;li style="font-size: 10pt; line-height: 13pt; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;default&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div style="background-attachment: initial; background-clip: initial; background-color: initial; background-image: none; background-origin: initial; background-position: initial initial; background-repeat: initial initial; color: #333333; font-size: 10pt; font-weight: normal; line-height: 13pt; margin-bottom: 10px; margin-left: 0px; margin-right: 0px; margin-top: 10px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;For a node with FQDN of foo.example.com and the&amp;nbsp;&lt;tt&gt;sudoers.erb&lt;/tt&gt;&amp;nbsp;resource above, we would match:&lt;/div&gt;&lt;ul style="font-size: 10pt; line-height: 13pt; list-style-type: disc;"&gt;&lt;li style="font-size: 10pt; line-height: 13pt; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;host-foo.example.com/sudoers.erb&lt;/li&gt;&lt;li style="font-size: 10pt; line-height: 13pt; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;ubuntu-8.04/sudoers.erb&lt;/li&gt;&lt;li style="font-size: 10pt; line-height: 13pt; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;ubuntu/sudoers.erb&lt;/li&gt;&lt;li style="font-size: 10pt; line-height: 13pt; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;default/sudoers.erb&lt;/li&gt;&lt;/ul&gt;&lt;div style="background-attachment: initial; background-clip: initial; background-color: initial; background-image: none; background-origin: initial; background-position: initial initial; background-repeat: initial initial; color: #333333; font-size: 10pt; font-weight: normal; line-height: 13pt; margin-bottom: 10px; margin-left: 0px; margin-right: 0px; margin-top: 10px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;&lt;em&gt;In that order.&lt;/em&gt;&lt;/div&gt;&lt;div style="background-attachment: initial; background-clip: initial; background-color: initial; background-image: none; background-origin: initial; background-position: initial initial; background-repeat: initial initial; color: #333333; font-size: 10pt; font-weight: normal; line-height: 13pt; margin-bottom: 10px; margin-left: 0px; margin-right: 0px; margin-top: 10px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;Then, for example:&amp;nbsp;&lt;tt&gt;sudoers.rb&lt;/tt&gt;&amp;nbsp;placed under the&amp;nbsp;&lt;tt&gt;files/host-foo.example.com/&lt;/tt&gt;&amp;nbsp;directory, means it will be only copied to the machine with the domain name&amp;nbsp;&lt;tt&gt;foo.example.com&lt;/tt&gt;. (Note the "host-" prefix to the directory name)&lt;/div&gt;&lt;div style="background-attachment: initial; background-clip: initial; background-color: initial; background-image: none; background-origin: initial; background-position: initial initial; background-repeat: initial initial; color: #333333; font-size: 10pt; font-weight: normal; line-height: 13pt; margin-bottom: 10px; margin-left: 0px; margin-right: 0px; margin-top: 10px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;&lt;b&gt;So, the rule distilled:&lt;/b&gt;&lt;/div&gt;&lt;ol style="font-size: 10pt; line-height: 13pt; list-style-type: decimal;"&gt;&lt;li style="font-size: 10pt; line-height: 13pt; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;&lt;tt&gt;host-node[:fqdn]&lt;/tt&gt;&lt;/li&gt;&lt;li style="font-size: 10pt; line-height: 13pt; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;&lt;tt&gt;node[:platform]-node[:platform_version]&lt;/tt&gt;&lt;/li&gt;&lt;li style="font-size: 10pt; line-height: 13pt; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;&lt;tt&gt;node[:platform]&lt;/tt&gt;&lt;/li&gt;&lt;li style="font-size: 10pt; line-height: 13pt; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;&lt;tt&gt;default&lt;/tt&gt;&lt;/li&gt;&lt;/ol&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4137806788465333774-29315084906199893?l=hustoknow.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hustoknow.blogspot.com/feeds/29315084906199893/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hustoknow.blogspot.com/2011/10/chef-template-specificity.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/29315084906199893'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/29315084906199893'/><link rel='alternate' type='text/html' href='http://hustoknow.blogspot.com/2011/10/chef-template-specificity.html' title='Chef template specificity'/><author><name>Roger</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4137806788465333774.post-3664617295560914287</id><published>2011-10-19T09:01:00.000-07:00</published><updated>2011-10-19T09:01:03.159-07:00</updated><title type='text'>Dealing with IOError and mod_wsgi</title><content type='html'>&lt;a href="http://permalink.gmane.org/gmane.comp.python.django.devel/30886"&gt;http://permalink.gmane.org/gmane.comp.python.django.devel/30886&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4137806788465333774-3664617295560914287?l=hustoknow.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hustoknow.blogspot.com/feeds/3664617295560914287/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hustoknow.blogspot.com/2011/10/dealing-with-ioerror-and-modwsgi.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/3664617295560914287'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/3664617295560914287'/><link rel='alternate' type='text/html' href='http://hustoknow.blogspot.com/2011/10/dealing-with-ioerror-and-modwsgi.html' title='Dealing with IOError and mod_wsgi'/><author><name>Roger</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4137806788465333774.post-4859636221429622261</id><published>2011-10-17T21:32:00.001-07:00</published><updated>2011-10-17T21:32:14.961-07:00</updated><title type='text'>Open source version of Facebook's JavaScript library</title><content type='html'>&lt;a href="https://github.com/facebook/connect-js/"&gt;https://github.com/facebook/connect-js/&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4137806788465333774-4859636221429622261?l=hustoknow.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hustoknow.blogspot.com/feeds/4859636221429622261/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hustoknow.blogspot.com/2011/10/open-source-version-of-facebooks.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/4859636221429622261'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/4859636221429622261'/><link rel='alternate' type='text/html' href='http://hustoknow.blogspot.com/2011/10/open-source-version-of-facebooks.html' title='Open source version of Facebook&apos;s JavaScript library'/><author><name>Roger</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4137806788465333774.post-160950336668560304</id><published>2011-10-17T14:56:00.001-07:00</published><updated>2011-10-17T14:56:42.515-07:00</updated><title type='text'>coroutines</title><content type='html'>David Beazley has an awesome set of PDF slides about Python co-routines:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.dabeaz.com/coroutines/Coroutines.pdf"&gt;http://www.dabeaz.com/coroutines/Coroutines.pdf&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.dabeaz.com/coroutines/"&gt;http://www.dabeaz.com/coroutines/&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;PyCon talk too:&lt;br /&gt;http://blip.tv/pycon-us-videos-2009-2010-2011/pycon-2011-an-outsider-s-look-at-co-routines-4899200&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4137806788465333774-160950336668560304?l=hustoknow.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hustoknow.blogspot.com/feeds/160950336668560304/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hustoknow.blogspot.com/2011/10/coroutines.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/160950336668560304'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/160950336668560304'/><link rel='alternate' type='text/html' href='http://hustoknow.blogspot.com/2011/10/coroutines.html' title='coroutines'/><author><name>Roger</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4137806788465333774.post-8100605368374200069</id><published>2011-10-16T21:56:00.000-07:00</published><updated>2011-10-17T17:52:48.742-07:00</updated><title type='text'>get_task_logger() in Celery...</title><content type='html'>If you looked at the Celery documentation, you'll notice that the get_task_logger() examples &lt;br /&gt;constantly show up.&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;@task&lt;br /&gt;def add(x, y):&lt;br /&gt;    logger = add.get_logger()&lt;br /&gt;    logger.info("Adding %s + %s" % (x, y))&lt;br /&gt;    return x + y&lt;br /&gt;&lt;/pre&gt;What does this function do?  Well, it turns out that it will create a separate logger instance specifically tied to the task name (submitted as a PR on https://github.com/ask/celery/issues/129).   The propagate=False is always set, so that any messages passed to it will not move up the parent/ancestor chain.&lt;br /&gt;&lt;br /&gt;Instead, a handler is always added to this task.  If you wish to adjust the logger level,&lt;br /&gt;you could do:&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;import logging&lt;br /&gt;logging.getLogger('myproject.add').setLevel(logging.DEBUG)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;If no loglevel is specified in get_logger(), then the default log level defined in CELERYD_LOG_LEVEL is used.  Be careful though!  The right way is to set the level number (not the level name) if you are modifying directly through Python:&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;from celery import current_app&lt;br /&gt;from celery.utils import LOG_LEVELS&lt;br /&gt;current_app.conf.CELERYD_LOG_LEVEL = LOG_LEVELS['DEBUG']  # pretty much the same as logging.DEBUG&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;What's the purpose of get_task_logger()?   Well it appears the motivation is to allow logging by task names.  If we were just to import the standard logging module, Celery will patch the logger module to add process-aware information (ensure_process_aware_logger()), and then add format/handlers to both the root logger and the logger defined by the multiprocessing module (the multiprocessing get_logger() does not use process shared-logs but it allows you to login things to the "multiprocessing" namespace, which adds SUBDEBUG/SUBWARNING debug levels).&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;def setup_logging_subsystem(self, loglevel=None, logfile=None, format=None, colorize=None, **kwargs):                                                             &lt;br /&gt;        if Logging._setup:                   &lt;br /&gt;            return                                                                                            &lt;br /&gt;        loglevel = loglevel or self.loglevel      &lt;br /&gt;        format = format or self.format                                                                         &lt;br /&gt;        if colorize is None:                &lt;br /&gt;            colorize = self.supports_color(logfile)                                                            &lt;br /&gt;                                                                                                              &lt;br /&gt;        if mputil and hasattr(mputil, "_logger"):                    &lt;br /&gt;            mputil._logger = None                                                                              &lt;br /&gt;        ensure_process_aware_logger()                               &lt;br /&gt;        receivers = signals.setup_logging.send(sender=None,                    &lt;br /&gt;                        loglevel=loglevel, logfile=logfile,                                 &lt;br /&gt;                        format=format, colorize=colorize)                      &lt;br /&gt;        if not receivers:                                           &lt;br /&gt;            root = logging.getLogger()                              &lt;br /&gt;                                                                                                              &lt;br /&gt;            if self.app.conf.CELERYD_HIJACK_ROOT_LOGGER:                       &lt;br /&gt;                root.handlers = []                                                                             &lt;br /&gt;                                                                    &lt;br /&gt;            mp = mputil.get_logger() if mputil else None                 &lt;br /&gt;            for logger in filter(None, (root, mp)):                         &lt;br /&gt;                self._setup_logger(logger, logfile, format, colorize, **kwargs)               &lt;br /&gt;                logger.setLevel(loglevel)                        &lt;br /&gt;                signals.after_setup_logger.send(sender=None, logger=logger,                                    &lt;br /&gt;                                        loglevel=loglevel, logfile=logfile,                                    &lt;br /&gt;                                        format=format, colorize=colorize)              &lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4137806788465333774-8100605368374200069?l=hustoknow.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hustoknow.blogspot.com/feeds/8100605368374200069/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hustoknow.blogspot.com/2011/10/gettasklogger-in-celery.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/8100605368374200069'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/8100605368374200069'/><link rel='alternate' type='text/html' href='http://hustoknow.blogspot.com/2011/10/gettasklogger-in-celery.html' title='get_task_logger() in Celery...'/><author><name>Roger</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4137806788465333774.post-8996804510224637630</id><published>2011-10-16T21:45:00.000-07:00</published><updated>2011-10-16T21:56:38.740-07:00</updated><title type='text'>Debugging Celery tasks locally</title><content type='html'>Want to make sure your Celery tasks work correctly before you deploy?  Here are a bunch of useful tips you can do:&lt;br /&gt;&lt;br /&gt;First, set the root logger and "celery.task.default" to use DEBUG mode:&lt;br /&gt;&lt;pre class="prettyprint"&gt;import logging &lt;br /&gt;logging.getLogger('celery.task.default').setLevel(pythonLogging.DEBUG)&lt;br /&gt;logging.getLogger().setLevel(pythonLogging.DEBUG)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Set ALWAYS_EAGER mode so that Celery will always invoke tasks locally instead of dispatching to the Celery machine. &lt;br /&gt;&lt;br /&gt;Set EAGER_PROPAGATES_EXCEPTION so that any exceptions within tasks will be bubbled up so that you can actually see any exceptions that may cause your batch calls to fail (i.e. any uncaught exception can cause a fatal error!)&lt;br /&gt;&lt;pre class="prettyprint"&gt;from celery import current_app&lt;br /&gt;current_app.conf.CELERY_ALWAYS_EAGER = True&lt;br /&gt;current_app.conf.CELERY_EAGER_PROPAGATES_EXCEPTIONS = True&lt;br /&gt;&lt;br /&gt;from celery.utils import LOG_LEVELS&lt;br /&gt;current_app.conf.CELERYD_LOG_LEVEL = LOG_LEVELS['DEBUG']  # pretty much the same as logging.DEBUG&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Finally, if you are invoking a task from the same Python script, you should import the task_name as if it were being imported, even if the function is declared within the same file.   The reason is that when running the Celeryd daemon and looks for registered tasks, Celery will consider the task function you invoked to come from the "__main__" class.  The way to get around it is to import the task residing in the same file, assuming your PYTHONPATH is set correctly.&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;from celery.decorators import task&lt;br /&gt;&lt;br /&gt;@task&lt;br /&gt;def task_name():&lt;br /&gt;   print "here"&lt;br /&gt;   return 1&lt;br /&gt;&lt;br /&gt;if "__name__ == "__main__":&lt;br /&gt;  from &lt;appname.module&gt; import task_name&lt;br /&gt;&lt;br /&gt;  task_name.apply_async()&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;(Note: This information has been updated to reflect Celery v2.3.3 inner-workings).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4137806788465333774-8996804510224637630?l=hustoknow.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hustoknow.blogspot.com/feeds/8996804510224637630/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hustoknow.blogspot.com/2011/06/debugging-celery-tasks-locally.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/8996804510224637630'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/8996804510224637630'/><link rel='alternate' type='text/html' href='http://hustoknow.blogspot.com/2011/06/debugging-celery-tasks-locally.html' title='Debugging Celery tasks locally'/><author><name>Roger</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4137806788465333774.post-6047062396901438657</id><published>2011-10-15T16:59:00.000-07:00</published><updated>2011-10-15T20:15:31.707-07:00</updated><title type='text'>Celery and the big instance refactor</title><content type='html'>One of the strange parts in &lt;a href="http://www.celeryproject.org"&gt;Celery&lt;/a&gt; is that if you want a logger that will write to celery.task.default instead of its own default name, you can do:&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;import celery.task import Task&lt;br /&gt;logger = Task.get_logger()&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The Task class appears to be a global instantiation of Celery.   Normally, the task logger is setup via the get_logger() method, which then calls setup_task_logger(), which in turn calls get_task_logger.  If you invoke get_logger() within a Task class, the name of the task name is used:&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;def setup_task_logger(self, loglevel=None, logfile=None, format=None,&lt;br /&gt;            colorize=None, task_name=None, task_id=None, propagate=False,&lt;br /&gt;            app=None, **kwargs):&lt;br /&gt;        logger = self._setup_logger(self.get_task_logger(loglevel, task_name),&lt;br /&gt;                                    logfile, format, colorize, **kwargs)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;If you use Task.get_logger(), no name is used and the logger namespace is set to celery.task.default.&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;def get_task_logger(self, loglevel=None, name=None):                                                                               &lt;br /&gt;      logger = logging.getLogger(name or "celery.task.default")  &lt;br /&gt;      if loglevel is not None:&lt;br /&gt;         logger.setLevel(loglevel)                                                                                  &lt;br /&gt;      return logger&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This Task appears to be part of the &lt;a href="http://readthedocs.org/docs/celery/en/3.0-devel/internals/app-overview.html"&gt;“The Big Instance” Refactor&lt;/a&gt;.  It appears that there are plans for multiple instances of the Celery object to be instantiated.&lt;br /&gt;&lt;br /&gt;Also, one thing to note:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://ask.github.com/celery/userguide/tasks.html#logging"&gt;http://ask.github.com/celery/userguide/tasks.html#logging&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Instantiation&lt;br /&gt;A task is not instantiated for every request, but is registered in the task registry as a global instance.&lt;br /&gt;&lt;br /&gt;This means that the __init__ constructor will only be called once per process, and that the task class is semantically closer to an Actor.&lt;br /&gt;&lt;br /&gt;If you have a task,&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4137806788465333774-6047062396901438657?l=hustoknow.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hustoknow.blogspot.com/feeds/6047062396901438657/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hustoknow.blogspot.com/2011/10/celery-and-big-instance-refactor.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/6047062396901438657'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/6047062396901438657'/><link rel='alternate' type='text/html' href='http://hustoknow.blogspot.com/2011/10/celery-and-big-instance-refactor.html' title='Celery and the big instance refactor'/><author><name>Roger</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4137806788465333774.post-3427288963151555791</id><published>2011-10-14T22:53:00.000-07:00</published><updated>2011-10-14T22:53:51.325-07:00</updated><title type='text'>FQL is not being deprecrated</title><content type='html'>You can now do FQL queries via the Open Graph API....&lt;br /&gt;&lt;br /&gt;One of the most common misconceptions we hear from developers is the belief that FQL will be deprecated with the REST API. The primary reason for this misunderstanding is that you can only issue FQL queries using the fql.query or fql.multiquery methods. We want to make it clear that FQL is here to stay and that we do not have any plans to deprecating it. Today, we are enabling developers to issue FQL queries using the Graph API. With this change, FQL is now just an extension of Graph API.&lt;br /&gt;&lt;br /&gt;You can issue an HTTP GET request to https://graph.facebook.com/fql?q=QUERY. The ‘q’ parameter can be a single FQL query or a multi-query. A multi-query is a JSON-encoded dictionary of queries.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://developers.facebook.com/blog/post/579/"&gt;http://developers.facebook.com/blog/post/579/&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4137806788465333774-3427288963151555791?l=hustoknow.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hustoknow.blogspot.com/feeds/3427288963151555791/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hustoknow.blogspot.com/2011/10/fql-is-not-being-deprecrated.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/3427288963151555791'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/3427288963151555791'/><link rel='alternate' type='text/html' href='http://hustoknow.blogspot.com/2011/10/fql-is-not-being-deprecrated.html' title='FQL is not being deprecrated'/><author><name>Roger</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4137806788465333774.post-1697001282465806851</id><published>2011-10-13T09:58:00.000-07:00</published><updated>2011-10-14T22:52:01.246-07:00</updated><title type='text'>Pylint on Ubuntu</title><content type='html'>Ever see this issue?  &lt;br /&gt;&lt;pre class="prettyprint"&gt;&gt;&gt; from logilab.common.compat import builtins&lt;br /&gt;Traceback (most recent call last):&lt;br /&gt;  File "&lt;stdin&gt;", line 1, in &lt;module&gt;&lt;br /&gt;ImportError: cannot import name builtins&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Chances are you have an old version of logilab that is stored inside /usr/lib/pymodules:&lt;br /&gt;&lt;pre class="prettyprint"&gt;&gt;&gt;&gt; import logilab&lt;br /&gt;&gt;&gt;&gt; logilab.common&lt;br /&gt;&lt;module 'logilab.common' from '/usr/lib/pymodules/python2.6/logilab/common/__init__.pyc'&gt;&lt;br /&gt;&gt;&gt;&gt; logilab.common.compat&lt;br /&gt;&lt;module 'logilab.common.compat' from '/usr/lib/pymodules/python2.6/logilab/common/compat.pyc'&gt;&lt;br /&gt;&gt;&gt;&gt; logilab.common.compat.__file__&lt;br /&gt;'/usr/lib/pymodules/python2.6/logilab/common/compat.pyc'&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The solution is to delete the logilab directory in /usr/lib/pymodules, or do:&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;sudo apt-get remove python-logilab-common&lt;br /&gt;sudo apt-get remove python-logilab-astng&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Then you can do:&lt;br /&gt;&lt;pre class="prettyprint"&gt;pip install -U pylint&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4137806788465333774-1697001282465806851?l=hustoknow.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hustoknow.blogspot.com/feeds/1697001282465806851/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hustoknow.blogspot.com/2011/10/pylint-on-ubuntu.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/1697001282465806851'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/1697001282465806851'/><link rel='alternate' type='text/html' href='http://hustoknow.blogspot.com/2011/10/pylint-on-ubuntu.html' title='Pylint on Ubuntu'/><author><name>Roger</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4137806788465333774.post-923960248893314631</id><published>2011-10-03T13:44:00.001-07:00</published><updated>2011-10-03T14:06:37.310-07:00</updated><title type='text'>Paper on Linux's pseudo-random number generator</title><content type='html'>&lt;a href="http://eprint.iacr.org/2006/086.pdf"&gt;http://eprint.iacr.org/2006/086.pdf&lt;/a&gt;&lt;br /&gt;&lt;a href="http://www.linuxfromscratch.org/hints/downloads/files/entropy.txt"&gt;http://www.linuxfromscratch.org/hints/downloads/files/entropy.txt&lt;/a&gt;&lt;br /&gt;&lt;a href="http://www.linuxcertified.com/hw_random.html"&gt;http://www.linuxcertified.com/hw_random.html&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4137806788465333774-923960248893314631?l=hustoknow.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hustoknow.blogspot.com/feeds/923960248893314631/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hustoknow.blogspot.com/2011/10/paper-on-linuxs-pseudo-random-number.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/923960248893314631'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/923960248893314631'/><link rel='alternate' type='text/html' href='http://hustoknow.blogspot.com/2011/10/paper-on-linuxs-pseudo-random-number.html' title='Paper on Linux&apos;s pseudo-random number generator'/><author><name>Roger</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4137806788465333774.post-295746607213932056</id><published>2011-09-29T11:32:00.000-07:00</published><updated>2011-09-29T11:32:09.556-07:00</updated><title type='text'>Changing end of line in Emacs</title><content type='html'>&lt;a href="http://mike.kruckenberg.com/archives/2004/08/replace_m_in_em.html"&gt;http://mike.kruckenberg.com/archives/2004/08/replace_m_in_em.html&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;M-x set-buffer-file-coding-system RET unix&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4137806788465333774-295746607213932056?l=hustoknow.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hustoknow.blogspot.com/feeds/295746607213932056/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hustoknow.blogspot.com/2011/09/changing-end-of-line-in-emacs.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/295746607213932056'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/295746607213932056'/><link rel='alternate' type='text/html' href='http://hustoknow.blogspot.com/2011/09/changing-end-of-line-in-emacs.html' title='Changing end of line in Emacs'/><author><name>Roger</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4137806788465333774.post-7329114725150347400</id><published>2011-09-23T11:25:00.001-07:00</published><updated>2011-09-23T11:28:34.814-07:00</updated><title type='text'>Facebook Python code for OAuth2</title><content type='html'>Facebook recently &lt;a href="http://developers.facebook.com/blog/post/534/"&gt;announced&lt;/a&gt; that on October 1st, 2011, all Facebook third-party apps will need to transition to OAuth2.   The JavaScript and PHP SDK code is posted, but how would you make the change if you're using Python/Django? &amp;nbsp;To help others make the transition, we've released our own set of Python code at this GitHub repo:&lt;br /&gt;&lt;br /&gt;&lt;a href="https://github.com/rogerhu/facebook_oauth2"&gt;https://github.com/rogerhu/facebook_oauth2&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;One of the pain points is that users may have existing OAuth cookies set on their browser, which you may use in your current application to authenticate. &amp;nbsp;However, because&amp;nbsp;Facebook Connect's JavaScript library requires an apiKey change parameter, it makes it hard to use their existing library to force these fbs_ cookie deletions. &amp;nbsp;Furthermore, you'd have to&amp;nbsp;&lt;a href="http://www.quirksmode.org/js/cookies.html"&gt;write your own JS&lt;/a&gt;&amp;nbsp;since the Facebook JS SDK is hard-coded to use only the new apiKey parameter.&lt;br /&gt;&lt;br /&gt;We also show in this code how you can force these fbs_ cookie deletions on the server-side, primarily by setting the expiration date and providing the correct domain= parameter back to the client. &amp;nbsp;It's worked well for us in managing the transition to OAuth2, so we hope you will find the same approach useful.&lt;br /&gt;&lt;br /&gt;Good luck!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4137806788465333774-7329114725150347400?l=hustoknow.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hustoknow.blogspot.com/feeds/7329114725150347400/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hustoknow.blogspot.com/2011/09/facebook-python-code-for-oauth2.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/7329114725150347400'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/7329114725150347400'/><link rel='alternate' type='text/html' href='http://hustoknow.blogspot.com/2011/09/facebook-python-code-for-oauth2.html' title='Facebook Python code for OAuth2'/><author><name>Roger</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4137806788465333774.post-4286762068759086663</id><published>2011-09-21T08:21:00.000-07:00</published><updated>2011-09-21T08:24:04.018-07:00</updated><title type='text'>Lessons Learned: Migrating Tests to Selenium v2</title><content type='html'>The slides from yesterday's talk, &lt;a href="http://www.slideshare.net/rogerjhu1/lessons-learned-migrating-tests-to-selenium-v2"&gt;Lessons Learned: Migrating Tests to Selenium v2&lt;/a&gt;, are now posted.  Remember the game Simon Says?  Check out the game &lt;a href="http://hearsaysocialsays.appspot.com/"&gt;Hearsay Social Says&lt;/a&gt;, which shows how Selenium v2 can be used to automate web browser testing.&lt;br /&gt;&lt;br /&gt;&lt;center&gt;&lt;br /&gt;&lt;div id="__ss_9351695" style="width: 425px;"&gt;&lt;iframe frameborder="0" height="355" marginheight="0" marginwidth="0" scrolling="no" src="http://www.slideshare.net/slideshow/embed_code/9351695" width="425"&gt;&lt;/iframe&gt; &lt;br /&gt;&lt;div style="padding: 5px 0 12px;"&gt;&lt;/div&gt;&lt;/div&gt;&lt;/center&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4137806788465333774-4286762068759086663?l=hustoknow.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hustoknow.blogspot.com/feeds/4286762068759086663/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hustoknow.blogspot.com/2011/09/lessons-learned-migrating-tests-to.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/4286762068759086663'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/4286762068759086663'/><link rel='alternate' type='text/html' href='http://hustoknow.blogspot.com/2011/09/lessons-learned-migrating-tests-to.html' title='Lessons Learned: Migrating Tests to Selenium v2'/><author><name>Roger</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4137806788465333774.post-4383794655932719300</id><published>2011-09-17T10:10:00.000-07:00</published><updated>2011-09-17T10:20:47.590-07:00</updated><title type='text'>How Selenium v1 bypasses cross-origin domain security policies...</title><content type='html'>The writeup by Simon Stewart from the &lt;a href="http://www.aosabook.org/en/selenium.html"&gt;Architecture of Open Source Applications&lt;/a&gt; discusses how the architecture of Selenium v2 was guided by the lessons drawn from Selenium 1.  The figures and discussions on the Selenium &lt;a href="http://seleniumhq.org/docs/05_selenium_rc.html"&gt;http://seleniumhq.org/docs/05_selenium_rc.html&lt;/a&gt; also mention the fact that a proxy-server is used to get past cross-domain security policies.  But how does Selenium v1 work under the covers?&lt;br /&gt;&lt;br /&gt;Here's the general steps of what happens when you go from launching a Selenium RC (v1) server.&lt;br /&gt;Suppose you wrote this Python script (let's call test1.py) on your local Linux box and launched a Selenium RC server on a Windows 7 machine.&lt;br /&gt;&lt;pre class="prettyprint"&gt;from selenium import selenium&lt;br /&gt;&lt;br /&gt;selenium = selenium(“10.10.10.1", 4444, "*iexplore", "http://www.google.com/")&lt;br /&gt;selenium.start()&lt;br /&gt;selenium.open(“/”)&lt;br /&gt;&lt;/pre&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-eABXzGr100I/TnTTGd55S7I/AAAAAAAACKE/XPymBh4jzWE/s1600/sel-v1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="320" src="http://3.bp.blogspot.com/-eABXzGr100I/TnTTGd55S7I/AAAAAAAACKE/XPymBh4jzWE/s320/sel-v1.png" width="275" /&gt;&lt;/a&gt;&lt;/div&gt;1. The Selenium RC server starts and waits for an HTTP connection from a client.  When the client connects and sends an instruction to initiate new Selenium instance, the Selenium RC server will launch the browser and configure the browser's proxy settings.  &lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/-pH6xBycyP_g/TnTRQ-OKMRI/AAAAAAAACKA/gYBOsZ3rrPE/s1600/sel-aut.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="167" src="http://4.bp.blogspot.com/-pH6xBycyP_g/TnTRQ-OKMRI/AAAAAAAACKA/gYBOsZ3rrPE/s400/sel-aut.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;2. Selenium v1 JavaScript code is also loaded into the browser.  This JavaScript code is what is used by Selenium for performing all its automated testing (i.e. used to query DOM elements, generate mouse clicks, etc.).  You can click View Source on the Selenium RC tab to see what JavaScript code is loaded.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-nm2Vkh2R2pc/TnTRFBssBsI/AAAAAAAACJ4/ICZETY6C2mY/s1600/rc-js.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="244" src="http://3.bp.blogspot.com/-nm2Vkh2R2pc/TnTRFBssBsI/AAAAAAAACJ4/ICZETY6C2mY/s400/rc-js.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;Note that there's also a runTest() JavaScript command executed when this page is loaded. When this happens, an Ajax connection is also initiated in selenium-remoterunner.js:&lt;br /&gt;&lt;pre class="prettyprint"&gt;nextCommand: function () {this.xmlHttpForCommandsAndResults = XmlHttp.create();&lt;br /&gt;sendToRC(postResult, urlParms, fnBind(this._HandleHttpResponse, this), this.xmlHttpForCommandsAndResults);&lt;br /&gt;};&lt;br /&gt;&lt;/pre&gt;During the configuration of this proxy, all /selenium-server URL's are intercepted and routed to the RC server.  By using an Ajax connection that uses the same URL as the one that is being tested, Selenium v1 can therefore avoid triggering browser cross-domain security policies and establish establish a connection back to the Selenium RC server to sending/receiving future commands.  You'll notice how this browser proxy configuration works if you tried to add a /selenium-server to any URL (i.e. http://www.google.com/selenium-server would normally return back a Page Not Found error).&lt;br /&gt;&lt;br /&gt;3. The RC server then opens a URL connection specified by the client API with a /selenium-server/core/Blank.html?start=true.  (Note that when creating a Selenium instance, a specific URL must also be provided.)  If this connection was successful, it also helps to verify that the proxy configuration was setup properly.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/-Z3DBC4LA3Gc/TnTN_LqmFaI/AAAAAAAACJw/iOVp0Y4HDtk/s1600/google.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="112" src="http://1.bp.blogspot.com/-Z3DBC4LA3Gc/TnTN_LqmFaI/AAAAAAAACJw/iOVp0Y4HDtk/s400/google.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;4. What happens if this Ajax connection from the browser to the RC server times out?  If the request timeouts (i.e. no command), another Ajax request is sent and the channel is re-established.  This is performed within the selenium-executionloop.js, which initiates a new Ajax connection back to the Selenium server.&lt;br /&gt;&lt;pre class="prettyprint"&gt;continueTest : function() {LOG.debug("currentTest.continueTest() - acquire the next command");                                            &lt;br /&gt;        if (! this.aborted) {                                                                                          &lt;br /&gt;            this.currentCommand = this.nextCommand();                                                                  &lt;br /&gt;        }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;6. When this communication channel is setup, Selenium clients sending an v1 API command will be sent to the server, which in turns relays the communicate on this Ajax channel to send/receive commands. Selenium v1 relies on JavaScript to simulate all browser interactions, and the appropriate command is executed.  &lt;br /&gt;&lt;br /&gt;Selenium v2 avoids this issue entirely by using native drivers that bind tightly to the operating system...but this approach introduces new issues and complexities, which will be discussed in this week's upcoming &lt;a href="http://www.meetup.com/seleniumsanfrancisco/events/32925722/"&gt;Meetup.com&lt;/a&gt; event.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4137806788465333774-4383794655932719300?l=hustoknow.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hustoknow.blogspot.com/feeds/4383794655932719300/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hustoknow.blogspot.com/2011/09/how-selenium-v1-bypasses-cross-origin.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/4383794655932719300'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/4383794655932719300'/><link rel='alternate' type='text/html' href='http://hustoknow.blogspot.com/2011/09/how-selenium-v1-bypasses-cross-origin.html' title='How Selenium v1 bypasses cross-origin domain security policies...'/><author><name>Roger</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/-eABXzGr100I/TnTTGd55S7I/AAAAAAAACKE/XPymBh4jzWE/s72-c/sel-v1.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4137806788465333774.post-4631254168949006162</id><published>2011-09-13T10:59:00.000-07:00</published><updated>2011-09-13T10:59:53.547-07:00</updated><title type='text'>BeautifulSoup v4</title><content type='html'>See this warning msg?  &lt;br /&gt;bs4/builder/_html5lib.py:60: DataLossWarning: namespaceHTMLElements not supported yet&lt;br /&gt;  DataLossWarning)&lt;br /&gt;bs4/builder/_html5lib.py:77: DataLossWarning: BeautifulSoup cannot represent elements in any namespace&lt;br /&gt;  warnings.warn("BeautifulSoup cannot represent elements in any namespace", DataLossWarning)&lt;br /&gt;&lt;br /&gt;Apparently here is the reason for this msg:&lt;br /&gt;&lt;a href="https://bugs.launchpad.net/beautifulsoup/+bug/727014"&gt;https://bugs.launchpad.net/beautifulsoup/+bug/727014&lt;/a&gt; &lt;br /&gt;&lt;br /&gt;Leonard Richardson (leonardr) wrote on 2011-03-01:  #1&lt;br /&gt;html5lib supports namespaced elements (like &lt;namespace:tag&gt;), and Beautiful Soup doesn't yet. These warnings are mostly a reminder to myself that I need to add namespace support. Unless you're actually parsing code that has namespaced tags, there won't be any real data loss.&lt;br /&gt;&lt;br /&gt;Changed in beautifulsoup:&lt;br /&gt;status:  New → Confirmed&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4137806788465333774-4631254168949006162?l=hustoknow.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hustoknow.blogspot.com/feeds/4631254168949006162/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hustoknow.blogspot.com/2011/09/beautifulsoup-v4.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/4631254168949006162'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/4631254168949006162'/><link rel='alternate' type='text/html' href='http://hustoknow.blogspot.com/2011/09/beautifulsoup-v4.html' title='BeautifulSoup v4'/><author><name>Roger</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4137806788465333774.post-8322738969302732574</id><published>2011-09-13T00:58:00.000-07:00</published><updated>2011-09-13T01:34:36.749-07:00</updated><title type='text'>lxml and removing nodes</title><content type='html'>The lxml library for Python represents a really effective tool for parsing and manipulating XML-based data.  You can manipulate the XML documents to deal with the W3C standards for &lt;a href="http://en.wikipedia.org/wiki/XML_Signature"&gt;Inclusive and Exclusive Canonicalization&lt;/a&gt;, which deals with all messy details of adjusting namespaces as you extract sections of the data.   &lt;br /&gt;&lt;br /&gt;XML is inherently a difficult data structure to manipulate.  The white spaces, return lines, and new lines make a big difference in validating signatures and/or digest values.  If you accidentally miss a character in your text manipulation, perform the wrong canonicalization, etc. your one-way SHA hash can easily be affected, causing you to be unable to verify the signature of the data.&lt;br /&gt;&lt;br /&gt;One of the idiosyncracies of the lxml library, described best in &lt;a href="http://infohost.nmt.edu/tcc/help/pubs/pylxml/pylxml.pdf"&gt;this lxml document&lt;/a&gt;, is that the internal data structures are stored as Element objects with a .text and .tail property.  The .text represents all the underlying value within the tag, while the .tail property represents the text between tags.  This data structure differs from the DOM-model in that the text after an element is represented by the parent.  For example, consider this XML-structure:&lt;br /&gt;&lt;br /&gt;&amp;lt;a&amp;gt;aTEXT&lt;br /&gt;&amp;nbsp; &amp;lt;b&amp;gt;bTEXT&amp;lt;/b&amp;gt;bTAIL&lt;br /&gt;&amp;lt;/a&amp;gt;aTAIL&lt;br /&gt;&lt;br /&gt;This can be represented with the following lxml code:&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;import etree&lt;br /&gt;&lt;br /&gt;a = etree.Element('a')&lt;br /&gt;a.text = "aTEXT"&lt;br /&gt;a.tail = "aTAIL"&lt;br /&gt;&lt;br /&gt;b = SubElement(a, 'b')&lt;br /&gt;b.text = "bTEXT"&lt;br /&gt;b.tail = "bTAIL"&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;What happens if you remove the 'b' node?   Ideally, the text with the 'b' tag disappears, while the bTAIL gets moved up.  The structure would look like the following:&lt;br /&gt;&lt;br /&gt;&amp;lt;a&amp;gt;aTEXTbTAIL&amp;lt;/a&amp;gt;aTAIL&lt;br /&gt;&lt;br /&gt;The command to remove the lxml node would be:&lt;br /&gt;&lt;pre class="prettyprint"&gt;a.remove(b)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Upon making this change, however, it appears in lxml v2.3, the output appeared as: &amp;lt;a&amp;gt;aTEXT&amp;lt;/a&amp;gt;aTAIL&amp;lt;/a&amp;gt;&lt;br /&gt;&lt;br /&gt;In order to understand what's going on, I had to download the source for the lxml, install the Cython library that converts the .pyx code to .C bindings, recompile, and link the new etree.so binary.  If you're curious, the instructions for doing so here are posted &lt;a href="http://hustoknow.blogspot.com/2011/09/compiling-lxml2.html"&gt;here&lt;/a&gt;. &lt;br /&gt;&lt;br /&gt;Upon inspecting the etree.pyx, I noticed the code to move the tail occured after unlinking the node.  What we really wanted is that the tail to be moved before the node is unlinked.  Otherwise, the information about the tail would also be potentially be removed, which may have explained why the tail was never copied.&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;def remove(self, _Element element not None):&lt;br /&gt;-        tree.xmlUnlinkNode(c_node)&lt;br /&gt;         _moveTail(c_next, c_node)&lt;br /&gt;+        tree.xmlUnlinkNode(c_node)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Examining the _moveTail code also points to something interesting.   The .tail is represented internally by XML-based text-based nodes, which are siblings of the current node (denoted by the .next pointer).  Text nodes are also XML-based text-nodes, but appear to be children of the node.   There is a loop that traverses the linked list of nodes, such that there can be multiple text-nodes, which could could happen if multiple subelements were removed, and you were left with a chain of XML-based .tail nodes.&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;cdef void _moveTail(xmlNode* c_tail, xmlNode* c_target):&lt;br /&gt;    cdef xmlNode* c_next&lt;br /&gt;    # tail support: look for any text nodes trailing this node and&lt;br /&gt;    # move them too&lt;br /&gt;    c_tail = _textNodeOrSkip(c_tail)&lt;br /&gt;    while c_tail is not NULL:&lt;br /&gt;        c_next = _textNodeOrSkip(c_tail.next)&lt;br /&gt;        tree.xmlUnlinkNode(c_tail)&lt;br /&gt;        tree.xmlAddNextSibling(c_target, c_tail)&lt;br /&gt;        c_target = c_tail&lt;br /&gt;        c_tail = c_next&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Upon fixing this code, the text_xinclude_test started failing.   If I recompiled and reverted back to the original etree.pyx, the test passed fine.   One even more unusual aspect was the invocation of the self.include(), which appeared to be overriden depending on whether the lxml library would rely on the native implementation of the xinclude() routine, or rely on its Python-based version that allows external URL's to referenced in &lt;a href="https://github.com/rogerhu/lxml/blob/18c235d4791160165fa1e64a8505186d7bfb0bae/src/lxml/ElementInclude.py"&gt;ElementInclude.py&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;def test_xinclude_text(self):&lt;br /&gt;        filename = fileInTestDir('test_broken.xml')&lt;br /&gt;        root = etree.XML(_bytes('''\&lt;br /&gt;        &lt;doc xmlns:xi="http://www.w3.org/2001/XInclude"&gt;&lt;br /&gt;          &lt;xi:include href="%s" parse="text"&gt;&lt;br /&gt;        &lt;/xi:include&gt;&lt;/doc&gt;&lt;br /&gt;        ''' % filename))&lt;br /&gt;        old_text = root.text&lt;br /&gt;        content = read_file(filename)&lt;br /&gt;        old_tail = root[0].tail&lt;br /&gt;&lt;br /&gt;&lt;b&gt;        self.include( etree.ElementTree(root) )&lt;/b&gt;&lt;br /&gt;        self.assertEquals(old_text + content + old_tail,&lt;br /&gt;                          root.text)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The test_xinclude_text() is a routine to verify that one can use &amp;lt;:xi:include&amp;gt; directives to incorporate other files within an XML-document.  When such a tag is discovered, the contents of the file is read (in this case, the contents of test_broken.xml) and the entire node is substituted with this text.   The parent node's .text property will then be set and the &amp;lt;xi:include&amp;gt; is removed.&lt;br /&gt;&lt;br /&gt;It appears that code within the &lt;a href="https://github.com/rogerhu/lxml/blob/18c235d4791160165fa1e64a8505186d7bfb0bae/src/lxml/ElementInclude.py"&gt;ElementInclude.py&lt;/a&gt; the text appeared to mask this issue by appending the tail before removing it:&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;@@ -204,7 +204,8 @@ def _include(elem, loader=None, _parent_hrefs=None, base_url=None):&lt;br /&gt;                 elif parent is None:&lt;br /&gt;                     return text # replaced the root node!&lt;br /&gt;                 else:&lt;br /&gt;-                    parent.text = (parent.text or "") + text + (e.tail or "")&lt;br /&gt;+                    parent.text = (parent.text or "") + text &lt;br /&gt;                 parent.remove(e)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The entire pull request for this fix is located here:&lt;br /&gt;&lt;br /&gt;&lt;a href="https://github.com/lxml/lxml/pull/14/files"&gt;https://github.com/lxml/lxml/pull/14/files&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Update on this PR:&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Note that this is a deliberate design choice. It will not change.&lt;br /&gt;&lt;br /&gt;http://lxml.de/FAQ.html#what-about-that-trailing-text-on-serialised-elements&lt;br /&gt;&lt;br /&gt;http://lxml.de/tutorial.html#elements-contain-text&lt;br /&gt;&lt;br /&gt;In other words, if you remove a subelement, you have to take care of the .tail and move it to the right tag.   The lxml library will not change so this PR request was rejected.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4137806788465333774-8322738969302732574?l=hustoknow.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hustoknow.blogspot.com/feeds/8322738969302732574/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hustoknow.blogspot.com/2011/09/lxml-bug.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/8322738969302732574'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/8322738969302732574'/><link rel='alternate' type='text/html' href='http://hustoknow.blogspot.com/2011/09/lxml-bug.html' title='lxml and removing nodes'/><author><name>Roger</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4137806788465333774.post-1047287443662998762</id><published>2011-09-12T00:23:00.000-07:00</published><updated>2011-09-13T00:42:33.722-07:00</updated><title type='text'>Compiling and testing lxml2..</title><content type='html'>The instructions for recompiling lxml seem pretty straightforward.  You have to pip install Cython, which is used to convert the .pyx file into a .c file, which then can be gcc compiled.  The libxml2-dev and libxslt-dev must be packaged installed.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://lxml.de/1.3/build.html"&gt;http://lxml.de/1.3/build.html&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;git clone https://github.com/lxml/lxml/&lt;br /&gt;sudo apt-get install libxml-dev&lt;br /&gt;sudo apt-get install libxslt-dev&lt;br /&gt;pip install Cython&lt;br /&gt;python setup.py build_ext&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;If you want to test that the unit tests still pass, you can link the uld type:&lt;br /&gt;&lt;pre class="prettyprint"&gt;cd src/lxml&lt;br /&gt;ln -s ../../build/lib.linux-x86_64-2.6/lxml/etree.so &lt;br /&gt;cd ../..&lt;br /&gt;python test.py test_etree.py&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;!--Had to add this line to make things work?@@ -271,6 +271,7 @@ def flags(option):     for flag in xslt_flags.split():         if flag not in flag_list:             flag_list.append(flag)+    flag_list.append('-Isrc/lxml')     return flag_list--&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4137806788465333774-1047287443662998762?l=hustoknow.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hustoknow.blogspot.com/feeds/1047287443662998762/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hustoknow.blogspot.com/2011/09/compiling-lxml2.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/1047287443662998762'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/1047287443662998762'/><link rel='alternate' type='text/html' href='http://hustoknow.blogspot.com/2011/09/compiling-lxml2.html' title='Compiling and testing lxml2..'/><author><name>Roger</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4137806788465333774.post-5457587200329998421</id><published>2011-09-09T19:16:00.000-07:00</published><updated>2011-09-12T09:46:49.431-07:00</updated><title type='text'>lxml</title><content type='html'>One of the best documentation for using lxml in Python is located here:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://infohost.nmt.edu/tcc/help/pubs/pylxml/pylxml.pdf"&gt;infohost.nmt.edu/tcc/help/pubs/pylxml/pylxml.pdf&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;One interesting tidbit:&lt;br /&gt;&lt;br /&gt;In the DOM, trees are build out of nodes represented as Node instances. Some nodes are Element instances, representing whole elements. Each Element has an assortment of child nodes of various types: Element nodes for its element children; Attribute nodes for its attributes; and Text nodes for textual content.&lt;br /&gt;&lt;br /&gt;The lxml view of an XML document, by contrast, builds a tree of only one node type: the Element.&lt;br /&gt;&lt;br /&gt;The text following the element. This is the most unusual departure. In the DOM model, any text&lt;br /&gt;following an element E is associated with the parent of E; in lxml, that text is considered the “tail” of E.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4137806788465333774-5457587200329998421?l=hustoknow.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hustoknow.blogspot.com/feeds/5457587200329998421/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hustoknow.blogspot.com/2011/09/lxml-and-python.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/5457587200329998421'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/5457587200329998421'/><link rel='alternate' type='text/html' href='http://hustoknow.blogspot.com/2011/09/lxml-and-python.html' title='lxml'/><author><name>Roger</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4137806788465333774.post-6009442948365292378</id><published>2011-09-01T11:30:00.001-07:00</published><updated>2011-09-01T11:30:58.038-07:00</updated><title type='text'>Getting the latest version of RabbitMQ v2.5.0+ via Ubuntu..</title><content type='html'>&lt;a href="http://www.rabbitmq.com/debian.html#apt"&gt;http://www.rabbitmq.com/debian.html#apt&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4137806788465333774-6009442948365292378?l=hustoknow.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hustoknow.blogspot.com/feeds/6009442948365292378/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hustoknow.blogspot.com/2011/09/getting-latest-version-of-rabbitmq-v250.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/6009442948365292378'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/6009442948365292378'/><link rel='alternate' type='text/html' href='http://hustoknow.blogspot.com/2011/09/getting-latest-version-of-rabbitmq-v250.html' title='Getting the latest version of RabbitMQ v2.5.0+ via Ubuntu..'/><author><name>Roger</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4137806788465333774.post-6512081661373960868</id><published>2011-08-31T13:33:00.000-07:00</published><updated>2011-08-31T13:52:16.814-07:00</updated><title type='text'>The trouble with Protected Mode in Selenium 2 for setting cookies...</title><content type='html'>If you try to set cookies using the WebDriver API through Selenium 2, you may find that Internet Explorer fails to even set the cookie. The issue has been reported here:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://code.google.com/p/selenium/issues/detail?id=1227&amp;q=cookie&amp;colspec=ID%20Stars%20Type%20Status%20Priority%20Milestone%20Owner%20Summary#makechanges"&gt;http://code.google.com/p/selenium/issues/detail?id=1227&amp;q=cookie&amp;colspec=ID%20Stars%20Type%20Status%20Priority%20Milestone%20Owner%20Summary#makechanges&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Selenium has a bunch of test suites to verify the behavior so it seemed strange that there would be an issue.  Examining the &lt;a href="http://selenium.googlecode.com/svn/trunk/cpp/IEDriver/"&gt;IEDriver code&lt;/a&gt; too shows that the &lt;a href="http://selenium.googlecode.com/svn/trunk/cpp/IEDriver/CommandHandlers/AddCookieCommandHandler.h"&gt;AddCookieCommandHandler&lt;/a&gt; is very similar to behavior of &lt;a href="http://selenium.googlecode.com/svn/trunk/cpp/IEDriver/CommandHandlers/DeleteAllCookiesCommandHandler.h"&gt;DeleteAllCookiesHandler&lt;/a&gt; and other IE command handlers, so I didn't really find an issue.  Nor did the AddCookie() method in &lt;a href="http://selenium.googlecode.com/svn/trunk/cpp/IEDriver/DocumentHost.cpp"&gt;DocumentHost.cpp&lt;/a&gt; handles the dispatching of adding cookies.  &lt;br /&gt;&lt;br /&gt;If we do:&lt;br /&gt;&lt;pre class="prettyprint"&gt;&gt;&gt;&gt; driver = webdriver.Remote(desired_capabilities=current_env,&lt;br /&gt;                                        command_executor="http://localhost:4444/wd/hb")&lt;br /&gt;&gt;&gt;&gt; driver.execute_script("document.cookie='a=1';")&lt;br /&gt;&gt;&gt;&gt; driver.get_cookies()&lt;br /&gt;[{u'name': u'a', u'value': u'1', u'path': u'/', u'hCode': 97, u'class': u'org.openqa.selenium.Cookie', u'secure': False}, {u'name': u'sessionid', u'value': u'e1257265399f35b5c7ae4cf630581c90', u'path': u'/', u'hCode': 607797809, u'class': u'org.openqa.selenium.Cookie', u'secure': False}]&lt;br /&gt;&gt;&gt;&gt; driver.delete_cookie('a')&lt;br /&gt;&gt;&gt;&gt; driver.get_cookies()&lt;br /&gt;[{u'name': u'sessionid', u'value': u'e1257265399f35b5c7ae4cf630581c90', u'path': u'/', u'hCode': 607797809, u'class': u'org.openqa.selenium.Cookie', u'secure': False}]&lt;br /&gt;&gt;&gt;&gt; driver.add_cookie({'name' : 'a' , 'value' : '2', 'secure' : False})&lt;br /&gt;(Pdb) driver.get_cookies()&lt;br /&gt;[{u'name': u'sessionid', u'value': u'e1257265399f35b5c7ae4cf630581c90', u'path': u'/', u'hCode': 607797809, u'class': u'org.openqa.selenium.Cookie', u'secure': False}]&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Both delete commands will work successfully.  But doing an add_cookie() fails to work if IE7/IE8 are in Protected mode in Selenium 2.5.0, even though Internet Explorer/IEDriver does not report any error.  However, I was able to get cookies to be set once I disabled Protected mode. &lt;br /&gt;&lt;br /&gt;Since Selenium 2.5.0 requires all Protected Mode settings to be consistent, you have to go into your Internet Options and uncheck the Protected Mode for every single zone (i.e. click through the icons for Internet, Local Internet, Trusted sites, Restricted sites).  Then cookies can be correctly set.&lt;br /&gt;&lt;br /&gt;It appears that IE7 and IE8 have this issue.  IE9 may not have this problem.  I was not able to get cookies set in either Protected/non-Protected mode using Selenium v2.0.0b3 though so you may still need to upgrade to Selenium 2 beyond this version to get cookie support working in IE7/IE8.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4137806788465333774-6512081661373960868?l=hustoknow.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hustoknow.blogspot.com/feeds/6512081661373960868/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hustoknow.blogspot.com/2011/08/trouble-with-protected-mode-in-selenium.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/6512081661373960868'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/6512081661373960868'/><link rel='alternate' type='text/html' href='http://hustoknow.blogspot.com/2011/08/trouble-with-protected-mode-in-selenium.html' title='The trouble with Protected Mode in Selenium 2 for setting cookies...'/><author><name>Roger</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4137806788465333774.post-5835124672410824195</id><published>2011-08-30T09:41:00.000-07:00</published><updated>2011-08-30T11:57:55.630-07:00</updated><title type='text'>Using add_cookie in Selenium 2</title><content type='html'>The documentation in the Python bindings for using the add_cookie() function Selenium 2 are unclear.  The add_cookie() appears to take in a simple key/value pair:&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;def add_cookie(self, cookie_dict):&lt;br /&gt;        """Adds a cookie to your current session.&lt;br /&gt;        Args:&lt;br /&gt;            cookie_dict: A dictionary object, with the desired cookie name as the key, and&lt;br /&gt;            the value being the desired contents.&lt;br /&gt;        Usage:&lt;br /&gt;            driver.add_cookie({'foo': 'bar',})&lt;br /&gt;        """&lt;br /&gt;        self.execute(Command.ADD_COOKIE, {'cookie': cookie_dict})&lt;br /&gt;&lt;/pre&gt;If you're encountering NullPointerExceptions similar to a &lt;a href="http://code.google.com/p/selenium/issues/detail?id=1526"&gt;bug&lt;/a&gt; it's possible the problem is that your dictionary needs to include name, value, path, and secure keys.  The tests in selenium/webdriver/common/cookies_test.py appear to back this point up:&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;self.COOKIE_A = {"name": "foo",&lt;br /&gt;                         "value": "bar",&lt;br /&gt;                         "path": "/",&lt;br /&gt;                         "secure": False}&lt;br /&gt;&lt;br /&gt;    def testAddCookie(self):&lt;br /&gt;        self.driver.execute_script("return document.cookie")&lt;br /&gt;        self.driver.add_cookie(self.COOKIE_A)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Even the section posted at &lt;a href="http://readthedocs.org/docs/selenium-python/en/latest/navigating.html#cookies"&gt;http://readthedocs.org/docs/selenium-python/en/latest/navigating.html#cookies&lt;/a&gt; suggest that adding cookie just a matter of connecting to using a key/value pair too:   &lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;Before we leave these next steps, you may be interested in understanding how to use cookies. First of all, you need to be on the domain that the cookie will be valid for:&lt;br /&gt;&lt;br /&gt;# Go to the correct domain&lt;br /&gt;driver.get("http://www.example.com")&lt;br /&gt;&lt;br /&gt;# Now set the cookie. This one's valid for the entire domain&lt;br /&gt;cookie = {"key": "value"})&lt;br /&gt;driver.add_cookie(cookie)&lt;br /&gt;&lt;br /&gt;# And now output all the available cookies for the current URL&lt;br /&gt;all_cookies = driver.get_cookies()&lt;br /&gt;for cookie_name, cookie_value in all_cookies.items():&lt;br /&gt;    print "%s -&gt; %s", cookie_name, cookie_value&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;For disabling the Django debug toolbar in Selenium 2, then the command should be: &lt;br /&gt;&lt;pre class="prettyprint"&gt;self.selenium.add_cookie({"name" : "djdt",&lt;br /&gt;                          "value" : "true",&lt;br /&gt;                          "path" : "/",&lt;br /&gt;                          "secure" : False})&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;As of Selenium v2.5.0, It appears that all name/value and secure must be specified to avoid triggering the NullPointerException error.  &lt;br /&gt;&lt;br /&gt;An issue report has been filed here:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://code.google.com/p/selenium/issues/detail?id=2367"&gt;http://code.google.com/p/selenium/issues/detail?id=2367&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4137806788465333774-5835124672410824195?l=hustoknow.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hustoknow.blogspot.com/feeds/5835124672410824195/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hustoknow.blogspot.com/2011/08/using-addcookie-in-selenium-2.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/5835124672410824195'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/5835124672410824195'/><link rel='alternate' type='text/html' href='http://hustoknow.blogspot.com/2011/08/using-addcookie-in-selenium-2.html' title='Using add_cookie in Selenium 2'/><author><name>Roger</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4137806788465333774.post-4103202142356493009</id><published>2011-08-28T19:53:00.000-07:00</published><updated>2011-08-28T19:53:28.536-07:00</updated><title type='text'>Extracting audio clips from YouTube videos</title><content type='html'>If you use the stock Ubuntu v10.04 youtube-dl version, you may encounter this error message when trying to download a YouTube clip:&lt;br /&gt;&lt;pre class="prettyprint bash"&gt;youtube-dl http://www.youtube.com/watch?v=&lt;youtube id&gt;&lt;br /&gt;ERROR: no fmt_url_map or conn information found in video info&lt;br /&gt;&lt;/pre&gt;The solution is to git clone the youtube-dl repo and use the latest youtube-dl version:&lt;br /&gt;&lt;pre class="prettyprint"&gt;git clone https://github.com/rg3/youtube-dl.git&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Extracting only the audio portion means that you should set the -vn option, which disables video encoding.  The -acodec option determines the output format.   So you would execute the command (depending if you want Ogg bitstream or Mp3 format)&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint bash"&gt;ffmpeg -i &lt;youtube-dl .mp4 file&gt; -vn -acodec vorbis &lt;output .ogg file&gt;&lt;br /&gt;ffmpeg -i &lt;youtube-dl .mp4 file&gt; -vn -acodec mp3 &lt;output .mp3 file&gt;&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4137806788465333774-4103202142356493009?l=hustoknow.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hustoknow.blogspot.com/feeds/4103202142356493009/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hustoknow.blogspot.com/2011/08/extracting-audio-clips-from-youtube.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/4103202142356493009'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/4103202142356493009'/><link rel='alternate' type='text/html' href='http://hustoknow.blogspot.com/2011/08/extracting-audio-clips-from-youtube.html' title='Extracting audio clips from YouTube videos'/><author><name>Roger</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4137806788465333774.post-3813310673542094586</id><published>2011-08-27T00:19:00.000-07:00</published><updated>2011-08-31T00:02:12.709-07:00</updated><title type='text'>Getting branch support to work with Nose/Coverage</title><content type='html'>While Ned Batchelder's coverage utility has supported &lt;a href="http://nedbatchelder.com/code/coverage/branch.html"&gt;branch measurements&lt;/a&gt; for sometime, it hasn't been supported in the main line of the nose unit discovery util.  We can see in the upcoming v1.1.3 release that the --cover-branches option will be supported:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://readthedocs.org/docs/nose/en/latest/news.html"&gt;http://readthedocs.org/docs/nose/en/latest/news.html&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Currently nose v1.1.3 is still labeled as a development version, so you'd have to get pip install nose==dev in order to install this copy.&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;pip install --upgrade coverage&lt;br /&gt;pip install --upgrade nosexcover&lt;br /&gt;pip install --upgrade nose==dev (1.1.3)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;An alternative, which has long been suggested in &lt;a href="http://groups.google.com/group/nose-users/browse_thread/thread/58429c3dacdac881"&gt;discussion groups&lt;/a&gt;, is to create a .coveragerc file to enable branch coverage by default.  This file must be placed in the location where coverage.py is run, not necessarily in your home directory:&lt;br /&gt;&lt;pre class="prettyprint"&gt;[run] &lt;br /&gt;     branch=True &lt;br /&gt;&lt;/pre&gt;If you've enabled things correctly, you should see the header (instead of the default) as follows:&lt;br /&gt;&lt;pre class="prettyprint"&gt;Name                                              Stmts   Miss Branch BrPart  Cover   Missing&lt;br /&gt;---------------------------------------------------------------------------------------------&lt;br /&gt;&lt;/pre&gt;If you're also using --with-xunit to generate Cobertura-style XML reports, hopefully you should also see the branch conditionals also being tallied correctly too!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4137806788465333774-3813310673542094586?l=hustoknow.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hustoknow.blogspot.com/feeds/3813310673542094586/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hustoknow.blogspot.com/2011/08/getting-branch-support-to-work-with.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/3813310673542094586'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/3813310673542094586'/><link rel='alternate' type='text/html' href='http://hustoknow.blogspot.com/2011/08/getting-branch-support-to-work-with.html' title='Getting branch support to work with Nose/Coverage'/><author><name>Roger</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4137806788465333774.post-1743962550240763748</id><published>2011-08-17T07:47:00.000-07:00</published><updated>2011-09-23T12:58:58.893-07:00</updated><title type='text'>Facebook's OAuth2 support for Python</title><content type='html'>Facebook recently &lt;a href="http://developers.facebook.com/blog/post/534/"&gt;announced&lt;/a&gt; that they will be phasing in OAuth 2.0 support and require its use starting October 1, 2011.  On the JavaScript SDK side, there are several &amp;nbsp;changes on the JavaScript code that have to be done, which are listed as follows.  &lt;br /&gt;&lt;br /&gt;You can download the Python code here:&lt;br /&gt;&lt;br /&gt;&lt;a href="https://github.com/rogerhu/facebook_oauth2"&gt;https://github.com/rogerhu/facebook_oauth2&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;1. FB.init has to be initialized with the Facebook APP ID instead of the API Key, though the apiKey parameter still is used.&lt;br /&gt;&lt;br /&gt;2. The oauth: true options. must be set in the FB.init() calls.&lt;br /&gt;&lt;br /&gt;In other words, the code changes would be:&lt;br /&gt;&lt;pre class="prettyprint"&gt;FB.init({apiKey: facebook_app_id,&lt;br /&gt;             oauth: true,&lt;br /&gt;             cookie: true});&lt;br /&gt;&lt;/pre&gt;3. Instead of response.session, the response should now be response.authResponse.  Also,&lt;br /&gt;make note that scope: should be used instead of perms:&lt;br /&gt;&lt;pre class="prettyprint"&gt;FB.login(function(response) {&lt;br /&gt;    if (response.authResponse) {&lt;br /&gt;    },&lt;br /&gt;    {scope: 'email,publish_stream,manage_pages'}&lt;br /&gt;    });&lt;br /&gt;&lt;/pre&gt;Also, if you need to retrieve the user id on the JavaScript, the value is stored as response.authResponse.userID instead of response.session.uid:&lt;br /&gt;&lt;pre class="prettyprint"&gt;FB.api(&lt;br /&gt;       { method: 'fql.query',&lt;br /&gt;        query: 'SELECT ' + permissions.join() + ' FROM permissions WHERE uid=' + response.authResponse.userID},&lt;br /&gt;        function (response) { });&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;If you see yourself not being able to logout, it means you haven't set the right APP ID or forgot to set oauth: true in both your login and logout code.  If you're going to make the change, you should make it everywhere in your code!&lt;br /&gt;&lt;br /&gt;On the Python/Django side, you need to implement a few helper routines.  If Facebook authenticates properly, a cookie with the prefix fbsr_ will be set as a cookie (instead of fbs_).  This signed request includes an encoded signature and payload, which must be separated and verified.  You can look at the PHP SDK code to understand how it's implemented, or you can review this Python version of the code (see &lt;a href="http://developers.facebook.com/docs/authentication/signed_request/"&gt;http://developers.facebook.com/docs/authentication/signed_request/&lt;/a&gt;)&lt;br /&gt;&lt;pre class="prettyprint"&gt;def parse_signed_request(signed_request, secret):&lt;br /&gt;&lt;br /&gt;    encoded_sig, payload = signed_request.split('.', 2)&lt;br /&gt;&lt;br /&gt;    sig = base64_urldecode(encoded_sig)&lt;br /&gt;    data = json.loads(base64_urldecode(payload))&lt;br /&gt;&lt;br /&gt;    if data.get('algorithm').upper() != 'HMAC-SHA256':&lt;br /&gt;        return None&lt;br /&gt;    else:&lt;br /&gt;        expected_sig = hmac.new(secret, msg=payload, digestmod=hashlib.sha256).digest()&lt;br /&gt;&lt;br /&gt;    if sig != expected_sig:&lt;br /&gt;        return None&lt;br /&gt;&lt;br /&gt;    return data&lt;br /&gt;&lt;/pre&gt;In the PHP SDK code, there is a base64_url_decode function that automatically adds the correct number of "=" characters to the end of the Base64 encoded string.  The basic problem is that Base64 encodes 3 bytes for every 4 characters, so the total length will be 4*len(string)/3.  We can use this knowledge to realize that the total length will be a multiple of 4 and then insert the appropriate number of '=' characters to the end of the string.  Facebook also appears to use a Base64-uRL variant in which the '+' and '/' characters of standard Base64 are respectively replaced by '-' and '_', which then must be replaced during the decode process (see &lt;a href="http://en.wikipedia.org/wiki/Base64#URL_applications"&gt;http://en.wikipedia.org/wiki/Base64#URL_applications&lt;/a&gt;).  The code looks like the following:&lt;br /&gt;&lt;pre class="prettyprint"&gt;def base64_urldecode(data):&lt;br /&gt;    # http://qugstart.com/blog/ruby-and-rails/facebook-base64-url-decode-for-signed_request/                       &lt;br /&gt;    # 1. Pad the encoded string with "+".                                                                          &lt;br /&gt;    # See http://fi.am/entry/urlsafe-base64-encodingdecoding-in-two-lines/                                         &lt;br /&gt;    data += "=" * (4 - (len(data) % 4) % 4)&lt;br /&gt;&lt;br /&gt;    return base64.urlsafe_b64decode(data)&lt;br /&gt;&lt;/pre&gt;If you're using the old Python SDK implementation, you may wish to implement code that mimics the way in which the Python SDK implemented &lt;a href="https://github.com/facebook/python-sdk/blob/master/src/facebook.py"&gt;get_user_from_cookie&lt;/a&gt;, since the expires, session_key, and oauth_token can be derived from retrieving the access token.  We also set an fbsr_signed parameter in case you have debugging statements in your code and want to differentiate between your old get_user_from_cookie from this &lt;a href="https://github.com/rogerhu/facebook_oauth2/blob/master/utils.py"&gt;code&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Note: in order to make things backward-compatible, you need to make an extra URL request back to Facebook to retrieve the access token.   This code was also inspired from the Facebook PHP SDK code too:&lt;br /&gt;&lt;pre class="prettyprint"&gt;def get_access_token_from_code(code, redirect_url=None):&lt;br /&gt;    """ OAuth2 code to retrieve an application access token. """&lt;br /&gt;&lt;br /&gt;    data = {&lt;br /&gt;        'client_id' : settings.FACEBOOK_APP_ID,&lt;br /&gt;        'client_secret' : settings.FACEBOOK_SECRET_KEY,&lt;br /&gt;        'code' : code,&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;    if redirect_url:&lt;br /&gt;        data['redirect_uri'] = redirect_url&lt;br /&gt;    else:&lt;br /&gt;        data['redirect_uri'] = ''&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;   return get_app_token_helper(data)&lt;br /&gt;&lt;br /&gt;BASE_LINK = "https://graph.facebook.com"&lt;br /&gt;&lt;br /&gt;def get_app_token_helper(data=None):&lt;br /&gt;   &lt;br /&gt;    if not data:&lt;br /&gt;        data = {}&lt;br /&gt;&lt;br /&gt;    try:&lt;br /&gt;        token_request = urllib.urlencode(data)&lt;br /&gt;&lt;br /&gt;        app_token = urllib2.urlopen(BASE_LINK + "/oauth/access_token?%s" % token_request).read()&lt;br /&gt;    except urllib2.HTTPError, e:&lt;br /&gt;        logging.debug("Exception trying to grab Facebook App token (%s)" % e)&lt;br /&gt;        return None&lt;br /&gt;&lt;br /&gt;    matches = re.match(r"access_token=(?P&lt;token&gt;.*)", app_token).groupdict()&lt;br /&gt;&lt;br /&gt;    return matches.get('token')&lt;br /&gt;&lt;/token&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4137806788465333774-1743962550240763748?l=hustoknow.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hustoknow.blogspot.com/feeds/1743962550240763748/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hustoknow.blogspot.com/2011/08/facebooks-oauth2-support-for-python.html#comment-form' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/1743962550240763748'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/1743962550240763748'/><link rel='alternate' type='text/html' href='http://hustoknow.blogspot.com/2011/08/facebooks-oauth2-support-for-python.html' title='Facebook&apos;s OAuth2 support for Python'/><author><name>Roger</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4137806788465333774.post-8120189344990628309</id><published>2011-08-16T03:16:00.001-07:00</published><updated>2011-08-16T03:16:36.089-07:00</updated><title type='text'>How to redirect bash time outputs..</title><content type='html'>&lt;a href="http://mywiki.wooledge.org/BashFAQ/032"&gt;http://mywiki.wooledge.org/BashFAQ/032&lt;/a&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="background-color: white; font-family: sans-serif; font-size: 24px; font-weight: bold;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="background-color: white; font-family: sans-serif; font-size: 24px; font-weight: bold;"&gt;How can I redirect the output of 'time' to a variable or file?&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="background-color: white; font-family: sans-serif; font-size: 16px;"&gt;&lt;span class="anchor" id="line-3"&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;div class="line862"&gt;Bash's&amp;nbsp;&lt;tt&gt;time&lt;/tt&gt;&amp;nbsp;keyword uses special trickery, so that you can do things like&lt;span class="anchor" id="line-4"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-5"&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="line867"&gt;&lt;span class="anchor" id="line-6"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-7"&gt;&lt;/span&gt;&lt;/div&gt;&lt;pre style="background-color: #f3f5f7; border-bottom-color: rgb(174, 189, 204); border-bottom-style: solid; border-bottom-width: 1pt; border-left-color: rgb(174, 189, 204); border-left-style: solid; border-left-width: 1pt; border-right-color: rgb(174, 189, 204); border-right-style: solid; border-right-width: 1pt; border-top-color: rgb(174, 189, 204); border-top-style: solid; border-top-width: 1pt; font-family: courier, monospace; padding-bottom: 5pt; padding-left: 5pt; padding-right: 5pt; padding-top: 5pt; white-space: pre-wrap; word-wrap: break-word;"&gt;&lt;span class="anchor" id="line-1"&gt;&lt;/span&gt;   time find ... | xargs ...&lt;/pre&gt;&lt;span class="anchor" id="line-8"&gt;&lt;/span&gt;&lt;div class="line862"&gt;and get the execution time of the entire pipeline, rather than just the simple command at the start of the pipe. (This is different from the behavior of the external command&amp;nbsp;&lt;tt&gt;time(1)&lt;/tt&gt;, for obvious reasons.)&lt;span class="anchor" id="line-9"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-10"&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="line862"&gt;Because of this, people who want to redirect&amp;nbsp;&lt;tt&gt;time&lt;/tt&gt;'s output often encounter difficulty figuring out where all the file descriptors are going. It's not as hard as most people think, though -- the trick is to call&amp;nbsp;&lt;tt&gt;time&lt;/tt&gt;&amp;nbsp;in a&amp;nbsp;&lt;a href="http://mywiki.wooledge.org/SubShell" style="border-bottom-width: 0px; border-color: initial; border-left-width: 0px; border-right-width: 0px; border-style: initial; border-top-width: 0px; color: #4477ff; text-decoration: none;"&gt;SubShell&lt;/a&gt;&amp;nbsp;or block, and then capture stderr of the subshell or block (which will contain&amp;nbsp;&lt;tt&gt;time&lt;/tt&gt;'s results). If you need to redirect the actual command's stdout or stderr, you do that inside the subshell/block. For example:&lt;span class="anchor" id="line-11"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-12"&gt;&lt;/span&gt;&lt;/div&gt;&lt;ul&gt;&lt;li&gt;File redirection:&lt;span class="anchor" id="line-13"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-14"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-15"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-16"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-17"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-18"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-19"&gt;&lt;/span&gt;&lt;span class="anchor" id="line-20"&gt;&lt;/span&gt;&lt;pre style="background-color: #f3f5f7; border-bottom-color: rgb(174, 189, 204); border-bottom-style: solid; border-bottom-width: 1pt; border-left-color: rgb(174, 189, 204); border-left-style: solid; border-left-width: 1pt; border-right-color: rgb(174, 189, 204); border-right-style: solid; border-right-width: 1pt; border-top-color: rgb(174, 189, 204); border-top-style: solid; border-top-width: 1pt; font-family: courier, monospace; padding-bottom: 5pt; padding-left: 5pt; padding-right: 5pt; padding-top: 5pt; white-space: pre-wrap; word-wrap: break-word;"&gt;&lt;span class="anchor" id="line-1-1"&gt;&lt;/span&gt;   bash -c "time ls" 2&amp;gt;time.output      # Explicit, but inefficient.&lt;br /&gt;&lt;span class="anchor" id="line-2"&gt;&lt;/span&gt;   ( time ls ) 2&amp;gt;time.output            # Slightly more efficient.&lt;br /&gt;&lt;span class="anchor" id="line-3"&gt;&lt;/span&gt;   { time ls; } 2&amp;gt;time.output           # Most efficient.&lt;br /&gt;&lt;span class="anchor" id="line-4"&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="anchor" id="line-5"&gt;&lt;/span&gt;   # The general case:&lt;br /&gt;&lt;span class="anchor" id="line-6"&gt;&lt;/span&gt;   { time some command &amp;gt;stdout 2&amp;gt;stderr; } 2&amp;gt;time.output&lt;/pre&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4137806788465333774-8120189344990628309?l=hustoknow.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hustoknow.blogspot.com/feeds/8120189344990628309/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hustoknow.blogspot.com/2011/08/how-to-redirect-bash-time-outputs.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/8120189344990628309'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/8120189344990628309'/><link rel='alternate' type='text/html' href='http://hustoknow.blogspot.com/2011/08/how-to-redirect-bash-time-outputs.html' title='How to redirect bash time outputs..'/><author><name>Roger</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4137806788465333774.post-2408414307761967293</id><published>2011-08-13T17:07:00.000-07:00</published><updated>2011-08-13T17:07:37.993-07:00</updated><title type='text'>Vizio 42" LCD TV Tivo remote control code</title><content type='html'>Tivo remote code is 0128.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.tivocommunity.com/tivo-vb/archive/index.php/t-263308.html"&gt;http://www.tivocommunity.com/tivo-vb/archive/index.php/t-263308.html&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4137806788465333774-2408414307761967293?l=hustoknow.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hustoknow.blogspot.com/feeds/2408414307761967293/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hustoknow.blogspot.com/2011/08/vizio-42-lcd-tv-tivo-remote-control.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/2408414307761967293'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/2408414307761967293'/><link rel='alternate' type='text/html' href='http://hustoknow.blogspot.com/2011/08/vizio-42-lcd-tv-tivo-remote-control.html' title='Vizio 42&quot; LCD TV Tivo remote control code'/><author><name>Roger</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4137806788465333774.post-7988169691341613269</id><published>2011-08-13T10:26:00.000-07:00</published><updated>2011-08-13T10:27:54.615-07:00</updated><title type='text'>Bash tests</title><content type='html'>&lt;a href="http://wiki.bash-hackers.org/commands/classictest"&gt;http://wiki.bash-hackers.org/commands/classictest&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4137806788465333774-7988169691341613269?l=hustoknow.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hustoknow.blogspot.com/feeds/7988169691341613269/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hustoknow.blogspot.com/2011/08/bash-tests.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/7988169691341613269'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/7988169691341613269'/><link rel='alternate' type='text/html' href='http://hustoknow.blogspot.com/2011/08/bash-tests.html' title='Bash tests'/><author><name>Roger</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4137806788465333774.post-1264563668133551449</id><published>2011-08-13T10:06:00.000-07:00</published><updated>2011-08-13T10:27:34.414-07:00</updated><title type='text'>Apache's mod_rewrite  and changing URL cases...</title><content type='html'>Suppose we want to use Apache's mod_rewrite RewriteMap:&lt;br /&gt;&lt;br /&gt;http://example.com/id to http://example.com/ID&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;RewriteMap uppercase int:toupper&lt;br /&gt;RewriteRule [a-z] %{uppercase:%{REQUEST_URI}} [L,R=301]&lt;br /&gt;&lt;/pre&gt;The list of internal functions are listed here:&lt;br /&gt;&lt;a href="http://httpd.apache.org/docs/2.0/mod/mod_rewrite.html#mapfunc"&gt;http://httpd.apache.org/docs/2.0/mod/mod_rewrite.html#mapfunc&lt;/a&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;Internal Function&lt;br /&gt;MapType: int, MapSource: Internal Apache function&lt;br /&gt;Here, the source is an internal Apache function. Currently you cannot create your own, but the following functions already exist:&lt;br /&gt;&lt;br /&gt;toupper:&lt;br /&gt;Converts the key to all upper case.&lt;br /&gt;tolower:&lt;br /&gt;Converts the key to all lower case.&lt;br /&gt;escape:&lt;br /&gt;Translates special characters in the key to hex-encodings.&lt;br /&gt;unescape:&lt;br /&gt;Translates hex-encodings in the key back to special characters.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;You can also use this approach to map static content to a list of servers:&lt;br /&gt;&lt;pre class="prettyprint"&gt;Example:&lt;br /&gt;&lt;br /&gt;Rewrite map file&lt;br /&gt;&lt;br /&gt;##&lt;br /&gt;##  map.txt -- rewriting map&lt;br /&gt;##&lt;br /&gt;&lt;br /&gt;static   www1|www2|www3|www4&lt;br /&gt;dynamic  www5|www6&lt;br /&gt;Configuration directives&lt;br /&gt;&lt;br /&gt;RewriteMap servers rnd:/path/to/file/map.txt&lt;br /&gt;&lt;br /&gt;RewriteRule ^/(.*\.(png|gif|jpg)) http://${servers:static}/$1 [NC,P,L]&lt;br /&gt;RewriteRule ^/(.*) http://${servers:dynamic}/$1 [P,L]&lt;br /&gt;Hash File&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4137806788465333774-1264563668133551449?l=hustoknow.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hustoknow.blogspot.com/feeds/1264563668133551449/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hustoknow.blogspot.com/2011/08/apaches-modrewrite-and-changing-url.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/1264563668133551449'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/1264563668133551449'/><link rel='alternate' type='text/html' href='http://hustoknow.blogspot.com/2011/08/apaches-modrewrite-and-changing-url.html' title='Apache&apos;s mod_rewrite  and changing URL cases...'/><author><name>Roger</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4137806788465333774.post-2505137958111767617</id><published>2011-08-09T10:35:00.000-07:00</published><updated>2011-08-09T10:36:14.479-07:00</updated><title type='text'>Celery v2.3.1</title><content type='html'>Celery v2.3.0 has a new setting for the &lt;a href="http://docs.celeryproject.org/en/latest/changelog.html#version-2-3-0"&gt;CELERY_RESULT_BACKEND&lt;/a&gt; that allows you to store the results of your apply_async() and dispatch() calls in something other than an AMQP-based backend.  In previous versions Celery (without the ignore_result=True) would store these results as a message created by a separate queue corresponding to the taskset ID (a UUID).  If you had a lot of tasks without consuming them (i.e. checking he result), you would eventually exhausting the memory usage.  &lt;br /&gt;&lt;br /&gt;The problem is well-described here.  One of the issues was using an older version of RabbitMQ, which used a different persister that would try to keep everything in memory and would crash.  With recent changes in RabbitMQ, which allow task results to be expired, the problem is much more mitigated.  Nonetheless, setting ignore_result=True also helps with this respect.  With the recent Celery v2.3.0 release you can also use a different backend (i.e. Redis) to store these task set results!&lt;br /&gt;&lt;br /&gt;&lt;a href="http://packages.python.org/celery/userguide/tasks.html#result-backends"&gt;http://packages.python.org/celery/userguide/tasks.html#result-backends&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Note: Celery is still highly dependent on an AMQP host.  Just because you can change the CELERY_RESULT_BACKEND doesn't mean you can use a completely different messaging system.   &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4137806788465333774-2505137958111767617?l=hustoknow.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hustoknow.blogspot.com/feeds/2505137958111767617/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hustoknow.blogspot.com/2011/08/celery-v231.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/2505137958111767617'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/2505137958111767617'/><link rel='alternate' type='text/html' href='http://hustoknow.blogspot.com/2011/08/celery-v231.html' title='Celery v2.3.1'/><author><name>Roger</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4137806788465333774.post-8300977690448707989</id><published>2011-08-09T03:02:00.000-07:00</published><updated>2011-08-09T03:02:46.842-07:00</updated><title type='text'>Using find with --regex option</title><content type='html'>&lt;a href="http://www.gnu.org/software/findutils/manual/html_mono/find.html#posix_002degrep-regular-expression-syntax"&gt;http://www.gnu.org/software/findutils/manual/html_mono/find.html#posix_002degrep-regular-expression-syntax&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Note that you need to specify .*/ in the beginning because find matches the whole path.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://stackoverflow.com/questions/6844785/how-to-use-regex-with-find-command"&gt;http://stackoverflow.com/questions/6844785/how-to-use-regex-with-find-command&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4137806788465333774-8300977690448707989?l=hustoknow.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hustoknow.blogspot.com/feeds/8300977690448707989/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hustoknow.blogspot.com/2011/08/using-find-with-regex-option.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/8300977690448707989'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/8300977690448707989'/><link rel='alternate' type='text/html' href='http://hustoknow.blogspot.com/2011/08/using-find-with-regex-option.html' title='Using find with --regex option'/><author><name>Roger</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4137806788465333774.post-7288758897475513055</id><published>2011-08-07T23:36:00.000-07:00</published><updated>2011-08-07T23:36:21.699-07:00</updated><title type='text'>Experimenting with the Kombu framework</title><content type='html'>The Kombu framework is an excellent way to work with RabbitMQ/AMQP message brokers. &amp;nbsp; The documentation has several different ways of doing so by instantiating the exchange, queue, and then the RabbitMQ (AMQP broker) connection (See &lt;a href="http://packages.python.org/kombu/introduction.html"&gt;http://packages.python.org/kombu/introduction.html&lt;/a&gt;).  The documentation at &lt;a href="http://blogs.digitar.com/jjww/2009/01/rabbits-and-warrens/"&gt;http://blogs.digitar.com/jjww/2009/01/rabbits-and-warrens/&lt;/a&gt; shows how you can use the py-amqplib to talk to RabbitMQ hosts, but the Kombu framework with its ability to support multiple back-ends provides a much more elegant approach.&lt;br /&gt; &lt;br /&gt;Here's a simple code that we can use to talk to our queue if we also have our Celery configuration settings defined too.  In this example, we only use the Queue class to consume messages.   We can bind the default queue and exchange to the channel and then register a callback that will dump the message to stdout.&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;from celery.conf import settings&lt;br /&gt;from kombu.connection import BrokerConnection&lt;br /&gt;from kombu.messaging import Exchange, Queue, Consumer&lt;br /&gt;&lt;br /&gt;connection = BrokerConnection(settings.BROKER_HOST, settings.BROKER_USER, settings.BROKER_PASSWORD, settings.BROKER_VHOST)&lt;br /&gt;&lt;br /&gt;# RabbitMQ connection&lt;br /&gt;channel = connection.channel()&lt;br /&gt;&lt;br /&gt;default_exchange = Exchange("default", "direct", durable=True)&lt;br /&gt;default_queue = Queue("default", exchange=default_exchange, key="default")&lt;br /&gt;bound_default_queue = default_queue(channel)&lt;br /&gt;&lt;br /&gt;def process_msg(msg):&lt;br /&gt;    print "%s" % repr(msg)&lt;br /&gt;&lt;br /&gt;bound_default_queue.consume(callback=process_msg)&lt;br /&gt;&lt;br /&gt;while True:&lt;br /&gt;    connection.drain_events()&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;We can also do the same by declaring a Consumer class too and calling the consume() to register the Consumer:&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;&lt;br /&gt;from celery.conf import settings&lt;br /&gt;from kombu.connection import BrokerConnection&lt;br /&gt;from kombu.messaging import Exchange, Queue, Consumer&lt;br /&gt;&lt;br /&gt;connection = BrokerConnection(settings.BROKER_HOST, settings.BROKER_USER, settings.BROKER_PASSWORD, settings.BROKER_VHOST)&lt;br /&gt;&lt;br /&gt;# RabbitMQ connection                                                                                                                                                                                                                          &lt;br /&gt;channel = connection.channel()&lt;br /&gt;&lt;br /&gt;default_exchange = Exchange("default", "direct", durable=True)&lt;br /&gt;&lt;br /&gt;default_queue = Queue("default", exchange=default_exchange, key="default")&lt;br /&gt;&lt;br /&gt;def process_msg(body, msg):&lt;br /&gt;    print "body %s, msg %s" % (repr(body), repr(msg))&lt;br /&gt;&lt;br /&gt;consumer = Consumer(channel, default_queue, callbacks=[process_msg])&lt;br /&gt;consumer.consume()&lt;br /&gt;&lt;br /&gt;while True:&lt;br /&gt;    connection.drain_events()&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4137806788465333774-7288758897475513055?l=hustoknow.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hustoknow.blogspot.com/feeds/7288758897475513055/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hustoknow.blogspot.com/2011/08/experimenting-with-kombu-framework.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/7288758897475513055'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/7288758897475513055'/><link rel='alternate' type='text/html' href='http://hustoknow.blogspot.com/2011/08/experimenting-with-kombu-framework.html' title='Experimenting with the Kombu framework'/><author><name>Roger</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4137806788465333774.post-6437051839254899288</id><published>2011-08-07T00:28:00.000-07:00</published><updated>2011-08-07T22:23:58.871-07:00</updated><title type='text'>How scheduled tasks and Celery and Kombu interfaces with RabbitMQ</title><content type='html'>A really good overview of how PBS Education Technology uses Celery:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.slideshare.net/tarequeh/life-in-a-queue-using-message-queue-with-django"&gt;http://www.slideshare.net/tarequeh/life-in-a-queue-using-message-queue-with-django&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;If you want to learn the AMQP interface and try building consumers, producers, exchanges, queues, the Python code here is extremely useful to learning how the basic standard works with the amqplib, which is the basis on which Celery is built. &amp;nbsp;Celery handles a lot of the higher-level functionality of building task queues, but the underlying plumbing is built on the &lt;a href="http://ask.github.com/kombu/"&gt;Kombu&lt;/a&gt; framework (originally Carrot but completely rewritten):&lt;br /&gt;&lt;br /&gt;&lt;a href="http://blogs.digitar.com/jjww/2009/01/rabbits-and-warrens/"&gt;http://blogs.digitar.com/jjww/2009/01/rabbits-and-warrens/&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;The internals of how scheduled tasks are outlined in the Celery documentation:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://ask.github.com/celery/internals/worker.html"&gt;http://ask.github.com/celery/internals/worker.html&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;When you create a scheduled task in Celery (using either the eta= or countdown= parameter), a message gets created that gets pickled (assuming you're using the default &lt;a href="http://docs.celeryproject.org/en/latest/configuration.html?highlight=serializer#CELERY_TASK_SERIALIZER"&gt;CELERY_TASK_SERIALIZER&lt;/a&gt; as pickle) that includes an 'eta' keyword:&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;print pickle.loads(msg.body).keys()&lt;br /&gt;['retries', 'task', 'args', 'expires', 'eta', 'kwargs', 'id']&lt;br /&gt;(Pdb) print pickle.loads(msg.body)['eta']&lt;br /&gt;2012-08-02T17:03:00.593141&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;When you startup a Celery worker, it will create a Consumer that will begin to receive messages from the RabbitMQ broker.  If it sees a message with an eta parameter, then the task is moved into an ETA scheduler queue instead of the ready queue.  The message itself is not acknowledged until the task is executed, though since it is received by the Consumer, any other workers will not receive the same task.  If the Consumer disconnects or loses connection from the RabbitMQ worker, then RabbitMQ will attempt to redeliver this message.  The message itself will not be deleted until an acknowledge is sent back to the broker:&lt;br /&gt;&lt;br /&gt;kombu/transport.py:&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;def ack(self):&lt;br /&gt;       """Acknowledge this message as being processed.,                                     &lt;br /&gt;       This will remove the message from the queue.                                         &lt;br /&gt;                                                                                            &lt;br /&gt;       :raises MessageStateError: If the message has already been                           &lt;br /&gt;           acknowledged/requeued/rejected.                                                  &lt;br /&gt;                                                                                            &lt;br /&gt;       """&lt;br /&gt;       if self.channel.no_ack_consumers is not None:&lt;br /&gt;           consumer_tag = self.delivery_info["consumer_tag"]&lt;br /&gt;           if consumer_tag in self.channel.no_ack_consumers:&lt;br /&gt;               return&lt;br /&gt;       if self.acknowledged:&lt;br /&gt;           raise self.MessageStateError(&lt;br /&gt;               "Message already acknowledged with state: %s" % self._state)&lt;br /&gt;       self.channel.basic_ack(self.delivery_tag)&lt;br /&gt;       self._state = "ACK"&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;When a message is received by Celery, it invokes a function called from_message(), which then passes on to the on_task() to insert into the queue.   Notice that the 'ack' function, which will be used to acknowledge a message once it has been processed, is passed along to this routine. &lt;br /&gt;&lt;br /&gt;celery/worker/listener.py:&lt;br /&gt;&lt;pre class="prettyprint"&gt;task = TaskRequest.from_message(message, message_data, ack,&lt;br /&gt;                                                logger=self.logger,&lt;br /&gt;                                                hostname=self.hostname,&lt;br /&gt;&lt;/pre&gt;The ready queue itself (assuming no rate limits) uses a basic Python queue and uses the process_task function, which will then call self.acknowledge() and invoke the ack function that was passed into the initial TaskRequest.from_message creation.&lt;br /&gt;celery/worker/__init.py:&lt;br /&gt;&lt;pre class="prettyprint"&gt;if disable_rate_limits:&lt;br /&gt;            self.ready_queue = FastQueue()&lt;br /&gt;            self.ready_queue.put = self.process_task&lt;br /&gt;&lt;/pre&gt;Also, it appears that revoked tasks are not persistent if you do not setup a &lt;a href="http://celery.readthedocs.org/en/latest/configuration.html"&gt;CELERYD_STATE_DB&lt;/a&gt; (defaults to None).  Celery appears to keep all revoked tasks in memory and skips tasks if they are in this list of revoked task ID's without this setting.  Without this configuration variable, all revoked tasks will be forgotten if you restart Celery.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4137806788465333774-6437051839254899288?l=hustoknow.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hustoknow.blogspot.com/feeds/6437051839254899288/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hustoknow.blogspot.com/2011/08/how-scheduled-tasks-and-celery-and.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/6437051839254899288'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/6437051839254899288'/><link rel='alternate' type='text/html' href='http://hustoknow.blogspot.com/2011/08/how-scheduled-tasks-and-celery-and.html' title='How scheduled tasks and Celery and Kombu interfaces with RabbitMQ'/><author><name>Roger</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4137806788465333774.post-7725474034842335479</id><published>2011-08-05T08:16:00.000-07:00</published><updated>2011-08-05T08:33:29.355-07:00</updated><title type='text'>Changes in Facebook's SWF code.</title><content type='html'>Yesterday evening (8/4/2011) at 9:55 pm, Facebook changed some code that affects its Flash code which is used by Internet Explorer to handle cross-domain communication:&lt;br /&gt;&lt;br /&gt;&lt;table style="border-collapse: collapse; font-family: arial, sans-serif; font-size: 11px; width: 1130px;"&gt;&lt;tbody&gt;&lt;tr bgcolor="#fdd" style="background-color: #ffdddd; font-size: 11px;"&gt;&lt;td align="right" bgcolor="#ccc" style="background-color: #cccccc; color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; text-align: right; width: 23px;"&gt;1&lt;/td&gt;&lt;td align="right" bgcolor="#ccc" style="background-color: #cccccc; color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; text-align: right; width: 23px;"&gt;&lt;/td&gt;&lt;td style="color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&amp;nbsp;/*&lt;span style="background-color: #ffaaaa; font-size: 11px;"&gt;1312412724&lt;/span&gt;,&lt;span style="background-color: #ffaaaa; font-size: 11px;"&gt;169546110&lt;/span&gt;,JIT&amp;nbsp;&lt;wbr&gt;&lt;/wbr&gt;Construction:&amp;nbsp;&lt;span style="background-color: #ffaaaa; font-size: 11px;"&gt;v416050&lt;/span&gt;,en_US*/&lt;/td&gt;&lt;/tr&gt;&lt;tr bgcolor="#dfd" style="background-color: #ddffdd; font-size: 11px;"&gt;&lt;td align="right" bgcolor="#ccc" style="background-color: #cccccc; color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; text-align: right; width: 23px;"&gt;&lt;/td&gt;&lt;td align="right" bgcolor="#ccc" style="background-color: #cccccc; color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; text-align: right; width: 23px;"&gt;1&lt;/td&gt;&lt;td style="color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&amp;nbsp;/*&lt;span style="background-color: #aaffaa; font-size: 11px;"&gt;1312520159&lt;/span&gt;,&lt;span style="background-color: #aaffaa; font-size: 11px;"&gt;169918336&lt;/span&gt;,JIT&amp;nbsp;&lt;wbr&gt;&lt;/wbr&gt;Construction:&amp;nbsp;&lt;span style="background-color: #aaffaa; font-size: 11px;"&gt;v416929&lt;/span&gt;,en_US*/&lt;/td&gt;&lt;/tr&gt;&lt;tr style="font-size: 11px;"&gt;&lt;td align="right" bgcolor="#ccc" style="background-color: #cccccc; color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; text-align: right; width: 23px;"&gt;2&lt;/td&gt;&lt;td align="right" bgcolor="#ccc" style="background-color: #cccccc; color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; text-align: right; width: 23px;"&gt;2&lt;/td&gt;&lt;td style="color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr style="font-size: 11px;"&gt;&lt;td align="right" bgcolor="#ccc" style="background-color: #cccccc; color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; text-align: right; width: 23px;"&gt;3&lt;/td&gt;&lt;td align="right" bgcolor="#ccc" style="background-color: #cccccc; color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; text-align: right; width: 23px;"&gt;3&lt;/td&gt;&lt;td style="color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&amp;nbsp;if&amp;nbsp;(!window.FB)&amp;nbsp;window.FB&amp;nbsp;=&amp;nbsp;{&lt;/td&gt;&lt;/tr&gt;&lt;tr style="font-size: 11px;"&gt;&lt;td align="right" bgcolor="#ccc" style="background-color: #cccccc; color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; text-align: right; width: 23px;"&gt;4&lt;/td&gt;&lt;td align="right" bgcolor="#ccc" style="background-color: #cccccc; color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; text-align: right; width: 23px;"&gt;4&lt;/td&gt;&lt;td style="color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;_apiKey:&amp;nbsp;null,&lt;/td&gt;&lt;/tr&gt;&lt;tr style="font-size: 11px;"&gt;&lt;td align="center" colspan="3" style="border-bottom-color: rgb(105, 105, 105); border-bottom-style: solid; border-bottom-width: 1px; border-top-color: rgb(105, 105, 105); border-top-style: solid; border-top-width: 1px; color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; text-align: center;" title="Unchanged content skipped between diff. blocks"&gt;…&lt;/td&gt;&lt;/tr&gt;&lt;tr style="font-size: 11px;"&gt;&lt;td align="right" bgcolor="#ccc" style="background-color: #cccccc; color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; text-align: right; width: 23px;"&gt;30&lt;/td&gt;&lt;td align="right" bgcolor="#ccc" style="background-color: #cccccc; color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; text-align: right; width: 23px;"&gt;30&lt;/td&gt;&lt;td style="color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;FB._&lt;wbr&gt;&lt;/wbr&gt;domain.api_read;&lt;/td&gt;&lt;/tr&gt;&lt;tr style="font-size: 11px;"&gt;&lt;td align="right" bgcolor="#ccc" style="background-color: #cccccc; color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; text-align: right; width: 23px;"&gt;31&lt;/td&gt;&lt;td align="right" bgcolor="#ccc" style="background-color: #cccccc; color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; text-align: right; width: 23px;"&gt;31&lt;/td&gt;&lt;td style="color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;case&amp;nbsp;'cdn':&lt;/td&gt;&lt;/tr&gt;&lt;tr style="font-size: 11px;"&gt;&lt;td align="right" bgcolor="#ccc" style="background-color: #cccccc; color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; text-align: right; width: 23px;"&gt;32&lt;/td&gt;&lt;td align="right" bgcolor="#ccc" style="background-color: #cccccc; color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; text-align: right; width: 23px;"&gt;32&lt;/td&gt;&lt;td style="color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;(window.&lt;wbr&gt;&lt;/wbr&gt;location.protocol&amp;nbsp;==&amp;nbsp;'https:'&amp;nbsp;&lt;wbr&gt;&lt;/wbr&gt;||&amp;nbsp;FB._https)&amp;nbsp;?&amp;nbsp;FB._domain.&lt;wbr&gt;&lt;/wbr&gt;https_cdn&amp;nbsp;:&amp;nbsp;FB._domain.cdn;&lt;/td&gt;&lt;/tr&gt;&lt;tr bgcolor="#dfd" style="background-color: #ddffdd; font-size: 11px;"&gt;&lt;td align="right" bgcolor="#ccc" style="background-color: #cccccc; color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; text-align: right; width: 23px;"&gt;&lt;/td&gt;&lt;td align="right" bgcolor="#ccc" style="background-color: #cccccc; color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; text-align: right; width: 23px;"&gt;33&lt;/td&gt;&lt;td style="color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;case&amp;nbsp;'cdn_foreign':&lt;/td&gt;&lt;/tr&gt;&lt;tr bgcolor="#dfd" style="background-color: #ddffdd; font-size: 11px;"&gt;&lt;td align="right" bgcolor="#ccc" style="background-color: #cccccc; color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; text-align: right; width: 23px;"&gt;&lt;/td&gt;&lt;td align="right" bgcolor="#ccc" style="background-color: #cccccc; color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; text-align: right; width: 23px;"&gt;34&lt;/td&gt;&lt;td style="color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;FB._&lt;wbr&gt;&lt;/wbr&gt;domain.cdn_foreign;&lt;/td&gt;&lt;/tr&gt;&lt;tr style="font-size: 11px;"&gt;&lt;td align="right" bgcolor="#ccc" style="background-color: #cccccc; color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; text-align: right; width: 23px;"&gt;33&lt;/td&gt;&lt;td align="right" bgcolor="#ccc" style="background-color: #cccccc; color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; text-align: right; width: 23px;"&gt;35&lt;/td&gt;&lt;td style="color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;case&amp;nbsp;'https_cdn':&lt;/td&gt;&lt;/tr&gt;&lt;tr style="font-size: 11px;"&gt;&lt;td align="right" bgcolor="#ccc" style="background-color: #cccccc; color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; text-align: right; width: 23px;"&gt;34&lt;/td&gt;&lt;td align="right" bgcolor="#ccc" style="background-color: #cccccc; color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; text-align: right; width: 23px;"&gt;36&lt;/td&gt;&lt;td style="color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;FB._&lt;wbr&gt;&lt;/wbr&gt;domain.https_cdn;&lt;/td&gt;&lt;/tr&gt;&lt;tr style="font-size: 11px;"&gt;&lt;td align="right" bgcolor="#ccc" style="background-color: #cccccc; color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; text-align: right; width: 23px;"&gt;35&lt;/td&gt;&lt;td align="right" bgcolor="#ccc" style="background-color: #cccccc; color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; text-align: right; width: 23px;"&gt;37&lt;/td&gt;&lt;td style="color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;case&amp;nbsp;'graph':&lt;/td&gt;&lt;/tr&gt;&lt;tr style="font-size: 11px;"&gt;&lt;td align="center" colspan="3" style="border-bottom-color: rgb(105, 105, 105); border-bottom-style: solid; border-bottom-width: 1px; border-top-color: rgb(105, 105, 105); border-top-style: solid; border-top-width: 1px; color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; text-align: center;" title="Unchanged content skipped between diff. blocks"&gt;…&lt;/td&gt;&lt;/tr&gt;&lt;tr style="font-size: 11px;"&gt;&lt;td align="right" bgcolor="#ccc" style="background-color: #cccccc; color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; text-align: right; width: 23px;"&gt;246&lt;/td&gt;&lt;td align="right" bgcolor="#ccc" style="background-color: #cccccc; color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; text-align: right; width: 23px;"&gt;248&lt;/td&gt;&lt;td style="color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for&amp;nbsp;(var&amp;nbsp;a&amp;nbsp;=&amp;nbsp;0,&amp;nbsp;&lt;wbr&gt;&lt;/wbr&gt;b&amp;nbsp;=&amp;nbsp;FB.Flash._callbacks.&lt;wbr&gt;&lt;/wbr&gt;length;&amp;nbsp;a&amp;nbsp;&amp;lt;&amp;nbsp;b;&amp;nbsp;a++)&amp;nbsp;FB.Flash._&lt;wbr&gt;&lt;/wbr&gt;callbacks[a]();&lt;/td&gt;&lt;/tr&gt;&lt;tr style="font-size: 11px;"&gt;&lt;td align="right" bgcolor="#ccc" style="background-color: #cccccc; color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; text-align: right; width: 23px;"&gt;247&lt;/td&gt;&lt;td align="right" bgcolor="#ccc" style="background-color: #cccccc; color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; text-align: right; width: 23px;"&gt;249&lt;/td&gt;&lt;td style="color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;FB.Flash._&lt;wbr&gt;&lt;/wbr&gt;callbacks&amp;nbsp;=&amp;nbsp;[];&lt;/td&gt;&lt;/tr&gt;&lt;tr style="font-size: 11px;"&gt;&lt;td align="right" bgcolor="#ccc" style="background-color: #cccccc; color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; text-align: right; width: 23px;"&gt;248&lt;/td&gt;&lt;td align="right" bgcolor="#ccc" style="background-color: #cccccc; color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; text-align: right; width: 23px;"&gt;250&lt;/td&gt;&lt;td style="color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;};&lt;/td&gt;&lt;/tr&gt;&lt;tr bgcolor="#fdd" style="background-color: #ffdddd; font-size: 11px;"&gt;&lt;td align="right" bgcolor="#ccc" style="background-color: #cccccc; color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; text-align: right; width: 23px;"&gt;249&lt;/td&gt;&lt;td align="right" bgcolor="#ccc" style="background-color: #cccccc; color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; text-align: right; width: 23px;"&gt;&lt;/td&gt;&lt;td style="color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;FB.Flash.embedSWF('&lt;wbr&gt;&lt;/wbr&gt;XdComm',&amp;nbsp;FB.getDomain('cdn')&amp;nbsp;+&lt;wbr&gt;&lt;/wbr&gt;&amp;nbsp;FB.Flash._swfPath);&lt;/td&gt;&lt;/tr&gt;&lt;tr bgcolor="#dfd" style="background-color: #ddffdd; font-size: 11px;"&gt;&lt;td align="right" bgcolor="#ccc" style="background-color: #cccccc; color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; text-align: right; width: 23px;"&gt;&lt;/td&gt;&lt;td align="right" bgcolor="#ccc" style="background-color: #cccccc; color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; text-align: right; width: 23px;"&gt;251&lt;/td&gt;&lt;td style="color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;FB.Flash.embedSWF('&lt;wbr&gt;&lt;/wbr&gt;XdComm',&amp;nbsp;FB.getDomain('cdn&lt;span style="background-color: #aaffaa; font-size: 11px;"&gt;_&lt;wbr&gt;&lt;/wbr&gt;foreign&lt;/span&gt;')&amp;nbsp;+&amp;nbsp;FB.Flash._swfPath)&lt;wbr&gt;&lt;/wbr&gt;;&lt;/td&gt;&lt;/tr&gt;&lt;tr style="font-size: 11px;"&gt;&lt;td align="right" bgcolor="#ccc" style="background-color: #cccccc; color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; text-align: right; width: 23px;"&gt;250&lt;/td&gt;&lt;td align="right" bgcolor="#ccc" style="background-color: #cccccc; color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; text-align: right; width: 23px;"&gt;252&lt;/td&gt;&lt;td style="color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;},&lt;/td&gt;&lt;/tr&gt;&lt;tr style="font-size: 11px;"&gt;&lt;td align="right" bgcolor="#ccc" style="background-color: #cccccc; color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; text-align: right; width: 23px;"&gt;251&lt;/td&gt;&lt;td align="right" bgcolor="#ccc" style="background-color: #cccccc; color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; text-align: right; width: 23px;"&gt;253&lt;/td&gt;&lt;td style="color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;embedSWF:&amp;nbsp;function(d,&amp;nbsp;e,&amp;nbsp;&lt;wbr&gt;&lt;/wbr&gt;b)&amp;nbsp;{&lt;/td&gt;&lt;/tr&gt;&lt;tr style="font-size: 11px;"&gt;&lt;td align="right" bgcolor="#ccc" style="background-color: #cccccc; color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; text-align: right; width: 23px;"&gt;252&lt;/td&gt;&lt;td align="right" bgcolor="#ccc" style="background-color: #cccccc; color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; text-align: right; width: 23px;"&gt;254&lt;/td&gt;&lt;td style="color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var&amp;nbsp;a&amp;nbsp;=&amp;nbsp;!!&amp;nbsp;document.&lt;wbr&gt;&lt;/wbr&gt;attachEvent,&lt;/td&gt;&lt;/tr&gt;&lt;tr style="font-size: 11px;"&gt;&lt;td align="center" colspan="3" style="border-bottom-color: rgb(105, 105, 105); border-bottom-style: solid; border-bottom-width: 1px; border-top-color: rgb(105, 105, 105); border-top-style: solid; border-top-width: 1px; color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; text-align: center;" title="Unchanged content skipped between diff. blocks"&gt;…&lt;/td&gt;&lt;/tr&gt;&lt;tr style="font-size: 11px;"&gt;&lt;td align="right" bgcolor="#ccc" style="background-color: #cccccc; color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; text-align: right; width: 23px;"&gt;4952&lt;/td&gt;&lt;td align="right" bgcolor="#ccc" style="background-color: #cccccc; color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; text-align: right; width: 23px;"&gt;4954&lt;/td&gt;&lt;td style="color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;"api":&amp;nbsp;"https:\/\/&lt;a href="http://api.facebook.com/" style="color: #0000cc;" target="_blank"&gt;api&lt;wbr&gt;&lt;/wbr&gt;.facebook.com&lt;/a&gt;\/",&lt;/td&gt;&lt;/tr&gt;&lt;tr style="font-size: 11px;"&gt;&lt;td align="right" bgcolor="#ccc" style="background-color: #cccccc; color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; text-align: right; width: 23px;"&gt;4953&lt;/td&gt;&lt;td align="right" bgcolor="#ccc" style="background-color: #cccccc; color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; text-align: right; width: 23px;"&gt;4955&lt;/td&gt;&lt;td style="color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;"api_read":&amp;nbsp;"https:\/&lt;wbr&gt;&lt;/wbr&gt;\/&lt;a href="http://api-read.facebook.com/" style="color: #0000cc;" target="_blank"&gt;api-read.facebook.com&lt;/a&gt;\/",&lt;/td&gt;&lt;/tr&gt;&lt;tr style="font-size: 11px;"&gt;&lt;td align="right" bgcolor="#ccc" style="background-color: #cccccc; color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; text-align: right; width: 23px;"&gt;4954&lt;/td&gt;&lt;td align="right" bgcolor="#ccc" style="background-color: #cccccc; color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; text-align: right; width: 23px;"&gt;4956&lt;/td&gt;&lt;td style="color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;"cdn":&amp;nbsp;"https:\/\/&lt;a href="http://s-static.ak.fbcdn.net/" style="color: #0000cc;" target="_blank"&gt;s-&lt;wbr&gt;&lt;/wbr&gt;static.ak.fbcdn.net&lt;/a&gt;\/",&lt;/td&gt;&lt;/tr&gt;&lt;tr bgcolor="#dfd" style="background-color: #ddffdd; font-size: 11px;"&gt;&lt;td align="right" bgcolor="#ccc" style="background-color: #cccccc; color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; text-align: right; width: 23px;"&gt;&lt;/td&gt;&lt;td align="right" bgcolor="#ccc" style="background-color: #cccccc; color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; text-align: right; width: 23px;"&gt;4957&lt;/td&gt;&lt;td style="color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;"cdn_foreign":&amp;nbsp;"&lt;wbr&gt;&lt;/wbr&gt;https:\/\/&lt;a href="http://connect.facebook.net/" style="color: #0000cc;" target="_blank"&gt;connect.facebook.net&lt;/a&gt;&lt;wbr&gt;&lt;/wbr&gt;\/",&lt;/td&gt;&lt;/tr&gt;&lt;tr style="font-size: 11px;"&gt;&lt;td align="right" bgcolor="#ccc" style="background-color: #cccccc; color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; text-align: right; width: 23px;"&gt;4955&lt;/td&gt;&lt;td align="right" bgcolor="#ccc" style="background-color: #cccccc; color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; text-align: right; width: 23px;"&gt;4958&lt;/td&gt;&lt;td style="color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;"graph":&amp;nbsp;"https:\/\/&lt;a href="http://graph.facebook.com/" style="color: #0000cc;" target="_blank"&gt;g&lt;wbr&gt;&lt;/wbr&gt;raph.facebook.com&lt;/a&gt;\/",&lt;/td&gt;&lt;/tr&gt;&lt;tr style="font-size: 11px;"&gt;&lt;td align="right" bgcolor="#ccc" style="background-color: #cccccc; color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; text-align: right; width: 23px;"&gt;4956&lt;/td&gt;&lt;td align="right" bgcolor="#ccc" style="background-color: #cccccc; color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; text-align: right; width: 23px;"&gt;4959&lt;/td&gt;&lt;td style="color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;"https_cdn":&amp;nbsp;"https:\&lt;wbr&gt;&lt;/wbr&gt;/\/&lt;a href="http://s-static.ak.fbcdn.net/" style="color: #0000cc;" target="_blank"&gt;s-static.ak.fbcdn.net&lt;/a&gt;\/",&lt;/td&gt;&lt;/tr&gt;&lt;tr style="font-size: 11px;"&gt;&lt;td align="right" bgcolor="#ccc" style="background-color: #cccccc; color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; text-align: right; width: 23px;"&gt;4957&lt;/td&gt;&lt;td align="right" bgcolor="#ccc" style="background-color: #cccccc; color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; text-align: right; width: 23px;"&gt;4960&lt;/td&gt;&lt;td style="color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;"https_staticfb":&amp;nbsp;"&lt;wbr&gt;&lt;/wbr&gt;https:\/\/&lt;a href="http://s-static.ak.facebook.com/" style="color: #0000cc;" target="_blank"&gt;s-static.ak.&lt;wbr&gt;&lt;/wbr&gt;facebook.com&lt;/a&gt;\/",&lt;/td&gt;&lt;/tr&gt;&lt;tr style="font-size: 11px;"&gt;&lt;td align="center" colspan="3" style="border-bottom-color: rgb(105, 105, 105); border-bottom-style: solid; border-bottom-width: 1px; border-top-color: rgb(105, 105, 105); border-top-style: solid; border-top-width: 1px; color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; text-align: center;" title="Unchanged content skipped between diff. blocks"&gt;…&lt;/td&gt;&lt;/tr&gt;&lt;tr style="font-size: 11px;"&gt;&lt;td align="right" bgcolor="#ccc" style="background-color: #cccccc; color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; text-align: right; width: 23px;"&gt;4968&lt;/td&gt;&lt;td align="right" bgcolor="#ccc" style="background-color: #cccccc; color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; text-align: right; width: 23px;"&gt;4971&lt;/td&gt;&lt;td style="color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;"_minVersions":&amp;nbsp;[&lt;/td&gt;&lt;/tr&gt;&lt;tr style="font-size: 11px;"&gt;&lt;td align="right" bgcolor="#ccc" style="background-color: #cccccc; color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; text-align: right; width: 23px;"&gt;4969&lt;/td&gt;&lt;td align="right" bgcolor="#ccc" style="background-color: #cccccc; color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; text-align: right; width: 23px;"&gt;4972&lt;/td&gt;&lt;td style="color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[10,&amp;nbsp;0,&amp;nbsp;22,&amp;nbsp;87]&lt;/td&gt;&lt;/tr&gt;&lt;tr style="font-size: 11px;"&gt;&lt;td align="right" bgcolor="#ccc" style="background-color: #cccccc; color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; text-align: right; width: 23px;"&gt;4970&lt;/td&gt;&lt;td align="right" bgcolor="#ccc" style="background-color: #cccccc; color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; text-align: right; width: 23px;"&gt;4973&lt;/td&gt;&lt;td style="color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;],&lt;/td&gt;&lt;/tr&gt;&lt;tr bgcolor="#fdd" style="background-color: #ffdddd; font-size: 11px;"&gt;&lt;td align="right" bgcolor="#ccc" style="background-color: #cccccc; color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; text-align: right; width: 23px;"&gt;4971&lt;/td&gt;&lt;td align="right" bgcolor="#ccc" style="background-color: #cccccc; color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; text-align: right; width: 23px;"&gt;&lt;/td&gt;&lt;td style="color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;"_swfPath":&amp;nbsp;"rsrc.php\/&lt;wbr&gt;&lt;/wbr&gt;v1\/&lt;span style="background-color: #ffaaaa; font-size: 11px;"&gt;yx&lt;/span&gt;\/r\/&lt;span style="background-color: #ffaaaa; font-size: 11px;"&gt;WFg56j28XFs&lt;/span&gt;.swf"&lt;/td&gt;&lt;/tr&gt;&lt;tr bgcolor="#dfd" style="background-color: #ddffdd; font-size: 11px;"&gt;&lt;td align="right" bgcolor="#ccc" style="background-color: #cccccc; color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; text-align: right; width: 23px;"&gt;&lt;/td&gt;&lt;td align="right" bgcolor="#ccc" style="background-color: #cccccc; color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; text-align: right; width: 23px;"&gt;4974&lt;/td&gt;&lt;td style="color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;"_swfPath":&amp;nbsp;"rsrc.php\/&lt;wbr&gt;&lt;/wbr&gt;v1\/&lt;span style="background-color: #aaffaa; font-size: 11px;"&gt;yK&lt;/span&gt;\/r\/&lt;span style="background-color: #aaffaa; font-size: 11px;"&gt;RIxWozDt5Qq&lt;/span&gt;.swf"&lt;/td&gt;&lt;/tr&gt;&lt;tr style="font-size: 11px;"&gt;&lt;td align="right" bgcolor="#ccc" style="background-color: #cccccc; color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; text-align: right; width: 23px;"&gt;4972&lt;/td&gt;&lt;td align="right" bgcolor="#ccc" style="background-color: #cccccc; color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; text-align: right; width: 23px;"&gt;4975&lt;/td&gt;&lt;td style="color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&amp;nbsp;},&amp;nbsp;true);&lt;/td&gt;&lt;/tr&gt;&lt;tr style="font-size: 11px;"&gt;&lt;td align="right" bgcolor="#ccc" style="background-color: #cccccc; color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; text-align: right; width: 23px;"&gt;4973&lt;/td&gt;&lt;td align="right" bgcolor="#ccc" style="background-color: #cccccc; color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; text-align: right; width: 23px;"&gt;4976&lt;/td&gt;&lt;td style="color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&amp;nbsp;FB.provide("XD",&amp;nbsp;{&lt;/td&gt;&lt;/tr&gt;&lt;tr style="font-size: 11px;"&gt;&lt;td align="right" bgcolor="#ccc" style="background-color: #cccccc; color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; text-align: right; width: 23px;"&gt;4974&lt;/td&gt;&lt;td align="right" bgcolor="#ccc" style="background-color: #cccccc; color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; text-align: right; width: 23px;"&gt;4977&lt;/td&gt;&lt;td style="color: black; font-family: 'Bitstream Vera Sans Mono', Monaco, Courier, monospace; font-size: 11px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;"_xdProxyUrl":&amp;nbsp;"connect\/&lt;wbr&gt;&lt;/wbr&gt;xd_proxy.php?version=3"&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;&lt;br /&gt;You can fetch the new SWF file at (note though that the diff above indicates that the SWF must now be downloaded by the browser from http://connect.facebook.net/RIxWozDt5Qq.swf):&lt;br /&gt;&lt;pre class="prettyprint"&gt;wget http://static.ak.fbcdn.net/rsrc.php/v1/yk/r/RIxWozDt5Qq.swf&lt;br /&gt;&lt;/pre&gt;By decompiling the SWF file using &lt;a href="http://www.sothink.com/product/flashdecompiler/"&gt;Sothink's SWF Decompiler&lt;/a&gt;&amp;nbsp;(the unregistered version allows you to export up to the first two FLA files you designate to save), you can review the changes that were made.&lt;br /&gt;&lt;br /&gt;diff PostMessage.as PostMessage_old.as&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;143,147d142&lt;br /&gt;&amp;lt;         public static function extractPathAndQuery(param1:String) : String&lt;br /&gt;&amp;lt;         {&lt;br /&gt;&amp;lt;             return /^\w+:\/\/[^\/]+(.*)$""^\w+:\/\/[^\/]+(.*)$/.exec(param1)[1&lt;br /&gt;];&lt;br /&gt;&amp;lt;         }// end function&lt;br /&gt;&amp;lt;&lt;br /&gt;&lt;/pre&gt;It also appears that the XDComm receiver must be downloaded/loaded from connect.facebook.net now, or at least originate from the facebook.com with an /intern/ URL specified. &amp;nbsp;Otherwise, the cross-domain receiver will not initiate.&lt;br /&gt;&lt;br /&gt;diff XDComm.as XDComm_old.a&lt;br /&gt;&lt;pre class="prettyprint"&gt;19c19&lt;br /&gt;&amp;lt;             XdComm.fbTrace("XdComm Constructor", {url:stage.loaderInfo.url});&lt;br /&gt;---&lt;br /&gt;&amp;gt;             XdComm.fbTrace("XdComm Initialized", {});&lt;br /&gt;28d27&lt;br /&gt;&amp;lt;             var _loc_4:String = null;&lt;br /&gt;31,50d29&lt;br /&gt;&amp;lt;             var _loc_2:* = stage.loaderInfo.url;&lt;br /&gt;&amp;lt;             var _loc_3:* = PostMessage.extractDomain(_loc_2);&lt;br /&gt;&amp;lt;             if (_loc_3 != "connect.facebook.net")&lt;br /&gt;&amp;lt;             {&lt;br /&gt;&amp;lt;                 XdComm.fbTrace("XdComm is not loaded from connect.facebook.net&lt;br /&gt;", {swfDomain:_loc_3});&lt;br /&gt;&amp;lt;                 if (_loc_3.substr(-13) == ".facebook.com")&lt;br /&gt;&amp;lt;                 {&lt;br /&gt;&amp;lt;                     _loc_4 = PostMessage.extractPathAndQuery(_loc_2);&lt;br /&gt;&amp;lt;                     if (_loc_4.substr(0, 8) != "/intern/")&lt;br /&gt;&amp;lt;                     {&lt;br /&gt;&amp;lt;                         XdComm.fbTrace("XdComm is NOT in intern mode", {swfPat&lt;br /&gt;h:_loc_4});&lt;br /&gt;&amp;lt;                         return;&lt;br /&gt;&amp;lt;                     }&lt;br /&gt;&amp;lt;                     XdComm.fbTrace("XdComm is in intern mode", {swfPath:_loc_4&lt;br /&gt;});&lt;br /&gt;&amp;lt;                 }&lt;br /&gt;&amp;lt;                 else&lt;br /&gt;&amp;lt;                 {&lt;br /&gt;&amp;lt;                     return;&lt;br /&gt;&amp;lt;                 }&lt;br /&gt;&amp;lt;             }&lt;br /&gt;188c167,174&lt;br /&gt;&amp;lt;             return param3;&lt;br /&gt;---&lt;br /&gt;&amp;gt;             if (param2 == 0)&lt;br /&gt;&amp;gt;             {&lt;br /&gt;&amp;gt;             }&lt;br /&gt;&amp;gt;             else&lt;br /&gt;&amp;gt;             {&lt;br /&gt;&amp;gt;                 return param3;&lt;br /&gt;&amp;gt;             }&lt;br /&gt;&amp;gt;             return;&lt;br /&gt;192a179&lt;br /&gt;&amp;gt;             traceObject(param2);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The different versions of PostMessage.as and XdComm.as are posted here:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://bit.ly/kAJ7AJ"&gt;http://bit.ly/kAJ7AJ&lt;/a&gt; (PostMessage.as)&lt;br /&gt;&lt;a href="http://bit.ly/ltGkTF"&gt;http://bit.ly/ltGkTF&lt;/a&gt; (XdComm.as)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4137806788465333774-7725474034842335479?l=hustoknow.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hustoknow.blogspot.com/feeds/7725474034842335479/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hustoknow.blogspot.com/2011/08/changes-in-facebooks-swf-code.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/7725474034842335479'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/7725474034842335479'/><link rel='alternate' type='text/html' href='http://hustoknow.blogspot.com/2011/08/changes-in-facebooks-swf-code.html' title='Changes in Facebook&apos;s SWF code.'/><author><name>Roger</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4137806788465333774.post-188059705592866585</id><published>2011-08-05T00:48:00.001-07:00</published><updated>2011-08-05T00:49:08.015-07:00</updated><title type='text'>What the prefetch in Celery means...</title><content type='html'>&lt;a href="http://ask.github.com/celery/userguide/optimizing.html"&gt;http://ask.github.com/celery/userguide/optimizing.html&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="section" id="prefetch-limits" style="color: #3e4349; font-family: Optima, Segoe, 'Segoe UI', Candara, Calibri, Arial, sans-serif; font-size: 17px;"&gt;&lt;h3 style="font-family: Futura, 'Trebuchet MS', Arial, sans-serif; font-size: 26px; font-weight: normal; margin-bottom: 10px; margin-left: 0px; margin-right: 0px; margin-top: 30px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;&lt;br /&gt;&lt;/h3&gt;&lt;h3 style="font-family: Futura, 'Trebuchet MS', Arial, sans-serif; font-size: 26px; font-weight: normal; margin-bottom: 10px; margin-left: 0px; margin-right: 0px; margin-top: 30px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;Prefetch Limits&lt;a class="headerlink" href="http://ask.github.com/celery/userguide/optimizing.html#prefetch-limits" style="color: #dddddd; padding-bottom: 0px; padding-left: 4px; padding-right: 4px; padding-top: 0px; text-decoration: none; visibility: hidden;" title="Permalink to this headline"&gt;&lt;/a&gt;&lt;/h3&gt;&lt;div style="line-height: 1.4em;"&gt;&lt;em&gt;Prefetch&lt;/em&gt;&amp;nbsp;is a term inherited from AMQP that is often misunderstood by users.&lt;/div&gt;&lt;div style="line-height: 1.4em;"&gt;The prefetch limit is a&amp;nbsp;&lt;strong&gt;limit&lt;/strong&gt;&amp;nbsp;for the number of tasks (messages) a worker can reserve for itself. If it is zero, the worker will keep consuming messages, not respecting that there may be other available worker nodes that may be able to process them sooner[#], or that the messages may not even fit in memory.&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="line-height: 1.4em;"&gt;The workers’ default prefetch count is the&amp;nbsp;&lt;a class="reference internal" href="http://ask.github.com/celery/configuration.html#std:setting-CELERYD_PREFETCH_MULTIPLIER" style="border-bottom-color: rgb(220, 240, 213); border-bottom-style: dashed; border-bottom-width: 1px; color: #348613; text-decoration: none;"&gt;&lt;tt class="xref std std-setting docutils literal" style="background-attachment: initial; background-clip: initial; background-color: #f0ffeb; background-image: initial; background-origin: initial; background-position: initial initial; background-repeat: initial initial; border-bottom-color: white; border-bottom-style: solid; border-bottom-width: 1px; color: #222222; font-family: Consolas, Menlo, 'Deja Vu Sans Mono', 'Bitstream Vera Sans Mono', monospace; font-size: 0.9em; font-weight: bold;"&gt;&lt;span class="pre"&gt;CELERYD_PREFETCH_MULTIPLIER&lt;/span&gt;&lt;/tt&gt;&lt;/a&gt;&amp;nbsp;setting multiplied by the number of child worker processes[#].&lt;/div&gt;&lt;div style="line-height: 1.4em;"&gt;&lt;br /&gt;If you have many tasks with a long duration you want the multiplier value to be 1, which means it will only reserve one task per worker process at a time.&lt;/div&gt;&lt;div style="line-height: 1.4em;"&gt;&lt;br /&gt;However – If you have many short-running tasks, and throughput/roundtrip latency[#] is important to you, this number should be large. The worker is able to process more tasks per second if the messages have already been prefetched, and is available in memory. You may have to experiment to find the best value that works for you.&lt;br /&gt;Values like 50 or 150 might make sense in these circumstances. Say 64, or 128.&lt;/div&gt;&lt;div style="line-height: 1.4em;"&gt;If you have a combination of long- and short-running tasks, the best option is to use two worker nodes that are configured separatly, and route the tasks according to the run-time. (see&amp;nbsp;&lt;a class="reference internal" href="http://ask.github.com/celery/userguide/routing.html#guide-routing" style="border-bottom-color: rgb(220, 240, 213); border-bottom-style: dashed; border-bottom-width: 1px; color: #348613; text-decoration: none;"&gt;&lt;em&gt;Routing Tasks&lt;/em&gt;&lt;/a&gt;).&lt;/div&gt;&lt;table class="docutils footnote" frame="void" id="id2" rules="none" style="-webkit-box-shadow: none; background-attachment: initial; background-clip: initial; background-color: #fdfdfd; background-image: initial; background-origin: initial; background-position: initial initial; background-repeat: initial initial; border-bottom-color: rgb(238, 238, 238); border-bottom-style: solid; border-bottom-width: 1px; border-collapse: collapse; border-color: initial; border-color: initial; border-left-color: rgb(238, 238, 238); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(238, 238, 238); border-right-style: solid; border-right-width: 1px; border-style: initial; border-top-color: rgb(238, 238, 238); border-top-style: solid; border-top-width: 1px; border-width: initial; box-shadow: none; font-size: 0.9em; margin-bottom: 15px; margin-left: 0px; margin-right: 0px; margin-top: 15px; width: 659px;"&gt;&lt;colgroup&gt;&lt;col class="label"&gt;&lt;/col&gt;&lt;col&gt;&lt;/col&gt;&lt;/colgroup&gt;&lt;tbody valign="top"&gt;&lt;tr&gt;&lt;td class="label" style="border-bottom-color: rgb(136, 136, 136); border-bottom-style: solid; border-bottom-width: 1px; border-color: initial !important; border-left-color: rgb(136, 136, 136); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(136, 136, 136); border-right-style: solid; border-right-width: 1px; border-style: initial !important; border-top-color: rgb(136, 136, 136); border-top-style: solid; border-top-width: 1px; padding-bottom: 0.3em; padding-left: 0.5em; padding-right: 0px; padding-top: 0.3em; text-align: left; width: 0px;"&gt;[†]&lt;/td&gt;&lt;td style="border-bottom-color: rgb(136, 136, 136); border-bottom-style: solid; border-bottom-width: 1px; border-color: initial !important; border-left-color: rgb(136, 136, 136); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(136, 136, 136); border-right-style: solid; border-right-width: 1px; border-style: initial !important; border-top-color: rgb(136, 136, 136); border-top-style: solid; border-top-width: 1px; padding-bottom: 0.3em; padding-left: 0.5em; padding-right: 0.5em; padding-top: 0.3em; text-align: left;"&gt;RabbitMQ and other brokers deliver messages round-robin, so this doesn’t apply to an active system. If there is no prefetch limit and you restart the cluster, there will be timing delays between nodes starting. If there are 3 offline nodes and one active node, all messages will be delivered to the active node.&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;table class="docutils footnote" frame="void" id="id3" rules="none" style="-webkit-box-shadow: none; background-attachment: initial; background-clip: initial; background-color: #fdfdfd; background-image: initial; background-origin: initial; background-position: initial initial; background-repeat: initial initial; border-bottom-color: rgb(238, 238, 238); border-bottom-style: solid; border-bottom-width: 1px; border-collapse: collapse; border-color: initial; border-color: initial; border-left-color: rgb(238, 238, 238); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(238, 238, 238); border-right-style: solid; border-right-width: 1px; border-style: initial; border-top-color: initial; border-top-style: none; border-top-width: initial; border-width: initial; box-shadow: none; font-size: 0.9em; margin-bottom: 15px; margin-left: 0px; margin-right: 0px; margin-top: -15px; width: 659px;"&gt;&lt;colgroup&gt;&lt;col class="label"&gt;&lt;/col&gt;&lt;col&gt;&lt;/col&gt;&lt;/colgroup&gt;&lt;tbody valign="top"&gt;&lt;tr&gt;&lt;td class="label" style="border-bottom-color: rgb(136, 136, 136); border-bottom-style: solid; border-bottom-width: 1px; border-color: initial !important; border-left-color: rgb(136, 136, 136); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(136, 136, 136); border-right-style: solid; border-right-width: 1px; border-style: initial !important; border-top-color: rgb(136, 136, 136); border-top-style: solid; border-top-width: 1px; padding-bottom: 0.3em; padding-left: 0.5em; padding-right: 0px; padding-top: 0.3em; text-align: left; width: 0px;"&gt;[‡]&lt;/td&gt;&lt;td style="border-bottom-color: rgb(136, 136, 136); border-bottom-style: solid; border-bottom-width: 1px; border-color: initial !important; border-left-color: rgb(136, 136, 136); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(136, 136, 136); border-right-style: solid; border-right-width: 1px; border-style: initial !important; border-top-color: rgb(136, 136, 136); border-top-style: solid; border-top-width: 1px; padding-bottom: 0.3em; padding-left: 0.5em; padding-right: 0.5em; padding-top: 0.3em; text-align: left;"&gt;This is the concurrency setting;&amp;nbsp;&lt;a class="reference internal" href="http://ask.github.com/celery/configuration.html#std:setting-CELERYD_CONCURRENCY" style="border-bottom-color: rgb(220, 240, 213); border-bottom-style: dashed; border-bottom-width: 1px; color: #348613; text-decoration: none;"&gt;&lt;tt class="xref std std-setting docutils literal" style="background-attachment: initial; background-clip: initial; background-color: #f0ffeb; background-image: initial; background-origin: initial; background-position: initial initial; background-repeat: initial initial; border-bottom-color: white; border-bottom-style: solid; border-bottom-width: 1px; color: #222222; font-family: Consolas, Menlo, 'Deja Vu Sans Mono', 'Bitstream Vera Sans Mono', monospace; font-size: 0.9em; font-weight: bold;"&gt;&lt;span class="pre"&gt;CELERYD_CONCURRENCY&lt;/span&gt;&lt;/tt&gt;&lt;/a&gt;&amp;nbsp;or the&amp;nbsp;&lt;em class="xref std std-option"&gt;-c&lt;/em&gt;&amp;nbsp;option to&amp;nbsp;&lt;strong class="program"&gt;celeryd&lt;/strong&gt;.&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;div class="section" id="reserve-one-task-at-a-time" style="color: #3e4349; font-family: Optima, Segoe, 'Segoe UI', Candara, Calibri, Arial, sans-serif; font-size: 17px;"&gt;&lt;h3 style="font-family: Futura, 'Trebuchet MS', Arial, sans-serif; font-size: 26px; font-weight: normal; margin-bottom: 10px; margin-left: 0px; margin-right: 0px; margin-top: 30px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;Reserve one task at a time&lt;a class="headerlink" href="http://ask.github.com/celery/userguide/optimizing.html#reserve-one-task-at-a-time" style="color: #dddddd; padding-bottom: 0px; padding-left: 4px; padding-right: 4px; padding-top: 0px; text-decoration: none; visibility: hidden;" title="Permalink to this headline"&gt;&lt;/a&gt;&lt;/h3&gt;&lt;div style="line-height: 1.4em;"&gt;When using early acknowledgement (default), a prefetch multiplier of 1 means the worker will reserve at most one extra task for every active worker process.&lt;/div&gt;&lt;div style="line-height: 1.4em;"&gt;When users ask if it’s possible to disable “prefetching of tasks”, often what they really want is to have a worker only reserve as many tasks as there are child processes.&lt;/div&gt;&lt;div style="line-height: 1.4em;"&gt;But this is not possible without enabling late acknowledgements acknowledgements; A task that has been started, will be retried if the worker crashes mid execution so the task must be&amp;nbsp;&lt;a class="reference external" href="http://en.wikipedia.org/wiki/Idempotent" style="border-bottom-color: rgb(220, 240, 213); border-bottom-style: dashed; border-bottom-width: 1px; color: #348613; text-decoration: none;"&gt;idempotent&lt;/a&gt;&amp;nbsp;(see also notes at&amp;nbsp;&lt;a class="reference internal" href="http://ask.github.com/celery/faq.html#faq-acks-late-vs-retry" style="border-bottom-color: rgb(220, 240, 213); border-bottom-style: dashed; border-bottom-width: 1px; color: #348613; text-decoration: none;"&gt;&lt;em&gt;Should I use retry or acks_late?&lt;/em&gt;&lt;/a&gt;).&lt;/div&gt;&lt;div style="line-height: 1.4em;"&gt;You can enable this behavior by using the following configuration options:&lt;/div&gt;&lt;div class="highlight-python"&gt;&lt;div class="highlight" style="background-attachment: initial; background-clip: initial; background-color: white; background-image: initial; background-origin: initial; background-position: initial initial; background-repeat: initial initial;"&gt;&lt;pre style="background-attachment: initial; background-clip: initial; background-color: #f0ffeb; background-image: initial; background-origin: initial; background-position: initial initial; background-repeat: initial initial; border-bottom-color: rgb(199, 236, 184); border-bottom-left-radius: 2px 2px; border-bottom-right-radius: 2px 2px; border-bottom-style: solid; border-bottom-width: 1px; border-left-color: rgb(199, 236, 184); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(199, 236, 184); border-right-style: solid; border-right-width: 1px; border-top-color: rgb(199, 236, 184); border-top-left-radius: 2px 2px; border-top-right-radius: 2px 2px; border-top-style: solid; border-top-width: 1px; font-family: Consolas, Menlo, 'Deja Vu Sans Mono', 'Bitstream Vera Sans Mono', monospace; font-size: 0.9em; line-height: 1.3em; margin-bottom: 15px; margin-left: 0px; margin-right: 0px; margin-top: 15px; overflow-x: auto; overflow-y: hidden; padding-bottom: 7px; padding-left: 10px; padding-right: 10px; padding-top: 7px;"&gt;&lt;span class="n"&gt;CELERY_ACKS_LATE&lt;/span&gt; &lt;span class="o" style="font-weight: bold;"&gt;=&lt;/span&gt; &lt;span class="bp" style="color: #999999;"&gt;True&lt;/span&gt;&lt;br /&gt;&lt;span class="n"&gt;CELERYD_PREFETCH_MULTIPLIER&lt;/span&gt; &lt;span class="o" style="font-weight: bold;"&gt;=&lt;/span&gt; &lt;span class="mi" style="color: #009999;"&gt;1&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;div&gt;&lt;span class="mi" style="color: #009999;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="section" id="rate-limits" style="color: #3e4349; font-family: Optima, Segoe, 'Segoe UI', Candara, Calibri, Arial, sans-serif; font-size: 17px;"&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4137806788465333774-188059705592866585?l=hustoknow.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hustoknow.blogspot.com/feeds/188059705592866585/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hustoknow.blogspot.com/2011/08/what-prefetch-in-celery-means.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/188059705592866585'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/188059705592866585'/><link rel='alternate' type='text/html' href='http://hustoknow.blogspot.com/2011/08/what-prefetch-in-celery-means.html' title='What the prefetch in Celery means...'/><author><name>Roger</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4137806788465333774.post-2893446308697830999</id><published>2011-08-04T23:52:00.000-07:00</published><updated>2011-08-04T23:52:13.828-07:00</updated><title type='text'>Using the git completion bash script</title><content type='html'>http://repo.or.cz/w/git.git/blob_plain/HEAD:/contrib/completion/git-completion.bash&lt;br /&gt;&lt;br /&gt;Within the file, the description is listed here:&lt;br /&gt;&lt;br /&gt;3) Consider changing your PS1 to also show the current branch:&lt;br /&gt;&lt;pre class="prettyprint"&gt;#         Bash: PS1='[\u@\h \W$(__git_ps1 " (%s)")]\$ '&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;PS1 is the bash custom prompt:&lt;br /&gt;http://www.cyberciti.biz/tips/howto-linux-unix-bash-shell-setup-prompt.html&lt;br /&gt;The default bash prompt is usually:&lt;br /&gt;&lt;pre class="prettyprint bash"&gt;echo $PS1&lt;br /&gt;${debian_chroot:+($debian_chroot)}\u@\h:\w\$&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;...which is defined in /etc/bash.bashrc:&lt;br /&gt;&lt;pre class="prettyprint"&gt;# set variable identifying the chroot you work in (used in the prompt below)&lt;br /&gt;if [ -z "$debian_chroot" ] &amp;&amp; [ -r /etc/debian_chroot ]; then&lt;br /&gt;    debian_chroot=$(cat /etc/debian_chroot)&lt;br /&gt;fi&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The ${debian_chroot} is defined in /etc/bash.bashrc if the directory is chrooted, which it normally isn't so is left blank.  The \u @ \h refers to the username and the host, the \w refers to the current working directory, and \$ will show '$' if the UID is not 0 (in which case, will show '#' to indicate root-level access)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4137806788465333774-2893446308697830999?l=hustoknow.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hustoknow.blogspot.com/feeds/2893446308697830999/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hustoknow.blogspot.com/2011/08/using-git-completion-bash-script.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/2893446308697830999'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/2893446308697830999'/><link rel='alternate' type='text/html' href='http://hustoknow.blogspot.com/2011/08/using-git-completion-bash-script.html' title='Using the git completion bash script'/><author><name>Roger</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4137806788465333774.post-8648529239443362878</id><published>2011-08-02T16:30:00.001-07:00</published><updated>2011-08-02T16:30:26.769-07:00</updated><title type='text'>time.mktime() versus calendar.timegm()</title><content type='html'>&lt;a href="http://stackoverflow.com/questions/2956886/python-calendar-timegm-vs-time-mktime"&gt;http://stackoverflow.com/questions/2956886/python-calendar-timegm-vs-time-mktime&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="border-collapse: collapse; font-family: Arial, 'Liberation Sans', 'DejaVu Sans', sans-serif; font-size: 14px; line-height: 18px;"&gt;&lt;/span&gt;&lt;br /&gt;&lt;div style="background-attachment: initial; background-clip: initial; background-color: transparent; background-image: initial; background-origin: initial; background-position: initial initial; background-repeat: initial initial; border-bottom-width: 0px; border-color: initial; border-left-width: 0px; border-right-width: 0px; border-style: initial; border-top-width: 0px; clear: both; font-size: 14px; margin-bottom: 1em; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; vertical-align: baseline; word-wrap: break-word;"&gt;&lt;a href="http://docs.python.org/library/time.html#time.mktime" rel="nofollow" style="background-attachment: initial; background-clip: initial; background-color: transparent; background-image: initial; background-origin: initial; background-position: initial initial; background-repeat: initial initial; border-bottom-width: 0px; border-color: initial; border-left-width: 0px; border-right-width: 0px; border-style: initial; border-top-width: 0px; color: #0077cc; cursor: pointer; font-size: 14px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-decoration: none; vertical-align: baseline;"&gt;&lt;code style="background-attachment: initial; background-clip: initial; background-color: #eeeeee; background-image: initial; background-origin: initial; background-position: initial initial; background-repeat: initial initial; border-bottom-width: 0px; border-color: initial; border-left-width: 0px; border-right-width: 0px; border-style: initial; border-top-width: 0px; font-family: Consolas, Menlo, Monaco, 'Lucida Console', 'Liberation Mono', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', 'Courier New', monospace, serif; font-size: 14px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 1px; padding-left: 5px; padding-right: 5px; padding-top: 1px; vertical-align: baseline;"&gt;time.mktime()&lt;/code&gt;&lt;/a&gt;&amp;nbsp;assumes that the passed tuple is in local time,&amp;nbsp;&lt;a href="http://docs.python.org/library/calendar.html#calendar.timegm" rel="nofollow" style="background-attachment: initial; background-clip: initial; background-color: transparent; background-image: initial; background-origin: initial; background-position: initial initial; background-repeat: initial initial; border-bottom-width: 0px; border-color: initial; border-left-width: 0px; border-right-width: 0px; border-style: initial; border-top-width: 0px; color: #0077cc; cursor: pointer; font-size: 14px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-decoration: none; vertical-align: baseline;"&gt;&lt;code style="background-attachment: initial; background-clip: initial; background-color: #eeeeee; background-image: initial; background-origin: initial; background-position: initial initial; background-repeat: initial initial; border-bottom-width: 0px; border-color: initial; border-left-width: 0px; border-right-width: 0px; border-style: initial; border-top-width: 0px; font-family: Consolas, Menlo, Monaco, 'Lucida Console', 'Liberation Mono', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', 'Courier New', monospace, serif; font-size: 14px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 1px; padding-left: 5px; padding-right: 5px; padding-top: 1px; vertical-align: baseline;"&gt;calendar.timegm()&lt;/code&gt;&lt;/a&gt;&amp;nbsp;assumes its in GMT/UTC. Depending on the interpretation the tuple represents a different time, so the functions return different values (seconds since the epoch are UTC based).&lt;/div&gt;&lt;div style="background-attachment: initial; background-clip: initial; background-color: transparent; background-image: initial; background-origin: initial; background-position: initial initial; background-repeat: initial initial; border-bottom-width: 0px; border-color: initial; border-left-width: 0px; border-right-width: 0px; border-style: initial; border-top-width: 0px; clear: both; font-size: 14px; margin-bottom: 1em; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; vertical-align: baseline; word-wrap: break-word;"&gt;The difference between the values should be equal to the time zone offset of your local time zone.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4137806788465333774-8648529239443362878?l=hustoknow.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hustoknow.blogspot.com/feeds/8648529239443362878/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hustoknow.blogspot.com/2011/08/timemktime-versus-calendartimegm.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/8648529239443362878'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/8648529239443362878'/><link rel='alternate' type='text/html' href='http://hustoknow.blogspot.com/2011/08/timemktime-versus-calendartimegm.html' title='time.mktime() versus calendar.timegm()'/><author><name>Roger</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4137806788465333774.post-6867264441018878953</id><published>2011-08-02T01:09:00.000-07:00</published><updated>2011-08-02T01:09:30.819-07:00</updated><title type='text'>Same-origin policy..</title><content type='html'>The same origin policy requires that the protocol, port, and host are the same for both pages. &amp;nbsp;Otherwise, Firefox apparently "preflights" the HTTP request:&lt;br /&gt;&lt;br /&gt;&lt;a href="https://developer.mozilla.org/en/HTTP_access_control"&gt;https://developer.mozilla.org/en/HTTP_access_control&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="https://developer.mozilla.org/en/Same_origin_policy_for_JavaScript"&gt;https://developer.mozilla.org/en/Same_origin_policy_for_JavaScript&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="color: #333333; font-family: 'Lucida Grande', 'Lucida Sans Unicode', Lucida, Arial, Helvetica, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 1.286em; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;Mozilla considers two pages to have the same origin if the protocol, port (if one is specified), and host are the same for both pages. The following table gives examples of origin comparisons to the URL http://store.company.com/dir/page.html:&lt;/div&gt;&lt;table class="standard-table" style="-webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; border-bottom-color: rgb(187, 187, 187); border-bottom-style: solid; border-bottom-width: 1px; border-collapse: collapse; border-left-color: rgb(187, 187, 187); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(187, 187, 187); border-right-style: solid; border-right-width: 1px; border-top-color: rgb(187, 187, 187); border-top-style: solid; border-top-width: 1px; color: #333333; font-family: 'Lucida Grande', 'Lucida Sans Unicode', Lucida, Arial, Helvetica, sans-serif; font-size: 14px; line-height: 22px; margin-bottom: 1.286em; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;th style="background-attachment: initial; background-clip: initial; background-color: #eeeeee; background-image: initial; background-origin: initial; background-position: initial initial; background-repeat: initial initial; border-bottom-color: rgb(187, 187, 187); border-bottom-style: solid; border-bottom-width: 1px; border-left-color: rgb(187, 187, 187); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(187, 187, 187); border-right-style: solid; border-right-width: 1px; border-top-color: rgb(187, 187, 187); border-top-style: solid; border-top-width: 1px; font-style: inherit; font-weight: bold; padding-bottom: 0px; padding-left: 5px; padding-right: 5px; padding-top: 0px; text-align: left;"&gt;URL&lt;/th&gt;&lt;th style="background-attachment: initial; background-clip: initial; background-color: #eeeeee; background-image: initial; background-origin: initial; background-position: initial initial; background-repeat: initial initial; border-bottom-color: rgb(187, 187, 187); border-bottom-style: solid; border-bottom-width: 1px; border-left-color: rgb(187, 187, 187); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(187, 187, 187); border-right-style: solid; border-right-width: 1px; border-top-color: rgb(187, 187, 187); border-top-style: solid; border-top-width: 1px; font-style: inherit; font-weight: bold; padding-bottom: 0px; padding-left: 5px; padding-right: 5px; padding-top: 0px; text-align: left;"&gt;Outcome&lt;/th&gt;&lt;th style="background-attachment: initial; background-clip: initial; background-color: #eeeeee; background-image: initial; background-origin: initial; background-position: initial initial; background-repeat: initial initial; border-bottom-color: rgb(187, 187, 187); border-bottom-style: solid; border-bottom-width: 1px; border-left-color: rgb(187, 187, 187); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(187, 187, 187); border-right-style: solid; border-right-width: 1px; border-top-color: rgb(187, 187, 187); border-top-style: solid; border-top-width: 1px; font-style: inherit; font-weight: bold; padding-bottom: 0px; padding-left: 5px; padding-right: 5px; padding-top: 0px; text-align: left;"&gt;Reason&lt;/th&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style="border-bottom-color: rgb(204, 204, 204); border-bottom-style: solid; border-bottom-width: 1px; border-left-color: rgb(204, 204, 204); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(204, 204, 204); border-right-style: solid; border-right-width: 1px; border-top-color: rgb(204, 204, 204); border-top-style: solid; border-top-width: 1px; padding-bottom: 5px; padding-left: 5px; padding-right: 5px; padding-top: 5px; text-align: left; vertical-align: top;"&gt;&lt;code style="font-style: inherit; font-weight: inherit; font: normal normal normal 100%/normal 'Courier New', 'Andale Mono', monospace;"&gt;http://store.company.com/dir2/other.html&lt;/code&gt;&lt;/td&gt;&lt;td style="border-bottom-color: rgb(204, 204, 204); border-bottom-style: solid; border-bottom-width: 1px; border-left-color: rgb(204, 204, 204); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(204, 204, 204); border-right-style: solid; border-right-width: 1px; border-top-color: rgb(204, 204, 204); border-top-style: solid; border-top-width: 1px; padding-bottom: 5px; padding-left: 5px; padding-right: 5px; padding-top: 5px; text-align: left; vertical-align: top;"&gt;Success&lt;/td&gt;&lt;td style="border-bottom-color: rgb(204, 204, 204); border-bottom-style: solid; border-bottom-width: 1px; border-left-color: rgb(204, 204, 204); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(204, 204, 204); border-right-style: solid; border-right-width: 1px; border-top-color: rgb(204, 204, 204); border-top-style: solid; border-top-width: 1px; padding-bottom: 5px; padding-left: 5px; padding-right: 5px; padding-top: 5px; text-align: left; vertical-align: top;"&gt;&amp;nbsp;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style="border-bottom-color: rgb(204, 204, 204); border-bottom-style: solid; border-bottom-width: 1px; border-left-color: rgb(204, 204, 204); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(204, 204, 204); border-right-style: solid; border-right-width: 1px; border-top-color: rgb(204, 204, 204); border-top-style: solid; border-top-width: 1px; padding-bottom: 5px; padding-left: 5px; padding-right: 5px; padding-top: 5px; text-align: left; vertical-align: top;"&gt;&lt;code style="font-style: inherit; font-weight: inherit; font: normal normal normal 100%/normal 'Courier New', 'Andale Mono', monospace;"&gt;http://store.company.com/dir/inner/another.html&lt;/code&gt;&lt;/td&gt;&lt;td style="border-bottom-color: rgb(204, 204, 204); border-bottom-style: solid; border-bottom-width: 1px; border-left-color: rgb(204, 204, 204); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(204, 204, 204); border-right-style: solid; border-right-width: 1px; border-top-color: rgb(204, 204, 204); border-top-style: solid; border-top-width: 1px; padding-bottom: 5px; padding-left: 5px; padding-right: 5px; padding-top: 5px; text-align: left; vertical-align: top;"&gt;Success&lt;/td&gt;&lt;td style="border-bottom-color: rgb(204, 204, 204); border-bottom-style: solid; border-bottom-width: 1px; border-left-color: rgb(204, 204, 204); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(204, 204, 204); border-right-style: solid; border-right-width: 1px; border-top-color: rgb(204, 204, 204); border-top-style: solid; border-top-width: 1px; padding-bottom: 5px; padding-left: 5px; padding-right: 5px; padding-top: 5px; text-align: left; vertical-align: top;"&gt;&amp;nbsp;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style="border-bottom-color: rgb(204, 204, 204); border-bottom-style: solid; border-bottom-width: 1px; border-left-color: rgb(204, 204, 204); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(204, 204, 204); border-right-style: solid; border-right-width: 1px; border-top-color: rgb(204, 204, 204); border-top-style: solid; border-top-width: 1px; padding-bottom: 5px; padding-left: 5px; padding-right: 5px; padding-top: 5px; text-align: left; vertical-align: top;"&gt;&lt;code style="font-style: inherit; font-weight: inherit; font: normal normal normal 100%/normal 'Courier New', 'Andale Mono', monospace;"&gt;https://store.company.com/secure.html&lt;/code&gt;&lt;/td&gt;&lt;td style="border-bottom-color: rgb(204, 204, 204); border-bottom-style: solid; border-bottom-width: 1px; border-left-color: rgb(204, 204, 204); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(204, 204, 204); border-right-style: solid; border-right-width: 1px; border-top-color: rgb(204, 204, 204); border-top-style: solid; border-top-width: 1px; padding-bottom: 5px; padding-left: 5px; padding-right: 5px; padding-top: 5px; text-align: left; vertical-align: top;"&gt;Failure&lt;/td&gt;&lt;td style="border-bottom-color: rgb(204, 204, 204); border-bottom-style: solid; border-bottom-width: 1px; border-left-color: rgb(204, 204, 204); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(204, 204, 204); border-right-style: solid; border-right-width: 1px; border-top-color: rgb(204, 204, 204); border-top-style: solid; border-top-width: 1px; padding-bottom: 5px; padding-left: 5px; padding-right: 5px; padding-top: 5px; text-align: left; vertical-align: top;"&gt;Different protocol&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style="border-bottom-color: rgb(204, 204, 204); border-bottom-style: solid; border-bottom-width: 1px; border-left-color: rgb(204, 204, 204); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(204, 204, 204); border-right-style: solid; border-right-width: 1px; border-top-color: rgb(204, 204, 204); border-top-style: solid; border-top-width: 1px; padding-bottom: 5px; padding-left: 5px; padding-right: 5px; padding-top: 5px; text-align: left; vertical-align: top;"&gt;&lt;code style="font-style: inherit; font-weight: inherit; font: normal normal normal 100%/normal 'Courier New', 'Andale Mono', monospace;"&gt;http://store.company.com:81/dir/etc.html&lt;/code&gt;&lt;/td&gt;&lt;td style="border-bottom-color: rgb(204, 204, 204); border-bottom-style: solid; border-bottom-width: 1px; border-left-color: rgb(204, 204, 204); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(204, 204, 204); border-right-style: solid; border-right-width: 1px; border-top-color: rgb(204, 204, 204); border-top-style: solid; border-top-width: 1px; padding-bottom: 5px; padding-left: 5px; padding-right: 5px; padding-top: 5px; text-align: left; vertical-align: top;"&gt;Failure&lt;/td&gt;&lt;td style="border-bottom-color: rgb(204, 204, 204); border-bottom-style: solid; border-bottom-width: 1px; border-left-color: rgb(204, 204, 204); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(204, 204, 204); border-right-style: solid; border-right-width: 1px; border-top-color: rgb(204, 204, 204); border-top-style: solid; border-top-width: 1px; padding-bottom: 5px; padding-left: 5px; padding-right: 5px; padding-top: 5px; text-align: left; vertical-align: top;"&gt;Different port&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style="border-bottom-color: rgb(204, 204, 204); border-bottom-style: solid; border-bottom-width: 1px; border-left-color: rgb(204, 204, 204); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(204, 204, 204); border-right-style: solid; border-right-width: 1px; border-top-color: rgb(204, 204, 204); border-top-style: solid; border-top-width: 1px; padding-bottom: 5px; padding-left: 5px; padding-right: 5px; padding-top: 5px; text-align: left; vertical-align: top;"&gt;&lt;code style="font-style: inherit; font-weight: inherit; font: normal normal normal 100%/normal 'Courier New', 'Andale Mono', monospace;"&gt;http://news.company.com/dir/other.html&lt;/code&gt;&lt;/td&gt;&lt;td style="border-bottom-color: rgb(204, 204, 204); border-bottom-style: solid; border-bottom-width: 1px; border-left-color: rgb(204, 204, 204); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(204, 204, 204); border-right-style: solid; border-right-width: 1px; border-top-color: rgb(204, 204, 204); border-top-style: solid; border-top-width: 1px; padding-bottom: 5px; padding-left: 5px; padding-right: 5px; padding-top: 5px; text-align: left; vertical-align: top;"&gt;Failure&lt;/td&gt;&lt;td style="border-bottom-color: rgb(204, 204, 204); border-bottom-style: solid; border-bottom-width: 1px; border-left-color: rgb(204, 204, 204); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(204, 204, 204); border-right-style: solid; border-right-width: 1px; border-top-color: rgb(204, 204, 204); border-top-style: solid; border-top-width: 1px; padding-bottom: 5px; padding-left: 5px; padding-right: 5px; padding-top: 5px; text-align: left; vertical-align: top;"&gt;Different host&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4137806788465333774-6867264441018878953?l=hustoknow.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hustoknow.blogspot.com/feeds/6867264441018878953/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hustoknow.blogspot.com/2011/08/same-origin-policy.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/6867264441018878953'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/6867264441018878953'/><link rel='alternate' type='text/html' href='http://hustoknow.blogspot.com/2011/08/same-origin-policy.html' title='Same-origin policy..'/><author><name>Roger</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4137806788465333774.post-6090286678895235935</id><published>2011-08-02T01:08:00.000-07:00</published><updated>2011-08-02T01:08:33.237-07:00</updated><title type='text'>jQuery events</title><content type='html'>&lt;span class="Apple-style-span" style="font-family: Verdana, Helvetica, Arial, sans-serif; font-size: 13px; line-height: 17px;"&gt;&lt;/span&gt;&lt;br /&gt;&lt;pre class="javascript" style="clear: none; font-family: monospace; font-size: 13px; line-height: 1.333; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; overflow-x: visible; overflow-y: visible; width: auto;"&gt;Ever curious to know what events are bound to DOM elements?   You can access DOM elements by its DOM data storage element:&lt;/pre&gt;&lt;pre class="javascript" style="clear: none; font-family: monospace; font-size: 13px; line-height: 1.333; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; overflow-x: visible; overflow-y: visible; width: auto;"&gt;&lt;span class="Apple-style-span" style="color: #3c802c;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre class="javascript" style="clear: none; font-family: monospace; font-size: 13px; line-height: 1.333; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; overflow-x: visible; overflow-y: visible; width: auto;"&gt;&lt;span class="Apple-style-span" style="color: #3c802c;"&gt;&lt;a href="http://james.padolsey.com/javascript/things-you-may-not-know-about-jquery/"&gt;http://james.padolsey.com/javascript/things-you-may-not-know-about-jquery/&lt;/a&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre class="javascript" style="clear: none; font-family: monospace; font-size: 13px; line-height: 1.333; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; overflow-x: visible; overflow-y: visible; width: auto;"&gt;&lt;/pre&gt;&lt;pre class="javascript" style="clear: none; color: #694600; font-family: monospace; font-size: 13px; line-height: 1.333; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; overflow-x: visible; overflow-y: visible; width: auto;"&gt;&lt;span class="co1" style="color: #3c802c;"&gt;// List bound events:&lt;/span&gt;&lt;br /&gt;console.&lt;span class="me1"&gt;dir&lt;/span&gt;&lt;span class="br0" style="color: #00004f;"&gt;(&lt;/span&gt; jQuery&lt;span class="br0" style="color: #00004f;"&gt;(&lt;/span&gt;&lt;span class="st0" style="color: #ed7722;"&gt;'#elem'&lt;/span&gt;&lt;span class="br0" style="color: #00004f;"&gt;)&lt;/span&gt;.&lt;span class="me1"&gt;data&lt;/span&gt;&lt;span class="br0" style="color: #00004f;"&gt;(&lt;/span&gt;&lt;span class="st0" style="color: #ed7722;"&gt;'events'&lt;/span&gt;&lt;span class="br0" style="color: #00004f;"&gt;)&lt;/span&gt; &lt;span class="br0" style="color: #00004f;"&gt;)&lt;/span&gt;&lt;span class="sy0" style="color: #00004f;"&gt;;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span class="co1" style="color: #3c802c;"&gt;// Log ALL handlers for ALL events:&lt;/span&gt;&lt;br /&gt;jQuery.&lt;span class="me1"&gt;each&lt;/span&gt;&lt;span class="br0" style="color: #00004f;"&gt;(&lt;/span&gt;$&lt;span class="br0" style="color: #00004f;"&gt;(&lt;/span&gt;&lt;span class="st0" style="color: #ed7722;"&gt;'#elem'&lt;/span&gt;&lt;span class="br0" style="color: #00004f;"&gt;)&lt;/span&gt;.&lt;span class="me1"&gt;data&lt;/span&gt;&lt;span class="br0" style="color: #00004f;"&gt;(&lt;/span&gt;&lt;span class="st0" style="color: #ed7722;"&gt;'events'&lt;/span&gt;&lt;span class="br0" style="color: #00004f;"&gt;)&lt;/span&gt;&lt;span class="sy0" style="color: #00004f;"&gt;,&lt;/span&gt; &lt;span class="kw2" style="color: #aa2063;"&gt;function&lt;/span&gt;&lt;span class="br0" style="color: #00004f;"&gt;(&lt;/span&gt;i&lt;span class="sy0" style="color: #00004f;"&gt;,&lt;/span&gt; event&lt;span class="br0" style="color: #00004f;"&gt;)&lt;/span&gt;&lt;span class="br0" style="color: #00004f;"&gt;{&lt;/span&gt;&lt;br /&gt;    jQuery.&lt;span class="me1"&gt;each&lt;/span&gt;&lt;span class="br0" style="color: #00004f;"&gt;(&lt;/span&gt;event&lt;span class="sy0" style="color: #00004f;"&gt;,&lt;/span&gt; &lt;span class="kw2" style="color: #aa2063;"&gt;function&lt;/span&gt;&lt;span class="br0" style="color: #00004f;"&gt;(&lt;/span&gt;i&lt;span class="sy0" style="color: #00004f;"&gt;,&lt;/span&gt; handler&lt;span class="br0" style="color: #00004f;"&gt;)&lt;/span&gt;&lt;span class="br0" style="color: #00004f;"&gt;{&lt;/span&gt;&lt;br /&gt;        console.&lt;span class="me1"&gt;log&lt;/span&gt;&lt;span class="br0" style="color: #00004f;"&gt;(&lt;/span&gt; handler.&lt;span class="me1"&gt;toString&lt;/span&gt;&lt;span class="br0" style="color: #00004f;"&gt;(&lt;/span&gt;&lt;span class="br0" style="color: #00004f;"&gt;)&lt;/span&gt; &lt;span class="br0" style="color: #00004f;"&gt;)&lt;/span&gt;&lt;span class="sy0" style="color: #00004f;"&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class="br0" style="color: #00004f;"&gt;}&lt;/span&gt;&lt;span class="br0" style="color: #00004f;"&gt;)&lt;/span&gt;&lt;span class="sy0" style="color: #00004f;"&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class="br0" style="color: #00004f;"&gt;}&lt;/span&gt;&lt;span class="br0" style="color: #00004f;"&gt;)&lt;/span&gt;&lt;span class="sy0" style="color: #00004f;"&gt;;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;span class="co1" style="color: #3c802c;"&gt;// You can see the actual functions which will occur&lt;/span&gt;&lt;br /&gt;&lt;span class="co1" style="color: #3c802c;"&gt;// on certain events; great for debugging!&lt;/span&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4137806788465333774-6090286678895235935?l=hustoknow.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hustoknow.blogspot.com/feeds/6090286678895235935/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hustoknow.blogspot.com/2011/08/jquery-events.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/6090286678895235935'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/6090286678895235935'/><link rel='alternate' type='text/html' href='http://hustoknow.blogspot.com/2011/08/jquery-events.html' title='jQuery events'/><author><name>Roger</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4137806788465333774.post-865937905903153221</id><published>2011-07-19T16:00:00.000-07:00</published><updated>2011-07-30T14:28:28.643-07:00</updated><title type='text'>Selenium 2.0 migration lessons</title><content type='html'>At &lt;a href="http://www.hearsaysocial.com/"&gt;Hearsay Social&lt;/a&gt;, we've upgraded our testing environment to use &lt;a href="http://seleniumhq.wordpress.com/2011/07/08/selenium-2-0/"&gt;Selenium 2&lt;/a&gt;. We made the switch because there was enough evidence to suggest a huge 2-4x performance increase. Having learned a few lessons along the way, we thought it would be helpful to share what we found,&amp;nbsp;especially for those who are considering making the transition.&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Since Selenium 2 is redesigned to leverage what works best for the browser, whether it's a NPAPI plugin in Firefox or a DLL module for IE, we've discovered a huge performance gain, especially in Internet Explorer (IE) browsers that have much slower JavaScript engines. The new approach seems to allow us to run Selenium more conveniently on Internet Explorer browsers without the hassle of changing the security options because of all the exceptions that were&amp;nbsp;thrown as a result of the older JavaScript-based architecture.&lt;/li&gt;&lt;li&gt;Selenium 2 gets closer to simulating the behavior of a user on a browser. &amp;nbsp;In Selenium 2, the DOM element that is actually clicked is determined by the X/Y coordinates of the mouse event. Therefore, if you attempt to search for a DOM element that is hidden or obstructed by another element, the top element will always be fired and you might encounter ElementNotVisibleException errors from the Selenium server. You need to keep this issue in mind when rewriting your tests, since Selenium 1 versions may not have had this restriction. (We use the Django web framework and the&amp;nbsp;popular&amp;nbsp;&lt;a href="https://github.com/dcramer/django-debug-toolbar"&gt;django-debug-toolbar&lt;/a&gt;, which adds a popup overlay in our web application that has to be&amp;nbsp;&lt;a href="http://hustoknow.blogspot.com/2011/05/selenium-2-and-django-toolbar.html"&gt;disabled&lt;/a&gt;&amp;nbsp;in our application during Selenium tests.)&lt;/li&gt;&lt;li&gt;We've found that the new Selenium 2 WebDriver-based API is easier to train our developers to use. The documentation for Selenium 2 is still somewhat sparse, especially for the updated Python bindings, so digging into the source code (in our case,&amp;nbsp;&lt;a href="http://www.google.com/codesearch#2tHw6m3DZzo/trunk/py/selenium/webdriver/remote/webdriver.py&amp;amp;q=webdriver.py%20package:http://selenium%5C.googlecode%5C.com"&gt;remote/webdriver.py&lt;/a&gt;&amp;nbsp;and&amp;nbsp;&lt;a href="http://www.google.com/codesearch#2tHw6m3DZzo/trunk/py/selenium/webdriver/remote/webelement.py&amp;amp;q=webelement.py%20package:http://selenium%5C.googlecode%5C.com&amp;amp;type=cs"&gt;remote/webelement.py&lt;/a&gt;&amp;nbsp;code) is still the best way to learn what API commands are available. While Java developers may have access to&amp;nbsp;&lt;a href="http://seleniumhq.org/docs/03_webdriver.html"&gt;WebDriverBackedSelenium&lt;/a&gt;class that can use existing Selenium 1 code while leveraging the WebDriver-based API, we didn't find any similar support for Python. So we took the plunge and refactored most of our tests.&lt;br /&gt;&lt;em&gt;webdriver/remote/webelement.py:&lt;/em&gt;&lt;br /&gt;&lt;pre style="margin: 5px;"&gt;@property&lt;br /&gt;    def tag_name(self):&lt;br /&gt;        """Gets this element's tagName property."""&lt;br /&gt;        return self._execute(Command.GET_ELEMENT_TAG_NAME)['value']&lt;br /&gt;&lt;br /&gt;    @property&lt;br /&gt;    def text(self):&lt;br /&gt;        """Gets the text of the element."""&lt;br /&gt;        return self._execute(Command.GET_ELEMENT_TEXT)['value']&lt;br /&gt;&lt;br /&gt;    def click(self):&lt;br /&gt;        """Clicks the element."""&lt;br /&gt;        self._execute(Command.CLICK_ELEMENT)&lt;br /&gt;&lt;br /&gt;    def submit(self):&lt;br /&gt;        """Submits a form."""&lt;br /&gt;        self._execute(Command.SUBMIT_ELEMENT)&lt;br /&gt;&lt;br /&gt;    def clear(self):&lt;br /&gt;        """Clears the text if it's a text entry element."""&lt;br /&gt;        self._execute(Command.CLEAR_ELEMENT)&lt;/pre&gt;&lt;/li&gt;&lt;/ul&gt;On the server-end, it's important to study how the client API&amp;nbsp;is sending remote commands by reviewing the&amp;nbsp;&lt;a href="http://code.google.com/p/selenium/wiki/JsonWireProtocol"&gt;JsonWireProtocol&lt;/a&gt;&amp;nbsp;document posted on the Selenium Wiki,&amp;nbsp;especially since &lt;strong&gt;&lt;a href="http://www.saucelabs.com/"&gt;Sauce Labs&lt;/a&gt;&lt;/strong&gt; provides you with the raw logs to see what commands are actually being issued by the client.&lt;br /&gt;&lt;img alt="" class="aligncenter size-full wp-image-4589" height="405" src="http://saucelabs.com/blog/wp-content/uploads/2011/07/json.png" width="992" /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;While experimenting with Selenium 2, we found it much easier to test out the new WebDriver API by downloading and running the Selenium server locally. This way, your connection won't constantly timeout as a result of using your&amp;nbsp;Sauce Labs account, giving you more freedom to experiment with all the various commands. If you need to run browser tests against an external site while using your own machine to drive the browser interactions, you can setup a&amp;nbsp;&lt;a href="http://hustoknow.blogspot.com/2010/10/using-selenium-tests-with-djangopython.html"&gt;reverse&amp;nbsp;SSH tunnel&amp;nbsp;&lt;/a&gt;and then experiment with Selenium 2 API by setting debugger breakpoints and testing out the API bindings. In the long-term, though, you definitely want to use Sauce Labs for &lt;a href="http://saucelabs.com/ondemand"&gt;hosting all the virtual machines in the cloud&lt;/a&gt; for running your browser tests!&lt;/li&gt;&lt;li&gt;If you're interested in using Firebug to help debug your application, Selenium 2 also provides a way to&amp;nbsp;&lt;a href="http://hustoknow.blogspot.com/2011/05/injecting-firebug-into-remote-firefox.html"&gt;inject Firefox profiles&lt;/a&gt;. You can create a Firefox profile with this plug-in extension, and Selenium 2 includes an API that will base-64, zip-encode the profile that will&amp;nbsp;be downloaded by the remote host. Note that this approach works best if you're running the Selenium server locally, since using it over a Sauce Labs instance only gives you access to view the video.&lt;/li&gt;&lt;li&gt;Selenium 2 continues to be a moving target with its API, so you'll want to keep up to date with any&amp;nbsp;&lt;a href="http://seleniumhq.wordpress.com/"&gt;release notes&lt;/a&gt;&amp;nbsp;posted on the &lt;a href="http://seleniumhq.wordpress.com/"&gt;Selenium HQ blog&lt;/a&gt;. Most recently, we found that the toggle() and select() commands have not only been deprecated but removed completely from the implementation. If you try to issue these commands, the Selenium server simply doesn't recognize the commands and WebDriverExceptions are raised. The best thing to do is look at the Selenium version number. In this particular example, version 2.0.0 (three decimal places) are used to represent the release candidate of the latest Selenium build. You may also instantiate your .jar files with the -debug flag to watch how your client bindings execute API commands to the Selenium server.&lt;/li&gt;&lt;/ul&gt;&lt;pre style="margin: 5px;"&gt;20:38:02.687 INFO - Java: Sun Microsystems Inc. 20.1-b02&lt;br /&gt;20:38:02.687 INFO - OS: Windows XP 5.1 x86&lt;br /&gt;20:38:02.703 INFO - v2.0.0, with Core v2.0.0. Built from revision 12817&lt;/pre&gt;&lt;ul&gt;&lt;li&gt;Selenium 1 users will find that is_text_present() and wait_for_condition() commands no longer exist, and are replaced by a more DOM-driven approach of selecting the element first before firing click() events or retrieving attribute values through get_attribute(). You no longer have to have wait_for_condition() for page loads. Instead, you set implicitly_wait() to a certain timeout limit to rely on find_element_by_id() to wait for the presence of DOM elements to&amp;nbsp;appear to between page loads.&lt;/li&gt;&lt;li&gt;Lastly, we've noticed in the Selenium discussion groups that often there are questions about how to deal with concurrent Ajax requests during your tests.&amp;nbsp; In many test frameworks, there's the concept of setup and tear down of the database between each set of tests.&amp;nbsp; One issue that we encountered is that if your browser is issuing multiple&amp;nbsp;requests, you're better off waiting for the Ajax requests to complete in your tear down function since the requests could arrive when the database is an unknown state. If this happens, then your Selenium tests will fail and you're going to spend extra time trying to track down these race conditions. If you're using jQuery, you can check the ajax.global state to determine whether to proceed between pages (i.e. execute_script("return jQuery.active === 0")). You'll want to keep looping until this condition is satisfied (for an example of implementing your own wait_for_condition() command, click&amp;nbsp;&lt;a href="http://hustoknow.blogspot.com/2011/05/migrating-from-selenium-10-to-selenium.html"&gt;here.&lt;/a&gt;)&lt;/li&gt;&lt;/ul&gt;Hope you find these tips helpful for migrating over to Selenium 2. Happy testing!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4137806788465333774-865937905903153221?l=hustoknow.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hustoknow.blogspot.com/feeds/865937905903153221/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hustoknow.blogspot.com/2011/07/selenium-20-migration-lessons.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/865937905903153221'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/865937905903153221'/><link rel='alternate' type='text/html' href='http://hustoknow.blogspot.com/2011/07/selenium-20-migration-lessons.html' title='Selenium 2.0 migration lessons'/><author><name>Roger</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4137806788465333774.post-3363954151053071160</id><published>2011-07-19T07:25:00.000-07:00</published><updated>2011-07-19T07:25:15.786-07:00</updated><title type='text'>Issues with Vista/Windows 7 with the Kinesis Keyboard</title><content type='html'>&lt;div style="font-family: Arial, Helvetica, sans-serif; font-size: 13px;"&gt;Kinesis has a few suggestions that may help resolve issues with the Kinesis on Vista/Windows 7.&lt;/div&gt;&lt;div style="font-family: Arial, Helvetica, sans-serif; font-size: 13px;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-family: Arial, Helvetica, sans-serif; font-size: 13px;"&gt;The suggestions seem to be:&lt;/div&gt;&lt;div style="font-family: Arial, Helvetica, sans-serif; font-size: 13px;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-family: Arial, Helvetica, sans-serif; font-size: 13px;"&gt;1. Hit Shift, both Ctrl keys, and both Alt keys to reset.&lt;/div&gt;&lt;div style="font-family: Arial, Helvetica, sans-serif; font-size: 13px;"&gt;2. Change advanced power settings (USB) and set to disabled.&lt;/div&gt;&lt;div style="font-family: Arial, Helvetica, sans-serif; font-size: 13px;"&gt;3. Replace the firmware (only through Kinesis) and/or firmware board.&lt;/div&gt;&lt;div style="font-family: Arial, Helvetica, sans-serif; font-size: 13px;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-family: Arial, Helvetica, sans-serif; font-size: 13px;"&gt;&lt;b&gt;&lt;a href="http://www.kinesis-ergo.com/tech_support/trouble.htm#q8"&gt;http://www.kinesis-ergo.com/tech_support/trouble.htm#q8&lt;/a&gt;&lt;/b&gt;&lt;/div&gt;&lt;div style="font-family: Arial, Helvetica, sans-serif; font-size: 13px;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-family: Arial, Helvetica, sans-serif; font-size: 13px;"&gt;&lt;/div&gt;&lt;b&gt;2.&amp;nbsp;&lt;a href="" name="q2"&gt;Advantage keyboard not working after Windows computer goes to sleep mode&lt;/a&gt;&lt;/b&gt;&lt;br /&gt;We have learned of a widely reported problem with Windows operating systems (XP &amp;amp; Vista) using third party keyboards (even though the problem has been reported with the Microsoft Natural keyboard as well) where the keyboard does not function after the computer enters sleep or hibernation mode. Fortunately there is a easy solution:&lt;br /&gt;Go to Start-Settings-Control Panel, double click Keyboard. Click the Hardware Tab, highlight the "HID Keyboard device" and select Properties. Click Power Management.&amp;nbsp;&lt;strong&gt;UNCHECK&lt;/strong&gt;&amp;nbsp;the box that says "Allow this device to bring the computer out of standby." Click OK. Now when your computer goes to standby or sleep mode, you will simply need to wake the computer by moving your pointing device. Your keyboard should now work perfectly fine.&lt;br /&gt;For&amp;nbsp;&lt;strong&gt;Vista&lt;/strong&gt;&amp;nbsp;users, go to Control Panel, Power Options, click "change plan settings" then "change advanced power settings" then go to "USB settings" and select "disabled."&lt;br /&gt;&lt;b&gt;&lt;a href="http://www.kinesis-ergo.com/tech_support/trouble.htm#top" style="color: blue; text-decoration: none;"&gt;Back to Top&lt;/a&gt;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="font-family: Arial, Helvetica, sans-serif; font-size: 13px;"&gt;&lt;b&gt;8.&amp;nbsp;&lt;a href="" name="q8"&gt;Weird behavior with contoured keyboard&lt;/a&gt;&lt;/b&gt;&lt;br /&gt;&lt;span style="color: blue;"&gt;Stuck modifier key&lt;/span&gt;. Sometimes the computer misses the upstroke from keys that are held down in key combinations.&amp;nbsp;&amp;nbsp; The result is a "stuck" key.&amp;nbsp; Try pressing both&amp;nbsp;&lt;strong&gt;Shift&amp;nbsp;&lt;/strong&gt;keys, both&lt;strong&gt;Ctrl&amp;nbsp;&lt;/strong&gt;keys&lt;strong&gt;,&amp;nbsp;&lt;/strong&gt;and both&amp;nbsp;&lt;strong&gt;Alt&amp;nbsp;&lt;/strong&gt;keys. If this problem happens to your contoured keyboard more than once every few weeks, you may need a firmware upgrade or new main circuit board.&amp;nbsp;&lt;/div&gt;&lt;div style="font-family: Arial, Helvetica, sans-serif; font-size: 13px;"&gt;&lt;span style="color: blue;"&gt;Check your firmware version (serial numbers 20,000 and higher).&amp;nbsp;&lt;/span&gt;&lt;span style="color: black;"&gt;Open a text editor other than Microsoft Word (e.g notepad, wordpad or equivalent). Press both&amp;nbsp;&lt;/span&gt;&lt;strong&gt;Shift&amp;nbsp;&lt;/strong&gt;keys&lt;span style="color: black;"&gt;&amp;nbsp;plus&amp;nbsp;&lt;/span&gt;&lt;strong&gt;F12&lt;/strong&gt;&lt;span style="color: black;"&gt;. The keyboard will produce a sentence which ends with the firmware version number and version date, such as:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; copyright 1986 - 1998 by interfatron-bbc, ltd.,&amp;nbsp;&lt;/span&gt;&lt;span style="color: blue;"&gt;rev 2.48 08/13/98.&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: Arial, Helvetica, sans-serif; font-size: 13px;"&gt;&lt;span style="color: blue;"&gt;Circuit board may need replacement.&amp;nbsp;&lt;/span&gt;&lt;span style="color: black;"&gt;If your contoured keyboard was built before April, 1998, it may need a new main circuit board. The old boards look brown if you look past the thumb keys at the underlying circuit board.&amp;nbsp; The new circuit boards look green with a gold grid.&amp;nbsp;&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: Arial, Helvetica, sans-serif; font-size: 13px;"&gt;&lt;b&gt;&lt;a href="http://www.kinesis-ergo.com/tech_support/trouble.htm#top" style="color: blue; text-decoration: none;"&gt;Back to Top&lt;/a&gt;&lt;/b&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4137806788465333774-3363954151053071160?l=hustoknow.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hustoknow.blogspot.com/feeds/3363954151053071160/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hustoknow.blogspot.com/2011/07/issues-with-vistawindows-7-with-kinesis.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/3363954151053071160'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/3363954151053071160'/><link rel='alternate' type='text/html' href='http://hustoknow.blogspot.com/2011/07/issues-with-vistawindows-7-with-kinesis.html' title='Issues with Vista/Windows 7 with the Kinesis Keyboard'/><author><name>Roger</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4137806788465333774.post-4325960376994485353</id><published>2011-07-13T01:40:00.000-07:00</published><updated>2011-07-13T01:40:55.836-07:00</updated><title type='text'>sudo race condition in Fabric 1.0.0</title><content type='html'>If you've used Fabric with sudo, you might notice keystrokes during the password phase sometimes doesn't work.  The problem with Fabric for sudo() command is tracked here:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://code.fabfile.org/issues/show/320"&gt;http://code.fabfile.org/issues/show/320&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;The diff for Fabric 1.0.2 is here:&lt;br /&gt;&lt;a href="http://code.fabfile.org/repositories/diff/fabric/efd9fef820dd6f2a593c172eac7e500c5e8f0602"&gt;http://code.fabfile.org/repositories/diff/fabric/efd9fef820dd6f2a593c172eac7e500c5e8f0602&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;It appears that the fix basically shuts off the input_loop when the connection is attempting to use get_pass() when performing an SSH connection to another machine with Fabric.  The problem is &lt;br /&gt;that while Fabric claims it is single-threaded, but it still creates thread handlers for standard input/output:&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;        workers = (&lt;br /&gt;            ThreadHandler('out', output_loop, channel, "recv", stdout),&lt;br /&gt;            ThreadHandler('err', output_loop, channel, "recv_stderr", stderr),&lt;br /&gt;            ThreadHandler('in', input_loop, channel, using_pty)&lt;br /&gt;        )&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The fix disables input checking so that the getpass inside the prompt_for_password() command can work correctly.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4137806788465333774-4325960376994485353?l=hustoknow.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hustoknow.blogspot.com/feeds/4325960376994485353/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hustoknow.blogspot.com/2011/07/sudo-race-condition-in-fabric-100.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/4325960376994485353'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/4325960376994485353'/><link rel='alternate' type='text/html' href='http://hustoknow.blogspot.com/2011/07/sudo-race-condition-in-fabric-100.html' title='sudo race condition in Fabric 1.0.0'/><author><name>Roger</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4137806788465333774.post-3876437209758177676</id><published>2011-07-12T15:15:00.001-07:00</published><updated>2011-07-12T15:15:18.594-07:00</updated><title type='text'>Pruning GitHub / merged</title><content type='html'>Tired of all your outdated branches?  Now there's a way..&lt;br /&gt;&lt;pre class="prettyprint"&gt;git branch --merged | grep -v "^* " | awk '{print "git branch -d " $1 "; git push origin :" $1}' | sh&lt;br /&gt;&lt;/pre&gt;What it boils down to:&lt;br /&gt;&lt;pre&gt;git branch --merged = shows all the merged ones&lt;br /&gt;grep -v "^*" = excludes the current branch (can't delete a branch you're currently on)&lt;br /&gt;awk '{print "git branch -d " $1 "; git push origin :" $1}' = delete merged branch and delete it on GitHub.&lt;br /&gt;&lt;/pre&gt;If you want to double-check, take away the last "| sh" to verify everything works.  You have been warned.&lt;br /&gt;&lt;br /&gt;You may get some errors if the branch that has already been merged no longer exists on your GitHub origin..&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4137806788465333774-3876437209758177676?l=hustoknow.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hustoknow.blogspot.com/feeds/3876437209758177676/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hustoknow.blogspot.com/2011/07/pruning-github-merged.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/3876437209758177676'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/3876437209758177676'/><link rel='alternate' type='text/html' href='http://hustoknow.blogspot.com/2011/07/pruning-github-merged.html' title='Pruning GitHub / merged'/><author><name>Roger</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4137806788465333774.post-2247134848366376411</id><published>2011-07-12T14:00:00.000-07:00</published><updated>2011-07-12T14:00:46.062-07:00</updated><title type='text'>Django and large datasets</title><content type='html'>If you've worked with large data sets with Python/Django, memory consumption can hit 2GB on dev, even if settings.DEBUG=False..&lt;br /&gt;&lt;br /&gt;Django's documentation about querysets hints about the issue does little to mention this issue about big datasets:&lt;br /&gt;&lt;br /&gt;&lt;a href="https://docs.djangoproject.com/en/dev/topics/db/queries/#"&gt;https://docs.djangoproject.com/en/dev/topics/db/queries/#&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;Caching and QuerySets&lt;br /&gt;Each QuerySet contains a cache, to minimize database access. It's important to understand how it works, in order to write the most efficient code.&lt;br /&gt;&lt;br /&gt;In a newly created QuerySet, the cache is empty. The first time a QuerySet is evaluated -- and, hence, a database query happens -- Django saves the query results in the QuerySet's cache and returns the results that have been explicitly requested (e.g., the next element, if the QuerySet is being iterated over). Subsequent evaluations of the QuerySet reuse the cached results.&lt;br /&gt;&lt;br /&gt;Keep this caching behavior in mind, because it may bite you if you don't use your QuerySets correctly. For example, the following will create twoQuerySets, evaluate them, and throw them away:&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;...so if you do queries against big datasets (i.e.): &lt;br /&gt;&lt;br /&gt;for item in MyTable.objects.all():&lt;br /&gt;&lt;br /&gt;...memory consumption can hit the roof.  The culprit was iterating through all objects (475,000)...as the size increases, the self._result_cache in the QuerySet object just keeps appending.  Even though Django fetches 100 rows at a time from MySQL, each time you iterate, it just adds to the result_cache array such that you will end up caching all 475K objects.  No clearing, nada.  &lt;br /&gt;&lt;br /&gt;Disqus approached the problem by implementing a SkinnyQuerySet to avoid storing extra data in memory, though it's not clear to me that the changes they discussed attempted to query against a large dataset (i.e. not using LIMIT range queries) See p. 25 at &lt;a href="http://www.scribd.com/doc/37072033/DjangoCon-2010-Scaling-Disqus"&gt;http://www.scribd.com/doc/37072033/DjangoCon-2010-Scaling-Disqus&lt;/a&gt; (or the source code for SkinnyQuerySet at https://gist.github.com/550438).  FYI -- the code also changes to prevent you from doing list() over a QuerySet.&lt;br /&gt;&lt;br /&gt;The code that does all this stuff is here (ITER_CHUNK_SIZE is 100):&lt;br /&gt;external/django/db/models/query.py(757)_fill_cache()&lt;br /&gt;&lt;pre class="prettyprint"&gt; def _result_iter(self):&lt;br /&gt;     pos = 0&lt;br /&gt;     while 1:&lt;br /&gt;         upper = len(self._result_cache)&lt;br /&gt;         while pos &lt; upper:&lt;br /&gt;             yield self._result_cache[pos]&lt;br /&gt;             pos = pos + 1&lt;br /&gt;         if not self._iter:&lt;br /&gt;             raise StopIteration&lt;br /&gt;         if len(self._result_cache) &lt;= pos:&lt;br /&gt;             self._fill_cache()&lt;br /&gt;&lt;br /&gt;def _fill_cache(self, num=None):&lt;br /&gt;    """&lt;br /&gt;    Fills the result cache with 'num' more entries (or until the results&lt;br /&gt;    iterator is exhausted).&lt;br /&gt;    """&lt;br /&gt;    if self._iter:&lt;br /&gt;&lt;br /&gt;        try:&lt;br /&gt;            for i in range(num or ITER_CHUNK_SIZE):&lt;br /&gt;                self._result_cache.append(self._iter.next())&lt;br /&gt;        except StopIteration:&lt;br /&gt;            self._iter = None&lt;br /&gt;&lt;/pre&gt;The iterator() fixes the problem with Django caching over time, but I realized that the problem happened on the first iteration of a queryset, and looking at this object reference graph (you have to install the objgraph library) I inspected one of these objects and you can see that it's part of all the objects already loaded in MySQLdb's _rows dictionary after just one QuerySet iteration.   So basically any QuerySet even with iterator() will store the entire SQL result in memory.&lt;pre clas="prettyprint"&gt;(Pdb) import objgraph&lt;br /&gt;(Pdb) objgraph.show_most_common_types(limit=20)&lt;br /&gt;tuple                      41682&lt;br /&gt;&lt;br /&gt;(Pdb) next&lt;br /&gt;-&gt; if not result_type:&lt;br /&gt;(Pdb) import objgraph&lt;br /&gt;(Pdb) objgraph.show_most_common_types(limit=20)&lt;br /&gt;tuple                      419858&lt;br /&gt;&lt;/pre&gt;What ultimately is happening is that it generates SQL command, sends it over to the Python MySQLdb wrapper, which eventually calls cursor.execute(), which calls store_result(), which stores the entire result set in memory (see store_result() reference in http://mysql-python.sourceforge.net/MySQLdb.html)  The tradeoff is either to load everything in memory or one row at a time.  Obviously we have more control and do LIMIT ranges to try to balance between too much memory and too many DB connections.&lt;pre class="prettyprint"&gt;django/db/models/sql/compiler.py:&lt;br /&gt;728  -&gt;         cursor = self.connection.cursor()&lt;br /&gt;729             cursor.execute(sql, params)&lt;br /&gt;&lt;br /&gt;/usr/local/lib/python2.6/dist-packages/MySQLdb/cursors.py(313)&lt;br /&gt;"""This is a MixIn class which causes the entire result set to be&lt;br /&gt;stored on the client side, i.e. it uses mysql_store_result(). If the&lt;br /&gt;result set can be very large, consider adding a LIMIT clause to your&lt;br /&gt;query, or using CursorUseResultMixIn instead."""&lt;br /&gt;&lt;br /&gt;def _get_result(self): return self._get_db().store_result()&lt;br /&gt;&lt;/pre&gt;The solution seems to use an implementation similar to query_iterator where you do LIMIT's upfront on QuerySet (you can also use iterator() to avoid caching the result set...so it seems a combination of Disqus and this snippet &lt;a href="http://djangosnippets.org/snippets/1949/"&gt;http://djangosnippets.org/snippets/1949/&lt;/a&gt;...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4137806788465333774-2247134848366376411?l=hustoknow.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hustoknow.blogspot.com/feeds/2247134848366376411/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hustoknow.blogspot.com/2011/07/django-and-large-datasets.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/2247134848366376411'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/2247134848366376411'/><link rel='alternate' type='text/html' href='http://hustoknow.blogspot.com/2011/07/django-and-large-datasets.html' title='Django and large datasets'/><author><name>Roger</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4137806788465333774.post-2760295522447257399</id><published>2011-07-12T03:16:00.000-07:00</published><updated>2011-07-12T07:38:31.526-07:00</updated><title type='text'>Selenium 2 RC3 and Python bindings have removed toggle() and select()...</title><content type='html'>&lt;pre class="prettyprint"&gt;-    def toggle(self):&lt;br /&gt;76   &lt;br /&gt;-        """Toggles the element state."""&lt;br /&gt;77   &lt;br /&gt;-        resp = self._execute(Command.TOGGLE_ELEMENT)&lt;br /&gt;78   &lt;br /&gt;-        return resp['value']&lt;br /&gt;79   &lt;br /&gt;-&lt;br /&gt;80 68 &lt;br /&gt;     def is_selected(self):&lt;br /&gt;81 69 &lt;br /&gt;         """Whether the element is selected."""&lt;br /&gt;82 70 &lt;br /&gt;         return self._execute(Command.IS_ELEMENT_SELECTED)['value']&lt;br /&gt;83 71 &lt;br /&gt; &lt;br /&gt;84   &lt;br /&gt;-    def select(self):&lt;br /&gt;85   &lt;br /&gt;-        """Selects an element."""&lt;br /&gt;86   &lt;br /&gt;-        self._execute(Command.SET_ELEMENT_SELECTED)&lt;br /&gt;87   &lt;br /&gt;-&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;IEDriver.dll changes:&lt;br /&gt;&lt;a href="http://code.google.com/p/selenium/source/browse/trunk/cpp/IEDriver/CommandHandlers/ClickElementCommandHandler.h?spec=svn12477&amp;r=12477"&gt;http://code.google.com/p/selenium/source/browse/trunk/cpp/IEDriver/CommandHandlers/ClickElementCommandHandler.h?spec=svn12477&amp;r=12477&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4137806788465333774-2760295522447257399?l=hustoknow.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hustoknow.blogspot.com/feeds/2760295522447257399/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hustoknow.blogspot.com/2011/07/selenium-2-rc3-and-python-bindings-have.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/2760295522447257399'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/2760295522447257399'/><link rel='alternate' type='text/html' href='http://hustoknow.blogspot.com/2011/07/selenium-2-rc3-and-python-bindings-have.html' title='Selenium 2 RC3 and Python bindings have removed toggle() and select()...'/><author><name>Roger</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4137806788465333774.post-3584673513284851110</id><published>2011-07-08T17:22:00.000-07:00</published><updated>2011-07-08T18:25:26.687-07:00</updated><title type='text'>How Facebook invalidates fbs_ cookies through all.js...</title><content type='html'>If you've ever had to integrate with &lt;a href="http://developers.facebook.com/blog/post/108/"&gt;Facebook Connec&lt;/a&gt;t, which allows third-party web sites to rely on Facebook for user authentication, you may find yourself wanting to test whether your integration works.   You can obviously use Selenium to automate the login process through the popup window, but what if you just wanted to try to skip this step and set the cookie yourself?  &lt;br /&gt;&lt;br /&gt;Each time you login through the Facebook popup window, the JavaScript code will set an fbs_ cookie.  The xxx in the fbs_xxxx cookie refers to the Facebook API key, and is signed with your application's secret key.  If you have an infinite session key and a valid application access token, you can create your own fbs_xxx cookie.  You can basically reverse the process that is normally done by the get_user_from_cookie from the Python Graph API library:&lt;br /&gt;&lt;pre class="prettyprint"&gt;def get_user_from_cookie(cookies, app_id, app_secret):&lt;br /&gt;    cookie = cookies.get("fbs_" + app_id, "")&lt;br /&gt;    if not cookie: return None&lt;br /&gt;    args = dict((k, v[-1]) for k, v in cgi.parse_qs(cookie.strip('"')).items())&lt;br /&gt;    payload = "".join(k + "=" + args[k] for k in sorted(args.keys())&lt;br /&gt;                      if k != "sig")&lt;br /&gt;    sig = hashlib.md5(payload + app_secret).hexdigest()&lt;br /&gt;    expires = int(args["expires"])&lt;br /&gt;    if sig == args.get("sig") and (expires == 0 or time.time() &amp;lt; expires):&lt;br /&gt;        return args&lt;br /&gt;    else:&lt;br /&gt;        return None&lt;br /&gt;&lt;/pre&gt;However, even when using a valid cookie signed by your app, it appears that Facebook invalidates the token.  You end up seeing the same login popup window, but if you remove the all.js library and use a fake substitute, you'll notice that your app all of a suddenly works.  So Facebook is doing something internally to disallow self-signed fbs_cookies.&lt;br /&gt;&lt;pre class="prettyprint"&gt;function do_nothing(a, b, c, d, e, f) {&lt;br /&gt;    return true;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;var FB = {&lt;br /&gt;    'init': do_nothing,&lt;br /&gt;    'getLoginStatus': function () {},&lt;br /&gt;    'login': do_nothing&lt;br /&gt;};&lt;br /&gt;&lt;/pre&gt;When you first run the init() function for the Facebook JavaScript SDK code, it first loads the cookie that you set (fbs_ + API_KEY) and sets FB_session to correspond to these cookie values.  The problem is that another auth.request command is sent to Facebook.  This auth.request command tries to verify whether a session key was obtained from Facebook.  If it fails, then the FB._session object is cleared and that pop-up window appears asking you to login.  The Facebook JavaScript does this auth.request by injecting an &amp;lt;iframe src="http://www.facebook.com/extern/login_status.php?"&amp;gt; with three different callback functions in the URL query string (no_user, no_session, and ok_session).  &lt;br /&gt;&lt;br /&gt;The result from this IFrame request basically used to invoke the response, which is handled by either the no_user, no_session, or ok_session xdResponseWrappers.  If the no_user/no_session response wrappers, then the 'connected' state will not be set internally and the session will be cleared.  The code that invalidates this is located inside the cross-domain (aka 'xd') xdResponseWrapper function inside all.js:  &lt;br /&gt;&lt;pre class="prettyprint"&gt;xdResponseWrapper: function(a, c, b) {&lt;br /&gt;   return function(d) {&lt;br /&gt;     try {&lt;br /&gt;       b = FB.JSON.parse(d.session);&lt;br /&gt;     } catch (f) {}&lt;br /&gt;     if (b) c = 'connected';&lt;br /&gt;     var e = FB.Auth.setSession(b || null, c);&lt;br /&gt;     e.perms = d &amp;amp;&amp;amp; d.perms || null;&lt;br /&gt;     a &amp;amp;&amp;amp; a(e);&lt;br /&gt;   };&lt;br /&gt;&lt;/pre&gt;When the no_user response is called, d.session apparently is null, which will then be used by FB.Auth.setSession() to clear the FB._session object.    You can't easily set a breakpoint on how this happens because the Facebook all.js code creates a function that does so, so it isn't apparent unless you insert alert() statements.  But basically if you put alert statements, you will observe that the no_user result is returned when you attempt to just set the cookie and login to your site.  &lt;br /&gt;&lt;br /&gt;It looks like Facebook has implemented this policy to try to prevent people from using robots to directly login to their site.  It also appears that there are additional cookies that must be set (datr, lsd, c_user, h_user, lxe, and xs) in order for the login_status.php to return back the ok_session callback.  You can observe this behavior by monitoring the cookie traffic if you were to login through the popup window.   &lt;br /&gt;&lt;br /&gt;Apparently the 'xs' cookie is a well-known Facebook cookie too:  &lt;a href="http://www.blogger.com/believe%20there%20are%20additional%20cookies%20that%20must%20be%20set%20(datr,%20lsd,%20c_user,%20h_user,%20lxe,%20and%20xs)%20in%20order%20for%20the%20login_status.php%20to%20return%20back%20ok_session.%20%20You%20can%20observe%20this%20behavior%20by%20monitoring%20the%20cookie%20traffic%20if%20you%20were%20to%20login%20through%20the%20popup%20window.%20%20Discussions%20also%20here:http://forum.developers.facebook.net/viewtopic.php?id=50502Apparently%20the%20'xs'%20cookie%20is%20a%20well-known%20Facebook%20cookie%20too:http://www.duke.edu/~jyw2/wwwsecurity.html"&gt;http://www.duke.edu/~jyw2/wwwsecurity.html&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4137806788465333774-3584673513284851110?l=hustoknow.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hustoknow.blogspot.com/feeds/3584673513284851110/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hustoknow.blogspot.com/2011/07/how-facebook-invalidates-fbs-cookies.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/3584673513284851110'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/3584673513284851110'/><link rel='alternate' type='text/html' href='http://hustoknow.blogspot.com/2011/07/how-facebook-invalidates-fbs-cookies.html' title='How Facebook invalidates fbs_ cookies through all.js...'/><author><name>Roger</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4137806788465333774.post-8448393513836557340</id><published>2011-06-25T05:01:00.001-07:00</published><updated>2011-06-27T00:46:32.409-07:00</updated><title type='text'>Deconstructing Facebook's Flash Cross Domain Handler in IE..</title><content type='html'>I got tired of trying to understand why Facebook kept breaking on their IE support, so decided to peer directly into how they use Flash to implement the cross-domain receiver (you can see inside the .js file they supply that it fetches the SWF file&amp;nbsp;&lt;span class="Apple-style-span" style="border-collapse: collapse; font-family: arial, sans-serif; font-size: 13px;"&gt;&lt;a href="http://static.ak.fbcdn.net/rsrc.php/v1/yx/r/WFg56j28XFs.swf" style="color: #0000cc;" target="_blank"&gt;http://static.ak.fbcdn.&lt;wbr&gt;&lt;/wbr&gt;net/rsrc.php/v1/yx/r/&lt;wbr&gt;&lt;/wbr&gt;WFg56j28XFs.swf&lt;/a&gt;&lt;/span&gt;&amp;nbsp;I now suspect the problems Fri. morning that other people reported had to do with 64-bit Internet Explorer, since the Adobe Flash version is still highly experimental, and rebooting my machine (and my other machines at work had no problems) solved the issue.&lt;br /&gt;&lt;br /&gt;Nonetheless, the hard part was to find a decompiler that could convert the SWF file that gets loaded in Internet Explorer back into bytecode. &amp;nbsp;I tried to use flasm but this file was encoded using Adobe Flex 4, which was released around March 2010 so the open source tools are a bit scant. &amp;nbsp; I ended up using an unregistered version of Sothink's SWFDecompiler, which wants $70.00 for a license. &amp;nbsp;An older 6.0 version seems to have some quirky bugs where it actually lets you export the ActionScript files back to the original format. &amp;nbsp;Regardless, the program preserved all the original variable names, so you can pretty much have the original format, and I found a way to post the decompiled files in their entirety here:&lt;br /&gt;&lt;a href="http://hustoknow.blogspot.com/2011/06/facebooks-flash-xdcomm-receiver.html" style="color: #0000cc;" target="_blank"&gt;http://hustoknow.blogspot.com/&lt;wbr&gt;&lt;/wbr&gt;2011/06/facebooks-flash-&lt;wbr&gt;&lt;/wbr&gt;xdcomm-receiver.html&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;The way in which Facebook has implemented cross-domain message passing in Internet Explorer mimics the use of the HTML5 postMessage. &amp;nbsp;Instead of creating an event sender/listener for messages from a different window to pass, Facebook relies on SWF objects to talk with other in Internet Explorer. In Flash, there is the concept of LocalConnection objects, where SWF objects can talk with other even when they are on different domains. &amp;nbsp;&amp;nbsp;It injects this SWF file into both&amp;nbsp;&lt;a href="http://360.hearsaylabs.com/" style="color: #0000cc;" target="_blank"&gt;y&lt;/a&gt;our site&amp;nbsp;as well as after you login through the window popup (facebook.com).&lt;br /&gt;&lt;br /&gt;&amp;lt;object type="application/x-shockwave-&lt;wbr&gt;&lt;/wbr&gt;flash" id="XdComm" name="XdComm" classid="clsid:d27cdb6e-ae6d-&lt;wbr&gt;&lt;/wbr&gt;11cf-96b8-444553540000" allowscriptaccess="always"&amp;gt;&amp;lt;&lt;wbr&gt;&lt;/wbr&gt;param name="movie" value="&lt;a href="https://s-static.ak.fbcdn.net/rsrc.php/v1/yx/r/WFg56j28XFs.swf" style="color: #0000cc;" target="_blank"&gt;https://s-static.ak.&lt;wbr&gt;&lt;/wbr&gt;fbcdn.net/rsrc.php/v1/yx/r/&lt;wbr&gt;&lt;/wbr&gt;WFg56j28XFs.swf&lt;/a&gt;"&amp;gt;&amp;lt;/param&amp;gt;&amp;lt;&lt;wbr&gt;&lt;/wbr&gt;param name="allowscriptaccess" value="always"&amp;gt;&amp;lt;/param&amp;gt;&amp;lt;/&lt;wbr&gt;&lt;/wbr&gt;object&amp;gt;&lt;br /&gt;&lt;br /&gt;So when you go to&amp;nbsp;any Facebook Connect-enabled web site, you instantiate a SWF object that creates a XdComm object, along with an extra postMessage object. &amp;nbsp; The XdComm class holds a PostMessage() object, which in turns instantiates a LocalConnection() object, which is basically becomes a port sender/receiver for messages.&amp;nbsp; Adobe SWF objects, which are contained within ActiveX can be accessed via JavaScript, so the object tag gets added with the XdComm ID and Facebook's JavaScript code can access the postMessage_init() function, which is initialized in the SWF file.&lt;br /&gt;&lt;br /&gt;&lt;pre style="border-bottom-color: rgb(136, 136, 136); border-bottom-style: solid; border-bottom-width: 1px; border-left-color: rgb(136, 136, 136); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(136, 136, 136); border-right-style: solid; border-right-width: 1px; border-top-color: rgb(136, 136, 136); border-top-style: solid; border-top-width: 1px; font-size: 10px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 2px; padding-left: 2px; padding-right: 2px; padding-top: 2px; white-space: pre-wrap;"&gt;&lt;span class="Apple-style-span" style="border-collapse: collapse; font-family: arial, sans-serif; font-size: 13px;"&gt;&lt;span style="color: #333333; font-family: Arial, serif; font-size: 14px; line-height: 22px;"&gt;&lt;span style="color: black;"&gt;   &lt;/span&gt;&lt;span style="color: #000088;"&gt;public&lt;/span&gt;&lt;span style="color: black;"&gt; &lt;/span&gt;&lt;span style="color: #000088;"&gt;function&lt;/span&gt;&lt;span style="color: black;"&gt; &lt;/span&gt;&lt;span style="color: #660066;"&gt;PostMessage&lt;/span&gt;&lt;span style="color: #666600;"&gt;()&lt;/span&gt;&lt;span style="color: black;"&gt;&lt;br /&gt;        &lt;/span&gt;&lt;span style="color: #666600;"&gt;{&lt;/span&gt;&lt;span style="color: black;"&gt;&lt;br /&gt;            &lt;/span&gt;&lt;span style="color: #000088;"&gt;this&lt;/span&gt;&lt;span style="color: #666600;"&gt;.&lt;/span&gt;&lt;span style="color: black;"&gt;connection &lt;/span&gt;&lt;span style="color: #666600;"&gt;=&lt;/span&gt;&lt;span style="color: black;"&gt; &lt;/span&gt;&lt;span style="color: #000088;"&gt;new&lt;/span&gt;&lt;span style="color: black;"&gt; &lt;/span&gt;&lt;span style="color: #660066;"&gt;LocalConnection&lt;/span&gt;&lt;span style="color: #666600;"&gt;();&lt;/span&gt;&lt;span style="color: black;"&gt;&lt;br /&gt;            &lt;/span&gt;&lt;span style="color: #000088;"&gt;this&lt;/span&gt;&lt;span style="color: #666600;"&gt;.&lt;/span&gt;&lt;span style="color: black;"&gt;connection&lt;/span&gt;&lt;span style="color: #666600;"&gt;.&lt;/span&gt;&lt;span style="color: black;"&gt;client &lt;/span&gt;&lt;span style="color: #666600;"&gt;=&lt;/span&gt;&lt;span style="color: black;"&gt; &lt;/span&gt;&lt;span style="color: #000088;"&gt;this&lt;/span&gt;&lt;span style="color: #666600;"&gt;;&lt;/span&gt;&lt;span style="color: black;"&gt;&lt;br /&gt;            &lt;/span&gt;&lt;span style="color: #000088;"&gt;this&lt;/span&gt;&lt;span style="color: #666600;"&gt;.&lt;/span&gt;&lt;span style="color: black;"&gt;connection&lt;/span&gt;&lt;span style="color: #666600;"&gt;.&lt;/span&gt;&lt;span style="color: black;"&gt;connect&lt;/span&gt;&lt;span style="color: #666600;"&gt;(&lt;/span&gt;&lt;span style="color: #660066;"&gt;Math&lt;/span&gt;&lt;span style="color: #666600;"&gt;.&lt;/span&gt;&lt;span style="color: black;"&gt;r&lt;wbr&gt;&lt;/wbr&gt;andom&lt;/span&gt;&lt;span style="color: #666600;"&gt;().&lt;/span&gt;&lt;span style="color: black;"&gt;toString&lt;/span&gt;&lt;span style="color: #666600;"&gt;());&lt;/span&gt;&lt;span style="color: black;"&gt;&lt;br /&gt;            &lt;/span&gt;&lt;span style="color: #660066;"&gt;ExternalInterface&lt;/span&gt;&lt;span style="color: #666600;"&gt;.&lt;/span&gt;&lt;span style="color: black;"&gt;addCallback&lt;/span&gt;&lt;span style="color: #666600;"&gt;(&lt;/span&gt;&lt;span style="color: #008800;"&gt;&lt;wbr&gt;&lt;/wbr&gt;"postMessage_init"&lt;/span&gt;&lt;span style="color: #666600;"&gt;,&lt;/span&gt;&lt;span style="color: black;"&gt; &lt;/span&gt;&lt;span style="color: #000088;"&gt;this&lt;/span&gt;&lt;span style="color: #666600;"&gt;.&lt;/span&gt;&lt;span style="color: black;"&gt;init&lt;/span&gt;&lt;span style="color: #666600;"&gt;);&lt;/span&gt;&lt;span style="color: black;"&gt;&lt;br /&gt;            &lt;/span&gt;&lt;span style="color: #660066;"&gt;ExternalInterface&lt;/span&gt;&lt;span style="color: #666600;"&gt;.&lt;/span&gt;&lt;span style="color: black;"&gt;addCallback&lt;/span&gt;&lt;span style="color: #666600;"&gt;(&lt;/span&gt;&lt;span style="color: #008800;"&gt;&lt;wbr&gt;&lt;/wbr&gt;"postMessage_send"&lt;/span&gt;&lt;span style="color: #666600;"&gt;,&lt;/span&gt;&lt;span style="color: black;"&gt; &lt;/span&gt;&lt;span style="color: #000088;"&gt;this&lt;/span&gt;&lt;span style="color: #666600;"&gt;.&lt;/span&gt;&lt;span style="color: black;"&gt;send&lt;/span&gt;&lt;span style="color: #666600;"&gt;);&lt;/span&gt;&lt;span style="color: black;"&gt;&lt;br /&gt;            &lt;/span&gt;&lt;span style="color: #000088;"&gt;return&lt;/span&gt;&lt;span style="color: #666600;"&gt;;&lt;/span&gt;&lt;span style="color: black;"&gt;&lt;br /&gt;        &lt;/span&gt;&lt;span style="color: #666600;"&gt;}&lt;/span&gt;&lt;span style="color: #880000;"&gt;// end function&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;/div&gt;&lt;div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;span class="Apple-style-span" style="border-collapse: collapse; font-family: arial, sans-serif; font-size: 13px;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;When you do an FB.init() on &amp;nbsp;your localhost, it instantiates FB.XD._openerOrigin (i.e. http://dev.myhost.com/ + 16-character random string), &amp;nbsp;which is used to define the connection name that SWF objects will be use to communicate. &amp;nbsp;&amp;nbsp;The FB.XD_openerOrign was the key to the problems this past week -- Facebook was messing around with stuff for IE8, and broke the connection name that the SWF objects would agree on listening. &amp;nbsp; They later fixed it based on the reviews of the diff I examined. &amp;nbsp;&amp;nbsp;When you type your login and password, the URL that is used when you submit goes to&amp;nbsp;&lt;a href="http://static.ak.fbcdn.net/connect/xd_proxy.php" style="color: #0000cc;" target="_blank"&gt;http://static.ak.fbcdn.net/&lt;wbr&gt;&lt;/wbr&gt;connect/xd_proxy.php&lt;/a&gt;, along with the origin= query string parameter. &amp;nbsp; &amp;nbsp;So origin must be the same on the window popup connecting to&amp;nbsp;&lt;a href="http://facebook.com/" style="color: #0000cc;" target="_blank"&gt;facebook.com&lt;/a&gt;&amp;nbsp;as the original one that was initialized on&amp;nbsp;your own Facebook Connect-enabled site.&lt;br /&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;span class="Apple-style-span" style="border-collapse: collapse; font-family: arial, sans-serif; font-size: 13px;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;span class="Apple-style-span" style="border-collapse: collapse; font-family: arial, sans-serif; font-size: 13px;"&gt;&lt;span style="color: #333333; font-family: Arial, serif; font-size: 14px; line-height: 22px;"&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;pre style="border-bottom-color: rgb(136, 136, 136); border-bottom-style: solid; border-bottom-width: 1px; border-left-color: rgb(136, 136, 136); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(136, 136, 136); border-right-style: solid; border-right-width: 1px; border-top-color: rgb(136, 136, 136); border-top-style: solid; border-top-width: 1px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 2px; padding-left: 2px; padding-right: 2px; padding-top: 2px; white-space: pre-wrap;"&gt;&lt;span class="Apple-style-span" style="border-collapse: collapse; font-family: arial, sans-serif; font-size: 13px;"&gt;&lt;span style="color: #333333; font-family: Arial, serif; font-size: 14px; line-height: 22px;"&gt;&lt;span style="color: black;"&gt;  &lt;/span&gt;&lt;span style="color: #666600;"&gt;}&lt;/span&gt;&lt;span style="color: black;"&gt; &lt;/span&gt;&lt;span style="color: #000088;"&gt;else&lt;/span&gt;&lt;span style="color: black;"&gt; &lt;/span&gt;&lt;span style="color: #000088;"&gt;if&lt;/span&gt;&lt;span style="color: black;"&gt; &lt;/span&gt;&lt;span style="color: #666600;"&gt;(&lt;/span&gt;&lt;span style="color: black;"&gt;f&lt;/span&gt;&lt;span style="color: #666600;"&gt;.&lt;/span&gt;&lt;span style="color: black;"&gt;transport &lt;/span&gt;&lt;span style="color: #666600;"&gt;==&lt;/span&gt;&lt;span style="color: black;"&gt; &lt;/span&gt;&lt;span style="color: #008800;"&gt;'flash'&lt;/span&gt;&lt;span style="color: #666600;"&gt;)&lt;/span&gt;&lt;span style="color: black;"&gt; &lt;/span&gt;&lt;span style="color: #666600;"&gt;{&lt;/span&gt;&lt;span style="color: black;"&gt;&lt;br /&gt;                    &lt;/span&gt;&lt;span style="color: #000088;"&gt;var&lt;/span&gt;&lt;span style="color: black;"&gt; d &lt;/span&gt;&lt;span style="color: #666600;"&gt;=&lt;/span&gt;&lt;span style="color: black;"&gt; window&lt;/span&gt;&lt;span style="color: #666600;"&gt;.&lt;/span&gt;&lt;span style="color: black;"&gt;location&lt;/span&gt;&lt;span style="color: #666600;"&gt;.&lt;/span&gt;&lt;span style="color: black;"&gt;toString&lt;/span&gt;&lt;span style="color: #666600;"&gt;()&lt;/span&gt;&lt;span style="color: black;"&gt; &lt;/span&gt;&lt;span style="color: #666600;"&gt;+&lt;/span&gt;&lt;span style="color: black;"&gt; &lt;/span&gt;&lt;span style="color: #660066;"&gt;Math&lt;/span&gt;&lt;span style="color: #666600;"&gt;.&lt;/span&gt;&lt;span style="color: black;"&gt;random&lt;/span&gt;&lt;span style="color: #666600;"&gt;();&lt;/span&gt;&lt;span style="color: black;"&gt;&lt;br /&gt;                    window&lt;/span&gt;&lt;span style="color: #666600;"&gt;.&lt;/span&gt;&lt;span style="color: black;"&gt;FB_OnFlashXdCommReady &lt;/span&gt;&lt;span style="color: #666600;"&gt;=&lt;/span&gt;&lt;span style="color: black;"&gt; &lt;/span&gt;&lt;span style="color: #000088;"&gt;function&lt;/span&gt;&lt;span style="color: #666600;"&gt;()&lt;/span&gt;&lt;span style="color: black;"&gt; &lt;/span&gt;&lt;span style="color: #666600;"&gt;{&lt;/span&gt;&lt;span style="color: black;"&gt;&lt;br /&gt;                        document&lt;/span&gt;&lt;span style="color: #666600;"&gt;.&lt;/span&gt;&lt;span style="color: #660066;"&gt;XdComm&lt;/span&gt;&lt;span style="color: #666600;"&gt;.&lt;/span&gt;&lt;span style="color: black;"&gt;postMessage_&lt;wbr&gt;&lt;/wbr&gt;init&lt;/span&gt;&lt;span style="color: #666600;"&gt;(&lt;/span&gt;&lt;span style="color: #008800;"&gt;'dummy'&lt;/span&gt;&lt;span style="color: #666600;"&gt;,&lt;/span&gt;&lt;span style="color: black;"&gt; d&lt;/span&gt;&lt;span style="color: #666600;"&gt;);&lt;/span&gt;&lt;span style="color: black;"&gt;&lt;br /&gt;                        document&lt;/span&gt;&lt;span style="color: #666600;"&gt;.&lt;/span&gt;&lt;span style="color: #660066;"&gt;XdComm&lt;/span&gt;&lt;span style="color: #666600;"&gt;.&lt;/span&gt;&lt;span style="color: black;"&gt;postMessage_&lt;wbr&gt;&lt;/wbr&gt;send&lt;/span&gt;&lt;span style="color: #666600;"&gt;(&lt;/span&gt;&lt;span style="color: black;"&gt;a&lt;/span&gt;&lt;span style="color: #666600;"&gt;,&lt;/span&gt;&lt;span style="color: black;"&gt; f&lt;/span&gt;&lt;span style="color: #666600;"&gt;.&lt;/span&gt;&lt;span style="color: black;"&gt;origin&lt;/span&gt;&lt;span style="color: #666600;"&gt;);&lt;/span&gt;&lt;span style="color: black;"&gt;&lt;br /&gt;                    &lt;/span&gt;&lt;span style="color: #666600;"&gt;};&lt;/span&gt;&lt;span style="color: black;"&gt;&lt;br /&gt;                    createXdCommSwf&lt;/span&gt;&lt;span style="color: #666600;"&gt;();&lt;/span&gt;&lt;span style="color: black;"&gt;&lt;br /&gt;                &lt;/span&gt;&lt;span style="color: #666600;"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;span class="Apple-style-span" style="border-collapse: collapse; font-family: arial, sans-serif; font-size: 13px;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;In addition to setting up the origin= query parameter, the FB.init() functions also sets up 3 separate callback functions: ok_session, cancel_session, and no_user, which are instantiated into a callback dictionary each accessible by &amp;nbsp;a random 16-character signature.&amp;nbsp;&amp;nbsp; So if you've ever wondered why the query string on the Facebook login is so long, it's partially because of all the info they encode to handle the results of different authentications. &amp;nbsp;Regardless, the message that gets passed includes a bunch of different URL-encoded string, but one of them is a cb= parameter, which specifies which callback function to run.&lt;br /&gt;&lt;br /&gt;Once that callback function is run, then the FB JavaScript calls FB.Auth.setSession() and fires off events...but the message between&amp;nbsp;&lt;a href="http://facebook.com/" style="color: #0000cc;" target="_blank"&gt;facebook.com&lt;/a&gt;&amp;nbsp;and your site&amp;nbsp;has been successfully exchanged...all via SWF objects in Internet Explorer!&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4137806788465333774-8448393513836557340?l=hustoknow.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hustoknow.blogspot.com/feeds/8448393513836557340/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hustoknow.blogspot.com/2011/06/deconstructing-facebooks-flash-cross.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/8448393513836557340'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/8448393513836557340'/><link rel='alternate' type='text/html' href='http://hustoknow.blogspot.com/2011/06/deconstructing-facebooks-flash-cross.html' title='Deconstructing Facebook&apos;s Flash Cross Domain Handler in IE..'/><author><name>Roger</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4137806788465333774.post-3532191659135178556</id><published>2011-06-25T02:42:00.000-07:00</published><updated>2011-06-25T05:02:40.412-07:00</updated><title type='text'>Initial investigations of the XD.Flash...</title><content type='html'>One of the first things the Facebook Connect library does when a user on Internet Explorer clicks the "Facebook Connect" is to insert this Adobe Flash file:&lt;br /&gt;&lt;br /&gt;&amp;lt;object type="application/x-shockwave-flash" id="XdComm" name="XdComm" classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" allowscriptaccess="always"&amp;gt;&amp;lt;param name="movie" value="https://s-static.ak.fbcdn.net/rsrc.php/v1/yx/r/WFg56j28XFs.swf"&amp;gt;&amp;lt;/param&amp;gt;&amp;lt;param name="allowscriptaccess" value="always"&amp;gt;&amp;lt;/param&amp;gt;&amp;lt;/object&amp;gt;&lt;br /&gt;&lt;br /&gt;(The classid refers to the ActiveX object of the Adobe Flash player, which apparently remains constant.   The &lt;a href="http://kb2.adobe.com/cps/164/tn_16494.html"&gt;allowscriptaccess&lt;/a&gt; option apparently allows outbound URL requests.)&lt;br /&gt;&lt;br /&gt;Within the Facebook Connect all.js file, there is a line that refers to initializing the XdComm object.  It passes in two parts, one the actual name of the callback handler, and the second is the connection name on which to listen for messages.   For messages to be received, the connection name must be the same.&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;document.XdComm.postMessage_init('FB.XD.Flash.onMessage', FB.XD._openerOrigin ? FB.XD._openerOrigin : FB.XD._origin);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;When you login to Facebook Connect, you're POST'ng to xd_proxy.php, which also loads a SWF file (assuming transport=flash) and sets the origin to the one provided in the origin=.  By doing so, both the window that was opened as well as the original document can now communicate with each other.  The xd_proxy.php if you were to download it looks like:&lt;br /&gt;&lt;pre class="prettyprint"&gt;&amp;lt;!doctype html&amp;gt;&lt;br /&gt;&amp;lt;html&amp;gt;&lt;br /&gt;    &lt;br /&gt;    &amp;lt;head&amp;gt;&lt;br /&gt;        &amp;lt;title&amp;gt;&lt;br /&gt;            XD Proxy&lt;br /&gt;        &amp;lt;/title&amp;gt;&lt;br /&gt;    &amp;lt;/head&amp;gt;&lt;br /&gt;    &lt;br /&gt;    &amp;lt;body onload="doFragmentSend()"&amp;gt;&lt;br /&gt;        &amp;lt;div id="swf_holder" style="position: absolute; top: -10000px; width: 1px; height: 1px"&amp;gt;&lt;br /&gt;        &amp;lt;/div&amp;gt;&lt;br /&gt;        &amp;lt;script&amp;gt;&lt;br /&gt;            var XdCommUrl = "http:\/\/static.ak.fbcdn.net\/rsrc.php\/v1\/yx\/r\/WFg56j28XFs.swf";&lt;br /&gt;            var AllowFbCom = false;&lt;br /&gt;&lt;br /&gt;            function resolveRelation(b) {&lt;br /&gt;                var g, d, f = b.split('.'),&lt;br /&gt;                    e = window;&lt;br /&gt;                for (var a = 0, c = f.length; a &amp;lt; c; a++) {&lt;br /&gt;                    g = f[a];&lt;br /&gt;                    if (g === 'opener' || g === 'parent' || g === 'top') {&lt;br /&gt;                        e = e[g];&lt;br /&gt;                    } else if (d = /^frames\[['"]?([a-zA-Z0-9-_]+)['"]?\]$/.exec(g)) {&lt;br /&gt;                        e = e.frames[d[1]];&lt;br /&gt;                    } else throw new SyntaxError('Malformed id to resolve: ' + b + ', pt: ' + g);&lt;br /&gt;                }&lt;br /&gt;                return e;&lt;br /&gt;            }&lt;br /&gt;            function createXdCommSwf() {&lt;br /&gt;                var a = !! document.attachEvent,&lt;br /&gt;                    c = /(?:MSIE.(\d+\.\d+))|(?:(?:Firefox|GranParadiso|Iceweasel).(\d+\.\d+))|(?:Opera(?:.+Version.|.)(\d+\.\d+))|(?:AppleWebKit.(\d+(?:\.\d+)?))/.exec(navigator.userAgent),&lt;br /&gt;                    b = (c[1] &amp;amp;&amp;amp; (parseFloat(c[1]) &amp;gt;= 9)),&lt;br /&gt;                    e = 'XdComm' + (b ? (Math.random() * (1 &amp;lt;&amp;lt; 30)).toString(16).replace('.', '') : ''),&lt;br /&gt;                    d = ('&amp;lt;object ' + 'type="application/x-shockwave-flash" ' + 'id="' + e + '" ' + (a ? 'name="XdComm" ' : '') + (a ? '' : 'data="' + XdCommUrl + '" ') + (a ? 'classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" ' : '') + 'allowscriptaccess="always"&amp;gt;' + '&amp;lt;param name="movie" value="' + XdCommUrl + '"&amp;gt;&amp;lt;/param&amp;gt;' + '&amp;lt;param name="allowscriptaccess" value="always"&amp;gt;&amp;lt;/param&amp;gt;' + '&amp;lt;/object&amp;gt;');&lt;br /&gt;                if (b) window.attachEvent('onunload', function() {&lt;br /&gt;                    document.getElementById(e).removeNode(true);&lt;br /&gt;                });&lt;br /&gt;                document.getElementById('swf_holder').innerHTML = d;&lt;br /&gt;            }&lt;br /&gt;            function doFragmentSend() {&lt;br /&gt;                if (!AllowFbCom &amp;amp;&amp;amp; /(^|\.)facebook.com$/.test(document.domain.toString())) return;&lt;br /&gt;                var c = window.location.toString(),&lt;br /&gt;                    a = c.substr(c.indexOf('#') + 1),&lt;br /&gt;                    f = {},&lt;br /&gt;                    g = a.split('&amp;amp;'),&lt;br /&gt;                    b, e;&lt;br /&gt;                for (b = 0; b &amp;lt; g.length; b++) {&lt;br /&gt;                    e = g[b].split('=', 2);&lt;br /&gt;                    f[decodeURIComponent(e[0])] = decodeURIComponent(e[1]);&lt;br /&gt;                }&lt;br /&gt;                if (f.transport == 'postmessage') {&lt;br /&gt;                    var h = resolveRelation(f.relation);&lt;br /&gt;                    if (h) {&lt;br /&gt;                        h.postMessage(a, f.origin);&lt;br /&gt;                    } else if (f.relation == 'opener') {&lt;br /&gt;                        var i = 0;&lt;br /&gt;                        i = setInterval(function() {&lt;br /&gt;                            if (window.fbrpc) {&lt;br /&gt;                                clearInterval(i);&lt;br /&gt;                                if (fbrpc.postMessage) {&lt;br /&gt;                                    fbrpc.postMessage(a, f.origin);&lt;br /&gt;                                } else fbrpc.call('postMessage', JSON.stringify({&lt;br /&gt;                                    target: f.relation,&lt;br /&gt;                                    message: a&lt;br /&gt;                                }));&lt;br /&gt;                            }&lt;br /&gt;                        }, 10);&lt;br /&gt;                    }&lt;br /&gt;                } else if (f.transport == 'flash') {&lt;br /&gt;                    var d = window.location.toString() + Math.random();&lt;br /&gt;                    window.FB_OnFlashXdCommReady = function() {&lt;br /&gt;                        document.XdComm.postMessage_init('dummy', d);&lt;br /&gt;                        document.XdComm.postMessage_send(a, f.origin);&lt;br /&gt;                    };&lt;br /&gt;                    createXdCommSwf();&lt;br /&gt;                }&lt;br /&gt;            }&lt;br /&gt;        &amp;lt;/script&amp;gt;&lt;br /&gt;    &amp;lt;/body&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;/html&amp;gt;    &lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;Remember in the previous XdComm file that the postMessage_init() and postMessae_send() were added as registered callbacks.  In ActionScript, you can invoke commands directly in JavaScript by calling them as if there were functions that are part of the Active-X module.  The loading of the 'dummy' and postMessage_send() gets sent to the LocalConnection() origin name, which your original page is also listening.  In this way, this message will be received by the onMessage handler with the Flash object of the all.js library, which is URI decoded before passing back so that the query string to be decoded and the callback specified in the cb= is provided.   &lt;br /&gt;&lt;pre class="prettyprint"&gt;Flash: {&lt;br /&gt;        init: function() {&lt;br /&gt;            FB.Flash.onReady(function() {&lt;br /&gt;                document.XdComm.postMessage_init('FB.XD.Flash.onMessage', FB.XD._openerOrigin ? FB.XD._openerOrigin : FB.XD._origin);&lt;br /&gt;            });&lt;br /&gt;        },&lt;br /&gt;        onMessage: function(a) {&lt;br /&gt;            FB.XD.recv(decodeURIComponent(a));&lt;br /&gt;        }&lt;br /&gt;    },&lt;br /&gt;&lt;/pre&gt;(Also, there is also a postMessage included in the code, but presumably if not event listener is added, it will be ignored by Internet Explorer): &lt;script type="text/javascript"&gt;parent.postMessage("cb=f30190e445cc1ec&amp;origin=http\u00253A\u00252F\u00252Fmydev.host.com\u00252Ffc37584edc70bc&amp;relation=parent&amp;transport=postmessage&amp;frame=f100f10becaecfd", "http:\/\/dev.myhost.com\/fc37584edc70bc");&lt;/script&gt;  FYI -- The old REST API relied on using the oauthRequest() code to sendXdHttpRequest() to make the external Facebook request via the FB.ApiServer.flash() command, which makes the external request, but this section of the code seems deprecated. When the request is finished, the XdCom library creates an event on FB_OnXdHttpResult using the &lt;a href="http://www.adobe.com/devnet/flash/articles/external_interface.html#articlecontentAdobe_numberedheader_3"&gt;ExternalInterface&lt;/a&gt;class that communicates back to the JavaScript/HTML container.     &lt;br /&gt;&lt;pre class="prettyprint"&gt;oauthRequest: function(b, f, c, e, a) {&lt;br /&gt;        try {&lt;br /&gt;            FB.ApiServer.jsonp(b, f, c, FB.JSON.flatten(e), a);&lt;br /&gt;        } catch (g) {&lt;br /&gt;            if (FB.Flash.hasMinVersion()) {&lt;br /&gt;                FB.ApiServer.flash(b, f, c, FB.JSON.flatten(e), a);&lt;br /&gt;            } else throw new Error('Flash is required for this API call.');&lt;br /&gt;        }&lt;br /&gt;&lt;/pre&gt;See the &lt;a href="http://hustoknow.blogspot.com/2011/06/facebooks-flash-xdcomm-receiver.html"&gt;http://hustoknow.blogspot.com/2011/06/facebooks-flash-xdcomm-receiver.html&lt;/a&gt; for an examination on the Flash XDComm .swf file decompiled....&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4137806788465333774-3532191659135178556?l=hustoknow.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hustoknow.blogspot.com/feeds/3532191659135178556/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hustoknow.blogspot.com/2011/06/how-flash-xdcomm-works-in-facebook.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/3532191659135178556'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/3532191659135178556'/><link rel='alternate' type='text/html' href='http://hustoknow.blogspot.com/2011/06/how-flash-xdcomm-works-in-facebook.html' title='Initial investigations of the XD.Flash...'/><author><name>Roger</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4137806788465333774.post-5804178307706897247</id><published>2011-06-24T20:07:00.000-07:00</published><updated>2011-06-27T00:47:11.964-07:00</updated><title type='text'>Decompiling Facebook's Flash XDComm Receiver..</title><content type='html'>Within Facebook's all.js file, there is a reference to a _swfPath that refers to &lt;a href="http://static.ak.fbcdn.net/rsrc.php/v1/yx/r/WFg56j28XFs.swf"&gt;http://static.ak.fbcdn.net/rsrc.php/v1/yx/r/WFg56j28XFs.swf&lt;/a&gt;.  It's used for Internet Explorer installed with Flash to handle cross-domain requests.  Ever wonder how it works?  Well, you can do a wget and decompile it.&lt;br /&gt;&lt;br /&gt;Decompiling the XDComm.swf file: None of the open source Flash decompilers work to decompile Facebook's XDComm Receiver, since it's compiled in Adobe Flex v4 (released apparently in March 2010).  You can use the Sothink SWF Decompiler tool, but without paying $70.00, the program doesn't even allow you to export the files to ActionScript.  Nonetheless, the unregistered version (as of the 6.00 version) limits you from decrypting more than the first two resources, but there's enough in the tool that allows you to save parts of the code.&lt;br /&gt;&lt;br /&gt;Here are flashutils\PostMessage.as and XdComm.as.  Hopefully it gives you a better idea of how the Flash cross-domain receiver works. &lt;br /&gt;&lt;br /&gt;The files can also be downloaded from here:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://bit.ly/kAJ7AJ"&gt;http://bit.ly/kAJ7AJ&lt;/a&gt;&lt;br /&gt;&lt;a href="http://bit.ly/ltGkTF"&gt;http://bit.ly/ltGkTF&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Apparently the XdComm utilizes the &lt;a href="http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/net/LocalConnection.html"&gt;LocalConnection&lt;/a&gt; library to communicate with the HTML page.  It used the send/receive functionality and the FB.XD._origin variable to determine what the connection name should be used.   &lt;br /&gt;&lt;br /&gt;In &lt;a href="http://hustoknow.blogspot.com/2011/06/how-flash-xdcomm-works-in-facebook.html"&gt;http://hustoknow.blogspot.com/2011/06/how-flash-xdcomm-works-in-facebook.html&lt;/a&gt;, we'll walk through in more detail how the Flash XDComm Receiver works with Facebook.&lt;br /&gt;&lt;br /&gt;XdComm.as:&lt;br /&gt;&lt;br /&gt;&lt;pre class="prettyprint"&gt;package &lt;br /&gt;{&lt;br /&gt;    import flash.display.*;&lt;br /&gt;    import flash.events.*;&lt;br /&gt;    import flash.external.*;&lt;br /&gt;    import flash.net.*;&lt;br /&gt;    import flash.system.*;&lt;br /&gt;    import flashutils.*;&lt;br /&gt;&lt;br /&gt;    public class XdComm extends Sprite&lt;br /&gt;    {&lt;br /&gt;        private var _cache:SharedObject;&lt;br /&gt;        private var _cacheContext:String = "unknown";&lt;br /&gt;        private var postMessage:PostMessage;&lt;br /&gt;        private static var requestIdCount:int = 0;&lt;br /&gt;&lt;br /&gt;        public function XdComm()&lt;br /&gt;        {&lt;br /&gt;            XdComm.fbTrace("XdComm Initialized", {});&lt;br /&gt;            Security.allowDomain("*");&lt;br /&gt;            Security.allowInsecureDomain("*");&lt;br /&gt;            this.addEventListener(Event.ENTER_FRAME, this.init);&lt;br /&gt;            return;&lt;br /&gt;        }// end function&lt;br /&gt;&lt;br /&gt;        private function init(event:Event) : void&lt;br /&gt;        {&lt;br /&gt;            XdComm.fbTrace("XdComm.init", {});&lt;br /&gt;            this.removeEventListener(Event.ENTER_FRAME, this.init);&lt;br /&gt;            this.postMessage = new PostMessage();&lt;br /&gt;            ExternalInterface.addCallback("sendXdHttpRequest", this.sendXdHttpRequest);&lt;br /&gt;            ExternalInterface.addCallback("setCache", this.setCache);&lt;br /&gt;            ExternalInterface.addCallback("getCache", this.getCache);&lt;br /&gt;            ExternalInterface.addCallback("setCacheContext", this.setCacheContext);&lt;br /&gt;            ExternalInterface.addCallback("clearAllCache", this.clearAllCache);&lt;br /&gt;            ExternalInterface.call("FB_OnFlashXdCommReady");&lt;br /&gt;            return;&lt;br /&gt;        }// end function&lt;br /&gt;&lt;br /&gt;        public function sendXdHttpRequest(param1:String, param2:String, param3:String, param4) : int&lt;br /&gt;        {&lt;br /&gt;            var loader:URLLoader;&lt;br /&gt;            var reqId:int;&lt;br /&gt;            var loaded:Function;&lt;br /&gt;            var key:String;&lt;br /&gt;            var value:String;&lt;br /&gt;            var method:* = param1;&lt;br /&gt;            var url:* = param2;&lt;br /&gt;            var requestBody:* = param3;&lt;br /&gt;            var extraHeaders:* = param4;&lt;br /&gt;            loaded = function (event:Event) : void&lt;br /&gt;            {&lt;br /&gt;                var _loc_2:* = loader.data.toString();&lt;br /&gt;                XdComm.fbTrace("Requested completed", {data:_loc_2});&lt;br /&gt;                ExternalInterface.call("FB_OnXdHttpResult", reqId, encodeURIComponent(_loc_2));&lt;br /&gt;                return;&lt;br /&gt;            }// end function&lt;br /&gt;            ;&lt;br /&gt;            XdComm.fbTrace("SendXdHttpRequest", {method:method, url:url, requestBody:requestBody, extraHeaders:extraHeaders});&lt;br /&gt;            if (url.indexOf("https://") != 0 &amp;amp;&amp;amp; url.indexOf("http://") != 0)&lt;br /&gt;            {&lt;br /&gt;                url = "http://" + url;&lt;br /&gt;            }&lt;br /&gt;            var host:* = PostMessage.extractDomain(url);&lt;br /&gt;            if (host != "api.facebook.com" &amp;amp;&amp;amp; host != "graph.facebook.com" &amp;amp;&amp;amp; !/^(api|graph)\.[A-Za-z0-9\-\.]+\.facebook\.com$""^(api|graph)\.[A-Za-z0-9\-\.]+\.facebook\.com$/.test(host))&lt;br /&gt;            {&lt;br /&gt;                return 0;&lt;br /&gt;            }&lt;br /&gt;            var _loc_6:* = XdComm;&lt;br /&gt;            var _loc_7:* = XdComm.requestIdCount + 1;&lt;br /&gt;            _loc_6.requestIdCount = _loc_7;&lt;br /&gt;            var req:* = new URLRequest(url);&lt;br /&gt;            loader = new URLLoader();&lt;br /&gt;            reqId = XdComm.requestIdCount;&lt;br /&gt;            req.method = method;&lt;br /&gt;            req.data = requestBody;&lt;br /&gt;            if (extraHeaders != null)&lt;br /&gt;            {&lt;br /&gt;                var _loc_6:int = 0;&lt;br /&gt;                var _loc_7:* = extraHeaders;&lt;br /&gt;                while (_loc_7 in _loc_6)&lt;br /&gt;                {&lt;br /&gt;                    &lt;br /&gt;                    key = _loc_7[_loc_6];&lt;br /&gt;                    value = extraHeaders[key];&lt;br /&gt;                    req.requestHeaders.push(new URLRequestHeader(key, value));&lt;br /&gt;                }&lt;br /&gt;            }&lt;br /&gt;            loader.addEventListener(Event.COMPLETE, loaded);&lt;br /&gt;            loader.load(req);&lt;br /&gt;            return reqId;&lt;br /&gt;        }// end function&lt;br /&gt;&lt;br /&gt;        private function setCacheContext(param1:String) : void&lt;br /&gt;        {&lt;br /&gt;            if (param1 == null)&lt;br /&gt;            {&lt;br /&gt;                param1 = "unknown";&lt;br /&gt;            }&lt;br /&gt;            this._cacheContext = param1;&lt;br /&gt;            return;&lt;br /&gt;        }// end function&lt;br /&gt;&lt;br /&gt;        private function clearAllCache() : void&lt;br /&gt;        {&lt;br /&gt;            this.cache.clear();&lt;br /&gt;            this.cache.flush();&lt;br /&gt;            return;&lt;br /&gt;        }// end function&lt;br /&gt;&lt;br /&gt;        private function getCache(param1:String) : String&lt;br /&gt;        {&lt;br /&gt;            return this.contextCache[param1];&lt;br /&gt;        }// end function&lt;br /&gt;&lt;br /&gt;        private function setCache(param1:String, param2:String) : void&lt;br /&gt;        {&lt;br /&gt;            var _loc_3:* = this.contextCache;&lt;br /&gt;            _loc_3[param1] = param2;&lt;br /&gt;            this.cache.flush();&lt;br /&gt;            return;&lt;br /&gt;        }// end function&lt;br /&gt;&lt;br /&gt;        private function get cache() : SharedObject&lt;br /&gt;        {&lt;br /&gt;            if (this._cache == null)&lt;br /&gt;            {&lt;br /&gt;                this._cache = SharedObject.getLocal("cache");&lt;br /&gt;            }&lt;br /&gt;            return this._cache;&lt;br /&gt;        }// end function&lt;br /&gt;&lt;br /&gt;        private function get contextCache() : Object&lt;br /&gt;        {&lt;br /&gt;            var _loc_1:* = this.cache.data[this._cacheContext];&lt;br /&gt;            if (_loc_1 == null)&lt;br /&gt;            {&lt;br /&gt;                _loc_1 = new Object();&lt;br /&gt;                this.cache.data[this._cacheContext] = _loc_1;&lt;br /&gt;            }&lt;br /&gt;            return _loc_1;&lt;br /&gt;        }// end function&lt;br /&gt;&lt;br /&gt;        public static function traceObject(param1:Object, param2:int = 0, param3:String = "")&lt;br /&gt;        {&lt;br /&gt;            var _loc_6:* = undefined;&lt;br /&gt;            var _loc_7:String = null;&lt;br /&gt;            var _loc_4:String = "";&lt;br /&gt;            var _loc_5:int = 0;&lt;br /&gt;            while (_loc_5 &amp;lt; param2)&lt;br /&gt;            {&lt;br /&gt;                &lt;br /&gt;                _loc_4 = _loc_4 + "\t";&lt;br /&gt;                _loc_5++;&lt;br /&gt;            }&lt;br /&gt;            for (_loc_6 in param1)&lt;br /&gt;            {&lt;br /&gt;                &lt;br /&gt;                param3 = param3 + (_loc_4 + "[" + _loc_6 + "] =&amp;gt; " + param1[_loc_6]);&lt;br /&gt;                _loc_7 = traceObject(param1[_loc_6], (param2 + 1));&lt;br /&gt;                if (_loc_7 != "")&lt;br /&gt;                {&lt;br /&gt;                    param3 = param3 + (" {\n" + _loc_7 + _loc_4 + "}");&lt;br /&gt;                }&lt;br /&gt;                param3 = param3 + "\n";&lt;br /&gt;            }&lt;br /&gt;            if (param2 == 0)&lt;br /&gt;            {&lt;br /&gt;            }&lt;br /&gt;            else&lt;br /&gt;            {&lt;br /&gt;                return param3;&lt;br /&gt;            }&lt;br /&gt;            return;&lt;br /&gt;        }// end function&lt;br /&gt;&lt;br /&gt;        public static function fbTrace(param1:String, param2:Object) : void&lt;br /&gt;        {&lt;br /&gt;            traceObject(param2);&lt;br /&gt;            return;&lt;br /&gt;        }// end function&lt;br /&gt;&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;...and postMessage.as:&lt;br /&gt;&lt;pre class="prettyprint"&gt;package flashutils&lt;br /&gt;{&lt;br /&gt;    import flash.events.*;&lt;br /&gt;    import flash.external.*;&lt;br /&gt;    import flash.net.*;&lt;br /&gt;    import flash.utils.*;&lt;br /&gt;&lt;br /&gt;    public class PostMessage extends Object&lt;br /&gt;    {&lt;br /&gt;        private var currentDomain:String;&lt;br /&gt;        private var callback:String;&lt;br /&gt;        private var connection:LocalConnection;&lt;br /&gt;        private var connectionName:String;&lt;br /&gt;&lt;br /&gt;        public function PostMessage()&lt;br /&gt;        {&lt;br /&gt;            this.connection = new LocalConnection();&lt;br /&gt;            this.connection.client = this;&lt;br /&gt;            this.connection.connect(Math.random().toString());&lt;br /&gt;            ExternalInterface.addCallback("postMessage_init", this.init);&lt;br /&gt;            ExternalInterface.addCallback("postMessage_send", this.send);&lt;br /&gt;            return;&lt;br /&gt;        }// end function&lt;br /&gt;&lt;br /&gt;        public function getCurrentDomain() : String&lt;br /&gt;        {&lt;br /&gt;            if (!this.currentDomain)&lt;br /&gt;            {&lt;br /&gt;                try&lt;br /&gt;                {&lt;br /&gt;                    this.currentDomain = ExternalInterface.call("self.document.domain.toString");&lt;br /&gt;                    this.fbTrace("getCurrentDomain", {currentDomain:this.currentDomain});&lt;br /&gt;                }&lt;br /&gt;                catch (e)&lt;br /&gt;                {&lt;br /&gt;                    logError("getCurrentDomain error", e);&lt;br /&gt;                }&lt;br /&gt;            }&lt;br /&gt;            return this.currentDomain;&lt;br /&gt;        }// end function&lt;br /&gt;&lt;br /&gt;        public function onFacebookDomain() : Boolean&lt;br /&gt;        {&lt;br /&gt;            return /(^|\.)facebook\.com$""(^|\.)facebook\.com$/.test(this.getCurrentDomain()) || /(^|\.)fbcdn\.net$""(^|\.)fbcdn\.net$/.test(this.getCurrentDomain());&lt;br /&gt;        }// end function&lt;br /&gt;&lt;br /&gt;        public function init(param1:String, param2:String) : void&lt;br /&gt;        {&lt;br /&gt;            var cb:* = param1;&lt;br /&gt;            var name:* = param2;&lt;br /&gt;            this.fbTrace("init", {cb:cb, name:name});&lt;br /&gt;            try&lt;br /&gt;            {&lt;br /&gt;                if (!this.onFacebookDomain() &amp;amp;&amp;amp; PostMessage.extractDomain(name) != this.getCurrentDomain())&lt;br /&gt;                {&lt;br /&gt;                    this.logError("init", "name must be a URL on the current domain: " + name);&lt;br /&gt;                }&lt;br /&gt;                else&lt;br /&gt;                {&lt;br /&gt;                    this.callback = cb;&lt;br /&gt;                    if (name == this.connectionName)&lt;br /&gt;                    {&lt;br /&gt;                        return;&lt;br /&gt;                    }&lt;br /&gt;                    this.connectionName = name;&lt;br /&gt;                    name = encodeURIComponent(name);&lt;br /&gt;                    this.connection = new LocalConnection();&lt;br /&gt;                    this.connection.client = this;&lt;br /&gt;                    this.connection.connect(name);&lt;br /&gt;                }&lt;br /&gt;            }&lt;br /&gt;            catch (e)&lt;br /&gt;            {&lt;br /&gt;                logError("init", e.toString());&lt;br /&gt;            }&lt;br /&gt;            return;&lt;br /&gt;        }// end function&lt;br /&gt;&lt;br /&gt;        public function send(param1:String, param2:String) : void&lt;br /&gt;        {&lt;br /&gt;            var msg:* = param1;&lt;br /&gt;            var name:* = param2;&lt;br /&gt;            this.fbTrace("send", {name:name, msg:msg});&lt;br /&gt;            if (!this.connection)&lt;br /&gt;            {&lt;br /&gt;                this.logError("send", "connection has not been initialized.");&lt;br /&gt;                return;&lt;br /&gt;            }&lt;br /&gt;            try&lt;br /&gt;            {&lt;br /&gt;                name = encodeURIComponent(name);&lt;br /&gt;                this.connection.send(name, "recv", msg);&lt;br /&gt;            }&lt;br /&gt;            catch (e)&lt;br /&gt;            {&lt;br /&gt;                logError("send", e.toString() + ". name: " + name + ", msg: " + msg);&lt;br /&gt;            }&lt;br /&gt;            return;&lt;br /&gt;        }// end function&lt;br /&gt;&lt;br /&gt;        public function recv(param1:String) : void&lt;br /&gt;        {&lt;br /&gt;            var deliverMessage:Function;&lt;br /&gt;            var msg:* = param1;&lt;br /&gt;            deliverMessage = function (event:TimerEvent) : void&lt;br /&gt;            {&lt;br /&gt;                var evt:* = event;&lt;br /&gt;                try&lt;br /&gt;                {&lt;br /&gt;                    ExternalInterface.call(callback, encodeURIComponent(msg));&lt;br /&gt;                }&lt;br /&gt;                catch (e)&lt;br /&gt;                {&lt;br /&gt;                    logError("recv", e.toString());&lt;br /&gt;                }&lt;br /&gt;                return;&lt;br /&gt;            }// end function&lt;br /&gt;            ;&lt;br /&gt;            this.fbTrace("recv", {msg:msg});&lt;br /&gt;            var timer:* = new Timer(1, 1);&lt;br /&gt;            timer.addEventListener(TimerEvent.TIMER, deliverMessage);&lt;br /&gt;            timer.start();&lt;br /&gt;            return;&lt;br /&gt;        }// end function&lt;br /&gt;&lt;br /&gt;        private function logError(param1:String, param2:Object) : void&lt;br /&gt;        {&lt;br /&gt;            XdComm.fbTrace("Error: XdComm.PostMessage." + param1, param2);&lt;br /&gt;            return;&lt;br /&gt;        }// end function&lt;br /&gt;&lt;br /&gt;        private function fbTrace(param1:String, param2:Object) : void&lt;br /&gt;        {&lt;br /&gt;            XdComm.fbTrace("XdComm.PostMessage." + param1, param2);&lt;br /&gt;            return;&lt;br /&gt;        }// end function&lt;br /&gt;&lt;br /&gt;        public static function extractDomain(param1:String) : String&lt;br /&gt;        {&lt;br /&gt;            return /^\w+:\/\/([^\/:]*)""^\w+:\/\/([^\/:]*)/.exec(param1)[1];&lt;br /&gt;        }// end function&lt;br /&gt;&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4137806788465333774-5804178307706897247?l=hustoknow.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hustoknow.blogspot.com/feeds/5804178307706897247/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hustoknow.blogspot.com/2011/06/facebooks-flash-xdcomm-receiver.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/5804178307706897247'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/5804178307706897247'/><link rel='alternate' type='text/html' href='http://hustoknow.blogspot.com/2011/06/facebooks-flash-xdcomm-receiver.html' title='Decompiling Facebook&apos;s Flash XDComm Receiver..'/><author><name>Roger</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4137806788465333774.post-549585503094285050</id><published>2011-06-23T12:49:00.000-07:00</published><updated>2011-06-27T00:52:24.322-07:00</updated><title type='text'>Daily snapshots of Facebook Connect Library</title><content type='html'>Nathan Friedly from &lt;a href="http://www.sociablelabs.com/"&gt;Sociable Labs&lt;/a&gt; put together a set of utils to grab daily snapshots of Facebook's connect.js here:&lt;br /&gt;&lt;br /&gt;&lt;a href="https://github.com/nfriedly/connect-js"&gt;https://github.com/nfriedly/connect-js&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;One thing you can do to keep yourself informed of changes that Facebook makes is to fork the repo, git clone the repo to your dev server, install the crontab script, and put a GitHub post-receive email hook to notify you of all changes. &amp;nbsp;Then the changes made by Facebook can more quickly diagnosed by developers like you!&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/-F4xtDe2A8v0/TgOYcCn9eCI/AAAAAAAACE0/WbPuj0O54Wc/s1600/github.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="219" src="http://3.bp.blogspot.com/-F4xtDe2A8v0/TgOYcCn9eCI/AAAAAAAACE0/WbPuj0O54Wc/s400/github.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;Often times Facebook only updates the Unix timestamp on the file, so here is a revised version of the script to only download/update the all_deminified.js file if there is more than one line of change made.  &lt;br /&gt;&lt;pre class="prettyprint"&gt;#!/bin/bash  -ex&lt;br /&gt;cd `dirname $0`&lt;br /&gt;TODAY=`date --rfc-3339=date`&lt;br /&gt;JS_OUTPUT="js/all_${TODAY}.js"&lt;br /&gt;if [ ! -d "js" ] &lt;br /&gt;then&lt;br /&gt;  mkdir "js"&lt;br /&gt;fi&lt;br /&gt;/usr/bin/wget https://connect.facebook.net/en_US/all.js -O ${JS_OUTPUT}&lt;br /&gt;/usr/bin/python jsbeautifier.py -o all_deminified.js ${JS_OUTPUT}&lt;br /&gt;# Avoid sending out unnecessary updates if only the timestamp has changed.&lt;br /&gt;ALL_JS_DIFF=`git diff --shortstat all_deminified.js | grep -v "1 insertions"`&lt;br /&gt;if [ ! -z "$ALL_JS_DIFF" ]                            &lt;br /&gt;then                                                  &lt;br /&gt;  echo "Commit has changed..."                        &lt;br /&gt;  git --no-pager diff . # Just to see what changed...turn off the pager.&lt;br /&gt;  /usr/bin/git add all_deminified.js&lt;br /&gt;  /usr/bin/git commit -m "Facebook Connect changes for $TODAY"&lt;br /&gt;  /usr/bin/git push origin&lt;br /&gt;fi                     &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Since the GitHub post-receive hook does not provide nice friendly diffs, we can also add a post-commit hook in our local repository and use the Ruby &lt;a href="http://rubygems.org/gems/git-commit-notifier"&gt;git-commit notifier&lt;/a&gt; to notify us whenever a change has been made.  This script dumps out last two revisions made in the commit(older to newer revision first).  Since the git-commit-notifier will only trigger if there is something more than the timestamp, we should expect diff emails only when there is a more substantial change made in the Facebook JavaScript.  You have to setup your own git-commit-notifier.yml and point it to an SMTP host (it apparently does not work with Gmail hosts since STARTTLS may be required).&lt;br /&gt;&lt;br /&gt;vi .git/hooks/post-commit&lt;br /&gt;&lt;pre class="prettyprint"&gt;#!/bin/bash -ex&lt;br /&gt;HASH_DIFF=`git log --pretty=%H --reverse | tail -2 | xargs echo`&lt;br /&gt;git-commit-notifier /home/rhu/fb/connect-js/git-commit-notifier.yml ${HASH_DIFF} refs/heads/master&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4137806788465333774-549585503094285050?l=hustoknow.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hustoknow.blogspot.com/feeds/549585503094285050/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hustoknow.blogspot.com/2011/06/daily-snapshots-of-facebook-connect.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/549585503094285050'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/549585503094285050'/><link rel='alternate' type='text/html' href='http://hustoknow.blogspot.com/2011/06/daily-snapshots-of-facebook-connect.html' title='Daily snapshots of Facebook Connect Library'/><author><name>Roger</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/-F4xtDe2A8v0/TgOYcCn9eCI/AAAAAAAACE0/WbPuj0O54Wc/s72-c/github.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4137806788465333774.post-1263184991186451671</id><published>2011-06-23T10:30:00.001-07:00</published><updated>2011-06-23T10:43:26.615-07:00</updated><title type='text'>Useful command to examine overall network stats</title><content type='html'>&lt;pre class="prettyprint"&gt;netstat -nat | awk '{print $6}' | sort | uniq -c | sort -n&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4137806788465333774-1263184991186451671?l=hustoknow.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://hustoknow.blogspot.com/feeds/1263184991186451671/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://hustoknow.blogspot.com/2011/06/useful-command-to-examine-overall.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/1263184991186451671'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4137806788465333774/posts/default/1263184991186451671'/><link rel='alternate' type='text/html' href='http://hustoknow.blogspot.com/2011/06/useful-command-to-examine-overall.html' title='Useful command to examine overall network stats'/><author><name>Roger</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4137806788465333774.post-3001684422818021075</id><published>2011-06-22T11:36:00.000-07:00</published><updated>2011-06-27T00:41:48.876-07:00</updated><title type='text'>How Facebook's xd_proxy.php seemed to have broken IE8...</title><content type='html'>The problem related to IE8, 32-bit and 64-bit versions of Internet Explorer with Flash installed when invoking the FB.getLoginStatus() call.  Facebook recently broke IE8 with a non-closing xd_proxy.php.  I just did a diff compare between the last set of changes today from a 6/17 snapshot and noticed this line change:&lt;br /&gt;&lt;pre class="prettyprint"&gt;&amp;lt;         document.XdComm.postMessage_init('FB.XD.Flash.onMessage', FB.XD._origin);&lt;br /&gt;---&lt;br /&gt;&amp;gt;         document.XdComm.postMessage_init('FB.XD.Flash.onMessage', FB.XD._openerOrigin ? FB.XD._openerOrigin : FB.XD._origin);&lt;br /&gt;&lt;/pre&gt;But why would this line do anything?   The issues point to problems with IE8 (not IE7) and Flash, which may explain why I didn't see any issues on IE7.  IE9, which I was also running on my desktop but did not have Flash installed, may also explain why I didn't encounter the problem.&lt;br /&gt;&lt;br /&gt;Facebook relies on the HTML5 postMessage functionality to send cross-browser JavaScript code between different window browsers visiting different domains.   For Firefox/Chrome, HTML5 support works great to pass JavaScript code from Facebook and inject it into your local app.  But with IE8, the postMessage seems to have issues with &lt;a href="http://felocity.com/article/2011/05/window_postmessage_problems_and_workarounds_for_ie8"&gt;popups&lt;/a&gt; so Facebook has gone for two different approaches, one with a Flash widget that mimics the same behavior as postMessage and using IFrame's. The latter works for IE with no Flash, the former requires a minimum of Adobe Flash 9.0.159.0 or 10.0.22.87.&lt;br /&gt;&lt;br /&gt;Well, it turns out Facebook on the last push last week added this section of code, which tries to handle specific cases of using IE8:&lt;br /&gt;&lt;pre class="prettyprint"&gt;var a = !! window.attachEvent;&lt;br /&gt;    if (FB.XD._transport != 'postmessage' &amp;amp;&amp;amp; a &amp;amp;&amp;amp; window.postMessage) {&lt;br /&gt;      FB.XD._openerTransport = FB.XD._transport;&lt;br /&gt;      FB.XD._openerOrigin = FB.XD._origin;&lt;br /&gt;      FB.XD._nonOpenerOrigin = d;&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;(The method of transport is 'flash', it supports the window.attachEvent, and two !! means double-negated, and IE8 apparently does support window.postMessage):&lt;br /&gt;&lt;br /&gt;...which is part of this section:&lt;br /&gt;&lt;pre class="prettyprint"&gt;var d = (window.location.protocol + '//' + window.location.host + '/' + FB.guid());&lt;br /&gt;  if (window.addEventListener &amp;amp;&amp;amp; !window.attachEvent &amp;amp;&amp;amp; window.postMessage) {&lt;br /&gt;      FB.XD._origin = d;&lt;br /&gt;      FB.XD.PostMessage.init();&lt;br /&gt;      FB.XD._transport = 'postmessage';&lt;br /&gt;    } else if (!b &amp;amp;&amp;amp; FB.Flash.hasMinVersion()) {&lt;br /&gt;      if (document.getElementById('fb-root')) {&lt;br /&gt;        var c = document.domain;&lt;br /&gt;&lt;b&gt;        if (c == 'facebook.com') c = window.location.host;&lt;/b&gt;&lt;br /&gt;        FB.XD._origin = (window.location.protocol + '//' + c + '/' + FB.guid());&lt;br /&gt;        FB.XD.Flash.init();&lt;br /&gt;        FB.XD._transport = 'flash';&lt;br /&gt;      } else {&lt;br /&gt;        if (FB.log) FB.log('missing fb-root, defaulting to fragment-based xdcomm');&lt;br /&gt;        FB.XD._transport = 'fragment';&lt;br /&gt;        FB.XD.Fragment._channelUrl = b || window.location.toString();&lt;br /&gt;      }&lt;br /&gt;    } else {&lt;br /&gt;      FB.XD._transport = 'fragment';&lt;br /&gt;      FB.XD.Fragment._channelUrl = b || window.location.toString();&lt;br /&gt;    }&lt;br /&gt;    var a = !! window.attachEvent;&lt;br /&gt;    if (FB.XD._transport != 'postmessage' &amp;amp;&amp;amp; a &amp;amp;&amp;amp; window.postMessage) {&lt;br /&gt;      FB.XD._openerTransport = FB.XD._transport;&lt;br /&gt;      FB.XD._openerOrigin = FB.XD._origin;&lt;br /&gt;      FB.XD._nonOpenerOrigin = d;&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;(Background info: The line (c == 'facebook.com') checks to see if the window you're opening is www.facebook.com, which the JavaScript on their site seems to set document.domain to facebook.com (There is JS in their site that sets document.domain=window.location.hostname.replace(/^.*(facebook\..*)$/i,'$1')).  If it is, then it sets the c variable to www.facebook.com (or any other facebook.com site you're looking at). Otherwise, it sets the origin location from document.domain, which is usually the page you're loading. &lt;br /&gt;&lt;br /&gt;Also, wit
