blob: 582dd69d01a86fea2df342b7d41d8cbe38010955 [file] [log] [blame] [raw]
/* SSHOUT Client
* Copyright 2015-2023 Rivoreo
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*/
#include "messagelog.h"
#include <QtSql/QSqlDatabase>
#include <QtSql/QSqlQuery>
#include <QtCore/QDateTime>
#include <QtCore/QVariant>
#include <QtCore/QFile>
#include <QtCore/QFileInfo>
#include <QtCore/QCoreApplication>
#include <signal.h>
#ifdef Q_OS_BSD4
#include <sys/sysctl.h>
#if defined Q_OS_DARWIN && MAC_OS_X_VERSION_MIN_REQUIRED >= 1050
#include <libproc.h>
#define HAVE_PROC_PIDPATH
#endif
#endif
#ifdef Q_OS_WIN
#include <windows.h>
#endif
MessageLog::MessageLog() {
//database = new QSqlDatabase;
//QSQLiteDriver *driver = new QSQLiteDriver;
//database = QSqlDatabase::addDatabase(driver);
database = QSqlDatabase::addDatabase("QSQLITE");
}
static bool is_another_instance(int pid) {
#ifndef Q_OS_WIN
if(pid < 1) return false;
if(kill(pid, 0) < 0) return false;
QString self_path = QCoreApplication::applicationFilePath();
QString path;
#ifdef Q_OS_SOLARIS
path = QFile::symLinkTarget(QString("/proc/%1/path/a.out").arg(pid));
#elif defined Q_OS_LINUX
if(self_path.endsWith(" (deleted)")) self_path.chop(10);
path = QFile::symLinkTarget(QString("/proc/%1/exe").arg(pid));
if(path.endsWith(" (deleted)")) path.chop(10);
#elif defined Q_OS_BSD4
#ifdef Q_OS_DARWIN
#define ki_comm kp_proc.p_comm
#endif
size_t len;
int mib[] = { CTL_KERN, KERN_PROC, 0, pid };
#ifdef KERN_PROC_PATHNAME
char buffer[PATH_MAX + 1];
len = sizeof buffer;
mib[2] = KERN_PROC_PATHNAME;
if(sysctl(mib, 4, buffer, &len, NULL, 0) == 0 && len) {
path = QString::fromLocal8Bit(buffer, len - 1);
}
#elif defined Q_OS_DARWIN && defined HAVE_PROC_PIDPATH
char buffer[PATH_MAX + 1];
if(proc_pidpath(pid, buffer, sizeof buffer) == 0) {
path = QString::fromLocal8Bit(buffer);
}
#endif
if(path.isEmpty()) {
struct kinfo_proc proc;
len = sizeof proc;
mib[2] = KERN_PROC_PID;
if(sysctl(mib, 4, &proc, &len, NULL, 0) == 0) {
path = QString::fromLocal8Bit(proc.ki_comm);
}
}
#endif
if(!path.isEmpty() && !self_path.isEmpty() && QFileInfo(path).fileName() != QFileInfo(self_path).fileName()) {
return false;
}
return true;
#else
HANDLE handle = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid);
// The Windows API design is shit!
if(handle != INVALID_HANDLE_VALUE && handle) {
CloseHandle(handle);
return true;
} else {
return false;
}
#endif
}
bool MessageLog::open(const QString &path) {
if(!database.isValid()) return false;
database.setDatabaseName(path);
//qDebug("is open ? %d", database.isOpen());
if(database.isOpen()) return false;
QByteArray lock_path_ba = path.toLocal8Bit();
lock_path_ba.append(".lock");
lock_file = new QFile(QString("%1.lock").arg(path));
if(lock_file->exists()) {
if(!lock_file->open(QFile::ReadOnly)) {
qWarning("MessageLog::open: lock file '%s' exist but can't open", lock_path_ba.data());
return false;
}
QByteArray content = lock_file->readLine();
lock_file->close();
if(!content.isEmpty()) {
int pid = content.toInt();
if(is_another_instance(pid)) {
qWarning("MessageLog::open: database file is locked by process %d via lock file '%s'", pid, lock_path_ba.data());
return false;
}
}
//lock_file->remove();
}
if(!lock_file->open(QFile::WriteOnly | QFile::Truncate)) {
qWarning("MessageLog::open: cannot open lock file '%s'", lock_path_ba.data());
return false;
}
if(lock_file->write(QByteArray::number(QCoreApplication::applicationPid())) < 0) {
QByteArray error_msg = lock_file->errorString().toLocal8Bit();
qWarning("MessageLog::open: cannot write to lock file '%s', %s", lock_path_ba.data(), error_msg.data());
lock_file->close();
return false;
}
lock_file->close();
QFile db_file(path);
bool need_chmod = !db_file.exists();
if(!database.open()) return false;
if(need_chmod) db_file.setPermissions(QFile::ReadUser | QFile::WriteUser);
QString sql_create_table("CREATE TABLE IF NOT EXISTS messages ("
"id INTEGER PRIMARY KEY AUTOINCREMENT,"
"receive_time DATETIME,"
"time DATETIME,"
"from_user CHAR(32),"
"to_user CHAR(32),"
"type INT8,"
"message TEXT );");
if(!QSqlQuery(sql_create_table, database).exec()) {
QByteArray ba = path.toLocal8Bit();
qWarning("MessageLog::open: failed to run create table SQL in database %s, giving up open", ba.data());
database.close();
return false;
}
return true;
}
void MessageLog::close() {
if(!database.isOpen()) return;
database.close();
lock_file->remove();
}
bool MessageLog::append_message(const QDateTime &dt, const QString &from_user, const QString &to_user, quint8 type, const QByteArray &message) {
QSqlQuery query(database);
query.prepare("INSERT INTO messages (receive_time,time,from_user,to_user,type,message) VALUES (?,?,?,?,?,?);");
query.bindValue(0, QDateTime::currentDateTime());
query.bindValue(1, dt);
query.bindValue(2, from_user);
query.bindValue(3, to_user);
query.bindValue(4, type);
query.bindValue(5, message);
return query.exec();
}