#!/usr/bin/env python # encoding: utf-8 ######################################################################################################################## # ___ ___ ___ ___ # /\ \ /\ \ /\ \ /\ \ # /::\ \ /::\ \ /::\ \ /::\ \ # /:/\:\ \ /:/\:\ \ /:/\:\ \ /:/\:\ \ # /:/ \:\__\ /::\~\:\ \ /::\~\:\ \ /::\~\:\ \ # /:/__/ \:|__| /:/\:\ \:\__\ /:/\:\ \:\__\ /:/\:\ \:\__\ # \:\ \ /:/ / \/__\:\/:/ / \/_|::\/:/ / \:\~\:\ \/__/ # \:\ /:/ / \::/ / |:|::/ / \:\ \:\__\ # \:\/:/ / /:/ / |:|\/__/ \:\ \/__/ # \::/__/ /:/ / |:| | \:\__\ # ~~ \/__/ \|__| \/__/ # # DARE - The Adobe AIR Development Web Server # # GOALS: # - Make Adobe AIR development, packaging, and testing as easy as pressing "REFRESH" in the browser (read identical)! # - Encourage the community to make development tools to support and enhance Adobe AIR # # BSD LICENSE: # Copyright (c) 2008, ONFLEX.ORG # All rights reserved. # Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following # conditions are met: # * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. # * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following # disclaimer in the documentation and/or other materials provided with the distribution. # * Neither the name of the nor the names of its contributors may be used to endorse or promote products derived # from this software without specific prior written permission. # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, # BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT # SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE # OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # # CREATOR: # Ted Patrick[ ted@adobe.com ] # ######################################################################################################################## # import from the Python Standard Library features used are supported in Python 2.3+ import asyncore import asynchat import os import socket import string import shutil import os.path import webbrowser import sys import thread def main(): ######################################################################################################################## # EDITABLE SETTINGS # PORT FOR LOCAL SERVER SERVER_PORT = 8888 # ADT ARGUMENTS FOR SIGNING AIR FILES SERVER_KEYSTORE_TYPE = "pkcs12" SERVER_KEYSTORE_FILENAME = "selfsign.pfx" SERVER_KEYSTORE_PASSWORD = "password" ######################################################################################################################## # EDIT PAST AT YOUR OWN PERIL!!!! ARGGGGGGGG! ######################################################################################################################## #define paths for processing paths={} #is the app running packaged? if os.path.basename(sys.path[0]) == 'Resources': paths['root'] = os.path.dirname( os.path.dirname( os.path.dirname( sys.path[0] ) ) ) else: paths['root'] = sys.path[0] paths['projects'] = paths['root'] + os.sep + 'projects' paths['template'] = paths['root'] + os.sep + 'template' paths['cert'] = paths['root'] + os.sep + SERVER_KEYSTORE_FILENAME paths['air_sdk'] = paths['root'] + os.sep + 'air_sdk' paths['keystore'] = "-storetype " + SERVER_KEYSTORE_TYPE + " -keystore " + paths['cert'] + " -storepass " + SERVER_KEYSTORE_PASSWORD # handle platform pathing issues in SDK with vars paths['adl'] = paths['air_sdk'] + os.sep + 'bin' + os.sep + 'adl' paths['adt'] = paths['air_sdk'] + os.sep + 'bin' + os.sep + 'adt' # Channel class to handle http socket connections class DAREChannel( asynchat.async_chat ): #construction def __init__( self , server , sock , addr ): asynchat.async_chat.__init__( self , sock ) self.set_terminator( "\r\n" ) self.request = None self.data = "" self.shutdown = 0 #data recieved def collect_incoming_data(self, data): self.data = self.data + data #once terminator located "\r\n\r\n" def found_terminator( self ): if not self.request: self.request = string.split( self.data , None , 2 ) if len( self.request ) != 3: self.shutdown = 1 else: self.push( "HTTP/1.0 200 OK\r\n" ) self.push( "Content-type: text/html\r\n" ) self.push( "Pragma: no-cache\r\n" ) self.push( "Cache-Control: no-cache, must-revalidate\r\n" ) self.push( "\r\n" ) self.data = self.data + "\r\n" self.set_terminator( "\r\n\r\n" ) else: # determine path of url requested requestPath = string.split( self.data , ' ' )[1][1:] # split the path into a list using backslash arguments = string.split( requestPath , '/' ) commandLineCalls = [] if len( arguments ) > 1: paths['app'] = paths['projects'] + os.sep + arguments[0] paths['config'] = paths['app'] + os.sep + 'application.xml' paths['bin'] = paths['app'] + os.sep + 'bin' paths['air'] = paths['bin'] + os.sep + arguments[0] + '.air' #add air_sdk path is not present if not os.path.exists( paths['air_sdk'] ): os.mkdir( paths['air_sdk'] ) #make sure AIR_SDK is installed if not os.path.exists( paths['adl'] ): return self.buildIndex( '

WARNING: The Adobe AIR SDK is not installed.

DARE requires a copy of the Adobe AIR SDK be located here: "' + paths['air_sdk'] + '"

1. Please download the Adobe AIR SDK (MAC)

2. Copy the SDK to this path: ' + paths['air_sdk'] + '

' ) # make sure there is a self-signed certificate, if not make one. if not os.path.exists( paths['root'] + os.sep + 'selfsign.pfx' ): command = paths['adt'] + ' -certificate -cn SelfSign -ou Dev -o "AIRPACHE" -c US 2048-RSA ' + paths['cert'] + ' ' + SERVER_KEYSTORE_PASSWORD self.buildIndex() #use system call to ADT to create self-signed cert try: os.system( command ) commandLineCalls.append( command ) except: print "Unexpected error:", sys.exc_info()[0] return # if there are not 2 arguments in the path, return the index if len( arguments ) != 2: return self.buildIndex() #define project path variables if not os.path.isdir( paths['projects'] ): os.mkdir( paths['projects'] ) return self.buildIndex( paths ) if arguments[1] == 'NEW': if( not os.path.exists( paths['app'] ) ): shutil.copytree( paths['template'] , paths['app'] ) command = paths['adl'] + ' ' + paths['config'] commandLineCalls.append( command ) self.buildIndex() # call ADL to run application try: os.system( command ) except: print "Unexpected error:", sys.exc_info()[0] return if ( not os.path.isdir( paths['app'] ) ): return self.buildIndex( "no project directory" ) if ( not os.path.isfile( paths['config'] ) ): return self.buildIndex( "no config file" ) if ( arguments[1] == 'RUN' ): command = paths['adl'] + ' ' + paths['config'] commandLineCalls.append( command ) self.buildIndex() # call ADL to run application try: os.system( command ) except: print "Unexpected error:", sys.exc_info()[0] return if ( arguments[1] == 'PACKAGE' ): #delete bin directory if exists if( os.path.exists( paths['bin'] ) ): #remove directories recursively shutil.rmtree( paths['bin'] ) #make bin directory os.mkdir( paths['bin'] ) #get a list of files within the project directory #these get put into ADT and bundled into AIR file dirlist = os.listdir( paths['app'] ) filePaths = "" #exclude some directory names for f in dirlist: #exlude from packaging if( f=='application.xml' ): continue if( f== 'src' ): continue if( f== 'bin' ): continue filePaths += ' ' + f #build the ADT command command = paths['adt'] + ' -package ' + paths['keystore'] + ' ' + paths['air'] + ' ' + paths['config'] + ' -C ' + paths['app'] + filePaths # run the command try: os.system( command ) commandLineCalls.append( command ) except: print "Unexpected error:", sys.exc_info()[0] # if the AIR file exists, run it if( os.path.exists( paths['air'] )): command = 'open "' + paths['air'] + '"' try: os.system( command ) commandLineCalls.append( command ) except: print "Unexpected error:", sys.exc_info()[0] #regardless return the index html return self.buildIndex() # create the index html page def buildIndex( self , error=''): dirlist = os.listdir( paths['projects'] ) self.push( "

DARE - The Adobe® AIR™ Development Web Server

" ) if( error != '' ): self.push( error + "

" ) if False: self.push( "

Paths:

" ) if paths.has_key('root'): self.push( 'root :: ' + paths['root'] + '
' ) if paths.has_key('projects'): self.push( 'proj :: ' + paths['projects'] + '
' ) if paths.has_key('template'): self.push( 'temp :: ' + paths['template'] + '
' ) if paths.has_key('cert'): self.push( 'cert :: ' + paths['cert'] + '
' ) if paths.has_key('air_sdk'): self.push( '-sdk :: ' + paths['air_sdk'] + '
' ) if paths.has_key('adl'): self.push( '-adl :: ' + paths['adl'] + '
' ) if paths.has_key('adt'): self.push( '-adt :: ' + paths['adt'] + '
' ) if paths.has_key('app'): self.push( '-app :: ' + paths['app'] + '
' ) if paths.has_key('config'): self.push( 'conf :: ' + paths['config'] + '
' ) if paths.has_key('bin'): self.push( '-bin :: ' + paths['bin'] + '
' ) if paths.has_key('air'): self.push( '-air :: ' + paths['air'] + '
' ) self.push( "
" ) self.push( "

Projects:

" ) for f in dirlist: if os.path.isdir( paths['projects'] + os.sep + f ): self.push( '' + f + ' :: RUN' + ' :: PACKAGE

') self.push( """New Project :: """ ) self.push( "

Links:

" ) self.push( "DARE Site

" ) self.push( "Adobe® AIR™ Product Page

" ) self.push( "Adobe® AIR™ Developer Center

" ) self.push( "Adobe® AIR™ SDK :: (MAC) :: (WINDOWS) :: (LINUX)" ) self.push( "
") self.push( "

Credits:

" ) self.push( "Icons by: FastIcon.com

Killer Dinosaur Icons by Dirceu Veiga

\r\n" ) self.push( "DARE created by: Ted Patrick
\r\n" ) self.close_when_done() class DAREServer( asyncore.dispatcher ): def __init__( self , port ): asyncore.dispatcher.__init__( self ) self.create_socket( socket.AF_INET , socket.SOCK_STREAM ) self.bind( ( "" , port ) ) self.listen( 5 ) webbrowser.open( "http://127.0.0.1:" + str( SERVER_PORT ) ) def handle_accept( self ): conn, addr = self.accept() DAREChannel( self , conn , addr ) def runServer(): s = DAREServer( SERVER_PORT ) print "DARE is running on port" , SERVER_PORT asyncore.loop() runServer() if __name__ == '__main__': main()