Skip to content

Latest commit

 

History

History
155 lines (120 loc) · 5.22 KB

site.md

File metadata and controls

155 lines (120 loc) · 5.22 KB

ProFTPD Developer Guide: Module Development: SITE Commands


Table of Contents


Custom SITE Commands

Adding your own custom SITE is straightforward. You need to define a SITE command handler in your module.

There should be two parts to a custom SITE command handler:

  • A part that handles the module-specific SITE command
  • A part to handle adding a description of that command to the SITE HELP response

Example SITE Command

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 PR_DECLINED(cmd);
  }

  /* This case does the work of returning the local time to the client. */
  if (strcasecmp(cmd->argv[1], "TIME") == 0) {
    char time_text[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) {
      pr_response_send(R_530, "Please login with USER and PASS.");
      return PR_ERROR(cmd);
    }

    /* Check for <Limit> restrictions */
    if (!dir_check(cmd->tmp_pool, "SITE_TIME", "MISC", session.cwd, NULL)) {
      pr_response_add_err(R_550, "%s: %s", cmd->arg, strerror(EPERM));
      return PR_ERROR(cmd);
    }

    /* Log that the user requested the time. */
    pr_log_pri(PR_LOG_INFO, "SITE TIME requested by user %s", session.user);

    /* Prepare the time string to be returned. */
    tnow = localtime(&now);
    if (tnow == 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).
       */
      pr_response_add(R_202, "Unable to provide the time right now");

      /* Of course, if something like localtime(3) fails, it should be
       * logged as well.
       */
      pr_log_pri(PR_LOG_NOTICE, "notice: localtime() returned NULL?!?");

      return PR_HANDLED(cmd);
    }

    /* Print the local time into the string buffer. */
    strftime(time_text, sizeof(time_text), "%a %b %d %T %Z %Y", tnow);
    time_text[sizeof(time_text)-1] = '\0';

    /* The 200 response code is the normal value for such "generic" commands
     * as site-specific SITE commands.
     */
    pr_response_add(R_200, "The current time is: %s", time_text);

    /* If you want to add a multiline response, use the R_DUP macro, like
     * so:
     *
     *  pr_response_add(R_DUP, ...);
     */

    return PR_HANDLED(cmd);
  }

  if (strcasecmp(cmd->argv[1], "HELP") == 0) {
    /* Add a description of SITE TIME to the output.  The response code
     * 214 is the appropriate value to use for "helpful" responses.
     */
    pr_response_add(R_214, "TIME");

    /* If this SITE command has needed a parameter (RFC 959 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:
     *
     *  pr_response_add(R_214, "TIME <param>");
     */
  }

  /* 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 PR_DECLINED(cmd);
}

The only remaining task is to register this command handler by adding an appropriate entry to the module cmdtable:

  { CMD, C_SITE, G_NONE, site_time, FALSE, FALSE, CL_MISC },

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
  • Perform as many checks on the input as necessary
  • 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 themselves if and how to perform authentication/authorization checks, rather than always relying on the core engine checking. Your SITE handlers might have different needs/criteria for their usage.

The CL_MISC value indicates that the registered SITE command is "miscellaneous" for purposes of logging by ExtendedLog. Custom SITE commands should always use this logging class so that administrators' ExtendedLog files properly show when that particular SITE command is used.


Table of Contents