/*
* ProFTPD: mod_lang -- a module for handling the LANG command [RFC2640]
*
* Copyright (c) 2006-2007 The ProFTPD Project
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
*
* As a special exemption, TJ Saunders and other respective copyright holders
* give permission to link this program with OpenSSL, and distribute the
* resulting executable, without including the source code for OpenSSL in the
* source distribution.
*
* $Id: mod_lang.c,v 1.5 2007/01/19 23:01:25 castaglia Exp $
*/
#include "conf.h"
#define MOD_LANG_VERSION "mod_lang/0.8"
#if PROFTPD_VERSION_NUMBER < 0x0001030101
# error "ProFTPD 1.3.1rc1 or later required"
#endif
#if PR_USE_NLS
module lang_module;
static const char *lang_default = "en";
static int lang_engine = TRUE;
static pool *lang_pool = NULL;
static pr_table_t *lang_tab = NULL;
/* Support routines
*/
static int lang_supported(const char *lang) {
if (strcmp(lang, "en") != 0)
return -1;
return 0;
}
/* Configuration handlers
*/
/* usage: LangDefault lang */
MODRET set_langdefault(cmd_rec *cmd) {
CHECK_ARGS(cmd, 1);
CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
(void) add_config_param_str(cmd->argv[0], 1, cmd->argv[1]);
return PR_HANDLED(cmd);
}
/* usage: LangEngine on|off */
MODRET set_langengine(cmd_rec *cmd) {
int bool;
config_rec *c;
CHECK_ARGS(cmd, 1);
CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
bool = get_boolean(cmd, 1);
if (bool == -1)
CONF_ERROR(cmd, "expected Boolean parameter");
c = add_config_param(cmd->argv[0], 1, NULL);
c->argv[0] = pcalloc(c->pool, sizeof(int));
*((int *) c->argv[0]) = bool;
return PR_HANDLED(cmd);
}
/* usage: LangPath path */
MODRET set_langpath(cmd_rec *cmd) {
CHECK_CONF(cmd, CONF_ROOT);
return PR_HANDLED(cmd);
}
/* Command handlers
*/
MODRET lang_lang(cmd_rec *cmd) {
unsigned char *authenticated;
if (!lang_engine)
return PR_DECLINED(cmd);
if (!dir_check(cmd->tmp_pool, cmd->argv[0], cmd->group, session.cwd, NULL)) {
pr_log_debug(DEBUG4, MOD_LANG_VERSION ": LANG command denied by <Limit>");
pr_response_add_err(R_500, _("Unable to handle command"));
return PR_ERROR(cmd);
}
/* If the user has already authenticated (and thus possibly chrooted),
* deny the command. Once chrooted, we will not have access to the
* message catalog files anymore.
*
* True, the user may not have been chrooted, but if we allow non-chrooted
* users to issue LANG commands while chrooted users cannot, it can
* constitute an information leak. Best to avoid that altogether.
*/
authenticated = get_param_ptr(cmd->server->conf, "authenticated", FALSE);
if (authenticated &&
*authenticated == TRUE) {
pr_response_add_err(R_500, _("Unable to handle command"));
return PR_ERROR(cmd);
}
if (cmd->argc > 2) {
pr_response_add_err(R_501, _("Invalid number of arguments"));
return PR_ERROR(cmd);
}
if (cmd->argc == 1) {
pr_log_debug(DEBUG7, MOD_LANG_VERSION
": resetting to default language '%s'", lang_default);
/* XXX Reset stuff here */
pr_response_add(R_200, _("Using default language %s"), lang_default);
return PR_HANDLED(cmd);
}
if (lang_supported(cmd->argv[1]) < 0) {
pr_response_add_err(R_504, _("Language %s not supported"), cmd->argv[1]);
return PR_ERROR(cmd);
}
/* If successful, remove the previous FEAT line for LANG, and update it
* with a new one showing the currently selected language.
*/
/* XXX As currently implemented, pr_feat_remove() allows for a memory
* leak in the feat pool. This means that a malicious client could
* send LANG repeatedly, and cause proftpd memory usage to grow
* (albeit very slowly). Perhaps the LANG command should only be
* accepted N number of times?
*/
pr_response_add(R_200, _("Using language %s"), cmd->argv[1]);
return PR_HANDLED(cmd);
}
MODRET lang_utf8(cmd_rec *cmd) {
register unsigned int i;
int bool, prev;
char *method = pstrdup(cmd->tmp_pool, cmd->argv[0]);
/* Convert underscores to spaces in the method name, for prettier
* logging.
*/
for (i = 0; method[i]; i++) {
if (method[i] == '_')
method[i] = ' ';
}
if (cmd->argc != 2) {
pr_response_add_err(R_501, _("'%s' not understood"), method);
return PR_ERROR(cmd);
}
bool = get_boolean(cmd, 1);
if (bool < 0) {
pr_response_add_err(R_501, _("'%s' not understood"), method);
return PR_ERROR(cmd);
}
prev = pr_fs_use_utf8(bool);
if (bool == TRUE &&
prev == FALSE) {
/* If we are being asked to enable UTF8, and the previous setting was to
* NOT use UTF8, it usually means that the sysadmin set "UseUTF8 off" in
* the proftpd.conf. Thus we revert back to the non-UTF8 case, and
* report an error back to the client.
*/
pr_fs_use_utf8(prev);
pr_log_debug(DEBUG5, "unable to accept 'OPTS UTF8 on' due to UseUTF8 "
"directive in config file");
pr_response_add_err(R_451, _("Unable to accept %s"), method);
return PR_ERROR(cmd);
}
pr_response_add(R_200, _("UTF8 set to %s"), bool ? "on" : "off");
return PR_HANDLED(cmd);
}
/* Event handlers
*/
static void lang_postparse_ev(const void *event_data, void *user_data) {
config_rec *c;
/* Scan the LangPath for the .mo files to read in. */
const char *lang_path = PR_LOCALE_DIR;
#ifdef HAVE_LIBINTL_H
const char *locale_path = NULL;
#endif
c = find_config(main_server->conf, CONF_PARAM, "LangPath", FALSE);
if (c) {
/* XXX How to make the configured path exported to any interested
* callers, e.g. modules that need to call bindtextdomain() for
* their own catalogs?
*/
lang_path = c->argv[0];
}
#ifdef HAVE_LIBINTL_H
pr_log_debug(DEBUG4, MOD_LANG_VERSION
": binding to text domain 'proftpd' using locale path '%s'", lang_path);
locale_path = bindtextdomain("proftpd", lang_path);
if (locale_path == NULL) {
pr_log_pri(PR_LOG_NOTICE, MOD_LANG_VERSION
": unable to bind to text domain 'proftpd' using locale path '%s': %s",
lang_path, strerror(errno));
}
#else
pr_log_debug(DEBUG2, MOD_LANG_VERSION
": unable to bind to text domain 'proftpd', lacking libintl support");
#endif /* !HAVE_LIBINTL_H */
/* Iterate through the server_rec list, checking each for a configured
* LangDefault. If configured, make sure that the specified lang
* is supported.
*/
c = find_config(main_server->conf, CONF_PARAM, "LangDefault", FALSE);
if (c) {
/* If the selected default language is not in LangPath,
* default to "en".
*/
}
}
static void lang_restart_ev(const void *event_data, void *user_data) {
destroy_pool(lang_pool);
lang_tab = NULL;
lang_pool = make_sub_pool(permanent_pool);
pr_pool_tag(lang_pool, MOD_LANG_VERSION);
}
/* Initialization functions
*/
static int lang_init(void) {
if (setlocale(LC_ALL, "") == NULL) {
pr_log_pri(PR_LOG_NOTICE, "unable to set LC_ALL: %s", strerror(errno));
return -1;
}
/* Preserve the POSIX/portable handling of number formatting; local
* formatting of decimal points, for example, can cause problems with
* numbers in SQL queries.
*/
if (setlocale(LC_NUMERIC, "C") == NULL) {
pr_log_pri(PR_LOG_NOTICE, "unable to set LC_NUMERIC: %s",
strerror(errno));
}
lang_pool = make_sub_pool(permanent_pool);
pr_pool_tag(lang_pool, MOD_LANG_VERSION);
pr_event_register(&lang_module, "core.postparse", lang_postparse_ev, NULL);
pr_event_register(&lang_module, "core.restart", lang_restart_ev, NULL);
return 0;
}
static int lang_sess_init(void) {
config_rec *c;
c = find_config(main_server->conf, CONF_PARAM, "LangEngine", FALSE);
if (c)
lang_engine = *((int *) c->argv[0]);
if (!lang_engine)
return 0;
pr_feat_add("UTF8");
/* Configure a proper FEAT line, for our supported languages and our
* default language.
*/
pr_feat_add("LANG en");
return 0;
}
/* Module API tables
*/
static conftable lang_conftab[] = {
{ "LangDefault", set_langdefault, NULL },
{ "LangEngine", set_langengine, NULL },
{ "LangPath", set_langpath, NULL },
{ NULL }
};
static cmdtable lang_cmdtab[] = {
{ CMD, C_LANG, G_NONE, lang_lang, FALSE, FALSE },
{ CMD, C_OPTS "_UTF8", G_NONE, lang_utf8, FALSE, FALSE },
{ 0, NULL }
};
module lang_module = {
NULL, NULL,
/* Module API version 2.0 */
0x20,
/* Module name */
"lang",
/* Module configuration handler table */
lang_conftab,
/* Module command handler table */
lang_cmdtab,
/* Module authentication handler table */
NULL,
/* Module initialization function */
lang_init,
/* Session initialization function */
lang_sess_init,
/* Module version */
MOD_LANG_VERSION
};
#endif /* PR_USE_NLS */
syntax highlighted by Code2HTML, v. 0.9.1