Mercurial > pidgin.yaz
diff src/protocols/yahoo/yahoo.c @ 2681:37d80035e77f
[gaim-migrate @ 2694]
don't ask.
committer: Tailor Script <tailor@pidgin.im>
author | Eric Warmenhoven <eric@warmenhoven.org> |
---|---|
date | Tue, 06 Nov 2001 23:58:24 +0000 |
parents | |
children | db2b0b733732 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/yahoo/yahoo.c Tue Nov 06 23:58:24 2001 +0000 @@ -0,0 +1,1074 @@ +/* + * gaim + * + * Some code copyright (C) 1998-1999, Mark Spencer <markster@marko.net> + * libfaim code copyright 1998, 1999 Adam Fritzler <afritz@auk.cx> + * + * 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 + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + + +#include <netdb.h> +#include <unistd.h> +#include <errno.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <time.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <ctype.h> +#include <crypt.h> +#include "multi.h" +#include "prpl.h" +#include "gaim.h" +#include "proxy.h" + +#include "pixmaps/status-away.xpm" +#include "pixmaps/status-here.xpm" +#include "pixmaps/status-idle.xpm" + +#define YAHOO_DEBUG + +#define USEROPT_MAIL 0 + +#define USEROPT_PAGERHOST 3 +#define YAHOO_PAGER_HOST "scs.yahoo.com" +#define USEROPT_PAGERPORT 4 +#define YAHOO_PAGER_PORT 5050 + +enum yahoo_service { + YAHOO_SERVICE_LOGON = 1, + YAHOO_SERVICE_LOGOFF, + YAHOO_SERVICE_ISAWAY, + YAHOO_SERVICE_ISBACK, + YAHOO_SERVICE_IDLE, + YAHOO_SERVICE_MESSAGE, + YAHOO_SERVICE_IDACT, + YAHOO_SERVICE_IDDEACT, + YAHOO_SERVICE_MAILSTAT, + YAHOO_SERVICE_USERSTAT, + YAHOO_SERVICE_NEWMAIL, + YAHOO_SERVICE_CHATINVITE, + YAHOO_SERVICE_CALENDAR, + YAHOO_SERVICE_NEWPERSONALMAIL, + YAHOO_SERVICE_NEWCONTACT, + YAHOO_SERVICE_ADDIDENT, + YAHOO_SERVICE_ADDIGNORE, + YAHOO_SERVICE_PING, + YAHOO_SERVICE_GROUPRENAME, + YAHOO_SERVICE_SYSMESSAGE = 20, + YAHOO_SERVICE_PASSTHROUGH2 = 22, + YAHOO_SERVICE_CONFINVITE = 24, + YAHOO_SERVICE_CONFLOGON, + YAHOO_SERVICE_CONFDECLINE, + YAHOO_SERVICE_CONFLOGOFF, + YAHOO_SERVICE_CONFADDINVITE, + YAHOO_SERVICE_CONFMSG, + YAHOO_SERVICE_CHATLOGON, + YAHOO_SERVICE_CHATLOGOFF, + YAHOO_SERVICE_CHATMSG = 32, + YAHOO_SERVICE_GAMELOGON = 40, + YAHOO_SERVICE_GAMELOGOFF = 41, + YAHOO_SERVICE_FILETRANSFER = 70, + YAHOO_SERVICE_LIST = 85, + YAHOO_SERVICE_ADDBUDDY = 131, + YAHOO_SERVICE_REMBUDDY = 132 +}; + +enum yahoo_status { + YAHOO_STATUS_AVAILABLE, + YAHOO_STATUS_BRB, + YAHOO_STATUS_BUSY, + YAHOO_STATUS_NOTATHOME, + YAHOO_STATUS_NOTATDESK, + YAHOO_STATUS_NOTINOFFICE, + YAHOO_STATUS_ONPHONE, + YAHOO_STATUS_ONVACATION, + YAHOO_STATUS_OUTTOLUNCH, + YAHOO_STATUS_STEPPEDOUT, + YAHOO_STATUS_INVISIBLE = 12, + YAHOO_STATUS_CUSTOM = 99, + YAHOO_STATUS_IDLE = 999, + YAHOO_STATUS_OFFLINE = 0x5a55aa56 +}; + +struct yahoo_data { + int fd; + guchar *rxqueue; + int rxlen; + GHashTable *hash; + GSList *login; + int current_status; + gboolean logged_in; +}; + +struct yahoo_pair { + int key; + char *value; +}; + +struct yahoo_packet { + guint16 service; + guint32 status; + guint32 id; + GSList *hash; +}; + +struct yahoo_buddy { + char *name; + int state; + char *msg; +}; + +static char *yahoo_name() { + return "Yahoo"; +} + +#define YAHOO_PACKET_HDRLEN (4 + 2 + 2 + 2 + 2 + 4 + 4) + +static struct yahoo_packet *yahoo_packet_new(enum yahoo_service service, enum yahoo_status status, int id) +{ + struct yahoo_packet *pkt = g_new0(struct yahoo_packet, 1); + + pkt->service = service; + pkt->status = status; + pkt->id = id; + + return pkt; +} + +static void yahoo_packet_hash(struct yahoo_packet *pkt, int key, char *value) +{ + struct yahoo_pair *pair = g_new0(struct yahoo_pair, 1); + pair->key = key; + pair->value = g_strdup(value); + pkt->hash = g_slist_append(pkt->hash, pair); +} + +static int yahoo_packet_length(struct yahoo_packet *pkt) +{ + GSList *l; + + int len = 0; + + l = pkt->hash; + while (l) { + struct yahoo_pair *pair = l->data; + int tmp = pair->key; + do { + tmp /= 10; + len++; + } while (tmp); + len += 2; + len += strlen(pair->value); + len += 2; + l = l->next; + } + + return len; +} + +/* sometimes i wish prpls could #include things from other prpls. then i could just + * use the routines from libfaim and not have to admit to knowing how they work. */ +#define yahoo_put16(buf, data) ( \ + (*(buf) = (u_char)((data)>>8)&0xff), \ + (*((buf)+1) = (u_char)(data)&0xff), \ + 2) +#define yahoo_get16(buf) ((((*(buf))<<8)&0xff00) + ((*((buf)+1)) & 0xff)) +#define yahoo_put32(buf, data) ( \ + (*((buf)) = (u_char)((data)>>24)&0xff), \ + (*((buf)+1) = (u_char)((data)>>16)&0xff), \ + (*((buf)+2) = (u_char)((data)>>8)&0xff), \ + (*((buf)+3) = (u_char)(data)&0xff), \ + 4) +#define yahoo_get32(buf) ((((*(buf))<<24)&0xff000000) + \ + (((*((buf)+1))<<16)&0x00ff0000) + \ + (((*((buf)+2))<< 8)&0x0000ff00) + \ + (((*((buf)+3) )&0x000000ff))) + +static void yahoo_packet_read(struct yahoo_packet *pkt, guchar *data, int len) +{ + int pos = 0; + + while (pos + 1 < len) { + char key[64], *value; + int x; + + struct yahoo_pair *pair = g_new0(struct yahoo_pair, 1); + pkt->hash = g_slist_append(pkt->hash, pair); + + x = 0; + while (pos + 1 < len) { + if (data[pos] == 0xc0 && data[pos + 1] == 0x80) + break; + key[x++] = data[pos++]; + } + key[x] = 0; + pos += 2; + pair->key = strtol(key, NULL, 10); + + value = g_malloc(len - pos); + x = 0; + while (pos + 1 < len) { + if (data[pos] == 0xc0 && data[pos + 1] == 0x80) + break; + value[x++] = data[pos++]; + } + value[x] = 0; + pos += 2; + pair->value = g_strdup(value); + g_free(value); + debug_printf("Key: %d \tValue: %s\n", pair->key, pair->value); + } +} + +static void yahoo_packet_write(struct yahoo_packet *pkt, guchar *data) +{ + GSList *l = pkt->hash; + int pos = 0; + + while (l) { + struct yahoo_pair *pair = l->data; + guchar buf[100]; + + g_snprintf(buf, sizeof(buf), "%d", pair->key); + strcpy(data + pos, buf); + pos += strlen(buf); + data[pos++] = 0xc0; + data[pos++] = 0x80; + + strcpy(data + pos, pair->value); + pos += strlen(pair->value); + data[pos++] = 0xc0; + data[pos++] = 0x80; + + l = l->next; + } +} + +static void yahoo_packet_dump(guchar *data, int len) +{ +#ifdef YAHOO_DEBUG + int i; + for (i = 0; i + 1 < len; i += 2) { + if ((i % 16 == 0) && i) + debug_printf("\n"); + debug_printf("%02x", data[i]); + debug_printf("%02x ", data[i+1]); + } + if (i < len) + debug_printf("%02x", data[i]); + debug_printf("\n"); + for (i = 0; i < len; i++) { + if ((i % 16 == 0) && i) + debug_printf("\n"); + if (isprint(data[i])) + debug_printf("%c ", data[i]); + else + debug_printf(". "); + } + debug_printf("\n"); +#endif +} + +static int yahoo_send_packet(struct yahoo_data *yd, struct yahoo_packet *pkt) +{ + int pktlen = yahoo_packet_length(pkt); + int len = YAHOO_PACKET_HDRLEN + pktlen; + int ret; + + guchar *data; + int pos = 0; + + if (yd->fd < 0) + return -1; + + data = g_malloc0(len + 1); + + memcpy(data + pos, "YMSG", 4); pos += 4; + pos += yahoo_put16(data + pos, 0x0600); + pos += yahoo_put16(data + pos, 0x0000); + pos += yahoo_put16(data + pos, pktlen); + pos += yahoo_put16(data + pos, pkt->service); + pos += yahoo_put32(data + pos, pkt->status); + pos += yahoo_put32(data + pos, pkt->id); + + yahoo_packet_write(pkt, data + pos); + + yahoo_packet_dump(data, len); + ret = write(yd->fd, data, len); + + g_free(data); + + return ret; +} + +static void yahoo_packet_free(struct yahoo_packet *pkt) +{ + while (pkt->hash) { + struct yahoo_pair *pair = pkt->hash->data; + g_free(pair->value); + g_free(pair); + pkt->hash = g_slist_remove(pkt->hash, pair); + } + g_free(pkt); +} + +static void yahoo_process_logon(struct gaim_connection *gc, struct yahoo_packet *pkt) +{ + struct yahoo_data *yd = gc->proto_data; + GSList *l = pkt->hash; + struct yahoo_buddy *buddy = NULL; + struct yahoo_packet *newpkt; + char *name = NULL; + int state = 0; + char *msg = NULL; + + while (l) { + struct yahoo_pair *pair = l->data; + + switch (pair->key) { + case 0: /* we won't actually do anything with this */ + break; + case 1: /* we don't get the full buddy list here. */ + account_online(gc); + serv_finish_login(gc); + g_snprintf(gc->displayname, sizeof(gc->displayname), "%s", pair->value); + do_import(gc, NULL); + yd->logged_in = TRUE; + + /* this requests the list. i have a feeling that this is very evil */ + newpkt = yahoo_packet_new(YAHOO_SERVICE_LIST, YAHOO_STATUS_OFFLINE, 0); + yahoo_send_packet(yd, newpkt); + yahoo_packet_free(newpkt); + + break; + case 8: /* how many online buddies we have */ + break; + case 7: /* the current buddy */ + name = pair->value; + break; + case 10: /* state */ + state = strtol(pair->value, NULL, 10); + break; + case 19: /* custom message */ + msg = pair->value; + break; + case 11: /* i didn't know what this was in the old protocol either */ + break; + case 17: /* in chat? */ + break; + case 13: /* in pager, i think this should always be 1 */ + /* we don't actually give notification here. we wait until after we've + * gotten the list, so that they get added to the right group */ + buddy = g_new0(struct yahoo_buddy, 1); + buddy->name = g_strdup(name); + buddy->state = state; + buddy->msg = msg ? g_strdup(msg) : NULL; + yd->login = g_slist_append(yd->login, buddy); + break; + default: + debug_printf("unknown login key %d\n", pair->key); + break; + } + + l = l->next; + } +} + +static void yahoo_process_list(struct gaim_connection *gc, struct yahoo_packet *pkt) +{ + struct yahoo_data *yd = gc->proto_data; + GSList *l = pkt->hash; + gboolean export = FALSE; + + while (l) { + char **lines; + char **split; + char **buddies; + char **tmp, **bud; + + struct yahoo_pair *pair = l->data; + l = l->next; + + if (pair->key != 87) + continue; + + lines = g_strsplit(pair->value, "\n", -1); + for (tmp = lines; *tmp; tmp++) { + split = g_strsplit(*tmp, ":", 2); + buddies = g_strsplit(split[1], ",", -1); + for (bud = buddies; *bud; bud++) + if (!find_buddy(gc, *bud)) { + add_buddy(gc, split[0], *bud, *bud); + export = TRUE; + } + g_strfreev(buddies); + g_strfreev(split); + } + g_strfreev(lines); + } + + if (export) + do_export(gc); + + while (yd->login) { + struct yahoo_buddy *buddy = yd->login->data; + int status = buddy->state; + yd->login = g_slist_remove(yd->login, buddy); + if (status == YAHOO_STATUS_AVAILABLE) + serv_got_update(gc, buddy->name, 1, 0, 0, 0, 0, 0); + else if (status == YAHOO_STATUS_IDLE) + serv_got_update(gc, buddy->name, 1, 0, 0, time(NULL) - 600, (status << 1), 0); + else + serv_got_update(gc, buddy->name, 1, 0, 0, 0, (status << 1) | UC_UNAVAILABLE, 0); + if (status == YAHOO_STATUS_CUSTOM) { + gpointer val = g_hash_table_lookup(yd->hash, buddy->name); + if (val) { + g_free(val); + g_hash_table_insert(yd->hash, buddy->name, g_strdup(buddy->msg)); + } else + g_hash_table_insert(yd->hash, g_strdup(buddy->name), g_strdup(buddy->msg)); + } + g_free(buddy->msg); + g_free(buddy->name); + g_free(buddy); + } +} + +static void yahoo_process_message(struct gaim_connection *gc, struct yahoo_packet *pkt) +{ + char *msg = NULL; + char *from = NULL; + time_t tm = time(NULL); + GSList *l = pkt->hash; + + while (l) { + struct yahoo_pair *pair = l->data; + if (pair->key == 4) + from = pair->value; + if (pair->key == 14) + msg = pair->value; + if (pair->key == 15) + tm = strtol(pair->value, NULL, 10); + l = l->next; + } + + if (pkt->status == 1) { + strip_linefeed(msg); + serv_got_im(gc, from, msg, 0, tm); + } else if (pkt->status == 2) { + do_error_dialog(_("Your message did not get sent."), _("Gaim - Error")); + } +} + +static void yahoo_process_status(struct gaim_connection *gc, struct yahoo_packet *pkt) +{ + struct yahoo_data *yd = gc->proto_data; + GSList *l = pkt->hash; + char *name = NULL; + int state = 0; + char *msg = NULL; + + while (l) { + struct yahoo_pair *pair = l->data; + + switch (pair->key) { + case 7: + name = pair->value; + break; + case 10: + state = strtol(pair->value, NULL, 10); + break; + case 19: + msg = pair->value; + break; + case 11: /* i didn't know what this was in the old protocol either */ + break; + case 17: /* in chat? */ + break; + case 13: + if (strtol(pair->value, NULL, 10) != 1) { + serv_got_update(gc, name, 0, 0, 0, 0, 0, 0); + break; + } + if (state == YAHOO_STATUS_AVAILABLE) + serv_got_update(gc, name, 1, 0, 0, 0, 0, 0); + else if (state == YAHOO_STATUS_IDLE) + serv_got_update(gc, name, 1, 0, 0, time(NULL) - 600, (state << 1), 0); + else + serv_got_update(gc, name, 1, 0, 0, 0, (state << 1) | UC_UNAVAILABLE, 0); + if (state == YAHOO_STATUS_CUSTOM) { + gpointer val = g_hash_table_lookup(yd->hash, name); + if (val) { + g_free(val); + g_hash_table_insert(yd->hash, name, g_strdup(msg)); + } else + g_hash_table_insert(yd->hash, g_strdup(name), g_strdup(msg)); + } + break; + } + + l = l->next; + } +} + +static void yahoo_process_contact(struct gaim_connection *gc, struct yahoo_packet *pkt) +{ + char *id = NULL; + char *who = NULL; + char *msg = NULL; + GSList *l = pkt->hash; + + while (l) { + struct yahoo_pair *pair = l->data; + if (pair->key == 1) + id = pair->value; + else if (pair->key == 3) + who = pair->value; + else if (pair->key == 14) + msg = pair->value; + l = l->next; + } + + show_got_added(gc, id, who, NULL, msg); +} + +static void yahoo_process_mail(struct gaim_connection *gc, struct yahoo_packet *pkt) +{ + char *who = NULL; + char *email = NULL; + char *subj = NULL; + int count = 0; + GSList *l = pkt->hash; + + while (l) { + struct yahoo_pair *pair = l->data; + if (pair->key == 9) + count = strtol(pair->value, NULL, 10); + else if (pair->key == 43) + who = pair->value; + else if (pair->key == 42) + email = pair->value; + else if (pair->key == 18) + subj = pair->value; + l = l->next; + } + + connection_has_mail(gc, count, NULL, NULL, "http://mail.yahoo.com/"); +} + +static void yahoo_packet_process(struct gaim_connection *gc, struct yahoo_packet *pkt) +{ + switch (pkt->service) + { + case YAHOO_SERVICE_LOGON: + yahoo_process_logon(gc, pkt); + break; + case YAHOO_SERVICE_ISAWAY: + yahoo_process_status(gc, pkt); + break; + case YAHOO_SERVICE_MESSAGE: + yahoo_process_message(gc, pkt); + break; + case YAHOO_SERVICE_NEWMAIL: + yahoo_process_mail(gc, pkt); + break; + case YAHOO_SERVICE_NEWCONTACT: + yahoo_process_contact(gc, pkt); + break; + case YAHOO_SERVICE_LIST: + yahoo_process_list(gc, pkt); + break; + default: + debug_printf("unhandled service %d\n", pkt->service); + break; + } +} + +static void yahoo_pending(gpointer data, gint source, GaimInputCondition cond) +{ + struct gaim_connection *gc = data; + struct yahoo_data *yd = gc->proto_data; + char buf[1024]; + int len; + + len = read(yd->fd, buf, sizeof(buf)); + + if (len <= 0) { + hide_login_progress(gc, "Unable to read"); + signoff(gc); + return; + } + + yd->rxqueue = g_realloc(yd->rxqueue, len + yd->rxlen); + memcpy(yd->rxqueue + yd->rxlen, buf, len); + yd->rxlen += len; + + while (1) { + struct yahoo_packet *pkt; + int pos = 0; + int pktlen; + + if (yd->rxlen < YAHOO_PACKET_HDRLEN) + return; + + pos += 4; /* YMSG */ + pos += 2; + pos += 2; + + pktlen = yahoo_get16(yd->rxqueue + pos); pos += 2; + debug_printf("%d bytes to read, rxlen is %d\n", pktlen, yd->rxlen); + + if (yd->rxlen < (YAHOO_PACKET_HDRLEN + pktlen)) + return; + + yahoo_packet_dump(yd->rxqueue, YAHOO_PACKET_HDRLEN + pktlen); + + pkt = yahoo_packet_new(0, 0, 0); + + pkt->service = yahoo_get16(yd->rxqueue + pos); pos += 2; + debug_printf("Yahoo Service: %d Status: %d\n", pkt->service, pkt->status); + pkt->status = yahoo_get32(yd->rxqueue + pos); pos += 4; + pkt->id = yahoo_get32(yd->rxqueue + pos); pos += 4; + + yahoo_packet_read(pkt, yd->rxqueue + pos, pktlen); + + yd->rxlen -= YAHOO_PACKET_HDRLEN + pktlen; + if (yd->rxlen) { + char *tmp = g_memdup(yd->rxqueue + YAHOO_PACKET_HDRLEN + pktlen, yd->rxlen); + g_free(yd->rxqueue); + yd->rxqueue = tmp; + } else { + g_free(yd->rxqueue); + yd->rxqueue = NULL; + } + + yahoo_packet_process(gc, pkt); + + yahoo_packet_free(pkt); + } +} + +static void yahoo_got_connected(gpointer data, gint source, GaimInputCondition cond) +{ + struct gaim_connection *gc = data; + struct yahoo_data *yd; + struct yahoo_packet *pkt; + + if (!g_slist_find(connections, gc)) { + close(source); + return; + } + + if (source < 0) { + hide_login_progress(gc, "Unable to connect"); + signoff(gc); + return; + } + + yd = gc->proto_data; + yd->fd = source; + + pkt = yahoo_packet_new(YAHOO_SERVICE_LOGON, YAHOO_STATUS_AVAILABLE, 0); + + yahoo_packet_hash(pkt, 0, gc->username); + yahoo_packet_hash(pkt, 1, gc->username); + yahoo_packet_hash(pkt, 6, crypt(gc->password, "$1$_2S43d5f$")); + + yahoo_send_packet(yd, pkt); + + yahoo_packet_free(pkt); + + gc->inpa = gaim_input_add(yd->fd, GAIM_INPUT_READ, yahoo_pending, gc); +} + +static void yahoo_login(struct aim_user *user) { + struct gaim_connection *gc = new_gaim_conn(user); + struct yahoo_data *yd = gc->proto_data = g_new0(struct yahoo_data, 1); + + set_login_progress(gc, 1, "Connecting"); + + yd->fd = -1; + yd->hash = g_hash_table_new(g_str_hash, g_str_equal); + + if (!proxy_connect(user->proto_opt[USEROPT_PAGERHOST][0] ? + user->proto_opt[USEROPT_PAGERHOST] : YAHOO_PAGER_HOST, + user->proto_opt[USEROPT_PAGERPORT][0] ? + atoi(user->proto_opt[USEROPT_PAGERPORT]) : YAHOO_PAGER_PORT, + yahoo_got_connected, gc)) { + hide_login_progress(gc, "Connection problem"); + signoff(gc); + return; + } + +} + +static gboolean yahoo_destroy_hash(gpointer key, gpointer val, gpointer data) +{ + g_free(key); + g_free(val); + return TRUE; +} + +static void yahoo_close(struct gaim_connection *gc) { + struct yahoo_data *yd = (struct yahoo_data *)gc->proto_data; + g_hash_table_foreach_remove(yd->hash, yahoo_destroy_hash, NULL); + g_hash_table_destroy(yd->hash); + if (yd->fd >= 0) + close(yd->fd); + if (yd->rxqueue) + g_free(yd->rxqueue); + while (yd->login) { + struct yahoo_buddy *buddy = yd->login->data; + yd->login = g_slist_remove(yd->login, buddy); + g_free(buddy->msg); + g_free(buddy->name); + g_free(buddy); + } + if (gc->inpa) + gaim_input_remove(gc->inpa); + g_free(yd); +} + +static char **yahoo_list_icon(int uc) +{ + if ((uc >> 1) == YAHOO_STATUS_IDLE) + return status_idle_xpm; + else if (uc == 0) + return status_here_xpm; + return status_away_xpm; +} + +static char *yahoo_get_status_string(enum yahoo_status a) +{ + switch (a) { + case YAHOO_STATUS_BRB: + return "Be Right Back"; + case YAHOO_STATUS_BUSY: + return "Busy"; + case YAHOO_STATUS_NOTATHOME: + return "Not At Home"; + case YAHOO_STATUS_NOTATDESK: + return "Not At Desk"; + case YAHOO_STATUS_NOTINOFFICE: + return "Not In Office"; + case YAHOO_STATUS_ONPHONE: + return "On Phone"; + case YAHOO_STATUS_ONVACATION: + return "On Vacation"; + case YAHOO_STATUS_OUTTOLUNCH: + return "Out To Lunch"; + case YAHOO_STATUS_STEPPEDOUT: + return "Stepped Out"; + default: + return NULL; + } +} + +static GList *yahoo_buddy_menu(struct gaim_connection *gc, char *who) +{ + GList *m = NULL; + struct proto_buddy_menu *pbm; + struct yahoo_data *yd = (struct yahoo_data *)gc->proto_data; + struct buddy *b = find_buddy(gc, who); /* this should never be null. if it is, + segfault and get the bug report. */ + static char buf[1024]; + + if (!(b->uc & UC_UNAVAILABLE)) + return NULL; + + pbm = g_new0(struct proto_buddy_menu, 1); + if ((b->uc >> 1) != YAHOO_STATUS_CUSTOM) + g_snprintf(buf, sizeof buf, "Status: %s", yahoo_get_status_string(b->uc >> 1)); + else + g_snprintf(buf, sizeof buf, "Custom Status: %s", + (char *)g_hash_table_lookup(yd->hash, b->name)); + pbm->label = buf; + pbm->callback = NULL; + pbm->gc = gc; + m = g_list_append(m, pbm); + + return m; +} + +static GList *yahoo_user_opts() +{ + GList *m = NULL; + struct proto_user_opt *puo; + + puo = g_new0(struct proto_user_opt, 1); + puo->label = "Pager Host:"; + puo->def = YAHOO_PAGER_HOST; + puo->pos = USEROPT_PAGERHOST; + m = g_list_append(m, puo); + + puo = g_new0(struct proto_user_opt, 1); + puo->label = "Pager Port:"; + puo->def = "5050"; + puo->pos = USEROPT_PAGERPORT; + m = g_list_append(m, puo); + + return m; +} + +static void yahoo_act_id(gpointer data, char *entry) +{ + struct gaim_connection *gc = data; + struct yahoo_data *yd = gc->proto_data; + + struct yahoo_packet *pkt = yahoo_packet_new(YAHOO_SERVICE_IDACT, YAHOO_STATUS_AVAILABLE, 0); + yahoo_packet_hash(pkt, 3, entry); + yahoo_send_packet(yd, pkt); + yahoo_packet_free(pkt); + + g_snprintf(gc->displayname, sizeof(gc->displayname), "%s", entry); +} + +static void yahoo_do_action(struct gaim_connection *gc, char *act) +{ + if (!strcmp(act, "Activate ID")) { + do_prompt_dialog("Activate which ID:", gc->displayname, gc, yahoo_act_id, NULL); + } +} + +static GList *yahoo_actions() { + GList *m = NULL; + + m = g_list_append(m, "Activate ID"); + + return m; +} + +static int yahoo_send_im(struct gaim_connection *gc, char *who, char *what, int flags) +{ + struct yahoo_data *yd = gc->proto_data; + struct yahoo_packet *pkt = yahoo_packet_new(YAHOO_SERVICE_MESSAGE, YAHOO_STATUS_OFFLINE, 0); + + yahoo_packet_hash(pkt, 1, gc->displayname); + yahoo_packet_hash(pkt, 5, who); + yahoo_packet_hash(pkt, 14, what); + + yahoo_send_packet(yd, pkt); + + yahoo_packet_free(pkt); + + return 1; +} + +static void yahoo_set_away(struct gaim_connection *gc, char *state, char *msg) +{ + struct yahoo_data *yd = (struct yahoo_data *)gc->proto_data; + struct yahoo_packet *pkt; + char s[4]; + + gc->away = NULL; + + if (msg) { + yd->current_status = YAHOO_STATUS_CUSTOM; + gc->away = ""; + } else if (state) { + gc->away = ""; + if (!strcmp(state, "Available")) { + yd->current_status = YAHOO_STATUS_AVAILABLE; + gc->away = NULL; + } else if (!strcmp(state, "Be Right Back")) { + yd->current_status = YAHOO_STATUS_BRB; + } else if (!strcmp(state, "Busy")) { + yd->current_status = YAHOO_STATUS_BUSY; + } else if (!strcmp(state, "Not At Home")) { + yd->current_status = YAHOO_STATUS_NOTATHOME; + } else if (!strcmp(state, "Not At Desk")) { + yd->current_status = YAHOO_STATUS_NOTATDESK; + } else if (!strcmp(state, "Not In Office")) { + yd->current_status = YAHOO_STATUS_NOTINOFFICE; + } else if (!strcmp(state, "On Phone")) { + yd->current_status = YAHOO_STATUS_ONPHONE; + } else if (!strcmp(state, "On Vacation")) { + yd->current_status = YAHOO_STATUS_ONVACATION; + } else if (!strcmp(state, "Out To Lunch")) { + yd->current_status = YAHOO_STATUS_OUTTOLUNCH; + } else if (!strcmp(state, "Stepped Out")) { + yd->current_status = YAHOO_STATUS_STEPPEDOUT; + } else if (!strcmp(state, "Invisible")) { + yd->current_status = YAHOO_STATUS_INVISIBLE; + } else if (!strcmp(state, GAIM_AWAY_CUSTOM)) { + if (gc->is_idle) { + yd->current_status = YAHOO_STATUS_IDLE; + } else { + yd->current_status = YAHOO_STATUS_AVAILABLE; + } + gc->away = NULL; + } + } else if (gc->is_idle) { + yd->current_status = YAHOO_STATUS_IDLE; + } else { + yd->current_status = YAHOO_STATUS_AVAILABLE; + } + + pkt = yahoo_packet_new(YAHOO_SERVICE_ISAWAY, yd->current_status, 0); + g_snprintf(s, sizeof(s), "%d", yd->current_status); + yahoo_packet_hash(pkt, 10, s); + if (yd->current_status == YAHOO_STATUS_CUSTOM) + yahoo_packet_hash(pkt, 19, msg); + + yahoo_send_packet(yd, pkt); + yahoo_packet_free(pkt); +} + +static void yahoo_set_idle(struct gaim_connection *gc, int idle) +{ + struct yahoo_data *yd = gc->proto_data; + struct yahoo_packet *pkt = NULL; + + if (idle && yd->current_status == YAHOO_STATUS_AVAILABLE) { + pkt = yahoo_packet_new(YAHOO_SERVICE_ISAWAY, YAHOO_STATUS_IDLE, 0); + yd->current_status = YAHOO_STATUS_IDLE; + } else if (!idle && yd->current_status == YAHOO_STATUS_IDLE) { + pkt = yahoo_packet_new(YAHOO_SERVICE_ISAWAY, YAHOO_STATUS_AVAILABLE, 0); + yd->current_status = YAHOO_STATUS_AVAILABLE; + } + + if (pkt) { + char buf[4]; + g_snprintf(buf, sizeof(buf), "%d", yd->current_status); + yahoo_packet_hash(pkt, 10, buf); + yahoo_send_packet(yd, pkt); + yahoo_packet_free(pkt); + } +} + +static GList *yahoo_away_states(struct gaim_connection *gc) +{ + GList *m = NULL; + + m = g_list_append(m, "Available"); + m = g_list_append(m, "Be Right Back"); + m = g_list_append(m, "Busy"); + m = g_list_append(m, "Not At Home"); + m = g_list_append(m, "Not At Desk"); + m = g_list_append(m, "Not In Office"); + m = g_list_append(m, "On Phone"); + m = g_list_append(m, "On Vacation"); + m = g_list_append(m, "Out To Lunch"); + m = g_list_append(m, "Stepped Out"); + m = g_list_append(m, "Invisible"); + m = g_list_append(m, GAIM_AWAY_CUSTOM); + + return m; +} + +static void yahoo_keepalive(struct gaim_connection *gc) +{ + struct yahoo_data *yd = gc->proto_data; + struct yahoo_packet *pkt = yahoo_packet_new(YAHOO_SERVICE_PING, YAHOO_STATUS_AVAILABLE, 0); + yahoo_send_packet(yd, pkt); + yahoo_packet_free(pkt); +} + +static void yahoo_add_buddy(struct gaim_connection *gc, char *who) +{ + struct yahoo_data *yd = (struct yahoo_data *)gc->proto_data; + struct yahoo_packet *pkt; + struct group *g; + char *group = NULL; + + if (!yd->logged_in) + return; + + g = find_group_by_buddy(gc, who); + if (g) + group = g->name; + else + group = "Buddies"; + + pkt = yahoo_packet_new(YAHOO_SERVICE_ADDBUDDY, YAHOO_STATUS_AVAILABLE, 0); + yahoo_packet_hash(pkt, 1, gc->displayname); + yahoo_packet_hash(pkt, 7, who); + yahoo_packet_hash(pkt, 65, group); + yahoo_send_packet(yd, pkt); + yahoo_packet_free(pkt); +} + +static void yahoo_remove_buddy(struct gaim_connection *gc, char *who, char *group) +{ + struct yahoo_data *yd = (struct yahoo_data *)gc->proto_data; + + struct yahoo_packet *pkt = yahoo_packet_new(YAHOO_SERVICE_REMBUDDY, YAHOO_STATUS_AVAILABLE, 0); + yahoo_packet_hash(pkt, 1, gc->displayname); + yahoo_packet_hash(pkt, 7, who); + yahoo_packet_hash(pkt, 65, group); + yahoo_send_packet(yd, pkt); + yahoo_packet_free(pkt); +} + +static struct prpl *my_protocol = NULL; + +void yahoo_init(struct prpl *ret) { + ret->protocol = PROTO_YAHOO; + ret->options = OPT_PROTO_MAIL_CHECK; + ret->name = yahoo_name; + ret->user_opts = yahoo_user_opts; + ret->login = yahoo_login; + ret->close = yahoo_close; + ret->buddy_menu = yahoo_buddy_menu; + ret->list_icon = yahoo_list_icon; + ret->actions = yahoo_actions; + ret->do_action = yahoo_do_action; + ret->send_im = yahoo_send_im; + ret->away_states = yahoo_away_states; + ret->set_away = yahoo_set_away; + ret->set_idle = yahoo_set_idle; + ret->keepalive = yahoo_keepalive; + ret->add_buddy = yahoo_add_buddy; + ret->remove_buddy = yahoo_remove_buddy; + + my_protocol = ret; +} + +#ifndef STATIC + +char *gaim_plugin_init(GModule *handle) +{ + load_protocol(yahoo_init, sizeof(struct prpl)); + return NULL; +} + +void gaim_plugin_remove() +{ + struct prpl *p = find_prpl(PROTO_YAHOO); + if (p == my_protocol) + unload_protocol(p); +} + +char *name() +{ + return "Yahoo"; +} + +char *description() +{ + return PRPL_DESC("Yahoo"); +} + +#endif