[gaim-migrate @ 3063]
[pidgin-git.git] / src / protocols / napster / napster.c
blob8f07836c03ba0c72a94637d012cafbd21ac20272
1 /*
2 * gaim - Napster Protocol Plugin
4 * Copyright (C) 2000-2001, Rob Flynn <rob@tgflinux.com>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 #include <config.h>
24 #include <netdb.h>
25 #include <unistd.h>
26 #include <errno.h>
27 #include <netinet/in.h>
28 #include <arpa/inet.h>
29 #include <time.h>
30 #include <string.h>
31 #include <stdlib.h>
32 #include <stdio.h>
33 #include <time.h>
34 #include <sys/socket.h>
35 #include <sys/stat.h>
36 #include "multi.h"
37 #include "prpl.h"
38 #include "gaim.h"
39 #include "proxy.h"
40 #include "pixmaps/napster.xpm"
42 #define NAP_BUF_LEN 4096
44 GSList *nap_connections = NULL;
46 static unsigned int chat_id = 0;
48 struct nap_channel {
49 unsigned int id;
50 gchar *name;
53 struct nap_data {
54 int fd;
55 int inpa;
57 gchar *email;
58 GSList *channels;
61 static char *nap_name()
63 return "Napster";
67 /* FIXME: Make this use va_arg stuff */
68 static void nap_write_packet(struct gaim_connection *gc, unsigned short command, char *message)
70 struct nap_data *ndata = (struct nap_data *)gc->proto_data;
71 unsigned short size;
73 size = strlen(message);
75 write(ndata->fd, &size, 2);
76 write(ndata->fd, &command, 2);
77 write(ndata->fd, message, size);
80 static int nap_send_im(struct gaim_connection *gc, char *who, char *message, int len, int flags)
82 gchar buf[NAP_BUF_LEN];
84 g_snprintf(buf, NAP_BUF_LEN, "%s %s", who, message);
85 nap_write_packet(gc, 0xCD, buf);
87 return 1;
90 static struct nap_channel *find_channel_by_name(struct gaim_connection *gc, char *name)
92 struct nap_channel *channel;
93 struct nap_data *ndata = (struct nap_data *)gc->proto_data;
94 GSList *channels;
96 channels = ndata->channels;
98 while (channels) {
99 channel = (struct nap_channel *)channels->data;
101 if (channel) {
102 if (!g_strcasecmp(name, channel->name)) {
103 return channel;
106 channels = g_slist_next(channels);
109 return NULL;
112 static struct nap_channel *find_channel_by_id(struct gaim_connection *gc, int id)
114 struct nap_channel *channel;
115 struct nap_data *ndata = (struct nap_data *)gc->proto_data;
116 GSList *channels;
118 channels = ndata->channels;
120 while (channels) {
121 channel = (struct nap_channel *)channels->data;
122 if (id == channel->id) {
123 return channel;
126 channels = g_slist_next(channels);
129 return NULL;
132 static struct conversation *find_conversation_by_id(struct gaim_connection *gc, int id)
134 GSList *bc = gc->buddy_chats;
135 struct conversation *b = NULL;
137 while (bc) {
138 b = (struct conversation *)bc->data;
139 if (id == b->id) {
140 break;
142 bc = bc->next;
143 b = NULL;
146 return b;
149 static void nap_callback(gpointer data, gint source, GaimInputCondition condition)
151 struct gaim_connection *gc = data;
152 struct nap_data *ndata = gc->proto_data;
153 gchar *buf;
154 unsigned short header[2];
155 int len;
156 int command;
157 gchar **res;
158 int i;
160 if (recv(source, header, 4, 0) != 4) {
161 hide_login_progress(gc, "Unable to read");
162 signoff(gc);
163 return;
166 len = header[0];
167 command = header[1];
169 buf = (gchar *)g_malloc(sizeof(gchar) * (len + 1));
171 i = 0;
172 do {
173 int tmp = recv(source, buf + i, len - i, 0);
174 if (tmp <= 0) {
175 g_free(buf);
176 hide_login_progress(gc, "Unable to read");
177 signoff(gc);
178 return;
180 i += tmp;
181 } while (i != len);
183 buf[len] = 0;
185 debug_printf("DEBUG: %s\n", buf);
187 if (command == 0xd6) {
188 res = g_strsplit(buf, " ", 0);
189 /* Do we want to report what the users are doing? */
190 printf("users: %s, files: %s, size: %sGB\n", res[0], res[1], res[2]);
191 g_strfreev(res);
192 g_free(buf);
193 return;
196 if (command == 0x26d) {
197 /* Do we want to use the MOTD? */
198 g_free(buf);
199 return;
202 if (command == 0xCD) {
203 res = g_strsplit(buf, " ", 1);
204 serv_got_im(gc, res[0], res[1], 0, time(NULL), -1);
205 g_strfreev(res);
206 g_free(buf);
207 return;
210 if (command == 0x195) {
211 struct nap_channel *channel;
213 channel = find_channel_by_name(gc, buf);
215 if (!channel) {
216 chat_id++;
218 channel = g_new0(struct nap_channel, 1);
220 channel->id = chat_id;
221 channel->name = g_strdup(buf);
223 ndata->channels = g_slist_append(ndata->channels, channel);
225 serv_got_joined_chat(gc, chat_id, buf);
228 g_free(buf);
229 return;
232 if (command == 0x198 || command == 0x196) {
233 struct nap_channel *channel;
234 struct conversation *convo;
235 gchar **res;
237 res = g_strsplit(buf, " ", 0);
239 channel = find_channel_by_name(gc, res[0]);
240 convo = find_conversation_by_id(gc, channel->id);
242 add_chat_buddy(convo, res[1]);
244 g_strfreev(res);
246 g_free(buf);
247 return;
250 if (command == 0x197) {
251 struct nap_channel *channel;
252 struct conversation *convo;
253 gchar **res;
255 res = g_strsplit(buf, " ", 0);
257 channel = find_channel_by_name(gc, res[0]);
258 convo = find_conversation_by_id(gc, channel->id);
260 remove_chat_buddy(convo, res[1], NULL);
262 g_strfreev(res);
263 g_free(buf);
264 return;
267 if (command == 0x193) {
268 gchar **res;
269 struct nap_channel *channel;
271 res = g_strsplit(buf, " ", 2);
273 channel = find_channel_by_name(gc, res[0]);
275 if (channel)
276 serv_got_chat_in(gc, channel->id, res[1], 0, res[2], time((time_t)NULL));
278 g_strfreev(res);
279 g_free(buf);
280 return;
283 if (command == 0x194) {
284 do_error_dialog(buf, "Gaim: Napster Error");
285 g_free(buf);
286 return;
289 if (command == 0x12e) {
290 gchar buf2[NAP_BUF_LEN];
292 g_snprintf(buf2, NAP_BUF_LEN, "Unable to add '%s' to your hotlist", buf);
293 do_error_dialog(buf2, "Gaim: Napster Error");
295 g_free(buf);
296 return;
300 if (command == 0x191) {
301 struct nap_channel *channel;
303 channel = find_channel_by_name(gc, buf);
305 if (!channel) /* I'm not sure how this would happen =) */
306 return;
308 serv_got_chat_left(gc, channel->id);
309 ndata->channels = g_slist_remove(ndata->channels, channel);
311 g_free(buf);
312 return;
316 if (command == 0xd1) {
317 gchar **res;
319 res = g_strsplit(buf, " ", 0);
321 serv_got_update(gc, res[0], 1, 0, 0, 0, 0, 0);
323 g_strfreev(res);
324 g_free(buf);
325 return;
328 if (command == 0xd2) {
329 serv_got_update(gc, buf, 0, 0, 0, 0, 0, 0);
330 g_free(buf);
331 return;
334 if (command == 0x12d) {
335 /* Our buddy was added successfully */
336 g_free(buf);
337 return;
340 if (command == 0x2ec) {
341 /* Looks like someone logged in as us! =-O */
342 g_free(buf);
344 signoff(gc);
345 return;
348 debug_printf("NAP: [COMMAND: 0x%04x] %s\n", command, buf);
350 g_free(buf);
354 static void nap_login_callback(gpointer data, gint source, GaimInputCondition condition)
356 struct gaim_connection *gc = data;
357 struct nap_data *ndata = gc->proto_data;
358 gchar buf[NAP_BUF_LEN];
359 unsigned short header[2];
360 int len;
361 int command;
363 read(source, header, 4);
364 len = header[0];
365 command = header[1];
367 read(source, buf, len);
368 buf[len] = 0;
370 /* If we have some kind of error, get outta here */
371 if (command == 0x00)
373 do_error_dialog(buf, "Gaim: Napster Error");
374 gaim_input_remove(ndata->inpa);
375 ndata->inpa = 0;
376 close(source);
377 signoff(gc);
378 return;
381 if (command == 0x03) {
382 printf("Registered with E-Mail address of: %s\n", buf);
383 ndata->email = g_strdup(buf);
385 /* Remove old inpa, add new one */
386 gaim_input_remove(ndata->inpa);
387 ndata->inpa = 0;
388 gc->inpa = gaim_input_add(ndata->fd, GAIM_INPUT_READ, nap_callback, gc);
390 /* Our signon is complete */
391 account_online(gc);
392 serv_finish_login(gc);
394 if (bud_list_cache_exists(gc))
395 do_import(gc, NULL);
397 return;
402 static void nap_login_connect(gpointer data, gint source, GaimInputCondition cond)
404 struct gaim_connection *gc = data;
405 struct nap_data *ndata;
406 char buf[NAP_BUF_LEN];
408 if (!g_slist_find(connections, gc)) {
409 close(source);
410 return;
413 if (source < 0) {
414 hide_login_progress(gc, "Unable to connect");
415 signoff(gc);
416 return;
419 ndata = gc->proto_data;
420 if (ndata->fd != source)
421 ndata->fd = source;
423 /* And write our signon data */
424 g_snprintf(buf, NAP_BUF_LEN, "%s %s 0 \"gaimster\" 0", gc->username, gc->password);
425 nap_write_packet(gc, 0x02, buf);
427 /* And set up the input watcher */
428 ndata->inpa = gaim_input_add(ndata->fd, GAIM_INPUT_READ, nap_login_callback, gc);
432 static void nap_login(struct aim_user *user)
434 struct gaim_connection *gc = new_gaim_conn(user);
435 struct nap_data *ndata = gc->proto_data = g_new0(struct nap_data, 1);
437 ndata->fd = proxy_connect("64.124.41.187", 8888, nap_login_connect, gc);
438 if (ndata->fd < 0) {
439 hide_login_progress(gc, "Unable to connect");
440 signoff(gc);
444 static GList *nap_chat_info(struct gaim_connection *gc)
446 GList *m = NULL;
447 struct proto_chat_entry *pce;
449 pce = g_new0(struct proto_chat_entry, 1);
450 pce->label = _("Join what group:");
451 m = g_list_append(m, pce);
453 return m;
456 static void nap_join_chat(struct gaim_connection *gc, GList *data)
458 gchar buf[NAP_BUF_LEN];
459 char *name;
461 if (!data)
462 return;
464 name = data->data;
466 /* Make sure the name has a # preceeding it */
467 if (name[0] != '#')
468 g_snprintf(buf, NAP_BUF_LEN, "#%s", name);
469 else
470 g_snprintf(buf, NAP_BUF_LEN, "%s", name);
472 nap_write_packet(gc, 0x190, buf);
475 static void nap_chat_leave(struct gaim_connection *gc, int id)
477 struct nap_data *ndata = (struct nap_data *)gc->proto_data;
478 struct nap_channel *channel = NULL;
480 channel = find_channel_by_id(gc, id);
482 if (!channel) /* Again, I'm not sure how this would happen */
483 return;
485 nap_write_packet(gc, 0x191, channel->name);
487 ndata->channels = g_slist_remove(ndata->channels, channel);
488 g_free(channel->name);
489 g_free(channel);
493 static int nap_chat_send(struct gaim_connection *gc, int id, char *message)
495 struct nap_channel *channel = NULL;
496 gchar buf[NAP_BUF_LEN];
498 channel = find_channel_by_id(gc, id);
500 if (!channel) {
501 /* This shouldn't happen */
502 return -EINVAL;
505 g_snprintf(buf, NAP_BUF_LEN, "%s %s", channel->name, message);
506 nap_write_packet(gc, 0x192, buf);
507 return 0;
510 static void nap_add_buddy(struct gaim_connection *gc, char *name)
512 nap_write_packet(gc, 0xCF, name);
515 static void nap_remove_buddy(struct gaim_connection *gc, char *name, char *group)
517 nap_write_packet(gc, 0x12F, name);
520 static void nap_close(struct gaim_connection *gc)
522 struct nap_data *ndata = (struct nap_data *)gc->proto_data;
523 struct nap_channel *channel;
525 if (gc->inpa)
526 gaim_input_remove(gc->inpa);
528 while (ndata->channels) {
529 channel = (struct nap_channel *)ndata->channels->data;
530 g_free(channel->name);
531 ndata->channels = g_slist_remove(ndata->channels, channel);
532 g_free(channel);
535 g_free(gc->proto_data);
538 static void nap_add_buddies(struct gaim_connection *gc, GList *buddies)
540 while (buddies) {
541 nap_write_packet(gc, 0xd0, (char *)buddies->data);
542 buddies = buddies -> next;
546 static char** nap_list_icon(int uc)
548 return napster_xpm;
551 static struct prpl *my_protocol = NULL;
553 void napster_init(struct prpl *ret)
555 ret->protocol = PROTO_NAPSTER;
556 ret->name = nap_name;
557 ret->list_icon = nap_list_icon;
558 ret->user_opts = NULL;
559 ret->login = nap_login;
560 ret->close = nap_close;
561 ret->send_im = nap_send_im;
562 ret->set_info = NULL;
563 ret->get_info = NULL;
564 ret->set_away = NULL;
565 ret->set_dir = NULL;
566 ret->get_dir = NULL;
567 ret->dir_search = NULL;
568 ret->set_idle = NULL;
569 ret->change_passwd = NULL;
570 ret->add_buddy = nap_add_buddy;
571 ret->add_buddies = nap_add_buddies;
572 ret->remove_buddy = nap_remove_buddy;
573 ret->add_permit = NULL;
574 ret->rem_permit = NULL;
575 ret->add_deny = NULL;
576 ret->rem_deny = NULL;
577 ret->warn = NULL;
578 ret->chat_info = nap_chat_info;
579 ret->join_chat = nap_join_chat;
580 ret->chat_invite = NULL;
581 ret->chat_leave = nap_chat_leave;
582 ret->chat_whisper = NULL;
583 ret->chat_send = nap_chat_send;
584 ret->keepalive = NULL;
586 my_protocol = ret;
589 #ifndef STATIC
591 char *gaim_plugin_init(GModule * handle)
593 load_protocol(napster_init, sizeof(struct prpl));
594 return NULL;
597 void gaim_plugin_remove()
599 struct prpl *p = find_prpl(PROTO_NAPSTER);
600 if (p == my_protocol)
601 unload_protocol(p);
604 char *name()
606 return "Napster";
609 char *description()
611 return PRPL_DESC("Napster");
614 #endif