/* #Specification: wuftpd / principles This module is the first done with the VIEWITEMS object.This object allows for two things -The best possible handling of comments (preserve comments written by the end user) -preserve the ordering of statement. In ftpaccess, they are not important. This is part of the better handling of comments above. -Allows for the management of some aspect of the configuration. This means that this module is not aware of all the features and keyword handled by wu-ftpd. So basically, the VIEWITEMS object is used to load the file and parse it somewhat. After that, various other object parse the lines in VIEWITEMS and maintain those lines. */ #include #include #include #include #include #include #include "wuftpd.h" #include "wuftpd.m" #include #include #include #include static const char subsys_ftpd[]="ftpd"; static HELP_FILE help_ftp ("wuftpd","wuftpd"); static HELP_FILE help_virtual ("wuftpd","virtual"); static HELP_FILE help_rootdir ("wuftpd","rootdir"); static LINUXCONF_SUBSYS subb (subsys_ftpd ,P_MSG_U(M_WUFTPD,"wu-ftpd server")); static CONFIG_FILE f_access ("/etc/ftpaccess" ,help_ftp,CONFIGF_MANAGED|CONFIGF_OPTIONAL ,"root","root",0600 ,subsys_ftpd); static CONFIG_FILE f_users ("/etc/ftpusers" ,help_ftp,CONFIGF_MANAGED|CONFIGF_OPTIONAL ,"root","root",0600 ,subsys_ftpd); static CONFIG_FILE f_groups ("/etc/ftpgroups" ,help_ftp,CONFIGF_MANAGED|CONFIGF_OPTIONAL ,"root","root",0600 ,subsys_ftpd); static CONFIG_FILE f_hosts ("/etc/ftphosts" ,help_ftp,CONFIGF_MANAGED|CONFIGF_OPTIONAL ,"root","root",0600 ,subsys_ftpd); PUBLIC VIRTUAL_FTP::VIRTUAL_FTP(const char *_addr) { root_it = logfile_it = banner_it = NULL; addr.setfrom (_addr); } PUBLIC VIRTUAL_FTP::VIRTUAL_FTP() { root_it = logfile_it = banner_it = NULL; } PUBLIC VIRTUAL_FTP *VIRTUAL_FTPS::getitem (int no) const { return (VIRTUAL_FTP*)ARRAY::getitem(no); } PUBLIC VIRTUAL_FTPS::VIRTUAL_FTPS() { } PUBLIC void VIRTUAL_FTP::updateone ( const SSTRING &val, const char *keyw, VIEWITEM *&item, VIEWITEMS &items) { if (val.is_empty() || addr.is_empty()){ items.remove_del (item); }else{ char buf[2*PATH_MAX]; snprintf (buf,sizeof(buf),"virtual %s %s %s",addr.get(),keyw ,val.get()); if (item == NULL){ item = new VIEWITEM(buf); items.add (item); }else{ item->line.setfrom (buf); } } } /* Update the VIEWITEMs of the ftpaccess config file with the value of the VIRTUAL_FTP definitions */ PUBLIC void VIRTUAL_FTPS::update (VIEWITEMS &items) { for (int i=0; iupdateone (f->root,"root",f->root_it,items); f->updateone (f->logfile,"logfile",f->logfile_it,items); f->updateone (f->banner,"banner",f->banner_it,items); if (f->addr.is_empty()){ remove_del (f); i--; } } } PUBLIC FTP_CTRL::FTP_CTRL() { setall (0); } PUBLIC void FTP_CTRL::setall (char val) { f_compress = val; f_tar = val; f_chmod = val; f_delete = val; f_overwrite = val; f_rename = val; f_log_inbound = val; f_log_outbound = val; } /* Grab one value for a line of a VIEWITEMS object */ PRIVATE void WUFTPD::load (SSTRING &val, VIEWITEMS &items, const char *key) { VIEWITEM *it = items.locate (key); if (it != NULL){ const char *pt = it->line.get(); pt = str_skip(pt); pt += strlen(key); pt = str_skip (pt); val.setfrom (pt); } } /* Grab one value for a line of a VIEWITEMS object */ PRIVATE void WUFTPD::load ( int &val, int defval, VIEWITEMS &items, const char *key1, const char *key2) { val = defval; VIEWITEM *it = items.locate (key1,key2); if (it != NULL){ const char *pt = it->line.get(); pt = str_skip(pt); pt += strlen(key1); pt = str_skip (pt); pt += strlen(key2); pt = str_skip (pt); val = atoi(pt); } } /* Update the VIEWITEMS object with a new value for a setting */ PRIVATE void WUFTPD::store (SSTRING &val, VIEWITEMS &items, const char *key) { VIEWITEM *it = items.locate (key); if (val.is_empty()){ if (it != NULL) items.remove_del (it); }else{ char buf[strlen(key)+val.getlen()+2]; sprintf (buf,"%s %s",key,val.get()); if (it != NULL){ it->line.setfrom (buf); }else{ items.add (new VIEWITEM(buf)); } } } /* Update the VIEWITEMS object with a new value for a setting */ PRIVATE void WUFTPD::store ( int val, int defval, VIEWITEMS &items, const char *key1, const char *key2) { VIEWITEM *it = items.locate (key1,key2); if (val == defval){ if (it != NULL) items.remove_del (it); }else{ char buf[strlen(key1)+1+strlen(key2)+10+2]; sprintf (buf,"%s %s %d",key1,key2,val); if (it != NULL){ it->line.setfrom (buf); }else{ items.add (new VIEWITEM(buf)); } } } /* Parse a line like: rename yes guest,anonymous */ PRIVATE void WUFTPD::setflags( const char *yes, const char *members, char &f_guest, char &f_real, char &f_anonymous) { char val = strcmp(yes,"yes")==0; char tb[3][100]; int nb = str_splitline (members,',',tb,3); for (int i=0; iline.get(),sizeof(p1)); pt = str_copyword (p2,pt,sizeof(p2)); pt = str_copyword (p3,pt,sizeof(p3)); pt = str_copyword (p4,pt,sizeof(p4)); if (strcmp(p1,"virtual")==0){ VIRTUAL_FTP *ftp = NULL; for (int j=0; jaddr.cmp(p2)==0){ ftp = f; break; } } if (ftp == NULL){ ftp = new VIRTUAL_FTP (p2); virtuals.add (ftp); } if (strcmp(p3,"root")==0){ ftp->root.setfrom (p4); ftp->root_it = it; }else if (strcmp(p3,"logfile")==0){ ftp->logfile.setfrom (p4); ftp->logfile_it = it; }else if (strcmp(p3,"banner")==0){ ftp->banner.setfrom (p4); ftp->banner_it = it; } }else if (strcmp(p1,"tar")==0){ setflags (p2,p3,guest.f_tar,rreal.f_tar,anonymous.f_tar); }else if (strcmp(p1,"compress")==0){ setflags (p2,p3,guest.f_compress,rreal.f_compress,anonymous.f_compress); }else if (strcmp(p1,"chmod") == 0){ setflags (p2,p3,guest.f_chmod,rreal.f_chmod,anonymous.f_chmod); }else if (strcmp(p1,"delete") == 0){ setflags (p2,p3,guest.f_delete,rreal.f_delete,anonymous.f_delete); }else if (strcmp(p1,"overwrite") == 0){ setflags (p2,p3,guest.f_overwrite,rreal.f_overwrite,anonymous.f_overwrite); }else if (strcmp(p1,"rename") == 0){ setflags (p2,p3,guest.f_rename,rreal.f_rename,anonymous.f_rename); }else if (strcmp(p1,"log") == 0){ if (strcmp(p2,"transfers")==0){ settransfers(p3,p4); } } } } PUBLIC int WUFTPD::write() { int ret = -1; if (access.write (f_access,NULL) != -1 && users.write (f_users,NULL) != -1 && groups.write (f_groups,NULL) != -1 && hosts.write (f_hosts,NULL) != -1){ ret = 0; } return ret; } static void setupaccess (DIALOG &dia, FTP_CTRL &ctrl) { dia.newf_chk ("",ctrl.f_compress,MSG_U(I_COMPRESS,"May request compress files")); dia.newf_chk ("",ctrl.f_tar,MSG_U(I_TAR,"May request tar files")); dia.newf_chk ("",ctrl.f_chmod,MSG_U(I_CHMOD,"May chmod files")); dia.newf_chk ("",ctrl.f_delete,MSG_U(I_DELETE,"May delete files")); dia.newf_chk ("",ctrl.f_overwrite,MSG_U(I_OVERWRITE,"May overwrite files")); dia.newf_chk ("",ctrl.f_rename,MSG_U(T_RENAME,"May rename files")); dia.newf_chk ("",ctrl.f_log_inbound,MSG_U(I_LOGINBOUND,"Log inbound transfers")); dia.newf_chk ("",ctrl.f_log_outbound,MSG_U(I_LOGOUTBOUND,"Log outbound transfers")); } PRIVATE VIEWITEM *WUFTPD::locate_alloc (const char *key1, const char *key2) { VIEWITEM *ret = access.locate (key1,key2); if (ret == NULL){ ret = new VIEWITEM(""); access.add (ret); } return ret; } static char *msgclient[]={"guest","real","anonymous"}; PRIVATE void WUFTPD::updatectrl ( const char *oper, char f_guest, char f_real, char f_anonymous) { char bufno[200],bufyes[200]; sprintf (bufno,"%-15s\tno\t",oper); sprintf (bufyes,"%-15s\tyes\t",oper); char tb[3]={f_guest,f_real,f_anonymous}; bool vir_no = false; bool vir_yes = false; for (int i=0; i<3; i++){ const char *m = msgclient[i]; if (tb[i]){ if (vir_yes) strcat (bufyes,","); strcat (bufyes,m); vir_yes = true; }else{ if (vir_no) strcat (bufno,","); strcat (bufno,m); vir_no = true; } } VIEWITEM *it = locate_alloc (oper,"yes"); if (vir_yes){ it->line.setfrom (bufyes); }else{ access.remove_del (it); } it = locate_alloc (oper,"no"); if (vir_no){ it->line.setfrom (bufno); }else{ access.remove_del (it); } } PRIVATE void WUFTPD::updatetrans () { char tb[3]={guest.f_log_inbound*2+guest.f_log_outbound ,rreal.f_log_inbound*2+rreal.f_log_outbound ,anonymous.f_log_inbound*2+anonymous.f_log_outbound}; // We try to generate "log transfers" statement // which group together the same type of traffics VIEWITEMS tbit; access.locate ("log","transfers",tbit); bool tbmark[tbit.getnb()]; memset (tbmark,0,sizeof(tbmark)); for (int j=0; j<3; j++){ char type = j+1; char buf[200]; strcpy (buf,"log transfers\t"); bool one = false; for (int i=0; i<3; i++){ if (tb[i] == type){ if (one) strcat (buf,","); strcat (buf,msgclient[i]); one = true; } } strcat (buf,"\t"); static char *tbdir[]={"outbound","inbound","inbound,outbound"}; strcat (buf,tbdir[j]); int k; for (k=0; kline.get(); char p1[PATH_MAX],p2[PATH_MAX],p3[PATH_MAX],p4[PATH_MAX]; line = str_copyword (p1,line,sizeof(p1)); line = str_copyword (p2,line,sizeof(p2)); line = str_copyword (p3,line,sizeof(p3)); line = str_copyword (p4,line,sizeof(p4)); if (strcmp(tbdir[j],p4)==0){ tbmark[k] = true; if (one){ it->line.setfrom (buf); }else{ access.remove_del (it); } break; } } if (k==tbit.getnb() && one){ access.add (new VIEWITEM (buf)); } } for (int d=0; dget(); if (getgrnam(g)==NULL){ err.appendf (MSG_U(E_NOGROUP,"Group %s does not exist\n"),g); } } int ret = 0; if (err.is_filled()){ xconf_error ("%s",err.get()); ret = -1; } return ret; } PUBLIC void WUFTPD::editaccess() { DIALOG dia; dia.newf_title ("",MSG_U(T_BASE,"Base configuration")); SSTRING email,guestgroup,banner,shutdown; load (email,access,K_EMAIL); load (guestgroup,access,K_GUESTGROUP); load (banner,access,K_BANNER); load (shutdown,access,K_SHUTDOWN); VIEWITEM *defprivate = access.locate (K_DEFAULTSERVER,K_PRIVATE); char allow_anonymous = defprivate == NULL ? 1 : 0; dia.newf_title (MSG_U(N_MISC,"Misc"),1,"",MSG_U(T_MISC,"Misc.")); dia.newf_str (MSG_U(T_EMAIL,"Email of admin"),email); int field_guestgroup = dia.getnb(); dia.newf_str (MSG_U(T_GUESTGROUP,"Guest groups"),guestgroup); dia.newf_str (MSG_U(T_BANNER,"Banner file"),banner); dia.newf_str (MSG_U(T_SHUTDOWN,"Shutdown message"),shutdown); dia.newf_chk ("",allow_anonymous,MSG_U(I_ALLOWANON,"Allow anonymous access")); dia.newf_title (MSG_U(N_ACCESSCTRL,"Control"),1,"",MSG_U(T_ACCESSCTRL,"Access controls")); dia.newf_title (MSG_U(T_REALUSERS,"Real users"),2,"",MSG_R(T_REALUSERS)); setupaccess (dia,rreal); dia.newf_title (MSG_U(T_GUESTUSERS,"Guest users"),2,"",MSG_R(T_GUESTUSERS)); setupaccess (dia,guest); dia.newf_title (MSG_U(T_ANONYMOUS,"Anonymous"),2,"",MSG_R(T_ANONYMOUS)); setupaccess (dia,anonymous); dia.newf_title (MSG_U(N_TIMEOUTS,"Timeouts"),1,"",MSG_U(T_TIMEOUTS,"Various timeouts")); int accept,connect,data,idle,maxidle,ident; load (accept,120,access,K_TIMEOUT,K_ACCEPT); load (connect,120,access,K_TIMEOUT,K_CONNECT); load (data,1200,access,K_TIMEOUT,K_DATA); load (idle,900,access,K_TIMEOUT,K_IDLE); load (maxidle,1200,access,K_TIMEOUT,K_MAXIDLE); load (ident,10,access,K_TIMEOUT,K_IDENT); static const char *tbdef[]={MSG_U(I_DEFAULT,"Default"),NULL}; static const int tb120[]={120}; static const int tb1200[]={1200}; static const int tb900[]={900}; static const int tb10[]={10}; dia.newf_chkm_num (MSG_U(F_ACCEPT,"Accept connection"),accept,120 ,tb120,tbdef); dia.newf_chkm_num (MSG_U(F_CONNECT,"Port connection"),connect,120 ,tb120,tbdef); dia.newf_chkm_num (MSG_U(F_DATA,"Data transfer"),data,1200 ,tb1200,tbdef); dia.newf_chkm_num (MSG_U(F_IDLE,"Default idle time"),idle,900 ,tb900,tbdef); dia.newf_chkm_num (MSG_U(F_MAXIDLE,"Maximum idle time"),maxidle,1200 ,tb1200,tbdef); dia.newf_chkm_num (MSG_U(F_IDENT,"Auth/Ident"),ident,10 ,tb10,tbdef); int nof = 0; dia.setbutinfo (MENU_USR1,MSG_U(B_EDITBANNER,"Edit banner"),MSG_R(B_EDITBANNER)); while (1){ MENU_STATUS code = dia.edit (MSG_U(T_FTPBASECONF ,"Ftp server configuration") ,"" ,help_ftp ,nof,MENUBUT_CANCEL|MENUBUT_ACCEPT|MENUBUT_USR1); if (code == MENU_CANCEL || code == MENU_ESCAPE){ break; }else if (code == MENU_USR1){ dia.save(); if (banner.is_empty()){ xconf_error (MSG_U(E_NOBANNERFILE ,"You must fill the field \"Banner file\"")); }else if (perm_rootaccess(MSG_U(P_EDITBANNER ,"edit the banner file"))){ wuftpd_editbanner (banner.get()); } }else if (wuftpd_checkgroup(guestgroup.get())==-1){ nof = field_guestgroup; }else{ store (email,access,K_EMAIL); store (guestgroup,access,K_GUESTGROUP); store (banner,access,K_BANNER); store (shutdown,access,K_SHUTDOWN); store (accept,120,access,K_TIMEOUT,K_ACCEPT); store (connect,120,access,K_TIMEOUT,K_CONNECT); store (data,1200,access,K_TIMEOUT,K_DATA); store (idle,900,access,K_TIMEOUT,K_IDLE); store (maxidle,1200,access,K_TIMEOUT,K_MAXIDLE); store (ident,10,access,K_TIMEOUT,K_IDENT); if (allow_anonymous){ access.remove_del(defprivate); }else if (defprivate == NULL){ access.add (new VIEWITEM("defaultserver private")); } //store (defaultserver,access,"defaultserver"); updatectrl ("tar",guest.f_tar,rreal.f_tar,anonymous.f_tar); updatectrl ("compress",guest.f_compress,rreal.f_compress,anonymous.f_compress); updatectrl ("chmod",guest.f_chmod,rreal.f_chmod,anonymous.f_chmod); updatectrl ("delete",guest.f_delete,rreal.f_delete,anonymous.f_delete); updatectrl ("overwrite",guest.f_overwrite,rreal.f_overwrite,anonymous.f_overwrite); updatectrl ("rename",guest.f_rename,rreal.f_rename,anonymous.f_rename); updatetrans (); write(); break; } } } static int cmp_by_addr (const ARRAY_OBJ *o1, const ARRAY_OBJ *o2) { VIRTUAL_FTP *f1 = (VIRTUAL_FTP*)o1; VIRTUAL_FTP *f2 = (VIRTUAL_FTP*)o2; return f1->addr.cmp(f2->addr); } PUBLIC int VIRTUAL_FTP::createroot() { const char *home_ftp = configf_lookuppath("/var/ftp"); char buf[1000]; snprintf (buf,1000,"%s %s" ,home_ftp ,root.get()); int ret = -1; if (file_type (home_ftp)!=1){ xconf_error (MSG_U(E_NOANONYMOUSDIR ,"There is no anonymous ftp environment installed on this system\n" "Linuxconf is using it as a template\n" "to setup the virtual host anonymous directory\n" "\n" "Linuxconf expects the anonymous ftp directory in\n" "%s"),home_ftp); }else{ ret = netconf_system_if ("install-anom-ftp.sh",buf); } return ret; } PUBLIC int VIRTUAL_FTP::edit() { DIALOG dia; dia.newf_str (MSG_U(F_ADDR,"Virtual host"),addr); dia.last_noempty(); dia.newf_str (MSG_U(F_ROOT,"Archive path"),root); dia.last_noempty(); dia.newf_str (MSG_U(F_BANNER,"Banner message file"),banner); dia.newf_str (MSG_U(F_LOGFILE,"Log file"),logfile); dia.delwhat (MSG_U(I_DELREC,"Select [Del] to delete this record")); int nof = 0; int ret = -1; while (1){ MENU_STATUS code = dia.edit (MSG_U(T_ONEVIRTUAL ,"One virtual ftp host") ,"" ,help_virtual ,nof,MENUBUT_DEL|MENUBUT_ACCEPT|MENUBUT_CANCEL); if (code == MENU_ESCAPE || code == MENU_CANCEL){ break; }else if (code == MENU_DEL){ if (xconf_delok()){ ret = 1; break; } }else{ if (!file_exist(root.get())){ char buf[1000]; snprintf (buf,1000,MSG_U(I_NEWROOTDIR ,"The directory for anonymous ftp does not exist.\n" "Do you want to create the directory %s\n" "and initialise it so it become suitable for\n" "anonymous ftp operation ?") ,root.get()); if(dialog_yesno (MSG_U(T_NEWROOTDIR,"Create root dir") ,buf ,help_rootdir)==MENU_YES){ createroot(); } } ret = 0; break; } } return ret; } PUBLIC void VIRTUAL_FTPS::edit (VIEWITEMS &items) { DIALOG_LISTE dia; dia.newf_head ("",MSG_U(H_VIRTUALS,"Address\tRoot dir")); int nof = 0; VIRTUAL_FTPS sorted; sorted.neverdelete(); while (1){ sorted.remove_all(); for (int i=0; iaddr.get(),f->root.get()); } dia.remove_last (getnb()+1); MENU_STATUS code = dia.editmenu( MSG_U(T_VIRTUALS,"Virtual hosts") ,MSG_U(I_ADMINTREES ,"Here are the various virtual hosts for anonymous ftp.") ,help_virtual ,nof,MENUBUT_ADD); bool must_update = false; if (code == MENU_QUIT || code == MENU_ESCAPE){ break; }else if (code == MENU_ADD){ VIRTUAL_FTP *f = new VIRTUAL_FTP(""); int code = f->edit(); if (code == 0){ add (f); must_update = true; }else{ delete f; } }else if (nof >=0 && nof < getnb()){ VIRTUAL_FTP *f = sorted.getitem(nof); int code = f->edit(); if (code != -1){ must_update = true; if (code == 1) f->addr.setfrom (""); } } if (must_update){ update (items); items.write (f_access,NULL); } } } PUBLIC void WUFTPD::editvirtuals() { virtuals.edit (access); } void wuftpd_edit() { const char *basic = MSG_U(M_BASIC,"Basic configuration"); const char *virtuals = MSG_U(M_VIRTUAL,"Virtual hosts"); const char *logs = MSG_U(M_LOGS,"Transfer logs"); const char *menuopt[]={ "", basic, "", virtuals, "", logs, NULL }; DIALOG_MENU dia; dia.new_menuitems (menuopt); int choice=0; while (1){ MENU_STATUS code = dia.editmenu ( MSG_U(T_FTPCONF,"Wu-ftpd configurator") ,"" ,help_ftp ,choice,0); if (code == MENU_QUIT || code == MENU_ESCAPE){ break; }else{ WUFTPD wu; const char *key = menuopt[choice*2+1]; if (key == basic){ wu.editaccess(); }else if (key == virtuals){ wu.editvirtuals(); }else if (key == logs){ if (perm_rootaccess(MSG_U(P_VIEWLOG,"view transfer logs"))){ wu.viewlogs(); } } } } } class FIXPERM_FTPD: public FIXPERM_TASK{ /*~PROTOBEG~ FIXPERM_FTPD */ private: int check (bool, bool); void list (FIXPERM_SPECS&specs); public: /*~PROTOEND~ FIXPERM_FTPD */ }; static FIXPERM_FTPD perm; int FIXPERM_FTPD::check (bool , bool ) { WUFTPD wu; FIXPERM_SPECS specs; wu.listperm (specs); return specs.check(); } PUBLIC void WUFTPD::listperm (FIXPERM_SPECS &specs) { for (int i=0; iroot.is_empty()){ char buf[2*PATH_MAX]; snprintf (buf,sizeof(buf),"%s root root d 755",f->root.get()); specs.addline (buf); } } } void FIXPERM_FTPD::list (FIXPERM_SPECS &specs) { WUFTPD wu; wu.listperm(specs); } /* Locate the virtual host definition. Return NULL if not found */ PUBLIC VIRTUAL_FTP *WUFTPD::locatevhost (const char *vhost) { VIRTUAL_FTP *ret = NULL; for (int i=0; iaddr.cmp(vhost)==0){ ret = f; break; } } return ret; } class VIRTUAL_COMNG: public USERACCT_COMNG{ VIRTUAL_FTP v; /*~PROTOBEG~ VIRTUAL_COMNG */ public: VIRTUAL_COMNG (DICTIONARY&_dict); int deluser (PRIVILEGE *); int save (PRIVILEGE *priv); void setupdia (DIALOG&dia); int validate (DIALOG&, int &nof); /*~PROTOEND~ VIRTUAL_COMNG */ }; PUBLIC VIRTUAL_COMNG::VIRTUAL_COMNG( DICTIONARY &_dict) : USERACCT_COMNG (_dict) { } PUBLIC void VIRTUAL_COMNG::setupdia ( DIALOG &dia) { dia.newf_title (MSG_U(T_MVIRTUAL,"ftp"),1 ,"",MSG_R(T_MVIRTUAL)); dia.newf_str (MSG_R(F_ROOT),v.root); dia.newf_str (MSG_R(F_BANNER),v.banner); dia.newf_str (MSG_R(F_LOGFILE),v.logfile); } PUBLIC int VIRTUAL_COMNG::save( PRIVILEGE *priv) { int ret = 0; char vhost[PATH_MAX]; snprintf (vhost,sizeof(vhost)-1,"ftp.%s",dict.get_str ("vdomain")); return ret; } PUBLIC int VIRTUAL_COMNG::validate( DIALOG &, int &nof) { int ret = 0; char vhost[PATH_MAX]; snprintf (vhost,sizeof(vhost)-1,"ftp.%s",dict.get_str ("vdomain")); WUFTPD wu; // Check if this vhost is already there if (wu.locatevhost (vhost)!=NULL){ xconf_error (MSG_U(E_VHOSTEXIST,"Virtual host %s\n" "is already configured\n" "for the service ftp"),vhost); ret = -1; } return ret; } PUBLIC int VIRTUAL_COMNG::deluser ( PRIVILEGE *) { return 0; } static USERACCT_COMNG *wuftpd_newcomng( const char *key, DICTIONARY &dict) { USERACCT_COMNG *ret = NULL; if (strcmp(key,"virtual")==0){ ret = new VIRTUAL_COMNG (dict); } return ret; } static REGISTER_USERACCT_COMNG xxx (wuftpd_newcomng); PUBLIC void WUFTPD::del (VIRTUAL_FTP *v) { v->addr.setfrom (""); virtuals.update (access); virtuals.remove_del (v); } int wuftpd_del (const char *host) { int ret = -1; WUFTPD conf; VIRTUAL_FTP *v = conf.locatevhost (host); if (v != NULL){ conf.del (v); ret = conf.write(); }else{ fprintf (stderr,MSG_U(E_MISSING ,"Wuftpd: Virtual host %s is not configured\n") ,host); } return ret; } PUBLIC void WUFTPD::add (VIRTUAL_FTP *v) { virtuals.add (v); virtuals.update (access); } /* Add a virtual host from the command line */ int wuftpd_add (const char *host, int argc, const char *argv[]) { int ret = -1; WUFTPD conf; VIRTUAL_FTP *v = conf.locatevhost (host); if (v != NULL){ fprintf (stderr,MSG_U(E_HOSTEXIST ,"Wuftpd: Virtual host %s already configured\n") ,host); }else{ v = new VIRTUAL_FTP; v->addr.setfrom (host); bool err = false; for (int i=0; iroot.setfrom (arg); i++; }else{ fprintf (stderr,MSG_U(E_IVLDOPT,"Invalid option %s\n"),opt); err = true; } } if (v->root.is_empty()){ fprintf (stderr,MSG_U(E_NOROOT,"Wuftpd: no root specified\n")); }else if (!err){ conf.add (v); v->createroot(); ret = conf.write(); } } return ret; }