-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathlogappend.cpp
More file actions
239 lines (205 loc) · 7.61 KB
/
logappend.cpp
File metadata and controls
239 lines (205 loc) · 7.61 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
#include "log_utils.h"
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <regex>
#include <unordered_map>
#include <set>
#include <sstream>
using std::cout; using std::endl; using std::string; using std::vector; using std::cerr;
using std::ifstream; using std::unordered_map; using std::set; using std::getline; using std::ofstream;
using std::stoi; using std::regex_match; using std::regex; using std::ios; using std::istringstream;
using std::fstream;
// Structure to represent a log entry
struct LogEntry {
string timestamp;
string personType;
string personName;
string action;
string roomID;
};
// Maps to maintain the current state of the system
unordered_map<string, set<string>> personRoomMap;
unordered_map<string, bool> inGalleryMap;
// Validate the name using regex
bool isValidName(const string& name) {
return regex_match(name, regex("^[a-zA-Z]+$"));
}
// Validate the room ID using regex
bool isValidRoomID(const string& roomID) {
return regex_match(roomID, regex("^[0-9]+$"));
}
// Parse the command-line arguments
bool parseCommandLine(int argc, char* argv[], string& timestamp, string& token,
string& personType, string& personName, string& action,
string& roomID, string& logFile, string& batchFile) {
for (int i = 1; i < argc; ++i) {
string arg = argv[i];
if (arg == "-T") timestamp = argv[++i];
else if (arg == "-K") token = argv[++i];
else if (arg == "-E") { personType = "Employee"; personName = argv[++i]; }
else if (arg == "-G") { personType = "Guest"; personName = argv[++i]; }
else if (arg == "-A") action = "Arrival";
else if (arg == "-L") action = "Leave";
else if (arg == "-R") roomID = argv[++i];
else if (arg == "-B") batchFile = argv[++i];
else logFile = argv[i];
}
return !(timestamp.empty() || token.empty() || logFile.empty() || personType.empty() || personName.empty() || action.empty());
}
// Validate the token
bool validateToken(const string& token, const string& storedEncryptedToken, const string& encryptionKey) {
if (!verifyToken(token, storedEncryptedToken, encryptionKey)) {
cerr << "invalid" << endl;
return false;
}
return true;
}
// Check if a new timestamp is greater than the last timestamp
bool checkTimestampOrder(const string& newTimestamp, const string& lastTimestamp) {
if (lastTimestamp.empty())
return true;
if (stoi(newTimestamp) > UINT32_MAX)
return false;
if (stoi(newTimestamp) < 0)
return false;
return stoi(newTimestamp) > stoi(lastTimestamp);
}
// Validate state consistency for departure
bool validateStateForDeparture(const string& personName, const string& roomID, bool leavingGallery) {
if (leavingGallery) {
if (!personRoomMap[personName].empty()) {
return false;
}
return inGalleryMap[personName];
}
else {
return personRoomMap[personName].count(roomID) > 0;
}
}
// Prevent entering a room before entering the gallery
bool validateStateForArrival(const string& personName, const string& roomID) {
if (roomID.empty()) {
return true; // No room specified, so it's an arrival at the gallery.
}
// If a room is specified, check if the person has already entered the gallery.
return inGalleryMap[personName];
}
// Update the state of the system based on the action
void updateState(const string& personName, const string& roomID, const string& action) {
if (action == "Arrival") {
// Ensure the person enters the gallery first, before entering any room
if (!validateStateForArrival(personName, roomID)) {
cerr << "invalid" << endl;
exit(255); // Exit immediately if inconsistency is found
}
inGalleryMap[personName] = true;
if (!roomID.empty()) {
personRoomMap[personName].insert(roomID);
}
}
else if (action == "Leave") {
if (roomID.empty()) {
personRoomMap[personName].clear();
inGalleryMap[personName] = false;
}
else {
personRoomMap[personName].erase(roomID);
}
}
}
// Process the log file to rebuild the current state
bool processLogFile(const string& logFile, const string& encryptionKey, string& lastTimestamp) {
fstream log(logFile, ios::in | ios::out | ios::app); // Open for reading and appending
// Check if the log file exists, if not, create it
if (!log.is_open()) {
ofstream createLog(logFile); // Create an empty log file if it doesn't exist
if (!createLog.is_open()) {
return false;
}
createLog.close(); // Close the file after creation
log.open(logFile, ios::in | ios::out | ios::app); // Reopen in read-write mode
}
if (!log.is_open()) {
return false;
}
string line;
while (getline(log, line)) {
string decryptedLogEntry = decryptData(line, encryptionKey);
size_t pos = decryptedLogEntry.find(',');
if (pos != string::npos) {
lastTimestamp = decryptedLogEntry.substr(0, pos); // Extract the timestamp from the decrypted entry
}
vector<string> tokens;
istringstream ss(decryptedLogEntry);
string token;
while (getline(ss, token, ',')) {
tokens.push_back(token);
}
if (tokens.size() >= 4) {
string personName = tokens[2];
string action = tokens[3];
string roomID = tokens.size() > 4 ? tokens[4].substr(5) : "";
updateState(personName, roomID, action);
}
}
log.close();
return true;
}
// Main function
int main(int argc, char* argv[]) {
string timestamp, token, personType, personName, action, roomID;
string logFile, batchFile;
//load secrets
auto envVars = loadEnv(".env");
string encryptionKey = envVars["ENCRYPTION_KEY"];
string secret = envVars["SECRET"];
//parse command line
if (!parseCommandLine(argc, argv, timestamp, token, personType, personName, action, roomID, logFile, batchFile)) {
cerr << "invalid" << endl;
return 255;
}
//validate token
string storedEncryptedToken = encryptData(secret, encryptionKey);
if (!validateToken(token, storedEncryptedToken, encryptionKey)) {
return 255;
}
//validate user input
if (!isValidName(personName) || (!roomID.empty() && !isValidRoomID(roomID)) || timestamp.empty()) {
cerr << "invalid" << endl;
return 255;
}
//open log file, decrypt it, find the last timestamp
string lastTimestamp;
if (!processLogFile(logFile, encryptionKey, lastTimestamp)) {
cerr << "invalid" << endl;
return 255;
}
//validate timestamp
if (!checkTimestampOrder(timestamp, lastTimestamp)) {
cerr << "invalid" << endl;
return 255;
}
//validate person is in gallery/room
if (action == "Leave") {
bool leavingGallery = roomID.empty();
if (!validateStateForDeparture(personName, roomID, leavingGallery)) {
cerr << "invalid" << endl;
return 255;
}
}
updateState(personName, roomID, action);
string logEntry = timestamp + "," + personType + "," + personName + "," + action;
if (!roomID.empty()) logEntry += ",Room:" + roomID;
string encryptedLogEntry = encryptData(logEntry, encryptionKey);
ofstream logAppend(logFile, ios::app);
if (!logAppend.is_open()) {
cerr << "invalid" << endl;
return 255;
}
logAppend << encryptedLogEntry << endl;
logAppend.close();
cout << "Log entry added successfully." << endl;
return 0;
}