anope- supernets anope source code & configuration |
git clone git://git.acid.vegas/anope.git |
Log | Files | Refs | Archive | README |
anopesmtp.cpp (10349B)
1 /* smtp stuff handler for win32. 2 * 3 * (C) 2003-2022 Anope Team 4 * Contact us at team@anope.org 5 * 6 * Please read COPYING and README for further details. 7 * 8 * Based on the original code of Epona by Lara. 9 * Based on the original code of Services by Andy Church. 10 * 11 * Written by Dominick Meglio <codemastr@unrealircd.com> 12 * *nix port by Trystan Scott Lee <trystan@nomadirc.net> 13 */ 14 15 #include "sysconf.h" 16 17 /* Some Linux boxes (or maybe glibc includes) require this for the 18 * prototype of strsignal(). */ 19 #ifndef _GNU_SOURCE 20 # define _GNU_SOURCE 21 #endif 22 23 #include <string> 24 #include <vector> 25 #include <cstdarg> 26 #include <cstdio> 27 #include <cstdlib> 28 #include <cstring> 29 #include <ctime> 30 #include <cerrno> 31 #include <iostream> 32 #include <fstream> 33 34 #ifndef _WIN32 35 # include <unistd.h> 36 # include <netdb.h> 37 # include <netinet/in.h> 38 # include <sys/socket.h> 39 # include <arpa/inet.h> 40 # include <sys/time.h> 41 #else 42 # include <winsock.h> 43 # define WIN32_LEAN_AND_MEAN 44 # include <windows.h> 45 #endif 46 47 #include <sys/types.h> 48 49 #ifdef _AIX 50 extern int strcasecmp(const char *, const char *); 51 extern int strncasecmp(const char *, const char *, size_t); 52 # if 0 /* These break on some AIX boxes (4.3.1 reported). */ 53 extern int socket(int, int, int); 54 extern int connect(int, struct sockaddr *, int); 55 # endif 56 #endif /* _AIX */ 57 58 /* Some SUN fixs */ 59 #ifdef __sun 60 /* Solaris specific code, types that do not exist in Solaris' 61 * * sys/types.h 62 * **/ 63 # ifndef INADDR_NONE 64 # define INADDR_NONE (-1) 65 # endif 66 #endif 67 68 #ifdef _WIN32 69 typedef SOCKET ano_socket_t; 70 #define ano_sockclose(fd) closesocket(fd) 71 #define ano_sockread(fd, buf, len) recv(fd, buf, len, 0) 72 #define ano_sockwrite(fd, buf, len) send(fd, buf, len, 0) 73 #else 74 typedef int ano_socket_t; 75 #define ano_sockclose(fd) close(fd) 76 #define ano_sockread(fd, buf, len) read(fd, buf, len) 77 #define ano_sockwrite(fd, buf, len) write(fd, buf, len) 78 #define SOCKET_ERROR -1 79 #endif 80 81 /* Data structures */ 82 struct smtp_message 83 { 84 std::vector<std::string> smtp_headers; 85 std::vector<std::string> smtp_body; 86 std::string from; 87 std::string to; 88 ano_socket_t sock; 89 }; 90 91 int smtp_debug = 0; 92 93 struct smtp_message smail; 94 95 static std::string get_logname(struct tm *tm = NULL) 96 { 97 char timestamp[32]; 98 99 if (!tm) 100 { 101 time_t t = time(NULL); 102 tm = localtime(&t); 103 } 104 105 strftime(timestamp, sizeof(timestamp), "%Y%m%d", tm); 106 std::string name = std::string("anopesmtp.") + timestamp; 107 return name; 108 } 109 110 /* TimeStamp for Email Header */ 111 static std::string GetTimeStamp() 112 { 113 char tbuf[256]; 114 time_t t = time(NULL); 115 struct tm *tm = gmtime(&t); 116 117 strftime(tbuf, sizeof(tbuf) - 1, "%a, %d %b %Y %H:%M:%S +0000", tm); 118 119 return tbuf; 120 } 121 122 /* Log stuff to the log file with a datestamp. Note that errno is 123 * preserved by this routine and log_perror(). 124 */ 125 126 void alog(const char *fmt, ...) 127 { 128 if (!smtp_debug || !fmt) 129 return; 130 131 std::fstream file; 132 file.open(get_logname().c_str(), std::ios_base::out | std::ios_base::app); 133 134 if (!file.is_open()) 135 return; 136 137 va_list args; 138 va_start(args, fmt); 139 140 time_t t = time(NULL); 141 struct tm *tm = localtime(&t); 142 143 char buf[256]; 144 strftime(buf, sizeof(buf) - 1, "[%b %d %H:%M:%S %Y] ", tm); 145 file << buf; 146 vsnprintf(buf, sizeof(buf), fmt, args); 147 file << buf << std::endl; 148 va_end(args); 149 va_end(args); 150 151 file.close(); 152 } 153 154 /* Remove a trailing \r\n */ 155 std::string strip(const std::string &buf) 156 { 157 std::string newbuf = buf; 158 char c = newbuf[newbuf.size() - 1]; 159 while (c == '\n' || c == '\r') 160 { 161 newbuf.erase(newbuf.end() - 1); 162 c = newbuf[newbuf.size() - 1]; 163 } 164 return newbuf; 165 } 166 167 /* Is the buffer a header? */ 168 bool smtp_is_header(const std::string &buf) 169 { 170 size_t tmp = buf.find(' '); 171 172 if (tmp == std::string::npos) 173 return false; 174 175 if (tmp > 0 && buf[tmp - 1] == ':') 176 return true; 177 return false; 178 } 179 180 /* Parse a header into a name and value */ 181 void smtp_parse_header(const std::string &buf, std::string &header, std::string &value) 182 { 183 std::string newbuf = strip(buf); 184 185 size_t space = newbuf.find(' '); 186 if (space != std::string::npos) 187 { 188 header = newbuf.substr(0, space); 189 value = newbuf.substr(space + 1); 190 } 191 else 192 { 193 header = newbuf; 194 value = ""; 195 } 196 } 197 198 /* Have we reached the end of input? */ 199 bool smtp_is_end(const std::string &buf) 200 { 201 if (buf[0] == '.') 202 if (buf[1] == '\r' || buf[1] == '\n') 203 return true; 204 205 return false; 206 } 207 208 /* Set who the email is to */ 209 void smtp_set_to(const std::string &to) 210 { 211 smail.to = to; 212 size_t c = smail.to.rfind('<'); 213 if (c != std::string::npos && c + 1 < smail.to.size()) 214 { 215 smail.to = smail.to.substr(c + 1); 216 smail.to.erase(smail.to.end() - 1); 217 } 218 } 219 220 /* Establish a connection to the SMTP server */ 221 int smtp_connect(const char *host, unsigned short port) 222 { 223 struct sockaddr_in addr; 224 225 if ((smail.sock = socket(AF_INET, SOCK_STREAM, 0)) == SOCKET_ERROR) 226 return 0; 227 228 if ((addr.sin_addr.s_addr = inet_addr(host)) == INADDR_NONE) 229 { 230 struct hostent *hent; 231 if (!(hent = gethostbyname(host))) 232 return 0; 233 memcpy(&addr.sin_addr, hent->h_addr, hent->h_length); 234 } 235 addr.sin_family = AF_INET; 236 addr.sin_port = htons(port ? port : 25); 237 if (connect(smail.sock, reinterpret_cast<struct sockaddr *>(&addr), sizeof(struct sockaddr_in)) == SOCKET_ERROR) 238 { 239 ano_sockclose(smail.sock); 240 return 0; 241 } 242 243 return 1; 244 } 245 246 /* Send a line of text */ 247 int smtp_send(const char *text) 248 { 249 int result = ano_sockwrite(smail.sock, text, strlen(text)); 250 251 alog("SMTP: sent %s",text); 252 253 if (result == SOCKET_ERROR) 254 ano_sockclose(smail.sock); 255 256 return result; 257 } 258 259 /* Read a line of text */ 260 int smtp_read(char *buf, int len) 261 { 262 int result; 263 264 memset(buf, 0, len); 265 result = ano_sockread(smail.sock, buf, len); 266 267 if (result == SOCKET_ERROR) 268 ano_sockclose(smail.sock); 269 270 return result; 271 } 272 273 /* Retrieve a response code */ 274 int smtp_get_code(const std::string &text) 275 { 276 size_t tmp = text.find(' '); 277 278 if (tmp == std::string::npos) 279 return 0; 280 281 return atol(text.substr(0, tmp).c_str()); 282 } 283 284 /* Send the email */ 285 int smtp_send_email() 286 { 287 char buf[1024]; 288 if (!smtp_read(buf, 1024)) 289 { 290 alog("SMTP: error reading buffer"); 291 return 0; 292 } 293 294 int code = smtp_get_code(buf); 295 if (code != 220) 296 { 297 alog("SMTP: error expected code 220 got %d",code); 298 return 0; 299 } 300 301 if (!smtp_send("HELO anope\r\n")) 302 { 303 alog("SMTP: error writing to socket"); 304 return 0; 305 } 306 307 if (!smtp_read(buf, 1024)) 308 { 309 alog("SMTP: error reading buffer"); 310 return 0; 311 } 312 313 code = smtp_get_code(buf); 314 if (code != 250) 315 { 316 alog("SMTP: error expected code 250 got %d",code); 317 return 0; 318 } 319 320 strcpy(buf, "MAIL FROM: <"); 321 strcat(buf, smail.from.c_str()); 322 strcat(buf, ">\r\n"); 323 324 if (!smtp_send(buf)) 325 { 326 alog("SMTP: error writing to socket"); 327 return 0; 328 } 329 330 if (!smtp_read(buf, 1024)) 331 { 332 alog("SMTP: error reading buffer"); 333 return 0; 334 } 335 336 code = smtp_get_code(buf); 337 if (code != 250) 338 return 0; 339 340 strcpy(buf, "RCPT TO: <"); 341 strcat(buf, smail.to.c_str()); 342 strcat(buf, ">\r\n"); 343 344 if (!smtp_send(buf)) 345 { 346 alog("SMTP: error writing to socket"); 347 return 0; 348 } 349 350 if (!smtp_read(buf, 1024)) 351 { 352 alog("SMTP: error reading buffer"); 353 return 0; 354 } 355 356 code = smtp_get_code(buf); 357 if (smtp_get_code(buf) != 250) 358 { 359 alog("SMTP: error expected code 250 got %d",code); 360 return 0; 361 } 362 363 if (!smtp_send("DATA\r\n")) 364 { 365 alog("SMTP: error writing to socket"); 366 return 0; 367 } 368 369 if (!smtp_read(buf, 1024)) 370 { 371 alog("SMTP: error reading buffer"); 372 return 0; 373 } 374 375 code = smtp_get_code(buf); 376 if (code != 354) 377 { 378 alog("SMTP: error expected code 354 got %d",code); 379 return 0; 380 } 381 382 for (std::vector<std::string>::const_iterator it = smail.smtp_headers.begin(), it_end = smail.smtp_headers.end(); it != it_end; ++it) 383 if (!smtp_send(it->c_str())) 384 { 385 alog("SMTP: error writing to socket"); 386 return 0; 387 } 388 389 if (!smtp_send("\r\n")) 390 { 391 alog("SMTP: error writing to socket"); 392 return 0; 393 } 394 395 bool skip_done = false; 396 for (std::vector<std::string>::const_iterator it = smail.smtp_body.begin(), it_end = smail.smtp_body.end(); it != it_end; ++it) 397 if (skip_done) 398 { 399 if (!smtp_send(it->c_str())) 400 { 401 alog("SMTP: error writing to socket"); 402 return 0; 403 } 404 } 405 else 406 skip_done = true; 407 408 if (!smtp_send("\r\n.\r\n")) 409 { 410 alog("SMTP: error writing to socket"); 411 return 0; 412 } 413 414 if (!smtp_read(buf, 1024)) 415 { 416 alog("SMTP: error reading buffer"); 417 return 0; 418 } 419 420 code = smtp_get_code(buf); 421 if (code != 250) 422 { 423 alog("SMTP: error expected code 250 got %d",code); 424 return 0; 425 } 426 427 return 1; 428 } 429 430 void smtp_disconnect() 431 { 432 char buf[1024]; 433 434 if (!smtp_send("QUIT\r\n")) 435 { 436 alog("SMTP: error writing to socket"); 437 } 438 439 if (!smtp_read(buf, 1024)) 440 { 441 alog("SMTP: error reading buffer"); 442 } 443 444 int code = smtp_get_code(buf); 445 if (code != 221) 446 { 447 alog("SMTP: error expected code 221 got %d",code); 448 } 449 450 ano_sockclose(smail.sock); 451 } 452 453 int main(int argc, char *argv[]) 454 { 455 /* Win32 stuff */ 456 #ifdef _WIN32 457 WSADATA wsa; 458 #endif 459 460 if (argc == 1) 461 return 0; 462 463 if (argc == 3 && !strcmp(argv[2], "--debug")) 464 smtp_debug = 1; 465 466 char *server = strtok(argv[1], ":"), *aport; 467 short port; 468 if ((aport = strtok(NULL, ""))) 469 port = atoi(aport); 470 else 471 port = 25; 472 473 if (!server) 474 { 475 alog("No Server"); 476 /* Bad, bad, bad. This was a return from main with no value! -GD */ 477 return 0; 478 } 479 else 480 alog("SMTP: server %s port %d",server,port); 481 482 /* The WSAStartup function initiates use of WS2_32.DLL by a process. */ 483 /* guessing we can skip it under *nix */ 484 #ifdef _WIN32 485 if (WSAStartup(MAKEWORD(1, 1), &wsa)) 486 return 0; 487 #endif 488 489 char buf[8192]; 490 bool headers_done = false; 491 /* Read the message and parse it */ 492 while (fgets(buf, 8192, stdin)) 493 { 494 if (smtp_is_header(buf) && !headers_done) 495 { 496 smail.smtp_headers.push_back(strip(buf) + "\r\n"); 497 std::string header, value; 498 smtp_parse_header(buf, header, value); 499 if (header == "From:") 500 { 501 alog("SMTP: from: %s", value.c_str()); 502 smail.from = value; 503 } 504 else if (header == "To:") 505 { 506 alog("SMTP: to: %s", value.c_str()); 507 smtp_set_to(value); 508 } 509 else if (smtp_is_end(buf)) 510 break; 511 else 512 { 513 smail.smtp_headers.push_back("Date: " + GetTimeStamp() + "\r\n"); 514 headers_done = true; 515 smail.smtp_body.push_back(strip(buf) + "\r\n"); 516 } 517 } 518 else 519 smail.smtp_body.push_back(strip(buf) + "\r\n"); 520 } 521 522 if (!smtp_connect(server, port)) 523 { 524 alog("SMTP: failed to connect to %s:%d", server, port); 525 return 0; 526 } 527 if (!smtp_send_email()) 528 { 529 alog("SMTP: error during sending of mail"); 530 return 0; 531 } 532 smtp_disconnect(); 533 534 return 1; 535 }