Леди в черномДмитрий «Dem@n» Тарасов Хакер, номер #106, стр. 116 Создание blacklist-приложения для смартфонов под SymbianДовелось мне как-то подвергнуться жестокой DDoS-атаке посредством звонков и sms’ок от одной назойливой барышни. В тот вечер, сидя в одном московском кабаке с другом, мы обратили внимание на тот факт, что производители смартфонов пичкают свои девайсы чем угодно, но только не реально полезным софтом. В частности, что мешало Nokia устанавливать на свои смартфоны бизнес-серии программу, фильтрующую вызовы с определенных номеров? Предлагаю восполнить этот пробел финского гиганта прямо сейчас! ФункционалДумаю, с инструментарием разработки под Symbian ты уже знаком по нашим предыдущим статьям, посвященным этой теме (если нет, то отложи этот номер и ознакомься с сентябрьским и декабрьским «Кодингом» за прошлый год). В своих экспериментах мы традиционно ориентируемся на смартфоны Series60. Если ты хочешь писать под новое поколение смартов на Symbian 9.X, то рекомендую поставить соответствующий SDK. Функционал у нас будет следующий: приложение должно блокировать входящие sms/mms, а также звонки с определенных номеров. Начинаем кодитьПриступим к реализации. Очевидно, что наше приложение должно иметь модульную структуру, иначе код превратится в кровавое месиво, что часто случается с плохо спроектированными проектами под Symbian. Не забывай, что C++ сам по себе благоприятствует созданию неудобоваримого для понимания кода, а в совокупности с извращенностью Symbian-парадигм в умелых руках это дает поразительной кривизны результат :). Поэтому предлагаю разбить функционал на модули (смотри рисунок «Структура приложения»). Посмотрим, что должен делать каждый из модулей по отдельности: Messaging module:
Telephony module:
Address Book module:
BlackList Engine module
Обрати внимание на то, что модули Messaging module и Telephony module по большому счету являются универсальными и могут быть задействованы практически в любом приложении, использующем возможности телефонии смартфона. Выбор способа хранения телефонов неудачников я предоставлю тебе - можно использовать встроенные классы для хранения данных, XML или вообще держать их в текстовом файле. Для ясности изложения будем рассматривать последний вариант, а заодно и разберем работу со строковыми дескрипторами. Посмотрим на реализацию каждого модуля в отдельности. Messaging engineЭтот модуль будет состоять из одного класса, являющегося так называемым классом Active Object. Active Objects введены в Symbian для поддержки асинхронного выполнения. В частности, подобные объекты используются в тех случаях, когда их работа связана с выполнением продолжительных операций, во время которых работа приложения в целом не должна останавливаться. Кроме того, класс CMessagingEngine будет унаследован от MMsvSessionObserver. Вообще в Symbian довольно много observer-классов, использующихся для отлова определенных событий (как в нашем случае). Обычно используется конструкция вида: void HandleSomething(…){ switch(aEvent) { … //ловим события } } В этом примере мы отлавливаем создание новой sms’ки в папке «Входящие», определяем номер, с которого она пришла, и удаляем ее, если этот номер присутствует в базе лузеров. Полный код класса CMessagingEngine ты можешь найти на диске. Создавать экземпляр класса нужно в конструкторе класса AppUi нашего приложения следующим образом: iMessagingEngine = CMessagingEngine::NewL(); Здесь мы вызываем стандартный симбиановский конструктор, о котором тебе стоит почитать в SDK (раздел Leaving Functions). После создания объекта наше приложение в состоянии отлавливать события входящих sms и mms. Остается решить вопрос их обработки. Для этого в методе HandleSessionEventL(…) класса мы отловим событие EMsvEntriesCreated, которое возникает при создании нового сообщения (причем неважно где: в папке «Входящие», «Черновики» и т.д.). HandleSessionEvent имеет 4 параметра: первый служит для обозначения события и имеет тип TMsvSessionEvent, а три других бестиповые (TAny*) и служат для передачи служебной информации. В частности, для события EMsvEntriesCreated один из этих параметров содержит ID директории, где была создана новая sms/mms, а другой является указателем на множество вновь созданных сообщений. Код отлова сообщения выглядит следующим образом: Код отлова и удаления непотребных sms void CMtmsExampleEngine::HandleSessionEventL(TMsvSessionEvent aEvent, TAny* aArg1, TAny* aArg2, TAny* aArg3) { switch (aEvent) { case EMsvEntriesCreated: //проверяем, что сообщение создано в папке «Входящие» if(*static_cast<TMsvId*>(aArg2)==KMsvGlobalInBoxIndexEntryId) { //инициализируем массив вновь созданных sms CMsvEntrySelection* entries = static_cast<CMsvEntrySelection*>(aArg1); //получаем ID первой созданной sms из массива (проще говоря, айди новой sms) TMsvId iNewMessageId = entries->At(0); TMsvId owningServiceId; //объект sms’ки TMsvEntry messageEntry; iSession->GetEntry((*entries)[entries->Count()-1],owningServiceId,messageEntry); TMsvId iSmsId = messageEntry.Id(); iSmsMtm->SwitchCurrentEntryL(iSmsId); //строковый дескриптор, куда записывается номер отправителя сообщения TBuf<20> senderNumber; senderNumber.Append(iSmsMtm->Entry().Entry().iDetails ); //если номер есть в базе, удаляем if(iBLEngine.CheckNumber(senderNumber) DeleteMessageL(iSmsId); } } } В приведенном коде задействован метод CheckNumber(TDesC& aNumber) класса CBLEngine, который мы рассмотрим позже. С остальным кодом ты можешь ознакомиться в проекте на диске. Telephony engineПринцип работы этого модуля в точности повторяет принцип работы предыдущего, разница лишь в классах и в том, что для создания и инициализации движка требуется несколько больше телодвижений. Код его я бы посоветовал попробовать сочинить самому, а если не получится – мой исходник всегда к твоим услугам :). Реализация движка blacklistПоскольку с вопросами отлова события и их блокирования мы определились, приступим к созданию основного функционала, который прост как 3 копейки: нам нужно научиться формировать список отстойных номеров и делать к нему интерфейс, позволяющий проверять, есть ли конкретный номер в этом списке. Поскольку мы условились держать номера в обычном текстовом файле, то суть описанного выше сводится добавлению строк в этот файл и их поиску. Для работы с файловой системой в Symbian имеется класс RFile. Тем не менее процедура считывания данных из файла или записи в файл занимает, на мой взгляд, слишком много места, поэтому для дальнейших выкладок мы будем использовать файловый движок, который ты сможешь найти на DVD и который можно использовать в любых Series60-проектах. Рассмотрим функцию добавления данных в файл: void CFileEngine::AddData(TDesC &aFileName, TDesC &aData, TBool aNewLine) { //строковый heap-дескриптор HBufC* Data=HBufC::NewLC(this->GetFileSize(aFileName)+aData.Length()+3); if(this->FindFile(aFileName)) { this->ReadData(aFileName,Data->Des()); if(aNewLine) { Data->Des().operator +=(_L("\n")); } } else { Data->Des().Copy(_L("")); } Data->Des().operator +=(aData); this->WriteData(aFileName,Data->Des()); } Как видишь, параметрами функции являются имя файла, в который добавляем данные, строковый дескриптор, содержащий строку данных, и флаг, показывающий, фигачить ли данные в конец файла или добавлять перед этим символ переноса строки. Особый интерес представляет в этом коде дескриптор Data, являющийся структурой, содержащей размер данных и ссылку на них. В начале выполнения функции мы выделяем под данные суммарный размер уже хранящихся данных в файле БД и данных к добавлению. После этого мы считываем в него данные из файла базы, добавляем к ним записываемый номер и загоняем все это дело обратно в файл БД. Механизм, конечно, кривенький, зато простой и эффективный. При этом в методе WriteData того же файлового движка выполняются манипуляции по поиску файла, его открытию, назначения соответствующих флагов и т.д. Для формирования собственно БД можно использовать 2 метода: задание номера вручную и с помощью доступа к адресной книге смартфона. Второй способ предпочтительнее, поскольку он более user-friendly. Что приятно, в Symbian есть встроенный механизм доступа к движку адресной книги. В частности, с помощью класса CPbkContactEngine ты можешь получить доступ к записям, редактировать их и добавлять новые. В нашем случае нас интересует показ диалога со списком контактов для предоставления пользователю возможности выбора необходимого контакта. После выбора нужно занести номер контакта в черный список. Соответствующий код несколько нетривиален и с первого взгляда сложен для понимания. Полностью и ясно откомментированный сорец, конечно же, я тебе подготовил :). После создания базы данных контактов наше приложение будет работать в фоновом режиме и ожидать поступления входящих вызовов и сообщений. При обнаружении таковых оно получит либо номер отправителя, либо номер звонящего. После этого вызывается упомянутый выше метод CheckNumber(TDesC& aNumber) класса CBLEngine. В нем мы делаем следующий финт ушами: записываем содержимое файла БД в один строковый дескриптор, после чего ищем в нем подстроку aNumber. Если строка найдена, значит номер занесен в черный список. В этом случае мы его блокируем. Поиск подстроки выполняется следующим образом: TBuf<50> iF(_L("C:\\lusers_bd")); HBufC* fileData=HBufC::NewL(iFileEngine->GetFileSize(iF)); iFileEngine->ReadData(iF,fileData->Des()); if(fileData->Des().Find(aNumber)!=KErrNotFound) return ETrue; Здесь мы инициализировали строковый дескриптор iF именем файла базы данных, после чего создали строковый heap-дескриптор, в который записали содержимое этого файла. Метод Find дескрипторов используется для поиска подстроки и возвращает позицию, в которой подстрока встречается в базе, либо KErrNotFound (что есть ноль), если подстроки нет. Вот и все, программа готова! ЗаключениеПосле прочтения этой статьи ты наверняка вставишь DVD в дисковод, откроешь сорцы, заваришь чайку и погрузишься в состояние глубокого внутреннего спокойствия. Еще бы! Ведь теперь ты сможешь мирно спать каждую ночь, и твой чуткий сон больше не потревожат звонки оголтелых фанаток! Ибо все лишние леди у нас теперь в черном! Сегодня мы еще раз убедились, что смартфон – это настоящий компьютер и писать для него одни лишь тетрисы и змейки – редкое расточительство. Мы – за настоящий, юзабельный софт! Вроде этой программки. Массу полезной информации можно найти на forum.nokia.com. |