/
trilirium
/
Archived_BLC
Обзор
Документация
Войти
/
trilirium
/
Archived_BLC
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
server.cpp
3 757 строк
84 KB
Trilirium
repo created OK
22 янв 2026, 16:43
22 янв 2026, 16:43
f05d14b
Код
Авторство
О чём код?
/* + === - === - === - === - === - === - === - === | | "Server.cpp" | | BLC Interraction (data storage/data retrieval) server | + === - === - === - === - === - === - === - === */ #include <fcntl.h> #include <signal.h> #include <string.h> #include <time.h> #include <winsock.h> #include <wininet.h> #include <winsock2.h> #include "mem_mgr.h" #include "console.h" #include "Container.h" // // (Memory manager interface) // static A_Console *MM_console = 0; // MM startup action static void MM_Startup (A_Console &console) { MM_console = &console; } // MM_Startup // MM error handler void MM_error (void *addr, char const *where, char const *message) { MM_console-> out_cstr ("MM error: "). out_cstr (where). out_label (" :"). out_cstr (message). out_nl (); } // MM_error // MM block dumper static void MM_block_dump (char const *tag, unsigned size, void *ptr) { MM_console-> out_ch ('-'). out_tab (). out_cstr (tag). out_ch (' '). out_ch('['). out_dec(size). out_ch(']'). out_nl (); } // MM_block_dump // MM final action static void MM_Finale () { unsigned summary, balance, peak_count, peak_size; MM_getstat (summary, balance, peak_count, peak_size); MM_console-> out_label ("Memory total/balance ="). out_dec(summary). out_cstr(" / "). out_dec(balance). out_nl (); unsigned t_count = 0, t_size = 0; MM_forall (MM_block_dump, t_count, t_size); if (t_count || t_size) { MM_console-> out_label ("Total:"). out_ch('#'). out_dec(t_count). out_tab (). out_ch('['). out_dec(t_size). out_ch(']'). out_nl (); } } // MM_Finale // // Logical console interface // struct W_Console : A_Console { // (Put text to console) void _put_text (char const *text, unsigned count) { write (2, text, count); } // // // // (out char 'ch') W_Console &out_ch (char ch) { print_char (ch); return *this; } // out_ch // (out new line) W_Console &out_nl () { print_char ('\n'); return *this; } // out_nl // (out tabulation) W_Console &out_tab () { print_char ('\t'); return *this; } // out_tab // (out exclamation) W_Console &out_excl () { print_char ('!'); return *this; } // out_excl // (out text 'text[len]') W_Console &out_text (char const *text, unsigned len) { print_text (text, len); return *this; } // out_text // (out string 'label') W_Console &out_label (char const *label) { print_cstr (label); print_char (' '); return *this; } // out_label // (out character 'label_ch') W_Console &out_label (char label_ch) { print_char (label_ch); print_char (' '); return *this; } // out_label // (out prefix 'pfx_ch') W_Console &out_pfx (char pfx_ch) { print_char (' '); print_char (pfx_ch); return *this; } // out_pfx // (out C-string 'cstr') W_Console &out_cstr (char const *cstr) { if (cstr) print_cstr (cstr); else print_cstr ("<>"); return *this; } // out_cstr // (out quoted string) W_Console &out_qstr (char const *c_str, char c_quo) { if (c_str) { print_char (c_quo); print_cstr (c_str); print_char (c_quo); } else print_cstr ("<>"); return *this; } // out_qstr // (out quoted string) W_Console &out_qstr (char const *c_str, char l_quo, char r_quo) { if (c_str) { print_char (l_quo); print_cstr (c_str); print_char (r_quo); } else print_cstr ("<>"); return *this; } // out_qstr // (out decimal value) W_Console &out_dec (unsigned value, short digits = 1) { print_decimal (value, digits); return *this; } // out_dec // (out hex value) W_Console &out_hex (unsigned value) { print_hex (value); return *this; } // out_hex // (out boolean value) W_Console &out_bool (bool flag) { print_bool (flag); return *this; } // out_bool // (out tristate value) W_Console &out_tristate (int sigval, char const *s_lt, char const *s_eq, char const *s_gt) { print_tristate (sigval, s_lt, s_eq, s_gt); return *this; } // out_tristate // Error information report void ErrorInfo (char const *message) { out_label ("Error:"). out_cstr (message). out_nl (); } // ErrorInfo void out_X_text (char const *text, unsigned len); // Dump one of the buffers void dump_buffer (char const *about, char buffer []); // Dump time stamp W_Console &dump_time (time_t Time, bool is_UTC); // Dump IP address W_Console &dump_IP_addr (unsigned IP_address); // Dump Query context with file W_Console &out_QFile (struct Query_Context &Q_Context, char const *file_name); }; // W_Console // Convert number (by radix) static void digits_unsigned (unsigned value, char *_buffer, short radix, short digits) { if (value == 0) { while (digits --) *_buffer ++ = '0'; *_buffer = '\0'; } else { unsigned count = 0, dig; do { for (int i = count; i; -- i) _buffer [i] = _buffer [i - 1]; count ++; dig = value % radix; *_buffer = dig < 10 ? '0' + dig : 'A' + dig - 10; } while (value /= radix); // (padding) if (count < digits) { unsigned shift = digits - count; while (count --) _buffer [count + shift] = _buffer [count]; while (shift --) _buffer [shift] = '0'; _buffer [digits] = '\0'; } else _buffer [count] = '\0'; } } // digits_unsigned // Put decimal number void A_Console::print_decimal (unsigned value, short digits) { char _buffer [10 + 1]; digits_unsigned (value, _buffer, 10, digits); print_cstr (_buffer); } // A_Console::print_decimal // Put octal number void A_Console::print_octal (unsigned value, short digits) { char _buffer [11 + 1]; digits_unsigned (value, _buffer, 8, digits); print_cstr (_buffer); } // A_Console::print_octal // Put hex number void A_Console::print_hex (unsigned value, short digits) { char _buffer [8 + 1]; digits_unsigned (value, _buffer, 16, digits); print_cstr (_buffer); } // A_Console::print_hex // Print text (in quotes) void A_Console::print_text (char const *text, unsigned len) { print_char ('"'); _put_text (text, len); print_char ('"'); } // A_Console::print_text // Print hex text (in brockets) void A_Console::print_hex_text (char const *text, unsigned len) { print_char ('<'); for (int i = 0; i != len; ++ i) { if (i) print_char (' '); print_hex (text [i] & 0xFF); } print_char ('>'); } // A_Console::print_hex_text // Print wide text (in brockets) void A_Console::print_w_text (wchar_t const *w_text, unsigned len) { print_char ('<'); for (int i = 0; i != len; ++ i) { if (i) print_char (' '); print_hex (w_text [i]); } print_char ('>'); } // A_Console::print_w_text void W_Console::out_X_text (char const *text, unsigned len) { bool state = 0; while (len --) { unsigned char ch = *text ++; if (ch < 0x20) { if (state) { out_ch ('"'); state = false; } out_ch (' '); out_ch ('<'); switch (ch) { case '\0': out_cstr ("NUL"); break; case '\t': out_cstr ("TAB"); break; case '\n': out_cstr ("LF"); break; case '\r': out_cstr ("CR"); break; case '\f': out_cstr ("FF"); break; default: out_hex (ch); break; } // switch (ch) out_ch ('>'); out_ch (' '); } else { if (! state) { out_ch ('"'); state = true; } out_ch (ch); } } // while (ch) if (state) out_ch ('"'); } // W_Console::out_X_text // Dump one of the buffers void W_Console::dump_buffer (char const *about, char buffer []) { unsigned size = (*buffer ++) & 0xFF; if (size) { out_tab (). out_label (about); out_ch ('['). out_dec (size). out_ch (']'); out_label (':'). out_tab (). out_X_text (buffer, size); out_nl (); } // (size) } // W_Console::dump_buffer // // (from: IRL_defs.h) // // // Container values // typedef unsigned char byte_t; // Encode: value => [__to] static byte_t *encode_value (byte_t * __to, unsigned value) { __to += sizeof (value); for (unsigned i = 0; i != sizeof (value); ++ i) { * --__to = value & 0xFF; value >>= 8; } return __to + sizeof (value); } // encode_value // Decode: value <= [__from] static byte_t *decode_value (byte_t * __from, unsigned &value) { unsigned __value = 0; for (unsigned i = 0; i != sizeof (__value); ++ i) { __value <<= 8; __value |= * __from ++; } value = __value; return __from; } // decode_value // // IP address container // (dumpable) // struct IPAddr : A_Dumpable { unsigned IP_addr; byte_t _internal [sizeof (unsigned)]; // (constructor) IPAddr (unsigned _addr) { IP_addr = _addr; } // output time value to logger void dump (A_Console &logger) { W_Console *console = dynamic_cast <W_Console *> (&logger); if (console) console->dump_IP_addr (IP_addr); } // dump // Encode (to internal form) void content_encoder () { encode_value (_internal, IP_addr); } // content_encoder // Decode (from internal form) void content_decoder () { decode_value (_internal, IP_addr); } // content_decoder // Get content: self <= C_IP void get_content (C_Value &C_IP) { CSaveDataVal (C_IP, _internal, sizeof (_internal)); content_decoder (); } // get_content // Set content: self => C_IP void set_content (C_Value &C_IP) { content_encoder (); CLoadDataVal (C_IP, _internal, sizeof (_internal)); } // set_content // Add IP value to container 'items' void add_container (char const *name, Container &items) { content_encoder (); items.add_item (name, sizeof (_internal), (char *) _internal); } // add_container }; // IPAddr // // Time container // struct Time : A_Dumpable { __time64_t timeval; bool use_UTC; byte_t _internal [sizeof (__time64_t)]; // (constructor) Time (bool use_UTC) { _time64 (&timeval); this->use_UTC = use_UTC; } // (constructor) Time (time_t timeval, bool use_UTC) { this->timeval = timeval; this->use_UTC = use_UTC; } // output time value to logger void dump (A_Console &logger) { W_Console *console = dynamic_cast <W_Console *> (&logger); if (console) console->dump_time (timeval, use_UTC); } // dump enum { TimeLen = sizeof (time_t) / sizeof (unsigned) }; // Encode (to internal form) void content_encoder () { time_t __value = timeval; unsigned _vector [TimeLen]; unsigned i = TimeLen; do { _vector [-- i] = __value; __value >>= 32; } while (i); byte_t *__to = _internal; for (i = 0; i != TimeLen; ++ i) __to = encode_value (__to, _vector [i]); } // content_encoder // Decode (from internal form) void content_decoder () { unsigned _vector [TimeLen]; byte_t *__from = _internal; unsigned i; for (i = 0; i != TimeLen; ++ i) __from = decode_value (__from, _vector [i]); time_t __value = 0; for (i = 0; i != TimeLen; ++ i) __value = (__value << 32) | _vector [i]; timeval = __value; } // content_decoder // Get content: self <= C_Time void get_content (C_Value &C_Time) { CSaveDataVal (C_Time, _internal, sizeof (_internal)); content_decoder (); } // get_content // Set content: self => C_Time void set_content (C_Value &C_Time) { content_encoder (); CLoadDataVal (C_Time, _internal, sizeof (_internal)); } // set_content // Add time value to container 'items' void add_container (char const *name, Container &items) { content_encoder (); items.add_item (name, sizeof (_internal), (char *) _internal); } // add_container }; // Time // Dump IP address W_Console &W_Console::dump_IP_addr (unsigned IP_address) { unsigned value = IP_address; for (unsigned i = 0; i != 4; ++ i) { if (i) out_ch ('.'); // (not first) out_dec (value & 0xFF); value >>= 8; } // for (i) return *this; } // W_Console::dump_IP_addr // Dump time stamp W_Console &W_Console::dump_time (time_t Time, bool is_UTC) { struct tm *s_time = is_UTC ? gmtime (&Time) : localtime (&Time); out_ch ('['); // (date section:) out_dec (s_time->tm_mday, 2); out_ch ('.'); out_dec (1 + s_time->tm_mon, 2); out_ch ('.'); out_dec (1900 + s_time->tm_year, 4); out_ch (' '); // (time section:) out_dec (s_time->tm_hour, 2); out_ch (':'); out_dec (s_time->tm_min, 2); out_ch (':'); out_dec (s_time->tm_sec, 2); out_ch (']'); return *this; } // W_Console::dump_time // // Filter source string // // filter: 'from' -> 'to' (not more than 'limit', with 'filter') static void filter_from_to (char *from, char *to, unsigned limit, char (* filter) (char ch)) { char ch; while (ch = *from ++) { if (ch = filter (ch)) { if (limit) { *to ++ = ch; limit --; } else break; } } // while *to = '\0'; // (end) } // filter_from_to // filter: 'in' -> 'in' (not more than 'limit', with 'filter') static void filter_in (char *in, unsigned limit, char (* filter) (char ch)) { filter_from_to (in, in, limit, filter); } // filter_in // // Verify content received from client // bool client_entry_verify (char *entry_data, unsigned entry_size) { // // (to be done...) // return true; } // client_entry_verify // // Query context // struct Query_Context { // (path to server) char const *server_path; // (host name) char const *host_name; // (user name) char const *user_name; // (user login name) char const *login_name; // // query context constructor // Query_Context (char const *path, char const *host) { server_path = path; host_name = host; login_name = user_name = 0; } // Query_Context // (dump query context) void dump (W_Console &log) { if (user_name) { log.out_cstr (user_name); log.out_ch ('@'); } } // dump // Setup PathDir void setup (PathDir &location) { location.path = server_path; location.dir = user_name; } // setup // Check for valid user name: bool check_username (char const *user_name); // Check for valid user password: bool check_userpass (char const *user_name, char const *password); // Change password: bool change_password (A_Console &logger, char const *user_name, char const *password); // Guard: check condition bool perm_guard (unsigned flags, char const *obj_name); }; // Query_Context // Guard flags enum ALLOW_FLAGS { ALLOW_NONE = 0, // (Server Admin only:) ALLOW_ADMIN = 1 << 0, // (Server Monitoring only:) ALLOW_MONIT = 1 << 1, // (User/owner, or admin:) ALLOW_OWNER = 1 << 2, // (User/owner, or admin:) ALLOW_REPLY = 1 << 3, // (User/owner, or admin:) ALLOW_USER = 1 << 4, // (Anybody!) ALLOW_ANY = 1 << 5, }; // G_FLAGS // (Symbolic flag name) static char const *flag_name (unsigned flag) { switch (flag) { case ALLOW_NONE: return "NONE"; case ALLOW_ADMIN: return "ADMIN"; case ALLOW_MONIT: return "MONIT"; case ALLOW_OWNER: return "OWNER"; case ALLOW_REPLY: return "REPLY"; case ALLOW_USER: return "USER"; case ALLOW_ANY: return "ANY"; default: return 0; } // switch } // flag_name // Check for valid user name: bool Query_Context::check_username (char const *user_name) { File_System _FS_ (0); char const *directory_full = path_name (user_name, server_path); bool outcome = _FS_.is_dir (directory_full); free_name (directory_full); return outcome; } // Query_Context::check_username // (Local password file) static char const *password_file = ".password.bin"; // Check for valid user password: bool Query_Context::check_userpass (char const *user_name, char const *password) { File_Input _input (password_file, user_name, server_path); if (_input.open ()) { char pass_buffer [128 + 1]; unsigned pass_length = _input.read (pass_buffer, sizeof (pass_buffer)); _input.close (); return pass_length == strlen (password) && memcmp (password, pass_buffer, pass_length) == 0; } // (OK to open) return false; } // Query_Context::check_userpass // Change password: bool Query_Context::change_password (A_Console &logger, char const *user_name, char const *password) { File_InOut _output (password_file, user_name, server_path); if (_output.open_create ()) { unsigned length = strlen (password); int result = 0; if (length) result = _output.write (password, length); _output.close (); logger. out_tab (). out_label ("Change password:"). out_cstr (user_name). out_tab (). out_qstr (password, '{', '}'). out_nl (); return result == length; } // (OK to create) return false; } // Query_Context::change_password // user equality check: static bool user_equal (char const *user_A, char const *user_B) { if (user_A && user_B && strcmp (user_A, user_B) == 0) return true; return false; } // user_equal // check condition bool Query_Context::perm_guard (unsigned flags, char const *obj_name) { switch (flags) { // (server admin/monitor permissions) case ALLOW_ADMIN: case ALLOW_MONIT: if (user_equal (login_name, "_admin_")) return true; // (fail!) return false; // (owner permissions) case ALLOW_OWNER: if (user_equal (user_name, login_name)) return true; // (fail!) return false; // (any user permissions) case ALLOW_USER: if (login_name) return true; // (fail!) return false; // (permissions to reply to...) case ALLOW_REPLY: // default: owner permissions! if (user_equal (user_name, login_name)) return true; if (login_name) { // (reply to messages:) if (obj_name && *obj_name == '+') return true; // (allow!) } // (fail!) return false; // (no restrictions) case ALLOW_ANY: return true; } // switch (flags) // (fail on default) return false; } // Query_Context::perm_guard // Dump Query context with file W_Console &W_Console::out_QFile (Query_Context &QC, char const *file_name) { QC.dump (*this); if (file_name) { out_ch ('"'); out_cstr (file_name); out_ch ('"'); } return *this; } // W_Console::out_QFile // // Client action // // [Abstract]: // make actual data transfer on socket struct A_DataEx { // (transfer data with 'Client_Action') virtual bool transfer (struct Client_Action &action) = 0; // (virtual destructor!) virtual ~A_DataEx () {} }; // A_DataEx // (predeclare!) struct Server_Command; // // (debug details) // static bool detail_Query = true, detail_Reply = true; static bool detail_Transmit = true, detail_Receive = true; static bool permit_zeroReceive = false, permit_zeroTransmit = false; static bool permit_zeroAppend = false, permit_zeroReplace = false; // // Client action // struct Client_Action { SOCKET sd; // (socket) unsigned IP; // (client IP address) int s_errno; // (error #) W_Console &logger; // (output log) // (constructor) Client_Action (SOCKET sd, W_Console &_logger) : logger (_logger) { this->sd = sd; } // Get actual error code: static unsigned get_errno () { return (errno); } // Receive data, from input socket // (total 'in_count' bytes, to 'in_buf') int read (void *in_buf, unsigned in_count) { // (receive data from socket) int count = recv (sd, (char *)in_buf, in_count, 0); if (count < 0) s_errno = get_errno (); return count; } // read // Transmit data, to output socket // (total 'out_count' bytes, from 'out_buf') int write (const void *out_buf, unsigned out_count) { // (send data to socket) int count = send (sd, (const char *)out_buf, out_count, 0); if (count < 0) s_errno = get_errno (); return count; } // write // (Query: received from client:) char client_query [0x100 + 1]; // (Reply: sent back to client:) char client_reply [0x100 + 1]; // Reset: clear buffers void reset () { memset (client_query, 0, sizeof (client_query)); memset (client_reply, 0, sizeof (client_reply)); } // reset // Send client reply bool transmit_reply () { unsigned length = client_reply[0] + 2; return write (client_reply, length) == length; } // transmit_reply // Dump reply void dump_reply () { if (detail_Reply) logger.dump_buffer (">" "\t" "Reply", client_reply); } // dump_reply // Receive client request bool receive_query () { if (1) { unsigned length; return read (client_query, 1) == 1 && read (client_query + 1, (length = *client_query + 1)) == length; } else { int length = read (client_query, sizeof (client_query) - 1); return (length > 0); } } // receive_query // Clear reply void reply_clear () { memset (client_reply, 0, sizeof (client_reply)); } // reply_clear // Add character to reply void reply_add (char arg_ch) { unsigned offset = ((*client_reply) ++) & 0xFF; client_reply [++ offset] = arg_ch; client_reply [++ offset] = '\0'; } // reply_add // Add string to reply void reply_add (char const *arg_str) { if (arg_str) { unsigned len = strlen (arg_str); unsigned offset = (*client_reply += len) & 0xFF; memcpy (client_reply + 1 + offset - len, arg_str, len + 1); } } // reply_add // Reply command: Client_Action &reply_cmd (char const *str_cmd) { reply_clear (); reply_add (str_cmd); return *this; } // reply_cmd // Reply argument: Client_Action &reply_arg (char const *str_arg) { reply_add ('\t'); reply_add (str_arg); return *this; } // reply_arg // Encode 'value' as hex (upper/lower case) static void encode_hex (char *_buffer, unsigned value, bool upper) { unsigned count = 8; _buffer [count] = '\0'; while (count --) { unsigned digit = value & 0xF; value >>= 4; _buffer [count] = digit + (digit < 10 ? '0' : (upper ? 'A' : 'a') - 10); } } // encode_hex // Skip leading zeroes: static char *zero_skip (char *_ptr) { while (*_ptr == '0') ++ _ptr; if (*_ptr == '\0') -- _ptr; return _ptr; } // zero_skip // Reply argument as hex number: Client_Action &reply_hex (unsigned value, bool upper) { char _buffer [8 + 1]; encode_hex (_buffer, value, upper); return reply_arg (zero_skip (_buffer)); } // reply_hex // Dump query void dump_query () { if (detail_Query) logger.dump_buffer ("<" "\t" "Query", client_query); } // dump_query // Read from file & transmit data over network bool read_transmit (File_Input &file_transmit, unsigned length) { enum { TransmitLen = 1024 }; char buffer [TransmitLen]; int amount; while (amount = length) { if (amount > TransmitLen) amount = TransmitLen; if ((amount = file_transmit.read (buffer, amount)) < 0) return false; // (file read fail) if (amount) { if (write (buffer, amount) != amount) return false; // (network transmit fail) length -= amount; } } // while (length) return true; // (success) } // read_transmit // Receive data over network & write to file bool write_receive (File_Output &file_receive, unsigned length) { enum { ReceiveLen = 1024 }; char buffer [ReceiveLen]; int amount; while (amount = length) { if (amount > ReceiveLen) amount = ReceiveLen; if ((amount = read (buffer, amount)) < 0) return false; // (network receive fail) if (amount) { if (file_receive.write (buffer, amount) != amount) return false; // (file write fail) length -= amount; } } // while (length) return true; // (success) } // write_receive #if 0 // Container insertion bool insert_container_old (File_InOut &file_append, char const *entry_name, unsigned entry_length, Container *content = 0) { char * entry_buffer = new ("[insert_container_buffer]") char [entry_length]; unsigned count = read (entry_buffer, entry_length); bool result; if (count == entry_length) { if (content) { content->add_trail_block (entry_buffer, entry_length); result = content->append_Multi (file_append, entry_name); } else // (old style) result = Container::append_data_Multi (file_append, entry_name, entry_buffer, entry_length); } else result = false; delete entry_buffer; return result; } // insert_container_old #endif // insert container && data content bool insert_container (File_InOut &file_append, char const *content_name, Container &content, char const *entry_name, unsigned entry_length) { char * entry_buffer = new ("[container_insert_data]") char [entry_length]; unsigned count = read (entry_buffer, entry_length); bool result; if (count == entry_length) { client_entry_verify (entry_buffer, entry_length); if (content.append_Multi (file_append, content_name)) result = file_append.seek (0) && Container::append_data_Multi (file_append, entry_name, entry_buffer, entry_length); else result = false; } else result = false; delete entry_buffer; return result; } // insert_container // Parse command (predeclare) A_DataEx *parse_query (char *command, Query_Context &context); // Final action bool complete () { shutdown (sd, SD_BOTH); closesocket (sd); } // complete // Debug output W_Console &debug_label (char const *label) { if (label) return logger. out_tab (). out_cstr (label). out_ch (':'). out_ch (' '); return logger; } // debug_label // Debug output (failure) void debug_fail (char const *fail) { logger. out_tab (). out_cstr (fail). out_excl (). out_nl (); } // debug_fail // Reply error Client_Action &reply_error (char const *cmd_name, char const *error) { return reply_cmd ("!!"). reply_arg (cmd_name). reply_arg (error); } // reply_error // Receive data [file_length] // (from network / to 'file_receive') bool receive_to_file (File_Output &file_receive, unsigned file_length) { if (file_receive.open ()) { bool result = write_receive (file_receive, file_length); file_receive.close (); if (detail_Receive) { if (result) logger. out_tab (). out_label ("File received"). out_qstr (file_receive.filename, '<', '>'). out_ch ('['). out_dec (file_length). out_ch (']'). out_nl (); else logger. out_tab (). out_label ("File receive failed"). out_qstr (file_receive.filename, '<', '>'). out_excl (). out_nl (); } return result; } // (can't write result file!) return false; } // receive_to_file // Transmit data [file_length] // (to network / from 'file_transmit') bool transmit_from_file (File_Input &file_transmit, unsigned file_length) { bool result = read_transmit (file_transmit, file_length); if (detail_Transmit) { if (result) logger. out_tab (). out_label ("File transmitted"). out_qstr (file_transmit.filename, '<', '>'). out_ch ('['). out_dec (file_length). out_ch (']'). out_nl (); else logger. out_tab (). out_label ("File transmit failed"). out_qstr (file_transmit.filename, '<', '>'). out_excl (). out_nl (); } return result; } // transmit_from_file // // Handling requests // int request_handler (); // (server commands map predeclaration) static Server_Command command_table []; }; // Client_Action // // Command sent to server // struct Server_Command { // (name of command) char const *cmd_name; // (info about command) char const *cmd_info; // (data exchange object builder) A_DataEx * (*cmd_exec) (Client_Action &action, char *command, char *arguments, Query_Context &context); }; // Server_Command // Get next argument from 'arguments' static char *get_arg (char * &arguments) { char *start = arguments; if (*start) { char *ptr = strchr (start, '\t'); if (ptr) { // next argument present: *ptr ++ = '\0'; arguments = ptr; } else // final argument: arguments = start + strlen (start); return start; } else // (nothing more to get!) return 0; } // get_arg // Decode 'value' from hex static unsigned decode_hex (char const *src) { unsigned result = 0; char ch; while (ch = *src ++) { unsigned digit = ch; if ('0' <= ch && ch <= '9') digit -= '0'; else if ('a' <= ch && ch <= 'f') digit += 10 - 'a'; else if ('A' <= ch && ch <= 'F') digit += 10 - 'A'; else break; result <<= 4; result |= digit; } // while (ch) return result; } // decode_hex // Get numeric hex argument static bool get_arg_hex (char * &arguments, unsigned &value) { char *argument = get_arg (arguments); if (argument) { value = decode_hex (argument); if (! value) return *argument == '0'; return true; } return false; } // get_arg_hex // (mapper for valid 'file' characters) static char _file_char_mapper (char ch) { if (!(ch & 0x80) && (ch > ' ') && (ch != '/') && (ch != '\\') && (ch != '~')) return ch; return 0; // (test fail!) } // _file_char_mapper // Get file name argument static bool get_arg_file (char * &arguments, char * &file_name) { char *_argument = get_arg (arguments); if (_argument) { enum { Limit = 32 }; filter_in (_argument, Limit, _file_char_mapper); if (*_argument && *_argument != '.') { file_name = _argument; return true; } } // (illegal file: failed!) file_name = 0; return false; } // get_arg_file // (mapper for valid 'entry' characters) static char _tag_char_mapper (char ch) { if ( ('A' <= ch && ch <= 'Z') || ('a' <= ch && ch <= 'z') || ('0' <= ch && ch <= '9') || (ch == '_') ) return ch; return 0; // (test fail!) } // _tag_char_mapper // Get entry name argument static bool get_arg_entry (char * &arguments, char * &entry_tag) { char *_argument = get_arg (arguments); if (_argument) { enum { Limit = 16 }; filter_in (_argument, Limit, _tag_char_mapper); if (*_argument) { entry_tag = _argument; return true; } } // (illegal entry name: failed!) entry_tag = 0; return 0; } // get_arg_entry // (mapper for valid 'user' characters) static char _user_char_mapper (char ch) { if ( ('A' <= ch && ch <= 'Z') || ('a' <= ch && ch <= 'z') || ('0' <= ch && ch <= '9') || (ch == '_') ) return ch; return 0; // (test fail!) } // _user_char_mapper // Get user name argument static bool get_arg_user (char * &arguments, char const * &user_name) { char *_argument = get_arg (arguments); if (_argument) { enum { Limit = 16 }; filter_in (_argument, Limit, _user_char_mapper); if (*_argument) { user_name = _argument; return true; } } // (illegal user name!) user_name = 0; return false; } // get_arg_user // (check for password char) static bool password_char (char ch) { // (valid chars: upper/lower letters and digits) return ('0' <= ch && ch <= '9') || ('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z') ; } // password_char // (filter encoded password) static bool password_check (char *_password) { char ch; while (ch = *_password ++) if (! password_char (ch)) return false; // (failed!) // (password validated) return true; } // password_check // Get password argument static bool get_arg_password (char * &arguments, char const * &password) { char *_password_ = get_arg (arguments); if (_password_) { if (password_check (_password_)) { password = _password_; return true; } } password = 0; return false; } // get_arg_password // // Server request parser // A_DataEx * Client_Action::parse_query (char *request, Query_Context &query_context) { // Look for command in list... char *command = get_arg (request); if (command) { Server_Command *entry = command_table; while (entry->cmd_name) if (strcmp (entry->cmd_name, command) == 0) { // (command found in list:) return (* entry->cmd_exec) (*this, command, request, query_context); } else entry ++; // (command not found!) logger. out_label ("Command not supported:"). out_cstr (command). out_excl (). out_nl (); reply_error (command, "E_COMMAND"); } else { logger.out_cstr ("Command is empty!"). out_nl (); reply_error ("-", "E_COMMAND"); } return 0; // (nothing to do...) } // Client_Action::parse_query // // Command: Echo argument(s) // // (no actual transfer) struct Nop_DataEx : A_DataEx { // (nothing transferred) bool transfer (struct Client_Action &action) { return true; } }; // Nop_DataEx static A_DataEx * Command_Echo (Client_Action &action, char *command, char *arguments, Query_Context &context) { char * arg; action. debug_label ("Echo"). out_cstr (arguments). out_nl (); action.reply_cmd ("=="); while (arg = get_arg (arguments)) action.reply_arg (arg); // (no more actions:) return 0; } // Command_Echo // // Command: Transmit data object from client // (== receive by server) // // (transfer: receive from client) struct Receive_DataEx : A_DataEx { File_Output file_receive; unsigned file_length; // (constructor) Receive_DataEx (char const *path, char const *user, char const *name, unsigned length) : file_receive (name, user, path) { file_length = length; } bool transfer (Client_Action &client) { return client. receive_to_file (file_receive, file_length); } // transfer // (virtual destructor) ~ Receive_DataEx () {} }; // Receive_DataEx static A_DataEx * Command_Transmit (Client_Action &action, char *command, char *arguments, Query_Context &context) { // expect arguments: file_name + file_len char *file_name; unsigned file_len; if (get_arg_file (arguments, file_name) && get_arg_hex (arguments, file_len) ) { if (context.perm_guard (ALLOW_OWNER, file_name)) { if (! file_len && ! permit_zeroTransmit) { action. debug_fail ("Empty transmit blocked"); action. reply_error (">>", "E_EMPTY"). reply_arg (file_name); return 0; } action. debug_label ("Transmit"). out_QFile (context, file_name). out_tab (). out_ch ('['). out_dec (file_len). out_ch (']'). out_nl (); action. reply_cmd ("<<"). reply_arg (file_name). reply_hex (file_len, true); return new ("Receive_DataEx") Receive_DataEx (context.server_path, context.user_name, file_name, file_len); } else { action. debug_fail ("Transmit permission failure"); action. reply_error (">>", "E_GUARD"). reply_arg (file_name); return 0; } } action. debug_fail ("Transmit arguments failure"); action. reply_error (">>", "E_ARGS"); return 0; // (arguments failure!) } // Command_Transmit // // Command: Receive data object to client // (== transmit by server) // // (transfer: transmit to client) struct Transmit_DataEx : A_DataEx { File_Input file_transmit; unsigned file_length; Transmit_DataEx (char const *path, char const *user, char const *name) : file_transmit (name, user, path) { file_length = 0; } bool startup () { return file_transmit.open () && file_transmit.get_length (file_length); } // startup bool transfer (Client_Action &action) { return action. transmit_from_file (file_transmit, file_length) && file_transmit.close (); } // transfer // (virtual destructor) ~ Transmit_DataEx () {} }; // Transmit_DataEx static A_DataEx * Command_Receive (Client_Action &action, char *command, char *arguments, Query_Context &context) { // expect arguments: file_name char *file_name; if (get_arg_file (arguments, file_name)) { if (context.perm_guard (ALLOW_ANY, file_name)) { Transmit_DataEx *transmit = new ("Transmit_DataEx") Transmit_DataEx (context.server_path, context.user_name, file_name); if (transmit->startup ()) { unsigned file_len = transmit->file_length; if (! file_len && ! permit_zeroReceive) { action. debug_fail ("Empty receive blocked"); action. reply_error ("<<", "E_EMPTY"). reply_arg (file_name); return 0; } action. debug_label ("Receive"). out_QFile (context, file_name). out_tab (). out_ch ('['). out_dec (file_len). out_ch (']'). out_nl (); action. reply_cmd (">>"). reply_arg (file_name). reply_hex (file_len, true); return transmit; } } else { // report error: action. debug_fail ("Receive permission failure"); action. reply_error ("<<", "E_GUARD"). reply_arg (file_name); return 0; } return 0; } else { action. debug_fail ("Receive arguments failure"); action. reply_error ("<<", "E_ARGS"); } return 0; // (arguments failure!) } // Command_Receive // // Command: Query length of (server) data object // static A_DataEx * Command_Length (Client_Action &action, char *command, char *arguments, Query_Context &context) { // expect arguments: file_name char *file_name; if (get_arg_file (arguments, file_name)) { if (context.perm_guard (ALLOW_ANY, file_name)) { File_Input file_checkup (file_name, context.user_name, context.server_path); unsigned file_length; if (file_checkup.open () && file_checkup.get_length (file_length) && file_checkup.close ()) { action. debug_label ("LengthOf"). out_QFile (context, file_name). out_tab (). out_ch ('['). out_dec (file_length). out_ch (']'). out_nl (); action. reply_cmd ("##"). reply_arg (file_name). reply_hex (file_length, true); return 0; } else { // report error: action. reply_error ("##", "E_LENGTH"). reply_arg (file_name); return 0; } } else { // report error: action. debug_fail ("Length permission failure"); action. reply_error ("##", "E_GUARD"). reply_arg (file_name); return 0; } } else { action. debug_fail ("Length arguments failure"); action. reply_error ("##", "E_ARGS"); } return 0; // (arguments failure!) } // Command_Length // // Command: Query change time of (server) data object // static A_DataEx * Command_Stamp (Client_Action &action, char *command, char *arguments, Query_Context &context) { // expect arguments: file_name char *file_name; if (get_arg_file (arguments, file_name)) { if (context.perm_guard (ALLOW_ANY, file_name)) { File_Input file_checkup (file_name, context.user_name, context.server_path); time_t file_chtime; if (file_checkup.open () && file_checkup.get_chtime (file_chtime) && file_checkup.close ()) { action. debug_label ("StampOf"). out_QFile (context, file_name). out_tab (). dump_time (file_chtime, true). out_nl (); action. reply_cmd ("?^"). reply_arg (file_name). reply_hex (file_chtime, true); return 0; } else { // report error: action. reply_error ("?^", "E_STAMP"). reply_arg (file_name); return 0; } } else { // report error: action. debug_fail ("StampOf permission failure"); action. reply_error ("?^", "E_GUARD"). reply_arg (file_name); return 0; } } else { action. debug_fail ("StampOf arguments failure"); action. reply_error ("?^", "E_ARGS"); } return 0; // (arguments failure!) } // Command_Stamp // // Command: Query number of items in container object // static A_DataEx * Command_Total (Client_Action &action, char *command, char *arguments, Query_Context &context) { // expect arguments: file_name char *file_name; if (get_arg_file (arguments, file_name)) { if (context.perm_guard (ALLOW_ANY, file_name)) { File_InOut file_checkup (file_name, context.user_name, context.server_path); unsigned items_no; if (file_checkup.open_read () && Container::total_Multi (file_checkup, items_no)) { action. debug_label ("TotalOf"). out_QFile (context, file_name). out_tab (). out_ch ('#'). out_dec (items_no). out_nl (); action. reply_cmd ("<#>"). reply_arg (file_name). reply_hex (items_no, true); return 0; } else { // report error: action. reply_error ("<#>", "E_TOTAL"). reply_arg (file_name); return 0; } } else { // report error: action. debug_fail ("Total permission failure"); action. reply_error ("<#>", "E_GUARD"). reply_arg (file_name); return 0; } } else { action. debug_fail ("Total arguments failure"); action. reply_error ("<#>", "E_ARGS"); } return 0; // (arguments failure!) } // Command_Total // // Command: Init container object // static A_DataEx * Command_Create (Client_Action &action, char *command, char *arguments, Query_Context &context) { // expect arguments: file_name char *file_name; if (get_arg_file (arguments, file_name)) { if (context.perm_guard (ALLOW_OWNER, file_name)) { File_InOut file_init (file_name, context.user_name, context.server_path); if (file_init.open_create ()) { action. debug_label ("CreateC"). out_QFile (context, file_name). out_nl (); Container::init_Multi (file_init); action. reply_cmd ("^^"). reply_arg (file_name); file_init.close (); return 0; } else { // report error: action. debug_label ("Object create failure") . out_cstr (file_name). out_nl (); action. reply_error ("^^", "E_CREATE"). reply_arg (file_name); return 0; } } else { // report error: action. debug_fail ("Object create permission failure"); action. reply_error ("^^", "E_GUARD"). reply_arg (file_name); return 0; } } else { action. debug_fail ("Object create arguments failure"); action. reply_error ("^^", "E_ARGS"); } return 0; // (arguments failure!) } // Command_Create // // Command: Add entry to container // static void load_server_meta (Container &content, char const *host_name, char const *user_name, unsigned client_IP) { // (host name) if (host_name) content.add_item ("Host", strlen (host_name), host_name); // (user name) if (user_name) content.add_item ("User", strlen (user_name), user_name); // (server time stamp) Time Now (true); Now.add_container ("Time", content); // (client IP) IPAddr ClientIP (client_IP); ClientIP.add_container ("C_IP", content); } // load_server_meta // (transfer: append to container) struct Append_DataEx : A_DataEx { File_InOut file_append; char const *entry_name; unsigned entry_length; char const *host_name; char const *user_name; // (constructor) Append_DataEx (char const *path, char const *user, char const *name) : file_append (name, user, path) { entry_name = 0; entry_length = 0; host_name = user_name = 0; } // Append_DataEx // (startup: open container file...) bool startup () { return file_append.open_update (); } // startup // (set actual file name && length) void set_params (char const *_name, unsigned _length, char const *_host_name, char const *_user_name) { entry_name = _name; entry_length = _length; host_name = _host_name; user_name = _user_name; } // set_params // (actual data transfer) bool transfer (Client_Action &action) { // // create server metadata container: // char const *_meta_tag = "ServMeta"; Container content; load_server_meta (content, host_name, user_name, action.IP); bool result = action. insert_container (file_append, _meta_tag, content, entry_name, entry_length); content.release_all (CRel_Self); return result && file_append.close (); } // transfer // (virtual destructor) ~ Append_DataEx () {} }; // Append_DataEx static A_DataEx * Command_Append (Client_Action &action, char *command, char *arguments, Query_Context &context) { // expect arguments: file_name char *file_name; char *entry_name; unsigned entry_size; if (get_arg_file (arguments, file_name) && get_arg_entry (arguments, entry_name) && get_arg_hex (arguments, entry_size)) { if (context.perm_guard (ALLOW_REPLY, file_name)) { Append_DataEx *append = new ("Append_DataEx") Append_DataEx (context.server_path, context.user_name, file_name); if (! entry_size && ! permit_zeroAppend) { action. debug_fail ("Empty append blocked"); action. reply_error (">>", "E_EMPTY"). reply_arg (file_name); return 0; } if (append->startup ()) { action. debug_label ("AppendC"). out_QFile (context, file_name). out_label (" :"). out_qstr (entry_name, '{', '}'). out_tab (). out_ch ('['). out_dec (entry_size). out_ch (']'). out_nl (); action. reply_cmd ("++"). reply_arg (file_name). reply_arg (entry_name). reply_hex (entry_size, true); append->set_params (entry_name, entry_size, context.host_name, context.login_name); return append; } else { // report error: action. reply_error ("++", "E_APPEND"). reply_arg (file_name); return 0; } } else { // report error: action. debug_fail ("Append permission failure"); action. reply_error ("++", "E_GUARD"). reply_arg (file_name); return 0; } } // (file_name) else { action. debug_fail ("Append arguments failure"); action. reply_error ("++", "E_ARGS"); return 0; // (arguments failure!) } } // Command_Append // // Command: retrieve entry from container // // Iterator to retrieve required entry from container struct Iterator_Retrieve : MultiRead_Iterator { char const *lookup_name; unsigned counter; char * &record_data; unsigned &record_size; // (constructor) Iterator_Retrieve (A_InOut &_inout, char const *_lookup_name, unsigned index, char * &_record_data, unsigned &_record_size) : MultiRead_Iterator (_inout), record_data (_record_data), record_size (_record_size) { lookup_name = _lookup_name; counter = index + 1; } // Iterator_Retrieve // (on begin) bool on_begin (unsigned offset_final) { return true; // (do nothing) } // on_begin // (on entry) bool on_entry (char const *entry_name, unsigned entry_index, unsigned entry_size) { if (strcmp (entry_name, lookup_name) == 0) { if (counter && ! -- counter) { char *entry_content = new ("retrieve_content") char [entry_size]; if (_inout.read (entry_content, entry_size) == entry_size) { record_data = entry_content; record_size = entry_size; } else { delete entry_content; } } } return true; } // on_entry // (on final) bool on_final (unsigned total_count) { return true; } // on_final }; // Iterator_Retrieve // (transfer: retrieve from container) struct Retrieve_DataEx : A_DataEx { File_InOut file_retrieve; char const *entry_name; unsigned entry_index; char *entry_data; unsigned entry_size; // (constructor) Retrieve_DataEx (char const *path, char const *user, char const *name) : file_retrieve (name, user, path) { entry_name = 0; entry_index = 0; } // Retrieve_DataEx // (do retrieve data from container) bool retrieve_content (char const *_name, unsigned _index) { entry_name = _name; entry_index = _index; if (file_retrieve.open_read ()) { Iterator_Retrieve retriever (file_retrieve, entry_name, entry_index, entry_data, entry_size); if (! Container::iterate_Multi (retriever)) return false; file_retrieve.close (); // (data read OK) if (entry_data) return true; } // (something failed...) return false; } // retrieve_content // (actual data transfer) bool transfer (Client_Action &action) { bool result = action.write (entry_data, entry_size);; delete entry_data; return result; } // transfer // (virtual destructor) ~ Retrieve_DataEx () {} }; // Retrieve_DataEx static A_DataEx * Command_Retrieve (Client_Action &action, char *command, char *arguments, Query_Context &context) { // expect arguments: file_name char *file_name; char *entry_name; unsigned entry_index; if ( get_arg_file (arguments, file_name) && get_arg_entry (arguments, entry_name) && get_arg_hex (arguments, entry_index) ) { if (context.perm_guard (ALLOW_USER, file_name)) { Retrieve_DataEx *retrieve = new ("Retrieve_DataEx") Retrieve_DataEx (context.server_path, context.user_name, file_name); if (retrieve->retrieve_content (entry_name, entry_index)) { action. debug_label ("RetrieveC"). out_QFile (context, file_name). out_label (':'). out_qstr (entry_name, '{', '}'). out_tab (). out_ch ('['). out_dec (retrieve->entry_size). out_ch (']'). out_nl (); action. reply_cmd (">?<"). reply_arg (file_name). reply_arg (entry_name). reply_hex (entry_index, true). reply_hex (retrieve->entry_size, true); return retrieve; } else { // report error: action. reply_error (">?<", "E_RETRIEVE"). reply_arg (file_name); return 0; } } else { // report error: action. debug_fail ("Retrieve permission failure"); action. reply_error (">?<", "E_GUARD"). reply_arg (file_name); return 0; } } // (arguments) else { action. debug_fail ("Retrieve arguments failure"); action. reply_error (">?<", "E_ARGS"); } return 0; // (arguments failure!) } // Command_Retrieve // // Command: replace entry in container // // Iterator to replace required entry in container struct Replace_Iterator : MultiCopy_Iterator { char const *lookup_name; unsigned counter; char * record_data; unsigned record_size; // constructor Replace_Iterator (A_InOut &input, A_InOut &output, char const *_lookup_name, unsigned index, char *_rec_data, unsigned _rec_size) : MultiCopy_Iterator (input, output) { lookup_name = _lookup_name; counter = index + 1; record_data = _rec_data; record_size = _rec_size; } // Replace_Iterator // Insert entry 'content' in 'output' (with 'content_name' && 'content_index') bool insert_content_data (char const * content_name, unsigned content_index) { if (Container::write_head (_output, content_name) && _output.put_value (content_index) && _output.put_value (record_size) ) { ++ actual_count; return _output.write (record_data, record_size) && _output.put_byte (0); } // (something failed!) return false; } // insert_content_data // (on entry) bool on_entry (char const *entry_name, unsigned entry_index, unsigned entry_size) { if (strcmp (entry_name, lookup_name) == 0 && ! -- counter) { // (required entry found: replace it!) skip_entry (entry_size); insert_content_data (entry_name, entry_index); return true; } else // (plain copy entry) return MultiCopy_Iterator::on_entry (entry_name, entry_index, entry_size); } // on_entry }; // Replace_Iterator // Create temporary file name // (based on 'file_name') static char *temp_file (char const *file_name, char temp_ch, char const *file_ext) { unsigned len = strlen (file_name); unsigned ext_len = strlen (file_ext); if (memcmp (file_name + len - ext_len, file_ext, ext_len) == 0) { // (create temporary file) char *temp_name = new ("temp_name") char [len + 1 + 1]; temp_name [len + 1] = '\0'; len -= ext_len; memcpy (temp_name, file_name, len); temp_name [len] = temp_ch; memcpy (temp_name + len + 1, file_ext, ext_len); return temp_name; } // (valid) // (failed!!!) return 0; } // temp_file // Move file: 'file_from' -> 'file_to' static bool file_move (char const *file_from, char const *file_to) { File_System _FS_ (0); return _FS_.delete_file (file_to) && _FS_.rename_file (file_from, file_to); } // file_move // Actually: do replace entry in container static bool container_replace_entry (File_InOut &file_input, File_InOut &file_output, char const *entry_name, unsigned entry_index, char *entry_data, unsigned entry_size) { bool do_replace = false; if (strcmp (file_input.filename, file_output.filename) == 0) { // (input same as output) file_output.filename = temp_file (file_input.filename, '~', ".blc"); do_replace = true; } if (file_input.open_read () && file_output.open_create ()) { Replace_Iterator _iterator (file_input, file_output, entry_name, entry_index, entry_data, entry_size); bool result = Container::iterate_Multi (_iterator); // TTT: check count! file_input.close (); file_output.close (); if (do_replace) { file_move (file_output.filename, file_input.filename); } return result; } return false; } // container_replace_entry // (transfer: replace in container) struct Replace_DataEx : A_DataEx { File_InOut file_input; File_InOut file_output; char const *entry_name; unsigned entry_index; unsigned entry_size; // (constructor) Replace_DataEx (char const *path, char const *user, char const *input_file, char const *output_file, char const *entry_name, unsigned entry_index, unsigned entry_size) : file_input (input_file, user, path), file_output (output_file, user, path) { this->entry_name = entry_name; this->entry_index = entry_index; this->entry_size = entry_size; } // Replace_DataEx // (check source file existence) bool source_verify () { File_System _FS_ (0); if (_FS_.is_exist (file_input.filename)) return true; return false; } // source_verify // (actual data transfer) bool transfer (Client_Action &action) { char *entry_data = new ("_entry_data_") char [entry_size]; int count = action.read (entry_data, entry_size); bool result = false; if (count == entry_size) { client_entry_verify (entry_data, entry_size); result = container_replace_entry (file_input, file_output, entry_name, entry_index, entry_data, entry_size); } delete [] entry_data; return result; } // transfer // (virtual destructor) ~ Replace_DataEx () {} }; // Replace_DataEx static A_DataEx * Command_Replace (Client_Action &action, char *command, char *arguments, Query_Context &context) { // expect arguments: file_names char *infile_name, *outfile_name; char *entry_name; unsigned entry_index, entry_size; if ( get_arg_file (arguments, infile_name) && get_arg_file (arguments, outfile_name) && get_arg_entry (arguments, entry_name) && get_arg_hex (arguments, entry_index) && get_arg_hex (arguments, entry_size) ) { // (infile == outfile??) if (*outfile_name == '$' && outfile_name [1] == '\0') outfile_name = infile_name; if (! entry_size && ! permit_zeroReplace) { action. debug_fail ("Empty replace blocked"); action. reply_error (">>", "E_EMPTY"). reply_arg (infile_name). reply_arg (outfile_name); return 0; } if (context.perm_guard (ALLOW_OWNER, infile_name)) { Replace_DataEx *replace = new ("Replace_DataEx") Replace_DataEx (context.server_path, context.user_name, infile_name, outfile_name, entry_name, entry_index, entry_size); if (replace->source_verify ()) { action. debug_label ("ReplaceC"). out_QFile (context, infile_name). out_ch ('/'). out_QFile (context, outfile_name). out_label (':'). out_qstr (entry_name, '{', '}'). out_tab (). out_ch ('#'). out_dec (entry_index). out_tab (). out_ch ('['). out_dec (entry_size). out_ch (']'). out_nl (); action. reply_cmd ("<-/+>"). reply_arg (infile_name). reply_arg (outfile_name). reply_arg (entry_name). reply_hex (entry_index, true); return replace; } else { // report error: action. reply_error ("<-/+>", "E_REPLACE"). reply_arg (infile_name). reply_arg (outfile_name); return 0; } } else { // report error: action. debug_fail ("Replace permission failure"); action. reply_error ("<-/+>", "E_GUARD"). reply_arg (infile_name). reply_arg (outfile_name); return 0; } } else { action. debug_fail ("Replace arguments failure"); action. reply_error ("<-/+>", "E_ARGS"); return 0; // (arguments failure!) } return 0; // (something failed) } // Command_Replace // // Commands: Server time // static A_DataEx * Command_Clock (Client_Action &action, char *command, char *arguments, Query_Context &context) { time_t Time = time (NULL); action. debug_label ("Time"). out_hex (Time). out_nl (); action. reply_cmd ("@!"). reply_hex (Time, true); return 0; } // Command_Clock // // Commands: User enter / leave // static A_DataEx * Command_Enter (Client_Action &action, char *command, char *arguments, Query_Context &context) { char const *user_name; if (get_arg_user (arguments, user_name)) { if (context.check_username (user_name)) { context.user_name = user_name; action. debug_label ("Enter"). out_cstr (user_name). out_nl (); action. reply_cmd ("]]"). reply_arg (user_name); } else { // report error: action. reply_error ("[[", "E_ENTER"). reply_arg (user_name); } } else { // (report enter error:) action. reply_error ("[[", "E_ARGS"); } return 0; } // Command_Enter static A_DataEx * Command_Leave (Client_Action &action, char *command, char *arguments, Query_Context &context) { if (context.user_name) { char const *user_name = context.user_name; context.user_name = 0; action. debug_label ("Leave"). out_cstr (user_name). out_nl (); action. reply_cmd ("[["). reply_arg (user_name); } else { // (not entered -- report error) action. debug_fail ("Leave context failure"); action. reply_error ("]]", "E_LEAVE"); } return 0; } // Command_Leave // // Users login / logout control // struct Authen_Control { // [User name] char const *auth_user; // [IP address, preserved] unsigned auth_IP; // [Auth ticket] unsigned auth_ticket; // (next entry in chain) Authen_Control *follow; // Constructor Authen_Control (char const *auth_user, unsigned auth_IP); // Destructor ~Authen_Control (); // // Global statics // // Control operation: Log in static bool server_login (char const *username, unsigned IP_addr, unsigned &auth_ticket); // Control operation: Authen static bool server_authen (char const *&username, unsigned IP_addr, unsigned auth_ticket); // Control operation: Log out static bool server_logout (char const *username, unsigned IP_addr, unsigned auth_ticket); // Dump all logged users static unsigned dump_all (Client_Action &action); // Global chain static Authen_Control * _chain; }; // Authen_Control // // Commands: User login / logout / confirm // // (User logging in) static A_DataEx * Command_Login (Client_Action &action, char *command, char *arguments, Query_Context &context) { char const *user_name; if (get_arg_user (arguments, user_name)) { char const *password; unsigned auth_ticket = 0; if (context.check_username (user_name) && get_arg_password (arguments, password) && context.check_userpass (user_name, password)) { if (Authen_Control::server_login (user_name, action.IP, auth_ticket)) { action. debug_label ("Login"). out_cstr (user_name). out_tab (). out_qstr (password, '<', '>'). out_cstr (" -> "). out_ch ('#'). out_hex (auth_ticket). out_nl (); action. reply_cmd ("}}"). reply_arg (user_name). reply_hex (auth_ticket, true); } } else { // (login failure!) action. debug_label ("Login failure"). out_cstr (user_name). out_excl (). out_nl (); action. reply_error ("{{", "E_LOGIN"); } } else { // report error: action. debug_fail ("Login arguments failure"); action. reply_error ("{{", "E_ARGS"); } return 0; } // Command_Login // (User logging probe) static A_DataEx * Command_LogProbe (Client_Action &action, char *command, char *arguments, Query_Context &context) { char const *user_name; if (get_arg_user (arguments, user_name)) { char const *password; if (context.check_username (user_name) && get_arg_password (arguments, password) && context.check_userpass (user_name, password)) { action. debug_label ("Logprobe"). out_cstr (user_name). out_tab (). out_qstr (password, '<', '>'). out_nl (); action. reply_cmd ("{!!}"). reply_arg (user_name); } else { // (login probe failure!) action. debug_label ("Logprobe failure"). out_cstr (user_name). out_excl (). out_nl (); action. reply_error ("{{", "E_LOGIN"); } } else { // report error: action. debug_fail ("Logprobe arguments failure"); action. reply_error ("{{", "E_ARGS"); } return 0; } // Command_LogProbe // (User logging out) static A_DataEx * Command_Logout (Client_Action &action, char *command, char *arguments, Query_Context &context) { char const *user_name; unsigned auth_ticket; if (get_arg_user (arguments, user_name) && get_arg_hex (arguments, auth_ticket)) { if (Authen_Control::server_logout (user_name, action.IP, auth_ticket)) { action. debug_label ("Logout"). out_cstr (user_name). out_tab (). out_ch ('#'). out_hex (auth_ticket). out_nl (); action. reply_cmd ("{{"). reply_arg (user_name). reply_hex (auth_ticket, true); } else { action. debug_label ("Logout failure"). out_cstr (user_name). out_excl (). out_nl (); action. reply_error ("}}", "E_LOGOUT"); } } else { // report error: action. debug_fail ("Logout arguments failure"); action. reply_error ("}}", "E_ARGS"); } return 0; } // Command_Logout // (User logging confirm) static A_DataEx * Command_Authen (Client_Action &action, char *command, char *arguments, Query_Context &context) { char const *username = 0; unsigned auth_ticket; if (get_arg_hex (arguments, auth_ticket)) { if (Authen_Control::server_authen (username, action.IP, auth_ticket)) { action. debug_label ("Authen"). out_ch ('#'). out_hex (auth_ticket). out_cstr (" -> "). out_cstr (username). out_nl (); context.login_name = username; // (user login name...) action. reply_cmd ("}{"). reply_hex (auth_ticket, true). reply_arg (username); } else { // report error: action. debug_label ("Authen failure"). out_hex (auth_ticket). out_excl (). out_nl (); action. reply_error ("{}", "E_LOGUP"); } } else { action. debug_fail ("Authen arguments failure"); action. reply_error ("{}", "E_ARGS"); } return 0; } // Command_Authen // (User password change) static A_DataEx * Command_ChangePassword (Client_Action &action, char *command, char *arguments, Query_Context &context) { char const *user_name; unsigned auth_ticket; char const *password; if (get_arg_user (arguments, user_name) && get_arg_hex (arguments, auth_ticket)) { if (Authen_Control::server_authen (user_name, action.IP, auth_ticket)) { action. debug_label ("Logup validated"). out_hex (auth_ticket). out_cstr (" -> "). out_cstr (user_name). out_nl (); if (get_arg_password (arguments, password)) { action. reply_cmd ("{>=<}"). reply_arg (user_name); context.change_password (action.logger, user_name, password); } return 0; } else { // report error: action. debug_label ("Logup failure"). out_hex (auth_ticket). out_excl (). out_nl (); action. reply_error ("{}", "E_LOGUP"); } } else { action. debug_fail ("Password change arguments failure"); action. reply_error ("{}", "E_ARGS"); } return 0; } // Command_ChangePassword // // Authen_Control implementation // // Login/logout counts: static unsigned login_count = 0, logout_count = 0; // Global control chain Authen_Control * Authen_Control::_chain = 0; // (constructor) Authen_Control::Authen_Control (char const *auth_user, unsigned auth_IP) { char * _user = new ("auth/user") char [strlen (auth_user) + 1]; strcpy (_user, auth_user); this->auth_user = _user; this->auth_IP = auth_IP; auth_ticket = 0; // (no ticket!) follow = 0; } // Authen_Control::Authen_Control // (destructor) Authen_Control::~Authen_Control () { auth_user = 0; auth_IP = 0; auth_ticket = 0; } // Authen_Control::~Authen_Control // (Create unique ticket) static unsigned create_ticket (unsigned previous, time_t Time) { // TODO: better ticket... previous += 64; return 0xAF0000 | (previous & ~63) | (Time & 63); } // create_ticket // Control operation: Log in bool Authen_Control::server_login (char const *username, unsigned IP_addr, unsigned &auth_ticket) { Authen_Control *control = new ("[Authen:Control]") Authen_Control (username, IP_addr); time_t Now = time (NULL); auth_ticket = _chain ? _chain->auth_ticket : 0; control->follow = _chain; _chain = control; auth_ticket = control->auth_ticket = create_ticket (auth_ticket, Now); ++ login_count; return true; } // Authen_Control::server_login // Control operation: Log up bool Authen_Control::server_authen (char const *&username, unsigned IP_addr, unsigned auth_ticket) { // (Scan through chain ...) for (Authen_Control *_ptr = _chain; _ptr; _ptr = _ptr->follow) if (_ptr->auth_IP == IP_addr && _ptr->auth_ticket == auth_ticket) { // found! username = _ptr->auth_user; return true; } return false; // (not found) } // Authen_Control::server_authen // Control operation: Log out bool Authen_Control::server_logout (char const *username, unsigned IP_addr, unsigned auth_ticket) { Authen_Control *_control; // (Scan through chain) for (Authen_Control **_ptr = &_chain; (_control = *_ptr); _ptr = &(*_ptr)->follow) if (_control->auth_IP == IP_addr && _control->auth_ticket == auth_ticket) { // entry found? Check username ... if (strcmp (_control->auth_user, username) == 0) { delete [] _control->auth_user; _control->auth_user = 0; _control->auth_IP = 0; _control->auth_ticket = 0; // (remove node from chain:) *_ptr = _control->follow; // (remove self!) delete _control; ++ logout_count; return true; // (success!) } // (user match) else break; } return false; // (fail or not found) } // Authen_Control::server_logout // Dump all logged users unsigned Authen_Control::dump_all (Client_Action &action) { action. debug_label ("Logged users list"). out_nl (); unsigned count = 0; for (Authen_Control *_control = _chain; _control; _control = _control->follow) { action. debug_label (0). out_tab (). out_ch ('#'). out_dec (count + 1). out_tab (). out_cstr (_control->auth_user). out_tab (). out_hex (_control->auth_ticket). out_nl (); ++ count; } // for (_control) action. debug_label ("Total"). out_dec (count). out_nl (); } // Authen_Control::dump_all // // Command: Rename remote file // static bool file_rename (char const *file_from, char const *file_to, Query_Context &context) { File_System _FS_ (0); PathDir location; context.setup (location); File_InOut File_from (file_from, location); File_InOut File_to (file_to, location); return _FS_.rename_file (File_from.filename, File_to.filename); } // file_rename static A_DataEx * Command_Rename (Client_Action &action, char *command, char *arguments, Query_Context &context) { // expect arguments: file_names char *file_from, *file_to; if ( get_arg_file (arguments, file_from) && get_arg_file (arguments, file_to) ) { if (context.perm_guard (ALLOW_OWNER, file_from)) { if (file_rename (file_from, file_to, context)) { action. debug_label ("Rename"). out_QFile (context, file_from). out_cstr (" :: "). out_QFile (context, file_to). out_nl (); action. reply_cmd (">->"). reply_arg (file_from). reply_arg (file_to); } else { // report error: action. debug_label ("Rename failure"). out_QFile (context, file_from). out_cstr (" :: "). out_QFile (context, file_to). out_excl (). out_nl (); action. reply_error (">->", "E_RENAME"); } } else { action. debug_fail ("Rename permission failure"); action. reply_error (">->", "E_GUARD"). reply_arg (file_from). reply_arg (file_to); return 0; } } else { action. debug_fail ("Rename arguments failure"); action. reply_error (">->", "E_ARGS"); } return 0; } // Command_Rename // // Command: Delete remote file // static bool file_delete (char const *file_name, Query_Context &context) { File_System _FS_ (0); PathDir location; context.setup (location); File_InOut File_name (file_name, location); return _FS_.delete_file (File_name.filename); } // file_delete static A_DataEx * Command_Delete (Client_Action &action, char *command, char *arguments, Query_Context &context) { // expect arguments: file_name char *file_name; if (get_arg_file (arguments, file_name)) { if (context.perm_guard (ALLOW_OWNER, file_name)) { if (file_delete (file_name, context)) { action. debug_label ("Delete"). out_QFile (context, file_name). out_nl (); action. reply_cmd (">*<"). reply_arg (file_name); } else { // report error: action. debug_label ("Delete failure"). out_QFile (context, file_name). out_nl (); action. reply_error (">*<", "E_DELETE"); } } else { action. debug_fail ("Delete permission failure"); action. reply_error (">*<", "E_GUARD"). reply_arg (file_name); } } // (argument OK!) else { action. debug_fail ("Delete arguments failure"); action. reply_error (">*<", "E_ARGS"); } return 0; } // Command_Delete // // Command: Touch remote file // static bool file_touch (char const *file_name, time_t time_stamp, bool time_flag, Query_Context &context) { File_System _FS_ (0); PathDir location; context.setup (location); File_InOut File_name (file_name, location); return _FS_.file_change_time (File_name.filename, time_stamp, time_flag); } // file_touch static A_DataEx * Command_Touch (Client_Action &action, char *command, char *arguments, Query_Context &context) { // expect arguments: file_name char *file_name; unsigned arg_time; if (get_arg_file (arguments, file_name) && get_arg_hex (arguments, arg_time)) { if (context.perm_guard (ALLOW_OWNER, file_name)) { time_t file_time = arg_time; if (file_touch (file_name, file_time, true, context)) { action. debug_label ("Touch"). out_QFile (context, file_name). out_tab (). out_hex (file_time). out_nl (); action. reply_cmd ("<!>"). reply_arg (file_name). reply_hex (file_time, true); } else { // report error: action. debug_label ("Touch failure"). out_QFile (context, file_name). out_nl (); action. reply_error (">!<", "E_TOUCH"); } } else { action. debug_fail ("Touch permission failure"); action. reply_error (">!<", "E_GUARD"). reply_arg (file_name); } } // (argument OK!) else { action. debug_fail ("Touch arguments failure"); action. reply_error (">!<", "E_ARGS"); } return 0; } // Command_Touch // // Command: list logged in // static A_DataEx * Command_Who (Client_Action &action, char *command, char *arguments, Query_Context &context) { if (context.perm_guard (ALLOW_MONIT, 0)) { unsigned count = Authen_Control::dump_all (action); action. reply_cmd ("~{?}"). reply_hex (count, true); } return 0; } // Command_Who // // Commands: Server version // static char const * Version = "0.0.32"; static A_DataEx * Command_Version (Client_Action &action, char *command, char *arguments, Query_Context &context) { action. debug_label ("Version"). out_cstr (Version). out_nl (); action. reply_cmd ("???"). reply_arg (Version); return 0; } // Command_Version // // Command: Shutdown (terminate server) // static A_DataEx * Command_ShutDown (Client_Action &action, char *command, char *arguments, Query_Context &context) { void do_exit (); // expect arguments: shutdown reason char const *shut_reason = get_arg (arguments); if (! shut_reason) shut_reason = "???"; if (context.perm_guard (ALLOW_ADMIN, 0)) { action. debug_label ("ShutDown"). out_cstr (shut_reason). out_nl (); action. reply_cmd ("!!!"). reply_arg (shut_reason); do_exit (); } else { action. debug_fail ("Shutdown permission failure"); action. reply_error ("!!!", "E_GUARD"). reply_arg (shut_reason); } return 0; } // Command_ShutDown // // Commands table // Server_Command Client_Action::command_table [] = { "==", "Echo query to reply", Command_Echo, "##", "Query length of file", Command_Length, "?^", "Query time stamp of file", Command_Stamp, "<<", "Receive file from server", Command_Receive, ">>", "Transmit file to server", Command_Transmit, "^^", "Create new container storage on server", Command_Create, "++", "Append new entry to storage", Command_Append, "<#>", "Query total entries in storage", Command_Total, "<?>", "Retrieve entry from storage", Command_Retrieve, "<+/->", "Replace entry in storage", Command_Replace, "@!", "Query server current time", Command_Clock, "[[", "Enter user storage area", Command_Enter, "]]", "Leave user storage area", Command_Leave, "{{", "Log in as user", Command_Login, "}}", "Log out as user", Command_Logout, "{}", "Login authentification", Command_Authen, "{??}", "Login verify", Command_LogProbe, "{<=>}", "Change user password", Command_ChangePassword, ">!<", "Change time of file on server", Command_Touch, ">->", "Rename file on server", Command_Rename, ">*<", "Delete file on server", Command_Delete, "~{?}", "List users logged in", Command_Who, "???", "Server version", Command_Version, "!!!", "Server shutdown", Command_ShutDown, // // [ end of command list ] // 0, 0, 0 }; // Client_Action::command_table // (current host name) char const * host_name = 0; // (server root directory) char * server_dir = 0; // Dump commands list void commands_dump (A_Console &logger) { unsigned i; logger. out_cstr ("Server commands:"). out_nl (); for (i = 0; ; i ++) { Server_Command &command = Client_Action::command_table [i]; if (command.cmd_name && command.cmd_info) { logger.out_dec (i). out_label (':'). out_cstr (command.cmd_name). out_tab (). out_cstr (command.cmd_info). out_nl (); } else break; } // for (i) logger. out_label ("total:"). out_dec (i). out_ch ('.'). out_nl (); } // commands_dump // // Client thread running... // int Client_Action::request_handler () { reset (); if (receive_query ()) { Query_Context Q_Context (server_dir, host_name); unsigned q_count = 0; dump_query (); // (dump query) // (???) memcpy (client_reply, client_query, sizeof (client_reply)); // (parse: request -> reply) unsigned total_size = (*client_query) & 0xFF; char *command = client_query + 1; unsigned cmd_len; while (total_size && (cmd_len = strlen (command))) { A_DataEx *exchange_action = parse_query (command, Q_Context); dump_reply (); // (dump reply) transmit_reply (); // (transmit reply) if (exchange_action) { if (! exchange_action->transfer (* this)) logger. out_cstr ("Transfer failure."). out_nl (); delete exchange_action; } // (next command in sequence) command += cmd_len + 1; total_size -= cmd_len + 1; ++ q_count; } // while (size) return q_count; } // if (request received) complete (); return 0; } // Client_Action::request_handler // // Signals handling // static int signal_state = -1; void signal_handler (int sig_num) { MM_console->out_label ("Signal received:"). out_dec (sig_num). out_nl (); signal_state = sig_num; } // signal_handler // (exit action) void do_exit () { ++ signal_state; } // (Client thread:) DWORD WINAPI ClientThread (LPVOID argument) { Client_Action *client = (Client_Action *) argument; if (client) return client->request_handler (); return 0; } // ClientThread // Query host name char *local_host_name () { static char host_buffer [127 + 1]; if (gethostname (host_buffer, sizeof (host_buffer) - 1) == 0) return host_buffer; } // local_host_name // (Wait for ENTER key) void key_wait () { File_Input Con_Input (0); File_Output Con_Output (1); char const *message = "Press [ENTER] to finish..."; char ch; Con_Output.write (message, strlen (message)); while (Con_Input.get_byte (ch)) if (ch == '\n') break; Con_Output.put_byte ('\n'); } // key_wait // // Arguments parsing // static struct Argument { char const *name; // (arg) Argument *arg_next; void install (); // (constructor) Argument (char const *_name) { name = _name; arg_next = 0; install (); } // Argument // (dump to logger) virtual void dump (A_Console &logger); // (parse argument) virtual bool parse (char const *val_ptr, unsigned val_len); // (default value, when NOT provided) virtual void unset (); // (release option) virtual void release (); // (assign from string:) bool assign (char const *source) { return parse (source, strlen (source)); } // assign } *arg_first = 0; // (install argument in chain) void Argument::install () { arg_next = arg_first; arg_first = this; } // Argument::install void Argument::dump (A_Console &logger) {} bool Argument::parse (char const *val_ptr, unsigned val_len) { return true; } void Argument::unset () {} void Argument::release () {} // ( dump arguments ) static void args_dump (A_Console &logger) { logger. out_cstr ("Argument value(s)"). out_ch (':'). out_nl (); unsigned count = 0; // (walk through args table) for (Argument *arg = arg_first; arg; arg = arg->arg_next) { logger. out_tab (). out_cstr (arg->name). out_cstr (" -> "); arg->dump (logger); logger. out_nl (); ++ count; } logger. out_ch ('('). out_label ("total:"). out_dec (count). out_ch (')'). out_ch ('.'). out_nl (); } // args_dump // ( parse single argument arg_ptr[arg_len] ) static bool argument_parse (char const *arg_ptr, unsigned arg_len, Argument * &save_chain) { if (arg_len) { char *val_ptr; unsigned val_len; // (split argument string) char *location = (char *) memchr (arg_ptr, '=', arg_len); if (location) { val_ptr = location + 1; val_len = arg_len - (val_ptr - arg_ptr); arg_len = location - arg_ptr; } else { val_ptr = 0; val_len = 0; } // (look through arguments table) for (Argument **arg = &arg_first; *arg; arg = &((*arg)->arg_next)) { Argument *entry = *arg; if (memcmp (entry->name, arg_ptr, arg_len) == 0 && strlen (entry->name) == arg_len) { // (parse value!) bool result = entry->parse (val_ptr, val_len); // (move argument:) *arg = entry->arg_next; entry->arg_next = save_chain; save_chain = entry; return result; } } // for (Argument) } // (arg_len) // (not found) return false; } // argument_parse // (Set to default value) void arguments_unset (Argument *save_chain) { Argument **arg_last = &arg_first; for (Argument *arg = arg_first; arg; arg = arg->arg_next) { arg->unset (); arg_last = &(arg->arg_next); } // for *arg_last = save_chain; } // arguments_unset // ( parse arglist with separators ) static unsigned args_parse (char const *arg_source, Argument *&save_chain) { unsigned count = 0; if (arg_source) { do { char *divider = strchr (arg_source, ';'); if (divider) { argument_parse (arg_source, divider - arg_source, save_chain); arg_source = divider + 1; } else { argument_parse (arg_source, strlen (arg_source), save_chain); arg_source = 0; // (nothing more...) } ++ count; } while (arg_source); } // (arg_source) return count; } // args_parse // [parse arguments vector] static unsigned argvec_parse (unsigned argc, char **argv) { Argument *arg_chain = 0; while (-- argc) args_parse (* ++ argv, arg_chain); // (reset arguments to default) arguments_unset (arg_chain); } // argvec_parse // ( release arguments ) static unsigned args_release () { unsigned count = 0; // (walk through args table) for (Argument *arg = arg_first; arg; arg = arg->arg_next) { arg->release (); ++ count; } // for (arg) return count; } // args_release // // Boolean/flag argument // struct Arg_Bool : Argument { bool &ref; bool def_val; // // (constructor) // Arg_Bool (char const *_name, bool &_ref, bool _val) : Argument (_name), ref(_ref) { def_val = _val; } void dump (A_Console &logger) { logger.out_cstr (ref ? "true" : "false"); } bool parse (char const *val_ptr, unsigned val_len) { if (val_len) { switch (*val_ptr) { case '-': case '0': ref = false; break; case '+': case '1': ref = true; break; default: return false; } // switch return true; // (OK) } // (val_len) else return false; } // parse void unset () { ref = def_val; } void release () { ref = false; } }; // Arg_Bool // // Number argument // struct Arg_Integer : Argument { unsigned &ref; unsigned def_val; // // (constructor) // Arg_Integer (char const *_name, unsigned &_ref, unsigned _val) : Argument (_name), ref(_ref) { def_val = _val; } void dump (A_Console &logger) { logger. out_ch ('#').out_hex (ref); } bool parse (char const *val_ptr, unsigned val_len) { if (val_len) { // (parse hex value) unsigned result = 0; while (val_len --) { unsigned digit = *val_ptr ++; if ('0' <= digit && digit <= '9') digit -= '0'; else if ('a' <= digit && digit <= 'f') digit += 10 - 'a'; else if ('A' <= digit && digit <= 'F') digit += 10 - 'A'; else break; result <<= 4; result |= digit; } // while (val_len) ref = result; return true; } return false; } // parse void unset () { ref = def_val; } void release () { ref = 0; } }; // Arg_Integer // // String argument // struct Arg_String : Argument { char * &ref; char const * def_val; // // (constructor) // Arg_String (char const *_name, char * &_ref, char const * _val) : Argument (_name), ref(_ref) { def_val = _val; } void dump (A_Console &logger) { logger. out_ch ('"'). out_cstr (ref). out_ch ('"'); } bool parse (char const *val_ptr, unsigned val_len) { char *text = new (name) char [val_len + 1]; memcpy (text, val_ptr, val_len); text [val_len] = '\0'; ref = text; return true; } // parse void unset () { parse (def_val, strlen (def_val)); } void release () { delete ref; ref = 0; } // release }; // Arg_String bool time_UTC = false; // (Output time delay) void out_delay (A_Console &logger, unsigned delay) { if (delay) { unsigned hours = 0; unsigned minutes = delay / 60; unsigned seconds = delay % 60; if (minutes) { hours = minutes / 60; minutes %= 60; } if (hours) logger.out_dec (hours). out_cstr ("hour(s)"). out_ch (' '); if (minutes) logger.out_dec (minutes). out_cstr ("minute(s)"). out_ch (' '); if (seconds) logger.out_dec (seconds). out_cstr ("second(s)"); } // (delay) else logger.out_cstr ("<no time>"); } // out_delay DWORD WINAPI NetThread (LPVOID lpParam, W_Console &logger) { SOCKET socket_listen, sClient; struct sockaddr_in localaddr, clientaddr; HANDLE hThread; DWORD dwThreadId; int iSize; // Default server TCP: enum { TCP_Port = 2112 }; // Listen to queries: enum { NBacklog = 5 }; bool mode_sync = false; errno = 0; time_t launch_time, final_time; logger. out_nl (); logger.out_cstr ("Data exchange server ..."). out_nl (); logger.out_label ("Version:"). out_cstr (Version). out_nl (); args_dump (logger); logger.out_label ("Server host:"). out_qstr (::host_name = local_host_name (), '"'). out_nl (); logger.out_label ("Server root directory:"). out_qstr (server_dir, '"'). out_nl (); logger.out_label ("Launch time:"). dump_time (launch_time = time (NULL), time_UTC). out_nl (); socket_listen = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP); if (socket_listen == SOCKET_ERROR) { logger.ErrorInfo ("Can't create socket!"); return 0; } memset (&localaddr, 0, sizeof (localaddr)); localaddr.sin_addr.s_addr = htonl (INADDR_ANY); localaddr.sin_family = AF_INET; localaddr.sin_port = htons (TCP_Port); if (bind (socket_listen, (struct sockaddr *)&localaddr, sizeof(localaddr)) == SOCKET_ERROR) { logger.ErrorInfo ("Can't bind socket!"); return 1; } logger.out_label ("Socket bound OK:"). out_cstr ("TCP/"). out_dec (TCP_Port). out_nl (); if (listen (socket_listen, NBacklog) != 0) { logger.ErrorInfo ("Can't listen to socket!"); } logger.out_label ("Service mode:"). out_cstr (mode_sync ? "sync" : "unsync"). out_nl (); logger.out_label ("Accepting connections:"). out_dec (NBacklog). out_nl (); signal (SIGINT, signal_handler); unsigned count = 0, s_count = 0; while (signal_state < 0) { iSize = sizeof (clientaddr); memset (&clientaddr, 0, sizeof (clientaddr)); sClient = accept (socket_listen, (struct sockaddr *) &clientaddr, &iSize); if (signal_state >= 0) break; // logger.out_label ("Socket:").out_hex (sClient).out_nl (); if (sClient == INVALID_SOCKET) { logger.ErrorInfo ("Accept failed!"); break; } logger.out_nl (); logger.out_label ("Request"). out_ch ('#'). out_hex (++ count). out_label (':'). dump_time (time (NULL), time_UTC). out_cstr (" / "). out_ch ('{'). dump_IP_addr (clientaddr.sin_addr.s_addr). out_ch ('}'). out_nl (); if (mode_sync) { Client_Action client (sClient, logger); client.IP = clientaddr.sin_addr.s_addr; hThread = CreateThread (NULL, 0, ClientThread, (LPVOID) &client, 0, &dwThreadId); if (hThread == NULL) { logger.ErrorInfo ("Create thread failed!"); break; } CloseHandle (hThread); } else { Client_Action client (sClient, logger); client.IP = clientaddr.sin_addr.s_addr; s_count += client.request_handler (); } } // while (signal_state < 0) logger. out_nl (). out_label ("Server terminated"). out_ch ('['). out_label ("total requests:"). out_dec (count). out_ch (':'). out_hex ('#'). out_hex (count). out_cstr (" / "). out_label ("actions done:"). out_dec (s_count). out_ch (']'). out_ch ('.'). out_nl (); logger. out_label ("Users:"). out_label ("logged in:"). out_dec (login_count). out_cstr (" / "). out_label ("logged out:"). out_dec (logout_count). out_ch ('.'). out_nl (); logger. out_label ("Final time:"). dump_time (final_time = time (NULL), time_UTC). out_nl (); logger. out_label ("Time_elapsed:"); out_delay (logger, final_time - launch_time); logger. out_nl (); closesocket (socket_listen); return 0; } // NetThread // // Windows console interface // enum { Std_Error = 2 }; struct RW_Console : W_Console { // (Put text to console) void _put_text (char const *text, unsigned count) { write (Std_Error, text, count); } }; // RW_Console struct TX_Console : W_Console { File_InOut cons_file; // (constructor) TX_Console (char const *name, char const *dir) : cons_file (name, dir) { cons_file.open_update (); cons_file.seek_end (); } // (destructor) ~TX_Console () { cons_file.close (); } // (Output text to console) void _put_text (char const *text, unsigned count) { write (Std_Error, text, count); cons_file.write (text, count); } // _put_text }; // TX_Console // // Config variables/arguments // char *log_file = 0; unsigned rand_seed = 0; bool x_debug = false; bool wait_flag = false; // // (server arguments) // Arg_String Arg_root ("ServerPath", server_dir, "./ROOT"); Arg_String Arg_logfile ("LogFile", log_file, "output.log"); Arg_Bool Arg_XDebug ("XDebug", x_debug, false); Arg_Bool Arg_wait ("KeyWait", wait_flag, false); Arg_Integer Arg_rand_seed ("RandSeed", rand_seed, 2048); Arg_Bool Arg_detail_Query ("DetailQuery", detail_Query, false); Arg_Bool Arg_detail_Reply ("DetailReply", detail_Reply, false); Arg_Bool Arg_detail_Transmit ("DetailTransmit", detail_Transmit, false); Arg_Bool Arg_detail_Receive ("DetailReceive", detail_Receive, false); Arg_Bool Arg_zero_Transmit ("ZeroTransmit", permit_zeroTransmit, false); Arg_Bool Arg_zero_Receive ("ZeroReceive", permit_zeroReceive, false); Arg_Bool Arg_zero_Append ("ZeroAppend", permit_zeroAppend, false); Arg_Bool Arg_zero_Replace ("ZeroReplace", permit_zeroReplace, false); Arg_Bool Arg_time_UTC ("UTC_Time", time_UTC, true); // // Main // int main (int ac, char ** av) { WSADATA wsd; int result; if (WSAStartup (MAKEWORD (2, 2), &wsd) != 0) { // logger.ErrorInfo ("Can't load WinSock!"); return 1; } argvec_parse (ac, av); // (create logfile, if none:) File_System _FS_ (0); if (! _FS_.is_exist (log_file, server_dir)) { File_InOut make_file (log_file, server_dir); make_file.open_create (); make_file.close (); } bool do_wait = wait_flag; // (isolate 'logger' in block!!!) { TX_Console logger (log_file, server_dir); MM_Startup (logger); result = NetThread (0, logger); // (arguments cleanup) args_release (); } MM_Finale (); WSACleanup (); if (do_wait) key_wait (); return result; } // main