Annotation of ChivanetAimPidgin/oscarprpl/src/c/oscar.c, revision 1.1.1.1

1.1       snw         1: /*
                      2:  * purple
                      3:  *
                      4:  * Some code copyright (C) 1998-1999, Mark Spencer <markster@marko.net>
                      5:  * Some code copyright (C) 1999-2001, Eric Warmenhoven
                      6:  * Some code copyright (C) 2001-2003, Sean Egan
                      7:  * Some code copyright (C) 2001-2007, Mark Doliner <thekingant@users.sourceforge.net>
                      8:  * Some code copyright (C) 2005, Jonathan Clark <ardentlygnarly@users.sourceforge.net>
                      9:  * Some code copyright (C) 2007, ComBOTS Product GmbH (htfv) <foss@combots.com>
                     10:  * Some code copyright (C) 2008, Aman Gupta
                     11:  *
                     12:  * Most libfaim code copyright (C) 1998-2001 Adam Fritzler <afritz@auk.cx>
                     13:  * Some libfaim code copyright (C) 2001-2004 Mark Doliner <thekingant@users.sourceforge.net>
                     14:  *
                     15:  * This program is free software; you can redistribute it and/or modify
                     16:  * it under the terms of the GNU General Public License as published by
                     17:  * the Free Software Foundation; either version 2 of the License, or
                     18:  * (at your option) any later version.
                     19:  *
                     20:  * This program is distributed in the hope that it will be useful,
                     21:  * but WITHOUT ANY WARRANTY; without even the implied warranty of
                     22:  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
                     23:  * GNU General Public License for more details.
                     24:  *
                     25:  * You should have received a copy of the GNU General Public License
                     26:  * along with this program; if not, write to the Free Software
                     27:  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
                     28:  *
                     29:  */
                     30: 
                     31: #include "internal.h"
                     32: 
                     33: #include "account.h"
                     34: #include "accountopt.h"
                     35: #include "buddyicon.h"
                     36: #include "cipher.h"
                     37: #include "conversation.h"
                     38: #include "core.h"
                     39: #include "debug.h"
                     40: #include "encoding.h"
                     41: #include "imgstore.h"
                     42: #include "network.h"
                     43: #include "notify.h"
                     44: #include "privacy.h"
                     45: #include "prpl.h"
                     46: #include "proxy.h"
                     47: #include "request.h"
                     48: #include "util.h"
                     49: #include "version.h"
                     50: #include "visibility.h"
                     51: #include <ctype.h>
                     52: #include "oscarcommon.h"
                     53: #include "oscar.h"
                     54: #include "peer.h"
                     55: 
                     56: #define AIMHASHDATA "http://pidgin.im/aim_data.php3"
                     57: 
                     58: #define OSCAR_CONNECT_STEPS 6
                     59: 
                     60: static guint64 purple_caps =
                     61:        OSCAR_CAPABILITY_CHAT
                     62:                | OSCAR_CAPABILITY_BUDDYICON
                     63:                | OSCAR_CAPABILITY_DIRECTIM
                     64:                | OSCAR_CAPABILITY_SENDFILE
                     65:                | OSCAR_CAPABILITY_UNICODE
                     66:                | OSCAR_CAPABILITY_INTEROPERATE
                     67:                | OSCAR_CAPABILITY_SHORTCAPS
                     68:                | OSCAR_CAPABILITY_TYPING
                     69:                | OSCAR_CAPABILITY_ICQSERVERRELAY
                     70:                | OSCAR_CAPABILITY_NEWCAPS
                     71:                | OSCAR_CAPABILITY_XTRAZ
                     72:                | OSCAR_CAPABILITY_HTML_MSGS;
                     73: 
                     74: static guint8 features_aim[] = {0x01, 0x01, 0x01, 0x02};
                     75: static guint8 features_icq[] = {0x01};
                     76: 
                     77: struct create_room {
                     78:        char *name;
                     79:        int exchange;
                     80: };
                     81: 
                     82: struct oscar_ask_directim_data
                     83: {
                     84:        OscarData *od;
                     85:        char *who;
                     86: };
                     87: 
                     88: /* All the libfaim->purple callback functions */
                     89: 
                     90: /* Only used when connecting with the old-style BUCP login */
                     91: static int purple_parse_auth_resp  (OscarData *, FlapConnection *, FlapFrame *, ...);
                     92: static int purple_parse_login      (OscarData *, FlapConnection *, FlapFrame *, ...);
                     93: static int purple_parse_auth_securid_request(OscarData *, FlapConnection *, FlapFrame *, ...);
                     94: 
                     95: static int purple_handle_redirect  (OscarData *, FlapConnection *, FlapFrame *, ...);
                     96: static int purple_info_change      (OscarData *, FlapConnection *, FlapFrame *, ...);
                     97: static int purple_account_confirm  (OscarData *, FlapConnection *, FlapFrame *, ...);
                     98: static int purple_parse_oncoming   (OscarData *, FlapConnection *, FlapFrame *, ...);
                     99: static int purple_parse_offgoing   (OscarData *, FlapConnection *, FlapFrame *, ...);
                    100: static int purple_parse_incoming_im(OscarData *, FlapConnection *, FlapFrame *, ...);
                    101: static int purple_parse_misses     (OscarData *, FlapConnection *, FlapFrame *, ...);
                    102: static int purple_parse_clientauto (OscarData *, FlapConnection *, FlapFrame *, ...);
                    103: static int purple_parse_motd       (OscarData *, FlapConnection *, FlapFrame *, ...);
                    104: static int purple_chatnav_info     (OscarData *, FlapConnection *, FlapFrame *, ...);
                    105: static int purple_conv_chat_join        (OscarData *, FlapConnection *, FlapFrame *, ...);
                    106: static int purple_conv_chat_leave       (OscarData *, FlapConnection *, FlapFrame *, ...);
                    107: static int purple_conv_chat_info_update (OscarData *, FlapConnection *, FlapFrame *, ...);
                    108: static int purple_conv_chat_incoming_msg(OscarData *, FlapConnection *, FlapFrame *, ...);
                    109: static int purple_email_parseupdate(OscarData *, FlapConnection *, FlapFrame *, ...);
                    110: static int purple_icon_parseicon   (OscarData *, FlapConnection *, FlapFrame *, ...);
                    111: static int purple_parse_searcherror(OscarData *, FlapConnection *, FlapFrame *, ...);
                    112: static int purple_parse_searchreply(OscarData *, FlapConnection *, FlapFrame *, ...);
                    113: static int purple_bosrights        (OscarData *, FlapConnection *, FlapFrame *, ...);
                    114: static int purple_connerr          (OscarData *, FlapConnection *, FlapFrame *, ...);
                    115: static int purple_parse_mtn        (OscarData *, FlapConnection *, FlapFrame *, ...);
                    116: static int purple_parse_locaterights(OscarData *, FlapConnection *, FlapFrame *, ...);
                    117: static int purple_parse_buddyrights(OscarData *, FlapConnection *, FlapFrame *, ...);
                    118: static int purple_parse_genericerr (OscarData *, FlapConnection *, FlapFrame *, ...);
                    119: static int purple_memrequest       (OscarData *, FlapConnection *, FlapFrame *, ...);
                    120: static int purple_selfinfo         (OscarData *, FlapConnection *, FlapFrame *, ...);
                    121: static int purple_popup            (OscarData *, FlapConnection *, FlapFrame *, ...);
                    122: static int purple_ssi_parseerr     (OscarData *, FlapConnection *, FlapFrame *, ...);
                    123: static int purple_ssi_parserights  (OscarData *, FlapConnection *, FlapFrame *, ...);
                    124: static int purple_ssi_parselist    (OscarData *, FlapConnection *, FlapFrame *, ...);
                    125: static int purple_ssi_parseack     (OscarData *, FlapConnection *, FlapFrame *, ...);
                    126: static int purple_ssi_parseaddmod  (OscarData *, FlapConnection *, FlapFrame *, ...);
                    127: static int purple_ssi_authgiven    (OscarData *, FlapConnection *, FlapFrame *, ...);
                    128: static int purple_ssi_authrequest  (OscarData *, FlapConnection *, FlapFrame *, ...);
                    129: static int purple_ssi_authreply    (OscarData *, FlapConnection *, FlapFrame *, ...);
                    130: static int purple_ssi_gotadded     (OscarData *, FlapConnection *, FlapFrame *, ...);
                    131: 
                    132: static void purple_icons_fetch(PurpleConnection *gc);
                    133: 
                    134: void oscar_set_info(PurpleConnection *gc, const char *info);
                    135: static void oscar_set_info_and_status(PurpleAccount *account, gboolean setinfo, const char *rawinfo, gboolean setstatus, PurpleStatus *status);
                    136: static void oscar_set_extended_status(PurpleConnection *gc);
                    137: static gboolean purple_ssi_rerequestdata(gpointer data);
                    138: 
                    139: void oscar_free_name_data(struct name_data *data) {
                    140:        g_free(data->name);
                    141:        g_free(data->nick);
                    142:        g_free(data);
                    143: }
                    144: 
                    145: #ifdef _WIN32
                    146: const char *oscar_get_locale_charset(void) {
                    147:        static const char *charset = NULL;
                    148:        if (charset == NULL)
                    149:                g_get_charset(&charset);
                    150:        return charset;
                    151: }
                    152: #endif
                    153: 
                    154: static char *oscar_icqstatus(int state) {
                    155:        /* Make a cute little string that shows the status of the dude or dudet */
                    156:        if (state & AIM_ICQ_STATE_CHAT)
                    157:                return g_strdup(_("Free For Chat"));
                    158:        else if (state & AIM_ICQ_STATE_DND)
                    159:                return g_strdup(_("Do Not Disturb"));
                    160:        else if (state & AIM_ICQ_STATE_OUT)
                    161:                return g_strdup(_("Not Available"));
                    162:        else if (state & AIM_ICQ_STATE_BUSY)
                    163:                return g_strdup(_("Occupied"));
                    164:        else if (state & AIM_ICQ_STATE_AWAY)
                    165:                return g_strdup(_("Away"));
                    166:        else if (state & AIM_ICQ_STATE_WEBAWARE)
                    167:                return g_strdup(_("Web Aware"));
                    168:        else if (state & AIM_ICQ_STATE_INVISIBLE)
                    169:                return g_strdup(_("Invisible"));
                    170:        else if (state & AIM_ICQ_STATE_EVIL)
                    171:                return g_strdup(_("Evil"));
                    172:        else if (state & AIM_ICQ_STATE_DEPRESSION)
                    173:                return g_strdup(_("Depression"));
                    174:        else if (state & AIM_ICQ_STATE_ATHOME)
                    175:                return g_strdup(_("At home"));
                    176:        else if (state & AIM_ICQ_STATE_ATWORK)
                    177:                return g_strdup(_("At work"));
                    178:        else if (state & AIM_ICQ_STATE_LUNCH)
                    179:                return g_strdup(_("At lunch"));
                    180:        else
                    181:                return g_strdup(_("Online"));
                    182: }
                    183: 
                    184: static char *extract_name(const char *name) {
                    185:        char *tmp, *x;
                    186:        int i, j;
                    187: 
                    188:        if (!name)
                    189:                return NULL;
                    190: 
                    191:        x = strchr(name, '-');
                    192:        if (!x)
                    193:                return NULL;
                    194: 
                    195:        x = strchr(x + 1, '-');
                    196:        if (!x)
                    197:                return NULL;
                    198: 
                    199:        tmp = g_strdup(++x);
                    200: 
                    201:        for (i = 0, j = 0; x[i]; i++) {
                    202:                char hex[3];
                    203:                if (x[i] != '%') {
                    204:                        tmp[j++] = x[i];
                    205:                        continue;
                    206:                }
                    207:                strncpy(hex, x + ++i, 2);
                    208:                hex[2] = 0;
                    209:                i++;
                    210:                tmp[j++] = strtol(hex, NULL, 16);
                    211:        }
                    212: 
                    213:        tmp[j] = 0;
                    214:        return tmp;
                    215: }
                    216: 
                    217: static struct chat_connection *
                    218: find_oscar_chat(PurpleConnection *gc, int id)
                    219: {
                    220:        OscarData *od = purple_connection_get_protocol_data(gc);
                    221:        GSList *cur;
                    222:        struct chat_connection *cc;
                    223: 
                    224:        for (cur = od->oscar_chats; cur != NULL; cur = cur->next)
                    225:        {
                    226:                cc = (struct chat_connection *)cur->data;
                    227:                if (cc->id == id)
                    228:                        return cc;
                    229:        }
                    230: 
                    231:        return NULL;
                    232: }
                    233: 
                    234: static struct chat_connection *
                    235: find_oscar_chat_by_conn(PurpleConnection *gc, FlapConnection *conn)
                    236: {
                    237:        OscarData *od = purple_connection_get_protocol_data(gc);
                    238:        GSList *cur;
                    239:        struct chat_connection *cc;
                    240: 
                    241:        for (cur = od->oscar_chats; cur != NULL; cur = cur->next)
                    242:        {
                    243:                cc = (struct chat_connection *)cur->data;
                    244:                if (cc->conn == conn)
                    245:                        return cc;
                    246:        }
                    247: 
                    248:        return NULL;
                    249: }
                    250: 
                    251: static struct chat_connection *
                    252: find_oscar_chat_by_conv(PurpleConnection *gc, PurpleConversation *conv)
                    253: {
                    254:        OscarData *od = purple_connection_get_protocol_data(gc);
                    255:        GSList *cur;
                    256:        struct chat_connection *cc;
                    257: 
                    258:        for (cur = od->oscar_chats; cur != NULL; cur = cur->next)
                    259:        {
                    260:                cc = (struct chat_connection *)cur->data;
                    261:                if (cc->conv == conv)
                    262:                        return cc;
                    263:        }
                    264: 
                    265:        return NULL;
                    266: }
                    267: 
                    268: void
                    269: oscar_chat_destroy(struct chat_connection *cc)
                    270: {
                    271:        g_free(cc->name);
                    272:        g_free(cc->show);
                    273:        g_free(cc);
                    274: }
                    275: 
                    276: static void
                    277: oscar_chat_kill(PurpleConnection *gc, struct chat_connection *cc)
                    278: {
                    279:        OscarData *od = purple_connection_get_protocol_data(gc);
                    280: 
                    281:        /* Notify the conversation window that we've left the chat */
                    282:        serv_got_chat_left(gc, purple_conv_chat_get_id(PURPLE_CONV_CHAT(cc->conv)));
                    283: 
                    284:        /* Destroy the chat_connection */
                    285:        od->oscar_chats = g_slist_remove(od->oscar_chats, cc);
                    286:        oscar_chat_destroy(cc);
                    287: }
                    288: 
                    289: /**
                    290:  * This is called from the callback functions for establishing
                    291:  * a TCP connection with an oscar host if an error occurred.
                    292:  */
                    293: static void
                    294: connection_common_error_cb(FlapConnection *conn, const gchar *error_message)
                    295: {
                    296:        OscarData *od;
                    297:        PurpleConnection *gc;
                    298: 
                    299:        od = conn->od;
                    300:        gc = od->gc;
                    301: 
                    302:        purple_debug_error("oscar", "unable to connect to FLAP "
                    303:                        "server of type 0x%04hx\n", conn->type);
                    304: 
                    305:        if (conn->type == SNAC_FAMILY_AUTH)
                    306:        {
                    307:                /* This only happens when connecting with the old-style BUCP login */
                    308:                gchar *msg;
                    309:                msg = g_strdup_printf(_("Unable to connect to authentication server: %s"),
                    310:                                error_message);
                    311:                purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, msg);
                    312:                g_free(msg);
                    313:        }
                    314:        else if (conn->type == SNAC_FAMILY_LOCATE)
                    315:        {
                    316:                gchar *msg;
                    317:                msg = g_strdup_printf(_("Unable to connect to BOS server: %s"),
                    318:                                error_message);
                    319:                purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, msg);
                    320:                g_free(msg);
                    321:        }
                    322:        else
                    323:        {
                    324:                /* Maybe we should call this for BOS connections, too? */
                    325:                flap_connection_schedule_destroy(conn,
                    326:                                OSCAR_DISCONNECT_COULD_NOT_CONNECT, error_message);
                    327:        }
                    328: }
                    329: 
                    330: /**
                    331:  * This is called from the callback functions for establishing
                    332:  * a TCP connection with an oscar host. Depending on the type
                    333:  * of host, we do a few different things here.
                    334:  */
                    335: static void
                    336: connection_common_established_cb(FlapConnection *conn)
                    337: {
                    338:        OscarData *od;
                    339:        PurpleConnection *gc;
                    340:        PurpleAccount *account;
                    341: 
                    342:        od = conn->od;
                    343:        gc = od->gc;
                    344:        account = purple_connection_get_account(gc);
                    345: 
                    346:        purple_debug_info("oscar", "connected to FLAP server of type 0x%04hx\n",
                    347:                        conn->type);
                    348: 
                    349:        if (conn->cookie == NULL)
                    350:                flap_connection_send_version(od, conn);
                    351:        else
                    352:        {
                    353:                const gchar *login_type = purple_account_get_string(account, "login_type", OSCAR_DEFAULT_LOGIN);
                    354: 
                    355:                if (!purple_strequal(login_type, OSCAR_MD5_LOGIN))
                    356:                {
                    357:                        ClientInfo aiminfo = CLIENTINFO_PURPLE_AIM;
                    358:                        ClientInfo icqinfo = CLIENTINFO_PURPLE_ICQ;
                    359:                        flap_connection_send_version_with_cookie_and_clientinfo(od,
                    360:                                        conn, conn->cookielen, conn->cookie,
                    361:                                        od->icq ? &icqinfo : &aiminfo,
                    362:                                        purple_account_get_bool(account, "allow_multiple_logins", OSCAR_DEFAULT_ALLOW_MULTIPLE_LOGINS));
                    363:                } else {
                    364:                        flap_connection_send_version_with_cookie(od, conn,
                    365:                                        conn->cookielen, conn->cookie);
                    366:                }
                    367: 
                    368: 
                    369:                g_free(conn->cookie);
                    370:                conn->cookie = NULL;
                    371:        }
                    372: 
                    373:        if (conn->type == SNAC_FAMILY_AUTH)
                    374:        {
                    375:                /* This only happens when connecting with the old-style BUCP login */
                    376:                aim_request_login(od, conn, purple_account_get_username(account));
                    377:                purple_debug_info("oscar", "Username sent, waiting for response\n");
                    378:                purple_connection_update_progress(gc, _("Username sent"), 1, OSCAR_CONNECT_STEPS);
                    379:        }
                    380:        else if (conn->type == SNAC_FAMILY_LOCATE)
                    381:        {
                    382:                purple_connection_update_progress(gc, _("Connection established, cookie sent"), 4, OSCAR_CONNECT_STEPS);
                    383:        }
                    384:        else if (conn->type == SNAC_FAMILY_CHAT)
                    385:        {
                    386:                od->oscar_chats = g_slist_prepend(od->oscar_chats, conn->new_conn_data);
                    387:                conn->new_conn_data = NULL;
                    388:        }
                    389: }
                    390: 
                    391: static void
                    392: connection_established_cb(gpointer data, gint source, const gchar *error_message)
                    393: {
                    394:        FlapConnection *conn;
                    395: 
                    396:        conn = data;
                    397: 
                    398:        conn->connect_data = NULL;
                    399:        conn->fd = source;
                    400: 
                    401:        if (source < 0)
                    402:        {
                    403:                connection_common_error_cb(conn, error_message);
                    404:                return;
                    405:        }
                    406: 
                    407:        conn->watcher_incoming = purple_input_add(conn->fd,
                    408:                        PURPLE_INPUT_READ, flap_connection_recv_cb, conn);
                    409:        connection_common_established_cb(conn);
                    410: }
                    411: 
                    412: static void
                    413: ssl_connection_established_cb(gpointer data, PurpleSslConnection *gsc,
                    414:                PurpleInputCondition cond)
                    415: {
                    416:        FlapConnection *conn;
                    417: 
                    418:        conn = data;
                    419: 
                    420:        purple_ssl_input_add(gsc, flap_connection_recv_cb_ssl, conn);
                    421:        connection_common_established_cb(conn);
                    422: }
                    423: 
                    424: static void
                    425: ssl_connection_error_cb(PurpleSslConnection *gsc, PurpleSslErrorType error,
                    426:                gpointer data)
                    427: {
                    428:        FlapConnection *conn;
                    429: 
                    430:        conn = data;
                    431: 
                    432:        if (conn->watcher_outgoing)
                    433:        {
                    434:                purple_input_remove(conn->watcher_outgoing);
                    435:                conn->watcher_outgoing = 0;
                    436:        }
                    437: 
                    438:        /* sslconn frees the connection on error */
                    439:        conn->gsc = NULL;
                    440: 
                    441:        connection_common_error_cb(conn, purple_ssl_strerror(error));
                    442: }
                    443: 
                    444: static void
                    445: flap_connection_established_bos(OscarData *od, FlapConnection *conn)
                    446: {
                    447:        PurpleConnection *gc = od->gc;
                    448: 
                    449:        aim_srv_reqpersonalinfo(od, conn);
                    450: 
                    451:        purple_debug_info("oscar", "ssi: requesting rights and list\n");
                    452:        aim_ssi_reqrights(od);
                    453:        aim_ssi_reqdata(od);
                    454:        if (od->getblisttimer > 0)
                    455:                purple_timeout_remove(od->getblisttimer);
                    456:        od->getblisttimer = purple_timeout_add_seconds(30, purple_ssi_rerequestdata, od);
                    457: 
                    458:        aim_locate_reqrights(od);
                    459:        aim_buddylist_reqrights(od, conn);
                    460:        aim_im_reqparams(od);
                    461:        aim_bos_reqrights(od, conn); /* TODO: Don't call this with ssi */
                    462: 
                    463:        purple_connection_update_progress(gc, _("Finalizing connection"), 5, OSCAR_CONNECT_STEPS);
                    464: }
                    465: 
                    466: static void
                    467: flap_connection_established_admin(OscarData *od, FlapConnection *conn)
                    468: {
                    469:        aim_srv_clientready(od, conn);
                    470:        purple_debug_info("oscar", "connected to admin\n");
                    471: 
                    472:        if (od->chpass) {
                    473:                purple_debug_info("oscar", "changing password\n");
                    474:                aim_admin_changepasswd(od, conn, od->newp, od->oldp);
                    475:                g_free(od->oldp);
                    476:                od->oldp = NULL;
                    477:                g_free(od->newp);
                    478:                od->newp = NULL;
                    479:                od->chpass = FALSE;
                    480:        }
                    481:        if (od->setnick) {
                    482:                purple_debug_info("oscar", "formatting username\n");
                    483:                aim_admin_setnick(od, conn, od->newformatting);
                    484:                g_free(od->newformatting);
                    485:                od->newformatting = NULL;
                    486:                od->setnick = FALSE;
                    487:        }
                    488:        if (od->conf) {
                    489:                purple_debug_info("oscar", "confirming account\n");
                    490:                aim_admin_reqconfirm(od, conn);
                    491:                od->conf = FALSE;
                    492:        }
                    493:        if (od->reqemail) {
                    494:                purple_debug_info("oscar", "requesting email address\n");
                    495:                aim_admin_getinfo(od, conn, 0x0011);
                    496:                od->reqemail = FALSE;
                    497:        }
                    498:        if (od->setemail) {
                    499:                purple_debug_info("oscar", "setting email address\n");
                    500:                aim_admin_setemail(od, conn, od->email);
                    501:                g_free(od->email);
                    502:                od->email = NULL;
                    503:                od->setemail = FALSE;
                    504:        }
                    505: }
                    506: 
                    507: static void
                    508: flap_connection_established_chat(OscarData *od, FlapConnection *conn)
                    509: {
                    510:        PurpleConnection *gc = od->gc;
                    511:        struct chat_connection *chatcon;
                    512:        static int id = 1;
                    513: 
                    514:        aim_srv_clientready(od, conn);
                    515: 
                    516:        chatcon = find_oscar_chat_by_conn(gc, conn);
                    517:        if (chatcon) {
                    518:                chatcon->id = id;
                    519:                chatcon->conv = serv_got_joined_chat(gc, id++, chatcon->show);
                    520:        }
                    521: }
                    522: 
                    523: static void
                    524: flap_connection_established_chatnav(OscarData *od, FlapConnection *conn)
                    525: {
                    526:        aim_srv_clientready(od, conn);
                    527:        aim_chatnav_reqrights(od, conn);
                    528: }
                    529: 
                    530: static void
                    531: flap_connection_established_alert(OscarData *od, FlapConnection *conn)
                    532: {
                    533:        aim_email_sendcookies(od);
                    534:        aim_email_activate(od);
                    535:        aim_srv_clientready(od, conn);
                    536: }
                    537: 
                    538: static void
                    539: flap_connection_established_bart(OscarData *od, FlapConnection *conn)
                    540: {
                    541:        PurpleConnection *gc = od->gc;
                    542: 
                    543:        aim_srv_clientready(od, conn);
                    544: 
                    545:        od->iconconnecting = FALSE;
                    546: 
                    547:        purple_icons_fetch(gc);
                    548: }
                    549: 
                    550: static int
                    551: flap_connection_established(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...)
                    552: {
                    553:        conn->connected = TRUE;
                    554:        purple_debug_info("oscar", "FLAP connection of type 0x%04hx is "
                    555:                        "now fully connected\n", conn->type);
                    556:        if (conn->type == SNAC_FAMILY_LOCATE)
                    557:                flap_connection_established_bos(od, conn);
                    558:        else if (conn->type == SNAC_FAMILY_ADMIN)
                    559:                flap_connection_established_admin(od, conn);
                    560:        else if (conn->type == SNAC_FAMILY_CHAT)
                    561:                flap_connection_established_chat(od, conn);
                    562:        else if (conn->type == SNAC_FAMILY_CHATNAV)
                    563:                flap_connection_established_chatnav(od, conn);
                    564:        else if (conn->type == SNAC_FAMILY_ALERT)
                    565:                flap_connection_established_alert(od, conn);
                    566:        else if (conn->type == SNAC_FAMILY_BART)
                    567:                flap_connection_established_bart(od, conn);
                    568: 
                    569:        return 1;
                    570: }
                    571: 
                    572: static void
                    573: idle_reporting_pref_cb(const char *name, PurplePrefType type,
                    574:                gconstpointer value, gpointer data)
                    575: {
                    576:        PurpleConnection *gc;
                    577:        OscarData *od;
                    578:        gboolean report_idle;
                    579:        guint32 presence;
                    580: 
                    581:        gc = data;
                    582:        od = purple_connection_get_protocol_data(gc);
                    583:        report_idle = !purple_strequal((const char *)value, "none");
                    584:        presence = aim_ssi_getpresence(od->ssi.local);
                    585: 
                    586:        if (report_idle)
                    587:                aim_ssi_setpresence(od, presence | AIM_SSI_PRESENCE_FLAG_SHOWIDLE);
                    588:        else
                    589:                aim_ssi_setpresence(od, presence & ~AIM_SSI_PRESENCE_FLAG_SHOWIDLE);
                    590: }
                    591: 
                    592: /**
                    593:  * Should probably make a "Use recent buddies group" account preference
                    594:  * so that this option is surfaced to the user.
                    595:  */
                    596: static void
                    597: recent_buddies_pref_cb(const char *name, PurplePrefType type,
                    598:                gconstpointer value, gpointer data)
                    599: {
                    600:        PurpleConnection *gc;
                    601:        OscarData *od;
                    602:        guint32 presence;
                    603: 
                    604:        gc = data;
                    605:        od = purple_connection_get_protocol_data(gc);
                    606:        presence = aim_ssi_getpresence(od->ssi.local);
                    607: 
                    608:        if (value)
                    609:                aim_ssi_setpresence(od, presence & ~AIM_SSI_PRESENCE_FLAG_NORECENTBUDDIES);
                    610:        else
                    611:                aim_ssi_setpresence(od, presence | AIM_SSI_PRESENCE_FLAG_NORECENTBUDDIES);
                    612: }
                    613: 
                    614: static const gchar *login_servers[] = {
                    615:        AIM_DEFAULT_LOGIN_SERVER,
                    616:        AIM_DEFAULT_SSL_LOGIN_SERVER,
                    617:        ICQ_DEFAULT_LOGIN_SERVER,
                    618:        ICQ_DEFAULT_SSL_LOGIN_SERVER,
                    619: };
                    620: 
                    621: static const gchar *
                    622: get_login_server(gboolean is_icq, gboolean use_ssl)
                    623: {
                    624:        return login_servers[(is_icq ? 2 : 0) + (use_ssl ? 1 : 0)];
                    625: }
                    626: 
                    627: static gint
                    628: compare_handlers(gconstpointer a, gconstpointer b)
                    629: {
                    630:        guint aa = GPOINTER_TO_UINT(a);
                    631:        guint bb = GPOINTER_TO_UINT(b);
                    632:        guint family1 = aa >> 16;
                    633:        guint family2 = bb >> 16;
                    634:        guint subtype1 = aa & 0xFFFF;
                    635:        guint subtype2 = bb & 0xFFFF;
                    636:        if (family1 != family2) {
                    637:                return family1 - family2;
                    638:        }
                    639:        return subtype1 - subtype2;
                    640: }
                    641: 
                    642: #if !GLIB_CHECK_VERSION(2,14,0)
                    643: static void hash_table_get_list_of_keys(gpointer key, gpointer value, gpointer user_data)
                    644: {
                    645:        GList **handlers = (GList **)user_data;
                    646: 
                    647:        *handlers = g_list_prepend(*handlers, key);
                    648: }
                    649: #endif /* GLIB < 2.14.0 */
                    650: 
                    651: void
                    652: oscar_login(PurpleAccount *account)
                    653: {
                    654:        PurpleConnection *gc;
                    655:        OscarData *od;
                    656:        const gchar *encryption_type;
                    657:        const gchar *login_type;
                    658:        GList *handlers;
                    659:        GList *sorted_handlers;
                    660:        GList *cur;
                    661:        GString *msg = g_string_new("");
                    662: 
                    663:        gc = purple_account_get_connection(account);
                    664:        od = oscar_data_new();
                    665:        od->gc = gc;
                    666:        purple_connection_set_protocol_data(gc, od);
                    667: 
                    668:        oscar_data_addhandler(od, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNERR, purple_connerr, 0);
                    669:        oscar_data_addhandler(od, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNINITDONE, flap_connection_established, 0);
                    670: 
                    671:        oscar_data_addhandler(od, SNAC_FAMILY_ADMIN, 0x0003, purple_info_change, 0);
                    672:        oscar_data_addhandler(od, SNAC_FAMILY_ADMIN, 0x0005, purple_info_change, 0);
                    673:        oscar_data_addhandler(od, SNAC_FAMILY_ADMIN, 0x0007, purple_account_confirm, 0);
                    674:        oscar_data_addhandler(od, SNAC_FAMILY_ALERT, 0x0001, purple_parse_genericerr, 0);
                    675:        oscar_data_addhandler(od, SNAC_FAMILY_ALERT, SNAC_SUBTYPE_ALERT_MAILSTATUS, purple_email_parseupdate, 0);
                    676: 
                    677:        /* These are only needed when connecting with the old-style BUCP login */
                    678:        oscar_data_addhandler(od, SNAC_FAMILY_AUTH, 0x0003, purple_parse_auth_resp, 0);
                    679:        oscar_data_addhandler(od, SNAC_FAMILY_AUTH, 0x0007, purple_parse_login, 0);
                    680:        oscar_data_addhandler(od, SNAC_FAMILY_AUTH, SNAC_SUBTYPE_AUTH_SECURID_REQUEST, purple_parse_auth_securid_request, 0);
                    681: 
                    682:        oscar_data_addhandler(od, SNAC_FAMILY_BART, SNAC_SUBTYPE_BART_RESPONSE, purple_icon_parseicon, 0);
                    683:        oscar_data_addhandler(od, SNAC_FAMILY_BOS, 0x0001, purple_parse_genericerr, 0);
                    684:        oscar_data_addhandler(od, SNAC_FAMILY_BOS, 0x0003, purple_bosrights, 0);
                    685:        oscar_data_addhandler(od, SNAC_FAMILY_BUDDY, 0x0001, purple_parse_genericerr, 0);
                    686:        oscar_data_addhandler(od, SNAC_FAMILY_BUDDY, SNAC_SUBTYPE_BUDDY_RIGHTSINFO, purple_parse_buddyrights, 0);
                    687:        oscar_data_addhandler(od, SNAC_FAMILY_BUDDY, SNAC_SUBTYPE_BUDDY_ONCOMING, purple_parse_oncoming, 0);
                    688:        oscar_data_addhandler(od, SNAC_FAMILY_BUDDY, SNAC_SUBTYPE_BUDDY_OFFGOING, purple_parse_offgoing, 0);
                    689:        oscar_data_addhandler(od, SNAC_FAMILY_CHAT, 0x0001, purple_parse_genericerr, 0);
                    690:        oscar_data_addhandler(od, SNAC_FAMILY_CHAT, SNAC_SUBTYPE_CHAT_USERJOIN, purple_conv_chat_join, 0);
                    691:        oscar_data_addhandler(od, SNAC_FAMILY_CHAT, SNAC_SUBTYPE_CHAT_USERLEAVE, purple_conv_chat_leave, 0);
                    692:        oscar_data_addhandler(od, SNAC_FAMILY_CHAT, SNAC_SUBTYPE_CHAT_ROOMINFOUPDATE, purple_conv_chat_info_update, 0);
                    693:        oscar_data_addhandler(od, SNAC_FAMILY_CHAT, SNAC_SUBTYPE_CHAT_INCOMINGMSG, purple_conv_chat_incoming_msg, 0);
                    694:        oscar_data_addhandler(od, SNAC_FAMILY_CHATNAV, 0x0001, purple_parse_genericerr, 0);
                    695:        oscar_data_addhandler(od, SNAC_FAMILY_CHATNAV, SNAC_SUBTYPE_CHATNAV_INFO, purple_chatnav_info, 0);
                    696:        oscar_data_addhandler(od, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_ERROR, purple_ssi_parseerr, 0);
                    697:        oscar_data_addhandler(od, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_RIGHTSINFO, purple_ssi_parserights, 0);
                    698:        oscar_data_addhandler(od, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_LIST, purple_ssi_parselist, 0);
                    699:        oscar_data_addhandler(od, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_SRVACK, purple_ssi_parseack, 0);
                    700:        oscar_data_addhandler(od, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_ADD, purple_ssi_parseaddmod, 0);
                    701:        oscar_data_addhandler(od, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_MOD, purple_ssi_parseaddmod, 0);
                    702:        oscar_data_addhandler(od, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_RECVAUTH, purple_ssi_authgiven, 0);
                    703:        oscar_data_addhandler(od, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_RECVAUTHREQ, purple_ssi_authrequest, 0);
                    704:        oscar_data_addhandler(od, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_RECVAUTHREP, purple_ssi_authreply, 0);
                    705:        oscar_data_addhandler(od, SNAC_FAMILY_FEEDBAG, SNAC_SUBTYPE_FEEDBAG_ADDED, purple_ssi_gotadded, 0);
                    706:        oscar_data_addhandler(od, SNAC_FAMILY_ICBM, SNAC_SUBTYPE_ICBM_INCOMING, purple_parse_incoming_im, 0);
                    707:        oscar_data_addhandler(od, SNAC_FAMILY_ICBM, SNAC_SUBTYPE_ICBM_MISSEDCALL, purple_parse_misses, 0);
                    708:        oscar_data_addhandler(od, SNAC_FAMILY_ICBM, SNAC_SUBTYPE_ICBM_CLIENTAUTORESP, purple_parse_clientauto, 0);
                    709:        oscar_data_addhandler(od, SNAC_FAMILY_ICBM, SNAC_SUBTYPE_ICBM_MTN, purple_parse_mtn, 0);
                    710:        oscar_data_addhandler(od, SNAC_FAMILY_LOCATE, SNAC_SUBTYPE_LOCATE_RIGHTSINFO, purple_parse_locaterights, 0);
                    711:        oscar_data_addhandler(od, SNAC_FAMILY_OSERVICE, 0x0001, purple_parse_genericerr, 0);
                    712:        oscar_data_addhandler(od, SNAC_FAMILY_OSERVICE, 0x000f, purple_selfinfo, 0);
                    713:        oscar_data_addhandler(od, SNAC_FAMILY_OSERVICE, 0x001f, purple_memrequest, 0);
                    714:        oscar_data_addhandler(od, SNAC_FAMILY_OSERVICE, SNAC_SUBTYPE_OSERVICE_REDIRECT, purple_handle_redirect, 0);
                    715:        oscar_data_addhandler(od, SNAC_FAMILY_OSERVICE, SNAC_SUBTYPE_OSERVICE_MOTD, purple_parse_motd, 0);
                    716:        oscar_data_addhandler(od, SNAC_FAMILY_POPUP, 0x0002, purple_popup, 0);
                    717:        oscar_data_addhandler(od, SNAC_FAMILY_USERLOOKUP, SNAC_SUBTYPE_USERLOOKUP_ERROR, purple_parse_searcherror, 0);
                    718:        oscar_data_addhandler(od, SNAC_FAMILY_USERLOOKUP, 0x0003, purple_parse_searchreply, 0);
                    719: 
                    720:        g_string_append(msg, "Registered handlers: ");
                    721: #if GLIB_CHECK_VERSION(2,14,0)
                    722:        handlers = g_hash_table_get_keys(od->handlerlist);
                    723: #else
                    724:        handlers = NULL;
                    725:        g_hash_table_foreach(od->handlerlist, hash_table_get_list_of_keys, &handlers);
                    726: #endif /* GLIB < 2.14.0 */
                    727:        sorted_handlers = g_list_sort(g_list_copy(handlers), compare_handlers);
                    728:        for (cur = sorted_handlers; cur; cur = cur->next) {
                    729:                guint x = GPOINTER_TO_UINT(cur->data);
                    730:                g_string_append_printf(msg, "%04x/%04x, ", x >> 16, x & 0xFFFF);
                    731:        }
                    732:        g_list_free(sorted_handlers);
                    733:        g_list_free(handlers);
                    734:        purple_debug_misc("oscar", "%s\n", msg->str);
                    735:        g_string_free(msg, TRUE);
                    736: 
                    737:        purple_debug_misc("oscar", "oscar_login: gc = %p\n", gc);
                    738: 
                    739:        if (!oscar_util_valid_name(purple_account_get_username(account))) {
                    740:                gchar *buf;
                    741:                buf = g_strdup_printf(_("Unable to sign on as %s because the username is invalid.  Usernames must be a valid email address, or start with a letter and contain only letters, numbers and spaces, or contain only numbers."), purple_account_get_username(account));
                    742:                purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_INVALID_SETTINGS, buf);
                    743:                g_free(buf);
                    744:                return;
                    745:        }
                    746: 
                    747:        gc->flags |= PURPLE_CONNECTION_HTML;
                    748:        if (purple_strequal(purple_account_get_protocol_id(account), "prpl-icq")) {
                    749:                od->icq = TRUE;
                    750:        } else {
                    751:                gc->flags |= PURPLE_CONNECTION_AUTO_RESP;
                    752:        }
                    753: 
                    754:        /* Set this flag based on the protocol_id rather than the username,
                    755:           because that is what's tied to the get_moods prpl callback. */
                    756:        if (purple_strequal(purple_account_get_protocol_id(account), "prpl-icq"))
                    757:                gc->flags |= PURPLE_CONNECTION_SUPPORT_MOODS;
                    758: 
                    759:        od->default_port = purple_account_get_int(account, "port", OSCAR_DEFAULT_LOGIN_PORT);
                    760: 
                    761:        login_type = purple_account_get_string(account, "login_type", OSCAR_DEFAULT_LOGIN);
                    762:        encryption_type = purple_account_get_string(account, "encryption", OSCAR_DEFAULT_ENCRYPTION);
                    763:        if (!purple_ssl_is_supported() && purple_strequal(encryption_type, OSCAR_REQUIRE_ENCRYPTION)) {
                    764:                purple_connection_error_reason(
                    765:                        gc,
                    766:                        PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT,
                    767:                        _("You required encryption in your account settings, but encryption is not supported by your system."));
                    768:                return;
                    769:        }
                    770:        od->use_ssl = purple_ssl_is_supported() && !purple_strequal(encryption_type, OSCAR_NO_ENCRYPTION);
                    771: 
                    772:        /* Connect to core Purple signals */
                    773:        purple_prefs_connect_callback(gc, "/purple/away/idle_reporting", idle_reporting_pref_cb, gc);
                    774:        purple_prefs_connect_callback(gc, "/plugins/prpl/oscar/recent_buddies", recent_buddies_pref_cb, gc);
                    775: 
                    776:        /*
                    777:         * On 2008-03-05 AOL released some documentation on the OSCAR protocol
                    778:         * which includes a new login method called clientLogin.  It is similar
                    779:         * (though not the same?) as what the AIM 6.0 series uses to
                    780:         * authenticate.
                    781:         *
                    782:         * AIM 5.9 and lower use an MD5-based login procedure called "BUCP".
                    783:         * This authentication method is used for both ICQ and AIM when
                    784:         * clientLogin is not enabled.
                    785:         */
                    786:        if (purple_strequal(login_type, OSCAR_CLIENT_LOGIN)) {
                    787:                /* Note: Actual server/port configuration is ignored here */
                    788:                send_client_login(od, purple_account_get_username(account));
                    789:        } else if (purple_strequal(login_type, OSCAR_KERBEROS_LOGIN)) {
                    790:                const char *server;
                    791: 
                    792:                if (!od->use_ssl) {
                    793:                        purple_connection_error_reason(
                    794:                                gc,
                    795:                                PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT,
                    796:                                _("You required Kerberos authentication but encryption is disabled in your account settings."));
                    797:                        return;
                    798:                }
                    799:                server = purple_account_get_string(account, "server", AIM_DEFAULT_KDC_SERVER);
                    800:                /*
                    801:                 * If the account's server is what the oscar protocol has offered as
                    802:                 * the default login server through the vast eons (all two of
                    803:                 * said default options, AFAIK) and the user wants KDC, we'll
                    804:                 * do what we know is best for them and change the setting out
                    805:                 * from under them to the KDC login server.
                    806:                 */
                    807:                if (purple_strequal(server, get_login_server(od->icq, FALSE)) ||
                    808:                        purple_strequal(server, get_login_server(od->icq, TRUE)) ||
                    809:                        purple_strequal(server, AIM_ALT_LOGIN_SERVER) ||
                    810:                        purple_strequal(server, "")) {
                    811:                        purple_debug_info("oscar", "Account uses Kerberos auth, so changing server to default KDC server\n");
                    812:                        purple_account_set_string(account, "server", AIM_DEFAULT_KDC_SERVER);
                    813:                        purple_account_set_int(account, "port", AIM_DEFAULT_KDC_PORT);
                    814:                }
                    815:                send_kerberos_login(od, purple_account_get_username(account));
                    816:        } else {
                    817:                FlapConnection *newconn;
                    818:                const char *server;
                    819: 
                    820:                newconn = flap_connection_new(od, SNAC_FAMILY_AUTH);
                    821: 
                    822:                if (od->use_ssl) {
                    823:                        server = purple_account_get_string(account, "server", get_login_server(od->icq, TRUE));
                    824: 
                    825:                        /*
                    826:                         * If the account's server is what the oscar prpl has offered as
                    827:                         * the default login server through the vast eons (all two of
                    828:                         * said default options, AFAIK) and the user wants SSL, we'll
                    829:                         * do what we know is best for them and change the setting out
                    830:                         * from under them to the SSL login server.
                    831:                         */
                    832:                        if (purple_strequal(server, get_login_server(od->icq, FALSE)) ||
                    833:                                purple_strequal(server, AIM_ALT_LOGIN_SERVER) ||
                    834:                                purple_strequal(server, AIM_DEFAULT_KDC_SERVER) ||
                    835:                                purple_strequal(server, "")) {
                    836:                                purple_debug_info("oscar", "Account uses SSL, so changing server to default SSL server\n");
                    837:                                purple_account_set_string(account, "server", get_login_server(od->icq, TRUE));
                    838:                                purple_account_set_int(account, "port", OSCAR_DEFAULT_LOGIN_PORT),
                    839:                                server = get_login_server(od->icq, TRUE);
                    840:                        }
                    841: 
                    842:                        newconn->gsc = purple_ssl_connect(account, server,
                    843:                                        purple_account_get_int(account, "port", OSCAR_DEFAULT_LOGIN_PORT),
                    844:                                        ssl_connection_established_cb, ssl_connection_error_cb, newconn);
                    845:                } else {
                    846:                        server = purple_account_get_string(account, "server", get_login_server(od->icq, FALSE));
                    847: 
                    848:                        /*
                    849:                         * See the comment above. We do the reverse here. If they don't want
                    850:                         * SSL but their server is set to OSCAR_DEFAULT_SSL_LOGIN_SERVER,
                    851:                         * set it back to the default.
                    852:                         */
                    853:                        if (purple_strequal(server, get_login_server(od->icq, TRUE)) ||
                    854:                                purple_strequal(server, AIM_DEFAULT_KDC_SERVER) ||
                    855:                                purple_strequal(server, "")) {
                    856:                                purple_debug_info("oscar", "Account does not use SSL, so changing server back to non-SSL\n");
                    857:                                purple_account_set_string(account, "server", get_login_server(od->icq, FALSE));
                    858:                                purple_account_set_int(account, "port", OSCAR_DEFAULT_LOGIN_PORT),
                    859:                                server = get_login_server(od->icq, FALSE);
                    860:                        }
                    861: 
                    862:                        newconn->connect_data = purple_proxy_connect(NULL, account, server,
                    863:                                        purple_account_get_int(account, "port", OSCAR_DEFAULT_LOGIN_PORT),
                    864:                                        connection_established_cb, newconn);
                    865:                }
                    866: 
                    867:                if (newconn->gsc == NULL && newconn->connect_data == NULL) {
                    868:                        purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
                    869:                                        _("Unable to connect"));
                    870:                        return;
                    871:                }
                    872:        }
                    873: 
                    874:        purple_connection_update_progress(gc, _("Connecting"), 0, OSCAR_CONNECT_STEPS);
                    875: }
                    876: 
                    877: void
                    878: oscar_close(PurpleConnection *gc)
                    879: {
                    880:        OscarData *od;
                    881: 
                    882:        od = purple_connection_get_protocol_data(gc);
                    883: 
                    884:        while (od->oscar_chats)
                    885:        {
                    886:                struct chat_connection *cc = od->oscar_chats->data;
                    887:                od->oscar_chats = g_slist_remove(od->oscar_chats, cc);
                    888:                oscar_chat_destroy(cc);
                    889:        }
                    890:        while (od->create_rooms)
                    891:        {
                    892:                struct create_room *cr = od->create_rooms->data;
                    893:                g_free(cr->name);
                    894:                od->create_rooms = g_slist_remove(od->create_rooms, cr);
                    895:                g_free(cr);
                    896:        }
                    897:        oscar_data_destroy(od);
                    898:        purple_connection_set_protocol_data(gc, NULL);
                    899: 
                    900:        purple_prefs_disconnect_by_handle(gc);
                    901: 
                    902:        purple_debug_info("oscar", "Signed off.\n");
                    903: }
                    904: 
                    905: /* XXX - Should use purple_util_fetch_url for the below stuff */
                    906: struct pieceofcrap {
                    907:        PurpleConnection *gc;
                    908:        unsigned long offset;
                    909:        unsigned long len;
                    910:        char *modname;
                    911:        int fd;
                    912:        FlapConnection *conn;
                    913:        unsigned int inpa;
                    914: };
                    915: 
                    916: static void damn_you(gpointer data, gint source, PurpleInputCondition c)
                    917: {
                    918:        struct pieceofcrap *pos = data;
                    919:        OscarData *od = purple_connection_get_protocol_data(pos->gc);
                    920:        char in = '\0';
                    921:        int x = 0;
                    922:        unsigned char m[17];
                    923:        GString *msg;
                    924: 
                    925:        while (read(pos->fd, &in, 1) == 1) {
                    926:                if (in == '\n')
                    927:                        x++;
                    928:                else if (in != '\r')
                    929:                        x = 0;
                    930:                if (x == 2)
                    931:                        break;
                    932:                in = '\0';
                    933:        }
                    934:        if (in != '\n') {
                    935:                char buf[256];
                    936:                g_snprintf(buf, sizeof(buf), _("You may be disconnected shortly.  "
                    937:                                "If so, check %s for updates."),
                    938:                                oscar_get_ui_info_string("website", PURPLE_WEBSITE));
                    939:                purple_notify_warning(pos->gc, NULL,
                    940:                                                        _("Unable to get a valid AIM login hash."),
                    941:                                                        buf);
                    942:                purple_input_remove(pos->inpa);
                    943:                close(pos->fd);
                    944:                g_free(pos);
                    945:                return;
                    946:        }
                    947:        if (read(pos->fd, m, 16) != 16)
                    948:        {
                    949:                purple_debug_warning("oscar", "Could not read full AIM login hash "
                    950:                                "from " AIMHASHDATA "--that's bad.\n");
                    951:        }
                    952:        m[16] = '\0';
                    953: 
                    954:        msg = g_string_new("Sending hash: ");
                    955:        for (x = 0; x < 16; x++)
                    956:                g_string_append_printf(msg, "%02hhx ", (unsigned char)m[x]);
                    957:        g_string_append(msg, "\n");
                    958:        purple_debug_misc("oscar", "%s", msg->str);
                    959:        g_string_free(msg, TRUE);
                    960: 
                    961:        purple_input_remove(pos->inpa);
                    962:        close(pos->fd);
                    963:        aim_sendmemblock(od, pos->conn, 0, 16, m, AIM_SENDMEMBLOCK_FLAG_ISHASH);
                    964:        g_free(pos);
                    965: }
                    966: 
                    967: static void
                    968: straight_to_hell(gpointer data, gint source, const gchar *error_message)
                    969: {
                    970:        struct pieceofcrap *pos = data;
                    971:        gchar *buf;
                    972:        gssize result;
                    973: 
                    974:        pos->fd = source;
                    975: 
                    976:        if (source < 0) {
                    977:                buf = g_strdup_printf(_("You may be disconnected shortly.  "
                    978:                                "If so, check %s for updates."),
                    979:                                oscar_get_ui_info_string("website", PURPLE_WEBSITE));
                    980:                purple_notify_warning(pos->gc, NULL,
                    981:                                                        _("Unable to get a valid AIM login hash."),
                    982:                                                        buf);
                    983:                g_free(buf);
                    984:                g_free(pos->modname);
                    985:                g_free(pos);
                    986:                return;
                    987:        }
                    988: 
                    989:        buf = g_strdup_printf("GET " AIMHASHDATA "?offset=%ld&len=%ld&modname=%s HTTP/1.0\n\n",
                    990:                        pos->offset, pos->len, pos->modname ? pos->modname : "");
                    991:        result = send(pos->fd, buf, strlen(buf), 0);
                    992:        if (result < 0)
                    993:                purple_debug_error("oscar", "Error writing %" G_GSIZE_FORMAT
                    994:                                " bytes to fetch AIM hash data: %s\n",
                    995:                                strlen(buf), g_strerror(errno));
                    996:        else if ((gsize)result != strlen(buf))
                    997:                purple_debug_error("oscar", "Tried to write %"
                    998:                                G_GSIZE_FORMAT " bytes to fetch AIM hash data but "
                    999:                                "instead wrote %" G_GSSIZE_FORMAT " bytes\n",
                   1000:                                strlen(buf), result);
                   1001:        g_free(buf);
                   1002:        g_free(pos->modname);
                   1003:        pos->inpa = purple_input_add(pos->fd, PURPLE_INPUT_READ, damn_you, pos);
                   1004:        return;
                   1005: }
                   1006: 
                   1007: /* size of icbmui.ocm, the largest module in AIM 3.5 */
                   1008: #define AIM_MAX_FILE_SIZE 98304
                   1009: 
                   1010: static int purple_memrequest(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...)
                   1011: {
                   1012:        va_list ap;
                   1013:        struct pieceofcrap *pos;
                   1014:        guint32 offset, len;
                   1015:        char *modname;
                   1016: 
                   1017:        va_start(ap, fr);
                   1018:        offset = va_arg(ap, guint32);
                   1019:        len = va_arg(ap, guint32);
                   1020:        modname = va_arg(ap, char *);
                   1021:        va_end(ap);
                   1022: 
                   1023:        purple_debug_misc("oscar", "offset: %u, len: %u, file: %s\n",
                   1024:                                        offset, len, (modname ? modname : "aim.exe"));
                   1025: 
                   1026:        if (len == 0) {
                   1027:                purple_debug_misc("oscar", "len is 0, hashing NULL\n");
                   1028:                aim_sendmemblock(od, conn, offset, len, NULL,
                   1029:                                AIM_SENDMEMBLOCK_FLAG_ISREQUEST);
                   1030:                return 1;
                   1031:        }
                   1032: 
                   1033:        pos = g_new0(struct pieceofcrap, 1);
                   1034:        pos->gc = od->gc;
                   1035:        pos->conn = conn;
                   1036: 
                   1037:        pos->offset = offset;
                   1038:        pos->len = len;
                   1039:        pos->modname = g_strdup(modname);
                   1040: 
                   1041:        if (purple_proxy_connect(pos->gc, pos->gc->account, "pidgin.im", 80,
                   1042:                        straight_to_hell, pos) == NULL)
                   1043:        {
                   1044:                char buf[256];
                   1045:                g_free(pos->modname);
                   1046:                g_free(pos);
                   1047: 
                   1048:                g_snprintf(buf, sizeof(buf), _("You may be disconnected shortly.  "
                   1049:                        "If so, check %s for updates."),
                   1050:                        oscar_get_ui_info_string("website", PURPLE_WEBSITE));
                   1051:                purple_notify_warning(pos->gc, NULL,
                   1052:                                                        _("Unable to get a valid login hash."),
                   1053:                                                        buf);
                   1054:        }
                   1055: 
                   1056:        return 1;
                   1057: }
                   1058: 
                   1059: int oscar_connect_to_bos(PurpleConnection *gc, OscarData *od, const char *host, guint16 port, guint8 *cookie, guint16 cookielen, const char *tls_certname)
                   1060: {
                   1061:        PurpleAccount *account;
                   1062:        FlapConnection *conn;
                   1063: 
                   1064:        account = purple_connection_get_account(gc);
                   1065: 
                   1066:        conn = flap_connection_new(od, SNAC_FAMILY_LOCATE);
                   1067:        conn->cookielen = cookielen;
                   1068:        conn->cookie = g_memdup(cookie, cookielen);
                   1069: 
                   1070:        /*
                   1071:         * Use TLS only if the server provided us with a tls_certname. The server might not specify a tls_certname even if we requested to use TLS,
                   1072:         * and that is something we should be prepared to.
                   1073:         */
                   1074:        if (tls_certname)
                   1075:        {
                   1076:                conn->gsc = purple_ssl_connect_with_ssl_cn(account, host, port,
                   1077:                                ssl_connection_established_cb, ssl_connection_error_cb,
                   1078:                                tls_certname, conn);
                   1079:        }
                   1080:        else
                   1081:        {
                   1082:                conn->connect_data = purple_proxy_connect(NULL,
                   1083:                                account, host, port,
                   1084:                                connection_established_cb, conn);
                   1085:        }
                   1086: 
                   1087:        if (conn->gsc == NULL && conn->connect_data == NULL)
                   1088:        {
                   1089:                purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Unable to connect"));
                   1090:                return 0;
                   1091:        }
                   1092: 
                   1093:        od->default_port = port;
                   1094: 
                   1095:        purple_connection_update_progress(gc, _("Received authorization"), 3, OSCAR_CONNECT_STEPS);
                   1096: 
                   1097:        return 1;
                   1098: }
                   1099: 
                   1100: /**
                   1101:  * Only used when connecting with the old-style BUCP login.
                   1102:  */
                   1103: static int
                   1104: purple_parse_auth_resp(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...)
                   1105: {
                   1106:        PurpleConnection *gc = od->gc;
                   1107:        PurpleAccount *account = purple_connection_get_account(gc);
                   1108:        char *host; int port;
                   1109:        size_t i;
                   1110:        FlapConnection *newconn;
                   1111:        va_list ap;
                   1112:        struct aim_authresp_info *info;
                   1113: 
                   1114:        port = purple_account_get_int(account, "port", od->default_port);
                   1115: 
                   1116:        va_start(ap, fr);
                   1117:        info = va_arg(ap, struct aim_authresp_info *);
                   1118:        va_end(ap);
                   1119: 
                   1120:        purple_debug_info("oscar",
                   1121:                           "inside auth_resp (Username: %s)\n", info->bn);
                   1122: 
                   1123:        if (info->errorcode || !info->bosip || !info->cookielen || !info->cookie) {
                   1124:                char buf[256];
                   1125:                switch (info->errorcode) {
                   1126:                case 0x01:
                   1127:                        /* Unregistered username */
                   1128:                        purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_INVALID_USERNAME, _("Username does not exist"));
                   1129:                        break;
                   1130:                case 0x05:
                   1131:                        /* Incorrect password */
                   1132:                        if (!purple_account_get_remember_password(account))
                   1133:                                purple_account_set_password(account, NULL);
                   1134:                        purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED, _("Incorrect password"));
                   1135:                        break;
                   1136:                case 0x11:
                   1137:                        /* Suspended account */
                   1138:                        purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED, _("Your account is currently suspended"));
                   1139:                        break;
                   1140:                case 0x02:
                   1141:                case 0x14:
                   1142:                        /* service temporarily unavailable */
                   1143:                        purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("The AOL Instant Messenger service is temporarily unavailable."));
                   1144:                        break;
                   1145:                case 0x18:
                   1146:                        /* username connecting too frequently */
                   1147:                        purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR, _("Your username has been connecting and disconnecting too frequently. Wait ten minutes and try again. If you continue to try, you will need to wait even longer."));
                   1148:                        break;
                   1149:                case 0x1c:
                   1150:                {
                   1151:                        /* client too old */
                   1152:                        g_snprintf(buf, sizeof(buf), _("The client version you are using is too old. Please upgrade at %s"),
                   1153:                                        oscar_get_ui_info_string("website", PURPLE_WEBSITE));
                   1154:                        purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR, buf);
                   1155:                        break;
                   1156:                }
                   1157:                case 0x1d:
                   1158:                        /* IP address connecting too frequently */
                   1159:                        purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR, _("Your IP address has been connecting and disconnecting too frequently. Wait a minute and try again. If you continue to try, you will need to wait even longer."));
                   1160:                        break;
                   1161:                default:
                   1162:                        purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED, _("Unknown reason"));
                   1163:                        break;
                   1164:                }
                   1165:                purple_debug_info("oscar", "Login Error Code 0x%04hx\n", info->errorcode);
                   1166:                purple_debug_info("oscar", "Error URL: %s\n", info->errorurl ? info->errorurl : "");
                   1167:                return 1;
                   1168:        }
                   1169: 
                   1170:        purple_debug_misc("oscar", "Reg status: %hu\n"
                   1171:                                                           "Email: %s\n"
                   1172:                                                           "BOSIP: %s\n",
                   1173:                                                           info->regstatus,
                   1174:                                                           info->email ? info->email : "null",
                   1175:                                                           info->bosip ? info->bosip : "null");
                   1176:        purple_debug_info("oscar", "Closing auth connection...\n");
                   1177:        flap_connection_schedule_destroy(conn, OSCAR_DISCONNECT_DONE, NULL);
                   1178: 
                   1179:        for (i = 0; i < strlen(info->bosip); i++) {
                   1180:                if (info->bosip[i] == ':') {
                   1181:                        port = atoi(&(info->bosip[i+1]));
                   1182:                        break;
                   1183:                }
                   1184:        }
                   1185:        host = g_strndup(info->bosip, i);
                   1186:        newconn = flap_connection_new(od, SNAC_FAMILY_LOCATE);
                   1187:        newconn->cookielen = info->cookielen;
                   1188:        newconn->cookie = g_memdup(info->cookie, info->cookielen);
                   1189: 
                   1190:        if (od->use_ssl)
                   1191:        {
                   1192:                /*
                   1193:                 * This shouldn't be hardcoded to "bos.oscar.aol.com" except that
                   1194:                 * the server isn't sending us a name to use for comparing the
                   1195:                 * certificate common name.
                   1196:                 */
                   1197:                newconn->gsc = purple_ssl_connect_with_ssl_cn(account, host, port,
                   1198:                                ssl_connection_established_cb, ssl_connection_error_cb,
                   1199:                                "bos.oscar.aol.com", newconn);
                   1200:        }
                   1201:        else
                   1202:        {
                   1203:                newconn->connect_data = purple_proxy_connect(NULL, account, host, port,
                   1204:                                connection_established_cb, newconn);
                   1205:        }
                   1206: 
                   1207:        g_free(host);
                   1208:        if (newconn->gsc == NULL && newconn->connect_data == NULL)
                   1209:        {
                   1210:                purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Unable to connect"));
                   1211:                return 0;
                   1212:        }
                   1213: 
                   1214:        purple_connection_update_progress(gc, _("Received authorization"), 3, OSCAR_CONNECT_STEPS);
                   1215: 
                   1216:        return 1;
                   1217: }
                   1218: 
                   1219: /**
                   1220:  * Only used when connecting with the old-style BUCP login.
                   1221:  */
                   1222: static void
                   1223: purple_parse_auth_securid_request_yes_cb(gpointer user_data, const char *msg)
                   1224: {
                   1225:        PurpleConnection *gc = user_data;
                   1226:        OscarData *od = purple_connection_get_protocol_data(gc);
                   1227: 
                   1228:        aim_auth_securid_send(od, msg);
                   1229: }
                   1230: 
                   1231: /**
                   1232:  * Only used when connecting with the old-style BUCP login.
                   1233:  */
                   1234: static void
                   1235: purple_parse_auth_securid_request_no_cb(gpointer user_data, const char *value)
                   1236: {
                   1237:        PurpleConnection *gc = user_data;
                   1238: 
                   1239:        /* Disconnect */
                   1240:        purple_connection_error_reason(gc,
                   1241:                PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED,
                   1242:                _("The SecurID key entered is invalid"));
                   1243: }
                   1244: 
                   1245: /**
                   1246:  * Only used when connecting with the old-style BUCP login.
                   1247:  */
                   1248: static int
                   1249: purple_parse_auth_securid_request(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...)
                   1250: {
                   1251:        PurpleConnection *gc = od->gc;
                   1252:        PurpleAccount *account = purple_connection_get_account(gc);
                   1253:        gchar *primary;
                   1254: 
                   1255:        purple_debug_info("oscar", "Got SecurID request\n");
                   1256: 
                   1257:        primary = g_strdup_printf("Enter the SecurID key for %s.", purple_account_get_username(account));
                   1258:        purple_request_input(gc, NULL, _("Enter SecurID"), primary,
                   1259:                                           _("Enter the 6 digit number from the digital display."),
                   1260:                                           FALSE, FALSE, NULL,
                   1261:                                           _("_OK"), G_CALLBACK(purple_parse_auth_securid_request_yes_cb),
                   1262:                                           _("_Cancel"), G_CALLBACK(purple_parse_auth_securid_request_no_cb),
                   1263:                                           account, NULL, NULL,
                   1264:                                           gc);
                   1265:        g_free(primary);
                   1266: 
                   1267:        return 1;
                   1268: }
                   1269: 
                   1270: /**
                   1271:  * Only used when connecting with the old-style BUCP login.
                   1272:  */
                   1273: static int
                   1274: purple_parse_login(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...)
                   1275: {
                   1276:        PurpleConnection *gc;
                   1277:        PurpleAccount *account;
                   1278:        ClientInfo aiminfo = CLIENTINFO_PURPLE_AIM;
                   1279:        ClientInfo icqinfo = CLIENTINFO_PURPLE_ICQ;
                   1280:        va_list ap;
                   1281:        char *key;
                   1282:        gboolean truncate_pass;
                   1283: 
                   1284:        gc = od->gc;
                   1285:        account = purple_connection_get_account(gc);
                   1286: 
                   1287:        va_start(ap, fr);
                   1288:        key = va_arg(ap, char *);
                   1289:        truncate_pass = va_arg(ap, int);
                   1290:        va_end(ap);
                   1291: 
                   1292:        aim_send_login(od, conn, purple_account_get_username(account),
                   1293:                        purple_connection_get_password(gc), truncate_pass,
                   1294:                        od->icq ? &icqinfo : &aiminfo, key,
                   1295:                        purple_account_get_bool(account, "allow_multiple_logins", OSCAR_DEFAULT_ALLOW_MULTIPLE_LOGINS));
                   1296: 
                   1297:        purple_connection_update_progress(gc, _("Password sent"), 2, OSCAR_CONNECT_STEPS);
                   1298: 
                   1299:        return 1;
                   1300: }
                   1301: 
                   1302: static int
                   1303: purple_handle_redirect(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...)
                   1304: {
                   1305:        PurpleConnection *gc = od->gc;
                   1306:        PurpleAccount *account = purple_connection_get_account(gc);
                   1307:        char *host, *separator;
                   1308:        int port;
                   1309:        FlapConnection *newconn;
                   1310:        va_list ap;
                   1311:        struct aim_redirect_data *redir;
                   1312: 
                   1313:        va_start(ap, fr);
                   1314:        redir = va_arg(ap, struct aim_redirect_data *);
                   1315:        va_end(ap);
                   1316: 
                   1317:        port = od->default_port;
                   1318:        separator = strchr(redir->ip, ':');
                   1319:        if (separator != NULL)
                   1320:        {
                   1321:                host = g_strndup(redir->ip, separator - redir->ip);
                   1322:                port = atoi(separator + 1);
                   1323:        }
                   1324:        else
                   1325:                host = g_strdup(redir->ip);
                   1326: 
                   1327:        if (!redir->use_ssl) {
                   1328:                const gchar *encryption_type = purple_account_get_string(account, "encryption", OSCAR_DEFAULT_ENCRYPTION);
                   1329:                if (purple_strequal(encryption_type, OSCAR_OPPORTUNISTIC_ENCRYPTION)) {
                   1330:                        purple_debug_warning("oscar", "We won't use SSL for FLAP type 0x%04hx.\n", redir->group);
                   1331:                } else if (purple_strequal(encryption_type, OSCAR_REQUIRE_ENCRYPTION)) {
                   1332:                        purple_debug_error("oscar", "FLAP server %s:%d of type 0x%04hx doesn't support encryption.", host, port, redir->group);
                   1333:                        purple_connection_error_reason(
                   1334:                                gc,
                   1335:                                PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT,
                   1336:                                _("You required encryption in your account settings, but one of the servers doesn't support it."));
                   1337:                        return 0;
                   1338:                }
                   1339:        }
                   1340: 
                   1341:        /*
                   1342:         * These FLAP servers advertise SSL (type "0x02"), but SSL connections to these hosts
                   1343:         * die a painful death. iChat and Miranda, when using SSL, still do these in plaintext.
                   1344:         */
                   1345:        if (redir->use_ssl && (redir->group == SNAC_FAMILY_ADMIN ||
                   1346:                               redir->group == SNAC_FAMILY_BART))
                   1347:        {
                   1348:                purple_debug_info("oscar", "Ignoring broken SSL for FLAP type 0x%04hx.\n", redir->group);
                   1349:                redir->use_ssl = 0;
                   1350:        }
                   1351: 
                   1352:        purple_debug_info("oscar", "Connecting to FLAP server %s:%d of type 0x%04hx\n", host, port, redir->group);
                   1353: 
                   1354:        newconn = flap_connection_new(od, redir->group);
                   1355:        newconn->cookielen = redir->cookielen;
                   1356:        newconn->cookie = g_memdup(redir->cookie, redir->cookielen);
                   1357:        if (newconn->type == SNAC_FAMILY_CHAT)
                   1358:        {
                   1359:                struct chat_connection *cc;
                   1360:                cc = g_new0(struct chat_connection, 1);
                   1361:                cc->conn = newconn;
                   1362:                cc->gc = gc;
                   1363:                cc->name = g_strdup(redir->chat.room);
                   1364:                cc->exchange = redir->chat.exchange;
                   1365:                cc->instance = redir->chat.instance;
                   1366:                cc->show = extract_name(redir->chat.room);
                   1367:                newconn->new_conn_data = cc;
                   1368:                purple_debug_info("oscar", "Connecting to chat room %s exchange %hu\n", cc->name, cc->exchange);
                   1369:        }
                   1370: 
                   1371: 
                   1372:        if (redir->use_ssl)
                   1373:        {
                   1374:                newconn->gsc = purple_ssl_connect_with_ssl_cn(account, host, port,
                   1375:                                ssl_connection_established_cb, ssl_connection_error_cb,
                   1376:                                redir->ssl_cert_cn, newconn);
                   1377:        }
                   1378:        else
                   1379:        {
                   1380:                newconn->connect_data = purple_proxy_connect(NULL, account, host, port,
                   1381:                                connection_established_cb, newconn);
                   1382:        }
                   1383: 
                   1384:        if (newconn->gsc == NULL && newconn->connect_data == NULL)
                   1385:        {
                   1386:                flap_connection_schedule_destroy(newconn,
                   1387:                                OSCAR_DISCONNECT_COULD_NOT_CONNECT,
                   1388:                                _("Unable to initialize connection"));
                   1389:                purple_debug_error("oscar", "Unable to connect to FLAP server "
                   1390:                                "of type 0x%04hx\n", redir->group);
                   1391:        }
                   1392:        g_free(host);
                   1393: 
                   1394:        return 1;
                   1395: }
                   1396: 
                   1397: 
                   1398: static int purple_parse_oncoming(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...)
                   1399: {
                   1400:        PurpleConnection *gc;
                   1401:        PurpleAccount *account;
                   1402:        PurpleBuddy *buddy = NULL;
                   1403:        PurpleStatus *previous_status = NULL;
                   1404:        struct buddyinfo *bi;
                   1405:        time_t time_idle = 0, signon = 0;
                   1406:        int type = 0;
                   1407:        gboolean buddy_is_away = FALSE;
                   1408:        const char *status_id;
                   1409:        va_list ap;
                   1410:        aim_userinfo_t *info;
                   1411:        char *message;
                   1412:        char *itmsurl = NULL;
                   1413: 
                   1414:        gc = od->gc;
                   1415:        account = purple_connection_get_account(gc);
                   1416: 
                   1417:        va_start(ap, fr);
                   1418:        info = va_arg(ap, aim_userinfo_t *);
                   1419:        va_end(ap);
                   1420: 
                   1421:        g_return_val_if_fail(info != NULL, 1);
                   1422:        g_return_val_if_fail(info->bn != NULL, 1);
                   1423: 
                   1424:        buddy = purple_find_buddy(account, info->bn);
                   1425:        if (buddy) {
                   1426:                previous_status = purple_presence_get_active_status(purple_buddy_get_presence(buddy));
                   1427:        }
                   1428: 
                   1429:        /*
                   1430:         * If this is an AIM buddy and their name has formatting, set their
                   1431:         * server alias.
                   1432:         */
                   1433:        if (!oscar_util_valid_name_icq(info->bn)) {
                   1434:                gboolean bn_has_formatting = FALSE;
                   1435:                char *c;
                   1436:                for (c = info->bn; *c != '\0'; c++) {
                   1437:                        if (!islower(*c)) {
                   1438:                                bn_has_formatting = TRUE;
                   1439:                                break;
                   1440:                        }
                   1441:                }
                   1442:                serv_got_alias(gc, info->bn,
                   1443:                                bn_has_formatting ? info->bn : NULL);
                   1444:        }
                   1445: 
                   1446:        if (info->present & AIM_USERINFO_PRESENT_FLAGS) {
                   1447:                if (info->flags & AIM_FLAG_AWAY)
                   1448:                        buddy_is_away = TRUE;
                   1449:        }
                   1450:        if (info->present & AIM_USERINFO_PRESENT_ICQEXTSTATUS) {
                   1451:                type = info->icqinfo.status;
                   1452:                if (!(info->icqinfo.status & AIM_ICQ_STATE_CHAT) &&
                   1453:                      (info->icqinfo.status != AIM_ICQ_STATE_NORMAL)) {
                   1454:                        buddy_is_away = TRUE;
                   1455:                }
                   1456:        }
                   1457: 
                   1458:        if (oscar_util_valid_name_icq(info->bn)) {
                   1459:                if (type & AIM_ICQ_STATE_CHAT)
                   1460:                        status_id = OSCAR_STATUS_ID_FREE4CHAT;
                   1461:                else if (type & AIM_ICQ_STATE_DND)
                   1462:                        status_id = OSCAR_STATUS_ID_DND;
                   1463:                else if (type & AIM_ICQ_STATE_OUT)
                   1464:                        status_id = OSCAR_STATUS_ID_NA;
                   1465:                else if (type & AIM_ICQ_STATE_BUSY)
                   1466:                        status_id = OSCAR_STATUS_ID_OCCUPIED;
                   1467:                else if (type & AIM_ICQ_STATE_AWAY)
                   1468:                        status_id = OSCAR_STATUS_ID_AWAY;
                   1469:                else if (type & AIM_ICQ_STATE_INVISIBLE)
                   1470:                        status_id = OSCAR_STATUS_ID_INVISIBLE;
                   1471:                else if (type & AIM_ICQ_STATE_EVIL)
                   1472:                        status_id = OSCAR_STATUS_ID_EVIL;
                   1473:                else if (type & AIM_ICQ_STATE_DEPRESSION)
                   1474:                        status_id = OSCAR_STATUS_ID_DEPRESSION;
                   1475:                else if (type & AIM_ICQ_STATE_ATHOME)
                   1476:                        status_id = OSCAR_STATUS_ID_ATHOME;
                   1477:                else if (type & AIM_ICQ_STATE_ATWORK)
                   1478:                        status_id = OSCAR_STATUS_ID_ATWORK;
                   1479:                else if (type & AIM_ICQ_STATE_LUNCH)
                   1480:                        status_id = OSCAR_STATUS_ID_LUNCH;
                   1481:                else
                   1482:                        status_id = OSCAR_STATUS_ID_AVAILABLE;
                   1483:        } else {
                   1484:                if (type & AIM_ICQ_STATE_INVISIBLE)
                   1485:                        status_id = OSCAR_STATUS_ID_INVISIBLE;
                   1486:                else if (buddy_is_away)
                   1487:                        status_id = OSCAR_STATUS_ID_AWAY;
                   1488:                else
                   1489:                        status_id = OSCAR_STATUS_ID_AVAILABLE;
                   1490:        }
                   1491: 
                   1492:        if (info->flags & AIM_FLAG_WIRELESS) {
                   1493:                purple_prpl_got_user_status(account, info->bn, OSCAR_STATUS_ID_MOBILE, NULL);
                   1494:        } else {
                   1495:                purple_prpl_got_user_status_deactive(account, info->bn, OSCAR_STATUS_ID_MOBILE);
                   1496:        }
                   1497: 
                   1498:        message = (info->status && info->status_len > 0)
                   1499:                        ? oscar_encoding_to_utf8(info->status_encoding, info->status, info->status_len)
                   1500:                        : NULL;
                   1501: 
                   1502:        if (purple_strequal(status_id, OSCAR_STATUS_ID_AVAILABLE)) {
                   1503:                /* TODO: If itmsurl is NULL, does that mean the URL has been
                   1504:                   cleared?  Or does it mean the URL should remain unchanged? */
                   1505:                if (info->itmsurl != NULL) {
                   1506:                        itmsurl = (info->itmsurl_len > 0) ? oscar_encoding_to_utf8(info->itmsurl_encoding, info->itmsurl, info->itmsurl_len) : NULL;
                   1507:                } else if (previous_status != NULL && purple_status_is_available(previous_status)) {
                   1508:                        itmsurl = g_strdup(purple_status_get_attr_string(previous_status, "itmsurl"));
                   1509:                }
                   1510:                purple_debug_info("oscar", "Activating status '%s' for buddy %s, message = '%s', itmsurl = '%s'\n", status_id, info->bn, message ? message : "(null)", itmsurl ? itmsurl : "(null)");
                   1511:                purple_prpl_got_user_status(account, info->bn, status_id, "message", message, "itmsurl", itmsurl, NULL);
                   1512:        } else {
                   1513:                purple_debug_info("oscar", "Activating status '%s' for buddy %s, message = '%s'\n", status_id, info->bn, message ? message : "(null)");
                   1514:                purple_prpl_got_user_status(account, info->bn, status_id, "message", message, NULL);
                   1515:        }
                   1516: 
                   1517:        g_free(message);
                   1518:        g_free(itmsurl);
                   1519: 
                   1520:        /* Login time stuff */
                   1521:        if (info->present & AIM_USERINFO_PRESENT_ONLINESINCE)
                   1522:                signon = info->onlinesince;
                   1523:        else if (info->present & AIM_USERINFO_PRESENT_SESSIONLEN)
                   1524:                signon = time(NULL) - info->sessionlen;
                   1525:        purple_prpl_got_user_login_time(account, info->bn, signon);
                   1526: 
                   1527:        /* Idle time stuff */
                   1528:        /* info->idletime is the number of minutes that this user has been idle */
                   1529:        if (info->present & AIM_USERINFO_PRESENT_IDLE)
                   1530:                time_idle = time(NULL) - info->idletime * 60;
                   1531: 
                   1532:        if (time_idle > 0)
                   1533:                purple_prpl_got_user_idle(account, info->bn, TRUE, time_idle);
                   1534:        else
                   1535:                purple_prpl_got_user_idle(account, info->bn, FALSE, 0);
                   1536: 
                   1537:        /* Server stored icon stuff */
                   1538:        bi = g_hash_table_lookup(od->buddyinfo, purple_normalize(account, info->bn));
                   1539:        if (!bi) {
                   1540:                bi = g_new0(struct buddyinfo, 1);
                   1541:                g_hash_table_insert(od->buddyinfo, g_strdup(purple_normalize(account, info->bn)), bi);
                   1542:        }
                   1543:        bi->typingnot = FALSE;
                   1544:        bi->ico_informed = FALSE;
                   1545:        bi->ipaddr = info->icqinfo.ipaddr;
                   1546: 
                   1547:        if (info->iconcsumlen) {
                   1548:                const char *saved_b16 = NULL;
                   1549:                char *b16 = NULL;
                   1550:                PurpleBuddy *b = NULL;
                   1551: 
                   1552:                b16 = purple_base16_encode(info->iconcsum, info->iconcsumlen);
                   1553:                b = purple_find_buddy(account, info->bn);
                   1554:                if (b != NULL)
                   1555:                        saved_b16 = purple_buddy_icons_get_checksum_for_user(b);
                   1556: 
                   1557:                if (!b16 || !saved_b16 || !purple_strequal(b16, saved_b16)) {
                   1558:                        /* Invalidate the old icon for this user */
                   1559:                        purple_buddy_icons_set_for_user(account, info->bn, NULL, 0, NULL);
                   1560: 
                   1561:                        /* Fetch the new icon (if we're not already doing so) */
                   1562:                        if (g_slist_find_custom(od->requesticon, info->bn,
                   1563:                                        (GCompareFunc)oscar_util_name_compare) == NULL)
                   1564:                        {
                   1565:                                od->requesticon = g_slist_prepend(od->requesticon,
                   1566:                                                g_strdup(purple_normalize(account, info->bn)));
                   1567:                                purple_icons_fetch(gc);
                   1568:                        }
                   1569:                }
                   1570:                g_free(b16);
                   1571:        }
                   1572: 
                   1573:        return 1;
                   1574: }
                   1575: 
                   1576: static int purple_parse_offgoing(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
                   1577:        PurpleConnection *gc = od->gc;
                   1578:        PurpleAccount *account = purple_connection_get_account(gc);
                   1579:        va_list ap;
                   1580:        aim_userinfo_t *info;
                   1581: 
                   1582:        va_start(ap, fr);
                   1583:        info = va_arg(ap, aim_userinfo_t *);
                   1584:        va_end(ap);
                   1585: 
                   1586:        purple_prpl_got_user_status(account, info->bn, OSCAR_STATUS_ID_OFFLINE, NULL);
                   1587:        purple_prpl_got_user_status_deactive(account, info->bn, OSCAR_STATUS_ID_MOBILE);
                   1588:        g_hash_table_remove(od->buddyinfo, purple_normalize(gc->account, info->bn));
                   1589: 
                   1590:        return 1;
                   1591: }
                   1592: 
                   1593: static int incomingim_chan1(OscarData *od, FlapConnection *conn, aim_userinfo_t *userinfo, struct aim_incomingim_ch1_args *args) {
                   1594:        PurpleConnection *gc = od->gc;
                   1595:        PurpleAccount *account = purple_connection_get_account(gc);
                   1596:        PurpleMessageFlags flags = 0;
                   1597:        struct buddyinfo *bi;
                   1598:        PurpleStoredImage *img;
                   1599:        gchar *tmp;
                   1600:        const char *start, *end;
                   1601:        GData *attribs;
                   1602: 
                   1603:        purple_debug_misc("oscar", "Received IM from %s\n", userinfo->bn);
                   1604: 
                   1605:        bi = g_hash_table_lookup(od->buddyinfo, purple_normalize(account, userinfo->bn));
                   1606:        if (!bi) {
                   1607:                bi = g_new0(struct buddyinfo, 1);
                   1608:                g_hash_table_insert(od->buddyinfo, g_strdup(purple_normalize(account, userinfo->bn)), bi);
                   1609:        }
                   1610: 
                   1611:        if (args->icbmflags & AIM_IMFLAGS_AWAY)
                   1612:                flags |= PURPLE_MESSAGE_AUTO_RESP;
                   1613: 
                   1614:        if (args->icbmflags & AIM_IMFLAGS_TYPINGNOT)
                   1615:                bi->typingnot = TRUE;
                   1616:        else
                   1617:                bi->typingnot = FALSE;
                   1618: 
                   1619:        if ((args->icbmflags & AIM_IMFLAGS_HASICON) && (args->iconlen) && (args->iconsum) && (args->iconstamp)) {
                   1620:                purple_debug_misc("oscar", "%s has an icon\n", userinfo->bn);
                   1621:                if ((args->iconlen != bi->ico_len) || (args->iconsum != bi->ico_csum) || (args->iconstamp != bi->ico_time)) {
                   1622:                        bi->ico_need = TRUE;
                   1623:                        bi->ico_len = args->iconlen;
                   1624:                        bi->ico_csum = args->iconsum;
                   1625:                        bi->ico_time = args->iconstamp;
                   1626:                }
                   1627:        }
                   1628: 
                   1629:        img = purple_buddy_icons_find_account_icon(account);
                   1630:        if ((img != NULL) &&
                   1631:            (args->icbmflags & AIM_IMFLAGS_BUDDYREQ) && !bi->ico_sent && bi->ico_informed) {
                   1632:                gconstpointer data = purple_imgstore_get_data(img);
                   1633:                size_t len = purple_imgstore_get_size(img);
                   1634:                purple_debug_info("oscar",
                   1635:                                "Sending buddy icon to %s (%" G_GSIZE_FORMAT " bytes)\n",
                   1636:                                userinfo->bn, len);
                   1637:                aim_im_sendch2_icon(od, userinfo->bn, data, len,
                   1638:                        purple_buddy_icons_get_account_icon_timestamp(account),
                   1639:                        aimutil_iconsum(data, len));
                   1640:        }
                   1641:        purple_imgstore_unref(img);
                   1642: 
                   1643:        tmp = g_strdup(args->msg);
                   1644: 
                   1645:        /*
                   1646:         * Convert iChat color tags to normal font tags.
                   1647:         */
                   1648:        if (purple_markup_find_tag("body", tmp, &start, &end, &attribs))
                   1649:        {
                   1650:                int len;
                   1651:                char *tmp2, *body;
                   1652:                const char *ichattextcolor, *ichatballooncolor;
                   1653:                const char *slash_body_start, *slash_body_end = NULL; /* </body> */
                   1654:                GData *unused;
                   1655: 
                   1656:                /*
                   1657:                 * Find the ending </body> so we can strip off the outer <html/>
                   1658:                 * and <body/>
                   1659:                 */
                   1660:                if (purple_markup_find_tag("/body", end + 1, &slash_body_start, &slash_body_end, &unused))
                   1661:                {
                   1662:                        body = g_strndup(start, slash_body_end - start + 1);
                   1663:                        g_datalist_clear(&unused);
                   1664:                }
                   1665:                else
                   1666:                {
                   1667:                        purple_debug_warning("oscar", "Broken message contains <body> but not </body>!\n");
                   1668:                        /* Take everything after <body> */
                   1669:                        body = g_strdup(start);
                   1670:                }
                   1671: 
                   1672:                ichattextcolor = g_datalist_get_data(&attribs, "ichattextcolor");
                   1673:                if (ichattextcolor != NULL)
                   1674:                {
                   1675:                        tmp2 = g_strdup_printf("<font color=\"%s\">%s</font>", ichattextcolor, body);
                   1676:                        g_free(body);
                   1677:                        body = tmp2;
                   1678:                }
                   1679: 
                   1680:                ichatballooncolor = g_datalist_get_data(&attribs, "ichatballooncolor");
                   1681:                if (ichatballooncolor != NULL)
                   1682:                {
                   1683:                        tmp2 = g_strdup_printf("<font back=\"%s\">%s</font>", ichatballooncolor, body);
                   1684:                        g_free(body);
                   1685:                        body = tmp2;
                   1686:                }
                   1687: 
                   1688:                g_datalist_clear(&attribs);
                   1689: 
                   1690:                len = start - tmp;
                   1691:                tmp2 = g_strdup_printf("%.*s%s%s", len, tmp, body, slash_body_end ? slash_body_end + 1: "</body>");
                   1692:                g_free(tmp);
                   1693:                g_free(body);
                   1694: 
                   1695:                tmp = tmp2;
                   1696:        }
                   1697: 
                   1698:        /*
                   1699:         * Are there <html/> surrounding tags? If so, strip them out, too.
                   1700:         */
                   1701:        if (purple_markup_find_tag("html", tmp, &start, &end, &attribs))
                   1702:        {
                   1703:                gchar *tmp2;
                   1704:                int len;
                   1705: 
                   1706:                g_datalist_clear(&attribs);
                   1707: 
                   1708:                len = start - tmp;
                   1709:                tmp2 = g_strdup_printf("%.*s%s", len, tmp, end + 1);
                   1710:                g_free(tmp);
                   1711:                tmp = tmp2;
                   1712:        }
                   1713: 
                   1714:        if (purple_markup_find_tag("/html", tmp, &start, &end, &attribs))
                   1715:        {
                   1716:                gchar *tmp2;
                   1717:                int len;
                   1718: 
                   1719:                g_datalist_clear(&attribs);
                   1720: 
                   1721:                len = start - tmp;
                   1722:                tmp2 = g_strdup_printf("%.*s%s", len, tmp, end + 1);
                   1723:                g_free(tmp);
                   1724:                tmp = tmp2;
                   1725:        }
                   1726: 
                   1727:        serv_got_im(gc, userinfo->bn, tmp, flags, (args->icbmflags & AIM_IMFLAGS_OFFLINE) ? args->timestamp : time(NULL));
                   1728:        g_free(tmp);
                   1729: 
                   1730:        return 1;
                   1731: }
                   1732: 
                   1733: static int
                   1734: incomingim_chan2(OscarData *od, FlapConnection *conn, aim_userinfo_t *userinfo, IcbmArgsCh2 *args)
                   1735: {
                   1736:        PurpleConnection *gc;
                   1737:        PurpleAccount *account;
                   1738:        PurpleMessageFlags flags = 0;
                   1739:        char *message = NULL;
                   1740: 
                   1741:        g_return_val_if_fail(od != NULL, 0);
                   1742:        g_return_val_if_fail(od->gc != NULL, 0);
                   1743: 
                   1744:        gc = od->gc;
                   1745:        account = purple_connection_get_account(gc);
                   1746:        od = purple_connection_get_protocol_data(gc);
                   1747: 
                   1748:        if (args == NULL)
                   1749:                return 0;
                   1750: 
                   1751:        purple_debug_misc("oscar", "Incoming rendezvous message of type %"
                   1752:                        G_GUINT64_FORMAT ", user %s, status %hu\n",
                   1753:                        args->type, userinfo->bn, args->status);
                   1754: 
                   1755:        if (args->msg != NULL) {
                   1756:                message = oscar_encoding_to_utf8(args->encoding, args->msg, args->msglen);
                   1757:        }
                   1758: 
                   1759:        if (args->type & OSCAR_CAPABILITY_CHAT)
                   1760:        {
                   1761:                char *utf8name, *tmp;
                   1762:                GHashTable *components;
                   1763: 
                   1764:                if (!args->info.chat.roominfo.name || !args->info.chat.roominfo.exchange) {
                   1765:                        g_free(message);
                   1766:                        return 1;
                   1767:                }
                   1768:                utf8name = oscar_encoding_to_utf8(args->encoding, args->info.chat.roominfo.name, args->info.chat.roominfo.namelen);
                   1769: 
                   1770:                tmp = extract_name(utf8name);
                   1771:                if (tmp != NULL)
                   1772:                {
                   1773:                        g_free(utf8name);
                   1774:                        utf8name = tmp;
                   1775:                }
                   1776: 
                   1777:                components = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
                   1778:                                g_free);
                   1779:                g_hash_table_replace(components, g_strdup("room"), utf8name);
                   1780:                g_hash_table_replace(components, g_strdup("exchange"),
                   1781:                                g_strdup_printf("%d", args->info.chat.roominfo.exchange));
                   1782:                serv_got_chat_invite(gc,
                   1783:                                     utf8name,
                   1784:                                     userinfo->bn,
                   1785:                                     message,
                   1786:                                     components);
                   1787:        }
                   1788: 
                   1789:        else if ((args->type & OSCAR_CAPABILITY_SENDFILE) || (args->type & OSCAR_CAPABILITY_DIRECTIM))
                   1790:        {
                   1791:                if (args->status == AIM_RENDEZVOUS_PROPOSE)
                   1792:                {
                   1793:                        peer_connection_got_proposition(od, userinfo->bn, message, args);
                   1794:                }
                   1795:                else if (args->status == AIM_RENDEZVOUS_CANCEL)
                   1796:                {
                   1797:                        /* The other user cancelled a peer request */
                   1798:                        PeerConnection *conn;
                   1799: 
                   1800:                        conn = peer_connection_find_by_cookie(od, userinfo->bn, args->cookie);
                   1801:                        /*
                   1802:                         * If conn is NULL it means we haven't tried to create
                   1803:                         * a connection with that user.  They may be trying to
                   1804:                         * do something malicious.
                   1805:                         */
                   1806:                        if (conn != NULL)
                   1807:                        {
                   1808:                                peer_connection_destroy(conn, OSCAR_DISCONNECT_REMOTE_CLOSED, NULL);
                   1809:                        }
                   1810:                }
                   1811:                else if (args->status == AIM_RENDEZVOUS_CONNECTED)
                   1812:                {
                   1813:                        /*
                   1814:                         * Remote user has accepted our peer request.  If we
                   1815:                         * wanted to we could look up the PeerConnection using
                   1816:                         * args->cookie, but we don't need to do anything here.
                   1817:                         */
                   1818:                }
                   1819:        }
                   1820: 
                   1821:        else if (args->type & OSCAR_CAPABILITY_GETFILE)
                   1822:        {
                   1823:        }
                   1824: 
                   1825:        else if (args->type & OSCAR_CAPABILITY_TALK)
                   1826:        {
                   1827:        }
                   1828: 
                   1829:        else if (args->type & OSCAR_CAPABILITY_BUDDYICON)
                   1830:        {
                   1831:                purple_buddy_icons_set_for_user(account, userinfo->bn,
                   1832:                                                                          g_memdup(args->info.icon.icon, args->info.icon.length),
                   1833:                                                                          args->info.icon.length,
                   1834:                                                                          NULL);
                   1835:        }
                   1836: 
                   1837:        else if (args->type & OSCAR_CAPABILITY_ICQSERVERRELAY)
                   1838:        {
                   1839:                purple_debug_info("oscar", "Got an ICQ Server Relay message of "
                   1840:                                "type %d\n", args->info.rtfmsg.msgtype);
                   1841: 
                   1842:                if (args->info.rtfmsg.msgtype == 1) {
                   1843:                        if (args->info.rtfmsg.msg != NULL) {
                   1844:                                char *rtfmsg;
                   1845:                                const char *encoding = args->encoding;
                   1846:                                size_t len = strlen(args->info.rtfmsg.msg);
                   1847:                                char *tmp, *tmp2;
                   1848: 
                   1849:                                if (encoding == NULL && !g_utf8_validate(args->info.rtfmsg.msg, len, NULL)) {
                   1850:                                        /* Yet another wonderful Miranda-related hack. If their user disables the "Send Unicode messages" setting,
                   1851:                                         * Miranda sends us ch2 messages in whatever Windows codepage is set as default on their user's system (instead of UTF-8).
                   1852:                                         * Of course, they don't bother to specify that codepage. Let's just fallback to the encoding OUR users can
                   1853:                                         * specify in account options as a last resort.
                   1854:                                         */
                   1855:                                        encoding = purple_account_get_string(account, "encoding", OSCAR_DEFAULT_CUSTOM_ENCODING);
                   1856:                                        purple_debug_info("oscar", "Miranda, is that you? Using '%s' as encoding\n", encoding);
                   1857:                                }
                   1858: 
                   1859:                                rtfmsg = oscar_encoding_to_utf8(encoding, args->info.rtfmsg.msg, len);
                   1860: 
                   1861:                                /* Channel 2 messages are supposed to be plain-text (never mind the name "rtfmsg", even
                   1862:                                 * the official client doesn't parse them as RTF). Therefore, we should escape them before
                   1863:                                 * showing to the user. */
                   1864:                                tmp = g_markup_escape_text(rtfmsg, -1);
                   1865:                                g_free(rtfmsg);
                   1866:                                tmp2 = purple_strreplace(tmp, "\r\n", "<br>");
                   1867:                                g_free(tmp);
                   1868: 
                   1869:                                serv_got_im(gc, userinfo->bn, tmp2, flags, time(NULL));
                   1870:                                aim_im_send_icq_confirmation(od, userinfo->bn, args->cookie);
                   1871:                                g_free(tmp2);
                   1872:                        }
                   1873:                } else if (args->info.rtfmsg.msgtype == 26) {
                   1874:                        purple_debug_info("oscar", "Sending X-Status Reply\n");
                   1875:                        icq_relay_xstatus(od, userinfo->bn, args->cookie);
                   1876:                }
                   1877:        }
                   1878:        else
                   1879:        {
                   1880:                purple_debug_error("oscar", "Unknown request class %"
                   1881:                                G_GUINT64_FORMAT "\n", args->type);
                   1882:        }
                   1883: 
                   1884:        g_free(message);
                   1885: 
                   1886:        return 1;
                   1887: }
                   1888: 
                   1889: /* When someone sends you buddies */
                   1890: static void
                   1891: purple_icq_buddyadd(struct name_data *data)
                   1892: {
                   1893:        PurpleConnection *gc = data->gc;
                   1894: 
                   1895:        purple_blist_request_add_buddy(purple_connection_get_account(gc), data->name, NULL, data->nick);
                   1896: 
                   1897:        oscar_free_name_data(data);
                   1898: }
                   1899: 
                   1900: static int
                   1901: incomingim_chan4(OscarData *od, FlapConnection *conn, aim_userinfo_t *userinfo, struct aim_incomingim_ch4_args *args, time_t t)
                   1902: {
                   1903:        PurpleConnection *gc = od->gc;
                   1904:        PurpleAccount *account = purple_connection_get_account(gc);
                   1905:        gchar **msg1, **msg2;
                   1906:        int i, numtoks;
                   1907: 
                   1908:        if (!args->type || !args->msg || !args->uin)
                   1909:                return 1;
                   1910: 
                   1911:        purple_debug_info("oscar",
                   1912:                "Received a channel 4 message of type 0x%02hx.\n",
                   1913:                (guint16)args->type);
                   1914: 
                   1915:        /*
                   1916:         * Split up the message at the delimeter character, then convert each
                   1917:         * string to UTF-8.  Unless, of course, this is a type 1 message.  If
                   1918:         * this is a type 1 message, then the delimiter 0xfe could be a valid
                   1919:         * character in whatever encoding the message was sent in.  Type 1
                   1920:         * messages are always made up of only one part, so we can easily account
                   1921:         * for this suck-ass part of the protocol by splitting the string into at
                   1922:         * most 1 baby string.
                   1923:         */
                   1924:        msg1 = g_strsplit(args->msg, "\376", (args->type == 0x01 ? 1 : 0));
                   1925:        for (numtoks=0; msg1[numtoks]; numtoks++);
                   1926:        msg2 = (gchar **)g_malloc((numtoks+1)*sizeof(gchar *));
                   1927:        for (i=0; msg1[i]; i++) {
                   1928:                gchar *uin = g_strdup_printf("%u", args->uin);
                   1929: 
                   1930:                purple_str_strip_char(msg1[i], '\r');
                   1931:                /* TODO: Should use an encoding other than ASCII? */
                   1932:                msg2[i] = oscar_decode_im(account, uin, AIM_CHARSET_ASCII, msg1[i], strlen(msg1[i]));
                   1933:                g_free(uin);
                   1934:        }
                   1935:        msg2[i] = NULL;
                   1936: 
                   1937:        switch (args->type) {
                   1938:                case 0x01: { /* MacICQ message or basic offline message */
                   1939:                        if (i >= 1) {
                   1940:                                gchar *uin = g_strdup_printf("%u", args->uin);
                   1941:                                gchar *tmp;
                   1942: 
                   1943:                                /* If the message came from an ICQ user then escape any HTML */
                   1944:                                tmp = g_markup_escape_text(msg2[0], -1);
                   1945: 
                   1946:                                if (t) { /* This is an offline message */
                   1947:                                        /* The timestamp is UTC-ish, so we need to get the offset */
                   1948: #ifdef HAVE_TM_GMTOFF
                   1949:                                        time_t now;
                   1950:                                        struct tm *tm;
                   1951:                                        now = time(NULL);
                   1952:                                        tm = localtime(&now);
                   1953:                                        t += tm->tm_gmtoff;
                   1954: #else
                   1955: #      ifdef HAVE_TIMEZONE
                   1956:                                        tzset();
                   1957:                                        t -= timezone;
                   1958: #      endif
                   1959: #endif
                   1960:                                        serv_got_im(gc, uin, tmp, 0, t);
                   1961:                                } else { /* This is a message from MacICQ/Miranda */
                   1962:                                        serv_got_im(gc, uin, tmp, 0, time(NULL));
                   1963:                                }
                   1964:                                g_free(uin);
                   1965:                                g_free(tmp);
                   1966:                        }
                   1967:                } break;
                   1968: 
                   1969:                case 0x04: { /* Someone sent you a URL */
                   1970:                        if (i >= 2) {
                   1971:                                if (msg2[1] != NULL) {
                   1972:                                        gchar *uin = g_strdup_printf("%u", args->uin);
                   1973:                                        gchar *message = g_strdup_printf("<A HREF=\"%s\">%s</A>",
                   1974:                                                                                                         msg2[1],
                   1975:                                                                                                         (msg2[0] && msg2[0][0]) ? msg2[0] : msg2[1]);
                   1976:                                        serv_got_im(gc, uin, message, 0, time(NULL));
                   1977:                                        g_free(uin);
                   1978:                                        g_free(message);
                   1979:                                }
                   1980:                        }
                   1981:                } break;
                   1982: 
                   1983:                case 0x06: { /* Someone requested authorization */
                   1984:                        if (i >= 6) {
                   1985:                                gchar *bn = g_strdup_printf("%u", args->uin);
                   1986:                                gchar *reason = NULL;
                   1987: 
                   1988:                                if (msg2[5] != NULL)
                   1989:                                        reason = oscar_decode_im(account, bn, AIM_CHARSET_LATIN_1, msg2[5], strlen(msg2[5]));
                   1990: 
                   1991:                                purple_debug_info("oscar",
                   1992:                                                   "Received an authorization request from UIN %u\n",
                   1993:                                                   args->uin);
                   1994:                                aim_icq_getalias(od, bn, TRUE, reason);
                   1995:                                g_free(bn);
                   1996:                                g_free(reason);
                   1997:                        }
                   1998:                } break;
                   1999: 
                   2000:                case 0x07: { /* Someone has denied you authorization */
                   2001:                        if (i >= 1) {
                   2002:                                gchar *dialog_msg = g_strdup_printf(_("The user %u has denied your request to add them to your buddy list for the following reason:\n%s"), args->uin, msg2[0] ? msg2[0] : _("No reason given."));
                   2003:                                purple_notify_info(gc, NULL, _("ICQ authorization denied."),
                   2004:                                                                 dialog_msg);
                   2005:                                g_free(dialog_msg);
                   2006:                        }
                   2007:                } break;
                   2008: 
                   2009:                case 0x08: { /* Someone has granted you authorization */
                   2010:                        gchar *dialog_msg = g_strdup_printf(_("The user %u has granted your request to add them to your buddy list."), args->uin);
                   2011:                        purple_notify_info(gc, NULL, "ICQ authorization accepted.",
                   2012:                                                         dialog_msg);
                   2013:                        g_free(dialog_msg);
                   2014:                } break;
                   2015: 
                   2016:                case 0x09: { /* Message from the Godly ICQ server itself, I think */
                   2017:                        if (i >= 5) {
                   2018:                                gchar *dialog_msg = g_strdup_printf(_("You have received a special message\n\nFrom: %s [%s]\n%s"), msg2[0], msg2[3], msg2[5]);
                   2019:                                purple_notify_info(gc, NULL, "ICQ Server Message", dialog_msg);
                   2020:                                g_free(dialog_msg);
                   2021:                        }
                   2022:                } break;
                   2023: 
                   2024:                case 0x0d: { /* Someone has sent you a pager message from http://www.icq.com/your_uin */
                   2025:                        if (i >= 6) {
                   2026:                                gchar *dialog_msg = g_strdup_printf(_("You have received an ICQ page\n\nFrom: %s [%s]\n%s"), msg2[0], msg2[3], msg2[5]);
                   2027:                                purple_notify_info(gc, NULL, "ICQ Page", dialog_msg);
                   2028:                                g_free(dialog_msg);
                   2029:                        }
                   2030:                } break;
                   2031: 
                   2032:                case 0x0e: { /* Someone has emailed you at your_uin@pager.icq.com */
                   2033:                        if (i >= 6) {
                   2034:                                gchar *dialog_msg = g_strdup_printf(_("You have received an ICQ email from %s [%s]\n\nMessage is:\n%s"), msg2[0], msg2[3], msg2[5]);
                   2035:                                purple_notify_info(gc, NULL, "ICQ Email", dialog_msg);
                   2036:                                g_free(dialog_msg);
                   2037:                        }
                   2038:                } break;
                   2039: 
                   2040:                case 0x12: {
                   2041:                        /* Ack for authorizing/denying someone.  Or possibly an ack for sending any system notice */
                   2042:                        /* Someone added you to their buddy list? */
                   2043:                } break;
                   2044: 
                   2045:                case 0x13: { /* Someone has sent you some ICQ buddies */
                   2046:                        guint i, num;
                   2047:                        gchar **text;
                   2048:                        text = g_strsplit(args->msg, "\376", 0);
                   2049:                        if (text) {
                   2050:                                /* Read the number of contacts that we were sent */
                   2051:                                errno = 0;
                   2052:                                num = text[0] ? strtoul(text[0], NULL, 10) : 0;
                   2053: 
                   2054:                                if (num > 0 && errno == 0) {
                   2055:                                        for (i=0; i<num; i++) {
                   2056:                                                struct name_data *data;
                   2057:                                                gchar *message;
                   2058: 
                   2059:                                                if (!text[i*2 + 1] || !text[i*2 + 2]) {
                   2060:                                                        /* We're missing the contact name or nickname.  Bail out. */
                   2061:                                                        gchar *tmp = g_strescape(args->msg, NULL);
                   2062:                                                        purple_debug_error("oscar", "Unknown syntax parsing "
                   2063:                                                                        "ICQ buddies.  args->msg=%s\n", tmp);
                   2064:                                                        g_free(tmp);
                   2065:                                                        break;
                   2066:                                                }
                   2067: 
                   2068:                                                message = g_strdup_printf(_("ICQ user %u has sent you a buddy: %s (%s)"), args->uin, text[i*2+2], text[i*2+1]);
                   2069: 
                   2070:                                                data = g_new(struct name_data, 1);
                   2071:                                                data->gc = gc;
                   2072:                                                data->name = g_strdup(text[i*2+1]);
                   2073:                                                data->nick = g_strdup(text[i*2+2]);
                   2074: 
                   2075:                                                purple_request_action(gc, NULL, message,
                   2076:                                                                _("Do you want to add this buddy "
                   2077:                                                                  "to your buddy list?"),
                   2078:                                                                PURPLE_DEFAULT_ACTION_NONE,
                   2079:                                                                purple_connection_get_account(gc), data->name, NULL,
                   2080:                                                                data, 2,
                   2081:                                                                _("_Add"), G_CALLBACK(purple_icq_buddyadd),
                   2082:                                                                _("_Decline"), G_CALLBACK(oscar_free_name_data));
                   2083:                                                g_free(message);
                   2084:                                        }
                   2085:                                } else {
                   2086:                                        gchar *tmp = g_strescape(args->msg, NULL);
                   2087:                                        purple_debug_error("oscar", "Unknown syntax parsing "
                   2088:                                                        "ICQ buddies.  args->msg=%s\n", tmp);
                   2089:                                        g_free(tmp);
                   2090:                                }
                   2091:                                g_strfreev(text);
                   2092:                        }
                   2093:                } break;
                   2094: 
                   2095:                case 0x1a: { /* Handle SMS or someone has sent you a greeting card or requested buddies? */
                   2096:                        ByteStream qbs;
                   2097:                        guint16 smstype;
                   2098:                        guint32 taglen, smslen;
                   2099:                        char *tagstr = NULL, *smsmsg = NULL;
                   2100:                        xmlnode *xmlroot = NULL, *xmltmp = NULL;
                   2101:                        gchar *uin = NULL, *message = NULL;
                   2102: 
                   2103:                        /* From libicq2000-0.3.2/src/ICQ.cpp */
                   2104:                        byte_stream_init(&qbs, (guint8 *)args->msg, args->msglen);
                   2105:                        byte_stream_advance(&qbs, 21);
                   2106:                        /* expected:    01 00 00 20 00 0e 28 f6 00 11 e7 d3 11 bc f3 00 04 ac 96 9d c2 | 00 00 | 06 00 00 00 | 49 43 51 53 43 53 ...*/
                   2107:                        /* unexpected:  00 00 26 00 81 1a 18 bc 0e 6c 18 47 a5 91 6f 18 dc c7 6f 1a | 00 00 | 0d 00 00 00 | 49 43 51 57 65 62 4d 65 73 73 61 67 65 ... */
                   2108:                        smstype = byte_stream_getle16(&qbs);
                   2109:                        if (smstype != 0)
                   2110:                                break;
                   2111:                        taglen = byte_stream_getle32(&qbs);
                   2112:                        if (taglen > 2000) {
                   2113:                                /* Avoid trying to allocate large amounts of memory, in
                   2114:                                   case we get something unexpected. */
                   2115:                                break;
                   2116:                        }
                   2117:                        tagstr = byte_stream_getstr(&qbs, taglen);
                   2118:                        if (tagstr == NULL)
                   2119:                                break;
                   2120:                        byte_stream_advance(&qbs, 3);
                   2121:                        byte_stream_advance(&qbs, 4);
                   2122:                        smslen = byte_stream_getle32(&qbs);
                   2123:                        if (smslen > 2000) {
                   2124:                                /* Avoid trying to allocate large amounts of memory, in
                   2125:                                   case we get something unexpected. */
                   2126:                                g_free(tagstr);
                   2127:                                break;
                   2128:                        }
                   2129:                        smsmsg = byte_stream_getstr(&qbs, smslen);
                   2130: 
                   2131:                        /* Check if this is an SMS being sent from server */
                   2132:                        if ((smstype == 0) && (purple_strequal(tagstr, "ICQSMS")) && (smsmsg != NULL))
                   2133:                        {
                   2134:                                xmlroot = xmlnode_from_str(smsmsg, -1);
                   2135:                                if (xmlroot != NULL)
                   2136:                                {
                   2137:                                        xmltmp = xmlnode_get_child(xmlroot, "sender");
                   2138:                                        if (xmltmp != NULL)
                   2139:                                                uin = xmlnode_get_data(xmltmp);
                   2140: 
                   2141:                                        xmltmp = xmlnode_get_child(xmlroot, "text");
                   2142:                                        if (xmltmp != NULL)
                   2143:                                                message = xmlnode_get_data(xmltmp);
                   2144: 
                   2145:                                        if ((uin != NULL) && (message != NULL))
                   2146:                                                        serv_got_im(gc, uin, message, 0, time(NULL));
                   2147: 
                   2148:                                        g_free(uin);
                   2149:                                        g_free(message);
                   2150:                                        xmlnode_free(xmlroot);
                   2151:                                }
                   2152:                        }
                   2153:                        g_free(tagstr);
                   2154:                        g_free(smsmsg);
                   2155:                } break;
                   2156: 
                   2157:                default: {
                   2158:                        purple_debug_info("oscar",
                   2159:                                           "Received a channel 4 message of unknown type "
                   2160:                                           "(type 0x%02hhx).\n", args->type);
                   2161:                } break;
                   2162:        }
                   2163: 
                   2164:        g_strfreev(msg1);
                   2165:        g_strfreev(msg2);
                   2166: 
                   2167:        return 1;
                   2168: }
                   2169: 
                   2170: static int purple_parse_incoming_im(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
                   2171:        guint16 channel;
                   2172:        int ret = 0;
                   2173:        aim_userinfo_t *userinfo;
                   2174:        va_list ap;
                   2175: 
                   2176:        va_start(ap, fr);
                   2177:        channel = (guint16)va_arg(ap, unsigned int);
                   2178:        userinfo = va_arg(ap, aim_userinfo_t *);
                   2179: 
                   2180:        switch (channel) {
                   2181:                case 1: { /* standard message */
                   2182:                        struct aim_incomingim_ch1_args *args;
                   2183:                        args = va_arg(ap, struct aim_incomingim_ch1_args *);
                   2184:                        ret = incomingim_chan1(od, conn, userinfo, args);
                   2185:                } break;
                   2186: 
                   2187:                case 2: { /* rendezvous */
                   2188:                        IcbmArgsCh2 *args;
                   2189:                        args = va_arg(ap, IcbmArgsCh2 *);
                   2190:                        ret = incomingim_chan2(od, conn, userinfo, args);
                   2191:                } break;
                   2192: 
                   2193:                case 4: { /* ICQ */
                   2194:                        struct aim_incomingim_ch4_args *args;
                   2195:                        args = va_arg(ap, struct aim_incomingim_ch4_args *);
                   2196:                        ret = incomingim_chan4(od, conn, userinfo, args, 0);
                   2197:                } break;
                   2198: 
                   2199:                default: {
                   2200:                        purple_debug_warning("oscar",
                   2201:                                           "ICBM received on unsupported channel (channel "
                   2202:                                           "0x%04hx).", channel);
                   2203:                } break;
                   2204:        }
                   2205: 
                   2206:        va_end(ap);
                   2207: 
                   2208:        return ret;
                   2209: }
                   2210: 
                   2211: static int purple_parse_misses(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
                   2212:        PurpleConnection *gc = od->gc;
                   2213:        PurpleAccount *account = purple_connection_get_account(gc);
                   2214:        char *buf;
                   2215:        va_list ap;
                   2216:        guint16 nummissed, reason;
                   2217:        aim_userinfo_t *userinfo;
                   2218: 
                   2219:        va_start(ap, fr);
                   2220:        va_arg(ap, unsigned int); /* guint16 chan */
                   2221:        userinfo = va_arg(ap, aim_userinfo_t *);
                   2222:        nummissed = (guint16)va_arg(ap, unsigned int);
                   2223:        reason = (guint16)va_arg(ap, unsigned int);
                   2224:        va_end(ap);
                   2225: 
                   2226:        switch(reason) {
                   2227:                case 0: /* Invalid (0) */
                   2228:                        buf = g_strdup_printf(
                   2229:                                   dngettext(PACKAGE,
                   2230:                                   "You missed %hu message from %s because it was invalid.",
                   2231:                                   "You missed %hu messages from %s because they were invalid.",
                   2232:                                   nummissed),
                   2233:                                   nummissed,
                   2234:                                   userinfo->bn);
                   2235:                        break;
                   2236:                case 1: /* Message too large */
                   2237:                        buf = g_strdup_printf(
                   2238:                                   dngettext(PACKAGE,
                   2239:                                   "You missed %hu message from %s because it was too large.",
                   2240:                                   "You missed %hu messages from %s because they were too large.",
                   2241:                                   nummissed),
                   2242:                                   nummissed,
                   2243:                                   userinfo->bn);
                   2244:                        break;
                   2245:                case 2: /* Rate exceeded */
                   2246:                        buf = g_strdup_printf(
                   2247:                                   dngettext(PACKAGE,
                   2248:                                   "You missed %hu message from %s because the rate limit has been exceeded.",
                   2249:                                   "You missed %hu messages from %s because the rate limit has been exceeded.",
                   2250:                                   nummissed),
                   2251:                                   nummissed,
                   2252:                                   userinfo->bn);
                   2253:                        break;
                   2254:                case 3: /* Evil Sender */
                   2255:                        buf = g_strdup_printf(
                   2256:                                   dngettext(PACKAGE,
                   2257:                                   "You missed %hu message from %s because his/her warning level is too high.",
                   2258:                                   "You missed %hu messages from %s because his/her warning level is too high.",
                   2259:                                   nummissed),
                   2260:                                   nummissed,
                   2261:                                   userinfo->bn);
                   2262:                        break;
                   2263:                case 4: /* Evil Receiver */
                   2264:                        buf = g_strdup_printf(
                   2265:                                   dngettext(PACKAGE,
                   2266:                                   "You missed %hu message from %s because your warning level is too high.",
                   2267:                                   "You missed %hu messages from %s because your warning level is too high.",
                   2268:                                   nummissed),
                   2269:                                   nummissed,
                   2270:                                   userinfo->bn);
                   2271:                        break;
                   2272:                default:
                   2273:                        buf = g_strdup_printf(
                   2274:                                   dngettext(PACKAGE,
                   2275:                                   "You missed %hu message from %s for an unknown reason.",
                   2276:                                   "You missed %hu messages from %s for an unknown reason.",
                   2277:                                   nummissed),
                   2278:                                   nummissed,
                   2279:                                   userinfo->bn);
                   2280:                        break;
                   2281:        }
                   2282: 
                   2283:        if (!purple_conv_present_error(userinfo->bn, account, buf))
                   2284:                purple_notify_error(od->gc, NULL, buf, NULL);
                   2285:        g_free(buf);
                   2286: 
                   2287:        return 1;
                   2288: }
                   2289: 
                   2290: static int
                   2291: purple_parse_clientauto_ch2(OscarData *od, const char *who, guint16 reason, const guchar *cookie)
                   2292: {
                   2293:        if (reason == 0x0003)
                   2294:        {
                   2295:                /* Rendezvous was refused. */
                   2296:                PeerConnection *conn;
                   2297: 
                   2298:                conn = peer_connection_find_by_cookie(od, who, cookie);
                   2299: 
                   2300:                if (conn == NULL)
                   2301:                {
                   2302:                        purple_debug_info("oscar", "Received a rendezvous cancel message "
                   2303:                                        "for a nonexistant connection from %s.\n", who);
                   2304:                }
                   2305:                else
                   2306:                {
                   2307:                        peer_connection_destroy(conn, OSCAR_DISCONNECT_REMOTE_REFUSED, NULL);
                   2308:                }
                   2309:        }
                   2310:        else
                   2311:        {
                   2312:                purple_debug_warning("oscar", "Received an unknown rendezvous "
                   2313:                                "message from %s.  Type 0x%04hx\n", who, reason);
                   2314:        }
                   2315: 
                   2316:        return 0;
                   2317: }
                   2318: 
                   2319: static int purple_parse_clientauto_ch4(OscarData *od, char *who, guint16 reason, guint32 state, char *msg) {
                   2320:        PurpleConnection *gc = od->gc;
                   2321: 
                   2322:        switch(reason) {
                   2323:                case 0x0003: { /* Reply from an ICQ status message request */
                   2324:                        char *statusmsg, **splitmsg;
                   2325:                        PurpleNotifyUserInfo *user_info;
                   2326: 
                   2327:                        /* Split at (carriage return/newline)'s, then rejoin later with BRs between. */
                   2328:                        statusmsg = oscar_icqstatus(state);
                   2329:                        splitmsg = g_strsplit(msg, "\r\n", 0);
                   2330: 
                   2331:                        user_info = purple_notify_user_info_new();
                   2332: 
                   2333:                        purple_notify_user_info_add_pair(user_info, _("UIN"), who);
                   2334:                        purple_notify_user_info_add_pair(user_info, _("Status"), statusmsg);
                   2335:                        purple_notify_user_info_add_section_break(user_info);
                   2336:                        purple_notify_user_info_add_pair(user_info, NULL, g_strjoinv("<BR>", splitmsg));
                   2337: 
                   2338:                        g_free(statusmsg);
                   2339:                        g_strfreev(splitmsg);
                   2340: 
                   2341:                        purple_notify_userinfo(gc, who, user_info, NULL, NULL);
                   2342:                        purple_notify_user_info_destroy(user_info);
                   2343: 
                   2344:                } break;
                   2345: 
                   2346:                case 0x0006: { /* Reply from an ICQ status message request */
                   2347:                        char *statusmsg, **splitmsg;
                   2348:                        PurpleNotifyUserInfo *user_info;
                   2349: 
                   2350:                        /* Split at (carriage return/newline)'s, then rejoin later with BRs between. */
                   2351:                        statusmsg = oscar_icqstatus(state);
                   2352:                        splitmsg = g_strsplit(msg, "\r\n", 0);
                   2353: 
                   2354:                        user_info = purple_notify_user_info_new();
                   2355: 
                   2356:                        purple_notify_user_info_add_pair(user_info, _("UIN"), who);
                   2357:                        purple_notify_user_info_add_pair(user_info, _("Status"), statusmsg);
                   2358:                        purple_notify_user_info_add_section_break(user_info);
                   2359:                        purple_notify_user_info_add_pair(user_info, NULL, g_strjoinv("<BR>", splitmsg));
                   2360: 
                   2361:                        g_free(statusmsg);
                   2362:                        g_strfreev(splitmsg);
                   2363: 
                   2364:                        purple_notify_userinfo(gc, who, user_info, NULL, NULL);
                   2365:                        purple_notify_user_info_destroy(user_info);
                   2366: 
                   2367:                } break;
                   2368: 
                   2369:                default: {
                   2370:                        purple_debug_warning("oscar",
                   2371:                                           "Received an unknown client auto-response from %s.  "
                   2372:                                           "Type 0x%04hx\n", who, reason);
                   2373:                } break;
                   2374:        } /* end of switch */
                   2375: 
                   2376:        return 0;
                   2377: }
                   2378: 
                   2379: static int purple_parse_clientauto(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
                   2380:        va_list ap;
                   2381:        guint16 chan, reason;
                   2382:        char *who;
                   2383:        int ret = 1;
                   2384: 
                   2385:        va_start(ap, fr);
                   2386:        chan = (guint16)va_arg(ap, unsigned int);
                   2387:        who = va_arg(ap, char *);
                   2388:        reason = (guint16)va_arg(ap, unsigned int);
                   2389: 
                   2390:        if (chan == 0x0002) { /* File transfer declined */
                   2391:                guchar *cookie = va_arg(ap, guchar *);
                   2392:                ret = purple_parse_clientauto_ch2(od, who, reason, cookie);
                   2393:        } else if (chan == 0x0004) { /* ICQ message */
                   2394:                guint32 state = 0;
                   2395:                char *msg = NULL;
                   2396:                if (reason == 0x0003) {
                   2397:                        state = va_arg(ap, guint32);
                   2398:                        msg = va_arg(ap, char *);
                   2399:                }
                   2400:                ret = purple_parse_clientauto_ch4(od, who, reason, state, msg);
                   2401:        }
                   2402: 
                   2403:        va_end(ap);
                   2404: 
                   2405:        return ret;
                   2406: }
                   2407: 
                   2408: static int purple_parse_genericerr(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
                   2409:        va_list ap;
                   2410:        guint16 reason;
                   2411: 
                   2412:        va_start(ap, fr);
                   2413:        reason = (guint16) va_arg(ap, unsigned int);
                   2414:        va_end(ap);
                   2415: 
                   2416:        purple_debug_error("oscar", "snac threw error (reason 0x%04hx: %s)\n",
                   2417:                        reason, oscar_get_msgerr_reason(reason));
                   2418:        return 1;
                   2419: }
                   2420: 
                   2421: static int purple_parse_mtn(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
                   2422:        PurpleConnection *gc = od->gc;
                   2423:        va_list ap;
                   2424:        guint16 channel, event;
                   2425:        char *bn;
                   2426: 
                   2427:        va_start(ap, fr);
                   2428:        channel = (guint16) va_arg(ap, unsigned int);
                   2429:        bn = va_arg(ap, char *);
                   2430:        event = (guint16) va_arg(ap, unsigned int);
                   2431:        va_end(ap);
                   2432: 
                   2433:        switch (event) {
                   2434:                case 0x0000: { /* Text has been cleared */
                   2435:                        serv_got_typing_stopped(gc, bn);
                   2436:                } break;
                   2437: 
                   2438:                case 0x0001: { /* Paused typing */
                   2439:                        serv_got_typing(gc, bn, 0, PURPLE_TYPED);
                   2440:                } break;
                   2441: 
                   2442:                case 0x0002: { /* Typing */
                   2443:                        serv_got_typing(gc, bn, 0, PURPLE_TYPING);
                   2444:                } break;
                   2445: 
                   2446:                case 0x000f: { /* Closed IM window */
                   2447:                        serv_got_typing_stopped(gc, bn);
                   2448:                } break;
                   2449: 
                   2450:                default: {
                   2451:                        purple_debug_info("oscar", "Received unknown typing "
                   2452:                                        "notification message from %s.  Channel is 0x%04x "
                   2453:                                        "and event is 0x%04hx.\n", bn, channel, event);
                   2454:                } break;
                   2455:        }
                   2456: 
                   2457:        return 1;
                   2458: }
                   2459: 
                   2460: static int purple_parse_motd(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...)
                   2461: {
                   2462:        char *msg;
                   2463:        guint16 id;
                   2464:        va_list ap;
                   2465: 
                   2466:        va_start(ap, fr);
                   2467:        id  = (guint16) va_arg(ap, unsigned int);
                   2468:        msg = va_arg(ap, char *);
                   2469:        va_end(ap);
                   2470: 
                   2471:        purple_debug_misc("oscar",
                   2472:                           "MOTD: %s (%hu)\n", msg ? msg : "Unknown", id);
                   2473:        if (id < 4)
                   2474:                purple_notify_warning(od->gc, NULL,
                   2475:                                                        _("Your AIM connection may be lost."), NULL);
                   2476: 
                   2477:        return 1;
                   2478: }
                   2479: 
                   2480: static int purple_chatnav_info(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
                   2481:        va_list ap;
                   2482:        guint16 type;
                   2483: 
                   2484:        va_start(ap, fr);
                   2485:        type = (guint16) va_arg(ap, unsigned int);
                   2486: 
                   2487:        switch(type) {
                   2488:                case 0x0002: {
                   2489:                        GString *msg = g_string_new("");
                   2490:                        guint8 maxrooms;
                   2491:                        struct aim_chat_exchangeinfo *exchanges;
                   2492:                        int exchangecount, i;
                   2493: 
                   2494:                        maxrooms = (guint8) va_arg(ap, unsigned int);
                   2495:                        exchangecount = va_arg(ap, int);
                   2496:                        exchanges = va_arg(ap, struct aim_chat_exchangeinfo *);
                   2497: 
                   2498:                        g_string_append_printf(msg, "chat info: Max Concurrent Rooms: %hhd, Exchange List (%d total): ", maxrooms, exchangecount);
                   2499:                        for (i = 0; i < exchangecount; i++) {
                   2500:                                g_string_append_printf(msg, "%hu", exchanges[i].number);
                   2501:                                if (exchanges[i].name) {
                   2502:                                        g_string_append_printf(msg, " %s", exchanges[i].name);
                   2503:                                }
                   2504:                                g_string_append(msg, ", ");
                   2505:                        }
                   2506:                        purple_debug_misc("oscar", "%s\n", msg->str);
                   2507:                        g_string_free(msg, TRUE);
                   2508: 
                   2509:                        while (od->create_rooms) {
                   2510:                                struct create_room *cr = od->create_rooms->data;
                   2511:                                purple_debug_info("oscar",
                   2512:                                                   "creating room %s\n", cr->name);
                   2513:                                aim_chatnav_createroom(od, conn, cr->name, cr->exchange);
                   2514:                                g_free(cr->name);
                   2515:                                od->create_rooms = g_slist_remove(od->create_rooms, cr);
                   2516:                                g_free(cr);
                   2517:                        }
                   2518:                        }
                   2519:                        break;
                   2520:                case 0x0008: {
                   2521:                        char *fqcn, *name, *ck;
                   2522:                        guint16 instance, flags, maxmsglen, maxoccupancy, unknown, exchange;
                   2523:                        guint8 createperms;
                   2524:                        guint32 createtime;
                   2525: 
                   2526:                        fqcn = va_arg(ap, char *);
                   2527:                        instance = (guint16)va_arg(ap, unsigned int);
                   2528:                        exchange = (guint16)va_arg(ap, unsigned int);
                   2529:                        flags = (guint16)va_arg(ap, unsigned int);
                   2530:                        createtime = va_arg(ap, guint32);
                   2531:                        maxmsglen = (guint16)va_arg(ap, unsigned int);
                   2532:                        maxoccupancy = (guint16)va_arg(ap, unsigned int);
                   2533:                        createperms = (guint8)va_arg(ap, unsigned int);
                   2534:                        unknown = (guint16)va_arg(ap, unsigned int);
                   2535:                        name = va_arg(ap, char *);
                   2536:                        ck = va_arg(ap, char *);
                   2537: 
                   2538:                        purple_debug_misc("oscar",
                   2539:                                        "created room: %s %hu %hu %hu %u %hu %hu %hhu %hu %s %s\n",
                   2540:                                        fqcn ? fqcn : "(null)", exchange, instance, flags, createtime,
                   2541:                                        maxmsglen, maxoccupancy, createperms, unknown,
                   2542:                                        name ? name : "(null)", ck);
                   2543:                        aim_chat_join(od, exchange, ck, instance);
                   2544:                        }
                   2545:                        break;
                   2546:                default:
                   2547:                        purple_debug_warning("oscar",
                   2548:                                           "chatnav info: unknown type (%04hx)\n", type);
                   2549:                        break;
                   2550:        }
                   2551: 
                   2552:        va_end(ap);
                   2553: 
                   2554:        return 1;
                   2555: }
                   2556: 
                   2557: static int purple_conv_chat_join(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
                   2558:        va_list ap;
                   2559:        int count, i;
                   2560:        aim_userinfo_t *info;
                   2561:        PurpleConnection *gc = od->gc;
                   2562: 
                   2563:        struct chat_connection *c = NULL;
                   2564: 
                   2565:        va_start(ap, fr);
                   2566:        count = va_arg(ap, int);
                   2567:        info  = va_arg(ap, aim_userinfo_t *);
                   2568:        va_end(ap);
                   2569: 
                   2570:        c = find_oscar_chat_by_conn(gc, conn);
                   2571:        if (!c)
                   2572:                return 1;
                   2573: 
                   2574:        for (i = 0; i < count; i++)
                   2575:                purple_conv_chat_add_user(PURPLE_CONV_CHAT(c->conv), info[i].bn, NULL, PURPLE_CBFLAGS_NONE, TRUE);
                   2576: 
                   2577:        return 1;
                   2578: }
                   2579: 
                   2580: static int purple_conv_chat_leave(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
                   2581:        va_list ap;
                   2582:        int count, i;
                   2583:        aim_userinfo_t *info;
                   2584:        PurpleConnection *gc = od->gc;
                   2585: 
                   2586:        struct chat_connection *c = NULL;
                   2587: 
                   2588:        va_start(ap, fr);
                   2589:        count = va_arg(ap, int);
                   2590:        info  = va_arg(ap, aim_userinfo_t *);
                   2591:        va_end(ap);
                   2592: 
                   2593:        c = find_oscar_chat_by_conn(gc, conn);
                   2594:        if (!c)
                   2595:                return 1;
                   2596: 
                   2597:        for (i = 0; i < count; i++)
                   2598:                purple_conv_chat_remove_user(PURPLE_CONV_CHAT(c->conv), info[i].bn, NULL);
                   2599: 
                   2600:        return 1;
                   2601: }
                   2602: 
                   2603: static int purple_conv_chat_info_update(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
                   2604:        va_list ap;
                   2605:        guint16 maxmsglen, maxvisiblemsglen;
                   2606:        PurpleConnection *gc = od->gc;
                   2607:        struct chat_connection *ccon = find_oscar_chat_by_conn(gc, conn);
                   2608: 
                   2609:        if (!ccon)
                   2610:                return 1;
                   2611: 
                   2612:        va_start(ap, fr);
                   2613:        maxmsglen = (guint16)va_arg(ap, unsigned int);
                   2614:        maxvisiblemsglen = (guint16)va_arg(ap, unsigned int);
                   2615:        va_end(ap);
                   2616: 
                   2617:        purple_debug_misc("oscar",
                   2618:                           "inside chat_info_update (maxmsglen = %hu, maxvislen = %hu)\n",
                   2619:                           maxmsglen, maxvisiblemsglen);
                   2620: 
                   2621:        ccon->maxlen = maxmsglen;
                   2622:        ccon->maxvis = maxvisiblemsglen;
                   2623: 
                   2624:        return 1;
                   2625: }
                   2626: 
                   2627: static int purple_conv_chat_incoming_msg(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
                   2628:        PurpleConnection *gc = od->gc;
                   2629:        struct chat_connection *ccon = find_oscar_chat_by_conn(gc, conn);
                   2630:        gchar *utf8;
                   2631:        va_list ap;
                   2632:        aim_userinfo_t *info;
                   2633:        int len;
                   2634:        char *msg;
                   2635:        char *charset;
                   2636: 
                   2637:        if (!ccon)
                   2638:                return 1;
                   2639: 
                   2640:        va_start(ap, fr);
                   2641:        info = va_arg(ap, aim_userinfo_t *);
                   2642:        len = va_arg(ap, int);
                   2643:        msg = va_arg(ap, char *);
                   2644:        charset = va_arg(ap, char *);
                   2645:        va_end(ap);
                   2646: 
                   2647:        utf8 = oscar_encoding_to_utf8(charset, msg, len);
                   2648:        serv_got_chat_in(gc, ccon->id, info->bn, 0, utf8, time(NULL));
                   2649:        g_free(utf8);
                   2650: 
                   2651:        return 1;
                   2652: }
                   2653: 
                   2654: static int purple_email_parseupdate(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
                   2655:        va_list ap;
                   2656:        PurpleConnection *gc;
                   2657:        PurpleAccount *account;
                   2658:        struct aim_emailinfo *emailinfo;
                   2659:        int havenewmail;
                   2660:        char *alertitle, *alerturl;
                   2661: 
                   2662:        gc = od->gc;
                   2663:        account = purple_connection_get_account(gc);
                   2664: 
                   2665:        va_start(ap, fr);
                   2666:        emailinfo = va_arg(ap, struct aim_emailinfo *);
                   2667:        havenewmail = va_arg(ap, int);
                   2668:        alertitle = va_arg(ap, char *);
                   2669:        alerturl  = va_arg(ap, char *);
                   2670:        va_end(ap);
                   2671: 
                   2672:        if (account != NULL && emailinfo != NULL && purple_account_get_check_mail(account) &&
                   2673:                        emailinfo->unread && havenewmail) {
                   2674:                gchar *to = g_strdup_printf("%s%s%s",
                   2675:                                purple_account_get_username(account),
                   2676:                                emailinfo->domain ? "@" : "",
                   2677:                                emailinfo->domain ? emailinfo->domain : "");
                   2678:                const char *tos[2] = { to };
                   2679:                const char *urls[2] = { emailinfo->url };
                   2680:                purple_notify_emails(gc, emailinfo->nummsgs, FALSE, NULL, NULL,
                   2681:                                tos, urls, NULL, NULL);
                   2682:                g_free(to);
                   2683:        }
                   2684: 
                   2685:        if (alertitle)
                   2686:                purple_debug_misc("oscar", "Got an alert '%s' %s\n", alertitle, alerturl ? alerturl : "");
                   2687: 
                   2688:        return 1;
                   2689: }
                   2690: 
                   2691: static int purple_icon_parseicon(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
                   2692:        PurpleConnection *gc = od->gc;
                   2693:        va_list ap;
                   2694:        char *bn;
                   2695:        guint8 *iconcsum, *icon;
                   2696:        guint16 iconcsumlen, iconlen;
                   2697: 
                   2698:        va_start(ap, fr);
                   2699:        bn = va_arg(ap, char *);
                   2700:        va_arg(ap, int); /* iconsumtype */
                   2701:        iconcsum = va_arg(ap, guint8 *);
                   2702:        iconcsumlen = va_arg(ap, int);
                   2703:        icon = va_arg(ap, guint8 *);
                   2704:        iconlen = va_arg(ap, int);
                   2705:        va_end(ap);
                   2706: 
                   2707:        /*
                   2708:         * Some AIM clients will send a blank GIF image with iconlen 90 when
                   2709:         * no icon is set.  Ignore these.
                   2710:         */
                   2711:        if ((iconlen > 0) && (iconlen != 90)) {
                   2712:                char *b16 = purple_base16_encode(iconcsum, iconcsumlen);
                   2713:                purple_buddy_icons_set_for_user(purple_connection_get_account(gc),
                   2714:                                                                          bn, g_memdup(icon, iconlen), iconlen, b16);
                   2715:                g_free(b16);
                   2716:        }
                   2717: 
                   2718:        return 1;
                   2719: }
                   2720: 
                   2721: static void
                   2722: purple_icons_fetch(PurpleConnection *gc)
                   2723: {
                   2724:        OscarData *od = purple_connection_get_protocol_data(gc);
                   2725:        aim_userinfo_t *userinfo;
                   2726:        FlapConnection *conn;
                   2727: 
                   2728:        conn = flap_connection_getbytype(od, SNAC_FAMILY_BART);
                   2729:        if (!conn) {
                   2730:                if (!od->iconconnecting) {
                   2731:                        aim_srv_requestnew(od, SNAC_FAMILY_BART);
                   2732:                        od->iconconnecting = TRUE;
                   2733:                }
                   2734:                return;
                   2735:        }
                   2736: 
                   2737:        if (od->set_icon) {
                   2738:                PurpleAccount *account = purple_connection_get_account(gc);
                   2739:                PurpleStoredImage *img = purple_buddy_icons_find_account_icon(account);
                   2740:                if (img == NULL) {
                   2741:                        aim_ssi_delicon(od);
                   2742:                } else {
                   2743:                        purple_debug_info("oscar",
                   2744:                                   "Uploading icon to icon server\n");
                   2745:                        aim_bart_upload(od, purple_imgstore_get_data(img),
                   2746:                                        purple_imgstore_get_size(img));
                   2747:                        purple_imgstore_unref(img);
                   2748:                }
                   2749:                od->set_icon = FALSE;
                   2750:        }
                   2751: 
                   2752:        while (od->requesticon != NULL)
                   2753:        {
                   2754:                userinfo = aim_locate_finduserinfo(od, (char *)od->requesticon->data);
                   2755:                if ((userinfo != NULL) && (userinfo->iconcsumlen > 0))
                   2756:                        aim_bart_request(od, od->requesticon->data, userinfo->iconcsumtype, userinfo->iconcsum, userinfo->iconcsumlen);
                   2757: 
                   2758:                g_free(od->requesticon->data);
                   2759:                od->requesticon = g_slist_delete_link(od->requesticon, od->requesticon);
                   2760:        }
                   2761: 
                   2762:        purple_debug_misc("oscar", "no more icons to request\n");
                   2763: }
                   2764: 
                   2765: static int purple_selfinfo(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
                   2766:        va_list ap;
                   2767:        aim_userinfo_t *info;
                   2768: 
                   2769:        va_start(ap, fr);
                   2770:        info = va_arg(ap, aim_userinfo_t *);
                   2771:        va_end(ap);
                   2772: 
                   2773:        purple_connection_set_display_name(od->gc, info->bn);
                   2774: 
                   2775:        return 1;
                   2776: }
                   2777: 
                   2778: static int purple_connerr(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
                   2779:        PurpleConnection *gc = od->gc;
                   2780:        va_list ap;
                   2781:        guint16 code;
                   2782:        char *msg;
                   2783: 
                   2784:        va_start(ap, fr);
                   2785:        code = (guint16)va_arg(ap, int);
                   2786:        msg = va_arg(ap, char *);
                   2787:        va_end(ap);
                   2788: 
                   2789:        purple_debug_info("oscar", "Disconnected.  Code is 0x%04x and msg is %s\n",
                   2790:                                        code, (msg != NULL ? msg : ""));
                   2791: 
                   2792:        g_return_val_if_fail(conn != NULL, 1);
                   2793: 
                   2794:        if (conn->type == SNAC_FAMILY_CHAT) {
                   2795:                struct chat_connection *cc;
                   2796:                PurpleConversation *conv = NULL;
                   2797: 
                   2798:                cc = find_oscar_chat_by_conn(gc, conn);
                   2799:                if (cc != NULL)
                   2800:                {
                   2801:                        conv = purple_find_chat(gc, cc->id);
                   2802: 
                   2803:                        if (conv != NULL)
                   2804:                        {
                   2805:                                /*
                   2806:                                 * TOOD: Have flap_connection_destroy_cb() send us the
                   2807:                                 *       error message stored in 'tmp', which should be
                   2808:                                 *       human-friendly, and print that to the chat room.
                   2809:                                 */
                   2810:                                gchar *buf;
                   2811:                                buf = g_strdup_printf(_("You have been disconnected from chat "
                   2812:                                                                                "room %s."), cc->name);
                   2813:                                purple_conversation_write(conv, NULL, buf, PURPLE_MESSAGE_ERROR, time(NULL));
                   2814:                                g_free(buf);
                   2815:                        }
                   2816:                        oscar_chat_kill(gc, cc);
                   2817:                }
                   2818:        }
                   2819: 
                   2820:        return 1;
                   2821: }
                   2822: 
                   2823: static int purple_parse_locaterights(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...)
                   2824: {
                   2825:        PurpleConnection *gc = od->gc;
                   2826:        PurpleAccount *account = purple_connection_get_account(gc);
                   2827:        va_list ap;
                   2828:        guint16 maxsiglen;
                   2829: 
                   2830:        va_start(ap, fr);
                   2831:        maxsiglen = (guint16) va_arg(ap, int);
                   2832:        va_end(ap);
                   2833: 
                   2834:        purple_debug_misc("oscar",
                   2835:                           "locate rights: max sig len = %d\n", maxsiglen);
                   2836: 
                   2837:        od->rights.maxsiglen = od->rights.maxawaymsglen = (guint)maxsiglen;
                   2838: 
                   2839:        aim_locate_setcaps(od, purple_caps);
                   2840:        oscar_set_info_and_status(account, TRUE, account->user_info, TRUE,
                   2841:                                                          purple_account_get_active_status(account));
                   2842: 
                   2843:        return 1;
                   2844: }
                   2845: 
                   2846: static int purple_parse_buddyrights(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
                   2847:        va_list ap;
                   2848:        guint16 maxbuddies, maxwatchers;
                   2849: 
                   2850:        va_start(ap, fr);
                   2851:        maxbuddies = (guint16) va_arg(ap, unsigned int);
                   2852:        maxwatchers = (guint16) va_arg(ap, unsigned int);
                   2853:        va_end(ap);
                   2854: 
                   2855:        purple_debug_misc("oscar",
                   2856:                           "buddy list rights: Max buddies = %hu / Max watchers = %hu\n", maxbuddies, maxwatchers);
                   2857: 
                   2858:        od->rights.maxbuddies = (guint)maxbuddies;
                   2859:        od->rights.maxwatchers = (guint)maxwatchers;
                   2860: 
                   2861:        return 1;
                   2862: }
                   2863: 
                   2864: static void oscar_format_username(PurpleConnection *gc, const char *new_display_name)
                   2865: {
                   2866:        OscarData *od;
                   2867:        const char *old_display_name, *username;
                   2868:        char *tmp, *at_sign;
                   2869: 
                   2870:        old_display_name = purple_connection_get_display_name(gc);
                   2871:        if (old_display_name && strchr(old_display_name, '@')) {
                   2872:                purple_debug_info("oscar", "Cowardly refusing to attempt to format "
                   2873:                                "screen name because the current formatting according to "
                   2874:                                "the server (%s) appears to be an email address\n",
                   2875:                                old_display_name);
                   2876:                return;
                   2877:        }
                   2878: 
                   2879:        username = purple_account_get_username(purple_connection_get_account(gc));
                   2880:        if (oscar_util_name_compare(username, new_display_name)) {
                   2881:                purple_notify_error(gc, NULL, _("The new formatting is invalid."),
                   2882:                                                  _("Username formatting can change only capitalization and whitespace."));
                   2883:                return;
                   2884:        }
                   2885: 
                   2886:        tmp = g_strdup(new_display_name);
                   2887: 
                   2888:        /*
                   2889:         * If our local username is an email address then strip off the domain.
                   2890:         * This allows formatting to work if the user entered their username as
                   2891:         * 'something@aim.com' or possibly other AOL-owned domains.
                   2892:         */
                   2893:        at_sign = strchr(tmp, '@');
                   2894:        if (at_sign)
                   2895:                at_sign[0] = '\0';
                   2896: 
                   2897:        od = purple_connection_get_protocol_data(gc);
                   2898:        if (!flap_connection_getbytype(od, SNAC_FAMILY_ADMIN)) {
                   2899:                /* We don't have a connection to an "admin" server.  Make one. */
                   2900:                od->setnick = TRUE;
                   2901:                g_free(od->newformatting);
                   2902:                od->newformatting = tmp;
                   2903:                aim_srv_requestnew(od, SNAC_FAMILY_ADMIN);
                   2904:        } else {
                   2905:                aim_admin_setnick(od, flap_connection_getbytype(od, SNAC_FAMILY_ADMIN), tmp);
                   2906:                g_free(tmp);
                   2907:        }
                   2908: }
                   2909: 
                   2910: static int purple_bosrights(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
                   2911:        PurpleConnection *gc;
                   2912:        PurpleAccount *account;
                   2913:        PurpleStatus *status;
                   2914:        gboolean is_available;
                   2915:        PurplePresence *presence;
                   2916:        const char *username, *message, *itmsurl;
                   2917:        char *tmp;
                   2918:        va_list ap;
                   2919:        guint16 maxpermits, maxdenies;
                   2920: 
                   2921:        gc = od->gc;
                   2922:        od = purple_connection_get_protocol_data(gc);
                   2923:        account = purple_connection_get_account(gc);
                   2924: 
                   2925:        va_start(ap, fr);
                   2926:        maxpermits = (guint16) va_arg(ap, unsigned int);
                   2927:        maxdenies = (guint16) va_arg(ap, unsigned int);
                   2928:        va_end(ap);
                   2929: 
                   2930:        purple_debug_misc("oscar",
                   2931:                           "BOS rights: Max permit = %hu / Max deny = %hu\n", maxpermits, maxdenies);
                   2932: 
                   2933:        od->rights.maxpermits = (guint)maxpermits;
                   2934:        od->rights.maxdenies = (guint)maxdenies;
                   2935: 
                   2936:        purple_debug_info("oscar", "buddy list loaded\n");
                   2937: 
                   2938:        if (purple_account_get_user_info(account) != NULL)
                   2939:                serv_set_info(gc, purple_account_get_user_info(account));
                   2940: 
                   2941:        username = purple_account_get_username(account);
                   2942:        if (!od->icq && !purple_strequal(username, purple_connection_get_display_name(gc))) {
                   2943:                /*
                   2944:                 * Format the username for AIM accounts if it's different
                   2945:                 * than what's currently set.
                   2946:                 */
                   2947:                oscar_format_username(gc, username);
                   2948:        }
                   2949: 
                   2950:        /* Set our available message based on the current status */
                   2951:        status = purple_account_get_active_status(account);
                   2952:        is_available = purple_status_is_available(status);
                   2953:        if (is_available)
                   2954:                message = purple_status_get_attr_string(status, "message");
                   2955:        else
                   2956:                message = NULL;
                   2957:        tmp = purple_markup_strip_html(message);
                   2958:        itmsurl = purple_status_get_attr_string(status, "itmsurl");
                   2959:        aim_srv_setextrainfo(od, FALSE, 0, is_available, tmp, itmsurl);
                   2960:        aim_srv_set_dc_info(od);
                   2961:        g_free(tmp);
                   2962: 
                   2963:        presence = purple_status_get_presence(status);
                   2964:        aim_srv_setidle(od, !purple_presence_is_idle(presence) ? 0 : time(NULL) - purple_presence_get_idle_time(presence));
                   2965: 
                   2966:        if (od->icq) {
                   2967:                oscar_set_extended_status(gc);
                   2968:                aim_icq_setsecurity(od,
                   2969:                        purple_account_get_bool(account, "authorization", OSCAR_DEFAULT_AUTHORIZATION),
                   2970:                        purple_account_get_bool(account, "web_aware", OSCAR_DEFAULT_WEB_AWARE));
                   2971:        }
                   2972: 
                   2973:        aim_srv_requestnew(od, SNAC_FAMILY_ALERT);
                   2974:        aim_srv_requestnew(od, SNAC_FAMILY_CHATNAV);
                   2975: 
                   2976:        od->bos.have_rights = TRUE;
                   2977: 
                   2978:        /*
                   2979:         * If we've already received our feedbag data then we're not waiting on
                   2980:         * anything else, so send the server clientready.
                   2981:         *
                   2982:         * Normally we get bos rights before we get our feedbag data, so this
                   2983:         * rarely (never?) happens.  And I'm not sure it actually matters if we
                   2984:         * wait for bos rights before calling clientready.  But it seems safer
                   2985:         * to do it this way.
                   2986:         */
                   2987:        if (od->ssi.received_data) {
                   2988:                aim_srv_clientready(od, conn);
                   2989: 
                   2990:                /* Request offline messages for AIM and ICQ */
                   2991:                aim_im_reqofflinemsgs(od);
                   2992: 
                   2993:                purple_connection_set_state(gc, PURPLE_CONNECTED);
                   2994:        }
                   2995: 
                   2996:        return 1;
                   2997: }
                   2998: 
                   2999: static int purple_popup(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...)
                   3000: {
                   3001:        PurpleConnection *gc = od->gc;
                   3002:        gchar *text;
                   3003:        va_list ap;
                   3004:        char *msg, *url;
                   3005: 
                   3006:        va_start(ap, fr);
                   3007:        msg = va_arg(ap, char *);
                   3008:        url = va_arg(ap, char *);
                   3009:        va_arg(ap, int); /* guint16 wid */
                   3010:        va_arg(ap, int); /* guint16 hei */
                   3011:        va_arg(ap, int); /* guint16 delay */
                   3012:        va_end(ap);
                   3013: 
                   3014:        text = g_strdup_printf("%s<br><a href=\"%s\">%s</a>", msg, url, url);
                   3015:        purple_notify_formatted(gc, NULL, _("Pop-Up Message"), NULL, text, NULL, NULL);
                   3016:        g_free(text);
                   3017: 
                   3018:        return 1;
                   3019: }
                   3020: 
                   3021: static void oscar_searchresults_add_buddy_cb(PurpleConnection *gc, GList *row, void *user_data)
                   3022: {
                   3023:        purple_blist_request_add_buddy(purple_connection_get_account(gc),
                   3024:                                                                 g_list_nth_data(row, 0), NULL, NULL);
                   3025: }
                   3026: 
                   3027: static int purple_parse_searchreply(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...)
                   3028: {
                   3029:        PurpleConnection *gc = od->gc;
                   3030:        PurpleNotifySearchResults *results;
                   3031:        PurpleNotifySearchColumn *column;
                   3032:        gchar *secondary;
                   3033:        int i, num;
                   3034:        va_list ap;
                   3035:        char *email, *usernames;
                   3036: 
                   3037:        va_start(ap, fr);
                   3038:        email = va_arg(ap, char *);
                   3039:        num = va_arg(ap, int);
                   3040:        usernames = va_arg(ap, char *);
                   3041:        va_end(ap);
                   3042: 
                   3043:        results = purple_notify_searchresults_new();
                   3044: 
                   3045:        if (results == NULL) {
                   3046:                purple_debug_error("oscar", "purple_parse_searchreply: "
                   3047:                                                 "Unable to display the search results.\n");
                   3048:                purple_notify_error(gc, NULL,
                   3049:                                                  _("Unable to display the search results."),
                   3050:                                                  NULL);
                   3051:                return 1;
                   3052:        }
                   3053: 
                   3054:        secondary = g_strdup_printf(
                   3055:                                        dngettext(PACKAGE, "The following username is associated with %s",
                   3056:                                                 "The following usernames are associated with %s",
                   3057:                                                 num),
                   3058:                                        email);
                   3059: 
                   3060:        column = purple_notify_searchresults_column_new(_("Username"));
                   3061:        purple_notify_searchresults_column_add(results, column);
                   3062: 
                   3063:        for (i = 0; i < num; i++) {
                   3064:                GList *row;
                   3065:                row = g_list_append(NULL, g_strdup(&usernames[i * (MAXSNLEN + 1)]));
                   3066:                purple_notify_searchresults_row_add(results, row);
                   3067:        }
                   3068:        purple_notify_searchresults_button_add(results, PURPLE_NOTIFY_BUTTON_ADD,
                   3069:                                                                                 oscar_searchresults_add_buddy_cb);
                   3070:        purple_notify_searchresults(gc, NULL, NULL, secondary, results, NULL, NULL);
                   3071: 
                   3072:        g_free(secondary);
                   3073: 
                   3074:        return 1;
                   3075: }
                   3076: 
                   3077: static int purple_parse_searcherror(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
                   3078:        va_list ap;
                   3079:        char *email;
                   3080:        char *buf;
                   3081: 
                   3082:        va_start(ap, fr);
                   3083:        email = va_arg(ap, char *);
                   3084:        va_end(ap);
                   3085: 
                   3086:        buf = g_strdup_printf(_("No results found for email address %s"), email);
                   3087:        purple_notify_error(od->gc, NULL, buf, NULL);
                   3088:        g_free(buf);
                   3089: 
                   3090:        return 1;
                   3091: }
                   3092: 
                   3093: static int purple_account_confirm(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
                   3094:        PurpleConnection *gc = od->gc;
                   3095:        guint16 status;
                   3096:        va_list ap;
                   3097:        char msg[256];
                   3098: 
                   3099:        va_start(ap, fr);
                   3100:        status = (guint16) va_arg(ap, unsigned int); /* status code of confirmation request */
                   3101:        va_end(ap);
                   3102: 
                   3103:        purple_debug_info("oscar",
                   3104:                           "account confirmation returned status 0x%04x (%s)\n", status,
                   3105:                        status ? "unknown" : "email sent");
                   3106:        if (!status) {
                   3107:                g_snprintf(msg, sizeof(msg), _("You should receive an email asking to confirm %s."),
                   3108:                                purple_account_get_username(purple_connection_get_account(gc)));
                   3109:                purple_notify_info(gc, NULL, _("Account Confirmation Requested"), msg);
                   3110:        }
                   3111: 
                   3112:        return 1;
                   3113: }
                   3114: 
                   3115: static int purple_info_change(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
                   3116:        PurpleConnection *gc = od->gc;
                   3117:        va_list ap;
                   3118:        guint16 perms, err;
                   3119:        char *url, *bn, *email;
                   3120:        int change;
                   3121: 
                   3122:        va_start(ap, fr);
                   3123:        change = va_arg(ap, int);
                   3124:        perms = (guint16) va_arg(ap, unsigned int);
                   3125:        err = (guint16) va_arg(ap, unsigned int);
                   3126:        url = va_arg(ap, char *);
                   3127:        bn = va_arg(ap, char *);
                   3128:        email = va_arg(ap, char *);
                   3129:        va_end(ap);
                   3130: 
                   3131:        purple_debug_misc("oscar",
                   3132:                                        "account info: because of %s, perms=0x%04x, err=0x%04x, url=%s, bn=%s, email=%s\n",
                   3133:                                        change ? "change" : "request", perms, err,
                   3134:                                        (url != NULL) ? url : "(null)",
                   3135:                                        (bn != NULL) ? bn : "(null)",
                   3136:                                        (email != NULL) ? email : "(null)");
                   3137: 
                   3138:        if ((err > 0) && (url != NULL)) {
                   3139:                char *dialog_msg;
                   3140: 
                   3141:                if (err == 0x0001)
                   3142:                        dialog_msg = g_strdup_printf(_("Error 0x%04x: Unable to format username because the requested name differs from the original."), err);
                   3143:                else if (err == 0x0006)
                   3144:                        dialog_msg = g_strdup_printf(_("Error 0x%04x: Unable to format username because it is invalid."), err);
                   3145:                else if (err == 0x00b)
                   3146:                        dialog_msg = g_strdup_printf(_("Error 0x%04x: Unable to format username because the requested name is too long."), err);
                   3147:                else if (err == 0x001d)
                   3148:                        dialog_msg = g_strdup_printf(_("Error 0x%04x: Unable to change email address because there is already a request pending for this username."), err);
                   3149:                else if (err == 0x0021)
                   3150:                        dialog_msg = g_strdup_printf(_("Error 0x%04x: Unable to change email address because the given address has too many usernames associated with it."), err);
                   3151:                else if (err == 0x0023)
                   3152:                        dialog_msg = g_strdup_printf(_("Error 0x%04x: Unable to change email address because the given address is invalid."), err);
                   3153:                else
                   3154:                        dialog_msg = g_strdup_printf(_("Error 0x%04x: Unknown error."), err);
                   3155:                purple_notify_error(gc, NULL,
                   3156:                                _("Error Changing Account Info"), dialog_msg);
                   3157:                g_free(dialog_msg);
                   3158:                return 1;
                   3159:        }
                   3160: 
                   3161:        if (email != NULL) {
                   3162:                char *dialog_msg = g_strdup_printf(_("The email address for %s is %s"),
                   3163:                                                   purple_account_get_username(purple_connection_get_account(gc)), email);
                   3164:                purple_notify_info(gc, NULL, _("Account Info"), dialog_msg);
                   3165:                g_free(dialog_msg);
                   3166:        }
                   3167: 
                   3168:        return 1;
                   3169: }
                   3170: 
                   3171: void
                   3172: oscar_keepalive(PurpleConnection *gc)
                   3173: {
                   3174:        OscarData *od;
                   3175:        GSList *l;
                   3176: 
                   3177:        od = purple_connection_get_protocol_data(gc);
                   3178:        for (l = od->oscar_connections; l; l = l->next) {
                   3179:                flap_connection_send_keepalive(od, l->data);
                   3180:        }
                   3181: }
                   3182: 
                   3183: unsigned int
                   3184: oscar_send_typing(PurpleConnection *gc, const char *name, PurpleTypingState state)
                   3185: {
                   3186:        OscarData *od;
                   3187:        PeerConnection *conn;
                   3188: 
                   3189:        od = purple_connection_get_protocol_data(gc);
                   3190:        conn = peer_connection_find_by_type(od, name, OSCAR_CAPABILITY_DIRECTIM);
                   3191: 
                   3192:        if ((conn != NULL) && (conn->ready))
                   3193:        {
                   3194:                peer_odc_send_typing(conn, state);
                   3195:        }
                   3196:        else {
                   3197:                /* Don't send if this turkey is in our deny list */
                   3198:                GSList *list;
                   3199:                for (list=gc->account->deny; (list && oscar_util_name_compare(name, list->data)); list=list->next);
                   3200:                if (!list) {
                   3201:                        struct buddyinfo *bi = g_hash_table_lookup(od->buddyinfo, purple_normalize(gc->account, name));
                   3202:                        if (bi && bi->typingnot) {
                   3203:                                if (state == PURPLE_TYPING)
                   3204:                                        aim_im_sendmtn(od, 0x0001, name, 0x0002);
                   3205:                                else if (state == PURPLE_TYPED)
                   3206:                                        aim_im_sendmtn(od, 0x0001, name, 0x0001);
                   3207:                                else
                   3208:                                        aim_im_sendmtn(od, 0x0001, name, 0x0000);
                   3209:                        }
                   3210:                }
                   3211:        }
                   3212:        return 0;
                   3213: }
                   3214: 
                   3215: /* TODO: Move this into odc.c! */
                   3216: static void
                   3217: purple_odc_send_im(PeerConnection *conn, const char *message, PurpleMessageFlags imflags)
                   3218: {
                   3219:        GString *msg;
                   3220:        GString *data;
                   3221:        gchar *tmp;
                   3222:        gsize tmplen;
                   3223:        guint16 charset;
                   3224:        GData *attribs;
                   3225:        const char *start, *end, *last;
                   3226:        int oscar_id = 0;
                   3227: 
                   3228:        msg = g_string_new("<HTML><BODY>");
                   3229:        data = g_string_new("<BINARY>");
                   3230:        last = message;
                   3231: 
                   3232:        /* for each valid IMG tag... */
                   3233:        while (last && *last && purple_markup_find_tag("img", last, &start, &end, &attribs))
                   3234:        {
                   3235:                PurpleStoredImage *image = NULL;
                   3236:                const char *id;
                   3237: 
                   3238:                if (start - last) {
                   3239:                        g_string_append_len(msg, last, start - last);
                   3240:                }
                   3241: 
                   3242:                id = g_datalist_get_data(&attribs, "id");
                   3243: 
                   3244:                /* ... if it refers to a valid purple image ... */
                   3245:                if (id && (image = purple_imgstore_find_by_id(atoi(id)))) {
                   3246:                        /* ... append the message from start to the tag ... */
                   3247:                        unsigned long size = purple_imgstore_get_size(image);
                   3248:                        const char *filename = purple_imgstore_get_filename(image);
                   3249:                        gconstpointer imgdata = purple_imgstore_get_data(image);
                   3250: 
                   3251:                        oscar_id++;
                   3252: 
                   3253:                        /* ... insert a new img tag with the oscar id ... */
                   3254:                        if (filename)
                   3255:                                g_string_append_printf(msg,
                   3256:                                        "<IMG SRC=\"%s\" ID=\"%d\" DATASIZE=\"%lu\">",
                   3257:                                        filename, oscar_id, size);
                   3258:                        else
                   3259:                                g_string_append_printf(msg,
                   3260:                                        "<IMG ID=\"%d\" DATASIZE=\"%lu\">",
                   3261:                                        oscar_id, size);
                   3262: 
                   3263:                        /* ... and append the data to the binary section ... */
                   3264:                        g_string_append_printf(data, "<DATA ID=\"%d\" SIZE=\"%lu\">",
                   3265:                                oscar_id, size);
                   3266:                        g_string_append_len(data, imgdata, size);
                   3267:                        g_string_append(data, "</DATA>");
                   3268:                }
                   3269:                        /* If the tag is invalid, skip it, thus no else here */
                   3270: 
                   3271:                g_datalist_clear(&attribs);
                   3272: 
                   3273:                /* continue from the end of the tag */
                   3274:                last = end + 1;
                   3275:        }
                   3276: 
                   3277:        /* append any remaining message data */
                   3278:        if (last && *last)
                   3279:                g_string_append(msg, last);
                   3280: 
                   3281:        g_string_append(msg, "</BODY></HTML>");
                   3282: 
                   3283:        /* Convert the message to a good encoding */
                   3284:        tmp = oscar_encode_im(msg->str, &tmplen, &charset, NULL);
                   3285:        g_string_free(msg, TRUE);
                   3286:        msg = g_string_new_len(tmp, tmplen);
                   3287:        g_free(tmp);
                   3288: 
                   3289:        /* Append any binary data that we may have */
                   3290:        if (oscar_id) {
                   3291:                msg = g_string_append_len(msg, data->str, data->len);
                   3292:                msg = g_string_append(msg, "</BINARY>");
                   3293:        }
                   3294:        g_string_free(data, TRUE);
                   3295: 
                   3296:        purple_debug_info("oscar", "sending direct IM %s using charset %i", msg->str, charset);
                   3297: 
                   3298:        peer_odc_send_im(conn, msg->str, msg->len, charset,
                   3299:                        imflags & PURPLE_MESSAGE_AUTO_RESP);
                   3300:        g_string_free(msg, TRUE);
                   3301: }
                   3302: 
                   3303: int
                   3304: oscar_send_im(PurpleConnection *gc, const char *name, const char *message, PurpleMessageFlags imflags)
                   3305: {
                   3306:        OscarData *od;
                   3307:        PurpleAccount *account;
                   3308:        PeerConnection *conn;
                   3309:        int ret;
                   3310:        char *tmp1, *tmp2;
                   3311:        gboolean is_sms, is_html;
                   3312: 
                   3313:        od = purple_connection_get_protocol_data(gc);
                   3314:        account = purple_connection_get_account(gc);
                   3315:        ret = 0;
                   3316: 
                   3317:        is_sms = oscar_util_valid_name_sms(name);
                   3318: 
                   3319:        if (od->icq && is_sms) {
                   3320:                /*
                   3321:                 * We're sending to a phone number and this is ICQ,
                   3322:                 * so send the message as an SMS using aim_icq_sendsms()
                   3323:                 */
                   3324:                int ret;
                   3325:                purple_debug_info("oscar", "Sending SMS to %s.\n", name);
                   3326:                ret = aim_icq_sendsms(od, name, message, purple_account_get_username(account));
                   3327:                return (ret >= 0 ? 1 : ret);
                   3328:        }
                   3329: 
                   3330:        if (imflags & PURPLE_MESSAGE_AUTO_RESP)
                   3331:                tmp1 = oscar_util_format_string(message, name);
                   3332:        else
                   3333:                tmp1 = g_strdup(message);
                   3334: 
                   3335:        conn = peer_connection_find_by_type(od, name, OSCAR_CAPABILITY_DIRECTIM);
                   3336:        if ((conn != NULL) && (conn->ready))
                   3337:        {
                   3338:                /* If we're directly connected, send a direct IM */
                   3339:                purple_debug_info("oscar", "Sending direct IM with flags %i\n", imflags);
                   3340:                purple_odc_send_im(conn, tmp1, imflags);
                   3341:        } else {
                   3342:                struct buddyinfo *bi;
                   3343:                struct aim_sendimext_args args;
                   3344:                PurpleConversation *conv;
                   3345:                PurpleStoredImage *img;
                   3346:                PurpleBuddy *buddy;
                   3347: 
                   3348:                conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, name, account);
                   3349: 
                   3350:                if (strstr(tmp1, "<IMG "))
                   3351:                        purple_conversation_write(conv, "",
                   3352:                                                _("Your IM Image was not sent. "
                   3353:                                                "You must be Direct Connected to send IM Images."),
                   3354:                                                PURPLE_MESSAGE_ERROR, time(NULL));
                   3355: 
                   3356:                buddy = purple_find_buddy(account, name);
                   3357: 
                   3358:                bi = g_hash_table_lookup(od->buddyinfo, purple_normalize(account, name));
                   3359:                if (!bi) {
                   3360:                        bi = g_new0(struct buddyinfo, 1);
                   3361:                        g_hash_table_insert(od->buddyinfo, g_strdup(purple_normalize(account, name)), bi);
                   3362:                }
                   3363: 
                   3364:                args.flags = 0;
                   3365: 
                   3366:                if (!is_sms && (!buddy || !PURPLE_BUDDY_IS_ONLINE(buddy)))
                   3367:                        args.flags |= AIM_IMFLAGS_OFFLINE;
                   3368: 
                   3369:                if (od->icq) {
                   3370:                        args.features = features_icq;
                   3371:                        args.featureslen = sizeof(features_icq);
                   3372:                } else {
                   3373:                        args.features = features_aim;
                   3374:                        args.featureslen = sizeof(features_aim);
                   3375: 
                   3376:                        if (imflags & PURPLE_MESSAGE_AUTO_RESP)
                   3377:                                args.flags |= AIM_IMFLAGS_AWAY;
                   3378:                }
                   3379: 
                   3380:                if (bi->ico_need) {
                   3381:                        purple_debug_info("oscar",
                   3382:                                           "Sending buddy icon request with message\n");
                   3383:                        args.flags |= AIM_IMFLAGS_BUDDYREQ;
                   3384:                        bi->ico_need = FALSE;
                   3385:                }
                   3386: 
                   3387:                img = purple_buddy_icons_find_account_icon(account);
                   3388:                if (img) {
                   3389:                        gconstpointer data = purple_imgstore_get_data(img);
                   3390:                        args.iconlen = purple_imgstore_get_size(img);
                   3391:                        args.iconsum = aimutil_iconsum(data, args.iconlen);
                   3392:                        args.iconstamp = purple_buddy_icons_get_account_icon_timestamp(account);
                   3393: 
                   3394:                        if ((args.iconlen != bi->ico_me_len) || (args.iconsum != bi->ico_me_csum) || (args.iconstamp != bi->ico_me_time)) {
                   3395:                                bi->ico_informed = FALSE;
                   3396:                                bi->ico_sent     = FALSE;
                   3397:                        }
                   3398: 
                   3399:                        /*
                   3400:                         * TODO:
                   3401:                         * For some reason sending our icon to people only works
                   3402:                         * when we're the ones who initiated the conversation.  If
                   3403:                         * the other person sends the first IM then they never get
                   3404:                         * the icon.  We should fix that.
                   3405:                         */
                   3406:                        if (!bi->ico_informed) {
                   3407:                                purple_debug_info("oscar",
                   3408:                                                   "Claiming to have a buddy icon\n");
                   3409:                                args.flags |= AIM_IMFLAGS_HASICON;
                   3410:                                bi->ico_me_len = args.iconlen;
                   3411:                                bi->ico_me_csum = args.iconsum;
                   3412:                                bi->ico_me_time = args.iconstamp;
                   3413:                                bi->ico_informed = TRUE;
                   3414:                        }
                   3415: 
                   3416:                        purple_imgstore_unref(img);
                   3417:                }
                   3418: 
                   3419:                args.destbn = name;
                   3420: 
                   3421:                if (oscar_util_valid_name_sms(name)) {
                   3422:                        /* Messaging an SMS (mobile) user--strip HTML */
                   3423:                        tmp2 = purple_markup_strip_html(tmp1);
                   3424:                        is_html = FALSE;
                   3425:                } else {
                   3426:                        /* ICQ 6 wants its HTML wrapped in these tags. Oblige it. */
                   3427:                        tmp2 = g_strdup_printf("<HTML><BODY>%s</BODY></HTML>", tmp1);
                   3428:                        is_html = TRUE;
                   3429:                }
                   3430:                g_free(tmp1);
                   3431:                tmp1 = tmp2;
                   3432: 
                   3433:                args.msg = oscar_encode_im(tmp1, &args.msglen, &args.charset, NULL);
                   3434:                if (is_html && (args.msglen > MAXMSGLEN)) {
                   3435:                        /* If the length was too long, try stripping the HTML and then running it back through
                   3436:                        * purple_strdup_withhtml() and the encoding process. The result may be shorter. */
                   3437:                        g_free((char *)args.msg);
                   3438: 
                   3439:                        tmp2 = purple_markup_strip_html(tmp1);
                   3440:                        g_free(tmp1);
                   3441: 
                   3442:                        /* re-escape the entities */
                   3443:                        tmp1 = g_markup_escape_text(tmp2, -1);
                   3444:                        g_free(tmp2);
                   3445: 
                   3446:                        tmp2 = purple_strdup_withhtml(tmp1);
                   3447:                        g_free(tmp1);
                   3448:                        tmp1 = tmp2;
                   3449: 
                   3450:                        args.msg = oscar_encode_im(tmp1, &args.msglen, &args.charset, NULL);
                   3451:                        purple_debug_info("oscar", "Sending %s as %s because the original was too long.\n",
                   3452:                                                                  message, (char *)args.msg);
                   3453:                }
                   3454: 
                   3455:                purple_debug_info("oscar", "Sending IM, charset=0x%04hx, length=%" G_GSIZE_FORMAT "\n", args.charset, args.msglen);
                   3456:                ret = aim_im_sendch1_ext(od, &args);
                   3457:                g_free((char *)args.msg);
                   3458:        }
                   3459: 
                   3460:        g_free(tmp1);
                   3461: 
                   3462:        if (ret >= 0)
                   3463:                return 1;
                   3464: 
                   3465:        return ret;
                   3466: }
                   3467: 
                   3468: /*
                   3469:  * As of 26 June 2006, ICQ users can request AIM info from
                   3470:  * everyone, and can request ICQ info from ICQ users, and
                   3471:  * AIM users can only request AIM info.
                   3472:  */
                   3473: void oscar_get_info(PurpleConnection *gc, const char *name) {
                   3474:        OscarData *od = purple_connection_get_protocol_data(gc);
                   3475: 
                   3476:        if (od->icq && oscar_util_valid_name_icq(name))
                   3477:                aim_icq_getallinfo(od, name);
                   3478:        else
                   3479:                aim_locate_getinfoshort(od, name, 0x00000003);
                   3480: }
                   3481: 
                   3482: void oscar_set_idle(PurpleConnection *gc, int time) {
                   3483:        OscarData *od = purple_connection_get_protocol_data(gc);
                   3484:        aim_srv_setidle(od, time);
                   3485: }
                   3486: 
                   3487: void
                   3488: oscar_set_info(PurpleConnection *gc, const char *rawinfo)
                   3489: {
                   3490:        PurpleAccount *account;
                   3491:        PurpleStatus *status;
                   3492: 
                   3493:        account = purple_connection_get_account(gc);
                   3494:        status = purple_account_get_active_status(account);
                   3495:        oscar_set_info_and_status(account, TRUE, rawinfo, FALSE, status);
                   3496: }
                   3497: 
                   3498: static guint32
                   3499: oscar_get_extended_status(PurpleConnection *gc)
                   3500: {
                   3501:        PurpleAccount *account;
                   3502:        PurpleStatus *status;
                   3503:        const gchar *status_id;
                   3504:        guint32 data = 0x00000000;
                   3505: 
                   3506:        account = purple_connection_get_account(gc);
                   3507:        status = purple_account_get_active_status(account);
                   3508:        status_id = purple_status_get_id(status);
                   3509: 
                   3510:        data |= AIM_ICQ_STATE_HIDEIP;
                   3511:        if (purple_account_get_bool(account, "web_aware", OSCAR_DEFAULT_WEB_AWARE))
                   3512:                data |= AIM_ICQ_STATE_WEBAWARE;
                   3513: 
                   3514:        if (purple_strequal(status_id, OSCAR_STATUS_ID_AVAILABLE))
                   3515:                data |= AIM_ICQ_STATE_NORMAL;
                   3516:        else if (purple_strequal(status_id, OSCAR_STATUS_ID_AWAY))
                   3517:                data |= AIM_ICQ_STATE_AWAY;
                   3518:        else if (purple_strequal(status_id, OSCAR_STATUS_ID_DND))
                   3519:                data |= AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_DND | AIM_ICQ_STATE_BUSY;
                   3520:        else if (purple_strequal(status_id, OSCAR_STATUS_ID_NA))
                   3521:                data |= AIM_ICQ_STATE_OUT | AIM_ICQ_STATE_AWAY;
                   3522:        else if (purple_strequal(status_id, OSCAR_STATUS_ID_OCCUPIED))
                   3523:                data |= AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_BUSY;
                   3524:        else if (purple_strequal(status_id, OSCAR_STATUS_ID_FREE4CHAT))
                   3525:                data |= AIM_ICQ_STATE_CHAT;
                   3526:        else if (purple_strequal(status_id, OSCAR_STATUS_ID_INVISIBLE))
                   3527:                data |= AIM_ICQ_STATE_INVISIBLE;
                   3528:        else if (purple_strequal(status_id, OSCAR_STATUS_ID_EVIL))
                   3529:                data |= AIM_ICQ_STATE_EVIL;
                   3530:        else if (purple_strequal(status_id, OSCAR_STATUS_ID_DEPRESSION))
                   3531:                data |= AIM_ICQ_STATE_DEPRESSION;
                   3532:        else if (purple_strequal(status_id, OSCAR_STATUS_ID_ATWORK))
                   3533:                data |= AIM_ICQ_STATE_ATWORK;
                   3534:        else if (purple_strequal(status_id, OSCAR_STATUS_ID_ATHOME))
                   3535:                data |= AIM_ICQ_STATE_ATHOME;
                   3536:        else if (purple_strequal(status_id, OSCAR_STATUS_ID_LUNCH))
                   3537:                data |= AIM_ICQ_STATE_LUNCH;
                   3538:        else if (purple_strequal(status_id, OSCAR_STATUS_ID_CUSTOM))
                   3539:                data |= AIM_ICQ_STATE_OUT | AIM_ICQ_STATE_AWAY;
                   3540: 
                   3541:        return data;
                   3542: }
                   3543: 
                   3544: static void
                   3545: oscar_set_extended_status(PurpleConnection *gc)
                   3546: {
                   3547:        aim_srv_setextrainfo(purple_connection_get_protocol_data(gc), TRUE, oscar_get_extended_status(gc), FALSE, NULL, NULL);
                   3548: }
                   3549: 
                   3550: static void
                   3551: oscar_set_info_and_status(PurpleAccount *account, gboolean setinfo, const char *rawinfo,
                   3552:                                                  gboolean setstatus, PurpleStatus *status)
                   3553: {
                   3554:        PurpleConnection *gc = purple_account_get_connection(account);
                   3555:        OscarData *od = purple_connection_get_protocol_data(gc);
                   3556:        PurpleStatusType *status_type;
                   3557:        PurpleStatusPrimitive primitive;
                   3558: 
                   3559:        char *info_encoding = NULL;
                   3560:        char *info = NULL;
                   3561:        gsize infolen = 0;
                   3562: 
                   3563:        char *away_encoding = NULL;
                   3564:        char *away = NULL;
                   3565:        gsize awaylen = 0;
                   3566: 
                   3567:        char *status_text = NULL;
                   3568:        const char *itmsurl = NULL;
                   3569: 
                   3570:        status_type = purple_status_get_type(status);
                   3571:        primitive = purple_status_type_get_primitive(status_type);
                   3572: 
                   3573:        if (!setinfo)
                   3574:        {
                   3575:                /* Do nothing! */
                   3576:        }
                   3577:        else if (od->rights.maxsiglen == 0)
                   3578:        {
                   3579:                purple_notify_warning(gc, NULL, _("Unable to set AIM profile."),
                   3580:                                                        _("You have probably requested to set your "
                   3581:                                                          "profile before the login procedure completed.  "
                   3582:                                                          "Your profile remains unset; try setting it "
                   3583:                                                          "again when you are fully connected."));
                   3584:        }
                   3585:        else if (rawinfo != NULL)
                   3586:        {
                   3587:                char *htmlinfo = purple_strdup_withhtml(rawinfo);
                   3588:                info = oscar_encode_im(htmlinfo, &infolen, NULL, &info_encoding);
                   3589:                g_free(htmlinfo);
                   3590: 
                   3591:                if (infolen > od->rights.maxsiglen)
                   3592:                {
                   3593:                        gchar *errstr;
                   3594:                        errstr = g_strdup_printf(dngettext(PACKAGE, "The maximum profile length of %d byte "
                   3595:                                                                         "has been exceeded.  It has been truncated for you.",
                   3596:                                                                         "The maximum profile length of %d bytes "
                   3597:                                                                         "has been exceeded.  It has been truncated for you.",
                   3598:                                                                         od->rights.maxsiglen), od->rights.maxsiglen);
                   3599:                        purple_notify_warning(gc, NULL, _("Profile too long."), errstr);
                   3600:                        g_free(errstr);
                   3601:                }
                   3602:        }
                   3603: 
                   3604:        if (setstatus)
                   3605:        {
                   3606:                const char *status_html;
                   3607: 
                   3608:                status_html = purple_status_get_attr_string(status, "message");
                   3609: 
                   3610:                if (status_html == NULL || primitive == PURPLE_STATUS_AVAILABLE || primitive == PURPLE_STATUS_INVISIBLE)
                   3611:                {
                   3612:                        /* This is needed for us to un-set any previous away message. */
                   3613:                        away = g_strdup("");
                   3614:                }
                   3615:                else
                   3616:                {
                   3617:                        gchar *linkified;
                   3618: 
                   3619:                        /* We do this for icq too so that they work for old third party clients */
                   3620:                        linkified = purple_markup_linkify(status_html);
                   3621:                        away = oscar_encode_im(linkified, &awaylen, NULL, &away_encoding);
                   3622:                        g_free(linkified);
                   3623: 
                   3624:                        if (awaylen > od->rights.maxawaymsglen)
                   3625:                        {
                   3626:                                gchar *errstr;
                   3627: 
                   3628:                                errstr = g_strdup_printf(dngettext(PACKAGE, "The maximum away message length of %d byte "
                   3629:                                                                                 "has been exceeded.  It has been truncated for you.",
                   3630:                                                                                 "The maximum away message length of %d bytes "
                   3631:                                                                                 "has been exceeded.  It has been truncated for you.",
                   3632:                                                                                 od->rights.maxawaymsglen), od->rights.maxawaymsglen);
                   3633:                                purple_notify_warning(gc, NULL, _("Away message too long."), errstr);
                   3634:                                g_free(errstr);
                   3635:                        }
                   3636:                }
                   3637:        }
                   3638: 
                   3639:        aim_locate_setprofile(od,
                   3640:                        info_encoding, info, MIN(infolen, od->rights.maxsiglen),
                   3641:                        away_encoding, away, MIN(awaylen, od->rights.maxawaymsglen));
                   3642:        g_free(info);
                   3643:        g_free(away);
                   3644: 
                   3645:        if (setstatus)
                   3646:        {
                   3647:                const char *status_html;
                   3648: 
                   3649:                status_html = purple_status_get_attr_string(status, "message");
                   3650:                if (status_html != NULL)
                   3651:                {
                   3652:                        status_text = purple_markup_strip_html(status_html);
                   3653:                        /* If the status_text is longer than 251 characters then truncate it */
                   3654:                        if (strlen(status_text) > MAXAVAILMSGLEN)
                   3655:                        {
                   3656:                                char *tmp = g_utf8_find_prev_char(status_text, &status_text[MAXAVAILMSGLEN - 2]);
                   3657:                                strcpy(tmp, "...");
                   3658:                        }
                   3659:                }
                   3660: 
                   3661:                itmsurl = purple_status_get_attr_string(status, "itmsurl");
                   3662: 
                   3663:                aim_srv_setextrainfo(od, TRUE, oscar_get_extended_status(gc), TRUE, status_text, itmsurl);
                   3664:                g_free(status_text);
                   3665:        }
                   3666: }
                   3667: 
                   3668: static void
                   3669: oscar_set_icq_permdeny(PurpleAccount *account)
                   3670: {
                   3671:        PurpleConnection *gc = purple_account_get_connection(account);
                   3672:        OscarData *od = purple_connection_get_protocol_data(gc);
                   3673:        gboolean invisible = purple_account_is_status_active(account, OSCAR_STATUS_ID_INVISIBLE);
                   3674: 
                   3675:        /*
                   3676:         * For ICQ the permit/deny setting controls who can see you
                   3677:         * online. Mimicking the official client's behavior, we use PURPLE_PRIVACY_ALLOW_USERS
                   3678:         * when our status is "invisible" and PURPLE_PRIVACY_DENY_USERS otherwise.
                   3679:         * In the former case, we are visible only to buddies on our "permanently visible" list.
                   3680:         * In the latter, we are invisible only to buddies on our "permanently invisible" list.
                   3681:         */
                   3682:        aim_ssi_setpermdeny(od, invisible ? PURPLE_PRIVACY_ALLOW_USERS : PURPLE_PRIVACY_DENY_USERS);
                   3683: }
                   3684: 
                   3685: void
                   3686: oscar_set_status(PurpleAccount *account, PurpleStatus *status)
                   3687: {
                   3688:        PurpleConnection *pc;
                   3689:        OscarData *od;
                   3690: 
                   3691:        purple_debug_info("oscar", "Set status to %s\n", purple_status_get_name(status));
                   3692: 
                   3693:        /* Either setting a new status active or setting a status inactive.
                   3694:         * (Only possible for independent status (i.e. X-Status moods.) */
                   3695:        if (!purple_status_is_active(status) && !purple_status_is_independent(status))
                   3696:                return;
                   3697: 
                   3698:        if (!purple_account_is_connected(account))
                   3699:                return;
                   3700: 
                   3701:        pc = purple_account_get_connection(account);
                   3702:        od = purple_connection_get_protocol_data(pc);
                   3703: 
                   3704:        /* There's no need to do the stuff below for mood updates. */
                   3705:        if (purple_status_type_get_primitive(purple_status_get_type(status)) == PURPLE_STATUS_MOOD) {
                   3706:                aim_locate_setcaps(od, purple_caps);
                   3707:                return;
                   3708:        }
                   3709: 
                   3710:        if (od->icq) {
                   3711:                /* Set visibility */
                   3712:                oscar_set_icq_permdeny(account);
                   3713:        }
                   3714: 
                   3715:        /* Set the AIM-style away message for both AIM and ICQ accounts */
                   3716:        oscar_set_info_and_status(account, FALSE, NULL, TRUE, status);
                   3717: }
                   3718: 
                   3719: void
                   3720: oscar_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group, const char *msg)
                   3721: {
                   3722:        OscarData *od;
                   3723:        PurpleAccount *account;
                   3724:        const char *bname, *gname;
                   3725: 
                   3726:        od = purple_connection_get_protocol_data(gc);
                   3727:        account = purple_connection_get_account(gc);
                   3728:        bname = purple_buddy_get_name(buddy);
                   3729:        gname = purple_group_get_name(group);
                   3730: 
                   3731:        if (!oscar_util_valid_name(bname)) {
                   3732:                gchar *buf;
                   3733:                buf = g_strdup_printf(_("Unable to add the buddy %s because the username is invalid.  Usernames must be a valid email address, or start with a letter and contain only letters, numbers and spaces, or contain only numbers."), bname);
                   3734:                if (!purple_conv_present_error(bname, account, buf))
                   3735:                        purple_notify_error(gc, NULL, _("Unable to Add"), buf);
                   3736:                g_free(buf);
                   3737: 
                   3738:                /* Remove from local list */
                   3739:                purple_blist_remove_buddy(buddy);
                   3740: 
                   3741:                return;
                   3742:        }
                   3743: 
                   3744:        if (od->ssi.received_data) {
                   3745:                if (!aim_ssi_itemlist_finditem(od->ssi.local, gname, bname, AIM_SSI_TYPE_BUDDY)) {
                   3746:                        purple_debug_info("oscar",
                   3747:                                           "ssi: adding buddy %s to group %s\n", bname, gname);
                   3748:                        aim_ssi_addbuddy(od, bname, gname, NULL, purple_buddy_get_alias_only(buddy), NULL, NULL, 0);
                   3749: 
                   3750:                        /* Mobile users should always be online */
                   3751:                        if (bname[0] == '+') {
                   3752:                                purple_prpl_got_user_status(account, bname,
                   3753:                                                OSCAR_STATUS_ID_AVAILABLE, NULL);
                   3754:                                purple_prpl_got_user_status(account, bname,
                   3755:                                                OSCAR_STATUS_ID_MOBILE, NULL);
                   3756:                        }
                   3757:                } else if (aim_ssi_waitingforauth(od->ssi.local,
                   3758:                                                  aim_ssi_itemlist_findparentname(od->ssi.local, bname),
                   3759:                                                  bname)) {
                   3760:                        /* Not authorized -- Re-request authorization */
                   3761:                        oscar_auth_sendrequest(gc, bname, msg);
                   3762:                }
                   3763:        }
                   3764: 
                   3765:        /* XXX - Should this be done from AIM accounts, as well? */
                   3766:        if (od->icq)
                   3767:                aim_icq_getalias(od, bname, FALSE, NULL);
                   3768: }
                   3769: 
                   3770: void oscar_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group) {
                   3771:        OscarData *od = purple_connection_get_protocol_data(gc);
                   3772: 
                   3773:        if (od->ssi.received_data) {
                   3774:                const char *gname = purple_group_get_name(group);
                   3775:                const char *bname = purple_buddy_get_name(buddy);
                   3776:                purple_debug_info("oscar",
                   3777:                                   "ssi: deleting buddy %s from group %s\n", bname, gname);
                   3778:                aim_ssi_delbuddy(od, bname, gname);
                   3779:        }
                   3780: }
                   3781: 
                   3782: void oscar_move_buddy(PurpleConnection *gc, const char *name, const char *old_group, const char *new_group) {
                   3783:        OscarData *od = purple_connection_get_protocol_data(gc);
                   3784: 
                   3785:        if (od->ssi.received_data && !purple_strequal(old_group, new_group)) {
                   3786:                purple_debug_info("oscar",
                   3787:                                   "ssi: moving buddy %s from group %s to group %s\n", name, old_group, new_group);
                   3788:                aim_ssi_movebuddy(od, old_group, new_group, name);
                   3789:        }
                   3790: }
                   3791: 
                   3792: void oscar_alias_buddy(PurpleConnection *gc, const char *name, const char *alias) {
                   3793:        OscarData *od = purple_connection_get_protocol_data(gc);
                   3794: 
                   3795:        if (od->ssi.received_data) {
                   3796:                char *gname = aim_ssi_itemlist_findparentname(od->ssi.local, name);
                   3797:                if (gname) {
                   3798:                        purple_debug_info("oscar",
                   3799:                                           "ssi: changing the alias for buddy %s to %s\n", name, alias ? alias : "(none)");
                   3800:                        aim_ssi_aliasbuddy(od, gname, name, alias);
                   3801:                }
                   3802:        }
                   3803: }
                   3804: 
                   3805: /*
                   3806:  * FYI, the OSCAR SSI code removes empty groups automatically.
                   3807:  */
                   3808: void oscar_rename_group(PurpleConnection *gc, const char *old_name, PurpleGroup *group, GList *moved_buddies) {
                   3809:        OscarData *od = purple_connection_get_protocol_data(gc);
                   3810: 
                   3811:        if (od->ssi.received_data) {
                   3812:                const char *gname = purple_group_get_name(group);
                   3813:                if (aim_ssi_itemlist_finditem(od->ssi.local, gname, NULL, AIM_SSI_TYPE_GROUP)) {
                   3814:                        GList *cur, *groups = NULL;
                   3815:                        PurpleAccount *account = purple_connection_get_account(gc);
                   3816: 
                   3817:                        /* Make a list of what the groups each buddy is in */
                   3818:                        for (cur = moved_buddies; cur != NULL; cur = cur->next) {
                   3819:                                PurpleBlistNode *node = cur->data;
                   3820:                                /* node is PurpleBuddy, parent is a PurpleContact.
                   3821:                                 * We must go two levels up to get the Group */
                   3822:                                groups = g_list_append(groups,
                   3823:                                                purple_buddy_get_group((PurpleBuddy*)node));
                   3824:                        }
                   3825: 
                   3826:                        purple_account_remove_buddies(account, moved_buddies, groups);
                   3827:                        purple_account_add_buddies(account, moved_buddies);
                   3828:                        g_list_free(groups);
                   3829:                        purple_debug_info("oscar",
                   3830:                                           "ssi: moved all buddies from group %s to %s\n", old_name, gname);
                   3831:                } else {
                   3832:                        aim_ssi_rename_group(od, old_name, gname);
                   3833:                        purple_debug_info("oscar",
                   3834:                                           "ssi: renamed group %s to %s\n", old_name, gname);
                   3835:                }
                   3836:        }
                   3837: }
                   3838: 
                   3839: void oscar_remove_group(PurpleConnection *gc, PurpleGroup *group)
                   3840: {
                   3841:        aim_ssi_delgroup(purple_connection_get_protocol_data(gc), purple_group_get_name(group));
                   3842: }
                   3843: 
                   3844: static gboolean purple_ssi_rerequestdata(gpointer data) {
                   3845:        OscarData *od = data;
                   3846: 
                   3847:        aim_ssi_reqdata(od);
                   3848: 
                   3849:        return TRUE;
                   3850: }
                   3851: 
                   3852: static int purple_ssi_parseerr(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
                   3853:        PurpleConnection *gc = od->gc;
                   3854:        va_list ap;
                   3855:        guint16 reason;
                   3856: 
                   3857:        va_start(ap, fr);
                   3858:        reason = (guint16)va_arg(ap, unsigned int);
                   3859:        va_end(ap);
                   3860: 
                   3861:        purple_debug_error("oscar", "ssi: SNAC error %hu\n", reason);
                   3862: 
                   3863:        if (reason == 0x0005) {
                   3864:                if (od->getblisttimer > 0)
                   3865:                        purple_timeout_remove(od->getblisttimer);
                   3866:                else
                   3867:                        /* We only show this error the first time it happens */
                   3868:                        purple_notify_error(gc, NULL,
                   3869:                                        _("Unable to Retrieve Buddy List"),
                   3870:                                        _("The AIM servers were temporarily unable to send "
                   3871:                                        "your buddy list.  Your buddy list is not lost, and "
                   3872:                                        "will probably become available in a few minutes."));
                   3873:                od->getblisttimer = purple_timeout_add_seconds(30, purple_ssi_rerequestdata, od);
                   3874:                return 1;
                   3875:        }
                   3876: 
                   3877:        return 1;
                   3878: }
                   3879: 
                   3880: static int purple_ssi_parserights(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
                   3881:        int i;
                   3882:        va_list ap;
                   3883:        int numtypes;
                   3884:        guint16 *maxitems;
                   3885:        GString *msg;
                   3886: 
                   3887:        va_start(ap, fr);
                   3888:        numtypes = va_arg(ap, int);
                   3889:        maxitems = va_arg(ap, guint16 *);
                   3890:        va_end(ap);
                   3891: 
                   3892:        msg = g_string_new("ssi rights:");
                   3893:        for (i=0; i<numtypes; i++)
                   3894:                g_string_append_printf(msg, " max type 0x%04x=%hd,", i, maxitems[i]);
                   3895:        g_string_append(msg, "\n");
                   3896:        purple_debug_misc("oscar", "%s", msg->str);
                   3897:        g_string_free(msg, TRUE);
                   3898: 
                   3899:        if (numtypes >= 0)
                   3900:                od->rights.maxbuddies = maxitems[0];
                   3901:        if (numtypes >= 1)
                   3902:                od->rights.maxgroups = maxitems[1];
                   3903:        if (numtypes >= 2)
                   3904:                od->rights.maxpermits = maxitems[2];
                   3905:        if (numtypes >= 3)
                   3906:                od->rights.maxdenies = maxitems[3];
                   3907: 
                   3908:        return 1;
                   3909: }
                   3910: 
                   3911: static int purple_ssi_parselist(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...)
                   3912: {
                   3913:        PurpleConnection *gc;
                   3914:        PurpleAccount *account;
                   3915:        PurpleGroup *g;
                   3916:        PurpleBuddy *b;
                   3917:        GSList *cur, *next, *buddies;
                   3918:        struct aim_ssi_item *curitem;
                   3919:        guint32 tmp;
                   3920:        PurpleStoredImage *img;
                   3921:        va_list ap;
                   3922:        guint16 deny_entry_type = aim_ssi_getdenyentrytype(od);
                   3923: 
                   3924:        gc = od->gc;
                   3925:        od = purple_connection_get_protocol_data(gc);
                   3926:        account = purple_connection_get_account(gc);
                   3927: 
                   3928:        va_start(ap, fr);
                   3929:        va_arg(ap, int); /* guint16 fmtver */
                   3930:        va_arg(ap, int); /* guint16 numitems */
                   3931:        va_arg(ap, guint32); /* timestamp */
                   3932:        va_end(ap);
                   3933: 
                   3934:        /* Don't attempt to re-request our buddy list later */
                   3935:        if (od->getblisttimer != 0) {
                   3936:                purple_timeout_remove(od->getblisttimer);
                   3937:                od->getblisttimer = 0;
                   3938:        }
                   3939: 
                   3940:        purple_debug_info("oscar", "ssi: syncing local list and server list\n");
                   3941: 
                   3942:        /* Clean the buddy list */
                   3943:        aim_ssi_cleanlist(od);
                   3944: 
                   3945:        /*** Begin code for pruning buddies from local list if they're not in server list ***/
                   3946: 
                   3947:        /* Buddies */
                   3948:        cur = NULL;
                   3949:        for (buddies = purple_find_buddies(account, NULL);
                   3950:                        buddies;
                   3951:                        buddies = g_slist_delete_link(buddies, buddies))
                   3952:        {
                   3953:                PurpleGroup *g;
                   3954:                const char *gname;
                   3955:                const char *bname;
                   3956: 
                   3957:                b = buddies->data;
                   3958:                g = purple_buddy_get_group(b);
                   3959:                gname = purple_group_get_name(g);
                   3960:                bname = purple_buddy_get_name(b);
                   3961: 
                   3962:                if (aim_ssi_itemlist_exists(od->ssi.local, bname)) {
                   3963:                        /* If the buddy is an ICQ user then load his nickname */
                   3964:                        const char *servernick = purple_blist_node_get_string((PurpleBlistNode*)b, "servernick");
                   3965:                        char *alias;
                   3966:                        const char *balias;
                   3967:                        if (servernick)
                   3968:                                serv_got_alias(gc, bname, servernick);
                   3969: 
                   3970:                        /* Store local alias on server */
                   3971:                        alias = aim_ssi_getalias(od->ssi.local, gname, bname);
                   3972:                        balias = purple_buddy_get_local_buddy_alias(b);
                   3973:                        if (!alias && balias && *balias)
                   3974:                                aim_ssi_aliasbuddy(od, gname, bname, balias);
                   3975:                        g_free(alias);
                   3976:                } else {
                   3977:                        purple_debug_info("oscar",
                   3978:                                        "ssi: removing buddy %s from local list\n", bname);
                   3979:                        /* Queue the buddy for removal from the local list */
                   3980:                        cur = g_slist_prepend(cur, b);
                   3981:                }
                   3982:        }
                   3983:        while (cur != NULL) {
                   3984:                purple_blist_remove_buddy(cur->data);
                   3985:                cur = g_slist_delete_link(cur, cur);
                   3986:        }
                   3987: 
                   3988:        /* Permit list (ICQ doesn't have one) */
                   3989:        if (!od->icq) {
                   3990:                next = account->permit;
                   3991:                while (next != NULL) {
                   3992:                        cur = next;
                   3993:                        next = next->next;
                   3994:                        if (!aim_ssi_itemlist_finditem(od->ssi.local, NULL, cur->data, AIM_SSI_TYPE_PERMIT)) {
                   3995:                                purple_debug_info("oscar",
                   3996:                                                "ssi: removing permit %s from local list\n", (const char *)cur->data);
                   3997:                                purple_privacy_permit_remove(account, cur->data, TRUE);
                   3998:                        }
                   3999:                }
                   4000:        }
                   4001: 
                   4002:        /* Deny list */
                   4003:        next = account->deny;
                   4004:        while (next != NULL) {
                   4005:                cur = next;
                   4006:                next = next->next;
                   4007:                if (!aim_ssi_itemlist_finditem(od->ssi.local, NULL, cur->data, deny_entry_type)) {
                   4008:                        purple_debug_info("oscar",
                   4009:                                        "ssi: removing deny %s from local list\n", (const char *)cur->data);
                   4010:                        purple_privacy_deny_remove(account, cur->data, TRUE);
                   4011:                }
                   4012:        }
                   4013: 
                   4014:        /* Presence settings (idle time visibility) */
                   4015:        tmp = aim_ssi_getpresence(od->ssi.local);
                   4016:        if (tmp != 0xFFFFFFFF) {
                   4017:                const char *idle_reporting_pref;
                   4018:                gboolean report_idle;
                   4019: 
                   4020:                idle_reporting_pref = purple_prefs_get_string("/purple/away/idle_reporting");
                   4021:                report_idle = !purple_strequal(idle_reporting_pref, "none");
                   4022: 
                   4023:                if (report_idle)
                   4024:                        aim_ssi_setpresence(od, tmp | AIM_SSI_PRESENCE_FLAG_SHOWIDLE);
                   4025:                else
                   4026:                        aim_ssi_setpresence(od, tmp & ~AIM_SSI_PRESENCE_FLAG_SHOWIDLE);
                   4027:        }
                   4028: 
                   4029:        /*** End code for pruning buddies from local list ***/
                   4030: 
                   4031:        /*** Begin code for adding from server list to local list ***/
                   4032: 
                   4033:        for (curitem=od->ssi.local; curitem; curitem=curitem->next) {
                   4034:                if (curitem->name && !g_utf8_validate(curitem->name, -1, NULL)) {
                   4035:                        /* Got node with invalid UTF-8 in the name.  Skip it. */
                   4036:                        purple_debug_warning("oscar", "ssi: server list contains item of "
                   4037:                                        "type 0x%04hx with a non-utf8 name\n", curitem->type);
                   4038:                        continue;
                   4039:                }
                   4040: 
                   4041:                switch (curitem->type) {
                   4042:                        case AIM_SSI_TYPE_BUDDY: { /* Buddy */
                   4043:                                if (curitem->name) {
                   4044:                                        struct aim_ssi_item *groupitem;
                   4045:                                        char *gname, *gname_utf8, *alias, *alias_utf8;
                   4046: 
                   4047:                                        groupitem = aim_ssi_itemlist_find(od->ssi.local, curitem->gid, 0x0000);
                   4048:                                        gname = groupitem ? groupitem->name : NULL;
                   4049:                                        gname_utf8 = oscar_utf8_try_convert(account, od, gname);
                   4050: 
                   4051:                                        g = purple_find_group(gname_utf8 ? gname_utf8 : _("Orphans"));
                   4052:                                        if (g == NULL) {
                   4053:                                                g = purple_group_new(gname_utf8 ? gname_utf8 : _("Orphans"));
                   4054:                                                purple_blist_add_group(g, NULL);
                   4055:                                        }
                   4056: 
                   4057:                                        alias = aim_ssi_getalias(od->ssi.local, gname, curitem->name);
                   4058:                                        alias_utf8 = oscar_utf8_try_convert(account, od, alias);
                   4059: 
                   4060:                                        b = purple_find_buddy_in_group(account, curitem->name, g);
                   4061:                                        if (b) {
                   4062:                                                /* Get server stored alias */
                   4063:                                                purple_blist_alias_buddy(b, alias_utf8);
                   4064:                                        } else {
                   4065:                                                b = purple_buddy_new(account, curitem->name, alias_utf8);
                   4066: 
                   4067:                                                purple_debug_info("oscar",
                   4068:                                                                   "ssi: adding buddy %s to group %s to local list\n", curitem->name, gname);
                   4069:                                                purple_blist_add_buddy(b, NULL, g, NULL);
                   4070:                                        }
                   4071: 
                   4072:                                        /* Mobile users should always be online */
                   4073:                                        if (curitem->name[0] == '+') {
                   4074:                                                purple_prpl_got_user_status(account,
                   4075:                                                                purple_buddy_get_name(b),
                   4076:                                                                OSCAR_STATUS_ID_AVAILABLE, NULL);
                   4077:                                                purple_prpl_got_user_status(account,
                   4078:                                                                purple_buddy_get_name(b),
                   4079:                                                                OSCAR_STATUS_ID_MOBILE, NULL);
                   4080:                                        }
                   4081: 
                   4082:                                        g_free(gname_utf8);
                   4083:                                        g_free(alias);
                   4084:                                        g_free(alias_utf8);
                   4085:                                }
                   4086:                        } break;
                   4087: 
                   4088:                        case AIM_SSI_TYPE_GROUP: { /* Group */
                   4089:                                if (curitem->name != NULL && purple_find_group(curitem->name) == NULL) {
                   4090:                                        g = purple_group_new(curitem->name);
                   4091:                                        purple_blist_add_group(g, NULL);
                   4092:                                }
                   4093:                        } break;
                   4094: 
                   4095:                        case AIM_SSI_TYPE_PERMIT: { /* Permit buddy (unless we're on ICQ) */
                   4096:                                if (!od->icq && curitem->name) {
                   4097:                                        for (cur = account->permit; (cur && oscar_util_name_compare(curitem->name, cur->data)); cur = cur->next);
                   4098:                                        if (!cur) {
                   4099:                                                purple_debug_info("oscar",
                   4100:                                                                   "ssi: adding permit buddy %s to local list\n", curitem->name);
                   4101:                                                purple_privacy_permit_add(account, curitem->name, TRUE);
                   4102:                                        }
                   4103:                                }
                   4104:                        } break;
                   4105: 
                   4106:                        case AIM_SSI_TYPE_ICQDENY:
                   4107:                        case AIM_SSI_TYPE_DENY: { /* Deny buddy */
                   4108:                                if (curitem->type == deny_entry_type && curitem->name) {
                   4109:                                        for (cur = account->deny; (cur && oscar_util_name_compare(curitem->name, cur->data)); cur = cur->next);
                   4110:                                        if (!cur) {
                   4111:                                                purple_debug_info("oscar",
                   4112:                                                                   "ssi: adding deny buddy %s to local list\n", curitem->name);
                   4113:                                                purple_privacy_deny_add(account, curitem->name, TRUE);
                   4114:                                        }
                   4115:                                }
                   4116:                        } break;
                   4117: 
                   4118:                        case AIM_SSI_TYPE_PDINFO: { /* Permit/deny setting */
                   4119:                                /*
                   4120:                                 * We don't inherit the permit/deny setting from the server
                   4121:                                 * for ICQ because, for ICQ, this setting controls who can
                   4122:                                 * see your online status when you are invisible.  Thus it is
                   4123:                                 * a part of your status and not really related to blocking.
                   4124:                                 */
                   4125:                                if (!od->icq && curitem->data) {
                   4126:                                        guint8 perm_deny = aim_ssi_getpermdeny(od->ssi.local);
                   4127:                                        if (perm_deny != 0 && perm_deny != account->perm_deny)
                   4128:                                        {
                   4129:                                                purple_debug_info("oscar",
                   4130:                                                                   "ssi: changing permdeny from %d to %hhu\n", account->perm_deny, perm_deny);
                   4131:                                                account->perm_deny = perm_deny;
                   4132:                                        }
                   4133:                                }
                   4134:                        } break;
                   4135: 
                   4136:                        case AIM_SSI_TYPE_PRESENCEPREFS: { /* Presence setting */
                   4137:                                /* We don't want to change Purple's setting because it applies to all accounts */
                   4138:                        } break;
                   4139:                } /* End of switch on curitem->type */
                   4140:        } /* End of for loop */
                   4141: 
                   4142:        /*** End code for adding from server list to local list ***/
                   4143: 
                   4144:        if (od->icq) {
                   4145:                oscar_set_icq_permdeny(account);
                   4146:        } else {
                   4147:                oscar_set_aim_permdeny(gc);
                   4148:        }
                   4149: 
                   4150:        /* Activate SSI */
                   4151:        /* Sending the enable causes other people to be able to see you, and you to see them */
                   4152:        /* Make sure your privacy setting/invisibility is set how you want it before this! */
                   4153:        purple_debug_info("oscar",
                   4154:                           "ssi: activating server-stored buddy list\n");
                   4155:        aim_ssi_enable(od);
                   4156: 
                   4157:        /*
                   4158:         * Make sure our server-stored icon is updated correctly in
                   4159:         * the event that the local user set a new icon while this
                   4160:         * account was offline.
                   4161:         */
                   4162:        img = purple_buddy_icons_find_account_icon(account);
                   4163:        oscar_set_icon(gc, img);
                   4164:        purple_imgstore_unref(img);
                   4165: 
                   4166:        /*
                   4167:         * If we've already received our bos rights then we're not waiting on
                   4168:         * anything else, so send the server clientready.
                   4169:         */
                   4170:        if (od->bos.have_rights) {
                   4171:                aim_srv_clientready(od, conn);
                   4172: 
                   4173:                /* Request offline messages for AIM and ICQ */
                   4174:                aim_im_reqofflinemsgs(od);
                   4175: 
                   4176:                purple_connection_set_state(gc, PURPLE_CONNECTED);
                   4177:        }
                   4178: 
                   4179:        return 1;
                   4180: }
                   4181: 
                   4182: static int purple_ssi_parseack(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
                   4183:        PurpleConnection *gc = od->gc;
                   4184:        va_list ap;
                   4185:        struct aim_ssi_tmp *retval;
                   4186: 
                   4187:        va_start(ap, fr);
                   4188:        retval = va_arg(ap, struct aim_ssi_tmp *);
                   4189:        va_end(ap);
                   4190: 
                   4191:        while (retval) {
                   4192:                purple_debug_misc("oscar",
                   4193:                                   "ssi: status is 0x%04hx for a 0x%04hx action with name %s\n", retval->ack,  retval->action, retval->item ? (retval->item->name ? retval->item->name : "no name") : "no item");
                   4194: 
                   4195:                if (retval->ack != 0xffff)
                   4196:                switch (retval->ack) {
                   4197:                        case 0x0000: { /* added successfully */
                   4198:                        } break;
                   4199: 
                   4200:                        case 0x000c: { /* you are over the limit, the cheat is to the limit, come on fhqwhgads */
                   4201:                                gchar *buf;
                   4202:                                buf = g_strdup_printf(_("Unable to add the buddy %s because you have too many buddies in your buddy list.  Please remove one and try again."), (retval->name ? retval->name : _("(no name)")));
                   4203:                                if ((retval->name != NULL) && !purple_conv_present_error(retval->name, purple_connection_get_account(gc), buf))
                   4204:                                        purple_notify_error(gc, NULL, _("Unable to Add"), buf);
                   4205:                                g_free(buf);
                   4206:                        } break;
                   4207: 
                   4208:                        case 0x000e: { /* buddy requires authorization */
                   4209:                                if ((retval->action == SNAC_SUBTYPE_FEEDBAG_ADD) && (retval->name))
                   4210:                                        oscar_auth_sendrequest(gc, retval->name, NULL);
                   4211:                        } break;
                   4212: 
                   4213:                        default: { /* La la la */
                   4214:                                gchar *buf;
                   4215:                                purple_debug_error("oscar", "ssi: Action 0x%04hx was unsuccessful with error 0x%04hx\n", retval->action, retval->ack);
                   4216:                                buf = g_strdup_printf(_("Unable to add the buddy %s for an unknown reason."),
                   4217:                                                (retval->name ? retval->name : _("(no name)")));
                   4218:                                if ((retval->name != NULL) && !purple_conv_present_error(retval->name, purple_connection_get_account(gc), buf))
                   4219:                                        purple_notify_error(gc, NULL, _("Unable to Add"), buf);
                   4220:                                g_free(buf);
                   4221:                        } break;
                   4222:                }
                   4223: 
                   4224:                retval = retval->next;
                   4225:        }
                   4226: 
                   4227:        return 1;
                   4228: }
                   4229: 
                   4230: static int
                   4231: purple_ssi_parseaddmod(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...)
                   4232: {
                   4233:        PurpleConnection *gc;
                   4234:        PurpleAccount *account;
                   4235:        char *gname, *gname_utf8, *alias, *alias_utf8;
                   4236:        PurpleBuddy *b;
                   4237:        PurpleGroup *g;
                   4238:        struct aim_ssi_item *ssi_item;
                   4239:        va_list ap;
                   4240:        guint16 snac_subtype, type;
                   4241:        const char *name;
                   4242: 
                   4243:        gc = od->gc;
                   4244:        account = purple_connection_get_account(gc);
                   4245: 
                   4246:        va_start(ap, fr);
                   4247:        snac_subtype = (guint16)va_arg(ap, int);
                   4248:        type = (guint16)va_arg(ap, int);
                   4249:        name = va_arg(ap, char *);
                   4250:        va_end(ap);
                   4251: 
                   4252:        if ((type != 0x0000) || (name == NULL))
                   4253:                return 1;
                   4254: 
                   4255:        gname = aim_ssi_itemlist_findparentname(od->ssi.local, name);
                   4256:        gname_utf8 = gname ? oscar_utf8_try_convert(account, od, gname) : NULL;
                   4257: 
                   4258:        alias = aim_ssi_getalias(od->ssi.local, gname, name);
                   4259:        alias_utf8 = oscar_utf8_try_convert(account, od, alias);
                   4260:        g_free(alias);
                   4261: 
                   4262:        b = purple_find_buddy(account, name);
                   4263:        if (b) {
                   4264:                /*
                   4265:                 * You're logged in somewhere else and you aliased one
                   4266:                 * of your buddies, so update our local buddy list with
                   4267:                 * the person's new alias.
                   4268:                 */
                   4269:                purple_blist_alias_buddy(b, alias_utf8);
                   4270:        } else if (snac_subtype == 0x0008) {
                   4271:                /*
                   4272:                 * You're logged in somewhere else and you added a buddy to
                   4273:                 * your server list, so add them to your local buddy list.
                   4274:                 */
                   4275:                b = purple_buddy_new(account, name, alias_utf8);
                   4276: 
                   4277:                if (!(g = purple_find_group(gname_utf8 ? gname_utf8 : _("Orphans")))) {
                   4278:                        g = purple_group_new(gname_utf8 ? gname_utf8 : _("Orphans"));
                   4279:                        purple_blist_add_group(g, NULL);
                   4280:                }
                   4281: 
                   4282:                purple_debug_info("oscar",
                   4283:                                   "ssi: adding buddy %s to group %s to local list\n", name, gname_utf8 ? gname_utf8 : _("Orphans"));
                   4284:                purple_blist_add_buddy(b, NULL, g, NULL);
                   4285: 
                   4286:                /* Mobile users should always be online */
                   4287:                if (name[0] == '+') {
                   4288:                        purple_prpl_got_user_status(account,
                   4289:                                        name, OSCAR_STATUS_ID_AVAILABLE, NULL);
                   4290:                        purple_prpl_got_user_status(account,
                   4291:                                        name, OSCAR_STATUS_ID_MOBILE, NULL);
                   4292:                }
                   4293: 
                   4294:        }
                   4295: 
                   4296:        ssi_item = aim_ssi_itemlist_finditem(od->ssi.local,
                   4297:                        gname, name, AIM_SSI_TYPE_BUDDY);
                   4298:        if (ssi_item == NULL)
                   4299:        {
                   4300:                purple_debug_error("oscar", "purple_ssi_parseaddmod: "
                   4301:                                "Could not find ssi item for oncoming buddy %s, "
                   4302:                                "group %s\n", name, gname);
                   4303:        }
                   4304: 
                   4305:        g_free(gname_utf8);
                   4306:        g_free(alias_utf8);
                   4307: 
                   4308:        return 1;
                   4309: }
                   4310: 
                   4311: static int purple_ssi_authgiven(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
                   4312:        PurpleConnection *gc = od->gc;
                   4313:        va_list ap;
                   4314:        char *bn;
                   4315:        gchar *dialog_msg, *nombre;
                   4316:        struct name_data *data;
                   4317:        PurpleBuddy *buddy;
                   4318: 
                   4319:        va_start(ap, fr);
                   4320:        bn = va_arg(ap, char *);
                   4321:        va_arg(ap, char *); /* msg */
                   4322:        va_end(ap);
                   4323: 
                   4324:        purple_debug_info("oscar",
                   4325:                           "ssi: %s has given you permission to add him to your buddy list\n", bn);
                   4326: 
                   4327:        buddy = purple_find_buddy(purple_connection_get_account(gc), bn);
                   4328:        if (buddy && (purple_buddy_get_alias_only(buddy)))
                   4329:                nombre = g_strdup_printf("%s (%s)", bn, purple_buddy_get_alias_only(buddy));
                   4330:        else
                   4331:                nombre = g_strdup(bn);
                   4332: 
                   4333:        dialog_msg = g_strdup_printf(_("The user %s has given you permission to add him or her to your buddy list.  Do you want to add this user?"), nombre);
                   4334:        g_free(nombre);
                   4335: 
                   4336:        data = g_new(struct name_data, 1);
                   4337:        data->gc = gc;
                   4338:        data->name = g_strdup(bn);
                   4339:        data->nick = (buddy ? g_strdup(purple_buddy_get_alias_only(buddy)) : NULL);
                   4340: 
                   4341:        purple_request_yes_no(gc, NULL, _("Authorization Given"), dialog_msg,
                   4342:                                                PURPLE_DEFAULT_ACTION_NONE,
                   4343:                                                purple_connection_get_account(gc), bn, NULL,
                   4344:                                                data,
                   4345:                                                G_CALLBACK(purple_icq_buddyadd),
                   4346:                                                G_CALLBACK(oscar_free_name_data));
                   4347:        g_free(dialog_msg);
                   4348: 
                   4349:        return 1;
                   4350: }
                   4351: 
                   4352: static int purple_ssi_authrequest(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...)
                   4353: {
                   4354:        va_list ap;
                   4355:        const char *bn;
                   4356:        char *msg;
                   4357: 
                   4358:        va_start(ap, fr);
                   4359:        bn = va_arg(ap, const char *);
                   4360:        msg = va_arg(ap, char *);
                   4361:        va_end(ap);
                   4362: 
                   4363:        purple_debug_info("oscar",
                   4364:                        "ssi: received authorization request from %s\n", bn);
                   4365: 
                   4366:        if (!msg) {
                   4367:                purple_debug_warning("oscar", "Received auth request from %s with "
                   4368:                                "empty message\n", bn);
                   4369:        } else if (!g_utf8_validate(msg, -1, NULL)) {
                   4370:                purple_debug_warning("oscar", "Received auth request from %s with "
                   4371:                                "invalid UTF-8 message\n", bn);
                   4372:                msg = NULL;
                   4373:        }
                   4374: 
                   4375:        aim_icq_getalias(od, bn, TRUE, msg);
                   4376:        return 1;
                   4377: }
                   4378: 
                   4379: static int purple_ssi_authreply(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
                   4380:        PurpleConnection *gc = od->gc;
                   4381:        va_list ap;
                   4382:        char *bn, *msg;
                   4383:        gchar *dialog_msg, *nombre;
                   4384:        guint8 reply;
                   4385:        PurpleBuddy *buddy;
                   4386: 
                   4387:        va_start(ap, fr);
                   4388:        bn = va_arg(ap, char *);
                   4389:        reply = (guint8)va_arg(ap, int);
                   4390:        msg = va_arg(ap, char *);
                   4391:        va_end(ap);
                   4392: 
                   4393:        purple_debug_info("oscar",
                   4394:                           "ssi: received authorization reply from %s.  Reply is 0x%04hhx\n", bn, reply);
                   4395: 
                   4396:        buddy = purple_find_buddy(purple_connection_get_account(gc), bn);
                   4397:        if (buddy && (purple_buddy_get_alias_only(buddy)))
                   4398:                nombre = g_strdup_printf("%s (%s)", bn, purple_buddy_get_alias_only(buddy));
                   4399:        else
                   4400:                nombre = g_strdup(bn);
                   4401: 
                   4402:        if (reply) {
                   4403:                /* Granted */
                   4404:                dialog_msg = g_strdup_printf(_("The user %s has granted your request to add them to your buddy list."), nombre);
                   4405:                purple_notify_info(gc, NULL, _("Authorization Granted"), dialog_msg);
                   4406:        } else {
                   4407:                /* Denied */
                   4408:                dialog_msg = g_strdup_printf(_("The user %s has denied your request to add them to your buddy list for the following reason:\n%s"), nombre, msg ? msg : _("No reason given."));
                   4409:                purple_notify_info(gc, NULL, _("Authorization Denied"), dialog_msg);
                   4410:        }
                   4411:        g_free(dialog_msg);
                   4412:        g_free(nombre);
                   4413: 
                   4414:        return 1;
                   4415: }
                   4416: 
                   4417: static int purple_ssi_gotadded(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
                   4418:        PurpleConnection *gc = od->gc;
                   4419:        PurpleAccount *account = purple_connection_get_account(gc);
                   4420:        va_list ap;
                   4421:        char *bn;
                   4422:        PurpleBuddy *buddy;
                   4423: 
                   4424:        va_start(ap, fr);
                   4425:        bn = va_arg(ap, char *);
                   4426:        va_end(ap);
                   4427: 
                   4428:        buddy = purple_find_buddy(account, bn);
                   4429:        purple_debug_info("oscar", "ssi: %s added you to their buddy list\n", bn);
                   4430:        purple_account_notify_added(account, bn, NULL,
                   4431:                        (buddy ? purple_buddy_get_alias_only(buddy) : NULL), NULL);
                   4432: 
                   4433:        return 1;
                   4434: }
                   4435: 
                   4436: GList *oscar_chat_info(PurpleConnection *gc) {
                   4437:        GList *m = NULL;
                   4438:        struct proto_chat_entry *pce;
                   4439: 
                   4440:        pce = g_new0(struct proto_chat_entry, 1);
                   4441:        pce->label = _("_Room:");
                   4442:        pce->identifier = "room";
                   4443:        pce->required = TRUE;
                   4444:        m = g_list_append(m, pce);
                   4445: 
                   4446:        pce = g_new0(struct proto_chat_entry, 1);
                   4447:        pce->label = _("_Exchange:");
                   4448:        pce->identifier = "exchange";
                   4449:        pce->required = TRUE;
                   4450:        pce->is_int = TRUE;
                   4451:        pce->min = 4;
                   4452:        pce->max = 20;
                   4453:        m = g_list_append(m, pce);
                   4454: 
                   4455:        return m;
                   4456: }
                   4457: 
                   4458: GHashTable *oscar_chat_info_defaults(PurpleConnection *gc, const char *chat_name)
                   4459: {
                   4460:        GHashTable *defaults;
                   4461: 
                   4462:        defaults = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_free);
                   4463: 
                   4464:        if (chat_name != NULL)
                   4465:                g_hash_table_insert(defaults, "room", g_strdup(chat_name));
                   4466:        g_hash_table_insert(defaults, "exchange", g_strdup("4"));
                   4467: 
                   4468:        return defaults;
                   4469: }
                   4470: 
                   4471: char *
                   4472: oscar_get_chat_name(GHashTable *data)
                   4473: {
                   4474:        return g_strdup(g_hash_table_lookup(data, "room"));
                   4475: }
                   4476: 
                   4477: void
                   4478: oscar_join_chat(PurpleConnection *gc, GHashTable *data)
                   4479: {
                   4480:        OscarData *od = purple_connection_get_protocol_data(gc);
                   4481:        FlapConnection *conn;
                   4482:        char *name, *exchange;
                   4483:        int exchange_int;
                   4484: 
                   4485:        name = g_hash_table_lookup(data, "room");
                   4486:        exchange = g_hash_table_lookup(data, "exchange");
                   4487: 
                   4488:        g_return_if_fail(name != NULL && *name != '\0');
                   4489:        g_return_if_fail(exchange != NULL);
                   4490: 
                   4491:        errno = 0;
                   4492:        exchange_int = strtol(exchange, NULL, 10);
                   4493:        g_return_if_fail(errno == 0);
                   4494: 
                   4495:        purple_debug_info("oscar", "Attempting to join chat room %s.\n", name);
                   4496: 
                   4497:        if ((conn = flap_connection_getbytype(od, SNAC_FAMILY_CHATNAV)))
                   4498:        {
                   4499:                purple_debug_info("oscar", "chatnav exists, creating room\n");
                   4500:                aim_chatnav_createroom(od, conn, name, exchange_int);
                   4501:        } else {
                   4502:                /* this gets tricky */
                   4503:                struct create_room *cr = g_new0(struct create_room, 1);
                   4504:                purple_debug_info("oscar", "chatnav does not exist, opening chatnav\n");
                   4505:                cr->exchange = exchange_int;
                   4506:                cr->name = g_strdup(name);
                   4507:                od->create_rooms = g_slist_prepend(od->create_rooms, cr);
                   4508:                aim_srv_requestnew(od, SNAC_FAMILY_CHATNAV);
                   4509:        }
                   4510: }
                   4511: 
                   4512: void
                   4513: oscar_chat_invite(PurpleConnection *gc, int id, const char *message, const char *name)
                   4514: {
                   4515:        OscarData *od = purple_connection_get_protocol_data(gc);
                   4516:        struct chat_connection *ccon = find_oscar_chat(gc, id);
                   4517: 
                   4518:        if (ccon == NULL)
                   4519:                return;
                   4520: 
                   4521:        aim_im_sendch2_chatinvite(od, name, message ? message : "",
                   4522:                        ccon->exchange, ccon->name, 0x0);
                   4523: }
                   4524: 
                   4525: void
                   4526: oscar_chat_leave(PurpleConnection *gc, int id)
                   4527: {
                   4528:        PurpleConversation *conv;
                   4529:        struct chat_connection *cc;
                   4530: 
                   4531:        conv = purple_find_chat(gc, id);
                   4532: 
                   4533:        g_return_if_fail(conv != NULL);
                   4534: 
                   4535:        purple_debug_info("oscar", "Leaving chat room %s\n",
                   4536:                        purple_conversation_get_name(conv));
                   4537: 
                   4538:        cc = find_oscar_chat(gc, purple_conv_chat_get_id(PURPLE_CONV_CHAT(conv)));
                   4539:        flap_connection_schedule_destroy(cc->conn, OSCAR_DISCONNECT_DONE, NULL);
                   4540:        oscar_chat_kill(gc, cc);
                   4541: }
                   4542: 
                   4543: int oscar_send_chat(PurpleConnection *gc, int id, const char *message, PurpleMessageFlags flags)
                   4544: {
                   4545:        OscarData *od = purple_connection_get_protocol_data(gc);
                   4546:        PurpleConversation *conv = NULL;
                   4547:        struct chat_connection *c = NULL;
                   4548:        char *buf, *buf2, *buf3;
                   4549:        guint16 charset;
                   4550:        char *charsetstr;
                   4551:        gsize len;
                   4552: 
                   4553:        if (!(conv = purple_find_chat(gc, id)))
                   4554:                return -EINVAL;
                   4555: 
                   4556:        if (!(c = find_oscar_chat_by_conv(gc, conv)))
                   4557:                return -EINVAL;
                   4558: 
                   4559:        buf = purple_strdup_withhtml(message);
                   4560: 
                   4561:        if (strstr(buf, "<IMG "))
                   4562:                purple_conversation_write(conv, "",
                   4563:                        _("Your IM Image was not sent. "
                   4564:                          "You cannot send IM Images in AIM chats."),
                   4565:                        PURPLE_MESSAGE_ERROR, time(NULL));
                   4566: 
                   4567:        buf2 = oscar_encode_im(buf, &len, &charset, &charsetstr);
                   4568:        /*
                   4569:         * Evan S. suggested that maxvis really does mean "number of
                   4570:         * visible characters" and not "number of bytes"
                   4571:         */
                   4572:        if ((len > c->maxlen) || (len > c->maxvis)) {
                   4573:                /* If the length was too long, try stripping the HTML and then running it back through
                   4574:                 * purple_strdup_withhtml() and the encoding process. The result may be shorter. */
                   4575:                g_free(buf2);
                   4576: 
                   4577:                buf3 = purple_markup_strip_html(buf);
                   4578:                g_free(buf);
                   4579: 
                   4580:                buf = purple_strdup_withhtml(buf3);
                   4581:                g_free(buf3);
                   4582: 
                   4583:                buf2 = oscar_encode_im(buf, &len, &charset, &charsetstr);
                   4584: 
                   4585:                if ((len > c->maxlen) || (len > c->maxvis)) {
                   4586:                        purple_debug_warning("oscar",
                   4587:                                        "Could not send %s because (%" G_GSIZE_FORMAT " > maxlen %i) or (%" G_GSIZE_FORMAT " > maxvis %i)\n",
                   4588:                                        buf2, len, c->maxlen, len, c->maxvis);
                   4589:                        g_free(buf);
                   4590:                        g_free(buf2);
                   4591:                        return -E2BIG;
                   4592:                }
                   4593: 
                   4594:                purple_debug_info("oscar", "Sending %s as %s because the original was too long.\n",
                   4595:                                message, buf2);
                   4596:        }
                   4597: 
                   4598:        aim_chat_send_im(od, c->conn, 0, buf2, len, charsetstr, "en");
                   4599:        g_free(buf2);
                   4600:        g_free(buf);
                   4601: 
                   4602:        return 0;
                   4603: }
                   4604: 
                   4605: PurpleMood* oscar_get_purple_moods(PurpleAccount *account)
                   4606: {
                   4607:        return icq_get_purple_moods(account);
                   4608: }
                   4609: 
                   4610: const char *oscar_list_icon_icq(PurpleAccount *a, PurpleBuddy *b)
                   4611: {
                   4612:        const char *name = b ? purple_buddy_get_name(b) : NULL;
                   4613:        if (name && !oscar_util_valid_name_sms(name) && oscar_util_valid_name_icq(name))
                   4614:                return "icq";
                   4615: 
                   4616:        return "icq";
                   4617: }
                   4618: 
                   4619: const char *oscar_list_icon_aim(PurpleAccount *a, PurpleBuddy *b)
                   4620: {
                   4621:        const char *name = b ? purple_buddy_get_name(b) : NULL;
                   4622:        if (name && !oscar_util_valid_name_sms(name) && oscar_util_valid_name_icq(name))
                   4623:                return "icq";
                   4624: 
                   4625:        return "aim";
                   4626: }
                   4627: 
                   4628: const char *oscar_list_emblem(PurpleBuddy *b)
                   4629: {
                   4630:        PurpleConnection *gc = NULL;
                   4631:        OscarData *od = NULL;
                   4632:        PurpleAccount *account = NULL;
                   4633:        PurplePresence *presence;
                   4634:        aim_userinfo_t *userinfo = NULL;
                   4635:        const char *name;
                   4636: 
                   4637:        account = purple_buddy_get_account(b);
                   4638:        name = purple_buddy_get_name(b);
                   4639:        if (account != NULL)
                   4640:                gc = purple_account_get_connection(account);
                   4641:        if (gc != NULL)
                   4642:                od = purple_connection_get_protocol_data(gc);
                   4643:        if (od != NULL)
                   4644:                userinfo = aim_locate_finduserinfo(od, name);
                   4645: 
                   4646:        presence = purple_buddy_get_presence(b);
                   4647: 
                   4648:        if (purple_presence_is_online(presence) == FALSE) {
                   4649:                char *gname;
                   4650:                if ((name) && (od) && (od->ssi.received_data) &&
                   4651:                        (gname = aim_ssi_itemlist_findparentname(od->ssi.local, name)) &&
                   4652:                        (aim_ssi_waitingforauth(od->ssi.local, gname, name))) {
                   4653:                        return "not-authorized";
                   4654:                }
                   4655:        }
                   4656: 
                   4657:        if (userinfo != NULL ) {
                   4658:                if (userinfo->flags & AIM_FLAG_ADMINISTRATOR)
                   4659:                        return "admin";
                   4660:                if (userinfo->flags & AIM_FLAG_ACTIVEBUDDY)
                   4661:                        return "bot";
                   4662:                if (userinfo->capabilities & OSCAR_CAPABILITY_SECUREIM)
                   4663:                        return "secure";
                   4664:                if (userinfo->icqinfo.status & AIM_ICQ_STATE_BIRTHDAY)
                   4665:                        return "birthday";
                   4666: 
                   4667:                /* Make the mood icon override anything below this. */
                   4668:                if (purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_MOOD))
                   4669:                        return NULL;
                   4670: 
                   4671:                if (userinfo->capabilities & OSCAR_CAPABILITY_HIPTOP)
                   4672:                        return "hiptop";
                   4673:        }
                   4674:        return NULL;
                   4675: }
                   4676: 
                   4677: void oscar_tooltip_text(PurpleBuddy *b, PurpleNotifyUserInfo *user_info, gboolean full)
                   4678: {
                   4679:        PurpleConnection *gc;
                   4680:        PurpleAccount *account;
                   4681:        OscarData *od;
                   4682:        aim_userinfo_t *userinfo;
                   4683: 
                   4684:        if (!PURPLE_BUDDY_IS_ONLINE(b))
                   4685:                return;
                   4686: 
                   4687:        account = purple_buddy_get_account(b);
                   4688:        gc = purple_account_get_connection(account);
                   4689:        od = purple_connection_get_protocol_data(gc);
                   4690:        userinfo = aim_locate_finduserinfo(od, purple_buddy_get_name(b));
                   4691: 
                   4692:        oscar_user_info_append_status(gc, user_info, b, userinfo, /* use_html_status */ FALSE);
                   4693: 
                   4694:        if (full)
                   4695:                oscar_user_info_append_extra_info(gc, user_info, b, userinfo);
                   4696: }
                   4697: 
                   4698: char *oscar_status_text(PurpleBuddy *b)
                   4699: {
                   4700:        PurpleConnection *gc;
                   4701:        PurpleAccount *account;
                   4702:        OscarData *od;
                   4703:        const PurplePresence *presence;
                   4704:        const PurpleStatus *status;
                   4705:        const char *message;
                   4706:        gchar *ret = NULL;
                   4707: 
                   4708:        gc = purple_account_get_connection(purple_buddy_get_account(b));
                   4709:        account = purple_connection_get_account(gc);
                   4710:        od = purple_connection_get_protocol_data(gc);
                   4711:        presence = purple_buddy_get_presence(b);
                   4712:        status = purple_presence_get_active_status(presence);
                   4713: 
                   4714:        if ((od != NULL) && !purple_presence_is_online(presence))
                   4715:        {
                   4716:                const char *name = purple_buddy_get_name(b);
                   4717:                char *gname = aim_ssi_itemlist_findparentname(od->ssi.local, name);
                   4718:                if (aim_ssi_waitingforauth(od->ssi.local, gname, name))
                   4719:                        ret = g_strdup(_("Not Authorized"));
                   4720:                else
                   4721:                        ret = g_strdup(_("Offline"));
                   4722:        }
                   4723:        else
                   4724:        {
                   4725:                message = purple_status_get_attr_string(status, "message");
                   4726:                if (message != NULL)
                   4727:                {
                   4728:                        gchar *tmp = oscar_util_format_string(message, purple_account_get_username(account));
                   4729:                        ret = purple_markup_escape_text(tmp, -1);
                   4730:                        g_free(tmp);
                   4731:                }
                   4732:                else if (purple_status_is_available(status))
                   4733:                {
                   4734:                        /* Don't show "Available" as status message in case buddy doesn't have a status message */
                   4735:                }
                   4736:                else
                   4737:                {
                   4738:                        ret = g_strdup(purple_status_get_name(status));
                   4739:                }
                   4740:        }
                   4741: 
                   4742:        return ret;
                   4743: }
                   4744: 
                   4745: void oscar_set_aim_permdeny(PurpleConnection *gc) {
                   4746:        PurpleAccount *account = purple_connection_get_account(gc);
                   4747:        OscarData *od = purple_connection_get_protocol_data(gc);
                   4748: 
                   4749:        /*
                   4750:         * Conveniently there is a one-to-one mapping between the
                   4751:         * values of libpurple's PurplePrivacyType and the values used
                   4752:         * by the oscar protocol.
                   4753:         */
                   4754:        aim_ssi_setpermdeny(od, account->perm_deny);
                   4755: }
                   4756: 
                   4757: void oscar_add_permit(PurpleConnection *gc, const char *who) {
                   4758:        OscarData *od = purple_connection_get_protocol_data(gc);
                   4759:        purple_debug_info("oscar", "ssi: About to add a permit\n");
                   4760:        aim_ssi_add_to_private_list(od, who, AIM_SSI_TYPE_PERMIT);
                   4761: }
                   4762: 
                   4763: void oscar_add_deny(PurpleConnection *gc, const char *who) {
                   4764:        OscarData *od = purple_connection_get_protocol_data(gc);
                   4765:        purple_debug_info("oscar", "ssi: About to add a deny\n");
                   4766:        aim_ssi_add_to_private_list(od, who, aim_ssi_getdenyentrytype(od));
                   4767: }
                   4768: 
                   4769: void oscar_rem_permit(PurpleConnection *gc, const char *who) {
                   4770:        OscarData *od = purple_connection_get_protocol_data(gc);
                   4771:        purple_debug_info("oscar", "ssi: About to delete a permit\n");
                   4772:        aim_ssi_del_from_private_list(od, who, AIM_SSI_TYPE_PERMIT);
                   4773: }
                   4774: 
                   4775: void oscar_rem_deny(PurpleConnection *gc, const char *who) {
                   4776:        OscarData *od = purple_connection_get_protocol_data(gc);
                   4777:        purple_debug_info("oscar", "ssi: About to delete a deny\n");
                   4778:        aim_ssi_del_from_private_list(od, who, aim_ssi_getdenyentrytype(od));
                   4779: }
                   4780: 
                   4781: GList *
                   4782: oscar_status_types(PurpleAccount *account)
                   4783: {
                   4784:        gboolean is_icq;
                   4785:        GList *status_types = NULL;
                   4786:        PurpleStatusType *type;
                   4787: 
                   4788:        g_return_val_if_fail(account != NULL, NULL);
                   4789: 
                   4790:        /* Used to flag some statuses as "user settable" or not */
                   4791:        is_icq = oscar_util_valid_name_icq(purple_account_get_username(account));
                   4792: 
                   4793:        /* Common status types */
                   4794:        /* Really the available message should only be settable for AIM accounts */
                   4795:        type = purple_status_type_new_with_attrs(PURPLE_STATUS_AVAILABLE,
                   4796:                                                                                   OSCAR_STATUS_ID_AVAILABLE,
                   4797:                                                                                   NULL, TRUE, TRUE, FALSE,
                   4798:                                                                                   "message", _("Message"),
                   4799:                                                                                   purple_value_new(PURPLE_TYPE_STRING),
                   4800:                                                                                   "itmsurl", _("iTunes Music Store Link"),
                   4801:                                                                                   purple_value_new(PURPLE_TYPE_STRING), NULL);
                   4802:        status_types = g_list_prepend(status_types, type);
                   4803: 
                   4804:        type = purple_status_type_new_with_attrs(PURPLE_STATUS_AVAILABLE,
                   4805:                                                                         OSCAR_STATUS_ID_FREE4CHAT,
                   4806:                                                                         _("Free For Chat"), TRUE, is_icq, FALSE,
                   4807:                                                                         "message", _("Message"),
                   4808:                                purple_value_new(PURPLE_TYPE_STRING), NULL);
                   4809: 
                   4810:        status_types = g_list_prepend(status_types, type);
                   4811: 
                   4812:        type = purple_status_type_new_with_attrs(PURPLE_STATUS_AVAILABLE,
                   4813:                                                                         OSCAR_STATUS_ID_EVIL,
                   4814:                                                                         _("Evil"), TRUE, is_icq, FALSE,
                   4815:                                 "message", _("Message"),
                   4816:                                purple_value_new(PURPLE_TYPE_STRING), NULL);
                   4817:        status_types = g_list_prepend(status_types, type);
                   4818: 
                   4819: 
                   4820:        type = purple_status_type_new_with_attrs(PURPLE_STATUS_AVAILABLE,
                   4821:                                                                         OSCAR_STATUS_ID_DEPRESSION,
                   4822:                                                                         _("Depression"), TRUE, is_icq, FALSE,
                   4823:                                 "message", _("Message"),
                   4824:                                purple_value_new(PURPLE_TYPE_STRING), NULL);
                   4825:        status_types = g_list_prepend(status_types, type);
                   4826: 
                   4827: 
                   4828:        type = purple_status_type_new_with_attrs(PURPLE_STATUS_AVAILABLE,
                   4829:                                                                         OSCAR_STATUS_ID_ATHOME,
                   4830:                                                                         _("At home"), TRUE, is_icq, FALSE,
                   4831:                                "message", _("Message"),
                   4832:                                purple_value_new(PURPLE_TYPE_STRING), NULL);
                   4833:        status_types = g_list_prepend(status_types, type);
                   4834: 
                   4835: 
                   4836:        type = purple_status_type_new_with_attrs(PURPLE_STATUS_AVAILABLE,
                   4837:                                                                         OSCAR_STATUS_ID_ATWORK,
                   4838:                                                                         _("At work"), TRUE, is_icq, FALSE,
                   4839:                                "message", _("Message"),
                   4840:                                purple_value_new(PURPLE_TYPE_STRING), NULL);
                   4841: 
                   4842:        status_types = g_list_prepend(status_types, type);
                   4843: 
                   4844: 
                   4845:        type = purple_status_type_new_with_attrs(PURPLE_STATUS_AVAILABLE,
                   4846:                                                                         OSCAR_STATUS_ID_LUNCH,
                   4847:                                                                         _("Lunch"), TRUE, is_icq, FALSE,
                   4848:                                "message", _("Message"),
                   4849:                                purple_value_new(PURPLE_TYPE_STRING), NULL);
                   4850: 
                   4851:        status_types = g_list_prepend(status_types, type);
                   4852: 
                   4853:        type = purple_status_type_new_with_attrs(PURPLE_STATUS_AWAY,
                   4854:                                                                                   OSCAR_STATUS_ID_AWAY,
                   4855:                                                                                   NULL, TRUE, TRUE, FALSE,
                   4856:                                                                                   "message", _("Message"),
                   4857:                                                                                   purple_value_new(PURPLE_TYPE_STRING), NULL);
                   4858:        status_types = g_list_prepend(status_types, type);
                   4859: 
                   4860:        type = purple_status_type_new_with_attrs(PURPLE_STATUS_INVISIBLE,
                   4861:                                                                         OSCAR_STATUS_ID_INVISIBLE,
                   4862:                                                                         NULL, TRUE, TRUE, FALSE,
                   4863:                                                                         "message", _("Message"),
                   4864:                                                                          purple_value_new(PURPLE_TYPE_STRING), NULL);
                   4865: 
                   4866:        status_types = g_list_prepend(status_types, type);
                   4867: 
                   4868:        type = purple_status_type_new_full(PURPLE_STATUS_MOBILE, OSCAR_STATUS_ID_MOBILE, NULL, FALSE, FALSE, TRUE);
                   4869:        status_types = g_list_prepend(status_types, type);
                   4870: 
                   4871:        /* ICQ-specific status types */
                   4872:        type = purple_status_type_new_with_attrs(PURPLE_STATUS_UNAVAILABLE,
                   4873:                                OSCAR_STATUS_ID_OCCUPIED,
                   4874:                                _("Occupied"), TRUE, is_icq, FALSE,
                   4875:                                "message", _("Message"),
                   4876:                                purple_value_new(PURPLE_TYPE_STRING), NULL);
                   4877:        status_types = g_list_prepend(status_types, type);
                   4878: 
                   4879:        type = purple_status_type_new_with_attrs(PURPLE_STATUS_UNAVAILABLE,
                   4880:                                OSCAR_STATUS_ID_DND,
                   4881:                                _("Do Not Disturb"), TRUE, is_icq, FALSE,
                   4882:                                "message", _("Message"),
                   4883:                                purple_value_new(PURPLE_TYPE_STRING), NULL);
                   4884:        status_types = g_list_prepend(status_types, type);
                   4885: 
                   4886:        type = purple_status_type_new_with_attrs(PURPLE_STATUS_EXTENDED_AWAY,
                   4887:                                OSCAR_STATUS_ID_NA,
                   4888:                                _("Not Available"), TRUE, is_icq, FALSE,
                   4889:                                "message", _("Message"),
                   4890:                                purple_value_new(PURPLE_TYPE_STRING), NULL);
                   4891:        status_types = g_list_prepend(status_types, type);
                   4892: 
                   4893:        type = purple_status_type_new_full(PURPLE_STATUS_OFFLINE,
                   4894:                                                                         OSCAR_STATUS_ID_OFFLINE,
                   4895:                                                                         NULL, TRUE, TRUE, FALSE);
                   4896:        status_types = g_list_prepend(status_types, type);
                   4897: 
                   4898:        type = purple_status_type_new_with_attrs(PURPLE_STATUS_MOOD,
                   4899:                        "mood", NULL, TRUE, is_icq, TRUE,
                   4900:                        PURPLE_MOOD_NAME, _("Mood Name"), purple_value_new(PURPLE_TYPE_STRING),
                   4901:                        PURPLE_MOOD_COMMENT, _("Mood Comment"), purple_value_new(PURPLE_TYPE_STRING),
                   4902:                        NULL);
                   4903:        status_types = g_list_prepend(status_types, type);
                   4904: 
                   4905:        return g_list_reverse(status_types);
                   4906: }
                   4907: 
                   4908: static void oscar_ssi_editcomment(struct name_data *data, const char *text) {
                   4909:        PurpleConnection *gc;
                   4910:        PurpleAccount *account;
                   4911:        OscarData *od;
                   4912:        PurpleBuddy *b;
                   4913:        PurpleGroup *g;
                   4914: 
                   4915:        gc = data->gc;
                   4916:        od = purple_connection_get_protocol_data(gc);
                   4917:        account = purple_connection_get_account(gc);
                   4918: 
                   4919:        b = purple_find_buddy(account, data->name);
                   4920:        if (b == NULL) {
                   4921:                oscar_free_name_data(data);
                   4922:                return;
                   4923:        }
                   4924: 
                   4925:        g = purple_buddy_get_group(b);
                   4926:        if (g == NULL) {
                   4927:                oscar_free_name_data(data);
                   4928:                return;
                   4929:        }
                   4930: 
                   4931:        aim_ssi_editcomment(od, purple_group_get_name(g), data->name, text);
                   4932:        oscar_free_name_data(data);
                   4933: }
                   4934: 
                   4935: static void oscar_buddycb_edit_comment(PurpleBlistNode *node, gpointer ignore) {
                   4936: 
                   4937:        PurpleBuddy *buddy;
                   4938:        PurpleConnection *gc;
                   4939:        OscarData *od;
                   4940:        struct name_data *data;
                   4941:        PurpleGroup *g;
                   4942:        char *comment;
                   4943:        gchar *comment_utf8;
                   4944:        gchar *title;
                   4945:        PurpleAccount *account;
                   4946:        const char *name;
                   4947: 
                   4948:        g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
                   4949: 
                   4950:        buddy = (PurpleBuddy *) node;
                   4951:        name = purple_buddy_get_name(buddy);
                   4952:        account = purple_buddy_get_account(buddy);
                   4953:        gc = purple_account_get_connection(account);
                   4954:        od = purple_connection_get_protocol_data(gc);
                   4955: 
                   4956:        if (!(g = purple_buddy_get_group(buddy)))
                   4957:                return;
                   4958: 
                   4959:        data = g_new(struct name_data, 1);
                   4960: 
                   4961:        comment = aim_ssi_getcomment(od->ssi.local, purple_group_get_name(g), name);
                   4962:        comment_utf8 = comment ? oscar_utf8_try_convert(account, od, comment) : NULL;
                   4963: 
                   4964:        data->gc = gc;
                   4965:        data->name = g_strdup(name);
                   4966:        data->nick = g_strdup(purple_buddy_get_alias_only(buddy));
                   4967: 
                   4968:        title = g_strdup_printf(_("Buddy Comment for %s"), data->name);
                   4969:        purple_request_input(gc, title, _("Buddy Comment:"), NULL,
                   4970:                                           comment_utf8, TRUE, FALSE, NULL,
                   4971:                                           _("_OK"), G_CALLBACK(oscar_ssi_editcomment),
                   4972:                                           _("_Cancel"), G_CALLBACK(oscar_free_name_data),
                   4973:                                           account, data->name, NULL,
                   4974:                                           data);
                   4975:        g_free(title);
                   4976: 
                   4977:        g_free(comment);
                   4978:        g_free(comment_utf8);
                   4979: }
                   4980: 
                   4981: static void
                   4982: oscar_ask_directim_yes_cb(struct oscar_ask_directim_data *data)
                   4983: {
                   4984:        peer_connection_propose(data->od, OSCAR_CAPABILITY_DIRECTIM, data->who);
                   4985:        g_free(data->who);
                   4986:        g_free(data);
                   4987: }
                   4988: 
                   4989: static void
                   4990: oscar_ask_directim_no_cb(struct oscar_ask_directim_data *data)
                   4991: {
                   4992:        g_free(data->who);
                   4993:        g_free(data);
                   4994: }
                   4995: 
                   4996: /* This is called from right-click menu on a buddy node. */
                   4997: static void
                   4998: oscar_ask_directim(gpointer object, gpointer ignored)
                   4999: {
                   5000:        PurpleBlistNode *node;
                   5001:        PurpleBuddy *buddy;
                   5002:        PurpleConnection *gc;
                   5003:        gchar *buf;
                   5004:        struct oscar_ask_directim_data *data;
                   5005:        PurpleAccount *account;
                   5006: 
                   5007:        node = object;
                   5008: 
                   5009:        g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
                   5010: 
                   5011:        buddy = (PurpleBuddy *)node;
                   5012:        account = purple_buddy_get_account(buddy);
                   5013:        gc = purple_account_get_connection(account);
                   5014: 
                   5015:        data = g_new0(struct oscar_ask_directim_data, 1);
                   5016:        data->who = g_strdup(purple_buddy_get_name(buddy));
                   5017:        data->od = purple_connection_get_protocol_data(gc);
                   5018:        buf = g_strdup_printf(_("You have selected to open a Direct IM connection with %s."),
                   5019:                        data->who);
                   5020: 
                   5021:        purple_request_action(gc, NULL, buf,
                   5022:                        _("Because this reveals your IP address, it "
                   5023:                          "may be considered a security risk.  Do you "
                   5024:                          "wish to continue?"),
                   5025:                        0, /* Default action is "connect" */
                   5026:                        account, data->who, NULL,
                   5027:                        data, 2,
                   5028:                        _("C_onnect"), G_CALLBACK(oscar_ask_directim_yes_cb),
                   5029:                        _("_Cancel"), G_CALLBACK(oscar_ask_directim_no_cb));
                   5030:        g_free(buf);
                   5031: }
                   5032: 
                   5033: static void
                   5034: oscar_close_directim(gpointer object, gpointer ignored)
                   5035: {
                   5036:        PurpleBlistNode *node;
                   5037:        PurpleBuddy *buddy;
                   5038:        PurpleAccount *account;
                   5039:        PurpleConnection *gc;
                   5040:        PurpleConversation *conv;
                   5041:        OscarData *od;
                   5042:        PeerConnection *conn;
                   5043:        const char *name;
                   5044: 
                   5045:        node = object;
                   5046: 
                   5047:        g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
                   5048: 
                   5049:        buddy = (PurpleBuddy*)node;
                   5050:        name = purple_buddy_get_name(buddy);
                   5051:        account = purple_buddy_get_account(buddy);
                   5052:        gc = purple_account_get_connection(account);
                   5053:        od = gc->proto_data;
                   5054:        conn = peer_connection_find_by_type(od, name, OSCAR_CAPABILITY_DIRECTIM);
                   5055: 
                   5056:        if (conn != NULL)
                   5057:        {
                   5058:                if (!conn->ready)
                   5059:                        aim_im_sendch2_cancel(conn);
                   5060: 
                   5061:                peer_connection_destroy(conn, OSCAR_DISCONNECT_LOCAL_CLOSED, NULL);
                   5062: 
                   5063:                /* OSCAR_DISCONNECT_LOCAL_CLOSED doesn't write anything to the convo
                   5064:                 * window. Let the user know that we cancelled the Direct IM. */
                   5065:                conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, name);
                   5066:                purple_conversation_write(conv, NULL, _("You closed the connection."),
                   5067:                                PURPLE_MESSAGE_SYSTEM, time(NULL));
                   5068:        }
                   5069: }
                   5070: 
                   5071: static void oscar_get_icqxstatusmsg(PurpleBlistNode *node, gpointer ignore)
                   5072: {
                   5073:        PurpleBuddy *buddy;
                   5074:        PurpleConnection *gc;
                   5075:        OscarData *od;
                   5076:        PurpleAccount *account;
                   5077:        const char *bname;
                   5078: 
                   5079:        g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
                   5080: 
                   5081:        buddy = (PurpleBuddy *)node;
                   5082:        bname = purple_buddy_get_name(buddy);
                   5083: 
                   5084:        account = purple_buddy_get_account(buddy);
                   5085:        gc = purple_account_get_connection(account);
                   5086:        od = purple_connection_get_protocol_data(gc);
                   5087: 
                   5088:        purple_debug_info("oscar", "Manual X-Status Get From %s to %s:\n", bname, purple_account_get_username(account));
                   5089: 
                   5090:        icq_im_xstatus_request(od, bname);
                   5091: }
                   5092: 
                   5093: static void
                   5094: oscar_get_aim_info_cb(PurpleBlistNode *node, gpointer ignore)
                   5095: {
                   5096:        PurpleBuddy *buddy;
                   5097:        PurpleConnection *gc;
                   5098: 
                   5099:        g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node));
                   5100: 
                   5101:        buddy = (PurpleBuddy *)node;
                   5102:        gc = purple_account_get_connection(purple_buddy_get_account(buddy));
                   5103: 
                   5104:        aim_locate_getinfoshort(purple_connection_get_protocol_data(gc),
                   5105:                        purple_buddy_get_name(buddy), 0x00000003);
                   5106: }
                   5107: 
                   5108: static GList *
                   5109: oscar_buddy_menu(PurpleBuddy *buddy) {
                   5110:        PurpleConnection *gc;
                   5111:        OscarData *od;
                   5112:        GList *menu;
                   5113:        PurpleMenuAction *act;
                   5114:        aim_userinfo_t *userinfo;
                   5115:        PurpleAccount *account;
                   5116:        const char *bname = purple_buddy_get_name(buddy);
                   5117: 
                   5118:        account = purple_buddy_get_account(buddy);
                   5119:        gc = purple_account_get_connection(account);
                   5120:        od = purple_connection_get_protocol_data(gc);
                   5121:        userinfo = aim_locate_finduserinfo(od, bname);
                   5122:        menu = NULL;
                   5123: 
                   5124:        if (od->icq && oscar_util_valid_name_icq(bname))
                   5125:        {
                   5126:                act = purple_menu_action_new(_("Get AIM Info"),
                   5127:                                                                   PURPLE_CALLBACK(oscar_get_aim_info_cb),
                   5128:                                                                   NULL, NULL);
                   5129:                menu = g_list_prepend(menu, act);
                   5130:        }
                   5131: 
                   5132:        if (purple_buddy_get_group(buddy) != NULL)
                   5133:        {
                   5134:                /* We only do this if the user is in our buddy list */
                   5135:                act = purple_menu_action_new(_("Edit Buddy Comment"),
                   5136:                                           PURPLE_CALLBACK(oscar_buddycb_edit_comment),
                   5137:                                           NULL, NULL);
                   5138:                menu = g_list_prepend(menu, act);
                   5139:        }
                   5140: 
                   5141:        if (od->icq)
                   5142:        {
                   5143:                act = purple_menu_action_new(_("Get X-Status Msg"),
                   5144:                                           PURPLE_CALLBACK(oscar_get_icqxstatusmsg),
                   5145:                                           NULL, NULL);
                   5146:                menu = g_list_prepend(menu, act);
                   5147:                menu = g_list_prepend(menu, create_visibility_menu_item(od, bname));
                   5148:        }
                   5149: 
                   5150:        if (userinfo &&
                   5151:                oscar_util_name_compare(purple_account_get_username(account), bname) &&
                   5152:                PURPLE_BUDDY_IS_ONLINE(buddy))
                   5153:        {
                   5154:                PeerConnection *conn;
                   5155:                conn = peer_connection_find_by_type(od, bname, OSCAR_CAPABILITY_DIRECTIM);
                   5156: 
                   5157:                if (userinfo->capabilities & OSCAR_CAPABILITY_DIRECTIM)
                   5158:                {
                   5159:                        if (conn)
                   5160:                        {
                   5161:                                act = purple_menu_action_new(_("End Direct IM Session"),
                   5162:                                                          PURPLE_CALLBACK(oscar_close_directim),
                   5163:                                                          NULL, NULL);
                   5164:                        }
                   5165:                        else
                   5166:                        {
                   5167:                                act = purple_menu_action_new(_("Direct IM"),
                   5168:                                                          PURPLE_CALLBACK(oscar_ask_directim),
                   5169:                                                          NULL, NULL);
                   5170:                        }
                   5171:                        menu = g_list_prepend(menu, act);
                   5172:                }
                   5173:        }
                   5174: 
                   5175:        if (od->ssi.received_data && purple_buddy_get_group(buddy) != NULL)
                   5176:        {
                   5177:                /*
                   5178:                 * We only do this if the user is in our buddy list and we're
                   5179:                 * waiting for authorization.
                   5180:                 */
                   5181:                char *gname;
                   5182:                gname = aim_ssi_itemlist_findparentname(od->ssi.local, bname);
                   5183:                if (gname && aim_ssi_waitingforauth(od->ssi.local, gname, bname))
                   5184:                {
                   5185:                        act = purple_menu_action_new(_("Re-request Authorization"),
                   5186:                                                   PURPLE_CALLBACK(oscar_auth_sendrequest_menu),
                   5187:                                                   NULL, NULL);
                   5188:                        menu = g_list_prepend(menu, act);
                   5189:                }
                   5190:        }
                   5191: 
                   5192:        menu = g_list_reverse(menu);
                   5193: 
                   5194:        return menu;
                   5195: }
                   5196: 
                   5197: 
                   5198: GList *oscar_blist_node_menu(PurpleBlistNode *node) {
                   5199:        if(PURPLE_BLIST_NODE_IS_BUDDY(node)) {
                   5200:                return oscar_buddy_menu((PurpleBuddy *) node);
                   5201:        } else {
                   5202:                return NULL;
                   5203:        }
                   5204: }
                   5205: 
                   5206: static void
                   5207: oscar_icq_privacy_opts(PurpleConnection *gc, PurpleRequestFields *fields)
                   5208: {
                   5209:        OscarData *od = purple_connection_get_protocol_data(gc);
                   5210:        PurpleAccount *account = purple_connection_get_account(gc);
                   5211:        PurpleRequestField *f;
                   5212:        gboolean auth, web_aware;
                   5213: 
                   5214:        f = purple_request_fields_get_field(fields, "authorization");
                   5215:        auth = purple_request_field_bool_get_value(f);
                   5216: 
                   5217:        f = purple_request_fields_get_field(fields, "web_aware");
                   5218:        web_aware = purple_request_field_bool_get_value(f);
                   5219: 
                   5220:        purple_account_set_bool(account, "authorization", auth);
                   5221:        purple_account_set_bool(account, "web_aware", web_aware);
                   5222: 
                   5223:        oscar_set_extended_status(gc);
                   5224:        aim_icq_setsecurity(od, auth, web_aware);
                   5225: }
                   5226: 
                   5227: static void
                   5228: oscar_show_icq_privacy_opts(PurplePluginAction *action)
                   5229: {
                   5230:        PurpleConnection *gc = (PurpleConnection *) action->context;
                   5231:        PurpleAccount *account = purple_connection_get_account(gc);
                   5232:        PurpleRequestFields *fields;
                   5233:        PurpleRequestFieldGroup *g;
                   5234:        PurpleRequestField *f;
                   5235:        gboolean auth, web_aware;
                   5236: 
                   5237:        auth = purple_account_get_bool(account, "authorization", OSCAR_DEFAULT_AUTHORIZATION);
                   5238:        web_aware = purple_account_get_bool(account, "web_aware", OSCAR_DEFAULT_WEB_AWARE);
                   5239: 
                   5240:        fields = purple_request_fields_new();
                   5241: 
                   5242:        g = purple_request_field_group_new(NULL);
                   5243: 
                   5244:        f = purple_request_field_bool_new("authorization", _("Require authorization"), auth);
                   5245:        purple_request_field_group_add_field(g, f);
                   5246: 
                   5247:        f = purple_request_field_bool_new("web_aware", _("Web aware (enabling this will cause you to receive SPAM!)"), web_aware);
                   5248:        purple_request_field_group_add_field(g, f);
                   5249: 
                   5250:        purple_request_fields_add_group(fields, g);
                   5251: 
                   5252:        purple_request_fields(gc, _("ICQ Privacy Options"), _("ICQ Privacy Options"),
                   5253:                                                NULL, fields,
                   5254:                                                _("OK"), G_CALLBACK(oscar_icq_privacy_opts),
                   5255:                                                _("Cancel"), NULL,
                   5256:                                                purple_connection_get_account(gc), NULL, NULL,
                   5257:                                                gc);
                   5258: }
                   5259: 
                   5260: static void oscar_confirm_account(PurplePluginAction *action)
                   5261: {
                   5262:        PurpleConnection *gc;
                   5263:        OscarData *od;
                   5264:        FlapConnection *conn;
                   5265: 
                   5266:        gc = (PurpleConnection *)action->context;
                   5267:        od = purple_connection_get_protocol_data(gc);
                   5268: 
                   5269:        conn = flap_connection_getbytype(od, SNAC_FAMILY_ADMIN);
                   5270:        if (conn != NULL) {
                   5271:                aim_admin_reqconfirm(od, conn);
                   5272:        } else {
                   5273:                od->conf = TRUE;
                   5274:                aim_srv_requestnew(od, SNAC_FAMILY_ADMIN);
                   5275:        }
                   5276: }
                   5277: 
                   5278: static void oscar_show_email(PurplePluginAction *action)
                   5279: {
                   5280:        PurpleConnection *gc = (PurpleConnection *) action->context;
                   5281:        OscarData *od = purple_connection_get_protocol_data(gc);
                   5282:        FlapConnection *conn = flap_connection_getbytype(od, SNAC_FAMILY_ADMIN);
                   5283: 
                   5284:        if (conn) {
                   5285:                aim_admin_getinfo(od, conn, 0x11);
                   5286:        } else {
                   5287:                od->reqemail = TRUE;
                   5288:                aim_srv_requestnew(od, SNAC_FAMILY_ADMIN);
                   5289:        }
                   5290: }
                   5291: 
                   5292: static void oscar_change_email(PurpleConnection *gc, const char *email)
                   5293: {
                   5294:        OscarData *od = purple_connection_get_protocol_data(gc);
                   5295:        FlapConnection *conn = flap_connection_getbytype(od, SNAC_FAMILY_ADMIN);
                   5296: 
                   5297:        if (conn) {
                   5298:                aim_admin_setemail(od, conn, email);
                   5299:        } else {
                   5300:                od->setemail = TRUE;
                   5301:                od->email = g_strdup(email);
                   5302:                aim_srv_requestnew(od, SNAC_FAMILY_ADMIN);
                   5303:        }
                   5304: }
                   5305: 
                   5306: static void oscar_show_change_email(PurplePluginAction *action)
                   5307: {
                   5308:        PurpleConnection *gc = (PurpleConnection *) action->context;
                   5309:        purple_request_input(gc, NULL, _("Change Address To:"), NULL, NULL,
                   5310:                                           FALSE, FALSE, NULL,
                   5311:                                           _("_OK"), G_CALLBACK(oscar_change_email),
                   5312:                                           _("_Cancel"), NULL,
                   5313:                                           purple_connection_get_account(gc), NULL, NULL,
                   5314:                                           gc);
                   5315: }
                   5316: 
                   5317: static void oscar_show_awaitingauth(PurplePluginAction *action)
                   5318: {
                   5319:        PurpleConnection *gc = (PurpleConnection *) action->context;
                   5320:        OscarData *od = purple_connection_get_protocol_data(gc);
                   5321:        PurpleAccount *account = purple_connection_get_account(gc);
                   5322:        GSList *buddies, *filtered_buddies, *cur;
                   5323:        gchar *text;
                   5324: 
                   5325:        buddies = purple_find_buddies(account, NULL);
                   5326:        filtered_buddies = NULL;
                   5327:        for (cur = buddies; cur != NULL; cur = cur->next) {
                   5328:                PurpleBuddy *buddy;
                   5329:                const gchar *bname, *gname;
                   5330: 
                   5331:                buddy = cur->data;
                   5332:                bname = purple_buddy_get_name(buddy);
                   5333:                gname = purple_group_get_name(purple_buddy_get_group(buddy));
                   5334:                if (aim_ssi_waitingforauth(od->ssi.local, gname, bname)) {
                   5335:                        filtered_buddies = g_slist_prepend(filtered_buddies, buddy);
                   5336:                }
                   5337:        }
                   5338: 
                   5339:        g_slist_free(buddies);
                   5340: 
                   5341:        filtered_buddies = g_slist_reverse(filtered_buddies);
                   5342:        text = oscar_format_buddies(filtered_buddies, _("you are not waiting for authorization"));
                   5343:        g_slist_free(filtered_buddies);
                   5344: 
                   5345:        purple_notify_formatted(gc, NULL, _("You are awaiting authorization from "
                   5346:                                                  "the following buddies"),     _("You can re-request "
                   5347:                                                  "authorization from these buddies by "
                   5348:                                                  "right-clicking on them and selecting "
                   5349:                                                  "\"Re-request Authorization.\""), text, NULL, NULL);
                   5350:        g_free(text);
                   5351: }
                   5352: 
                   5353: static void search_by_email_cb(PurpleConnection *gc, const char *email)
                   5354: {
                   5355:        OscarData *od = purple_connection_get_protocol_data(gc);
                   5356: 
                   5357:        aim_search_address(od, email);
                   5358: }
                   5359: 
                   5360: static void oscar_show_find_email(PurplePluginAction *action)
                   5361: {
                   5362:        PurpleConnection *gc = (PurpleConnection *) action->context;
                   5363:        purple_request_input(gc, _("Find Buddy by Email"),
                   5364:                                           _("Search for a buddy by email address"),
                   5365:                                           _("Type the email address of the buddy you are "
                   5366:                                                 "searching for."),
                   5367:                                           NULL, FALSE, FALSE, NULL,
                   5368:                                           _("_Search"), G_CALLBACK(search_by_email_cb),
                   5369:                                           _("_Cancel"), NULL,
                   5370:                                           purple_connection_get_account(gc), NULL, NULL,
                   5371:                                           gc);
                   5372: }
                   5373: 
                   5374: static void oscar_show_set_info(PurplePluginAction *action)
                   5375: {
                   5376:        PurpleConnection *gc = (PurpleConnection *) action->context;
                   5377:        purple_account_request_change_user_info(purple_connection_get_account(gc));
                   5378: }
                   5379: 
                   5380: static void oscar_show_set_info_icqurl(PurplePluginAction *action)
                   5381: {
                   5382:        PurpleConnection *gc = (PurpleConnection *) action->context;
                   5383:        purple_notify_uri(gc, "http://www.icq.com/whitepages/user_details.php");
                   5384: }
                   5385: 
                   5386: static void oscar_change_pass(PurplePluginAction *action)
                   5387: {
                   5388:        PurpleConnection *gc = (PurpleConnection *) action->context;
                   5389:        purple_account_request_change_password(purple_connection_get_account(gc));
                   5390: }
                   5391: 
                   5392: /**
                   5393:  * Only used when connecting with the old-style BUCP login.
                   5394:  */
                   5395: static void oscar_show_chpassurl(PurplePluginAction *action)
                   5396: {
                   5397:        PurpleConnection *gc = (PurpleConnection *) action->context;
                   5398:        OscarData *od = purple_connection_get_protocol_data(gc);
                   5399:        gchar *substituted = purple_strreplace(od->authinfo->chpassurl, "%s", purple_account_get_username(purple_connection_get_account(gc)));
                   5400:        purple_notify_uri(gc, substituted);
                   5401:        g_free(substituted);
                   5402: }
                   5403: 
                   5404: static void oscar_show_imforwardingurl(PurplePluginAction *action)
                   5405: {
                   5406:        PurpleConnection *gc = (PurpleConnection *) action->context;
                   5407:        purple_notify_uri(gc, "http://mymobile.aol.com/dbreg/register?action=imf&clientID=1");
                   5408: }
                   5409: 
                   5410: void oscar_set_icon(PurpleConnection *gc, PurpleStoredImage *img)
                   5411: {
                   5412:        OscarData *od = purple_connection_get_protocol_data(gc);
                   5413: 
                   5414:        if (img == NULL) {
                   5415:                aim_ssi_delicon(od);
                   5416:        } else {
                   5417:                PurpleCipherContext *context;
                   5418:                guchar md5[16];
                   5419:                gconstpointer data = purple_imgstore_get_data(img);
                   5420:                size_t len = purple_imgstore_get_size(img);
                   5421: 
                   5422:                context = purple_cipher_context_new_by_name("md5", NULL);
                   5423:                purple_cipher_context_append(context, data, len);
                   5424:                purple_cipher_context_digest(context, 16, md5, NULL);
                   5425:                purple_cipher_context_destroy(context);
                   5426: 
                   5427:                aim_ssi_seticon(od, md5, 16);
                   5428:        }
                   5429: }
                   5430: 
                   5431: /**
                   5432:  * Called by the Purple core to determine whether or not we're
                   5433:  * allowed to send a file to this user.
                   5434:  */
                   5435: gboolean
                   5436: oscar_can_receive_file(PurpleConnection *gc, const char *who)
                   5437: {
                   5438:        OscarData *od;
                   5439:        PurpleAccount *account;
                   5440: 
                   5441:        od = purple_connection_get_protocol_data(gc);
                   5442:        account = purple_connection_get_account(gc);
                   5443: 
                   5444:        if (od != NULL)
                   5445:        {
                   5446:                aim_userinfo_t *userinfo;
                   5447:                userinfo = aim_locate_finduserinfo(od, who);
                   5448: 
                   5449:                /*
                   5450:                 * Don't allowing sending a file to a user that does not support
                   5451:                 * file transfer, and don't allow sending to ourselves.
                   5452:                 */
                   5453:                if (((userinfo == NULL) ||
                   5454:                        (userinfo->capabilities & OSCAR_CAPABILITY_SENDFILE)) &&
                   5455:                        oscar_util_name_compare(who, purple_account_get_username(account)))
                   5456:                {
                   5457:                        return TRUE;
                   5458:                }
                   5459:        }
                   5460: 
                   5461:        return FALSE;
                   5462: }
                   5463: 
                   5464: PurpleXfer *
                   5465: oscar_new_xfer(PurpleConnection *gc, const char *who)
                   5466: {
                   5467:        PurpleXfer *xfer;
                   5468:        OscarData *od;
                   5469:        PurpleAccount *account;
                   5470:        PeerConnection *conn;
                   5471: 
                   5472:        od = purple_connection_get_protocol_data(gc);
                   5473:        account = purple_connection_get_account(gc);
                   5474: 
                   5475:        xfer = purple_xfer_new(account, PURPLE_XFER_SEND, who);
                   5476:        if (xfer)
                   5477:        {
                   5478:                purple_xfer_ref(xfer);
                   5479:                purple_xfer_set_init_fnc(xfer, peer_oft_sendcb_init);
                   5480:                purple_xfer_set_cancel_send_fnc(xfer, peer_oft_cb_generic_cancel);
                   5481:                purple_xfer_set_request_denied_fnc(xfer, peer_oft_cb_generic_cancel);
                   5482:                purple_xfer_set_ack_fnc(xfer, peer_oft_sendcb_ack);
                   5483: 
                   5484:                conn = peer_connection_new(od, OSCAR_CAPABILITY_SENDFILE, who);
                   5485:                conn->flags |= PEER_CONNECTION_FLAG_INITIATED_BY_ME;
                   5486:                conn->flags |= PEER_CONNECTION_FLAG_APPROVED;
                   5487:                aim_icbm_makecookie(conn->cookie);
                   5488:                conn->xfer = xfer;
                   5489:                xfer->data = conn;
                   5490:        }
                   5491: 
                   5492:        return xfer;
                   5493: }
                   5494: 
                   5495: /*
                   5496:  * Called by the Purple core when the user indicates that a
                   5497:  * file is to be sent to a special someone.
                   5498:  */
                   5499: void
                   5500: oscar_send_file(PurpleConnection *gc, const char *who, const char *file)
                   5501: {
                   5502:        PurpleXfer *xfer;
                   5503: 
                   5504:        xfer = oscar_new_xfer(gc, who);
                   5505: 
                   5506:        if (file != NULL)
                   5507:                purple_xfer_request_accepted(xfer, file);
                   5508:        else
                   5509:                purple_xfer_request(xfer);
                   5510: }
                   5511: 
                   5512: GList *
                   5513: oscar_actions(PurplePlugin *plugin, gpointer context)
                   5514: {
                   5515:        PurpleConnection *gc = (PurpleConnection *) context;
                   5516:        OscarData *od = purple_connection_get_protocol_data(gc);
                   5517:        GList *menu = NULL;
                   5518:        PurplePluginAction *act;
                   5519: 
                   5520:        act = purple_plugin_action_new(_("Set User Info..."),
                   5521:                        oscar_show_set_info);
                   5522:        menu = g_list_prepend(menu, act);
                   5523: 
                   5524:        if (od->icq)
                   5525:        {
                   5526:                act = purple_plugin_action_new(_("Set User Info (web)..."),
                   5527:                                oscar_show_set_info_icqurl);
                   5528:                menu = g_list_prepend(menu, act);
                   5529:        }
                   5530: 
                   5531:        act = purple_plugin_action_new(_("Change Password..."),
                   5532:                        oscar_change_pass);
                   5533:        menu = g_list_prepend(menu, act);
                   5534: 
                   5535:        if (od->authinfo != NULL && od->authinfo->chpassurl != NULL)
                   5536:        {
                   5537:                /* This only happens when connecting with the old-style BUCP login */
                   5538:                act = purple_plugin_action_new(_("Change Password (web)"),
                   5539:                                oscar_show_chpassurl);
                   5540:                menu = g_list_prepend(menu, act);
                   5541:        }
                   5542: 
                   5543:        if (!od->icq)
                   5544:        {
                   5545:                act = purple_plugin_action_new(_("Configure IM Forwarding (web)"),
                   5546:                                oscar_show_imforwardingurl);
                   5547:                menu = g_list_prepend(menu, act);
                   5548:        }
                   5549: 
                   5550:        menu = g_list_prepend(menu, NULL);
                   5551: 
                   5552:        if (od->icq)
                   5553:        {
                   5554:                /* ICQ actions */
                   5555:                act = purple_plugin_action_new(_("Set Privacy Options..."),
                   5556:                                oscar_show_icq_privacy_opts);
                   5557:                menu = g_list_prepend(menu, act);
                   5558: 
                   5559:                act = purple_plugin_action_new(_("Show Visible List"), oscar_show_visible_list);
                   5560:                menu = g_list_prepend(menu, act);
                   5561: 
                   5562:                act = purple_plugin_action_new(_("Show Invisible List"), oscar_show_invisible_list);
                   5563:                menu = g_list_prepend(menu, act);
                   5564:        }
                   5565:        else
                   5566:        {
                   5567:                /* AIM actions */
                   5568:                act = purple_plugin_action_new(_("Confirm Account"),
                   5569:                                oscar_confirm_account);
                   5570:                menu = g_list_prepend(menu, act);
                   5571: 
                   5572:                act = purple_plugin_action_new(_("Display Currently Registered Email Address"),
                   5573:                                oscar_show_email);
                   5574:                menu = g_list_prepend(menu, act);
                   5575: 
                   5576:                act = purple_plugin_action_new(_("Change Currently Registered Email Address..."),
                   5577:                                oscar_show_change_email);
                   5578:                menu = g_list_prepend(menu, act);
                   5579:        }
                   5580: 
                   5581:        menu = g_list_prepend(menu, NULL);
                   5582: 
                   5583:        act = purple_plugin_action_new(_("Show Buddies Awaiting Authorization"),
                   5584:                        oscar_show_awaitingauth);
                   5585:        menu = g_list_prepend(menu, act);
                   5586: 
                   5587:        menu = g_list_prepend(menu, NULL);
                   5588: 
                   5589:        act = purple_plugin_action_new(_("Search for Buddy by Email Address..."),
                   5590:                        oscar_show_find_email);
                   5591:        menu = g_list_prepend(menu, act);
                   5592: 
                   5593:        menu = g_list_reverse(menu);
                   5594: 
                   5595:        return menu;
                   5596: }
                   5597: 
                   5598: void oscar_change_passwd(PurpleConnection *gc, const char *old, const char *new)
                   5599: {
                   5600:        OscarData *od = purple_connection_get_protocol_data(gc);
                   5601: 
                   5602:        if (od->icq) {
                   5603:                aim_icq_changepasswd(od, new);
                   5604:        } else {
                   5605:                FlapConnection *conn;
                   5606:                conn = flap_connection_getbytype(od, SNAC_FAMILY_ADMIN);
                   5607:                if (conn) {
                   5608:                        aim_admin_changepasswd(od, conn, new, old);
                   5609:                } else {
                   5610:                        od->chpass = TRUE;
                   5611:                        od->oldp = g_strdup(old);
                   5612:                        od->newp = g_strdup(new);
                   5613:                        aim_srv_requestnew(od, SNAC_FAMILY_ADMIN);
                   5614:                }
                   5615:        }
                   5616: }
                   5617: 
                   5618: void
                   5619: oscar_convo_closed(PurpleConnection *gc, const char *who)
                   5620: {
                   5621:        OscarData *od;
                   5622:        PeerConnection *conn;
                   5623: 
                   5624:        od = purple_connection_get_protocol_data(gc);
                   5625:        conn = peer_connection_find_by_type(od, who, OSCAR_CAPABILITY_DIRECTIM);
                   5626: 
                   5627:        if (conn != NULL)
                   5628:        {
                   5629:                if (!conn->ready)
                   5630:                        aim_im_sendch2_cancel(conn);
                   5631: 
                   5632:                peer_connection_destroy(conn, OSCAR_DISCONNECT_LOCAL_CLOSED, NULL);
                   5633:        }
                   5634: }
                   5635: 
                   5636: const char *
                   5637: oscar_normalize(const PurpleAccount *account, const char *str)
                   5638: {
                   5639:        static char buf[BUF_LEN];
                   5640:        char *tmp1, *tmp2;
                   5641:        int i, j;
                   5642: 
                   5643:        g_return_val_if_fail(str != NULL, NULL);
                   5644: 
                   5645:        /* copy str to buf and skip all blanks */
                   5646:        i = 0;
                   5647:        for (j = 0; str[j]; j++) {
                   5648:                if (str[j] != ' ') {
                   5649:                        buf[i++] = str[j];
                   5650:                        if (i >= BUF_LEN - 1)
                   5651:                                break;
                   5652:                }
                   5653:        }
                   5654:        buf[i] = '\0';
                   5655: 
                   5656:        tmp1 = g_utf8_strdown(buf, -1);
                   5657:        tmp2 = g_utf8_normalize(tmp1, -1, G_NORMALIZE_DEFAULT);
                   5658:        if (strlen(tmp2) > sizeof(buf) - 1) {
                   5659:                purple_debug_error("oscar", "normalized string exceeds buffer length!\n");
                   5660:        }
                   5661:        g_strlcpy(buf, tmp2, sizeof(buf));
                   5662:        g_free(tmp2);
                   5663:        g_free(tmp1);
                   5664: 
                   5665:        return buf;
                   5666: }
                   5667: 
                   5668: gboolean
                   5669: oscar_offline_message(const PurpleBuddy *buddy)
                   5670: {
                   5671:        return TRUE;
                   5672: }
                   5673: 
                   5674: /* TODO: Find somewhere to put this instead of including it in a bunch of places.
                   5675:  * Maybe just change purple_accounts_find() to return anything for the prpl if there is no acct_id.
                   5676:  */
                   5677: static PurpleAccount *find_acct(const char *prpl, const char *acct_id)
                   5678: {
                   5679:        PurpleAccount *acct = NULL;
                   5680: 
                   5681:        /* If we have a specific acct, use it */
                   5682:        if (acct_id) {
                   5683:                acct = purple_accounts_find(acct_id, prpl);
                   5684:                if (acct && !purple_account_is_connected(acct))
                   5685:                        acct = NULL;
                   5686:        } else { /* Otherwise find an active account for the protocol */
                   5687:                GList *l = purple_accounts_get_all();
                   5688:                while (l) {
                   5689:                        if (purple_strequal(prpl, purple_account_get_protocol_id(l->data))
                   5690:                                        && purple_account_is_connected(l->data)) {
                   5691:                                acct = l->data;
                   5692:                                break;
                   5693:                        }
                   5694:                        l = l->next;
                   5695:                }
                   5696:        }
                   5697: 
                   5698:        return acct;
                   5699: }
                   5700: 
                   5701: 
                   5702: static gboolean oscar_uri_handler(const char *proto, const char *cmd, GHashTable *params)
                   5703: {
                   5704:        char *acct_id = g_hash_table_lookup(params, "account");
                   5705:        char prpl[11];
                   5706:        PurpleAccount *acct;
                   5707: 
                   5708:        if (g_ascii_strcasecmp(proto, "aim") && g_ascii_strcasecmp(proto, "icq"))
                   5709:                return FALSE;
                   5710: 
                   5711:        g_snprintf(prpl, sizeof(prpl), "prpl-%s", proto);
                   5712: 
                   5713:        acct = find_acct(prpl, acct_id);
                   5714: 
                   5715:        if (!acct)
                   5716:                return FALSE;
                   5717: 
                   5718:        /* aim:GoIM?screenname=SCREENNAME&message=MESSAGE */
                   5719:        if (!g_ascii_strcasecmp(cmd, "GoIM")) {
                   5720:                char *bname = g_hash_table_lookup(params, "screenname");
                   5721:                if (bname) {
                   5722:                        char *message = g_hash_table_lookup(params, "message");
                   5723: 
                   5724:                        PurpleConversation *conv = purple_find_conversation_with_account(
                   5725:                                PURPLE_CONV_TYPE_IM, bname, acct);
                   5726:                        if (conv == NULL)
                   5727:                                conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, acct, bname);
                   5728:                        purple_conversation_present(conv);
                   5729: 
                   5730:                        if (message) {
                   5731:                                /* Spaces are encoded as '+' */
                   5732:                                g_strdelimit(message, "+", ' ');
                   5733:                                purple_conv_send_confirm(conv, message);
                   5734:                        }
                   5735:                }
                   5736:                /*else
                   5737:                        **If pidgindialogs_im() was in the core, we could use it here.
                   5738:                         * It is all purple_request_* based, but I'm not sure it really belongs in the core
                   5739:                        pidgindialogs_im();*/
                   5740: 
                   5741:                return TRUE;
                   5742:        }
                   5743:        /* aim:GoChat?roomname=CHATROOMNAME&exchange=4 */
                   5744:        else if (!g_ascii_strcasecmp(cmd, "GoChat")) {
                   5745:                char *rname = g_hash_table_lookup(params, "roomname");
                   5746:                if (rname) {
                   5747:                        /* This is somewhat hacky, but the params aren't useful after this command */
                   5748:                        g_hash_table_insert(params, g_strdup("exchange"), g_strdup("4"));
                   5749:                        g_hash_table_insert(params, g_strdup("room"), g_strdup(rname));
                   5750:                        serv_join_chat(purple_account_get_connection(acct), params);
                   5751:                }
                   5752:                /*else
                   5753:                        ** Same as above (except that this would have to be re-written using purple_request_*)
                   5754:                        pidgin_blist_joinchat_show(); */
                   5755: 
                   5756:                return TRUE;
                   5757:        }
                   5758:        /* aim:AddBuddy?screenname=SCREENNAME&groupname=GROUPNAME*/
                   5759:        else if (!g_ascii_strcasecmp(cmd, "AddBuddy")) {
                   5760:                char *bname = g_hash_table_lookup(params, "screenname");
                   5761:                char *gname = g_hash_table_lookup(params, "groupname");
                   5762:                purple_blist_request_add_buddy(acct, bname, gname, NULL);
                   5763:                return TRUE;
                   5764:        }
                   5765: 
                   5766:        return FALSE;
                   5767: }
                   5768: 
                   5769: void oscar_init(PurplePlugin *plugin, gboolean is_icq)
                   5770: {
                   5771:        PurplePluginProtocolInfo *prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(plugin);
                   5772:        PurpleAccountOption *option;
                   5773:        static gboolean init = FALSE;
                   5774:        static const gchar *encryption_keys[] = {
                   5775:                N_("Use encryption if available"),
                   5776:                N_("Require encryption"),
                   5777:                N_("Don't use encryption"),
                   5778:                NULL
                   5779:        };
                   5780:        static const gchar *encryption_values[] = {
                   5781:                OSCAR_OPPORTUNISTIC_ENCRYPTION,
                   5782:                OSCAR_REQUIRE_ENCRYPTION,
                   5783:                OSCAR_NO_ENCRYPTION,
                   5784:                NULL
                   5785:        };
                   5786:        static const gchar *aim_login_keys[] = {
                   5787:                N_("clientLogin"),
                   5788:                N_("Kerberos"),
                   5789:                N_("MD5-based"),
                   5790:                NULL
                   5791:        };
                   5792:        static const gchar *aim_login_values[] = {
                   5793:                OSCAR_CLIENT_LOGIN,
                   5794:                OSCAR_KERBEROS_LOGIN,
                   5795:                OSCAR_MD5_LOGIN,
                   5796:                NULL
                   5797:        };
                   5798:        static const gchar *icq_login_keys[] = {
                   5799:                N_("clientLogin"),
                   5800:                N_("MD5-based"),
                   5801:                NULL
                   5802:        };
                   5803:        static const gchar *icq_login_values[] = {
                   5804:                OSCAR_CLIENT_LOGIN,
                   5805:                OSCAR_MD5_LOGIN,
                   5806:                NULL
                   5807:        };
                   5808:        const gchar **login_keys;
                   5809:        const gchar **login_values;
                   5810:        GList *encryption_options = NULL;
                   5811:        GList *login_options = NULL;
                   5812:        int i;
                   5813: 
                   5814:        option = purple_account_option_string_new(_("Server"), "server", get_login_server(is_icq, TRUE));
                   5815:        prpl_info->protocol_options = g_list_append(prpl_info->protocol_options, option);
                   5816: 
                   5817:        option = purple_account_option_int_new(_("Port"), "port", OSCAR_DEFAULT_LOGIN_PORT);
                   5818:        prpl_info->protocol_options = g_list_append(prpl_info->protocol_options, option);
                   5819: 
                   5820:        for (i = 0; encryption_keys[i]; i++) {
                   5821:                PurpleKeyValuePair *kvp = g_new0(PurpleKeyValuePair, 1);
                   5822:                kvp->key = g_strdup(_(encryption_keys[i]));
                   5823:                kvp->value = g_strdup(encryption_values[i]);
                   5824:                encryption_options = g_list_append(encryption_options, kvp);
                   5825:        }
                   5826:        option = purple_account_option_list_new(_("Connection security"), "encryption", encryption_options);
                   5827:        prpl_info->protocol_options = g_list_append(prpl_info->protocol_options, option);
                   5828: 
                   5829:        if (is_icq) {
                   5830:                login_keys = icq_login_keys;
                   5831:                login_values = icq_login_values;
                   5832:        } else {
                   5833:                login_keys = aim_login_keys;
                   5834:                login_values = aim_login_values;
                   5835:        }
                   5836:        for (i = 0; login_keys[i]; i++) {
                   5837:                PurpleKeyValuePair *kvp = g_new0(PurpleKeyValuePair, 1);
                   5838:                kvp->key = g_strdup(_(login_keys[i]));
                   5839:                kvp->value = g_strdup(login_values[i]);
                   5840:                login_options = g_list_append(login_options, kvp);
                   5841:        }
                   5842:        option = purple_account_option_list_new(_("Authentication method"), "login_type", login_options);
                   5843:        prpl_info->protocol_options = g_list_append(prpl_info->protocol_options, option);
                   5844: 
                   5845:        option = purple_account_option_bool_new(
                   5846:                _("Always use AIM/ICQ proxy server for\nfile transfers and direct IM (slower,\nbut does not reveal your IP address)"), "always_use_rv_proxy",
                   5847:                OSCAR_DEFAULT_ALWAYS_USE_RV_PROXY);
                   5848:        prpl_info->protocol_options = g_list_append(prpl_info->protocol_options, option);
                   5849: 
                   5850:        if (purple_strequal(purple_plugin_get_id(plugin), "prpl-aim")) {
                   5851:                option = purple_account_option_bool_new(_("Allow multiple simultaneous logins"), "allow_multiple_logins",
                   5852:                                                                                                OSCAR_DEFAULT_ALLOW_MULTIPLE_LOGINS);
                   5853:                prpl_info->protocol_options = g_list_append(prpl_info->protocol_options, option);
                   5854:        }
                   5855: 
                   5856:        if (init)
                   5857:                return;
                   5858:        init = TRUE;
                   5859: 
                   5860:        /* Preferences */
                   5861:        purple_prefs_add_none("/plugins/prpl/oscar");
                   5862:        purple_prefs_add_bool("/plugins/prpl/oscar/recent_buddies", FALSE);
                   5863: 
                   5864:        purple_prefs_remove("/plugins/prpl/oscar/show_idle");
                   5865:        purple_prefs_remove("/plugins/prpl/oscar/always_use_rv_proxy");
                   5866: 
                   5867:        /* protocol handler */
                   5868:        /* TODO: figure out a good instance to use here */
                   5869:        purple_signal_connect(purple_get_core(), "uri-handler", &init,
                   5870:                PURPLE_CALLBACK(oscar_uri_handler), NULL);
                   5871: }
                   5872: 

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>