ProFTPD Developer's Guide: Module SITE commands

ProFTPD Version 1.2


Table of Contents

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.

Table of Contents



Author: $Author: castaglia $
Last Updated: $Date: 2003/01/02 17:39:31 $


© Copyright 2000-2003 TJ Saunders
All Rights Reserved