10.7. Server Core Routines

We now turn to less frequently used calls that are part of the Apache C-language API. These calls allow you to create custom error handlers, to create and manipulate subrequests, and to write formatted error messages to the log file.

10.7.1. The Subrequest API and Internal Redirects

The subrequest API can be used to ask Apache "what if" questions. A subrequest acts just like an ordinary request, except that the response phase is never actually run. The earlier phases, including the URI translation handler and the MIME type checker, are run as usual, and you can use their output to do such things as translating URIs into filenames.

A special case of a subrequest is an internal redirect, in which the current content handler discontinues processing the currently requested URI and tells Apache to return a different local URI instead. The content handler that eventually gets run is not necessarily the same as the one that invoked the internal redirect, although you can arrange for this to happen with ap_internal_redirect_handler().

These routines are declared in the header file http_request.h.


int ap_is_initial_req (request_rec *r)

This function returns a true value if the current request is the initial one. It will return false for handlers invoked as the result of subrequests or internal redirects.

if(!ap_is_initial_req(r)) {
   return DECLINED;
}

The Perl API provides a method called is_main() which returns true for initial requests and for requests triggered by internal redirects but not for subrequests. Although there is no direct equivalent in the C API, you can get the same information by examining the main field of the request record. If it is NULL, then the current request is the main one.

if (r->main != NULL) {
    return DECLINED; /* don't handle subrequests */
}

You might wish to declare a macro like the following:

#define is_main(r) (r->main == NULL)

The Perl API also defines a method called last() which returns the last request in a subrequest chain. This can be useful in logging handlers for recovering the status code of the last subrequest. A corresponding call is not defined in the C API but can be easily reproduced by the following function:

static request_rec *last(request_rec *r)
{
   request_rec *last;
   for(last=r; last->next != NULL; last=last->next) {
       continue;
   }
   return last;
}


request_rec *ap_sub_req_lookup_uri (const char *uri, const request_rec *r)

The ap_sub_req_lookup_uri() function creates a subrequest from the given URI, returning the resulting request record as the function result. You can then examine the request record to retrieve the URI's filename, MIME type, or other information. The following example shows how you can use a subrequest to translate a URI into a physical pathname:

request_rec *subr = ap_sub_req_lookup_uri(uri, r);
char *filename = subr->filename;


request_rec *ap_sub_req_lookup_ file (const char *file, const request_rec *r)

This call behaves similarly to ap_sub_req_lookup_uri(), except that the first argument is a filename rather than a URI and that Apache skips the URI translation phase while processing the subrequest. This example uses a subrequest to fetch the MIME type of the file given in filename:

request_rec *subr = ap_sub_req_lookup_file(filename, r);
char *mime_type   = subr->content_type;

It isn't necessary that the specified file actually exist in order to get useful information with ap_sub_req_lookup_ file(). For example, the default MIME type lookup operation depends only on the filename suffix, not on the contents of the file.


void ap_destroy_sub_req (request_rec *r)

When you are through with a subrequest, you should release the memory occupied by its data structures by passing the subrequest record to ap_destroy_sub_req(). If you forget to do this, the subrequest will be deallocated anyway when the main transaction is complete.

ap_destroy_sub_req(subr);


int ap_run_sub_req (request_rec *r)

If you have already created a subrequest using ap_sub_req_lookup_uri() or ap_sub_req_lookup_ file(), you can run its content handler by calling ap_run_sub_req(). This is sometimes used by modules that implement server-side include systems in order to incorporate a CGI script's output into the HTML page. The function will return the status code of the subrequest's content handler.

Here's the definition of a utility function called include_virtual(), which creates a subrequest, runs it, then destroys it:

static int include_virtual(request_rec *r, char *uri)
{
    int status = OK;
    request_rec *subr = ap_sub_req_lookup_uri(uri, r);
    status = ap_run_sub_req(subr);
    ap_destroy_sub_req(subr);
    return status;
}

And here's how include_virtual() might be used:

int status = include_virtual("/footers/standard_footer.html", r);


void ap_internal_redirect (const char *new_uri, request_rec *r)

The ap_internal_redirect() method will cause Apache to create a new request from the indicated URI and then run it. The effect is for Apache to send the client a different URI than the one originally requested. Unlike a formal redirect (in which Apache sends the browser a 301 or 302 redirect status code), the browser is not informed that this substitution has taken place.

