Categorias
MSN Messenger em delphi
como trabalhar com o protocolo do MSN Messenger em delphi
Este artigo encontra-se fortemente desatualizado e será atualizado em breve
Esta é uma implementação do protocolo do MSN Messenger em delphi. Não está completo e para construí-lo, você precisará do pacote WSocket. A maior parte do que é apresentado aqui é uma parte da especificação (ainda não o suficiente para sequer fazer um clone do MSN Messenger). O trabalho que você vê aqui ainda tem aprimorações a serem feitas (muito devido ao fato de que eu sou novo em programação Socket), este artigo é baseado em obras de venkydude, artigos de MSN e numa versão antiga do KMerlin (um clone do MSN Messenger para opensource linux). Este é o segundo artigo que escrevi sobre Instant Messaging (O primeiro foi um protocolo sobre o yahoo, algo que eu não tenha conseguido concluir devido às limitações de tempo (muito trabalho)) estou planejando atualizar este artigo assim que possível.
CÓDIGO <--------------------------------- --------------- ------------------------->
(A FAZER GLOBAL: IMPLEMENTAR AFAZERES LOCAIS, limpar, estender)
unidade MSNMessenger;
interface
utiliza
WSocket, MD5, Classes, SysUtils;
tipo
TUserState = (
usOnline, / / você está online
usBusy, / / ocupado
usBRB, / / Já volta
usAway, / / Ausente
usOnPhone, / / No Telefone
usLunch, / / Almoço
usHidden, / / Invisível
usOffline / / offline
);
TMSNMessenger = class (TComponent)
privado
FConnected: Boolean;
FUserName: String;
FPassword: String;
FFriendlyUserName: String;
Flog: TStrings;
FFriendlyNameChange: TNotifyEvent;
FState: TUserState;
função GetHost: String;
procedimento SetHost (const Valor: String);
função GetPort: String;
procedimento SetPort (const Valor: String);
procedimento SetUserName (const Valor: String);
procedimento SetPassword (const Valor: String);
função GetFriendlyUserName: String;
procedimento SetFriendlyUserName (const Valor: String);
procedimento SetState (const Valor: TUserState);
protegido
FSocket: TWSocket;
FTrialID: Integer;
procedimento SendVER;
procedimento ReceiveSYN;
procedimento SocketWrite (const AString: String);
procedimento LogWrite (const Dados: Seqüência);
procedimento ProcessCommand (const ACommand: String);
procedimento SocketDisconnect (Sender: TObject; Erro: Word);
procedimento SocketDataAvailable (Sender: TObject; Erro: Word);
procedimento SocketConnect (Sender: TObject; Erro: Word);
procedimento TriggerFriendlyNameChange; dinâmico;
público
construtor Criar (AOwner: TComponent); substituir;
destruidor Destroy; substituir;
procedimento Login;
procedimento Logoff;
publicado
propriedade Host: String lê GetHost escreve SetHost;
propriedade Porta: String lê GetPort escreve SetPort;
propriedade Nome_de_Usuário: String lê FUserName escreve SetUserName;
propriedade Senha: String lê FPassword escreve SetPassword;
propriedade FriendlyUserName: String lê GetFriendlyUserName escreve SetFriendlyUserName;
propriedade Conectado: Boolean lê FConnected;
propriedade Log: TStrings lê flog escreve flog;
propriedade FriendlyNameChange: TNotifyEvent lê FFriendlyNameChange escreve FFriendlyNameChange;
propriedade Status: TUserState lê FState escreve SetState;
fiml;
implementação
usa windows;
constRealState: arrumar [TUserState] do String =
( "CHG NLN% d ',' CHG BSY% d ',' CHG BRB% d ',' CHG AWY% d ',' CHG NPH% d ',' CHG LUN% d ',
«CHG HDN% d ',' CHG FLN% d ');
tipo
CharSet = Conjunto de char;
função UTF8ToAnsi (x: string): ansistring;
(Função que recebe sequencia UTF8 e converte a ansi string)
var
i: integer;
B1, B2: byte;
começo
Resultado: = x;
i: = 1;
enquanto i <= Comprimento (Resultado) começa
se (ord (Resultado [i]) e US $ 80) <> 0, então, começar
b1: = ord (Resultado [i]);
b2: = ord (Resultado [i + 1]);
if (b1 e US $ F0) <> $ C0 então
Resultado [i]: = # 128
se não, começar
Resultado [i]: = Chr ((b1 SHL 6) ou (b2 e US $ 3F));
Apagar (Resultado, i + 1,1);
fim;
fim;
inc (i);
fim;
fim;
função AnsiToUtf8 (x: ansistring): string;
(Função que recebe ansi string e converte para a sequência UTF8)
var
i: integer;
B1, B2: byte;
começo
Resultado: = x;
para i: = Comprimento (Resultado) para 1
se Resultado [i]> = # 127, então, começar
b1: = $ C0 ou (ord (Resultado [i]) shr 6);
b2: = $ 80 ou (ord (Resultado [i]) e US $ 3F);
Resultado [i]: = chr (b1);
Inserir (chr (b2), Resultado, i + 1);
fim;
fim;
FunçãoExtractWord (N: Integer; S: String; WordDelims: CharSet): String;
Var
I, J: Word;
Count: Integer;
SLen: Integer;
começo
Contador: = 0;
I: = 1;
Resultado: ='';
SLen: = Length (S);
Enquanto I <= sLen Começar
(preskoc oddelovace)
Enquanto (I <= sLen) E (S [I] Em WordDelims) São Inc (I);
(neni-li na konci retezce, bude nalezen zacatek slova)
Se I <= sLen Então, Inc (Count);
J: = I;
(um je zde konec slova)
Enquanto (J <= sLen) e não (S [J] Em WordDelims) são Inc (J);
(je-li-te toto n slovo, vloz ho nd vystup)
Se Count = N, então, começe
Resultado: = Copy (S, I, JI);
Sair
Fim;
I: = J;
Fim; (enquanto)
Fim;
função WordAt (const Texto: string; Posição: Integer): string;
começo
Resultado: = ExtractWord (Posição, Texto, [ '']);
Fim;
TMSNMessenger ()
construtorTMSNMessenger.Criar (AOwner: TComponent);
começar
herdada Criar (AOwner);
FSocket: = TWSocket.Create (Self);
FSocket.Addr: = 'messenger.hotmail.com';
FSocket.Port: ='1863 ';
FSocket.Proto: = 'tcp';
FSocket.OnSessionConnected: = SocketConnect;
FSocket.OnSessionClosed: = SocketDisconnect;
FSocket.OnDataAvailable: = SocketDataAvailable;
FConnected: = False;
Fim;
destruidor TMSNMessenger.Destroy;
começo
FSocket.Free;
FSocket: = nil;
herdou Destroy;
Fim;
função TMSNMessenger.GetFriendlyUserName: String;
começo
se não FConnected então
Resultado: = FFriendlyUserName;
Fim;
função TMSNMessenger.GetHost: String;
começo
Resultado: = FSocket.Addr;
Fim;
função TMSNMessenger.GetPort: String;
começo
Resultado: = FSocket.Port;
Fim;
procedimento TMSNMessenger.Login;
começo
FSocket.Connect;
Fim;
procedimento TMSNMessenger.Logoff;
começo
Fim;
procedimento TMSNMessenger.LogWrite (const Dados: Seqüência);
começo
se Assigned (flog) então
FLog.Add (Dados);
Fim;
(Processcommand aqui é semelhante a um processo windowproc, aqui processamos todo o tipo de informações enviadas do servidor a partir de agora, é IFFull (repleto de se's) talvez se eu tiver algum tempo livre vai transformar isso em um caso)
A FAZER: Limpar este procedimento bagunçado
A FAZER: Adicionar mais comandos
TMSNMessenger.ProcessCommand procedimento;
var
Tmp: String;
Hash: String;
começo
Tmp: = WordAt (ACommand, 1);
se Tmp = 'VER', então,
SocketWrite ( 'INF% d');
se Tmp = 'INF', então,
SocketWrite ( 'USR% d MD5 Eu + FUserName);
se Tmp = 'USR', então,
começo
se WordAt (ACommand, 4) ='S', então,
começo
Hash: = WordAt (ACommand, 5);
Apagar (Hash, pos (# 13 # 10, hash), Comprimento (hash));
Hash: = StrMD5 (hash + senha);
SocketWrite ( 'USR% d MD5 S' + Minúsculas (hash));
outro fim
começo
FFriendlyUserName: = WordAt (ACommand, 5);
SocketWrite («SYN 1% d ');
ReceiveSYN;
fim;
fim;
(Quando você receber um XFR e você não estiver conectado ao servidor msn, significa redirecionar para um outro servidor)
se (TMP = 'XFR') e não Conectado então
começo
TMP: = WordAt (ACommand, 4);
FSocket.Fechar;
Apagar (Tmp, pos ( ':', Tmp), Comprimento (Tmp));
FSocket.Addr: = Tmp;
TMP: = WordAt (ACommand, 4);
Apagar (Tmp, 1, pos ( ':', Tmp));
FSocket.Port: = Tmp;
FSocket.Connect;
Sair;
fim;
Rename Friendly name
se (TMP = "REA") então
começo
FFriendlyUserName: = WordAt (ACommand,5);
FFriendlyUserName: = StringReplace (FFriendlyUserName, «20%», « ', [rfReplaceall]);
TriggerFriendlyNameChange;
fim;
(O comando out é recebido antes do servidor nos desconecta, se é porque nós nos loggamos em outra máquina, vamos receber a mensagem OUT OTH (OUTRA MÁQUINA)
A Fazer: escrever algum evento ou algo para recuperar essa notificação)
se (TMP = "OUT") então
começo
se pos ( "OTH", ACommand)> 1, então
LogWrite ( 'entrou em um outro computador desligando');
fim;
fim;
(SYN é sem dúvida um dos Comandos MSN Messenger mais informativos que o SYN nos informa sobre:
e-mail disponível
Lista de Amigos
Lista de Bloqueados
Lista invertida (as pessoas que têm você em suas listas)
Os números de telefone (Principal, móveis, etc)
configurações MSN Messenger
etc
No entanto, isto tem um preço, já que há tanta informação WSocket que pode não obter todas as informações corretamente (uma qualidade do não bloqueio de sockets) assim, para obtê-lo, vamos congelar esta thread durante 5 segundos (significando que seus formulários não irão receber qualquer mensagem e parecem não responder por um tempo), Eu sei que deve ter um jeito melhor, se alguém souber, me mande um email.
A FAZER: Analisar o conteúdo recebido
A FAZER: procurar um caminho que não te obriga a congelar a thread
)
procedimento TMSNMessenger.ReceiveSYN;
var
Tmp: String;
começo
FSocket.OnDataAvailable: = nil;
Sleep (5000);
Tmp: = FSocket.ReceiveStr;
FSocket.OnDataAvailable: = SocketDataAvailable;
Tmp: = UTF8ToAnsi (Tmp);
LogWrite ( 'RECV:' + Tmp);
SocketWrite ( 'CHG NLN% d');
fim;
procedimento TMSNMessenger.SendVER;
começo
SocketWrite ( 'VER% d CVR0 MSNP5 MSNP6 MSNP7')
fim;
procedimento TMSNMessenger.SetFriendlyUserName (const Valor: String);
var
tmp: String;
começo
se FConnected e (FUserName <> Value), então,
começo
tmp: = StringReplace (Valor, '', '% 20', [rfReplaceAll]);
tmp: = AnsiToUtf8 (Tmp);
SocketWrite ( "REA% d '+ + FUsername'' + Tmp);
fim;
fim;
procedimento TMSNMessenger.SetHost (const Valor: String);
começo
se não Conectado, então,
se FSocket.Addr <> Value, então,
FSocket.Addr: = Valor;
fim;
procedimento TMSNMessenger.SetPassWord (const Valor: String);
começo
se não Conectado, então,
se (FPassword <> Value), então,
FPassword: = Valor;
fim;
procedimento TMSNMessenger.SetPort (const Valor: String);
começo
se não Conectado, então,
se FSocket.Port <> Value, então,
FSocket.Port: = Valor;
fim;
procedimento TMSNMessenger.SetState (const Valor: TUserState);
começo
se FConnected então
se (FState <> Value), então
SocketWrite (RealState [Valor]);
fim;
procedimento TMSNMessenger.SetUserName (const Valor: String);
começo
se não FConnected então
se FUsername <> Value, então,
FUserName: = Valor;
fim;
procedimento TMSNMessenger.SocketConnect (Sender: TObject; Erro: Word);
começo
FTrialID: = 1;
SendVER;
fim;
procedimentoTMSNMessenger.SocketDataAvailable (Sender: TObject; Erro: Word);
var
Tmp: String;
começo
Tmp: = FSocket.ReceiveStr;
Tmp: = UTF8ToAnsi (Tmp);
LogWrite ( 'RECV:' + Tmp);
ProcessCommand (Tmp);
fim;
procedimento TMSNMessenger.SocketDisconnect (Sender: TObject; Erro: Word);
começo
FConnected: = False;
LogWrite ( 'Desligado');
fim;
procedimento TMSNMessenger.SocketWrite (const AString: String);
começo
FSocket.SendStr (Format (AString, [FTrialID]) + # 13 + # 10);
LogWrite ( «ENVIO: '+ Format (AString, [FTrialID]));
Inc (FTrialID);
fim;
procedimento TMSNMessenger.TriggerFriendlyNameChange;
começo
se Assigned (FFriendlyNameChange), então,
FFriendlyNameChange (Self);
fim;
fim.
CÓDIGO <---------------------------------/ -------------- -------------------------> Uma amostra seria: amsn: = TMSNMessenger.Create (Self); / / amsn é uma variável do tipo TMSNMessenger AMSN.UserName: =''; / / Isto indica que o nome de usuário deve ser de forma * @ hotmail.com AMSN.PassWord: ='';// Isso indica a senha AMSN.Log: = MEmo1.Lines; / / Log indica um destino para despejar as informações recebidas e enviadas, vou usá-lo para obter protocolo de informações e tal, mas não é obrigatória a sua utilização por AMSN.Login; / / procedimento que indica que devemos iniciar o processo de entrada.