mysqmail-0.4.9/0000755000175000017500000000000011271172140012056 5ustar zigozigomysqmail-0.4.9/SConstruct0000644000175000017500000000200111241670477014116 0ustar zigozigo# first, the imports import os # then the install paths def unfuck(path): while "//" in path: path = path.replace("//","/") return path prefix = os.getenv("PREFIX","/usr/local") destdir = os.getenv("DESTDIR","/") bindir = unfuck(os.path.join( destdir+prefix, "sbin" )) mandir = unfuck(os.path.join( destdir+prefix, "man8" )) # then the environment env = Environment() g = Builder(action = "gzip -9 < $SOURCE > $TARGET") env.Append(BUILDERS={"Gzip":g}) env.Decider('timestamp-newer') # then the targets unique = [ env.Object( t ) for t in env.Glob("mysqmail-*-logger.c") ] common = [ env.Object( t ) for t in env.Split("mydaemon.c myconfig.c") ] # then the dynamic libs libs = ['mysqlclient', 'dotconf'] # then the programs programs = [ env.Program( [ t ] + common, LIBS = libs) for t in unique ] # then the manpages manpages = [ env.Gzip("%s.gz"%s,s) for s in env.Glob("doc/*.8") ] # then the installation all = [ env.Install(k,v) for k,v in {bindir:programs,mandir:manpages}.items() ] env.Alias('install', all) mysqmail-0.4.9/myconfig.c0000644000175000017500000000520611241670477014055 0ustar zigozigo#include #include #include #include #include "myconfig.h" extern mysqmail_config_t mysqmail_config; DOTCONF_CB(cb_mysql_hostname); DOTCONF_CB(cb_mysql_user); DOTCONF_CB(cb_mysql_pass); DOTCONF_CB(cb_mysql_db); DOTCONF_CB(cb_mysql_table_smtp_logs); DOTCONF_CB(cb_mysql_table_pop_access); DOTCONF_CB(cb_mysql_table_scoreboard); DOTCONF_CB(cb_mysql_table_domain); static configoption_t options[] = { {"mysql_hostname", ARG_STR, cb_mysql_hostname, NULL, 0}, {"mysql_user", ARG_STR, cb_mysql_user, NULL, 0}, {"mysql_pass", ARG_STR, cb_mysql_pass, NULL, 0}, {"mysql_db", ARG_STR, cb_mysql_db, NULL, 0}, {"mysql_table_smtp_logs", ARG_STR, cb_mysql_table_smtp_logs, NULL, 0}, {"mysql_table_pop_access", ARG_STR, cb_mysql_table_pop_access, NULL, 0}, {"mysql_table_scoreboard", ARG_STR, cb_mysql_table_scoreboard, NULL, 0}, {"mysql_table_domain", ARG_STR, cb_mysql_table_domain, NULL, 0}, LAST_OPTION }; DOTCONF_CB(cb_mysql_hostname){ mysqmail_config.mysql_hostname = (char*)malloc(strlen(cmd->data.str)+1); strcpy(mysqmail_config.mysql_hostname,cmd->data.str); return NULL; } DOTCONF_CB(cb_mysql_user){ mysqmail_config.mysql_user = (char*)malloc(strlen(cmd->data.str)+1); strcpy(mysqmail_config.mysql_user,cmd->data.str); return NULL; } DOTCONF_CB(cb_mysql_pass){ mysqmail_config.mysql_pass = (char*)malloc(strlen(cmd->data.str)+1); strcpy(mysqmail_config.mysql_pass,cmd->data.str); return NULL; } DOTCONF_CB(cb_mysql_db){ mysqmail_config.mysql_db = (char*)malloc(strlen(cmd->data.str)+1); strcpy(mysqmail_config.mysql_db,cmd->data.str); return NULL; } DOTCONF_CB(cb_mysql_table_smtp_logs){ mysqmail_config.mysql_table_smtp_logs = (char*)malloc(strlen(cmd->data.str)+1); strcpy(mysqmail_config.mysql_table_smtp_logs,cmd->data.str); return NULL; } DOTCONF_CB(cb_mysql_table_pop_access){ mysqmail_config.mysql_table_pop_access = (char*)malloc(strlen(cmd->data.str)+1); strcpy(mysqmail_config.mysql_table_pop_access,cmd->data.str); return NULL; } DOTCONF_CB(cb_mysql_table_scoreboard){ mysqmail_config.mysql_table_scoreboard = (char*)malloc(strlen(cmd->data.str)+1); strcpy(mysqmail_config.mysql_table_scoreboard,cmd->data.str); return NULL; } DOTCONF_CB(cb_mysql_table_domain){ mysqmail_config.mysql_table_domain = (char*)malloc(strlen(cmd->data.str)+1); strcpy(mysqmail_config.mysql_table_domain,cmd->data.str); return NULL; } int read_config_file(){ configfile_t *configfile; configfile = dotconf_create("/etc/mysqmail.conf", options, 0, CASE_INSENSITIVE); if (dotconf_command_loop(configfile) == 0){ syslog(LOG_ERR, "Error reading config file: exiting!"); exit(2); } dotconf_cleanup(configfile); return 0; } mysqmail-0.4.9/mysqmail.spec0000644000175000017500000001313711271172140014573 0ustar zigozigoName: mysqmail Version: 0.4.9 Release: 0.1.20090818 License: LGPL Group: System Environment/Daemons URL: http://www.gplhost.com/software-mysqmail.html Source: mysqmail-%{version}.tar.gz BuildRoot:%{_tmppath}/%{name}-%{version}-%{release}-root BuildRequires: make gcc dotconf-devel mysql-devel Requires: dotconf /usr/bin/tail mysql Summary: Use MySQL accouting and auth for most used MTA (configuration file) Group: System Environment/Daemons %description MySQMail is a set of tiny daemon loggers for Qmail, Postfix, Pure-ftpd and Courier that will save trafic informations in database. It's also a replacement for the qmail standard checkpasswd that does the auth via a MySQL table. When done, it setups 2 more environment variables: MYSQMAIL_USERNAME MYSQMAIL_DOMAINNAME that the mysqmail's qmail-pop3d replacement will use to do the traffic accounting in the MySQL table for this account. This package holds the configuration file management for the other packages which share the same /etc/mysqmail.conf: mysqmail-postfix-logger and mysqmail-courier-logger %package postfix-logger Summary: Use MySQL accouting and auth for most used MTA (postfix logger) Group: System Environment/Daemons Requires: dtc-core, mysqmail %description postfix-logger MySQMail is a set of tiny daemon loggers for Qmail, Postfix, Pure-ftpd and Courier that will save trafic informations in database. It's also a replacement for the qmail standard checkpasswd that does the auth via a MySQL table. When done, it setups 2 more environment variables: MYSQMAIL_USERNAME MYSQMAIL_DOMAINNAME that the mysqmail's qmail-pop3d replacement will use to do the traffic accounting in the MySQL table for this account. This package holds the configuration file management for the other packages which share the same /etc/mysqmail.conf: mysqmail-postfix-logger and mysqmail-courier-logger %package courier-logger Summary: Use MySQL accouting and auth for most used MTA (courier logger) Group: System Environment/Daemons Requires: dtc-core, mysqmail %description courier-logger MySQMail is a set of tiny daemon loggers for Qmail, Postfix, Pure-ftpd and Courier that will save trafic informations in database. It's also a replacement for the qmail standard checkpasswd that does the auth via a MySQL table. When done, it setups 2 more environment variables: MYSQMAIL_USERNAME MYSQMAIL_DOMAINNAME that the mysqmail's qmail-pop3d replacement will use to do the traffic accounting in the MySQL table for this account. This package holds the configuration file management for the other packages which share the same /etc/mysqmail.conf: mysqmail-postfix-logger and mysqmail-courier-logger %package pure-ftpd-logger Summary: Use MySQL accouting and auth for most used MTA (pure-ftpd logger) Group: System Environment/Daemons Requires: dtc-core, mysqmail %description pure-ftpd-logger MySQMail is a set of tiny daemon loggers for Qmail, Postfix, Pure-ftpd and Courier that will save trafic informations in database. It's also a replacement for the qmail standard checkpasswd that does the auth via a MySQL table. When done, it setups 2 more environment variables: MYSQMAIL_USERNAME MYSQMAIL_DOMAINNAME that the mysqmail's qmail-pop3d replacement will use to do the traffic accounting in the MySQL table for this account. This package holds the configuration file management for the other packages which share the same /etc/mysqmail.conf: mysqmail-postfix-logger and mysqmail-courier-logger %package dovecot-logger Summary: Use MySQL accouting and auth for most used MTA (dovecot logger) Group: System Environment/Daemons Requires: dtc-core, mysqmail %description dovecot-logger MySQMail is a set of tiny daemon loggers for Qmail, Postfix, Pure-ftpd and Courier that will save trafic informations in database. It's also a replacement for the qmail standard checkpasswd that does the auth via a MySQL table. When done, it setups 2 more environment variables: MYSQMAIL_USERNAME MYSQMAIL_DOMAINNAME that the mysqmail's qmail-pop3d replacement will use to do the traffic accounting in the MySQL table for this account. This package holds the configuration file management for the other packages which share the same /etc/mysqmail.conf: mysqmail-postfix-logger and mysqmail-courier-logger %prep %setup %build make %install set -e %{__rm} -rf %{buildroot} mkdir -p %{buildroot} make install DESTDIR=%{buildroot} SBIN_DIR=%{_sbindir} MAN_DIR=%{_mandir} INSTALL=install make install-conf DESTDIR=%{buildroot} ETCDIR=%{_sysconfdir} INSTALL=install install -D -m 0755 etc/init.d/mysqmail-postfix-logger %{buildroot}%{_sysconfdir}/rc.d/init.d/mysqmail-postfix-logger install -D -m 0755 etc/init.d/mysqmail-courier-logger %{buildroot}%{_sysconfdir}/rc.d/init.d/mysqmail-courier-logger install -D -m 0755 etc/init.d/mysqmail-pure-ftpd-logger %{buildroot}%{_sysconfdir}/rc.d/init.d/mysqmail-pure-ftpd-logger install -D -m 0755 etc/init.d/mysqmail-dovecot-logger %{buildroot}%{_sysconfdir}/rc.d/init.d/mysqmail-dovecot-logger %pre %clean %{__rm} -rf %{buildroot} 2>&1 >/dev/null %files %defattr(-, root, root, -) %config(noreplace) %{_sysconfdir}/mysqmail.conf %{_mandir}/man?/* %files postfix-logger %{_sbindir}/mysqmail-postfix-logger %{_sysconfdir}/rc.d/init.d/mysqmail-postfix-logger #%{_mandir}/man8/mysqmail-postfix-logger.8 %files courier-logger %{_sbindir}/mysqmail-courier-logger %{_sysconfdir}/rc.d/init.d/mysqmail-courier-logger %files dovecot-logger %{_sbindir}/mysqmail-dovecot-logger %{_sysconfdir}/rc.d/init.d/mysqmail-dovecot-logger %files pure-ftpd-logger %{_sbindir}/mysqmail-pure-ftpd-logger %{_sysconfdir}/rc.d/init.d/mysqmail-pure-ftpd-logger %changelog * Sat Aug 08 2009 Thomas Goirand (zigo) 0.30.4-0.1.20090818 - Initial Package mysqmail-0.4.9/Makefile0000644000175000017500000000720711250713326013530 0ustar zigozigoCFLAGS=-O2 -Wall CC=gcc SBIN_DIR?=/usr/sbin MAN_DIR?=/usr/share/man DESTDIR?="" INSTALL?=install LIBS=-lmysqlclient -ldotconf -L/usr/lib/mysql -L/usr/lib64/mysql EXEC=mysqmail-pure-ftpd-logger mysqmail-postfix-logger mysqmail-courier-logger mysqmail-dovecot-logger MYOB=mydaemon.o myconfig.o HFILES=mydaemon.h myconfig.h all: $(EXEC) mydaemon.o: mydaemon.c mydaemon.h myconfig.o: myconfig.c myconfig.h mysqmail-dovecot-logger.o: mysqmail-dovecot-logger.c $(HFILES) mysqmail-pure-ftpd-logger.o: mysqmail-pure-ftpd-logger.c $(HFILES) mysqmail-postfix-logger.o: mysqmail-postfix-logger.c $(HFILES) mysqmail-courier-logger.o: mysqmail-courier-logger.c $(HFILES) mysqmail-pure-ftpd-logger: mysqmail-pure-ftpd-logger.o $(MYOB) $(HFILES) $(CC) $< $(MYOB) $(LIBS) -o $@ mysqmail-postfix-logger: mysqmail-postfix-logger.o $(MYOB) $(HFILES) $(CC) $< $(MYOB) $(LIBS) -o $@ mysqmail-qmail-logger: mysqmail-qmail-logger.o $(MYOB) $(HFILES) $(CC) $< $(MYOB) $(LIBS) -o $@ mysqmail-courier-logger: mysqmail-courier-logger.o $(MYOB) $(HFILES) $(CC) $< $(MYOB) $(LIBS) -o $@ mysqmail-dovecot-logger: mysqmail-dovecot-logger.o $(MYOB) $(HFILES) $(CC) $< $(MYOB) $(LIBS) -o $@ strip: all for i in $(EXEC) ; do strip $${i} ; done clean: rm -rf *.o $(EXEC) install-pure: mysqmail-pure-ftpd-logger $(INSTALL) -D -m 0755 mysqmail-pure-ftpd-logger $(DESTDIR)$(SBIN_DIR)/mysqmail-pure-ftpd-logger $(INSTALL) -D -m 0644 doc/mysqmail-pure-ftpd-logger.8 $(DESTDIR)$(MAN_DIR)/man8/mysqmail-pure-ftpd-logger.8 gzip -9 $(DESTDIR)$(MAN_DIR)/man8/mysqmail-pure-ftpd-logger.8 install-post: mysqmail-postfix-logger $(INSTALL) -D -m 0755 mysqmail-postfix-logger $(DESTDIR)$(SBIN_DIR)/mysqmail-postfix-logger $(INSTALL) -D -m 0644 doc/mysqmail-postfix-logger.8 $(DESTDIR)$(MAN_DIR)/man8/mysqmail-postfix-logger.8 gzip -9 $(DESTDIR)$(MAN_DIR)/man8/mysqmail-postfix-logger.8 install-qmail: mysqmail-qmail-logger $(INSTALL) -D -m 0755 mysqmail-qmail-logger $(DESTDIR)$(SBIN_DIR)/mysqmail-qmail-logger $(INSTALL) -D -m 0644 doc/mysqmail-qmail-logger.8 $(DESTDIR)$(MAN_DIR)/man8/mysqmail-qmail-logger.8 gzip -9 $(DESTDIR)$(MAN_DIR)/man8/mysqmail-qmail-logger.8 install-courier: mysqmail-courier-logger $(INSTALL) -D -m 0755 mysqmail-courier-logger $(DESTDIR)$(SBIN_DIR)/mysqmail-courier-logger $(INSTALL) -D -m 0644 doc/mysqmail-courier-logger.8 $(DESTDIR)$(MAN_DIR)/man8/mysqmail-courier-logger.8 gzip -9 $(DESTDIR)$(MAN_DIR)/man8/mysqmail-courier-logger.8 install-dovecot: mysqmail-dovecot-logger $(INSTALL) -D -m 0755 mysqmail-dovecot-logger $(DESTDIR)$(SBIN_DIR)/mysqmail-dovecot-logger $(INSTALL) -D -m 0644 doc/mysqmail-dovecot-logger.8 $(DESTDIR)$(MAN_DIR)/man8/mysqmail-dovecot-logger.8 gzip -9 $(DESTDIR)$(MAN_DIR)/man8/mysqmail-dovecot-logger.8 install-conf: $(INSTALL) -D -m 0640 doc/mysqmail.conf $(DESTDIR)$(ETCDIR)/mysqmail.conf install: all $(MAKE) strip $(MAKE) DESTDIR=$(DESTDIR) SBIN_DIR=$(SBIN_DIR) MAN_DIR=$(MAN_DIR) INSTALL=$(INSTALL) install-pure $(MAKE) DESTDIR=$(DESTDIR) SBIN_DIR=$(SBIN_DIR) MAN_DIR=$(MAN_DIR) INSTALL=$(INSTALL) install-post $(MAKE) DESTDIR=$(DESTDIR) SBIN_DIR=$(SBIN_DIR) MAN_DIR=$(MAN_DIR) INSTALL=$(INSTALL) install-courier $(MAKE) DESTDIR=$(DESTDIR) SBIN_DIR=$(SBIN_DIR) MAN_DIR=$(MAN_DIR) INSTALL=$(INSTALL) install-dovecot dist: ./dist rpm: $(MAKE) dist VERS=`head -n 1 debian/changelog | cut -d'(' -f2 | cut -d')' -f1 | cut -d'-' -f1` ; \ PKGNAME=`head -n 1 debian/changelog | cut -d' ' -f1` ; \ cd .. ; rpmbuild -ta $${PKGNAME}-$${VERS}.tar.gz deb: if [ -z $(SIGN)"" ] ; then \ ./deb ; \ else \ ./deb --sign ; \ fi .PHONY: clean install all strip dist rpm install-conf install-courier install-qmail install-post install-pure deb install-dovecot mysqmail-0.4.9/mysqmail-courier-logger.c0000644000175000017500000001537411250147671017023 0ustar zigozigo#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mydaemon.h" #include "myconfig.h" #define DAEMON_NAME "mysqmail-courier-logger" int extern errno; extern char **environ; extern FILE* stdin; extern FILE* stdout; extern FILE* stderr; // Config file reader stuff: using dotconf lib // mysqmail_config_t mysqmail_config; // SQL stuffs MYSQL mysql,*sock; MYSQL_RES *res; /////////////////////////////////////////////////////////////////// // A line of log must be parsed and sent to correct mysql record // /////////////////////////////////////////////////////////////////// #define MAX_LOG_WORDS 32 #define EMAIL_ADDR_SIZE 256 #define MAX_QUERY_SIZE 1024 typedef struct{ long tv_sec; long tv_usec; }timeval_t; unsigned int STR_VALUE(char* value){ if( value == NULL ){ return 0; }else{ return atoi(value); } } int log_a_line(char* logline){ /* Log lines sent by courier should have the following format Jul 13 15:32:52 new imaplogin: LOGIN, user=username@hostname, ip=[::ffff:xxx.xxx.xxx.xxx], protocol=IMAP Jul 13 15:39:15 new imaplogin: DISCONNECTED, user=username@hostname, ip=[::ffff:xxx.xxx.xxx.xxx], headers=1590, body=778 Jul 13 15:40:55 new courierpop3login: Connection, ip=[::ffff:127.0.0.1] Jul 13 15:41:00 new courierpop3login: LOGIN, user=username@hostname, ip=[::ffff:127.0.0.1] Jul 13 15:41:03 new courierpop3login: LOGOUT, user=username@hostname, ip=[::ffff:127.0.0.1], top=0, retr=0 */ char* words[MAX_LOG_WORDS+1]; char query[MAX_QUERY_SIZE+1]=""; char* cur_p; char* end; long mysqmail_bytes = 0; char* user; char* domain; int num_words=0; struct timeval tv; struct tm *ptr; time_t tm; char sql_month[60]; char sql_year[60]; unsigned int i,j; #define M_USER 0 #define M_IP 1 #define M_PROTO 2 #define M_HEADER 3 #define M_BODY 4 #define M_TOP 5 #define M_RETR 6 #define M_RCVD 7 #define M_SENT 8 #define M_TIME 9 #define NUM_KEYWORDS 10 char *keywords[]= {"user", "ip", "protocol", "headers", "body", "top", "retr", "rcvd", "sent", "time"}; char *values[NUM_KEYWORDS+1]; // *** Tokenise the line in words *** cur_p = strtok(logline," "); while(num_words < MAX_LOG_WORDS && (words[num_words++] = cur_p) != NULL){ cur_p = strtok(NULL," "); } num_words--; if (num_words <= 6){ return 0; //not enough data on line } if ( ! (strstr(words[4], "pop3") || strstr(words[4], "imap")) || ! (strstr(words[5], "LOGOUT") || strstr(words[5], "DISCONNECTED"))){ return 0; //this isn't a courier line logout/disconnected, just exit and log nothing! } // These ones is very important, because we will use it in our tests later, to know if a keyword is present in the logged line values[M_USER] = NULL; values[M_IP] = NULL; values[M_PROTO] = NULL; values[M_HEADER] = NULL; values[M_BODY] = NULL; values[M_TOP] = NULL; values[M_RETR] = NULL; values[M_RCVD] = NULL; values[M_SENT] = NULL; values[M_TIME] = NULL; // *** Parse all words in a key / value array *** for(i=6;i #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mydaemon.h" #include "myconfig.h" #define DAEMON_NAME "mysqmail-q-logger" int extern errno; extern char **environ; extern FILE* stdin; extern FILE* stdout; extern FILE* stderr; ///////////////////////////////////////////////// // Config file reader stuff: using dotconf lib // ///////////////////////////////////////////////// mysqmail_config_t mysqmail_config; ///////////////////////// // Mysql connect stuff // ///////////////////////// MYSQL mysql,*sock; MYSQL_RES *res; void cleanup_all_recs(){ char query[256]=""; sprintf(query,"DELETE FROM %s WHERE newmsg_id=NULL;", mysqmail_config.mysql_table_smtp_logs); if(mysql_query(sock,query)){ syslog(LOG_NOTICE, "Query: \"%s\" failed ! %s\n",query,mysql_error(sock)); exit(2); } syslog(LOG_NOTICE, "qmail-logger connected to MySQL !\n"); } /////////////////////////////////////////////////////////////////// // A line of log must be parsed and sent to correct mysql record // /////////////////////////////////////////////////////////////////// // Here is a standard qmail log file thrue syslog: // Feb 24 12:51:15 www qmail: 1109249475.344018 bounce msg 6144008 qp 22053 // The parser will then skip 6 words (from "Feb" to the microtime) // and start analysing at "bounce", so we set SKIP_WORD to 6. // We will also check that the message is fom "qmail:", if not, then it's // another daemon message... #define MAX_LOG_WORDS 1024 #define EMAIL_ADDR_SIZE 256 #define MAX_QUERY_SIZE 1024 #define SKIP_WORD 6 typedef struct{ long tv_sec; long tv_usec; }timeval_t; int log_a_line(char* logline){ /* Log lines sent by qmail should have the following format, as the qmail-log man tels... new msg MSG_NUMBER info msg MSG_NUMBER: bytes NUMBER from SENDER [...] starting delivery D_NUMBER: msg MSG_NUMBER to [local/remote] DELIVERY_EMAIL delivery D_NUMBER: success: delivery D_NUMBER: failure: delivery D_NUMBER: deferral: bounce msg MSG_NUMBER triple bounce: discarding bounce/MSG_NUMBER end msg MSG_NUMBER */ char* words[MAX_LOG_WORDS+1]; char query[MAX_QUERY_SIZE+1]=""; char* delivery_id; char* sender_user; char* sender_domain; char* delivery_user; char* delivery_domain; char* msg_id; char* cur_p; char* tmp; int num_words=0; char* smtp_logs; char ds[1024]; char* ds2; // int i; struct timeval tv; smtp_logs = mysqmail_config.mysql_table_smtp_logs; // Tokenise the line in words ds2 = strdup(logline); cur_p = strtok(logline," "); while( num_words < MAX_LOG_WORDS && (words[num_words++] = cur_p) != NULL){ cur_p = strtok(NULL," "); } num_words--; sprintf(ds,"Found %d words: %s",num_words,ds2); strcpy(query,""); // sprintf(query,""); // new msg MSG_NUMBER if(num_words <= SKIP_WORD || !strstr(words[4], "qmail") || !strcmp(words[SKIP_WORD],"new")){ strcpy(query,""); sprintf(ds,"Founded not parsable log line."); // info msg MSG_NUMBER: bytes NUMBER from SENDER [...] }else if(!strcmp(words[SKIP_WORD],"info") && !strcmp(words[SKIP_WORD+1],"msg")){ sprintf(ds,"Founded info"); msg_id = words[SKIP_WORD+2]; msg_id[ (strlen(msg_id)-1) ] = 0; sender_user = words[SKIP_WORD+6]; // Try to detect bounced or double-bounced messages by sender addr (don't know if it's the good way...) if(!strcmp(sender_user,"<>") || !strcmp(sender_user,"<#@[]>")){ sprintf(query,"UPDATE %s SET bytes='%s',newmsg_id='%s' WHERE bounce_qp='%s';", smtp_logs,words[SKIP_WORD+4],msg_id,words[SKIP_WORD+8]); }else{ sender_user++; sender_domain = strstr(sender_user,"@"); if(sender_domain != NULL){ *sender_domain++ = 0; sender_domain[ (strlen(sender_domain)-1) ] = 0; gettimeofday(&tv,NULL); sprintf(query,"INSERT INTO %s (time_stamp,newmsg_id,bytes,sender_user,sender_domain) VALUES('%ld','%s','%s','%s','%s')", smtp_logs,tv.tv_sec,words[SKIP_WORD+2],words[SKIP_WORD+4],sender_user,sender_domain); } } // starting delivery D_NUMBER: msg MSG_NUMBER to local domain-com-user@domain.com // starting delivery D_NUMBER: msg MSG_NUMBER to remote user@domain.com }else if(!strcmp(words[SKIP_WORD+0],"starting") && !strcmp(words[SKIP_WORD+1],"delivery")){ sprintf(ds,"Founded statring delivery %s",words[SKIP_WORD+2]); delivery_id = words[SKIP_WORD+2]; delivery_id[strlen(delivery_id)-1] = 0; // Remove the : delivery_user = words[SKIP_WORD+7]; delivery_domain = strstr(delivery_user,"@"); // Separate domain & user *delivery_domain++ = 0; if(!strcmp(words[SKIP_WORD+6],"local")){ // If to local, virtual domains does domain-com-user@domain.com // so we have to remove domain-com- from the username... if(strlen(delivery_user)>strlen(delivery_domain)){ char tmp2[256]; char* c__p; strncpy(tmp2,delivery_domain,254); tmp2[254]=0; c__p = &tmp2[0]; while(*c__p){ if(*c__p == '.') *c__p = '-'; c__p++; } *c__p++ = '-'; *c__p++ = 0; if(!strncmp(delivery_user,tmp2,strlen(tmp2))){ delivery_user += strlen(tmp2); } } sprintf(query,"UPDATE %s SET delivery_id='%s',delivery_user='%s',delivery_domain='%s' WHERE newmsg_id='%s';", smtp_logs,delivery_id,delivery_user,delivery_domain,words[SKIP_WORD+4]); }else{ sprintf(query,"UPDATE %s SET delivery_id='%s',delivery_user='%s',delivery_domain='%s' WHERE newmsg_id='%s';", smtp_logs,delivery_id,delivery_user,delivery_domain,words[SKIP_WORD+4]); } // delivery D_NUMBER: }else if(!strcmp(words[SKIP_WORD+0],"delivery")){ sprintf(ds,"Founded delivery #%s",words[SKIP_WORD+1]); delivery_id = words[SKIP_WORD+1]; delivery_id[strlen(delivery_id)-1] = 0; // delivery D_NUMBER: success: if(!strcmp(words[SKIP_WORD+2],"success:")){ sprintf(query,"UPDATE %s SET delivery_success='yes',delivery_id=NULL WHERE delivery_id='%s';", smtp_logs,delivery_id); // delivery D_NUMBER: failure: }else if(!strcmp(words[SKIP_WORD+2],"failure:")){ // delivery D_NUMBER: deferral: }else if(!strcmp(words[SKIP_WORD+2],"deferral:")){ }else{ } // bounce msg 236400 qp 16889 }else if(!strcmp(words[SKIP_WORD+0],"bounce")){ sprintf(ds,"Founded bounce #%s",words[SKIP_WORD+4]); sprintf(query,"UPDATE %s SET bounce_qp='%s' WHERE newmsg_id='%s';", smtp_logs,words[SKIP_WORD+4],words[SKIP_WORD+2]); // triple bounce: discarding bounce/MSG_NUMBER }else if(!strcmp(words[SKIP_WORD+0],"triple")){ sprintf(ds,"Founded tripple bounce #%s",words[SKIP_WORD+3]); tmp = words[SKIP_WORD+3]; tmp += strlen("bounce/"); sprintf(query,"DELETE FROM %s WHERE newmsg_id='%s';", smtp_logs,tmp); // end msg MSG_NUMBER }else if(!strcmp(words[SKIP_WORD+0],"end")){ MYSQL_RES *res; MYSQL_ROW row; char* bytes; char* sender_user; char* sender_domain; char* delivery_user; char* delivery_domain; int num_rows; // Message delivery finished, we should get infos and log them in the scoreboard sprintf(query,"SELECT * FROM %s WHERE newmsg_id='%s';", smtp_logs,words[SKIP_WORD+2]); if(mysql_query(sock,query)){ syslog(LOG_NOTICE, "Query: \"%s\" failed ! %s\n",query,mysql_error(sock)); return 1; } if (!(res=mysql_store_result(sock))){ syslog(LOG_NOTICE, "Couldn't get result from %s\n",mysql_error(sock)); return 1; } num_rows = mysql_num_rows(res); if(num_rows == 1){ row = mysql_fetch_row(res); if(row == NULL){ syslog(LOG_NOTICE, "Couldn't fetch row: %s\n",mysql_error(sock)); }else{ struct tm *ptr; time_t tm; char sql_month[60]; char sql_year[60]; tm = time(NULL); ptr = localtime(&tm); strftime(sql_month ,60 , "\%m",ptr); strftime(sql_year ,60 , "\%Y",ptr); bytes = strdup(row[3]); sender_user = strdup(row[4]); sender_domain = strdup(row[5]); delivery_user = strdup(row[7]); delivery_domain = strdup(row[8]); // Check to see if one of the domain is hosted here // First check sender domain sprintf(query,"SELECT name FROM %s WHERE name='%s'" ,mysqmail_config.mysql_table_domain, sender_domain); if(mysql_query(sock,query)){ syslog(LOG_NOTICE, "Query: \"%s\" failed ! %s\n",query,mysql_error(sock)); return 1; } if (!(res=mysql_store_result(sock))){ syslog(LOG_NOTICE, "Couldn't get result from %s\n",mysql_error(sock)); return 1; } num_rows = mysql_num_rows(res); if(num_rows == 1){ sprintf(query,"INSERT IGNORE INTO %s (domain_name,month,year) VALUES ('%s','%s','%s');" ,mysqmail_config.mysql_table_scoreboard, sender_domain, sql_month, sql_year); if(mysql_query(sock,query)){ syslog(LOG_NOTICE, "Query: \"%s\" failed ! %s\n",query,mysql_error(sock)); return 1; } sprintf(query,"UPDATE %s SET pop_trafic=pop_trafic+%s \ WHERE domain_name='%s' AND month='%s' AND year='%s'" ,mysqmail_config.mysql_table_scoreboard, bytes, sender_domain, sql_month, sql_year); if(mysql_query(sock,query)){ syslog(LOG_NOTICE, "Query: \"%s\" failed ! %s\n",query,mysql_error(sock)); return 1; } } // Then check reciever domain sprintf(query,"SELECT name FROM %s WHERE name='%s'" ,mysqmail_config.mysql_table_domain, delivery_domain); if(mysql_query(sock,query)){ syslog(LOG_NOTICE, "Query: \"%s\" failed ! %s\n",query,mysql_error(sock)); return 1; } if (!(res=mysql_store_result(sock))){ syslog(LOG_NOTICE, "Couldn't get result from %s\n",mysql_error(sock)); return 1; } num_rows = mysql_num_rows(res); if(num_rows == 1){ sprintf(query,"INSERT IGNORE INTO %s (domain_name,month,year) VALUES ('%s','%s','%s');" ,mysqmail_config.mysql_table_scoreboard, delivery_domain, sql_month, sql_year); if(mysql_query(sock,query)){ syslog(LOG_NOTICE, "Query: \"%s\" failed ! %s\n",query,mysql_error(sock)); return 1; } sprintf(query,"UPDATE %s SET pop_trafic=pop_trafic+%s \ WHERE domain_name='%s' AND month='%s' AND year='%s'" ,mysqmail_config.mysql_table_scoreboard, bytes, delivery_domain, sql_month, sql_year); if(mysql_query(sock,query)){ syslog(LOG_NOTICE, "Query: \"%s\" failed ! %s\n",query,mysql_error(sock)); return 1; } } } } mysql_free_result(res); // Delete the message from the log table sprintf(query,"DELETE FROM %s WHERE newmsg_id='%s';", smtp_logs,words[SKIP_WORD+2]); // Message not recognised: send to next piped logger (syslog?) }else{ } // fprintf(stdout,"Query: \"%s\" !\n",query); // Issue the query return from function. if(strlen(query)!=0){ if(mysql_query(sock,query)){ syslog(LOG_NOTICE, "Query: \"%s\" failed ! %s\n",query,mysql_error(sock)); } } return 0; } int main(int argc, char **argv){ int ret; daemonize(); setlogmask (LOG_UPTO (LOG_NOTICE)); openlog(DAEMON_NAME, LOG_NDELAY | LOG_CONS | LOG_PID, LOG_LOCAL1); write_pidfile("mysqlmail-qmail-logger"); reg_hand(); read_config_file(); do_ze_mysql_connect(); cleanup_all_recs(); ret = log_all_lines("syslog",&log_a_line); mysql_close(sock); return ret; } mysqmail-0.4.9/mysqmail-postfix-logger.c0000644000175000017500000003311111250033437017026 0ustar zigozigo#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //#include "mydaemon.h" #include "myconfig.h" #include "mydaemon.h" #define DAEMON_NAME "mysqmail-post-logger" int extern errno; extern char **environ; extern FILE* stdin; extern FILE* stdout; extern FILE* stderr; ///////////////////////////////////////////////// // Config file reader stuff: using dotconf lib // ///////////////////////////////////////////////// mysqmail_config_t mysqmail_config; ///////////////////////// // Mysql connect stuff // ///////////////////////// MYSQL mysql,*sock; MYSQL_RES *res; void cleanup_old_recs (){ char query[256]=""; sprintf(query,"DELETE FROM %s WHERE newmsg_id=NULL;", mysqmail_config.mysql_table_smtp_logs); if(mysql_query(sock,query)){ syslog(LOG_NOTICE, "Query: \"%s\" failed: %s",query,mysql_error(sock)); } syslog(LOG_INFO, "Connected to MySQL!"); return; } /////////////////////////////////////////////////////////////////// // A line of log must be parsed and sent to correct mysql record // /////////////////////////////////////////////////////////////////// #define MAX_LOG_WORDS 32 #define EMAIL_ADDR_SIZE 256 #define MAX_QUERY_SIZE 1024 typedef struct{ long tv_sec; long tv_usec; }timeval_t; int parseEmail(char* source,char** domain,char** user){ char *domain_tmp; int i; if(source[0] == '<' && source[1] == '>'){ *domain = NULL; *user = NULL; return 1; } if(source == NULL) return 0; if(source[0] == '<') source ++; i = strlen(source); if(i > 128) return 0; while ( (source[i-1] == '>') && i > 2){ i--; } source[i] = 0; domain_tmp = strstr(source,"@"); if(domain_tmp == NULL) return 0; // There must be an @ sign if(strlen(domain_tmp) < 4) return 0; // and at least 4 chars after *domain_tmp++ = 0; if(strlen(source) < 1) return 0; *domain = domain_tmp; *user = source; return 1; } int log_a_line(char* logline){ /* Log lines sent by postfix should have the following format Mar 16 00:00:08 postfix/smtpd[]: connect from [] Mar 16 00:00:09 postfix/smtpd[]: NOQUEUE: reject: RCPT from [] Mar 16 00:00:11 postfix/smtpd[]: disconnect from [] Mar 16 00:01:25 postfix/smtpd[]: connect from [] Mar 16 00:01:28 postfix/smtpd[]: : client=[] Mar 16 00:01:29 postfix/cleanup[]: : message-id= Mar 16 00:01:29 postfix/qmgr[]: : from=, size=, nrcpt=1 (queue active) Mar 16 00:01:29 postfix/local[]: : to=, relay=local, delay=4, status=sent (delivered to command: /usr/bin/procmail) Mar 16 00:01:29 postfix/qmgr[]: : removed Mar 16 00:01:29 postfix/smtpd[]: disconnect from []*/ char* words[MAX_LOG_WORDS+1]; char query[MAX_QUERY_SIZE+1]=""; char* delivery_id=NULL; char* sender_user=NULL; char* sender_domain=NULL; char* delivery_user=NULL; char* delivery_domain=NULL; char* cur_p=NULL; char* end=NULL; char* bytes=NULL; int num_words=0,parentesis; char* smtp_logs=NULL; char* scoreboard=NULL; char* domain=NULL; int i,j,ret; #define M_FROM 0 #define M_SIZE 1 #define M_NRCPT 2 #define M_ORIGTO 3 #define M_CLIENT 4 #define M_RELAY 5 #define M_DELAYS 6 #define M_DELAY 7 #define M_STATUS 8 #define M_TO 9 #define M_DSN 10 #define M_MSGID 11 #define NUM_KEYWORDS 12 char *keywords[]= {"from", "size", "nrcpt", "orig_to", "client", "relay", "delays", "delay", "status", "to", "dsn", "message-id"}; char *values[NUM_KEYWORDS+1]; char *tokenizer; smtp_logs = mysqmail_config.mysql_table_smtp_logs; scoreboard = mysqmail_config.mysql_table_scoreboard; domain = mysqmail_config.mysql_table_domain; // Tokenise the line in words tokenizer = " "; num_words = 0; end = logline + strlen(logline); words[num_words++] = logline; cur_p = strtok(logline,tokenizer); while(num_words<6 && cur_p != NULL){ cur_p = strtok(NULL,tokenizer); words[num_words] = cur_p; // fprintf(stdout,"Word %d: \"%s\"\n",num_words,cur_p); num_words++; } cur_p += strlen(cur_p) + 1; while(num_words < MAX_LOG_WORDS && cur_p != NULL && cur_p < end){ if( *cur_p == ','){ *cur_p++ = '\0'; } if( *cur_p == ' '){ *cur_p++ = '\0'; } words[num_words] = cur_p; parentesis = 0; while(cur_p < end && (*cur_p != ',' || parentesis > 0)){ // fprintf(stdout,"Word %d: \"%s\"\n",num_words,cur_p); if(*cur_p == '('){ parentesis++; } if(*cur_p == ')'){ parentesis--; } cur_p++; } num_words++; } // for(i=0;i 6 ){ for(i=6;i, size=286, nrcpt=1 (queue active) // 0 1 2 3 postfix/qmgr[17116]: E8A8E1007DA9: from=, size=569, nrcpt=1 (queue active) // 0 1 2 3 postfix/qmgr[1108]: E0AB117E26: from=<>, size=8507, nrcpt=1 (queue active) // 0 1 2 3 postfix/qmgr[1108]: A4A4A17E2B: from=, size=25573, nrcpt=1 (queue active) //----new to details appear like this: // 0 1 2 3 postfix/virtual[17491]: 04D341007DA9: to=, relay=virtual, delay=0, status=sent (maildir) // 0 1 2 3 postfix/virtual[17491]: E8A8E1007DA9: to=, relay=virtual, delay=0, status=sent (maildir) // 0 1 2 3 postfix/smtp[3191]: E0AB117E26: to=, orig_to=, relay=127.0.0.1[127.0.0.1], delay=2, status=sent (250 2.6.0 Ok, id=03278-02, from MTA: 250 Ok: queued as F0BF617E30) //----reject message // 0 1 2 3 postfix/smtpd[2410]: A1CE861A83: reject: RCPT from unknown[218.246.34.68]: 557 //----bounce message // 0 1 2 3 postfix/local[6334]: A4A4A17E2B: to=, relay=local, delay=1, status=bounced (unknown user: "dee") // *** Do basic checks on the queue ID, then store it in the delivery_id var *** cur_p = words[5]; if( strlen(cur_p) != 12 || cur_p[11] != ':'){ // fprintf(stdout,"postfix id not right!"); return 0; } cur_p[11] = '\0'; delivery_id = cur_p; if(values[M_FROM] != NULL && values[M_SIZE] != NULL){ if(*values[M_SIZE] == '\0'){ return 0; // If size is zero, then we really don't care } ret = parseEmail(values[M_FROM],&sender_user,&sender_domain); if(sender_user == NULL || sender_domain == NULL){ return 0; // If we don't even have a domain, who cares? } sprintf(query,"INSERT INTO %s (sender_user, sender_domain, bytes, delivery_id_text) VALUES ('%s','%s','%s','%s');", smtp_logs, sender_user, sender_domain, values[M_SIZE], delivery_id); if(mysql_query(sock,query)){ syslog(LOG_NOTICE, "Query: \"%s\" failed: %s",query,mysql_error(sock)); } return 0; }else if(values[M_TO] != NULL && values[M_STATUS] != NULL){ MYSQL_RES *res=NULL; MYSQL_ROW row; int num_rows; char sql_month[60]; char sql_year[60]; struct tm *ptr; time_t tm; char* dom_to_search; if( strncmp(values[M_STATUS], "sent",strlen("sent")) !=0){ return 0; // We account only sent messages (which include remotely sent and delivered by maildrop) } ret = parseEmail(values[M_TO],&delivery_domain,&delivery_user); if(ret == 0) return 1; // Get current date tm = time(NULL); ptr = localtime(&tm); strftime(sql_month ,100 , "\%m",ptr); strftime(sql_year ,100 , "\%Y",ptr); // Get the domain name of the sender and the number of bytes from previous saved SQL record sprintf(query,"SELECT bytes, sender_domain FROM %s WHERE delivery_id_text='%s'" , smtp_logs, delivery_id); if(mysql_query(sock,query)){ syslog(LOG_NOTICE, "Query: \"%s\" failed: %s",query,mysql_error(sock)); return 0; } if (!(res=mysql_store_result(sock))){ syslog(LOG_NOTICE, "Couldn't get result from %s\n",mysql_error(sock)); return 0; } num_rows = mysql_num_rows(res); if(num_rows != 1){ mysql_free_result(res); return 0; } row = mysql_fetch_row(res); if(row == NULL){ syslog(LOG_NOTICE, "Couldn't fetch row: %s\n",mysql_error(sock)); if(res != NULL){ mysql_free_result(res); res = NULL; } return 0; } sender_domain = strdup(row[1]); bytes = strdup(row[0]); if(res != NULL){ mysql_free_result(res); res = NULL; } // Maybe we are delivering to a local mailbox, // and we should account to a local domain for delivery // So we check if the domain name we founded is hosted here dom_to_search = delivery_domain; sprintf(query,"SELECT owner,name FROM %s WHERE name='%s'", domain, dom_to_search); if(mysql_query(sock,query)){ syslog(LOG_NOTICE, "Query: \"%s\" failed: %s",query,mysql_error(sock)); return 0; } if (!(res=mysql_store_result(sock))){ syslog(LOG_NOTICE, "Couldn't get result: %s",mysql_error(sock)); if(res != NULL){ mysql_free_result(res); res = NULL; } return 0; } num_rows = mysql_num_rows(res); if(res != NULL){ mysql_free_result(res); res = NULL; } if(num_rows == 1){ // Update the accounting tables sprintf(query,"INSERT IGNORE INTO %s (domain_name,month,year) VALUES ('%s','%s','%s')" , scoreboard, dom_to_search, sql_month, sql_year); if(mysql_query(sock,query)){ syslog(LOG_NOTICE, "Query: \"%s\" failed: %s",query,mysql_error(sock)); } sprintf(query,"UPDATE %s SET smtp_trafic=smtp_trafic+%s WHERE domain_name='%s' AND month='%s' AND year='%s';", scoreboard, bytes, dom_to_search, sql_month, sql_year); if(mysql_query(sock,query)){ syslog(LOG_NOTICE, "Query: \"%s\" failed: %s",query,mysql_error(sock)); } }else{ // It seems it's a mail for the outside. Try to guess if the from= is in our database dom_to_search = sender_domain; sprintf(query,"SELECT owner,name FROM %s WHERE name='%s'", domain, dom_to_search); if(mysql_query(sock,query)){ syslog(LOG_NOTICE, "Query: \"%s\" failed: %s",query,mysql_error(sock)); return 0; } if (!(res=mysql_store_result(sock))){ syslog(LOG_NOTICE, "Couldn't get result from %s\n",mysql_error(sock)); if(res != NULL){ mysql_free_result(res); res = NULL; } return 0; } num_rows = mysql_num_rows(res); if(res != NULL){ mysql_free_result(res); res = NULL; } if(num_rows == 1){ // Update the accounting tables sprintf(query,"INSERT IGNORE INTO %s (domain_name,month,year) VALUES ('%s','%s','%s')" , scoreboard, dom_to_search, sql_month, sql_year); if(mysql_query(sock,query)){ syslog(LOG_NOTICE, "Query: \"%s\" failed: %s",query,mysql_error(sock)); } sprintf(query,"UPDATE %s SET smtp_trafic=smtp_trafic+%s WHERE domain_name='%s' AND month='%s' AND year='%s';", scoreboard, bytes, dom_to_search, sql_month, sql_year); if(mysql_query(sock,query)){ syslog(LOG_NOTICE, "Query: \"%s\" failed: %s",query,mysql_error(sock)); } } } if(sender_domain != NULL){ free(sender_domain); sender_domain = NULL; } if(bytes != NULL){ free(bytes); bytes = NULL; } }else if(strstr(words[6],"removed") ){ sprintf(query,"DELETE FROM %s WHERE delivery_id_text='%s'", smtp_logs, delivery_id); if(mysql_query(sock,query)){ fprintf(stdout,"Query: \"%s\" failed ! %s\n",query,mysql_error(sock)); } } return 0; } int main(int argc, char **argv){ int ret; daemonize(); setlogmask (LOG_UPTO (LOG_NOTICE)); openlog(DAEMON_NAME, LOG_NDELAY | LOG_CONS | LOG_PID, LOG_LOCAL1); syslog (LOG_NOTICE, "Program started by User %d", getuid ()); write_pidfile("mysqmail-postfix-logger"); reg_hand(); read_config_file(); do_ze_mysql_connect(); cleanup_old_recs(); ret = log_all_lines("syslog",&log_a_line); mysql_close(sock); return ret; } mysqmail-0.4.9/mydaemon.h0000644000175000017500000000032311250032114014027 0ustar zigozigo#define MAX_LOG_LINE 1024 void sighand(int sig); void daemonize(); void reg_hand(); void write_pidfile(char* daemon_name); int log_all_lines( char* fifo_path, int (*log_me)(char*) ); int do_ze_mysql_connect(); mysqmail-0.4.9/doc/0000755000175000017500000000000011265702507012634 5ustar zigozigomysqmail-0.4.9/doc/mysqmail-qmail-logger.80000644000175000017500000000264311243461765017150 0ustar zigozigo.TH mysqmail-qmail-logger 8 .SH NAME mysqmail-qmail-logger \- logs smtp traffic to a mysql database .SH SYNOPSIS .B mysqmail-qmail-logger .I subprogram [ .I args ... ] .SH DESCRIPTION .B mysqmail-qmail-logger reads the syslog using tail -F and for each lines, does an action in a selected table in the selected mysql server (configuration done using /etc/mysqmail.conf). All messages that have been delivered are marqued as so in the from and in the to field of the smtp traffic table. .B mysqmail-qmail-logger uses a table corresponding to this one: CREATE TABLE IF NOT EXISTS smtp_logs ( id int(11) NOT NULL auto_increment, newmsg_id bigint(20) default NULL, bounce_qp int(11) default NULL, bytes int(11) NOT NULL default '0', sender_user varchar(128) NOT NULL default '', sender_domain varchar(128) NOT NULL default '', delivery_id bigint(20) default NULL, delivery_user varchar(128) NOT NULL default '', delivery_domain varchar(128) NOT NULL default '', delivery_success enum('yes','no') NOT NULL default 'no', time_stamp timestamp(14) NOT NULL, PRIMARY KEY (id), UNIQUE KEY bounce_qp (bounce_qp), UNIQUE KEY newmsg_id (newmsg_id), KEY sender_domain (sender_domain), KEY delivery_domain (delivery_domain) ) TYPE=MyISAM; .SH "VERSION" This documentation describes .B mysqmail-logger version 0.1.4. See .B http://gplhost.com/?rub=softwares for updates. .SH "SEE ALSO" qmail-send(3), syslog(3), logger(8) mysqmail-0.4.9/doc/LICENSE0000644000175000017500000006346710645603430013655 0ustar zigozigo GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! mysqmail-0.4.9/doc/README0000644000175000017500000001211311243463233013506 0ustar zigozigoHi folks ! What does do MySQMail ? ----------------------- MySQmail helps keeping all the qmail MTA login and trafic informations in a database, and IN REAL TIME. This tool was written mostly to work with Domain Technologie Control (DTC). MySQMail is not mandatory for DTC, but it's a way better to have it. I needed to have qmail accounting, so I had to write couples of binaries. Fortunatly qmail is modular and it's realy easy to make some add-ons. Here is what MySQmail package will add to qmail: pop will use MySQL backend for password, and both SMTP and POP will have trafic logged into MySQL by mailbox. MySQmail requipement -------------------- - mysql client library and dev files - dotconf lib for reading conf file /etc/mysqmail.conf MySQmail binaries ----------------- MySQMail is made of few binaries. It currently consists of 4 loggers that are all working IN REAL TIME, so you can monitor your email traffic in real time as well. - mysqmail-postfix-logger: A mysql logger for postfix. Reads from syslog using tail -F and send corresponding results in a MySQL table, so you have monthly statistics. - mysqmail-courier-logger: Seaches for POP3 / IMAP connections printed in the syslog, and generate monthly traffic report in SQL. - mysqmail-dovecot-logger: Same as mysqmail-courier-logger, but for the much faster dovecot daemon. - mysqmail-pure-ftpd-logger: Logs traffic from /var/log/pure-ftpd/transfer.log or /var/log/pureftpd.log (which ever is present in your system, the first one above having priority), using tail -F, and writes a score board in MySQL. - mysqmail-qmail-logger: A mysql logger for qmail-send. It will read from the syslog using tail -F, and all successfull delivery (either remote or local) will generate a tiny mysql record containing remote and local user and domain name. - mysqmail-checkpwd: A mysql alternative checkpassword for qmail-pop3d. Reads the passwords and mailbox account infos for pop3 from a mysql db, and set env variable DOMAINNAME and USER to be used for putting trafic info in the DB. - mysqmail-pop3d-patch: A qmail-pop3d.c patched version of the orriginal one from DJB that logs the trafic of all pop3 sessions users into the password table. MySQmail config file -------------------- In order to read it's /etc/mysqmail.conf config file, all of the MySQmail binaries uses libdotconf. This small library is perfect for reading one program config file, and I encourage all developpers to use it. It's available for all Unix and is compatible with config files like apache's httpd.conf. MySQmail has been written using libdotconf 1.0.9, with it's config files directive set as case insensitive. No need to explain the content of this config file, I suppose you know what is a mysql login/password... MySQail instalation ------------------- Get qmail sources, apply my qmail-pop3d patch, and recopile qmail-pop3d binary. Don't forget that you need both mysqlclient and dotconf library installed in your system (and -dev or -devel flavor maybe). The provided Makefile only adds mysql and dotconf to linking. Install the new qmail-pop3d it according to your system (normaly in /var/qmail/bin). Compile and install mysqmail-checkpwd and mysqmail-logger using: > make install Edit your /etc/mysqmail.conf so all 3 binaries can log in the mysql server. Then edit your qmail start file so it uses the new programs for loggin. Note that mysqmail-logger will flush all unknown or non-trafic logs to standard output, so you can still keep a logfile for status/alert/etc... Keep the normal system logger for qmail-smptd, only qmail-send gives accounting informations. Change the qmail checkpasswd to the new one for pop3. Here is what I have on my debian machine. Note that this may differ on some other system, aspecialy if you are using the svscan (daemontools) stuff, but it's by the way a good example. sh -c "start-stop-daemon --start --quiet --user qmails \ --exec /usr/sbin/qmail-send \ --startas /usr/sbin/qmail-start -- \"Maildir\" \ /usr/sbin/mysqmail-logger >>/var/log/qmail.log &" sh -c "start-stop-daemon --start --quiet --user root \ --exec /usr/bin/tcpserver -v -- \ 0 pop-3 /usr/sbin/qmail-popup `hostname` \ /usr/sbin/mysqmail-checkpwd /usr/sbin/qmail-pop3d Maildir The POP MySQL Database ---------------------- Be sure that you have your tables corresponding to the ones described in the *.sql scripts. Remember that because of virtual domains support, pop3 logins will be of the style "user@host.com" (and NOT simply "user"). Credit ------ All code and patch done by Thomas GOIRAND Mysql password check code was written reading the code from Jedi's alternative checklocalpwd programm. Thanks him for that good example and usefull auth. Mysql logger was written from scratch. Message to DJB -------------- On next version, please add login & traffic log to your pop3d. Please include all the good ideas other developpers have add to your software and release a toaster with more options. Why don't you release something with all options (relay control, SSL, password auths, spam prevention, etc...) BUT configurable with some simple config files ? mysqmail-0.4.9/doc/mysqmail-courier-logger.80000644000175000017500000000402411265702463017505 0ustar zigozigo.TH mysqmail-logger 8 .SH NAME mysqmail-courier-logger \- logs courier-imap traffic to a mysql database .SH SYNOPSIS .B mysqmail-courier-logger .I subprogram [ .I args ... ] .SH DESCRIPTION .B mysqmail-courier-logger reads the syslog using tail \-F, and for each dovecot lines of POP3 and IMAP corresponding to a disconnection, mysqmail-courier-logger writes the total used bandwidth corresponding to the username. Configuration is read from /etc/mysqmail.conf. .B mysqmail-courier-logger uses a table corresponding to this one: CREATE TABLE IF NOT EXISTS pop_access ( id varchar(32) NOT NULL default '', uid int(11) NOT NULL default '65534', gid int(11) NOT NULL default '65534', home varchar(255) NOT NULL default '', shell varchar(255) NOT NULL default '', mbox_host varchar(120) NOT NULL default '', crypt varchar(50) NOT NULL default '', passwd varchar(50) NOT NULL default '', active int(11) NOT NULL default '1', start_date date NOT NULL default '0000-00-00', expire_date date NOT NULL default '0000-00-00', quota_size int(11) NOT NULL default '0', type varchar(20) NOT NULL default 'default', memo text,du bigint(20) NOT NULL default '0', another_perso varchar(5) NOT NULL default 'no', redirect1 varchar(255) default NULL, redirect2 varchar(255) default NULL, localdeliver varchar(10) NOT NULL default 'yes', pop3_login_count int(9) NOT NULL default '0', pop3_transfered_bytes int(14) NOT NULL default '0', imap_login_count int(9) NOT NULL default '0', imap_transfered_bytes int(14) NOT NULL default '0', last_login int(14) NOT NULL default '0', PRIMARY KEY (id,mbox_host) ) TYPE=MyISAM To have mysqmail-courier-logger working, you should have issued mkfifo /var/log/courier.fifo to create the fifo and use nohup cat /var/log/courier.fifo | /usr/bin/mysqmail-courier-logger > /dev/null 2>&1 & to launch it. .SH "VERSION" This documentation describes .B mysqmail-courier-logger version 0.1.4. See .B http://gplhost.com/softwares-mysqmail.html for updates. .SH "SEE ALSO" qmail-send(3), syslog(3), logger(8) mysqmail-0.4.9/doc/mysqmail.conf0000644000175000017500000000046010645603430015333 0ustar zigozigo# # This is the MySQmail config file, parsed using dotconf lib # Normaly, should be in /etc/mysqmail.conf # # # Mysql server connection stuff (self explanatory) # mysql_hostname localhost mysql_user root mysql_pass XXXXXX mysql_db dtc mysql_table_smtp_logs smtp_logs mysql_table_pop_access pop_access mysqmail-0.4.9/doc/mysqmail-pure-ftpd-logger.80000644000175000017500000000113510645603430017736 0ustar zigozigo.TH mysqmail-pure-ftpd-logger 8 .SH NAME mysqmail-pure-ftpd-logger \- logs pureftpd traffic to a mysql database .SH SYNOPSIS .B mysqmail-pure-ftpd-logger .I subprogram [ .I args ... ] .SH DESCRIPTION .B mysqmail-pure-ftpd-logger reads standard input and for each lines, does an action in a selected table in the selected mysql server (configuration done using /etc/mysqmail.conf). .B mysqmail-pure-ftpd-logger uses a table corresponding to this one: todo... .SH "VERSION" This documentation describes .B mysqmail-pure-ftpd-logger version 0.3.0-R0. See .B http://gplhost.com/?rub=softwares for updates. mysqmail-0.4.9/doc/mysqmail-dovecot-logger.80000644000175000017500000000353711265702507017507 0ustar zigozigo.TH mysqmail-dovecot-logger 8 .SH NAME mysqmail-dovecot-logger \- logs dovecot-imapd and dovecot-pop3d traffic to a mysql database .SH SYNOPSIS .B mysqmail-dovecot-logger .I subprogram [ .I args ... ] .SH DESCRIPTION .B mysqmail-dovecot-logger reads the syslog using tail \-F, and for each dovecot lines of POP3 and IMAP corresponding to a disconnection, mysqmail-dovecot-logger writes the total used bandwidth corresponding to the username. Configuration is read from /etc/mysqmail.conf. .B mysqmail-dovecot-logger uses a table corresponding to this one: CREATE TABLE IF NOT EXISTS pop_access ( id varchar(32) NOT NULL default '', uid int(11) NOT NULL default '65534', gid int(11) NOT NULL default '65534', home varchar(255) NOT NULL default '', shell varchar(255) NOT NULL default '', mbox_host varchar(120) NOT NULL default '', crypt varchar(50) NOT NULL default '', passwd varchar(50) NOT NULL default '', active int(11) NOT NULL default '1', start_date date NOT NULL default '0000-00-00', expire_date date NOT NULL default '0000-00-00', quota_size int(11) NOT NULL default '0', type varchar(20) NOT NULL default 'default', memo text,du bigint(20) NOT NULL default '0', another_perso varchar(5) NOT NULL default 'no', redirect1 varchar(255) default NULL, redirect2 varchar(255) default NULL, localdeliver varchar(10) NOT NULL default 'yes', pop3_login_count int(9) NOT NULL default '0', pop3_transfered_bytes int(14) NOT NULL default '0', imap_login_count int(9) NOT NULL default '0', imap_transfered_bytes int(14) NOT NULL default '0', last_login int(14) NOT NULL default '0', PRIMARY KEY (id,mbox_host) ) TYPE=MyISAM .SH "VERSION" This documentation describes .B mysqmail-dovecot-logger version 0.4.4 or superior. See .B http://gplhost.com/softwares-mysqmail.html for updates. .SH "SEE ALSO" qmail-send(3), syslog(3), logger(8) mysqmail-0.4.9/doc/changelog0000644000175000017500000000060011251055447014501 0ustar zigozigo07-09-2009: - Full rewrite of all parsers, much more stable - Now piping using tail -f to avoid silly fifo - Read daemon using fork at startup - Creates pid files - Has a RPM port 21-11-2006: Created a unique source package for Debian. 21-02-2005: splitted mysqmail into different packages. Old version: Hi folks ! Enjoy tranfic accouting now ! Thomas GOIRAND mysqmail-0.4.9/doc/mysqmail.conf.proposal0000644000175000017500000000146010645603430017172 0ustar zigozigo# # This is the MySQmail config file, parsed using dotconf lib # Normaly, should be in /etc/mysqmail.conf # # # Mysql server connection stuff (self explanatory) # mysql_hostname localhost mysql_user root mysql_pass XXXXXX mysql_db dtc mysql_table_smtp_logs smtp_logs mysql_table_pop_access pop_access # # mysqmail-logger options (not used for the moment # # If set to yes, then you can use multilog or syslog piped after mysqmail-logger logger_forward_to_stdout yes # If set to yes, start msg/info msg/starting delivery/delivery/bounce & # tripple bounce will not be forwarded to stdout, so you get only # serious/errors debuggin informations logged to file logger_forward_only_unknown yes # If set to yes, then all queries issued to SQL are sent to stdout for # debugging purpose logger_queries_to_stdout no mysqmail-0.4.9/doc/mysqmail-postfix-logger.80000644000175000017500000000266011265702473017536 0ustar zigozigo.TH mysqmail-postfix-logger 8 .SH NAME mysqmail-postfix-logger \- logs smtp traffic to a mysql database .SH SYNOPSIS .B mysqmail-postfix-logger .I subprogram [ .I args ... ] .SH DESCRIPTION .B mysqmail-postfix-logger reads the syslog using tail \-F and for each lines, does an action in a selected table in the selected mysql server (configuration done using /etc/mysqmail.conf). All messages that have been delivered are marqued as so in the from and in the to field of the smtp traffic table. .B mysqmail-postfix-logger uses a table corresponding to this one: CREATE TABLE IF NOT EXISTS smtp_logs ( id int(11) NOT NULL auto_increment, newmsg_id bigint(20) default NULL, bounce_qp int(11) default NULL, bytes int(11) NOT NULL default '0', sender_user varchar(128) NOT NULL default '', sender_domain varchar(128) NOT NULL default '', delivery_id bigint(20) default NULL, delivery_user varchar(128) NOT NULL default '', delivery_domain varchar(128) NOT NULL default '', delivery_success enum('yes','no') NOT NULL default 'no', time_stamp timestamp(14) NOT NULL, PRIMARY KEY (id), UNIQUE KEY bounce_qp (bounce_qp), UNIQUE KEY newmsg_id (newmsg_id), KEY sender_domain (sender_domain), KEY delivery_domain (delivery_domain) ) TYPE=MyISAM; .SH "VERSION" This documentation describes .B mysqmail-postfix-logger version 0.1.4. See .B http://gplhost.com/softwares-mysqmail.html for updates. .SH "SEE ALSO" syslog(3), logger(8) mysqmail-0.4.9/doc/changelog.Debian0000644000175000017500000000016610645603430015666 0ustar zigozigodtc Debian maintainer and upstream author are identical. Therefore see also normal changelog file for Debian changes. mysqmail-0.4.9/doc/copyright0000644000175000017500000000040310645603430014560 0ustar zigozigoThis package was done by the creator of mysqmail: Thomas GOIRAND See http://gplhost.com for details and project homepage. On Debian systems, a copy of the GNU General Public License (GPL) may be found in /usr/share/common-licenses/LGPL. mysqmail-0.4.9/mydaemon.c0000644000175000017500000001041311250033517014033 0ustar zigozigo#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mydaemon.h" #include "myconfig.h" // Filename of the pid static char pid_fn[8096]; extern MYSQL mysql,*sock; extern mysqmail_config_t mysqmail_config; int do_ze_mysql_connect(){ mysql_init(&mysql); if (!(sock = mysql_real_connect(&mysql,mysqmail_config.mysql_hostname, mysqmail_config.mysql_user,mysqmail_config.mysql_pass,mysqmail_config.mysql_db,0,NULL,0))){ syslog(LOG_NOTICE, "Couldn't connect to engine: %s",mysql_error(&mysql)); exit(2); } syslog(LOG_INFO, "Connected to MySQL!"); return 0; } int check_sql_connection (){ int alive; alive = mysql_ping(sock); if(alive != 0){ mysql_close(sock); if (!(sock = mysql_real_connect(&mysql,mysqmail_config.mysql_hostname, mysqmail_config.mysql_user,mysqmail_config.mysql_pass,mysqmail_config.mysql_db,0,NULL,0))){ syslog(LOG_NOTICE, "Couldn't connect to engine: %s",mysql_error(&mysql)); return 1; }else{ syslog(LOG_NOTICE, "Reconnection to MySQL server with success!"); return 0; } } return 0; } void my_cleanups(){ sigset_t oldset; sigset_t newset; // Make it so a SIGQUIT / SIGTERM also kills the tail that we are piping. // Before calling killpg() we block SIGTERM to not receive it a 2nd time // and enter a signal loop. sigemptyset( &newset ); sigaddset( &newset, SIGTERM ); sigprocmask(SIG_BLOCK, &newset, &oldset); killpg(0,SIGTERM); sigprocmask(SIG_SETMASK, &oldset, NULL); // Close the MySQL connection mysql_close(sock); // Unlink the /var/run/DAEMON-NAME.pid unlink(pid_fn); } // Signal handler (HUP, TERM, etc.) void sighand(int sig) { switch(sig) { case SIGHUP: break; case SIGQUIT: case SIGTERM: my_cleanups(); exit(0); break; default: break; } } void daemonize(){ pid_t pid; pid = fork(); // Fork failed if (pid < 0) { exit(EXIT_FAILURE); } // We're parent, exit if (pid > 0) { exit(EXIT_SUCCESS); } // We're the child, get our pid if ((pid = getpid()) < 0) { exit(EXIT_FAILURE); } // Forwards the signals to the process we will open with popen, and be a session leader setsid(); return; } void reg_hand(){ signal(SIGHUP, sighand); signal(SIGTERM, sighand); signal(SIGINT, sighand); signal(SIGQUIT, sighand); } void write_pidfile(char* daemon_name){ pid_t pid; FILE* fp; if ((pid = getpid()) < 0) { exit(EXIT_FAILURE); } strcpy(pid_fn,"/var/run/"); strcat(pid_fn,daemon_name); strcat(pid_fn,".pid"); fp = fopen(pid_fn,"w"); if( fp == NULL ){ syslog(LOG_NOTICE, "Couldn't open PID file %s: exiting.", pid_fn); my_cleanups(); }else{ fprintf(fp,"%d\n",pid); fclose(fp); } } // This read from fifo_path until EOF and reopen if the fifo // is closed for whatever reason (eg: syslog restart) int log_all_lines( char* file_path, int (*log_me)(char*) ){ static char logline[MAX_LOG_LINE+1]; FILE* fifo_p; char* tail_cmd; // Manages 2 style of syslog file location if(strcmp("syslog",file_path) == 0){ if( access("/var/log/syslog", R_OK) == 0){ syslog(LOG_NOTICE, "Dedected syslog in /var/log/syslog"); tail_cmd = "tail -n 0 -F /var/log/syslog"; }else if( access("/var/log/messages", R_OK) == 0){ syslog(LOG_NOTICE, "Dedected syslog in /var/log/messages"); tail_cmd = "tail -n 0 -F /var/log/messages"; }else{ return 1; } }else{ if( access(file_path, R_OK) != 0){ syslog(LOG_NOTICE, "Cannot access file_path"); return 1; } syslog(LOG_NOTICE, "Using %s", file_path); tail_cmd = malloc( strlen("tail -n 0 -F ") + strlen(file_path) + 1); if( tail_cmd == NULL){ return 1; } strcpy(tail_cmd,"tail -n 0 -F "); strcat(tail_cmd,file_path); } fifo_p = popen(tail_cmd,"r"); if(fifo_p == NULL){ syslog(LOG_NOTICE, "Couldn't open pipe to syslog: exiting."); free(tail_cmd); return 1; } while(NULL != fgets(logline,MAX_LOG_LINE-1,fifo_p)){ check_sql_connection(); log_me(logline); } syslog(LOG_NOTICE, "End of syslog pipe: exiting!"); free(tail_cmd); my_cleanups(); return 0; } mysqmail-0.4.9/buildrpm0000755000175000017500000000040511242570612013625 0ustar zigozigo#!/bin/bash set -e cd ` dirname "$0" ` ver=`grep Version: mysqmail.spec | awk ' { print $2 } ' ` cd .. echo "Compressing the MySQMail source code..." tar --exclude mysqmail-0.4.4/.git -czf mysqmail-$ver.tar.gz mysqmail-0.4.4 rpmbuild -ta mysqmail-$ver.tar.gz mysqmail-0.4.9/etc/0000755000175000017500000000000011247425101012632 5ustar zigozigomysqmail-0.4.9/etc/init.d/0000755000175000017500000000000011247425101014017 5ustar zigozigomysqmail-0.4.9/etc/init.d/mysqmail-qmail-logger0000644000175000017500000000275511247425101020165 0ustar zigozigo#!/bin/sh # mysqmail-qmail-logger Logs qmail traffic to MySQL # # chkconfig: 345 99 00 # description: mysqmail-qmail-logger logs all the traffic of your MTA # to a MySQL database in order to keep monthly score boards in DTC. # processname: mysqmail-qmail-logger # pidfile: /var/run/mysqmail-qmail-logger.pid # config: /etc/mysqmail.conf # # Based on Postfix startup script distributed in Fedora . /etc/rc.d/init.d/functions PATH=$PATH:/bin:/usr/bin:/usr/sbin DESC="MySQMail qmail logger" NAME=mysqmail-qmail-logger DAEMON=/usr/sbin/${NAME} PID_FILE=/var/run/${NAME}.pid if [ ! -x ${DAEMON} ] ; then echo "${DAEMON} not executable or not present!" exit 1 fi if [ ! -f /etc/mysqmail.conf ] ; then echo "Config file /etc/mysqmail.conf not found: exiting" exit 1 fi status -p $PID_FILE ${NAME} >/dev/null 2>&1 running=$? start() { # Start daemons. echo -n $"Starting ${DESC}: " daemon ${DAEMON} RETVAL=$? echo "" return $RETVAL } stop() { # Stop daemons. echo -n $"Stopping ${DESC}: " killproc -p $${PID_FILE} ${NAME} && success || failure $"$prog stop" RETVAL=$? echo "" return $RETVAL } # See how we were called. case "$1" in start) if [ $running -eq 0 ] ; then exit 0 fi start ;; stop) if ! [ $running -eq 0 ] ; then exit 0 fi stop ;; restart) stop start ;; status) status -p $pidfile dtc-xen ;; condrestart) if ! [ $running -eq 0 ] ; then exit 0 fi stop start ;; *) echo $"Usage: $0 {start|stop|restart|status|condrestart}" exit 2 esac exit $? mysqmail-0.4.9/etc/init.d/mysqmail-pure-ftpd-logger0000644000175000017500000000301111247425101020752 0ustar zigozigo#!/bin/sh # mysqmail-pure-ftpd-logger Logs pure-ftpd traffic to MySQL # # chkconfig: 345 99 00 # description: mysqmail-pure-ftpd-logger logs all the traffic of your MTA # to a MySQL database in order to keep monthly score boards in DTC. # processname: mysqmail-pure-ftpd-logger # pidfile: /var/run/mysqmail-pure-ftpd-logger.pid # config: /etc/mysqmail.conf # # Based on Postfix startup script distributed in Fedora . /etc/rc.d/init.d/functions PATH=$PATH:/bin:/usr/bin:/usr/sbin DESC="MySQMail pure-ftpd logger" NAME=mysqmail-pure-ftpd-logger DAEMON=/usr/sbin/${NAME} PID_FILE=/var/run/${NAME}.pid if [ ! -x ${DAEMON} ] ; then echo "${DAEMON} not executable or not present!" exit 1 fi if [ ! -f /etc/mysqmail.conf ] ; then echo "Config file /etc/mysqmail.conf not found: exiting" exit 1 fi status -p $PID_FILE ${NAME} >/dev/null 2>&1 running=$? start() { # Start daemons. echo -n $"Starting ${DESC}: " daemon ${DAEMON} RETVAL=$? echo "" return $RETVAL } stop() { # Stop daemons. echo -n $"Stopping ${DESC}: " killproc -p $${PID_FILE} ${NAME} && success || failure $"$prog stop" RETVAL=$? echo "" return $RETVAL } # See how we were called. case "$1" in start) if [ $running -eq 0 ] ; then exit 0 fi start ;; stop) if ! [ $running -eq 0 ] ; then exit 0 fi stop ;; restart) stop start ;; status) status -p $pidfile dtc-xen ;; condrestart) if ! [ $running -eq 0 ] ; then exit 0 fi stop start ;; *) echo $"Usage: $0 {start|stop|restart|status|condrestart}" exit 2 esac exit $? mysqmail-0.4.9/etc/init.d/mysqmail-courier-logger0000644000175000017500000000277311247425101020532 0ustar zigozigo#!/bin/sh # mysqmail-courier-logger Logs courier traffic to MySQL # # chkconfig: 345 99 00 # description: mysqmail-courier-logger logs all the traffic of your MTA # to a MySQL database in order to keep monthly score boards in DTC. # processname: mysqmail-courier-logger # pidfile: /var/run/mysqmail-courier-logger.pid # config: /etc/mysqmail.conf # # Based on Postfix startup script distributed in Fedora . /etc/rc.d/init.d/functions PATH=$PATH:/bin:/usr/bin:/usr/sbin DESC="MySQMail courier logger" NAME=mysqmail-courier-logger DAEMON=/usr/sbin/${NAME} PID_FILE=/var/run/${NAME}.pid if [ ! -x ${DAEMON} ] ; then echo "${DAEMON} not executable or not present!" exit 1 fi if [ ! -f /etc/mysqmail.conf ] ; then echo "Config file /etc/mysqmail.conf not found: exiting" exit 1 fi status -p $PID_FILE ${NAME} >/dev/null 2>&1 running=$? start() { # Start daemons. echo -n $"Starting ${DESC}: " daemon ${DAEMON} RETVAL=$? echo "" return $RETVAL } stop() { # Stop daemons. echo -n $"Stopping ${DESC}: " killproc -p $${PID_FILE} ${NAME} && success || failure $"$prog stop" RETVAL=$? echo "" return $RETVAL } # See how we were called. case "$1" in start) if [ $running -eq 0 ] ; then exit 0 fi start ;; stop) if ! [ $running -eq 0 ] ; then exit 0 fi stop ;; restart) stop start ;; status) status -p $pidfile dtc-xen ;; condrestart) if ! [ $running -eq 0 ] ; then exit 0 fi stop start ;; *) echo $"Usage: $0 {start|stop|restart|status|condrestart}" exit 2 esac exit $? mysqmail-0.4.9/etc/init.d/mysqmail-postfix-logger0000644000175000017500000000277311247425101020556 0ustar zigozigo#!/bin/sh # mysqmail-postfix-logger Logs postfix traffic to MySQL # # chkconfig: 345 99 00 # description: mysqmail-postfix-logger logs all the traffic of your MTA # to a MySQL database in order to keep monthly score boards in DTC. # processname: mysqmail-postfix-logger # pidfile: /var/run/mysqmail-postfix-logger.pid # config: /etc/mysqmail.conf # # Based on Postfix startup script distributed in Fedora . /etc/rc.d/init.d/functions PATH=$PATH:/bin:/usr/bin:/usr/sbin DESC="MySQMail postfix logger" NAME=mysqmail-postfix-logger DAEMON=/usr/sbin/${NAME} PID_FILE=/var/run/${NAME}.pid if [ ! -x ${DAEMON} ] ; then echo "${DAEMON} not executable or not present!" exit 1 fi if [ ! -f /etc/mysqmail.conf ] ; then echo "Config file /etc/mysqmail.conf not found: exiting" exit 1 fi status -p $PID_FILE ${NAME} >/dev/null 2>&1 running=$? start() { # Start daemons. echo -n $"Starting ${DESC}: " daemon ${DAEMON} RETVAL=$? echo "" return $RETVAL } stop() { # Stop daemons. echo -n $"Stopping ${DESC}: " killproc -p $${PID_FILE} ${NAME} && success || failure $"$prog stop" RETVAL=$? echo "" return $RETVAL } # See how we were called. case "$1" in start) if [ $running -eq 0 ] ; then exit 0 fi start ;; stop) if ! [ $running -eq 0 ] ; then exit 0 fi stop ;; restart) stop start ;; status) status -p $pidfile dtc-xen ;; condrestart) if ! [ $running -eq 0 ] ; then exit 0 fi stop start ;; *) echo $"Usage: $0 {start|stop|restart|status|condrestart}" exit 2 esac exit $? mysqmail-0.4.9/etc/init.d/mysqmail-dovecot-logger0000644000175000017500000000277311247425101020525 0ustar zigozigo#!/bin/sh # mysqmail-dovecot-logger Logs dovecot traffic to MySQL # # chkconfig: 345 99 00 # description: mysqmail-dovecot-logger logs all the traffic of your MTA # to a MySQL database in order to keep monthly score boards in DTC. # processname: mysqmail-dovecot-logger # pidfile: /var/run/mysqmail-dovecot-logger.pid # config: /etc/mysqmail.conf # # Based on Postfix startup script distributed in Fedora . /etc/rc.d/init.d/functions PATH=$PATH:/bin:/usr/bin:/usr/sbin DESC="MySQMail dovecot logger" NAME=mysqmail-dovecot-logger DAEMON=/usr/sbin/${NAME} PID_FILE=/var/run/${NAME}.pid if [ ! -x ${DAEMON} ] ; then echo "${DAEMON} not executable or not present!" exit 1 fi if [ ! -f /etc/mysqmail.conf ] ; then echo "Config file /etc/mysqmail.conf not found: exiting" exit 1 fi status -p $PID_FILE ${NAME} >/dev/null 2>&1 running=$? start() { # Start daemons. echo -n $"Starting ${DESC}: " daemon ${DAEMON} RETVAL=$? echo "" return $RETVAL } stop() { # Stop daemons. echo -n $"Stopping ${DESC}: " killproc -p $${PID_FILE} ${NAME} && success || failure $"$prog stop" RETVAL=$? echo "" return $RETVAL } # See how we were called. case "$1" in start) if [ $running -eq 0 ] ; then exit 0 fi start ;; stop) if ! [ $running -eq 0 ] ; then exit 0 fi stop ;; restart) stop start ;; status) status -p $pidfile dtc-xen ;; condrestart) if ! [ $running -eq 0 ] ; then exit 0 fi stop start ;; *) echo $"Usage: $0 {start|stop|restart|status|condrestart}" exit 2 esac exit $? mysqmail-0.4.9/mysqmail-dovecot-logger.c0000644000175000017500000001566411250147611017012 0ustar zigozigo#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mydaemon.h" #include "myconfig.h" #define DAEMON_NAME "mysqmail-courier-logger" int extern errno; extern char **environ; extern FILE* stdin; extern FILE* stdout; extern FILE* stderr; ///////////////////////////////////////////////// // Config file reader stuff: using dotconf lib // ///////////////////////////////////////////////// mysqmail_config_t mysqmail_config; // Mysql connect stuff // MYSQL mysql,*sock; MYSQL_RES *res; /////////////////////////////////////////////////////////////////// // A line of log must be parsed and sent to correct mysql record // /////////////////////////////////////////////////////////////////// #define MAX_LOG_WORDS 32 #define EMAIL_ADDR_SIZE 256 #define MAX_QUERY_SIZE 1024 typedef struct{ long tv_sec; long tv_usec; }timeval_t; unsigned int STR_VALUE(char* value){ if( value == NULL ){ return 0; }else{ return atoi(value); } } int log_a_line(char* logline){ /* Log lines sent by courier should have the following format Jul 13 15:32:52 new imaplogin: LOGIN, user=username@hostname, ip=[::ffff:xxx.xxx.xxx.xxx], protocol=IMAP Jul 13 15:39:15 new imaplogin: DISCONNECTED, user=username@hostname, ip=[::ffff:xxx.xxx.xxx.xxx], headers=1590, body=778 Jul 13 15:40:55 new courierpop3login: Connection, ip=[::ffff:127.0.0.1] Jul 13 15:41:00 new courierpop3login: LOGIN, user=username@hostname, ip=[::ffff:127.0.0.1] Jul 13 15:41:03 new courierpop3login: LOGOUT, user=username@hostname, ip=[::ffff:127.0.0.1], top=0, retr=0 */ char* words[MAX_LOG_WORDS+1]; char query[MAX_QUERY_SIZE+1]=""; char* cur_p; long mysqmail_bytes = 0; char* user; char* domain; int num_words=0; char* user_str; struct timeval tv; struct tm *ptr; time_t tm; char sql_month[60]; char sql_year[60]; char* bytes_fld; // *** Tokenise the line in words *** cur_p = strtok(logline," "); while(num_words < MAX_LOG_WORDS && (words[num_words++] = cur_p) != NULL){ cur_p = strtok(NULL," "); } num_words--; if (num_words <= 6){ return 0; //not enough data on line } if ( ! strstr(words[4], "dovecot") || ! (strstr(words[5], "IMAP(") || strstr(words[5], "POP3(")) || ! strstr(words[6], "Disconnected:")){ return 0; //this isn't a courier line logout/disconnected, just exit and log nothing! } // Calculate a timestamp and a date (year+month) // syslog(LOG_NOTICE, "Found a dov im/po line, calculating timeofday"); gettimeofday(&tv,NULL); tm = time(NULL); ptr = localtime(&tm); strftime(sql_month ,100 , "\%m",ptr); strftime(sql_year ,100 , "\%Y",ptr); user_str = malloc( strlen(words[5]) + 1); if(user_str == NULL){ syslog(LOG_NOTICE, "Malloc failed"); return 0; } strcpy( user_str, words[5] + strlen("IMAP(") ); user_str[ strlen(user_str) ] = '\0'; // *** Parse the user value to get the domain part *** // syslog(LOG_NOTICE, "Searching domain"); user = user_str; domain = "null"; if (strstr(user,"@")){ domain = strstr(user,"@"); user[strlen(user) - strlen(domain)] = 0; //strip off the domain portion domain++; //shift past the @ to get the domain } // *** Calculate the total traffic for the session *** mysqmail_bytes = 0; // Build the queries // Aug 20 17:31:43 mx dovecot: POP3(test@xen650901.gplhost.com): Disconnected: Logged out top=0/0, retr=1/4047, del=0/1, size=4030 if ( strstr(words[5], "POP3(") ){ // syslog(LOG_NOTICE, "Found pop"); bytes_fld = strstr(words[10],"retr="); if(bytes_fld == NULL){ // syslog(LOG_NOTICE,"Didn't found retr="); free(user_str); return 0; } bytes_fld += strlen("retr="); bytes_fld = strstr(words[10],"/"); if(bytes_fld == NULL){ // syslog(LOG_NOTICE,"Didn't found slash"); free(user_str); return 0; } bytes_fld = bytes_fld + 1; mysqmail_bytes += atoi(bytes_fld); sprintf(query,"INSERT IGNORE INTO %s (domain_name,month,year) VALUES ('%s','%s','%s');" ,mysqmail_config.mysql_table_scoreboard, domain, sql_month, sql_year); if(mysql_query(sock,query)){ syslog(LOG_NOTICE, "Query: \"%s\" failed: %s",query,mysql_error(sock)); free(user_str); return 0; } sprintf(query,"UPDATE %s SET pop_trafic=pop_trafic+%ld WHERE domain_name='%s' AND month='%s' AND year='%s'" ,mysqmail_config.mysql_table_scoreboard, mysqmail_bytes, domain, sql_month, sql_year); if(mysql_query(sock,query)){ syslog(LOG_NOTICE, "Query: \"%s\" failed: %s",query,mysql_error(sock)); free(user_str); return 0; } sprintf(query,"UPDATE %s SET pop3_login_count=pop3_login_count+1 , pop3_transfered_bytes=pop3_transfered_bytes+%ld,last_login=%ld WHERE id='%s' AND mbox_host='%s';", mysqmail_config.mysql_table_pop_access,mysqmail_bytes,tv.tv_sec,user,domain); if(mysql_query(sock,query)){ syslog(LOG_NOTICE, "Query: \"%s\" failed: %s",query,mysql_error(sock)); } // Aug 20 17:25:12 mx dovecot: IMAP(test@xen650901.gplhost.com): Disconnected: Logged out bytes=464/1270 } else if ( strstr(words[5], "IMAP(") ){ // syslog(LOG_NOTICE, "Found imap"); bytes_fld = strstr(words[9],"bytes="); if(bytes_fld == NULL){ // syslog(LOG_NOTICE,"Didn't found bytes="); free(user_str); return 0; } bytes_fld = bytes_fld + strlen("bytes="); mysqmail_bytes += atoi(bytes_fld); bytes_fld = strstr(bytes_fld,"/"); if( bytes_fld != NULL){ // syslog(LOG_NOTICE,"Didn't found slash"); bytes_fld++; mysqmail_bytes += atoi(bytes_fld); } sprintf(query,"INSERT IGNORE INTO %s (domain_name,month,year) VALUES ('%s','%s','%s');" ,mysqmail_config.mysql_table_scoreboard, domain, sql_month, sql_year); if(mysql_query(sock,query)){ syslog(LOG_NOTICE, "Query: \"%s\" failed: %s",query,mysql_error(sock)); free(user_str); return 0; } sprintf(query,"UPDATE %s SET imap_trafic=imap_trafic+%ld \ WHERE domain_name='%s' AND month='%s' AND year='%s'" ,mysqmail_config.mysql_table_scoreboard, mysqmail_bytes, domain, sql_month, sql_year); if(mysql_query(sock,query)){ syslog(LOG_NOTICE, "Query: \"%s\" failed: %s",query,mysql_error(sock)); free(user_str); return 0; } sprintf(query,"UPDATE %s SET imap_login_count=imap_login_count+1 , imap_transfered_bytes=imap_transfered_bytes+%ld,last_login=%ld WHERE id='%s' AND mbox_host='%s';", mysqmail_config.mysql_table_pop_access,mysqmail_bytes,tv.tv_sec,user,domain); if(mysql_query(sock,query)){ syslog(LOG_NOTICE, "Query: \"%s\" failed: %s",query,mysql_error(sock)); } } free(user_str); return 0; } int main(int argc, char **argv){ int ret; daemonize(); setlogmask (LOG_UPTO (LOG_NOTICE)); openlog(DAEMON_NAME, LOG_NDELAY | LOG_CONS | LOG_PID, LOG_LOCAL1); write_pidfile("mysqmail-dov-logger"); reg_hand(); read_config_file(); do_ze_mysql_connect(); ret = log_all_lines("syslog",&log_a_line); mysql_close(sock); return ret; } mysqmail-0.4.9/mysqmail-pure-ftpd-logger.c0000644000175000017500000001262211250147717017253 0ustar zigozigo#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mydaemon.h" #include "myconfig.h" #define DAEMON_NAME "mysqmail-pure-ftpd-logger" int extern errno; extern char **environ; extern FILE* stdin; extern FILE* stdout; extern FILE* stderr; mysqmail_config_t mysqmail_config; ///////////////////////// // Mysql connect stuff // ///////////////////////// MYSQL mysql,*sock; MYSQL_RES *res; /////////////////////////////////////////////////////////////////// // A line of log must be parsed and sent to correct mysql record // /////////////////////////////////////////////////////////////////// // Here is a standard qmail log file thrue syslog: // Feb 24 12:51:15 www qmail: 1109249475.344018 bounce msg 6144008 qp 22053 // The parser will then skip 6 words (from "Feb" to the microtime) // and start analysing at "bounce", so we set SKIP_WORD to 6. // We will also check that the message is fom "qmail:", if not, then it's // another daemon message... #define MAX_LOG_WORDS 1024 #define EMAIL_ADDR_SIZE 256 #define MAX_QUERY_SIZE 1024 #define MAX_LOGLINE_SIZE 2048 #define SKIP_WORD 6 typedef struct{ long tv_sec; long tv_usec; }timeval_t; void log_to_domain_table(char* transfered_bytes,char* domain_name){ char query[1024]=""; static char sql_month[60]; static char sql_year[60]; struct tm *ptr; time_t tm; tm = time(NULL); ptr = localtime(&tm); strftime(sql_month ,60 , "\%m",ptr); strftime(sql_year ,60 , "\%Y",ptr); sprintf(query,"INSERT IGNORE INTO %s (id,sub_domain,transfer,hits,month,year) VALUES ('','%s','0','0',%s,%s)", "ftp_accounting",domain_name,sql_month,sql_year); if(mysql_query(sock,query)){ syslog(LOG_ERR, "Query: \"%s\" failed: %s",query,mysql_error(sock)); return; } sprintf(query,"UPDATE %s SET transfer=transfer+%s,hits=hits+1 WHERE sub_domain='%s' AND month='%s' AND year='%s'", "ftp_accounting",transfered_bytes,domain_name,sql_month,sql_year); if(mysql_query(sock,query)){ syslog(LOG_ERR, "Query: \"%s\" failed: %s",query,mysql_error(sock)); return; } } int log_a_line(char* logline){ char* words[MAX_LOG_WORDS+1]; char query[MAX_QUERY_SIZE+1]=""; char* cur_p; int num_words=0; char* smtp_logs; // static char ds[MAX_LOGLINE_SIZE+2]; static char logline_cpy[MAX_LOGLINE_SIZE+2]; long num_rows; MYSQL_ROW row; MYSQL_RES *res; smtp_logs = mysqmail_config.mysql_table_smtp_logs; if( strlen(logline) > MAX_LOG_WORDS ){ syslog(LOG_ERR, "Log line bigger than buffer size: skipping line"); } // Tokenise the line in words strcpy(logline_cpy,logline); cur_p = strtok(logline," "); while( num_words < MAX_LOG_WORDS && (words[num_words++] = cur_p) != NULL){ cur_p = strtok(NULL," "); } num_words--; if (num_words < 5) { return 1; } strcpy(query,""); // Start of gramar cheks // Handle anonymous logins differently! if( strcmp(words[2],"ftp") == 0 || strcmp(words[2],"anonymous") == 0 ){ syslog(LOG_ERR, "Anonymous login: %s bytes",words[num_words-1]); }else{ //3rd word should be the user name sprintf(query,"SELECT hostname FROM %s WHERE login='%s';","ftp_access",words[2]); if(mysql_query(sock,query)){ syslog(LOG_ERR, "Query: \"%s\" failed line %d: %s",query,__LINE__,mysql_error(sock)); return 1; } if (!(res=mysql_store_result(sock))){ syslog(LOG_ERR, "Couldn't get result: %s",mysql_error(sock)); return 1; } num_rows = mysql_num_rows(res); if(num_rows == 1){ row = mysql_fetch_row(res); if(row == NULL){ mysql_free_result(res); return 1; } if( strcmp("\"GET",words[5]) == 0 ){ // Last word should be the number of bytes transfered sprintf(query,"UPDATE %s SET dl_count=dl_count+1, dl_bytes=dl_bytes+%s WHERE login='%s'","ftp_access",words[num_words-1],words[2]); log_to_domain_table(words[num_words-1],row[0]); }else if( strcmp("\"PUT",words[5]) == 0 ){ sprintf(query,"UPDATE %s SET ul_count=dl_count+1, ul_bytes=ul_bytes+%s WHERE login='%s'","ftp_access",words[num_words-1],words[2]); log_to_domain_table(words[num_words-1],row[0]); }else{ strcpy(query,""); syslog(LOG_ERR, "Commande %s not understood",words[5]); } }else{ strcpy(query,""); syslog(LOG_ERR, "User %s not found in table!",words[2]); } mysql_free_result(res); } // End of gramar cheks // fprintf(stdout,"Query: \"%s\" !\n",query); // Issue the query return from function. if(strlen(query)!=0){ if(mysql_query(sock,query)){ syslog(LOG_ERR, "Query: \"%s\" failed: %s",query,mysql_error(sock)); } } return 0; } int main(int argc, char **argv){ int ret; // if (argc > 1 && !strcmp(argv[1],"-D")) daemonize(); daemonize(); setlogmask (LOG_UPTO (LOG_NOTICE)); openlog("mysqmail-pur-logger", LOG_NDELAY | LOG_CONS | LOG_PID, LOG_LOCAL1); syslog(LOG_NOTICE, "mysqmail-pure-ftpd-logger started"); write_pidfile("mysqmail-pure-ftpd-logger"); reg_hand(); read_config_file(); do_ze_mysql_connect(); // Manages locations of the log file for both CentOS and Debian. if( access("/var/log/pure-ftpd/transfer.log", R_OK) == 0){ ret = log_all_lines("/var/log/pure-ftpd/transfer.log",&log_a_line); }else{ ret = log_all_lines("/var/log/pureftpd.log",&log_a_line); } mysql_close(sock); return ret; } mysqmail-0.4.9/myconfig.h0000644000175000017500000000063011241670477014056 0ustar zigozigo///////////////////////////////////////////////// // Config file reader stuff: using dotconf lib // ///////////////////////////////////////////////// typedef struct{ char* mysql_hostname; char* mysql_user; char* mysql_pass; char* mysql_db; char* mysql_table_smtp_logs; char* mysql_table_pop_access; char* mysql_table_scoreboard; char* mysql_table_domain; }mysqmail_config_t; int read_config_file(); mysqmail-0.4.9/dist0000755000175000017500000000121111247202225012743 0ustar zigozigo#!/bin/sh set -e set -x VERS=`head -n 1 debian/changelog | cut -d'(' -f2 | cut -d')' -f1 | cut -d'-' -f1` PKGNAME=`head -n 1 debian/changelog | cut -d' ' -f1` if [ -e /etc/redhat-release ] ; then MKTEMP="mktemp -d -p /tmp" else MKTEMP="mktemp -d -t" fi TMPDIR=`${MKTEMP} ${PKGNAME}.XXXXXX` DIRNAME=${PKGNAME}-${VERS} MYCWD=`pwd` mkdir -p ${TMPDIR}/${DIRNAME} cp -auxf * ${TMPDIR}/${DIRNAME} sed -i "s/__VERSION__/${VERS}/" ${TMPDIR}/${DIRNAME}/${PKGNAME}.spec rm -rf ${TMPDIR}/${DIRNAME}/debian rm -rf ${TMPDIR}/${DIRNAME}/.git cd ${TMPDIR} tar -czf ${DIRNAME}.tar.gz ${DIRNAME} cd ${MYCWD} mv ${TMPDIR}/${DIRNAME}.tar.gz .. rm -rf ${TMPDIR}