The content handler for the new URI is not necessarily the same as the content handler that generated the redirect. Apache will determine which content handler to run by examining the new URI's MIME type and applicable configuration directives, just as if the browser had requested the URI directly.

ap_internal_redirect("/new/place", r);

After recalling this function, your handler should return without further processing the request.


void ap_internal_redirect_handler (const char *new_uri, request_rec *r)

If you wish to redirect to a new URI but continue to use the current content handler, call ap_internal_redirect_handler() instead of the previous function.

ap_internal_redirect_handler("/new/place", r);
								
								
								
								
								

10.7.2. The Cleanup API

As explained in Chapter 3, cleanup handlers are code subroutines that Apache invokes after the transaction is finished. These are usually used by modules to clean up data structures that could not be allocated from resource pools, such as device drivers and database handles, but can also be used for other tasks, such as deferring logging until after the transaction is completed (see Chapter 7 for a discussion of this technique).

Cleanup handlers use a different calling convention than that used by phase handlers. A cleanup handler takes a single void* argument and returns no function result. Its function prototype looks like this:

void cleanup_handler (void *data)

The data argument is provided for your convenience as a way to pass runtime information to the cleanup handler. It can be a pointer to any data structure of your choosing, or NULL if you don't care to pass any information. As we discuss later, the data argument is specified when you install the cleanup handler using ap_register_cleanup().

One common trick is to pass a pointer to the current request record so that the cleanup handler has access to information about the transaction. In the examples that follow, we use a cleanup handler that simply prints a message to standard error indicating that it's been called:

static void my_cleanup(void *data)
{
    request_rec *r = (request_rec *)data;
    fprintf(stderr, "process %d all done with %s
", (int)getpid(), r->uri);
}

Apache can accommodate an unlimited number of cleanup handlers, although few modules will need more than one. All cleanup functions are declared in the header file alloc.h.


void ap_register_cleanup (pool *p, void *data, void (*plain_cleanup) (void *), void (*child_cleanup) (void *))

To install a cleanup handler, call ap_register_cleanup(). It takes four arguments: a pool pointer (usually the one stored in the request record), a block of module-specific data to pass to the routine, and two function pointers to cleanup handlers. The first function pointer is the one you will usually use. It points to the cleanup handler to be called when the transaction is terminated. The second function pointer is only used when your module forks a child process and you need a routine to perform cleanup before the child terminates, for example, closing an open file inherited from the parent process. Since it is highly unusual for a module to fork, you will ordinarily pass the "do nothing" routine ap_null_cleanup for this argument. Always be sure to use ap_null_cleanup rather than NULL.

In the following example, we install my_cleanup() as the cleanup handler and arrange for it to be passed a copy of the current request record when it runs:

ap_register_cleanup(r->pool, (void *)r, my_cleanup, ap_null_cleanup);


void ap_kill_cleanup (pool *p, void *data, void (*cleanup)(void *))

Should you need to unregister a cleanup function before it runs, pass the address of the routine and its data block to ap_kill_cleanup(). Both the routine and the data block must match the values passed to ap_register_cleanup() in order for the removal to take effect.

ap_kill_cleanup(r->pool, (void *)r, my_cleanup);


void ap_run_cleanup (pool *p, void *data, void (*cleanup)(void *))

If you need to run a cleanup immediately, you can do so by calling this routine. The cleanup will be unregistered after it is run so that it is not run again during the ordinary cleanup period. It is unlikely that you will need to use this function, since it is easy enough to invoke the cleanup function directly.

10.7.3. Custom Response Handlers

As described in Chapters Chapter 3 and Chapter 4 and Chapter 6, Apache provides an API for creating custom error handlers. Modules can arrange for Apache to take special action when a handler returns a particular status code. Possible actions include displaying a static string, invoking a local URI, or redirecting to a remote URI. This is the mechanism that underlies the ErrorDocument directive.

As of Version 1.3.2, the Apache C-language API allows you to install a custom response handler from within a handler by calling the ap_custom_response( ) function, which is defined in the http_core.h header file. Here is the function's prototype:

void ap_custom_response (request_rec *r, int status, char *string);

r is, as usual, the current request record. status contains the status code that you wish to intercept, selected from among the symbolic constants defined in httpd.h. The last argument, string, can be a simple text message for Apache to display when the indicated error occurs, a remote URI, in which case Apache generates an external redirect, or a local URI, for which Apache generates a transparent internal redirect.

Apache distinguishes between these three possibilities by looking at the first few characters of the string. If it begins with a double quote mark, it is assumed to be a simple message string (the quote is stripped from the message before displaying it). Otherwise, if the string looks like a full URL (determined by calling ap_is_url() ), Apache takes it to be an external URL. Finally, if the string begins with a forward slash, Apache assumes the string to be a local URI. If the string doesn't satisfy any of these criteria, then it is again treated as a simple text message.

Here is an example of using ap_custom_response() to set a text message to be displayed when authentication fails:

ap_custom_response(r, HTTP_UNAUTHORIZED, "sorry, I don't know you.");

And here is an example that will generate an internal redirect to the Perl script server_error_handler.pl when any sort of internal server error occurs:

ap_custom_response(r, HTTP_INTERNAL_SERVER_ERROR,
                   "/perl/server_error_handler.pl");

The next example will redirect the client to another site if an HTTP_METHOD_NOT_ALLOWED error is raised:

ap_custom_response(r, HTTP_METHOD_NOT_ALLOWED,
                   "http://www.w3.org/pub/WWW/Protocols/rfc2068/rfc2068");

If you wish to use custom response handlers to pass information from the original request onward to the new request, there are a number of techniques that you can use to preserve headers, cookies, and other information. See Section 4.6.2 and Section 6.5 for a discussion of these techniques and their practical application. The main point to recall is that outgoing HTTP headers stored in the request record's headers_out field are not sent to the browser on an error, nor are they preserved across internal redirects. The contents of the err_headers_out table, however, have both characteristics.

10.7.4. Error Logging

At server startup time, Apache reopens the standard error file descriptor to the ErrorLog file.[4] If configured, each virtual server can also have its own error log. Modules can write messages to the error log in a simple way just by writing directly to standard error.

[4] When native syslog support is enabled, the stderr stream will be redirected to /dev/null !

However, the simple way is less than desirable because it leaves a bare string in the error log, with no indication of the time or date that the error occurred or which module left the message. Apache's error-logging API avoids these problems by providing module writers with two functions, ap_log_rerror() and ap_log_error(), both of which write nicely formatted error messages to the error log. In addition to a timestamp and a message, optional flags allow modules to include the name and line number of the C source code file where the error occurred as well as the contents of the system errno variable.

As of Version 1.3, Apache supports the notion of a message severity level. In this scheme, which should be familiar to users of the Unix syslog system,[5] each message is assigned one of eight severities that range from high (APLOG_EMERG) to low (APLOG_DEBUG). A log level setting, set by the webmaster with the configuration directive LogLevel , controls which messages actually get sent to the log file. For example, if LogLevel is set to warn, only messages with severity APLOG_WARN or higher will be written to the log file. Messages at a lower priority will be ignored. This facility allows your module to write lots and lots of debugging messages at a low severity level. During module development, you can set LogLevel to a low level in order to see the debugging messages. Later you can raise the log level so that the debugging messages are suppressed on the production server.

[5] In fact, the error log API maps directly to syslog when native syslog support is enabled. See the Apache documentation on the ErrorLog directive for details on enabling native syslog support.

All logging constants and routines are declared in http_log.h:


void ap_log_error (const char *file, int line, int level, const server_rec *s, const char *fmt, ...)


void ap_log_rerror (const char *file, int line, int level, const request_rec *r, const char *fmt, ...)

ap_log_rerror() and ap_log_error() are the two main entry points for the Apache error log API. These calls have many arguments, and C programmers might want to define some macros in order to save keystrokes. A couple of examples of this technique are given at the end of this section.

The first two arguments are the filename and line number where the error occurred. Most modules will want to use the APLOG_MARK macro here. It uses the C compiler __FILE__ and __LINE__ tokens to automatically pass this information. The third argument, level, is the severity level at which to record the message. level should be selected from the list of symbolic constants given later. The severity level is actually a bit mask; by setting other bits in the mask, you can adjust other logging options, as we describe later. The fourth argument is different for the two calls. For ap_log_error(), it is the server_rec, ordinarily obtained from r->server. For ap_log_rerror(), it is the request record itself, r. Internally, the logging API uses the server record to find the error log's FILE* for writing, or it passes messages to the syslog() function if native syslog support is enabled. The fifth argument, fmt, is a sprintf()-style format string. It, and the variable number of arguments that follow it, are passed to sprintf() to generate the message written to the log file.

if (!(fh = ap_pfopen(r->pool, cfg->config_file, "r"))) {
   ap_log_error(APLOG_MARK, APLOG_EMERG, r->server,
                "Cannot open configuration file %s.", cfg->config_file);
   return HTTP_INTERNAL_SERVER_ERROR;
}

One difference between ap_log_error() and ap_log_rerror() is that the latter function can optionally write the error message to the notes table under a key named error-notes. This message can then be retrieved and displayed by ErrorDocument handlers and other error processors. The message is only written to the notes table if the message severity level is warn or higher, and there is not already an error-notes entry in the notes table. Another difference is that ap_log_error() includes the client's dotted IP address in the formatted error message.


void ap_log_reason (const char *reason, const char *fname, request_rec *r)

It is so common to encounter a system error while opening a file or performing I/O on the system that a special routine is provided in the API. ap_log_reason() takes a character string describing the problem, the name of the file that was involved in the error, and the current request record. It is also common to use this function to log unsuccessful attempts to access protected documents, since the remote hos t's name is incorporated into the error message as well.

Here's a typical example of using ap_log_reason() and the line that it writes to the log file.

ap_log_reason("Can't open index.html", r->uri, r);

[Tue Jul 21 16:30:47 1998] [error] access to /
failed for w15.yahoo.com, reason: Can't open index.html No such file
or directory

Internally, ap_log_reason() is just a frontend to the following call:

ap_log_error(APLOG_MARK, APLOG_ERR, r->server,
           "access to %s failed for %s, reason: %s",
           file,
           ap_get_remote_host(r->connection, r->per_dir_config, REMOTE_NAME),
           reason);

The level flag passed to ap_log_error() and ap_log_rerror() should be one of the severity level constants listed below, possibly logically ORed with either of the constants APLOG_NOERRNO or APLOG_WIN32ERROR.


APLOG_NOERRNO

By default, the logging API will include the contents of the system errno variable in the message. This feature is sometimes useful, as when you log an error that results from a failed system call, and sometimes not useful at all (and may in fact lead to misleading messages since errno is not reset by successful calls). Combine the severity level with APLOG_NOERRNO to suppress the automatic inclusion of errno.

ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, r,
            "The requested URI was %s", r->uri);


APLOG_WIN32ERROR

This constant, available on Win32 platforms only, will make Apache log the value returned by the GetLastError() system call in addition to the value of errno from the standard C library.


APLOG_EMERG

This severity level indicates that an emergency condition has occurred. It should be reserved for problems that render the server unusable.

ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_EMERG, r->server,
            "Cannot find lock file.  Aborting.");


APLOG_ALERT

This level is intended for problems that require immediate attention.


APLOG_CRIT

This logs messages at a level intended for severe problems that require immediate attention.


APLOG_ERR

This logs the message at the error severity level, intended for use with noncritical errors that nevertheless require someone's attention.

ap_log_error(APLOG_MARK, APLOG_ERR, r->server,
             "Could not open file", r->filename);


APLOG_WARN

The warn level is one step less severe than error and is intended for warnings that may or may not require attention.


APLOG_NOTICE

notice messages are used for normal but significant conditions.

ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, r->server,
             "Cannot connect to master database, using backup.");


APLOG_INFO

The info severity level is used for informational messages issued for nonerror conditions.


APLOG_DEBUG

The lowest severity of all is debug, used for issuing messages during the development and debugging of a module.

ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, r->server,
             "Filename=%s,uri=%s,mtime=%d,",
             r->filename, r->uri, r->finfo.mtime);

If the ap_log_rerror( ) and ap_log_error( ) calls are too verbose for your tastes, we recommend that you create a few preprocessor macros for frequently used combinations. For example:

#define my_error(mess) ap_log_error(APLOG_MARK,
                                 APLOG_NOERRNO|APLOG_ERROR,
                                  r->server, mess)

#define my_debug(mess) ap_log_error(APLOG_MARK,
                                 APLOG_NOERRNO|APLOG_DEBUG,
                                  r->server, mess)

Now you can log simple error messages this way:

my_error("Can't find lock file.  Aborting.");
					

10.7.5. The Piped Log API

