#pragma implementation #include #include #include #include #include #include #include "mailconf.h" #include "internal.h" #include #include #include "userconf.h" #include #include #include "../paths.h" #include "mailconf.m" #include "alias.h" #include static MAILCONF_HELP_FILE help_aliases("aliases"); static CONFIG_FILE f_aliases (ETC_ALIASES,help_aliases ,CONFIGF_MANAGED|CONFIGF_OPTIONAL,subsys_mail); PUBLIC ALIAS::ALIAS() { } static char *str_copyupto (char *dest, const char *src, char stop) { while (*src > ' ' && *src != stop) *dest++ = *src++; *dest = '\0'; return ((char*) src); } /* Add one element of the alias list */ PUBLIC void ALIAS::addoneval(const char *val) { char *pt = str_skip (val); if (pt[0] == '|'){ pt++; filter.setfrom (str_skip(pt)); }else if (strncmp(val,":include:",9)==0){ file.setfrom(val+9); }else{ values.add (new SSTRING(val)); } } /* Parse an alias list */ PRIVATE void ALIAS::splitline(char *ptpt) { /* #Specification: mailconf / alias / limits We assume that an alias list has at most one command redirection and one include file. It may have as many other element (email address) as needed. */ while (1){ ptpt = str_skip (ptpt); if (ptpt[0] == '\0'){ break; }else if (ptpt[0] == ','){ ptpt++; }else if (ptpt[0] == '"'){ char word[200]; ptpt++; char *ptw = word; while (*ptpt != '\0' && *ptpt != '"') *ptw++ = *ptpt++; *ptw = '\0'; if (*ptpt == '"') ptpt++; addoneval (word); }else{ char word[200]; ptpt = str_copyupto (word,ptpt,','); addoneval(word); } } } /* Used when reading the /etc/aliases file line will be trashed. */ PUBLIC ALIAS::ALIAS(const char *line) { char *pt = str_skip(line); if (pt[0] == '#'){ comment.setfrom (pt); }else{ char *ptpt = strchr(pt,':'); if (ptpt != NULL){ *ptpt++ = '\0'; strip_end (pt); name.setfrom (pt); splitline (ptpt); } } } PUBLIC const char *ALIAS::getname() { return name.get(); } PUBLIC bool ALIAS::filter_ok() { if (!filter.is_empty()){ /* #Specification: mailconf / aliases / filter program Linuxconf assumes the first word of the filter part of an alias is the program name (with or without path). It will check if the path exist either as is, or in /bin or /usr/bin. It will send a warning if the program can't be found. Complex script can be entered here and linuxconf may be fooled by this. It will just print a warning anyway and the alias will be accepted. If the program can be located, linuxconf will check that it is indeed executable. */ char prog[PATH_MAX],binprog[PATH_MAX+5],usrbinprog[PATH_MAX+9]; str_copyword(prog,filter.get(),PATH_MAX); sprintf (binprog,"/bin/%s",prog); sprintf (usrbinprog,"/usr/bin/%s",prog); struct stat st; if (stat (prog,&st) == -1 && stat (binprog,&st) == -1 && stat (usrbinprog,&st) == -1){ xconf_notice (MSG_U(N_MISSING,"%s does not exist"),prog); }else if ((st.st_mode & 0111)==0){ xconf_notice (MSG_U(N_NOEXEC,"%s is not executable")); } } return true; } PUBLIC bool ALIAS::file_ok() { /* #Specification: mailconf / aliases / in a file When the user specify the indirect file for aliases mailconf check if the file exist and send a notice to the user if this is not the case. The operation is accepted anyway. */ if (!file.is_empty()){ if (!file_exist(file.get())){ xconf_notice (MSG_R(N_MISSING),file.get()); } } return true; } /* Write one record */ PUBLIC void ALIAS::write (FILE_CFG *fout) { if (!name.is_empty()){ fprintf (fout,"%s: ",name.get()); char *comma = " "; if (!filter.is_empty()){ fprintf (fout,"\"| %s\" ",filter.get()); comma = ","; } if (!file.is_empty()){ fprintf (fout,"%s:include:%s ",comma,file.get()); comma = ","; } int nb = values.getnb(); for (int i=0; iget()); if (i > 0 && i % 4 == 0){ fputc ('\n',fout); comma = "\t,"; }else{ comma = ","; } } } fprintf (fout,"%s\n",comment.get()); } /* Is it a valid record or a comment */ PUBLIC bool ALIAS::is_valid() { return !name.is_empty(); } /* Check if the alias ali is a duplicate of another */ PUBLIC bool ALIASES::isdup (ALIAS *ali) { bool ret = false; int nb = getnb(); const char *name = ali->getname(); for (int i=0; igetname(),name)==0){ ret = true; break; } } return ret; } static void alias_editfile (const char *fname) { CONFIG_FILE file (fname,help_nil,CONFIGF_OPTIONAL,subsys_mail); DIALOG dia; SSTRING text; FILE_CFG *fin = file.fopen ("r"); if (fin != NULL){ char line[1000]; while (fgets(line,sizeof(line)-1,fin)!=NULL){ text.append (line); } fclose (fin); } dia.newf_textarea (NULL,text,70,15); int nof = 0; SSTRING title; title.setfromf (MSG_U(T_ALIASFILE,"Alias file %s"),fname); while (1){ MENU_STATUS code = dia.edit (title.get() ,MSG_U(I_ALIASFILE,"Enter one email address per line") ,help_nil ,nof); if (code == MENU_ESCAPE || code == MENU_CANCEL){ break; }else{ FILE_CFG *fout = file.fopen ("w"); if (fout != NULL){ fputs (text.get(),fout); fclose (fout); break; } } } } static bool has_space (SSTRINGS &tb, int &no) { bool ret = false; for (int i=0; istrip_end(); if (v->strchr(' ')!=NULL){ no = i; ret = true; break; } } return ret; } /* Edit an alias definition. Return -1 if the user abort the edition (The record is left unchanged then). Return 0 if the user accepted the changes Return 1 if the user request the deletion of this record */ PUBLIC int ALIAS::edit(PRIVILEGE *privi, ALIASES &aliases) { DIALOG dia; SSTRING old_filter = filter; SSTRING old_file = file; dia.newf_str (MSG_U(F_ALIASNAME,"alias name"),name); dia.newf_str (MSG_U(F_FILTER,"Filter program"),filter); dia.newf_str (MSG_U(F_LISTFILE,"List file"),file); PRIVATE_MESSAGE filemsg; dia.set_helpdia (filemsg); /* #Specification: mailconf / aliases / limitation The user interface present 10 empty lines as well as the current aliases. A button ADD allows the user to get more lines. */ dia.newf_title ("",""); int i; for (i=0; i<10; i++) values.add (new SSTRING); int vnb = values.getnb(); for (i=0; ivnb; i++){ values.getitem(i)->strip_end(); } for (i=0; iis_filled()){ break; } } if (i == vnb){ xconf_error (MSG_U(E_ALLEMPTY,"All values are empty")); no = 4; }else{ ret = 0; break; } } } } } if (ret == 0){ setmodified(); }else{ dia.restore(); } values.remove_empty(); return ret; } PUBLIC ALIAS *ALIASES::getitem(int no) { return (ALIAS*)ARRAY::getitem(no); } PRIVATE void ALIASES::addline (SSTRING &buf) { if (buf.is_filled()) add (new ALIAS(buf.get())); buf.setempty(); } PUBLIC ALIASES::ALIASES( CONFIG_FILE &_f, PRIVILEGE *_privi, bool _dodb) // Should we create an index for the alias // file (vdeliver does not use it) : f(_f) { privi = _privi; dodb = _dodb; FILE_CFG *fin = f.fopen ("r"); if (fin != NULL){ SSTRING buf; char line[1000]; while (fgets(line,sizeof(line)-1,fin)!=NULL){ strip_end (line); if (line[0] == '\0'){ addline (buf); }else if (isspace(line[0])){ buf.append (line); }else{ addline (buf); buf.setfrom(line); } } fclose (fin); addline(buf); rstmodified(); } } /* Write back /etc/aliases */ PUBLIC int ALIASES::write () { int ret = -1; FILE_CFG *fout = f.fopen (privi,"w"); if (fout != NULL){ for (int i=0; iwrite(fout); } ret = fclose (fout); rstmodified(); if (dodb){ /* #Specification: mailconf / aliases / updating Whenever the /etc/aliases is updated, the command "/usr/lib/sendmail -bi" is executed. */ DAEMON_INTERNAL *dae = daemon_find ("sendmail"); if (dae->is_managed()){ char cmd[200]; sprintf (cmd,"%s -bi",dae->getpath()); netconf_system (8,cmd); } } } return ret; } /* Find an alias in the table. Return -1 if not found. Return the index otherwise. */ PUBLIC int ALIASES::locate (const char *name) { int ret = -1; int nb = getnb(); for (int i=0; igetname(),name)==0){ ret = i; break; } } return ret; } /* Add a new record */ PUBLIC void ALIASES::addnew() { ALIAS *a = new ALIAS; while (a->edit(privi,*this)==0){ if (locate(a->getname())!=-1){ xconf_error (MSG_R(E_DUPALIAS)); }else{ add (a); write(); a = NULL; break; } } delete a; } /* Edit, add, delete record in the /etc/aliases file */ PUBLIC int ALIASES::edit(const char *title) { glocal ALIASES *tb = this; glocal PRIVILEGE *privi = privi; (title ,MSG_U(IEDITALIASES ,"You are allowed to edit/add/delete\n" "system wide aliases for electronic mail") ,help_aliases); newf_head (MSG_U(H_MALIASES,"Alias\tAddress\tFile\tFilter")); sortable(); sortpolicy ("aaaaa"); addwhat (MSG_U(I_NEWALIAS,"Select [Add] to add a new alias")); for (int i=0; isize(); i++){ ALIAS *a = glocal.tb->getitem(i); if (a->is_valid()){ SSTRING *v0 = a->values.getitem(0); const char *value0 = v0 != NULL ? v0->get() : ""; new_menuitemf (a->name.get(),"%s\t%s\t%s" ,value0,a->file.get(),a->filter.get()); set_lookup(i); } } ALIAS *item = glocal.tb->getitem(no); int ok = item->edit (glocal.privi,*glocal.tb); glocal.tb->manage_edit (item,ok); if (perm_access(glocal.privi,MSG_U(P_WRITE,"write %s") ,f_aliases.getpath())){ glocal.tb->addnew(); } return 0; } void aliases_edit (CONFIG_FILE &f, PRIVILEGE *privi, const char *title) { ALIASES al (f,privi,false); al.edit(title); } void aliases_edit() { ALIASES al (f_aliases,NULL,true); al.edit(MSG_U(T_EDITALIASES,"Edit global mail aliases")); } /* Set the value(s) of an alias. If the alias does not exist, it is created. If it exist its values are replaced. Return -1 if any error. */ PUBLIC int ALIASES::setalias (const char *name, int nb, const char *vals[]) { ALIAS *one = NULL; for (int i=0; iname.cmp(name)==0){ a->values.remove_all(); one = a; break; } } if (one == NULL){ one = new ALIAS; add (one); } one->name.setfrom (name); for (int i=0; iaddoneval (vals[i]); } return 0; } /* Remove some values from an alias definition */ PUBLIC void ALIAS::removesome (const char *vals[], int nb) { for (int j=0; jcmp(vals[k])==0){ values.remove_del(s); j--; break; } } } } /* Unset all or some values of one or some aliases. If the name of the alias is -, then the supplied values will be removed from all aliases instead of just one. If no values are supplied, then the alias is removed. Return -1 if any error. */ PUBLIC int ALIASES::unsetalias (const char *name, int nb, const char *vals[]) { if (strcmp(name,"-")==0){ // Remove the values from all aliases. Useful when you remove // a user account and wish to delete it from all mailing lists for (int i=0; iremovesome(vals,nb); if (a->values.getnb()==0 && a->file.is_empty() && a->filter.is_empty()){ remove_del (a); i--; } } }else{ for (int i=0; iname.cmp(name)==0){ if (nb == 0){ // The alias is deleted remove_del (a); }else{ a->removesome (vals,nb); } break; } } } return 0; } /* Set the value(s) of an alias in the main domain aliases. If the alias does not exist, it is created. If it exist its values are replaced. Return -1 if any error. */ int aliases_set (const char *name, int nb, const char *vals[]) { ALIASES al (f_aliases,NULL,true); al.setalias (name,nb,vals); return al.write(); } /* Unset some or all values in one alias of the main domain aliases. Return -1 if any error. */ int aliases_unset (const char *name, int nb, const char *vals[]) { ALIASES al (f_aliases,NULL,true); al.unsetalias (name,nb,vals); return al.write(); }