modules.html.en revision 7d9f2bf92f3b96b3d651365013e5da845849571b
7d9f2bf92f3b96b3d651365013e5da845849571brbowen<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
7d9f2bf92f3b96b3d651365013e5da845849571brbowen "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
7d9f2bf92f3b96b3d651365013e5da845849571brbowen
7d9f2bf92f3b96b3d651365013e5da845849571brbowen<html xmlns="http://www.w3.org/1999/xhtml">
7d9f2bf92f3b96b3d651365013e5da845849571brbowen <head>
7d9f2bf92f3b96b3d651365013e5da845849571brbowen <meta name="generator" content="HTML Tidy, see www.w3.org" />
7d9f2bf92f3b96b3d651365013e5da845849571brbowen
7d9f2bf92f3b96b3d651365013e5da845849571brbowen <title>Converting Modules from Apache 1.3 to Apache 2.0</title>
7d9f2bf92f3b96b3d651365013e5da845849571brbowen </head>
7d9f2bf92f3b96b3d651365013e5da845849571brbowen <!-- Background white, links blue (unvisited), navy (visited), red (active) -->
7d9f2bf92f3b96b3d651365013e5da845849571brbowen
7d9f2bf92f3b96b3d651365013e5da845849571brbowen <body bgcolor="#FFFFFF" text="#000000" link="#0000FF"
7d9f2bf92f3b96b3d651365013e5da845849571brbowen vlink="#000080" alink="#FF0000">
7d9f2bf92f3b96b3d651365013e5da845849571brbowen <!--#include virtual="header.html" -->
7d9f2bf92f3b96b3d651365013e5da845849571brbowen
7d9f2bf92f3b96b3d651365013e5da845849571brbowen <h1 align="CENTER">From Apache 1.3 to Apache 2.0<br />
7d9f2bf92f3b96b3d651365013e5da845849571brbowen Modules</h1>
7d9f2bf92f3b96b3d651365013e5da845849571brbowen
7d9f2bf92f3b96b3d651365013e5da845849571brbowen <p>This is a first attempt at writing the lessons I learned
7d9f2bf92f3b96b3d651365013e5da845849571brbowen when trying to convert the mod_mmap_static module to Apache
7d9f2bf92f3b96b3d651365013e5da845849571brbowen 2.0. It's by no means definitive and probably won't even be
7d9f2bf92f3b96b3d651365013e5da845849571brbowen correct in some ways, but it's a start.</p>
7d9f2bf92f3b96b3d651365013e5da845849571brbowen <hr />
7d9f2bf92f3b96b3d651365013e5da845849571brbowen
7d9f2bf92f3b96b3d651365013e5da845849571brbowen <h2>The easier changes...</h2>
7d9f2bf92f3b96b3d651365013e5da845849571brbowen
7d9f2bf92f3b96b3d651365013e5da845849571brbowen <h3>Cleanup Routines</h3>
7d9f2bf92f3b96b3d651365013e5da845849571brbowen
7d9f2bf92f3b96b3d651365013e5da845849571brbowen <p>These now need to be of type apr_status_t and return a value
7d9f2bf92f3b96b3d651365013e5da845849571brbowen of that type. Normally the return value will be APR_SUCCESS
7d9f2bf92f3b96b3d651365013e5da845849571brbowen unless there is some need to signal an error in the cleanup. Be
7d9f2bf92f3b96b3d651365013e5da845849571brbowen aware that even though you signal an error not all code yet
7d9f2bf92f3b96b3d651365013e5da845849571brbowen checks and acts upon the error.</p>
7d9f2bf92f3b96b3d651365013e5da845849571brbowen
7d9f2bf92f3b96b3d651365013e5da845849571brbowen <h3>Initialisation Routines</h3>
7d9f2bf92f3b96b3d651365013e5da845849571brbowen
7d9f2bf92f3b96b3d651365013e5da845849571brbowen <p>These should now be renamed to better signify where they sit
7d9f2bf92f3b96b3d651365013e5da845849571brbowen in the overall process. So the name gets a small change from
7d9f2bf92f3b96b3d651365013e5da845849571brbowen mmap_init to mmap_post_config. The arguments passed have
7d9f2bf92f3b96b3d651365013e5da845849571brbowen undergone a radical change and now look like</p>
7d9f2bf92f3b96b3d651365013e5da845849571brbowen
7d9f2bf92f3b96b3d651365013e5da845849571brbowen <ul style="list-style:none">
7d9f2bf92f3b96b3d651365013e5da845849571brbowen <li>apr_pool_t *p,</li>
7d9f2bf92f3b96b3d651365013e5da845849571brbowen
7d9f2bf92f3b96b3d651365013e5da845849571brbowen <li>apr_pool_t *plog,</li>
7d9f2bf92f3b96b3d651365013e5da845849571brbowen
7d9f2bf92f3b96b3d651365013e5da845849571brbowen <li>apr_pool_t *ptemp,</li>
7d9f2bf92f3b96b3d651365013e5da845849571brbowen
7d9f2bf92f3b96b3d651365013e5da845849571brbowen <li>server_rec *s</li>
7d9f2bf92f3b96b3d651365013e5da845849571brbowen </ul>
7d9f2bf92f3b96b3d651365013e5da845849571brbowen
7d9f2bf92f3b96b3d651365013e5da845849571brbowen <h3>Data Types</h3>
7d9f2bf92f3b96b3d651365013e5da845849571brbowen
7d9f2bf92f3b96b3d651365013e5da845849571brbowen <p>A lot of the data types have been moved into the APR. This
7d9f2bf92f3b96b3d651365013e5da845849571brbowen means that some have had a name change, such as the one shown
7d9f2bf92f3b96b3d651365013e5da845849571brbowen above. The following is a brief list of some of the changes
7d9f2bf92f3b96b3d651365013e5da845849571brbowen that you are likely to have to make.</p>
7d9f2bf92f3b96b3d651365013e5da845849571brbowen
7d9f2bf92f3b96b3d651365013e5da845849571brbowen <ul style="list-style:none">
7d9f2bf92f3b96b3d651365013e5da845849571brbowen <li>pool becomes apr_pool_t</li>
7d9f2bf92f3b96b3d651365013e5da845849571brbowen
7d9f2bf92f3b96b3d651365013e5da845849571brbowen <li>table becomes apr_table_t</li>
7d9f2bf92f3b96b3d651365013e5da845849571brbowen </ul>
7d9f2bf92f3b96b3d651365013e5da845849571brbowen <hr />
7d9f2bf92f3b96b3d651365013e5da845849571brbowen
7d9f2bf92f3b96b3d651365013e5da845849571brbowen <h2>The <em>messier</em> changes...</h2>
7d9f2bf92f3b96b3d651365013e5da845849571brbowen
7d9f2bf92f3b96b3d651365013e5da845849571brbowen <h3>Register Hooks</h3>
7d9f2bf92f3b96b3d651365013e5da845849571brbowen
7d9f2bf92f3b96b3d651365013e5da845849571brbowen <p>The new architecture uses a series of hooks to provide for
7d9f2bf92f3b96b3d651365013e5da845849571brbowen calling your functions. These you'll need to add to your module
7d9f2bf92f3b96b3d651365013e5da845849571brbowen by way of a new function, static void register_hooks(void). The
7d9f2bf92f3b96b3d651365013e5da845849571brbowen function is really reasonably straightforward once you
7d9f2bf92f3b96b3d651365013e5da845849571brbowen understand what needs to be done. Each function that needs
7d9f2bf92f3b96b3d651365013e5da845849571brbowen calling at some stage in the processing of a request needs to
7d9f2bf92f3b96b3d651365013e5da845849571brbowen be registered, handlers do not. There are a number of phases
7d9f2bf92f3b96b3d651365013e5da845849571brbowen where functions can be added, and for each you can specify with
7d9f2bf92f3b96b3d651365013e5da845849571brbowen a high degree of control the relative order that the function
7d9f2bf92f3b96b3d651365013e5da845849571brbowen will be called in.</p>
7d9f2bf92f3b96b3d651365013e5da845849571brbowen
7d9f2bf92f3b96b3d651365013e5da845849571brbowen <p>This is the code that was added to mod_mmap_static:</p>
bbbd690dc97941b1496fc940380969d3737f9ca1dreid<pre>
bbbd690dc97941b1496fc940380969d3737f9ca1dreidstatic void register_hooks(void)
bbbd690dc97941b1496fc940380969d3737f9ca1dreid{
bbbd690dc97941b1496fc940380969d3737f9ca1dreid static const char * const aszPre[]={ "http_core.c",NULL };
bbbd690dc97941b1496fc940380969d3737f9ca1dreid ap_hook_post_config(mmap_post_config,NULL,NULL,HOOK_MIDDLE);
bbbd690dc97941b1496fc940380969d3737f9ca1dreid ap_hook_translate_name(mmap_static_xlat,aszPre,NULL,HOOK_LAST);
bbbd690dc97941b1496fc940380969d3737f9ca1dreid};
bbbd690dc97941b1496fc940380969d3737f9ca1dreid</pre>
7d9f2bf92f3b96b3d651365013e5da845849571brbowen
7d9f2bf92f3b96b3d651365013e5da845849571brbowen <p>This registers 2 functions that need to be called, one in
7d9f2bf92f3b96b3d651365013e5da845849571brbowen the post_config stage (virtually every module will need this
7d9f2bf92f3b96b3d651365013e5da845849571brbowen one) and one for the translate_name phase. note that while
7d9f2bf92f3b96b3d651365013e5da845849571brbowen there are different function names the format of each is
7d9f2bf92f3b96b3d651365013e5da845849571brbowen identical. So what is the format?</p>
7d9f2bf92f3b96b3d651365013e5da845849571brbowen
7d9f2bf92f3b96b3d651365013e5da845849571brbowen <p><strong>ap_hook_[phase_name](function_name, predecessors,
7d9f2bf92f3b96b3d651365013e5da845849571brbowen successors, position);</strong></p>
7d9f2bf92f3b96b3d651365013e5da845849571brbowen
7d9f2bf92f3b96b3d651365013e5da845849571brbowen <p>There are 3 hook positions defined...</p>
7d9f2bf92f3b96b3d651365013e5da845849571brbowen
7d9f2bf92f3b96b3d651365013e5da845849571brbowen <ul style="list-style:none">
7d9f2bf92f3b96b3d651365013e5da845849571brbowen <li>HOOK_FIRST</li>
7d9f2bf92f3b96b3d651365013e5da845849571brbowen
7d9f2bf92f3b96b3d651365013e5da845849571brbowen <li>HOOK_MIDDLE</li>
7d9f2bf92f3b96b3d651365013e5da845849571brbowen
7d9f2bf92f3b96b3d651365013e5da845849571brbowen <li>HOOK_LAST</li>
7d9f2bf92f3b96b3d651365013e5da845849571brbowen </ul>
7d9f2bf92f3b96b3d651365013e5da845849571brbowen
7d9f2bf92f3b96b3d651365013e5da845849571brbowen <p>To define the position you use the position and then modify
7d9f2bf92f3b96b3d651365013e5da845849571brbowen it with the predecessors and successors. each of the modifiers
7d9f2bf92f3b96b3d651365013e5da845849571brbowen can be a list of functions that should be called, either before
7d9f2bf92f3b96b3d651365013e5da845849571brbowen the function is run (predecessors) or after the function has
7d9f2bf92f3b96b3d651365013e5da845849571brbowen run (successors).</p>
7d9f2bf92f3b96b3d651365013e5da845849571brbowen
7d9f2bf92f3b96b3d651365013e5da845849571brbowen <p>In the mod_mmap_static case I didn't care about the
7d9f2bf92f3b96b3d651365013e5da845849571brbowen post_config stage, but the mmap_static_xlat MUST be called
7d9f2bf92f3b96b3d651365013e5da845849571brbowen after the core module had done it's name translation, hence the
7d9f2bf92f3b96b3d651365013e5da845849571brbowen use of the aszPre to define a modifier to the position
7d9f2bf92f3b96b3d651365013e5da845849571brbowen HOOK_LAST.</p>
7d9f2bf92f3b96b3d651365013e5da845849571brbowen
7d9f2bf92f3b96b3d651365013e5da845849571brbowen <h3>Module Definition</h3>
7d9f2bf92f3b96b3d651365013e5da845849571brbowen
7d9f2bf92f3b96b3d651365013e5da845849571brbowen <p>There are now a lot fewer stages to worry about when
7d9f2bf92f3b96b3d651365013e5da845849571brbowen creating your module definition. The old defintion looked
7d9f2bf92f3b96b3d651365013e5da845849571brbowen like</p>
bbbd690dc97941b1496fc940380969d3737f9ca1dreid<pre>
bbbd690dc97941b1496fc940380969d3737f9ca1dreidmodule MODULE_VAR_EXPORT [module_name]_module =
bbbd690dc97941b1496fc940380969d3737f9ca1dreid{
bbbd690dc97941b1496fc940380969d3737f9ca1dreid STANDARD_MODULE_STUFF,
bbbd690dc97941b1496fc940380969d3737f9ca1dreid /* initializer */
bbbd690dc97941b1496fc940380969d3737f9ca1dreid /* dir config creater */
bbbd690dc97941b1496fc940380969d3737f9ca1dreid /* dir merger --- default is to override */
bbbd690dc97941b1496fc940380969d3737f9ca1dreid /* server config */
bbbd690dc97941b1496fc940380969d3737f9ca1dreid /* merge server config */
bbbd690dc97941b1496fc940380969d3737f9ca1dreid /* command handlers */
bbbd690dc97941b1496fc940380969d3737f9ca1dreid /* handlers */
bbbd690dc97941b1496fc940380969d3737f9ca1dreid /* filename translation */
bbbd690dc97941b1496fc940380969d3737f9ca1dreid /* check_user_id */
bbbd690dc97941b1496fc940380969d3737f9ca1dreid /* check auth */
bbbd690dc97941b1496fc940380969d3737f9ca1dreid /* check access */
bbbd690dc97941b1496fc940380969d3737f9ca1dreid /* type_checker */
bbbd690dc97941b1496fc940380969d3737f9ca1dreid /* fixups */
bbbd690dc97941b1496fc940380969d3737f9ca1dreid /* logger */
bbbd690dc97941b1496fc940380969d3737f9ca1dreid /* header parser */
bbbd690dc97941b1496fc940380969d3737f9ca1dreid /* child_init */
bbbd690dc97941b1496fc940380969d3737f9ca1dreid /* child_exit */
bbbd690dc97941b1496fc940380969d3737f9ca1dreid /* post read-request */
bbbd690dc97941b1496fc940380969d3737f9ca1dreid};
bbbd690dc97941b1496fc940380969d3737f9ca1dreid</pre>
7d9f2bf92f3b96b3d651365013e5da845849571brbowen
7d9f2bf92f3b96b3d651365013e5da845849571brbowen <p>The new structure is a great deal simpler...</p>
bbbd690dc97941b1496fc940380969d3737f9ca1dreid<pre>
bbbd690dc97941b1496fc940380969d3737f9ca1dreidmodule MODULE_VAR_EXPORT [module_name]_module =
bbbd690dc97941b1496fc940380969d3737f9ca1dreid{
bbbd690dc97941b1496fc940380969d3737f9ca1dreid STANDARD20_MODULE_STUFF,
bbbd690dc97941b1496fc940380969d3737f9ca1dreid /* create per-directory config structures */
bbbd690dc97941b1496fc940380969d3737f9ca1dreid /* merge per-directory config structures */
bbbd690dc97941b1496fc940380969d3737f9ca1dreid /* create per-server config structures */
bbbd690dc97941b1496fc940380969d3737f9ca1dreid /* merge per-server config structures */
bbbd690dc97941b1496fc940380969d3737f9ca1dreid /* command handlers */
bbbd690dc97941b1496fc940380969d3737f9ca1dreid /* handlers */
bbbd690dc97941b1496fc940380969d3737f9ca1dreid /* register hooks */
bbbd690dc97941b1496fc940380969d3737f9ca1dreid };
bbbd690dc97941b1496fc940380969d3737f9ca1dreid</pre>
bbbd690dc97941b1496fc940380969d3737f9ca1dreid
7d9f2bf92f3b96b3d651365013e5da845849571brbowen <p>Some of these read directly across, some don't. I'll try to
7d9f2bf92f3b96b3d651365013e5da845849571brbowen summarise what should be done below.</p>
7d9f2bf92f3b96b3d651365013e5da845849571brbowen
7d9f2bf92f3b96b3d651365013e5da845849571brbowen <p>The stages that read directly across :</p>
7d9f2bf92f3b96b3d651365013e5da845849571brbowen
7d9f2bf92f3b96b3d651365013e5da845849571brbowen <ul style="list-style:none">
7d9f2bf92f3b96b3d651365013e5da845849571brbowen <li>/* dir config creater */ ==&gt; /* create per-directory
7d9f2bf92f3b96b3d651365013e5da845849571brbowen config structures */</li>
7d9f2bf92f3b96b3d651365013e5da845849571brbowen
7d9f2bf92f3b96b3d651365013e5da845849571brbowen <li>/* server config */ ==&gt; /* create per-server config
7d9f2bf92f3b96b3d651365013e5da845849571brbowen structures */</li>
7d9f2bf92f3b96b3d651365013e5da845849571brbowen
7d9f2bf92f3b96b3d651365013e5da845849571brbowen <li>/* dir merger */ ==&gt; /* merge per-directory config
7d9f2bf92f3b96b3d651365013e5da845849571brbowen structures */</li>
7d9f2bf92f3b96b3d651365013e5da845849571brbowen
7d9f2bf92f3b96b3d651365013e5da845849571brbowen <li>/* merge server config */ ==&gt; /* merge per-server
7d9f2bf92f3b96b3d651365013e5da845849571brbowen config structures */</li>
7d9f2bf92f3b96b3d651365013e5da845849571brbowen
7d9f2bf92f3b96b3d651365013e5da845849571brbowen <li>/* command table */ ==&gt; /* command apr_table_t */</li>
7d9f2bf92f3b96b3d651365013e5da845849571brbowen
7d9f2bf92f3b96b3d651365013e5da845849571brbowen <li>/* handlers */ ==&gt; /* handlers */</li>
7d9f2bf92f3b96b3d651365013e5da845849571brbowen </ul>
7d9f2bf92f3b96b3d651365013e5da845849571brbowen
7d9f2bf92f3b96b3d651365013e5da845849571brbowen <p>The remainder of the old functions should be registered as
7d9f2bf92f3b96b3d651365013e5da845849571brbowen hooks. There are the following hook stages defined so
7d9f2bf92f3b96b3d651365013e5da845849571brbowen far...</p>
7d9f2bf92f3b96b3d651365013e5da845849571brbowen
7d9f2bf92f3b96b3d651365013e5da845849571brbowen <ul style="list-style:none">
7d9f2bf92f3b96b3d651365013e5da845849571brbowen <li>ap_hook_post_config <em>(this is where the old _init
7d9f2bf92f3b96b3d651365013e5da845849571brbowen routines get registered)</em></li>
7d9f2bf92f3b96b3d651365013e5da845849571brbowen
7d9f2bf92f3b96b3d651365013e5da845849571brbowen <li>ap_hook_http_method <em>(retrieve the http method from a
7d9f2bf92f3b96b3d651365013e5da845849571brbowen request. (legacy))</em></li>
7d9f2bf92f3b96b3d651365013e5da845849571brbowen
7d9f2bf92f3b96b3d651365013e5da845849571brbowen <li>ap_hook_open_logs <em>(open any specified logs)</em></li>
7d9f2bf92f3b96b3d651365013e5da845849571brbowen
7d9f2bf92f3b96b3d651365013e5da845849571brbowen <li>ap_hook_auth_checker <em>(check if the resource requires
7d9f2bf92f3b96b3d651365013e5da845849571brbowen authorization)</em></li>
bbbd690dc97941b1496fc940380969d3737f9ca1dreid
7d9f2bf92f3b96b3d651365013e5da845849571brbowen <li>ap_hook_access_checker <em>(check for module-specific
7d9f2bf92f3b96b3d651365013e5da845849571brbowen restrictions)</em></li>
bbbd690dc97941b1496fc940380969d3737f9ca1dreid
7d9f2bf92f3b96b3d651365013e5da845849571brbowen <li>ap_hook_check_user_id <em>(check the user-id and
7d9f2bf92f3b96b3d651365013e5da845849571brbowen password)</em></li>
bbbd690dc97941b1496fc940380969d3737f9ca1dreid
7d9f2bf92f3b96b3d651365013e5da845849571brbowen <li>ap_hook_default_port <em>(retrieve the default port for
7d9f2bf92f3b96b3d651365013e5da845849571brbowen the server)</em></li>
7d9f2bf92f3b96b3d651365013e5da845849571brbowen
7d9f2bf92f3b96b3d651365013e5da845849571brbowen <li>ap_hook_pre_connection <em>(do any setup required just
7d9f2bf92f3b96b3d651365013e5da845849571brbowen before processing, but after accepting)</em></li>
7d9f2bf92f3b96b3d651365013e5da845849571brbowen
7d9f2bf92f3b96b3d651365013e5da845849571brbowen <li>ap_hook_process_connection <em>(run the correct
7d9f2bf92f3b96b3d651365013e5da845849571brbowen protocol)</em></li>
7d9f2bf92f3b96b3d651365013e5da845849571brbowen
7d9f2bf92f3b96b3d651365013e5da845849571brbowen <li>ap_hook_child_init <em>(call as soon as the child is
7d9f2bf92f3b96b3d651365013e5da845849571brbowen started)</em></li>
7d9f2bf92f3b96b3d651365013e5da845849571brbowen
7d9f2bf92f3b96b3d651365013e5da845849571brbowen <li>ap_hook_create_request <em>(??)</em></li>
7d9f2bf92f3b96b3d651365013e5da845849571brbowen
7d9f2bf92f3b96b3d651365013e5da845849571brbowen <li>ap_hook_fixups <em>(last chance to modify things before
7d9f2bf92f3b96b3d651365013e5da845849571brbowen generating content)</em></li>
7d9f2bf92f3b96b3d651365013e5da845849571brbowen
7d9f2bf92f3b96b3d651365013e5da845849571brbowen <li>ap_hook_handler <em>(generate the content)</em></li>
7d9f2bf92f3b96b3d651365013e5da845849571brbowen
7d9f2bf92f3b96b3d651365013e5da845849571brbowen <li>ap_hook_header_parser <em>(let's modules look at the
7d9f2bf92f3b96b3d651365013e5da845849571brbowen headers, not used by most modules, because they use
7d9f2bf92f3b96b3d651365013e5da845849571brbowen post_read_request for this.)</em></li>
7d9f2bf92f3b96b3d651365013e5da845849571brbowen
7d9f2bf92f3b96b3d651365013e5da845849571brbowen <li>ap_hook_insert_filter <em>(to insert filters into the
7d9f2bf92f3b96b3d651365013e5da845849571brbowen filter chain)</em></li>
7d9f2bf92f3b96b3d651365013e5da845849571brbowen
7d9f2bf92f3b96b3d651365013e5da845849571brbowen <li>ap_hook_log_transaction <em>(log information about the
7d9f2bf92f3b96b3d651365013e5da845849571brbowen request)</em></li>
7d9f2bf92f3b96b3d651365013e5da845849571brbowen
7d9f2bf92f3b96b3d651365013e5da845849571brbowen <li>ap_hook_optional_fn_retrieve <em>(retrieve any functions
7d9f2bf92f3b96b3d651365013e5da845849571brbowen registered as optional)</em></li>
7d9f2bf92f3b96b3d651365013e5da845849571brbowen
7d9f2bf92f3b96b3d651365013e5da845849571brbowen <li>ap_hook_post_read_request <em>(called after reading the
7d9f2bf92f3b96b3d651365013e5da845849571brbowen request, before any other phase)</em></li>
7d9f2bf92f3b96b3d651365013e5da845849571brbowen
7d9f2bf92f3b96b3d651365013e5da845849571brbowen <li>ap_hook_quick_handler <em>(??)</em></li>
7d9f2bf92f3b96b3d651365013e5da845849571brbowen
7d9f2bf92f3b96b3d651365013e5da845849571brbowen <li>ap_hook_translate_name <em>(translate the URI into a
7d9f2bf92f3b96b3d651365013e5da845849571brbowen filename)</em></li>
7d9f2bf92f3b96b3d651365013e5da845849571brbowen
7d9f2bf92f3b96b3d651365013e5da845849571brbowen <li>ap_hook_type_checker <em>(determine and/or set the doc
7d9f2bf92f3b96b3d651365013e5da845849571brbowen type)</em> <!--#include virtual="footer.html" -->
7d9f2bf92f3b96b3d651365013e5da845849571brbowen </li>
7d9f2bf92f3b96b3d651365013e5da845849571brbowen </ul>
7d9f2bf92f3b96b3d651365013e5da845849571brbowen </body>
7d9f2bf92f3b96b3d651365013e5da845849571brbowen</html>
bbbd690dc97941b1496fc940380969d3737f9ca1dreid