Apache Version 1.3.0 introduced reliable piped log support, which allows your module to send log data to a running program.[6] If the program happens to die, it will be automatically restarted. Internally, this API is used when processing log file directives that begin with the | (pipe character). Everything following the pipe character is treated as a program to be run. After the program is launched, log entries are sent to the program on its standard input.

[6] Reliable piped log support was not available on Win32 platforms at the time this was written.

You will probably not need to use the piped log routines because this functionality is already handled in a generic way by Apache's mod_log_config module. However, if you wish to support this feature in a custom logging module of your own, these calls are available for your use.

The main data structure used by the piped log API is the piped_log record, defined in http_log.h. This data structure should be treated as an opaque data structure. Use ap_open_piped_log() to open a pipe to a new process, ap_piped_log_read_fd() and ap_piped_log_write_fd() to obtain file descriptors that you can use to read and write from the process, and ap_close_piped_log() to close the pipe when you're through.


piped_log *ap_open_piped_log (pool *p, const char *program)

Given a resource pool p and the path to an executable program, ap_open_piped_log() will launch the program, open a bidirectional pipe to it, and return a piped_log pointer if successful or NULL if not.

You should make this call during the module or child initialization phases. This will avoid the overhead of opening and closing the pipe for each request. If you open the pipe at module initialization time, the subprocess will be run as root, but there will be only one copy running. If you open the pipe during child initialization, it will run as the httpd user, but there will be one copy of the subprocess running for each child. It's your call which to use.

Here's an example of opening a log file specified by the module-specific configuration record cfg. If the initial character of the filename is the pipe symbol, the code opens it as a piped command. Otherwise, it opens it as a normal file. For simplicity, we've omitted error checking from this example.

if(*cfg->log_file == '|') {
    /* open as a command pipe */
    piped_log *pl = ap_open_piped_log(p, cfg->log_file + 1);
    cfg->log_fd = ap_piped_log_write_fd(pl);
}

else {
   /* open as normal file */
   cls->log_fd = ap_popenf(p, cfg->log_file, flags, mode);
}
if (!cls->log_fd) {
       ... raise some sort of error...

Some of the routines in this example are described in the next chapter.


void ap_close_piped_log (piped_log *pl)

This function closes a previously opened piped log. Conveniently, this function will be called at pool destruction time if you don't call it yourself.


int ap_piped_log_write_ fd (piped_log *pl)

ap_piped_log_write_ fd() returns a file descriptor that you can use to write to the logging process using the standard write() library call. Typically, you will write some form of accounting or status information, but the contents of the information you send are entirely up to you. Because all writing is done through a file descriptor, the same code routines that write to plain text files can be used to write to the pipe.


int ap_piped_log_read_ fd (piped_log *pl)

ap_piped_log_read() returns a file descriptor that you can use to read from the logging process with the standard read() library call. It is far more usual to write to a logging process than to read from one, but you can do this if the process provides status information, for instance. If you both read and write from the process, beware of deadlock situations in which both your module and the logging process are waiting for the other.

10.7.6. Authorization and Authentication Routines

The last core routines we'll consider are those used for access control, authentication, and authorization. If you are familiar with the Perl API from Chapter 6, you'll find no surprises here.

These routines are declared in http_core.h unless otherwise specified:


int ap_allow_options (request_rec *r)

The ap_allow_options() call returns a bit mask containing the contents of the Perl-directory Options directive. You can logically AND this bit mask with a set of symbolic constants in order to determine which options are set. For example, the following code fragment checks whether ExecCGI is active for the directory containing the currently requested document:

if(!(ap_allow_options(r) & OPT_EXECCGI)) {
    ap_log_reason("Options ExecCGI is off in this directory",
                   $r->filename, r);
     return HTTP_FORBIDDEN;
}

The options constants are as follows:

Constant Meaning
OPT_INDEXES

The Indexes option is set.
OPT_INCLUDES

The Includes option is set.
OPT_SYM_LINKS

The SymLinks option is set.
OPT_EXECCGI

The ExecCGI option is set.
OPT_UNSET

(See the description that follows.)
OPT_INCNOEXEC

The IncludeNoExec option is set.
OPT_SYM_OWNER

The SymLinksIfOwnerMatch option is set.
OPT_MULTI

The MultiViews option is set.

Also available are the constants OPT_NONE, for no options set (this is defined as zero), and OPT_ALL, for all but the MultiViews option set.

OPT_UNSET corresponds to a bit that is initially set to 1 in the options flag but is not otherwise used. If no absolute assignment to the Options directive has been made, then this bit will remain set; otherwise, it will be unset. In other words, you can test this bit to determine whether only additive and subtractive assignments to Options have been made. In a directory with this Options directive, the OPT_UNSET bit will be true:

Options +ExecCGI -Indexes

However, in a directory with this directive, the bit will be false:

Options ExecCGI

As Commander Spock would say, "Fascinating."


const char *ap_auth_name (request_rec *r)

If authentication is configured for the currently requested document or directory, ap_auth_name() will return the name of the current authentication realm, as defined by the AuthName directive. If no realm is currently defined, this function will return NULL.

Note that it is quite possible for the current request to have an authentication realm without authentication actually being active. For example, there may be no requires directive in the directory configuration.

const char *auth_name = ap_auth_name(r);


const char *ap_auth_type (request_rec *r)

This call returns the type of authentication configured for the current file or directory or NULL if none. The current possibilities are Basic and Digest.

const char *auth_type = ap_auth_type(r);

if(strcasecmp(auth_type, "basic")) {
    ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARN, r->server,
                  "%s can't handle AuthType %s", __FILE__, auth_type);
    return DECLINED;
}

Although the information returned by ap_auth_type() seems redundant with the contents of the connection record's ap_auth_type field, there is an important difference. ap_auth_type() returns the authentication scheme configured for the current directory, whereas the connection record's ap_auth_type field returns the authentication scheme only if authentication is actually in use. To determine whether authentication is active, you should only trust the connection record's field.


int ap_get_basic_auth_pw (request_rec *r, const char **pw)

If the browser provided password authentication in making its request, the ap_get_basic_auth_pw() call will return the password. You pass the function the request record in r and the address of a character pointer in pw. If successful, the function will return a result code of OK and place a copy of the password in pw. Otherwise, the function will return one of the result codes DECLINED, HTTP_INTERNAL_SERVER_ERROR, or HTTP_UNAUTHORIZED. DECLINED is returned when the current request isn't for a directory that is protected by Basic authentication. HTTP_INTERNAL_SERVER_ERROR can occur when the authorization realm directive is missing. Finally, HTTP_UNAUTHORIZED is returned if the browser fails to provide a password or attempts to use the wrong authentication scheme.

This call is typically used by authentication handlers to recover the user's password. The username can be retrieved from the connection record's user field. You should then do something with the two values to validate them.

const char *sent_pw = NULL;
char *user;
int ret = ap_get_basic_auth_pw(r, &sent_pw);
if(ret != OK) {
    return ret;
}

user = r->connection->user;
...


void ap_note_basic_auth_ failure (request_rec *r)


void ap_note_digest_auth_ failure (request_rec *r)


void ap_note_auth_ failure (request_rec *r)

(Declared in the header file http_protocol.h.) If authentication is required for the current directory, but the browser did not provide the required information, these three variants set the HTTP authentication gears in motion by sending an "Authentication Required" message to the browser.

ap_note_basic_auth_failure() and ap_note_digest_auth_failure() are used for Basic and Digest authentication schemes, respectively. The generic ap_note_auth_failure() call will dispatch to one of those two routines based on which type of authentication the current directory is configured to use.

We can now write the skeleton for username/password authentication. In this example, check_auth() is some routine that you provide to check that the login name and password are valid. Replace this routine with a function that always returns 1, and you have our Apache::AuthAny module from Chapter 6!

const char *sent_pw = NULL;
char *user = r->connection->user;
int ret = ap_get_basic_auth_pw(r, &sent_pw);
if (ret != OK) {
    return ret;
}
if(!(user && sent_pwd && check_auth(user, sent_pw)) {
    ap_note_basic_auth_failure(r);
    ap_log_reason("User did not authenticate", r->uri, r);
    return HTTP_UNAUTHORIZED;
}


const array_header *ap_requires (request_rec *r)

As we described in Chapter 6, after a successful authentication, Apache calls the authorization handler to determine whether the authenticated user is allowed access to the requested document. To do this, the authorization handler needs to process any and all requires directives in the current directory configuration. The ap_requires() call returns the contents of these directives in predigested form.

The function result of ap_requires() is an array_header* containing a list of require_line structs. The definition of this data type, as found in http_core.h, is as follows:

typedef struct {
    int method_mask;
    char *requirement;
} require_line;

method_mask is an integer bitmask constructed from the request methods listed in the current <Limit> directive, or -1 if no <Limit> section applies. The set bit numbers correspond to the method numbers M_GET, M_POST, and so on. For example, you could determine whether the first requirement applies to POST requests with the following code fragment:

int isPost = 0 != (requirement[0].method_mask & (1 << M_POST));

requirement is a character string containing the exact text of the requires directive. You will need to parse this text in order to determine what type of requirement to apply.

Example 10.6 gives a short example of iterating over the ap_requires() array and printing out the information it contains. You should be able to use this code in a real authorization module by replacing the various print statements with code that performs the actual authorization checks. For real-life examples, see mod_auth, mod_auth_dbm, and the other standard authorization modules.

Example 10.6. Processing requires Directives
static char *request_methods[] = {
   "GET","PUT","POST","DELETE","CONNECT","OPTIONS","TRACE",NULL
};

#define comma_or_newline(value) 
if(value) fprintf(stderr, ", "); 
else      fprintf(stderr, "
");

static void hello_util_requires_dump(request_rec *r)
{
   const array_header *requires = ap_requires(r);
   require_line *rq;
   int x;

   if (!requires) {
       fprintf(stderr,
               "requires: there are no requirements for this request
");
       return;
   }

   rq = (require_line *) requires->elts;

   for (x = 0; x < requires->nelts; x++) {
       const char *line, *requirement;
       int i;

       fprintf(stderr, "requires: limited to request methods: ");
       for(i=0; request_methods[i]; i++) {
           if (rq[x].method_mask & (1 << i))
               fprintf(stderr, "%s ", request_methods[i]);
       }
       fprintf(stderr, "
");

       line = rq[x].requirement;
       requirement = ap_getword(r->pool, &line, ' '),

       if (!strcmp(requirement, "valid-user")) {
           fprintf(stderr, "requires: any valid-user allowed here.
");
           return;
       }

       if (!strcmp(requirement, "user")) {
           fprintf(stderr, "requires: allowed users: ");
           while (line[0]) {
               requirement = ap_getword_conf(r->pool, &line);
               fprintf(stderr, "`%s'", requirement);
               comma_or_newline(line[0]);
           }
       }

       else if (!strcmp(requirement, "group")) {
           fprintf(stderr, "requires: allowed groups: ");
           while (line[0]) {
               requirement = ap_getword_conf(r->pool, &line);
               fprintf(stderr, "`%s'", requirement);
               comma_or_newline(line[0]);
           }
       }
   }
}


int ap_satisfies (request_rec *r)

The Satisfy directive determines whether a request for a URI that is protected by both access control and authentication must pass through both phases successfully or either one or the other. If Satisfy is set to all, all access control and authentication tests must be passed successfully. In contrast, if the directive is set to any, then the request will be allowed if any of the checks returns OK.

Handlers involved with access control can gain access to this configuration directive using the ap_satisfies() function. It returns one of the constants SATISFY_ANY, SATISFY_ALL, or SATISFY_NOSPEC. The last constant indicates that the directive wasn't present at all. Each of these constants, and the declaration of ap_satisfies() itself, is found in http_core.h.

As an example, consider an access control handler that wants to write an error log message when a user is denied access, but not when SATISFY_ANY is set, because the user might still be allowed in during the authentication phase. It can do the following:

if (return_value == HTTP_FORBIDDEN) {
   if (!(r->satisfies == SATISFY_ANY && ap_some_auth_required(r)))
     ap_log_reason("Client denied by server configuration", r->uri, r);
}
return return_value;


int ap_some_auth_required (request_rec *r)

The ap_some_auth_required() function can be used within any handler to determine whether authentication is required for the requested document. If you are writing a module that must always run with authentication enabled (such as a password changing program), you can use this call to make sure that the module is never inadvertently run without protection. For example:

if(!ap_some_auth_required(r)) {
    ap_log_reason("I won't go further unless the user is authenticated",
                  r->uri, r);
    return HTTP_FORBIDDEN;
}

The next chapter shows you how to create configuration directives with the C API and covers less frequently used parts of the C-language API.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.141.7.174