smtp._pike
Go to the documentation of this file.
1 /*0*/
2 inherit Service.Service;
3 #include <config.h>
4 class smtp {
5 public:
6 
7 
8 //#define SMTP_DEBUG
9 
10 #ifdef SMTP_DEBUG
11 #define SMTP_LOG(s, args...) werror("smtp: "+s+"\n", args)
12 #else
13 #define SMTP_LOG(s, args...)
14 #endif
15 
16 #define MESSAGE(s, args...) werror("smtp: "+s+"\n", args)
17 #define FATAL(s, args...) werror("error: "+s+"\n", args)
18 
19 #if constant(Protocols.SMTP.client)
20 #define SMTPCLIENT Protocols.SMTP.client
21 #else
22 #define SMTPCLIENT Protocols.SMTP.Client
23 #endif
24 
25  Thread.Queue MsgQueue = Thread.Queue();
26  Thread.Mutex saveMutex = Thread.Mutex();
27 
28  object oSMTP; // cache smtp object (connection)
29  object oDNS; // cache DNS Client
30  object dbhandle;
31 
32 void call_service(object user, mixed args, void|int id)
33 {
34  MsgQueue->write(args->msg);
35 }
36 
37 
38 protected:
39  void run()
40 {
41  dbhandle = Sql.Sql(serverCfg["database"]);
42  mixed err = catch( oDNS = Protocols.DNS.client() );
43  if ( err ) {
44  MESSAGE( "warning, could not create DNS client.\n" );
45  }
46  thread_create(smtp_thread);
47 }
48 
49 public:
50 
51 private:
52  private void got_kill(int sig) {
53  _exit(1);
54 }
55 
56 public:
57 
58 int main(int argc, array argv)
59 {
60  signal(signum("QUIT"), got_kill);
61  init( "smtp", argv );
62  start();
63  return -17;
64 }
65 
66 
67 void send_message(mapping msg)
68 {
69  string server;
70  int port;
71  mixed err;
72  object smtp;
73 
74  server = serverCfg[CFG_MAILSERVER];
75  port = (int)serverCfg[CFG_MAILPORT];
76  if ( !intp(port) || port <= 0 ) port = 25;
77 
78  SMTP_LOG("send_message: server: %O:%O", server, port );
79 
80  if ( !stringp(msg->from) ) {
81  msg->from = "admin@"+get_server_name();
82  SMTP_LOG("send_message: invalid 'from', using %s",
83  msg->from );
84  }
85  SMTP_LOG("send_message: mail from %O to %O (server: %O:%O)\n"+
86  " Subject: %O", msg->from, msg->email, server, port, msg->subject);
87 
88  if ( !arrayp(msg->email) )
89  msg->email = ({ msg->email });
90 
91  foreach ( msg->email, string email ) {
92  string tmp_server = server;
93  int tmp_port = port;
94  mixed smtp_error;
95  if ( stringp(server) && sizeof(server) > 0 ) {
96  smtp_error = catch {
97  smtp = SMTPCLIENT( server, port );
98  };
99  }
100  else {
101  // if no server is configured use the e-mail of the receiver
102  string host;
103  sscanf( email, "%*s@%s", host );
104  if ( !stringp(host) )
105  error("MX Lookup failed, host = 0 in %O", msg->email);
106 
107  if ( !objectp(oDNS) )
108  error("MX Lookup failed, no DNS");
109  tmp_server = oDNS->get_primary_mx(host);
110  array dns_data = oDNS->gethostbyname(tmp_server);
111  if ( arrayp(dns_data) && sizeof(dns_data) > 1 &&
112  arrayp(dns_data[1]) && sizeof(dns_data[1]) > 0 )
113  tmp_server = dns_data[1][0];
114  tmp_port = 25;
115  SMTP_LOG("send_message: MX lookup: %O:%O",
116  tmp_server, tmp_port );
117  smtp_error = catch {
118  smtp = SMTPCLIENT( tmp_server, tmp_port );
119  };
120  }
121 
122  if ( !objectp(smtp) || smtp_error ) {
123  string msg = sprintf( "Invalid mail server %O:%O (from %O to %O)\n",
124  tmp_server, tmp_port, msg->from, email );
125  if ( smtp_error ) msg += sprintf( "%O", smtp_error );
126  FATAL(msg );
127  continue;
128  }
129 
130  if ( stringp(msg->rawmime) ) {
131  // send directly
132  smtp_error = catch {
133  smtp->send_message(msg->from, ({ email }), msg->rawmime);
134  };
135  if ( smtp_error ) {
136  FATAL("Failed to send mail directly from %O to %O via %O:%O : %O",
137  msg->from, email, tmp_server, tmp_port, smtp_error[0]);
138  }
139  else {
140  MESSAGE("Mail sent directly from %O to %O via %O:%O\n",
141  msg->from, email, tmp_server, tmp_port );
142  }
143  continue;
144  }
145 
146  if ( !stringp(msg->mimetype) )
147  msg->mimetype = "text/plain";
148 
149  MIME.Message mmsg =
150  MIME.Message(
151  msg->body||"",
152  ([ "Content-Type": (msg->mimetype||"text/plain") + "; charset=utf-8",
153  "Mime-Version": "1.0 (generated by open-sTeam)",
154  "Subject": msg->subject||"",
155  "Date": msg->date || timelib.smtp_time(time()),
156  "From": msg->fromname||msg->from||msg->fromobj||"",
157  "To": (msg->fromobj ? msg->fromobj : email)||"",
158  "Message-Id": msg->message_id||"",
159  ]) );
160 
161  if(msg->mail_followup_to)
162  mmsg->headers["Mail-Followup-To"]=msg->mail_followup_to;
163  if(msg->reply_to)
164  mmsg->headers["Reply-To"]=msg->reply_to;
165  if(msg->in_reply_to)
166  mmsg->headers["In-Reply-To"]=msg->in_reply_to;
167 
168  smtp_error = catch {
169  smtp->send_message(msg->from, ({ email }), (string)mmsg);
170  };
171  if ( smtp_error ) {
172  FATAL("Failed to send mail from %O to %O via %O:%O"
173  + " : %O\n", msg->from, email, tmp_server, tmp_port, smtp_error[0] );
174  }
175  else {
176  MESSAGE("Mail sent from %O to %O via %O:%O\n",
177  msg->from, email, tmp_server, tmp_port );
178  }
179  }
180 }
181 
182 void delete_mail(mapping msg)
183 {
184  dbhandle->big_query(sprintf("delete from i_rawmails where mailid='%d'",
185  msg->msgid));
186 }
187 
188 protected:
189  void smtp_thread()
190 {
191  mapping msg;
192 
193  while ( 1 ) {
194  SMTP_LOG("smtp-thread running...");
195  msg = MsgQueue->read();
196  mixed err = catch {
197  send_message(msg);
198  delete_mail(msg);
199  MESSAGE("Message from %O to %O sent: '%O'",
200  msg->from, msg->email,
201  (stringp(msg->rawmime)?"mime message": msg->subject));
202  };
203  if ( err != 0 ) {
204  FATAL("Error while sending message:" + err[0] +
205  sprintf("\n%O\n", err[1]));
206  FATAL("MAILSERVER="+serverCfg[CFG_MAILSERVER]);
207  if ( objectp(oSMTP) ) {
208  destruct(oSMTP);
209  oSMTP = 0;
210  }
211  sleep(60); // wait one minute before retrying
212  }
213  }
214 }
215 
216 public:
217 
218 
219 };