вторник, 19 октября 2010 г.

Интеграция Silverlight в Qt приложения

Хотелось бы опубликовать статью на русском языке об интеграции роликов Silverlight в Qt приложения.
Для этого используется библиотека Qtitan, она коммерческая, увы, но может быть кого-то заинтересует. К тому же ее вполне можно использовать в демо-режиме в некоммерческих целях.
Статья публикуется с негласного соглашения авторов фреймворка Qtitan Multimedia.

Интеграция Silverlight в Qt приложения


Разработчики, которым довелось разрабатывать приложения на фреймворке Qt, любят его за удобство и простоту использования, и за мощность которая скрывается под капотом технологии.

Мы тоже любим Qt, и мы захотели расширить наш любимый фреймворк возможностью работы с современной векторной графикой, например, мы захотели интегрировать в приложение ролики Adobe Flash или Microsoft Silverlight.

среда, 13 октября 2010 г.

QScintilla: определяем styleText

Вот мы и добрались до самой важной функции лексера void styleText(int start, int end)

Займемся ее реализацией.

void LexerZ::styleText(int start, int end)
{
  //editor() - функция из родительского класса.
  //Возвращает указатель на QsciScintilla
  if (!editor())
   return;

  //получить исходник
  //выделим кусок памяти
  // +1 - под \0
  char * chars = new char[end - start + 1];
  //заполним кусок обратившись сцинтилле
  editor()->SendScintilla(QsciScintilla::SCI_GETTEXTRANGE,
              start, end, chars);
  //преобразуем в строку
  QString source(chars);
  delete [] chars;
 
  //Оптимизация - кэшируем уже ранее отрисованную строку
  if (source == cache)
   return;
  else
   cache = source;
  //Не работаем с пустой строкой
  if (source.isEmpty())
   return;
  //вызываем функцию отрисовки комментария
  //передаем исходную строку, начало и конец
  commentStyling(source, start, end);
}

* This source code was highlighted with Source Code Highlighter.


В комментариях описано, что мы делаем в этой функции. Здесь мы получаем строку, которую требуется отрисовать (это может быть не только текущая строка, а в том числе и соседние строки, сцинтилла в этом плане довольно умна).
Дальше вызываем функцию commentStyling(source, start, end);

Ее реализация приведена ниже:

понедельник, 11 октября 2010 г.

QScintilla: раскрашиваем комментарии

Сегодня мы будем раскрашивать комментарии для некого гипотетического языка Z.
Допустим в этом языке комментарии строк принято обозначать символом **, аналогично // в C++. Давайте предположим, что цвет шрифта комментария - синий, цвет фона - светло-зеленый, а сам шрифт - Comic Sans.
Начнем изменения в нашем коде Lexer.cpp и Lexer.h. (Можно переименовать эти файлы в LexerZ.h и LexerZ.cpp).

пятница, 8 октября 2010 г.

QScintilla: пишем Lexer

Перед дальнейшим написанием лексера нужно уяснить парадигму паттерна "Виртуальный метод", который использует QScintilla.
Лескер состоит из набора виртуальный функций, которые нужно переопределить. Класс QsciScintilla при необходимости вызывает виртуальные методы базового класса, однако в соответствии с полиморфизмом, будут вызываться методы нашего лексера.
Вот перечень виртуальных функций, необходимых для переопределения:
  //! Возвращает название языка
  virtual const char * language() const;
  //! Цвета для стилей
  virtual QColor defaultColor(int style) const;
  //! Шрифты для стилей
  virtual QFont defaultFont(int style) const;
  //! Бакгроунд для стилей
  virtual QColor defaultPaper(int style) const;
  //! Разбор текста на стили
  virtual void styleText (int start, int end);
  //! Описание
  virtual QString description (int style) const;

* This source code was highlighted with Source Code Highlighter.
Переменная style - это просто идентификатор стиля. Мы сами ее определяем. Для этого в лексере мы определяем некий enum для наших стилей.
  enum
  {
   Default = 0,
   Comment = 1,
   Binary  = 2,
   String  = 3,
   Keyword1 = 4,
   Keyword2 = 5,
   Keyword3 = 6,
   Keyword4 = 7,
   Const  = 8
  };

* This source code was highlighted with Source Code Highlighter.
Имена стилей и их числовые обозначения значения не имеют, далее станет ясно почему.
Самой важной виртуальной функцией является   virtual void styleText (int start, int end);

Именно она отвечает за отрисовку. К этой функции QScintilla обращается всякий раз, когда ей надо перерисовать кусок текста начиная с позиции start до позиции end.

четверг, 7 октября 2010 г.

QScintilla: создаем свой лексер

Как уже было сказано ранее, QScintilla поддерживает около 80 языков, раскрашивая каждый в соответствии с их синтаксисом. Т.е. для C++ комментарии задаются символом //, а для языка R символом #. Соответственно, раскрашивать в каждом случае надо по своему.

Трудно представить, что кроме этих 80 языков вам может понадобиться что-то свое, но тем не менее такая ситуация возникнуть может и надо знать как тут поступать.

Вообще, есть два способа добавить свой язык для раскраски.
Первый - это правка собственно исходного кода библиотеки qscintilla2.dll.
Достоинства этого метода - простота добавления. Достаточно взять за пример уже какой-нибудь существующий лексер, скопировать его в новый файл, исправить несколько строк, пересобрать библиотеку и, вуаля, новый лексер готов.
Недостаток этого метода очевиден - необходимость пересобирать библиотеку. Хотелось бы обойтись без этого.
Для этого в QScintilla есть путь истинного воина - наследование от QsciLexerCustom и самостоятельное написание алгоритмов раскраски. Это более сложный путь, на мой взгляд, но зато более правильный с концептуальной точки зрения, т.к. не нужно пересобирать библиотеку.

Итак, первое что мы должны сделать - это отнаследоваться от QsciLexerCustom:

#include <qsci/qscilexercustom.h>

class Lexer : public QsciLexerCustom
{
  Q_OBJECT
public:
  //! Конструктор
  Lexer(QObject * parent = 0);
  //! Деструктор
  virtual ~Lexer();
private:
  Lexer(const Lexer &);
  Lexer &operator=(const Lexer &);
};

* This source code was highlighted with Source Code Highlighter.
В следующих выпусках мы продолжим написание своего лексера.

QScintilla: создание редактора

Сейчас мы создадим экземпляр QScintilla и зададим ему некоторые свойства.
В качестве языка для подсветки будем использовать C++.

#include <Qsci/qsciscintilla.h>
#include <Qsci/QsciLexerCPP.h>

//Создаем редактор
QsciScintilla * textEdit = new QsciScintilla(this);

//Создаем и устанавливаем лексер для C++
QsciLexerCPP * lexCpp = new QsciLexerCPP(this);
textEdit->setLexer(lexCpp);

//Задаем формат ввода - без этого не будут вводиться русские буквы
textEdit->setUtf8(true);

//Текущая строка подсвечивается горизонтальной линией
textEdit->setCaretLineVisible(true);
//Цвет линии
textEdit->setCaretLineBackgroundColor(QColor("gainsboro"));

//Автоматический отступ
textEdit->setAutoIndent(true);
textEdit->setIndentationGuides(true);
//Заменять Tab на пробелы
textEdit->setIndentationsUseTabs(false);
//Ширина отступа - 4 пробела
textEdit->setIndentationWidth(4);

//Задаем символ конца строки
#if defined Q_WS_X11
textEdit->setEolMode(QsciScintilla::EolUnix);
#elif defined Q_WS_WIN
textEdit->setEolMode(QsciScintilla::EolWindows);
#elif defined Q_WS_MAC
textEdit->setEolMode(QsciScintilla::EolMac);
#endif

//Задаем цвет вертикальной полосы слева - там где ставятся breakpoints, закладки, номера строк
textEdit->setMarginsBackgroundColor(QColor("gainsboro"));
//Отображать номера строк
textEdit->setMarginLineNumbers(1, true);
//Ширина полосы - такая чтобы влезли символы до 1000
textEdit->setMarginWidth(1, QString("1000"));

//Автозавершение лексем
textEdit->setAutoCompletionSource(QsciScintilla::AcsAll);
textEdit->setAutoCompletionCaseSensitivity(true);
textEdit->setAutoCompletionReplaceWord(true);
textEdit->setAutoCompletionShowSingle(true);
textEdit->setAutoCompletionThreshold(2);

//Автоподсветка скобок
textEdit->setBraceMatching(QsciScintilla::SloppyBraceMatch);
//Цвет подсветки - желный с синим
textEdit->setMatchedBraceBackgroundColor(Qt::yellow);
textEdit->setUnmatchedBraceForegroundColor(Qt::blue);

//Слот на сигнал - "документ изменился"
connect(textEdit, SIGNAL(textChanged()),
 this, SLOT(documentWasModified()));
//Слот на сигнал - "курсор сместился"
connect(textEdit, SIGNAL(cursorPositionChanged(int, int)),
 this, SLOT(cursorMoved(int, int)));

* This source code was highlighted with Source Code Highlighter.
За дополнительной информацией по редактору можно обращаться к документации:
http://www.riverbankcomputing.co.uk/static/Docs/QScintilla2/classQsciScintilla.html

среда, 6 октября 2010 г.

QScintilla: собираем проект

Напишем pro файл для включения QScintilla в свой проект. Я не буду приводить весь файл, а укажу на что нужно обратить внимание.
  1. Конечно подключить библиотеку:
    unix:LIBS += -lqscintilla2
    win32:LIBS += qscintilla2.lib
  2. Определить переменные
    DEFINES += QSCINTILLA_DLL SCI_LEXER
    Здесь определены 2 переменные. Первая говорит, что мы используем экспорт из библиотеки qscintilla2. Вторая, что мы определяем свой лексер. Лексер - это объект, который отвечает за отрисовку текста. Соответственно, если в планах нет желания писать свой лексер (например, для какого-нибудь особо экзотического языка), то переменную SCI_LEXER определять не нужно.
Вот и все изменения в файле проекта.
В пределах видимости нужно расположить qscintilla2.dll и включить в INCLUDEPATH путь до заголовочных файлов QScintilla (например, QScintilla\Qt4\Qsci\).

После чего можно собирать проект.

QScintilla: установка

Живет QScintilla здесь:
http://www.riverbankcomputing.co.uk/software/qscintilla/intro
Периодически выходят обновления, на сегодняшний день последняя версия - 2.4.5.
QScintilla портирована как для Qt 3, так и для Qt 4. С третьей версией я не работаю и не знаю насколько там все хорошо, но для четверки все нормально, ничего допиливать не надо.

О лицензии.
RiverBank распространяет QScintilla под тремя лицензиями:
  • GNU General Public License v2
  • GNU General Public License v3
  • Riverbank Commercial License
К сожалению, GPL. GPL - несвободная лицензия с серьезным ограничением. Вы не сможете использовать QScintilla в коммерческих приложениях. Точнее вам придется тогда открыть весь исходный код вашего приложения. Вы также не сможете включать собранную библиотеку QScintilla в дистрибутив вашего приложения.
Но способ обойти ограничение есть.
Как обмануть GPL:
Например, можно на сайте сделать два линка: один на коммерческую программу , а второй — на GPL драйвер(библиотеку). Две независимые работы можно скачать по отдельности.
Вот такая неприятность.

Установка.
В сборке библиотеки нет ничего сложного - скачиваем, распаковываем, переходим в каталог QScintilla/Qt4 и запускаем qmake -tp vc. В итоге получаем проект qscintilla2.vcproj.
Собираем его.

Получаем в итоге qscintilla2.dll и qscintilla2.lib, которые будем использовать в программе.

В следующем выпуске начнем работу с Qscintilla.

QScintilla - продвинутый редактор для Qt

Открываю цикл статей о QScintilla. QScintilla это порт проекта Scintilla на Qt. Scintilla - это мощный кросплатформенный редактор текста.
В его возможности входит:
  • Подсветка исходного кода различных языков. Поддерживается 78 самых известных языков (Abaqus, Ada, APDL, Asm, Asn1, ASY, AU3, AVE, Baan, Bash, Basic, Bullant, Caml, CLW, Cmake, COBOL, Conf, CPP, Crontab, Csound, CSS, D, Eiffel, Erlang, EScript, Flagship, Forth, Fortran, GAP, Gen.py, Gui4Cli, Haskell, HTML, Inno, Kix, Lisp, Lout, Lua, Magik, Matlab, Metapost, MMIXAL, MPT, MSSQL, MySQL, Nimrod, Nsis, Opal, Others, Pascal, PB, Perl, PLM, POV, PowerPro, PowerShell, Progress, PS, Python, R, Rebol, Ruby, Scriptol, Smalltalk, SML, Sorcus, Specman, Spice, SQL, TACL, TADS3, TAL, TCL, TeX, VB, Verilog, VHDL, YAML)
  • Собственно редактирование кода и все, что с ним связано - отступы, cut/copy/paste, подсветка скобок и прочее. Возможностей - на полноценный текстовый редактор.
  • Есть возможность добавить собственный язык и задать для него собственную раскраску.
Среди русской части Интернета крайне мало информации о работе с QScintilla. В основном информацию приходилось собирать по форумам, вникать в документацию, которая, кстати без примеров.
В итоге я хочу в своем блоге выложить все, что мне известно о работе с QScintilla.

понедельник, 21 июня 2010 г.

вторник, 15 июня 2010 г.

Помещаем и извлекаем данные из PDF

Разобрался как работать с аттачами в PDF через PoDoFo (кстати он недавно обновился до 0.8.1).
Смысл в следующем - создается страница и к ней прикрепляется аннотация размером 0 на 0. Таких аннотаций на страницу может быть много. В качестве аннотации может быть текст, картинка или вложенный файл. Вот этим и воспользуемся.
Итак - сама страница будет отображать данные в красивом виде, таблички там и прочее. Это нам неинтересно на данном этапе. Сейчас важно научиться создавать вложенные файлы.

четверг, 3 июня 2010 г.

PDF в Qt

Столкнулся со следующей задачей.

Нужно хранить некие данные (текст + графика) достаточно большого объема (~50 Мб) в одном файле.
Желательно, чтобы это был общедоступный формат типа XML, но чтобы его мог просмотреть неискушенный юзер.
То есть, данные в документе еще должны быть и оформлены красивым образом.
На ум приходят:
1) XML + XSL. Данные храним в XML, для отображения используем файл стилей. Минус — нет сжатия данных, вместо одного файла получаем два.
2) OpenDocument. Не работал с ним.
3) XPS файл.
4) PDF.

четверг, 25 марта 2010 г.

DirectMapTableModel

От DirectMap легко породить модель для представления данных.
DirectMapTableModel - представляет простую таблицу из 2-х колонок.
В первой колонке - ключи мапа, во второй - значения.
Это нечто вроде QStringListModel, только с двумя колонками вместо одной.

понедельник, 1 марта 2010 г.

Qt баг

Под виндой с любой Qt, вплоть до последней, есть такой косяк с тулбаром.
Нужно: QMainWindow с тулбаром. Кнопка на тулбаре должна что-нибудь делать, желательно показывать некий диалог. Диалог должен закрываться по Esc.
Делаем: Быстро быстро клацаем по кнопке тулбара и тут же жмем Esc, закрывая тем самым диалог. На определенной итерации мы увидем, что кнопку тулбара залипла. Так, как будто у нее стоит статус Checkable.
Это легко проверить с Assistant'ом. Вызываем печать через тулбар и отменяем ее по Esc.
Итог: Весьма неприятно. На Qt и так полно нападок от нативщиков, и тут этот очевидный баг.

Rotated Label

Компонента "Вращаемая надпись" - QLabel, который может быть повернут на любой угол. Стандартными Qt выми средствами этого не сделать, поэтому нужно изобретать велосипед.

Сначала мой велосипед. Здесь используется отрисовка текста QPainter'ом.
И другой велосипед. От shapoclak.
Здесь товарищ подошел с более интересной стороны
В качестве девайса отрисовки виджета используется QPixmap. А сам QPixmap после любых манипуляций уже отрисовывается в любом другом месте. Подход хорош тем, что весь функционал QLabel остается.

суббота, 27 февраля 2010 г.

AbstractDateEdit - замена QDateEdit

QDateEdit - компонент для редактирования даты в Qt не умеет задавать пустую дату. Как это ни пародоксально. Пустая дата для него означает 01.01.2000.
Qt сообщество по этому поводу неоднократно сокрушалось и продолжало грызть кактус.
Результат моих грызений - собственный компонент AbstractDateEdit. Он обозван абстрактным, т.к. от него я планирую создать компоненты для работы с другими исчислениями даты. Но этот компонент ни фига не абстрактный а самый обычный, работающий с григорианским счислением. Имеет выпадающий календарик и прочие прелести.
Качать здесь .

Узнаем свой IP под Qt

В файле проекта подключаем QT += network
Используем.
#include <QNetworkInterface>

QString localIP()
{
  QString locIP;
  QList<QHostAddress> addr = QNetworkInterface::allAddresses();
  locIP = addr.first().toString();
  return locIP;
}

* This source code was highlighted with Source Code Highlighter.

пятница, 26 февраля 2010 г.

AuthUnit - модуль авторизации

Добавил простенький модуль авторизации на Qt.

Функции модуля:
Авторизация пользователя и добавление нового с разграничением прав.
Модуль использует QSQLite. Требуемые модули Qt : QtSql, QtXml.
Пароли хэшируются MD5 и хранятся в конфигурационном файле config.xml.
При отсутствии конф. файла или повреждении его структуры модуль не проводит авторизацию.

Скачать можно здесь.
В архиве файл проекта .pri. Там же есть пример использования.
Использовать крайне просто. Нужно прицепить файл проекта к своему проекту вписав в него строчку include(AuthUnit/AuthUnit.pri)
После этого использовать
  AuthUnit au;
 
if (au.exec() != QDialog::Accepted)
  {
   isAuthorized =
false;
   close();
   
return;
  }
 
else
  {
   isAuthorized =
true;
   UserLogon user = au.logon();
  }


* This source code was highlighted with Source Code Highlighter.
 Используется структура UserLogon.
struct UserLogon
{
  QString name;
  QString permission;
};

* This source code was highlighted with Source Code Highlighter.

DirectMap, оператор склейки

Расширим DirectMap оператором добавления.

template <class Key, class Value>
void DirectMap<Key, Value>::operator<<(const DirectMap<Key, Value> & right)
{
  foreach (QString key, right.keys())
  {
   append(key, right[key]);
  }
}


* This source code was highlighted with Source Code Highlighter.

четверг, 25 февраля 2010 г.

DirectMap - упорядоченный map

Недостаток ассоциативного массива QMap в том, что в нем ключи упорядочиваются. Т.е. если я добавлю в QMap" Key2", "Key1", то на выходе получу, сюрприз, "Key1", "Key2". Ключи будут отсортированы.
Такое поведение честно говоря подзае..ло, а альтернативы в Qt нету.
В качестве альтернативы они предлагают использовать QList<QPair<>>. Крайне громоздкая структура.
Наконец я решил сделать класс с удобством пользования как у QMap и с функционалом как у QList, т.е. без автоматического упорядочивания по ключам.

Начнем пожалуй.

среда, 24 февраля 2010 г.

Qt и zip архивирование

Архивирование в Qt представлено методами qCompress() qUnCompress(). Они сожмут/разожмут некую последовательность байт, но обо всем остальном (запись в файл, сохранение структуры директорий, запись комментария и проч.) придется позаботиться тебе.
Поэтому для Qt написано много пакетов для работы с архивами.
Я лично предпочитаю The OSDaB Project.
http://code.google.com/p/osdab/source/browse/trunk/OSDaB-Zip
Meet a couple of pure-Qt/C++ classes capable of handling PKZIP 2.0 compatible zip archives.

Чтобы им пользоваться Qt должна быть собрана с поддержкой zlib. Само использование крайне легко, в примерах все описано.
Я же расскажу о неком досадном баге в этом пакете.
Я не буду углубляться в формат zip файла, он довольно сложен, главное вот это
Файл ZIP определяется наличием центрального каталога, который расположен в конце файла. В каталоге хранится список имен записей (файлов или каталогов), которые хранятся в файле ZIP, наряду с другими метаданными о входе и смещении в файле ZIP, указывающими на фактическое расположение сжатых данных.
Проблема в том, что OSDaB считает что центральный каталог начинается начинается с минус 22 байта от конца архива. Т.е. он отступает 22 байта от конца файла архива и ищет там начало секции Центрального Каталога. Секция ЦК начинается с байт PK56. Так вот если он ее там не найдет, то начинается двигаться в начало файла но с шагом кратным 22. Для большинства архивов это работает. По ходу работы пришлось столкнуться с архивами у которых ЦК начинается вовсле не с -22 байта, а с 18 или 21. Поэтому проект пришлось подправить чтобы начинал поиск секции ЦК с 18 и шагал по 1 байту в начало. Это понижает скорость работы архиватора, но по крайней мере нужные мне архивы стали нормально читаться.

Правки, которые пришлось сделать, располагаются в файле  unzip.cpp
Магический параметр UNZIP_EOCD_SIZE установлен равным 18
В функции
UnZip::ErrorCode UnzipPrivate::seekToCentralDirectory()
закоментарена строчка
//offset -= UNZIP_EOCD_SIZE;
чтобы шаг был 1 байт. Кроме того
offset -= 1 /*UNZIP_EOCD_SIZE*/;
И ввел дополнительную проверку
if (offset > length)
   offset = getSULong((
const unsigned char*) buffer1, UNZIP_EOCD_OFF_CDOFF + 4);
Определив функцию getSULong
quint32 UnzipPrivate::getSULong(const unsigned char* data, quint32 offset) const
{
   quint32 res = (quint32) data[offset];
   res |= (((quint32)data[offset+1]) << 8);
  
return res;
}


