Alguns dias atrás, o usuário Khyser postou um tópico em que fazia algumas perguntas relacionadas a representação de dados de usuários num SGBD, e um dos itens sobre os quais perguntava era a respeito da validação de endereços de e-mail.
EDIT (12:14): Eis o texto original dele, de acordo com o cache do Google, referido abaixo.
Enviado em 17/03/2018 - 15:57h
Criei um sistema de login em C usando como banco de dados o MySQL e gostaria saber se ele ficou bom, ruim ou se precisa ser melhorado em certos pontos. Aqui vai o link do projeto: https://github.com/Khyser/Login-System/tree/master/Project
Alguns problemas com esse sistema que não sei como resolver:
1 - Um amigo meu disse que meu código estava vulnerável e poderia ser alvo de SQL Injection, mas eu não entendo muito disso então deixei sem nenhuma proteção para esse problema, então caso vocês queiram fazer aguma sugestões saibam que ela será bem vinda :D
2 - Não consegui "bolar" uma função que validasse o e-mail digitado, ou seja, se era Gmail, Yahoo, Hotmail ou ProtonMail (na verdade tinha criado uma, porém era uma verdadeira gambiarra cheia de loops que não funcionava corretamente).
3 - Se minha senha for Lixo_toxico123 e eu digitar lixo_toxico123 eu faço login normalmente.
Com relação a essa pergunta específica (2), eu dei uma resposta inicial, indicando uma referência que ele poderia tomar como ponto de partida para montar uma solução, e, depois de enviar tal resposta, comecei a escrever outra mensagem dando um exemplo de implementação. Infelizmente, porém, a máquina onde eu comecei a escrever tal resposta (um Raspberry Pi 3 que eu estava testando) travou por superaquecimento, e eu perdi o que já tinha começado a escrever. Depois disso, eu comecei a escrever a resposta de novo no trabalho, mas, no meio das atividades do dia-a-dia, não consegui terminá-la até a manhã de hoje. Quando finalmente a concluí e tentei enviá-la, recebi um erro, e depois vi que o tópico tinha sido apagado e que ele removera a conta no VoL.
Refinando um pouco minha resposta anterior, perece-me que você está menos interessado num formato de como um endereço de e-mail pode aparecer no corpo de uma mensagem, e mais na forma como ele é efetivamente associado a um usuário dentro de um determinado domínio.
Olhando a gramática da RFC 5322, isso implicaria restringir:
• address ao caso em que ele é uma mailbox (descartando o caso em que ela poderia indicar um grupo, em vez de uma única mailbox);
• mailbox ao caso em que ela é uma addr-spec (descartando o caso em que você tem um texto (quase) livre de identificação do endereço);
• local-part ao caso em que ela é composta apenas por dot-atom (descartando os casos de quoted-string e de sintaxes obsoletas);
• domain ao caso em que ele é composto apenas por dot-atom (descantando os casos de domain-literal e de sintaxes obsoletas);
• dot-atom ao caso em que não ocorram os opcionais CFWSs, o que na prática o torna idêntico a dot-atom-text.
Uma forma de implementar essa gramática é por meio de expressões regulares, com os elementos mais genéricos se fazendo pela composição dos elementos mais básicos.
Abaixo segue um esqueleto (não-testado) de implementação da parte que nos interessa da gramática, por meio de composição de strings (usando o preprocessador) e de uma descrição bottom-up dos elementos que formam a gramática.
#define ATEXT ALPHA DIGIT "!#$%&'*+/=?^_`{|}~-" // Note que o hífen está no final da lista.
#define DOT_ATOM_TEXT "[" ATEXT "]+(?:\\.[" ATEXT "]+)?" // "(?: ... )" agrupa pedaços sem criar back-references.
// "[...]" é uma forma sucinta de informar conjunto de caracteres válidos.
#define DOT_ATOM "(?:" DOT_ATOM_TEXT ")"
#define ADDR_SPEC "(" LOCAL_PART ")@(" DOMAIN_ ")" // Aqui eu uso agrupamento com back-references, para poder ter a opção
// de testar os valores de nome e domínio (validade, não apenas sintaxe).
#define MAILBOX "^(?:" ADDR_SPEC ")$"
#define ADDRESS "(?:" MAILBOX ")"
#include <regex>
#include <string>
#ifdef VALIDATE_DOMAIN
#include <dns.h> // Parte do pacote libowfat-dev.
#include <stralloc.h> // Parte do pacote libowfat-dev.
#endif
// Tipo que indica o motivo de um endereço ser eventualmente inválido.
enum class invalid_addr_reason { is_valid, format, local_part, domain };
// Função que testa se um endereço de e-mail é válido ou não. Se for, a
// função retorna true; caso contrário, retorna false e, se o segundo parâmetro
// tiver sido usado com um argumento não-nulo, coloca na região apontada por
// ele um valor indicando a causa do erro.
bool is_valid_address(const string &addr, invalid_addr_reason *p_reason=nullptr){
smatch parts;
// A sintaxe está OK. Agora examina se o domínio existe e se o nome do
// usuário remoto é suspeito.
#ifdef VALIDATE_DOMAIN
// Para o domínio ser válido, deve haver no DNS um registro do tipo MX ou
// um registro do tipo A (IPv4) ou AAAA (IPv6).
static stralloc domain={0}, response={0};
#ifdef VALIDATE_LOCAL_PART
// Exemplo de tratamento: rejected_username_res é um conjunto de expressões
// regulares contendo nomes que não devam ser aceitos como parte do endere-
// ço. Esse conjunto poderia conter expressões regulares para impedir o
// uso de nomes reservados, tais como "postmaster", "mailerdaemon", "root",
// etc., palavrões (e.g. ".*\b(?:lista|de|palavrões|aqui)\b.*") e outros
// nomes embaraçosos. Alternativamente, algum outro critério ou algoritmo
// poderia ser usado.
const string local_part=parts[1];
for(const auto &invalid_name_re: rejected_username_res)
if(regex_match(local_part, invalid_name_re)){
if(p_reason)
*p_reason=invalid_addr_reason::local_part;
return false;
}
#endif
// Se chegou a este ponto, não achou falha no endereço. Assim sendo,
// consideramo-lo válido.
if(p_reason)
*p_reason=invalid_addr_reason::is_valid;
return true;
}