суббота, 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.
Но тут надо помнить о дыре, которую дает данный код. Дело в том, что в программе может быть код, дающий пользователю доступ к файловой системе с правами админа. Например, если в вашей программе есть диалог открытия файла и вы дали своей программе админский доступ, то такой доступ получит и пользователь программы.
Поэтому, перед тем как вызвать диалог открытия/сохранения файла - верните программе юзерский уровень и поднимите его обратно после работы с диалогом.
Или давайте админский уровень своей программе только на очень короткий срок и немедленно возвращайте его обратно, как только надобность в нем отпадет.