/* Copyright (C) 2000-2003 Markus Lausser (sgop@users.sf.net) This is free software distributed under the terms of the GNU Public License. See the file COPYING for details. */ #ifdef HAVE_CONFIG_H # include #endif #ifdef HAVE_LIBLCONV_H # include #endif #include #include #include #include #include #include "buffer.h" #include "utils.h" #include "lopster.h" #include "handler.h" #include "global.h" #include "log.h" #include "connection.h" #include "support.h" #include "server.h" #include "napster.h" #include "chat.h" static const int napster_command_id[COMMAND_SIZE] = { 0, // 0 unused 0, // login // unused 100, // share file 102, // unshare file 110, // unshare all files 200, // 5 search 203, // download request 205, // private message 205, // notice (never is replied) 207, // hotlist add 208, // 10 hotlist add (at server login) 211, // browse users files 218, // download start notification 219, // download end notification 220, // upload start notification 221, // 15 upload end notification 214, // request server stats (short) 303, // hotlist remove 400, // join channel 401, // part channel 402, // 20 send public message 410, // get channel topic 410, // set channel topic 420, // get channel ban list 422, // ban user from channel 423, // 25 unban user from channel 500, // request firewalled download 600, // get user speed 10213, // list muzzle in channel 603, // whois user 606, // 30 set user level 608, // accept upload 610, // kill (disconnect) user 611, // nuke (delete, unregister) user 612, // ban user 613, // 35 change user's upload port 614, // unban user 615, // get global ban list 619, // upload rejected (limit) 621, // get MOTD 622, // 40 muzzle user 623, // unmuzzle user 625, // change user's linespeed 627, // message to all ops 628, // message to all users 640, // 45 request direct browse 641, // accept direct browse request 652, // cloak 700, // change linespeed 701, // change password 702, // 50 change email 703, // change data port 750, // ping server 751, // ping user 752, // pong user 753, // 55 change user's password 801, // get server version 820, // clear channel 823, // get channel level 823, // set channel level 624, // 60 unnuke user 10117, // reload server config 810, // get server vars 810, // get server var 810, // set server var 821, // 65 redirect user 822, // cycle user 824, // public emote message 826, // get channel limit 826, // set channel limit 827, // 70 get channel list 829, // kick user from channel 830, // get channel user list 831, // get global user list 10100, // link server 10101, // 75 delink server 10111, // remove server 10110, // shutdown server 10112, // get server links 10115, // get server stats (long) 10118, // 80 get client version stats 10119, // locate user 10121, // whowas user 10122, // masskill ip 10123, // server incoming command histogram 10125, // 85 server outgoing command histogram 10200, // register user 10203, // get user mode 10203, // set user mode 10204, // op user in channel 10205, // 90 dep user in channel 10208, // channel wallop message 10209, // get channel mode 10209, // set channel mode 10210, // invite user to channel 10211, // 95 give user voice in channel 10212, // remove voice from user 10213, // muzzle user in channel 10214, // unmuzzle user in channel 10250, // class add 10251, //100 class remove 10252, // class list 10253, // dline add 10254, // dline remove 10255, // dline list 10256, //105 iline add 10257, // iline remove 10258, // iline list 10259, // eline add 10260, // eline remove 10261, //110 eline list 10300, // share generic media file 800, // reset server var to default 10204, // list ops in channel 10211, // list voice in channel 0, //115 change nick 116, // raw command // dummy command id 0, // private emote message }; static char* ChannelModeNap(int id) { switch (id) { case CMODE_TOPIC: return "TOPIC"; case CMODE_REGISTERED: return "REGISTERED"; case CMODE_PRIVATE: return "PRIVATE"; case CMODE_INVITE: return "INVITE"; case CMODE_MODERATED: return "MODERATED"; default: return "???"; } } static char* int2chmode_nap(int set, int clear) { static char mode[2048]; int cnt; set &= CMODE_MASK_NAP; clear &= CMODE_MASK_NAP; // switch topic flag if (set & CMODE_TOPIC) { clear |= CMODE_TOPIC; set &= ~CMODE_TOPIC; } else if (clear & CMODE_TOPIC) { set |= CMODE_TOPIC; clear &= ~CMODE_TOPIC; } *mode = 0; cnt = 0; while (set) { if (set & 1) { if (*mode) strcat(mode, " +"); else strcat(mode, "+"); strcat(mode, ChannelModeNap(1<>= 1; } cnt = 0; while (clear) { if (clear & 1) { if (*mode) strcat(mode, " -"); else strcat(mode, "-"); strcat(mode, ChannelModeNap(1<>= 1; } if (*mode) return mode; else return NULL; } static int command_supported_nap(int id) { if (id >= COMMAND_SIZE || id < 0) return 0; return napster_command_id[id]; } static void protocol_message_nap(net_t* net, int direction, int type, char* text) { char text2[2048]; char *prefix; chat_page_t *page; page = chat_page_search(net, "Protocol", P_OTHER, 3); if (!page) page = create_other_page(net, "Protocol", "Protocol"); if (!page) return; chat_print_time_stamp(page, M_PUBLIC); if (!direction) prefix = cparse("%K>%r>%R> "); else prefix = cparse("%C<%c<%K< "); chat_print_colored(page, M_PUBLIC, "error", prefix); sprintf(text2, "%5d ", type); chat_print_colored(page, M_PUBLIC, "error", text2); chat_print_text(page, M_PUBLIC, "message", "["); chat_print_colored(page, M_PUBLIC, "text", text); chat_print_text(page, M_PUBLIC, "message", "]"); chat_print_text(page, M_PUBLIC, "text", "\n"); } static void command_pack_nap(net_t* net, gint16 type, va_list ap) { char* str1; char* str2; char* str3; char* str4; char* pos; gint16 added; unsigned long ulong1; long long1; long long2; int int1; int int2; int int3; int int4; int int5; int int6; int int7; int int8; int int9; int int10; buffer_t* buffer; gint16 command; command = command_supported_nap(type); if (!command) return; buffer = net->out_buffer; // FIXME: we assume that we dont need more space than 2048 bytes pos = buffer_need_space(buffer, 2048); switch (type) { case CMD_RAW: str1 = va_arg(ap, char*); str2 = g_strdup(str1); str3 = arg(str2, 0); if (!str3) return; str4 = arg(NULL, 1); command = atoi(str3); // overwrite command id if (str4) added = sprintf(pos+4, "%s", str4); else added = 0; break; case CMD_ADD_FILE: str1 = va_arg(ap, char*); // filename str2 = va_arg(ap, char*); // md5 long1 = va_arg(ap, long); // size int1 = va_arg(ap, int); // bitrate int2 = va_arg(ap, int); // frequency int3 = va_arg(ap, int); // duration added = sprintf(pos+4, "\"%s\" %s %ld %d %d %d", str1, str2, long1, int1, int2, int3); break; case CMD_SHARE_FILE: str1 = va_arg(ap, char*); // filename long1 = va_arg(ap, long); // size str2 = va_arg(ap, char*); // md5 str3 = va_arg(ap, char*); // mediatype added = sprintf(pos+4, "\"%s\" %ld %s %s", str1, long1, str2, str3); break; case CMD_UPLOAD_OK: case CMD_DOWNLOAD: case CMD_DOWNLOAD_FIREWALL: case CMD_PUBLIC_EMOTE: str1 = va_arg(ap, char*); // user str2 = va_arg(ap, char*); // winname added = sprintf(pos+4, "%s \"%s\"", str1, str2); break; case CMD_UPLOAD_LIMIT: str1 = va_arg(ap, char*); // user str2 = va_arg(ap, char*); // winname int1 = va_arg(ap, int); // limit added = sprintf(pos+4, "%s \"%s\" %d", str1, str2, int1); break; case CMD_GET_CHAN_MODE: // channel case CMD_GET_CHAN_LIMIT: case CMD_GET_CHAN_LEVEL: case CMD_NAMES_LIST: case CMD_CHANNEL_BAN_LIST: case CMD_GET_TOPIC: case CMD_LIST_OP: case CMD_LIST_VOICE: case CMD_JOIN: case CMD_PART: // possible second argument is ignored case CMD_BROWSE_DIRECT: // nick case CMD_BROWSE_DIRECT_OK: case CMD_PONG: case CMD_PING: case CMD_WHICH_SERVER: case CMD_NUKE: case CMD_UNNUKE: case CMD_BROWSE: case CMD_WHO_WAS: case CMD_ADD_HOTLIST: case CMD_REMOVE_HOTLIST: case CMD_ADD_HOTLIST_SEQ: case CMD_WHOIS: case CMD_GET_USERSPEED: case CMD_REMOVE_FILE: case CMD_PING_SERVER: case CMD_GET_SERVER_VAR: case CMD_SERVER_RECONFIG: case CMD_GUSER_LIST: case CMD_CHANGE_EMAIL: case CMD_CHANGE_PASS: case CMD_CHANGE_SPEED: case CMD_SET_USER_MODE: case CMD_SERVER_CONNECT: case CMD_CLASS_ADD: case CMD_CLASS_REMOVE: case CMD_ILINE_ADD: case CMD_ILINE_REMOVE: case CMD_ELINE_ADD: case CMD_ELINE_REMOVE: case CMD_DLINE_ADD: case CMD_DLINE_REMOVE: case CMD_ANNOUNCE: case CMD_WALLOP: str1 = va_arg(ap, char*); // args added = sprintf(pos+4, "%s", str1); break; case CMD_SEARCH: str1 = va_arg(ap, char*); // include str2 = va_arg(ap, char*); // exclude=NULL int1 = va_arg(ap, int); // results=0 int2 = va_arg(ap, int); // mediatype int3 = va_arg(ap, int); // bitlo=0 int4 = va_arg(ap, int); // bithi=0 int5 = va_arg(ap, int); // freqlo=0 int6 = va_arg(ap, int); // freqlo=0 int7 = va_arg(ap, int); // speedlo=0 int8 = va_arg(ap, int); // speedhi=0 long1 = va_arg(ap, long); // sizelo=0 long2 = va_arg(ap, long); // sizehi=0 int9 = va_arg(ap, int); // durationlo=0 int10 = va_arg(ap, int); // duartionhi=0 added = 4; // skip header bytes added += sprintf(pos+added, "FILENAME CONTAINS \"%s\"", str1); if (str2 && *str2) added += sprintf(pos+added, " FILENAME EXCLUDES \"%s\"", str2); if (int1) added += sprintf(pos+added, " MAX_RESULTS %d", int1); if (int7 && int7 == int8) { added += sprintf(pos+added, " LINESPEED \"EQUAL TO\" %d", int7); } else { if (int7) { added += sprintf(pos+added, " LINESPEED \"AT LEAST\" %d", int7); } if (int8) { added += sprintf(pos+added, " LINESPEED \"AT BEST\" %d", int8); } } if (int3 && int3 == int4) { added += sprintf(pos+added, " BITRATE \"EQUAL TO\" \"%d\"", int3); } else { if (int3) { added += sprintf(pos+added, " BITRATE \"AT LEAST\" \"%d\"", int3); } if (int4) { added += sprintf(pos+added, " BITRATE \"AT BEST\" \"%d\"", int4); } } if (int5 && int5 == int6) { added += sprintf(pos+added, " FREQ \"EQUAL TO\" \"%d\"", int5); } else { if (int5) { added += sprintf(pos+added, " FREQ \"AT LEAST\" \"%d\"", int5); } if (int6) { added += sprintf(pos+added, " FREQ \"AT BEST\" \"%d\"", int6); } } if (int9 && int9 == int10) { added += sprintf(pos+added, " DURATION \"EQUAL TO\" \"%d\"", int9); } else { if (int9) { added += sprintf(pos+added, " DURATION \"AT LEAST\" \"%d\"", int9); } if (int10) { added += sprintf(pos+added, " DURATION \"AT BEST\" \"%d\"", int10); } } if (long1 && long1 == long2) { added += sprintf(pos+added, " SIZE \"EQUAL TO\" \"%ld\"", long1); } else { if (long1) { added += sprintf(pos+added, " SIZE \"AT LEAST\" \"%ld\"", long1); } if (long2) { added += sprintf(pos+added, " SIZE \"AT BEST\" \"%ld\"", long2); } } if (int2 != MEDIA_MP3) { added += sprintf(pos+added, " TYPE %s", int2media(int2, 1)); } // remove header from counter again added -= 4; break; case CMD_UNBAN: case CMD_MUZZLE: case CMD_KILL: case CMD_UNMUZZLE: case CMD_MASS_KILL: case CMD_SERVER_DISCONNECT: case CMD_KILL_SERVER: case CMD_SERVER_REMOVE: str1 = va_arg(ap, char*); // nick/ip/server/... str2 = va_arg(ap, char*); // reason if (str2) added = sprintf(pos+4, "%s \"%s\"", str1, str2); else added = sprintf(pos+4, "%s", str1); break; case CMD_CLEAR_CHANNEL: str1 = va_arg(ap, char*); // channel str2 = va_arg(ap, char*); // reason if (str2) added = sprintf(pos+4, "%s %s", str1, str2); else added = sprintf(pos+4, "%s", str1); break; case CMD_CHAN_BAN: case CMD_CHAN_UNBAN: case CMD_CHAN_MUZZLE: case CMD_CHAN_UNMUZZLE: case CMD_KICK: str1 = va_arg(ap, char*); // channel str2 = va_arg(ap, char*); // nick str3 = va_arg(ap, char*); // reason if (str3) added = sprintf(pos+4, "%s %s \"%s\"", str1, str2, str3); else added = sprintf(pos+4, "%s %s", str1, str2); break; case CMD_CHANGE_DATA_PORT: int1 = va_arg(ap, int); // port added = sprintf(pos+4, "%d", int1); break; case CMD_UNSHARE_ALL: case CMD_VERSION_STATS: case CMD_BANLIST: case CMD_CLOAK: case CMD_SERVER_STATS: case CMD_GET_SERVER_VARS: case CMD_GET_USER_MODE: case CMD_HISTOGRAM: case CMD_SHISTOGRAM: case CMD_CLASS_LIST: case CMD_ELINE_LIST: case CMD_DLINE_LIST: case CMD_ILINE_LIST: case CMD_FULL_CHANNEL_LIST: case CMD_UPLOAD_START: case CMD_UPLOAD_END: case CMD_DOWNLOAD_START: case CMD_DOWNLOAD_END: case CMD_LINKS: case CMD_MOTD: added = 0; break; case CMD_BAN: str1 = va_arg(ap, char*); // user str2 = va_arg(ap, char*); // reason ulong1 = va_arg(ap, unsigned long);// timeout if (str2) { if (ulong1 > 0) added = sprintf(pos+4, "%s \"%s\" %lu", str1, str2, ulong1); else added = sprintf(pos+4, "%s \"%s\"", str1, str2); } else { added = sprintf(pos+4, "%s", str1); } break; case CMD_SET_CHAN_MODE: str1 = va_arg(ap, char*); // channel int1 = va_arg(ap, int); // set int2 = va_arg(ap, int); // clear str2 = int2chmode_nap(int1, int2); if (!str2) return; added = sprintf(pos+4, "%s %s", str1, str2); break; case CMD_SET_CHAN_LEVEL: case CMD_SET_USERLEVEL: case CMD_SET_TOPIC: case CMD_ALTER_PASS: case CMD_ALTER_SPEED: case CMD_SET_SERVER_VAR: case CMD_OP: case CMD_CHAN_VOICE: case CMD_DEOP: case CMD_CHAN_UNVOICE: case CMD_CHAN_INVITE: case CMD_CYCLE: case CMD_PRIVMSG: case CMD_NOTICE: case CMD_PUBLIC: case CMD_CHAN_WALLOP: str1 = va_arg(ap, char*); // channel/nick str2 = va_arg(ap, char*); // level/topic/mode/.. added = sprintf(pos+4, "%s %s", str1, str2); break; case CMD_ALTER_PORT: case CMD_SET_CHAN_LIMIT: str1 = va_arg(ap, char*); // nick int1 = va_arg(ap, int); // port added = sprintf(pos+4, "%s %d", str1, int1); break; case CMD_SERVER_REHASH: case CMD_USAGE_STATS: case CMD_SERVER_VERSION: str1 = va_arg(ap, char*); // server if (str1) added = sprintf(pos+4, "%s", str1); else added = 0; break; case CMD_REGISTER_USER: str1 = va_arg(ap, char*); // nick str2 = va_arg(ap, char*); // passwd str3 = va_arg(ap, char*); // email str4 = va_arg(ap, char*); // level added = sprintf(pos+4, "%s %s %s %s", str1, str2, str3, str4); break; case CMD_REDIRECT: str1 = va_arg(ap, char*); // nick str2 = va_arg(ap, char*); // server int1 = va_arg(ap, int); // port added = sprintf(pos+4, "%s %s %d", str1, str2, int1); break; default: g_warning("unknown command %d", type); return; } pos[added+4] = 0; #ifdef HAVE_LIBLCONV_H if (global.options.use_iconv) { char* lconv_buf = lconv_strdup_conv(LCONV_SPEC, global.options.dest_codeset, local_codeset, pos+4); added = strlen(lconv_buf); strncpy(pos+4, lconv_buf, added); free(lconv_buf); } #endif pos[added+4] = 0; #ifdef PROTOCOL_DEBUG l_log(net, "protocol", LOG_PROTOCOL, "S: %5d L: %4d [%s]\n", command, added, pos+4); #endif if ((global.options.no_piping & NOPIPE_PROTOCOL2) == 0) { protocol_message_nap(net, 1, command, pos+4); } buffer->datasize += added + 4; // now write the command header added = BSWAP16(added); command = BSWAP16(command); memcpy(pos, &added, 2); memcpy(pos+2, &command, 2); } static int delete_0chars(char* message, int length) { int pos1, pos2; pos1 = pos2 = 0; while (pos2 < length) { if (message[pos2] != 0) { message[pos1] = message[pos2]; pos1++; } pos2++; } message[pos1] = 0; return pos2-pos1; } static int consume_command_nap(net_t* net) { char* result; buffer_t* buffer = net->in_buffer; if (!buffer) return 0; if (buffer->datasize < buffer->consumed+2) return 0; memcpy(&(net->c_length), buffer->data+buffer->consumed, 2); net->c_length = BSWAP16(net->c_length); if (buffer->datasize - buffer->consumed < net->c_length + 4) return 0; memcpy(&(net->c_type), buffer->data+buffer->consumed+2, 2); net->c_type = BSWAP16(net->c_type); if (net->c_length < 0) { server_disconnect(net->active_server, "Not synchronized", 1); return -1; } result = g_malloc(sizeof(char)*net->c_length+1); memcpy(result, buffer->data+buffer->consumed+4, net->c_length); result[net->c_length] = 0; buffer_consume_virt(buffer, net->c_length + 4); if (net->c_length != (signed)strlen(result)) delete_0chars(result, net->c_length); #ifdef PROTOCOL_DEBUG l_log(net, "protocol", LOG_PROTOCOL, "R: %5d L: %4d [%s]\n", net->c_type, net->c_length, result); #endif if ((global.options.no_piping & NOPIPE_PROTOCOL1) == 0) { protocol_message_nap(net, 0, net->c_type, result); } net->c_message = result; return 1; } static int server_login_nap(gpointer data, gint source ATTR_UNUSED, GdkInputCondition condition) { int port; socket_t *socket = data; net_t *net = socket->data; char* email; char text[2084]; gint16 size; gint16 type; if (global.email && *(global.email)) email = global.email; else email = NULL; if (condition != GDK_INPUT_WRITE) { net->active_server->ip = INADDR_NONE; // printf("could not login %d\n", condition); server_disconnect(net->active_server, "Could not login (write error)", 1); return 1; } gdk_input_remove(socket->output); socket->output = -1; server_set_status(net->active_server, SERVER_CONNECTING, "Logging in..."); if (!global.upload_socket) port = 0; else port = ntohs(global.upload_socket->port); #ifdef HAVE_ZLIB size = sprintf(text+4, "%s %s %d \"Lopster %s\" %d \"%s\" \"\" 9", net->user.username, net->user.password, port, VERSION, global.linespeed, email?email:"anon@anon"); #else size = sprintf(text+4, "%s %s %d \"Lopster %s\" %d %s", net->user.username, strlen(net->user.password)?net->user.password:net->user.username, port, VERSION, global.linespeed, email?email:"anon@anon" ); #endif size = BSWAP16(size); type = BSWAP16(2); // napster login code memcpy(text, &size, 2); memcpy(text+2, &type, 2); size = BSWAP16(size) + 4; if (send_safe(socket->fd, text, size, size) < 0) { server_disconnect(net->active_server, "Could not login (command send error)", 1); return 1; } socket->input = gdk_input_add(socket->fd, GDK_INPUT_READ, GTK_SIGNAL_FUNC(server_get_input), socket); return 0; } static void channel_setup_nap(chat_page_t* page) { GtkWidget* wid; wid = gtk_object_get_data(GTK_OBJECT(page->main), CModeNames[2]); gtk_widget_hide(wid); wid = gtk_object_get_data(GTK_OBJECT(page->main), CModeNames[3]); gtk_widget_hide(wid); wid = gtk_object_get_data(GTK_OBJECT(page->main), CModeNames[7]); gtk_widget_hide(wid); wid = gtk_object_get_data(GTK_OBJECT(page->main), CModeNames[8]); gtk_widget_hide(wid); wid = gtk_object_get_data(GTK_OBJECT(page->main), CModeNames[9]); gtk_widget_hide(wid); } static int network_success_nap(net_t* net) { #ifdef HAVE_ZLIB int n; if (net->compress) { if (net->compress > 9 || net->compress < 0) { printf("[ZLIB] [%s] invalid compression level: %d\n", net->name, net->compress); net->compress = 0; return 0; } // printf ( "[ZLIB] [%s] init compression\n", net->name ); net->socket->zin = calloc (1, sizeof (z_stream)); net->socket->zout = calloc (1, sizeof (z_stream)); net->socket->zbuf = buffer_new(4096); net->socket->zcom = buffer_new(4096); if (!net->socket->zin || !net->socket->zout || !net->socket->zbuf || !net->socket->zcom) { printf ( "[ZLIB] [%s] init compression FAILED\n", net->name ); return 0; } n = inflateInit (net->socket->zin); if (n != Z_OK) { printf ( "[ZLIB] [%s] inflateInit: %s (%d)\n", net->name, net->socket->zin->msg, n ); return 0; } n = deflateInit (net->socket->zout, net->compress); if (n != Z_OK) { printf ( "[ZLIB] [%s] deflateInit: %s (%d)\n", net->name, net->socket->zout->msg, n ); return 0; } } #endif net->subtype = -1; return 1; } static void login_nap(socket_t* socket) { socket->output = gdk_input_add(socket->fd, GDK_INPUT_WRITE, GTK_SIGNAL_FUNC(server_login_nap), socket); } HANDLER_ENTRY* search_handler_nap(int command); void handle_command_nap(net_t* net) { HANDLER_ENTRY* handler; if (!net->c_message) g_warning("no message"); handler = search_handler_nap(net->c_type); if (!handler) { not_implemented(net, net->c_message); } else if (handler->handler) { handler->handler(net, net->c_message); } } void setup_functions_nap(net_t* net) { net->success = network_success_nap; net->consume_command = consume_command_nap; net->login = login_nap; net->command_pack = command_pack_nap; net->channel_setup = channel_setup_nap; net->handle_command = handle_command_nap; }