пятница, 31 октября 2014 г.

SQLCipher и Qt - часть вторая, Windows (MinGW)

Переходим к сборке плагина для базы данных в Windows, используя Qt 5.3.2 с 32-битным набором компиляторов MinGW 4.8.2. Никакого способа автоматической сборки проекта qt5-sqlcipher под Windows я не придумал, поскольку требуется запускать скрипт configure из MSYS - как это сделать из проекта Qt, я не знаю. Придется компилировать SQLCipher вручную. Для сборки его под Windows требуется много всего.

Устанавливаем готовую библиотеку OpenSSL отсюда (ссылка взята с сайта OpenSSL). Внимание - ставить нужно полную версию, а не Light, поскольку нам нужны заголовочные файлы и статическая библиотека для компоновки. При установке OpenSSL ругается по поводу отсутствия "Microsoft Visual C++ 2008 Redistributable". Ставим пакет отсюда, OpenSSL почему-то продолжает ругаться, но работает. В моем случае OpenSSL установилась в каталог C:\OpenSSL-Win32, пути дальше указаны для такого варианта.

Устанавливаем MSYS - я поставил 1.0.11 в варианте "все в одном". При установке указываем путь к уже имеющемуся MinGW (в составе Qt, в моем случае это C:\Qt\Tools\mingw482_32).

Устанавливаем Tcl/Tk - я поставил ActiveTcl Community Edition для x86.

Перезагружаемся, чтобы добавленные пути гарантированно присутствовали в переменной PATH.

Скачиваем qt5-sqlcipher и сам SQLCipher, если не установлен Git, можно взять ZIP-архивы. Структура каталогов такая же, как под Linux и Mac OS X - каталог с проектом qt5-sqlcipher, в нем подкаталог sqlcipher с исходниками SQLCipher.

Запускаем MSYS, переходим в каталог sqlcipher. Запускаем следующие команды:

make clean
./configure --enable-tempstore=yes --disable-tcl --with-crypto-lib=none CFLAGS="-DSQLITE_HAS_CODEC -I/c/OpenSSL-Win32/include" LDFLAGS="-L/c/OpenSSL-Win32/lib/MinGW" LIBS="-leay32"
make


Команда make clean необходима, только если были предыдущие попытки сборки. Ключ --with-crypto-lib=none для configure нужен, поскольку скрипт не находит OpenSSL, никак (похоже, он ищет libcrypto, а под Windows ее нет, есть libeay32). Результатом успешной работы make будет библиотека libsqlcipher в подкаталоге .libs (именно так, с точкой в начале) каталога sqlcipher. Копируем в этот подкаталог библиотеку libeay32.a из C:\OpenSSL-Win32\lib\MinGW.

Правим файл проекта qsqlcipher.pro. Комментируем строку с командой для сборки SQLCipher (мы его уже собрали вручную, а запуск make приведет к ошибке):

#sqlcipher.commands = $(MAKE) -C $$PWD/sqlcipher install

Добавляем:

win32 {
    INCLUDEPATH += $$PWD/sqlcipher
    LIBS -= -lcrypto
    LIBS += -L $$PWD/sqlcipher/.libs -leay32
}

После этого проект должен собраться. Как и в других системах, копируем готовый плагин qsqlcipher.dll из подкаталога plugins/sqldrivers в соответствующий подкаталог плагинов Qt и проверяем. Работает, как это ни удивительно.

SQLCipher и Qt - часть первая, Linux и Mac OS X

Появилась потребность (пока не срочная, но возможная) сделать шифрование базы данных SQLite в проекте, написанном с использованием Qt.

Вариантов шифрования баз SQLite немного, один из них - SQLCipher, имеющий открытый исходный код и свободно распространяемый в варианте Community Edition. Более того, имеется возможность скомпилировать его в виде плагина для баз данных в Qt 5 и для этого уже есть готовый проект: qt5-sqlcipher.

Проблема в том, что вышеупомянутый проект тестировался автором только на Mac OS X 10.7, а мне нужны плагины под Windows, Mac OS X и, возможно, для Android и Linux. Начать я решил с простого - Linux и Mac OS X.

На всех системах имеется Qt 5.3.2 с уже установленными исходниками qtbase, поэтому выкачивать еще раз их не нужно. Git под Mac OS X и Linux установлен. Сначала берем исходники самого qt5-sqlcipher (без ключа --recursive):

git clone https://github.com/sijk/qt5-sqlcipher.git

Переходим в каталог qt5-sqlcipher и скачиваем исходники самого SQLCipher:

git clone https://github.com/sqlcipher/sqlcipher.git

В каталоге проекта qt5-sqlcipher создаем файл .qmake.conf, указывающий путь к исходникам qtbase. В моем случае (под Linux) это была строчка

QT_SRCDIR=/home/az/Qt/5.3/Src/qtbase

Под Mac OS X 10.9 проект qt5-sqlcipher собрался без каких-либо правок - достаточно было просто открыть файл проекта в Qt Creator и запустить сборку. Библиотека OpenSSL, которая нужна для сборки SQLCipher, уже была. Готовый плагин (файл libqsqlcipher.dlyb, есть также отладочный вариант - libqsqlcipher_debug.dlyb) находится в подкаталоге plugins/sqldrivers каталога сборки проекта. Для проверки его работоспособности я просто скопировал плагин в подкаталог plugins/sqldrivers Qt. База данных с драйвером "QSQLCIPHER" работает.

Под Linux (Debian Testing) нужно поставить пакет libssl-dev (сама OpenSSL почти наверняка уже присутствует). Параметр LDFLAGS="-lcrypto" скрипту configure под Linux не понравился, что, в общем-то, ожидаемо - для этого нужно использовать LIBS. Поэтому в файл проекта qsqlcipher.pro была внесена следующая правка:

mac {
    SQLCIPHER_CONFIGURE = --enable-tempstore=yes \
                          --disable-tcl \
                          CFLAGS="-DSQLITE_HAS_CODEC" \
                          LDFLAGS="-lcrypto"
}

linux {
    SQLCIPHER_CONFIGURE = --enable-tempstore=yes \
                          --disable-tcl \
                          CFLAGS="-DSQLITE_HAS_CODEC" \
                          LIBS="-lcrypto"
}

Во-вторых, не находится заголовочный файл sqlite3.h. Для этого добавляем путь к нему:

linux {
    INCLUDEPATH += $$PWD/sqlcipher
}

После этого проект собрался "одним движением", то есть просто запуском сборки. Готовый плагин libqsqlcipher.so из подкаталога plugins/sqldrivers также был скопирован в соответствующий подкаталог плагинов Qt и проверен. Работает.

История же со сборкой под Windows куда более длинная и извилистая и будет рассказана в следующем посте.

вторник, 24 июня 2014 г.

OS X Mavericks и VirtualBox

После некоторых изысканий я все-таки смог установить OS X 10.9 Mavericks под VirtualBox.

Хост: Intel Core i5-3550, Debian GNU/Linux Testing (Jessie), 32-разрядный (i386, по историческим причинам), VirtualBox 4.3.12. Хакинтош: сборка Mavericks 10.9.0 от Niresh (была скачана мной с Рутрекера, но можно взять торрент-файл и с сайта Niresh).

Скачанный файл OSX-Mavericks.dmg представляет собой "сырой" образ диска, которой можно конвертировать в формат VirtualBox командой

 VBoxManage convertfromraw OSX-Mavericks.dmg OSX-Mavericks.vdi --format VDI

В Linux его также можно записать на флэшку командой dd, если захочется поставить Хакинтош на реальное железо, а не в виртуальную машину:

dd if=OSX-Mavericks.dmg of=/dev/XXX

Вместо XXX, разумеется, нужно подставить имя реального устройства - и не ошибиться!

В  VirtualBox создается новая виртуальная машина Mac OS X 10.9 Mavericks 64-bit (я оставил почти все параметры по умолчанию, лишь изменив доступное число ядер процессора с 1 на 2 и видеопамять на 128 Мбайт). После этого подключаем существующий образ, сконвертированный из DMG, в качестве загрузочного диска и создаем еще один, новый образ диска, на который будет ставиться система. Загружаемся с установочного образа, ставим систему на пустой диск (разметив его в процессе при помощи Disk Utility), после окончания установки отсоединяем установочный образ и загружаемся с основного диска. Все. В моем случае не потребовалось никаких ключей для запуска.

Дополнение о том, как получить полноэкранное разрешение (в моем случае - 1920 x 1080) в гостевой системе.

1. В консоли даем команду

vboxmanage setextradata "Mac OS X Mavericks" "CustomVideoMode1" "1920x1080x32"

