Breakthrough!

Thursday, 2006-09-07; 04:32:00


Cookies were the (almost) final obstacle.

That's right, I hacked through the last hurdle of the .mac commenting system! Victory is mine*!

*albeit with a few not-so-small catches

The cookie that appeared in the packet dumps when iWeb contacted the WSComments.woa web application were indeed what was causing the problem with authentication. After authentication happens over HTTPS, the .mac server responds with an XML response that indicates success. But the key, in this case, was the fact that there was also a "Set-Cookie" HTTP response header included with the XML response, and this isn't caught if you only examine the XML that is returned.

Given that I saw this header, I went scouring around Google to find how I could read and write HTTP headers in XML responses and requests. I ended up stumbling upon this page, which indicated a way to set extra HTTP headers to go along with the XML request. (This page appears on page 5 of a Google search for "Cocoa xmlrpc headers".)

This provided a way to set HTTP headers. Now how to read them? Armed with this first tidbit, I went through the Apple developer documentation and found this. It looked like a promising way to be able to read the extra headers from an XML response. Unfortunately, my attempts at using this information failed -- I would always get NULL rather than the expected info that I wanted. So I posted on the Cocoa-Dev mailing list, seeing if anybody knew how to use these properties correctly.

The next day, I received a response from a certain Byron on the Cocoa-Dev list, who gave me three perfect lines of code that would do exactly what I wanted:

CFDictionaryRef result = WSMethodInvocationInvoke(theRPCCall);
CFHTTPMessageRef headers = (CFHTTPMessageRef)CFDictionaryGetValue(result,kWSHTTPResponseMessage);
CFDictionaryRef  headerDict = CFHTTPMessageCopyAllHeaderFields(headers);


Printing the resulting header dictionary revealed what I was hoping for: the XML response after authenticating to the mac.com server responded with a header that contained values for the variables "wosid" and "woinst", the same variables that iWeb seemed to transmit with the subsequent XML requests. Duplicating this functionality of iWeb by retransmitting these headers:

NSDictionary *cookieHeaderDict = [NSDictionary dictionaryWithObjectsAndKeys:
                [NSString stringWithFormat:@"wosid=%@; woinst=%@",sessionIDString,sessionInstanceString],@"Cookie",nil];
WSMethodInvocationSetProperty(theRPCCall, (CFStringRef) kWSHTTPExtraHeaders, (CFTypeRef) cookieHeaderDict);


allowed me to get a valid response from the XML web application. In this case, it gave me a "1404" error, but at least it wasn't complaining that I wasn't authenticated anymore! (It was giving me this error because I was sending the XML requests out of order, since the first request required an unwieldy struct as a parameter, so I used a different method first to facilitate slightly easier coding.)

Once I got to this point, everything started falling into place. I invoked the various XML requests in the proper order, and voilà! I was able to get a functional "Add a Comment" page up linked to a URL that was wholly unrelated to iWeb.

The hardest part at this point was to figure out the display of comments in the weblog entry pages. And there are some definite quirks with this system. The comment.js JavaScript that's linked to from every iWeb page dynamically inserts the comments into the page. So my friend and I had to do a bit of reducing on the iWeb pages to figure out which divs were the targets for the dynamic insertion of the comments. (iWeb's resultant pages are a perfect example of div hell. Yeesh, tables have just gotten replaced by divs, but the markup created from GUI webpage editors is still remarkably horrid.)

Basically, when the page loads, a JavaScript call is executed that makes an XML request of the .mac commenting server, retrieves the comments for the page's URL, and then targets specific divs by IDs and inserts any comments into there (the div ids are "comment_layer", "comment_title", "paragraph Comment_Count", "comment_list", "comment_footer", and "paragraph Comment_Add_A_Comment"). For the most part, I was able to use the comment.js file straight on the .mac server without modification. However, iWeb also creates entry-specific JavaScript files, and I had to filch three functions from there and put them in iBlog's site-wide JavaScript file.

Once the reduction was complete, I was able to get a weblog entry page working with .mac comments! Check out the bottom of the page -- you should see at least one comment from me at the bottom of the page. Note that all the functionality of the .mac comments are preserved: the lock icon that allows you to moderate the comments, the dynamic refreshing of the page once you add a comment, etc.

To make the page work, I had to add this to the page header:

<script language="javascript" type="text/javascript"><!--
  var GUID = 'index.html';
  var commentsActivated = true;
