Skip to content

Instantly share code, notes, and snippets.

@kroo
Last active June 26, 2017 18:11
Show Gist options
  • Save kroo/11205755 to your computer and use it in GitHub Desktop.
Save kroo/11205755 to your computer and use it in GitHub Desktop.
A quick n' dirty hack to fix a dying GrowlVoice

How to Use

Open up Terminal.app in your /Applications/Utilities directory, then type in these commands, one after each other:

  1. Create a temporary directory for cycript:

    mkdir cycript && cd cycript
    
  2. Pull the latest cycript from cycript.org:

    curl -L https://cydia.saurik.com/api/latest/3 -o cycript.zip
    
  3. Unzip cycript:

    unzip cycript.zip
    
  4. Download fix_growlvoice.js:

    curl https://gist.githubusercontent.com/kroo/11205755/raw/fix_growlvoice.js -o fix_growlvoice.js
    
  5. Start GrowlVoice now.

    open -a GrowlVoice
    
  6. A popup window should appear about malformed JSON. Close it.

  7. Ensure cycript is executable:

    chmod +x ./cycript
    
  8. Patch the running GrowlVoice process:

    sudo ./cycript -p GrowlVoice fix_growlvoice.js
    

Each time you restart GrowlVoice, you will need to run this fix script again. Run cd ~/cycript && sudo ./cycript -p GrowlVoice fix_growlvoice.js to run the script sometime later.

/**
* A quick n' dirty hack to fix a dying GrowlVoice
*
* GrowlVoice is now officially dead: https://twitter.com/GrowlVoice/status/455931023868448768
* However, Google didn't really shut off API access (there wasn't really any to begin with);
* they slightly mangled a JSON object GrowlVoice was looking for on one of Google Voice's
* internal pages.
*
* The following is a Cycript script that will bring a dead GrowlVoice back
* to life for the moment (until Google decides to mangle things more).
*
* To use:
*
* - Download cycript from cycript.org, and extract somewhere sane
* - download this script to the same place as cycript
* - Start GrowlVoice (it will complain about malformed JSON)
* - Run sudo ./cycript -p GrowlVoice fix_growlvoice.js
*/
@import com.saurik.substrate.MS
var oldm = {},
NSUTF8StringEncoding = 4,
MESSAGE = @selector(accountInfoFetcher:finishedWithData:error:),
START_TOKEN = 'var _gcData = ',
END_TOKEN = '};';
// It's failing in accountInfoFetcher:finishedWithData:error:, a callback method
// that parses a response from Google's auth page, and pulls in a bunch of info.
//
// Since it's failing there, let's hook it, and massage the data we're getting
// back until GrowlVoice can understand it again.
//
// Using MobileSubstrate, the following line 'swizzles' the method, replacing
// it with our own wrapper function:
MS.hookMessage(GoogleVoiceLoginInterface, MESSAGE, function (fetcher, data, err) {
// In particular, we need to find a json object embedded in the page that was
// just returned into this function (as the second arg, data).
// Convert to NSString from NSData
var sdata = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
// Find the start and end of the JSON object, then split the page into 3
// sections: before the json object, after, and the object itself. This is
// pretty fragile, but exactly what GrowlVoice does, so we might as well be
// consistant.
var startIndex = sdata.indexOf(START_TOKEN) + START_TOKEN.length;
var endIndex = sdata.indexOf(END_TOKEN, startIndex) + 1;
var prefix = sdata.slice(0, startIndex);
var suffix = sdata.slice(endIndex);
var authJSON = sdata.slice(startIndex, endIndex);
// Now, fix the JSON object. This method is robust, but dangerous:
// Google's new JSON encoding format only works when evaluating as a
// javascript statement -- it's really not even close to valid json.
// Therefore, we evaluate that line as a javascript statement, then
// re-encode back to well-formed json.
eval('var gcData = ' + authJSON);
var fixedJSON = JSON.stringify(gcData);
// reconstruct the original page, and pass back through to the original method
var newdata = [(prefix + fixedJSON + suffix) dataUsingEncoding:NSUTF8StringEncoding];
oldm->call(this, fetcher, newdata, err);
}, oldm);
// trigger the signin process again
[choose(GoogleVoiceLoginInterface)[0] signIn];
@Critter
Copy link

Critter commented Apr 23, 2014

I get this when I run the command..
*** _krncall(task_for_pid(self, pid, &task)):../Mach/Inject.cpp(65):InjectLibrary [return=0x5]

any ideas?

Edit: sudo fixed it. Thanks

@kroo
Copy link
Author

kroo commented Apr 23, 2014

Good point. Updated instructions.

@katzly
Copy link

katzly commented Apr 23, 2014

really glad you took the time to write this up and post.

noob question, but apparently i am doing something wrong. i am using the following string:

sudo /Users/Eli/GrowlVoiceFix/Cycript cycript -p GrowlVoice fix_growlvoice.js

I am getting "command not found" error.

