DIY Google Hangouts Client Independent of Chrome Browser

Hangouts Logo

Safari, Chrome and Battery Life

Remember when Chrome was new and fast and light and minimalist? The name Chrome was meant an in-joke to the UX jargon chrome, meaning the frame around an app. Chrome was just a frame to view the web. Those days are long gone. Now that Chrome has a plurality market share, Google is positioning it as an enhanced web experience, just like Microsoft did with IE. Chrome is a great browser but it also wants to be an operating system that has its own launcher and app ecosystem. It literally is an operating system when packaged as Chrome OS. Chrome is a large application these days.

With the power management improvements and battery shaming Apple built into OS X 10.9 and 10.10, is has become clear to me that Chrome requires a lot of power and memory to run. Running Chrome with only core Google plugins and extensions for Hangouts and Drive, I get about 2 hours less battery life on my 2014 15″ MacBook Pro 11,2. To put that in perspective, it is the same ballpark that I lose if I fire up VMWare to run my Windows Server 2012 R2 with Visual Studio. Running Chrome is literally a similar workload to running a hypervisor running a whole other operating system.

Enough with the Extensions

Transitioning away from Chrome is not easy, especially if you get hooked on the extension and app ecosystem. Without my even realizing it, I left the Open Web and moved into Google’s Web. I hadn’t really paid attention but it turns out that the extensions themselves each consume a lot of resources and I have run into extensions that monetize with sneaky tricks. My first step to wean myself out of this cesspool was to go on an acetic extension diet. In Chrome, I have two extensions:

In Safari and Firefox, I only have the Adblock Plus extension and nothing else.

(AdBlock Plus has become a controversial topic because of their extortion of big sites as a monetization strategy. I’ve turned off “acceptable ads” and I don’t want to see any ads. If it wasn’t AdBlock Plus, I would use something else and have done so in the past. This may make me a bad person. I don’t care. The ad networks are now a malware vector and the quantity of the ads is overwhelming. The internet needs a new monetization strategy.)

Hangouts and XMPP/Jabber

The extension diet caused me a problem because it killed Hangouts. We use Hangouts at my company so that’s a problem. I tried using the XMPP/Jabber protocol gateway to Hangouts but it is unsatisfactory:

  • The Jabber client stream doesn’t include any messages sent or received when Jabber is not connected
  • Jabber gets disconnected all the time
  • Voice and Video don’t work, although they used to when Hangouts was Google Talk
  • Google Voice voicemail messages are not delivered to Jabber
  • Google Voice SMS integration doesn’t work

So basically the XMPP gateway for Hangouts sucks.

Roll Your Own Hangouts.app With Fluid.app

It turns out that there is a Hangouts page on Google+. This page works in Chrome but also in Safari and Firefox. Pretty much everything in the Hangouts works. The only problem is that I can’t remember to open a browser window and point it there.

If you kind of squint at the Hangouts Google+ page, it kind of looks like a cross between the Hangouts Chrome extension and the Hangouts Chrome app for Windows but with a bunch of other crap in there too. I got the idea that I could get something similar to the Hangouts App for Chrome for Windows and Chrome OS on OS X if I used Fluid.app to roll my own native app wrapper for Hangouts. Fluid.app is a tool for generating WebKit site wrapper apps and it works pretty well to solve my Hangouts problem.

  • Chat history works
  • SMS and Voicemail works
  • Voice and Video works
  • It does everything that I want it to do
  • I can even pop out chats in and out of a tab or new window

Screen Shot 2015 02 11 at 12 19 25 PM
Screen Shot 2015 02 11 at 12 24 11 PM

Recipe

Fluid.app is a pretty geeky tool but the recipe to create a Hangouts app is pretty simple. At the most basic level, you can just create a new Fluid app by pointing to https://plus.google.com/hangouts and be done. It will not work correctly until you set user agent string for your new Hangouts.app to be Safari 7 but once you do that, it will work fine. You can use the Hangouts logo at the top of this article for the Dock icon.

By default Fluid apps use Safari’s cookies and will load Safari plugins. That means my Hangouts.app Just WorksTM. I am logged in by my Google Apps token in Safari. The Google Voice and Video plugin that I installed for Chrome also works in Safari and in the Hangouts.app to enable voice and video.

If you want to keep Hangouts open, even if you close the window, then in the Hangouts.app Preferences go to Behavior and select “Closing the last browser window: only hides the window”.

If you want it to be more minimalist standalone app look, then it is mostly a matter of hiding elements with some custom CSS injection in the Window > Userstyles menu.