"Mac OS X Mavericks" нужно заменить на имя созданной виртуальной машины, разрешение - на требуемое.

2. В Mac OS X в файл /Extra/org.chameleon.Boot.plist добавляем строки:

<key>Graphics Mode</key>
<string>1920x1080x32</string>

пятница, 16 мая 2014 г.

Qt под Mac OS X: ARC и Valgrind

Для того, чтобы включить Automatic Reference Counting (ARC) для исходников на Objective C / Objective C++, входящих в состав проекта на Qt, достаточно добавить в проекте (файл .pro) следующую строку:

QMAKE_OBJECTIVE_CFLAGS += -fobjc-arc

Встречал упоминание и

QMAKE_OBJECTIVE_CXXFLAGS += -fobjc-arc

(для исходников Objective C++), но в моем случае эта строчка ничего не дает, хотя файл и имеет расширение .mm.

Самый простой способ поставить Valgrind под Mac OS X, похоже, следующий:

  • Ставим Homebrew по инструкции с заглавной страницы сайта (одна команда в терминале)
  • В терминале набираем команду brew install valgrind
Этого оказалось достаточно, чтобы заполучить Valgrind в Mac OS X 10.9, но, как выяснилось, с моей программой он не работает - valgrind: Unrecognised instruction at address... Впрочем, официальной поддержки Mac OS X 10.9 в последнем Valgrind 3.9 нет.

среда, 14 мая 2014 г.

Bluetooth, Mac OS X и ошибки в примере

Потребовалось мне написать модуль для Mac OS X, который общается с одним устройством по Bluetooth, по протоколу RFCOMM. Задача не слишком сложная - устройству посылается команда и принимается ответ. И то, и другое - строки в UTF8. Основная программа кросс-платформенная и написана на С++ (используется Qt 5.2), но модуль для работы по Bluetooth, разумеется, пришлось писать на Objective C.

Документация есть (Bluetooth Device Access Guide), примеров кода - не отдельных строчек, а полноценных примеров, пусть и простейших - нет. После некоторых поисков выяснилось, что когда-то давно они поставлялись с XCode, но потом их почему-то исключили. К счастью, в ответах на вопрос дали ссылку на сохраненный архив примеров.

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

В примере RFCOMMClientSample, на который я ориентировался, есть метод sendData:

(BOOL)sendData:(void*)buffer length:(UInt32)length

Метод этот передает данные из буфера в устройство, подключенное по протоколу RFCOMM, при необходимости разбивая их на порции меньшего размера, если длина буфера больше MTU. Вызывается он в примере следующим образом:

[myBluetoothInterface sendData:(void*)[theString UTF8String] length:[theString length]];

Но метод length класса NSString (theString может быть объектом класса NSString или NSMutableString) возвращает число Юникод-символов в строке, а не число символов типа char, массив которых возвращает UTF8String! В результате при наличии в строке символов, представляемых в UTF8 более чем одним байтом, длина буфера, передаваемая в sendData, оказывается меньше реальной. Похоже, что пример этот со строками, содержащими что-то, отличное от латиницы, в Apple не проверяли... Вместо length нужно использовать lengthOfBytesUsingEncoding:NSUTF8StringEncoding:

[myBluetoothInterface sendData:(void*)[theString UTF8String] length:[theString lengthOfBytesUsingEncoding:NSUTF8StringEncoding]];

Вторая проблема - скорее не баг, а наследие чистого C. В методе sendData есть такая строчка:

buffer += numBytesToSend;

Арифметика указателей, давно я ее не встречал. Вот только современные компиляторы C++ применение такой операции к переменной типа void* считают ошибкой. Указателя на void требует метод writeSync класса IOBluetoothRFCOMMChannel, но преобразование типов лучше делать непосредственно при его вызове. Тогда sendData будет выглядеть так:

(BOOL)sendData:(const char*)buffer length:(UInt32)length

Его вызов больше не требует преобразования типа (UTF8String возвращает const char*):

[myBluetoothInterface sendData:[theString UTF8String] length:[theString lengthOfBytesUsingEncoding:NSUTF8StringEncoding]];

А вот вызов writeSync требует:

result = [mRFCOMMChannel writeSync:(void*)buffer length:numBytesToSend];

С такими исправлениями код, написанный на основе примера, можно спокойно использовать в качестве модуля в приложении, написанном на C++/Qt.