forked from cellularmitosis/ADC-reference-library-2009-july
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathlisting13.html
More file actions
executable file
·713 lines (584 loc) · 24.9 KB
/
Copy pathlisting13.html
File metadata and controls
executable file
·713 lines (584 loc) · 24.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd">
<html>
<head>
<!-- BEGIN META TAG INFO -->
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<link rel="home" href="http://developer.apple.com/">
<link rel="find" href="http://developer.apple.com/search/">
<link rel="stylesheet" type="text/css" href="../../documentation/css/adcstyle.css" title="fonts">
<script language="JavaScript" src="../../documentation/js/adc.js" type="text/javascript"></script>
<!-- END META TAG INFO -->
<!-- BEGIN TITLE -->
<title>OTMP - /MoreOpenTransport/OTMPSimpleServerHTTP/OTMPSimpleServerHTTPTest.c</title>
<!-- END TITLE -->
<script language="JavaScript">
function JumpToNewPage() {
window.location=document.scpopupmenu.gotop.value;
return true;
}
</script>
</head>
<!-- BEGIN BODY OPEN -->
<body>
<!--END BODY OPEN -->
<!-- START CENTER OPEN -->
<center>
<!-- END CENTER OPEN -->
<!-- BEGIN LOGO AND SEARCH -->
<!--#include virtual="/includes/adcnavbar"-->
<!-- END LOGO AND SEARCH -->
<!-- START BREADCRUMB -->
<div id="breadcrumb">
<table width="680" border="0" cellpadding="0" cellspacing="0">
<tr>
<td scope="row"><img width="340" height="10" src="images/1dot.gif" alt=""></td>
<td><img width="340" height="10" src="images/1dot.gif" alt=""></td>
</tr>
<tr valign="middle">
<td align="left" colspan="2">
<a href="http://developer.apple.com/">ADC Home</a> > <a href="../../referencelibrary/index.html">Reference Library</a> > <a href="../../samplecode/index.html">Sample Code</a> > <a href="../../samplecode/Carbon/index.html">Carbon</a> > <a href="../../samplecode/Carbon/idxNetworking-date.html">Networking</a> > <A HREF="javascript:location.replace('index.html');">OTMP</A> >
</td>
</tr>
<tr>
<td colspan="2" scope="row"><img width="680" height="35" src="images/1dot.gif" alt=""></td>
</tr>
</table>
</div>
<!-- END BREADCRUMB -->
<div style="width:100%; position:fixed;"><div align="center" id="watermark" style="position: relative; margin-left:auto; margin-right:auto; z-index:20; width:500px;"><div class="legacybox"><h1>Not Recommended Document<span class=closebutton><a href="javascript:closeWatermark()"><img src="../../images/closebutton.png" width="14" height="14" border="0" alt="close button"></a></span></h1>
<p><strong>Important: </strong>The information in this document is <strong>Not Recommended</strong> and should not be used for new development.</p>
<div class="reflibtopic">
<p>Current information on this Reference Library topic can be found here:</p>
<ul>
<li><a href="http://developer.apple.com/referencelibrary/Carbon/idxNetworking-date.html" target="_blank">Carbon > Networking</a></li>
</ul>
</div>
</div></div></div>
<!-- START MAIN CONTENT -->
<!-- START TITLE GRAPHIC AND INTRO-->
<table width="680" border="0" cellpadding="0" cellspacing="0">
<tr align="left" valign="top">
<td><h1><div id="pagehead">OTMP</div></h1></td>
</tr>
</table>
<!-- END TITLE GRAPHIC AND INTRO -->
<!-- START WIDE COLUMN -->
<table width="680" border="0" cellpadding="0" cellspacing="0">
<tr align="left" valign="top">
<td id="scdetails">
<h2>/MoreOpenTransport/OTMPSimpleServerHTTP/OTMPSimpleServerHTTPTest.c</h2>
<form name="scpopupmenu" onSubmit="return false;" method=post>
<p><strong>View Source Code:</strong>
<select name="gotop" onChange="JumpToNewPage();" style="width:340px"><option selected value="ingnore">Select File</option>
<option value="listing1.html">/MIB_Carbon.h</option>
<option value="listing2.html">/MoreMemory/MoreMemory.c</option>
<option value="listing3.html">/MoreMemory/MoreMemory.h</option>
<option value="listing4.html">/MoreMultiprocessing/MoreBlueActions.c</option>
<option value="listing5.html">/MoreMultiprocessing/MoreBlueActions.h</option>
<option value="listing6.html">/MoreMultiprocessing/MoreMPLog.c</option>
<option value="listing7.html">/MoreMultiprocessing/MoreMPLog.h</option>
<option value="listing8.html">/MoreOpenTransport/OTClassicContext.h</option>
<option value="listing9.html">/MoreOpenTransport/OTMP.c</option>
<option value="listing10.html">/MoreOpenTransport/OTMP.h</option>
<option value="listing11.html">/MoreOpenTransport/OTMPSimpleServerHTTP/OTMPSimpleServerHTTP.c</option>
<option value="listing12.html">/MoreOpenTransport/OTMPSimpleServerHTTP/OTMPSimpleServerHTTP.h</option>
<option value="listing13.html">/MoreOpenTransport/OTMPSimpleServerHTTP/OTMPSimpleServerHTTPTest.c</option>
<option value="listing14.html">/MoreOpenTransport/OTMPSimpleServerHTTP/OTMPSimpleServerHTTPTest.r</option>
<option value="listing15.html">/MoreOpenTransport/OTMPSimpleServerHTTP/Sample HTTP Source/index.html</option>
<option value="listing16.html">/MoreOpenTransport/Read_Me_About_OTMP.html</option>
<option value="listing17.html">/MoreProcesses/MoreProcesses.cp</option>
<option value="listing18.html">/MoreProcesses/MoreProcesses.h</option>
<option value="listing19.html">/MoreSetup.h</option></select>
</p>
</form>
<p><strong><a href="OTMP.zip">Download Sample</a></strong> (“OTMP.zip”, 808.3K)<BR>
<strong><a href="OTMP.dmg">Download Sample</a></strong> (“OTMP.dmg”, 1.20M)</p>
<!--
<p><strong><a href="#">Download Sample</a></strong> (“filename.sit”, 500K)</p>
-->
</td>
</tr>
<tr>
<td scope="row"><img width="680" height="10" src="images/1dot.gif" alt=""><br>
<img height="1" width="680" src="images/1dot_919699.gif" alt=""><br>
<img width="680" height="20" src="images/1dot.gif" alt=""></td>
</tr>
<tr>
<td scope="row">
<!--googleon: index -->
<pre class="sourcecodebox">/* File: OTMPSimpleServerHTTPTest.c Contains: A test program for the simple HTTP server code (using MP tasks). Written by: Quinn "The Eskimo!" Copyright: Copyright © 1997-2000 by Apple Computer, Inc., All Rights Reserved. Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. ("Apple") in consideration of your agreement to the following terms, and your use, installation, modification or redistribution of this Apple software constitutes acceptance of these terms. If you do not agree with these terms, please do not use, install, modify or redistribute this Apple software. In consideration of your agreement to abide by the following terms, and subject to these terms, Apple grants you a personal, non-exclusive license, under Apple's copyrights in this original Apple software (the "Apple Software"), to use, reproduce, modify and redistribute the Apple Software, with or without modifications, in source and/or binary forms; provided that if you redistribute the Apple Software in its entirety and without modifications, you must retain this notice and the following text and disclaimers in all such redistributions of the Apple Software. Neither the name, trademarks, service marks or logos of Apple Computer, Inc. may be used to endorse or promote products derived from the Apple Software without specific prior written permission from Apple. Except as expressly stated in this notice, no other rights or licenses, express or implied, are granted by Apple herein, including but not limited to any patent rights that may be infringed by your derivative works or by other works in which the Apple Software may be incorporated. The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. Change History (most recent first): <9> 21/9/01 Quinn Fix Project Builder warning. <8> 21/9/01 Quinn Version 1.0a5 update. <7> 9/7/01 Quinn More MPLogPrintfSlow elimination. Also added a UI command for printing the number of active threads. <6> 5/7/01 Quinn Don't init BlueActions because OTMP now does it for us. <5> 15/2/01 Quinn Minor tweaks to get it building under Project Builder. <4> 8/2/01 Quinn [2619462] The fix for the MP bug is in CarbonLib 1.2.5. <3> 5/12/00 Quinn Looks like the bug fix won't make it into CarbonLib 1.2. <2> 10/11/00 Quinn Call OTMPGetCanRunStatus and refuse to run on unsupported systems. <1> 7/11/00 Quinn First checked in.*//////////////////////////////////////////////////////////////////// MIB Setup#include "MoreSetup.h"// Mac OS Interfaces#if ! MORE_FRAMEWORK_INCLUDES #include <OpenTransportProviders.h> #include <Gestalt.h> #include <Multiprocessing.h> #include <Folders.h>#endif// Standard C interfaces#include <stdio.h>// MIB prototypes#include "MoreProcesses.h"#include "OTMP.h"// Prototypes for the actual core HTTP server code.#include "MoreMPLog.h"#include "OTMPSimpleServerHTTP.h"/////////////////////////////////////////////////////////////////// The only way to tell whether OT supports IP single link// multihoming is to check the version number. The feature was// added in OT 1.3. The initialisation code sets // gHaveIPSingleLinkMultihoming depending on the version number// to avoid the rest of the code having to call Gestalt repeatedly.enum{ kOTIPSingleLinkMultihomingVersion = 0x01300000};static Boolean gHaveIPSingleLinkMultihoming;/////////////////////////////////////////////////////////////////// When this boolean is to set true, this module assumes that the// host application is trying to quit and all the threads created by// this module start dying. See the associated comment in the// YieldingNotifier routine.static Boolean gQuitNow = false;static Boolean gServerStarted = false;/////////////////////////////////////////////////////////////////static OSErr FSpGetCatInfo(FSSpecPtr fss, short ioFDirIndex, CInfoPBPtr cpb) // A simple wrapper for GetCatInfo.{ cpb->hFileInfo.ioVRefNum = fss->vRefNum; cpb->hFileInfo.ioDirID = fss->parID; cpb->hFileInfo.ioNamePtr = fss->name; cpb->hFileInfo.ioFDirIndex = ioFDirIndex; return PBGetCatInfoSync(cpb);}/////////////////////////////////////////////////////////////////// MP tasks are started with a single parameter. Because we need // more than one parameter for our HTTPServerProc task, we pack them // into a parameter block (ServerContext).struct ServerContext { InetHost ipAddr; FSVolumeRefNum rootVRefNum; UInt32 rootDirID;};typedef struct ServerContext ServerContext;static OSStatus HTTPServerProc(void *param) // This routine is the initial start routine for the server's // preemptive thread. It unpacks the parameters from the // context then calls our HTTP server module to execute the server.{ OSStatus err; ServerContext *context; context = (ServerContext *) param; err = RunHTTPServerMP(context->ipAddr, context->rootVRefNum, context->rootDirID); MPFree(context); return err;}static OSStatus RunOneServer(InetHost ipAddr) // This routine is the main line of the thread that runs // an HTTP server. ipAddr is the address on which the // server is listening. // // The routine uses a directory whose name is the // dotted decimal string representation of ipAddr as the // root directory of the HTTP server.{ OSStatus err; OSStatus junk; Str255 ipAddrString; FSSpec dirSpec; CInfoPBRec cpb; ServerContext *context; MPTaskID junkServerThread; // Allocate a context for the preemptive thread. err = noErr; context = (ServerContext *) MPAllocateAligned(sizeof(*context), kMPAllocateDefaultAligned, kNilOptions); if (context == nil) { err = memFullErr; } // Fill out the context. We do this here because it needs // to use services that aren't MP-safe. if (err == noErr) { context->ipAddr = ipAddr; // Get ipAddr as a dotted decimal Pascal string. OTInetHostToString(ipAddr, ((char *) ipAddrString) + 1); ipAddrString[0] = OTStrLength(((char *) ipAddrString) + 1); // Find the associated dirID, creating the directory // if necessary. junk = MoreProcGetCurrentProcessFSSpec(&dirSpec); MoreAssertQ(junk == noErr); (void) FSMakeFSSpec(dirSpec.vRefNum, dirSpec.parID, ipAddrString, &dirSpec); context->rootVRefNum = dirSpec.vRefNum; err = FSpGetCatInfo(&dirSpec, 0, &cpb); if (err == noErr && ( (cpb.hFileInfo.ioFlAttrib & (1 << 4)) != 0) ) { context->rootDirID = cpb.hFileInfo.ioDirID; } else { err = FSpDirCreate(&dirSpec, 0, (SInt32 *) &context->rootDirID); } } // Start a preemptive thread to run an HTTP server on the IP address. if (err == noErr) { err = MPCreateTask(HTTPServerProc, context, 65536, kInvalidID, nil, nil, kNilOptions, &junkServerThread); if (err == noErr) { context = nil; // it's now the task's responsibility } } if (context != nil) { MPFree(context); } return err;}/////////////////////////////////////////////////////////////////static OSStatus RunServersForInterface(InetInterfaceInfo* interfaceInfo, SInt32 interfaceIndex) // Run HTTP servers for all of the IP addresses associated // with the interface denoted by interfaceInfo and interfaceIndex. // This routine first starts a server for the primary address // of the interface, then iterates through the secondary addresses on // the interface, starting a server thread for each one.{ OSStatus err; InetHost *secondaryAddressBuffer; UInt32 numberOfSecondaryAddresses; UInt32 addressIndex; secondaryAddressBuffer = nil; // First run the server for the interfaces primary address. err = RunOneServer(interfaceInfo->fAddress); // Now start a server for each of the interface's secondary // addresses. This stuff can only be done on systems that // support IP single link multihoming. numberOfSecondaryAddresses = interfaceInfo->fIPSecondaryCount; if ( err == noErr && gHaveIPSingleLinkMultihoming && numberOfSecondaryAddresses > 0 ) { // Allocate a buffer for the secondary address info. secondaryAddressBuffer = (InetHost *) OTAllocMemInContext( numberOfSecondaryAddresses * sizeof(InetHost) , nil); if (secondaryAddressBuffer == nil) { err = kENOMEMErr; } // Ask OT for the list of secondary addresses on this interface. if (err == noErr) { err = OTInetGetSecondaryAddresses(secondaryAddressBuffer, &numberOfSecondaryAddresses, interfaceIndex); } // Start a server for each secondary address. addressIndex = 0; while (err == noErr && addressIndex < numberOfSecondaryAddresses) { err = RunOneServer(secondaryAddressBuffer[addressIndex]); if (err == noErr) { addressIndex += 1; } } } // Clean up. if (secondaryAddressBuffer != nil) { OTFreeMem(secondaryAddressBuffer); } return err;}/////////////////////////////////////////////////////////////////static OSStatus RunAllHTTPServers(void) // Run HTTP servers for all of the IP addresses on the machine. // This routine iterates through the active Internet interfaces, // starting server threads for each active IP address on each // interface.{ OSStatus err; OSStatus junk; EndpointRef dummyEP; InetInterfaceInfo info; SInt32 interfaceIndex; Boolean done; TEndpointInfo epInfo; // Force TCP to load by creating a dummy endpoint. Otherwise, // if we're the first TCP application to run, OTInetGetInterfaceInfo // will not return any active interfaces )-: // Note that we do this with OTOpenEndpoint rather than OTMPXOpenEndpoint // because we know we're running at system task time so we might as // well give OT time to dial the modem etc. dummyEP = OTOpenEndpointInContext(OTCreateConfiguration("tcp"), 0, &epInfo, &err, nil); if (err == noErr) { // Iterate through the interfaces, starting HTTP servers on each. done = false; interfaceIndex = 0; do { done = ( OTInetGetInterfaceInfo(&info, interfaceIndex) != noErr ); if ( ! done ) { err = RunServersForInterface(&info, interfaceIndex); interfaceIndex += 1; } } while (err == noErr && !done); } if (dummyEP != nil) { junk = OTCloseProvider(dummyEP); MoreAssertQ(junk == noErr); } return err;}/////////////////////////////////////////////////////////////////#if MORE_DEBUG static void DoWriteMPLog(void) // This routine writes out the MP log contents to // a file "MPLog.txt" on the desktop. { OSStatus err; FSSpec fss; err = FindFolder(kOnSystemDisk, kDesktopFolderType, true, &fss.vRefNum, &fss.parID); if (err == noErr) { (void) FSMakeFSSpec(fss.vRefNum, fss.parID, "\pMPLog.txt", &fss); err = MPLogWriteToFile(&fss); } if (err == noErr) { printf("Success!\n"); } else { printf("Failed with error %ld.\n", err); } }#endifstatic void ServerCommandLoop(void) // After initialising the various server modules, the main // routine calls this routine to execute the server's user // interface command loop. This loop asks the user to // enter a command and then executes that command. // // This loop is radicially different from the loop used by // the cooperative scheduled predecessor of this sample. // Because we use preemptive threads, we can block inside // the standard C library gets routine. The preemptive threads // will continue to handle network operations even while we're // blocked.{ OSStatus err; char commandStr[256]; // In the debug version, set the default debug log mask to // log all OTMP API calls. #if MORE_DEBUG MPLogSetMask(1 << kOTMPAPILogID); #endif // The server command loop. Print the list of commands, get // a command, execute it. do { printf("s) Start HTTP servers\n"); printf("S) Stop HTTP servers\n"); #if MORE_DEBUG printf("l) Set MPLog mask\n"); printf("w) Write MPLog to disk\n"); printf("t) Print running thread count\n"); #endif printf("q) Quit\n"); printf("Enter a command:\n"); fflush(stdout); if ( fgets(commandStr, sizeof(commandStr), stdin) == nil ) { commandStr[0] = 0; } switch (commandStr[0]) { case 's': if (gServerStarted) { printf("Server is already running (supposedly).\n"); } else { err = RunAllHTTPServers(); if (err == noErr) { gServerStarted = true; printf("Success!\n"); } else { printf("Failed with error %ld\n", err); } } break; case 'S': if (gServerStarted) { TermOTMPSimpleServerHTTP(); gServerStarted = false; err = InitOTMPSimpleServerHTTP(); if (err == noErr) { printf("Success!\n"); } else { printf("Failed to reinitialise server (%ld)\n", err); } } else { printf("Server is not running.\n"); } break; #if MORE_DEBUG case 'l': printf("Enter new log mask (in hex):\n"); gets(commandStr); if (commandStr[0] != 0) { UInt32 newMask; sscanf(commandStr, "%lx", &newMask); MPLogSetMask(newMask); printf("MPLogSetMask(0x%08lx)\n", newMask); } break; case 'w': DoWriteMPLog(); break; case 't': { extern volatile SInt32 gRunningThreads; printf("gRunningThreads = %ld\n", gRunningThreads); } break; #endif case 'q': gQuitNow = true; break; default: printf("Huh?\n"); break; } } while ( ! gQuitNow ); }int main(void) // The main line of the application. This routine performs // two functions. At startup, it initialises the various modules. // It then calls ServerCommandLoop to enter the server's user interface.{ OSStatus err; OTClientContextPtr junkContext; NumVersionVariant otVersion; printf("OTMPSimpleServerHTTP!\n"); // Use printf because MP is not yet initialised. printf("-- World's dumbest HTTP server.\n"); printf("-- Now using preemptive threads.\n"); printf("\n"); err = noErr; if ( ! MPLibraryIsLoaded() ) { err = -1; } #if MORE_DEBUG if (err == noErr) { OSStatus junk; junk = InitMPLog(65536); } #endif if (err == noErr) { UInt32 response; err = Gestalt(gestaltSystemVersion, (SInt32 *) &response); if ( (err == noErr) && (response == 0x0910) ) { printf("***\xCAThis code may be unreliable on Mac OS 9.1.\n"); printf("***\xCASee the documentation for details.\n"); } } if (err == noErr) { switch ( OTMPGetCanRunStatus() ) { case kOTMPCanRun: // do nothing break; case kOTMPSystemTooOld: printf("OTMP is not supported on this system\n"); err = unimpErr; break; case kOTMPBadCarbon: printf("OTMP can not be used when CarbonLib < 1.2.5 is installed.\n"); err = unimpErr; break; default: MoreAssertQ(false); break; } } if (err == noErr) { gHaveIPSingleLinkMultihoming = ( Gestalt(gestaltOpenTptVersions, (long *) &otVersion) == noErr && (otVersion.whole >= kOTIPSingleLinkMultihomingVersion ) ); err = InitOpenTransportMPXInContext(kInitOTForApplicationMask, &junkContext); if (err == noErr) { err = InitOTMPSimpleServerHTTP(); if (err == noErr) { ServerCommandLoop(); if (gServerStarted) { TermOTMPSimpleServerHTTP(); } } CloseOpenTransportMPXInContext(nil); } } if (err == noErr) { printf("Success.\n"); // Use printf because we might not have successfully initialised MP. } else { printf("Failed with error %ld.\n", err); } printf("Done. Press command-Q to Quit.\n"); return 0;}</pre>
<!--googleoff: index -->
</td>
</tr>
</table>
<!-- END WIDE COLUMN -->
<!-- END MAIN CONTENT -->
<table width="680" border="0" cellpadding="0" cellspacing="0">
<tr>
<td><div style="width: 100%; height: 1px; background-color: #919699; margin-top: 5px; margin-bottom: 15px"></div></td>
</tr>
<tr>
<td align="center"><br/>
<table border="0" cellpadding="0" cellspacing="0" class="graybox">
<tr>
<th>Did this document help you?</th>
</tr>
<tr>
<td>
<div style="margin-bottom: 8px"><a href="http://developer.apple.com/feedback/?v=1&url=/samplecode/OTMP/listing13.html%3Fid%3DDTS10000251-1.0&media=dvd" target=_new>Yes</a>: Tell us what works for you.</div>
<div style="margin-bottom: 8px"><a href="http://developer.apple.com/feedback/?v=2&url=/samplecode/OTMP/listing13.html%3Fid%3DDTS10000251-1.0&media=dvd" target=_new>It’s good, but:</a> Report typos, inaccuracies, and so forth.</div>
<div><a href="http://developer.apple.com/feedback/?v=3&url=/samplecode/OTMP/listing13.html%3Fid%3DDTS10000251-1.0&media=dvd" target=_new>It wasn’t helpful</a>: Tell us what would have helped.</div>
</td>
</tr>
</table>
</td>
</tr>
</table>
<!-- START BOTTOM APPLE NAVIGATION -->
<!--#include virtual="/includes/footer"-->
<!-- END BOTTOM APPLE NAVIGATION -->
<!-- START CENTER CLOSE -->
</center>
<!-- END CENTER CLOSE -->
</body>
</html>