SMTP Class - C++ (Linux e Windows)

Started by Dark_Side, 05 de May , 2007, 03:55:12 AM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

Dark_Side

Hi,

Acabei de fazer uma classe que demonstra a implementação básica do protocolo SMTP (lado cliente).
Eu testei a classe no Windows e no Linux, com êxito em ambas as plataformas.

Bem, esta classe aborda os comandos básicos para enviar um email utilizando o protocolo SMTP, tais como, MAIL FROM, RCPT TO, DATA, etc; além da possibilidade de enviar emails com conteúdo HTML.

Postarei, respectivamente, o código da classe + dependências, um código de exemplo de uso e, por fim, um arquivo .ZIP contendo todos os arquivos.

smtp_class.h
#include "base64.h"
#define SMTP_PORTA 25

#ifdef WIN32
class WinSock{
     private:
                WSADATA wsa;
                int    iniciado;
     public:
                void    Iniciar();
                void    Terminar();
 }WinSock;
#endif

class SMTP{
      private:

          #ifdef WIN32
          WSADATA wsa;
          #endif

          int     sock;
          struct  sockaddr_in addr;
          struct  hostent * host;
          char    buffer [1024];
          string  cmd;
          string  nome_de,email_assunto,email_msg;
          void    envia_comando(string * dados);
          void    recebe_resposta();
          int    verifica_codigo(string codigo);
          int    conectado;
     
      public:
          void    Conectar(string servidor);
          void    Desconectar();
          void    Logar(string user,string password);
          void    De(string nome_remetente,string email_remetente);
          void    Para(string email_dest);
          void    Assunto(string assunto);
          void    Mensagem(string msg);     
          void    Enviar();

         };

/////////////////////////////////////////////////////////////////////////////////////////
#ifdef WIN32
void WinSock::Iniciar()
{
     
     if(iniciado == 1)
       throw "O Winsock ja foi inicializado!";
       
     if(WSAStartup(0x202,&wsa) == -1)
        throw "Erro ao incializar o Winsock.";         
     
     iniciado = 1;                     
}
/////////////////////////////////////////////////////////////////////////////////////////


void WinSock::Terminar()
{
     if(WSACleanup() == -1)
       throw  "Falha ao finalizar o uso do WinSock.";

       
     iniciado = 0;
}
/////////////////////////////////////////////////////////////////////////////////////////
#endif

void SMTP::Conectar(string servidor)
{
 
  if(conectado == 1)
    throw "Ja existe uma conexao estabelecida!";
     
  if((sock = socket(AF_INET,SOCK_STREAM,0)) == -1)
    throw "Erro ao criar um socket principal.";                     

  if(!(host = gethostbyname(servidor.c_str())))
     throw "Erro ao resolver o host do servidor especificado.";
     
  addr.sin_family      = AF_INET;
  addr.sin_port        = htons(SMTP_PORTA);   
  addr.sin_addr.s_addr = *(unsigned long*)host->h_addr;
  memset(&addr.sin_zero,0x0,sizeof(addr.sin_zero));
 
  if(connect(sock,(struct sockaddr*)&addr,sizeof(addr)) == -1)
    throw "Erro ao se conectar no servidor!";
 
  recebe_resposta(); 

  if(!verifica_codigo("220"))
    throw "O servidor retornou uma resposta inesperada!";

  cmd = "EHLO localhost\r\n";
  envia_comando(&cmd);
  recebe_resposta(); 
    if(!verifica_codigo("250"))
    throw "O servidor retornou uma resposta inesperada!";
 
 conectado = 1;   
}

/////////////////////////////////////////////////////////////////////////////////////////

void SMTP::Desconectar()
{
    #ifdef WIN32
      closesocket(sock);
    #else
      close(sock);
    #endif
     
    conectado = 0;
}

/////////////////////////////////////////////////////////////////////////////////////////

void SMTP::Logar(string user,string password)
{
 
  cmd = "auth login\r\n";
  envia_comando(&cmd);
  recebe_resposta();
 
  if(!verifica_codigo("334"))
    throw "O servidor retornou uma resposta inesperada!";

  cmd = base64((unsigned char*)user.c_str(),user.length());
  cmd += "\r\n";
  envia_comando(&cmd);
  recebe_resposta();
 
  if(!verifica_codigo("334"))
    throw "O servidor retornou uma resposta inesperada!";
   
  cmd = base64((unsigned char*)password.c_str(),password.length());
  cmd += "\r\n";
  envia_comando(&cmd);
  recebe_resposta();

  if(!verifica_codigo("235"))
    throw "Falha no login!";

 }
/////////////////////////////////////////////////////////////////////////////////////////

void SMTP::De(string nome_remetente,string email_remetente)
{
cmd = "MAIL FROM: <" + email_remetente + ">\r\n";
envia_comando(&cmd);
recebe_resposta();

if(!verifica_codigo("250"))
 throw "O email do remetente foi recusado!";

nome_de = nome_remetente;

}
/////////////////////////////////////////////////////////////////////////////////////////

void SMTP::Para(string email_dest)
{
cmd = "RCPT TO: <" + email_dest + ">\r\n";
envia_comando(&cmd);
recebe_resposta();

if(!verifica_codigo("250"))
 throw "O email do destinatario foi recusado!";

}
/////////////////////////////////////////////////////////////////////////////////////////

