Browse files

Initial revision

git-svn-id: https://svn.ic-s.nl/svn/dbmail/trunk/dbmail@3 7b491191-dbf0-0310-aff6-d879d4d69008
  • Loading branch information...
0 parents commit f17891e11d755bd4fc0c0d18fa956f9457cb3431 eelco committed Jun 15, 2001
Showing with 2,331 additions and 0 deletions.
  1. +94 −0 INSTALL
  2. +40 −0 Makefile
  3. +351 −0 README
  4. +3 −0 TODO
  5. +27 −0 config.h
  6. +343 −0 dbmysql.c
  7. +24 −0 dbmysql.h
  8. +47 −0 debug.c
  9. +29 −0 debug.h
  10. +102 −0 list.c
  11. +30 −0 list.h
  12. +68 −0 main.c
  13. +8 −0 main.h
  14. +178 −0 mime.c
  15. +3 −0 mime.h
  16. +43 −0 misc.c
  17. +3 −0 misc.h
  18. +222 −0 pipe.c
  19. +4 −0 pipe.h
  20. +373 −0 pop3.c
  21. +84 −0 pop3.h
  22. +214 −0 pop3d.c
  23. +41 −0 sql/create_tables.sql
94 INSTALL
@@ -0,0 +1,94 @@
+DBMAIL installation
+===================
+
+What do you need?
+
+- working MYSQL
+- working smtp server (preferrably Postfix)
+- a little knowledge about SQL
+
+1. First you'll need to create the DBMAIL database in MYSQL.
+ This is easy. You'll need the mysql commandline program for this.
+ Just do :
+
+ mysql -u root -p <sql/create_tables.sql
+
+ After this, the DBMAIL tables will be created.
+
+
+2. Next you will need to create some users into the database.
+ Run the mysql utility again.
+ Now type:
+
+ use dbmail;
+ insert into mailbox (userid,passwd,clientid,maxmail) values
+ ("username", "password",0,0);
+
+ username should be replaced by a username (which is used by the pop3d
+ as well as by the deliver program to identify a mailbox) and password
+ should be replaced by a cleartext password.
+ Now you'll need to create a alias to this user. Every mailbox needs
+ at least one alias. In the same session as above type:
+
+ insert into aliases (alias,useridnr) values ("emailaddress",0);
+
+ emailaddress should be replaced with an address that your machine is
+ final destination for. The useridnr is the useridnr of the mailbox that
+ mail for this emailaddress should be delivered to.
+
+
+3. Look at the config.h file in this directory. You will see a few #define
+ statements. First the #define HOST should contain the hostname of the
+ machine that runs the MYSQL database. USER and PASS need to be replaced
+ with a user and password that can access the DBMAIL database.
+
+ Do a find or a locate for mysql.h. Copy this path and set it in the
+ dbmysql.h in the #include "full path to mysql.h"
+
+ If you want verbose debugging you can set the TRACE_LEVEL to 4 or 5.
+ For normal messages the TRACE_LEVEL should be 2.
+
+ You might check the location for the mysqlclient file (usually it's
+ in /usr/local/lib/mysql). If it is different you need to change this
+ into the Makefile.
+
+
+4. Run make
+ Both dbmail-pop3d as dbmail-smtp should be created.
+
+5 Become root and copy dbmail-pop3d and dbmail-smtp to
+ /usr/local/bin.
+
+-- The next lines are MTA dependend, this is for postfix --
+
+6. In the master.cf file for postfix add the following:
+ dbmail unix - n n - - pipe
+ flags= user=eelco:eelco argv=/usr/local/bin/dbmail-smtp -d ${recipient}
+
+ and insert into the /etc/postfix/transport file
+ <domain> dbmail:
+ for all domains you want dbmail to be destiation for.
+ Next run postmap /etc/postfix/transport
+ Edit /etc/postfix/main.cf and add the line
+ transport_maps = /etc/postfix/transport
+
+ do a postfix reload and if everything goes well
+ mail should be delivered to dbmail. If not, try running
+ postfix with the -v option to see what's going on. If the
+ problem seems to be in dbmail, try changing the tracelevel in debug.h
+ to 4 or 5. This should give enough info. You need to recompile and
+ copy the dbmail- executables.
+
+-- -----------------------------------------------------------
+
+If you do not use postfix. The dbmail injector program, dbmail-smtp,
+can receive information in two ways. Either through raw mail (for example,
+delivered by procmail) using the -n option or from a MTA with receipients
+in the commandline using the -d option.
+
+If you get dbmail running on other MTA's please let me know at
+eelco@eelco.com.
+
+For other problems you can subscribe to the dbmaillist. If you want to
+subscribe, send a subscribe dbmail in the body of a message to
+majordomo@fastxs.net.
40 Makefile
@@ -0,0 +1,40 @@
+#! /bin/sh
+
+
+SMTP_OBJECTS = list.o debug.o pipe.o mime.o dbmysql.o misc.o
+POP_OBJECTS = pop3.o list.o debug.o dbmysql.o
+CC = cc
+
+MYSQLLIBDIR=/usr/local/lib/mysql
+
+LIBS = -L$(MYSQLLIBDIR)
+LIB = -lmysqlclient
+
+# Added the -D_BSD_SOURCE option to suppress warnings
+# from compiler about vsyslog function
+
+CFLAGS = -Wall -ansi -ggdb -D_BSD_SOURCE
+
+.PHONY: clean install
+
+all: smtp pop3d
+
+smtp: config.h main.h $(SMTP_OBJECTS) main.c
+ $(CC) main.c -o dbmail-smtp $(SMTP_OBJECTS) $(LIBS) $(LIB)
+
+pop3d: pop3.h $(POP_OBJECTS) pop3d.c
+ $(CC) pop3d.c -o dbmail-pop3d $(POP_OBJECTS) $(LIBS) $(LIB)
+
+list.o: list.h
+debug.o: debug.h
+pipe.o: pipe.h
+mime.o: mime.h
+dbmysql.o:dbmysql.h
+misc.o:misc.h
+pop3.o:pop3.h
+
+distclean: clean
+ rm -rf dbmail-smtp dbmail-pop3d
+
+clean:
+ rm -f *.o core
351 README
@@ -0,0 +1,351 @@
+Who created it?
+===============
+Me, Eelco van Beek <eelco@eelco.com>. At first, i created
+a very sloppy version of this product in, what supposedly
+should have been, C++.
+Eric Knauel <eric@macnews.de> worked on it for a few months
+to make it better but he also got busy with other things.
+I decided to create some time to recreat the whole thing
+and build it in good old C. It's not finished, and never will
+be, but it seems functional.
+
+***
+Disclaimer: I'm not responsible for anything. This software
+could eat your harddisk then kill your processor. You will get upset,
+drive to the store to get a new one and run over a cat. You will not
+be able to use it against me. It's your own fault, you decided
+to use it!
+***
+It should work without a lot of problems. I've been running it
+for about a week now and i haven't got any problems (i'm
+testing it with about 10 users and about 5000 messages of very
+different sizes). If anything weird happends, please copy as much
+as you can and mail it to bugs@dbmail.org. I will reply as soon as
+i can. Even better is to subscribe to the dbmail mailing list
+and do the same thing there.
+
+What is it?
+===========
+DBMAIL is a collection of programs that enables email to be
+stored in and retrieved from a (MY)sql database. Currently
+there are two programs, dbmail-smtp will insert email into the
+database, dbmail-pop3d will provide a frontend to retreive
+email from the database.
+
+Why is it usefull?
+==================
+Well, for me it's usefull because a number of reasons.
+- it enables me to create mailboxes without the need of systemusers.
+- mail is more effeciently stored and therefore it can
+ be inserted an retrieved much faster dan any regular system
+ (DBmail is currently able to retrieve 500 mail messages in 2
+ seconds, depending on the mailclient used).
+- it's more expandable. A database is much easier to access than
+ a flat file or a Maildir. We don't need to parse first.
+- In my case, i can easily link a mailbox to a certain client
+ which enables me to let the client maintaining his/her own mailboxes
+ without me needed to technically support it.
+
+How do i install it?
+====================
+Check the INSTALL file.
+
+
+Future
+======
+- finish the complete pop3 implementation (APOP, AUTH stuff like that)
+- finish the smtp software to fully insert, and also bounce and forward
+- create an IMAP daemon that talks to (MY)sql.
+- create a dbpostgresql.c/dbpostgresql.h to talk to PostgreSQL.
+check the website for further dbmail plans.
+
+What kind of licence is DBmail?
+===============================
+DBmail is used under the GPL+ licence.
+The + is that i would like to know how stuff i working out. So
+If you have DBMAIL running and you have like 1002919183 users,
+i would want to know (to bragg about it ;).
+
+The rest of the licence is described below:
+-------------------------------------------
+
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, 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 or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+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 give any other recipients of the Program a copy of this License
+along with the Program.
+
+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 Program or any portion
+of it, thus forming a work based on the Program, 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) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+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 Program, 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 Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) 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; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, 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 executable. However, as a
+special exception, the source code 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.
+
+If distribution of executable or 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 counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program 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.
+
+ 5. 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 Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program 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 to
+this License.
+
+ 7. 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 Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program 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 Program.
+
+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.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program 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.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the 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 Program
+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 Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, 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
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "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 PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. 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 PROGRAM 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 PROGRAM (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 PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
3 TODO
@@ -0,0 +1,3 @@
+- commandline program should be created for adding email addresses
+- cronjob should be created for deleting messsages
+- it would be interesting to know who is receiving a message (in log)
27 config.h
@@ -0,0 +1,27 @@
+#ifndef _CONFIG_H
+#define _CONFIG_H
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <unistd.h>
+#include <syslog.h>
+
+/* DATABASE SPECIFIC */
+#define HOST "localhost"
+#define USER "root"
+#define PASS ""
+#define MAILDATABASE "dbmail"
+
+#define UID_SIZE 70
+
+
+void mime_list(char *blkdata,unsigned long blksize);
+char *read_header(unsigned long *blksize);
+int db_connect();
+int mail_adr_list();
+int mail_adr_list_dlv(char *local_rec);
+void check_duplicates();
+int insert_messages();
343 dbmysql.c
@@ -0,0 +1,343 @@
+ /* Functions for connecting and talking to the Mysql database */
+
+#include "dbmysql.h"
+#include "config.h"
+#include "pop3.h"
+
+#define DEF_QUERYSIZE 1024
+
+MYSQL conn;
+MYSQL_RES *res;
+MYSQL_ROW row;
+
+int db_connect ()
+{
+ /* connecting */
+ mysql_init(&conn);
+ mysql_real_connect (&conn,HOST,USER,PASS,MAILDATABASE,0,NULL,0);
+#ifdef mysql_errno
+ if (mysql_errno(&conn)) {
+ trace(TRACE_ERROR,"dbconnect(): mysql_real_connect failed: %s",mysql_error(&conn));
+ return -1;
+ }
+#endif
+
+ /* selecting the right database */
+ if (mysql_select_db(&conn,MAILDATABASE)) {
+ trace(TRACE_ERROR,"dbconnect(): mysql_select_db failed: %s",mysql_error(&conn));
+ return -1;
+ }
+ return 0;
+}
+
+unsigned long db_insert_result ()
+{
+ unsigned long insert_result;
+ insert_result=mysql_insert_id(&conn);
+ return insert_result;
+}
+
+
+int db_query (char *query)
+{
+ if (mysql_real_query(&conn, query,strlen(query)) <0)
+ {
+ trace(TRACE_ERROR,"db_query(): mysql_real_query failed: %s",mysql_error(&conn));
+ return -1;
+ }
+ return 0;
+}
+
+unsigned long db_insert_message_block (char *block, int messageidnr)
+{
+ char *escblk, *tmpquery;
+ /* allocate memory twice as much, for eacht character might be escaped */
+ memtst((escblk=(char *)malloc((strlen(block)*2)))==NULL);
+ mysql_escape_string (escblk,block,strlen(block));
+
+ /* add an extra 500 characters for the query */
+ memtst((tmpquery=(char *)malloc(strlen(escblk)+500))==NULL);
+
+ sprintf (tmpquery,"INSERT INTO messageblk(messageblk,blocksize,messageidnr) VALUES (\"%s\",%d,%d)",escblk,strlen(block),messageidnr);
+
+ if (db_query (tmpquery)==-1)
+ {
+ free(tmpquery);
+ trace(TRACE_STOP,"db_insert_message_block(): dbquery failed");
+ }
+ /* freeing buffers */
+ free (tmpquery);
+ free (escblk);
+ return db_insert_result(&conn);
+}
+
+
+int db_check_user (char *username, struct list *userids)
+{
+ char *ckquery;
+ int occurences=0;
+ unsigned long messageid;
+
+ trace(TRACE_DEBUG,"db_check_user(): checking user [%s] in alias table",username);
+ memtst((ckquery=(char *)malloc(DEF_QUERYSIZE))==NULL);
+ sprintf (ckquery, "SELECT * FROM aliases WHERE alias=\"%s\"",username);
+ trace(TRACE_DEBUG,"db_check_user(): executing query : [%s]",ckquery);
+ if (db_query(ckquery)==-1)
+ {
+ free(ckquery);
+ return occurences;
+ }
+ if ((res = mysql_store_result(&conn)) == NULL)
+ {
+ trace(TRACE_ERROR,"db_check_user: mysql_store_result failed: %s",mysql_error(&conn));
+ free(ckquery);
+ return occurences;
+ }
+ if (mysql_num_rows(res)<1)
+ {
+ trace (TRACE_DEBUG,"db_check_user(): user %s not in aliases table", username);
+ mysql_free_result(res);
+ free(ckquery);
+ return occurences;
+ }
+
+ /* row[2] is the idnumber */
+ while ((row = mysql_fetch_row(res))!=NULL)
+ {
+ occurences++;
+ messageid=atol(row[2]);
+ list_nodeadd(userids, &messageid,sizeof(messageid));
+ }
+
+ trace(TRACE_INFO,"db_check_user(): user [%s] has [%d] entries",username,occurences);
+ mysql_free_result(res);
+ return occurences;
+}
+
+int db_send_header (void *fstream, unsigned long messageidnr)
+ {
+ /* this function writes the header to stream */
+ char *ckquery;
+
+ memtst((ckquery=(char *)malloc(DEF_QUERYSIZE))==NULL);
+ sprintf (ckquery, "SELECT * FROM messageblk WHERE messageidnr=%li",
+ messageidnr);
+ if (db_query(ckquery)==-1)
+ {
+ free(ckquery);
+ return 0;
+ }
+ if ((res = mysql_store_result(&conn)) == NULL)
+ {
+ trace(TRACE_ERROR,"db_send_header: mysql_store_result failed: %s",mysql_error(&conn));
+ free(ckquery);
+ return 0;
+ }
+ if (mysql_num_rows(res)<1)
+ {
+ trace (TRACE_ERROR,"db_send_header(): no messageblks for messageid %li",messageidnr);
+ mysql_free_result(res);
+ free(ckquery);
+ return 0;
+ }
+
+ while ((row = mysql_fetch_row(res))!=NULL)
+ fprintf ((FILE *)fstream,row[2]);
+ mysql_free_result(res);
+ return 0;
+ }
+
+int db_send_message (void *fstream, unsigned long messageidnr)
+ {
+ /* this function writes the messageid to stream
+ * returns -1 on err, 1 on success */
+ char *ckquery;
+
+ memtst((ckquery=(char *)malloc(DEF_QUERYSIZE))==NULL);
+ sprintf (ckquery, "SELECT * FROM messageblk WHERE messageidnr=%lu",
+ messageidnr);
+ if (db_query(ckquery)==-1)
+ {
+ free(ckquery);
+ return -1;
+ }
+ if ((res = mysql_store_result(&conn)) == NULL)
+ {
+ trace (TRACE_ERROR,"db_send_message(): mysql_store_result failed: %s",mysql_error(&conn));
+ free(ckquery);
+ return -1;
+ }
+ if (mysql_num_rows(res)<1)
+ {
+ trace (TRACE_ERROR,"db_send_message(): no message blocks for user %lu",messageidnr);
+ free(ckquery);
+ return -1;
+ }
+
+ trace(TRACE_DEBUG,"db_send_message(): retrieving message=[%lu]",
+ messageidnr);
+ while ((row = mysql_fetch_row(res))!=NULL)
+ {
+ fprintf ((FILE *)fstream,"%s",row[2]);
+ }
+ trace(TRACE_DEBUG,"db_send_message(): done sending, freeing result");
+ mysql_free_result(res);
+
+ /* end of stream for pop
+ * has to be send on a clearline */
+
+ fprintf ((FILE *)fstream,"\r\n.\r\n");
+ return 1;
+ }
+
+unsigned long db_validate (char *user, char *password)
+ {
+ /* returns useridnr on OK, 0 on validation failed, -1 on error */
+ char *ckquery;
+
+ memtst((ckquery=(char *)malloc(DEF_QUERYSIZE))==NULL);
+ sprintf (ckquery, "SELECT useridnr FROM mailbox WHERE userid=\"%s\" AND passwd=\"%s\"",
+ user,password);
+
+ if (db_query(ckquery)==-1)
+ {
+ free(ckquery);
+ return -1;
+ }
+
+ if ((res = mysql_store_result(&conn)) == NULL)
+ {
+ trace (TRACE_ERROR,"db_validate(): mysql_store_result failed: %s",mysql_error(&conn));
+ free(ckquery);
+ return -1;
+ }
+
+ if (mysql_num_rows(res)<1)
+ {
+ /* no such user found */
+ free(ckquery);
+ return 0;
+ }
+
+ row = mysql_fetch_row(res);
+
+ return atoi(row[0]);
+ }
+
+int db_createsession (unsigned long useridnr, struct session *sessionptr)
+ {
+ /* returns 1 with a successfull session, -1 when something goes wrong
+ * sessionptr is changed with the right session info
+ * useridnr is the userid index for the user whose mailbox we're viewing */
+
+ /* first we do a query on the messages of this user */
+
+ char *ckquery;
+ struct message tmpmessage;
+ unsigned long messagecounter=0;
+
+ memtst((ckquery=(char *)malloc(DEF_QUERYSIZE))==NULL);
+ /* query is <2 because we don't want deleted messages
+ * the unique_id should not be empty, this could mean that the message is still being delivered */
+ sprintf (ckquery, "SELECT * FROM message WHERE useridnr=%li AND status<002 AND unique_id!=\"\" order by status ASC",
+ useridnr);
+
+ if (db_query(ckquery)==-1)
+ {
+ free(ckquery);
+ return -1;
+ }
+
+ if ((res = mysql_store_result(&conn)) == NULL)
+ {
+ trace (TRACE_ERROR,"db_validate(): mysql_store_result failed: %s",mysql_error(&conn));
+ free(ckquery);
+ return -1;
+ }
+
+ sessionptr->totalmessages=0;
+ sessionptr->totalsize=0;
+
+
+ if ((messagecounter=mysql_num_rows(res))<1)
+ {
+ /* there are no messages for this user */
+ return 1;
+ }
+
+ /* messagecounter is total message, +1 tot end at message 1 */
+ messagecounter+=1;
+
+ /* filling the list */
+
+ trace (TRACE_DEBUG,"db_create_session(): adding items to list");
+ while ((row = mysql_fetch_row(res))!=NULL)
+ {
+ tmpmessage.msize=atol(row[2]);
+ tmpmessage.realmessageid=atol(row[0]);
+ tmpmessage.messagestatus=atoi(row[3]);
+ strncpy(tmpmessage.uidl,row[4],UID_SIZE);
+
+ tmpmessage.virtual_messagestatus=atoi(row[3]);
+
+ sessionptr->totalmessages+=1;
+ sessionptr->totalsize+=tmpmessage.msize;
+ /* descending to create inverted list */
+ messagecounter-=1;
+ tmpmessage.messageid=messagecounter;
+ list_nodeadd (&sessionptr->messagelst, &tmpmessage, sizeof (tmpmessage));
+ }
+
+ trace (TRACE_DEBUG,"db_create_session(): adding succesfull");
+
+ /* setting all virtual values */
+ sessionptr->virtual_totalmessages=sessionptr->totalmessages;
+ sessionptr->virtual_totalsize=sessionptr->totalsize;
+
+ mysql_free_result(res);
+ return 1;
+ }
+
+int db_update_pop (struct session *sessionptr)
+ {
+ char *ckquery;
+ struct element *tmpelement;
+
+ memtst((ckquery=(char *)malloc(DEF_QUERYSIZE))==NULL);
+
+ /* get first element in list */
+ tmpelement=list_getstart(&sessionptr->messagelst);
+
+
+ while (tmpelement!=NULL)
+ {
+ /* check if they need an update in the database */
+ if (((struct message *)tmpelement->data)->virtual_messagestatus!=
+ ((struct message *)tmpelement->data)->messagestatus)
+ {
+ /* yes they need an update, do the query */
+ sprintf (ckquery, "UPDATE message set status=%lu WHERE messageidnr=%lu",
+ ((struct message *)tmpelement->data)->virtual_messagestatus,
+ ((struct message *)tmpelement->data)->realmessageid);
+
+ /* FIXME: a message could be deleted already if it has been accessed
+ * by another interface and be deleted by sysop
+ * we need a check if the query failes because it doesn't excists anymore
+ * now it will just bailout */
+
+ if (db_query(ckquery)==-1)
+ {
+ trace(TRACE_ERROR,"db_update_pop(): could not execute query: [%s]");
+ free(ckquery);
+ return -1;
+ }
+ }
+ tmpelement=tmpelement->nextnode;
+ }
+ return 0;
+ }
+
+int db_disconnect (char *query)
+{
+ return 0;
+}
24 dbmysql.h
@@ -0,0 +1,24 @@
+ /* Functions for database communication */
+
+#ifndef _DBMYSQL_H
+#define _DBMYSQL_H
+#endif
+
+#include "/usr/include/mysql/mysql.h"
+#include "debug.h"
+
+struct session;
+struct list;
+
+int db_connect();
+int db_query (char *query);
+int db_check_user (char *username, struct list *userids);
+unsigned long db_insert_message_block (char *block, int nextblock);
+int db_check_id (char *id);
+int db_disconnect();
+unsigned long db_insert_result ();
+int db_send_header (void *fstream, unsigned long messageidnr);
+int db_send_message (void *fstream, unsigned long messageidnr);
+unsigned long db_validate (char *user, char *password);
+int db_createsession (unsigned long useridnr, struct session *sessionptr);
+int db_update_pop (struct session *sessionptr);
47 debug.c
@@ -0,0 +1,47 @@
+ /* Debugging and memory checking functions */
+
+#include "debug.h"
+
+#define err_out_stream stderr
+
+void func_memtst (char filename[255],int line,int tst)
+{
+ if (tst)
+ trace(TRACE_FATAL,"func_memtst(): fatal: %s:%d Memory error, result should not be NULL)");
+}
+
+void trace (int level, const char *formatstring, ...)
+{
+ va_list argp;
+
+ va_start(argp, formatstring);
+
+ if (level <= TRACE_LEVEL)
+ {
+ if (TRACE_VERBOSE)
+ vfprintf (err_out_stream, formatstring, argp);
+ if (TRACE_TO_SYSLOG)
+ {
+ if (level <= TRACE_WARNING)
+ {
+ /* set LOG_ALERT at warnings */
+ vsyslog (LOG_ALERT, formatstring, argp);
+ }
+ else
+ vsyslog (LOG_NOTICE, formatstring, argp);
+ }
+ va_end(argp);
+ }
+ else
+ if (level == TRACE_MESSAGE)
+ vsyslog (LOG_NOTICE, formatstring, argp);
+
+ /* very big fatal error
+ * bailout */
+
+ if (level==TRACE_FATAL)
+ exit(1);
+
+ if (level==TRACE_STOP)
+ exit(0);
+}
29 debug.h
@@ -0,0 +1,29 @@
+/* debug.h : headers for debug.c
+*/
+
+#include <stdio.h>
+#include <sys/syslog.h>
+#include <stdarg.h>
+
+#ifndef _DEBUG_H
+#define _DEBUG_H
+#endif
+
+#define TRACE_TO_SYSLOG 1
+#define TRACE_VERBOSE 0
+
+#define TRACE_LEVEL 2 /* 5 maximum debugging */
+/* #define TRACE_LEVEL 2 */ /* normal operations */
+
+#define TRACE_FATAL -1
+#define TRACE_STOP 0
+#define TRACE_MESSAGE 1
+#define TRACE_ERROR 2
+#define TRACE_WARNING 3
+#define TRACE_INFO 4
+#define TRACE_DEBUG 5
+
+#define memtst(tstbool) func_memtst (__FILE__,__LINE__,tstbool)
+
+void func_memtst (char filename[255],int line,int tst);
+void trace (int level, const char *formatstring, ...);
102 list.c
@@ -0,0 +1,102 @@
+/* functions to create lists and add/delete items
+ * (c) 2001 eelco@eelco.com */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "debug.h"
+#include "list.h"
+
+extern void func_memtst (char filename[255],int line,int tst);
+
+void list_init (struct list *tlist)
+ {
+ tlist->start=NULL;
+ tlist->current=NULL;
+ tlist->itptr=NULL;
+ tlist->total_nodes=0;
+ tlist->list_inited=1;
+ }
+
+struct element *list_nodeadd(struct list *tlist, void *data,
+ size_t dsize)
+ {
+ /* Adds a node to a linked list (list structure) */
+ struct element *p;
+ p=tlist->start;
+
+ tlist->start=(struct element *)malloc(sizeof(struct element));
+ memtst(tlist->start==NULL);
+
+ /* allocating data for object */
+ memtst((tlist->start->data=(void *)malloc(dsize))==NULL);
+
+ /* copying data */
+ memtst(((tlist->start->data=memcpy(tlist->start->data,data,dsize)))
+ ==NULL);
+ tlist->start->nextnode=p;
+
+ /* updating node count */
+ tlist->total_nodes++;
+ return tlist->start;
+ }
+
+struct element *list_nodedel(struct list *tlist,void *data)
+ {
+ struct element *temp;
+ struct element *item;
+ item=NULL;
+ temp=tlist->start;
+
+ /* checking if lists exist else return NULL*/
+
+ if (temp==NULL) return NULL;
+
+ while (temp!=NULL) /* walk the list */
+ {
+ if (temp->data==data)
+ {
+ if (item==NULL)
+ {
+ tlist->start=temp->nextnode;
+ free (temp->data);
+ free ((struct element *)temp);
+ break;
+ }
+ else
+ {
+ item->nextnode=temp->nextnode;
+ free(temp->data); /* freeing memory */
+ free ((struct element *)temp);
+ break;
+ }
+ /* updating node count */
+ tlist->total_nodes--;
+ }
+ item=temp;
+ temp=temp->nextnode;
+ }
+
+ return NULL;
+ }
+
+struct element *list_getstart(struct list *tlist)
+ {
+ return tlist->start;
+ }
+
+int list_totalnodes(struct list *tlist)
+ {
+ return tlist->total_nodes;
+ }
+
+void list_showlist(struct list *tlist)
+ {
+ struct element *temp;
+ temp=tlist->start;
+ while (temp!=NULL)
+ {
+ trace (TRACE_MESSAGE,"list_showlist():item found [%s]\n",(char *)temp->data);
+ temp=temp->nextnode;
+ }
+ }
30 list.h
@@ -0,0 +1,30 @@
+/* list.h: list header */
+
+#ifndef _LIST_H
+#define _LIST_H
+#endif
+
+#include <stdlib.h>
+
+struct element
+{
+ void *data;
+ struct element *nextnode;
+};
+
+struct list
+{
+ struct element *start;
+ struct element *current;
+ struct element *itptr;
+ long total_nodes;
+ int list_inited; /* 1 if the list is initiated */
+};
+
+extern struct element *list_nodeadd(struct list *tlist, void *data,
+ size_t dsize);
+struct element *list_nodedel(struct list *tlist, void *data);
+struct element *list_getstart(struct list *tlist);
+int list_totalnodes(struct list *tlist);
+void list_showlist(struct list *tlist);
+void list_init(struct list *tlist);
68 main.c
@@ -0,0 +1,68 @@
+/* main file */
+
+#include "config.h"
+#include "main.h"
+
+#define MESSAGEIDSIZE 100
+#define NORMAL_DELIVERY 1
+#define SPECIAL_DELIVERY 2
+
+#define INDEX_DELIVERY_MODE 1
+
+/* syslog */
+#define PNAME "dbmail/smtp"
+
+struct list mimelist; /* raw unformatted mimefields and values */
+struct list users; /* list of email addresses in message */
+
+int mode; /* how should we process */
+
+int main (int argc, char *argv[]) {
+
+ char *header;
+ unsigned long headersize;
+
+ openlog(PNAME, LOG_PID, LOG_MAIL);
+
+ /* first check for commandline options */
+ if (argc<2)
+ {
+ printf ("\nUsage: %s -n for normal deliveries (scanner scans Deliver-To: header)\n",argv[0]);
+ printf (" %s -d [addresses] for delivery without using scanner\n\n",argv[0]);
+ return 0;
+ }
+
+ if (db_connect() < 0)
+ trace(TRACE_FATAL,"main(): database connection failed");
+
+ /* first we need to read the header */
+ if ((header=read_header(&headersize))==NULL)
+ trace (TRACE_STOP,"main(): read_header() returned an invalid header");
+
+ /* we need to decide what delivery mode we're in */
+ if (strcmp ("-d",argv[INDEX_DELIVERY_MODE])==0)
+ {
+ trace (TRACE_INFO,"main(): using SPECIAL_DELIVERY");
+ /* mail_adr_list_special will take the command line
+ * email addresses and use those addresses for this message
+ * delivery */
+ if (mail_adr_list_special(INDEX_DELIVERY_MODE+1,argc, argv)==0)
+ trace(TRACE_STOP,"main(): could not find any addresses");
+ }
+ else
+ {
+ trace (TRACE_INFO,"main(): using NORMAL_DELIVERY");
+ /* parse the list and scan for field and content */
+ mime_list(header,headersize);
+ /* parse for destination addresses */
+
+ if (!mail_adr_list())
+ trace(TRACE_STOP,"main(): scanner found no email addresses");
+ }
+
+ /* inserting messages into the database */
+ insert_messages(header, headersize);
+
+ free(header); /* cleanup the header */
+ return 0;
+}
8 main.h
@@ -0,0 +1,8 @@
+#ifndef _MAIN_H
+#define _MAIN_H
+#endif
+
+#include "debug.h"
+#include "list.h"
+#include "dbmysql.h"
+
178 mime.c
@@ -0,0 +1,178 @@
+/*
+Functions for parsing a mime mailheader (actually just for scanning for email messages
+ and parsing the messageID */
+
+#include "config.h"
+#include "mime.h"
+
+#define MIME_FIELD_MAX 128
+#define MIME_VALUE_MAX 1024
+#define MEM_BLOCK 1024
+
+struct mime_record
+{
+ char field[MIME_FIELD_MAX];
+ char value[MIME_VALUE_MAX];
+};
+
+extern struct list mimelist;
+extern struct list users;
+
+void mime_list(char *blkdata, unsigned long blksize)
+/* returns <0 on failure */
+/* created a mimelist and maillist as positive result */
+{
+ int t,x,i=0; /* counter */
+
+ int valid_mime_lines=0;
+
+ char *tmpstr, *tmpfield, *tmpvalue, *ptr;
+ struct mime_record *mr;
+ struct element *el;
+
+ trace (TRACE_INFO, "mime_list(): entering mime loop");
+
+ memtst((tmpstr=(char *)malloc(MEM_BLOCK))==NULL);
+ memtst((tmpfield=(char *)malloc(MIME_FIELD_MAX))==NULL);
+ memtst((tmpvalue=(char *)malloc(MIME_VALUE_MAX))==NULL);
+ memtst((mr=(struct mime_record *)malloc(sizeof(struct mime_record)))==NULL);
+
+ while (i<blksize)
+ {
+ /* quick hack to jump over those naughty \n\t fields */
+ ptr=blkdata;
+ for (t=0;ptr!=NULL;t++)
+ {
+ if ((ptr[0]=='\n') && (ptr[1]!='\t'))
+ break;
+ ptr++;
+ }
+
+ x=strlen(blkdata)-strlen(ptr); /* first line */
+ if (x>1)
+ {
+ memtst((strncpy(tmpstr,blkdata,x))==NULL);
+ memtst((strcpy (&tmpstr[x],"\0"))==NULL);
+ if (ptr!=NULL)
+ {
+ blkdata=ptr+1;
+ trace (TRACE_DEBUG,"mime_list(): captured array [%s]",tmpstr);
+
+ /* parsing tmpstring for field and data */
+ /* field is xxxx: */
+
+ ptr=strstr(tmpstr,":");
+ if (ptr!=NULL)
+ {
+ valid_mime_lines++;
+ memtst((strncpy(tmpfield,tmpstr,ptr-tmpstr))==NULL);
+ tmpfield[ptr-tmpstr]='\0';
+
+ /* skip all spaces and semicollons after the fieldname */
+ while ((*ptr==':') || (*ptr==' ')) ptr++;
+ memtst((strcpy (tmpvalue,ptr))==NULL);
+ tmpvalue[strlen(ptr)]='\0';
+
+ memtst((strcpy (mr->field,tmpfield))==NULL);
+ memtst((strcpy (mr->value,tmpvalue))==NULL);
+
+ trace (TRACE_DEBUG,"mime_list(): mimepair found: [%s] [%s] \n",mr->field, mr->value);
+
+ memtst((el=list_nodeadd(&mimelist,mr,sizeof (*mr)))==NULL);
+
+ i=i+x;
+ }
+ }
+ }
+ else break;
+ }
+ trace (TRACE_DEBUG,"mime_list(): mimeloop finished");
+ if (valid_mime_lines<2)
+ {
+ free(blkdata);
+ trace (TRACE_STOP,"mime_list(): no valid mime headers found");
+ }
+}
+
+int mail_adr_list_special(int offset, int max, char *address_array[])
+ {
+ int mycount;
+
+ trace (TRACE_INFO,"mail_adr_list_special(): gathering info from command line");
+ for (mycount=offset;mycount!=max; mycount++)
+ {
+ trace(TRACE_DEBUG,"mail_adr_list_special(): adding [%s] to userlist",address_array[mycount]);
+ memtst((list_nodeadd(&users,address_array[mycount],(strlen(address_array[mycount])+1)))==NULL);
+ }
+ return mycount;
+ }
+
+int mail_adr_list()
+{
+ struct element *raw;
+ struct mime_record *mr;
+ char *tmpvalue, *ptr,*tmp;
+
+
+ memtst((tmpvalue=(char *)calloc(MIME_VALUE_MAX,sizeof(char)))==NULL);
+
+ trace (TRACE_INFO,"mail_adr_list(): mail address parser starting");
+
+ while ((raw=list_getstart(&mimelist))!=NULL)
+ {
+ mr=(struct mime_record *)raw->data;
+ /* FIXME: need to check which header we
+ * need to scan. IMHO it's only the delivered
+ * to */
+ if ((strcasecmp(mr->field,"delivered-to")==0))
+ {
+ /* Scan for email addresses and add them to our list */
+ /* the idea is to first find the first @ and go both ways */
+ /* until an non-emailaddress character is found */
+ ptr=strstr(mr->value,"@");
+ while (ptr!=NULL)
+ {
+ /* found an @! */
+ /* first go as far left as possible */
+ tmp=ptr;
+ while ((tmp!=mr->value) &&
+ (tmp[0]!='<') &&
+ (tmp[0]!=' ') &&
+ (tmp[0]!='\0') &&
+ (tmp[0]!=','))
+ tmp--;
+ if ((tmp[0]=='<') || (tmp[0]==' ') || (tmp[0]=='\0')
+ || (tmp[0]==',')) tmp++;
+ while ((ptr!=NULL) &&
+ (ptr[0]!='>') &&
+ (ptr[0]!=' ') &&
+ (ptr[0]!=',') &&
+ (ptr[0]!='\0'))
+ ptr++;
+ memtst((strncpy(tmpvalue,tmp,ptr-tmp))==NULL);
+ /* always set last value to \0 to end string */
+ tmpvalue[ptr-tmp]='\0';
+
+ /* one extra for \0 in strlen */
+ memtst((list_nodeadd(&users,tmpvalue,
+ (strlen(tmpvalue)+1)))==NULL);
+
+ /* printf ("total nodes:\n");
+ list_showlist(&users);
+ next address */
+ ptr=strstr(ptr,"@");
+ trace (TRACE_DEBUG,"mail_adr_list(): found %s, next in list is %s",
+ tmpvalue,ptr);
+ }
+ }
+ list_nodedel(&mimelist,raw->data);
+ }
+
+ trace (TRACE_DEBUG,"mail_adr_list(): found %d emailaddresses",list_totalnodes(&users));
+
+ trace (TRACE_INFO,"mail_adr_list(): mail address parser finished");
+
+ if (list_totalnodes(&users)==0) /* no addresses found */
+ return -1;
+ return 0;
+}
3 mime.h
@@ -0,0 +1,3 @@
+#include "dbmysql.h"
+#include "debug.h"
+#include "list.h"
43 misc.c
@@ -0,0 +1,43 @@
+/* Miscelaneous functions */
+
+#include "config.h"
+#include "misc.h"
+
+extern struct list users;
+
+struct list tmplist;
+
+struct element *finduser(char *username)
+{
+ struct element *e;
+
+ e=list_getstart(&tmplist);
+ while (e!=NULL)
+ {
+ if (strcmp((char *)e->data,username)==0)
+ return e;
+ e=e->nextnode;
+ }
+ return NULL;
+}
+
+void check_duplicates()
+{
+/* The target email addresses must not contain duplicates.
+ This would happen if somebody is in the to: and the Cc: field.
+ The MTA would deliver it twice. We won't. */
+
+ struct element *tmp;
+
+ printf ("Checking for duplicates\n");
+
+ tmp=list_getstart(&users);
+ while (tmp!=NULL)
+ {
+ if (finduser((char *)tmp->data)==NULL)
+ list_nodeadd(&tmplist,(void *)tmp->data,strlen((char *)tmp->data));
+ tmp=tmp->nextnode;
+ }
+ users=tmplist;
+ printf ("total email addresses found %d\n",list_totalnodes(&users));
+}
3 misc.h
@@ -0,0 +1,3 @@
+#include "dbmysql.h"
+#include "debug.h"
+#include "list.h"
222 pipe.c
@@ -0,0 +1,222 @@
+ /* Functions for reading the pipe from the MTA */
+
+#include "config.h"
+#include "pipe.h"
+
+#define READ_BLOCK_SIZE 524288 /* be carefull, MYSQL has a limit */
+#define HEADER_BLOCK_SIZE 1024
+#define QUERY_SIZE 255
+/* including the possible all escape strings blocks */
+
+extern struct list users;
+
+void create_unique_id(char *target, unsigned long messageid)
+ {
+ time_t now;
+ time(&now);
+ trace (TRACE_DEBUG,"create_unique_id(): createding id",target);
+ snprintf (target,UID_SIZE,"%luA%lu",messageid,now);
+ trace (TRACE_DEBUG,"create_unique_id(): created: %s",target);
+ }
+
+
+char *read_header(unsigned long *blksize)
+/* returns <0 on failure */
+{
+ /* reads incoming pipe until header is found */
+ /* we're going to check every READ_BLOCK_SIZE if header is read in memory */
+
+ char *header, *strblock;
+ int usedmem=0;
+ int end_of_header=0;
+
+ memtst ((strblock = (char *)malloc(READ_BLOCK_SIZE))==NULL);
+ memtst ((header = (char *)malloc(HEADER_BLOCK_SIZE))==NULL);
+
+ /* here we will start a loop to read in the message header */
+ /* the header will be everything up until \n\n or an EOF of */
+ /* in_stream (stdin) */
+
+ trace (TRACE_INFO, "read_header(): readheader start");
+
+ while ((end_of_header==0) && (!feof(stdin)))
+ {
+ strblock=fgets(strblock,READ_BLOCK_SIZE,stdin);
+
+ usedmem=usedmem + strlen(strblock);
+
+ if (usedmem>HEADER_BLOCK_SIZE)
+
+ /* add one for \0, since we use strlen for size */
+ memtst(((char *)realloc(header,usedmem+1))==NULL);
+
+ /* now we concatenate all we have to the header array */
+ memtst((header=strcat (header,strblock))==NULL);
+ if (strstr(header,"\n\n")!=NULL)
+ end_of_header=1;
+
+ /* reset strlen to 0 */
+ strblock[0]='\0';
+ }
+
+ trace (TRACE_INFO, "read_header(): readheader done");
+ trace (TRACE_DEBUG, "read_header(): found header [%s]",header);
+
+ free(strblock);
+
+ if (usedmem==0)
+ {
+ free(strblock);
+ free(header);
+ trace (TRACE_STOP, "read_header(): not a valid mailheader found");
+ }
+ *blksize=strlen(header);
+
+ trace (TRACE_INFO, "read_header(): function successfull");
+ return header;
+}
+
+int insert_messages(char *firstblock, unsigned long headersize)
+{
+ /* this loop gets all the users from the list
+ and check if they're in the database
+ firstblock is the header which was already read */
+
+ struct element *tmp;
+ char *insertquery;
+ char *updatequery;
+ char *unique_id;
+ char *strblock;
+ size_t usedmem=0, totalmem=0;
+ struct list userids;
+ struct list messageids;
+ unsigned long temp;
+
+ /* step 1.
+ inserting first message
+ first insert the header into the database
+ the result is the first message block
+ next create a message record
+ update the first block with the messagerecord id number
+ add the rest of the messages
+ update the last and the total memory field*/
+
+ /* creating a message record for the user */
+ /* all email users to which this message is sent much receive this */
+
+
+ memtst((insertquery = (char *)malloc(QUERY_SIZE))==NULL);
+ memtst((updatequery = (char *)malloc(QUERY_SIZE))==NULL);
+ memtst((unique_id = (char *)malloc(UID_SIZE))==NULL);
+
+ /* initiating list with userid's */
+ list_init(&userids);
+
+ /* initiating list with messageid's */
+ list_init(&messageids);
+
+ tmp=list_getstart(&users);
+
+
+ while (tmp!=NULL)
+ {
+ /* loops all mailusers and adds them to the list */
+ /* db_check_user(): returns a list with longs containing
+ * userid's */
+ db_check_user((char *)tmp->data,&userids);
+ trace (TRACE_DEBUG,"insert_messages(): user [%s] found total of [%d] aliases",(char *)tmp->data,
+ userids.total_nodes);
+ tmp=tmp->nextnode;
+ }
+
+ tmp=list_getstart(&userids);
+
+ while (tmp!=NULL)
+ {
+ /* traversing list with userids and creating a message for each userid */
+ trace (TRACE_DEBUG,"insert_messages(): -----> debug tmp is [%d],nextnode is [%d]",
+ tmp,tmp->nextnode);
+ sprintf(insertquery,"INSERT INTO message(useridnr,messagesize,status,unique_id) VALUES (%lu,0,0,\" \")",
+ *(unsigned long*)tmp->data);
+
+ trace (TRACE_DEBUG,"insert_messages(): executing query [%s]",insertquery);
+
+ /* message id is an array of returned message id's
+ * all messageblks are inserted for each message id
+ * we could change this in the future for efficiency
+ * still we would need a way of checking which messageblks
+ * belong to which messages */
+
+ db_query(insertquery);
+ temp=db_insert_result();
+
+ /* adding this messageid to the message id list */
+ list_nodeadd(&messageids,&temp,sizeof(temp));
+
+ /* adding the first header block per user */
+ db_insert_message_block (firstblock,temp);
+
+ tmp=tmp->nextnode;
+ }
+
+ /* reading rest of the pipe and creating messageblocks
+ * we need to create a messageblk for each messageid */
+
+ trace (TRACE_DEBUG,"insert_messages(): allocating [%d] memory for readblock",READ_BLOCK_SIZE);
+
+ memtst ((strblock = (char *)malloc(READ_BLOCK_SIZE))==NULL);
+
+ /* here we'll loop until we've read all what's left in the buffer
+ * fread is used here because we want large blocks */
+
+ while (!feof(stdin))
+ {
+ /* strblock=fgets(strblock,READ_BLOCK_SIZE,stdin); */
+ usedmem = fread (strblock, sizeof (char), READ_BLOCK_SIZE, stdin);
+
+ if (strblock!=NULL) /* this happends when a eof occurs */
+ {
+ totalmem=totalmem+usedmem;
+
+ tmp=list_getstart(&messageids);
+
+ while (tmp!=NULL)
+ {
+ trace(TRACE_DEBUG,"insert_messages(): inserting for [%lu]",
+ *(unsigned long *)tmp->data);
+ db_insert_message_block (strblock,*(unsigned long *)tmp->data);
+ tmp=tmp->nextnode;
+ }
+
+ /* resetting strlen for strblock */
+ strblock[0]='\0';
+ usedmem = 0;
+ }
+ }
+
+ trace (TRACE_DEBUG,"insert_messages(): updating size fields");
+
+ /* we need to update messagesize in all messages */
+ tmp=list_getstart(&messageids);
+ while (tmp!=NULL)
+ {
+ /* we need to create a unique id per message
+ * we're using the messageidnr for this, it's unique
+ * a special field is created in the database for other possible
+ * even more unique strings */
+ create_unique_id(unique_id,*(unsigned long*)tmp->data);
+ sprintf(updatequery,
+ "UPDATE message SET messagesize=%lu, unique_id=\"%s\" where messageidnr=%lu",
+ totalmem+headersize,unique_id,*(unsigned long*)tmp->data);
+ trace (TRACE_MESSAGE,"insert_messages(): message id=%lu, size=%lu is inserted",
+ *(unsigned long*)tmp->data, totalmem+headersize);
+ db_query(updatequery);
+ tmp=tmp->nextnode;
+ }
+
+ free(unique_id);
+ free (strblock);
+ free(insertquery);
+ free(updatequery);
+ return 0;
+}
4 pipe.h
@@ -0,0 +1,4 @@
+#include <time.h>
+#include "dbmysql.h"
+#include "debug.h"
+#include "list.h"
373 pop3.c
@@ -0,0 +1,373 @@
+/* implementation for pop3 commands accoording to RFC 1081 */
+
+#include "config.h"
+#include "pop3.h"
+#include "dbmysql.h"
+
+extern int state; /* tells the current negotiation state of the server */
+extern char *username, *password; /* session username and password */
+extern struct session curr_session;
+
+
+/* allowed pop3 commands */
+const char *commands [] =
+ {
+ "quit", "user", "pass", "stat", "list", "retr", "dele", "noop", "last", "rset",
+ "uidl"
+ };
+
+const char validchars[] = ".@ abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
+
+
+int pop3 (void *stream, char *buffer)
+ {
+ /* returns a 0 on a quit
+ * -1 on a failure
+ * 1 on a success */
+ char *command, *value;
+ int cmdtype, found=0;
+ int indx=0;
+ unsigned long result;
+ struct element *tmpelement;
+
+ while (strchr(validchars, buffer[indx]))
+ indx++;
+
+ buffer[indx]='\0';
+
+ trace(TRACE_DEBUG,"pop3(): incoming buffer: [%s]",buffer);
+
+ command=buffer;
+
+ value=strstr (command," "); /* look for the separator */
+
+ if (value!=NULL) /* none found */
+ {
+ /* clear the last \0 */
+ value[indx-1]='\0';
+
+ if (strlen(value)==1)
+ value=NULL; /* there is only a space! */
+ else
+ {
+ /* set value one further then the space */
+ value++;
+
+ /* set a \0 on the command end */
+ command[value-command-1]='\0';
+ trace (TRACE_DEBUG,"pop3(): command issued :cmd [%s], value [%s]\n",command, value);
+ }
+ }
+
+ for (cmdtype = POP3_STRT; cmdtype < POP3_END; cmdtype ++)
+ if (strcasecmp(command, commands[cmdtype]) == 0) break;
+
+ if ((value==NULL) && (cmdtype!=POP3_QUIT) && (cmdtype!=POP3_LIST) &&
+ (cmdtype!=POP3_STAT) && (cmdtype!=POP3_RSET) && (cmdtype!=POP3_NOOP) &&
+ (cmdtype!=POP3_LAST) && (cmdtype!=POP3_UIDL))
+ {
+ fprintf ((FILE *)stream,"-ERR does not compute\r\n");
+ return 1;
+ }
+
+ switch (cmdtype)
+ {
+ case POP3_QUIT :
+ {
+ fprintf ((FILE *)stream, "+OK see ya later\r\n");
+ return 0;
+ }
+ case POP3_USER :
+ {
+ if (state!=AUTHORIZATION)
+ {
+ fprintf ((FILE *)stream,"-ERR wrong command mode\r\n");
+ return 1;
+ }
+
+ if (username!=NULL)
+ {
+ free(username);
+ username=NULL;
+ }
+
+ if (username==NULL)
+ {
+ /* create memspace for username */
+ memtst((username=(char *)malloc(strlen(value)+1))==NULL);
+ strncpy (username,value,strlen(value)+1);
+ }
+
+ fprintf ((FILE *)stream, "+OK Password required for %s\r\n",username);
+ break;
+ }
+
+ case POP3_PASS :
+ {
+ if (state!=AUTHORIZATION)
+ {
+ fprintf ((FILE *)stream,"-ERR wrong command mode\r\n");
+ return 1;
+ }
+
+ if (password!=NULL)
+ {
+ free(password);
+ password=NULL;
+ }
+
+ if (password==NULL)
+ {
+ /* create memspace for password */
+ memtst((password=(char *)malloc(strlen(value)+1))==NULL);
+ strncpy (password,value,strlen(value)+1);
+ }
+
+ result=db_validate (username,password);
+
+ switch (result)
+ {
+ case -1: return -1;
+ case 0:
+ {
+ fprintf ((FILE *)stream, "-ERR username/password combination is incorrect\r\n");
+ return 1;
+ }
+ default:
+ {
+ state = TRANSACTION;
+ /* now we're going to build up a session for this user */
+ trace(TRACE_DEBUG,"pop3(): validation ok, creating session");
+ result=db_createsession (result, &curr_session);
+ if (result==1)
+ {
+ fprintf ((FILE *)stream, "+OK %s has %lu messages (%lu octets).\r\n",
+ username, curr_session.virtual_totalmessages,
+ curr_session.virtual_totalsize);
+ trace(TRACE_MESSAGE,"pop3(): user %s logged in [messages=%lu, octets=%lu]",
+ username, curr_session.virtual_totalmessages,
+ curr_session.virtual_totalsize);
+ }
+ return result;
+ }
+ }
+ break;
+ }
+
+ case POP3_LIST :
+ {
+ if (state!=TRANSACTION)
+ {
+ fprintf ((FILE *)stream,"-ERR wrong command mode\r\n");
+ return 1;
+ }
+ tmpelement=list_getstart(&curr_session.messagelst);
+ if (value!=NULL)
+ {
+ /* they're asking for a specific message */
+ while (tmpelement!=NULL)
+ {
+ if (((struct message *)tmpelement->data)->messageid==atol(value) &&
+ ((struct message *)tmpelement->data)->virtual_messagestatus<2)
+ {
+ fprintf ((FILE *)stream,"+OK %lu %lu\r\n",((struct message *)tmpelement->data)->messageid,
+ ((struct message *)tmpelement->data)->msize);
+ found=1;
+ }
+ tmpelement=tmpelement->nextnode;
+ }
+ if (!found)
+ fprintf ((FILE *)stream,"-ERR no such message\r\n");
+ return 1;
+ }
+
+ /* just drop the list */
+ fprintf ((FILE *)stream, "+OK %lu messages (%lu octets)\r\n",curr_session.virtual_totalmessages,
+ curr_session.virtual_totalsize);
+ if (curr_session.virtual_totalmessages>0)
+ {
+ /* traversing list */
+ while (tmpelement!=NULL)
+ {
+ if (((struct message *)tmpelement->data)->virtual_messagestatus<2)
+ fprintf ((FILE *)stream,"%lu %lu\r\n",((struct message *)tmpelement->data)->messageid,
+ ((struct message *)tmpelement->data)->msize);
+ tmpelement=tmpelement->nextnode;
+ }
+ }
+ fprintf ((FILE *)stream,".\r\n");
+ return 1;
+ }
+
+ case POP3_STAT :
+ {
+ if (state!=TRANSACTION)
+ {
+ fprintf ((FILE *)stream,"-ERR wrong command mode\r\n");
+ return 1;
+ }
+ fprintf ((FILE *)stream, "+OK %lu %lu\r\n",curr_session.virtual_totalmessages,
+ curr_session.virtual_totalsize);
+ return 1;
+ }
+
+ case POP3_RETR :
+ {
+ trace(TRACE_DEBUG,"pop3():RETR command, retrieving message");
+ if (state!=TRANSACTION)
+ {
+ fprintf ((FILE *)stream,"-ERR wrong command mode\r\n");
+ return 1;
+ }
+ tmpelement=list_getstart(&curr_session.messagelst);
+ /* selecting a message */
+ trace(TRACE_DEBUG,"pop3(): RETR command, selecting message");
+ while (tmpelement!=NULL)
+ {
+ if (((struct message *)tmpelement->data)->messageid==atol(value) &&
+ ((struct message *)tmpelement->data)->virtual_messagestatus<2) /* message is not deleted */
+ {
+ ((struct message *)tmpelement->data)->virtual_messagestatus=1;
+ fprintf ((FILE *)stream,"+OK %lu octets\r\n",((struct message *)tmpelement->data)->msize);
+ return db_send_message ((void *)stream, ((struct message *)tmpelement->data)->realmessageid);
+ }
+ tmpelement=tmpelement->nextnode;
+ }
+ fprintf ((FILE *)stream,"-ERR no such message\r\n");
+ return 1;
+ }
+
+ case POP3_DELE :
+ {
+ if (state!=TRANSACTION)
+ {
+ fprintf ((FILE *)stream,"-ERR wrong command mode\r\n");
+ return 1;
+ }
+ tmpelement=list_getstart(&curr_session.messagelst);
+ /* selecting a message */
+ while (tmpelement!=NULL)
+ {
+ if (((struct message *)tmpelement->data)->messageid==atol(value) &&
+ ((struct message *)tmpelement->data)->virtual_messagestatus<2) /* message is not deleted */
+ {
+ ((struct message *)tmpelement->data)->virtual_messagestatus=2;
+ /* decrease our virtual list fields */
+ curr_session.virtual_totalsize-=((struct message *)tmpelement->data)->msize;
+ curr_session.virtual_totalmessages-=1;
+ fprintf((FILE *)stream,"+OK message %lu deleted\r\n",
+ ((struct message *)tmpelement->data)->messageid);
+ return 1;
+ }
+ tmpelement=tmpelement->nextnode;
+ }
+ fprintf ((FILE *)stream,"-ERR [%s] no such message\r\n",value);
+ return 1;
+ }
+
+ case POP3_RSET :
+ {
+ if (state!=TRANSACTION)
+ {
+ fprintf ((FILE *)stream,"-ERR wrong command mode\r\n");
+ return 1;
+ }
+ tmpelement=list_getstart(&curr_session.messagelst);
+
+ curr_session.virtual_totalsize=curr_session.totalsize;
+ curr_session.virtual_totalmessages=curr_session.totalmessages;
+ while (tmpelement!=NULL)
+ {
+ ((struct message *)tmpelement->data)->virtual_messagestatus=((struct message *)tmpelement->data)->messagestatus;
+ tmpelement=tmpelement->nextnode;
+ }
+
+ fprintf ((FILE *)stream, "+OK %lu messages (%lu octets)\r\n",curr_session.virtual_totalmessages,
+ curr_session.virtual_totalsize);
+
+ return 1;
+ }
+
+ case POP3_LAST :
+ {
+ if (state!=TRANSACTION)
+ {
+ fprintf ((FILE *)stream,"-ERR wrong command mode\r\n");
+ return 1;
+ }
+ tmpelement=list_getstart(&curr_session.messagelst);
+
+ while (tmpelement!=NULL)
+ {
+ if (((struct message *)tmpelement->data)->virtual_messagestatus==0)
+ {
+ /* we need the last message that has been accessed */
+ fprintf ((FILE *)stream, "+OK %lu\r\n",((struct message *)tmpelement->data)->messageid-1);
+ return 1;
+ }
+ tmpelement=tmpelement->nextnode;
+ }
+ /* all old messages */
+ fprintf ((FILE *)stream, "+OK %lu\r\n",curr_session.virtual_totalmessages);
+ return 1;
+ }
+
+ case POP3_NOOP :
+ {
+ if (state!=TRANSACTION)
+ {
+ fprintf ((FILE *)stream,"-ERR wrong command mode\r\n");
+ return 1;
+ }
+ fprintf ((FILE *)stream, "+OK\r\n");
+ return 1;
+ }
+ case POP3_UIDL :
+ {
+ if (state!=TRANSACTION)
+ {
+ fprintf ((FILE *)stream,"-ERR wrong command mode\r\n");
+ return 1;
+ }
+ tmpelement=list_getstart(&curr_session.messagelst);
+ if (value!=NULL)
+ {
+ /* they're asking for a specific message */
+ while (tmpelement!=NULL)
+ {
+ if (((struct message *)tmpelement->data)->messageid==atol(value))
+ {
+ fprintf ((FILE *)stream,"+OK %lu %s\r\n",((struct message *)tmpelement->data)->messageid,
+ ((struct message *)tmpelement->data)->uidl);
+ found=1;
+ }
+ tmpelement=tmpelement->nextnode;
+ }
+ if (!found)
+ fprintf ((FILE *)stream,"-ERR no such message\r\n");
+ return 1;
+ }
+ /* just drop the list */
+ fprintf ((FILE *)stream, "+OK Some very unique numbers for you\r\n");
+ if (curr_session.virtual_totalmessages>0)
+ {
+ /* traversing list */
+ while (tmpelement!=NULL)
+ {
+ fprintf ((FILE *)stream,"%lu %s\r\n",((struct message *)tmpelement->data)->messageid,
+ ((struct message *)tmpelement->data)->uidl);
+ tmpelement=tmpelement->nextnode;
+ }
+ }
+ fprintf ((FILE *)stream,".\r\n");
+ return 1;
+ }
+
+ default :
+ {
+ fprintf ((FILE *)stream, "-ERR Huh? command not understood\r\n");
+ return 1;
+ }
+ }
+ return 1;
+ }
84 pop3.h
@@ -0,0 +1,84 @@
+/* this defines some default messages for POP3 */
+
+#ifndef _POP3_H
+#define _POP3_H
+#endif
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <sys/wait.h>
+#include <signal.h>
+
+#include "list.h"
+#include "debug.h"
+#include "config.h"
+
+/* processes */
+
+#define MAXCHILDREN 5
+#define DEFAULT_CHILDREN 5
+
+/* connection */
+
+#define PORT 10115
+#define BACKLOG 10
+
+#define AUTHORIZATION 1
+#define TRANSACTION 2
+#define UPDATE 3
+
+#define POP3_STRT 0
+#define POP3_QUIT 0
+#define POP3_USER 1
+#define POP3_PASS 2
+#define POP3_STAT 3
+#define POP3_LIST 4
+#define POP3_RETR 5
+#define POP3_DELE 6
+#define POP3_NOOP 7
+#define POP3_LAST 8
+#define POP3_RSET 9
+#define POP3_UIDL 10
+#define POP3_END 11
+
+/* all virtual_ definitions are session specific
+ * when a RSET occurs all will be set to the real values */
+
+struct message
+ {
+ unsigned long msize;
+ unsigned long messageid;
+ unsigned long realmessageid;
+ char uidl[UID_SIZE];
+ /* message status :
+ * 000 message is new, never touched
+ * 001 message is read
+ * 002 message is deleted by user
+ * ----------------------------------
+ * The server additionally uses:
+ * 003 message is deleted by sysop
+ * 004 message is ready for final deletion */
+
+ unsigned long messagestatus;
+ unsigned long virtual_messagestatus;
+ };
+
+struct session
+ {
+ unsigned long totalsize;
+ unsigned long virtual_totalsize;
+ unsigned long totalmessages;
+ unsigned long virtual_totalmessages;
+ struct list messagelst;
+ };
+
+int pop3 (void *stream, char *buffer);
214 pop3d.c
@@ -0,0 +1,214 @@
+/* pop3 daemon */
+
+#include "pop3.h"
+#include "dbmysql.h"
+
+#define INCOMING_BUFFER_SIZE 512
+#define MAXTIMEOUT 300
+#define IP_ADDR_MAXSIZE 16
+
+#ifndef SHUT_RDWR
+#define SHUR_RDWR 3
+#endif
+
+/* syslog */
+#define PNAME "dbmail/pop3"
+
+int state;
+char *username=NULL, *password=NULL;
+struct session curr_session;
+char *myhostname;
+
+char *buffer;
+int done=1;
+
+/* signal handler */
+static void sigchld_handler (int signo)
+ {
+ pid_t PID;
+ int status;
+
+ if (signo == SIGCHLD)
+ {
+ while (waitpid (-1, &status, WNOHANG));
+ return;
+ }
+ else
+ trace (TRACE_STOP,"sigchld_handler(): received fatal signal [%d]",signo);
+ }
+
+int main (int argc, char *argv[])
+{
+ struct sockaddr_in adr_srvr;
+ struct sockaddr_in adr_clnt;
+ char *myhostname;
+ char *theiraddress;
+
+ struct hostent *clientinfo;
+
+ int len_inet;
+ int s = -1;
+ int c = -1;
+ int z;
+ int children=0; /* child process counter */
+ FILE *tx = NULL; /* write socket */
+ FILE *rx = NULL; /* read socket */
+
+ /* open logs */
+
+ openlog(PNAME, LOG_PID, LOG_MAIL);
+
+
+ /* daemonize */
+ if (fork ())
+ exit (0);
+ setsid ();
+
+ if (fork ())
+ exit (0);
+
+ close (0);
+ close (1);
+ close (2);
+
+ /* reserve memory for hostname */
+ memtst((myhostname=(char *)malloc(64))==NULL);
+ memtst((clientinfo=(struct hostent *)malloc(1))==NULL);
+
+ /* getting hostname */
+ gethostname (myhostname,64);
+
+ /* set signal handler for SIGCHLD */
+ signal (SIGCHLD, sigchld_handler);
+ signal (SIGINT, sigchld_handler);
+ signal (SIGQUIT, sigchld_handler);
+ signal (SIGILL, sigchld_handler);
+ signal (SIGBUS, sigchld_handler);
+ signal (SIGFPE, sigchld_handler);
+ signal (SIGSEGV, sigchld_handler);
+ signal (SIGTERM, sigchld_handler);
+ signal (SIGSTOP, sigchld_handler);
+
+
+ adr_srvr.sin_family = AF_INET;
+ adr_srvr.sin_addr.s_addr = htonl (INADDR_ANY); /* bind to any address */
+ adr_srvr.sin_port = htons (PORT); /* listning port */
+
+ len_inet=sizeof (adr_srvr);
+
+ s = socket (PF_INET, SOCK_STREAM, 0); /* creating the socket */
+ if (s == -1 )
+ trace (TRACE_FATAL,"main(): call socket(2) failed");
+
+ z = bind (s, (struct sockaddr *)&adr_srvr, len_inet); /* bind to socket */
+ if (z == -1 )
+ trace (TRACE_FATAL,"main(): call bind(2) failed");
+
+ z = listen (s, BACKLOG); /* make the socket listen */
+ if (z == -1 )
+ trace (TRACE_FATAL,"main(): call listen(2) failed");
+
+ /* server loop */
+
+ trace (TRACE_MESSAGE,"main(): DBmail pop3 server ready");
+
+ for (;;)
+ {
+ if (children < MAXCHILDREN)
+ {
+ if (!fork())
+ {
+ /* wait for a connection */
+ len_inet = sizeof (adr_clnt);
+ c = accept (s, (struct sockaddr *)&adr_clnt,
+ &len_inet); /* incoming connection */
+
+ if (c == -1)
+ trace (TRACE_FATAL,"main(): call accept(2) failed");
+
+ /* register incoming connection */
+ theiraddress=inet_ntoa(adr_clnt.sin_addr);
+ clientinfo=gethostbyname(theiraddress);
+
+ if (theiraddress != NULL)
+ trace (TRACE_MESSAGE,"main(): incoming connection from [%s] [resolved to: %s]"
+ ,theiraddress,clientinfo->h_name);
+ else
+ trace (TRACE_FATAL,"main(): fatal, could not get address of client");
+
+ rx = fdopen (dup (c), "r"); /* duplicate descriptor and open it */
+ if (!rx)
+ {
+ /* opening of descriptor failed */
+ close(c);
+ continue;
+ }
+
+ tx = fdopen (dup (c), "w"); /* same thing for writing */
+ if (!tx)
+ {
+ /* opening of descriptor failed */
+ close (c);
+ continue;
+ }
+
+ /* set stream to line buffered mode
+ * this way when we send a newline the buffer is flushed */
+ setlinebuf(rx);
+ setlinebuf(tx);
+
+ /* process client requests */
+
+ /* connect to the database */
+ if (db_connect()< 0)
+ trace(TRACE_FATAL,"main(): could not connect to database");
+
+ /* first initiate AUTHORIZATION state */
+ state = AUTHORIZATION;
+
+ memtst((buffer=(char *)malloc(INCOMING_BUFFER_SIZE))==NULL);
+
+ /* sending greeting */
+ fprintf (tx,"+OK DBMAIL (currently running at %s) welcome you\r\n",myhostname);
+
+ /* scanning for commands */
+
+ while ((done>0) && (buffer=fgets(buffer,INCOMING_BUFFER_SIZE,rx)))
+ done=pop3(tx,buffer);
+
+ /* we've reached the update state */
+ state = UPDATE;
+
+ /* memory cleanup */
+ free(buffer);
+
+ trace(TRACE_MESSAGE,"pop3(): user %s logging out [message=%lu, octets=%lu]",
+ username, curr_session.virtual_totalmessages,
+ curr_session.virtual_totalsize);
+
+ if (done==0)
+ db_update_pop(&curr_session);
+ else db_disconnect();
+
+ fclose(tx);
+ shutdown (fileno(rx), SHUT_RDWR);
+ fclose(rx);
+
+ /* we must send an exit as child */
+
+ free(myhostname);
+ exit(0);
+ }
+ else
+ {
+ children++;
+ }
+ }
+ else
+ {
+ wait (NULL);
+ children--;
+ }
+ }
+ return 0;
+}
41 sql/create_tables.sql
@@ -0,0 +1,41 @@
+CREATE DATABASE dbmail;
+USE dbmail;
+CREATE TABLE aliases (
+ alias_idnr int(11) DEFAULT '0' NOT NULL auto_increment,
+ alias varchar(100) NOT NULL,
+ useridnr int(11) NOT NULL,
+ PRIMARY KEY (alias_idnr),
+ KEY alias_idnr (alias_idnr),
+ UNIQUE alias_idnr_2 (alias_idnr)
+);
+CREATE TABLE mailbox (
+ useridnr int(11) DEFAULT '0' NOT NULL auto_increment,
+ userid varchar(100) NOT NULL,
+ passwd varchar(15) NOT NULL,
+ clientid varchar(8) NOT NULL,
+ maxmail int(11) DEFAULT '0' NOT NULL,
+ PRIMARY KEY (useridnr),
+ KEY useridnr (useridnr, userid),
+ UNIQUE useridnr_2 (useridnr)
+);
+CREATE TABLE message (
+ messageidnr bigint(21) DEFAULT '0' NOT NULL auto_increment,
+ useridnr int(11) DEFAULT '0' NOT NULL,
+ messagesize bigint(21) DEFAULT '0' NOT NULL,
+ status tinyint(3) unsigned zerofill DEFAULT '000' NOT NULL,
+ unique_id varchar(70) NOT NULL,
+ PRIMARY KEY (messageidnr),
+ KEY messageidnr (messageidnr),
+ UNIQUE messageidnr_2 (messageidnr)
+);
+
+CREATE TABLE messageblk (
+ messageblknr bigint(21) DEFAULT '0' NOT NULL auto_increment,
+ messageidnr bigint(21) DEFAULT '0' NOT NULL,
+ messageblk longtext NOT NULL,
+ blocksize bigint(21) DEFAULT '0' NOT NULL,
+ PRIMARY KEY (messageblknr),
+ KEY messageblknr (messageblknr),
+ KEY msg_index (messageidnr),
+ UNIQUE messageblknr_2 (messageblknr)
+) TYPE=MyISAM;

0 comments on commit f17891e

Please sign in to comment.