comparison libpurple/protocols/mxit/login.c @ 28526:69aa4660401a

Initial addition of the MXit protocol plugin, provided by the MXit folks themselves.
author John Bailey <rekkanoryo@rekkanoryo.org>
date Sun, 08 Nov 2009 23:55:56 +0000
parents
children 95f8e7fb1f67
comparison
equal deleted inserted replaced
28525:13e668ef158d 28526:69aa4660401a
1 /*
2 * MXit Protocol libPurple Plugin
3 *
4 * -- MXit user login functionality --
5 *
6 * Pieter Loubser <libpurple@mxit.com>
7 *
8 * (C) Copyright 2009 MXit Lifestyle (Pty) Ltd.
9 * <http://www.mxitlifestyle.com>
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
24 */
25
26 #include <stdio.h>
27 #include <string.h>
28
29 #include "purple.h"
30
31 #include "protocol.h"
32 #include "mxit.h"
33 #include "cipher.h"
34 #include "login.h"
35 #include "profile.h"
36
37 /* requesting captcha size */
38 #define MXIT_CAPTCHA_HEIGHT 50
39 #define MXIT_CAPTCHA_WIDTH 150
40
41
42 /* prototypes */
43 static void mxit_register_view( struct MXitSession* session );
44 static void get_clientinfo( struct MXitSession* session );
45
46
47 /*------------------------------------------------------------------------
48 * Create a new mxit session object
49 *
50 * @return The MXit session object
51 */
52 static struct MXitSession* mxit_create_object( PurpleAccount* account )
53 {
54 struct MXitSession* session = NULL;
55 PurpleConnection* con = NULL;
56
57 /* currently the wapsite does not handle a '+' in front of the username (mxitid) so we just strip it */
58 if ( account->username[0] == '+' ) {
59 char* fixed;
60
61 /* cut off the '+' */
62 fixed = g_strdup( &account->username[1] );
63 purple_account_set_username( account, fixed );
64 g_free( fixed );
65 }
66
67 session = g_new0( struct MXitSession, 1 );
68
69 /* configure the connection (reference: "libpurple/connection.h") */
70 con = purple_account_get_connection( account );
71 con->proto_data = session;
72 con->flags |= PURPLE_CONNECTION_NO_BGCOLOR | PURPLE_CONNECTION_NO_URLDESC | PURPLE_CONNECTION_HTML;
73 session->con = con;
74
75 /* add account */
76 session->acc = account;
77
78 /* configure the session (reference: "libpurple/account.h") */
79 g_strlcpy( session->server, purple_account_get_string( account, MXIT_CONFIG_SERVER_ADDR, DEFAULT_SERVER ), sizeof( session->server ) );
80 g_strlcpy( session->http_server, purple_account_get_string( account, MXIT_CONFIG_HTTPSERVER, DEFAULT_HTTP_SERVER ), sizeof( session->http_server ) );
81 session->port = purple_account_get_int( account, MXIT_CONFIG_SERVER_PORT, DEFAULT_PORT );
82 g_strlcpy( session->distcode, purple_account_get_string( account, MXIT_CONFIG_DISTCODE, "" ), sizeof( session->distcode ) );
83 g_strlcpy( session->clientkey, purple_account_get_string( account, MXIT_CONFIG_CLIENTKEY, "" ), sizeof( session->clientkey ) );
84 g_strlcpy( session->dialcode, purple_account_get_string( account, MXIT_CONFIG_DIALCODE, "" ), sizeof( session->dialcode ) );
85 session->http = purple_account_get_bool( account, MXIT_CONFIG_USE_HTTP, FALSE );
86 session->iimages = g_hash_table_new( g_str_hash, g_str_equal );
87 session->rx_state = RX_STATE_RLEN;
88 session->http_interval = MXIT_HTTP_POLL_MIN;
89 session->http_last_poll = time( NULL );
90
91 return session;
92 }
93
94
95 /*------------------------------------------------------------------------
96 * We now have a connection established with MXit, so we can start the
97 * login procedure
98 *
99 * @param session The MXit session object
100 */
101 static void mxit_connected( struct MXitSession* session )
102 {
103 int state;
104
105 purple_debug_info( MXIT_PLUGIN_ID, "mxit_connected\n" );
106
107 session->flags |= MXIT_FLAG_CONNECTED;
108 purple_connection_update_progress( session->con, _( "Logging In..." ), 2, 4 );
109
110 /* create a timer to send a ping packet if the connection is idle */
111 session->last_tx = time( NULL );
112
113 /* encrypt the user password */
114 session->encpwd = mxit_encrypt_password( session );
115
116 state = purple_account_get_int( session->acc, MXIT_CONFIG_STATE, MXIT_STATE_LOGIN );
117 if ( state == MXIT_STATE_LOGIN ) {
118 /* create and send login packet */
119 mxit_send_login( session );
120 }
121 else {
122 if ( !session->profile ) {
123 /* we have lost the session profile, so ask the user to enter it again */
124 mxit_register_view( session );
125 }
126 else {
127 /* create and send the register packet */
128 mxit_send_register( session );
129 }
130 }
131
132 /* enable signals */
133 mxit_enable_signals( session );
134
135 #ifdef MXIT_LINK_CLICK
136 /* register for uri click notification */
137 mxit_register_uri_handler();
138 #endif
139
140 /* start the polling if this is a HTTP connection */
141 if ( session->http ) {
142 session->http_timer_id = purple_timeout_add_seconds( 2, mxit_manage_polling, session );
143 }
144
145 /* start the tx queue manager timer */
146 session->q_timer = purple_timeout_add_seconds( 2, mxit_manage_queue, session );
147 }
148
149
150 /*------------------------------------------------------------------------
151 * Callback invoked once the connection has been established to the MXit server,
152 * or on connection failure.
153 *
154 * @param user_data The MXit session object
155 * @param source The file-descriptor associated with the connection
156 * @param error_message Message explaining why the connection failed
157 */
158 static void mxit_cb_connect( gpointer user_data, gint source, const gchar* error_message )
159 {
160 struct MXitSession* session = (struct MXitSession*) user_data;
161
162 purple_debug_info( MXIT_PLUGIN_ID, "mxit_cb_connect\n" );
163
164 /* source is the file descriptor of the new connection */
165 if ( source < 0 ) {
166 purple_debug_info( MXIT_PLUGIN_ID, "mxit_cb_connect failed: %s\n", error_message );
167 purple_connection_error( session->con, _( "Unable to connect to the mxit server. Please check your server server settings." ) );
168 return;
169 }
170
171 /* we now have an open and active TCP connection to the mxit server */
172 session->fd = source;
173
174 /* start listening on the open connection for messages from the server (reference: "libpurple/eventloop.h") */
175 session->con->inpa = purple_input_add( session->fd, PURPLE_INPUT_READ, mxit_cb_rx, session );
176
177 mxit_connected( session );
178 }
179
180
181 /*------------------------------------------------------------------------
182 * Attempt to establish a connection to the MXit server.
183 *
184 * @param session The MXit session object
185 */
186 static void mxit_login_connect( struct MXitSession* session )
187 {
188 PurpleProxyConnectData* data = NULL;
189
190 purple_debug_info( MXIT_PLUGIN_ID, "mxit_login_connect\n" );
191
192 purple_connection_update_progress( session->con, _( "Connecting..." ), 1, 4 );
193
194 /*
195 * at this stage we have all the user's information we require
196 * for logging into MXit. we will now create a new connection to
197 * a MXit server.
198 */
199
200 if ( !session->http ) {
201 /* socket connection */
202 data = purple_proxy_connect( session->con, session->acc, session->server, session->port, mxit_cb_connect, session );
203 if ( !data ) {
204 purple_connection_error( session->con, _( "Unable to connect to the mxit server. Please check your server server settings." ) );
205 return;
206 }
207 }
208 else {
209 /* http connection */
210 mxit_connected( session );
211 }
212 }
213
214
215 /*------------------------------------------------------------------------
216 * Register a new account with MXit
217 *
218 * @param gc The connection object
219 * @param fields This is the fields filled-in by the user
220 */
221 static void mxit_cb_register_ok( PurpleConnection *gc, PurpleRequestFields *fields )
222 {
223 struct MXitSession* session = (struct MXitSession*) gc->proto_data;
224 struct MXitProfile* profile = session->profile;
225 const char* str;
226 const char* pin;
227 char* err = NULL;
228 int len;
229 int i;
230
231 purple_debug_info( MXIT_PLUGIN_ID, "mxit_cb_register_ok\n" );
232
233 if ( !PURPLE_CONNECTION_IS_VALID( gc ) ) {
234 purple_debug_error( MXIT_PLUGIN_ID, "Unable to register; account offline.\n" );
235 return;
236 }
237
238 /* nickname */
239 str = purple_request_fields_get_string( fields, "nickname" );
240 if ( ( !str ) || ( strlen( str ) < 3 ) ) {
241 err = "The nick name you entered is invalid.";
242 goto out;
243 }
244 g_strlcpy( profile->nickname, str, sizeof( profile->nickname ) );
245
246 /* birthdate */
247 str = purple_request_fields_get_string( fields, "bday" );
248 if ( ( !str ) || ( strlen( str ) < 10 ) || ( !validateDate( str ) ) ) {
249 err = "The birthday you entered is invalid. The correct format is: 'YYYY-MM-DD'.";
250 goto out;
251 }
252 g_strlcpy( profile->birthday, str, sizeof( profile->birthday ) );
253
254 /* gender */
255 if ( purple_request_fields_get_choice( fields, "male" ) == 0 )
256 profile->male = FALSE;
257 else
258 profile->male = TRUE;
259
260 /* pin */
261 pin = purple_request_fields_get_string( fields, "pin" );
262 if ( !pin ) {
263 err = "The PIN you entered is invalid.";
264 goto out;
265 }
266 len = strlen( pin );
267 if ( ( len < 7 ) || ( len > 10 ) ) {
268 err = "The PIN you entered has an invalid length [7-10].";
269 goto out;
270 }
271 for ( i = 0; i < len; i++ ) {
272 if ( !g_ascii_isdigit( pin[i] ) ) {
273 err = "The PIN is invalid. It should only consist of digits [0-9].";
274 goto out;
275 }
276 }
277 str = purple_request_fields_get_string( fields, "pin2" );
278 if ( ( !str ) || ( strcmp( pin, str ) != 0 ) ) {
279 err = "The two PINs you entered does not match.";
280 goto out;
281 }
282 g_strlcpy( profile->pin, pin, sizeof( profile->pin ) );
283
284 out:
285 if ( !err ) {
286 purple_account_set_password( session->acc, session->profile->pin );
287 mxit_login_connect( session );
288 }
289 else {
290 /* show error to user */
291 mxit_popup( PURPLE_NOTIFY_MSG_ERROR, _( "Registration Error" ), _( err ) );
292 mxit_register_view( session );
293 }
294 }
295
296
297 /*------------------------------------------------------------------------
298 * Register a new account with MXit
299 *
300 * @param gc The connection object
301 * @param fields This is the fields filled-in by the user
302 */
303 static void mxit_cb_register_cancel( PurpleConnection *gc, PurpleRequestFields *fields )
304 {
305 purple_debug_info( MXIT_PLUGIN_ID, "mxit_cb_register_cancel\n" );
306
307 /* disconnect */
308 purple_account_disconnect( gc->account );
309 }
310
311
312 /*------------------------------------------------------------------------
313 * Show a window to the user so that he can enter his information
314 *
315 * @param session The MXit session object
316 */
317 static void mxit_register_view( struct MXitSession* session )
318 {
319 struct MXitProfile* profile;
320 PurpleRequestFields* fields;
321 PurpleRequestFieldGroup* group;
322 PurpleRequestField* field;
323
324 if ( !session->profile ) {
325 /* we need to create a profile object here */
326 session->profile = g_new0( struct MXitProfile, 1 );
327 }
328 profile = session->profile;
329
330 fields = purple_request_fields_new();
331 group = purple_request_field_group_new( NULL );
332 purple_request_fields_add_group( fields, group );
333
334 /* mxit login name */
335 field = purple_request_field_string_new( "loginname", _( "MXit Login Name" ), purple_account_get_username( session->acc ), FALSE );
336 purple_request_field_string_set_editable( field, FALSE );
337 purple_request_field_group_add_field( group, field );
338
339 /* nick name */
340 field = purple_request_field_string_new( "nickname", _( "Nick Name" ), profile->nickname, FALSE );
341 purple_request_field_group_add_field( group, field );
342
343 /* birthday */
344 field = purple_request_field_string_new( "bday", _( "Birthday" ), profile->birthday, FALSE );
345 purple_request_field_string_set_default_value( field, "YYYY-MM-DD" );
346 purple_request_field_group_add_field( group, field );
347
348 /* gender */
349 field = purple_request_field_choice_new( "male", _( "Gender" ), ( profile->male ) ? 1 : 0 );
350 purple_request_field_choice_add( field, _( "Female" ) ); /* 0 */
351 purple_request_field_choice_add( field, _( "Male" ) ); /* 1 */
352 purple_request_field_group_add_field( group, field );
353
354 /* pin */
355 field = purple_request_field_string_new( "pin", _( "PIN" ), profile->pin, FALSE );
356 purple_request_field_string_set_masked( field, TRUE );
357 purple_request_field_group_add_field( group, field );
358 field = purple_request_field_string_new( "pin2", _( "Verify PIN" ), "", FALSE );
359 purple_request_field_string_set_masked( field, TRUE );
360 purple_request_field_group_add_field( group, field );
361
362 /* show the form to the user to complete */
363 purple_request_fields( session->con, _( "Register New MXit Account" ), _( "Register New MXit Account" ), _( "Please fill in the following fields:" ), fields, _( "OK" ), G_CALLBACK( mxit_cb_register_ok ), _( "Cancel" ), G_CALLBACK( mxit_cb_register_cancel ), session->acc, NULL, NULL, session->con );
364 }
365
366
367 /*------------------------------------------------------------------------
368 * Callback function invoked once the Authorization information has been submitted
369 * to the MXit WAP site.
370 *
371 * @param url_data libPurple internal object (see purple_util_fetch_url_request)
372 * @param user_data The MXit session object
373 * @param url_text The data returned from the WAP site
374 * @param len The length of the data returned
375 * @param error_message Descriptive error message
376 */
377 static void mxit_cb_clientinfo2( PurpleUtilFetchUrlData* url_data, gpointer user_data, const gchar* url_text, gsize len, const gchar* error_message )
378 {
379 struct MXitSession* session = (struct MXitSession*) user_data;
380 gchar** parts;
381 gchar** host;
382 int state;
383
384 purple_debug_info( MXIT_PLUGIN_ID, "mxit_clientinfo_cb2\n" );
385
386 #ifdef DEBUG_PROTOCOL
387 purple_debug_info( MXIT_PLUGIN_ID, "HTTP RESPONSE: '%s'\n", url_text );
388 #endif
389
390 if ( !url_text ) {
391 /* no reply from the WAP site */
392 purple_connection_error( session->con, _( "Error contacting the MXit WAP site. Please try again later." ) );
393 return;
394 }
395
396 /* explode the response from the WAP site into an array */
397 parts = g_strsplit( url_text, ";", 15 );
398
399 if ( !parts ) {
400 /* wapserver error */
401 purple_connection_error( session->con, _( "MXit is currently unable to process the request. Please try again later." ) );
402 return;
403 }
404
405 /* check wapsite return code */
406 switch ( parts[0][0] ) {
407 case '0' :
408 /* valid reply! */
409 break;
410 case '1' :
411 purple_connection_error( session->con, _( "Wrong security code entered. Please try again later." ) );
412 return;
413 case '2' :
414 purple_connection_error( session->con, _( "Your session has expired. Please try again later." ) );
415 return;
416 case '5' :
417 purple_connection_error( session->con, _( "Invalid country selected. Please try again." ) );
418 return;
419 case '6' :
420 purple_connection_error( session->con, _( "Username is not registered. Please register first." ) );
421 return;
422 case '7' :
423 purple_connection_error( session->con, _( "Username is already registered. Please choose another username." ) );
424 /* this user's account already exists, so we need to change the registration login flag to be login */
425 purple_account_set_int( session->acc, MXIT_CONFIG_STATE, MXIT_STATE_LOGIN );
426 return;
427 case '3' :
428 case '4' :
429 default :
430 purple_connection_error( session->con, _( "Internal error. Please try again later." ) );
431 return;
432 }
433
434 /* now parse and split the distribution code and the client key */
435 g_strlcpy( session->distcode, &parts[1][2], 36 + 1 );
436 g_strlcpy( session->clientkey, &parts[1][38], 8 + 1 );
437
438 /* get the dial code for the client */
439 g_strlcpy( session->dialcode, parts[4], sizeof( session->dialcode ) );
440
441 /* parse the proxy server address and port number */
442 host = g_strsplit( parts[2], ":", 4 );
443 g_strlcpy( session->server, &host[1][2], sizeof( session->server ) );
444 session->port = atoi( &host[2][0] );
445
446 /* parse the http proxy server address and port number */
447 g_strlcpy( session->http_server, parts[3], sizeof( session->http_server ) );
448
449 purple_debug_info( MXIT_PLUGIN_ID, "distcode='%s', clientkey='%s', dialcode='%s'\n", session->distcode, session->clientkey, session->dialcode );
450 purple_debug_info( MXIT_PLUGIN_ID, "sock_server='%s', http_server='%s', port='%i', cc='%s'\n", session->server, session->http_server, session->port, parts[11] );
451
452 /* save the information (reference: "libpurple/account.h") */
453 purple_account_set_string( session->acc, MXIT_CONFIG_DISTCODE, session->distcode );
454 purple_account_set_string( session->acc, MXIT_CONFIG_CLIENTKEY, session->clientkey );
455 purple_account_set_string( session->acc, MXIT_CONFIG_DIALCODE, session->dialcode );
456 purple_account_set_string( session->acc, MXIT_CONFIG_SERVER_ADDR, session->server );
457 purple_account_set_int( session->acc, MXIT_CONFIG_SERVER_PORT, session->port );
458 purple_account_set_string( session->acc, MXIT_CONFIG_HTTPSERVER, session->http_server );
459
460 /* update the state */
461 state = purple_account_get_int( session->acc, MXIT_CONFIG_STATE, MXIT_STATE_LOGIN );
462 if ( state == MXIT_STATE_REGISTER1 )
463 purple_account_set_int( session->acc, MXIT_CONFIG_STATE, MXIT_STATE_REGISTER2 );
464
465 /* freeup the memory */
466 g_strfreev( host );
467 g_strfreev( parts );
468
469 if ( state == MXIT_STATE_LOGIN ) {
470 /* now we can continue with the login process */
471 mxit_login_connect( session );
472 }
473 else {
474 /* the user is registering so we need to get more information from him/her first to complete the process */
475 mxit_register_view( session );
476 }
477 }
478
479
480 /*------------------------------------------------------------------------
481 * Free up the data associated with the Authorization process.
482 *
483 * @param data The data object to free
484 */
485 static void free_logindata( struct login_data* data )
486 {
487 if ( !data )
488 return;
489
490 /* free up the login resources */
491 g_free( data->wapserver );
492 g_free( data->sessionid );
493 g_free( data->captcha );
494 g_free( data->cc );
495 g_free( data->locale );
496 g_free( data );
497 }
498
499
500 /*------------------------------------------------------------------------
501 * This function is called when the user accepts the Authorization form.
502 *
503 * @param gc The connection object
504 * @param fields The list of fields in the accepted form
505 */
506 static void mxit_cb_captcha_ok( PurpleConnection* gc, PurpleRequestFields* fields )
507 {
508 struct MXitSession* session = (struct MXitSession*) gc->proto_data;
509 PurpleUtilFetchUrlData* url_data;
510 PurpleRequestField* field;
511 const char* captcha_resp;
512 GList* entries;
513 GList* entry;
514 char* url;
515 int state;
516
517 /* get the captcha response */
518 captcha_resp = purple_request_fields_get_string( fields, "code" );
519 if ( ( captcha_resp == NULL ) || ( captcha_resp[0] == '\0' ) ) {
520 /* the user did not fill in the captcha */
521 mxit_popup( PURPLE_NOTIFY_MSG_ERROR, _( "Error" ), _( "You did not enter the security code" ) );
522 free_logindata( session->logindata );
523 purple_account_disconnect( session->acc );
524 return;
525 }
526
527 /* get chosen country */
528 field = purple_request_fields_get_field( fields, "country" );
529 entries = purple_request_field_list_get_selected( field );
530 entry = g_list_first( entries );
531 session->logindata->cc = purple_request_field_list_get_data( field, entry->data );
532 purple_account_set_string( session->acc, MXIT_CONFIG_COUNTRYCODE, session->logindata->cc );
533
534 /* get chosen language */
535 field = purple_request_fields_get_field( fields, "locale" );
536 entries = purple_request_field_list_get_selected( field );
537 entry = g_list_first( entries );
538 session->logindata->locale = purple_request_field_list_get_data( field, entry->data );
539 purple_account_set_string( session->acc, MXIT_CONFIG_LOCALE, session->logindata->locale );
540
541 #ifdef DEBUG_PROTOCOL
542 purple_debug_info( MXIT_PLUGIN_ID, "cc='%s', locale='%s', captcha='%s'\n", session->logindata->cc, session->logindata->locale, captcha_resp );
543 #endif
544
545 /* get state */
546 state = purple_account_get_int( session->acc, MXIT_CONFIG_STATE, MXIT_STATE_LOGIN );
547
548 url = g_strdup_printf( "%s?type=getpid&sessionid=%s&login=%s&ver=%s&clientid=%s&cat=%s&chalresp=%s&cc=%s&loc=%s&path=%i&brand=%s&model=%s&h=%i&w=%i&ts=%li",
549 session->logindata->wapserver, session->logindata->sessionid, purple_url_encode( session->acc->username ), MXIT_CP_RELEASE, MXIT_CLIENT_ID, MXIT_CP_ARCH,
550 captcha_resp, session->logindata->cc, session->logindata->locale, ( state == MXIT_STATE_REGISTER1 ) ? 0 : 1, MXIT_CP_PLATFORM, MXIT_CP_OS,
551 MXIT_CAPTCHA_HEIGHT, MXIT_CAPTCHA_WIDTH, time( NULL ) );
552 url_data = purple_util_fetch_url_request( url, TRUE, MXIT_HTTP_USERAGENT, TRUE, NULL, FALSE, mxit_cb_clientinfo2, session );
553
554 #ifdef DEBUG_PROTOCOL
555 purple_debug_info( MXIT_PLUGIN_ID, "HTTP REQUEST: '%s'\n", url );
556 #endif
557 g_free( url );
558
559 /* free up the login resources */
560 free_logindata( session->logindata );
561 }
562
563
564 /*------------------------------------------------------------------------
565 * This function is called when the user cancels the Authorization form.
566 *
567 * @param gc The connection object
568 * @param fields The list of fields in the cancelled form
569 */
570 static void mxit_cb_captcha_cancel( PurpleConnection* gc, PurpleRequestFields* fields )
571 {
572 struct MXitSession* session = (struct MXitSession*) gc->proto_data;
573
574 /* free up the login resources */
575 free_logindata( session->logindata );
576
577 /* we cannot continue, so we disconnect this account */
578 purple_account_disconnect( session->acc );
579 }
580
581
582 /*------------------------------------------------------------------------
583 * Callback function invoked once the client information has been retrieved from
584 * the MXit WAP site. Display page where user can select their authorization information.
585 *
586 * @param url_data libPurple internal object (see purple_util_fetch_url_request)
587 * @param user_data The MXit session object
588 * @param url_text The data returned from the WAP site
589 * @param len The length of the data returned
590 * @param error_message Descriptive error message
591 */
592 static void mxit_cb_clientinfo1( PurpleUtilFetchUrlData* url_data, gpointer user_data, const gchar* url_text, gsize len, const gchar* error_message )
593 {
594 struct MXitSession* session = (struct MXitSession*) user_data;
595 struct login_data* logindata;
596 PurpleRequestFields* fields;
597 PurpleRequestFieldGroup* group = NULL;
598 PurpleRequestField* field = NULL;
599 gchar** parts;
600 gchar** countries;
601 gchar** locales;
602 int i;
603
604 purple_debug_info( MXIT_PLUGIN_ID, "mxit_clientinfo_cb1\n" );
605
606 #ifdef DEBUG_PROTOCOL
607 purple_debug_info( MXIT_PLUGIN_ID, "RESPONSE: %s\n", url_text );
608 #endif
609
610 if ( !url_text ) {
611 /* no reply from the WAP site */
612 purple_connection_error( session->con, _( "Error contacting the MXit WAP site. Please try again later." ) );
613 return;
614 }
615
616 /* explode the response from the WAP site into an array */
617 parts = g_strsplit( url_text, ";", 15 );
618
619 if ( ( !parts ) || ( parts[0][0] != '0' ) ) {
620 /* server could not find the user */
621 purple_connection_error( session->con, _( "MXit is currently unable to process the request. Please try again later." ) );
622 return;
623 }
624
625 /* save received settings */
626 logindata = g_new0( struct login_data, 1 );
627 logindata->wapserver = g_strdup( parts[1] );
628 logindata->sessionid = g_strdup( parts[2] );
629 session->logindata = logindata;
630
631 /* now generate the popup requesting the user for action */
632
633 fields = purple_request_fields_new();
634 group = purple_request_field_group_new( NULL );
635 purple_request_fields_add_group( fields, group );
636
637 /* add the captcha */
638 logindata->captcha = purple_base64_decode( parts[3], &logindata->captcha_size );
639 field = purple_request_field_image_new( "capcha", _( "Security Code" ), (gchar*) logindata->captcha, logindata->captcha_size );
640 purple_request_field_group_add_field( group, field );
641
642 /* ask for input */
643 field = purple_request_field_string_new( "code", _( "Enter Security Code" ), NULL, FALSE );
644 purple_request_field_group_add_field( group, field );
645
646 /* choose your country, but be careful, we already know your IP! ;-) */
647 countries = g_strsplit( parts[4], ",", 500 );
648 field = purple_request_field_list_new( "country", _( "Your Country" ) );
649 purple_request_field_list_set_multi_select( field, FALSE );
650 for ( i = 0; countries[i]; i++ ) {
651 gchar** country;
652
653 country = g_strsplit( countries[i], "|", 2 );
654 if ( !country ) {
655 /* oops, this is not good, time to bail */
656 break;
657 }
658 purple_request_field_list_add( field, country[1], g_strdup( country[0] ) );
659 if ( strcmp( country[1], parts[6] ) == 0 ) {
660 /* based on the user's ip, this is his current country code, so we default to it */
661 purple_request_field_list_add_selected( field, country[1] );
662 }
663 g_strfreev( country );
664 }
665 purple_request_field_group_add_field( group, field );
666
667 /* choose your language */
668 locales = g_strsplit( parts[5], ",", 200 );
669 field = purple_request_field_list_new( "locale", _( "Your Language" ) );
670 purple_request_field_list_set_multi_select( field, FALSE );
671 for ( i = 0; locales[i]; i++ ) {
672 gchar** locale;
673
674 locale = g_strsplit( locales[i], "|", 2 );
675 if ( !locale ) {
676 /* oops, this is not good, time to bail */
677 break;
678 }
679 purple_request_field_list_add( field, locale[1], g_strdup( locale[0] ) );
680 g_strfreev( locale );
681 }
682 purple_request_field_list_add_selected( field, "English" );
683 purple_request_field_group_add_field( group, field );
684
685 /* display the form to the user and wait for his/her input */
686 purple_request_fields( session->con, "MXit", _( "MXit Authorization" ), _( "MXit account validation" ), fields,
687 _( "Continue" ), G_CALLBACK( mxit_cb_captcha_ok ), _( "Cancel" ), G_CALLBACK( mxit_cb_captcha_cancel ), session->acc, NULL, NULL, session->con );
688
689 /* freeup the memory */
690 g_strfreev( parts );
691 }
692
693
694 /*------------------------------------------------------------------------
695 * Initiate a request for the client information (distribution code, client key, etc)
696 * required for logging in from the MXit WAP site.
697 *
698 * @param session The MXit session object
699 */
700 static void get_clientinfo( struct MXitSession* session )
701 {
702 PurpleUtilFetchUrlData* url_data;
703 const char* wapserver;
704 char* url;
705
706 purple_debug_info( MXIT_PLUGIN_ID, "get_clientinfo\n" );
707
708 purple_connection_update_progress( session->con, _( "Retrieving User Information..." ), 0, 4 );
709
710 /* get the WAP site as was configured by the user in the advanced settings */
711 wapserver = purple_account_get_string( session->acc, MXIT_CONFIG_WAPSERVER, DEFAULT_WAPSITE );
712
713 /* reference: "libpurple/util.h" */
714 url = g_strdup_printf( "%s/res/?type=challenge&getcountries=true&getlanguage=true&getimage=true&h=%i&w=%i&ts=%li", wapserver, MXIT_CAPTCHA_HEIGHT, MXIT_CAPTCHA_WIDTH, time( NULL ) );
715 url_data = purple_util_fetch_url_request( url, TRUE, MXIT_HTTP_USERAGENT, TRUE, NULL, FALSE, mxit_cb_clientinfo1, session );
716
717 #ifdef DEBUG_PROTOCOL
718 purple_debug_info( MXIT_PLUGIN_ID, "HTTP REQUEST: '%s'\n", url );
719 #endif
720 g_free( url );
721 }
722
723
724 /*------------------------------------------------------------------------
725 * Log the user into MXit.
726 *
727 * @param account The account object
728 */
729 void mxit_login( PurpleAccount* account )
730 {
731 struct MXitSession* session = NULL;
732
733 purple_debug_info( MXIT_PLUGIN_ID, "mxit_login\n" );
734
735 /* create and save a new mxit session */
736 session = mxit_create_object( account );
737
738 /*
739 * before we can login we need to have a valid distribution code and client key for authentication.
740 * if we don't have any info saved from a previous login, we need to get it from the MXit WAP site.
741 * we do cache it, so this step is only done on the very first login for each account.
742 */
743 if ( ( session->distcode == NULL ) || ( strlen( session->distcode ) == 0 ) ) {
744 /* this must be the very first login, so we need to retrieve the user information */
745 get_clientinfo( session );
746 }
747 else {
748 /* we can continue with the login */
749 mxit_login_connect( session );
750 }
751 }
752
753
754 /*------------------------------------------------------------------------
755 * Perform a reconnect to the MXit server, and maintain same session object.
756 *
757 * @param account The account object
758 */
759 void mxit_reconnect( struct MXitSession* session )
760 {
761 purple_debug_info( MXIT_PLUGIN_ID, "mxit_reconnect\n" );
762
763 /* close existing connection */
764 session->flags &= ~MXIT_FLAG_CONNECTED;
765 purple_proxy_connect_cancel_with_handle( session->con );
766
767 /* perform the re-connect */
768 mxit_login_connect( session );
769 }
770
771
772 /*------------------------------------------------------------------------
773 * Register a new account with MXit
774 *
775 * @param acc The account object
776 */
777 void mxit_register( PurpleAccount* account )
778 {
779 struct MXitSession* session = NULL;
780
781 purple_debug_info( MXIT_PLUGIN_ID, "mxit_register\n" );
782
783 /* create and save a new mxit session */
784 session = mxit_create_object( account );
785 purple_account_set_int( account, MXIT_CONFIG_STATE, MXIT_STATE_REGISTER1 );
786
787 get_clientinfo( session );
788 }
789