source: protocols/twitter/twitter_lib.c @ 186bd04

Last change on this file since 186bd04 was a7b9ec7, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-05-23T13:49:54Z

Improved error reporting (get textual HTTP error message and error message
from Twitter API response if possible).

  • Property mode set to 100644
File size: 18.4 KB
RevLine 
[1b221e0]1/***************************************************************************\
2*                                                                           *
3*  BitlBee - An IRC to IM gateway                                           *
4*  Simple module to facilitate twitter functionality.                       *
5*                                                                           *
6*  Copyright 2009 Geert Mulders <g.c.w.m.mulders@gmail.com>                 *
7*                                                                           *
8*  This library is free software; you can redistribute it and/or            *
9*  modify it under the terms of the GNU Lesser General Public               *
10*  License as published by the Free Software Foundation, version            *
11*  2.1.                                                                     *
12*                                                                           *
13*  This library is distributed in the hope that it will be useful,          *
14*  but WITHOUT ANY WARRANTY; without even the implied warranty of           *
15*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU        *
16*  Lesser General Public License for more details.                          *
17*                                                                           *
18*  You should have received a copy of the GNU Lesser General Public License *
19*  along with this library; if not, write to the Free Software Foundation,  *
20*  Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA           *
21*                                                                           *
22****************************************************************************/
23
[08579a1]24/* For strptime(): */
25#define _XOPEN_SOURCE
26
[1b221e0]27#include "twitter_http.h"
28#include "twitter.h"
29#include "bitlbee.h"
30#include "url.h"
31#include "misc.h"
32#include "base64.h"
33#include "xmltree.h"
34#include "twitter_lib.h"
35#include <ctype.h>
36#include <errno.h>
37
38#define TXL_STATUS 1
[62d2cfb]39#define TXL_USER 2
40#define TXL_ID 3
41
[1b221e0]42struct twitter_xml_list {
[62d2cfb]43        int type;
[1b221e0]44        int next_cursor;
45        GSList *list;
46        gpointer data;
47};
48
49struct twitter_xml_user {
50        char *name;
51        char *screen_name;
52};
53
54struct twitter_xml_status {
[08579a1]55        time_t created_at;
[1b221e0]56        char *text;
57        struct twitter_xml_user *user;
58        guint64 id;
59};
60
[62d2cfb]61/**
62 * Frees a twitter_xml_user struct.
63 */
64static void txu_free(struct twitter_xml_user *txu)
65{
66        g_free(txu->name);
67        g_free(txu->screen_name);
[2abceca]68        g_free(txu);
[62d2cfb]69}
70
71
72/**
73 * Frees a twitter_xml_status struct.
74 */
75static void txs_free(struct twitter_xml_status *txs)
76{
77        g_free(txs->text);
78        txu_free(txs->user);
[2abceca]79        g_free(txs);
[62d2cfb]80}
81
82/**
83 * Free a twitter_xml_list struct.
84 * type is the type of list the struct holds.
85 */
86static void txl_free(struct twitter_xml_list *txl)
87{
88        GSList *l;
89        for ( l = txl->list; l ; l = g_slist_next(l) )
90                if (txl->type == TXL_STATUS)
91                        txs_free((struct twitter_xml_status *)l->data);
92                else if (txl->type == TXL_ID)
93                        g_free(l->data);
94        g_slist_free(txl->list);
95}
96
97/**
98 * Add a buddy if it is not allready added, set the status to logged in.
99 */
[3e69802]100static void twitter_add_buddy(struct im_connection *ic, char *name, const char *fullname)
[62d2cfb]101{
[1014cab]102        struct twitter_data *td = ic->proto_data;
103
[62d2cfb]104        // Check if the buddy is allready in the buddy list.
[d569019]105        if (!imcb_find_buddy( ic, name ))
[62d2cfb]106        {
[e88fbe27]107                char *mode = set_getstr(&ic->acc->set, "mode");
108               
[62d2cfb]109                // The buddy is not in the list, add the buddy and set the status to logged in.
110                imcb_add_buddy( ic, name, NULL );
[3e69802]111                imcb_rename_buddy( ic, name, fullname );
[e88fbe27]112                if (g_strcasecmp(mode, "chat") == 0)
[1014cab]113                        imcb_chat_add_buddy( td->home_timeline_gc, name );
[e88fbe27]114                else if (g_strcasecmp(mode, "many") == 0)
[1014cab]115                        imcb_buddy_status( ic, name, OPT_LOGGED_IN, NULL, NULL );
[62d2cfb]116        }
117}
[1b221e0]118
[a7b9ec7]119/* Warning: May return a malloc()ed value, which will be free()d on the next
120   call. Only for short-term use. */
121static char *twitter_parse_error(struct http_request *req)
122{
123        static char *ret = NULL;
124        struct xt_parser *xp;
125        struct xt_node *node;
126        char *err_s = NULL;
127       
128        g_free(ret);
129        ret = NULL;
130       
131        xp = xt_new(NULL, NULL);
132        xt_feed(xp, req->reply_body, req->body_size);
133       
134        if ((node = xt_find_node(xp->root, "hash")) &&
135            (node = xt_find_node(node->children, "error")) &&
136            node->text_len > 0)
137                err_s = node->text;
138       
139        if (err_s)
140        {
141                ret = g_strdup_printf("%s (%s)", req->status_string, err_s);
142                xt_free(xp);
143                return ret;
144        }
145        else
146        {
147                return req->status_string;
148        }
149}
150
[1b221e0]151static void twitter_http_get_friends_ids(struct http_request *req);
152
153/**
154 * Get the friends ids.
155 */
156void twitter_get_friends_ids(struct im_connection *ic, int next_cursor)
157{
158        // Primitive, but hey! It works...     
159        char* args[2];
160        args[0] = "cursor";
161        args[1] = g_strdup_printf ("%d", next_cursor);
[bb5ce4d1]162        twitter_http(ic, TWITTER_FRIENDS_IDS_URL, twitter_http_get_friends_ids, ic, 0, args, 2);
[1b221e0]163
164        g_free(args[1]);
165}
166
167/**
168 * Function to help fill a list.
169 */
170static xt_status twitter_xt_next_cursor( struct xt_node *node, struct twitter_xml_list *txl )
171{
172        // Do something with the cursor.
[2abceca]173        txl->next_cursor = node->text != NULL ? atoi(node->text) : -1;
[1b221e0]174
175        return XT_HANDLED;
176}
177
178/**
179 * Fill a list of ids.
180 */
181static xt_status twitter_xt_get_friends_id_list( struct xt_node *node, struct twitter_xml_list *txl )
182{
183        struct xt_node *child;
[62d2cfb]184       
185        // Set the list type.
186        txl->type = TXL_ID;
[1b221e0]187
188        // The root <statuses> node should hold the list of statuses <status>
189        // Walk over the nodes children.
190        for( child = node->children ; child ; child = child->next )
191        {
192                if ( g_strcasecmp( "id", child->name ) == 0)
193                {
194                        // Add the item to the list.
[2abceca]195                        txl->list = g_slist_append (txl->list, g_memdup( child->text, child->text_len + 1 ));
[1b221e0]196                }
197                else if ( g_strcasecmp( "next_cursor", child->name ) == 0)
198                {
199                        twitter_xt_next_cursor(child, txl);
200                }
201        }
202
203        return XT_HANDLED;
204}
205
206/**
207 * Callback for getting the friends ids.
208 */
209static void twitter_http_get_friends_ids(struct http_request *req)
210{
211        struct im_connection *ic;
212        struct xt_parser *parser;
213        struct twitter_xml_list *txl;
[3bd4a93]214        struct twitter_data *td;
[1b221e0]215
216        ic = req->data;
217
[62d2cfb]218        // Check if the connection is still active.
219        if( !g_slist_find( twitter_connections, ic ) )
220                return;
[37aa317]221       
222        td = ic->proto_data;
[62d2cfb]223
[1b221e0]224        // Check if the HTTP request went well.
225        if (req->status_code != 200) {
226                // It didn't go well, output the error and return.
[3bd4a93]227                if (++td->http_fails >= 5)
[a7b9ec7]228                        imcb_error(ic, "Could not retrieve friends: %s", twitter_parse_error(req));
[3bd4a93]229               
[1b221e0]230                return;
[3bd4a93]231        } else {
232                td->http_fails = 0;
[1b221e0]233        }
234
235        txl = g_new0(struct twitter_xml_list, 1);
236
237        // Parse the data.
238        parser = xt_new( NULL, txl );
239        xt_feed( parser, req->reply_body, req->body_size );
240        twitter_xt_get_friends_id_list(parser->root, txl);
241        xt_free( parser );
242
243        if (txl->next_cursor)
244                twitter_get_friends_ids(ic, txl->next_cursor);
245
[62d2cfb]246        txl_free(txl);
[1b221e0]247        g_free(txl);
248}
249
250/**
251 * Function to fill a twitter_xml_user struct.
252 * It sets:
253 *  - the name and
254 *  - the screen_name.
255 */
256static xt_status twitter_xt_get_user( struct xt_node *node, struct twitter_xml_user *txu )
257{
258        struct xt_node *child;
259
260        // Walk over the nodes children.
261        for( child = node->children ; child ; child = child->next )
262        {
263                if ( g_strcasecmp( "name", child->name ) == 0)
264                {
265                        txu->name = g_memdup( child->text, child->text_len + 1 );
266                }
267                else if (g_strcasecmp( "screen_name", child->name ) == 0)
268                {
269                        txu->screen_name = g_memdup( child->text, child->text_len + 1 );
270                }
271        }
272        return XT_HANDLED;
273}
274
[62d2cfb]275/**
276 * Function to fill a twitter_xml_list struct.
277 * It sets:
278 *  - all <user>s from the <users> element.
279 */
280static xt_status twitter_xt_get_users( struct xt_node *node, struct twitter_xml_list *txl )
281{
282        struct twitter_xml_user *txu;
283        struct xt_node *child;
284
285        // Set the type of the list.
286        txl->type = TXL_USER;
287
288        // The root <users> node should hold the list of users <user>
289        // Walk over the nodes children.
290        for( child = node->children ; child ; child = child->next )
291        {
292                if ( g_strcasecmp( "user", child->name ) == 0)
293                {
294                        txu = g_new0(struct twitter_xml_user, 1);
295                        twitter_xt_get_user(child, txu);
296                        // Put the item in the front of the list.
297                        txl->list = g_slist_prepend (txl->list, txu);
298                }
299        }
300
301        return XT_HANDLED;
302}
303
304/**
305 * Function to fill a twitter_xml_list struct.
306 * It calls twitter_xt_get_users to get the <user>s from a <users> element.
307 * It sets:
308 *  - the next_cursor.
309 */
310static xt_status twitter_xt_get_user_list( struct xt_node *node, struct twitter_xml_list *txl )
311{
312        struct xt_node *child;
313
314        // Set the type of the list.
315        txl->type = TXL_USER;
316
317        // The root <user_list> node should hold a users <users> element
318        // Walk over the nodes children.
319        for( child = node->children ; child ; child = child->next )
320        {
321                if ( g_strcasecmp( "users", child->name ) == 0)
322                {
323                        twitter_xt_get_users(child, txl);
324                }
325                else if ( g_strcasecmp( "next_cursor", child->name ) == 0)
326                {
327                        twitter_xt_next_cursor(child, txl);
328                }
329        }
330
331        return XT_HANDLED;
332}
333
334
[1b221e0]335/**
336 * Function to fill a twitter_xml_status struct.
337 * It sets:
338 *  - the status text and
339 *  - the created_at timestamp and
340 *  - the status id and
341 *  - the user in a twitter_xml_user struct.
342 */
343static xt_status twitter_xt_get_status( struct xt_node *node, struct twitter_xml_status *txs )
344{
345        struct xt_node *child;
346
347        // Walk over the nodes children.
348        for( child = node->children ; child ; child = child->next )
349        {
350                if ( g_strcasecmp( "text", child->name ) == 0)
351                {
352                        txs->text = g_memdup( child->text, child->text_len + 1 );
353                }
354                else if (g_strcasecmp( "created_at", child->name ) == 0)
355                {
[08579a1]356                        struct tm parsed;
357                       
358                        /* Very sensitive to changes to the formatting of
359                           this field. :-( Also assumes the timezone used
360                           is UTC since C time handling functions suck. */
361                        if( strptime( child->text, "%a %b %d %H:%M:%S %z %Y", &parsed ) != NULL )
362                                txs->created_at = mktime_utc( &parsed );
[1b221e0]363                }
364                else if (g_strcasecmp( "user", child->name ) == 0)
365                {
366                        txs->user = g_new0(struct twitter_xml_user, 1);
367                        twitter_xt_get_user( child, txs->user );
368                }
369                else if (g_strcasecmp( "id", child->name ) == 0)
370                {
371                        txs->id = g_ascii_strtoull (child->text, NULL, 10);
372                }
373        }
374        return XT_HANDLED;
375}
376
377/**
378 * Function to fill a twitter_xml_list struct.
379 * It sets:
380 *  - all <status>es within the <status> element and
381 *  - the next_cursor.
382 */
383static xt_status twitter_xt_get_status_list( struct xt_node *node, struct twitter_xml_list *txl )
384{
385        struct twitter_xml_status *txs;
386        struct xt_node *child;
387
[62d2cfb]388        // Set the type of the list.
389        txl->type = TXL_STATUS;
390
[1b221e0]391        // The root <statuses> node should hold the list of statuses <status>
392        // Walk over the nodes children.
393        for( child = node->children ; child ; child = child->next )
394        {
395                if ( g_strcasecmp( "status", child->name ) == 0)
396                {
397                        txs = g_new0(struct twitter_xml_status, 1);
398                        twitter_xt_get_status(child, txs);
399                        // Put the item in the front of the list.
400                        txl->list = g_slist_prepend (txl->list, txs);
401                }
402                else if ( g_strcasecmp( "next_cursor", child->name ) == 0)
403                {
404                        twitter_xt_next_cursor(child, txl);
405                }
406        }
407
408        return XT_HANDLED;
409}
410
411static void twitter_http_get_home_timeline(struct http_request *req);
412
413/**
414 * Get the timeline.
415 */
416void twitter_get_home_timeline(struct im_connection *ic, int next_cursor)
417{
418        struct twitter_data *td = ic->proto_data;
419
420        char* args[4];
421        args[0] = "cursor";
422        args[1] = g_strdup_printf ("%d", next_cursor);
423        if (td->home_timeline_id) {
424                args[2] = "since_id";
[0519b0a]425                args[3] = g_strdup_printf ("%llu", (long long unsigned int) td->home_timeline_id);
[1b221e0]426        }
427
[bb5ce4d1]428        twitter_http(ic, TWITTER_HOME_TIMELINE_URL, twitter_http_get_home_timeline, ic, 0, args, td->home_timeline_id ? 4 : 2);
[1b221e0]429
430        g_free(args[1]);
431        if (td->home_timeline_id) {
432                g_free(args[3]);
433        }
434}
435
[62d2cfb]436/**
437 * Function that is called to see the statuses in a groupchat window.
438 */
439static void twitter_groupchat(struct im_connection *ic, GSList *list)
440{
441        struct twitter_data *td = ic->proto_data;
442        GSList *l = NULL;
443        struct twitter_xml_status *status;
444        struct groupchat *gc;
445
446        // Create a new groupchat if it does not exsist.
447        if (!td->home_timeline_gc)
448        {   
[cca0692]449                char *name_hint = g_strdup_printf( "Twitter_%s", ic->acc->user );
[62d2cfb]450                td->home_timeline_gc = gc = imcb_chat_new( ic, "home/timeline" );
[cca0692]451                imcb_chat_name_hint( gc, name_hint );
452                g_free( name_hint );
[62d2cfb]453                // Add the current user to the chat...
454                imcb_chat_add_buddy( gc, ic->acc->user );
455        }
456        else
457        {   
458                gc = td->home_timeline_gc;
459        }
460
461        for ( l = list; l ; l = g_slist_next(l) )
462        {
463                status = l->data;
[3e69802]464                twitter_add_buddy(ic, status->user->screen_name, status->user->name);
[d569019]465               
[0b3ffb1]466                strip_html(status->text);
467               
[62d2cfb]468                // Say it!
[d569019]469                if (g_strcasecmp(td->user, status->user->screen_name) == 0)
470                        imcb_chat_log (gc, "Your Tweet: %s", status->text);
471                else
[08579a1]472                        imcb_chat_msg (gc, status->user->screen_name, status->text, 0, status->created_at );
[d569019]473               
[62d2cfb]474                // Update the home_timeline_id to hold the highest id, so that by the next request
475                // we won't pick up the updates allready in the list.
476                td->home_timeline_id = td->home_timeline_id < status->id ? status->id : td->home_timeline_id;
477        }
478}
479
480/**
481 * Function that is called to see statuses as private messages.
482 */
483static void twitter_private_message_chat(struct im_connection *ic, GSList *list)
484{
485        struct twitter_data *td = ic->proto_data;
486        GSList *l = NULL;
487        struct twitter_xml_status *status;
[e88fbe27]488        char from[MAX_STRING];
489        gboolean mode_one;
490       
491        mode_one = g_strcasecmp( set_getstr( &ic->acc->set, "mode" ), "one" ) == 0;
[62d2cfb]492
[e88fbe27]493        if( mode_one )
494        {
495                g_snprintf( from, sizeof( from ) - 1, "twitter_%s", ic->acc->user );
496                from[MAX_STRING-1] = '\0';
497        }
498       
[62d2cfb]499        for ( l = list; l ; l = g_slist_next(l) )
500        {
[e88fbe27]501                char *text = NULL;
502               
[62d2cfb]503                status = l->data;
[e88fbe27]504               
[0b3ffb1]505                strip_html( status->text );
[e88fbe27]506                if( mode_one )
507                        text = g_strdup_printf( "\002<\002%s\002>\002 %s",
508                                                status->user->screen_name, status->text );
[55b1e69]509                else
510                        twitter_add_buddy(ic, status->user->screen_name, status->user->name);
[e88fbe27]511               
512                imcb_buddy_msg( ic,
513                                mode_one ? from : status->user->screen_name,
514                                mode_one ? text : status->text,
515                                0, status->created_at );
516               
[62d2cfb]517                // Update the home_timeline_id to hold the highest id, so that by the next request
518                // we won't pick up the updates allready in the list.
519                td->home_timeline_id = td->home_timeline_id < status->id ? status->id : td->home_timeline_id;
[e88fbe27]520               
521                g_free( text );
[62d2cfb]522        }
523}
524
[1b221e0]525/**
526 * Callback for getting the home timeline.
527 */
528static void twitter_http_get_home_timeline(struct http_request *req)
529{
[62d2cfb]530        struct im_connection *ic = req->data;
[37aa317]531        struct twitter_data *td;
[1b221e0]532        struct xt_parser *parser;
533        struct twitter_xml_list *txl;
[62d2cfb]534
535        // Check if the connection is still active.
536        if( !g_slist_find( twitter_connections, ic ) )
537                return;
[37aa317]538       
539        td = ic->proto_data;
[1b221e0]540
541        // Check if the HTTP request went well.
[3bd4a93]542        if (req->status_code == 200)
543        {
544                td->http_fails = 0;
[c2ecadc]545                if (!(ic->flags & OPT_LOGGED_IN))
[3bd4a93]546                        imcb_connected(ic);
547        }
548        else if (req->status_code == 401)
549        {
550                imcb_error( ic, "Authentication failure" );
551                imc_logout( ic, FALSE );
552                return;
553        }
554        else
555        {
[1b221e0]556                // It didn't go well, output the error and return.
[3bd4a93]557                if (++td->http_fails >= 5)
[a7b9ec7]558                        imcb_error(ic, "Could not retrieve " TWITTER_HOME_TIMELINE_URL ": %s", twitter_parse_error(req));
[3bd4a93]559               
[1b221e0]560                return;
561        }
562
563        txl = g_new0(struct twitter_xml_list, 1);
564        txl->list = NULL;
[62d2cfb]565
[1b221e0]566        // Parse the data.
567        parser = xt_new( NULL, txl );
568        xt_feed( parser, req->reply_body, req->body_size );
569        // The root <statuses> node should hold the list of statuses <status>
570        twitter_xt_get_status_list(parser->root, txl);
571        xt_free( parser );
572
[62d2cfb]573        // See if the user wants to see the messages in a groupchat window or as private messages.
[e88fbe27]574        if (g_strcasecmp(set_getstr(&ic->acc->set, "mode"), "chat") == 0)
[62d2cfb]575                twitter_groupchat(ic, txl->list);
[b4dd253]576        else
[62d2cfb]577                twitter_private_message_chat(ic, txl->list);
[1b221e0]578
579        // Free the structure. 
[62d2cfb]580        txl_free(txl);
[1b221e0]581        g_free(txl);
582}
583
584/**
[62d2cfb]585 * Callback for getting (twitter)friends...
586 *
587 * Be afraid, be very afraid! This function will potentially add hundreds of "friends". "Who has
588 * hundreds of friends?" you wonder? You probably not, since you are reading the source of
589 * BitlBee... Get a life and meet new people!
[1b221e0]590 */
[62d2cfb]591static void twitter_http_get_statuses_friends(struct http_request *req)
[1b221e0]592{
[62d2cfb]593        struct im_connection *ic = req->data;
[37aa317]594        struct twitter_data *td;
[62d2cfb]595        struct xt_parser *parser;
596        struct twitter_xml_list *txl;
[2abceca]597        GSList *l = NULL;
598        struct twitter_xml_user *user;
[62d2cfb]599
600        // Check if the connection is still active.
601        if( !g_slist_find( twitter_connections, ic ) )
602                return;
[37aa317]603       
604        td = ic->proto_data;
605       
[62d2cfb]606        // Check if the HTTP request went well.
607        if (req->status_code != 200) {
608                // It didn't go well, output the error and return.
[3bd4a93]609                if (++td->http_fails >= 5)
[a7b9ec7]610                        imcb_error(ic, "Could not retrieve " TWITTER_SHOW_FRIENDS_URL ": %s", twitter_parse_error(req));
[3bd4a93]611               
[62d2cfb]612                return;
[3bd4a93]613        } else {
614                td->http_fails = 0;
[62d2cfb]615        }
616
617        txl = g_new0(struct twitter_xml_list, 1);
618        txl->list = NULL;
619
620        // Parse the data.
621        parser = xt_new( NULL, txl );
622        xt_feed( parser, req->reply_body, req->body_size );
623
624        // Get the user list from the parsed xml feed.
625        twitter_xt_get_user_list(parser->root, txl);
626        xt_free( parser );
627
628        // Add the users as buddies.
[1b221e0]629        for ( l = txl->list; l ; l = g_slist_next(l) )
[62d2cfb]630        {
631                user = l->data;
[3e69802]632                twitter_add_buddy(ic, user->screen_name, user->name);
[62d2cfb]633        }
[1b221e0]634
[62d2cfb]635        // if the next_cursor is set to something bigger then 0 there are more friends to gather.
636        if (txl->next_cursor > 0)
637                twitter_get_statuses_friends(ic, txl->next_cursor);
638
639        // Free the structure.
640        txl_free(txl);
641        g_free(txl);
[1b221e0]642}
643
644/**
[62d2cfb]645 * Get the friends.
[1b221e0]646 */
[62d2cfb]647void twitter_get_statuses_friends(struct im_connection *ic, int next_cursor)
[1b221e0]648{
[62d2cfb]649        char* args[2];
650        args[0] = "cursor";
651        args[1] = g_strdup_printf ("%d", next_cursor);
652
[bb5ce4d1]653        twitter_http(ic, TWITTER_SHOW_FRIENDS_URL, twitter_http_get_statuses_friends, ic, 0, args, 2);
[62d2cfb]654
655        g_free(args[1]);
[1b221e0]656}
657
658/**
659 * Callback after sending a new update to twitter.
660 */
661static void twitter_http_post_status(struct http_request *req)
662{
663        struct im_connection *ic = req->data;
664
[62d2cfb]665        // Check if the connection is still active.
666        if( !g_slist_find( twitter_connections, ic ) )
667                return;
668
[1b221e0]669        // Check if the HTTP request went well.
670        if (req->status_code != 200) {
671                // It didn't go well, output the error and return.
[a7b9ec7]672                imcb_error(ic, "Could not post message: %s", twitter_parse_error(req));
[1b221e0]673                return;
674        }
675}
676
677/**
678 * Function to POST a new status to twitter.
679 */ 
680void twitter_post_status(struct im_connection *ic, char* msg)
681{
682        char* args[2];
683        args[0] = "status";
684        args[1] = msg;
[bb5ce4d1]685        twitter_http(ic, TWITTER_STATUS_UPDATE_URL, twitter_http_post_status, ic, 1, args, 2);
[62d2cfb]686//      g_free(args[1]);
[1b221e0]687}
688
689
[62d2cfb]690/**
691 * Function to POST a new message to twitter.
692 */
693void twitter_direct_messages_new(struct im_connection *ic, char *who, char *msg)
694{
695        char* args[4];
696        args[0] = "screen_name";
697        args[1] = who;
698        args[2] = "text";
699        args[3] = msg;
700        // Use the same callback as for twitter_post_status, since it does basically the same.
[bb5ce4d1]701        twitter_http(ic, TWITTER_DIRECT_MESSAGES_NEW_URL, twitter_http_post_status, ic, 1, args, 4);
[62d2cfb]702//      g_free(args[1]);
703//      g_free(args[3]);
704}
Note: See TracBrowser for help on using the repository browser.