--></script>
<script type="text/javascript" src="http://www.mac.com/1/up/comments/scripts/comments.js"></script>


The BODY tag needs to look like this:

<body onload="onPageLoad();">


And this set of divs needs to be added where you want comments to be inserted:

        <div>
                <div id="comment_layer">
                        <div id="comment_title">
                                <div class="graphic_textbox_style_default" id="id2">
                                        <div>
                                                <div class="graphic_textbox_layout_style_default">
                                                        <div class="paragraph Comment_Count"><span><span id="count_0"></span></span><span> </span><span id="manage_comments_0" class="light"></span></div>
                                                </div>
                                        </div>
                                </div>
                        </div>
                        <div id="comment_list"></div>
                        <div id="comment_footer">
                                <div class="graphic_textbox_style_default" id="id3">
                                        <div>
                                                <div class="graphic_textbox_layout_style_default">
                                                        <div class="paragraph Comment_Add_A_Comment"><span id="post_link_0"></span></div>
                                                </div>
                                        </div>
                                </div>
                        </div>
                </div>
        </div>


Finally, you need to these functions to a site-wide JavaScript file, or, I suppose, in the HTML source itself in the header:

function onPageLoad()
{
    evalAndIgnoreException('initComments();');
    evalAndIgnoreException('commentLayerLinkAssist();');
    return true;
}

function evalAndIgnoreException(s)
{
  try
  {
    eval(s);
  }
  catch (e)
  {
  }
}

function commentLayerLinkAssist()
{
    if (location.hash == '#comment_layer')
    {
        var node = document.getElementById('comment_layer');
        if (node)
        {
            var offsetY = node.offsetTop;
            while (node.offsetParent != null)
            {
                node = node.offsetParent;
                offsetY = offsetY + node.offsetTop;
            }
            window.scrollTo(0, offsetY);
        }
    }
}


Of course, you also need my nifty little app that contacts the xmlrpc web app and submits the required XML requests. I will be releasing that soon, when I work out some of the kinks.

Caveats

The .mac commenting server uses URLs as the IDs for pages. It also uses URLs to open the "Add a Comment" pages. Obviously these URLs will be limited to the mac.com domain. However, while the .mac server back-end itself isn't limited beyond that, the "Add a Comment" pages are: there's no way to make a URL from the homepage.mac.com subdomain into the required format for the "Add a Comment" URLs. The result is that you'll only be able to use .mac commenting using the web.mac.com subdomain (unless you do more hacking).

If you don't have iWeb, here's what you need to do: in the root folder of your iDisk, create a folder named "Web" (you may need to, instead, create an empty folder on the desktop, rename it, and copy it to the root folder of your iDisk). Inside this folder create one called "Sites". Anything you put inside this "Sites" folder will be accessible on the web. So if you have a file at the path Web/Sites/test.html , you can access it at http://web.mac.com/username/test.html .

There's another more insidious caveat. I mentioned before that you must have a valid HTML file existing at a given URL before you can use that URL to post comments. If there's no HTML file present, you'll get a "This entry has been moved or deleted," error when you try to add a comment. I also noted before that you can temporarily rename the file and then rename it back, and any comments that were there were preserved.

However, here's the bad news: if you even touch the HTML source of a file that has comments attached to it through the .mac commenting system, those comments will be obliterated once you save the file. You can just add a space and delete it again, and your comments will still be gone. It seems that iWeb has some way to overcome this limitation -- however, if you manually modify an iWeb-created file, even iWeb won't be able to recover your comments: it'll give you an error when it attempts to publish any changes you've made in iWeb.

I haven't figured out how iWeb partially overcomes this limitation, but it is quite serious. I'm not even sure why the .mac commenting system is sensitive to changes in the HTML files -- what possible reason would there be for this "feature"?

One workaround to this problem would involve a little more hacking: I could have dummy html files in the Web folder of my iDisk, that I will strictly not touch at all. These files would exist solely for the purpose of placating the .mac commenting system to show it that there are valid HTML files at those URLs. Then, I would keep my weblog pages on the homepage.mac.com subdomain, but hack the JavaScript so that the retrieval URL for the comments comes from the web.mac.com subdomain instead. The benefit of this would be two-fold: I wouldn't have to worry about modifications done to HTML files for my weblog, AND I wouldn't have to move my weblog from homepage.mac.com to web.mac.com and break many existing links.

In any case, that's how things stand right now.


Technological Supernova   Tips   Older   Newer   Post a Comment