Какой самый популярный IM-клиент в России? Конечно же, аська.
А в США? Конечно, MSN. Даже те, кто сидят на маках (в смысле, компьютерах), пользуются этим мессенджером. И даже те, кто сидят на маках опийных, тоже пользуются майкрософтовской системой быстрых сообщений.
Как оно работает?
Из этого следует простой вывод — хочешь внедриться в спам-бизнес, затрагивающий цивилизованные страны (те, где у людей есть деньги) — изучай их ПО, знай их протоколы передачи данных.
Когда я впервые (это было довольно давно) решил разобраться с работой протокола Microsoft Messenger, то оказалось, что в интернете не так уж и много информации по этой теме. При этом сама Microsoft по ходу пьесы притворилась Буратино, или тем, из чего это существо сделано — я беседовал с сотрудниками компании, искал в интернете блоги программистов, работающих в MS, просил их поделиться знаниями. Все бесполезно.
На некоторое время я оставил попытки узнать про протокол, но через полгодика наткнулся на библиотеку MSNPSharp (http://code.google.com/p/msnp-sharp/). Долгое время эта библиотека лежала у меня на диске мертвым грузом, но вот недавно на работе мне пришлось писать модуль, который должен был рассылать сообщения на MSN-клиенты. Я тут же вспомнил про MSNPSharp, скачал последнюю версию и разобрался с ней в два счета.
Ползучая тварь
Точек приложения для этой библиотеки много — от легального софта до реализации различных противозаконных программулин, вроде спамеров и червяков. Ну, например, работающих вот так:
- Пытаемся разослать приглашения на общения;
- Если кто-то отзывается, то пытаемся заставить пользователя принять и запустить файл. В свое время таким же образом распространялись вирусы по почте, и вполне успешно;
- Если кто-то повелся и запустил файл, то этот файл рассылает себя дальше.
Еще один сценарий — написать программу, которая будет подбирать пароли к аккаунтам из списка по словарю. Если аккаунт взломан (а чем больше аккаунтов, тем проще найти ламера с примитивным паролем), то можно прочитать содержимое контактов и разослать файл от имени ламера. Такая рассылка отработает намного эффективнее, потому что MSN’у пока доверяют.
Асинхронная модель
Все в библиотеке крутится вокруг класса Messenger, который создается банально и без параметров:
Messenger messenger = new Messenger();
Класс работает с протоколом в асинхронном режиме. И за это разработчикам MSNPSharp однозначно нужно пожать руку. Дело в том, что если бы класс работал в синхронном режиме, то на нас свалилась бы куча проблем по асинхронизации. Некоторые команды выполняются очень долго. Например, в момент скачивания контактов класс может капитально заснуть на несколько секунд. Если выполнять операцию синхронно, то в спячку ушло бы все приложение, а так — только класс.
Просто вызывай нужную функцию, и жди, когда она сгенерит соответствующее событие. Событий у класса довольно много. Вот минимальный набор, который может пригодиться в реальной жизни:
- NameserverProcessor.ConnectionEstablished — генерируется, когда соединение удачно установлено;
- Nameserver.SignedIn — мы вошли в систему;
- Nameserver.SignedOff — удачно вышли, прямо как Винни-Пух, у которого все прекрасно входит и выходит;
- Nameserver.AuthenticationError — авторизация не прошла;
- ConversationCreated — начато общение с удаленным клиентом.
Достаточно установить обработчик на каждое событие, и можно начинать соединение с сервером. Хотя подожди. Сначала нужно указать свои данные — те, с которыми мы подключаемся к серверу. У класса мессенджера есть свойство Credentials, которое имеет одноименный тип.
У конструктора класса Credentials есть два параметра — почтовый ящик и пароль в чистом и красивом виде. Итак, параметры учетной записи, с помощью которой мы будем подключаться к серверу сообщений, прописываем следующим образом:
messenger.Credentials = new Credentials(
"youaccount@hotmail.com", "qwerty");
Если ты подцепился на все нужные нам обработчики событий и указал учетную запись, можно запускать процесс коннекта к серверу методом Connect();.
Поболтаем
Разговоры между незнакомыми абонентами категорически запрещены, и этот запрет представляет собой реальное западло для хакера. Чтобы кого-то вызвать на серьезный и жесткий разговор, нужно сначала направить приглашение, и только если твой invite приняли, можно посылать сообщения. Такой протокол должен предотвращать бесконтрольные рассылки заразы, которые происходят в классической e-почте. Правда, теоретически вполне реально написать червя, который будет распространяться по алгоритму Морриса, ведь все хорошее новое — это хорошо забытое старое. Вспомним, как работал один из самых нашумевших червей. Для взлома аккаунтов он использовал подбор паролей по словарю, который брал из *nix-системы. Так как в те времена о безопасности мало думали даже специалисты, червь удачно ломал системы.
Сейчас о безопасности думают больше, но только специалисты и продвинутые пользователи. Ламеров на просторах нашей Сети все еще очень много, поэтому можно попробовать повторить взлом. Проникая на компьютер, можно попытаться взломать MSN-аккаунты банальным перебором всех почтовых ящиков, которые есть в адресной книге. Если что-то взломалось, то подключаемся от имени лохоюзера и рассылаем себя других лохоюзерам Сети. Известный факт — когда файлы приходят от друзей, то их запускают намного чаще. Итак, чтобы отправить кому-то сообщение, мы сначала должны пригласить его в свой список друзейтоварищей.
Все, что касается адресной книги MSN, находится в классе Nameserver мессенджера. Чтобы пригласить нового друга, вызываем метод AddNewContact сервиса ContactService. Запутался? Проще один раз показать, чем десять раз рассказать:
Messenger.Nameserver.ContactService.
AddNewContact("pamela_anderson@hotmail.com");
Теперь о том, как получить список контактов. Контакты находятся в свойстве ContactList, которое является достаточно серьезным классом. Если тебе нужны все контакты, то обращайся к коллекции All (ContactList.All).
Помимо этого можно увидеть следующие типы контактов:
- Allowed — разрешенные контакты;
- BlockedList — здесь мы можем увидеть юзеров, которых заблокировали.
Сам по себе список не синхронизируется с сервером, и сразу после подключения все списки контактов будут пустыми. Дело в том, что эта операция происходит довольно медленно. Если нужно забрать с сервера свои контакты, то можно изменить свойство AutoSynchronize на true:
messenger.Nameserver.AutoSynchronize = true;
Если же скорость старта тебя особенно не волнует, то изменение свойства AutoSynchronize можно поместить в обработчике события ConnectionEstablished. Это вполне логично — сразу после установки подключения синхронизировать контакты.
Общение
Когда мы вошли в систему, то есть произошло событие SignedIn, мы можем изменять состояние и начинать общаться. Чтобы показать, что ты в сети и готов к общению, измени свое состояние на online следующим образом:
messenger.Owner.Status = PresenceStatus.Online;
Разумеется, для спама и прочих сомнительных с точки зрения законности мероприятий статус значения не имеет — сообщения можно слать и при PresenceStatus.Busy. Лично мне кажется, что с психологической точки зрения занятость даже более предпочтительна — если собеседник занят, но отправляет тебе что-то, ты не будешь приставать к нему с лишними вопросами о том, что это и зачем.
Теперь сама отправка сообщения. Этот процесс чуть более запутан. Для этого нужно создать новое общение, за которое отвечает класс Conversation:
Conversation conversation =
messenger.CreateConversation();
Теперь просто дожидаемся, когда сработает событие ConversationCreated. А вот уже когда новое общение создано, мы должны найти в списке контактов человека, которому хотим отправить послание, и пригласить его на разговор.
Допустим, что messenger-аккаунт человека, которому мы хотим отправить сообщение, находится в строковой переменной MsnAccountTo:
private void messenger_ConversationCreated(
object sender, ConversationCreatedEventArgs e)
{
if (e.Initiator != null)
{
foreach (MSNPSharp.Contact
contact in messenger.ContactList.All)
{
if (contact.Mail == MsnAccountTo)
{
e.Conversation.ContactJoined +=
new EventHandler<ContactEventArgs>(
ContactJoined);
e.Conversation.Invite(contact);
return;
}
}
messenger.Nameserver.ContactService.AddNewContact
(MsnAccountTo);
}
}
Код банален — мы просто ищем контакт в списке всех, кого мы знаем. Если чей-то почтовый ящик совпадает с тем, кого мы хотим нежно и ласково полюбить, то вызываем этого человека на разговор. Созданная беседа покоится в свойстве Conversation второго параметра события.
Мы должны подписаться на событие ContactJoined этого класса общения, чтобы узнать, когда удаленный пользователь готов пообщаться:
e.Conversation.ContactJoined +=
new EventHandler<ContactEventArgs>(ContactJoined);
А приглашение на этот разговор делается вызовом метода Invite и указанием ящика бедолаги:
e.Conversation.Invite(contact);
Тут нужно быть внимательным и аккуратным. Событие ConversationCreated может вызываться несколько раз. Я не понял, с какого перепуга это происходит, но факт есть факт — приходится гасить повторяющуюся генерацию события. Это можно сделать, например, вводя дополнительную булеву переменную, которая будет устанавливаться при создании общения и гаситься после обработки события. Если переменная не установлена, то игнорировать событие.
Но и это еще не все. Теперь в недрах библиотеки снова начинается асинхронный для нас процесс, по завершению которого вызывается событие ContactJoined. И вот в нем мы уже можем отправить пользователю сообщение. Следующий пример показывает, как отправить простое текстовое послание:
private void ContactJoined(object sender,
ContactEventArgs e)
{
TextMessage message =
new TextMessage(currentmessage);
(sender as Conversation).SendTextMessage(message);
}
В первой строке мы подготавливаем текстовое сообщение к отправке. Оно должно иметь тип класса TextMessage. Конструктору этого класса мы банально передаем строку текста.
Так как событие генерирует класс Conversation, то первый параметр как раз на него и указывает, и мы можем его использовать. Например, для вызова метода SendTextMessage с целью непосредственной отправки сообщения.
На уже созданном классе Conversation и пришедшем на разговор удаленным клиентом, можно отсылать несколько сообщений подряд. Если пользователь не в сети, Conversation все равно будет создан.
Итого
Вот и все. Еще раз повторю, что MSN реально популярен в США — лишь однажды я видел контору, в которой использовали Skype, но при ближайшем рассмотрении оказалось, что большинство ее программистов сидят в Петербурге. На диске к журналу ты найдешь небольшой класс-помощник, который упрощает работу с библиотекой и сводит отправку сообщений к вызову всего одного метода, если не считать конструктора, который вызывается автоматом при инициализации объекта. В большинстве простых ситуаций этого класса будет достаточно.