📄 yahoo.c
字号:
/* * gaim * * Gaim is the legal property of its developers, whose names are too numerous * to list here. Please refer to the COPYRIGHT file distributed with this * source distribution. * * 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 "internal.h"#include "account.h"#include "accountopt.h"#include "blist.h"#include "debug.h"#include "notify.h"#include "privacy.h"#include "prpl.h"#include "proxy.h"#include "request.h"#include "server.h"#include "util.h"#include "version.h"#include "sha.h"#include "yahoo.h"#include "yahoo_friend.h"#include "yahoochat.h"#include "ycht.h"#include "yahoo_auth.h"#include "yahoo_filexfer.h"#include "yahoo_picture.h"#include "md5.h"extern char *yahoo_crypt(const char *, const char *);/* #define YAHOO_DEBUG */static void yahoo_add_buddy(GaimConnection *gc, GaimBuddy *, GaimGroup *);static void yahoo_login_page_cb(void *user_data, const char *buf, size_t len);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;}void yahoo_packet_hash(struct yahoo_packet *pkt, int key, const 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);}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;}static void yahoo_packet_read(struct yahoo_packet *pkt, guchar *data, int len){ int pos = 0; while (pos + 1 < len) { char key[64], *value = NULL, *esc; int accept; int x; struct yahoo_pair *pair = g_new0(struct yahoo_pair, 1); /* this is weird, and in one of the chat packets, and causes us * think all the values are keys and all the keys are values after * this point if we don't handle it */ if (data[pos] == '\0') { while (pos + 1 < len) { if (data[pos] == 0xc0 && data[pos + 1] == 0x80) break; pos++; } pos += 2; g_free(pair); continue; } x = 0; while (pos + 1 < len) { if (data[pos] == 0xc0 && data[pos + 1] == 0x80) break; if (x >= sizeof(key)-1) { x++; pos++; continue; } key[x++] = data[pos++]; } if (x >= sizeof(key)-1) { x = 0; } key[x] = 0; pos += 2; pair->key = strtol(key, NULL, 10); accept = x; /* if x is 0 there was no key, so don't accept it */ if (len - pos + 1 <= 0) { /* Truncated. Garbage or something. */ accept = 0; } if (accept) { value = g_malloc(len - pos + 1); x = 0; while (pos + 1 < len) { if (data[pos] == 0xc0 && data[pos + 1] == 0x80) break; value[x++] = data[pos++]; } value[x] = 0; pair->value = g_strdup(value); g_free(value); pkt->hash = g_slist_append(pkt->hash, pair); esc = g_strescape(pair->value, NULL); gaim_debug(GAIM_DEBUG_MISC, "yahoo", "Key: %d \tValue: %s\n", pair->key, esc); g_free(esc); } else { g_free(pair); } pos += 2; /* Skip over garbage we've noticed in the mail notifications */ if (data[0] == '9' && data[pos] == 0x01) pos++; }}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; gaim_debug(GAIM_DEBUG_MISC, "yahoo", ""); for (i = 0; i + 1 < len; i += 2) { if ((i % 16 == 0) && i) { gaim_debug(GAIM_DEBUG_MISC, NULL, "\n"); gaim_debug(GAIM_DEBUG_MISC, "yahoo", ""); } gaim_debug(GAIM_DEBUG_MISC, NULL, "%02x%02x ", data[i], data[i + 1]); } if (i < len) gaim_debug(GAIM_DEBUG_MISC, NULL, "%02x", data[i]); gaim_debug(GAIM_DEBUG_MISC, NULL, "\n"); gaim_debug(GAIM_DEBUG_MISC, "yahoo", ""); for (i = 0; i < len; i++) { if ((i % 16 == 0) && i) { gaim_debug(GAIM_DEBUG_MISC, NULL, "\n"); gaim_debug(GAIM_DEBUG_MISC, "yahoo", ""); } if (g_ascii_isprint(data[i])) gaim_debug(GAIM_DEBUG_MISC, NULL, "%c ", data[i]); else gaim_debug(GAIM_DEBUG_MISC, NULL, ". "); } gaim_debug(GAIM_DEBUG_MISC, NULL, "\n");#endif}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; if (yd->wm) pos += yahoo_put16(data + pos, YAHOO_WEBMESSENGER_PROTO_VER); else pos += yahoo_put16(data + pos, YAHOO_PROTO_VER); 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); if (ret != len) gaim_debug_warning("yahoo", "Only wrote %d of %d bytes!", ret, len); g_free(data); return ret;}int yahoo_send_packet_special(int fd, struct yahoo_packet *pkt, int pad){ int pktlen = yahoo_packet_length(pkt); int len = YAHOO_PACKET_HDRLEN + pktlen; int ret; guchar *data; int pos = 0; if (fd < 0) return -1; data = g_malloc0(len + 1); memcpy(data + pos, "YMSG", 4); pos += 4; pos += yahoo_put16(data + pos, YAHOO_PROTO_VER); pos += yahoo_put16(data + pos, 0x0000); pos += yahoo_put16(data + pos, pktlen + pad); 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); ret = write(fd, data, len); g_free(data); return ret;}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_update_status(GaimConnection *gc, const char *name, YahooFriend *f){ gboolean online = TRUE; if (!gc || !name || !f || !gaim_find_buddy(gaim_connection_get_account(gc), name)) return; if (f->status == YAHOO_STATUS_OFFLINE) online = FALSE; serv_got_update(gc, name, online, 0, 0, f->idle, f->away ? UC_UNAVAILABLE : 0);}static void yahoo_process_status(GaimConnection *gc, struct yahoo_packet *pkt){ struct yahoo_data *yd = gc->proto_data; GSList *l = pkt->hash; YahooFriend *f = NULL; char *name = NULL; if (pkt->service == YAHOO_SERVICE_LOGOFF && pkt->status == -1) { gc->wants_to_die = TRUE; gaim_connection_error(gc, _("You have been logged off as you have logged in on a different machine or device.")); return; } 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. */ if (!yd->logged_in) { gaim_connection_set_display_name(gc, pair->value); gaim_connection_set_state(gc, GAIM_CONNECTED); serv_finish_login(gc); yd->logged_in = TRUE; if (yd->picture_upload_todo) { yahoo_buddy_icon_upload(gc, yd->picture_upload_todo); yd->picture_upload_todo = NULL; } /* this requests the list. i have a feeling that this is very evil * * scs.yahoo.com sends you the list before this packet without it being * requested * * do_import(gc, NULL); * 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; if (name && g_utf8_validate(name, -1, NULL)) f = yahoo_friend_find_or_new(gc, name); else { f = NULL; name = NULL; } break; case 10: /* state */ if (!f) break; f->status = strtol(pair->value, NULL, 10); if ((f->status >= YAHOO_STATUS_BRB) && (f->status <= YAHOO_STATUS_STEPPEDOUT)) f->away = 1; else f->away = 0; if (f->status == YAHOO_STATUS_IDLE) f->idle = time(NULL); else f->idle = 0; if (f->status != YAHOO_STATUS_CUSTOM) yahoo_friend_set_status_message(f, NULL); f->sms = 0; break; case 19: /* custom message */ if (f) yahoo_friend_set_status_message(f, yahoo_string_decode(gc, pair->value, FALSE)); break; case 11: /* this is the buddy's session id */ break; case 17: /* in chat? */ break; case 47: /* is custom status away or not? 2=idle*/ if (!f) break; /* I have no idea what it means when this is * set when someone's available, but it doesn't * mean idle. */ if (f->status == YAHOO_STATUS_AVAILABLE) break; f->away = strtol(pair->value, NULL, 10); if (f->away == 2) f->idle = time(NULL); break; case 138: /* either we're not idle, or we are but won't say how long */ if (!f) break; if (f->idle) f->idle = -1; break; case 137: /* usually idle time in seconds, sometimes login time */ if (!f) break; if (f->status != YAHOO_STATUS_AVAILABLE) f->idle = time(NULL) - strtol(pair->value, NULL, 10); break; case 13: /* bitmask, bit 0 = pager, bit 1 = chat, bit 2 = game */ if (strtol(pair->value, NULL, 10) == 0) { if (f) f->status = YAHOO_STATUS_OFFLINE; serv_got_update(gc, name, FALSE, 0, 0, 0, 0); break; } if (f) yahoo_update_status(gc, name, f); break; case 60: /* SMS */ if (f) { f->sms = strtol(pair->value, NULL, 10); yahoo_update_status(gc, name, f); } break; case 197: /* Avatars */ { char *decoded, *tmp; guint len; if (pair->value) { gaim_base64_decode(pair->value, &decoded, &len); if (len) { tmp = gaim_str_binary_to_ascii(decoded, len); gaim_debug_info("yahoo", "Got key 197, value = %s\n", tmp); g_free(tmp); } g_free(decoded); } break; } case 192: /* Pictures, aka Buddy Icons, checksum */ { int cksum = strtol(pair->value, NULL, 10); GaimBuddy *b; if (!name) break; b = gaim_find_buddy(gc->account, name); if (!cksum || (cksum == -1)) { if (f) yahoo_friend_set_buddy_icon_need_request(f, TRUE); gaim_buddy_icons_set_for_user(gc->account, name, NULL, 0); if (b) gaim_blist_node_remove_setting((GaimBlistNode *)b, YAHOO_ICON_CHECKSUM_KEY); break; } if (!f) break; yahoo_friend_set_buddy_icon_need_request(f, FALSE); if (cksum != gaim_blist_node_get_int((GaimBlistNode*)b, YAHOO_ICON_CHECKSUM_KEY)) yahoo_send_picture_request(gc, name); break; } case 16: /* Custom error message */ { char *tmp = yahoo_string_decode(gc, pair->value, TRUE); gaim_notify_error(gc, NULL, tmp, NULL); g_free(tmp); } break; default: gaim_debug(GAIM_DEBUG_ERROR, "yahoo", "Unknown status key %d\n", pair->key); break; } l = l->next; }}static void yahoo_do_group_check(GaimAccount *account, GHashTable *ht, const char *name, const char *group){ GaimBuddy *b; GaimGroup *g; GSList *list, *i; gboolean onlist = 0; char *oname = NULL; char **oname_p = &oname; GSList **list_p = &list; if (!g_hash_table_lookup_extended(ht, gaim_normalize(account, name), (gpointer *) oname_p, (gpointer *) list_p)) list = gaim_find_buddies(account, name); else g_hash_table_steal(ht, name); for (i = list; i; i = i->next) { b = i->data; g = gaim_find_buddys_group(b); if (!gaim_utf8_strcasecmp(group, g->name)) { gaim_debug(GAIM_DEBUG_MISC, "yahoo", "Oh good, %s is in the right group (%s).\n", name, group); list = g_slist_delete_link(list, i);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -