text: #!/usr/bin/env python
  #
  # Copyright 2007 Google Inc.
  #
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  #
  #     http://www.apache.org/licenses/LICENSE-2.0
  #
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  #
  
  """Helper CGI for OAuth in the development app server."""
  
  
  
  import cgi
  
  
  _GET_REQUEST_TOKEN_URL = '/_ah/OAuthGetRequestToken'
  _AUTHORIZE_TOKEN_URL = '/_ah/OAuthAuthorizeToken'
  _GET_ACCESS_TOKEN_URL = '/_ah/OAuthGetAccessToken'
  
  _OAUTH_CALLBACK_PARAM = 'oauth_callback'
  
  OAUTH_URL_PATTERN = (_GET_REQUEST_TOKEN_URL
                       + '|' + _AUTHORIZE_TOKEN_URL
                       + '|' + _GET_ACCESS_TOKEN_URL)
  
  
  TOKEN_APPROVAL_TEMPLATE = """<html>
  <head>
    <title>OAuth Access Request</title>
  </head>
  <body>
  
  <form method="POST">
    <div style="width: 20em; margin: 1em auto;
                text-align: left;
                padding: 0 2em 1.25em 2em;
                background-color: #d6e9f8;
                font: 13px sans-serif;
                border: 2px solid #67a7e3">
      <h3>OAuth Access Request</h3>
      <input type="hidden" name="oauth_callback" value="%(oauth_callback)s"/>
      <p style="margin-left: 3em;">
        <input name="action" type="submit" value="Grant Access"/>
      </p>
    </div>
  </form>
  
  </body>
  </html>
  """
  
  TOKEN_APPROVED_TEMPLATE = """<html>
  <head>
    <title>OAuth Access Granted</title>
  </head>
  <body>
  
  <div style="width: 20em; margin: 1em auto;
              text-align: left;
              padding: 0 2em 1.25em 2em;
              background-color: #d6e9f8;
              font: 13px sans-serif;
              border: 2px solid #67a7e3">
    <h3>OAuth Access Granted</h3>
  </div>
  
  </body>
  </html>
  """
  
  
  def RenderTokenApprovalTemplate(oauth_callback):
    """Renders the token approval page.
  
    Args:
      oauth_callback: Parameter passed to OAuthAuthorizeTokenCGI.
  
    Returns:
      String containing the contents of the token approval page.
    """
    template_dict = {
        'oauth_callback': cgi.escape(oauth_callback, quote=True),
    }
  
    return TOKEN_APPROVAL_TEMPLATE % template_dict
  
  
  def RenderTokenApprovedTemplate():
    """Renders the token approved page.
  
    Returns:
      String containing the contents of the token approved page.
    """
    return TOKEN_APPROVED_TEMPLATE
  
  
  def OAuthGetRequestTokenCGI(outfile):
    """Runs the OAuthGetRequestToken CGI.
  
    Args:
      outfile: File-like object to which all output data should be written.
    """
    outfile.write('Status: 200\r\n')
    outfile.write('Content-Type: text/plain\r\n')
    outfile.write('\r\n')
    outfile.write('oauth_token=REQUEST_TOKEN')
    outfile.write('&')
    outfile.write('oauth_token_secret=REQUEST_TOKEN_SECRET')
  
  
  def OAuthAuthorizeTokenCGI(method, parameters, outfile):
    """Runs the OAuthAuthorizeToken CGI.
  
    Args:
      method: HTTP method
      parameters: Dictionary of parameters from the request.
      outfile: File-like object to which all output data should be written.
    """
    oauth_callback = GetFirst(parameters, _OAUTH_CALLBACK_PARAM, '')
    if method == 'GET':
      outfile.write('Status: 200\r\n')
      outfile.write('Content-Type: text/html\r\n')
      outfile.write('\r\n')
      outfile.write(RenderTokenApprovalTemplate(oauth_callback))
    elif method == 'POST':
      if oauth_callback:
        outfile.write('Status: 302 Redirecting to callback URL\r\n')
        outfile.write('Location: %s\r\n' % oauth_callback)
        outfile.write('\r\n')
      else:
        outfile.write('Status: 200\r\n')
        outfile.write('Content-Type: text/html\r\n')
        outfile.write('\r\n')
        outfile.write(RenderTokenApprovedTemplate())
    else:
      outfile.write('Status: 400 Unsupported method\r\n')
  
  
  def OAuthGetAccessTokenCGI(outfile):
    """Runs the OAuthGetAccessToken CGI.
  
    Args:
      outfile: File-like object to which all output data should be written.
    """
    outfile.write('Status: 200\r\n')
    outfile.write('Content-Type: text/plain\r\n')
    outfile.write('\r\n')
    outfile.write('oauth_token=ACCESS_TOKEN')
    outfile.write('&')
    outfile.write('oauth_token_secret=ACCESS_TOKEN_SECRET')
  
  
  def GetFirst(parameters, key, default=None):
    """Returns the first value of the given key.
  
    Args:
      parameters: A dictionary of lists, {key: [value1, value2]}
      key: name of parameter to retrieve
      default: value to return if the key isn't found
  
    Returns:
      The first value in the list, or default.
    """
    if key in parameters:
      if parameters[key]:
        return parameters[key][0]
    return default
  
  
  def MainCGI(method, path, unused_headers, parameters, outfile):
    """CGI for all OAuth handlers.
  
    Args:
      method: HTTP method
      path: Path of the request
      unused_headers: Instance of mimetools.Message with headers from the request.
      parameters: Dictionary of parameters from the request.
      outfile: File-like object to which all output data should be written.
    """
    if method != 'GET' and method != 'POST':
      outfile.write('Status: 400\r\n')
      return
  
    if path == _GET_REQUEST_TOKEN_URL:
      OAuthGetRequestTokenCGI(outfile)
    elif path == _AUTHORIZE_TOKEN_URL:
      OAuthAuthorizeTokenCGI(method, parameters, outfile)
    elif path == _GET_ACCESS_TOKEN_URL:
      OAuthGetAccessTokenCGI(outfile)
    else:
      outfile.write('Status: 404 Unknown OAuth handler\r\n')
  
  
  def CreateOAuthDispatcher():
    """Function to create OAuth dispatcher.
  
    Returns:
      New dispatcher capable of handling requests to the built-in OAuth handlers.
    """
  
    from google.appengine.tools import dev_appserver
  
    class OAuthDispatcher(dev_appserver.URLDispatcher):
      """Dispatcher that handles requests to the built-in OAuth handlers."""
  
      def Dispatch(self,
                   request,
                   outfile,
                   base_env_dict=None):
        """Handles dispatch to OAuth handlers.
  
        Args:
          request: AppServerRequest.
          outfile: The response file.
          base_env_dict: Dictionary of CGI environment parameters if available.
            Defaults to None.
        """
        if not base_env_dict:
          outfile.write('Status: 500\r\n')
          return
        method, path, headers, parameters = self._Parse(request, base_env_dict)
        MainCGI(method, path, headers, parameters, outfile)
  
      def _Parse(self, request, base_env_dict):
        """Parses a request into convenient pieces.
  
        Args:
          request: AppServerRequest.
          base_env_dict: Dictionary of CGI environment parameters.
  
        Returns:
          A tuple (method, path, headers, parameters) of the HTTP method, the
          path (minus query string), an instance of mimetools.Message with
          headers from the request, and a dictionary of parameter lists from the
          body or query string (in the form of {key :[value1, value2]}).
        """
        method = base_env_dict['REQUEST_METHOD']
        path, query = dev_appserver.SplitURL(request.relative_url)
        parameters = {}
        if method == 'POST':
          form = cgi.FieldStorage(fp=request.infile,
                                  headers=request.headers,
                                  environ=base_env_dict)
          for key in form:
            if key not in parameters:
              parameters[key] = []
            for value in form.getlist(key):
              parameters[key].append(value)
        elif method == 'GET':
          parameters = cgi.parse_qs(query)
        return method, path, request.headers, parameters
  
    return OAuthDispatcher()
  