did i place the "fix growlvoice" in the wrong place - see directory results here (i place the .js file in the cycript main folder - http://www.screencast.com/t/NeaFzVNrNZ6i

@kroo
Copy link
Author

kroo commented Apr 23, 2014

@katzly, you probably need a / between Cycript and cycript. Try:

sudo /Users/Eli/GrowlVoiceFix/Cycript/cycript -p GrowlVoice /Users/Eli/GrowlVoiceFix/Cycript/fix_growlvoice.js

(assuming fix_growlvoice.js is in the Cycript directory)

@rdodesigns
Copy link

It seems that the only malformed parts of the json are

  1. ' instead of " for strings, and
  2. the following object contains an extraneous comma
'settings': {
   'voicemailFeature1': false,
}

When I regexed the single quotes into double quotes and fixed the comma the resulting json was considered valid by at least one parser. It should be simple to take out all cases of commas before a ] or } and all the single quotes using NSRegularExpression.

@kroo
Copy link
Author

kroo commented Apr 26, 2014

@rdodesigns, agreed. GV may decide to change that at some point, of course.

@jgruman
Copy link

jgruman commented Apr 27, 2014

Trying to run it for the first time, and getting the following error when I run the command in step 8:
dyld: Library not loaded: /usr/lib/libapr-1.0.dylib
Referenced from: /Users/jgruman/cycript/Cycript.lib/cycript
Reason: Incompatible library version: cycript requires version 5.0.0 or later, but libapr-1.0.dylib provides version 4.0.0
Trace/BPT trap

@kroo
Copy link
Author

kroo commented Apr 27, 2014

@jgruman, which version of OS X are you running? It looks like cycript was compiled with a newer version of libapr than is installed on your system...

@jgruman
Copy link

jgruman commented Apr 28, 2014

@kroo, 10.6.8

@tonyknight
Copy link

Not sure if this is going to work for me but even if it doesn't, many thanks for putting the effort out to help those with severe GrowlVoice withdrawals (like me).

@tonyknight
Copy link

It worked for me, mate. Great job!

@jaydixit
Copy link

jaydixit commented May 3, 2014

The line to run the script later should be:
cd /Applications/Utilities/cycript && sudo ./cycript -p GrowlVoice fix_growlvoice.js

@aholub
Copy link

aholub commented May 5, 2014

I'm getting the error

Error: unrecognized selector signIn sent to object 0x60000001fd20

Any guesses as to why? Thanks.

@tarblaster
Copy link

@kroo - I'm on OSX 10.6.8 as well with the same error, any ideas on getting libapr-1.0.dylib
v5? Other than building it from http://www.opensource.apple.com/source/apr/apr-29.1/ which I assume is the correct source?

I wonder if someone on system 10.7+ could share their library file "/usr/lib/libapr-1.0.dylib" and those of us on 10.6.8 could use it to link our cycript?

@tarblaster
Copy link

Finally able to fix the libapr-1.0.dylib error, used Mac Ports to download a new version of "apr".

Now I'm having a new error:
Error: *** _assert(count == TASK_DYLD_INFO_COUNT):../Mach/Inject.cpp(117):InjectLibrary
any ideas?

@szhu
Copy link

szhu commented May 8, 2014

I made a wrapper to automate running this patch every time GrowlVoice is launched.
This should remove any recurring hassle that comes with using this hack.

See here » http://szhu.github.io/fix-growlvoice/

@tjluoma
Copy link

tjluoma commented May 10, 2014

I have used szhu's patch and it worked flawlessly for me.

Yay Internet!

@debryc
Copy link

debryc commented May 18, 2014

I'm getting a similar "unrecognized selector signIn sent to object" error. Any ideas?

@jpwagner
Copy link

to those with "unrecognized selector signIn sent to object":

did you delete your user account? simply attempt to create it again. worked for me.

@vicentemendez
Copy link

This script does not work OSX 10.10. Will patiently wait for a fix.

@andrewcking
Copy link

I receive the following error in Mac OS X 10.10: "ReferenceError: Can't find variable: require"

@edgardluz
Copy link

+1 not working on 10.10 with the same referenceError: Can't find variable: require. Any love for Yosemite?

@dnasralla
Copy link

I was thinking of making an Applescript to run Growl Voice and execute the command. Doesn't quite work:
do shell script "open -a GrowlVoice"
do shell script "~/cycript/cycript -p GrowlVoice fix_growlvoice.js" with administrator privileges

It returns an error
error "*** _assert(!stream->fail()):../Console.cpp(874):Main" number 1

Any thoughts on how to make a single double-clickable to launch GrowlVoice?

(this has been a helpful thread!)

dave

@bmourit
Copy link

bmourit commented Jul 3, 2014

@dnasralla,

If you are just wanting to avoid running the terminal command manually at each launch, then this should take care of things for you.

@szhu
Copy link

szhu commented Jul 29, 2014

Hey guys, if you're trying to get this to work under 10.10 and have some time to spare for debugging, cycript author @saurik gave some pointers on how to fix this in szhu/fix-growlvoice#6.

@szhu
Copy link

szhu commented Aug 4, 2014

Another update for you guys — d235j/GVFixer is a SIMBL plugin that works on 10.10 and is dead simple to set up.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment