ICQ-клиент
Введение
Сегодня Интернет играет очень важную роль в жизни многих людей. Его
используют для работы, развлечения, поиска информации, а также знакомства и
общения.
В первые годы существования Всемирной паутины пользователи общались
преимущественно через электронную почту, но отправлять письма было неудобно и
даже нерационально, если оба собеседника одновременно находились в Интернете.
Все изменилось, когда появились первые программы мгновенного общения или, менее
официально, мессенджеры или интернет-пейджеры.
Эти программы используются для передачи сообщений через Интернет в
реальном времени через специальные службы. Если раньше речь шла только о
текстовых сообщениях, то теперь с помощью подобных программ можно отсылать и
голосовые, и видеосообщения, и файлы, и фотографии.
Как правило, мессенджер скачивается на персональный компьютер и с этого
момента начинает составлять достойную конкуренцию электронной почте и даже
телефону. Каждый пользователь получает собственный идентификационный номер (или
логин), который становится его уникальным именем в Сети.
Программа мгновенного обмена сообщениями (англ. Instant messenger, IM) -
программа для обмена сообщениями через интернет в реальном времени через службы
мгновенных сообщений (Instant Messaging Service, IMS). С помощью нее могут
передаваться текстовые сообщения, звуковые сигналы, изображения, видео, а также
производиться такие действия, как совместное рисование или игры. Многие из
таких программ могут применяться для организации групповых текстовых чатов или
видеоконференций.
Для этого вида коммуникации необходима клиентская программа, так
называемый мессенджер (от англ. messenger - курьер). Отличие от электронной
почты здесь в том, что обмен сообщениями идёт в реальном времени (англ. instant
- мгновенно). Большинство IM-клиентов позволяет видеть, подключены ли в данный
момент абоненты, занесённые в список контактов. В ранних версиях программ всё,
что печатал пользователь, тут же передавалось. Если он делал ошибку и исправлял
её, это тоже было видно. В таком режиме общение напоминало телефонный разговор.
В современных программах сообщения появляются на мониторе собеседника уже после
окончания редактирования и отправки сообщения.
Как правило, мессенджеры не работают самостоятельно, а подключаются к
центральному компьютеру сети обмена сообщениями, называемому сервером. Поэтому
мессенджеры и называют клиентами (клиентскими программами). Термин является
понятием из клиент-серверных технологий.
Широкому кругу пользователей известно некоторое количество популярных
сетей обмена сообщениями, таких, как XMPP, ICQ, MSN, Yahoo!. Каждая из этих
сетей разработана отдельной группой разработчиков, имеет отдельный сервер и
протоколы, отличается своими правилами и особенностями. Между различными сетями
обычно нет никакой взаимосвязи. Таким образом, пользователь сети ICQ не может
связаться с пользователем сети MSN. Однако ничто не мешает быть одновременно
пользователем нескольких сетей.
Почти для каждой из сетей есть свой мессенджер, разработанный той же
командой разработчиков. Так, для пользования вышеуказанными сетями
разработчиками предлагаются программы с одноимёнными названиями: ICQ, MSN
Messenger, Yahoo! Messenger. Таким образом, если один из адресатов пользуется
только сетью ICQ, а другой - только сетью MSN, то можно общаться с ними одновременно,
установив на своем компьютере и ICQ, и MSN Messenger, и зарегистрировавшись в
обеих сетях.
В качестве альтернативного мессенджера можно выбрать программу стороннего
производителя: как коммерческую, так и бесплатную.
Популярными альтернативными программами для общения в сети ICQ являются
QIP, Trillian, Miranda IM, Pidgin. Также они позволяют подключаться
одновременно к нескольким сетям, что избавляет от необходимости устанавливать
отдельный мессенджер для каждой сети и позволяет общаться со всеми адресатами
единым образом независимо от сети.
Большинство IM-сетей используют закрытые протоколы, поэтому
альтернативные клиенты теоретически могут обладать меньшим количеством базовых
функций, чем официальные, хотя на практике чаще бывает наоборот. Однако при
изменениях протокола на стороне сервера сети альтернативные клиенты могут
внезапно перестать работать (например, подобное явление наблюдалось для
«нефирменных» клиентов популярного в России сервиса ICQ).
В качестве альтернативы проприетарным протоколам для IM был разработан
открытый протокол XMPP (также известный, как Jabber), используемый в таких
сервисах, как Google Talk, Я.Онлайн и др. Этот протокол часто используется для
организации общения в корпоративных и других локальных сетях.
1. АНАЛИТИЧЕСКИЙ ОБЗОР ЛИТЕРАТУРЫ
1.1 Анализ предметной области
ICQ
основан на протоколе OSCAR,
который является протоколом прикладного уровня.
Прикладной уровень - седьмой уровень модели OSI, предназначенный для
обеспечения взаимодействия пользователя (процесса-отправителя) с приложениями
на удаленном компьютере (процессом-получателем).
Прикладной уровень обеспечивает:
описание форм и методов взаимодействия прикладных процессов;
управление заданиями, передачу файлов, управление системой и т.д.;
идентификацию пользователей по их паролям, адресам и электронным
подписям;
определение функционирующих абонентов;
объявление о возможности доступа к новым прикладным процессам;
определение достаточности имеющихся ресурсов;
посылку запросов на соединение с другими прикладными процессами;
управление данными, которыми обмениваются прикладные процессы;
синхронизацию взаимодействия прикладных процессов и др.
Обычно прикладной уровень подразделяется:
на верхний подуровень, включающий сетевые службы; и
на нижний подуровень, содержащий стандартные сервисные элементы,
поддерживающие работу сетевых служб.
1.2 Сравнительная характеристика существующих
аналогов
На данном этапе существует достаточно большое количество мессенджеров,
большинство из которых бесплатны (некоторые в целом бесплатны, но имеют набор
дополнительных платных услуг). Ниже приведено описание самых популярных,
используемых миллионами пользователей по всему миру. - наиболее
распространенный в мире мессенджер с закрытым протоколом. Предоставляет
видеосвязь и видеоконференции до 10 человек. Позволяет звонить на обычные
телефоны, принимать звонки. Есть версии для мобильных устройств.
MyChat
- клиент-серверный мессенджер для локальной сети. Интегрируется с Active Directory, многоуровневый список контактов,
доска объявлений, система массовых оповещений, конференции, SSL шифрование трафика, передача оффлайн
сообщений и файлов, гибкая система управления правами пользователей, передача
файлов и папок.
Google Talk используется
как десктоп-клиент и как вэб-дополнение к GMail для текстового чата, аудио и видеосвязи. Совместим с Jabber, AIM и ICQ, а
также используется как гаджет для общения с посетителями на сайте.
QIP Infium - быстрый и
легкий клиент ICQ, Mail.ru
Агент, Я.online, GTalk, SIPNET,
LJ Talk с поддержкой различных скинов и плагинов.
Поддеррживает видеосвязь и VoIP
связь. Можно звонить на обычные и мобильные телефоны по низким тарифам.
Fring
- мобильный мультипротокольный клиент для VoIP и видеосвязи, поддерживает Skype, ICQ, Google Talk, MSN Messenger, SIP, twitter, Yahoo!
и AIM. Доступен на iPhone, Android, Blackberry, Symbian. Включает сервис IP-телефонии FringOut
AIM Pro - бизнес
мессенджер, обеспечивающий высокое качество связи, высокий уровень безопасности
и предоставляющий инструменты для совместной работы над приложениями.
Интегрируется с Outlook и
сервисами Webex для вэб-конференций. Основан на
открытой платформе Open AIM и Open Voice. Позволяет звонить на обычные телефоны.
ICQ -
очень распространенный на отечественном интернет-пространстве мессенджер с
возможностью аудио и видео связи. Работает только под windows. Имеет большое количество плагинов.
Miranda IM -
десктоп-клиент, совмещающий в себе несколько ваших аккаунтов в различных
системах сообщений (ICQ, AIM, Jabber, MSN
...). Поддержка других мессенджеров обычно реализуется с помощью плагинов.
Zoho Chat - групповой
вэб-чат для бизнеса + мультипротокольный web IM + мультиоператорский чат поддержки. Поддерживает Yahoo, AIM, MSN, ICQ, GTalk и Jabber.
Умеет пересылать файлы, доставлять оффлайн сообщения, просматривать историю
чатов (и искать в ней), чатиться с незарегистрированными в Zoho людьми через браузер, извещать о
новых событиях в Zoho-офисе.
Позволяет встраивать чат в сайт в виде виджета.
Trillian - мультипротокольный IM-клиент,
поддерживает AIM, ICQ, Windows Live Messenger, Yahoo Messenger и IRC. Функции включают групповой чат, аудиочат, SMS, напоминания, шифрование сообщений,
поддержка прокси.
1.3 Постановка задачи
Требуется реализовать ICQ-клиент.
Выбранный язык программирования - C#, платформа .NET. Пользовательский
интерфейс создать при помощи технологии Windows Presentation Foundation.
После успешной авторизации клиент ICQ загружает с сервера список контактов пользователя.
Контакты в списке могут быть разделены на группы, имена и количество которых
изменяются пользователем.
При добавлении контакта может потребоваться авторизация - разрешение
видеть его статус присутствия и отправлять ему файлы. Для таких контактов
формируется запрос на авторизацию, который доходит до пользователя добавляемой
учётной записи в виде системного уведомления, на которое он может ответить либо
согласием, либо отказом, по желанию снабдив решение текстовым комментарием.
С каждым из контактов можно вести личную переписку. Если отправитель не
отключил эту возможность, то, в зависимости от клиента, получатель
информируется о наборе сообщения, что создаёт эффект присутствия отправителя.
Длина отправляемого сообщения ограничена.
В случае, если в момент отправки сообщений адресат не находился в сети,
они будут сохранены службой и доставлены адресату, как только тот подключится к
сети.
мгновенный
обмен сообщение интернет
2. МОДЕЛИ, ПОЛОЖЕННЫЕ В ОСНОВУ ПС
2.1 Функциональные модели
Программное средство разрабатывалось в соответствии со следующими
функциональными моделями представленными на рисунках 2.1, 2.2 и 2.3
Рисунок 2.1 Общение с помощью ICQ(IDEF0)
Рисунок 2.2 Посылка сообщения на сервер(IDEF3)
Рисунок 2.3 Посылка сообщения собеседнику(DFD)
.2
Разработка спецификаций требований к
программному средству
Разрабатываемое программное средство может быть использовано в
соответствии со следующей диаграммой, представленной на рис. 2.1
К нему предъявляется следующая спецификация требований:
) Программное средство должно быть разработано на платформе .NET с
помощью языка программирования C# (Sharp);
) Удобный и понятный интерфейс;
) Минимизация используемых объемов памяти;
) Реализовать хранение истории сообщений;
)Реализовать возможность отправки и получения файлов;
) Размер программного средства не должен превышать 1024 Кб.
Рисунок 2.3 Диаграмма вариантов использования UML
3. Разработка ПРОГРАММНОГО СРЕДСТВА
.1 Обоснование выбора среды разработки
В качестве среды разработки была выбрана IDE Visual Studio 2010 и платформа .NET Framework, сочетающие в себе удобство
переносимости и мощь объектно - ориентированного программирования.
Для разработки пользовательского интерфейса была использована платформа
Windows Presentation Foundation (WPF). Presentation Foundation (WPF, кодовое название - Avalon) - система для построения клиентских приложений Windows с визуально привлекательными
возможностями взаимодействия с пользователем, графическая (презентационная)
подсистема в составе .NET Framework (начиная
с версии 3.0), имеющая прямое отношение к XAML.
В основе WPF лежит
векторная система визуализации, не зависящая от разрешения и созданная с
расчетом на возможности современного графического оборудования. WPF предоставляет средства для создания
визуального интерфейса, включая Язык XAML (Extensible Application Markup Language), элементы управления, привязку
данных, макеты, двухмерную и трехмерную графику, анимацию, стили, шаблоны,
документы, текст, мультимедиа и оформление [1].
Графической технологией, лежащей в основе WPF является DirectX, в отличие от Windows Forms, где
используется GDI/GDI+[2]. Производительность WPF выше, чем у GDI+ за счёт использования аппаратного ускорения графики через DirectX. представляет собой XML, в котором фактически реализованы
классы .NET Framework. Так же реализована модель
разделения кода и дизайна, позволяющая кооперироваться программисту и
дизайнеру. Кроме того, есть встроенная поддержка стилей элементов, а сами
элементы легко разделить на элементы управления второго уровня, которые в свою
очередь разлагаются до уровня векторных фигур и свойств/действий.
3.2 Особенности реализации
- клиент реализован на базе протокола OSCAR.- открытый (с 5 марта 2008 года), но не свободный сетевой
протокол, обеспечивающий обмен мгновенными и оффлайновыми текстовыми
сообщениями. В данный момент используется для двух систем: AIM (компания AOL,
управляемая Time Warner) и ICQ (компания Mail.Ru).
OSCAR. "Open System for Communication in Realtime"
(рус. "Открытая система для общения в
реальном времени") - внутреннее название проекта (в отличие от внешнего
маркетингового названия) используется для определения протокола обмена
мгновенными сообщениями. - это низкоуровневый протокол передачи данных, который
облегчает разработку высокоуровневых, дейтаграмно-ориентированных слоев по
передачи данных. Он использует TCP-соединения
между клиентами и серверами.
Байт FLAP-идентификатора всегда 0х2А. FLAP datagram sequence number - используется для
выявления ошибок.
FLAP channel - это
идентификатор, используемый для определения различных типов данных во FLAP-пакете, для передачи данных через
один и тот же TCP-сокет. В некотором роде это аналог TCP/UPD номеров портов. В OSCAR используются пять видов каналов:
x01 -
договор о новом соединении;
х02 - SNAC-пакет;
х03 - ошибка FLAP-уровня;
х04 - договор о завершении соединения;
х05 - оставаться в сети.
Наиболее часто используется канал 0х02, так как только по нему могут
передаваться SNAC-пакеты.
SNAC -
это базовый модуль передачи данных между клиентом и сервером. SNAC-слой находится выше FLAP-слоя, он является содержимым поля
данных FLAP-а, передается только по каналу 0х02.
Формат SNAC:
Нет никакой информации о длине данных в SNAC. Она должна быть определена из заголовка FLAP-а.Флаги являются одним из главных
свойств пакета данных. Например, первый бит двухбайтового флага
устанавлявается, если да данного id
запроса было послано более одного SPAC.
TLV (type-length-value
- тип-длина-значение) - очень удобный и эффективный метод для передачи данных в
организованном формате, особенно строк переменной длины. 16 бит кода типа, 16
бит значения длины данных, сами данные:
TLV-блоки
могут находиться внутри SNAC-блоков,
но это не обязательно. Иногда их располагают непосредственно во FLAP-поле с данными, но чаще внутри SNAC. TLV-блоков в SNAC может быть несколько. Введение этих блоков было большим прорывом, ведь
стало удобно посылать несколько строк или значений различной длины одним
пакетом, например как {0x0011, 0x000c, "afritz@iname.com"}.
3.3 Разработка программной
архитектуры
Сам проект состоит из двух подпроектов: Core и ICQLite. Первый отвечает за базовые функции ICQ, авторизацию, передачу данных и
прочую бизнес-логику. ICQLite
- содержит классы и окна, используемые для построения пользовательского
интерфейса.
.3.1 Core
Основной класс - Session, представляет собой ICQ сессию. Он агрегирует в
себе классы-менеджеры ConnectionManager, SSIManager, MessageManager,
StatusManager, и т.д. Ниже будут рассмотрены некоторые из них. Класс содержит
методы InitializeLogger, Logon, Logoff, AddBuddy, RemoveBuddy,
ActivateBuddyList, SendAuthorizationRequest и многие другие (все они
располагаются в папке ObjectManagers проекта), которые далее оперируют с
методами менеджеров. Также в нем имеются множество событий, которые срабатывают
при происхождении каких-либо действий, на которые можно назначить обработчики
событий из пользовательского интерфейса для его обновления.
AuthorizationManager - отвечает за авторизацию, содержит методы LoginToService, ProcessIncomingPacket,
SendAuthorizationRequest, ProcessLoginResponse и др. - класс, отвечающий за установку
соединения с сервером. Содержит методы CreateNewConnection,
DeregisterConnection, CreateNewChatConnection, CreateNewFileTransferConnection,
RemoveChatConnection и многие
другие.
Если рассмотреть подробнее методы классов-менеджеров, то можно заметить,
что во многих происходит построение TLV-блоков с данными, которые потом обворачиваются в SNAC-пакеты и отправляются на сервер.
Различные SNAC-и описаны как
классы в папке SNACs, также там представлены методы работы с ними.
Класс Logging отвечает за логгирование поступивших и отправленных
пакетов, что намного упрощает разработку и отладку проекта.
Также имеются классы с описанием делегатов для событий и аргументов
различных событий.
3.3.2 ICQLite
В данном подпроекте реализован пользовательский интерфейс приложения. В
нем используется концепция событийно-ориентированного программирования. Это
значит, что в коде явным образом выделяется главный цикл программы, тело
которого состоит из двух основных частей: выборки события и обработки события.
Понятно, что недопустимы для приложения длительные выполнения обработчиков
событий, поэтому многие операции выполняются в фоновом потоке. Но тут встает
проблема обновления пользовательского интерфейса результатами обработки, ведь
технология WPF не позволяет изменять UI из потоков, не являющихся основным.
Решение - ставить обработчики событий в очередь на выполнение главному потоку
методом Dispatcher.Invoke(…).
Первое окно, видимое пользователю при запуске приложения - класса LoginWindow, в нем имеются поля для ввода номера
ICQ и пароля, в нем же происходит
отправка на сервер этих данных. При успешно прошедшей авторизации это окно
загружает окно с контактами класса ContactListWindow, а само закрывается.
ContactListWindow отображает контакты, расформированные по группам, в виде
дерева элементов. В главном меню окна имеется возможность добавить контакт - за
это отвечает окно AddContactWindow.
Каждый контакт из списка контактов имеет контекстное меню, в нем присутствуют
опции отображения информации о контакте (AboutWindow) и удаления контакта из серверного
списка контактов.
При двойном щелчке на контакте открывается окно с чатом для данного
контакта. При повторном двойном щелчке на нем же - то же самое окно повторно
выходит на передний план.
ChatWindow. Имееет RichTextBox для ввода текста,
а сама переписка отображается выше, с датой отправки сообщения. CheckBox внизу позволяет управлять отправкой
сообщения по нажатию клавиши Enter,
либо по нажатию кнопки “Send”.
Если окно ChatWindow c перепиской с определенным контактом
не было открыто, либо было закрыто, а этот контакт отправил сообщение
пользователю, то напротив него в списке контактов отображается значок в виде
сообщения, по нажатию на который он пропадает, и открывается окно с чатом.
ТЕСТИРОВАНИЕ ПРОГРАММНОГО СРЕДСТВА
Для данного программного средства было использовано и функциональное
тестирование:
Тестовые сценарии приведены в таблице 1.
Таблица 1 - Тестовые сценарии
№
|
Название модуля
|
Последовательность действий
|
Ожидаемый результат
|
1
|
Авторизация
|
1. Заполнить обязательные
поля на форме авторизации 2. Нажать на кнопку авторизации
|
1. Проверить наличие
проверки на обязательные поля 2. Проверить факт авторизации
|
2
|
Добавление контакта
|
1. Заполнить поля на форме
2. Нажать на кнопку добавления
|
1. Проверить удалось ли добавить
новый контакт
|
3
|
Список контактов
|
1. Удалить контакт
|
1. Проверить удален ли
контакт.
|
4
|
Список контактов
|
1. Посмотреть информацию о
контакте
|
1. Проверить достоверность
информации о контакте
|
5
|
Чат
|
1. Отправить сообщение для
пользователя, находящегося онлайн
|
1. Проверить дошло ли
сообщение до собеседника 2. Проверить достоверность дошедшего сообщения
|
6
|
Чат
|
1. Отправить сообщение для
пользователя, находящегося оффлайн
|
1 Проверить дошло ли
сообщение до собеседника 2 Проверить достоверность дошедшего сообщения
|
7
|
Чат
|
1. Отправить сообщение
другим контактом для контакта, находящегося онлайн
|
1 Проверить дошло ли
сообщение до контакта 2 Проверить достоверность дошедшего сообщения
|
7
|
Чат
|
1. Отправить сообщение
другим контактом для контакта, находящегося оффлайн
|
1 Проверить дошло ли
сообщение до контакта 2 Проверить достоверность дошедшего сообщения
|
МЕТОДИКА РАБОТЫ С ПРОГРАММНЫМ СРЕДСТВОМ
Login-форма
cодержит поля для ввода логина (ICQ номера), пароля.
Рисунок 4.1Login - форма
По нажатию кнопки “Sign In” происходит
отправка данных на сервер, выполняется авторизация пользователя в сети. При
неудаче выводится сообщение о типе ошибки.
Также обновляется статус и проценты выполненной на сервере работы по
авторизации:
Рисунок 4.2Статус авторизации
Рисунок 4.3 Общий вид программы
Контакты отображаются по названиям групп, внутри них - списки контактов,
зеленый индификатор - контакт находится в сети, красный - оффлайн. Двойным
щелчком по контакту открывается окно чата.
В окне чата реализована поддержка оффлайн сообщений (отображаются серым
цветом). Есть форма для ввода текста, и форма для отображения переписки. Также
можно включить отсылку сообщения по нажатию клавиши Enter.
Если окно чата закрыто, а сообщение от собеседника пришло, в списке
контактов отображается значок письма. По двойному щелчку окно с чатом
открывается, а он пропадает (заменяется на онлайн/оффлайн значок).
Рисунок 4.4Окно чата
Рисунок 4.5 Общий вид программы (Пришло сообщение)
Рисунок 4.5 Информация о пользователе
Добавление контакта идет в отдельном окне, после контакт лист заново
перерисовывается. Удаление происходит при выборе данной опции из контекстного
меню контакт-листа, после чего он также заново перерисовывается.
Рисунок 4.6 Добавление контакта
ЗАКЛЮЧЕНИЕ
В результате выполнения данной курсовой работы был получен программный
продукт, названный "ICQLite".
Было проведено исследование .NET
Framework, которая использовалась при создании
программного средства, а также протокола OSCAR.
В результате проделанной работы были усвоены основные приемы и навыки
объектно-ориентированного программирования с использованием языка
программирования высокого уровня C#.
Применялись такие базовые концепции как: наследование, полиморфизм,
инкапсуляция.
Программа реализует cредство
мгновенного обмена сообщениями (англ. Instant messenger, способ обмена
сообщениями через Интернет в реальном времени через службы мгновенных сообщений
(Instant Messaging Service, IMS))
СПИСОК ИСПОЛЬЗОВАННЫХ ИСТОЧНИКОВ
[1] Крупник
А. Изучаем С#. Питер, 2001. - 480 c.
[2] Глинн Д.
Платформа .NET 3.0 для профессионалов, 2008. - 1024c.
[3] Агуров
П.В. C#. Сборник рецептов М.: ООО «Бином-Пресс», 2006. - 512
[4] Шилдт Г. C#: учебный курс, 2007. - 768 c.
[5] Джейсон
П. Visual C# 2.0. NET
Полное руководство. СПб. : Питер Ком, 2004. - 288 с.
[6] Фролов
А., Фролов Г. C#. Визуальное проектирование приложений. М.: ООО «Бином-Пресс»,
2006. - 512 c.
[7] Кариев
Ч.А. Разработка Windows-приложений на основе Visual C#, 2005. 1000 c.
[8] Буч Г.
Объектно-ориентированный анализ и проектирование. Диалог-МИФИ, 2006. - 800 c.
[9] Бадд Т.
Объектно-ориентированное программирование в действии. Диалог-МИФИ, 2005. - 650 c.
[10] Виейра
Р. Программирование баз данных. Диалог-МИФИ, 2005. - 1020
ПРИЛОЖЕНИЕ А
ЛИСТИНГ ПРОГРАММНОГО СРЕДСТВА
System;System.Collections;System.IO;System.Security.Cryptography;System.Text;csammisrun.OscarLib.Utility;
csammisrun.OscarLib
{
#region Logged in / out exceptions
/// <summary>
/// Thrown when an operation is requested that requires the
<see cref="Session"/>
/// to be logged in
/// </summary>class NotLoggedInException : Exception
{
}
/// <summary>
/// Thrown when an operation is requested that requires the
<see cref="Session"/>
/// to be logged out
/// </summary>class LoggedInException : Exception
{readonly string _message;
/// <summary>
/// Creates a new LoggedInException with a blank message
/// </summary>LoggedInException()
{
_message = "";
}
/// <summary>
/// Creates a new LoggedInException
/// </summary>
/// <param name="message">The message to be
passed with the exception</param>LoggedInException(string message)
{
_message = message;
}
/// <summary>
/// Gets a message that describes the current exception
/// </summary>override string Message
{{ return _message; }
}
}
#endregion
/// <summary>
/// The representation of an AOL Instant Messenger or ICQ
session
/// </summary>class Session
{readonly OSCARIdentification _clientid = new
OSCARIdentification();readonly string _password;readonly Hashtable
_requestidstorage = new Hashtable();readonly AuthorizationManager
authManager;readonly ChatRoomManager chatRoomManager;readonly ConnectionManager
connectionManager;readonly PacketDispatcher dispatcher = new PacketDispatcher();readonly
FamilyManager familyManager = new FamilyManager();readonly GraphicsManager
graphicsManager;readonly IcqManager icqManager;readonly LimitManager
limitManager = new LimitManager();readonly MessageManager
messageManager;readonly RateClassManager rateManager;readonly SearchManager
searchManager;readonly ServiceManager serviceManager;readonly SSIManager
ssiManager;readonly StatusManager statusManager;
Capabilities _caps = Capabilities.OscarLib;bool
_loggedin;ushort _loginport = 5190;ushort _parametercount;PrivacySetting
_privacy;bool _publicidletime;string _screenname;
/// <summary>
/// Create a new OSCAR session
/// </summary>
/// <param name="screenname">The screenname
to log in</param>
/// <param name="password">The password
associated with the screenname</param>
/// <exception
cref="ArgumentException">Thrown when <paramref
name="screenname"/> is not
/// a valid AIM or ICQ
screenname.</exception>Session(string screenname, string password)
{
// Check to make sure the screenname is something
valid(!ScreennameVerifier.IsValidAIM(screenname) &&
!ScreennameVerifier.IsValidICQ(screenname))
{new ArgumentException(screenname + " is not a valid AIM
or ICQ screenname", "screenname");
}
// Save parameter values
_screenname = screenname;
_password = password;
= new ConnectionManager(this);= new ServiceManager(this);=
new SSIManager(this);= new IcqManager(this);= new MessageManager(this);= new
StatusManager(this);= new ChatRoomManager(this);= new GraphicsManager(this);=
new AuthorizationManager(this);= new SearchManager(this);= new
RateClassManager(this);
.CreateNewConnection(0x0017);
// Create a default set of capabilities for this session();
// Set up some default values for public properties
_publicidletime = true;
// Set initial values for internal properties
_loggedin = false;
_privacy = PrivacySetting.AllowAllUsers;
_parametercount = 0;
}
#region Public methods
/// <summary>
/// Sets the session's <see
cref="ClientIdentification"/> to the AOL defaults
/// </summary>
/// <exception
cref="LoggedInException">Thrown when the <see
cref="Session"/> is already logged in</exception>void
SetDefaultIdentification()
{(LoggedIn)
{new LoggedInException("Identification cannot be changed
after the session is logged in");
}
_clientid.ClientName = Constants.CLIENT_NAME;
_clientid.ClientId = Constants.CLIENT_ID;
_clientid.ClientMajor = Constants.CLIENT_MAJOR;
_clientid.ClientMinor = Constants.CLIENT_MINOR;
_clientid.ClientLesser = Constants.CLIENT_LESSER;
_clientid.ClientBuild = Constants.CLIENT_BUILD;
_clientid.ClientDistribution = Constants.CLIENT_DISTRIBUTION;
}
/// <summary>
/// Initialize the logging system
/// </summary>
/// <param name="baseDir">The directory in
which to save log files</param>
/// <returns>The full logfile
path</returns>string InitializeLogger(string baseDir)
{
// Initialize the logging systemcurrentTime =
DateTime.Now;logfileName = String.Empty;
+= ScreenName + "_";+=
currentTime.Year.ToString("0000") + "-" +
currentTime.Month.ToString("00") + "-"
+.Day.ToString("00") + "_";+=
currentTime.Hour.ToString("00") + "." + currentTime.Minute.ToString("00")
+ "." +.Second.ToString("00") + ".";+=
currentTime.Millisecond.ToString("000") + "_";+=
"OscarLib.log";
logFilePath = Path.Combine(baseDir,
logfileName);(!Directory.Exists(Path.GetDirectoryName(logFilePath)))
{.CreateDirectory(Path.GetDirectoryName(logFilePath));
}
.sw = new StreamWriter(logFilePath);
logFilePath;
}
/// <summary>
/// Begins the process of logging in to the OSCAR service
/// </summary>
/// <param name="loginserver">The OSCAR login
server</param>
/// <param name="port">The OSCAR service
port</param>
/// <remarks>
/// <para>
/// This function is non-blocking, because the login process
does not happen
/// instantly. The OSCAR library will raise the <see
cref="Session.LoginCompleted"/> event
/// when the login process has finished successfully.
/// </para>
/// <para>
/// The OSCAR library raises periodic status update events
throughout the login process
/// via the <see
cref="Session.StatusUpdate"/> event.
/// </para>
/// <para>
/// Errors may occur during the login process; if an error
occurs, the OSCAR library raises
/// the <see cref="Session.ErrorMessage"/>
event, and stops the remaining login sequence.
/// </para>
/// </remarks>
/// <exception
cref="LoggedInException">Thrown when the <see
cref="Session"/> is already logged in</exception>void
Logon(string loginserver, int port)
{(LoggedIn)
{new LoggedInException();
}
= false;.LoginToService(loginserver,
port);("Connecting...", 0.00);
}
/// <summary>
/// Disconnects all active OSCAR connections and resets the
session
/// </summary>void Logoff()
{= false;
(Connection conn in connectionManager.UniqueConnections())
{.DisconnectFromServer(false);
}
}
/// <summary>
/// Adds a buddy to the client's server-side buddy list
/// </summary>
/// <param name="screenname">The screenname
of the buddy to add</param>
/// <param name="parentID">The ID of the
parent group of the buddy</param>
/// <param name="index">The index of the
buddy in the group</param>
/// <param name="alias">The alias of the
buddy ("" for none)</param>
/// <param name="email">The email address of
the buddy ("" for none)</param>
/// <param name="comment">The comment to be
stored for the buddy ("" for none)</param>
/// <param name="SMS">The SMS number for the
buddy ("" for none)</param>
/// <param name="soundfile">The soundfile for
the buddy ("" for none)</param>
/// <param
name="authorziationRequired"><c>true</c> if we require
authorization for this buddy, <c>false</c> otherwise</param>
///<param name="authorizationReason">The
authorization reason/message that will be send to the client</param>
/// <exception
cref="NotLoggedInException">Thrown when the <see
cref="Session"/> is not logged in</exception>
/// <remarks>This function will probably not remain
here; the SSI Manager will be made public</remarks>
[Obsolete(
"This method is obsolete and will be removed soon. Use
the overloaded AddBuddy method without the index parameter."
)]void AddBuddy(string screenname, ushort parentID, int
index, string alias, string email, string comment,SMS, string soundfile, bool
authorziationRequired, string authorizationReason)
{(!LoggedIn)
{new NotLoggedInException();
}
.AddBuddy(screenname, parentID, index, alias, email, SMS,
comment, soundfile, authorziationRequired,);
}
/// <summary>
/// Adds a buddy to the client's server-side buddy list
/// </summary>
/// <param name="screenname">The screenname
of the buddy to add</param>
/// <param name="parentID">The ID of the
parent group of the buddy</param>
/// <param name="alias">The alias of the
buddy ("" for none)</param>
/// <param name="email">The email address of
the buddy ("" for none)</param>
/// <param name="comment">The comment to be
stored for the buddy ("" for none)</param>
/// <param name="SMS">The SMS number for the
buddy ("" for none)</param>
/// <param name="soundfile">The soundfile for
the buddy ("" for none)</param>
/// <param
name="authorziationRequired"><c>true</c> if we require
authorization for this buddy, <c>false</c> otherwise</param>
/// <exception
cref="NotLoggedInException">Thrown when the <see
cref="Session"/> is not logged in</exception>
/// <remarks>This function will probably not remain
here; the SSI Manager will be made public</remarks>void AddBuddy(string
screenname, ushort parentID, string alias, string email, string comment,SMS,
string soundfile, bool authorziationRequired, string authorizationReason)
{(!LoggedIn)
{new NotLoggedInException();
}
.AddBuddy(screenname, parentID, alias, email, SMS, comment,
soundfile, authorziationRequired,);
}
/// <summary>
/// Moves a buddy
/// </summary>
/// <param name="buddyID">The ID of the buddy
to move</param>
/// <param name="parentID">The ID of the
destination group</param>
/// <param name="index">The index in the
destination group to move to</param>void MoveBuddy(ushort buddyID, ushort
parentID, int index)
{(!LoggedIn)
{new NotLoggedInException();
}
.MoveBuddy(SSI.GetBuddyByID(buddyID, parentID),
SSI.GetGroupByID(parentID), index);
}
/// <summary>
/// Remove a buddy
/// </summary>
/// <param name="buddyID">The ID of the buddy
to remove</param>void RemoveBuddy(ushort buddyID, ushort parentID)
{(!LoggedIn)
{new NotLoggedInException();
}buddy = SSI.GetBuddyByID(buddyID, parentID);(buddy !=
null).RemoveBuddy(buddy);
}
/// <summary>
/// Adds a group to the client's server-side buddy list
/// </summary>
/// <param name="groupname">The name of the
new group</param>
/// <param name="index">The index into the
current list of groups</param>
[Obsolete(
"This method is obsolete and will be removed soon. Use
the overloaded AddGroup methods without the index parameter."
)]void AddGroup(string groupname, int index)
{(!LoggedIn)
{new NotLoggedInException();
}
.AddGroup(groupname, index);
}
/// <summary>
/// Adds a group to the client's server-side buddy list
/// </summary>
/// <param name="groupname">The name of the
new group</param>
/// <param name="id">The group
id</param>void AddGroup(string groupname, ushort id)
{(!LoggedIn)
{new NotLoggedInException();
}
.AddGroup(groupname, id);
}
/// <summary>
/// Adds a group to the client's server-side buddy list
/// </summary>
/// <param name="groupname">The name of the
new group</param>void AddGroup(string groupname)
{(!LoggedIn)
{new NotLoggedInException();
}
.AddGroup(groupname);
}
/// <summary>
/// Adds the master group. This is necessary if a contact
list is empty to add further groups after
/// </summary>
/// <param name="groupname">The master group
name</param>void AddMasterGroup(string groupname)
{(!LoggedIn)
{new NotLoggedInException();
}(groupname, (ushort) 0);
}
/// <summary>
/// Move a group in the buddy list
/// </summary>
/// <param name="groupID">The ID of the group
to move</param>
/// <param name="index">The new index of the
group</param>void MoveGroup(ushort groupID, int index)
{(!LoggedIn)
{new NotLoggedInException();
}
.MoveGroup(SSI.GetGroupByID(groupID), index);
}
/// <summary>
/// Remove a group from the server-side buddy list
/// </summary>
/// <param name="groupID">ID of the group to
remove</param>void RemoveGroup(ushort groupID)
{(!LoggedIn)
{new NotLoggedInException();
}group = SSI.GetGroupByID(groupID);(group !=
null).RemoveGroup(group);
}
/// <summary>
/// Tells AIM to begin sending UserStatus objects to client
(online or away)
/// Client should call in response to <see
cref="ContactListFinished"/> event
/// </summary>void ActivateBuddyList()
{.ActivateSSI(this);();
}
/// <summary>
/// Requests a list of user interests from the server
/// </summary>
/// <exception
cref="NotLoggedInException">Thrown when the <see
cref="Session"/> is not logged in</exception>void
RequestInterestsList()
{(!LoggedIn)
{new NotLoggedInException();
}F.RequestInterestList(this);
}
/// <summary>
/// Sends an icq authorization request
/// </summary>
/// <param name="screenname">the destination
screenname</param>
/// <param name="reason">the request
reason</param>void SendAuthorizationRequest(string screenname, string
reason)
{(!LoggedIn)
{new NotLoggedInException();
}
// TODO reason string works only with ASCII encoding until
now.SendAuthorizationRequest(this, screenname, reason);
}
/// <summary>
/// Sends an icq authorization response
/// </summary>
/// <param name="screenname">the destination
screenname</param>
/// <param name="grantAuthorization">true, if
the authorization should be granted, otherwise false</param>
/// <param name="reason">the reason for the
decision</param>void SendAuthorizationResponse(string screenname, bool
grantAuthorization, string reason)
{(!LoggedIn)
{new NotLoggedInException();
}.SendAuthorizationResponse(this, screenname,
grantAuthorization, reason);
}
/// <summary>
/// Grants the authorization to another screenname for the
future
/// </summary>
/// <param name="screenname">The
uin/screenname</param>
/// <param name="reason">The reason
message</param>
/// <remarks>TODO ... seems to be obsolete in the
current Oscar version</remarks>void SendFutureAuthorizationGrant(string
screenname, string reason)
{(!LoggedIn)
{new NotLoggedInException();
}.SendFutureAuthorizationGrant(this, screenname, reason);
}
/// <summary>
/// Sends a requests for the server side buddylist. Server
should reply with
/// the buddylist, or with the info that the client side
buddylist is up to date
/// <remarks>TODO have to be tested</remarks>
/// </summary>void SendContactListCheckout()
{(LastModificationDate);
}
/// <summary>
/// Sends a requests for the server side buddylist. Server
should reply with
/// the buddylist, or with the info that the client side
buddylist is up to date
/// </summary>
/// <param name="lastModificationDate">the
date when the client side buddylist was updated the last time</param>
/// <remarks>TODO have to be tested</remarks>void
SendContactListCheckout(DateTime lastModificationDate)
{(!LoggedIn)
{new NotLoggedInException();
}localSSIItemCount =
SSI.GetLocalSSIItemCount();.SendContactListCheckout(this, LastModificationDate,
true, localSSIItemCount);
}
#region File transfer methods
/// <summary>
/// Send a file to a remote client via a direct connection
/// </summary>
/// <param name="recipient">The screenname of
the remote client</param>
/// <param name="filename">The path of the file
to send</param>
/// <returns>A key with which to reference this file
transfer, or "" if a warning was
/// generated during the initialization
process</returns>
/// <exception
cref="NotLoggedInException">Thrown when the <see
cref="Session"/> is not logged in</exception>Cookie
SendFile(string recipient, string filename)
{(!LoggedIn)
{new NotLoggedInException();
}
ftconn
=.CreateNewFileTransferConnection(DirectConnectionMethod.Direct,
DirectConnectRole.Initiator);.Other.ScreenName = recipient;.LocalFileName =
filename;.ConnectToServer();ftconn.Cookie;
}
/// <summary>
/// Start a DirectIM session with a remote client via a
direct connection
/// </summary>
/// <param name="recipient">The screenname of
the remote client</param>
/// <param name="message">A message with
which to invite the remote client</param>
/// <returns>A key with which to reference this
DirectIM session, or "" if a warning was
/// generated during the initialization
process</returns>
/// <exception cref="NotLoggedInException">Thrown
when the <see cref="Session"/> is not logged
in</exception>Cookie StartDirectIM(string recipient, string message)
{(!LoggedIn)
{new NotLoggedInException();
}
dimconn
=.CreateNewDirectIMConnection(DirectConnectionMethod.Direct, DirectConnectRole.Initiator);.Other.ScreenName
= recipient;.Message = message;.ConnectToServer();dimconn.Cookie;
}
/// <summary>
/// Send a file to a remote client via an AOL proxy
/// </summary>
/// <param name="recipient">The screenname of
the remote client</param>
/// <param name="filename">The path of the
file to send</param>
/// <returns>A key with which to reference this file
transfer, or "" if a warning was
/// generated during the initialization
process</returns>
/// <exception cref="NotLoggedInException">Thrown
when the <see cref="Session"/> is not logged
in</exception>Cookie SendFileProxied(string recipient, string filename)
{(!LoggedIn)
{new NotLoggedInException();
}newconn
=.CreateNewFileTransferConnection(DirectConnectionMethod.Proxied, DirectConnectRole.Initiator);.Other.ScreenName
= recipient;.LocalFileName = filename;.ConnectToServer();newconn.Cookie;
}
/// <summary>
/// Start a DirectIM session with a remote client via an AOL
proxy
/// </summary>
/// <param name="recipient">The screenname of
the remote client</param>
/// <param name="message">A message with
which to invite the remote client</param>
/// <returns>A key with which to reference this
DirectIM session, or "" if a warning was
/// generated during the initialization
process</returns>
/// <exception
cref="NotLoggedInException">Thrown when the <see
cref="Session"/> is not logged in</exception>Cookie
StartDirectIMProxied(string recipient, string message)
{(!LoggedIn)
{new NotLoggedInException();
}
dimconn =.CreateNewDirectIMConnection(DirectConnectionMethod.Proxied,
DirectConnectRole.Initiator);.Other.ScreenName = recipient;.Message =
message;.ConnectToServer();dimconn.Cookie;
}
/// <summary>
/// Accept an invitation to a DirectIM session
/// </summary>
/// <param name="key">The key received in the
<see cref="OscarLib_DirectIMRequestReceived"/>
event</param>void AcceptDirectIMSession(Cookie key)
{(!LoggedIn)
{new NotLoggedInException();
}
conn = Connections.GetDirectConnectionByCookie(key) as
DirectIMConnection;(conn != null)
{.ConnectToServer();
}
{new Exception("Invalid DirectIM session key:
\"" + key + "\"");
}
}
/// <summary>
/// Accept a file being sent to the client
/// </summary>
/// <param name="key">The key received in the
<see cref="FileTransferRequestReceived"/> event</param>
/// <param name="savelocation">The path to
which to save the file</param>
/// <exception
cref="NotLoggedInException">Thrown when the <see
cref="Session"/> is not logged in</exception>
/// <exception cref="System.Exception">Thrown
when <paramref name="key"/> is not a valid file transfer
key</exception>void AcceptFileTransfer(Cookie key, string savelocation)
{(!LoggedIn)
{new NotLoggedInException();
}
conn = Connections.GetDirectConnectionByCookie(key) as
FileTransferConnection;(conn != null)
{.LocalFileName = savelocation;.ConnectToServer();
}
{new Exception("Invalid file transfer key: \""
+ key + "\"");
}
}
/// <summary>
/// Cancel a pending or in-progress file transfer
/// </summary>
/// <param name="key">The key received with
the transfer request</param>
/// <exception
cref="NotLoggedInException">Thrown when the <see
cref="Session"/> is not logged in</exception>
/// <exception cref="System.Exception">Thrown
when <paramref name="key"/> is not a valid file transfer
key</exception>void CancelFileTransfer(Cookie key)
{(!LoggedIn)
{new NotLoggedInException();
}
conn = Connections.GetDirectConnectionByCookie(key) as
FileTransferConnection;(conn != null)
{.CancelFileTransfer("User cancelled transfer");
}
}
/// <summary>
/// Cancel a pending or in-progress Direct IM session
/// </summary>
/// <param name="key">The key received with
the connection request</param>
/// <exception
cref="NotLoggedInException">Thrown when the <see
cref="Session"/> is not logged in</exception>
/// <exception cref="System.Exception">Thrown
when <paramref name="key"/> is not a valid file transfer
key</exception>void CancelDirectIMSession(Cookie key)
{(!LoggedIn)
{new NotLoggedInException();
}
conn = Connections.GetDirectConnectionByCookie(key) as
DirectIMConnection;(conn != null)
{.SendDirectConnectionCancellation(conn, "User cancelled
Direct Connection");.DisconnectFromServer(false);
}
}
#endregion
#endregion
#region Internal methods
/// <summary>
/// Returns an MD5 hash of the client's password, an
authorization key, and a constant string
/// </summary>
/// <param name="authkey">The authorization
key sent by the server</param>
/// <returns>A 16-byte MD5 hash</returns>
/// <remarks>
/// <para>
/// The hashing process is fairly simple:
/// <list>
/// <item>The authorization key is put into a
buffer</item>
/// <item>The password itself is hashed via MD5 and
appended to the buffer</item>
/// <item>The constant string, "AOL Instant
Messenger (SM)", is appended to the buffer in plaintext</item>
/// <item>The entire buffer is MD5 hashed and returned
to the caller</item>
/// </list>
/// </para>
/// <para>
/// This method exists to prevent the password from having to
be passed around in a data structure
/// </para>
/// </remarks>internal byte[] HashPassword(byte[]
authkey)
{md5 = new MD5CryptoServiceProvider();
stream = new
ByteStream();.WriteByteArray(authkey);.WriteByteArray(md5.ComputeHash(Encoding.ASCII.GetBytes(_password)));.WriteString(Constants.AIM_MD5_STRING,
Encoding.ASCII);md5.ComputeHash(stream.GetBytes());
}
/// <summary>
/// Stores data associated with a SNAC request/reply
/// </summary>
/// <param name="requestid">A SNAC request
ID</param>
/// <param name="data">The data to be
stored</param>internal void StoreRequestID(uint requestid, object data)
{
_requestidstorage.Add(requestid, data);
}
/// <summary>
/// Retrieves data associated with a SNAC request/reply
/// </summary>
/// <param name="requestid">A SNAC request
ID</param>
/// <returns>The data previously stored by <see
cref="StoreRequestID"/></returns>internal object
RetrieveRequestID(uint requestid)
{_requestidstorage[requestid];
}
/// <summary>
/// Sets the session's privacy setting sent by the server in
SNAC(13,06)
/// </summary>
/// <param name="ps">One of the <see
cref="PrivacySetting"/> enumeration members</param>internal
void SetPrivacyFromServer(PrivacySetting ps)
{
_privacy = ps;
}
/// <summary>
/// Sets whether or not the client's idle time is public --
SNAC(13,06)
/// </summary>
/// <param name="publicidletime">true if
others can see this client's idle time, false otherwise</param>internal
void SetPresence(bool publicidletime)
{
_publicidletime = publicidletime;
}
/// <summary>
/// Keeps track of the SNAC parameter responses that have
been received thus far
/// </summary>internal void ParameterSetArrived()
{
_parametercount++;
(_parametercount == 5)
{
// We can send more stuff now
}
}
#endregion
#region Properties
#region Connection properties
ProxyType proxySetting = ProxyType.None;
/// <summary>
/// The username to use for a proxy server
/// </summary>string ProxyUsername { get; set; }
/// <summary>
/// The password to use for a proxy server
/// </summary>string ProxyPassword { get; set; }
/// <summary>
/// The proxy server to connect through
/// </summary>string ProxyServer { get; set; }
/// <summary>
/// The port to connect to on the proxy server
/// </summary>int ProxyPort { get; set; }
/// <summary>
/// The <see cref="ProxyType"/> to use for
this connection
/// </summary>ProxyType ProxySetting
{{ return proxySetting; }{ proxySetting = value; }
}
#endregion Connection propertiesEncoding encoding;
/// <summary>
/// Gets or sets the screen name associated with this session
/// </summary>
/// <remarks>
/// The screen name cannot be set by this property while the
client is offline.
/// When the client is online, setting the screen name by
this property changes the
/// screen name's formatting on the server.
/// </remarks>
/// <exception
cref="ArgumentException">Thrown when <paramref
name="screenname"/> is not
/// a valid AIM or ICQ screenname.</exception>string
ScreenName
{{ return _screenname; }
{(LoggedIn)
{new ArgumentException(value + " is not a valid AIM or
ICQ screenname");
}
_screenname = value;
// TODO: Actually reset the formatting...
}
}
}
/// <summary>
/// Gets or sets the port number used for OSCAR logins
/// </summary>
/// <remarks>
/// Traditionally, this is port 5190; however, AIM 6 has been
caught using port 443 to negotiate
/// connections with login.oscar.aol.com and
ars.oscar.aol.com. Future versions of OscarLib may use
/// this property to support login via port 443.
/// </remarks>ushort LoginPort
{{ return _loginport; }set { _loginport = value; }
}
/// <summary>
/// Gets or sets this session's OSCAR identification
information
/// </summary>
/// <exception
cref="LoggedInException">Thrown when the <see
cref="Session"/> is already logged
in</exception>OSCARIdentification ClientIdentification
{{ return _clientid; }
}
/// <summary>
/// Gets or sets the OSCAR capabilities associated with the
session
/// </summary>
/// <remarks>
/// The client capabilities must be set before the session is
logged in because the
/// client's capabilities are communicated during the login process
and are kept through
/// the session.
/// </remarks>
/// <exception
cref="LoggedInException">Thrown when the <see
cref="Session"/> is already logged
in</exception>Capabilities ClientCapabilities
{{ return _caps; }
{(LoggedIn)
{new LoggedInException("Client capabilities cannot be
set after the session is logged in");
}
_caps = value;((value & Capabilities.UTF8) ==
Capabilities.UTF8)
{= Encoding.UTF8;
}
{= Encoding.ASCII;
}
}
}
/// <summary>
/// Gets the recommended enocding format depending on the
client capability settings
/// </summary>Encoding Encoding
{{ return encoding; }
}
/// <summary>
/// Gets a value indicating whether this client has completed
the login process
/// </summary>bool LoggedIn
{{ return _loggedin; }set { _loggedin = value; }
}
/// <summary>
/// Gets the <see cref="SSIManager"/>
associated with this session
/// </summary>SSIManager SSI
{{ return ssiManager; }
}
/// <summary>
/// Gets the <see cref="LimitManager"/> associated
with this session
/// </summary>LimitManager Limits
{{ return limitManager; }
}
/// <summary>
/// Gets the <see cref="IcqManager"/>
associated with this session
/// </summary>IcqManager ICQ
{{ return icqManager; }
}
/// <summary>
/// Gets the <see cref="MessageManager"/>
associated with this session
/// </summary>MessageManager Messages
{{ return messageManager; }
}
/// <summary>
/// Gets the <see cref="ChatRoomManager"/>
associated with this session
/// </summary>ChatRoomManager ChatRooms
{{ return chatRoomManager; }
}
/// <summary>
/// Gets the <see cref="GraphicsManager"/>
associated with this session
/// </summary>GraphicsManager Graphics
{{ return graphicsManager; }
}
/// <summary>
/// Gets the <see cref="StatusManager"/> associated
with this session
/// </summary>StatusManager Statuses
{{ return statusManager; }
}
/// <summary>
/// Gets the <see cref="SearchManager"/>
associated with this session
/// </summary>SearchManager Searches
{{ return searchManager; }
}
/// <summary>
/// Gets or sets a filesystem path where OscarLib can place
received data
/// </summary>
/// <remarks>During an OSCAR Direct Connect session,
"transient" files may come over the wire.
/// If ScratchPath is set to a valid path, OscarLib will save
the files locally and return
/// <see cref="System.IO.FileStream"/>
references to the objects. Otherwise, the files will
/// be returned as <see
cref="System.IO.MemoryStream"/> objects, which will take more
active memory.</remarks>string ScratchPath { get; set; }
/// <summary>
/// Gets the <see cref="ConnectionManager"/>
associated with this session
/// </summary>ConnectionManager Connections
{{ return connectionManager; }
}
/// <summary>
/// Gets the <see cref="ServiceManager"/>
associated with this session
/// </summary>ServiceManager Services
{{ return serviceManager; }
}
/// <summary>
/// Gets the <see cref="PacketDispatcher"/>
associated with this session
/// </summary>PacketDispatcher Dispatcher
{{ return dispatcher; }
}
/// <summary>
/// Gets the <see cref="FamilyManager"/>
associated with this session
/// </summary>FamilyManager Families
{{ return familyManager; }
}
/// <summary>
/// Gets the <see cref="RateClassManager"/>
associated with this session
/// </summary>RateClassManager RateClasses
{{ return rateManager; }
}
/// <summary>
/// Gets the <see
cref="AuthorizationManager"/> associated with this session
/// </summary>AuthorizationManager Authorization
{{ return authManager; }
}
#endregion
/// <summary>
/// Gets or Sets the last modification date and time of the
buddylist
/// </summary>DateTime LastModificationDate { get; set;
}
#region Public events and protected event firing functions
#region OscarLib-generated events
/// <summary>
/// Occurs when an unhandled exception is raised in the
course of dispatching and processing a packet
/// </summary>event PacketDispatchExceptionHandler
PacketDispatchException;
/// <summary>
/// Raises the <see
cref="PacketDispatchException"/> event
/// </summary>internal void
OnPacketDispatchException(Exception ex, DataPacket packet)
{(PacketDispatchException != null)
{(this, new PacketDispatchExceptionArgs(ex, packet));
}
}
/// <summary>
/// Occurs when the library generates a status update message
/// </summary>event InformationMessageHandler
StatusUpdate;
/// <summary>
/// Raises the <see cref="StatusUpdate"/>
event
/// </summary>
/// <param name="message">A status
message</param>internal void OnStatusUpdate(string message)
{(StatusUpdate != null)(this, message);
}
/// <summary>
/// Occurs when the library generates a status update message
during login
/// </summary>
public event LoginStatusUpdateHandler LoginStatusUpdate;
/// <summary>
/// Raises the <see
cref="LoginStatusUpdate"/> event
/// </summary>
/// <param name="message">A status
message</param>
/// <param name="percentdone">The percentage
of the login progress that has been completed</param>internal void
OnLoginStatusUpdate(string message, double percentdone)
{
if (LoginStatusUpdate != null)
{(this, message, percentdone);
}
}
/// <summary>
/// Occurs when the library generates a warning message
/// </summary>event WarningMessageHandler
WarningMessage;
/// <summary>
/// Raises the <see cref="WarningMessage"/>
event
/// </summary>
/// <param name="errorcode">A <see
cref="ServerErrorCode"/> describing the
warning</param>internal void OnWarning(ServerErrorCode errorcode)
{
// csammis: Losing a secondary connection (chat room, icon
downloader)
// isn't cause for logging off the session...and setting LoggedIn
to false
// doesn't log off the session anyway. Call .Logoff() for
that.
//if (errorcode == ServerErrorCode.LostSecondaryConnection)
// this.LoggedIn = false;
(WarningMessage != null)
{(this, errorcode);
}
}
/// <summary>
/// Occurs when the library generates an error message
/// </summary>event ErrorMessageHandler ErrorMessage;
/// <summary>
/// Raises the <see cref="ErrorMessage"/>
event or the <see cref="LoginFailed"/> event
/// </summary>
/// <param name="errorcode">A <see
cref="ServerErrorCode"/> describing the error</param>
/// <remarks>If the login process has not completed,
<see cref="LoginFailed"/> is raised.
/// Otherwise, <see cref="ErrorMessage"/> is
raised.</remarks>internal void OnError(ServerErrorCode errorcode)
{(!_loggedin)
{(LoginFailed != null)
{(errorcode == ServerErrorCode.LostBOSConnection)
{= false;
LoginFailed(this, LoginErrorCode.CantReachBOSServer);
}(this, LoginErrorCode.UnknownError);
}
}
{(ErrorMessage != null)(this, errorcode);
}
}
#endregion
#region SNAC01 events
/// <summary>
/// Occurs when the login process is complete.
/// </summary>event LoginCompletedHandler
LoginCompleted;
/// <summary>
/// Raises the <see cref="LoginCompleted"/>
event
/// </summary>internal void OnLoginComplete()
{= true;(LoginCompleted != null)
{(this);
}
}
/// <summary>
/// Occurs when a remote client has warned this client
/// </summary>event WarningReceivedHandler
WarningReceived;
/// <summary>
/// Raises the <see cref="WarningReceived"/>
event.
/// </summary>
/// <param name="newlevel">The client's new
warning level</param>
/// <param
name="anonymous"><c>true</c> if this warning was sent
anonymously, <c>false</c> otherwise</param>
/// <param name="ui">A <see
cref="UserInfo"/> structure describing the warning user. If
<paramref name="anonymous"/> is
/// <c>true</c>, this structure is
unpopulated</param>internal void OnWarningReceived(ushort newlevel, bool
anonymous, UserInfo ui)
{(WarningReceived != null)
WarningReceived(this, newlevel, anonymous, ui);
}
#endregion
#region SNAC02 events
/// <summary>
/// Occurs when the server sends acknowledgement of a
directory update request
/// </summary>event DirectoryUpdateAcknowledgedHandler
DirectoryUpdateAcknowledged;
/// <summary>
/// Raises the <see
cref="DirectoryUpdateAcknowledged"/> event
/// </summary>
/// <param
name="success"><c>true</c> if the directory update
succeded, and <c>false</c> otherwise</param>internal void
OnDirectoryUpdateAcknowledged(bool success)
{(DirectoryUpdateAcknowledged != null)(this, success);
}
#endregion
#region SNAC04 events
/// <summary>
/// Occurs when a file transfer request is received
/// </summary>event FileTransferRequestReceivedHandler
FileTransferRequestReceived;
/// <summary>
/// Occurs when a Direct IM transfer request is received
/// </summary>event DirectIMRequestReceivedHandler
DirectIMRequestReceived;
/// <summary>
/// Raises the <see
cref="FileTransferRequestReceived"/> event
/// </summary>
/// <param name="key">The unique key needed to
respond to this request</param>internal void
OnDirectConnectionRequestReceived(Cookie key)
{conn = Connections.GetDirectConnectionByCookie(key);
(conn is FileTransferConnection &&
FileTransferRequestReceived != null)
{
var ftc = conn as FileTransferConnection;(this, ftc.Other,
ftc.VerifiedIP, ftc.FileHeader.Name,.TotalFileSize, ftc.Message, key);
}if (conn is DirectIMConnection &&
DirectIMRequestReceived != null)
{
DirectIMRequestReceived(this, conn.Other, conn.Message, key);
}
//else if (rd.DirectConnection.ConnectionType ==
DirectConnectType.DirectIM &&
// this.OscarLib_DirectIMRequestReceived != null)
//{
// this.OscarLib_DirectIMRequestReceived(this, rd.UserInfo);
//}
}
/// <summary>
/// Occurs when a chat room invitation is received
/// </summary>
public event ChatInvitationReceivedHandler
ChatInvitationReceived;
/// <summary>
/// Raises the <see
cref="ChatInvitationReceived"/> event
/// </summary>
/// <param name="sender">A <see
cref="UserInfo"/> object represnting the inviter</param>
/// <param name="roomname">The name of the
chatroom</param>
/// <param name="message">An invitation
chatroom</param>
/// <param name="encoding">The text encoding
used in the chatroom</param>
/// <param name="language">The language used
in the chatroom</param>
/// <param name="key">The unique key needed
to respond to this request</param>internal void
OnChatInvitationReceived(UserInfo sender,
string roomname,
string message,encoding,language,key)
{(ChatInvitationReceived != null)(this, sender, roomname,
message, encoding, language, key);
}
#endregion
#region SNAC0F events
/// <summary>
/// Occurs when the server sends the results of a directory
search
/// </summary>event SearchResultsHandler SearchResults;
/// <summary>
/// Raises the <see cref="SearchResults"/>
event
/// </summary>
/// <param name="results">The results of the
directory search</param>internal void OnSearchResults(DirectoryEntry[]
results)
{(SearchResults != null)(this, results);
}
/// <summary>
/// Occurs when the server sends a list of interests
/// </summary>event InterestsReceivedHandler
InterestsReceived;
/// <summary>
/// Raises the <see
cref="InterestsReceived"/> event
/// </summary>
/// <param name="results">The results of the
interests request</param>internal void OnInterestsReceived(InterestItem[]
results)
{(InterestsReceived != null)(this, results);
}
#endregion
#region SNAC13 events
/// <summary>
/// Occurs when the buddy list has been completely sent by
the server
/// </summary>event ContactListFinishedHandler
ContactListFinished;
/// <summary>
/// Notifies the server to activate the SSI data for the
client, and to begin
/// alerting its contacts that it is now online and ready to
receive messages
///
/// Implementing clients should call <see
cref="ActivateBuddyList"/> in response to this event
/// </summary>internal void
OnContactListFinished(DateTime lastModificationDate)
{(ContactListFinished != null)
{= lastModificationDate;(this, lastModificationDate);
}
}
/// <summary>
/// Occurs when the server sends a new buddy item to the
client
/// </summary>
public event BuddyItemReceivedHandler BuddyItemReceived;
/// <summary>
/// Raises the <see cref="BuddyItemReceived"/>
event
/// </summary>
/// <param name="buddy">An <see
cref="SSIBuddy"/> object</param>internal void
OnBuddyItemReceived(SSIBuddy buddy)
{(BuddyItemReceived != null)(this, buddy);
}
/// <summary>
/// Occurs when a buddy item has been removed from the
server-side list
/// </summary>event BuddyItemRemovedHandler
BuddyItemRemoved;
/// <summary>
/// Raises the <see cref="BuddyItemRemoved"/>
event
/// </summary>
/// <param name="buddy">An <see
cref="SSIBuddy"/> object</param>internal void
OnBuddyItemRemoved(SSIBuddy buddy)
{
if (BuddyItemRemoved != null)
{(this, buddy);
}
}
/// <summary>
/// Occurs when the server sends a new group item to the
client
/// </summary>event GroupItemReceivedHandler GroupItemReceived;
/// <summary>
/// Raises the <see
cref="GroupItemReceived"/> event
/// </summary>
/// <param name="group">An <see
cref="SSIGroup"/>"/> object</param>internal void
OnGroupItemReceived(SSIGroup group)
{
}
/// <summary>
/// Occurs when a buddy item has been removed from the
server-side list
/// </summary>event GroupItemRemovedHandler
GroupItemRemoved;
/// <summary>
/// Raises the <see cref="GroupItemRemoved"/>
event
/// </summary>
/// <param name="group">An <see
cref="SSIGroup"/> object</param>internal void
OnGroupItemRemoved(SSIGroup group)
{(GroupItemRemoved != null)
{(this, group);
}
}
/// <summary>
/// Occurs when the server sends the master group item to the
client
/// </summary>
public event MasterGroupItemReceivedHandler
MasterGroupItemReceived;
/// <summary>
/// Raises the <see
cref="MasterGroupItemReceived"/> event
/// </summary>
/// <param name="numgroups">The number of
groups we are going to receive</param>internal void OnMasterGroupItemReceived(int
numgroups)
{(MasterGroupItemReceived != null)(this, numgroups);
}
/// <summary>
/// Occurs when the an SSI edit is completed
/// </summary>event SSIEditCompleteHandler
SSIEditComplete;
/// <summary>
/// Raises the <see cref="SSIEditComplete"/>
event
/// </summary>
protected internal void OnSSIEditComplete()
{(SSIEditComplete != null)
{(this);
}
}
/// <summary>
/// Occurs when a client ask for authorization (ICQ)
/// </summary>event AuthorizationRequestReceivedHandler
AuthorizationRequestReceived;
/// <summary>
/// Raises the <see
cref="AuthorizationRequestReceived"/> event
/// </summary>
/// <param name="screenname">the screenname
that ask for authorization</param>
/// <param name="reason">the reason
message</param>internal void OnAuthorizationRequestReceived(string
screenname, string reason)
{(AuthorizationRequestReceived != null)(this, screenname,
reason);
}
/// <summary>
/// Occurs when a client granted or declined the
authorization (ICQ)
/// </summary>event AuthorizationResponseReceivedHandler
AuthorizationResponseReceived;
/// <summary>
/// Raises the <see
cref="AuthorizationResponseReceived"/> event
/// </summary>
/// <param name="screenname">the screenname
that should get the response</param>
/// <param
name="authorizationGranted">Determines, if the authorization will
be granted or not.</param>
/// <param name="reason">The reason
message</param>internal void OnAuthorizationResponseReceived(string
screenname, bool authorizationGranted,reason)
{(AuthorizationResponseReceived != null)(this, screenname,
authorizationGranted, reason);
}
/// <summary>
/// Occurs when a client granted the authorization for the
future (ICQ)
/// </summary>
public event FutureAuthorizationReceivedHandler
FutureAuthorizationReceived;
/// <summary>
/// Raises the <see
cref="FutureAuthorizationReceived"/> event
/// </summary>
/// <param name="screenname">the screenname
that should get the future authorization</param>
/// <param name="reason">The reason
message</param>internal void OnAuthorizationResponseReceived(string
screenname, string reason)
{(FutureAuthorizationReceived != null)(this, screenname,
reason);
}
#endregion
#region Authorization manager events
/// <summary>
/// Occurs when the login sequence fails
/// </summary>event LoginFailedHandler LoginFailed;
/// <summary>
/// Raises the <see cref="LoginFailed"/>
event
/// </summary>
/// <param name="errorcode">A <see
cref="LoginErrorCode"/> describing the failure</param>internal
void OnLoginFailed(LoginErrorCode errorcode)
{(LoginFailed != null)
{
LoggedIn = false;(this, errorcode);
}
}
#endregion
#region Direct Connection events
/// <summary>
/// Occurs during a file transfer to indicate transfer
progression
/// </summary>event FileTransferProgressHandler
FileTransferProgress;
/// <summary>
/// Raises the <see
cref="FileTransferProgress"/> event
/// </summary>
/// <param name="cookie">The rendezvous
cookie belonging to the file being transfered</param>
/// <param name="bytestransfered">The number
of bytes transfered so far</param>
/// <param name="bytestotal">The total number
of bytes to be transfered</param>internal void
OnFileTransferProgress(Cookie cookie,
uint bytestransfered, uint bytestotal)
{(FileTransferProgress != null)
{
FileTransferProgress(this, cookie, bytestransfered,
bytestotal);
}
}
/// <summary>
/// Occurs during a DirectIM session to indicate the progress
of an incoming message
/// </summary>
/// <remarks>This event will only fire if the incoming
message contains attachments</remarks>
public event DirectIMIncomingMessageProgressHandler
DirectIMIncomingMessageProgress;
/// <summary>
/// Occurs during a DirectIM session to indicate the progress
of an outgoing message
/// </summary>
/// <remarks>This event will only fire if the outgoing
message contains attachments</remarks>event
DirectIMOutgoingMessageProgressHandler DirectIMOutgoingMessageProgress;
/// <summary>
/// Raises the DirectIM message progress events
/// </summary>
/// <param name="incoming">A value indicating
whether the message is incoming or outgoing</param>
/// <param name="cookie">The rendezvous
cookie belonging to the DirectIM session</param>
/// <param name="bytestransfered">The number
of bytes transfered so far</param>
/// <param name="bytestotal">The total number
of bytes to be transfered</param>internal void
OnDirectIMMessageProgress(bool incoming, Cookie cookie, uint bytestransfered,
uint bytestotal)
{(incoming)
{(DirectIMIncomingMessageProgress != null)
{
DirectIMIncomingMessageProgress(this, cookie,
bytestransfered, bytestotal);
}
}
{(DirectIMOutgoingMessageProgress != null)
{(this, cookie, bytestransfered, bytestotal);
}
}
}
/// <summary>
/// Occurs when a file transfer has been cancelled
/// </summary>event FileTransferCancelledHandler
FileTransferCancelled;
/// <summary>
/// Raises the <see
cref="FileTransferCancelled"/> event
/// </summary>
/// <param name="other">The <see
cref="UserInfo"/> of the user on the other side of the
connection</param>
/// <param name="cookie">The rendezvous
cookie belonging to the cancelled file</param>
/// <param name="reason">The reason for the
cancellation</param>internal void OnFileTransferCancelled(UserInfo other,
Cookie cookie, string reason)
{(FileTransferCancelled != null)
{(this, other, cookie, reason);
}
}
/// <summary>
/// Raised when a DirectIM session has been cancelled
/// </summary>event FileTransferCancelledHandler
DirectIMSessionCancelled;
/// <summary>
/// Raises the <see
cref="DirectIMSessionCancelled"/> event
/// </summary>
/// <param name="cookie">The rendezvous
cookie belonging to the cancelled session</param>
/// <param name="reason">The reason for the
cancellation</param>internal void
OnDirectIMSessionCancelled(DirectConnection conn, string reason)
{.RemoveDirectConnection(conn.Cookie);
Messages.SendDirectConnectionCancellation(conn, reason);
(DirectIMSessionCancelled != null)
{(this, conn.Other, conn.Cookie, reason);
}
}
/// <summary>
/// Raised when a DirectIM session has been closed
/// </summary>event DirectIMSessionChangedHandler
DirectIMSessionClosed;
/// <summary>
/// Raises the <see
cref="DirectIMSessionClosed"/>
/// </summary>
/// <param name="other">A <see
cref="UserInfo"/> object describing the other session
participant</param>
/// <param name="cookie">The rendezvous
cookie belonging to the cancelled session</param>internal void
OnDirectIMSessionClosed(UserInfo other, Cookie cookie)
{.RemoveDirectConnection(cookie);
if (DirectIMSessionClosed != null)
{(this, other, cookie);
}
}
/// <summary>
/// Raised when a DirectIM session is ready for data
/// </summary>event DirectIMSessionChangedHandler
DirectIMSessionReady;
/// <summary>
/// Raises the <see
cref="DirectIMSessionReady"/> event
/// </summary>
/// <param name="other">A <see
cref="UserInfo"/> object describing the other session
participant</param>
/// <param name="cookie">The rendezvous
cookie belonging to the session</param>internal void
OnDirectConnectionComplete(UserInfo other, Cookie cookie)
{(DirectIMSessionReady != null)
{(this, other, cookie);
}
}
/// <summary>
/// Occurs when a file transfer has completed
/// </summary>event FileTransferCompletedHandler FileTransferCompleted;
/// <summary>
/// Raises the <see
cref="FileTransferCompleted"/> event
/// </summary>
/// <param name="cookie">The rendezvous
cookie belonging to the completed file</param>internal void
OnFileTransferCompleted(Cookie cookie)
{(FileTransferCompleted != null)
{(this, cookie);
}
}
/// <summary>
/// Occurs when a Direct IM has been received
/// </summary>event DirectIMReceivedHandler
DirectIMReceived;
/// <summary>
/// Raises the <see
cref="OscarLib_DirectIMReceived"/> event
/// </summary>
/// <param name="message">The <see
cref="DirectIM"/> received</param>internal void
OnDirectIMReceived(DirectIM message)
{(DirectIMReceived != null)
{(this, message);
}
}
#endregion
#endregion
}
}