DocumentationGenerated on Thu Aug 31 00:02:28 2006 |
||
Configuration.cppGo to the documentation of this file.00001 00002 // $Id: Configuration.cpp,v 1.101 2005/08/23 19:34:02 klas Exp $ 00003 // 00004 // Usage: Functions and stuff for Configure class, holding and 00005 // reading config stuff. 00006 // 00007 // A part of babyftpd, licensed under the GNU GPL, see the file 00008 // LICENSE for complete information. 00010 00011 #define CONFIGURATION_CPP 00012 #define BABY_IO 00013 #include "Configuration.h" 00014 00015 class Configuration *config; 00016 00017 Configuration::Configuration() 00018 { 00019 conf_file = "babyftpd.conf"; 00020 parsed = false; 00021 00022 general.connection_limit = INT_MAX; // ain't this a bit high? 00023 general.port = 21; // default port 00024 general.data_port = 20; // default outgoing data port 00025 general.no_daemon = false; // daemon mode or not? 00026 general.os = "UNIX"; // what OS is this? 00027 general.admin_email = ""; // admin email? 00028 general.lifebeat_tick = 300; // run the lifebeat() function every this 00029 char tmp[20]; 00030 gethostname(tmp, 20); 00031 general.welcome_msg = "BabyFTPd "; 00032 general.welcome_msg += VERSION; 00033 general.welcome_msg += " Server ["; 00034 general.welcome_msg += tmp; 00035 general.welcome_msg += "]"; 00036 general.user = 0; // by default when run all as root 00037 general.group = 0; 00038 general.readonly = false; 00039 general.anonymous = A_DISABLE; 00040 #ifdef USE_TLS 00041 general.tls_key = "/etc/babyftpd/tls_key.pem"; 00042 general.tls_cert = "/etc/babyftpd/tls_cert.pem"; 00043 general.tls_ca = "/etc/babyftpd/tls_ca.pem"; 00044 general.tls_crl = "/etc/babyftpd/tls_crl.pem"; 00045 general.dh_regenerate = 3600; // seconds between diffie-hellman regenerations 00046 general.verify_client = "/etc/babyftpd/verify_client.pl"; 00047 00048 general_user.encryption = ALLOW; 00049 #endif 00050 logger.log_level = 3; // 3 is probably a sensible default? 00051 logger.xferlog = true; // by default all transfers are logged 00052 logger.xferlog_file = "/var/log/xferlog"; // here 00053 logger.thread_id = false; // we don't want thread ids all over the logs 00054 logger.time_format = "%b %e %H:%M:%S "; // time format 00055 00056 // these options end up as default for all users, that is, 00057 // those that don't occur in the conf file get these values 00058 general_user.id = "user_general"; 00059 general_user.sim_data = INT_MAX; 00060 general_user.idle = 900; // idle timeout after how long? 00061 general_user.noftp = false; // can this user login with ftp or not? 00062 general_user.only_home = false; // false chroot in homedir? 00063 general_user.only_passive = false; 00064 general_user.user_limit = INT_MAX; // maximum control connections 00065 } 00066 00067 Configuration::~Configuration() 00068 { 00069 } 00070 00071 bool Configuration::parse_config_file() 00072 { 00073 vector<string> file_buff; 00074 if(!fill_buff(file_buff, conf_file)) 00075 return(false); 00076 00077 set_structs(get_variables(file_buff, "general", 1), GENERAL, &general); 00078 set_structs(get_variables(file_buff, "general", 2), USER, &general_user); 00079 set_structs(get_variables(file_buff, "log", 1), LOG, &logger); 00080 00081 string user_names; 00082 num_users = count_users(file_buff, user_names) - 1; // first user is 0. 00083 00084 class StringTokenizer *names = new StringTokenizer(user_names, ",", false); 00085 names->nextToken(); // not nice, but nicer. 00086 00087 if(!users.empty()) // that is, on SIGHUP 00088 users.clear(); 00089 00090 users.reserve(num_users + 1); 00091 for(int i = 0; i < num_users; i++) 00092 { 00093 struct users_stru user_tmp = general_user; 00094 set_structs(get_variables(file_buff, names->nextToken().c_str(), 2), 00095 USER, &user_tmp); 00096 users.push_back(user_tmp); 00097 } 00098 00099 parsed = true; 00100 delete names; 00101 return true; 00102 } 00103 00104 bool Configuration::set_structs(string variables, enum sect section, 00105 void *this_struct) 00106 { 00107 struct users_stru *usr_tmp = NULL; 00108 struct general_stru *gen_tmp = NULL; 00109 struct log_stru *log_tmp = NULL; 00110 vector<config_entry> this_entry; 00111 string for_log = "Configuration for "; 00112 00113 switch(section) 00114 { 00115 case USER: 00116 { 00117 usr_tmp = (static_cast<struct users_stru*>(this_struct)); 00118 00119 this_entry.push_back(config_entry( 00120 "id", CONF_STRING, &usr_tmp->id)); 00121 this_entry.push_back(config_entry( 00122 "sim_data", CONF_INT, &usr_tmp->sim_data)); 00123 this_entry.push_back(config_entry( 00124 "idle", CONF_TIME, &usr_tmp->idle)); 00125 this_entry.push_back(config_entry( 00126 "noftp", CONF_BOOL, &usr_tmp->noftp)); 00127 this_entry.push_back(config_entry( 00128 "only_home", CONF_BOOL, &usr_tmp->only_home)); 00129 this_entry.push_back(config_entry( 00130 "only_passive", CONF_BOOL, &usr_tmp->only_passive)); 00131 this_entry.push_back(config_entry( 00132 "user_limit", CONF_INT, &usr_tmp->user_limit)); 00133 #ifdef USE_TLS 00134 this_entry.push_back(config_entry( 00135 "encryption", CONF_ENC, &usr_tmp->encryption)); 00136 #endif // USE_TLS 00137 for_log += "user section "; 00138 } 00139 break; 00140 00141 case LOG: 00142 00143 { 00144 log_tmp = (static_cast<struct log_stru*>(this_struct)); 00145 00146 this_entry.push_back(config_entry( 00147 "log_level", CONF_INT, &log_tmp->log_level)); 00148 this_entry.push_back(config_entry( 00149 "xferlog", CONF_BOOL, &log_tmp->xferlog)); 00150 this_entry.push_back(config_entry( 00151 "xferlog_file", CONF_LOGF, &log_tmp->xferlog_file)); 00152 this_entry.push_back(config_entry( 00153 "authlog", CONF_LOGF, &log_tmp->authlog)); 00154 this_entry.push_back(config_entry( 00155 "infolog", CONF_LOGF, &log_tmp->infolog)); 00156 this_entry.push_back(config_entry( 00157 "debuglog", CONF_LOGF, &log_tmp->debuglog)); 00158 this_entry.push_back(config_entry( 00159 "thread_id", CONF_BOOL, &log_tmp->thread_id)); 00160 this_entry.push_back(config_entry( 00161 "time_format", CONF_TIMEF, &log_tmp->time_format)); 00162 for_log += "log section "; 00163 } 00164 break; 00165 00166 case GENERAL: 00167 { 00168 gen_tmp = (static_cast<struct general_stru*>(this_struct)); 00169 00170 this_entry.push_back(config_entry( 00171 "connection_limit", CONF_INT, &gen_tmp->connection_limit)); 00172 this_entry.push_back(config_entry( 00173 "port", CONF_INT, &gen_tmp->port)); 00174 this_entry.push_back(config_entry( 00175 "data_port", CONF_INT, &gen_tmp->data_port)); 00176 this_entry.push_back(config_entry( 00177 "no_daemon", CONF_BOOL, &gen_tmp->no_daemon)); 00178 this_entry.push_back(config_entry( 00179 "admin_email", CONF_STRING, &gen_tmp->admin_email)); 00180 this_entry.push_back(config_entry( 00181 "welcome_msg", CONF_STRING, &gen_tmp->welcome_msg)); 00182 this_entry.push_back(config_entry( 00183 "lifebeat_tick", CONF_TIME, &gen_tmp->lifebeat_tick)); 00184 this_entry.push_back(config_entry( 00185 "user", CONF_USER, NULL)); 00186 this_entry.push_back(config_entry( 00187 "pid_file", CONF_STRING, &gen_tmp->pid_file)); 00188 this_entry.push_back(config_entry( 00189 "readonly", CONF_BOOL, &gen_tmp->readonly)); 00190 this_entry.push_back(config_entry( 00191 "anonymous", CONF_ANON, &gen_tmp->anonymous)); 00192 #ifdef USE_TLS 00193 this_entry.push_back(config_entry( 00194 "tls_key", CONF_STRING, &gen_tmp->tls_key)); 00195 this_entry.push_back(config_entry( 00196 "tls_cert", CONF_STRING, &gen_tmp->tls_cert)); 00197 this_entry.push_back(config_entry( 00198 "tls_ca", CONF_STRING, &gen_tmp->tls_ca)); 00199 this_entry.push_back(config_entry( 00200 "tls_crl", CONF_STRING, &gen_tmp->tls_crl)); 00201 this_entry.push_back(config_entry( 00202 "dh_regenerate", CONF_TIME, &gen_tmp->dh_regenerate)); 00203 this_entry.push_back(config_entry( 00204 "verify_client", CONF_STRING, &gen_tmp->verify_client)); 00205 #endif // USE_TLS 00206 for_log += "general section "; 00207 } 00208 break; 00209 } 00210 class StringTokenizer *var = new StringTokenizer(variables, "\n", false); 00211 string id = var->nextToken(); 00212 if(section == USER) 00213 { 00214 *(string*)this_entry[0].variabel = id; 00215 for_log += "for " + id + " "; 00216 } 00217 00218 while(var->hasMoreTokens()) 00219 { 00220 string log_me, str = var->nextToken(); 00221 class StringTokenizer *st = new StringTokenizer(str, " ", false); 00222 string op = st->nextToken(); 00223 string arg = st->restTokens(); 00224 vector<config_entry>::iterator this_it = this_entry.begin(); 00225 00226 for(; this_it != this_entry.end(); this_it++) 00227 if(op == (*this_it).name) 00228 break; 00229 00230 if(this_it == this_entry.end()) 00231 log_me = "Parameter " + op + " doesn't exist, check your spelling."; 00232 else 00233 { 00234 switch((*this_it).type) 00235 { 00236 case CONF_TIME: 00237 case CONF_ANON: 00238 #ifdef USE_TLS 00239 case CONF_ENC: 00240 if((*this_it).type == CONF_ENC) 00241 { 00242 if(arg == "allow") 00243 arg = util.itos(ALLOW); 00244 else if(arg == "deny") 00245 arg = util.itos(DENY); 00246 else if(arg == "force") 00247 arg = util.itos(FORCE); 00248 else if(arg == "force_cert") 00249 arg = util.itos(FORCE_CERT); 00250 else 00251 log_me = "only allow, force, foce_cert and deny are legal " 00252 "parameters to encryption, " + arg + " isn't one of them."; 00253 } 00254 else 00255 #endif // USE_TLS 00256 if((*this_it).type == CONF_TIME) 00257 { 00258 int multi = 1; 00259 string::iterator st_it = arg.begin(); 00260 for(; st_it != arg.end(); st_it++) 00261 { 00262 if(isdigit(*st_it) || isspace(*st_it)) 00263 continue; 00264 else 00265 break; 00266 } 00267 if(st_it != arg.end()) 00268 { 00269 switch(*st_it) 00270 { 00271 case 's': 00272 multi = 1; 00273 break; 00274 case 'm': 00275 multi = 60; 00276 break; 00277 case 'h': 00278 multi = 3600; 00279 break; 00280 case 'd': 00281 multi = 86400; 00282 break; 00283 default: 00284 log_me = "I don't understand your time unit \'"; 00285 log_me += *st_it; 00286 log_me += "\' to paramater " + op + " I will ignore it."; 00287 } 00288 if(!log_me.empty()) 00289 break; 00290 00291 arg.erase(st_it, arg.end()); 00292 int temp_num = atoi(arg.c_str()) * multi; 00293 arg = util.itos(temp_num); 00294 } 00295 } 00296 else if((*this_it).type == CONF_ANON) 00297 { 00298 if(arg == "enable") 00299 arg = util.itos(A_ENABLE); 00300 else if(arg == "disable") 00301 arg = util.itos(A_DISABLE); 00302 else if(arg == "only") 00303 arg = util.itos(A_ONLY); 00304 else 00305 log_me = "only enable, disable and only are legal " 00306 "parameters to anonymous, " + arg + " isn't one of them."; 00307 } 00308 case CONF_INT: 00309 { 00310 int i_value = 0; 00311 bool error = false; 00312 for(string::iterator it = arg.begin(); it != arg.end(); it++) 00313 if(!isdigit(*it)) 00314 { 00315 error = true; 00316 break; 00317 } 00318 if(!error) 00319 i_value = atoi(arg.c_str()); 00320 else 00321 if(log_me.empty()) 00322 log_me = "Non int value supplied to " + op + 00323 " value supplied was " + arg + "."; 00324 00325 if(log_me.empty()) 00326 { 00327 *(static_cast<int*>((*this_it).variabel)) = i_value; 00328 for_log += (*this_it).name + "=" + util.itos(i_value) + "; "; 00329 } 00330 } 00331 break; 00332 00333 case CONF_BOOL: 00334 { 00335 bool b_value = true; 00336 if(arg == "true" || arg == "t" || arg == "1" || arg == "y") 00337 b_value = true; 00338 else if(arg == "false" || arg == "f" || arg == "0" || arg == "n") 00339 b_value = false; 00340 else 00341 log_me = arg + " not recognised as a boolean value supplied to " 00342 + op + "."; 00343 00344 if(log_me.empty()) 00345 { 00346 *(static_cast<bool*>((*this_it).variabel)) = b_value; 00347 for_log += (*this_it).name + (b_value ? "=true; " : "=false; "); 00348 } 00349 } 00350 break; 00351 00352 case CONF_USER: 00353 { 00354 struct passwd pwd, *pwd_ptr; 00355 struct group grp, *grp_ptr; 00356 char pwdata[PWBUF_SIZE], grpdata[PWBUF_SIZE]; 00357 int delim = arg.find_first_of(':'); 00358 getpwnam_r(arg.substr(0, delim).c_str(), 00359 &pwd, pwdata, sizeof(pwdata), &pwd_ptr); 00360 getgrnam_r(arg.substr(delim + 1).c_str(), 00361 &grp, grpdata, sizeof(grpdata), &grp_ptr); 00362 if(pwd_ptr == NULL) 00363 { 00364 log_me = "Tried to use nonexistent user " + arg.substr(0, 00365 delim) + " to run the daemon under. using root instead."; 00366 pwd.pw_uid = 0; 00367 grp.gr_gid = 0; 00368 } 00369 else if(grp_ptr == NULL) 00370 { 00371 log_me = "Tried to use nonexistent group " + arg.substr(delim +1) 00372 + " to run the daemon under. using root instead."; 00373 grp.gr_gid = 0; 00374 } 00375 general.user = pwd.pw_uid; 00376 general.group = grp.gr_gid; 00377 for_log += (*this_it).name + "=" + 00378 util.itos(pwd.pw_uid) + ":" + util.itos(grp.gr_gid) + "; "; 00379 } 00380 break; 00381 00382 case CONF_LOGF: 00383 if(arg == "syslog") 00384 arg.clear(); 00385 case CONF_TIMEF: 00386 if((*this_it).type == CONF_TIMEF) 00387 arg += " "; 00388 case CONF_STRING: 00389 *(static_cast<string*>((*this_it).variabel)) = arg; 00390 for_log += (*this_it).name + "=" + arg + "; "; 00391 break; 00392 00393 default: 00394 log_me = "Parameter " + op + " doesn't exist, check your spelling."; 00395 break; 00396 } 00397 } 00398 delete st; 00399 if(!log_me.empty()) 00400 logging->log_this(2, TYPE_INFO, log_me); 00401 } 00402 00403 if(this->general.anonymous >= A_ENABLE && this->general.user == 0) 00404 { 00405 this->general.anonymous = A_DISABLE; 00406 logging->log_this(2, TYPE_INFO, 00407 "you enabled anonymous logins but you don't have a configured user." 00408 " anonymous logins disabled."); 00409 } 00410 00411 logging->log_this(3, TYPE_INFO, for_log); 00412 delete var; 00413 return true; 00414 } 00415 00416 struct users_stru &Configuration::get_user(string name) 00417 { 00418 for(vector<users_stru>::iterator use_it = users.begin(); 00419 use_it != users.end(); use_it++) 00420 if((*use_it).id == name && name != "") 00421 return(*use_it); 00422 return general_user; 00423 } 00424 00425 int Configuration::count_users(vector<string> buff, string &users) 00426 { 00427 int ant = 0; 00428 int deep = 1, curr_deep = 0; 00429 string name = "user", section = ""; 00430 for(vector<string>::iterator buff_it = buff.begin(); buff_it != buff.end(); 00431 buff_it++) 00432 { 00433 if(*buff_it == "{") 00434 { 00435 if(curr_deep == deep && section == name) 00436 { 00437 ant++; 00438 users += *(buff_it - 1) + ","; 00439 } 00440 curr_deep++; 00441 if(curr_deep == deep) 00442 section = *(buff_it - 1); 00443 } 00444 else if(*buff_it == "}") 00445 { 00446 if(curr_deep == deep && section == name) 00447 return ant; 00448 curr_deep--; 00449 } 00450 } 00451 return ant; 00452 } 00453 00454 00455 string Configuration::get_variables(vector<string> &buff, const char *key, 00456 int deep) 00457 { 00458 int curr_deep = 0; 00459 string section, str; 00460 for(vector<string>::iterator buff_it = buff.begin(); buff_it != buff.end(); 00461 buff_it++) 00462 { 00463 if((*buff_it).empty()) 00464 continue; 00465 size_t comment = (*buff_it).find_first_of('#'); 00466 if(comment != string::npos) 00467 (*buff_it).erase(comment); 00468 if(*buff_it == "{") 00469 { 00470 section = *(buff_it - 1); 00471 curr_deep++; 00472 if(curr_deep == deep) 00473 str = section + "\n"; 00474 } 00475 else if(*buff_it == "}") 00476 { 00477 if(curr_deep == deep && section == key) 00478 break; 00479 curr_deep--; 00480 } 00481 else if(curr_deep == deep && section == key) 00482 str += *buff_it + "\n"; 00483 } 00484 return(str); 00485 } 00486 00487 bool Configuration::fill_buff(vector<string> &buff, string filename) 00488 { 00489 ifstream fin; 00490 fin.open(filename.c_str(), ifstream::in); 00491 00492 while(fin.good()) 00493 { 00494 string buffer; 00495 getline(fin, buffer); 00496 util.clean_string(buffer); 00497 if(!fin.good() || buffer.empty()) 00498 continue; 00499 buff.push_back(buffer); 00500 } 00501 00502 fin.close(); 00503 if(buff.empty()) 00504 return(false); 00505 else 00506 return(true); 00507 } 00508 00509 00510 bool Configuration::parse_command_line(int argc, char **argv) 00511 { 00512 int opt = 0; 00513 const char *cmdopts = "hc:nVl:p:P:"; 00514 00515 // struct for the long options... neat, huh? 00516 struct option longopts[] = 00517 { 00518 { "help", 0, 0, 'h' }, 00519 { "conf-file", 1, 0, 'c' }, 00520 { "no-daemon", 0, 0, 'd' }, 00521 { "version", 0, 0, 'V' }, 00522 { "log-level", 1, 0, 'l' }, 00523 { "port", 1, 0, 'p' }, 00524 { "pid_file", 1, 0, 'P' }, 00525 { 0, 0, 0, 0 } 00526 }; 00527 00528 while((opt = getopt_long(argc, argv, cmdopts, longopts, NULL)) != -1) 00529 { 00530 switch(opt) 00531 { 00532 case 'h': 00533 cout << "Usage: " << argv[0] << " [OPTION]...\n\n" 00534 << " -h, --help display this help and exit\n" 00535 << " -c, --conf-file=FILE supply another conf file instead " 00536 << "of the default\n" 00537 << " -n, --no-daemon do not fork into the background\n" 00538 << " -V, --version display version information and exit\n" 00539 << " -l, --log-level=LEVEL set the log level, 1-10, default is 5\n" 00540 << " -p, --port=PORT specify an alternate port\n" 00541 << " -P, --pid_file=FILE a file to write our pid in\n\n" 00542 << "Please, report bugs, either directly to the developers\n" 00543 << "or via sourceforge (http://www.sf.net/projects/babyftpd).\n"; 00544 00545 exit_baby(0, EXIT_EARLY); 00546 00547 case 'c': 00548 config->conf_file = optarg; 00549 break; 00550 00551 case 'n': 00552 config->general.no_daemon = true; 00553 break; 00554 00555 case 'V': 00556 cout << "BabyFTPd version " << VERSION << "\nBuilt on " << __DATE__ 00557 << " " << " " << __TIME__ << endl; 00558 exit_baby(0, EXIT_EARLY); 00559 00560 case 'l': 00561 // Log level code... 00562 if(atoi(optarg) > 10) 00563 logger.log_level = 10; 00564 else if(atoi(optarg) < 0) 00565 logger.log_level = 0; 00566 else 00567 logger.log_level = atoi(optarg); 00568 break; 00569 00570 case 'p': 00571 // alternate port... 00572 config->general.port = atoi(optarg); 00573 break; 00574 00575 case 'P': 00576 // pid_file 00577 config->general.pid_file = optarg; 00578 } 00579 } 00580 return true; 00581 } 00582 |
- Copyright © 2005, BabyFTPd
- Powered by: