SITE
commands
Module SITE
Commands
Adding your own custom SITE
is trivial. You merely need
to define a SITE
command handler in your module.
There should be two parts to a custom SITE
command handler: one
part that handles the module-specific SITE
command, and one
part to handle adding a description of that command in response to a
SITE HELP
request from a client.
The example command handler below implements SITE TIME
:
MODRET site_time(cmd_rec *cmd) { /* Make sure it's a valid SITE TIME command, with no additional * parameters sent by the client. */ if (cmd->argc != 2) return DECLINED(cmd); /* This case does the work of returning the local time to the client */ if (!strcasecmp(cmd->argv[1], "TIME")) { char timestr[80] = {'\0'}; time_t now = time(NULL); struct tm *tnow = NULL; /* If the user is required to be authenticated/logged in, do the * necessary check. This may or may not be required, depending on * the SITE command being implemented. */ if (get_param_int(cmd->server->conf, "authenticated", FALSE) != TRUE) { send_response(R_530, "Please login with USER and PASS."); return ERROR(cmd); } /* Check for <Limit> restrictions */ if (!dir_check(cmd->tmp_pool, "SITE_TIME", "MISC", session.cwd, NULL)) { add_response_err(R_550, "%s: %s", cmd->arg, strerror(EPERM)); return ERROR(cmd); } /* Log that the user requested the time. */ log_pri(LOG_INFO, "SITE TIME requested by user %s", session.user); /* Prepare the time string to be returned. */ if ((tnow = localtime(&now)) == NULL) { /* If, for some reason, an error occurs, use the 202 error code. * Other legal error codes for a SITE command include 500 (syntax * error, command not recognized), 501 (syntax error in parameters or * arguments), and 530 (not logged in). */ add_response(R_202, "Unable to provide the time right now"); /* Of course, if something like localtime(3) fails, it should be * logged as well. */ log_pri(LOG_NOTICE, "notice: localtime() returned NULL?!?"); return HANDLED(cmd); } /* Print the local time into the string buffer. */ strftime(timestr, sizeof(timestr), "%a %b %d %T %Z %Y", tnow); timestr[sizeof(timestr) - 1] = '\0'; /* The 200 response code is the normal value for such "generic" commands * as site-specific SITE commands. */ add_response(R_200, "The current time is: %s", timestr); /* If you want to add a multiline response, use the R_DUP macro, like * so: * * add_response(R_DUP, ...); */ return HANDLED(cmd); } if (!strcasecmp(cmd->argv[1], "HELP")) { /* Add a description of SITE TIME to the output. The response code * 214 is the appropriate value to use for "helpful" responses. */ add_response(R_214, "TIME"); /* If this SITE command has needed a parameter (RFC959 only allows * one parameter to the SITE command, although not many servers seem to * strictly enforce this), this HELP line would have looked something like: * * add_response(R_214, "TIME "); */ } /* This defaults to returning DECLINED in the case of a SITE HELP command. * This may seem wrong, but is actually correct: the case above adds * a response to the response chain for the current command, but does * not actually handle the command. By returning DECLINED now, it * allows other modules to add their own SITE HELP responses to the chain * before the full chain is flushed back to the client once the command * is "officially" handled by mod_site. */ return DECLINED(cmd); }The only remaining task is to register this command handler by adding an appropriate entry to the module's
cmdtable
:
{ CMD, C_SITE, G_NONE, site_time, FALSE, FALSE },Simple. The key points to keep in mind are: use the correct response codes as appropriate, provide support for the
SITE HELP
command
as well as for the custom functionality, require authentication as necessary,
and, as normal, perform as many checks on the input as necessary. Do not
forget to document any custom SITE
commands supported by your
module.
Note that the requires_auth
field of the above
cmdtable
entry (i.e. the first FALSE
)
is FALSE
, and that the example handler performs the same
authentication check (requiring that the user be authenticated before
using the command) as if requires_auth
had been TRUE
.
This is normal, and in general a good idea: let your SITE
handlers determine if and how to perform an authentication/authorization
check for themselves, rather than always relying on the core checking
ability.