/* #Specification: mail / virtual domain / concept The concept of the virtual email domain is simply the same as for virtual web hosting. From the outside, one computer looks like several independant one. In the same way as a Web server may deliver different page depending on the virtual host being queried, the virtual email server will have the following: # -One independant user list per virtual email domain These users are really "virtual". The only service they can get is the POP service (need a special POP server) -One independant folder directory -Potentially an independant aliases file. -Potentially (one day) a special hierarchy for HOME directory which may be used by a virtualised IMAP server. # To make it simple, if you define two virtual email domain, say virtual1.com and virtual2.com, then joe@virtual1.com and joe@virtual2.com are two different users with different password and different messages. */ #include #include #include #include #include #include #include #include #include #include #include "internal.h" #include "mailconf.h" #include "mailconf.m" #include "../paths.h" #include #include "alias.h" #include "../module_apis/useracct_apidef.h" static const char K_VOTHER[]= "vdomain_other"; static const char K_VALIAS[]= "vdomain_alias"; static const char K_FALLBACK[]="vdomain_fallback"; static const char K_STARTUID[]="vdomain_startuid"; static const char K_FILTER[]="vdomain_filter"; static const char K_QUOTA[]="vdomain_quota"; static const char K_MAXUSERS[]="vdomain_maxusers"; static const char K_COADMIN[]="vdomain_coadmin"; static const char K_GECOS[]="vdomain_gecos"; static const char K_ACCEPTLOCK[]="vdomain_acceptlock"; static const char K_RETRIEVELOCK[]="vdomain_retrievelock"; static const char K_PASSWORD[]="vdomain_passwd"; static MAILCONF_HELP_FILE help_vdomain("vdomain"); static const char subsys_vdomain[]="vdomain"; static PRIVILEGE alldom_priv ("allvdomains" ,P_MSG_U(F_ALLVDOMAINS,"Manage all virtual email domains") ,P_MSG_R(T_VIRTDOMPRIV)); PUBLIC VDOMAIN::VDOMAIN(const char *line) { domain.copyword (line); oldname.copyword (line); const char *dom = domain.get(); linuxconf_getall (K_VALIAS,dom,faliases,1); linuxconf_getall (K_VOTHER,dom,others,1); fallback.setfrom(linuxconf_getval (K_FALLBACK,dom)); startuid = linuxconf_getvalnum (K_STARTUID,dom,60000); quota = linuxconf_getvalnum (K_QUOTA,dom,0); maxusers = linuxconf_getvalnum (K_MAXUSERS,dom,0); match_gecos = linuxconf_getvalnum (K_GECOS,dom,0); filter.setfrom (linuxconf_getval(K_FILTER,dom,"")); acceptlock = linuxconf_getvalnum (K_ACCEPTLOCK,dom,0); retrievelock = linuxconf_getvalnum (K_RETRIEVELOCK,dom,0); coadmin = linuxconf_getvalnum (K_COADMIN,dom,0); password.setfrom (linuxconf_getval(K_PASSWORD,dom)); } PUBLIC VDOMAIN::VDOMAIN() { /* #Specification: virtual domain / users / uid UID for users of virtual email domain are receiving UIDs starting at 60000 to avoid clash with normal users. This is done so a normal user won't be able to read the folder of any virtual users. This is the default. This is configurable. For technical reason and compatibility with barely modified POP server, the spool directory for virtual account is world readable. */ startuid = 60000; quota = 0; match_gecos = 0; acceptlock = 0; retrievelock = 0; maxusers = 0; coadmin = 0; } static char tbkey[10]; // Just to assign unique addresses PRIVATE void VDOMAIN::showother (DIALOG &dia, int item) { SSTRING *s = others.getitem(item); dia.newf_str ("",*s); dia.set_registry_key (tbkey+item); } PRIVATE void VDOMAIN::addother (DIALOG &dia) { int item = others.getnb(); others.add (new SSTRING); showother (dia,item); } static char faliases2_key; struct VDOMAIN_DIAINFO { SSTRING pass1,pass2; int field_pass; int field_alias; PRIVATE_MESSAGE grow; }; PUBLIC void VDOMAIN::setupdia ( DIALOG &dia, struct VDOMAIN_DIAINFO &info) { dia.newf_title (MSG_U(T_DBASIC,"Base info"),1,"",MSG_R(T_DBASIC)); dia.newf_str (MSG_U(F_FALLBACK,"Fallback destination (opt)"),fallback); dia.newf_num (MSG_U(F_STARTUID,"Allocate UID from"),startuid); static const char *tb[]={MSG_R(I_NOLIMIT),NULL}; static const int tbv[]={0,0}; dia.newf_chkm_num (MSG_U(F_QUOTA,"Limit user inbox to (k)"),quota,tbv,tb); static const char *tb2[]={MSG_R(I_NOLIMIT),NULL}; static const int tbv2[]={0,0}; dia.newf_title (MSG_R(T_FEATURES),1,"",MSG_R(T_FEATURES)); dia.newf_chkm_num (MSG_U(F_MAXUSERS,"Maximum number of users"),maxusers,tbv2,tb2); dia.newf_chk ("",match_gecos,MSG_U(F_GECOS,"Match user full name")); dia.newf_str (MSG_U(F_VFILTER,"Filter program + args (opt)"),filter); dia.newf_chk (MSG_U(F_LOCKDOMAIN,"Lock domain"),acceptlock,MSG_U(I_LOCKACCEPT,"Incoming mail are rejected")); dia.newf_chk ("",retrievelock,MSG_U(I_LOCKRETRIEVE,"Users can't retrieve their messages")); dia.newf_title (MSG_U(T_COADMIN,"Administrator"),1,"",MSG_R(T_COADMIN)); vacation_drawinfo (dia,MSG_U(I_COADMIN ,"A pseudo-user admin@this-domain\n" "may administer the vdomain.\n" "It works only in WEB mode")); dia.newf_chk (MSG_U(F_ENACOADMIN,"Enable"),coadmin,MSG_U(I_ENACOADMIN,"co-administration")); vacation_drawinfo (dia,MSG_U(I_COADMINPASS ,"Enter the co-administrator password\n" "or leave it blank to keep the previous one")); info.field_pass = dia.getnb(); dia.newf_pass (MSG_U(F_COADMINPASS1,"Co-administrator password"),info.pass1); dia.newf_pass (MSG_U(F_COADMINPASS2,"(Re-enter password)"),info.pass2); dia.newf_title (MSG_U(T_ALIASFILES,"Extra aliases files"),1,"",MSG_R(T_ALIASFILES)); while (faliases.getnb()<2) faliases.add (new SSTRING); dia.newf_str (MSG_U(T_FILEPATH,"File path (absolute)"),*faliases.getitem(0)); dia.newf_str ("",*faliases.getitem(1)); dia.set_registry_key (&faliases2_key); dia.newf_title (MSG_U(T_DALIAS,"Domain Aliases"),1,"",MSG_R(T_DALIAS)); info.field_alias = dia.getnb(); for (int i=0; ito_lower(); } // Check if we are doing a rename if (!oldname.is_empty() && domain.cmp(oldname)!=0){ renameold (VHOME "/"); renameold (VAR_SPOOL_VMAIL "/"); renameold (ETC_VMAIL "/aliases."); renameold (ETC_VMAIL "/passwd."); renameold (ETC_VMAIL "/shadow."); vdomain_cleanupconf (oldname.get()); oldname.setfrom (domain); } ret = 0; break; } } } } } if (ret != 0) dia.restore(); return ret; } /* Format different path associated with the domain */ PUBLIC void VDOMAIN::setpwdpaths ( char *pwdfile, // password file char *shadowfile, // shadow passwords char *pathhome, // directory holding homes char *root) // root of the domain { const char *dom = domain.get(); snprintf (pwdfile,PATH_MAX-1,ETC_VMAIL "/passwd.%s",dom); snprintf (shadowfile,PATH_MAX-1,ETC_VMAIL "/shadow.%s",dom); snprintf (root,PATH_MAX-1,"%s/%s",VHOME,dom); snprintf (pathhome,PATH_MAX-1,"%s/home",root); } static int cmp_by_domain (const ARRAY_OBJ *o1, const ARRAY_OBJ *o2) { VDOMAIN *d1 = (VDOMAIN*)o1; VDOMAIN *d2 = (VDOMAIN*)o2; return d1->domain.cmp(d2->domain.get()); } PUBLIC void VDOMAINS::sort() { ARRAY::sort (cmp_by_domain); } PUBLIC VDOMAIN *VDOMAINS::getitem (int no) const { return (VDOMAIN*)ARRAY::getitem(no); } PUBLIC VDOMAIN *VDOMAINS::getitem (const char *domain) const { VDOMAIN *ret = NULL; int n = getnb(); for (int i=0; idomain.cmp(domain)==0){ ret = d; break; } } return ret; } static const char K_VDOMAIN[] = "vdomain"; static const char K_INDEX[] = "index"; PUBLIC VDOMAINS::VDOMAINS() { SSTRINGS tb; linuxconf_getall (K_VDOMAIN,K_INDEX,tb,0); int n = tb.getnb(); for (int i=0; iget())); } } /* Verifie if a domain is either a virtual email domain or an alias for one of the virtual email domain Return the VDOMAIN record or NULL if not found */ PUBLIC VDOMAIN *VDOMAINS::lookup (const char *str) const { VDOMAIN *ret = NULL; for (int i=0; idomain.cmp(str)==0){ ret = d; break; }else{ for (int o=0; oothers.getnb(); o++){ SSTRING *s = d->others.getitem(o); if (s->cmp(str)==0){ ret = d; break; } } if (ret != NULL) break; } } return ret; } PUBLIC int VDOMAINS::lookup (VDOMAIN *pt) const { return ARRAY::lookup(pt); } PUBLIC int VDOMAINS::write () { linuxconf_setcursys (subsys_vdomain); linuxconf_removeall (K_VDOMAIN,K_INDEX); int n = getnb(); for (int i=0; idomain.get(); vdomain_cleanupconf (dom); linuxconf_add (K_VDOMAIN,K_INDEX,dom); linuxconf_replace (K_VOTHER,dom,d->others); linuxconf_replace (K_VALIAS,dom,d->faliases); // avoid saving default values in conf.linuxconf if (d->startuid != 60000){ linuxconf_replace (K_STARTUID,dom,d->startuid); } if (d->quota != 0){ linuxconf_replace (K_QUOTA,dom,d->quota); } if (d->maxusers != 0){ linuxconf_replace (K_MAXUSERS,dom,d->maxusers); } if (d->match_gecos != 0){ linuxconf_replace (K_GECOS,dom,d->match_gecos); } if (!d->fallback.is_empty()){ linuxconf_replace (K_FALLBACK,dom,d->fallback); } if (!d->filter.is_empty()){ linuxconf_replace (K_FILTER,dom,d->filter); } if (d->retrievelock){ linuxconf_replace (K_RETRIEVELOCK,dom,1); } if (d->acceptlock){ linuxconf_replace (K_ACCEPTLOCK,dom,1); } if (d->coadmin){ linuxconf_replace (K_COADMIN,dom,d->coadmin); } if (d->password.is_filled()){ linuxconf_replace (K_PASSWORD,dom,d->password); } } return linuxconf_save(); } PUBLIC void VDOMAIN::createfiles() { char pwdfile[PATH_MAX],shadowfile[PATH_MAX],home[PATH_MAX],root[PATH_MAX]; setpwdpaths (pwdfile,shadowfile,home,root); context_mkdir (ETC_VMAIL,"root","mail",0750); if (!context_fexist(pwdfile)){ context_create (pwdfile,"root","mail",0640); context_create (shadowfile,"root","mail",0640); } context_mkdir (VAR_SPOOL_VMAIL,"root","root",0755); char path[PATH_MAX]; snprintf (path,sizeof(path)-1,"%s/%s",VAR_SPOOL_VMAIL,domain.get()); context_mkdir (path,"root","mail",01777); snprintf (path,sizeof(path)-1,"%s/%s",VHOME,domain.get()); context_mkdir (path,"root","root",0755); snprintf (path,sizeof(path)-1,"%s/%s/home",VHOME,domain.get()); context_mkdir (path,"root","root",0755); } static bool privi_changed = false; /* Edit the definition of virtual email domains. Return != 0 if the has been one modification. */ PUBLIC int VDOMAINS::edit() { int ret = 0; if (perm_rootaccess(MSG_R(P_SEEDOMAIN))){ int nof = 0; DIALOG_LISTE dia; while (1){ MENU_STATUS code = select (dia ,MSG_R(T_VDOMAINS) ,MSG_U(I_VDOMAINS ,"You can define new virtual email domain\n" "virtual email hosting is a new concept.\n" "It is not required for most mail server configuration.\n") ,1 ,nof); if (code == MENU_QUIT || code == MENU_ESCAPE){ break; }else if (code == MENU_ADD){ VDOMAIN *d = new VDOMAIN; if (manage_edit (d,d->edit(*this)) >= 0){ d->createfiles(); ret = 1; } }else if (nof >= 0 && nof < getnb()){ VDOMAIN *d = getitem(nof); if (manage_edit (d,d->edit(*this)) >= 0) ret = 1; } } } if (ret == 1){ checkperm(false,false); privi_changed = true; } return ret; } class FIXPERM_VDOMAIN: public FIXPERM_TASK{ int check (bool boottime, bool silentflag); void list (FIXPERM_SPECS &specs); }; static FIXPERM_VDOMAIN perm; int FIXPERM_VDOMAIN::check (bool boottime, bool silentflag) { VDOMAINS conf; return conf.checkperm(boottime,silentflag); } void FIXPERM_VDOMAIN::list (FIXPERM_SPECS &specs) { VDOMAINS conf; conf.listperm(specs); } PUBLIC void VDOMAINS::listperm(FIXPERM_SPECS &specs) { int n = getnb(); if (n > 0){ specs.addline (ETC_VMAIL " root mail d 750 required"); specs.addline (VHOME " root root d 755 required"); specs.addline (VAR_SPOOL_VMAIL " root root d 755 required"); } for (int i=0; idomain.get(); char buf[2*PATH_MAX]; snprintf (buf,sizeof(buf)-1,"%s/%s root mail d 1777 required",VAR_SPOOL_VMAIL,dom); specs.addline (buf); snprintf (buf,sizeof(buf)-1,"%s/%s root root d 755 required",VHOME,dom); specs.addline (buf); snprintf (buf,sizeof(buf)-1,"%s/%s/home root root d 755 required",VHOME,dom); specs.addline (buf); snprintf (buf,sizeof(buf)-1,"%s/aliases.%s root mail f 640",ETC_VMAIL,dom); specs.addline (buf); snprintf (buf,sizeof(buf)-1,"%s/passwd.%s root mail f 640 required",ETC_VMAIL,dom); specs.addline (buf); snprintf (buf,sizeof(buf)-1,"%s/shadow.%s root mail f 640",ETC_VMAIL,dom); specs.addline (buf); for (int a=0; afaliases.getnb(); a++){ const char *filea = d->faliases.getitem(a)->get(); if (filea[0] != '\0'){ snprintf (buf,sizeof(buf)-1,"%s root mail f 640",filea); specs.addline (buf); } } } } PUBLIC VDOMAIN *VDOMAINS::locate (const char *domain) { VDOMAIN *ret = NULL; for (int i=0; idomain.cmp(domain)==0){ ret = d; break; } } return ret; } PUBLIC int VDOMAINS::checkperm(bool, bool ) { FIXPERM_SPECS specs; listperm (specs); return specs.check(); } static PRIVILEGES tb; static void vdomain_setprivi() { if (tb.getnb()==0 || privi_changed){ privi_changed = false; tb.remove_all(); VDOMAINS vdom; int n = vdom.getnb(); for (int i=0; idomain.get(); char id[PATH_MAX]; snprintf (id,sizeof(id)-1,"vdomain_%s",dname); tb.add (new PRIVILEGE (id,dname ,MSG_U(T_VIRTDOMPRIV,"Virtual email domains"))); } } } static PRIVILEGE_DECLARATOR vdom_decl(vdomain_setprivi); PUBLIC USERS* VDOMAIN::getusers( CONFIG_FILE *&file, CONFIG_FILE *&file_shadow) { const char *dname = domain.get(); char pwdfile[PATH_MAX],shadowfile[PATH_MAX]; char pathhome[PATH_MAX],root[PATH_MAX]; setpwdpaths(pwdfile,shadowfile,pathhome,root); file = new CONFIG_FILE (pwdfile,help_nil ,CONFIGF_MANAGED|CONFIGF_OPTIONAL ,"root","mail",0640 ,subsys_vdomain); file_shadow = new CONFIG_FILE (shadowfile,help_nil ,CONFIGF_MANAGED|CONFIGF_OPTIONAL ,"root","mail",0640 ,subsys_vdomain); return new USERS (*file,*file_shadow,root,pathhome,dname,startuid); } /* Return the co-admin privilege associated with a vdomain */ static PRIVILEGE *vdomain_lookuppriv (const char *dname) { PRIVILEGE *ret = NULL; vdomain_setprivi(); if (perm_checkpriv(&alldom_priv)){ ret = &alldom_priv; }else{ char id[PATH_MAX]; snprintf (id,sizeof(id)-1,"vdomain_%s",dname); ret = privilege_lookup (id); } return ret; } static void vdomain_editone (VDOMAIN *v, USER *like) { const char *dname = v->domain.get(); CONTEXT_LOCK lk ("vdomainusers",dname); if (lk.isok()){ CONFIG_FILE *file,*file_shadow; USERS *users = v->getusers (file,file_shadow); users->setmaxusers(v->maxusers); PRIVILEGE *priv = vdomain_lookuppriv (dname); if (perm_access (priv,MSG_U(P_MANAGEDOM,"manage a virtual domain"))){ users->edit(like,priv,0); } delete users; delete file; delete file_shadow; } } /* Edit the definition of virtual email domains. Return != 0 if the has been one modification. */ PUBLIC MENU_STATUS VDOMAINS::select( DIALOG_LISTE &dia, const char *title, const char *intro, int mayadd, int &nof) { sort(); if (dia.getnb()==0){ dia.newf_head ("",MSG_U(H_VDOMAINS,"Virtual email domains\tStatus")); } int lookup[getnb()]; int found = 0; for (int i=0; idomain.get()); if (perm_checkpriv(priv)){ lookup[found] = i; dia.set_menuitem(found,d->domain.get() ,(d->acceptlock || d->retrievelock) ? MSG_U(I_LOCKED,"Locked") : ""); found++; } } dia.remove_last (found+1); if (dia.getnb()==1){ // No domain visible by the user // check with root privileges if (perm_rootaccess(MSG_U(P_SEEDOMAIN,"See virtual domains"))){ for (int i=0; idomain.get(),""); } } } int buttons = 0; if (mayadd){ dia.addwhat (MSG_U(I_ONEVDOMAIN,"Select [Add] to add one virtual domain")); buttons = MENUBUT_ADD; } MENU_STATUS code = dia.editmenu (title,intro,help_vdomain,nof,buttons); if (code == MENU_OK) nof = lookup[nof]; return code; } /* Edit the users of a single virtual domain */ void vdomain_editone (const char *dom, USER *like) { VDOMAINS vdom; VDOMAIN *v = vdom.getitem(dom); if (v == NULL){ xconf_error (MSG_U(E_NOSUCHVDOM,"Virtual domain %s does not exist"),dom); }else{ vdomain_editone (v,like); } } /* Select a virtual domain and edit the users */ void vdomain_editusers (USER *like) { VDOMAINS vdom; if (vdom.getnb()==0){ xconf_error (MSG_U(E_NOVDOM,"No virtual email domain defined")); }else{ int nof = 0; DIALOG_LISTE dia; while (1){ MENU_STATUS code = vdom.select(dia ,MSG_U(T_PICKVDOM,"Pick the domain") ,"" ,0,nof); if (code == MENU_QUIT || code == MENU_ESCAPE){ break; }else{ VDOMAIN *v = vdom.getitem(nof); if (v != NULL){ vdomain_editone(v,like); } } } } } /* Edit one of the aliases file of a vdomain without question */ static void vdomain_editaliases (VDOMAIN *v, int no) { /* #Specification: vdomain / aliases file / privilege The main aliases file of a domain may be managed with the same privilege as the POP account of the vdomain. All other (up to 2) aliases file of the vdomain required root privilege */ PRIVILEGE *priv = NULL; char path[PATH_MAX]; if (no == 0){ const char *dname = v->domain.get(); snprintf (path,sizeof(path)-1,"%s/aliases.%s",ETC_VMAIL,dname); priv = vdomain_lookuppriv (dname); }else{ v->faliases.getitem(no-1)->copy (path); } CONFIG_FILE file (path,help_nil ,CONFIGF_MANAGED|CONFIGF_OPTIONAL ,"root","mail",0640 ,subsys_vdomain); aliases_edit (file,priv,path); } static void vdomain_editaliases (VDOMAIN *v) { int nof = 0; while (1){ DIALOG_LISTE dia; const char *dname = v->domain.get(); char path[PATH_MAX]; snprintf (path,sizeof(path)-1,"%s/aliases.%s",ETC_VMAIL,dname); dia.new_menuitem (path,""); for (int i=0; ifaliases.getnb(); i++){ dia.new_menuitem(v->faliases.getitem(i)->get(),""); } char title[100]; snprintf (title,sizeof(title)-1 ,MSG_U(T_SELALIAS,"Alias files for domain %s") ,dname); MENU_STATUS code = dia.editmenu (title ,MSG_U(I_SELALIAS ,"This virtual email domain has several alias file\n" "pick the one to edit") ,help_vdomain,nof,0); if (code == MENU_OK){ vdomain_editaliases (v,nof); }else{ break; } } } /* Select a vdomain and edit one of its aliases file */ void vdomain_editaliases () { VDOMAINS vdom; if (vdom.getnb()==0){ xconf_error (MSG_R(E_NOVDOM)); }else{ int nof = 0; DIALOG_LISTE dia; while (1){ MENU_STATUS code = vdom.select(dia ,MSG_R(T_PICKVDOM) ,"" ,0,nof); if (code == MENU_QUIT || code == MENU_ESCAPE){ break; }else{ VDOMAIN *v = vdom.getitem(nof); if (v != NULL){ if (v->faliases.getnb()==0){ vdomain_editaliases (v,0); }else{ vdomain_editaliases (v); } } } } } } /* Select a vdomain and edit its policies */ void vdomain_editpolicies () { VDOMAINS vdom; if (vdom.getnb()==0){ xconf_error (MSG_R(E_NOVDOM)); }else{ int nof = 0; DIALOG_LISTE dia; while (1){ MENU_STATUS code = vdom.select(dia ,MSG_R(T_PICKVDOM) ,"" ,0,nof); if (code == MENU_QUIT || code == MENU_ESCAPE){ break; }else{ VDOMAIN *v = vdom.getitem(nof); if (v != NULL){ } } } } } /* Compute the path of the password file for a potential virtual domain. First try to locate it. hostname is either a host member of a virtual domain, or simply the name of a virtual domain. Return -1 if no virtual domain did match */ int vdomain_locateinfo ( const char *hostname, char *pwdfile, char *shadowfile, char *root) { int ret = -1; pwdfile[0] = '\0'; VDOMAINS vdoms; int n = vdoms.getnb(); for (int i=0; i<2 && ret == -1; i++){ for (int j=0; jdomain.icmp(hostname)==0){ char pathhome[PATH_MAX]; v->setpwdpaths (pwdfile,shadowfile,pathhome,root); ret = 0; break; } } hostname = strchr(hostname,'.'); if (hostname == NULL){ break; }else{ hostname++; } } return ret; } /* Show the special url used to access the html mode of linuxconf */ void vdomain_listspc () { VDOMAINS vdom; #if 1 if (vdom.getnb() > 0){ html_printf ("

