Archive

Archive for the ‘Google App Engine’ Category

Special Cross Domain Restriction In Firefox

March 2, 2011 Leave a comment

The Mystery

While working with Iframe and document.domain trick to get around the same-origin policy against cross domain restriction on web-browsers I came across an interesting oddity in Firefox (a hidden security feature if you will!).

The reference page for document.domain setting policy here says:

A script can set the value of document.domain to a suffix of the current domain

What is obvious that this prefix can go only upto anything below a top-level-domain (TLD). This page provides some more info about this (in the first two paragraphs).

Armed with this info I expected to be able to communicate between two of my apps, both hosted on google appengine (http://app1.appspot.com & http://app2.appspot.com). The idea was to set document.domain to appspot.com in both, the parent page on http://app1.appspot.com and in the child iframe loaded from http://app2.appspot.com.

Works great in Google Chrome! However, true to its spirit of being a pain in an unmentionable, the cross-browser beast rises again and bites me in the same unmentionable: It fails on firefox! On the JS document.domain = appspot.com on the page hosted on http://app1.appspot.com, it throws an error: “Illegal document.domain value” code: “1009.

A little more digging. I test using DynDNS. Now with my apps accessible at http://mydomain1.dyndns.org and http://mydomain2.dyndns.org it works! So it has to be a problem specific to appspot! Is firefox handling appspot.com specially? To be honest I found the idea ridiculous when some colleagues suggested this. But I still decided to give it a try: I search for appspot.com through mozilla source code at mxr. Lo and behold!, there is an entry for appspot.com here. Looking for effective tld names on google reveals the mystery.

The Mystery: Solved

As hinted here firefox needed to tell that both .com and .co.uk types of names are valid TLDs (top level domains). To do this they have included a special static file with firefox with a list of all TLDs. So for each entry X in this file you can strip document.domain to any sub-domain of X (y1.X or y2.y1.X or y3.y2.y1.X and so on) but not to X (even if X has dots(.) inside it). The evil piece is the appspot.com is also an entry in this file (so that firefox considers appspot.com as a TLD). As a result the Iframe and document.domain trick will not work for any app hosted on app engine.

Posting photo to wall using Facebook Graph API

February 12, 2011 22 comments

First post in 8 months! I have been busy (no, really! :P)

I have been recently working on a small Facebook app FriendGeo, which is on the verge of being finished as I write. One conclusion: Facebook API is cool, the documentation is NOT. I had my full share of frustration trying to deal with hidden (or so it seems) features and, more painful, restrictions on Facebook API.

Anyway, coming back to the topic, my app needs to post an image to the user’s wall. True to its RESTfulness, the graph API allows you to do so by POSTing to the photos connection of the user. In a perfect world, a simple HTTP POST to https://graph.facebook.com/userid/photos with required parameters (the names of which you are left to guess or google!) should do.

A little bit of tinkering with google search query leads to forum posts like this. Fortunately, the PHP SDK for the Facebook API allows you to upload the photo by using a simple call to setFileUploadSupport. Unfortunately for me, I am using App Engine as my hosting solution and using Python for writing my backend and the official Python SDK for Facebook does not seem to be supporting any such provision. Solution: fall back on Python ‘batteries’ and use raw Python and HTTP.

To demonstrate how to do this with a self contained example I have written this small script that creates a test user and uploads a photo from disk to the test user’s account. The comments in the code below should be enough to guide you through the code.

import sys
import os
import itertools
import mimetypes
import mimetools
import urllib
import urllib2
import cgi

try:
    import json
except ImportError:
    from django.utils import simplejson as json

# this class allows you to upload a file to a URL
# using pure urllib
class MultiPartForm(object):
    """Accumulate the data to be used when posting a form.
       source: http://www.doughellmann.com/PyMOTW/urllib2/#uploading-files
    """

    def __init__(self):
        self.form_fields = []
        self.files = []
        self.boundary = mimetools.choose_boundary()
        return

    def get_content_type(self):
        return 'multipart/form-data; boundary=%s' % self.boundary

    def add_field(self, name, value):
        """Add a simple field to the form data."""
        self.form_fields.append((name, value))
        return

    def add_file(self, fieldname, filename, fileHandle, mimetype=None):
        """Add a file to be uploaded."""
        body = fileHandle.read()
        if mimetype is None:
            mimetype = mimetypes.guess_type(filename)[0] or 'application/octet-stream'
        self.files.append((fieldname, filename, mimetype, body))
        return

    def __str__(self):
        """Return a string representing the form data, including attached files."""
        # Build a list of lists, each containing "lines" of the
        # request.  Each part is separated by a boundary string.
        # Once the list is built, return a string where each
        # line is separated by '\r\n'.
        parts = []
        part_boundary = '--' + self.boundary

        # Add the form fields
        parts.extend(
                     [part_boundary,
                     'Content-Disposition: form-data; name="%s"' % name,
                     '',
                     value,
                     ]
                     for name, value in self.form_fields
                     )

        # Add the files to upload
        parts.extend(
                     [part_boundary,
                     'Content-Disposition: file; name="%s"; filename="%s"' % \
                     (field_name, filename),
                     'Content-Type: %s' % content_type,
                     '',
                     body,
                     ]
                     for field_name, filename, content_type, body in self.files
                     )

        # Flatten the list and add closing boundary marker,
        # then return CR+LF separated data
        flattened = list(itertools.chain(*parts))
        flattened.append('--' + self.boundary + '--')
        flattened.append('')
        flattened = map(str, flattened)
        return '\r\n'.join(flattened)

def get_client_access_token(app_id, app_secret):
    #create an access token acting as if we are a
    #desktop client (we in fact are!). app_id and
    #app_secrets are the parameters that you get
    #get when you create a new app
    params = {
        'grant_type': 'client_credentials',
        'client_id': app_id,
        'client_secret': app_secret
    }

    response = cgi.parse_qs(urllib.urlopen(
                "https://graph.facebook.com/oauth/access_token?" +
                urllib.urlencode(params)).read())
    access_token = response["access_token"][-1]

    return access_token

def create_user(app_id, access_token):
    #create a test user using desktop-client
    #access token. We need 'publish_stream' and
    #'user_photos' extended permissions
    params = {
        'access_token': access_token,
        'installed': 'true',
        'permissions': 'publish_stream,user_photos'
    }

    params = urllib.urlencode(params)
    url = 'https://graph.facebook.com/%s/accounts/test-users'%(app_id)
    request = urllib2.Request(url, headers = {}, data = params)

    response = urllib2.urlopen(request)
    response = response.read()

    response = json.loads(response)
    return response

def upload_pic(uid, access_token, pic_caption, pic_file_name, pic_file):
    #using access_token for the newly created test user upload
    #an image file from disk (or any other file like source) to
    #the user's account.
    #pic_caption: the caption to the pic
    #pic_file_name: name of the file (used only to guess mimetype)
    #pic_file: a file like object representing the file (one can
    #open a url and use the response here if the file to be uploaded
    #is on the web (and not on the disk)
    form = MultiPartForm()
    form.add_field('access_token', access_token)
    form.add_field('caption', pic_caption)

    form.add_file('source', pic_file_name, pic_file)

    request = urllib2.Request('https://graph.facebook.com/%s/photos'%(uid))
    body = str(form)
    request.add_header('Content-type', form.get_content_type())
    request.add_header('Content-length', len(body))
    request.add_data(body)

    return urllib2.urlopen(request).read()

def do():
    if not len(sys.argv) == 5:
        print "usage: python %s <app_id> <app_secret> <caption> <file_path>"%(sys.argv[0])
        return

    app_id = sys.argv[1]
    app_secret = sys.argv[2]
    p_caption = sys.argv[3]
    file_path = sys.argv[4]

    client_access_token = get_client_access_token(app_id, app_secret)
    user_info = create_user(app_id, client_access_token)

    u_id = user_info['id']
    print 'created test user account with id: %s'%(u_id)

    u_access_token = user_info['access_token']
    user_login_url = user_info['login_url']

    p_file_name = os.path.basename(file_path)
    p_file = open(file_path)

    upload_pic(u_id, u_access_token, p_caption, p_file_name, p_file)
    print 'login as the test user and view the uploaded pic: %s'%(user_login_url)

if __name__ == '__main__':
    do()

Forcing Digest Authentication in Web Applications

January 10, 2010 2 comments

A simple way to password-protect a page in Apache web server is the use of htaccess. However, some times you don’t have enough privileges to place or modify a/the htaccess file, e.g., in Google App Engine. Here is a very simple way to achieve this on the application level (using GAE as example)

Read more…