Changeset 537d9b9 for protocols


Ignore:
Timestamp:
2016-11-20T08:40:36Z (8 years ago)
Author:
dequis <dx@…>
Children:
3f44e43
Parents:
ba52ac5 (diff), 9f03c47 (diff)
Note: this is a merge changeset, the changes displayed below correspond to the merge itself.
Use the (diff) links above to see all the changes relative to each parent.
Message:

Merge master up to commit '9f03c47' into parson

Location:
protocols
Files:
27 edited

Legend:

Unmodified
Added
Removed
  • protocols/account.c

    rba52ac5 r537d9b9  
    6363
    6464        s = set_add(&a->set, "password", NULL, set_eval_account, a);
    65         s->flags |= SET_NOSAVE | SET_NULL_OK | SET_PASSWORD;
     65        s->flags |= SET_NOSAVE | SET_NULL_OK | SET_PASSWORD | ACC_SET_LOCKABLE;
    6666
    6767        s = set_add(&a->set, "tag", NULL, set_eval_account, a);
     
    6969
    7070        s = set_add(&a->set, "username", NULL, set_eval_account, a);
    71         s->flags |= SET_NOSAVE | ACC_SET_OFFLINE_ONLY;
     71        s->flags |= SET_NOSAVE | ACC_SET_OFFLINE_ONLY | ACC_SET_LOCKABLE;
    7272        set_setstr(&a->set, "username", user);
    7373
  • protocols/account.h

    rba52ac5 r537d9b9  
    6161        ACC_SET_OFFLINE_ONLY = 0x02,    /* Allow changes only if the acct is offline. */
    6262        ACC_SET_ONLINE_ONLY = 0x04,     /* Allow changes only if the acct is online. */
     63        ACC_SET_LOCKABLE = 0x08         /* Setting cannot be changed if the account is locked down */
    6364} account_set_flag_t;
    6465
     
    6869        ACC_FLAG_HANDLE_DOMAINS = 0x04, /* Contact handles need a domain portion. */
    6970        ACC_FLAG_LOCAL_CONTACTS = 0x08, /* Contact list is local. */
     71        ACC_FLAG_LOCKED = 0x10,         /* Account is locked (cannot be deleted, certain settings can't changed) */
    7072} account_flag_t;
    7173
  • protocols/bee.h

    rba52ac5 r537d9b9  
    8484} bee_user_t;
    8585
     86typedef struct bee_chat_info {
     87        char *title;
     88        char *topic;
     89} bee_chat_info_t;
     90
    8691/* This one's mostly used so save space and make it easier (cheaper) to
    8792   compare groups of contacts, etc. */
     
    131136
    132137        void (*log)(bee_t *bee, const char *tag, const char *msg);
     138        gboolean (*user_nick_change)(bee_t *bee, bee_user_t *bu, const char *hint);
    133139} bee_ui_funcs_t;
    134140
     
    184190G_MODULE_EXPORT void imcb_chat_invite(struct im_connection *ic, const char *name, const char *who, const char *msg);
    185191
     192G_GNUC_DEPRECATED G_MODULE_EXPORT void bee_chat_list_finish(struct im_connection *ic);
     193G_MODULE_EXPORT void imcb_chat_list_finish(struct im_connection *ic);
     194G_MODULE_EXPORT void imcb_chat_list_free(struct im_connection *ic);
     195
    186196#endif /* __BEE_H__ */
  • protocols/bee_chat.c

    rba52ac5 r537d9b9  
    274274        }
    275275}
     276
     277void imcb_chat_list_finish(struct im_connection *ic)
     278{
     279        cmd_chat_list_finish(ic);
     280}
     281
     282void bee_chat_list_finish(struct im_connection *ic)
     283{
     284        imcb_log(ic, "Warning: using deprecated bee_chat_list_finish. This will be removed in the stable release.");
     285        imcb_chat_list_finish(ic);
     286}
     287
     288void imcb_chat_list_free(struct im_connection *ic)
     289{
     290        bee_chat_info_t *ci;
     291        GSList *l = ic->chatlist;
     292
     293        while (l) {
     294                ci = l->data;
     295                l = g_slist_delete_link(l, l);
     296
     297                g_free(ci->title);
     298                g_free(ci->topic);
     299                g_free(ci);
     300        }
     301
     302        ic->chatlist = NULL;
     303}
  • protocols/bee_ft.c

    rba52ac5 r537d9b9  
    3131        bee_user_t *bu = bee_user_by_handle(bee, ic, handle);
    3232
    33         if (bee->ui->ft_in_start) {
     33        if (bee->ui->ft_in_start && bu) {
    3434                return bee->ui->ft_in_start(bee, bu, file_name, file_size);
    3535        } else {
  • protocols/jabber/conference.c

    rba52ac5 r537d9b9  
    2828static xt_status jabber_chat_self_message(struct im_connection *ic, struct xt_node *node, struct xt_node *orig);
    2929
    30 struct groupchat *jabber_chat_join(struct im_connection *ic, const char *room, const char *nick, const char *password)
     30struct groupchat *jabber_chat_join(struct im_connection *ic, const char *room, const char *nick, const char *password,
     31                                   gboolean always_use_nicks)
    3132{
    3233        struct jabber_chat *jc;
     
    5758                g_free(jc);
    5859                return NULL;
     60        }
     61
     62        if (always_use_nicks) {
     63                jc->flags = JCFLAG_ALWAYS_USE_NICKS;
    5964        }
    6065
     
    95100        g_free(cserv);
    96101
    97         c = jabber_chat_join(ic, rjid, jd->username, NULL);
     102        c = jabber_chat_join(ic, rjid, jd->username, NULL, FALSE);
    98103        g_free(rjid);
    99104        if (c == NULL) {
     
    244249}
    245250
     251static int jabber_chat_has_other_resources(struct im_connection *ic, struct jabber_buddy *bud)
     252{
     253        struct jabber_buddy *cur;
     254
     255        for (cur = jabber_buddy_by_jid(ic, bud->bare_jid, GET_BUDDY_FIRST); cur; cur = cur->next) {
     256                if (cur != bud && jabber_compare_jid(cur->ext_jid, bud->ext_jid)) {
     257                        return TRUE;
     258                }
     259        }
     260       
     261        return FALSE;
     262}
     263
    246264/* Not really the same syntax as the normal pkt_ functions, but this isn't
    247265   called by the xmltree parser directly and this way I can add some extra
     
    328346                        *s = 0; /* Should NEVER be NULL, but who knows... */
    329347                }
     348
     349                if (bud != jc->me && (jc->flags & JCFLAG_ALWAYS_USE_NICKS) && !(bud->flags & JBFLAG_IS_ANONYMOUS)) {
     350                        imcb_buddy_nick_change(ic, bud->ext_jid, bud->resource);
     351                }
     352
    330353                imcb_chat_add_buddy(chat, bud->ext_jid);
    331354                if (s) {
     
    333356                }
    334357        } else if (type) { /* type can only be NULL or "unavailable" in this function */
    335                 if ((bud->flags & JBFLAG_IS_CHATROOM) && bud->ext_jid) {
     358                if ((bud->flags & JBFLAG_IS_CHATROOM) && bud->ext_jid && !jabber_chat_has_other_resources(ic, bud)) {
    336359                        char *reason = NULL;
    337360                        char *status = NULL;
     
    443466
    444467        if (subject && chat) {
    445                 char *subject_text = subject->text_len > 0 ? subject->text : "";
     468                char empty[1] = "";
     469                char *subject_text = subject->text_len > 0 ? subject->text : empty;
    446470                if (g_strcmp0(chat->topic, subject_text) != 0) {
    447471                        bare_jid = (bud) ? jabber_get_bare_jid(bud->ext_jid) : NULL;
     
    479503                bare_jid = jabber_get_bare_jid(bud->ext_jid ? bud->ext_jid : bud->full_jid);
    480504                final_from = bare_jid;
    481                 flags = (bud == jc->me) ? OPT_SELFMESSAGE : 0;
     505                if (bud == jc->me || (g_strcasecmp(final_from, ic->acc->user) == 0)) {
     506                        flags = OPT_SELFMESSAGE;
     507                }
    482508        } else {
    483509                final_from = nick;
  • protocols/jabber/iq.c

    rba52ac5 r537d9b9  
    6767                        xt_add_child(reply, xt_new_node("name", set_getstr(&ic->acc->set, "user_agent"), NULL));
    6868                        xt_add_child(reply, xt_new_node("version", BITLBEE_VERSION, NULL));
    69                         xt_add_child(reply, xt_new_node("os", ARCH, NULL));
    7069                } else if (strcmp(s, XMLNS_TIME_OLD) == 0) {
    7170                        time_t time_ep;
     
    234233                imcb_log(ic, "Warning: Received incomplete IQ packet while authenticating");
    235234                imc_logout(ic, FALSE);
    236                 return XT_HANDLED;
     235                return XT_ABORT;
    237236        }
    238237
     
    287286                imcb_log(ic, "Warning: Received incomplete IQ packet while authenticating");
    288287                imc_logout(ic, FALSE);
    289                 return XT_HANDLED;
     288                return XT_ABORT;
    290289        }
    291290
     
    10601059        return XT_HANDLED;
    10611060}
     1061
     1062xt_status jabber_iq_disco_muc_response(struct im_connection *ic, struct xt_node *node, struct xt_node *orig);
     1063
     1064int jabber_iq_disco_muc(struct im_connection *ic, const char *muc_server)
     1065{
     1066        struct xt_node *node;
     1067        int st;
     1068
     1069        node = xt_new_node("query", NULL, NULL);
     1070        xt_add_attr(node, "xmlns", XMLNS_DISCO_ITEMS);
     1071        node = jabber_make_packet("iq", "get", (char *) muc_server, node);
     1072
     1073        jabber_cache_add(ic, node, jabber_iq_disco_muc_response);
     1074        st = jabber_write_packet(ic, node);
     1075
     1076        return st;
     1077}
     1078
     1079xt_status jabber_iq_disco_muc_response(struct im_connection *ic, struct xt_node *node, struct xt_node *orig)
     1080{
     1081        struct xt_node *query, *c;
     1082        struct jabber_error *err;
     1083        GSList *rooms =  NULL;
     1084
     1085        if ((err = jabber_error_parse(xt_find_node(node->children, "error"), XMLNS_STANZA_ERROR))) {
     1086                imcb_error(ic, "The server replied with an error: %s%s%s",
     1087                           err->code, err->text ? ": " : "", err->text ? err->text : "");
     1088                jabber_error_free(err);
     1089                return XT_HANDLED;
     1090        }
     1091
     1092        if (!(query = xt_find_node(node->children, "query"))) {
     1093                imcb_error(ic, "Received incomplete MUC list reply");
     1094                return XT_HANDLED;
     1095        }
     1096
     1097        c = query->children;
     1098        while ((c = xt_find_node(c, "item"))) {
     1099                char *jid = xt_find_attr(c, "jid");
     1100
     1101                if (!jid || !strchr(jid, '@')) {
     1102                        c = c->next;
     1103                        continue;
     1104                }
     1105
     1106                bee_chat_info_t *ci = g_new(bee_chat_info_t, 1);
     1107                ci->title = g_strdup(xt_find_attr(c, "jid"));
     1108                ci->topic = g_strdup(xt_find_attr(c, "name"));
     1109                rooms = g_slist_prepend(rooms, ci);
     1110
     1111                c = c->next;
     1112        }
     1113
     1114        imcb_chat_list_free(ic);
     1115        ic->chatlist = g_slist_reverse(rooms);
     1116        imcb_chat_list_finish(ic);
     1117
     1118        return XT_HANDLED;
     1119}
  • protocols/jabber/jabber.c

    rba52ac5 r537d9b9  
    8282        s->flags |= SET_NOSAVE | ACC_SET_OFFLINE_ONLY | SET_NULL_OK;
    8383
     84        set_add(&acc->set, "oauth", "false", set_eval_oauth, acc);
     85
    8486        if (strcmp(acc->prpl->name, "hipchat") == 0) {
    8587                set_setstr(&acc->set, "server", "chat.hipchat.com");
    8688        } else {
    87                 set_add(&acc->set, "oauth", "false", set_eval_oauth, acc);
    88 
    8989                /* this reuses set_eval_oauth, which clears the password */
    9090                set_add(&acc->set, "anonymous", "false", set_eval_oauth, acc);
     
    321321        struct jabber_data *jd = ic->proto_data;
    322322
     323        imcb_chat_list_free(ic);
     324
    323325        while (jd->filetransfers) {
    324326                imcb_file_canceled(ic, (( struct jabber_transfer *) jd->filetransfers->data)->ft, "Logging out");
     
    397399        if (g_strcasecmp(who, JABBER_OAUTH_HANDLE) == 0 &&
    398400            !(jd->flags & OPT_LOGGED_IN) && jd->fd == -1) {
    399                 if (sasl_oauth2_get_refresh_token(ic, message)) {
     401
     402                if (jd->flags & JFLAG_HIPCHAT) {
     403                        sasl_oauth2_got_token(ic, message, NULL, NULL);
     404                        return 1;
     405                } else if (sasl_oauth2_get_refresh_token(ic, message)) {
    400406                        return 1;
    401407                } else {
     
    577583        } else {
    578584                /* jabber_chat_join without the underscore is the conference.c one */
    579                 return jabber_chat_join(ic, room, final_nick, set_getstr(sets, "password"));
     585                return jabber_chat_join(ic, room, final_nick, set_getstr(sets, "password"),
     586                                        set_getbool(sets, "always_use_nicks"));
    580587        }
    581588
     
    586593{
    587594        return jabber_chat_with(ic, who);
     595}
     596
     597static void jabber_chat_list_(struct im_connection *ic, const char *server)
     598{
     599        struct jabber_data *jd = ic->proto_data;
     600
     601        if (server && *server) {
     602                jabber_iq_disco_muc(ic, server);
     603        } else if (jd->muc_host && *jd->muc_host) {
     604                jabber_iq_disco_muc(ic, jd->muc_host);
     605        } else {
     606                /* throw an error here, don't query conference.[server] directly.
     607                 * for things like jabber.org it gets you 18000 results of garbage */
     608                imcb_error(ic, "Please specify a server name such as `conference.%s'", jd->server);
     609        }
    588610}
    589611
     
    686708void jabber_chat_add_settings(account_t *acc, set_t **head)
    687709{
     710        set_add(head, "always_use_nicks", "false", set_eval_bool, NULL);
     711
    688712        /* Meh. Stupid room passwords. Not trying to obfuscate/hide
    689713           them from the user for now. */
     
    693717void jabber_chat_free_settings(account_t *acc, set_t **head)
    694718{
     719        set_del(head, "always_use_nicks");
     720
    695721        set_del(head, "password");
    696722}
     
    759785        ret->chat_join = jabber_chat_join_;
    760786        ret->chat_with = jabber_chat_with_;
     787        ret->chat_list = jabber_chat_list_;
    761788        ret->chat_add_settings = jabber_chat_add_settings;
    762789        ret->chat_free_settings = jabber_chat_free_settings;
  • protocols/jabber/jabber.h

    rba52ac5 r537d9b9  
    7575        JCFLAG_MESSAGE_SENT = 1,        /* Set this after sending the first message, so
    7676                                           we can detect echoes/backlogs. */
     77        JCFLAG_ALWAYS_USE_NICKS = 2,
    7778} jabber_chat_flags_t;
    7879
     
    262263void jabber_iq_version_send(struct im_connection *ic, struct jabber_buddy *bud, void *data);
    263264int jabber_iq_disco_server(struct im_connection *ic);
     265int jabber_iq_disco_muc(struct im_connection *ic, const char *muc_server);
    264266
    265267/* si.c */
     
    339341int sasl_oauth2_get_refresh_token(struct im_connection *ic, const char *msg);
    340342int sasl_oauth2_refresh(struct im_connection *ic, const char *refresh_token);
     343void sasl_oauth2_got_token(gpointer data, const char *access_token, const char *refresh_token, const char *error);
    341344
    342345extern const struct oauth2_service oauth2_service_google;
    343346
    344347/* conference.c */
    345 struct groupchat *jabber_chat_join(struct im_connection *ic, const char *room, const char *nick, const char *password);
     348struct groupchat *jabber_chat_join(struct im_connection *ic, const char *room, const char *nick, const char *password,
     349                                   gboolean always_use_nicks);
    346350struct groupchat *jabber_chat_with(struct im_connection *ic, char *who);
    347351struct groupchat *jabber_chat_by_jid(struct im_connection *ic, const char *name);
  • protocols/jabber/jabber_util.c

    rba52ac5 r537d9b9  
    315315        int i;
    316316
     317        if (!jid1 || !jid2) {
     318                return FALSE;
     319        }
     320
    317321        for (i = 0;; i++) {
    318322                if (jid1[i] == '\0' || jid1[i] == '/' || jid2[i] == '\0' || jid2[i] == '/') {
  • protocols/jabber/message.c

    rba52ac5 r537d9b9  
    2727{
    2828        struct im_connection *ic = data;
    29         struct jabber_data *jd = ic->proto_data;
    3029        char *from = xt_find_attr(node, carbons_sent ? "to" : "from");
    3130        char *type = xt_find_attr(node, "type");
     
    3837        if (!from) {
    3938                return XT_HANDLED; /* Consider this packet corrupted. */
    40         }
    41 
    42         /* try to detect hipchat's own version of self-messages */
    43         if (jd->flags & JFLAG_HIPCHAT) {
    44                 struct xt_node *c;
    45 
    46                 if ((c = xt_find_node_by_attr(node->children, "delay", "xmlns", XMLNS_DELAY)) &&
    47                     (s = xt_find_attr(c, "from_jid")) &&
    48                     jabber_compare_jid(s, jd->me)) {
    49                         carbons_sent = TRUE;
    50                 }
    5139        }
    5240
  • protocols/jabber/presence.c

    rba52ac5 r537d9b9  
    222222        cap = xt_new_node("c", NULL, NULL);
    223223        xt_add_attr(cap, "xmlns", XMLNS_CAPS);
    224 
    225         if (jd->flags & JFLAG_HIPCHAT) {
    226                 /* hipchat specific node, whitelisted by request to receive self-messages */
    227                 xt_add_attr(cap, "node", "http://bitlbee.org/xmpp/caps/hipchat");
    228         } else {
    229                 xt_add_attr(cap, "node", "http://bitlbee.org/xmpp/caps");
    230         }
     224        xt_add_attr(cap, "node", "http://bitlbee.org/xmpp/caps");
    231225        xt_add_attr(cap, "ver", BITLBEE_VERSION);   /* The XEP wants this hashed, but nobody's doing that. */
    232226        xt_add_child(node, cap);
  • protocols/jabber/s5bytestream.c

    rba52ac5 r537d9b9  
    928928                proxy = next;
    929929        }
     930
     931        g_free(proxysetting);
    930932}
    931933
  • protocols/jabber/sasl.c

    rba52ac5 r537d9b9  
    3939};
    4040
     41/* """"""""""""""""""""""""""""""oauth"""""""""""""""""""""""""""""" */
     42#define HIPCHAT_SO_CALLED_OAUTH_URL "https://hipchat.com/account/api"
     43
    4144xt_status sasl_pkt_mechanisms(struct xt_node *node, gpointer data)
    4245{
     
    4548        struct xt_node *c, *reply;
    4649        char *s;
    47         int sup_plain = 0, sup_digest = 0, sup_gtalk = 0, sup_anonymous = 0;
     50        int sup_plain = 0, sup_digest = 0, sup_gtalk = 0, sup_anonymous = 0, sup_hipchat_oauth = 0;
    4851        int want_oauth = FALSE, want_hipchat = FALSE, want_anonymous = FALSE;
    4952        GString *mechs;
     
    8083                } else if (c->text && g_strcasecmp(c->text, "X-OAUTH2") == 0) {
    8184                        sup_gtalk = 1;
     85                } else if (c->text && g_strcasecmp(c->text, "X-HIPCHAT-OAUTH2") == 0) {
     86                        sup_hipchat_oauth = 1;
    8287                }
    8388
     
    9095
    9196        if (!want_oauth && !sup_plain && !sup_digest) {
    92                 if (!sup_gtalk) {
     97                if (sup_gtalk || sup_hipchat_oauth) {
    9398                        imcb_error(ic, "This server requires OAuth "
    9499                                   "(supported schemes:%s)", mechs->str);
     
    110115        }
    111116
    112         if (sup_gtalk && want_oauth) {
    113                 int len;
    114 
    115                 /* X-OAUTH2 is, not *the* standard OAuth2 SASL/XMPP implementation.
    116                    It's currently used by GTalk and vaguely documented on
    117                    http://code.google.com/apis/cloudprint/docs/rawxmpp.html . */
    118                 xt_add_attr(reply, "mechanism", "X-OAUTH2");
    119 
    120                 len = strlen(jd->username) + strlen(jd->oauth2_access_token) + 2;
    121                 s = g_malloc(len + 1);
    122                 s[0] = 0;
    123                 strcpy(s + 1, jd->username);
    124                 strcpy(s + 2 + strlen(jd->username), jd->oauth2_access_token);
    125                 reply->text = base64_encode((unsigned char *) s, len);
     117        if ((sup_gtalk || sup_hipchat_oauth) && want_oauth) {
     118                GString *gs;
     119
     120                gs = g_string_sized_new(128);
     121
     122                g_string_append_c(gs, '\0');
     123
     124                if (sup_gtalk) {
     125                        /* X-OAUTH2 is not *the* standard OAuth2 SASL/XMPP implementation.
     126                           It's currently used by GTalk and vaguely documented on
     127                           http://code.google.com/apis/cloudprint/docs/rawxmpp.html */
     128                        xt_add_attr(reply, "mechanism", "X-OAUTH2");
     129
     130                        g_string_append(gs, jd->username);
     131                        g_string_append_c(gs, '\0');
     132                        g_string_append(gs, jd->oauth2_access_token);
     133                } else if (sup_hipchat_oauth) {
     134                        /* Hipchat's variant, not standard either, is documented here:
     135                           https://docs.atlassian.com/hipchat.xmpp/latest/xmpp/auth.html */
     136                        xt_add_attr(reply, "mechanism", "oauth2");
     137
     138                        g_string_append(gs, jd->oauth2_access_token);
     139                        g_string_append_c(gs, '\0');
     140                        g_string_append(gs, set_getstr(&ic->acc->set, "resource"));
     141                }
     142
     143                reply->text = base64_encode((unsigned char *) gs->str, gs->len);
    126144                reply->text_len = strlen(reply->text);
    127                 g_free(s);
     145                g_string_free(gs, TRUE);
     146
    128147        } else if (want_oauth) {
    129148                imcb_error(ic, "OAuth requested, but not supported by server");
     
    149168                /* The rest will be done later, when we receive a <challenge/>. */
    150169        } else if (sup_plain) {
    151                 int len;
    152170                GString *gs;
    153171                char *username;
     
    174192                }
    175193
    176                 len = gs->len;
    177                 s = g_string_free(gs, FALSE);
    178 
    179                 reply->text = base64_encode((unsigned char *) s, len);
     194                reply->text = base64_encode((unsigned char *) gs->str, gs->len);
    180195                reply->text_len = strlen(reply->text);
    181                 g_free(s);
     196                g_string_free(gs, TRUE);
    182197        }
    183198
     
    428443{
    429444        struct jabber_data *jd = ic->proto_data;
    430         char *msg, *url;
    431445
    432446        imcb_log(ic, "Starting OAuth authentication");
     
    434448        /* Temporary contact, just used to receive the OAuth response. */
    435449        imcb_add_buddy(ic, JABBER_OAUTH_HANDLE, NULL);
    436         url = oauth2_url(jd->oauth2_service);
    437         msg = g_strdup_printf("Open this URL in your browser to authenticate: %s", url);
    438         imcb_buddy_msg(ic, JABBER_OAUTH_HANDLE, msg, 0, 0);
     450
     451        if (jd->flags & JFLAG_HIPCHAT) {
     452                imcb_buddy_msg(ic, JABBER_OAUTH_HANDLE,
     453                        "Open this URL and generate a token with 'View Group' and 'Send Message' scopes: "
     454                        HIPCHAT_SO_CALLED_OAUTH_URL, 0, 0);
     455        } else {
     456                char *msg, *url;
     457
     458                url = oauth2_url(jd->oauth2_service);
     459                msg = g_strdup_printf("Open this URL in your browser to authenticate: %s", url);
     460                imcb_buddy_msg(ic, JABBER_OAUTH_HANDLE, msg, 0, 0);
     461
     462                g_free(msg);
     463                g_free(url);
     464        }
    439465        imcb_buddy_msg(ic, JABBER_OAUTH_HANDLE, "Respond to this message with the returned "
    440466                       "authorization token.", 0, 0);
    441467
    442         g_free(msg);
    443         g_free(url);
    444468}
    445469
     
    453477        return FALSE;
    454478}
    455 
    456 static void sasl_oauth2_got_token(gpointer data, const char *access_token, const char *refresh_token,
    457                                   const char *error);
    458479
    459480int sasl_oauth2_get_refresh_token(struct im_connection *ic, const char *msg)
     
    486507}
    487508
    488 static void sasl_oauth2_got_token(gpointer data, const char *access_token, const char *refresh_token, const char *error)
     509void sasl_oauth2_got_token(gpointer data, const char *access_token, const char *refresh_token, const char *error)
    489510{
    490511        struct im_connection *ic = data;
  • protocols/jabber/si.c

    rba52ac5 r537d9b9  
    393393        struct jabber_transfer *tf = NULL;
    394394        struct jabber_data *jd = ic->proto_data;
     395        struct jabber_error *err;
    395396
    396397        if (!(tgt_jid = xt_find_attr(node, "from")) ||
    397             !(ini_jid = xt_find_attr(node, "to"))) {
     398            !(ini_jid = xt_find_attr(node, "to")) ||
     399            !(iq_id   = xt_find_attr(node, "id"))) {
    398400                imcb_log(ic, "Invalid SI response from=%s to=%s", tgt_jid, ini_jid);
     401                return XT_HANDLED;
     402        }
     403
     404        /* Let's see if we can find out what this bytestream should be for... */
     405
     406        for (tflist = jd->filetransfers; tflist; tflist = g_slist_next(tflist)) {
     407                struct jabber_transfer *tft = tflist->data;
     408                if ((strcmp(tft->iq_id, iq_id) == 0)) {
     409                        tf = tft;
     410                        break;
     411                }
     412        }
     413
     414        if (!tf) {
     415                imcb_log(ic, "WARNING: Received bytestream request from %s that doesn't match an SI request", ini_jid);
     416                return XT_HANDLED;
     417        }
     418
     419        err = jabber_error_parse(xt_find_node(node->children, "error"), XMLNS_STANZA_ERROR);
     420
     421        if (err) {
     422                if (g_strcmp0(err->code, "forbidden") == 0) {
     423                        imcb_log(ic, "File %s: %s rejected the transfer", tf->ft->file_name, tgt_jid);
     424                } else {
     425                        imcb_log(ic, "Error: Stream initiation request failed: %s (%s)", err->code, err->text);
     426                }
     427                imcb_file_canceled(ic, tf->ft, "Stream initiation request failed");
     428                jabber_error_free(err);
    399429                return XT_HANDLED;
    400430        }
     
    409439         *                                      <value>
    410440         */
    411         if (!(tgt_jid = xt_find_attr(node, "from")) ||
    412             !(ini_jid = xt_find_attr(node, "to")) ||
    413             !(iq_id   = xt_find_attr(node, "id")) ||
    414             !(c = xt_find_node(node->children, "si")) ||
     441        if (!(c = xt_find_node(node->children, "si")) ||
    415442            !(cmp = xt_find_attr(c, "xmlns")) ||
    416443            !(strcmp(cmp, XMLNS_SI) == 0) ||
     
    439466        }
    440467
    441         /* Let's see if we can find out what this bytestream should be for... */
    442 
    443         for (tflist = jd->filetransfers; tflist; tflist = g_slist_next(tflist)) {
    444                 struct jabber_transfer *tft = tflist->data;
    445                 if ((strcmp(tft->iq_id, iq_id) == 0)) {
    446                         tf = tft;
    447                         break;
    448                 }
    449         }
    450 
    451         if (!tf) {
    452                 imcb_log(ic, "WARNING: Received bytestream request from %s that doesn't match an SI request", ini_jid);
    453                 return XT_HANDLED;
    454         }
    455 
    456468        tf->ini_jid = g_strdup(ini_jid);
    457469        tf->tgt_jid = g_strdup(tgt_jid);
  • protocols/nogaim.c

    rba52ac5 r537d9b9  
    4040
    4141#ifdef WITH_PLUGINS
     42GList *plugins = NULL;
     43
     44static gint pluginscmp(gconstpointer a, gconstpointer b, gpointer data)
     45{
     46        const struct plugin_info *ia = a;
     47        const struct plugin_info *ib = b;
     48
     49        return g_strcasecmp(ia->name, ib->name);
     50}
     51
    4252gboolean load_plugin(char *path)
    4353{
     54        GList *l;
     55        struct plugin_info *i;
     56        struct plugin_info *info;
     57        struct plugin_info * (*info_function) (void) = NULL;
    4458        void (*init_function) (void);
    4559
    4660        GModule *mod = g_module_open(path, G_MODULE_BIND_LAZY);
     61        gboolean loaded = FALSE;
    4762
    4863        if (!mod) {
    49                 log_message(LOGLVL_ERROR, "Can't find `%s', not loading (%s)\n", path, g_module_error());
     64                log_message(LOGLVL_ERROR, "Error loading plugin `%s': %s\n", path, g_module_error());
    5065                return FALSE;
     66        }
     67
     68        if (g_module_symbol(mod, "init_plugin_info", (gpointer *) &info_function)) {
     69                info = info_function();
     70
     71                if (info->abiver != BITLBEE_ABI_VERSION_CODE) {
     72                        log_message(LOGLVL_ERROR,
     73                                    "`%s' uses ABI %u but %u is required\n",
     74                                    path, info->abiver,
     75                                    BITLBEE_ABI_VERSION_CODE);
     76                        g_module_close(mod);
     77                        return FALSE;
     78                }
     79
     80                if (!info->name || !info->version) {
     81                        log_message(LOGLVL_ERROR,
     82                                    "Name or version missing from the "
     83                                    "plugin info in `%s'\n", path);
     84                        g_module_close(mod);
     85                        return FALSE;
     86                }
     87
     88                for (l = plugins; l; l = l->next) {
     89                        i = l->data;
     90
     91                        if (g_strcasecmp(i->name, info->name) == 0) {
     92                                loaded = TRUE;
     93                                break;
     94                        }
     95                }
     96
     97                if (loaded) {
     98                        log_message(LOGLVL_WARNING,
     99                                    "%s plugin already loaded\n",
     100                                    info->name);
     101                        g_module_close(mod);
     102                        return FALSE;
     103                }
     104        } else {
     105                log_message(LOGLVL_WARNING, "Can't find function `init_plugin_info' in `%s'\n", path);
    51106        }
    52107
    53108        if (!g_module_symbol(mod, "init_plugin", (gpointer *) &init_function)) {
    54109                log_message(LOGLVL_WARNING, "Can't find function `init_plugin' in `%s'\n", path);
     110                g_module_close(mod);
    55111                return FALSE;
    56112        }
    57113
     114        if (info_function) {
     115                plugins = g_list_insert_sorted_with_data(plugins, info,
     116                                                         pluginscmp, NULL);
     117        }
     118
    58119        init_function();
    59 
    60120        return TRUE;
    61121}
     
    73133
    74134                while ((entry = g_dir_read_name(dir))) {
     135                        if (!g_str_has_suffix(entry, "." G_MODULE_SUFFIX)) {
     136                                continue;
     137                        }
     138
    75139                        path = g_build_filename(global.conf->plugindir, entry, NULL);
    76140                        if (!path) {
     
    86150                g_dir_close(dir);
    87151        }
     152}
     153
     154GList *get_plugins()
     155{
     156        return plugins;
    88157}
    89158#endif
     
    168237        load_plugins();
    169238#endif
     239}
     240
     241GList *get_protocols()
     242{
     243        return protocols;
     244}
     245
     246GList *get_protocols_disabled()
     247{
     248        return disabled_protocols;
    170249}
    171250
     
    495574}
    496575
    497 /* Mainly meant for ICQ (and now also for Jabber conferences) to allow IM
    498    modules to suggest a nickname for a handle. */
    499 void imcb_buddy_nick_hint(struct im_connection *ic, const char *handle, const char *nick)
     576/* Implements either imcb_buddy_nick_hint() or imcb_buddy_nick_change() depending on the value of 'change' */
     577static void buddy_nick_hint_or_change(struct im_connection *ic, const char *handle, const char *nick, gboolean change)
    500578{
    501579        bee_t *bee = ic->bee;
     
    509587        bu->nick = g_strdup(nick);
    510588
    511         if (bee->ui->user_nick_hint) {
     589        if (change && bee->ui->user_nick_change) {
     590                bee->ui->user_nick_change(bee, bu, nick);
     591        } else if (!change && bee->ui->user_nick_hint) {
    512592                bee->ui->user_nick_hint(bee, bu, nick);
    513593        }
     594}
     595
     596/* Soft variant, for newly created users. Does nothing if it's already online */
     597void imcb_buddy_nick_hint(struct im_connection *ic, const char *handle, const char *nick)
     598{
     599        buddy_nick_hint_or_change(ic, handle, nick, FALSE);
     600}
     601
     602/* Hard variant, always changes the nick */
     603void imcb_buddy_nick_change(struct im_connection *ic, const char *handle, const char *nick)
     604{
     605        buddy_nick_hint_or_change(ic, handle, nick, TRUE);
    514606}
    515607
     
    667759        if (away && *away) {
    668760                GList *m = ic->acc->prpl->away_states(ic);
     761                if (m == NULL) {
     762                        return 0;
     763                }
    669764                msg = ic->acc->flags & ACC_FLAG_AWAY_MESSAGE ? away : NULL;
    670765                away = imc_away_state_find(m, away, &msg) ? :
  • protocols/nogaim.h

    rba52ac5 r537d9b9  
    102102
    103103        GSList *groupchats;
     104        GSList *chatlist;
    104105};
    105106
     
    144145        char *description;
    145146};
     147
     148/* This enum takes a few things from libpurple and a few things from old OPT_ flags.
     149 * The only flag that was used before this struct was PRPL_OPT_NOOTR.
     150 *
     151 * The libpurple ones only use the same values as the PurpleProtocolOptions
     152 * enum for convenience, but there's no promise of direct compatibility with
     153 * those values. As of libpurple 2.8.0 they use up to 0x800 (1 << 11), which is
     154 * a nice coincidence.
     155 */
     156typedef enum {
     157        /* The protocol doesn't use passwords
     158         * Mirrors libpurple's OPT_PROTO_NO_PASSWORD */
     159        PRPL_OPT_NO_PASSWORD = 1 << 4,
     160
     161        /* The protocol doesn't require passwords, but may use them
     162         * Mirrors libpurple's OPT_PROTO_PASSWORD_OPTIONAL */
     163        PRPL_OPT_PASSWORD_OPTIONAL = 1 << 7,
     164
     165        /* The protocol is not suitable for OTR, see OPT_NOOTR */
     166        PRPL_OPT_NOOTR = 1 << 12,
     167} prpl_options_t;
    146168
    147169struct prpl {
     
    269291        gboolean (* handle_is_self) (struct im_connection *, const char *who);
    270292
     293        /* This sets/updates the im_connection->chatlist field with a
     294         * bee_chat_info_t GSList. This function should ensure the
     295         * bee_chat_list_finish() function gets called at some point
     296         * after the chat list is completely updated.
     297         */
     298        void (* chat_list) (struct im_connection *, const char *server);
     299
    271300        /* Some placeholders so eventually older plugins may cooperate with newer BitlBees. */
    272301        void *resv1;
     
    277306};
    278307
     308struct plugin_info
     309{
     310        guint abiver;
     311        const char *name;
     312        const char *version;
     313        const char *description;
     314        const char *author;
     315        const char *url;
     316};
     317
     318#ifdef WITH_PLUGINS
     319G_MODULE_EXPORT GList *get_plugins();
     320#endif
     321
    279322/* im_api core stuff. */
    280323void nogaim_init();
     324G_MODULE_EXPORT GList *get_protocols();
     325G_MODULE_EXPORT GList *get_protocols_disabled();
    281326G_MODULE_EXPORT GSList *get_connections();
    282327G_MODULE_EXPORT struct prpl *find_protocol(const char *name);
     
    330375G_MODULE_EXPORT void imcb_rename_buddy(struct im_connection *ic, const char *handle, const char *realname);
    331376G_MODULE_EXPORT void imcb_buddy_nick_hint(struct im_connection *ic, const char *handle, const char *nick);
     377G_MODULE_EXPORT void imcb_buddy_nick_change(struct im_connection *ic, const char *handle, const char *nick);
    332378G_MODULE_EXPORT void imcb_buddy_action_response(bee_user_t *bu, const char *action, char * const args[], void *data);
    333379G_MODULE_EXPORT GSList *imcb_get_local_contacts(struct im_connection *ic);
  • protocols/oscar/conn.c

    rba52ac5 r537d9b9  
    383383{
    384384        aim_conn_t *connstruct;
    385         guint16 port = AIM_LOGIN_PORT;
    386         char *host;
    387         int i;
    388385
    389386        if (!(connstruct = aim_conn_getnext(sess))) {
     
    400397        }
    401398
    402         /*
    403          * As of 23 Jul 1999, AOL now sends the port number, preceded by a
    404          * colon, in the BOS redirect.  This fatally breaks all previous
    405          * libfaims.  Bad, bad AOL.
    406          *
    407          * We put this here to catch every case.
    408          *
    409          */
    410 
    411         for (i = 0; i < (int) strlen(dest); i++) {
    412                 if (dest[i] == ':') {
    413                         port = atoi(&(dest[i + 1]));
    414                         break;
    415                 }
    416         }
    417 
    418         host = (char *) g_malloc(i + 1);
    419         strncpy(host, dest, i);
    420         host[i] = '\0';
    421 
    422         connstruct->fd = proxy_connect(host, port, NULL, NULL);
    423 
    424         g_free(host);
     399        /* The code that used to be here was very broken */
     400        g_return_val_if_reached(connstruct);
    425401
    426402        return connstruct;
  • protocols/purple/bpurple.h

    rba52ac5 r537d9b9  
    1313    GHashTable *input_requests;
    1414    guint next_request_id;
     15    char *chat_list_server;
     16    GSList *filetransfers;
    1517};
    1618
  • protocols/purple/ft.c

    rba52ac5 r537d9b9  
    4242        char *fn, *handle;
    4343        gboolean ui_wants_data;
     44        int timeout;
    4445};
    4546
     
    6465        struct prpl_xfer_data *px = ft->data;
    6566
    66         purple_xfer_request_denied(px->xfer);
     67        if (px->xfer) {
     68                if (!purple_xfer_is_completed(px->xfer) && !purple_xfer_is_canceled(px->xfer)) {
     69                        purple_xfer_cancel_local(px->xfer);
     70                }
     71                px->xfer->ui_data = NULL;
     72                purple_xfer_unref(px->xfer);
     73                px->xfer = NULL;
     74        }
     75}
     76
     77static void prpl_xfer_free(struct file_transfer *ft)
     78{
     79        struct prpl_xfer_data *px = ft->data;
     80        struct purple_data *pd = px->ic->proto_data;
     81
     82        pd->filetransfers = g_slist_remove(pd->filetransfers, px);
     83
     84        if (px->xfer) {
     85                px->xfer->ui_data = NULL;
     86                purple_xfer_unref(px->xfer);
     87        }
     88
     89        if (px->timeout) {
     90                b_event_remove(px->timeout);
     91        }
     92
     93        g_free(px->fn);
     94        g_free(px->handle);
     95        if (px->fd >= 0) {
     96                close(px->fd);
     97        }
     98        g_free(px);
    6799}
    68100
    69101static void prplcb_xfer_new(PurpleXfer *xfer)
    70102{
     103        purple_xfer_ref(xfer);
     104
    71105        if (purple_xfer_get_type(xfer) == PURPLE_XFER_RECEIVE) {
    72106                struct prpl_xfer_data *px = g_new0(struct prpl_xfer_data, 1);
     107                struct purple_data *pd;
    73108
    74109                xfer->ui_data = px;
     
    77112                px->fd = -1;
    78113                px->ic = purple_ic_by_pa(xfer->account);
     114
     115                pd = px->ic->proto_data;
     116                pd->filetransfers = g_slist_prepend(pd->filetransfers, px);
    79117
    80118                purple_xfer_set_local_filename(xfer, px->fn);
     
    112150        px->ft->accept = prpl_xfer_accept;
    113151        px->ft->canceled = prpl_xfer_canceled;
     152        px->ft->free = prpl_xfer_free;
    114153        px->ft->write_request = prpl_xfer_write_request;
    115154
     
    164203
    165204
    166 /* Generic (IM<>UI): */
    167205static void prplcb_xfer_destroy(PurpleXfer *xfer)
    168206{
    169207        struct prpl_xfer_data *px = xfer->ui_data;
    170208
    171         g_free(px->fn);
    172         g_free(px->handle);
    173         if (px->fd >= 0) {
    174                 close(px->fd);
    175         }
    176         g_free(px);
     209        if (px) {
     210                px->xfer = NULL;
     211        }
    177212}
    178213
     
    224259        struct prpl_xfer_data *px = xfer->ui_data;
    225260
    226         if (px->ft) {
     261        if (px && px->ft) {
    227262                imcb_file_canceled(px->ic, px->ft, "Canceled by remote end");
    228         } else {
     263        } else if (px) {
    229264                /* px->ft == NULL for sends, because of the two stages. :-/ */
    230265                imcb_error(px->ic, "File transfer cancelled by remote end");
     
    240275{
    241276        struct prpl_xfer_data *px = g_new0(struct prpl_xfer_data, 1);
     277        struct purple_data *pd;
    242278        char *dir, *basename;
    243279
    244280        ft->data = px;
    245281        px->ft = ft;
     282        px->ft->free = prpl_xfer_free;
    246283
    247284        dir = g_strdup("/tmp/bitlbee-purple-ft.XXXXXX");
     
    272309        px->handle = g_strdup(handle);
    273310
     311        pd = px->ic->proto_data;
     312        pd->filetransfers = g_slist_prepend(pd->filetransfers, px);
     313
    274314        imcb_log(ic,
    275315                 "Due to libpurple limitations, the file has to be cached locally before proceeding with the actual file transfer. Please wait...");
    276316
    277         b_timeout_add(0, purple_transfer_request_cb, ft);
     317        px->timeout = b_timeout_add(0, purple_transfer_request_cb, ft);
    278318}
    279319
     
    295335        struct prpl_xfer_data *px = ft->data;
    296336
     337        px->timeout = 0;
     338
    297339        if (ft->write == NULL) {
    298340                ft->write = prpl_xfer_write;
     
    322364                px->ft = NULL;
    323365        } else {
    324                 b_timeout_add(0, purple_transfer_request_cb, ft);
     366                px->timeout = b_timeout_add(0, purple_transfer_request_cb, ft);
    325367        }
    326368
    327369        return TRUE;
     370}
     371
     372void purple_transfer_cancel_all(struct im_connection *ic)
     373{
     374        struct purple_data *pd = ic->proto_data;
     375
     376        while (pd->filetransfers) {
     377                struct prpl_xfer_data *px = pd->filetransfers->data;
     378
     379                if (px->ft) {
     380                        imcb_file_canceled(ic, px->ft, "Logging out");
     381                }
     382
     383                pd->filetransfers = g_slist_remove(pd->filetransfers, px);
     384        }
    328385}
    329386
  • protocols/purple/purple.c

    rba52ac5 r537d9b9  
    4343                                   const char *message, const char *who);
    4444
     45void purple_transfer_cancel_all(struct im_connection *ic);
     46
    4547/* purple_request_input specific stuff */
    4648typedef void (*ri_callback_t)(gpointer, const gchar *);
     
    5456};
    5557
     58struct purple_roomlist_data {
     59        GSList *chats;
     60        gint topic;
     61        gboolean initialized;
     62};
     63
     64
    5665struct im_connection *purple_ic_by_pa(PurpleAccount *pa)
    5766{
     
    94103}
    95104
     105static char *purple_get_account_prpl_id(account_t *acc)
     106{
     107        /* "oscar" is how non-purple bitlbee calls it,
     108         * and it might be icq or aim, depending on the username */
     109        if (g_strcmp0(acc->prpl->name, "oscar") == 0) {
     110                return (g_ascii_isdigit(acc->user[0])) ? "prpl-icq" : "prpl-aim";
     111        }
     112
     113        return acc->prpl->data;
     114}
     115
    96116static void purple_init(account_t *acc)
    97117{
    98         PurplePlugin *prpl = purple_plugins_find_with_id((char *) acc->prpl->data);
     118        char *prpl_id = purple_get_account_prpl_id(acc);
     119        PurplePlugin *prpl = purple_plugins_find_with_id(prpl_id);
    99120        PurplePluginProtocolInfo *pi = prpl->info->extra_info;
    100121        PurpleAccount *pa;
     
    262283        /* Go through all away states to figure out if away/status messages
    263284           are possible. */
    264         pa = purple_account_new(acc->user, (char *) acc->prpl->data);
     285        pa = purple_account_new(acc->user, prpl_id);
    265286        for (st = purple_account_get_status_types(pa); st; st = st->next) {
    266287                PurpleStatusPrimitive prim = purple_status_type_get_primitive(st->data);
     
    345366
    346367        ic->proto_data = pd = g_new0(struct purple_data, 1);
    347         pd->account = purple_account_new(acc->user, (char *) acc->prpl->data);
     368        pd->account = purple_account_new(acc->user, purple_get_account_prpl_id(acc));
    348369        pd->input_requests = g_hash_table_new_full(g_direct_hash, g_direct_equal,
    349370                                                   NULL, g_free);
     
    371392        }
    372393
     394        if (pd->filetransfers) {
     395                purple_transfer_cancel_all(ic);
     396        }
     397
    373398        purple_account_set_enabled(pd->account, "BitlBee", FALSE);
    374399        purple_connections = g_slist_remove(purple_connections, ic);
    375400        purple_accounts_remove(pd->account);
     401        imcb_chat_list_free(ic);
     402        g_free(pd->chat_list_server);
    376403        g_hash_table_destroy(pd->input_requests);
    377404        g_free(pd);
     
    673700}
    674701
     702void purple_chat_set_topic(struct groupchat *gc, char *topic)
     703{
     704        PurpleConversation *pc = gc->data;
     705        PurpleConvChat *pcc = PURPLE_CONV_CHAT(pc);
     706        struct purple_data *pd = gc->ic->proto_data;
     707        PurplePlugin *prpl = purple_plugins_find_with_id(pd->account->protocol_id);
     708        PurplePluginProtocolInfo *pi = prpl->info->extra_info;
     709
     710        if (pi->set_chat_topic) {
     711                pi->set_chat_topic(purple_account_get_connection(pd->account),
     712                                   purple_conv_chat_get_id(pcc),
     713                                   topic);
     714        }
     715}
     716
    675717void purple_chat_kick(struct groupchat *gc, char *who, const char *message)
    676718{
     
    697739        GHashTable *chat_hash;
    698740        PurpleConversation *conv;
     741        struct groupchat *gc;
    699742        GList *info, *l;
    700743
     
    730773        g_list_free(info);
    731774
     775        /* do this before serv_join_chat to handle cases where prplcb_conv_new is called immediately (not async) */
     776        gc = imcb_chat_new(ic, room);
     777
    732778        serv_join_chat(purple_account_get_connection(pd->account), chat_hash);
    733779
    734780        g_hash_table_destroy(chat_hash);
    735781
    736         return imcb_chat_new(ic, room);
     782        return gc;
     783}
     784
     785void purple_chat_list(struct im_connection *ic, const char *server)
     786{
     787        PurpleRoomlist *list;
     788        struct purple_data *pd = ic->proto_data;
     789        PurplePlugin *prpl = purple_plugins_find_with_id(pd->account->protocol_id);
     790        PurplePluginProtocolInfo *pi = prpl->info->extra_info;
     791
     792        if (!pi || !pi->roomlist_get_list) {
     793                imcb_log(ic, "Room listing unsupported by this purple plugin");
     794                return;
     795        }
     796
     797        g_free(pd->chat_list_server);
     798        pd->chat_list_server = (server && *server) ? g_strdup(server) : NULL;
     799
     800        list = purple_roomlist_get_list(pd->account->gc);
     801
     802        if (list) {
     803                struct purple_roomlist_data *rld = list->ui_data;
     804                rld->initialized = TRUE;
     805
     806                purple_roomlist_ref(list);
     807        }
    737808}
    738809
     
    9701041
    9711042/* Generic handler for IM or chat messages, covers write_chat, write_im and write_conv */
    972 static void handle_conv_msg(PurpleConversation *conv, const char *who, const char *message, guint32 bee_flags, time_t mtime)
     1043static void handle_conv_msg(PurpleConversation *conv, const char *who, const char *message_, guint32 bee_flags, time_t mtime)
    9731044{
    9741045        struct im_connection *ic = purple_ic_by_pa(conv->account);
    9751046        struct groupchat *gc = conv->ui_data;
     1047        char *message = g_strdup(message_);
    9761048        PurpleBuddy *buddy;
    9771049
     
    9821054
    9831055        if (conv->type == PURPLE_CONV_TYPE_IM) {
    984                 imcb_buddy_msg(ic, (char *) who, (char *) message, bee_flags, mtime);
     1056                imcb_buddy_msg(ic, who, message, bee_flags, mtime);
    9851057        } else if (gc) {
    986                 imcb_chat_msg(gc, who, (char *) message, bee_flags, mtime);
    987         }
     1058                imcb_chat_msg(gc, who, message, bee_flags, mtime);
     1059        }
     1060
     1061        g_free(message);
    9881062}
    9891063
     
    11711245        struct im_connection *ic = purple_ic_by_pa(account);
    11721246        struct purple_data *pd = ic->proto_data;
    1173         struct request_input_data *ri = g_new0(struct request_input_data, 1);
    1174         guint id = pd->next_request_id++;
     1247        struct request_input_data *ri;
     1248        guint id;
     1249
     1250        /* hack so that jabber's chat list doesn't ask for conference server twice */
     1251        if (pd->chat_list_server && title && g_strcmp0(title, "Enter a Conference Server") == 0) {
     1252                ((ri_callback_t) ok_cb)(user_data, pd->chat_list_server);
     1253                g_free(pd->chat_list_server);
     1254                pd->chat_list_server = NULL;
     1255                return NULL;
     1256        }
     1257
     1258        id = pd->next_request_id++;
     1259        ri = g_new0(struct request_input_data, 1);
    11751260
    11761261        ri->id = id;
     
    11821267
    11831268        imcb_add_buddy(ic, ri->buddy, NULL);
    1184         imcb_buddy_msg(ic, ri->buddy, secondary, 0, 0);
     1269
     1270        if (title && *title) {
     1271                imcb_buddy_msg(ic, ri->buddy, title, 0, 0);
     1272        }
     1273
     1274        if (primary && *primary) {
     1275                imcb_buddy_msg(ic, ri->buddy, primary, 0, 0);
     1276        }
     1277
     1278        if (secondary && *secondary) {
     1279                imcb_buddy_msg(ic, ri->buddy, secondary, 0, 0);
     1280        }
    11851281
    11861282        return ri;
     
    12561352        prplcb_privacy_deny_added,         /* deny_added */
    12571353        prplcb_privacy_deny_removed,       /* deny_removed */
     1354};
     1355
     1356static void prplcb_roomlist_create(PurpleRoomlist *list)
     1357{
     1358        struct purple_roomlist_data *rld;
     1359
     1360        list->ui_data = rld = g_new0(struct purple_roomlist_data, 1);
     1361        rld->topic = -1;
     1362}
     1363
     1364static void prplcb_roomlist_set_fields(PurpleRoomlist *list, GList *fields)
     1365{
     1366        gint topic = -1;
     1367        GList *l;
     1368        guint i;
     1369        PurpleRoomlistField *field;
     1370        struct purple_roomlist_data *rld = list->ui_data;
     1371
     1372        for (i = 0, l = fields; l; i++, l = l->next) {
     1373                field = l->data;
     1374
     1375                /* Use the first visible string field as a fallback topic */
     1376                if (i != 0 && topic < 0 && !field->hidden &&
     1377                    field->type == PURPLE_ROOMLIST_FIELD_STRING) {
     1378                        topic = i;
     1379                }
     1380
     1381                if ((g_strcasecmp(field->name, "description") == 0) ||
     1382                    (g_strcasecmp(field->name, "topic") == 0)) {
     1383                        if (field->type == PURPLE_ROOMLIST_FIELD_STRING) {
     1384                                rld->topic = i;
     1385                        }
     1386                }
     1387        }
     1388
     1389        if (rld->topic < 0) {
     1390                rld->topic = topic;
     1391        }
     1392}
     1393
     1394static void prplcb_roomlist_add_room(PurpleRoomlist *list, PurpleRoomlistRoom *room)
     1395{
     1396        bee_chat_info_t *ci;
     1397        const char *title;
     1398        const char *topic;
     1399        GList *fields;
     1400        struct purple_roomlist_data *rld = list->ui_data;
     1401
     1402        fields = purple_roomlist_room_get_fields(room);
     1403        title = purple_roomlist_room_get_name(room);
     1404
     1405        if (rld->topic >= 0) {
     1406                topic = g_list_nth_data(fields, rld->topic);
     1407        } else {
     1408                topic = NULL;
     1409        }
     1410
     1411        ci = g_new(bee_chat_info_t, 1);
     1412        ci->title = g_strdup(title);
     1413        ci->topic = g_strdup(topic);
     1414        rld->chats = g_slist_prepend(rld->chats, ci);
     1415}
     1416
     1417static void prplcb_roomlist_in_progress(PurpleRoomlist *list, gboolean in_progress)
     1418{
     1419        struct im_connection *ic;
     1420        struct purple_data *pd;
     1421        struct purple_roomlist_data *rld = list->ui_data;
     1422
     1423        if (in_progress || !rld) {
     1424                return;
     1425        }
     1426
     1427        ic = purple_ic_by_pa(list->account);
     1428        imcb_chat_list_free(ic);
     1429
     1430        pd = ic->proto_data;
     1431        g_free(pd->chat_list_server);
     1432        pd->chat_list_server = NULL;
     1433
     1434        ic->chatlist = g_slist_reverse(rld->chats);
     1435        rld->chats = NULL;
     1436
     1437        imcb_chat_list_finish(ic);
     1438
     1439        if (rld->initialized) {
     1440                purple_roomlist_unref(list);
     1441        }
     1442}
     1443
     1444static void prplcb_roomlist_destroy(PurpleRoomlist *list)
     1445{
     1446        g_free(list->ui_data);
     1447        list->ui_data = NULL;
     1448}
     1449
     1450static PurpleRoomlistUiOps bee_roomlist_uiops =
     1451{
     1452        NULL,                         /* show_with_account */
     1453        prplcb_roomlist_create,       /* create */
     1454        prplcb_roomlist_set_fields,   /* set_fields */
     1455        prplcb_roomlist_add_room,     /* add_room */
     1456        prplcb_roomlist_in_progress,  /* in_progress */
     1457        prplcb_roomlist_destroy,      /* destroy */
    12581458};
    12591459
     
    14221622        purple_request_set_ui_ops(&bee_request_uiops);
    14231623        purple_privacy_set_ui_ops(&bee_privacy_uiops);
     1624        purple_roomlist_set_ui_ops(&bee_roomlist_uiops);
    14241625        purple_notify_set_ui_ops(&bee_notify_uiops);
    14251626        purple_accounts_set_ui_ops(&bee_account_uiops);
     
    14381639        char *dir;
    14391640
     1641        if (purple_get_core() != NULL) {
     1642                log_message(LOGLVL_ERROR, "libpurple already initialized. "
     1643                            "Please use inetd or ForkDaemon mode instead.");
     1644                return;
     1645        }
     1646
    14401647        g_assert((int) B_EV_IO_READ == (int) PURPLE_INPUT_READ);
    14411648        g_assert((int) B_EV_IO_WRITE == (int) PURPLE_INPUT_WRITE);
     
    14431650        dir = g_strdup_printf("%s/purple", global.conf->configdir);
    14441651        purple_util_set_user_dir(dir);
     1652        g_free(dir);
     1653
     1654        dir = g_strdup_printf("%s/purple", global.conf->plugindir);
     1655        purple_plugins_add_search_path(dir);
    14451656        g_free(dir);
    14461657
     
    15061717        funcs.chat_with = purple_chat_with;
    15071718        funcs.chat_invite = purple_chat_invite;
     1719        funcs.chat_topic = purple_chat_set_topic;
    15081720        funcs.chat_kick = purple_chat_kick;
    15091721        funcs.chat_leave = purple_chat_leave;
    15101722        funcs.chat_join = purple_chat_join;
     1723        funcs.chat_list = purple_chat_list;
    15111724        funcs.transfer_request = purple_transfer_request;
    15121725
     
    15171730        for (prots = purple_plugins_get_protocols(); prots; prots = prots->next) {
    15181731                PurplePlugin *prot = prots->data;
     1732                PurplePluginProtocolInfo *pi = prot->info->extra_info;
    15191733                struct prpl *ret;
    15201734
     
    15301744                        ret->name += 5;
    15311745                }
     1746
     1747                if (pi->options & OPT_PROTO_NO_PASSWORD) {
     1748                        ret->options |= PRPL_OPT_NO_PASSWORD;
     1749                }
     1750
     1751                if (pi->options & OPT_PROTO_PASSWORD_OPTIONAL) {
     1752                        ret->options |= PRPL_OPT_PASSWORD_OPTIONAL;
     1753                }
     1754
    15321755                register_protocol(ret);
    15331756
     
    15391762                        ret = g_memdup(&funcs, sizeof(funcs));
    15401763                        ret->name = "oscar";
    1541                         ret->data = prot->info->id;
     1764                        /* purple_get_account_prpl_id() determines the actual protocol ID (icq/aim) */
     1765                        ret->data = NULL;
    15421766                        register_protocol(ret);
    15431767                }
  • protocols/skype/skype.c

    rba52ac5 r537d9b9  
    17631763        register_protocol(ret);
    17641764}
     1765
     1766struct plugin_info *init_plugin_info(void)
     1767{
     1768        static struct plugin_info info = {
     1769                BITLBEE_ABI_VERSION_CODE,
     1770                "skype",
     1771                BITLBEE_VERSION,
     1772                "Skype protocol plugin",
     1773                NULL,
     1774                NULL
     1775        };
     1776
     1777        return &info;
     1778}
  • protocols/twitter/twitter.c

    rba52ac5 r537d9b9  
    345345                imcb_log(ic, "Getting contact list");
    346346                twitter_get_friends_ids(ic, -1);
     347                twitter_get_mutes_ids(ic, -1);
     348                twitter_get_noretweets_ids(ic, -1);
    347349        } else {
    348350                twitter_main_loop_start(ic);
     
    469471        g_regex_match(regex, msg, 0, &match_info);
    470472        while (g_match_info_matches(match_info)) {
    471                 gchar *s, *url;
     473                gchar *url;
    472474
    473475                url = g_match_info_fetch(match_info, 2);
    474476                url_len_diff += target_len - g_utf8_strlen(url, -1);
    475477
    476                 /* Add another character for https://t.co/... URLs */
    477                 if ((s = g_match_info_fetch(match_info, 3))) {
    478                         url_len_diff += 1;
    479                         g_free(s);
    480                 }
    481478                g_free(url);
    482479                g_match_info_next(match_info, NULL);
     
    541538        if (strcmp(acc->prpl->name, "twitter") == 0) {
    542539                def_url = TWITTER_API_URL;
    543                 def_tul = "22";
     540                def_tul = "23";
    544541                def_mentions = "true";
    545542        } else {                /* if( strcmp( acc->prpl->name, "identica" ) == 0 ) */
     
    690687                }
    691688
     689                g_slist_foreach(td->mutes_ids, (GFunc) g_free, NULL);
     690                g_slist_free(td->mutes_ids);
     691
     692                g_slist_foreach(td->noretweets_ids, (GFunc) g_free, NULL);
     693                g_slist_free(td->noretweets_ids);
     694
    692695                http_close(td->stream);
    693696                twitter_filter_remove_all(ic);
     
    948951        } else if ((g_strcasecmp(cmd[0], "favourite") == 0 ||
    949952                    g_strcasecmp(cmd[0], "favorite") == 0 ||
    950                     g_strcasecmp(cmd[0], "fav") == 0) && cmd[1]) {
     953                    g_strcasecmp(cmd[0], "fav") == 0 ||
     954                    g_strcasecmp(cmd[0], "like") == 0) && cmd[1]) {
    951955                if ((id = twitter_message_id_from_command_arg(ic, cmd[1], NULL))) {
    952956                        twitter_favourite_tweet(ic, id);
     
    960964        } else if (g_strcasecmp(cmd[0], "unfollow") == 0 && cmd[1]) {
    961965                twitter_remove_buddy(ic, cmd[1], NULL);
     966                goto eof;
     967        } else if (g_strcasecmp(cmd[0], "mute") == 0 && cmd[1]) {
     968                twitter_mute_create_destroy(ic, cmd[1], 1);
     969                goto eof;
     970        } else if (g_strcasecmp(cmd[0], "unmute") == 0 && cmd[1]) {
     971                twitter_mute_create_destroy(ic, cmd[1], 0);
    962972                goto eof;
    963973        } else if ((g_strcasecmp(cmd[0], "report") == 0 ||
     
    10821092        struct prpl *ret = g_new0(struct prpl, 1);
    10831093
    1084         ret->options = OPT_NOOTR;
     1094        ret->options = PRPL_OPT_NOOTR | PRPL_OPT_NO_PASSWORD;
    10851095        ret->name = "twitter";
    10861096        ret->login = twitter_login;
     
    11091119        ret = g_memdup(ret, sizeof(struct prpl));
    11101120        ret->name = "identica";
     1121        ret->options =  PRPL_OPT_NOOTR;
    11111122        register_protocol(ret);
    11121123}
  • protocols/twitter/twitter.h

    rba52ac5 r537d9b9  
    6161
    6262        GSList *follow_ids;
     63        GSList *mutes_ids;
     64        GSList *noretweets_ids;
    6365        GSList *filters;
    6466
  • protocols/twitter/twitter_http.c

    rba52ac5 r537d9b9  
    7979        g_string_printf(request, "%s %s%s%s%s HTTP/1.1\r\n"
    8080                        "Host: %s\r\n"
    81                         "User-Agent: BitlBee " BITLBEE_VERSION " " ARCH "/" CPU "\r\n",
     81                        "User-Agent: BitlBee " BITLBEE_VERSION "\r\n",
    8282                        is_post ? "POST" : "GET",
    8383                        base_url ? base_url->file : td->url_path,
  • protocols/twitter/twitter_lib.c

    rba52ac5 r537d9b9  
    245245
    246246static void twitter_http_get_friends_ids(struct http_request *req);
     247static void twitter_http_get_mutes_ids(struct http_request *req);
     248static void twitter_http_get_noretweets_ids(struct http_request *req);
    247249
    248250/**
     
    257259        args[1] = g_strdup_printf("%" G_GINT64_FORMAT, next_cursor);
    258260        twitter_http(ic, TWITTER_FRIENDS_IDS_URL, twitter_http_get_friends_ids, ic, 0, args, 2);
     261
     262        g_free(args[1]);
     263}
     264
     265/**
     266 * Get the muted users ids.
     267 */
     268void twitter_get_mutes_ids(struct im_connection *ic, gint64 next_cursor)
     269{
     270        char *args[2];
     271
     272        args[0] = "cursor";
     273        args[1] = g_strdup_printf("%" G_GINT64_FORMAT, next_cursor);
     274        twitter_http(ic, TWITTER_MUTES_IDS_URL, twitter_http_get_mutes_ids, ic, 0, args, 2);
     275
     276        g_free(args[1]);
     277}
     278
     279/**
     280 * Get the ids for users from whom we should ignore retweets.
     281 */
     282void twitter_get_noretweets_ids(struct im_connection *ic, gint64 next_cursor)
     283{
     284        char *args[2];
     285
     286        args[0] = "cursor";
     287        args[1] = g_strdup_printf("%" G_GINT64_FORMAT, next_cursor);
     288        twitter_http(ic, TWITTER_NORETWEETS_IDS_URL, twitter_http_get_noretweets_ids, ic, 0, args, 2);
    259289
    260290        g_free(args[1]);
     
    334364                twitter_get_users_lookup(ic);
    335365        }
     366
     367        txl->list = NULL;
     368        txl_free(txl);
     369}
     370
     371/**
     372 * Callback for getting the mutes ids.
     373 */
     374static void twitter_http_get_mutes_ids(struct http_request *req)
     375{
     376        struct im_connection *ic = req->data;
     377        JSON_Value *parsed;
     378        struct twitter_xml_list *txl;
     379        struct twitter_data *td;
     380
     381        // Check if the connection is stil active
     382        if (!g_slist_find(twitter_connections, ic)) {
     383                return;
     384        }
     385
     386        td = ic->proto_data;
     387
     388        if (req->status_code != 200) {
     389                /* Fail silently */
     390                return;
     391        }
     392
     393        // Parse the data.
     394        if (!(parsed = twitter_parse_response(ic, req))) {
     395                return;
     396        }
     397
     398        txl = g_new0(struct twitter_xml_list, 1);
     399        txl->list = td->mutes_ids;
     400
     401        /* mute ids API response is similar enough to friends response
     402           to reuse this method */
     403        twitter_xt_get_friends_id_list(parsed, txl);
     404        json_value_free(parsed);
     405
     406        td->mutes_ids = txl->list;
     407        if (txl->next_cursor) {
     408                /* Recurse while there are still more pages */
     409                twitter_get_mutes_ids(ic, txl->next_cursor);
     410        }
     411
     412        txl->list = NULL;
     413        txl_free(txl);
     414}
     415
     416/**
     417 * Callback for getting the no-retweets ids.
     418 */
     419static void twitter_http_get_noretweets_ids(struct http_request *req)
     420{
     421        struct im_connection *ic = req->data;
     422        JSON_Value *parsed;
     423        struct twitter_xml_list *txl;
     424        struct twitter_data *td;
     425
     426        // Check if the connection is stil active
     427        if (!g_slist_find(twitter_connections, ic)) {
     428                return;
     429        }
     430
     431        if (req->status_code != 200) {
     432                /* Fail silently */
     433                return;
     434        }
     435
     436        td = ic->proto_data;
     437
     438        // Parse the data.
     439        if (!(parsed = twitter_parse_response(ic, req))) {
     440                return;
     441        }
     442
     443        txl = g_new0(struct twitter_xml_list, 1);
     444        txl->list = td->noretweets_ids;
     445       
     446        // Process the retweet ids
     447        txl->type = TXL_ID;
     448        if (json_type(parsed) == JSONArray) {
     449                JSON_Array *arr = json_array(parsed);
     450                unsigned int i;
     451                for (i = 0; i < json_array_get_count(arr); i++) {
     452                        jint id = json_array_get_integer(arr, i);
     453                        txl->list = g_slist_prepend(txl->list,
     454                                                    g_strdup_printf("%lld", id));
     455                }
     456        }
     457
     458        json_value_free(parsed);
     459        td->noretweets_ids = txl->list;
    336460
    337461        txl->list = NULL;
     
    467591#endif
    468592
    469 static void expand_entities(char **text, const JSON_Object *node);
     593static void expand_entities(char **text, const JSON_Object *node, const JSON_Object *extended_node);
    470594
    471595/**
     
    479603static struct twitter_xml_status *twitter_xt_get_status(const JSON_Object *node)
    480604{
    481         struct twitter_xml_status *txs;
     605        struct twitter_xml_status *txs = {0};
    482606        const JSON_Object *rt = NULL;
     607        const JSON_Value *text_value = NULL;
     608        const JSON_Object *extended_node = NULL;
    483609
    484610        if (!node) {
     
    488614
    489615        JSON_O_FOREACH(node, k, v) {
    490                 if (strcmp("text", k) == 0 && (txs->text = g_strdup(json_string(v)))) {
    491                         // TODO: Huh strip html? In json? Not sure whether I have to..
    492                         strip_html(txs->text);
     616                if (strcmp("text", k) == 0 && json_type(v) == JSONString && text_value == NULL) {
     617                        text_value = v;
     618                } else if (strcmp("full_text", k) == 0 && json_type(v) == JSONString) {
     619                        text_value = v;
     620                } else if (strcmp("extended_tweet", k) == 0 && json_type(v) == JSONObject) {
     621                        text_value = json_object_get_value(json_object(v), "full_text");
     622                        extended_node = json_object(v);
    493623                } else if (strcmp("retweeted_status", k) == 0 && (rt = json_object(v))) {
    494624                        // Handling below.
     
    516646                struct twitter_xml_status *rtxs = twitter_xt_get_status(rt);
    517647                if (rtxs) {
    518                         g_free(txs->text);
    519648                        txs->text = g_strdup_printf("RT @%s: %s", rtxs->user->screen_name, rtxs->text);
    520649                        txs->id = rtxs->id;
    521650                        txs_free(rtxs);
    522651                }
    523         } else {
    524                 expand_entities(&txs->text, node);
     652        } else if (text_value && json_type(text_value) == JSONString) {
     653                txs->text = g_strdup(json_string(text_value));
     654                strip_html(txs->text);
     655                expand_entities(&txs->text, node, extended_node);
    525656        }
    526657
     
    564695        }
    565696
    566         expand_entities(&txs->text, node);
     697        expand_entities(&txs->text, node, NULL);
    567698
    568699        if (txs->text && txs->user && txs->id) {
     
    574705}
    575706
    576 static void expand_entities(char **text, const JSON_Object *node)
    577 {
    578         JSON_Object *entities, *quoted;
     707static void expand_entities(char **text, const JSON_Object *node, const JSON_Object *extended_node)
     708{
     709        JSON_Object *entities, *extended_entities, *quoted;
    579710        char *quote_url = NULL, *quote_text = NULL;
    580711
     
    594725        }
    595726
     727        if (extended_node) {
     728                extended_entities = json_object_get_object(extended_node, "entities");
     729                if (extended_entities) {
     730                        entities = extended_entities;
     731                }
     732        }
     733
    596734        JSON_O_FOREACH(entities, k, v) {
    597735                int i;
     
    837975        struct twitter_data *td = ic->proto_data;
    838976        char *last_id_str;
     977        char *uid_str;
    839978
    840979        if (status->user == NULL || status->text == NULL) {
     980                return;
     981        }
     982       
     983        /* Check this is not a tweet that should be muted */
     984        uid_str = g_strdup_printf("%" G_GUINT64_FORMAT, status->user->uid);
     985
     986        if (g_slist_find_custom(td->mutes_ids, uid_str, (GCompareFunc)strcmp)) {
     987                g_free(uid_str);
     988                return;
     989        }
     990        if (status->id != status->rt_id && g_slist_find_custom(td->noretweets_ids, uid_str, (GCompareFunc)strcmp)) {
     991                g_free(uid_str);
    841992                return;
    842993        }
     
    8631014        set_setstr(&ic->acc->set, "_last_tweet", last_id_str);
    8641015        g_free(last_id_str);
     1016        g_free(uid_str);
    8651017}
    8661018
     
    8801032        }
    8811033
    882         ic->flags |= OPT_PONGED;
    8831034        td = ic->proto_data;
    8841035
     
    8961047                imc_logout(ic, TRUE);
    8971048                return;
     1049        }
     1050
     1051        if (req == td->stream) {
     1052                ic->flags |= OPT_PONGED;
    8981053        }
    8991054
     
    9991154        JSON_Object *target = json_object_get_object(o, "target");
    10001155        const char *type = json_object_get_string(o, "event");
     1156        struct twitter_xml_user *us = NULL;
     1157        struct twitter_xml_user *ut = NULL;
    10011158
    10021159        if (!type || !source || !target) {
     
    10051162
    10061163        if (strcmp(type, "follow") == 0) {
    1007                 struct twitter_xml_user *us = twitter_xt_get_user(source);
    1008                 struct twitter_xml_user *ut = twitter_xt_get_user(target);
     1164                us = twitter_xt_get_user(source);
     1165                ut = twitter_xt_get_user(target);
    10091166                if (g_strcasecmp(us->screen_name, td->user) == 0) {
    10101167                        twitter_add_buddy(ic, ut->screen_name, ut->name);
    10111168                }
    1012                 txu_free(us);
    1013                 txu_free(ut);
    1014         }
     1169        } else if (strcmp(type, "mute") == 0) {
     1170                GSList *found;
     1171                char *uid_str;
     1172                ut = twitter_xt_get_user(target);
     1173                uid_str = g_strdup_printf("%" G_GUINT64_FORMAT, ut->uid);
     1174                if (!(found = g_slist_find_custom(td->mutes_ids, uid_str,
     1175                                                  (GCompareFunc)strcmp))) {
     1176                        td->mutes_ids = g_slist_prepend(td->mutes_ids, uid_str);
     1177                }
     1178                twitter_log(ic, "Muted user %s", ut->screen_name);
     1179                if (getenv("BITLBEE_DEBUG")) {
     1180                        fprintf(stderr, "New mute: %s %"G_GUINT64_FORMAT"\n",
     1181                                ut->screen_name, ut->uid);
     1182                }
     1183        } else if (strcmp(type, "unmute") == 0) {
     1184                GSList *found;
     1185                char *uid_str;
     1186                ut = twitter_xt_get_user(target);
     1187                uid_str = g_strdup_printf("%" G_GUINT64_FORMAT, ut->uid);
     1188                if ((found = g_slist_find_custom(td->mutes_ids, uid_str,
     1189                                                (GCompareFunc)strcmp))) {
     1190                        char *found_str = found->data;
     1191                        td->mutes_ids = g_slist_delete_link(td->mutes_ids, found);
     1192                        g_free(found_str);
     1193                }
     1194                g_free(uid_str);
     1195                twitter_log(ic, "Unmuted user %s", ut->screen_name);
     1196                if (getenv("BITLBEE_DEBUG")) {
     1197                        fprintf(stderr, "New unmute: %s %"G_GUINT64_FORMAT"\n",
     1198                                ut->screen_name, ut->uid);
     1199                }
     1200        }
     1201
     1202        txu_free(us);
     1203        txu_free(ut);
    10151204
    10161205        return TRUE;
     
    13091498        td->flags &= ~TWITTER_GOT_TIMELINE;
    13101499
    1311         char *args[6];
     1500        char *args[8];
    13121501        args[0] = "cursor";
    13131502        args[1] = g_strdup_printf("%" G_GINT64_FORMAT, next_cursor);
    13141503        args[2] = "include_entities";
    13151504        args[3] = "true";
     1505        args[4] = "tweet_mode";
     1506        args[5] = "extended";
    13161507        if (td->timeline_id) {
    1317                 args[4] = "since_id";
    1318                 args[5] = g_strdup_printf("%" G_GUINT64_FORMAT, td->timeline_id);
     1508                args[6] = "since_id";
     1509                args[7] = g_strdup_printf("%" G_GUINT64_FORMAT, td->timeline_id);
    13191510        }
    13201511
    13211512        if (twitter_http(ic, TWITTER_HOME_TIMELINE_URL, twitter_http_get_home_timeline, ic, 0, args,
    1322                          td->timeline_id ? 6 : 4) == NULL) {
     1513                         td->timeline_id ? 8 : 6) == NULL) {
    13231514                if (++td->http_fails >= 5) {
    13241515                        imcb_error(ic, "Could not retrieve %s: %s",
     
    13311522        g_free(args[1]);
    13321523        if (td->timeline_id) {
    1333                 g_free(args[5]);
     1524                g_free(args[7]);
    13341525        }
    13351526}
     
    13461537        td->flags &= ~TWITTER_GOT_MENTIONS;
    13471538
    1348         char *args[6];
     1539        char *args[8];
    13491540        args[0] = "cursor";
    13501541        args[1] = g_strdup_printf("%" G_GINT64_FORMAT, next_cursor);
     
    13581549                args[5] = g_strdup_printf("%d", set_getint(&ic->acc->set, "show_old_mentions"));
    13591550        }
     1551        args[6] = "tweet_mode";
     1552        args[7] = "extended";
    13601553
    13611554        if (twitter_http(ic, TWITTER_MENTIONS_URL, twitter_http_get_mentions,
    1362                          ic, 0, args, 6) == NULL) {
     1555                         ic, 0, args, 8) == NULL) {
    13631556                if (++td->http_fails >= 5) {
    13641557                        imcb_error(ic, "Could not retrieve %s: %s",
     
    15291722}
    15301723
     1724/**
     1725 * Mute or unmute a user
     1726 */
     1727void twitter_mute_create_destroy(struct im_connection *ic, char *who, int create)
     1728{
     1729        char *args[2];
     1730
     1731        args[0] = "screen_name";
     1732        args[1] = who;
     1733        twitter_http(ic, create ? TWITTER_MUTES_CREATE_URL : TWITTER_MUTES_DESTROY_URL,
     1734                     twitter_http_post, ic, 1, args, 2);
     1735}
     1736
    15311737void twitter_status_destroy(struct im_connection *ic, guint64 id)
    15321738{
  • protocols/twitter/twitter_lib.h

    rba52ac5 r537d9b9  
    6363#define TWITTER_FRIENDS_IDS_URL "/friends/ids.json"
    6464#define TWITTER_FOLLOWERS_IDS_URL "/followers/ids.json"
     65#define TWITTER_MUTES_IDS_URL "/mutes/users/ids.json"
     66#define TWITTER_NORETWEETS_IDS_URL "/friendships/no_retweets/ids.json"
    6567
    6668/* Account URLs */
     
    7678#define TWITTER_BLOCKS_DESTROY_URL "/blocks/destroy/"
    7779
     80/* Mute URLs */
     81#define TWITTER_MUTES_CREATE_URL "/mutes/users/create.json"
     82#define TWITTER_MUTES_DESTROY_URL "/mutes/users/destroy.json"
     83
    7884/* Report spam */
    7985#define TWITTER_REPORT_SPAM_URL "/users/report_spam.json"
     
    8793gboolean twitter_get_timeline(struct im_connection *ic, gint64 next_cursor);
    8894void twitter_get_friends_ids(struct im_connection *ic, gint64 next_cursor);
     95void twitter_get_mutes_ids(struct im_connection *ic, gint64 next_cursor);
     96void twitter_get_noretweets_ids(struct im_connection *ic, gint64 next_cursor);
    8997void twitter_get_statuses_friends(struct im_connection *ic, gint64 next_cursor);
    9098
     
    92100void twitter_direct_messages_new(struct im_connection *ic, char *who, char *message);
    93101void twitter_friendships_create_destroy(struct im_connection *ic, char *who, int create);
     102void twitter_mute_create_destroy(struct im_connection *ic, char *who, int create);
    94103void twitter_status_destroy(struct im_connection *ic, guint64 id);
    95104void twitter_status_retweet(struct im_connection *ic, guint64 id);
Note: See TracChangeset for help on using the changeset viewer.