* This source code was highlighted with Source Code Highlighter.

суббота, 20 февраля 2010 г.

Цытата

Плакал от цитаты. Насколько верно....
Eсли кучку программистов оставить на некоторое время без присмотра (читай без управления), они очень быстро скатываются в производство не продукта, а прикольных фиговин для самих себя. Как правило, эти фиговины остальным людям не интересны, поэтому программистам перестают давать за них деньги. 
Прикольные фиговины для себя - моя слабость ))

вторник, 16 февраля 2010 г.

Криптография под Qt выпуск 4

Набор библиотек для переноса QCA:
plugins\crypto\qca-ossl2.dll          196 Kb
qca2.dll                                        749 Kb
libeay32.dll                                   1 Mb
ssleay32.dll                                   200 Kb

Получается довесок в пару мегабайт за возможность шифровать/дешифровать.

Криптография под Qt выпуск 3

Криптография с симметричным ключом вполне заработала. Ширфемся по алгоритму AES.

Чтобы расшифроваться нужно у чипера  поменять QCA::Encode на QCA::Decode.
 
QString encrypt(const QString & strToEnrypt)
{
  //инициализация
  QCA::Initializer init;
  //переводим строку в засекречивамый массив
  QCA::SecureArray arg = QVariant(strToEnrypt).toByteArray();
 
  QString res;
  // проверка что поддерживаем алгоритм AES128 testing
  if (QCA::isSupported("aes128-cbc-pkcs7"))
  {
   QString sKey = "aes128-cbc-pkcs7-sn93-sh21-jks-12";
   //некоторые преобразования над ключом
   //по идее должны затруднять реверсинг
   QStringList lst = sKey.split("-");
   lst = lst.replaceInStrings("c", "s");
   lst.removeAt(0);
   lst.removeAt(5);
   QString sKeyCh = lst.join(".");
   //создаем ключ
   QCA::SymmetricKey key(QVariant(lst.join(".")).toByteArray());
  
   //Создать случайный инициализирующий вектор
   //вам нужно это значение чтобы расшифровать результат шифра
   //но это нужно хранить в секрете в отличие от ключа
   QCA::InitializationVector iv(QVariant(lst.join(">")).toByteArray());

   //содать 128 битный AES шифр объект используя CBC режим
   QCA::Cipher cipher(QString("aes128"), QCA::Cipher::CBC,
      //использовать отступ по умолчанию который эквивалентен PKCS7 для CBC
              QCA::Cipher::DefaultPadding,
              // этот объект будет зашифрован
              QCA::Encode,
              key, iv);

   QCA::SecureArray u = cipher.update(arg);
   if (!cipher.ok())
   {
     return res;
   }

   QCA::SecureArray f = cipher.final();
   if (!cipher.ok())
   {
     return res;
   }
   res = QString(f.data());
  }
 
  return res;
}

* This source code was highlighted with Source Code Highlighter.

В следующем выпуске рассмотрим какие библиотеки нужны для переноса OpenSSL на чистую машину.

понедельник, 15 февраля 2010 г.

Криптография под Qt выпуск 2

Как оказалось, чтобы собрать нормальную дебаг версию надо было указать ключ --debug-and-release для configure.exe.
Но это еще не все.
По умолчанию QCA поддерживает только алгоритмы SHA1 и MD5. Чтобы получить остальное нужно ставить дополнительный плагин qca-ossl-2.0.0-beta3.tar.bz2.
Установка его дает нам такой комбайн поддерживаемых алгоритмов: TLS, CMS, X.509, RSA, DSA, Diffie-Hellman, PKCS#7, PKCS#12, SHA0, SHA1, SHA224, SHA256, SHA384, SHA512, MD2, MD4, MD5, RIPEMD160, Blowfish, DES, 3DES, AES128, AES192, AES256, CAST5, HMAC(SHA1, MD5, RIPEMD160), PBKDF1(MD2, SHA1), PBKDF2(SHA1).
Этот плагин в свою очередь зависит от OpenSSL версии не ниже 0.9.6
Взять его можно здесь openssl-0.9.8l
OpenSSL — криптографический пакет с открытым исходным кодом для работы с SSL/TLS. Позволяет создавать ключи RSA, DH, DSA и сертификаты X.509, подписывать их, формировать CSR и CRT. Также имеется возможность шифрования данных и тестирования SSL/TLS соединений.
OpenSSL поддерживает разные алгоритмы шифрования и хеширования:
Симметричные
Blowfish, Camellia, DES, RC2, RC4, RC5, IDEA, AES, ГОСТ 28147-89
Хеш-функции
MD5, MD2, SHA, MDC-2, ГОСТ Р 34.11-94
Асимметричные
RSA, DSA, Diffie-Hellman key exchange, ГОСТ Р 34.10-2001, ГОСТ Р 34.10-94
Поддержка алгоритмов ГОСТ появилась в версии 1.0.0, в данный момент находящейся в стадии бета-тестирования.
Чтобы собрать OpenSSL нужен Perl. Пришлось поставить Strawberry Perl.

После чего конфигуряется командой
> perl Configure VC-WIN32 --prefix=c:/some/openssl/dir
Затем
> ms\do_ms
И
 > nmake -f ms\ntdll.mak
> nmake -f ms\ntdll.mak install
После установки OpenSSL нужно сплясать еще один танец с бубном. Это касатся уже установки плагина qca-ossl.
В Папке %QTDIR% \mkspecs\features нужно создать файл winlocal.prf со строчкой WINLOCAL_PREFIX = D:/OpenSSL
То есть, указать путь до собранной OpenSSL.

В ходе сборки плагина у меня выскакивала ошибка, что де не найден OBJ_whirlpool. Эта переменная определена в OpenSSL, и управляет включением алгоритма Whirlpool — криптографическая хеш-функция.
Однако плагин в упор не видит исходного кода алгоритма и вылетает. Пришлось в коде плагина убрать упоминания о Whirlpool.
В итоге плагин qca-ossl2.dll все таки собрался.
Копируем его в %QTDIR%\plugins\crypto.
Завтра будем пробовать. По крайней мере if(QCA::isSupported("aes128-cbc-pkcs7")) стало возвращать true.

Конвертация QString в LPTSTR

LPTSTR это 32-битный указатель на Unicode строку.
Возвращать указатель из функции не совсем корректно и, например, у нас на фирме запрещено писать функции, возвращающие указатели. Т.к. программист в такой функции выделяет память и возвращает указатель на нее. А об удалении должен заботиться прикладной программист, а он может легко это забыть и получим утечку ресурсов.
Поэтому я приведу кусок кода, конвертирующий QString в LPTSTR без этих тонкостей. Кому надо напишут нужную функцию.
  QString str = "String";

  WCHAR * wStr = new WCHAR[str.length() + 1];
  MultiByteToWideChar( 0, 0, str.toAscii().data(), str.length(), wStr, str.length());
  wStr[str.length()] = '\0';
  LPTSTR lpszStr = wStr;
  delete [] wStr;
  wStr = 0;

* This source code was highlighted with Source Code Highlighter.

пятница, 12 февраля 2010 г.

Криптография под Qt выпуск 1

В стандартной поставке Qt совсем немного средств для криптографии. По сути все ограничивается одним классом QCryptographicHash.

Этот класс может получать хэш от некоторых данных, представленных в QByteArray, используя алгоритмы Md4, Md5, Sha.
Ну а что такое хэш? Хэш это необратимая последовательность символов, зная которую исходные данные никак не получить. Это удобно для хранения паролей. Хранишь хэш пароля и сравниваешь хэши паролей вместо них самих.
Однако в ряде случаев этого бывает недостаточно. И хочется иметь нормальное обратимое шифрование с ключами.

Порыскал в инете на эту тему, нашел пакет Qt Cryptographic Architecture (QCA).
Обитает он на:
http://delta.affinix.com/qca/
Скачиваем, разворачиваем в %QTDIR%/src/3rdparty/
Далее, configure.exe & nmake & installwin.exe
В результате оно установит Qt feature, т.е. фишку Qt.
Теперь в файле проекта можно написать CONFIG += crypto
И использовать в коде #include <QtCrypto>

На этом пока мое знакомство с QCA закончилось. В дебаге оно почему то не может найти либу qcad.lib, в релизе собирается нормально. Будем разбираться дальше.

Upd:
Для дебага пришлось поправить строку в %QTDIR%/mkspecs/features/crypto.prf
Вместо        windows:LINKAGE = -lqcad
написал         windows:LINKAGE = -lqca2

Поднимаем привилегии до админа под Windows XP

Понадобилось мне как-то сделать так, чтобы моя программа, будучи запущенной под ограниченным пользователем,  могла сама себе поднимать привилегии до администраторских.

