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

Установка ownCloud под Cubian

Недавно я приобрел Cubietruck для использования в качестве маленького домашнего сервера. В качестве ОС был взят вариант Debian для плат Cubieboard, Cubian, поскольку на своем основном компьютере я использую Debian уже давно. Дистрибутив был скачан и установлен на оказавшуюся под рукой карту micro-SD на 4 Гб - все инструкции есть на сайте Cubian.

Одна тонкость - в варианте "Desktop", который я поставил (предполагая, что иногда я буду использовать Cubietruck и в качестве медиаплеера), не поднимался сетевой интерфейс Loopback (lo). Ethernet (eth0) есть, IP по DHCP получил, SSH работает, а 127.0.0.1 не пингуется, в связи с чем наблюдались разнообразные глюки при установке, например, MySQL. Поиск в Интернете выявил виновного почти сразу - пакет gnome-network-manager. Виновник был немедленно удален, а lo  прописан в /etc/networking/interfaces.

Сервер SSH был перенастроен на стандартный порт, 22. На порту 63000, установленном по умолчанию в Cubian, у меня почему-то не работал SFTP (именно он, копирование по команде scp происходило без каких либо проблем).

Установка торрент-клиента Transmission описана, например, здесь (у меня он стоял еще на Raspberry Pi, где он настраивается аналогично).

Переходим к установке ownCloud (7.0 community edition). Я использовал веб-сервер lighttpd и базу данных MySQL и частично ориентировался по двум руководствам, по установке ownCloud 6 на Debian 7 wheezy (с apache и MySQL) и по установке ownCloud с postreSQL и lighttpd. Из первого я целиком взял все шаги по настройке MySQL, из второго - шаги по конфигурированию lighttpd, в том числе для использования SSL.

Маленький домашний сервер, разумеется, обитает за маршрутизатором с NAT, а доступ к облаку хотелось получить и и извне, причем, разумеется, по HTTPS. Также захотелось попробовать подключить в качестве внешнего носителя Google Drive (в руководстве по ownCloud подробно написано, как это сделать). Доменное имя, полученное через no-ip (раньше был DynDNS, но он умер стал платным), у маршрутизатора есть, порты 80 и 443 проброшены. Тем не менее, возникла проблема - если при доступе к основному сайту на lighttpd все в порядке, то при добавлении к URL /owncloud доменное имя при загрузке страницы меняется на имя машинки с Cubian во внутренней сети (получается https://cubian/owncloud/) и при доступе "снаружи" имя "cubian", разумеется, не резолвится. Как выяснилось, нужно поправить файл конфигурации ownCloud, config/config.php, и вписать в секцию $CONFIG параметры для домена, по которому осуществляется доступ извне:

'trusted_domains' => 
array (
  0 => 'cubian',
  1 => 'a1exanderz.noip.me',
),
'owerwritehost' => a1exanderz.noip.me,

Здесь "cubian" - это имя компьютера в локальной сети, а "a1exanderz.noip.me" -доменное имя, полученное через no-ip.

четверг, 6 ноября 2014 г.

SQLCipher и Qt - часть третья, Android

С SQLCipher под Android все намного проще - свободно распространяются не только исходники, но и собранные бинарные файлы Community Edition. Осваивать кросс-платформенную компиляцию для Android мне не хотелось, поэтому я взял бинарные файлы. К тому же сборку плагина qt-sqlcipher c их использованием делали до меня.

Добавляем в файл qsqlcipher.pro пути к библиотекам скачанной бинарной сборки:

android {
    equals(ANDROID_TARGET_ARCH, x86) {
        LIBS += -L$$PWD/../../../../sqlcipher-for-android-v3.2.0/libs/x86
    }
    equals(ANDROID_TARGET_ARCH, armeabi-v7a) {
        LIBS += -L$$PWD/../../../../sqlcipher-for-android-v3.2.0/libs/armeabi-v7a
    }
}

Добавляем условие для включения нужных библиотек - под Android нет libsqlciper и libcrypto, а используется одна  библиотека из готовой сборки:

!system-sqlite:!contains(LIBS, .*sqlite3.*) {
    CONFIG(release, debug|release):DEFINES *= NDEBUG
    DEFINES += $$SQLITE_DEFINES
    !contains(CONFIG, largefile):DEFINES += SQLITE_DISABLE_LFS
    INCLUDEPATH += $$OUT_PWD/include
    !android {
        LIBS += -L$$OUT_PWD/lib -lsqlcipher -lcrypto
    }
    else {
        LIBS += -L$$OUT_PWD/lib -lsqlcipher_android
    }
    QMAKE_RPATHDIR += $$OUT_PWD/lib
} else {
    LIBS *= $$QT_LFLAGS_SQLITE
    QMAKE_CXXFLAGS *= $$QT_CFLAGS_SQLITE
}

Нужен еще путь к заголовочному файлу sqlite3.h и сам файл. Я его просто взял из сборки для Linux (описанной раньше), INCLUDEPATH остался тот же.

Кроме копирования плагина в проект, использующий этот плагин, нужно добавить библиотеки для компоновки и деплоймента:

android {
    contains(ANDROID_TARGET_ARCH,armeabi-v7a) {
        LIBS += -L$$PWD/armeabi-v7a -lsqlcipher_android
        ANDROID_EXTRA_LIBS = $$PWD/armeabi-v7a/libstlport_shared.so \
            $$PWD/armeabi-v7a/libsqlcipher_android.so
    }
    contains(ANDROID_TARGET_ARCH,x86) {
        LIBS += -L$$PWD/x86 -lsqlcipher_android -lstlport_shared
        ANDROID_EXTRA_LIBS = $$PWD/x86/libstlport_shared.so \
            $$PWD/x86/libsqlcipher_android.so
    }
}

Здесь выяснилась одна интересная особенность - порядок указания библиотек в ANDROID_EXTRA_LIBS имеет значение! Если указать libstlport_shared.so второй, а не первой (как в приведенной выше ссылке), то под Android версий, более старых, чем 4.3 (я проверял на 4.0.3 и 4.2.2) при запуске проекта эта библиотека не находится (could not load library "libstlport_shared.so"), причем эта ошибка воспроизводится под обеими архитектурами. Если же libstlport_shared.so указана первой - все нормально работает под всеми версиями (от 4.0.3 до 4.4.2).

пятница, 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.