📄 vpopmail.c
字号:
/* * $Id: vpopmail.c,v 1.28.2.4 2004/06/26 02:20:56 tomcollins Exp $ * Copyright (C) 2000-2002 Inter7 Internet Technologies, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */#include <stdio.h>#include <stdlib.h>#include <stdarg.h>#include <unistd.h>#include <string.h>#include <pwd.h>#include <sys/stat.h>#include <sys/types.h>#include <sys/wait.h>#ifdef HAVE_SYS_VARARGS_H#include <sys/varargs.h>#endif#include <signal.h>#include <ctype.h>#include <fcntl.h>#include <time.h>#include <dirent.h>#include <pwd.h>#include "config.h"#include "md5.h"#include "vpopmail.h"#include "file_lock.h"#include "vauth.h"#include "vlimits.h"#include "maildirquota.h"#define MAX_BUFF 256#ifdef POP_AUTH_OPEN_RELAY/* keep a output pipe to tcp.smtp file */int tcprules_fdm;static char relay_tempfile[MAX_BUFF];#endifint verrori = 0;extern int cdb_seek();/* Global Flags */int NoMakeIndex = 0;int OptimizeAddDomain = 0;#define PS_TOKENS " \t"#define CDB_TOKENS ":\n\r"#ifdef IP_ALIAS_DOMAINSint host_in_locals(char *domain);#endifstatic char gen_chars[] = "abcdefghijklmnopqrstuvwxyz" \ "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \ "0123456789.@!#%*";static char ok_env_chars[] = "abcdefghijklmnopqrstuvwxyz" \ "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \ "1234567890_-.@";/************************************************************************//* * Add a domain to the email system * * input: domain name * dir to put the files * uid and gid to assign to the files */int vadddomain( char *domain, char *dir, uid_t uid, gid_t gid ){ FILE *fs; int i; char *domain_hash; char DomainSubDir[MAX_BUFF]; char dir_control_for_uid[MAX_BUFF]; char tmpbuf[MAX_BUFF]; char Dir[MAX_BUFF]; char calling_dir[MAX_BUFF]; /* we only do lower case */ lowerit(domain); /* reject domain names that are too short to be valid */ if ( strlen( domain) <3) return (VA_INVALID_DOMAIN_NAME); /* reject domain names that exceed our max permitted/storable size */ if ( strlen( domain ) >= MAX_PW_DOMAIN ) return(VA_DOMAIN_NAME_TOO_LONG); /* check invalid email domain characters */ for(i=0;domain[i]!=0;++i) { if (i == 0 && domain[i] == '-' ) return(VA_INVALID_DOMAIN_NAME); if (isalnum((int)domain[i])==0 && domain[i]!='-' && domain[i]!='.') { return(VA_INVALID_DOMAIN_NAME); } } if ( domain[i-1] == '-' ) return(VA_INVALID_DOMAIN_NAME); /* after the name is okay, check if it already exists */ if ( vget_assign(domain, NULL, 0, NULL, NULL ) != NULL ) { return(VA_DOMAIN_ALREADY_EXISTS); } /* set our file creation mask for machines where the * sysadmin has tightened default permissions */ umask(VPOPMAIL_UMASK); /* store the calling directory */ getcwd(calling_dir, sizeof(calling_dir)); /* go to the directory where our Domains dir is to be stored * check for error and return error on error */ if ( chdir(dir) != 0 ) return(VA_BAD_V_DIR); /* go into the Domains subdir */ if ( chdir(DOMAINS_DIR) != 0 ) { /* if it's not there, no problem, just try to create it */ if ( mkdir(DOMAINS_DIR, VPOPMAIL_DIR_MODE) != 0 ) { chdir(calling_dir); return(VA_CAN_NOT_MAKE_DOMAINS_DIR); } /* set the permisions on our new Domains dir */ chown(DOMAINS_DIR,uid,gid); /* now try moving into the Domains subdir again */ if ( chdir(DOMAINS_DIR) != 0 ) { chdir(calling_dir); return(VA_BAD_D_DIR); } } /* since domains can be added under any /etc/passwd * user, we have to create dir_control information * for each user/domain combination */ snprintf(dir_control_for_uid, sizeof(dir_control_for_uid), "dom_%lu", (long unsigned)uid); /* work out a subdir name for the domain * Depending on how many domains we have, it may need to be hashed */ open_big_dir(dir_control_for_uid, uid, gid); domain_hash = next_big_dir(uid, gid); close_big_dir(dir_control_for_uid, uid, gid); if ( strlen(domain_hash) > 0 ) { snprintf(DomainSubDir, sizeof(DomainSubDir), "%s/%s", domain_hash, domain); } else { snprintf(DomainSubDir,sizeof(DomainSubDir), "%s", domain); } /* Check to make sure length of the dir isnt going to exceed * the maximum storable size * We dont want to start creating dirs and putting entries in * the assign file etc if the path is going to be too long */ if (strlen(dir)+strlen(DOMAINS_DIR)+strlen(DomainSubDir) >= MAX_PW_DIR) { /* back out of changes made so far */ dec_dir_control(dir_control_for_uid, uid, gid); chdir(calling_dir); return(VA_DIR_TOO_LONG); } /* Make the subdir for the domain */ if ( r_mkdir(DomainSubDir, uid, gid ) != 0 ) { /* back out of changes made so far */ dec_dir_control(dir_control_for_uid, uid, gid); chdir(calling_dir); return(VA_COULD_NOT_MAKE_DOMAIN_DIR); } if ( chdir(DomainSubDir) != 0 ) { /* back out of changes made so far */ vdelfiles(DomainSubDir); dec_dir_control(dir_control_for_uid, uid, gid); chdir(calling_dir); return(VA_BAD_D_DIR); } /* create the .qmail-default file */ snprintf(tmpbuf, sizeof(tmpbuf), "%s/%s/%s/.qmail-default", dir, DOMAINS_DIR, DomainSubDir); if ( (fs = fopen(tmpbuf, "w+"))==NULL) { /* back out of changes made so far */ chdir(dir); chdir(DOMAINS_DIR); if (vdelfiles(DomainSubDir) != 0) { fprintf(stderr, "Failed to delete directory tree :%s\n", DomainSubDir); } dec_dir_control(dir_control_for_uid, uid, gid); chdir(calling_dir); return(VA_COULD_NOT_OPEN_QMAIL_DEFAULT); } else { fprintf(fs, "| %s/bin/vdelivermail '' bounce-no-mailbox\n", VPOPMAILDIR); fclose(fs); } /* create an entry in the assign file for our new domain */ snprintf(tmpbuf, sizeof(tmpbuf), "%s/%s/%s", dir, DOMAINS_DIR, DomainSubDir); if (add_domain_assign( domain, domain, tmpbuf, uid, gid ) != 0) { /* back out of changes made so far */ chdir(dir); chdir(DOMAINS_DIR); if (vdelfiles(DomainSubDir) != 0) { fprintf(stderr, "Failed to delete directory tree: %s\n", DomainSubDir); } dec_dir_control(dir_control_for_uid, uid, gid); chdir(calling_dir); fprintf (stderr, "Error. Failed to add domain to assign file\n"); return (VA_COULD_NOT_UPDATE_FILE); } /* recursively change ownership to new file system entries */ snprintf(tmpbuf, sizeof(tmpbuf), "%s/%s/%s", dir, DOMAINS_DIR, DomainSubDir); r_chown(tmpbuf, uid, gid); /* ask the authentication module to add the domain entry */ /* until now we checked if domain already exists in cdb and * setup all dirs, but vauth_adddomain may __fail__ so we need to check */ if (vauth_adddomain( domain ) != VA_SUCCESS ) { /* ok we have run into problems here. adding domain to auth backend failed * so now we need to reverse the steps we have already performed above */ fprintf(stderr, "Error. Failed while attempting to add domain to auth backend\n"); chdir(dir); chdir(DOMAINS_DIR); if (vdelfiles(DomainSubDir) != 0) { fprintf(stderr, "Failed to delete directory tree: %s\n", DomainSubDir); } dec_dir_control(dir_control_for_uid, uid, gid); vget_assign(domain, Dir, sizeof(Dir), &uid, &gid ); if ( del_domain_assign(domain, domain, Dir, uid, gid) != 0) { fprintf(stderr, "Failed while attempting to remove domain from assign file\n"); } if (del_control(domain) !=0) { fprintf(stderr, "Failed while attempting to delete domain from the qmail control files\n"); } if (vdel_dir_control(domain) != 0) { fprintf (stderr, "Failed while attempting to delete domain from dir_control\n"); } /* send a HUP signal to qmail-send process to reread control files */ signal_process("qmail-send", SIGHUP); return (VA_NO_AUTH_CONNECTION); } /* ask qmail to re-read it's new control files */ if ( OptimizeAddDomain == 0 ) { signal_process("qmail-send", SIGHUP); } /* return back to the callers directory and return success */ chdir(calling_dir); return(VA_SUCCESS);}/************************************************************************//* Delete a domain from the entire mail system * * If we have problems at any of the following steps, it has been * decided that the best course of action is to continue rather than * abort. The idea behind this is to allow the removal of a partially * installed domain. We will emit warnings should any of the expected * cleanup steps fail. */int vdeldomain( char *domain ){ struct stat statbuf; char Dir[MAX_BUFF]; char domain_to_del[MAX_BUFF]; char dircontrol[MAX_BUFF]; uid_t uid; gid_t gid; /* we always convert domains to lower case */ lowerit(domain); /* Check the length of the domain to del * If it exceeds the max storable size, * then the user has made some sort of error in * asking to del that domain, because such a domain * wouldnt be able to exist in the 1st place */ if (strlen(domain) >= MAX_PW_DOMAIN) return (VA_DOMAIN_NAME_TOO_LONG); /* now we want to check a couple for things : * a) if the domain to del exists in the system * b) if the domain to del is an aliased domain or not */ /* Take a backup of the domain we want to del, * because when we call vget_assign, if the domain * is an alias, then the domain parameter will be * rewritten on return as the name of the real domain */ snprintf(domain_to_del, sizeof(domain_to_del), "%s", domain); /* check if the domain exists. If so extract the dir, uid, gid */ if (vget_assign(domain, Dir, sizeof(Dir), &uid, &gid ) == NULL) { return(VA_DOMAIN_DOES_NOT_EXIST); } /* if this is an NOT aliased domain.... * (aliased domains dont have any filestructure of their own) */ if ( strcmp(domain_to_del, domain) == 0 ) { /* check if the domain's dir exists */ if ( stat(Dir, &statbuf) != 0 ) { fprintf(stderr, "Warning: Could not access (%s)\n",Dir); } /* * Michael Bowe 23rd August 2003 * * at this point, we need to write some code to check if any alias domains * point to this (real) domain. If we find such aliases, then I guess we * have a couple of options : * 1. Abort with an error, saying cant delete domain until all * aliases are removed 1st (list them) * 2. Zap all the aliases in additon to this domain * */ /* call the auth module to delete the domain from the storage */ /* Note !! We must del domain from auth module __before__ we delete it from * fs, because deletion from auth module may fail !!!! */ /* del a domain from the auth backend which includes : * - drop the domain's table, or del all users from users table * - delete domain's entries from lastauth table * - delete domain's limit's entries */ if (vauth_deldomain(domain) != VA_SUCCESS ) { fprintf (stderr, "Warning: Failed while attempting to delete domain from auth backend\n"); } /* vdel_limits does the following : * If we have mysql_limits enabled, * it will delete the domain's entries from the limits table * Or if we arent using mysql_limits, * it will delete the .qmail-admin file from the domain's dir * * Note there are inconsistencies in the auth backends. Some * will run vdel_limits() in vauth_deldomain(), others don't. * For now, we always run it to be safe. Ultimately, the auth * backends should to be updated to do this. */ vdel_limits(domain); /* delete the dir control info for this domain */ if (vdel_dir_control(domain) != 0) { fprintf (stderr, "Warning: Failed to delete dir_control for %s\n", domain); } /* Now remove domain from filesystem */ /* if it's a symbolic link just remove the link */ if ( S_ISLNK(statbuf.st_mode) ) { if ( unlink(Dir) !=0) { fprintf (stderr, "Warning: Failed to remove symlink for %s\n", domain); } } else { char cwdbuff[MAX_BUFF]; char *cwd; /* Not a symlink.. so we have to del some files structure now */ /* zap the domain's directory tree */ cwd = getcwd (cwdbuff, sizeof(cwdbuff)); /* save calling directory */ if ( vdelfiles(Dir) != 0 ) { fprintf(stderr, "Warning: Failed to delete directory tree: %s\n", domain); } if (cwd != NULL) chdir (cwd); } /* decrement the master domain control info */ snprintf(dircontrol, sizeof(dircontrol), "dom_%lu", (long unsigned)uid); dec_dir_control(dircontrol, uid, gid); } /* The following things need to happen for real and aliased domains */ /* delete the email domain from the qmail control files : * rcpthosts, morercpthosts, virtualdomains */ if (del_control(domain_to_del) != 0) { fprintf (stderr, "Warning: Failed to delete domain from qmail's control files\n"); } /* delete the assign file line */ if (del_domain_assign(domain_to_del, domain, Dir, uid, gid) != 0) { fprintf (stderr, "Warning: Failed to delete domain from the assign file\n"); } /* send a HUP signal to qmail-send process to reread control files */ signal_process("qmail-send", SIGHUP); return(VA_SUCCESS);}/************************************************************************//* * Add a virtual domain user */int vadduser( char *username, char *domain, char *password, char *gecos, int apop ){ char Dir[MAX_BUFF]; char *user_hash; char calling_dir [MAX_BUFF]; uid_t uid = VPOPMAILUID; gid_t gid = VPOPMAILGID; struct vlimits limits; char quota[50]; /* check gecos for : characters - bad */ if ( strchr(gecos,':')!=0) return(VA_BAD_CHAR); if ( strlen(username) >= MAX_PW_NAME ) return(VA_USER_NAME_TOO_LONG);#ifdef USERS_BIG_DIR if ( strlen(username) == 1 ) return(VA_ILLEGAL_USERNAME);#endif if ( strlen(domain) >= MAX_PW_DOMAIN ) return(VA_DOMAIN_NAME_TOO_LONG); if ( strlen(domain) < 3) return(VA_INVALID_DOMAIN_NAME); if ( strlen(password) >= MAX_PW_CLEAR_PASSWD ) return(VA_PASSWD_TOO_LONG);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -