Mercurial > pidgin
view src/protocols/napster/napster.c @ 5388:5d0df915ca09
[gaim-migrate @ 5764]
This patch is Sofar's (Auke Kok) napster update warmenhovinated by me.
So... the Napster PRPL works with Opennap servers now.
Thanks Auke.
There are a few places that still need a bit of work. Namely joining
and parting chats. I don't have time to look at it right now, but
search for XXX and // and you'll see the stuff that is relevant to it,
if anyone wants to fix the memleak or two.
For any potential Gaim hackers out there, a few notes:
-Put all declarations at the top of a block--we're not all using C99
-Please don't use // comments, use /* */ (// is for C)
-Try to use dynamic buffers rather than static buffers in most places.
I think most of us Gaim people perfer dynamic rather than static.
I wish oscar.c was this short. Or this clean.
committer: Tailor Script <tailor@pidgin.im>
author | Mark Doliner <mark@kingant.net> |
---|---|
date | Thu, 15 May 2003 21:43:42 +0000 |
parents | 890b29f00b68 |
children | f3cf3bff72f0 |
line wrap: on
line source
/* * gaim - Napster Protocol Plugin * * Copyright (C) 2000-2001, Rob Flynn <rob@marko.net> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include <config.h> #ifndef _WIN32 #include <netdb.h> #include <unistd.h> #include <netinet/in.h> #include <arpa/inet.h> #include <sys/socket.h> #else #include <winsock.h> #endif #include <errno.h> #include <time.h> #include <string.h> #include <stdlib.h> #include <stdio.h> #include <time.h> #include <sys/stat.h> #include "gaim.h" #include "multi.h" #include "prpl.h" #include "proxy.h" #ifdef _WIN32 #include "win32dep.h" #endif /* for win32 compatability */ G_MODULE_IMPORT GSList *connections; #define USEROPT_NAPSERVER 3 #define NAP_SERVER "64.124.41.187" #define USEROPT_NAPPORT 4 #define NAP_PORT 8888 GSList *nap_connections = NULL; static unsigned int chat_id = 0; struct nap_channel { unsigned int id; gchar *name; }; struct nap_data { int fd; gchar *email; GSList *channels; }; static struct nap_channel *find_channel_by_name(struct gaim_connection *gc, char *name) { struct nap_data *ndata = (struct nap_data *)gc->proto_data; struct nap_channel *channel; GSList *channels; channels = ndata->channels; while (channels) { channel = (struct nap_channel *)channels->data; if ((channel) && (!gaim_utf8_strcasecmp(name, channel->name))) return channel; channels = g_slist_next(channels); } return NULL; } static struct nap_channel *find_channel_by_id(struct gaim_connection *gc, int id) { struct nap_data *ndata = (struct nap_data *)gc->proto_data; struct nap_channel *channel; GSList *channels; channels = ndata->channels; while (channels) { channel = (struct nap_channel *)channels->data; if (id == channel->id) return channel; channels = g_slist_next(channels); } return NULL; } static struct gaim_conversation *find_conversation_by_id(struct gaim_connection *gc, int id) { struct gaim_conversation *b = NULL; GSList *bc; bc = gc->buddy_chats; while (bc) { b = (struct gaim_conversation *)bc->data; if (id == gaim_chat_get_id(GAIM_CHAT(b))) return b; bc = g_slist_next(bc); } return NULL; } static void nap_write_packet(struct gaim_connection *gc, unsigned short command, const char *format, ...) { struct nap_data *ndata = (struct nap_data *)gc->proto_data; va_list ap; gchar *message; unsigned short size; va_start(ap, format); message = g_strdup_vprintf(format, ap); va_end(ap); size = strlen(message); gaim_debug(GAIM_DEBUG_MISC, "napster", "S %3hd: %s\n", command, message); write(ndata->fd, &size, 2); write(ndata->fd, &command, 2); write(ndata->fd, message, size); g_free(message); } static int nap_do_irc_style(struct gaim_connection *gc, const char *message, const char *channelname) { gchar **res; gaim_debug(GAIM_DEBUG_MISC, "napster", "C %s\n", message); res = g_strsplit(message, " ", 2); if (!strcasecmp(res[0], "/ME")) { /* MSG_CLIENT_PUBLIC */ nap_write_packet(gc, 824, "%s \"%s\"", channelname, res[1]); } else if (!strcasecmp(res[0], "/MSG")) { /* MSG_CLIENT_PUBLIC */ nap_write_packet(gc, 205, "%s", res[1]); } else if (!strcasecmp(res[0], "/JOIN")) { /* join chatroom MSG_CLIENT_JOIN */ if (!res[1]) { g_strfreev(res); return 1; } if (res[1][0] != '#') nap_write_packet(gc, 400, "#%s", res[1]); else nap_write_packet(gc, 400, "%s", res[1]); } else if (!strcasecmp(res[0], "/PART")) { /* partchatroom MSG_CLIENT_PART */ nap_write_packet(gc, 401, "%s", res[1] ? res[1] : channelname); } else if (!strcasecmp(res[0], "/TOPIC")) { /* set topic MSG_SERVER_TOPIC */ nap_write_packet(gc, 410, "%s", res[1] ? res[1] : channelname); } else if (!strcasecmp(res[0], "/WHOIS")) { /* whois request MSG_CLIENT_WHOIS */ nap_write_packet(gc, 603, "%s", res[1]); } else if (!strcasecmp(res[0], "/PING")) { /* send ping MSG_CLIENT_PING */ nap_write_packet(gc, 751, "%s", res[1]); } else if (!strcasecmp(res[0], "/KICK")) { /* kick asswipe MSG_CLIENT_KICK */ nap_write_packet(gc, 829, "%s", res[1]); } else { g_strfreev(res); return 1; } g_strfreev(res); return 0; } /* 205 - MSG_CLIENT_PRIVMSG */ static int nap_send_im(struct gaim_connection *gc, const char *who, const char *message, int len, int flags) { if ((strlen(message) < 2) || (message[0] != '/' ) || (message[1] == '/')) { /* Actually send a chat message */ nap_write_packet(gc, 205, "%s %s", who, message); } else { /* user typed an IRC-style command */ nap_do_irc_style(gc, message, who); } return 1; } /* 207 - MSG_CLIENT_ADD_HOTLIST */ static void nap_add_buddy(struct gaim_connection *gc, const char *name) { nap_write_packet(gc, 207, "%s", name); } /* 208 - MSG_CLIENT_ADD_HOTLIST_SEQ */ static void nap_add_buddies(struct gaim_connection *gc, GList *buddies) { while (buddies) { nap_write_packet(gc, 208, "%s", (char *)buddies->data); buddies = buddies -> next; } } /* 303 - MSG_CLIENT_REMOVE_HOTLIST */ static void nap_remove_buddy(struct gaim_connection *gc, char *name, char *group) { nap_write_packet(gc, 303, "%s", name); } /* 400 - MSG_CLIENT_JOIN */ static void nap_join_chat(struct gaim_connection *gc, GHashTable *data) { char *name; if (!data) return; name = g_hash_table_lookup(data, "group"); /* Make sure the name has a # preceeding it */ if (name[0] != '#') nap_write_packet(gc, 400, "#%s", name); else nap_write_packet(gc, 400, "%s", name); } /* 401 - MSG_CLIENT_PART */ static void nap_chat_leave(struct gaim_connection *gc, int id) { struct nap_data *ndata = (struct nap_data *)gc->proto_data; struct nap_channel *channel = NULL; channel = find_channel_by_id(gc, id); if (!channel) /* Again, I'm not sure how this would happen */ return; serv_got_chat_left(gc, id); nap_write_packet(gc, 401, "%s", channel->name); ndata->channels = g_slist_remove(ndata->channels, channel); g_free(channel->name); g_free(channel); } /* 402 - MSG_CLIENT_PUBLIC */ static int nap_chat_send(struct gaim_connection *gc, int id, char *message) { struct nap_channel *channel = NULL; channel = find_channel_by_id(gc, id); if (!channel) { /* This shouldn't happen */ return -EINVAL; } if ((strlen(message) < 2) || (message[0] != '/' ) || (message[1] == '/')) { /* Actually send a chat message */ nap_write_packet(gc, 402, "%s %s", channel->name, message); } else { /* user typed an IRC-style command */ nap_do_irc_style(gc, message, channel->name); } return 0; } /* 603 - MSG_CLIENT_WHOIS */ static void nap_get_info(struct gaim_connection *gc, const char *who) { nap_write_packet(gc, 603, "%s", who); } static void nap_callback(gpointer data, gint source, GaimInputCondition condition) { struct gaim_connection *gc = data; struct nap_data *ndata = gc->proto_data; struct nap_channel *channel; struct gaim_conversation *convo; gchar *buf, *buf2, *buf3; gchar **res; unsigned short header[2]; int len; int command; int i; if (read(source, (void*)header, 4) != 4) { hide_login_progress(gc, _("Unable to read header from server")); signoff(gc); return; } len = header[0]; command = header[1]; buf = (gchar *)g_malloc((len + 1) * sizeof(gchar)); buf[len] = '\0'; i = 0; do { int tmp = read(source, buf + i, len - i); if (tmp <= 0) { g_free(buf); buf = g_strdup_printf("Unable to read mesage from server. Command is %hd, length is %hd.", len, command); hide_login_progress(gc, buf); g_free(buf); signoff(gc); return; } i += tmp; } while (i != len); gaim_debug(GAIM_DEBUG_MISC, "napster", "R %3hd: %s\n", command, buf); switch (command) { case 000: /* MSG_SERVER_ERROR */ do_error_dialog(buf, NULL, GAIM_ERROR); gaim_input_remove(gc->inpa); gc->inpa = 0; close(source); signoff(gc); break; case 003: /* MSG_SERVER_EMAIL */ gaim_debug(GAIM_DEBUG_MISC, "napster", "Registered with e-mail address: %s\n", buf); ndata->email = g_strdup(buf); /* Our signon is complete */ account_online(gc); serv_finish_login(gc); break; case 201: /* MSG_SERVER_SEARCH_RESULT */ res = g_strsplit(buf, " ", 0); serv_got_update(gc, res[0], 1, 0, 0, 0, 0); g_strfreev(res); break; case 202: /* MSG_SERVER_SEARCH_END */ serv_got_update(gc, buf, 0, 0, 0, 0, 0); break; case 205: /* MSG_CLIENT_PRIVMSG */ res = g_strsplit(buf, " ", 2); serv_got_im(gc, res[0], res[1], 0, time(NULL), -1); g_strfreev(res); break; case 209: /* MSG_SERVER_USER_SIGNON */ /* USERNAME SPEED */ res = g_strsplit(buf, " ", 2); serv_got_update(gc, res[0], 1, 0, 0, 0, 0); g_strfreev(res); break; case 210: /* MSG_SERVER_USER_SIGNOFF */ /* USERNAME SPEED */ res = g_strsplit(buf, " ", 2); serv_got_update(gc, res[0], 0, 0, 0, 0, 0); g_strfreev(res); break; case 214: /* MSG_SERVER_STATS */ res = g_strsplit(buf, " ", 3); buf2 = g_strdup_printf(_("users: %s, files: %s, size: %sGB"), res[0], res[1], res[2]); serv_got_im(gc, "server", buf2, 0, time(NULL), -1); g_free(buf2); g_strfreev(res); break; case 301: /* MSG_SERVER_HOTLIST_ACK */ /* Our buddy was added successfully */ break; case 302: /* MSG_SERVER_HOTLIST_ERROR */ buf2 = g_strdup_printf(_("Unable to add \"%s\" to your Napster hotlist"), buf); do_error_dialog(buf2, NULL, GAIM_ERROR); g_free(buf2); break; case 316: /* MSG_SERVER_DISCONNECTING */ /* we have been kicked off =^( */ do_error_dialog(_("You were disconnected from the server."), NULL, GAIM_ERROR); signoff(gc); break; case 401: /* MSG_CLIENT_PART */ /* XXX - this is not handled correctly now: a kick fails to get through here! */ channel = find_channel_by_name(gc, buf); // convo = find_conversation_by_id(gc, channel->id); // gaim_conversation_destroy(convo); if (!channel) /* in case we closed the chat window */ break; serv_got_chat_left(gc, channel->id); ndata->channels = g_slist_remove(ndata->channels, channel); // g_free(channel->name); g_free(channel); break; case 403: /* MSG_SERVER_PUBLIC */ res = g_strsplit(buf, " ", 3); channel = find_channel_by_name(gc, res[0]); if (channel) serv_got_chat_in(gc, channel->id, res[1], 0, res[2], time((time_t)NULL)); g_strfreev(res); break; case 404: /* MSG_SERVER_NOSUCH */ /* abused by opennap servers to broadcast stuff */ serv_got_im(gc, "server", buf, 0, time(NULL), -1); break; case 405: /* MSG_SERVER_JOIN_ACK */ channel = find_channel_by_name(gc, buf); if (!channel) { chat_id++; channel = g_new0(struct nap_channel, 1); channel->id = chat_id; channel->name = g_strdup(buf); /* XXX - This doesn't get freed when parting */ ndata->channels = g_slist_append(ndata->channels, channel); serv_got_joined_chat(gc, chat_id, buf); } break; case 407: /* MSG_SERVER_PART */ res = g_strsplit(buf, " ", 0); channel = find_channel_by_name(gc, res[0]); convo = find_conversation_by_id(gc, channel->id); // remove_chat_buddy(convo, res[1], NULL); gaim_chat_remove_user(GAIM_CHAT(convo), res[1], NULL); g_strfreev(res); break; case 406: /* MSG_SERVER_JOIN */ case 408: /* MSG_SERVER_CHANNEL_USER_LIST */ res = g_strsplit(buf, " ", 4); channel = find_channel_by_name(gc, res[0]); convo = find_conversation_by_id(gc, channel->id); // add_chat_buddy(convo, res[1], NULL); gaim_chat_add_user(GAIM_CHAT(convo), res[1], NULL); g_strfreev(res); break; case 409: /* MSG_SERVER_CHANNEL_USER_LIST_END */ break; case 410: /* MSG_SERVER_TOPIC */ /* display the topic in the channel */ res = g_strsplit(buf, " ", 2); channel = find_channel_by_name(gc, res[0]); convo = find_conversation_by_id(gc, channel->id); gaim_chat_set_topic(GAIM_CHAT(convo), res[0], res[1]); g_strfreev(res); break; case 603: /* MSG_CLIENT_WHOIS */ buf2 = g_strdup_printf(_("%s requested your information"), buf); serv_got_im(gc, "server", buf2, 0, time(NULL), -1); g_free(buf2); break; case 604: /* MSG_SERVER_WHOIS_RESPONSE */ /* XXX - Format is: "Elite" 37 " " "Active" 0 0 0 0 "gaim 0.63cvs" 0 0 192.168.1.41 32798 0 unknown flounder */ res = g_strsplit(buf, " ", 2); g_show_info_text(gc, res[0], 2, res[1], NULL); g_strfreev(res); break; case 621: case 622: /* MSG_CLIENT_MOTD */ /* also replaces MSG_SERVER_MOTD, so we should display it */ serv_got_im(gc, "motd", buf, 0, time(NULL), -1); break; case 627: /* MSG_CLIENT_WALLOP */ /* abused by opennap server maintainers to broadcast stuff */ serv_got_im(gc, "wallop", buf, 0, time(NULL), -1); break; case 628: /* MSG_CLIENT_ANNOUNCE */ serv_got_im(gc, "announce", buf, 0, time(NULL), -1); break; case 748: /* MSG_SERVER_GHOST */ /* Looks like someone logged in as us! =-O */ do_error_dialog(_("You were disconnected from the server, because you logged on from a different location"), NULL, GAIM_ERROR); signoff(gc); break; case 751: /* MSG_CLIENT_PING */ buf2 = g_strdup_printf(_("%s requested a PING"), buf); serv_got_im(gc, "server", buf2, 0, time(NULL), -1); g_free(buf2); /* send back a pong */ /* MSG_CLIENT_PONG */ nap_write_packet(gc, 752, "%s", buf); break; case 752: /* MSG_CLIENT_PONG */ buf2 = g_strdup_printf("Received pong from %s", buf); do_error_dialog(buf2, NULL, GAIM_INFO); g_free(buf2); break; case 824: /* MSG_CLIENT_EMOTE */ res = g_strsplit(buf, " ", 3); buf2 = g_strndup(res[2]+1, strlen(res[2]) - 2); /* chomp off the surround quotes */ buf3 = g_strdup_printf("/me %s", buf2); g_free(buf2); if ((channel = find_channel_by_name(gc, res[0]))) { convo = find_conversation_by_id(gc, channel->id); gaim_chat_write(GAIM_CHAT(convo), res[1], buf3, WFLAG_NICK, time(NULL)); } g_free(buf3); g_strfreev(res); break; default: gaim_debug(GAIM_DEBUG_MISC, "napster", "Unknown packet %hd: %s\n", command, buf); break; } g_free(buf); } /* 002 - MSG_CLIENT_LOGIN */ static void nap_login_connect(gpointer data, gint source, GaimInputCondition cond) { struct gaim_connection *gc = data; struct nap_data *ndata = (struct nap_data *)gc->proto_data; gchar *buf; if (!g_slist_find(connections, gc)) { close(source); return; } if (source < 0) { hide_login_progress(gc, "Unable to connect"); signoff(gc); return; } ndata->fd = source; /* Update the login progress status display */ buf = g_strdup_printf("Logging in: %s", gc->username); set_login_progress(gc, 4, buf); g_free(buf); /* Write our signon data */ nap_write_packet(gc, 2, "%s %s 0 \"gaim %s\" 0", gc->username, gc->password, VERSION); /* And set up the input watcher */ gc->inpa = gaim_input_add(ndata->fd, GAIM_INPUT_READ, nap_callback, gc); } static void nap_login(struct gaim_account *account) { struct gaim_connection *gc = new_gaim_conn(account); set_login_progress(gc, 2, _("Connecting")); gc->proto_data = g_new0(struct nap_data, 1); if (proxy_connect(account, account->proto_opt[USEROPT_NAPSERVER][0] ? account->proto_opt[USEROPT_NAPSERVER] : NAP_SERVER, account->proto_opt[USEROPT_NAPPORT][0] ? atoi(account->proto_opt[USEROPT_NAPPORT]) : NAP_PORT, nap_login_connect, gc) != 0) { hide_login_progress(gc, _("Unable to connect")); signoff(gc); } } static void nap_close(struct gaim_connection *gc) { struct nap_data *ndata = (struct nap_data *)gc->proto_data; struct nap_channel *channel; if (gc->inpa) gaim_input_remove(gc->inpa); if (!ndata) return; g_free(ndata->email); while (ndata->channels) { channel = (struct nap_channel *)ndata->channels->data; g_free(channel->name); ndata->channels = g_slist_remove(ndata->channels, channel); g_free(channel); } g_free(ndata); } static const char* nap_list_icon(struct gaim_account *a, struct buddy *b) { return "napster"; } static void nap_list_emblems(struct buddy *b, char **se, char **sw, char **nw, char **ne) { if (b->present == GAIM_BUDDY_OFFLINE) *se = "offline"; } static GList *nap_buddy_menu(struct gaim_connection *gc, const char *who) { GList *m = NULL; struct proto_buddy_menu *pbm; pbm = g_new0(struct proto_buddy_menu, 1); pbm->label = _("Get Info"); pbm->callback = nap_get_info; pbm->gc = gc; m = g_list_append(m, pbm); return m; } static GList *nap_chat_info(struct gaim_connection *gc) { GList *m = NULL; struct proto_chat_entry *pce; pce = g_new0(struct proto_chat_entry, 1); pce->label = _("Join what group:"); pce->identifier = "group"; m = g_list_append(m, pce); return m; } static GaimPlugin *my_protocol = NULL; static GaimPluginProtocolInfo prpl_info = { GAIM_PROTO_NAPSTER, OPT_PROTO_CHAT_TOPIC, NULL, NULL, nap_list_icon, nap_list_emblems, NULL, NULL, NULL, NULL, nap_buddy_menu, nap_chat_info, nap_login, nap_close, nap_send_im, NULL, NULL, nap_get_info, NULL, NULL, NULL, NULL, NULL, NULL, NULL, nap_add_buddy, nap_add_buddies, nap_remove_buddy, NULL, NULL, NULL, NULL, NULL, NULL, NULL, nap_join_chat, NULL, nap_chat_leave, NULL, nap_chat_send, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; static GaimPluginInfo info = { 2, /**< api_version */ GAIM_PLUGIN_PROTOCOL, /**< type */ NULL, /**< ui_requirement */ 0, /**< flags */ NULL, /**< dependencies */ GAIM_PRIORITY_DEFAULT, /**< priority */ "prpl-napster", /**< id */ "Napster", /**< name */ VERSION, /**< version */ /** summary */ N_("NAPSTER Protocol Plugin"), /** description */ N_("NAPSTER Protocol Plugin"), NULL, /**< author */ WEBSITE, /**< homepage */ NULL, /**< load */ NULL, /**< unload */ NULL, /**< destroy */ NULL, /**< ui_info */ &prpl_info /**< extra_info */ }; static void __init_plugin(GaimPlugin *plugin) { struct proto_user_opt *puo; puo = g_new0(struct proto_user_opt, 1); puo->label = g_strdup(_("Server")); puo->def = g_strdup(NAP_SERVER); puo->pos = USEROPT_NAPSERVER; prpl_info.user_opts = g_list_append(prpl_info.user_opts, puo); puo = g_new0(struct proto_user_opt, 1); puo->label = g_strdup(_("Port:")); puo->def = g_strdup("8888"); puo->pos = USEROPT_NAPPORT; prpl_info.user_opts = g_list_append(prpl_info.user_opts, puo); my_protocol = plugin; } GAIM_INIT_PLUGIN(napster, __init_plugin, info);