void SMTP::Assunto(string assunto)
{
email_assunto = assunto;     
}
/////////////////////////////////////////////////////////////////////////////////////////

void SMTP::Mensagem(string msg)
{
     email_msg = msg;
}
/////////////////////////////////////////////////////////////////////////////////////////

void SMTP::Enviar()
{
     cmd = "DATA\r\n";
     envia_comando(&cmd);
     recebe_resposta();
     if(!verifica_codigo("354"))
      throw "O servidor retornou uma resposta inesperada!";

     cmd = "From: " + nome_de + "\r\n";
     envia_comando(&cmd);
     
     
     if(email_assunto.size() > 0){
       cmd = "Subject: " + email_assunto + "\r\n";
       envia_comando(&cmd);
       email_assunto = "";
       }
   
    cmd = "MIME-Version: 1.0\r\n";
    envia_comando(&cmd);
   
    cmd = "Content-Type: text/html\r\n";
    envia_comando(&cmd);
   
    if(email_msg.size() > 0){
       cmd = "\r\n" + email_msg + "\r\n";
       envia_comando(&cmd);
       email_msg = "";}
   
   
     cmd = ".\r\n";
     envia_comando(&cmd);
     recebe_resposta();

     if(!verifica_codigo("250"))
       throw "Ocorreu um erro ao enviar o email!";
     
     cmd = "QUIT\r\n";
     envia_comando(&cmd);

}
/////////////////////////////////////////////////////////////////////////////////////////

void SMTP::envia_comando(string * dados)
{
    if(send(sock,dados->c_str(),dados->length(),0) == -1)
      throw "Erro ao enviar comandos ao servidor!";
}
/////////////////////////////////////////////////////////////////////////////////////////

void SMTP::recebe_resposta()
{
  memset(buffer,0x0,sizeof(buffer));
  if(recv(sock,buffer,sizeof(buffer),0) == -1) 
    throw "Erro ao receber resposta do servidor!";
 
}
/////////////////////////////////////////////////////////////////////////////////////////

int SMTP::verifica_codigo(string codigo)
{
     return (int)(string(buffer,0,3) == codigo);
}
/////////////////////////////////////////////////////////////////////////////////////////

base64.h


// Algorítimo:     http://www.adp-gmbh.ch/cpp/common/base64.html xD //



static const std::string base64_chars =
             "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
             "abcdefghijklmnopqrstuvwxyz"
             "0123456789+/";


string base64(unsigned char const* bytes_to_encode, unsigned int in_len) {
 
  string ret;
  int i = 0;
  int j = 0;
  unsigned char char_array_3[3];
  unsigned char char_array_4[4];

  while (in_len--) {
    char_array_3[i++] = *(bytes_to_encode++);
    if (i == 3) {
      char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
      char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
      char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
      char_array_4[3] = char_array_3[2] & 0x3f;

      for(i = 0; (i <4) ; i++)
        ret += base64_chars[char_array_4[i]];
      i = 0;
    }
  }

  if (i)
  {
    for(j = i; j < 3; j++)
      char_array_3[j] = '\0';

    char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
    char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
    char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
    char_array_4[3] = char_array_3[2] & 0x3f;

    for (j = 0; (j < i + 1); j++)
      ret += base64_chars[char_array_4[j]];

    while((i++ < 3))
      ret += '=';

  }

  return ret;

}

exemplo.cpp
#include <iostream>

#ifdef WIN32
  #include <winsock.h>
#else
  #include <sys/types.h>
  #include <sys/socket.h>
  #include <netdb.h>

#endif

using namespace std;
#include "smtp_class.h"


int main()
{
SMTP email;

try{

#ifdef WIN32
WinSock.Iniciar();
#endif

email.Conectar("smtp.servidor.lol"); // Servidor SMTP   
email.Logar("LOL","LOL123"); // Usuário e senha
email.De("lol","lol@lol.com.br"); // Remetente
email.Para("amigo_do_lol@lol2.com.br"); // Destinatario
email.Assunto("HI"); // Assunto
email.Mensagem("<font color=red><b>xD</b></font>"); // Mensagem
email.Enviar(); // Enviar-a
email.Desconectar(); // Desconecta-se

#ifdef WIN32
WinSock.Terminar();
#endif

cout << "Email Enviado xD\n";
}

catch(const char * msg){ // Possíveis exceções
            printf("%s\n",msg);
            return 1;
                       }
           
catch(...){ // Exceções não esperadas
           cout << "Ocorreu um erro inesperado no programa.";
           return 1;
          }

    return 0;
}

Obs:

1) Para compilar o código no Windows, deve-se adicionar "-lwsock32" no linker do compilador;

Quoteex: g++ exemplo.cpp -o exemplo.exe -lwsock32

2) No Linux, não é necessário incluir parâmetros no linker.
Quoteex: g++ exemplo.cpp -o exemplo

Source + exemplo:

http://three.fsphost.com/darkside/smtp_exemplo.zip

Bye xD

OnlyOne

cara , vc e um dos caras q mais sabem de C q eu conheço , mas particularmente eu nao uso mais c/c++ pros meus projetos por causa da mao de obra pra se fazer certas coisas , ta certo q em C vc faz tudo , aprende a "raiz" das coisas , mas eu acho q tem horas q vc precisa fazer as coisas mais rapido , e um soft simples pode tomar muito do seu tempo

see you 
No céu toca Joy Division


HadeS