Исходные условия: Windows XP, известные логин и пароль админа.
Цель: Научить программу поднимать себе привилегии.

Все оказалось достаточно просто:
#include "windows.h"

//Поднять права до админских
bool logonAsAdmin()
{
  LPTSTR lpszUsername = L"Admin"; // логин админа
  LPTSTR lpszDomain = NULL;  // домен
  LPTSTR lpszPassword = L"passw"; // пароль админа
  HANDLE hToken;  //маркер доступа (токен)

  //Получаем токен админа
  if (!LogonUser(lpszUsername, lpszDomain, lpszPassword, LOGON32_LOGON_INTERACTIVE,
         LOGON32_PROVIDER_DEFAULT, &hToken))
   return false;

  //Прикидываемся админом
  if (!ImpersonateLoggedOnUser(hToken))
   return false;

  return true;
}

* This source code was highlighted with Source Code Highlighter.
Но тут надо помнить о дыре, которую дает данный код. Дело в том, что в программе может быть код, дающий пользователю доступ к файловой системе с правами админа. Например, если в вашей программе есть диалог открытия файла и вы дали своей программе админский доступ, то такой доступ получит и пользователь программы.
Поэтому, перед тем как вызвать диалог открытия/сохранения файла - верните программе юзерский уровень и поднимите его обратно после работы с диалогом.
Или давайте админский уровень своей программе только на очень короткий срок и немедленно возвращайте его обратно, как только надобность в нем отпадет.

среда, 27 января 2010 г.

Продолжаем удалять файлы и каталоги

Функция рекурсивного удаления папок не работает если в папке есть файлы только для чтения.
Функция QFile::remove() при попытке удалить такой файл возвращает false. Чтобы этого избежать нужно выставлять права для таких файлов. В Qt для этого есть функция
bool QFile::setPermissions ( const QString & fileNamePermissions permissions )   [static]
Таким образом окончательный вид функции удаления каталога имеет вид:

четверг, 21 января 2010 г.

Очистка каталога

void cleanFolder(const QString & folder)
{
  QDir dir(folder);
  //Получаем список файлов 
  QStringList lstFiles = dir.entryList(QDir::Files);

  //Удаляем файлы
  foreach (QString entry, lstFiles)
  {
   QString entryAbsPath = dir.absolutePath() + "/" + entry;
   QFile::remove(entryAbsPath);
  }
}


* This source code was highlighted with Source Code Highlighter.

вторник, 19 января 2010 г.

Подсветка кода для блога

Изначально я планировал в этом блоге писать о программировании на Qt. Однако сейчас на блоге больше всего постов о покере, Москве, пиве и прочем непотребстве.
Объясняю в чем секрет - отсутствие толковой подсветки кода. Те куски кода, что я помещал в блог имели столь ужасное форматирование и подсветку, что без содрогания читать программу просто невозможно. Я перепробовал множество подсветчиков, однако у всех были недостатки - кто-то по  глупому делает подсветку, кто-то мутит с CSS, что вызывает трудности со вставкой кода.

Наконец я нашел приличный подсветчик: http://source.virtser.net
Он не использует CSS и делает подсветку аналогично Visual Studio.

Вот пример сгенеренного им кода.

ApPhotoRobot::ApPhotoRobot(QWidget * parent )
: DirSelecter(tr("Applications - PhotoRobot"), parent)
{
  lstlblPath.at(0)->setText(tr("Folder of Bars PhotoRobot"));
  lstlblName.at(0)->setText(tr("Executable name of Bars PhotoRobot"));
 
  leOut = new QLineEdit(this);

  QLabel * lblOut = new QLabel(tr("Output folder for Bars Photorobot"), this);

  QToolButton * btnBars = new QToolButton(this);
  btnBars->setFixedSize(20, 20);
  btnBars->setText("...");
  connect(btnBars, SIGNAL(clicked()), this, SLOT(browseDir()));

  mapBtnEdt[btnBars] = leOut;
  grid->addItem(new QSpacerItem(1, 10, QSizePolicy::Fixed, QSizePolicy::Fixed), 5, 0); 
  grid->addWidget(lblOut, 6, 0, 1, 2);
  grid->addWidget(leOut, 7, 0);
  grid->addWidget(btnBars, 7, 1);
 
  grid->setRowStretch(8, 1);

}

* This source code was highlighted with Source Code Highlighter.

На мой взгляд - лучшее решение для вставки кода в блог.