Archive for April, 2009

As promised my hacks to get past the the quirks in the webservice API for the Adobe forums. I am deliberately not publishing the full application, the lack of local caching in it makes it more of a DOS tool then a forum client.

Authentication

The PermissionsService authenticate method doesn’t work since the Adobe forums do not use the standard Jive login methods, but a custom Adobe SSO login method. To get a login on the forums from AIR replay what a browser does when logging in to the forums. First visit the index page of the forums to GET a few cookies, then POST the credentials to the Adobe authentication server, then GET the index page of the forums again to allow the forums to do a call back to the Adobe authentication server to collect the user profile. So all in all it takes 3 HTTP requests to log on.
I have included the code snippet that my AIR app uses to log in to the forums below for those who wish to experiment with it. Be warned that the full sequence takes on average 15 seconds.

?View Code ACTIONSCRIPT
// General variables
private const _AdobeAuthURL:String = "http://www.adobe.com/cfusion/entitlement/index.cfm?loc=en&e=ca&returnurl=http%3A%2F%2Fforums%2Eadobe%2Ecom%2Flogin%2Ejspa";
private const _ForumRootURL:String = "http://forums.adobe.com/index.jspa";
 
private function startLoginSequence():void {
	writeLog("Starting login ");
	getForumRoot(preAuthResult);
}
private function getForumRoot(resultFn:Function):void {
	// just GET the forum root to collect cookies
	var forumRootService:HTTPService = new HTTPService();
	forumRootService.method = "GET";
	forumRootService.url = _ForumRootURL;
	forumRootService.useProxy = false;
	forumRootService.resultFormat = "text";
	forumRootService.addEventListener(FaultEvent.FAULT, faultHandler);
	forumRootService.addEventListener(ResultEvent.RESULT, resultFn);
	forumRootService.send();
}
private function preAuthResult(event:Event):void {
	// We have now collected the forum cookies, log in to the Adobe ID
	login();
}
private function login():void {
	// login does a login request to the main Adobe site
 
	// credentials Object with all name value pairs
	var credentials:Object = new Object();
	credentials['returnURL'] = 'http://forums.adobe.com%2Flogin.jspa';
	credentials['up_login'] = 'yes';
	credentials['ignore_email_validation'] = 'yes';
	credentials['up_username'] = "spam@vandieten.net";
	credentials['has_pwd'] = "true";
	credentials['up_password'] = "sihtyrt";
 
	// login Service
	var loginService:HTTPService = new HTTPService();
	loginService.method = "POST";
	loginService.url = _AdobeAuthURL;
	loginService.useProxy = false;
	loginService.resultFormat = "text";
	loginService.addEventListener(FaultEvent.FAULT, faultHandler);
	loginService.addEventListener(ResultEvent.RESULT, loginResult);
	loginService.send(credentials);
}
private function loginResult(event:ResultEvent):void {
	// check if we are really logged in
	var loggedInTestString:String = '<a href="http://www.adobe.com/cfusion/membership/logout.cfm">Sign out</a>';
	if (event.result.indexOf(loggedInTestString) != -1) {
		// Login success
		// Do a new HTTP request to the forums to propagate the login from Adobe to Jive
		getForumRoot(postAuthResult);
	} else {
		// Login failure
		throw("Username password combination incorrect.");
	}
}
private function postAuthResult(event:ResultEvent):void {
	// Fully logged in, ready to use the API
	writeLog("Login complete");
 
	// extractUserDetails(event.result);
}

Once we are logged in we can use all of the APIs to get the actual useful information from the forums. The FlexBuilder WSDL import tool works quite well with the APIs, all cases where it failed turned out to be mistakes in my programming. To get stared call getRecursiveCommunities of the CommunityService to get a list of all the communities (forums) available. Depending on how busy the forums are, internet bandwidth, traffic and the position of the moon this can take between 25 seconds and 5 minutes. From the list of forums you get you can drill down into the list of threads (ForumService getThreadsByCommunityID: up to 50 seconds to get the thread list of the DreamWeaver forum, the busiest forum with about 50K threads) and then into the list of messages per thread (ForumService getMessagesByThreadID: usually less then a second). When you get the messages you will also get the users and all things you need to display a tree of who responded to who.

Getting your own userID

Apart from the login methods in the API there appears to be another problem in the webservice API (or maybe just something I haven’t figured out). Searching for a user, either by his username or by his emailaddress, has never returned any results for me. (I do in fact get a 500 Internal server error when I try to use the UserService getUserByUsername.) But the user details are needed for all API methods to post messages. The workaround I implemented at last for that is to just do a string lookup in the HTML of the forum start page (the commented out call to extractUserDetails in the previous code snippet):

?View Code ACTIONSCRIPT
public function extractUserDetails(pageObj:Object):void {
	var page:String = pageObj as String;
	var userIDPreString:String = 'quickuserprofile.getUserProfileTooltip(';
	var userIDPreStringIndex:int = page.indexOf(userIDPreString) + 39;
	var userIDPostString:String = ')';
	var userIDPostStringIndex:int = page.indexOf(userIDPostString, userIDPreStringIndex);
	_userID = page.substring(userIDPreStringIndex, userIDPostStringIndex) as int;
}

Obviously depending on visiting some sites in a certain order to pick up cookies along the way and scraping generated HTML is rather fragile. The smallest change in the HTML or the authentication will break any application that uses this. Ideally Adobe would implement some authentication webservices on its own site to facilitate logging in from an application.

With this the basic services to list forums, retrieve threads, read messages and post your own messages can be accessed without further surprises. I intend to continue working on my ForumClient, but it will be a while. I will need a proper design for the application, I am thinking about modules to support different forum software API’s, storing configuration data in XML, message caching in a SQLite database per configured site etc. Then I need to develop the whole thing. And in the mean time real life is catching up and I am going on a training tour of Europe, so I will have little time for the next three weeks. Drop me a line if you are interested in helping out, or don’t wait for me and just get started on your own client :)

I managed to get a proof of concept AIR client for the Adobe forums running. It can log in, browse communities, threads, and messages and can even reply. But since it doesn’t do much with local storage yet it doesn’t remember which messages are already read. I’ll post some more on the hacks to log in to the forums soon, but for now I have screenshots.

ForumClient Screenshot

ForumClient Screenshot

ForumClient screenshot

ForumClient screenshot

Just a quick update on the two avenues I am pursuing to get the new Adobe forums to open up and share their content.

Adobe is going to followup on the email headers with Jive to see what they can implement. Hopefully this will result in getting the References and / or In-Reply-To headers added to the outgoing email notifications soon so email clients can thread them properly. It should be almost trivial to implement that, because judging by the definition of the ForumMessage object each forum message knows its own ID to put in the Message-ID header and also knows its ParentMessageID to put in the References header.

The other avenue is getting access through the webservice APIs. I have been looking at the login sequence of the Adobe site and it looks like the master cookie to determine whether you are logged in is the AUID cookie for the .adobe.com domain. Subsites may add more cookies and eventually maintain state through their own cookies, but if you have the AUID cookie you can get in. So I have reduced the dozens of requests involved in a browser logging in to the forums (many of them image requests) to two requests a client must make. I have included the minimal wget testscript to simulate the login here.

?View Code WINBATCH
REM parameters
SET USERNAME=spam@vandieten.net
SET PASSWORD=sihtyrt
SET RETURNURL=http%3A%2F%2Fforums.adobe.com%2Flogin.jspa
SET LOGINURL="https://www.adobe.com/cfusion/entitlement/index.cfm?loc=en&amp;e=ca&amp;returnurl=http%3A%2F%2Fforums%2Eadobe%2Ecom%2Flogin%2Ejspa"
SET FORUMURL=http://forums.adobe.com/index.jspa
 
REM remove old cookie and index
del adobe.txt
del forumindex.html
 
REM login
wget -S --no-check-certificate --save-cookies=adobe.txt --post-data "returnURL=%RETURNURL%&amp;up_login=yes&amp;ignore_email_validation=yes&amp;up_username=%USERNAME%&amp;has_pwd=true&amp;up_password=%PASSWORD%" %LOGINURL%
REM get forum index
wget -S --load-cookies=adobe.txt -O forumindex.html "%FORUMURL%"

I need to tweak this a bit to do proper login validation, but it looks like just ignoring all the authentication mechanisms in the API and running these requests instead and then maintaining the collected cookies with all webservice calls will do the trick. Even though the entire login sequence will end up taking close to 15 seconds due to the response times of the Adobe sites, that is progress!