Pattern: *plus.google.com*hangouts*

    div.Ege.qMc {
        visibility:hidden;
    }

    div#gbq {
        visibility:hidden;
    }

    div.gb_8.gb_Sc.gb_i.gb_Rc.gb_Qc {
        visibility:hidden;
    }

    div.ona.Fdb.csa {
        visibility:hidden;
    }

    div.Dge.fOa.vld {
        visibility:hidden;
    }

    div.Ima.dacD0d {
        visibility:hidden;
    }

    div.Bdc.FQb {
        visibility:hidden;
    }

And to add a little slickness add a little Userscript to fix the logo link so it links to /hangouts to pop out the buddy list by default as shown in my screenshots.

Pattern: *plus.google.com*hangouts*

    var i=0, 
        a = document.getElementsByClassName('gb_Wa gb_Ra'); //home logo link

    for(i=0; i<a.length; i++) {
        a[i].href='/hangouts';
    }

    window.onload = function() {
        setTimeout(function() {
        var j, h = document.getElementsByClassName('qoeSyc uoNTwd'); //hangouts buddy list icon element
        for(j=0; j<h.length; j++) {
            h[j].click(); //open the buddy list
        }
       }, 3000);
    };

Overall, I’m pretty pleased with how this turned out. I’m able to easily control my logged-in status on Hangouts by launching or exiting the app from my Dock. All the key feautures of Hangouts that I use work.

Update

These instructions are now obsolete. Google has created a standalone website for Hangouts at https://hangouts.google.com/. This site works great as a Fluid app without having to do any of the javascript and css hacks described above.

Screen Shot 2015 08 20 at 10 36 32 AM

Using Chrome with Tor on OS X

I’m living and traveling overseas. I want to have Tor as an option but I really just want to use it with Chrome — which I like a lot. My goal is to have the option to avoid national firewalls in some countries which use them. I’ve generally used SOCKS proxy over SSH in the past but it is good to have options. Plus, I have been reading Cory Doctorow’s Homeland (sequel to Little Brother) in which Tor is a prominent plot point in Homeland like “Finux” (Linux) and “Ordo” (PGP/GPG) in Cyrptonomicon.

I realize that Chrome sends information back to Google. I am even logged into Chrome, so this procedure isn’t hiding anything from them. Perhaps Chromium would be better. I’m not sure I want to constantly build from source every few weeks because Chromium is huge. These people have packaged vanilla Chromium plus Sparkle to update it. I may look into this in future.

The simplest way to use Tor for anonymized browsing is to download and install the Tor Browser Bundle. There are some aspects of this that I don’t find ideal — mostly I want to maintain Tor as part of my UNIX environment on OS X via MacPorts. I also like to have my hands in all the moving parts to learn how they work.

$ sudo port install tor

—> Updating database of binaries: 100.0%
—> Scanning binaries for linking errors: 100.0%
—> No broken files found.

$ tor
Mar 12 12:13:42.839 [notice] Tor v0.2.3.25 (git-17c24b3118224d65) running on Darwin.
Mar 12 12:13:42.840 [notice] Tor can’t help you if you use it wrong! Learn how to be safe at https://www.torproject.org/download/download#warning
Mar 12 12:13:42.840 [notice] Configuration file “/opt/local/etc/tor/torrc” not present, using reasonable defaults.
Mar 12 12:13:42.843 [notice] We were compiled with headers from version 2.0.19-stable of Libevent, but we’re using a Libevent library that says it’s version 2.0.21-stable.
Mar 12 12:13:42.843 [notice] Initialized libevent version 2.0.21-stable using method kqueue. Good.
Mar 12 12:13:42.843 [notice] Opening Socks listener on 127.0.0.1:9050
Mar 12 12:13:42.000 [notice] Parsing GEOIP file /opt/local/share/tor/geoip.
Mar 12 12:13:42.000 [notice] This version of OpenSSL has a known-good EVP counter-mode implementation. Using it.
Mar 12 12:13:42.000 [notice] OpenSSL OpenSSL 1.0.1e 11 Feb 2013 looks like version 0.9.8m or later; I will try SSL_OP to enable renegotiation
Mar 12 12:13:43.000 [notice] Reloaded microdescriptor cache. Found 3239 descriptors.
Mar 12 12:13:43.000 [notice] We now have enough directory information to build circuits.
Mar 12 12:13:43.000 [notice] Bootstrapped 80%: Connecting to the Tor network.
Mar 12 12:13:44.000 [notice] Heartbeat: Tor’s uptime is 0:00 hours, with 1 circuits open. I’ve sent 0 kB and received 0 kB.
Mar 12 12:13:44.000 [notice] Bootstrapped 85%: Finishing handshake with first hop.
Mar 12 12:13:45.000 [notice] Bootstrapped 90%: Establishing a Tor circuit.
Mar 12 12:13:48.000 [notice] Tor has successfully opened a circuit. Looks like client functionality is working.
Mar 12 12:13:48.000 [notice] Bootstrapped 100%: Done.

Tor creates a SOCKS proxy listening on localhost 9050. My first thought was to create an OS X network Location for Tor which configures all of my network interfaces to use SOCKS on localhost 9050.

Tor location

This does work in that applications that use the OS networking stack will switch to passing their traffic to SOCKS on localhost 9050, but it isn’t necessarily good enough for anonymizing with Tor because of the DNS leaking problem. In particular, browsers — specifically Chrome — not only don’t send their DNS traffic to the SOCKS server by default which affects your anonomyzation by leaking unencrypted UDP DNS requests to your ISP but also interferes with resolving Tor services on .onion domains.

I wanted to try and use Chrome with Tor, so this presented a problem. Poking around, I discovered a Chromium design document which has the solution for forcing Chrome to send all traffic — including DNS — to a SOCKS server. It requires passing arguments to Chrome or Chromium when starting the app.

–proxy-server=”socks5://myproxy:8080
–host-resolver-rules=”MAP * 0.0.0.0 , EXCLUDE myproxy
In order to use this mechanism, you have to exit all Chrome/Chromium processes and launch a new process with the appropriate flags.
 

killall Google\ Chrome
sleep 1 # give processes a chance to exit before launching
open -a Google\ Chrome –args –proxy-server=”socks5://localhost:9050″ –host-resolver-rules=”MAP * 0.0.0.0, EXCLUDE localhost”

A nifty feature of OS X is Automator, which can turn a script into an app via the Application document type. Start Automator and create a new Application document and add the “run a shell script” Action and paste in the script above. Automator will then allow you to save a .app file which can live in your Applications folder.

Screen Shot 2013 03 12 at 11 23 27 AM

I saved this automation as “Google Chrome for Tor.app”. Launching “Google Chrome for Tor” will close all my sessions in Chrome and launch a new Chrome process tree configured as a SOCKS client on my local Tor proxy. Using the chrome://net-internals URL verifies that Chrome is talking to Tor and also sending all of its DNS requests through Tor.

Screen Shot 2013 03 12 at 11 40 24 AMScreen Shot 2013 03 12 at 11 40 24 AM

Also, as an aside and note to self. SSH can be used with Tor via netcat. This means that the SSH tunnel passes through the Tor network and is useful if ssh over TCP 22 is blocked or monitored. It is bloody slow over my — relatively slow-ish, high-ish latency connection in Africa — it reminds me of SSH over GPRS.

 

 

Microsoft Puts h.264 Back into Chrome

Is I suspected they would, Microsoft has released an h.264 codec extension for Google Chrome to support h.264 in the <video /> tag. They provide the same mechanism for Firefox.

http://www.interoperabilitybridges.com/wmp-extension-for-chrome

This Extension is based on a Chrome Extension that parses HTML5 pages and replaces Video tags with a call to the Windows Media Player plug-in so that the content can be played in the browser. The Extension replaces video tags only if the video formats specified in the tag are among those supported by Windows Media Player. Tags that contain other video formats are not touched.

The Extension also checks if the browser version already supports MP4 (H.264) video codec, if so the extension is not used.

 

http://blogs.msdn.com/b/ie/archive/2011/02/02/html5-and-web-video-questions-for-the-industry-from-the-community.aspx

Any browser running on Windows can play H.264 video via the built-in Windows APIs that support the format. Our point of view here is that Windows customers should be able to play mainstream video on the Web.

 

Interesting.

Shocking: Google Removing h.264 Support from Chrome

To that end, we are changing Chrome’s HTML5 <video> support to make it consistent with the codecs already supported by the open Chromium project. Specifically, we are supporting the WebM (VP8) and Theora video codecs, and will consider adding support for other high-quality open codecs in the future. Though H.264 plays an important role in video, as our goal is to enable open innovation, support for the codec will be removed and our resources directed towards completely open codec technologies.

via The Chromium Blog

I’m very, very sad to see this announcement. h.264 is the web video standard while WebM is a new Google codec that nobody uses and nobody uses Theora. We really don’t need video codec fragmentation in HTML5. The likely result of that will just be standardization on Flash video. Do we get to re-hash the video format wars of the 1990s in the 2010s?

This sucks and basically shows that HTML5 is not standardized and not ready for prime time. Not cool at all.

This is an opportunity for Microsoft to look like a hero and fix this for Google like they did for Mozilla.

Headbang: Chrome, Flash and AppLocker Don’t Play Together

Google Chrome has bundled  a private copy of Flash since 5.0.375.86. Flash is packaged as gcswf32.dll in the Chrome application folder. All of the EXE and DLL files that come with Chrome as cryptographically signed by Google except for gcswf32.dll which is not signed at all. However, the NSAPI compatible Flash plugin distributed by Adobe is signed by Adobe.

This matters if you are using AppLocker DLL rules to define execution rules because there is no good way to create a permit rule for this DLL. AppLocker is a component of Windows 7 Enterprise and Ultimate SKUs which provides a straightforward mechanism for configuring Windows with a usable default-deny execution policy.

If you enable AppLocker EXE and DLL rules with the default rule set, Chrome will not run at all because Chrome is installed into the %userprofile%\AppData\AppData\Google\Chrome directory structure which is writable by unprivileged users. The obvious solution is to create EXE and DLL rules to permit code published by Google to execute. This works great, except that Flash will not execute. Why oh why is gcswf32.dll not signed by either Google or Adobe? There’s no way to whitelist this DLL except creating a permit *\gcswf32.dll rule or giving up on DLL rules altogether.

Workaround

A viable workaround is to use about:plugins to disable the Flash plugin Google distributes and globally install the “Flash player for Firefox, Mozilla, Netscape, Opera (and other plugin-based browsers)”. This plugin gets installed into C:\Windows\SysWOW64\Macromed\Flash on x64 or C:\Windows\system32\Macromed\Flash on x86 (and is signed by Adobe) is whitelisted by the default DLL rule set because it is inside of the Windows directory.

applocker-exeapplocker-dlldisable-chrome-bundled-flash

Chrome Team, Please Fix Me

You have to disable the bundled Flash player bundled by Chrome in order for Flash to work under this scenario, which means this is a workaround that is not enterprise ready unless your company is full of nerds. Also, this means that I have to worry about updating Flash instead of Chrome just handling it.

AppLocker is good stuff. Chrome is good stuff. I’d these technologies to play better together.

All Google needs to do is distribute a signed version of the Flash player plugin. Why aren’t they already doing this, anyway?

Please Chrome team, this is trivial to fix. Thanks in advance.

Reported as issue 65322.

Edit

A reasonable solution is to use the Chrome enterprise MSI installer which installs Chrome into the standard “program files'” location which means the default AppLocker permit rule on “program files” covers Chrome.

Update

I’m not sure when Google started distributing signed versions of Flash but in Chrome 12, gcswf32.dll comes with a valid signature from Adobe.

Mozilla Compatible Silverlight 4 Plugin Requires Loading DLLs from CWD

chrome-silverlight-agcore-missingI visited a site yesterday in Chrome that tried to load Silverlight to provide a video player. I have KB2264107 installed and have globally disabled loading of DLLs from the current working directory in order to mitigate luring attacks against apps that use the default insecure DLL loading behavior of LoadLibrary(). Just like the Java plugin for Mozilla, Chrome generated a big fat bonk dialog trying to load the DLLs that the Silverlight plugin uses. The specific missing file is agcore.dll, which is found in “C:\Program Files (x86)\Microsoft Silverlight\4.0.50524.0” on my system.

I tried creating a symlink to agcore,dll so that agcore.dll is in the same directory as Chrome.exe, which fixes the bonk but Silverlight doesn’t work. I just end up with a black box where the movie player should be. I also tried adding the Silverlight directory to $env:path which removed the bonk but, instead, I got the “Install Microsoft Silverlight” button. I tried various combinations of symlinking DLLs and messing with the $env:path but I didn’t arrive at a combination that can actually work.

The only solution that I found is to dial the CWDIllegalInDllSearch value for Chrome and Firefox to 2 (DLLs not allowed to load from CWD if CWD is any remote, network location) instead of 0xffffffff (it also works to change this globally). I then have to hope that Firefox and Chrome are careful about how they are using CWD. I hope they are setting CWD just for loading the installed plugins in “Prgram Files” but cannot be lured into loading some evil DLL from a spurious location when doing something like opening an HTML document on a USB stick.


PS> Get-ItemProperty chrome.exe, firefox.exe | select pspath,cwdillegalindllsearch | fl


PSPath                : Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVer
                        sion\Image File Execution Options\chrome.exe
CWDIllegalInDllSearch : 2

PSPath                : Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVer
                        sion\Image File Execution Options\firefox.exe
CWDIllegalInDllSearch : 2




Google Developer Dashboard: “An error occurred: please try again later.”

When I use the Google Developer Dashboard to upload an update to my extension, it will often fail. The dashboard says, “An error occurred: please try again later.”.

Trying again later doesn’t help. For me, the fix is to log out and log back in again.

My First Chrome Extension, part 2

I was fixated on how to get Chrome to pass a feed URL to my registered feed reader, Outlook 2010. After I figured out how to pass the message, it was nearly a one-liner to modify an existing extension. How many things could be wrong with one line of code that seems to be working?

url = url.replace( "%f", feedUrl.replace( "http:", "feed:" ) );

There are two bugs that I see in here.

1: URLs can legitimately include %f

From RFC 1738:

In addition, octets may be encoded by a character triplet consisting of the character "%" followed by the two hexadecimal digits (from "0123456789ABCDEF") which forming the hexadecimal value of the octet. (The characters "abcdef" may also be used in hexadecimal encodings.)

In practice this would affect URLs that include these characters:

  • ð -> %F0
  • ñ –> %F1
  • ò –> %F2
  • ó –> %F3
  • ô –> %F4
  • õ –> %F5
  • ö –> %F6
  • ÷ –> %F7
  • ø –> %F8
  • ù –> %F9
  • ú –> %FA
  • û –> %FB
  • ü –> %FC
  • ý –> %FD
  • þ –> %FE
  • ÿ –> %FF

The simple solution is to avoid using a macro character sequence that includes valid hexadecimal values. Sticking with a single ASCII character it could be anything from ‘g’ trhough ‘z’ except ‘s’, which is already being used.

2: A feed URL could have “http:” anywhere

The scheme for a URI is always at the beginning, but it is common for one URI to encode another one inside it. That is exactly how you pass a feed URL to Google Reader.

Imagine a feed URL like “http://feedify.exmaple/q=http://mysite.somewhere/page”. In order to pass this to the feed scheme handler I need to replace the http: scheme with feed: but my original code would also replace the second http:, which would break the URL.

The solution is to use a regular expression so that I only replace the http: scheme rather than every occurrence of the pattern “http:”. In Regular Expression syntax, the ^ character means that the pattern has to start with the beginning of the string.

^http:

I JavaScript, the shorthand for a Regular Expression object is paired / characters:

/^http:/

3:URI schemes are not case sensitive

The replace() method of the JavaScript String object is case sensitive. My code would not work on a URL like “HTTP://mysite.example/stuff”.

Fortunately, the Regular Expression object in JavaScript has a case-insensitive match mode. You set this with the ‘i’ option:

/^http:/i

3 Bugs in 1 Line: Fixed

url = url.replace( "%g", feedUrl.replace( /^http:/i, "feed:" ) );

My First Chrome Extension

I am fairly hooked on Microsoft Outlook and I like to use the RSS reader function. I also have been living in Chrome 4 instead of Firefox for a few weeks now. One of the features of Firefox and IE is the ability to discover RSS and ATOM feeds and pass them to the registered feed reader.

Google has an extension for Chrome that almost does what I want. It discovers the feeds but it will only subscribe them with a web-based feed reader. What I did was hack on the RSS Subscriptions Extension (by Google) so that it would work with Outlook.

screen-shot

Outlook feeds by registering itself as the handler for the FEED scheme.

hkcr-scheme

That means anything that tries to invoke a URI like feed://somewhere.com/feed.rss, Outlook will be invoked with the /share switch like this:

"C:\PROGRA~1\MICROS~2\Office14\OUTLOOK.EXE"/share "feed://somewhere.com/feed.rss”

Outlook is very picky about the scheme on the URL. It has to be feed:// or it won’t work and that is the problem with the Google extension. It always puts the http:// scheme onto the URL and it assumes that you are going to embed this into a larger querystring to a web-based feed aggregator.

It was pretty easy to tweak the extension to do what I wanted. The meat of the change is really just one line of code:

url = url.replace( "%f", feedUrl.replace( "http:", "feed:" ) );

That makes it so that %f is a feed: scheme URI that will invoke the registered feed handler. For me this is Outlook 2010.

edit-shot

There is a little bit of supporting stuff here and there to make this work, but that’s the gist. Now, %f is the magic configuration to invoke the registered feed:// scheme application. It was surprisingly easy to get hacking on the extension and Google made it very easy to publish my work.

If this scratches your itch, you can get it from Chrome Extensions site.