#pragma implementation #include #include #include #include #include #include #include "postfixconf.h" #include #include #include #include "postfixconf.m" #include "alias.h" 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(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 int 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 1; } PUBLIC int 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 1; } /* 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 int ALIAS::is_valid() { return !name.is_empty(); } /* 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) { DIALOG dia; 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); /* #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_empty()){ 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 (char *buf) { if (buf[0] != '\0') add (new ALIAS(buf)); buf[0] = '\0'; } PUBLIC ALIASES::ALIASES( const char *config, PRIVILEGE *_privi, bool _dodb, int _btype) // Should we created an index for the alias // file (vdeliver does not use it) : f(config,help_postfix_aliases, CONFIGF_OPTIONAL|CONFIGF_MANAGED , subsys_postfix) { privi = _privi; dodb = _dodb; btype = _btype; FILE_CFG *fin = f.fopen ("r"); if (fin != NULL){ char buf[10000]; char line[1000]; buf[0] = '\0'; while (fgets(line,sizeof(line)-1,fin)!=NULL){ strip_end (line); if (line[0] == '\0'){ addline (buf); }else if (isspace(line[0])){ strcat (buf,line); }else{ addline (buf); strcpy (buf,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/postfix -bi" is executed. */ SSTRING notice; const char *cmd = "postalias"; char *buf = type(btype); char path[50]; sprintf (path,"%s%s",buf,f.getpath()); if (execute (cmd,path,notice)){ xconf_notice ("%s",notice.get()); }else xconf_notice (MSG_R(E_EXECCOMM),cmd,path); } } return ret; } PRIVATE char *ALIASES::type (int nametable) { char *buf; switch (nametable){ case BTREE_DATABASE: buf = "btree:"; break; case DBM_DATABASE: buf = "dbm:"; break; case HASH_DATABASE: buf = "hash:"; break; default: buf = ""; } return buf; } /* 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)==0){ if (locate(a->getname())!=-1){ xconf_error (MSG_U(E_DUPALIAS,"Duplicate alias, rejected")); }else{ add (a); write(); a = NULL; break; } } delete a; } /* Edit, add, delete record in the /etc/aliases file */ PUBLIC int ALIASES::edit() { int choice = 0; DIALOG_LISTE *dia = NULL; SSTRINGS tbsort; tbsort.neverdelete(); while (1){ if (dia == NULL){ dia = new DIALOG_LISTE; tbsort.remove_all(); dia->newf_head ("",MSG_U(H_MALIASES,"Aliases")); for (int i=0; iis_valid()){ tbsort.add (&a->name); } } tbsort.sort(); for (int i=0; inew_menuitem(tbsort.getitem(i)->get(),""); } dia->addwhat (MSG_U(I_NEWALIAS,"Select [Add] to add a new alias")); } MENU_STATUS code = dia->editmenu ( MSG_U(T_EDITALIASES,"Here, you can find a list of alias.") ,MSG_U(I_EDITALIASES ,"You are allowed to edit/add/delete\n" "system wide aliases for electronic mail") ,help_postfix_aliases ,choice,0); ALIAS *item = NULL; if (choice >=0 && choice < nb){ int no = locate(tbsort.getitem(choice)->get()); item = getitem(no); } bool must_delete = false; if (code == MENU_ESCAPE || code == MENU_QUIT){ break; }else if (code == MENU_OK){ if (item != NULL){ int ok = item->edit (privi); if (manage_edit (item,ok) > 0) must_delete = true; } }else if (perm_access(privi,MSG_U(P_WRITE,"write %s"),f.getpath())){ if (code == MENU_ADD){ addnew(); must_delete = true; } } if (must_delete){ delete dia; dia = NULL; } } delete dia; return nb; } /* 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 *p,int tbase,const char *name, int nb, const char *vals[]) { ALIASES al (p,NULL,true,tbase); 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 *p,int tbase,const char *name, int nb, const char *vals[]) { ALIASES al (p,NULL,true,tbase); al.unsetalias (name,nb,vals); return al.write(); }