\n"); html_printf ("

%s
\n" ,MSG_U(I_MNGVDOM,"Manage virtual email domains accounts")); html_printf ("

\n"); html_printf ("

%s
\n" ,MSG_U(I_MNGVALIASES,"Manage virtual email domains aliases")); html_printf ("

\n"); html_printf ("

%s
\n" ,MSG_U(I_MNGVACATION,"Setup your vacation message")); html_printf ("

\n"); html_printf ("

%s
\n" ,MSG_U(I_CHGVPASS,"Change your password (Email users)")); html_printf ("

\n"); } #else vdom.sort(); html_printf ("


\n"); html_printf ("

%s

\n" ,MSG_U(T_VDOMLISTS,"List of virtual email domains on this server")); html_printf ("
\n"); html_printf ("
\n"); html_printf ("\n"); html_printf ("
%s%s\n" ,MSG_U(T_VDOMAIN,"Virtual email domain") ,MSG_U(T_CHGVPASS,"Change your password")); for (int i=0; idomain.get(); html_printf ("
%s\n" ,name,name); html_printf ("\t____\n" ,name); } html_printf ("
\n"); html_printf ("
\n"); #endif } static LINUXCONF_SUBSYS subb (subsys_vdomain ,P_MSG_U(M_VDOMAINSUB,"Virtual email domains")); static void vdomain_list() { VDOMAINS vdoms; for (int i=0; idomain.get(); static const char *tb[]={"passwd","shadow","aliases"}; for (int j=0; j<3; j++){ char path[PATH_MAX]; snprintf (path,sizeof(path)-1,"%s/%s.%s",ETC_VMAIL,tb[j],name); new CONFIG_FILE (path,help_vdomain,CONFIGF_MANAGED ,"root","mail",0640 ,subsys_vdomain); } for (int a=0; afaliases.getnb(); a++){ const char *alfile = dom->faliases.getitem(a)->get(); if (alfile[0] != '\0'){ new CONFIG_FILE (alfile ,help_vdomain,CONFIGF_MANAGED ,"root","mail",0640 ,subsys_vdomain); } } } } static CONFIG_FILE_LISTER vdomain_lister(vdomain_list); class VIRTUAL_COMNG: public USERACCT_COMNG{ VDOMAINS vdoms; VDOMAIN *vdom; char needed; const char *domain; bool may_delete; VDOMAIN_DIAINFO info; /*~PROTOBEG~ VIRTUAL_COMNG */ public: VIRTUAL_COMNG (DICTIONARY&_dict); int deluser (PRIVILEGE *); int save (PRIVILEGE *priv); void setupdia (DIALOG&dia); int validate (DIALOG&, int &nof); ~VIRTUAL_COMNG (void); /*~PROTOEND~ VIRTUAL_COMNG */ }; PUBLIC VIRTUAL_COMNG::VIRTUAL_COMNG( DICTIONARY &_dict) : USERACCT_COMNG (_dict) { domain = dict.get_str ("vdomain"); may_delete = false; vdom = vdoms.locate (domain); if (vdom == NULL){ vdom = new VDOMAIN; may_delete = false; needed = 0; }else{ needed = true; } } PUBLIC VIRTUAL_COMNG::~VIRTUAL_COMNG() { if (may_delete) delete vdom; } PUBLIC void VIRTUAL_COMNG::setupdia ( DIALOG &dia) { dia.newf_title (MSG_U(T_MVIRTUAL,"email"),1 ,"",MSG_R(T_MVIRTUAL)); dia.newf_chk ("",needed,MSG_U(F_NEEDED,"Virtual email domain needed")); vdom->setupdia (dia,info); } PUBLIC int VIRTUAL_COMNG::save( PRIVILEGE *priv) { int ret = 0; if (needed){ VDOMAIN *d = vdoms.locate (dict.get_str("vdomain")); if (d == NULL){ vdoms.add (vdom); may_delete = false; } vdom->createfiles(); ret = vdoms.write (); } return ret; } PUBLIC int VIRTUAL_COMNG::validate( DIALOG &, int &nof) { int ret = 0; #if 0 // Check if this vhost is already there if (vdoms.locate (domain)!=NULL){ xconf_error (MSG_U(E_VHOSTEXIST,"Virtual email domain %s\n" "is already configured"),domain); ret = -1; } #endif return ret; } PUBLIC int VIRTUAL_COMNG::deluser ( PRIVILEGE *) { int ret = 0; if (vdoms.remove_del (vdom) != -1){ ret = vdoms.write(); } return ret; } USERACCT_COMNG *vdomain_newcomng( const char *key, DICTIONARY &dict) { USERACCT_COMNG *ret = NULL; if (strcmp(key,"virtual")==0){ ret = new VIRTUAL_COMNG (dict); } return ret; } int vdomain_del (const char *host) { int ret = -1; VDOMAINS conf; VDOMAIN *dom = conf.locate (host); if (dom != NULL){ vdomain_cleanupconf (host); conf.remove_del(dom); ret = conf.write(); }else{ fprintf (stderr,MSG_U(E_MISSING ,"Mailconf: Virtual email domain %s is not configured\n") ,host); } return ret; } int vdomain_add (const char *edom, int argc, const char *argv[]) { int ret = -1; VDOMAINS conf; VDOMAIN *dom = conf.locate (edom); if (dom != NULL){ fprintf (stderr,MSG_U(E_DOMEXIST ,"Mailconf: Virtual email domain %s already configured\n") ,edom); }else{ dom = new VDOMAIN; dom->domain.setfrom (edom); bool err = false; for (int i=0; ifallback.setfrom (arg); i++; }else if (strcmp(opt,"--filter")==0){ dom->filter.setfrom (arg); i++; }else if (strcmp(opt,"--faliases")==0){ dom->faliases.add (new SSTRING (arg)); i++; }else if (strcmp(opt,"--daliases")==0){ dom->others.add (new SSTRING (arg)); i++; }else if (strcmp(opt,"--startuid")==0){ dom->startuid = atoi(arg); i++; }else if (strcmp(opt,"--quota")==0){ dom->quota = atoi(arg); i++; }else{ fprintf (stderr,MSG_U(E_IVLDOPT,"Invalid option %s\n"),opt); err = true; } } if (!err){ dom->createfiles(); conf.add (dom); ret = conf.write(); } } return ret; } static HELP_FILE help_privi ("mailconf","vdomprivi"); static REGISTER_PRIVI_HELP p (help_privi ,P_MSG_U(T_VDOMPRIVI,"Privileges: Virtual email domains")); int vdomain_useradd ( const char *domain, const char *id, const char *name) { int ret = -1; VDOMAINS conf; VDOMAIN *v = conf.locate (domain); if (v == NULL){ fprintf (stderr ,MSG_U(E_NOVDOMAIN ,"Virtual email domain %s does not exist\n") ,domain); }else{ CONFIG_FILE *file,*file_shadow; USERS *users = v->getusers (file,file_shadow); ret = users->addbatch (id,"popusers",name,"/bin/false","","",true); delete users; delete file; delete file_shadow; } return ret; } int vdomain_userdel ( const char *domain, const char *id) { int ret = -1; VDOMAINS conf; VDOMAIN *v = conf.locate (domain); if (v == NULL){ fprintf (stderr ,MSG_R(E_NOVDOMAIN),domain); }else{ CONFIG_FILE *file,*file_shadow; USERS *users = v->getusers (file,file_shadow); ret = users->delbatch (id,DELOPER_ARCHIVE); delete users; delete file; delete file_shadow; } return ret; } int vdomain_passwd ( const char *domain, const char *id) { int ret = -1; VDOMAINS conf; VDOMAIN *v = conf.locate (domain); if (v == NULL){ fprintf (stderr ,MSG_R(E_NOVDOMAIN),domain); }else{ CONFIG_FILE *file,*file_shadow; USERS *users = v->getusers (file,file_shadow); USER *u = users->getitem (id); char buf[100]; if (u == NULL){ fprintf (stderr ,MSG_U(E_NOUSERINDOMAIN ,"User account %s does not exist in domain %s\n") ,id,domain); }else{ printf ("Password: "); fflush (stdout); if (fgets (buf,sizeof(buf)-1,stdin)!=NULL){ SHADOW *shadow = users->getshadow(u); strip_end (buf); u->update_passwd (buf,shadow,false,domain); ret = users->write(NULL); } } delete users; delete file; delete file_shadow; } return ret; } static int vdomain_setmainaliaspath ( const char *domain, char path[PATH_MAX], bool must_exist) // Make sure the file exist { int ret = -1; VDOMAINS conf; VDOMAIN *v = conf.locate (domain); if (v == NULL){ fprintf (stderr ,MSG_R(E_NOVDOMAIN),domain); }else{ snprintf (path,PATH_MAX-1,"%s/aliases.%s",ETC_VMAIL,domain); if (file_type (path)!=0 && must_exist){ fprintf (stderr,MSG_U(E_NOALIASEFILE,"Aliases file %s does not exist\n") ,path); }else{ ret = 0; } } return ret; } /* Set the value(s) of an alias in one domain aliases. If the alias does not exist, it is created. If it exist its values are replaced. Return -1 if any error. */ int vdomain_setalias ( const char *domain, const char *name, int nb, const char *vals[]) { int ret = -1; char path[PATH_MAX]; if (vdomain_setmainaliaspath (domain,path,false)!=-1){ CONFIG_FILE file (path,help_nil ,CONFIGF_MANAGED|CONFIGF_OPTIONAL ,"root","mail",0640 ,subsys_vdomain); ALIASES al (file,NULL,false); al.setalias (name,nb,vals); ret = al.write(); } return ret; } /* Unset some or all values in one alias of the main domain aliases. Return -1 if any error. */ int vdomain_unsetalias ( const char *domain, const char *name, int nb, const char *vals[]) { int ret = -1; char path[PATH_MAX]; if (vdomain_setmainaliaspath (domain,path,true)!=-1){ CONFIG_FILE file (path,help_nil ,CONFIGF_MANAGED|CONFIGF_OPTIONAL ,"root","mail",0640 ,subsys_vdomain); ALIASES al (file,NULL,false); al.unsetalias (name,nb,vals); ret = al.write(); } return ret; } #include static void vdomains_list (SSTRINGS &tb) { VDOMAINS vdom; for (int i=0; idomain)); } } static void vdomains_edit (const char *s, bool setting) { VDOMAINS vdom; VDOMAIN *dom = vdom.locate (s); if (dom != NULL){ if (dom->edit(vdom)==0) vdom.write(); } if (dom == NULL && setting){ dom = new VDOMAIN; dom->domain.setfrom(s); if (vdom.manage_edit (dom,dom->edit(vdom)) >= 0){ dom->createfiles(); } } } static PUBLISH_VARIABLES_MSG var_list1[]={ {"fallback",P_MSG_R(F_FALLBACK)}, {"startuid",P_MSG_R(F_STARTUID)}, {"quota",P_MSG_R(F_QUOTA)}, {"maxusers",P_MSG_R(F_MAXUSERS)}, {"matchgecos",P_MSG_R(F_GECOS)}, {"filter",P_MSG_R(F_VFILTER)}, {"acceptlock",P_MSG_R(F_LOCKDOMAIN)}, {"retrievelock",P_MSG_R(I_LOCKRETRIEVE)}, {"faliases1",P_MSG_R(T_FILEPATH)}, {NULL,NULL}, }; static REGISTER_VARIABLES vdomain_registry1("vdomain","vdomains" ,var_list1 ,NULL,vdomains_edit,vdomains_list); static PUBLISH_VARIABLES_STR var_list2[]={ {"faliases2",&faliases2_key}, {"daliase1",tbkey+0}, {"daliase2",tbkey+1}, {"daliase3",tbkey+2}, {"daliase4",tbkey+3}, {"daliase5",tbkey+4}, {NULL,NULL}, }; static REGISTER_VARIABLES vdomain_registry2("vdomain","vdomains" ,var_list2 ,NULL,vdomains_edit,vdomains_list); static int vdomain_editaccount (USERACCT_API *, const char *domain, const char *account, bool setting) { int ret = 0; VDOMAINS vdom; VDOMAIN *dom = vdom.locate (domain); if (dom != NULL){ CONFIG_FILE *file,*file_shadow; USERS *users = dom->getusers (file,file_shadow); USER *usr = users->getitem (account); if (usr != NULL){ users->editone (usr,false,NULL,0); } delete users; delete file; delete file_shadow; ret = 1; } return ret; } static int vdomain_listaccounts (USERACCT_API *, const char *domain, SSTRINGS &tb) { int ret = 0; VDOMAINS vdom; VDOMAIN *dom = vdom.locate (domain); if (dom != NULL){ CONFIG_FILE *file,*file_shadow; USERS *users = dom->getusers (file,file_shadow); for (int i=0; igetnb(); i++){ USER *u = users->getitem(i); tb.add (new SSTRING(u->getname())); } delete users; delete file; delete file_shadow; ret = 1; } return ret; } void *vdomain_useracct_api_get() { USERACCT_API *api = new USERACCT_API; api->editaccount = vdomain_editaccount; api->listaccounts = vdomain_listaccounts; return api; } void vdomain_useracct_api_release(void *api) { delete (USERACCT_API*)api; } /* Validate a pseudo-user co-administrator password */ bool vdomain_checkauth (const char *user, const char *password) { bool ret = false; VDOMAINS vdoms; for (int i=0; idomain.get()); if (coadmin.icmp(user)==0){ if (v->coadmin){ const char *copass = v->password.get(); ret = strcmp(copass,crypt(password,copass))==0; } break; } } return ret; } /* Check if a pseudo-user co-administrator has a given privilege */ bool vdomain_checkpriv (const char *user, PRIVILEGE *priv) { bool ret = false; if (strncasecmp(user,"admin@",6)==0){ const char *dname = user+6; vdomain_setprivi (); char id[PATH_MAX]; if ((unsigned)snprintf (id,sizeof(id)-1,"vdomain_%s",dname) < sizeof(id)-1){ char *pt = id; while (*pt != '\0'){ *pt = tolower(*pt); pt++; } PRIVILEGE *privdom = privilege_lookup (id); ret = privdom == priv; } } return ret; }