mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2025-08-20 13:23:51 -07:00
better offline mode
This commit is contained in:
parent
f8f39d6196
commit
820a6d99a9
13 changed files with 47 additions and 57 deletions
|
@ -333,7 +333,7 @@ int CmdLFSetConfig(const char *Cmd) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool lf_read(bool silent, uint32_t samples) {
|
bool lf_read(bool silent, uint32_t samples) {
|
||||||
if (IsOffline()) return false;
|
if (!session.pm3_present) return false;
|
||||||
clearCommandBuffer();
|
clearCommandBuffer();
|
||||||
SendCommandOLD(CMD_ACQUIRE_RAW_ADC_SAMPLES_125K, silent, samples, 0, NULL, 0);
|
SendCommandOLD(CMD_ACQUIRE_RAW_ADC_SAMPLES_125K, silent, samples, 0, NULL, 0);
|
||||||
|
|
||||||
|
@ -354,7 +354,7 @@ bool lf_read(bool silent, uint32_t samples) {
|
||||||
|
|
||||||
int CmdLFRead(const char *Cmd) {
|
int CmdLFRead(const char *Cmd) {
|
||||||
|
|
||||||
if (IsOffline()) return 0;
|
if (!session.pm3_present) return 0;
|
||||||
|
|
||||||
bool errors = false;
|
bool errors = false;
|
||||||
bool silent = false;
|
bool silent = false;
|
||||||
|
@ -843,7 +843,7 @@ int CmdLFfind(const char *Cmd) {
|
||||||
|
|
||||||
if (cmdp == 'u') testRaw = 'u';
|
if (cmdp == 'u') testRaw = 'u';
|
||||||
|
|
||||||
bool isOnline = (!IsOffline() && (cmdp != '1'));
|
bool isOnline = (session.pm3_present && (cmdp != '1'));
|
||||||
|
|
||||||
if (isOnline)
|
if (isOnline)
|
||||||
lf_read(true, 30000);
|
lf_read(true, 30000);
|
||||||
|
|
|
@ -467,7 +467,7 @@ static int CmdAWIDBrute(const char *Cmd) {
|
||||||
// main loop
|
// main loop
|
||||||
for (;;) {
|
for (;;) {
|
||||||
|
|
||||||
if (IsOffline()) {
|
if (!session.pm3_present) {
|
||||||
PrintAndLogEx(WARNING, "Device offline\n");
|
PrintAndLogEx(WARNING, "Device offline\n");
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
|
@ -569,7 +569,7 @@ static int CmdHIDBrute(const char *Cmd) {
|
||||||
// main loop
|
// main loop
|
||||||
for (;;) {
|
for (;;) {
|
||||||
|
|
||||||
if (IsOffline()) {
|
if (!session.pm3_present) {
|
||||||
PrintAndLogEx(WARNING, "Device offline\n");
|
PrintAndLogEx(WARNING, "Device offline\n");
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
|
@ -505,7 +505,7 @@ static bool DecodeT5555TraceBlock(void) {
|
||||||
|
|
||||||
// sanity check. Don't use proxmark if it is offline and you didn't specify useGraphbuf
|
// sanity check. Don't use proxmark if it is offline and you didn't specify useGraphbuf
|
||||||
static int SanityOfflineCheck(bool useGraphBuffer) {
|
static int SanityOfflineCheck(bool useGraphBuffer) {
|
||||||
if (!useGraphBuffer && IsOffline()) {
|
if (!useGraphBuffer && !session.pm3_present) {
|
||||||
PrintAndLogEx(WARNING, "Your proxmark3 device is offline. Specify [1] to use graphbuffer data instead");
|
PrintAndLogEx(WARNING, "Your proxmark3 device is offline. Specify [1] to use graphbuffer data instead");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -1914,7 +1914,7 @@ static int CmdT55xxChkPwds(const char *Cmd) {
|
||||||
uint64_t curr_password = 0x00;
|
uint64_t curr_password = 0x00;
|
||||||
for (uint16_t c = 0; c < keycount; ++c) {
|
for (uint16_t c = 0; c < keycount; ++c) {
|
||||||
|
|
||||||
if (IsOffline()) {
|
if (!session.pm3_present) {
|
||||||
PrintAndLogEx(WARNING, "Device offline\n");
|
PrintAndLogEx(WARNING, "Device offline\n");
|
||||||
free(keyBlock);
|
free(keyBlock);
|
||||||
return 2;
|
return 2;
|
||||||
|
|
|
@ -21,7 +21,7 @@ void CmdsHelp(const command_t Commands[]) {
|
||||||
if (Commands[0].Name == NULL) return;
|
if (Commands[0].Name == NULL) return;
|
||||||
int i = 0;
|
int i = 0;
|
||||||
while (Commands[i].Name) {
|
while (Commands[i].Name) {
|
||||||
if (!IsOffline() || Commands[i].Offline)
|
if (session.pm3_present || Commands[i].Offline)
|
||||||
PrintAndLogEx(NORMAL, "%-16s %s", Commands[i].Name, Commands[i].Help);
|
PrintAndLogEx(NORMAL, "%-16s %s", Commands[i].Name, Commands[i].Help);
|
||||||
++i;
|
++i;
|
||||||
}
|
}
|
||||||
|
@ -44,8 +44,17 @@ int CmdsParse(const command_t Commands[], const char *Cmd) {
|
||||||
sscanf(Cmd, "%127s%n", cmd_name, &len);
|
sscanf(Cmd, "%127s%n", cmd_name, &len);
|
||||||
str_lower(cmd_name);
|
str_lower(cmd_name);
|
||||||
int i = 0;
|
int i = 0;
|
||||||
while (Commands[i].Name && strcmp(Commands[i].Name, cmd_name))
|
while (Commands[i].Name) {
|
||||||
|
if (0 == strcmp(Commands[i].Name, cmd_name)) {
|
||||||
|
if (session.pm3_present || Commands[i].Offline) {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
PrintAndLogEx(WARNING, "This command is only available in " _YELLOW_("online") "mode");
|
||||||
|
return PM3_ENOTIMPL;
|
||||||
|
}
|
||||||
|
}
|
||||||
++i;
|
++i;
|
||||||
|
}
|
||||||
|
|
||||||
/* try to find exactly one prefix-match */
|
/* try to find exactly one prefix-match */
|
||||||
if (!Commands[i].Name) {
|
if (!Commands[i].Name) {
|
||||||
|
@ -53,7 +62,7 @@ int CmdsParse(const command_t Commands[], const char *Cmd) {
|
||||||
int matches = 0;
|
int matches = 0;
|
||||||
|
|
||||||
for (i = 0; Commands[i].Name; i++) {
|
for (i = 0; Commands[i].Name; i++) {
|
||||||
if (!strncmp(Commands[i].Name, cmd_name, strlen(cmd_name))) {
|
if (!strncmp(Commands[i].Name, cmd_name, strlen(cmd_name)) && (session.pm3_present || Commands[i].Offline)) {
|
||||||
last_match = i;
|
last_match = i;
|
||||||
matches++;
|
matches++;
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,9 +23,6 @@
|
||||||
static serial_port sp = NULL;
|
static serial_port sp = NULL;
|
||||||
static char *serial_port_name = NULL;
|
static char *serial_port_name = NULL;
|
||||||
|
|
||||||
// If TRUE, then there is no active connection to the PM3, and we will drop commands sent.
|
|
||||||
static bool offline;
|
|
||||||
|
|
||||||
communication_arg_t conn;
|
communication_arg_t conn;
|
||||||
capabilities_t pm3_capabilities;
|
capabilities_t pm3_capabilities;
|
||||||
|
|
||||||
|
@ -59,16 +56,6 @@ static uint64_t timeout_start_time;
|
||||||
|
|
||||||
static bool dl_it(uint8_t *dest, uint32_t bytes, uint32_t start_index, PacketResponseNG *response, size_t ms_timeout, bool show_warning, uint32_t rec_cmd);
|
static bool dl_it(uint8_t *dest, uint32_t bytes, uint32_t start_index, PacketResponseNG *response, size_t ms_timeout, bool show_warning, uint32_t rec_cmd);
|
||||||
|
|
||||||
// These wrappers are required because it is not possible to access a static
|
|
||||||
// global variable outside of the context of a single file.
|
|
||||||
void SetOffline(bool value) {
|
|
||||||
offline = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsOffline() {
|
|
||||||
return offline;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SendCommand(PacketCommandOLD *c) {
|
void SendCommand(PacketCommandOLD *c) {
|
||||||
|
|
||||||
#ifdef COMMS_DEBUG
|
#ifdef COMMS_DEBUG
|
||||||
|
@ -80,7 +67,7 @@ void SendCommand(PacketCommandOLD *c) {
|
||||||
print_hex_break((uint8_t *)&c->d, sizeof(c->d), 32);
|
print_hex_break((uint8_t *)&c->d, sizeof(c->d), 32);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (offline) {
|
if (!session.pm3_present) {
|
||||||
PrintAndLogEx(WARNING, "Sending bytes to Proxmark3 failed." _YELLOW_("offline"));
|
PrintAndLogEx(WARNING, "Sending bytes to Proxmark3 failed." _YELLOW_("offline"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -123,7 +110,7 @@ static void SendCommandNG_internal(uint16_t cmd, uint8_t *data, size_t len, bool
|
||||||
PrintAndLogEx(NORMAL, "Sending %s", ng ? "NG" : "MIX");
|
PrintAndLogEx(NORMAL, "Sending %s", ng ? "NG" : "MIX");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (offline) {
|
if (!session.pm3_present) {
|
||||||
PrintAndLogEx(NORMAL, "Sending bytes to proxmark failed - offline");
|
PrintAndLogEx(NORMAL, "Sending bytes to proxmark failed - offline");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -326,18 +313,18 @@ bool hookUpPM3() {
|
||||||
sp = NULL;
|
sp = NULL;
|
||||||
serial_port_name = NULL;
|
serial_port_name = NULL;
|
||||||
ret = false;
|
ret = false;
|
||||||
offline = 1;
|
session.pm3_present = false;
|
||||||
} else if (sp == CLAIMED_SERIAL_PORT) {
|
} else if (sp == CLAIMED_SERIAL_PORT) {
|
||||||
PrintAndLogEx(WARNING, "Reconnect failed, retrying... (reason: serial port is claimed by another process)\n");
|
PrintAndLogEx(WARNING, "Reconnect failed, retrying... (reason: serial port is claimed by another process)\n");
|
||||||
sp = NULL;
|
sp = NULL;
|
||||||
serial_port_name = NULL;
|
serial_port_name = NULL;
|
||||||
ret = false;
|
ret = false;
|
||||||
offline = 1;
|
session.pm3_present = false;
|
||||||
} else {
|
} else {
|
||||||
PrintAndLogEx(SUCCESS, "Proxmark3 reconnected\n");
|
PrintAndLogEx(SUCCESS, "Proxmark3 reconnected\n");
|
||||||
serial_port_name = ;
|
serial_port_name = ;
|
||||||
ret = true;
|
ret = true;
|
||||||
offline = 0;
|
session.pm3_present = true;
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,9 +53,6 @@ typedef struct {
|
||||||
|
|
||||||
extern communication_arg_t conn;
|
extern communication_arg_t conn;
|
||||||
|
|
||||||
void SetOffline(bool value);
|
|
||||||
bool IsOffline(void);
|
|
||||||
|
|
||||||
void *uart_receiver(void *targ);
|
void *uart_receiver(void *targ);
|
||||||
void SendCommand(PacketCommandOLD *c);
|
void SendCommand(PacketCommandOLD *c);
|
||||||
void SendCommandOLD(uint64_t cmd, uint64_t arg0, uint64_t arg1, uint64_t arg2, void *data, size_t len);
|
void SendCommandOLD(uint64_t cmd, uint64_t arg0, uint64_t arg1, uint64_t arg2, void *data, size_t len);
|
||||||
|
|
|
@ -15,14 +15,14 @@
|
||||||
static ProxGuiQT *gui = NULL;
|
static ProxGuiQT *gui = NULL;
|
||||||
static WorkerThread *main_loop_thread = NULL;
|
static WorkerThread *main_loop_thread = NULL;
|
||||||
|
|
||||||
WorkerThread::WorkerThread(char *script_cmds_file, char *script_cmd, bool pm3_present) : script_cmds_file(script_cmds_file), script_cmd(script_cmd), pm3_present(pm3_present) {
|
WorkerThread::WorkerThread(char *script_cmds_file, char *script_cmd) : script_cmds_file(script_cmds_file), script_cmd(script_cmd) {
|
||||||
}
|
}
|
||||||
|
|
||||||
WorkerThread::~WorkerThread() {
|
WorkerThread::~WorkerThread() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void WorkerThread::run() {
|
void WorkerThread::run() {
|
||||||
main_loop(script_cmds_file, script_cmd, pm3_present);
|
main_loop(script_cmds_file, script_cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" void ShowGraphWindow(void) {
|
extern "C" void ShowGraphWindow(void) {
|
||||||
|
@ -53,12 +53,12 @@ extern "C" void MainGraphics(void) {
|
||||||
gui->MainLoop();
|
gui->MainLoop();
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" void InitGraphics(int argc, char **argv, char *script_cmds_file, char *script_cmd, bool pm3_present) {
|
extern "C" void InitGraphics(int argc, char **argv, char *script_cmds_file, char *script_cmd) {
|
||||||
#ifdef Q_WS_X11
|
#ifdef Q_WS_X11
|
||||||
if (getenv("DISPLAY") == NULL)
|
if (getenv("DISPLAY") == NULL)
|
||||||
return;
|
return;
|
||||||
#endif
|
#endif
|
||||||
main_loop_thread = new WorkerThread(script_cmds_file, script_cmd, pm3_present);
|
main_loop_thread = new WorkerThread(script_cmds_file, script_cmd);
|
||||||
gui = new ProxGuiQT(argc, argv, main_loop_thread);
|
gui = new ProxGuiQT(argc, argv, main_loop_thread);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,7 @@ void ShowGraphWindow(void);
|
||||||
void HideGraphWindow(void);
|
void HideGraphWindow(void);
|
||||||
void RepaintGraphWindow(void);
|
void RepaintGraphWindow(void);
|
||||||
void MainGraphics(void);
|
void MainGraphics(void);
|
||||||
void InitGraphics(int argc, char **argv, char *script_cmds_file, char *script_cmd, bool pm3_present);
|
void InitGraphics(int argc, char **argv, char *script_cmds_file, char *script_cmd);
|
||||||
void ExitGraphics(void);
|
void ExitGraphics(void);
|
||||||
#ifndef MAX_GRAPH_TRACE_LEN
|
#ifndef MAX_GRAPH_TRACE_LEN
|
||||||
#define MAX_GRAPH_TRACE_LEN (40000 * 8)
|
#define MAX_GRAPH_TRACE_LEN (40000 * 8)
|
||||||
|
|
|
@ -93,13 +93,12 @@ class ProxWidget : public QWidget {
|
||||||
class WorkerThread : public QThread {
|
class WorkerThread : public QThread {
|
||||||
Q_OBJECT;
|
Q_OBJECT;
|
||||||
public:
|
public:
|
||||||
WorkerThread(char *, char *, bool);
|
WorkerThread(char *, char *);
|
||||||
~WorkerThread();
|
~WorkerThread();
|
||||||
void run();
|
void run();
|
||||||
private:
|
private:
|
||||||
char *script_cmds_file = NULL;
|
char *script_cmds_file = NULL;
|
||||||
char *script_cmd = NULL;
|
char *script_cmd = NULL;
|
||||||
bool pm3_present;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class ProxGuiQT : public QObject {
|
class ProxGuiQT : public QObject {
|
||||||
|
|
|
@ -58,7 +58,7 @@ void
|
||||||
__attribute__((force_align_arg_pointer))
|
__attribute__((force_align_arg_pointer))
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
main_loop(char *script_cmds_file, char *script_cmd, bool pm3_present) {
|
main_loop(char *script_cmds_file, char *script_cmd) {
|
||||||
|
|
||||||
char *cmd = NULL;
|
char *cmd = NULL;
|
||||||
bool execCommand = (script_cmd != NULL);
|
bool execCommand = (script_cmd != NULL);
|
||||||
|
@ -73,15 +73,12 @@ main_loop(char *script_cmds_file, char *script_cmd, bool pm3_present) {
|
||||||
|
|
||||||
PrintAndLogEx(DEBUG, "ISATTY/STDIN_FILENO == %s\n", (stdinOnPipe) ? "true" : "false");
|
PrintAndLogEx(DEBUG, "ISATTY/STDIN_FILENO == %s\n", (stdinOnPipe) ? "true" : "false");
|
||||||
|
|
||||||
if (pm3_present) {
|
if (session.pm3_present) {
|
||||||
SetOffline(false);
|
|
||||||
// cache Version information now:
|
// cache Version information now:
|
||||||
if (execCommand || script_cmds_file || stdinOnPipe)
|
if (execCommand || script_cmds_file || stdinOnPipe)
|
||||||
pm3_version(false);
|
pm3_version(false);
|
||||||
else
|
else
|
||||||
pm3_version(true);
|
pm3_version(true);
|
||||||
} else {
|
|
||||||
SetOffline(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (script_cmds_file) {
|
if (script_cmds_file) {
|
||||||
|
@ -100,13 +97,13 @@ main_loop(char *script_cmds_file, char *script_cmd, bool pm3_present) {
|
||||||
bool printprompt = false;
|
bool printprompt = false;
|
||||||
// this should hook up the PM3 again.
|
// this should hook up the PM3 again.
|
||||||
/*
|
/*
|
||||||
if ( IsOffline() ) {
|
if ( !session.pm3_present ) {
|
||||||
|
|
||||||
// sets the global variable, SP and offline)
|
// sets the global variable, SP and offline)
|
||||||
pm3_present = hookUpPM3();
|
session.pm3_present = hookUpPM3();
|
||||||
|
|
||||||
// usb and the reader_thread is NULL, create a new reader thread.
|
// usb and the reader_thread is NULL, create a new reader thread.
|
||||||
if (pm3_present && !IsOffline() ) {
|
if (session.pm3_present) {
|
||||||
rarg.run = 1;
|
rarg.run = 1;
|
||||||
pthread_create(&reader_thread, NULL, &uart_receiver, &rarg);
|
pthread_create(&reader_thread, NULL, &uart_receiver, &rarg);
|
||||||
// cache Version information now:
|
// cache Version information now:
|
||||||
|
@ -293,7 +290,7 @@ static void show_help(bool showFullHelp, char *exec_name) {
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
srand(time(0));
|
srand(time(0));
|
||||||
|
|
||||||
bool pm3_present = false;
|
session.pm3_present = false;
|
||||||
bool waitCOMPort = false;
|
bool waitCOMPort = false;
|
||||||
bool addLuaExec = false;
|
bool addLuaExec = false;
|
||||||
char *script_cmds_file = NULL;
|
char *script_cmds_file = NULL;
|
||||||
|
@ -489,38 +486,38 @@ int main(int argc, char *argv[]) {
|
||||||
|
|
||||||
// try to open USB connection to Proxmark
|
// try to open USB connection to Proxmark
|
||||||
if (port != NULL)
|
if (port != NULL)
|
||||||
pm3_present = OpenProxmark(port, waitCOMPort, 20, false, speed);
|
session.pm3_present = OpenProxmark(port, waitCOMPort, 20, false, speed);
|
||||||
|
|
||||||
if (pm3_present && (TestProxmark() != PM3_SUCCESS)) {
|
if (session.pm3_present && (TestProxmark() != PM3_SUCCESS)) {
|
||||||
PrintAndLogEx(ERR, _RED_("ERROR:") "cannot communicate with the Proxmark\n");
|
PrintAndLogEx(ERR, _RED_("ERROR:") "cannot communicate with the Proxmark\n");
|
||||||
CloseProxmark();
|
CloseProxmark();
|
||||||
pm3_present = false;
|
session.pm3_present = false;
|
||||||
}
|
}
|
||||||
if (!pm3_present)
|
if (!session.pm3_present)
|
||||||
PrintAndLogEx(INFO, "Running in " _YELLOW_("OFFLINE") "mode. Check \"%s -h\" if it's not what you want.\n", exec_name);
|
PrintAndLogEx(INFO, "Running in " _YELLOW_("OFFLINE") "mode. Check \"%s -h\" if it's not what you want.\n", exec_name);
|
||||||
|
|
||||||
#ifdef HAVE_GUI
|
#ifdef HAVE_GUI
|
||||||
|
|
||||||
# ifdef _WIN32
|
# ifdef _WIN32
|
||||||
InitGraphics(argc, argv, script_cmds_file, script_cmd, pm3_present);
|
InitGraphics(argc, argv, script_cmds_file, script_cmd);
|
||||||
MainGraphics();
|
MainGraphics();
|
||||||
# else
|
# else
|
||||||
// for *nix distro's, check enviroment variable to verify a display
|
// for *nix distro's, check enviroment variable to verify a display
|
||||||
char *display = getenv("DISPLAY");
|
char *display = getenv("DISPLAY");
|
||||||
if (display && strlen(display) > 1) {
|
if (display && strlen(display) > 1) {
|
||||||
InitGraphics(argc, argv, script_cmds_file, script_cmd, pm3_present);
|
InitGraphics(argc, argv, script_cmds_file, script_cmd);
|
||||||
MainGraphics();
|
MainGraphics();
|
||||||
} else {
|
} else {
|
||||||
main_loop(script_cmds_file, script_cmd, pm3_present);
|
main_loop(script_cmds_file, script_cmd);
|
||||||
}
|
}
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
#else
|
#else
|
||||||
main_loop(script_cmds_file, script_cmd, pm3_present);
|
main_loop(script_cmds_file, script_cmd);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Clean up the port
|
// Clean up the port
|
||||||
if (pm3_present) {
|
if (session.pm3_present) {
|
||||||
CloseProxmark();
|
CloseProxmark();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,7 @@ extern "C" {
|
||||||
|
|
||||||
const char *get_my_executable_path(void);
|
const char *get_my_executable_path(void);
|
||||||
const char *get_my_executable_directory(void);
|
const char *get_my_executable_directory(void);
|
||||||
void main_loop(char *script_cmds_file, char *script_cmd, bool pm3_present);
|
void main_loop(char *script_cmds_file, char *script_cmd);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,7 @@ typedef struct {
|
||||||
bool stdinOnTTY;
|
bool stdinOnTTY;
|
||||||
bool stdoutOnTTY;
|
bool stdoutOnTTY;
|
||||||
bool supports_colors;
|
bool supports_colors;
|
||||||
|
bool pm3_present;
|
||||||
} session_arg_t;
|
} session_arg_t;
|
||||||
|
|
||||||
extern session_arg_t session;
|
extern session_arg_t session;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue