Skip to content
149 changes: 129 additions & 20 deletions ComplaintBox.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,45 @@
#include <fstream>
#include <sstream>
#include <bits/stdc++.h>
#include <openssl/sha.h>
#include <conio.h>
#include "globals.h"


std::string logged_in_username = "random";

using namespace std;

string getHiddenPassword() {
string pass;
char ch;
while ((ch = _getch()) != '\r') { // Enter key
if (ch == '\b') { // Backspace
if (!pass.empty()) {
cout << "\b \b";
pass.pop_back();
}
} else {
pass += ch;
cout << '*';
}
}
cout << endl;
return pass;
}

string hashPassword(const string& password) {
unsigned char hash[SHA256_DIGEST_LENGTH];
SHA256((const unsigned char*)password.c_str(), password.size(), hash);

stringstream ss;
for (int i = 0; i < SHA256_DIGEST_LENGTH; ++i)
ss << hex << setw(2) << setfill('0') << (int)hash[i];

return ss.str();
}


ComplaintBox::ComplaintBox() {
sqlite3_open("complaints.db", &db);
createTables();
Expand All @@ -19,18 +55,21 @@ void ComplaintBox::createTables() {
string sqlUsers = "CREATE TABLE IF NOT EXISTS users (username TEXT PRIMARY KEY, password TEXT);";
string sqlAdmins = "CREATE TABLE IF NOT EXISTS adminusers (username TEXT PRIMARY KEY, password TEXT);";
string sqlComplaints = "CREATE TABLE IF NOT EXISTS complaints ("
"complaint_id INTEGER PRIMARY KEY AUTOINCREMENT, "
"category TEXT, "
"subCategory TEXT, "
"message TEXT);";
"complaint_id INTEGER PRIMARY KEY AUTOINCREMENT, "
"category TEXT, "
"subCategory TEXT, "
"message TEXT, "
"status TEXT DEFAULT 'Pending', "
"timestamp DATETIME DEFAULT CURRENT_TIMESTAMP, "
"notified INTEGER DEFAULT 0);";

sqlite3_exec(db, sqlUsers.c_str(), 0, 0, &errMsg);
sqlite3_exec(db, sqlAdmins.c_str(), 0, 0, &errMsg);
sqlite3_exec(db, sqlComplaints.c_str(), 0, 0, &errMsg);
}

void ComplaintBox::registerUser(bool isAdmin) {
string uname, pass;
string uname;
cout << PURPLE << "Enter username: " << RESET;
cin >> uname;

Expand All @@ -48,28 +87,40 @@ void ComplaintBox::registerUser(bool isAdmin) {
}

cout << PURPLE << "Enter password: " << RESET;
cin >> pass;
string pass1 = getHiddenPassword();

cout << PURPLE << "Confirm password: " << RESET;
string pass2 = getHiddenPassword();

if (pass1 != pass2) {
cout << RED << "Passwords do not match. Registration failed.\n" << RESET;
return;
}

string table = isAdmin ? "adminusers" : "users";
string sql = "INSERT INTO " + table + " (username, password) VALUES ('" + uname + "', '" + pass + "');";
string hashedPass = hashPassword(pass1);
string insertSql = "INSERT INTO " + table + " (username, password) VALUES ('" + uname + "', '" + hashedPass + "');";

if (sqlite3_exec(db, sql.c_str(), 0, 0, &errMsg) != SQLITE_OK) {
if (sqlite3_exec(db, insertSql.c_str(), 0, 0, &errMsg) != SQLITE_OK) {
cout << RED << "Error: " << errMsg << RESET << endl;
sqlite3_free(errMsg);
} else {
cout << BOLDGREEN << "Registration successful!\n" << RESET;
}
}


bool ComplaintBox::loginUser(bool isAdmin) {
string uname, pass;
cout << CYAN << "Enter username: " << RESET;
cin >> uname;
cout << CYAN << "Enter password: " << RESET;
cin >> pass;
pass = getHiddenPassword();

string table = isAdmin ? "adminusers" : "users";
string sql = "SELECT * FROM " + table + " WHERE username = '" + uname + "' AND password = '" + pass + "';";
string hashedPass = hashPassword(pass);
string sql = "SELECT * FROM " + table + " WHERE username = '" + uname + "' AND password = '" + hashedPass + "';";

bool success = false;

sqlite3_exec(db, sql.c_str(), [](void *successPtr, int, char **, char **) -> int {
Expand All @@ -79,13 +130,35 @@ bool ComplaintBox::loginUser(bool isAdmin) {

if (success) {
cout << GREEN << "Login successful!\n" << RESET;
admin_logged_in = isAdmin;
logged_in_username = uname;

if (isAdmin) {
const char* countSQL = "SELECT COUNT(*) FROM complaints WHERE notified = 0;";
int newComplaints = 0;
sqlite3_exec(db, countSQL, [](void* data, int argc, char** argv, char**) -> int {
*(int*)data = atoi(argv[0]);
return 0;
}, &newComplaints, &errMsg);

if (newComplaints > 0) {
cout << YELLOW << "You have " << newComplaints << " new complaint(s)!\n" << RESET;

const char* updateSQL = "UPDATE complaints SET notified = 1 WHERE notified = 0;";
sqlite3_exec(db, updateSQL, 0, 0, &errMsg);
} else {
cout << GREEN << "No new complaints since your last login.\n" << RESET;
}
}

return true;
} else {
cout << RED << "Invalid credentials!\n" << RESET;
return false;
}
}


void ComplaintBox::fileComplaint() {
string category, subCategory, message;
cout << YELLOW << "Enter category: " << RESET;
Expand All @@ -96,52 +169,62 @@ void ComplaintBox::fileComplaint() {
cout << YELLOW << "Enter complaint message: " << RESET;
getline(cin, message);

string sql = "INSERT INTO complaints (category, subCategory, message) VALUES ('" + category + "', '" + subCategory + "', '" + message + "');";
string filedBy = logged_in_username.empty() ? "random" : logged_in_username;

string sql = "INSERT INTO complaints (category, subCategory, message, status, filed_by) VALUES ('"
+ category + "', '" + subCategory + "', '" + message + "', 'Pending', '" + filedBy + "');";

if (sqlite3_exec(db, sql.c_str(), 0, 0, &errMsg) != SQLITE_OK) {
cout << RED << "Error: " << errMsg << RESET << endl;
sqlite3_free(errMsg);
} else {
cout << BOLDGREEN << "Complaint filed successfully!\n" << RESET;
cout << BOLDGREEN << "Complaint filed successfully by '" << filedBy << "' with status 'Pending'!\n" << RESET;
}
}



void ComplaintBox::exportComplaintsToCSV() {
ofstream file("complaints_export.csv");
if (!file.is_open()) {
cout << RED << "Failed to create CSV file.\n" << RESET;
return;
}

file << "complaint_id,category,subCategory,message\n";
string sql = "SELECT complaint_id, category, subCategory, message FROM complaints;";

file << "complaint_id,filed_by,category,subCategory,message,status,timestamp\n";

string sql = "SELECT complaint_id, filed_by, category, subCategory, message, status, timestamp FROM complaints;"; // Updated SQL

auto callback = [](void *data, int argc, char **argv, char **colName) -> int {
ofstream *f = static_cast<ofstream *>(data);
for (int i = 0; i < argc; i++) {
*f << (argv[i] ? argv[i] : "") << (i < argc - 1 ? "," : "\n");
}
return 0;
};

if (sqlite3_exec(db, sql.c_str(), callback, &file, &errMsg) != SQLITE_OK) {
cout << RED << "Export failed: " << errMsg << RESET << endl;
sqlite3_free(errMsg);
} else {
cout << BOLDGREEN << "Complaints exported to 'complaints_export.csv'!\n" << RESET;
cout << BOLDGREEN << "Complaints exported to 'complaints_export.csv' with filed_by and timestamp!\n" << RESET;
}

file.close();
}


void ComplaintBox::searchComplaints() {
string keyword;
cout << YELLOW << "Enter keyword to search for: " << RESET;
cin.ignore();
getline(cin, keyword);

string sql = "SELECT complaint_id, category, subCategory, message FROM complaints "
string sql = "SELECT complaint_id, category, subCategory, message, status FROM complaints "
"WHERE category LIKE '%" + keyword + "%' OR "
"subCategory LIKE '%" + keyword + "%' OR "
"message LIKE '%" + keyword + "%';";
"message LIKE '%" + keyword + "%' OR "
"status LIKE '%" + keyword + "%';";

cout << CYAN << "\nSearch Results:\n" << RESET;
auto callback = [](void *data, int argc, char **argv, char **colName) -> int {
Expand All @@ -157,3 +240,29 @@ void ComplaintBox::searchComplaints() {
sqlite3_free(errMsg);
}
}


void ComplaintBox::updateComplaintStatus(int complaint_id, const std::string& new_status) {
if (!admin_logged_in) {
cout << RED << "Only admins can update complaint status.\n" << RESET;
return;
}

sqlite3_stmt* stmt;
string sql = "UPDATE complaints SET status = ? WHERE complaint_id = ?";

if (sqlite3_prepare_v2(db, sql.c_str(), -1, &stmt, nullptr) == SQLITE_OK) {
sqlite3_bind_text(stmt, 1, new_status.c_str(), -1, SQLITE_STATIC);
sqlite3_bind_int(stmt, 2, complaint_id);

if (sqlite3_step(stmt) == SQLITE_DONE) {
cout << GREEN << "Status updated successfully.\n" << RESET;
} else {
cerr << RED << "Failed to update status.\n" << RESET;
}
} else {
cerr << RED << "SQL Prepare Failed: " << sqlite3_errmsg(db) << "\n" << RESET;
}

sqlite3_finalize(stmt);
}
3 changes: 3 additions & 0 deletions ComplaintBox.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,13 @@ class ComplaintBox {
void fileComplaint();
void exportComplaintsToCSV();
void searchComplaints();
void updateComplaintStatus(int complaint_id, const string& new_status);
bool isAdminLoggedIn() const { return admin_logged_in; }

private:
sqlite3 *db;
char *errMsg;
bool admin_logged_in = false;
void createTables();
};

Expand Down
10 changes: 6 additions & 4 deletions complaints_export.csv
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
complaint_id,category,subCategory,message
1,Infrastructural,Hostel,Washrooms are not being regularly cleaned.
2,Management,College Events,Admin paisa kha gaya bc
3,Infrastructure,Hostel,MMC change karo mc
complaint_id,filed_by,category,subCategory,message,status,timestamp
1,random,College,Fests,Admin chor mc,Pending,2025-04-11 14:43:14
2,random,College,Infrastructure,Add AC in class,Pending,2025-04-11 16:46:37
3,Kartik,College,Faculty,B Nath,Pending,2025-04-11 17:19:58
4,XD,Admin,Admin,Salary nahi milti sed,Pending,2025-04-11 17:21:05
5,random,College,Gymkhana,Why,Pending,2025-04-11 17:21:58
8 changes: 8 additions & 0 deletions globals.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#ifndef GLOBALS_H
#define GLOBALS_H

#include <string>

extern std::string logged_in_username;

#endif
Loading