diff options
| -rw-r--r-- | facebook/facebook-api.c | 550 | ||||
| -rw-r--r-- | facebook/facebook-api.h | 47 | ||||
| -rw-r--r-- | facebook/facebook.c | 41 | 
3 files changed, 496 insertions, 142 deletions
diff --git a/facebook/facebook-api.c b/facebook/facebook-api.c index 144304a..8cbde7f 100644 --- a/facebook/facebook-api.c +++ b/facebook/facebook-api.c @@ -37,6 +37,134 @@ GQuark fb_api_error_quark(void)  }  /** + * Creates a new #json_value for JSON contents of the #fb_api. This + * function is special in that it handles all errors, unlike the parent + * function #fb_json_new(). The returned #json_value should be freed + * with #json_value_free() when no longer needed. + * + * @param api  The #fb_api. + * @param data The data. + * @param size The size of the data. + * + * @return TRUE if the data was parsed without error, otherwise FALSE. + **/ +static json_value *fb_api_json_new(fb_api_t *api, const gchar *data, +                                   gsize size) +{ +    json_value  *json; +    const gchar *msg; +    gint64       code; + +    json = fb_json_new(data, size, &api->err); + +    if (api->err != NULL) { +        fb_api_error(api, 0, NULL); +        return NULL; +    } + +    if (fb_json_int_chk(json, "error_code", &code)) { +        if (!fb_json_str_chk(json, "error_msg", &msg)) +            msg = "Generic Error"; + +        fb_api_error(api, FB_API_ERROR_GENERAL, "%s", msg); +        json_value_free(json); +        return NULL; +    } + +    return json; +} + +/** + * Creates a new #fb_http_req for a #fb_api request. + * + * @param api    The #fb_api. + * @param host   The host. + * @param path   The path. + * @param func   The #fb_http_func. + * @param class  The class. + * @param name   The friendly name. + * @param method The method. + **/ +static fb_http_req_t *fb_api_req_new(fb_api_t *api, const gchar *host, +                                     const gchar *path, fb_http_func_t func, +                                     const gchar *class, const gchar *name, +                                     const gchar *method) +{ +    fb_http_req_t *req; + +    req = fb_http_req_new(api->http, host, 443, path, func, api); +    req->flags = FB_HTTP_REQ_FLAG_POST | FB_HTTP_REQ_FLAG_SSL; + +    fb_http_req_params_set(req, +        FB_HTTP_PAIR("api_key",                  FB_API_KEY), +        FB_HTTP_PAIR("fb_api_caller_class",      class), +        FB_HTTP_PAIR("fb_api_req_friendly_name", name), +        FB_HTTP_PAIR("method",                   method), +        FB_HTTP_PAIR("client_country_code",      "US"), +        FB_HTTP_PAIR("format",                   "json"), +        FB_HTTP_PAIR("locale",                   "en_US"), +        NULL +    ); + +    return req; +} + +/** + * Sends a #fb_http_req for a #fb_api. This computes the signature for + * the request and sets the "sig" parameter. This sets the OAuth header + * for authorization. + * + * @param api The #fb_api. + * @param req The #fb_http_req. + **/ +static void fb_api_req_send(fb_api_t *api, fb_http_req_t *req) +{ +    GString     *gstr; +    GList       *keys; +    GList       *l; +    const gchar *key; +    const gchar *val; +    gchar       *hash; +    gchar       *auth; + +    /* Ensure an old signature is not computed */ +    g_hash_table_remove(req->params, "sig"); + +    gstr = g_string_sized_new(128); +    keys = g_hash_table_get_keys(req->params); +    keys = g_list_sort(keys, (GCompareFunc) g_ascii_strcasecmp); + +    for (l = keys; l != NULL; l = l->next) { +        key = l->data; +        val = g_hash_table_lookup(req->params, key); +        g_string_append_printf(gstr, "%s=%s", key, val); +    } + +    g_string_append(gstr, FB_API_SECRET); +    hash = g_compute_checksum_for_string(G_CHECKSUM_MD5, gstr->str, gstr->len); + +    fb_http_req_params_set(req, +        FB_HTTP_PAIR("sig", hash), +        NULL +    ); + +    g_free(hash); +    g_list_free(keys); +    g_string_free(gstr, TRUE); + +    if (api->token != NULL) { +        auth = g_strdup_printf("OAuth %s", api->token); +        fb_http_req_headers_set(req, +            FB_HTTP_PAIR("Authorization", auth), +            NULL +        ); +        g_free(auth); +    } + +    fb_http_req_send(req); +} + +/**   * Implements #fb_mqtt_funcs->error().   *   * @param mqtt The #fb_mqtt. @@ -74,7 +202,12 @@ static void fb_api_cb_mqtt_open(fb_mqtt_t *mqtt, gpointer data)              "\"a\":\"" FB_API_AGENT "\","              "\"mqtt_sid\":%s,"              "\"d\":\"%s\"," -            "\"chat_on\":true" +            "\"chat_on\":true," +            "\"no_auto_fg\":true," +            "\"fg\":false," +            "\"pf\":\"jz\"," +            "\"nwt\":1," +            "\"nwst\":0"          "}", api->uid, api->mid, api->cuid);      fb_mqtt_connect(mqtt, @@ -88,6 +221,73 @@ static void fb_api_cb_mqtt_open(fb_mqtt_t *mqtt, gpointer data)  }  /** + * Implemented #fb_http_func for the sequence identifier. + * + * @param req  The #fb_http_req. + * @param data The user-defined data, which is #fb_api. + **/ +static void fb_api_cb_seqid(fb_http_req_t *req, gpointer data) +{ +    fb_api_t    *api = data; +    json_value  *json; +    json_value  *jv; +    const gchar *str; + +    json = fb_api_json_new(api, req->body, req->body_size); + +    if (json == NULL) +        return; + +    /* Scattered values lead to a gnarly conditional... */ +    if (!fb_json_val_chk(json, "data", json_array, &jv) || + +        /* Obtain the first array element */ +        (jv->u.array.length != 1) || +        ((jv = jv->u.array.values[0]) == NULL) || + +        /* Check the name */ +        !fb_json_str_chk(jv, "name", &str) || +        (g_ascii_strcasecmp(str, "thread_list_ids") != 0) || + +        /* Obtain the sequence identifier */ +        !fb_json_val_chk(jv, "fql_result_set", json_array, &jv) || +        (jv->u.array.length != 1) || +        !fb_json_str_chk(jv->u.array.values[0], "sync_sequence_id", &str)) +    { +        fb_api_error(api, FB_API_ERROR, "Failed to obtain SequenceID"); +        goto finish; +    } + +    if (G_UNLIKELY(api->stoken == NULL)) { +        fb_api_publish(api, "/messenger_sync_create_queue", "{" +                "\"device_params\":{}," +                "\"encoding\":\"JSON\"," +                "\"max_deltas_able_to_process\":1250," +                "\"initial_titan_sequence_id\":%s," +                "\"sync_api_version\":2," +                "\"delta_batch_size\":125," +                "\"device_id\":\"%s\"" +            "}", str, api->cuid); + +        goto finish; +    } + +    fb_api_publish(api, "/messenger_sync_get_diffs", "{" +            "\"encoding\":\"JSON\"," +            "\"last_seq_id\":%s," +            "\"max_deltas_able_to_process\":1250," +            "\"sync_api_version\":2," +            "\"sync_token\":\"%s\"," +            "\"delta_batch_size\":125" +        "}", str, api->stoken); + +    FB_API_FUNC(api, connect); + +finish: +    json_value_free(json); +} + +/**   * Implements #fb_mqtt_funcs->connack().   *   * @param mqtt The #fb_mqtt. @@ -95,9 +295,122 @@ static void fb_api_cb_mqtt_open(fb_mqtt_t *mqtt, gpointer data)   **/  static void fb_api_cb_mqtt_connack(fb_mqtt_t *mqtt, gpointer data)  { -    fb_api_t *api = data; +    fb_api_t      *api = data; +    fb_http_req_t *req; -    FB_API_FUNC(api, connect); +    fb_api_publish(api, "/foreground_state", "{" +            "\"foreground\": true," +            "\"keepalive_timeout\": %d" +        "}", FB_MQTT_KA); + +    fb_mqtt_subscribe(mqtt, +        "/quick_promotion_refresh", 0, +        "/webrtc", 0, +        "/delete_messages_notification", 0, +        "/orca_message_notifications", 0, +        "/messaging_events", 0, +        "/mercury", 0, +        "/t_rtc", 0, +        "/inbox", 0, +        "/orca_presence", 0, +        "/webrtc_response", 0, +        "/push_notification", 0, +        "/pp", 0, +        "/orca_typing_notifications", 0, +        "/t_ms", 0, +        "/t_p", 0, +        NULL +    ); + +    req = fb_api_req_new(api, FB_API_GHOST, FB_API_PATH_FQL, +                         fb_api_cb_seqid, +                         "com.facebook.orca.protocol.methods.u", +                         "fetchThreadList", +                         "GET"); + +    static const gchar *query = "{" +        "\"thread_list_ids\":\"" +            "SELECT sync_sequence_id " +                "FROM unified_thread " +                "WHERE folder='inbox' " +                "ORDER BY sync_sequence_id " +                "DESC LIMIT 1\"" +        "}"; + +    fb_http_req_params_set(req, FB_HTTP_PAIR("q", query), NULL); +    fb_api_req_send(api, req); +} + +/** + * Handles messages which are to be published to the user. + * + * @param api   The #fb_api. + * @param pload The message payload. + **/ +static void fb_api_cb_publish_ms(fb_api_t *api, const GByteArray *pload) +{ +    GSList       *msgs; +    fb_api_msg_t *msg; +    json_value   *json; +    json_value   *jv; +    json_value   *jx; +    json_value   *jy; +    json_value   *jz; +    const gchar  *str; +    gint64        in; +    gint64        auid; +    guint         i; + +    /* Start at 1 to skip the NULL byte */ +    json = fb_api_json_new(api, (gchar*) pload->data + 1, pload->len - 1); +    auid = g_ascii_strtoll(api->uid, NULL, 10); +    msgs = NULL; + +    if (json == NULL) +        return; + +    if (fb_json_str_chk(json, "syncToken", &str)) { +        g_free(api->stoken); +        api->stoken = g_strdup(str); +        FB_API_FUNC(api, connect); +        goto finish; +    } + +    if (!fb_json_val_chk(json, "deltas", json_array, &jv)) +        goto finish; + +    for (i = 0; i < jv->u.array.length; i++) { +        jx = jv->u.array.values[i]; + +        if (!fb_json_val_chk(jx, "deltaNewMessage", json_object, &jy) || +            !fb_json_val_chk(jy, "messageMetadata", json_object, &jz) || +            !fb_json_int_chk(jz, "actorFbId", &in) || +            (in == auid)) +        { +            continue; +        } + +        if (fb_json_str_chk(jy, "body", &str)) { +            msg = fb_api_msg_new(NULL, str); +            msg->uid = g_strdup_printf("%" G_GINT64_FORMAT, in); +            msgs = g_slist_prepend(msgs, msg); +        } + +        if (fb_json_val_chk(jy, "attachments", json_array, &jy) && +            (jy->u.array.length > 0)) +        { +            msg = fb_api_msg_new(NULL, "* Non-Displayable Attachments *"); +            msg->uid = g_strdup_printf("%" G_GINT64_FORMAT, in); +            msgs = g_slist_prepend(msgs, msg); +        } +    } + +    msgs = g_slist_reverse(msgs); +    FB_API_FUNC(api, message, msgs); + +finish: +    g_slist_free_full(msgs, (GDestroyNotify) fb_api_msg_free); +    json_value_free(json);  }  /** @@ -128,7 +441,10 @@ static void fb_api_cb_mqtt_publish(fb_mqtt_t *mqtt, const gchar *topic,          bytes = (GByteArray*) pload;      } -    fb_util_hexdump(bytes, 2, "Publishing:"); +    fb_util_hexdump(bytes, 2, "Reading message:"); + +    if (g_ascii_strcasecmp(topic, "/t_ms") == 0) +        fb_api_cb_publish_ms(api, bytes);      if (G_LIKELY(comp))          g_byte_array_free(bytes, TRUE); @@ -183,7 +499,7 @@ void fb_api_rehash(fb_api_t *api)          api->cid = g_compute_checksum_for_data(G_CHECKSUM_MD5, rb, sizeof rb);      } -    if (api->mid == NULL) +    if (api->mid == 0)          api->mid = g_strdup_printf("%" G_GUINT32_FORMAT, g_random_int());      if (api->cuid == NULL) { @@ -215,10 +531,10 @@ void fb_api_free(fb_api_t *api)      fb_mqtt_free(api->mqtt);      fb_http_free(api->http); -    g_free(api->sid);      g_free(api->cuid);      g_free(api->mid);      g_free(api->cid); +    g_free(api->stoken);      g_free(api->token);      g_free(api->uid);      g_free(api); @@ -256,134 +572,6 @@ void fb_api_error(fb_api_t *api, fb_api_error_t err, const gchar *fmt, ...)  }  /** - * Creates a new #json_value for JSON contents of the #fb_api. This - * function is special in that it handles all errors, unlike the parent - * function #fb_json_new(). The returned #json_value should be freed - * with #json_value_free() when no longer needed. - * - * @param api  The #fb_api. - * @param data The data. - * @param size The size of the data. - * - * @return TRUE if the data was parsed without error, otherwise FALSE. - **/ -static json_value *fb_api_json_new(fb_api_t *api, const gchar *data, -                                   gsize size) -{ -    json_value  *json; -    const gchar *msg; -    gint64       code; - -    json = fb_json_new(data, size, &api->err); - -    if (api->err != NULL) { -        fb_api_error(api, 0, NULL); -        return NULL; -    } - -    if (fb_json_int_chk(json, "error_code", &code)) { -        if (!fb_json_str_chk(json, "error_msg", &msg)) -            msg = "Generic Error"; - -        fb_api_error(api, FB_API_ERROR_GENERAL, "%s", msg); -        json_value_free(json); -        return NULL; -    } - -    return json; -} - -/** - * Creates a new #fb_http_req for a #fb_api request. - * - * @param api    The #fb_api. - * @param host   The host. - * @param path   The path. - * @param func   The #fb_http_func. - * @param class  The class. - * @param name   The friendly name. - * @param method The method. - **/ -static fb_http_req_t *fb_api_req_new(fb_api_t *api, const gchar *host, -                                     const gchar *path, fb_http_func_t func, -                                     const gchar *class, const gchar *name, -                                     const gchar *method) -{ -    fb_http_req_t *req; - -    req = fb_http_req_new(api->http, host, 443, path, func, api); -    req->flags = FB_HTTP_REQ_FLAG_POST | FB_HTTP_REQ_FLAG_SSL; - -    fb_http_req_params_set(req, -        FB_HTTP_PAIR("api_key",                  FB_API_KEY), -        FB_HTTP_PAIR("fb_api_caller_class",      class), -        FB_HTTP_PAIR("fb_api_req_friendly_name", name), -        FB_HTTP_PAIR("method",                   method), -        FB_HTTP_PAIR("client_country_code",      "US"), -        FB_HTTP_PAIR("format",                   "json"), -        FB_HTTP_PAIR("locale",                   "en_US"), -        NULL -    ); - -    return req; -} - -/** - * Sends a #fb_http_req for a #fb_api. This computes the signature for - * the request and sets the "sig" parameter. This sets the OAuth header - * for authorization. - * - * @param api The #fb_api. - * @param req The #fb_http_req. - **/ -static void fb_api_req_send(fb_api_t *api, fb_http_req_t *req) -{ -    GString     *gstr; -    GList       *keys; -    GList       *l; -    const gchar *key; -    const gchar *val; -    gchar       *hash; -    gchar       *auth; - -    /* Ensure an old signature is not computed */ -    g_hash_table_remove(req->params, "sig"); - -    gstr = g_string_sized_new(128); -    keys = g_hash_table_get_keys(req->params); -    keys = g_list_sort(keys, (GCompareFunc) g_ascii_strcasecmp); - -    for (l = keys; l != NULL; l = l->next) { -        key = l->data; -        val = g_hash_table_lookup(req->params, key); -        g_string_append_printf(gstr, "%s=%s", key, val); -    } - -    g_string_append(gstr, FB_API_SECRET); -    hash = g_compute_checksum_for_string(G_CHECKSUM_MD5, gstr->str, gstr->len); - -    fb_http_req_params_set(req, -        FB_HTTP_PAIR("sig", hash), -        NULL -    ); - -    g_free(hash); -    g_list_free(keys); -    g_string_free(gstr, TRUE); - -    if (api->token != NULL) { -        auth = g_strdup_printf("OAuth %s", api->token); -        fb_http_req_headers_set(req, -            FB_HTTP_PAIR("Authorization", auth), -            NULL -        ); -        g_free(auth); -    } - -    fb_http_req_send(req); -} - -/**   * Implemented #fb_http_func for #fb_api_auth().   *   * @param req  The #fb_http_req. @@ -571,6 +759,100 @@ void fb_api_disconnect(fb_api_t *api)  }  /** + * Sends a message to a user. + * + * @param api The #fb_api. + * @param uid The target user identifier. + * @param msg The message. + **/ +void fb_api_message(fb_api_t *api, const gchar *uid, const gchar *msg) +{ +    guint64 msgid; + +    g_return_if_fail(api != NULL); +    g_return_if_fail(uid != NULL); +    g_return_if_fail(msg != NULL); + +    msgid = FB_API_MSGID(g_get_real_time() / 1000, g_random_int()); + +    fb_api_publish(api, "/send_message2", "{" +            "\"body\":\"%s\"," +            "\"to\":\"%s\"," +            "\"sender_fbid\":\"%s\"," +            "\"msgid\":%" G_GUINT64_FORMAT +        "}", msg, uid, api->uid, msgid); +} + +/** + * Publishes a string based message to the MQTT service. This enables + * compression of the message via zlib. + * + * @param api   The #fb_api. + * @param topic The message topic. + * @param fmt   The format string. + * @param ...   The format arguments. + **/ +void fb_api_publish(fb_api_t *api, const gchar *topic, const gchar *fmt, ...) +{ +    GByteArray *bytes; +    GByteArray *cytes; +    gchar      *msg; +    va_list     ap; + +    g_return_if_fail(api   != NULL); +    g_return_if_fail(topic != NULL); +    g_return_if_fail(fmt   != NULL); + +    va_start(ap, fmt); +    msg = g_strdup_vprintf(fmt, ap); +    va_end(ap); + +    bytes = g_byte_array_new_take((guint8*) msg, strlen(msg)); +    cytes = fb_util_zcompress(bytes); + +    fb_util_hexdump(bytes, 2, "Writing message:"); +    fb_mqtt_publish(api->mqtt, topic, cytes); + +    g_byte_array_free(cytes, TRUE); +    g_byte_array_free(bytes, TRUE); +} + +/** + * Creates a new #fb_api_msg. The returned #fb_api_msg should be freed + * with #fb_api_msg_free() when no longer needed. + * + * @param uid  The user identifier. + * @param text The message text. + * + * @return The #fb_api_msg or NULL on error. + **/ +fb_api_msg_t *fb_api_msg_new(const gchar *uid, const gchar *text) +{ +    fb_api_msg_t *msg; + +    msg = g_new0(fb_api_msg_t, 1); +    msg->uid  = g_strdup(uid); +    msg->text = g_strdup(text); + +    return msg; +} + +/** + * Frees all memory used by a #fb_api_msg. + * + * @param msg The #fb_api_msg. + **/ +void fb_api_msg_free(fb_api_msg_t *msg) +{ +    if (G_UNLIKELY(msg == NULL)) +        return; + +    g_free(msg->text); +    g_free(msg->uid); +    g_free(msg); +} + +/**   * Creates a new #fb_api_user. The returned #fb_api_user should be   * freed with #fb_api_user_free() when no longer needed.   * diff --git a/facebook/facebook-api.h b/facebook/facebook-api.h index b6bd8bd..5c84967 100644 --- a/facebook/facebook-api.h +++ b/facebook/facebook-api.h @@ -34,11 +34,11 @@  #define FB_API_SECRET "374e60f8b9bb6b8cbb30f78030438895"  #define FB_API_PATH_AUTH "/method/auth.login" +#define FB_API_PATH_FQL  "/fql"  #define FB_API_PATH_GQL  "/graphql"  #define FB_API_QRYID_CONTACTS  "10153122424521729" -  /**   * Executes one of the #fb_api_funcs.   * @@ -53,6 +53,19 @@          }                                              \      } G_STMT_END +/** + * Creates a message identifier. + * + * @param m The time in miliseconds (UTC). + * @param i The random integer. + * + * @return The 64-bit message identifier. + **/ +#define FB_API_MSGID(m, i) ((guint64) ( \ +        (((guint32) i) & 0x3FFFFF) |    \ +        (((guint64) m) << 22)           \ +    )) +  /** The #GError codes of #fb_api. **/  typedef enum fb_api_error fb_api_error_t; @@ -63,6 +76,9 @@ typedef struct fb_api fb_api_t;  /** The main structure for #fb_api callback functions. **/  typedef struct fb_api_funcs fb_api_funcs_t; +/** The structure for representing an #fb_api message. **/ +typedef struct fb_api_msg fb_api_msg_t; +  /** The structure for representing an #fb_api user. **/  typedef struct fb_api_user fb_api_user_t; @@ -120,6 +136,16 @@ struct fb_api_funcs       * @param data  The user-defined data or NULL.       **/      void (*contacts) (fb_api_t *api, const GSList *users, gpointer data); + +    /** +     * The message function. This is called whenever the #fb_api has +     * retrieved a message. +     * +     * @param api  The #fb_api. +     * @param msgs The #GSList of #fb_api_msg. +     * @param data The user-defined data or NULL. +     **/ +    void (*message) (fb_api_t *api, const GSList *msgs, gpointer data);  };  /** @@ -136,10 +162,19 @@ struct fb_api      gchar *uid;           /** The user identifier. **/      gchar *token;         /** The session token. **/ +    gchar *stoken;        /** The sync token. **/      gchar *cid;           /** The client identifier. **/      gchar *mid;           /** The MQTT identifier. **/      gchar *cuid;          /** The client unique identifier. **/ -    gchar *sid;           /** The sync identifier. **/ +}; + +/** + * The structure for representing an #fb_api message. + **/ +struct fb_api_msg +{ +    gchar *uid;  /** The user identifier. **/ +    gchar *text; /** The message text. **/  };  /** @@ -172,6 +207,14 @@ void fb_api_connect(fb_api_t *api);  void fb_api_disconnect(fb_api_t *api); +void fb_api_message(fb_api_t *api, const gchar *uid, const gchar *msg); + +void fb_api_publish(fb_api_t *api, const gchar *topic, const gchar *fmt, ...); + +fb_api_msg_t *fb_api_msg_new(const gchar *uid, const gchar *text); + +void fb_api_msg_free(fb_api_msg_t *msg); +  fb_api_user_t *fb_api_user_new(const gchar *uid, const gchar *name);  void fb_api_user_free(fb_api_user_t *user); diff --git a/facebook/facebook.c b/facebook/facebook.c index aed3893..a35a837 100644 --- a/facebook/facebook.c +++ b/facebook/facebook.c @@ -62,10 +62,12 @@ static void fb_cb_api_auth(fb_api_t *api, gpointer data)  static void fb_cb_api_connect(fb_api_t *api, gpointer data)  {      fb_data_t  *fata = data; +    account_t  *acc  = fata->ic->acc;      GSList     *l;      bee_user_t *bu;      imcb_connected(fata->ic); +    set_setstr(&acc->set, "stoken", api->stoken);      for (l = fata->ic->bee->users; l != NULL; l = l->next) {          bu = l->data; @@ -101,6 +103,25 @@ static void fb_cb_api_contacts(fb_api_t *api, const GSList *users,  }  /** + * Implemented #fb_api_funcs->message(). + * + * @param api  The #fb_api. + * @param msgs The #GSList of #fb_api_msg. + * @param data The user defined data, which is #fb_data. + **/ +static void fb_cb_api_message(fb_api_t *api, const GSList *msgs, gpointer data) +{ +    fb_data_t    *fata = data; +    fb_api_msg_t *msg; +    const GSList *l; + +    for (l = msgs; l != NULL; l = l->next) { +        msg = l->data; +        imcb_buddy_msg(fata->ic, msg->uid, (gchar*) msg->text, 0, 0); +    } +} + +/**   * Creates a new #fb_data with an #account. The returned #fb_data   * should be freed with #fb_data_free() when no longer needed.   * @@ -116,7 +137,8 @@ fb_data_t *fb_data_new(account_t *acc)          .error    = fb_cb_api_error,          .auth     = fb_cb_api_auth,          .connect  = fb_cb_api_connect, -        .contacts = fb_cb_api_contacts +        .contacts = fb_cb_api_contacts, +        .message  = fb_cb_api_message      };      g_return_val_if_fail(acc != NULL, NULL); @@ -127,11 +149,12 @@ fb_data_t *fb_data_new(account_t *acc)      fata->ic = imcb_new(acc);      fata->ic->proto_data = fata; -    fata->api->uid   = g_strdup(set_getstr(&acc->set, "uid")); -    fata->api->token = g_strdup(set_getstr(&acc->set, "token")); -    fata->api->cid   = g_strdup(set_getstr(&acc->set, "cid")); -    fata->api->mid   = g_strdup(set_getstr(&acc->set, "mid")); -    fata->api->cuid  = g_strdup(set_getstr(&acc->set, "cuid")); +    fata->api->uid    = g_strdup(set_getstr(&acc->set, "uid")); +    fata->api->token  = g_strdup(set_getstr(&acc->set, "token")); +    fata->api->stoken = g_strdup(set_getstr(&acc->set, "stoken")); +    fata->api->cid    = g_strdup(set_getstr(&acc->set, "cid")); +    fata->api->mid    = g_strdup(set_getstr(&acc->set, "mid")); +    fata->api->cuid   = g_strdup(set_getstr(&acc->set, "cuid"));      fb_api_rehash(fata->api); @@ -177,6 +200,9 @@ static void fb_init(account_t *acc)      s = set_add(&acc->set, "token", NULL, NULL, acc);      s->flags = SET_NULL_OK | SET_HIDDEN | SET_PASSWORD; +    s = set_add(&acc->set, "stoken", NULL, NULL, acc); +    s->flags = SET_NULL_OK | SET_HIDDEN; +      s = set_add(&acc->set, "uid", NULL, NULL, acc);      s->flags = SET_NULL_OK | SET_HIDDEN;  } @@ -229,6 +255,9 @@ static void fb_logout(struct im_connection *ic)  static int fb_buddy_msg(struct im_connection *ic, char *to, char *message,                          int flags)  { +    fb_data_t *fata = ic->proto_data; + +    fb_api_message(fata->api, to, message);      return 0;  }  | 
