Mercurial > pidgin.yaz
comparison libpurple/protocols/mxit/protocol.c @ 28903: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 | 7d0b473f2295 |
comparison
equal
deleted
inserted
replaced
28901:13e668ef158d | 28903:69aa4660401a |
---|---|
1 /* | |
2 * MXit Protocol libPurple Plugin | |
3 * | |
4 * -- MXit client protocol implementation -- | |
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 <unistd.h> | |
28 #include <string.h> | |
29 #include <errno.h> | |
30 | |
31 #include "purple.h" | |
32 | |
33 #include "protocol.h" | |
34 #include "mxit.h" | |
35 #include "roster.h" | |
36 #include "chunk.h" | |
37 #include "filexfer.h" | |
38 #include "markup.h" | |
39 #include "multimx.h" | |
40 #include "splashscreen.h" | |
41 #include "login.h" | |
42 #include "formcmds.h" | |
43 #include "http.h" | |
44 | |
45 | |
46 #define MXIT_MS_OFFSET 3 | |
47 | |
48 /* configure the right record terminator char to use */ | |
49 #define CP_REC_TERM ( ( session->http ) ? CP_HTTP_REC_TERM : CP_SOCK_REC_TERM ) | |
50 | |
51 | |
52 | |
53 /*------------------------------------------------------------------------ | |
54 * Display a notification popup message to the user. | |
55 * | |
56 * @param type The type of notification: | |
57 * - info: PURPLE_NOTIFY_MSG_INFO | |
58 * - warning: PURPLE_NOTIFY_MSG_WARNING | |
59 * - error: PURPLE_NOTIFY_MSG_ERROR | |
60 * @param heading Heading text | |
61 * @param message Message text | |
62 */ | |
63 void mxit_popup( int type, const char* heading, const char* message ) | |
64 { | |
65 /* (reference: "libpurple/notify.h") */ | |
66 purple_notify_message( NULL, type, _( MXIT_POPUP_WIN_NAME ), heading, message, NULL, NULL ); | |
67 } | |
68 | |
69 | |
70 /*------------------------------------------------------------------------ | |
71 * For compatibility with legacy clients, all usernames are sent from MXit with a domain | |
72 * appended. For MXit contacts, this domain is set to "@m". This function strips | |
73 * those fake domains. | |
74 * | |
75 * @param username The username of the contact | |
76 */ | |
77 void mxit_strip_domain( char* username ) | |
78 { | |
79 if ( g_str_has_suffix( username, "@m" ) ) | |
80 username[ strlen(username) - 2 ] = '\0'; | |
81 } | |
82 | |
83 | |
84 /*------------------------------------------------------------------------ | |
85 * Dump a byte buffer to the console for debugging purposes. | |
86 * | |
87 * @param buf The data | |
88 * @param len The data length | |
89 */ | |
90 void dump_bytes( struct MXitSession* session, const char* buf, int len ) | |
91 { | |
92 char msg[( len * 3 ) + 1]; | |
93 int i; | |
94 | |
95 memset( msg, 0x00, sizeof( msg ) ); | |
96 | |
97 for ( i = 0; i < len; i++ ) { | |
98 if ( buf[i] == CP_REC_TERM ) /* record terminator */ | |
99 msg[i] = '!'; | |
100 else if ( buf[i] == CP_FLD_TERM ) /* field terminator */ | |
101 msg[i] = '^'; | |
102 else if ( buf[i] == CP_PKT_TERM ) /* packet terminator */ | |
103 msg[i] = '@'; | |
104 else if ( buf[i] < 0x20 ) | |
105 msg[i] = '_'; | |
106 else | |
107 msg[i] = buf[i]; | |
108 | |
109 } | |
110 | |
111 purple_debug_info( MXIT_PLUGIN_ID, "DUMP: '%s'\n", msg ); | |
112 } | |
113 | |
114 | |
115 /*------------------------------------------------------------------------ | |
116 * Determine if we have an active chat with a specific contact | |
117 * | |
118 * @param session The MXit session object | |
119 * @param who The contact name | |
120 * @return Return true if we have an active chat with the contact | |
121 */ | |
122 gboolean find_active_chat( const GList* chats, const char* who ) | |
123 { | |
124 const GList* list = chats; | |
125 const char* chat = NULL; | |
126 | |
127 while ( list ) { | |
128 chat = (const char*) list->data; | |
129 | |
130 if ( strcmp( chat, who ) == 0 ) | |
131 return TRUE; | |
132 | |
133 list = g_list_next( list ); | |
134 } | |
135 | |
136 return FALSE; | |
137 } | |
138 | |
139 | |
140 /*======================================================================================================================== | |
141 * Low-level Packet transmission | |
142 */ | |
143 | |
144 /*------------------------------------------------------------------------ | |
145 * Remove next packet from transmission queue. | |
146 * | |
147 * @param session The MXit session object | |
148 * @return The next packet for transmission (or NULL) | |
149 */ | |
150 static struct tx_packet* pop_tx_packet( struct MXitSession* session ) | |
151 { | |
152 struct tx_packet* packet = NULL; | |
153 | |
154 if ( session->queue.count > 0 ) { | |
155 /* dequeue the next packet */ | |
156 packet = session->queue.packets[session->queue.rd_i]; | |
157 session->queue.packets[session->queue.rd_i] = NULL; | |
158 session->queue.rd_i = ( session->queue.rd_i + 1 ) % MAX_QUEUE_SIZE; | |
159 session->queue.count--; | |
160 } | |
161 | |
162 return packet; | |
163 } | |
164 | |
165 | |
166 /*------------------------------------------------------------------------ | |
167 * Add packet to transmission queue. | |
168 * | |
169 * @param session The MXit session object | |
170 * @param packet The packet to transmit | |
171 * @return Return TRUE if packet was enqueue, or FALSE if queue is full. | |
172 */ | |
173 static gboolean push_tx_packet( struct MXitSession* session, struct tx_packet* packet ) | |
174 { | |
175 if ( session->queue.count < MAX_QUEUE_SIZE ) { | |
176 /* enqueue packet */ | |
177 session->queue.packets[session->queue.wr_i] = packet; | |
178 session->queue.wr_i = ( session->queue.wr_i + 1 ) % MAX_QUEUE_SIZE; | |
179 session->queue.count++; | |
180 return TRUE; | |
181 } | |
182 else | |
183 return FALSE; /* queue is full */ | |
184 } | |
185 | |
186 | |
187 /*------------------------------------------------------------------------ | |
188 * Deallocate transmission packet. | |
189 * | |
190 * @param packet The packet to deallocate. | |
191 */ | |
192 static void free_tx_packet( struct tx_packet* packet ) | |
193 { | |
194 g_free( packet->data ); | |
195 g_free( packet ); | |
196 packet = NULL; | |
197 } | |
198 | |
199 | |
200 /*------------------------------------------------------------------------ | |
201 * Flush all the packets from the tx queue and release the resources. | |
202 * | |
203 * @param session The MXit session object | |
204 */ | |
205 static void flush_queue( struct MXitSession* session ) | |
206 { | |
207 struct tx_packet* packet; | |
208 | |
209 purple_debug_info( MXIT_PLUGIN_ID, "flushing the tx queue\n" ); | |
210 | |
211 while ( (packet = pop_tx_packet( session ) ) != NULL ) | |
212 free_tx_packet( packet ); | |
213 } | |
214 | |
215 | |
216 /*------------------------------------------------------------------------ | |
217 * TX Step 3: Write the packet data to the TCP connection. | |
218 * | |
219 * @param fd The file descriptor | |
220 * @param pktdata The packet data | |
221 * @param pktlen The length of the packet data | |
222 * @return Return -1 on error, otherwise 0 | |
223 */ | |
224 static int mxit_write_sock_packet( int fd, const char* pktdata, int pktlen ) | |
225 { | |
226 int written; | |
227 int res; | |
228 | |
229 written = 0; | |
230 while ( written < pktlen ) { | |
231 res = write( fd, &pktdata[written], pktlen - written ); | |
232 if ( res <= 0 ) { | |
233 /* error on socket */ | |
234 if ( errno == EAGAIN ) | |
235 continue; | |
236 | |
237 purple_debug_error( MXIT_PLUGIN_ID, "Error while writing packet to MXit server (%i)\n", res ); | |
238 return -1; | |
239 } | |
240 written += res; | |
241 } | |
242 | |
243 return 0; | |
244 } | |
245 | |
246 | |
247 /*------------------------------------------------------------------------ | |
248 * Callback called for handling a HTTP GET response | |
249 * | |
250 * @param url_data libPurple internal object (see purple_util_fetch_url_request) | |
251 * @param user_data The MXit session object | |
252 * @param url_text The data returned (could be NULL if error) | |
253 * @param len The length of the data returned (0 if error) | |
254 * @param error_message Descriptive error message | |
255 */ | |
256 static void mxit_cb_http_rx( PurpleUtilFetchUrlData* url_data, gpointer user_data, const gchar* url_text, gsize len, const gchar* error_message ) | |
257 { | |
258 struct MXitSession* session = (struct MXitSession*) user_data; | |
259 | |
260 /* clear outstanding request */ | |
261 session->http_out_req = NULL; | |
262 | |
263 if ( ( !url_text ) || ( len == 0 ) ) { | |
264 /* error with request */ | |
265 purple_debug_error( MXIT_PLUGIN_ID, "HTTP response error (%s)\n", error_message ); | |
266 return; | |
267 } | |
268 | |
269 /* convert the HTTP result */ | |
270 memcpy( session->rx_dbuf, url_text, len ); | |
271 session->rx_i = len; | |
272 | |
273 mxit_parse_packet( session ); | |
274 } | |
275 | |
276 | |
277 /*------------------------------------------------------------------------ | |
278 * TX Step 3: Write the packet data to the HTTP connection (GET style). | |
279 * | |
280 * @param session The MXit session object | |
281 * @param pktdata The packet data | |
282 * @param pktlen The length of the packet data | |
283 * @return Return -1 on error, otherwise 0 | |
284 */ | |
285 static void mxit_write_http_get( struct MXitSession* session, struct tx_packet* packet ) | |
286 { | |
287 char* part = NULL; | |
288 char* url = NULL; | |
289 | |
290 if ( packet->datalen > 0 ) { | |
291 char* tmp = NULL; | |
292 | |
293 tmp = g_strndup( packet->data, packet->datalen ); | |
294 part = g_strdup( purple_url_encode( tmp ) ); | |
295 g_free( tmp ); | |
296 } | |
297 | |
298 url = g_strdup_printf( "%s?%s%s", session->http_server, purple_url_encode( packet->header ), ( !part ) ? "" : part ); | |
299 | |
300 #ifdef DEBUG_PROTOCOL | |
301 purple_debug_info( MXIT_PLUGIN_ID, "HTTP GET: '%s'\n", url ); | |
302 #endif | |
303 | |
304 /* send the HTTP request */ | |
305 session->http_out_req = purple_util_fetch_url_request( url, TRUE, MXIT_HTTP_USERAGENT, TRUE, NULL, FALSE, mxit_cb_http_rx, session ); | |
306 | |
307 g_free( url ); | |
308 if ( part ) | |
309 g_free( part ); | |
310 } | |
311 | |
312 | |
313 /*------------------------------------------------------------------------ | |
314 * TX Step 3: Write the packet data to the HTTP connection (POST style). | |
315 * | |
316 * @param session The MXit session object | |
317 * @param pktdata The packet data | |
318 * @param pktlen The length of the packet data | |
319 * @return Return -1 on error, otherwise 0 | |
320 */ | |
321 static void mxit_write_http_post( struct MXitSession* session, struct tx_packet* packet ) | |
322 { | |
323 char request[256 + packet->datalen]; | |
324 int reqlen; | |
325 char* host_name; | |
326 int host_port; | |
327 gboolean ok; | |
328 | |
329 /* extract the HTTP host name and host port number to connect to */ | |
330 ok = purple_url_parse( session->http_server, &host_name, &host_port, NULL, NULL, NULL ); | |
331 if ( !ok ) { | |
332 purple_debug_error( MXIT_PLUGIN_ID, "HTTP POST error: (host name '%s' not valid)\n", session->http_server ); | |
333 } | |
334 | |
335 /* strip off the last '&' from the header */ | |
336 packet->header[packet->headerlen - 1] = '\0'; | |
337 packet->headerlen--; | |
338 | |
339 /* build the HTTP request packet */ | |
340 reqlen = g_snprintf( request, 256, | |
341 "POST %s?%s HTTP/1.1\r\n" | |
342 "User-Agent: " MXIT_HTTP_USERAGENT "\r\n" | |
343 "Content-Type: application/octet-stream\r\n" | |
344 "Host: %s\r\n" | |
345 "Content-Length: %" G_GSIZE_FORMAT "\r\n" | |
346 "\r\n", | |
347 session->http_server, | |
348 purple_url_encode( packet->header ), | |
349 host_name, | |
350 packet->datalen - MXIT_MS_OFFSET | |
351 ); | |
352 | |
353 /* copy over the packet body data (could be binary) */ | |
354 memcpy( request + reqlen, packet->data + MXIT_MS_OFFSET, packet->datalen - MXIT_MS_OFFSET ); | |
355 reqlen += packet->datalen; | |
356 | |
357 #ifdef DEBUG_PROTOCOL | |
358 purple_debug_info( MXIT_PLUGIN_ID, "HTTP POST:\n" ); | |
359 dump_bytes( session, request, reqlen ); | |
360 #endif | |
361 | |
362 /* send the request to the HTTP server */ | |
363 mxit_http_send_request( session, host_name, host_port, request, reqlen ); | |
364 } | |
365 | |
366 | |
367 /*------------------------------------------------------------------------ | |
368 * TX Step 2: Handle the transmission of the packet to the MXit server. | |
369 * | |
370 * @param session The MXit session object | |
371 * @param packet The packet to transmit | |
372 */ | |
373 static void mxit_send_packet( struct MXitSession* session, struct tx_packet* packet ) | |
374 { | |
375 int res; | |
376 | |
377 if ( !( session->flags & MXIT_FLAG_CONNECTED ) ) { | |
378 /* we are not connected so ignore all packets to be send */ | |
379 purple_debug_error( MXIT_PLUGIN_ID, "Dropping TX packet (we are not connected)\n" ); | |
380 return; | |
381 } | |
382 | |
383 purple_debug_info( MXIT_PLUGIN_ID, "Packet send CMD:%i (%i)\n", packet->cmd, packet->headerlen + packet->datalen ); | |
384 #ifdef DEBUG_PROTOCOL | |
385 dump_bytes( session, packet->header, packet->headerlen ); | |
386 dump_bytes( session, packet->data, packet->datalen ); | |
387 #endif | |
388 | |
389 if ( !session->http ) { | |
390 /* socket connection */ | |
391 char data[packet->datalen + packet->headerlen]; | |
392 int datalen; | |
393 | |
394 /* create raw data buffer */ | |
395 memcpy( data, packet->header, packet->headerlen ); | |
396 memcpy( data + packet->headerlen, packet->data, packet->datalen ); | |
397 datalen = packet->headerlen + packet->datalen; | |
398 | |
399 res = mxit_write_sock_packet( session->fd, data, datalen ); | |
400 if ( res < 0 ) { | |
401 /* we must have lost the connection, so terminate it so that we can reconnect */ | |
402 purple_connection_error( session->con, _( "We have lost the connection to MXit. Please reconnect." ) ); | |
403 } | |
404 } | |
405 else { | |
406 /* http connection */ | |
407 | |
408 if ( packet->cmd == CP_CMD_MEDIA ) { | |
409 /* multimedia packets must be send with a HTTP POST */ | |
410 mxit_write_http_post( session, packet ); | |
411 } | |
412 else { | |
413 mxit_write_http_get( session, packet ); | |
414 } | |
415 } | |
416 | |
417 /* update the timestamp of the last-transmitted packet */ | |
418 session->last_tx = time( NULL ); | |
419 | |
420 /* | |
421 * we need to remember that we are still waiting for the ACK from | |
422 * the server on this request | |
423 */ | |
424 session->outack = packet->cmd; | |
425 | |
426 /* free up the packet resources */ | |
427 free_tx_packet( packet ); | |
428 } | |
429 | |
430 | |
431 /*------------------------------------------------------------------------ | |
432 * TX Step 1: Create a new Tx packet and queue it for sending. | |
433 * | |
434 * @param session The MXit session object | |
435 * @param data The packet data (payload) | |
436 * @param datalen The length of the packet data | |
437 * @param cmd The MXit command for this packet | |
438 */ | |
439 static void mxit_queue_packet( struct MXitSession* session, const char* data, int datalen, int cmd ) | |
440 { | |
441 struct tx_packet* packet; | |
442 char header[256]; | |
443 int hlen; | |
444 | |
445 /* create a packet for sending */ | |
446 packet = g_new0( struct tx_packet, 1 ); | |
447 packet->data = g_malloc0( datalen ); | |
448 packet->cmd = cmd; | |
449 packet->headerlen = 0; | |
450 | |
451 /* create generic packet header */ | |
452 hlen = sprintf( header, "id=%s%c", session->acc->username, CP_REC_TERM ); /* client msisdn */ | |
453 | |
454 if ( session->http ) { | |
455 /* http connection only */ | |
456 hlen += sprintf( header + hlen, "s=" ); | |
457 if ( session->http_sesid > 0 ) { | |
458 hlen += sprintf( header + hlen, "%u%c", session->http_sesid, CP_FLD_TERM ); /* http session id */ | |
459 } | |
460 session->http_seqno++; | |
461 hlen += sprintf( header + hlen, "%u%c", session->http_seqno, CP_REC_TERM ); /* http request sequence id */ | |
462 } | |
463 | |
464 hlen += sprintf( header + hlen, "cm=%i%c", cmd, CP_REC_TERM ); /* packet command */ | |
465 | |
466 if ( !session->http ) { | |
467 /* socket connection only */ | |
468 packet->headerlen += sprintf( packet->header, "ln=%i%c", ( datalen + hlen ), CP_REC_TERM ); /* packet length */ | |
469 } | |
470 | |
471 /* copy the header to packet */ | |
472 memcpy( packet->header + packet->headerlen, header, hlen ); | |
473 packet->headerlen += hlen; | |
474 | |
475 /* copy payload to packet */ | |
476 if ( datalen > 0 ) | |
477 memcpy( packet->data, data, datalen ); | |
478 packet->datalen = datalen; | |
479 | |
480 | |
481 /* | |
482 * shortcut: first check if there are any commands still outstanding. | |
483 * if not, then we might as well just write this packet directly and | |
484 * skip the whole queueing thing | |
485 */ | |
486 if ( session->outack == 0 ) { | |
487 /* no outstanding ACKs, so we might as well write it directly */ | |
488 mxit_send_packet( session, packet ); | |
489 } | |
490 else { | |
491 /* ACK still outstanding, so we need to queue this request until we have the ACK */ | |
492 | |
493 if ( ( packet->cmd == CP_CMD_PING ) || ( packet->cmd == CP_CMD_POLL ) ) { | |
494 /* we do NOT queue HTTP poll nor socket ping packets */ | |
495 free_tx_packet( packet ); | |
496 return; | |
497 } | |
498 | |
499 purple_debug_info( MXIT_PLUGIN_ID, "queueing packet for later sending cmd=%i\n", cmd ); | |
500 if ( !push_tx_packet( session, packet ) ) { | |
501 /* packet could not be queued for transmission */ | |
502 mxit_popup( PURPLE_NOTIFY_MSG_ERROR, _( "Message Send Error" ), _( "Unable to process your request at this time" ) ); | |
503 free_tx_packet( packet ); | |
504 } | |
505 } | |
506 } | |
507 | |
508 | |
509 /*------------------------------------------------------------------------ | |
510 * Callback to manage the packet send queue (send next packet, timeout's, etc). | |
511 * | |
512 * @param session The MXit session object | |
513 */ | |
514 gboolean mxit_manage_queue( gpointer user_data ) | |
515 { | |
516 struct MXitSession* session = (struct MXitSession*) user_data; | |
517 struct tx_packet* packet = NULL; | |
518 | |
519 if ( !( session->flags & MXIT_FLAG_CONNECTED ) ) { | |
520 /* we are not connected, so ignore the queue */ | |
521 return TRUE; | |
522 } | |
523 else if ( session->outack > 0 ) { | |
524 /* we are still waiting for an outstanding ACK from the MXit server */ | |
525 if ( session->last_tx <= time( NULL ) - MXIT_ACK_TIMEOUT ) { | |
526 /* ack timeout! so we close the connection here */ | |
527 purple_debug_info( MXIT_PLUGIN_ID, "mxit_manage_queue: Timeout awaiting ACK for command '%X'\n", session->outack ); | |
528 purple_connection_error( session->con, _( "Timeout while waiting for a response from the MXit server." ) ); | |
529 } | |
530 return TRUE; | |
531 } | |
532 | |
533 packet = pop_tx_packet( session ); | |
534 if ( packet != NULL ) { | |
535 /* there was a packet waiting to be sent to the server, now is the time to do something about it */ | |
536 | |
537 /* send the packet to MXit server */ | |
538 mxit_send_packet( session, packet ); | |
539 } | |
540 | |
541 return TRUE; | |
542 } | |
543 | |
544 | |
545 /*------------------------------------------------------------------------ | |
546 * Callback to manage HTTP server polling (HTTP connections ONLY) | |
547 * | |
548 * @param session The MXit session object | |
549 */ | |
550 gboolean mxit_manage_polling( gpointer user_data ) | |
551 { | |
552 struct MXitSession* session = (struct MXitSession*) user_data; | |
553 gboolean poll = FALSE; | |
554 time_t now = time( NULL ); | |
555 int polldiff; | |
556 int rxdiff; | |
557 | |
558 if ( !( session->flags & MXIT_FLAG_LOGGEDIN ) ) { | |
559 /* we only poll if we are actually logged in */ | |
560 return TRUE; | |
561 } | |
562 | |
563 /* calculate the time differences */ | |
564 rxdiff = now - session->last_rx; | |
565 polldiff = now - session->http_last_poll; | |
566 | |
567 if ( rxdiff < MXIT_HTTP_POLL_MIN ) { | |
568 /* we received some reply a few moments ago, so reset the poll interval */ | |
569 session->http_interval = MXIT_HTTP_POLL_MIN; | |
570 } | |
571 else if ( session->http_last_poll < ( now - session->http_interval ) ) { | |
572 /* time to poll again */ | |
573 poll = TRUE; | |
574 | |
575 /* back-off some more with the polling */ | |
576 session->http_interval = session->http_interval + ( session->http_interval / 2 ); | |
577 if ( session->http_interval > MXIT_HTTP_POLL_MAX ) | |
578 session->http_interval = MXIT_HTTP_POLL_MAX; | |
579 } | |
580 | |
581 /* debugging */ | |
582 //purple_debug_info( MXIT_PLUGIN_ID, "POLL TIMER: %i (%i,%i)\n", session->http_interval, rxdiff, polldiff ); | |
583 | |
584 if ( poll ) { | |
585 /* send poll request */ | |
586 session->http_last_poll = time( NULL ); | |
587 mxit_send_poll( session ); | |
588 } | |
589 | |
590 return TRUE; | |
591 } | |
592 | |
593 | |
594 /*======================================================================================================================== | |
595 * Send MXit operations. | |
596 */ | |
597 | |
598 /*------------------------------------------------------------------------ | |
599 * Send a ping/keepalive packet to MXit server. | |
600 * | |
601 * @param session The MXit session object | |
602 */ | |
603 void mxit_send_ping( struct MXitSession* session ) | |
604 { | |
605 /* queue packet for transmission */ | |
606 mxit_queue_packet( session, NULL, 0, CP_CMD_PING ); | |
607 } | |
608 | |
609 | |
610 /*------------------------------------------------------------------------ | |
611 * Send a poll request to the HTTP server (HTTP connections ONLY). | |
612 * | |
613 * @param session The MXit session object | |
614 */ | |
615 void mxit_send_poll( struct MXitSession* session ) | |
616 { | |
617 /* queue packet for transmission */ | |
618 mxit_queue_packet( session, NULL, 0, CP_CMD_POLL ); | |
619 } | |
620 | |
621 | |
622 /*------------------------------------------------------------------------ | |
623 * Send a logout packet to the MXit server. | |
624 * | |
625 * @param session The MXit session object | |
626 */ | |
627 void mxit_send_logout( struct MXitSession* session ) | |
628 { | |
629 /* queue packet for transmission */ | |
630 mxit_queue_packet( session, NULL, 0, CP_CMD_LOGOUT ); | |
631 } | |
632 | |
633 | |
634 /*------------------------------------------------------------------------ | |
635 * Send a register packet to the MXit server. | |
636 * | |
637 * @param session The MXit session object | |
638 */ | |
639 void mxit_send_register( struct MXitSession* session ) | |
640 { | |
641 struct MXitProfile* profile = session->profile; | |
642 const char* locale; | |
643 char data[CP_MAX_PACKET]; | |
644 int datalen; | |
645 | |
646 locale = purple_account_get_string( session->acc, MXIT_CONFIG_LOCALE, MXIT_DEFAULT_LOCALE ); | |
647 | |
648 /* convert the packet to a byte stream */ | |
649 datalen = sprintf( data, "ms=%s%c%s%c%i%c%s%c" /* "ms"=password\1version\1maxreplyLen\1name\1 */ | |
650 "%s%c%i%c%s%c%s%c" /* dateOfBirth\1gender\1location\1capabilities\1 */ | |
651 "%s%c%i%c%s%c%s", /* dc\1features\1dialingcode\1locale */ | |
652 session->encpwd, CP_FLD_TERM, MXIT_CP_VERSION, CP_FLD_TERM, CP_MAX_FILESIZE, CP_FLD_TERM, profile->nickname, CP_FLD_TERM, | |
653 profile->birthday, CP_FLD_TERM, ( profile->male ) ? 1 : 0, CP_FLD_TERM, MXIT_DEFAULT_LOC, CP_FLD_TERM, MXIT_CP_CAP, CP_FLD_TERM, | |
654 session->distcode, CP_FLD_TERM, MXIT_CP_FEATURES, CP_FLD_TERM, session->dialcode, CP_FLD_TERM, locale | |
655 ); | |
656 | |
657 /* queue packet for transmission */ | |
658 mxit_queue_packet( session, data, datalen, CP_CMD_REGISTER ); | |
659 } | |
660 | |
661 | |
662 /*------------------------------------------------------------------------ | |
663 * Send a login packet to the MXit server. | |
664 * | |
665 * @param session The MXit session object | |
666 */ | |
667 void mxit_send_login( struct MXitSession* session ) | |
668 { | |
669 const char* splashId; | |
670 const char* locale; | |
671 char data[CP_MAX_PACKET]; | |
672 int datalen; | |
673 | |
674 locale = purple_account_get_string( session->acc, MXIT_CONFIG_LOCALE, MXIT_DEFAULT_LOCALE ); | |
675 | |
676 /* convert the packet to a byte stream */ | |
677 datalen = sprintf( data, "ms=%s%c%s%c%i%c" /* "ms"=password\1version\1getContacts\1 */ | |
678 "%s%c%s%c%i%c" /* capabilities\1dc\1features\1 */ | |
679 "%s%c%s", /* dialingcode\1locale */ | |
680 session->encpwd, CP_FLD_TERM, MXIT_CP_VERSION, CP_FLD_TERM, 1, CP_FLD_TERM, | |
681 MXIT_CP_CAP, CP_FLD_TERM, session->distcode, CP_FLD_TERM, MXIT_CP_FEATURES, CP_FLD_TERM, | |
682 session->dialcode, CP_FLD_TERM, locale | |
683 ); | |
684 | |
685 /* include "custom resource" information */ | |
686 splashId = splash_current( session ); | |
687 if ( splashId != NULL ) | |
688 datalen += sprintf( data + datalen, "%ccr=%s", CP_REC_TERM, splashId ); | |
689 | |
690 /* queue packet for transmission */ | |
691 mxit_queue_packet( session, data, datalen, CP_CMD_LOGIN ); | |
692 } | |
693 | |
694 | |
695 /*------------------------------------------------------------------------ | |
696 * Send a chat message packet to the MXit server. | |
697 * | |
698 * @param session The MXit session object | |
699 * @param to The username of the recipient | |
700 * @param msg The message text | |
701 */ | |
702 void mxit_send_message( struct MXitSession* session, const char* to, const char* msg, gboolean parse_markup ) | |
703 { | |
704 char data[CP_MAX_PACKET]; | |
705 char* markuped_msg; | |
706 int datalen; | |
707 int msgtype = CP_MSGTYPE_NORMAL; | |
708 | |
709 /* first we need to convert the markup from libPurple to MXit format */ | |
710 if ( parse_markup ) | |
711 markuped_msg = mxit_convert_markup_tx( msg, &msgtype ); | |
712 else | |
713 markuped_msg = g_strdup( msg ); | |
714 | |
715 /* convert the packet to a byte stream */ | |
716 datalen = sprintf( data, "ms=%s%c%s%c%i%c%i", /* "ms"=jid\1msg\1type\1flags */ | |
717 to, CP_FLD_TERM, markuped_msg, CP_FLD_TERM, msgtype, CP_FLD_TERM, CP_MSG_MARKUP | CP_MSG_EMOTICON | |
718 ); | |
719 | |
720 /* free the resources */ | |
721 g_free( markuped_msg ); | |
722 | |
723 /* queue packet for transmission */ | |
724 mxit_queue_packet( session, data, datalen, CP_CMD_TX_MSG ); | |
725 } | |
726 | |
727 | |
728 /*------------------------------------------------------------------------ | |
729 * Send a extended profile request packet to the MXit server. | |
730 * | |
731 * @param session The MXit session object | |
732 * @param username Username who's profile is being requested (NULL = our own) | |
733 * @param nr_attribs Number of attributes being requested | |
734 * @param attributes The names of the attributes | |
735 */ | |
736 void mxit_send_extprofile_request( struct MXitSession* session, const char* username, unsigned int nr_attrib, const char* attribute[] ) | |
737 { | |
738 char data[CP_MAX_PACKET]; | |
739 int datalen; | |
740 unsigned int i; | |
741 | |
742 datalen = sprintf( data, "ms=%s%c%i", /* "ms="mxitid\1nr_attributes */ | |
743 (username ? username : ""), CP_FLD_TERM, nr_attrib); | |
744 | |
745 /* add attributes */ | |
746 for ( i = 0; i < nr_attrib; i++ ) | |
747 datalen += sprintf( data + datalen, "%c%s", CP_FLD_TERM, attribute[i] ); | |
748 | |
749 /* queue packet for transmission */ | |
750 mxit_queue_packet( session, data, datalen, CP_CMD_EXTPROFILE_GET ); | |
751 } | |
752 | |
753 | |
754 /*------------------------------------------------------------------------ | |
755 * Send an update profile packet to the MXit server. | |
756 * | |
757 * @param session The MXit session object | |
758 * @param password The new password to be used for logging in (optional) | |
759 * @param nr_attrib The number of attributes | |
760 * @param attributes String containing the attributes and settings seperated by '0x01' | |
761 */ | |
762 void mxit_send_extprofile_update( struct MXitSession* session, const char* password, unsigned int nr_attrib, const char* attributes ) | |
763 { | |
764 char data[CP_MAX_PACKET]; | |
765 gchar** parts; | |
766 int datalen; | |
767 unsigned int i; | |
768 | |
769 parts = g_strsplit( attributes, "\01", ( MXIT_MAX_ATTRIBS * 3 ) ); | |
770 | |
771 /* convert the packet to a byte stream */ | |
772 datalen = sprintf( data, "ms=%s%c%i", /* "ms"=password\1nr_attibutes */ | |
773 ( password ) ? password : "", CP_FLD_TERM, nr_attrib | |
774 ); | |
775 | |
776 /* add attributes */ | |
777 for ( i = 1; i < nr_attrib * 3; i+=3 ) | |
778 datalen += sprintf( data + datalen, "%c%s%c%s%c%s", /* \1name\1type\1value */ | |
779 CP_FLD_TERM, parts[i], CP_FLD_TERM, parts[i + 1], CP_FLD_TERM, parts[i + 2] ); | |
780 | |
781 /* queue packet for transmission */ | |
782 mxit_queue_packet( session, data, datalen, CP_CMD_EXTPROFILE_SET ); | |
783 | |
784 /* freeup the memory */ | |
785 g_strfreev( parts ); | |
786 } | |
787 | |
788 | |
789 /*------------------------------------------------------------------------ | |
790 * Send a presence update packet to the MXit server. | |
791 * | |
792 * @param session The MXit session object | |
793 * @param presence The presence (as per MXit types) | |
794 * @param statusmsg The status message (can be NULL) | |
795 */ | |
796 void mxit_send_presence( struct MXitSession* session, int presence, const char* statusmsg ) | |
797 { | |
798 char data[CP_MAX_PACKET]; | |
799 int datalen; | |
800 | |
801 /* convert the packet to a byte stream */ | |
802 datalen = sprintf( data, "ms=%i%c", /* "ms"=show\1status */ | |
803 presence, CP_FLD_TERM | |
804 ); | |
805 | |
806 /* append status message (if one is set) */ | |
807 if ( statusmsg ) | |
808 datalen += sprintf( data + datalen, "%s", statusmsg ); | |
809 | |
810 /* queue packet for transmission */ | |
811 mxit_queue_packet( session, data, datalen, CP_CMD_STATUS ); | |
812 } | |
813 | |
814 | |
815 /*------------------------------------------------------------------------ | |
816 * Send a mood update packet to the MXit server. | |
817 * | |
818 * @param session The MXit session object | |
819 * @param mood The mood (as per MXit types) | |
820 */ | |
821 void mxit_send_mood( struct MXitSession* session, int mood ) | |
822 { | |
823 char data[CP_MAX_PACKET]; | |
824 int datalen; | |
825 | |
826 /* convert the packet to a byte stream */ | |
827 datalen = sprintf( data, "ms=%i", /* "ms"=mood */ | |
828 mood | |
829 ); | |
830 | |
831 /* queue packet for transmission */ | |
832 mxit_queue_packet( session, data, datalen, CP_CMD_MOOD ); | |
833 } | |
834 | |
835 | |
836 /*------------------------------------------------------------------------ | |
837 * Send an invite contact packet to the MXit server. | |
838 * | |
839 * @param session The MXit session object | |
840 * @param username The username of the contact being invited | |
841 * @param alias Our alias for the contact | |
842 * @param groupname Group in which contact should be stored. | |
843 */ | |
844 void mxit_send_invite( struct MXitSession* session, const char* username, const char* alias, const char* groupname ) | |
845 { | |
846 char data[CP_MAX_PACKET]; | |
847 int datalen; | |
848 | |
849 /* convert the packet to a byte stream */ | |
850 datalen = sprintf( data, "ms=%s%c%s%c%s%c%i%c%s", /* "ms"=group\1username\1alias\1type\1msg */ | |
851 groupname, CP_FLD_TERM, username, CP_FLD_TERM, alias, | |
852 CP_FLD_TERM, MXIT_TYPE_MXIT, CP_FLD_TERM, "" | |
853 ); | |
854 | |
855 /* queue packet for transmission */ | |
856 mxit_queue_packet( session, data, datalen, CP_CMD_INVITE ); | |
857 } | |
858 | |
859 | |
860 /*------------------------------------------------------------------------ | |
861 * Send a remove contact packet to the MXit server. | |
862 * | |
863 * @param session The MXit session object | |
864 * @param username The username of the contact being removed | |
865 */ | |
866 void mxit_send_remove( struct MXitSession* session, const char* username ) | |
867 { | |
868 char data[CP_MAX_PACKET]; | |
869 int datalen; | |
870 | |
871 /* convert the packet to a byte stream */ | |
872 datalen = sprintf( data, "ms=%s", /* "ms"=username */ | |
873 username | |
874 ); | |
875 | |
876 /* queue packet for transmission */ | |
877 mxit_queue_packet( session, data, datalen, CP_CMD_REMOVE ); | |
878 } | |
879 | |
880 | |
881 /*------------------------------------------------------------------------ | |
882 * Send an accept subscription (invite) packet to the MXit server. | |
883 * | |
884 * @param session The MXit session object | |
885 * @param username The username of the contact being accepted | |
886 * @param alias Our alias for the contact | |
887 */ | |
888 void mxit_send_allow_sub( struct MXitSession* session, const char* username, const char* alias ) | |
889 { | |
890 char data[CP_MAX_PACKET]; | |
891 int datalen; | |
892 | |
893 /* convert the packet to a byte stream */ | |
894 datalen = sprintf( data, "ms=%s%c%s%c%s", /* "ms"=username\1group\1alias */ | |
895 username, CP_FLD_TERM, "", CP_FLD_TERM, alias | |
896 ); | |
897 | |
898 /* queue packet for transmission */ | |
899 mxit_queue_packet( session, data, datalen, CP_CMD_ALLOW ); | |
900 } | |
901 | |
902 | |
903 /*------------------------------------------------------------------------ | |
904 * Send an deny subscription (invite) packet to the MXit server. | |
905 * | |
906 * @param session The MXit session object | |
907 * @param username The username of the contact being denied | |
908 */ | |
909 void mxit_send_deny_sub( struct MXitSession* session, const char* username ) | |
910 { | |
911 char data[CP_MAX_PACKET]; | |
912 int datalen; | |
913 | |
914 /* convert the packet to a byte stream */ | |
915 datalen = sprintf( data, "ms=%s", /* "ms"=username */ | |
916 username | |
917 ); | |
918 | |
919 /* queue packet for transmission */ | |
920 mxit_queue_packet( session, data, datalen, CP_CMD_DENY ); | |
921 } | |
922 | |
923 | |
924 /*------------------------------------------------------------------------ | |
925 * Send an update contact packet to the MXit server. | |
926 * | |
927 * @param session The MXit session object | |
928 * @param username The username of the contact being denied | |
929 * @param alias Our alias for the contact | |
930 * @param groupname Group in which contact should be stored. | |
931 */ | |
932 void mxit_send_update_contact( struct MXitSession* session, const char* username, const char* alias, const char* groupname ) | |
933 { | |
934 char data[CP_MAX_PACKET]; | |
935 int datalen; | |
936 | |
937 /* convert the packet to a byte stream */ | |
938 datalen = sprintf( data, "ms=%s%c%s%c%s", /* "ms"=groupname\1username\1alias */ | |
939 groupname, CP_FLD_TERM, username, CP_FLD_TERM, alias | |
940 ); | |
941 | |
942 /* queue packet for transmission */ | |
943 mxit_queue_packet( session, data, datalen, CP_CMD_UPDATE ); | |
944 } | |
945 | |
946 | |
947 /*------------------------------------------------------------------------ | |
948 * Send a splash-screen click event packet. | |
949 * | |
950 * @param session The MXit session object | |
951 * @param splashid The identifier of the splash-screen | |
952 */ | |
953 void mxit_send_splashclick( struct MXitSession* session, const char* splashid ) | |
954 { | |
955 char data[CP_MAX_PACKET]; | |
956 int datalen; | |
957 | |
958 /* convert the packet to a byte stream */ | |
959 datalen = sprintf( data, "ms=%s", /* "ms"=splashId */ | |
960 splashid | |
961 ); | |
962 | |
963 /* queue packet for transmission */ | |
964 mxit_queue_packet( session, data, datalen, CP_CMD_SPLASHCLICK ); | |
965 } | |
966 | |
967 | |
968 /*------------------------------------------------------------------------ | |
969 * Send packet to create a MultiMX room. | |
970 * | |
971 * @param session The MXit session object | |
972 * @param groupname Name of the room to create | |
973 * @param nr_usernames Number of users in initial invite | |
974 * @param usernames The usernames of the users in the initial invite | |
975 */ | |
976 void mxit_send_groupchat_create( struct MXitSession* session, const char* groupname, int nr_usernames, const char* usernames[] ) | |
977 { | |
978 char data[CP_MAX_PACKET]; | |
979 int datalen; | |
980 int i; | |
981 | |
982 /* convert the packet to a byte stream */ | |
983 datalen = sprintf( data, "ms=%s%c%i", /* "ms"=roomname\1nr_jids\1jid0\1..\1jidN */ | |
984 groupname, CP_FLD_TERM, nr_usernames | |
985 ); | |
986 | |
987 /* add usernames */ | |
988 for ( i = 0; i < nr_usernames; i++ ) | |
989 datalen += sprintf( data + datalen, "%c%s", CP_FLD_TERM, usernames[i] ); | |
990 | |
991 /* queue packet for transmission */ | |
992 mxit_queue_packet( session, data, datalen, CP_CMD_GRPCHAT_CREATE ); | |
993 } | |
994 | |
995 | |
996 /*------------------------------------------------------------------------ | |
997 * Send packet to invite users to existing MultiMX room. | |
998 * | |
999 * @param session The MXit session object | |
1000 * @param roomid The unique RoomID for the MultiMx room. | |
1001 * @param nr_usernames Number of users being invited | |
1002 * @param usernames The usernames of the users being invited | |
1003 */ | |
1004 | |
1005 void mxit_send_groupchat_invite( struct MXitSession* session, const char* roomid, int nr_usernames, const char* usernames[] ) | |
1006 { | |
1007 char data[CP_MAX_PACKET]; | |
1008 int datalen; | |
1009 int i; | |
1010 | |
1011 /* convert the packet to a byte stream */ | |
1012 datalen = sprintf( data, "ms=%s%c%i", /* "ms"=roomid\1nr_jids\1jid0\1..\1jidN */ | |
1013 roomid, CP_FLD_TERM, nr_usernames | |
1014 ); | |
1015 | |
1016 /* add usernames */ | |
1017 for ( i = 0; i < nr_usernames; i++ ) | |
1018 datalen += sprintf( data + datalen, "%c%s", CP_FLD_TERM, usernames[i] ); | |
1019 | |
1020 /* queue packet for transmission */ | |
1021 mxit_queue_packet( session, data, datalen, CP_CMD_GRPCHAT_INVITE ); | |
1022 } | |
1023 | |
1024 | |
1025 /*------------------------------------------------------------------------ | |
1026 * Send a "send file direct" multimedia packet. | |
1027 * | |
1028 * @param session The MXit session object | |
1029 * @param username The username of the recipient | |
1030 * @param filename The name of the file being sent | |
1031 * @param buf The content of the file | |
1032 * @param buflen The length of the file contents | |
1033 */ | |
1034 void mxit_send_file( struct MXitSession* session, const char* username, const char* filename, const unsigned char* buf, int buflen ) | |
1035 { | |
1036 char data[CP_MAX_PACKET]; | |
1037 int datalen = 0; | |
1038 struct raw_chunk* chunk; | |
1039 int size; | |
1040 | |
1041 purple_debug_info( MXIT_PLUGIN_ID, "SENDING FILE '%s' of %i bytes to user '%s'\n", filename, buflen, username ); | |
1042 | |
1043 /* convert the packet to a byte stream */ | |
1044 datalen = sprintf( data, "ms=" ); | |
1045 | |
1046 /* map chunk header over data buffer */ | |
1047 chunk = (struct raw_chunk *) &data[datalen]; | |
1048 | |
1049 size = mxit_chunk_create_senddirect( chunk->data, username, filename, buf, buflen ); | |
1050 if ( size < 0 ) { | |
1051 purple_debug_error( MXIT_PLUGIN_ID, "Error creating senddirect chunk (%i)\n", size ); | |
1052 return; | |
1053 } | |
1054 | |
1055 chunk->type = CP_CHUNK_DIRECT_SND; | |
1056 chunk->length = htonl( size ); | |
1057 datalen += sizeof( struct raw_chunk ) + size; | |
1058 | |
1059 /* send the byte stream to the mxit server */ | |
1060 mxit_queue_packet( session, data, datalen, CP_CMD_MEDIA ); | |
1061 } | |
1062 | |
1063 | |
1064 /*------------------------------------------------------------------------ | |
1065 * Send a "reject file" multimedia packet. | |
1066 * | |
1067 * @param session The MXit session object | |
1068 * @param fileid A unique ID that identifies this file | |
1069 */ | |
1070 void mxit_send_file_reject( struct MXitSession* session, const char* fileid ) | |
1071 { | |
1072 char data[CP_MAX_PACKET]; | |
1073 int datalen = 0; | |
1074 struct raw_chunk* chunk; | |
1075 int size; | |
1076 | |
1077 purple_debug_info( MXIT_PLUGIN_ID, "mxit_send_file_reject\n" ); | |
1078 | |
1079 /* convert the packet to a byte stream */ | |
1080 datalen = sprintf( data, "ms=" ); | |
1081 | |
1082 /* map chunk header over data buffer */ | |
1083 chunk = (struct raw_chunk *) &data[datalen]; | |
1084 | |
1085 size = mxit_chunk_create_reject( chunk->data, fileid ); | |
1086 if ( size < 0 ) { | |
1087 purple_debug_error( MXIT_PLUGIN_ID, "Error creating reject chunk (%i)\n", size ); | |
1088 return; | |
1089 } | |
1090 | |
1091 chunk->type = CP_CHUNK_REJECT; | |
1092 chunk->length = htonl( size ); | |
1093 datalen += sizeof( struct raw_chunk ) + size; | |
1094 | |
1095 /* send the byte stream to the mxit server */ | |
1096 mxit_queue_packet( session, data, datalen, CP_CMD_MEDIA ); | |
1097 } | |
1098 | |
1099 | |
1100 /*------------------------------------------------------------------------ | |
1101 * Send a "get file" multimedia packet. | |
1102 * | |
1103 * @param session The MXit session object | |
1104 * @param fileid A unique ID that identifies this file | |
1105 * @param filesize The number of bytes to retrieve | |
1106 * @param offset Offset in file at which to start retrieving | |
1107 */ | |
1108 void mxit_send_file_accept( struct MXitSession* session, const char* fileid, int filesize, int offset ) | |
1109 { | |
1110 char data[CP_MAX_PACKET]; | |
1111 int datalen = 0; | |
1112 struct raw_chunk* chunk; | |
1113 int size; | |
1114 | |
1115 purple_debug_info( MXIT_PLUGIN_ID, "mxit_send_file_accept\n" ); | |
1116 | |
1117 /* convert the packet to a byte stream */ | |
1118 datalen = sprintf( data, "ms=" ); | |
1119 | |
1120 /* map chunk header over data buffer */ | |
1121 chunk = (struct raw_chunk *) &data[datalen]; | |
1122 | |
1123 size = mxit_chunk_create_get( chunk->data, fileid, filesize, offset ); | |
1124 if ( size < 0 ) { | |
1125 purple_debug_error( MXIT_PLUGIN_ID, "Error creating getfile chunk (%i)\n", size ); | |
1126 return; | |
1127 } | |
1128 | |
1129 chunk->type = CP_CHUNK_GET; | |
1130 chunk->length = htonl( size ); | |
1131 datalen += sizeof( struct raw_chunk ) + size; | |
1132 | |
1133 /* send the byte stream to the mxit server */ | |
1134 mxit_queue_packet( session, data, datalen, CP_CMD_MEDIA ); | |
1135 } | |
1136 | |
1137 | |
1138 /*------------------------------------------------------------------------ | |
1139 * Send a "received file" multimedia packet. | |
1140 * | |
1141 * @param session The MXit session object | |
1142 * @param status The status of the file-transfer | |
1143 */ | |
1144 void mxit_send_file_received( struct MXitSession* session, const char* fileid, short status ) | |
1145 { | |
1146 char data[CP_MAX_PACKET]; | |
1147 int datalen = 0; | |
1148 struct raw_chunk* chunk; | |
1149 int size; | |
1150 | |
1151 purple_debug_info( MXIT_PLUGIN_ID, "mxit_send_file_received\n" ); | |
1152 | |
1153 /* convert the packet to a byte stream */ | |
1154 datalen = sprintf( data, "ms=" ); | |
1155 | |
1156 /* map chunk header over data buffer */ | |
1157 chunk = (struct raw_chunk *) &data[datalen]; | |
1158 | |
1159 size = mxit_chunk_create_received( chunk->data, fileid, status ); | |
1160 if ( size < 0 ) { | |
1161 purple_debug_error( MXIT_PLUGIN_ID, "Error creating received chunk (%i)\n", size ); | |
1162 return; | |
1163 } | |
1164 | |
1165 chunk->type = CP_CHUNK_RECIEVED; | |
1166 chunk->length = htonl( size ); | |
1167 datalen += sizeof( struct raw_chunk ) + size; | |
1168 | |
1169 /* send the byte stream to the mxit server */ | |
1170 mxit_queue_packet( session, data, datalen, CP_CMD_MEDIA ); | |
1171 } | |
1172 | |
1173 | |
1174 /*------------------------------------------------------------------------ | |
1175 * Send a "set avatar" multimedia packet. | |
1176 * | |
1177 * @param session The MXit session object | |
1178 * @param data The avatar data | |
1179 * @param buflen The length of the avatar data | |
1180 */ | |
1181 void mxit_set_avatar( struct MXitSession* session, const unsigned char* avatar, int avatarlen ) | |
1182 { | |
1183 char data[CP_MAX_PACKET]; | |
1184 int datalen = 0; | |
1185 struct raw_chunk* chunk; | |
1186 int size; | |
1187 | |
1188 purple_debug_info( MXIT_PLUGIN_ID, "mxit_set_avatar: %i bytes\n", avatarlen ); | |
1189 | |
1190 /* convert the packet to a byte stream */ | |
1191 datalen = sprintf( data, "ms=" ); | |
1192 | |
1193 /* map chunk header over data buffer */ | |
1194 chunk = (struct raw_chunk *) &data[datalen]; | |
1195 | |
1196 size = mxit_chunk_create_set_avatar( chunk->data, avatar, avatarlen ); | |
1197 if ( size < 0 ) { | |
1198 purple_debug_error( MXIT_PLUGIN_ID, "Error creating set avatar chunk (%i)\n", size ); | |
1199 return; | |
1200 } | |
1201 | |
1202 chunk->type = CP_CHUNK_SET_AVATAR; | |
1203 chunk->length = htonl( size ); | |
1204 datalen += sizeof( struct raw_chunk ) + size; | |
1205 | |
1206 /* send the byte stream to the mxit server */ | |
1207 mxit_queue_packet( session, data, datalen, CP_CMD_MEDIA ); | |
1208 } | |
1209 | |
1210 | |
1211 /*------------------------------------------------------------------------ | |
1212 * Send a "get avatar" multimedia packet. | |
1213 * | |
1214 * @param session The MXit session object | |
1215 * @param mxitId The username who's avatar to request | |
1216 * @param avatarId The id of the avatar image (as string) | |
1217 * @param data The avatar data | |
1218 * @param buflen The length of the avatar data | |
1219 */ | |
1220 void mxit_get_avatar( struct MXitSession* session, const char* mxitId, const char* avatarId ) | |
1221 { | |
1222 char data[CP_MAX_PACKET]; | |
1223 int datalen = 0; | |
1224 struct raw_chunk* chunk; | |
1225 int size; | |
1226 | |
1227 purple_debug_info( MXIT_PLUGIN_ID, "mxit_get_avatar: %s\n", mxitId ); | |
1228 | |
1229 /* convert the packet to a byte stream */ | |
1230 datalen = sprintf( data, "ms=" ); | |
1231 | |
1232 /* map chunk header over data buffer */ | |
1233 chunk = (struct raw_chunk *) &data[datalen]; | |
1234 | |
1235 size = mxit_chunk_create_get_avatar( chunk->data, mxitId, avatarId, MXIT_AVATAR_SIZE ); | |
1236 if ( size < 0 ) { | |
1237 purple_debug_error( MXIT_PLUGIN_ID, "Error creating get avatar chunk (%i)\n", size ); | |
1238 return; | |
1239 } | |
1240 | |
1241 chunk->type = CP_CHUNK_GET_AVATAR; | |
1242 chunk->length = htonl( size ); | |
1243 datalen += sizeof( struct raw_chunk ) + size; | |
1244 | |
1245 /* send the byte stream to the mxit server */ | |
1246 mxit_queue_packet( session, data, datalen, CP_CMD_MEDIA ); | |
1247 } | |
1248 | |
1249 | |
1250 /*------------------------------------------------------------------------ | |
1251 * Process a login message packet. | |
1252 * | |
1253 * @param session The MXit session object | |
1254 * @param records The packet's data records | |
1255 * @param rcount The number of data records | |
1256 */ | |
1257 static void mxit_parse_cmd_login( struct MXitSession* session, struct record** records, int rcount ) | |
1258 { | |
1259 PurpleStatus* status; | |
1260 int presence; | |
1261 const char* profilelist[] = { CP_PROFILE_BIRTHDATE, CP_PROFILE_GENDER, CP_PROFILE_HIDENUMBER, CP_PROFILE_FULLNAME, | |
1262 CP_PROFILE_TITLE, CP_PROFILE_FIRSTNAME, CP_PROFILE_LASTNAME, CP_PROFILE_EMAIL, | |
1263 CP_PROFILE_MOBILENR }; | |
1264 | |
1265 purple_account_set_int( session->acc, MXIT_CONFIG_STATE, MXIT_STATE_LOGIN ); | |
1266 | |
1267 /* we were not yet logged in so we need to complete the login sequence here */ | |
1268 session->flags |= MXIT_FLAG_LOGGEDIN; | |
1269 purple_connection_update_progress( session->con, _( "Successfully Logged In..." ), 3, 4 ); | |
1270 purple_connection_set_state( session->con, PURPLE_CONNECTED ); | |
1271 | |
1272 /* display the current splash-screen */ | |
1273 if ( splash_popup_enabled( session ) ) | |
1274 splash_display( session ); | |
1275 | |
1276 /* update presence status */ | |
1277 status = purple_account_get_active_status( session->acc ); | |
1278 presence = mxit_convert_presence( purple_status_get_id( status ) ); | |
1279 if ( presence != MXIT_PRESENCE_ONLINE ) { | |
1280 /* when logging into MXit, your default presence is online. but with the UI, one can change | |
1281 * the presence to whatever. in the case where its changed to a different presence setting | |
1282 * we need to send an update to the server, otherwise the user's presence will be out of | |
1283 * sync between the UI and MXit. | |
1284 */ | |
1285 mxit_send_presence( session, presence, purple_status_get_attr_string( status, "message" ) ); | |
1286 } | |
1287 | |
1288 /* save extra info if this is a HTTP connection */ | |
1289 if ( session->http ) { | |
1290 /* save the http server to use for this session */ | |
1291 g_strlcpy( session->http_server, records[1]->fields[3]->data, sizeof( session->http_server ) ); | |
1292 | |
1293 /* save the session id */ | |
1294 session->http_sesid = atoi( records[0]->fields[0]->data ); | |
1295 } | |
1296 | |
1297 /* retrieve our MXit profile */ | |
1298 mxit_send_extprofile_request( session, NULL, ARRAY_SIZE( profilelist ), profilelist ); | |
1299 } | |
1300 | |
1301 | |
1302 /*------------------------------------------------------------------------ | |
1303 * Process a received message packet. | |
1304 * | |
1305 * @param session The MXit session object | |
1306 * @param records The packet's data records | |
1307 * @param rcount The number of data records | |
1308 */ | |
1309 static void mxit_parse_cmd_message( struct MXitSession* session, struct record** records, int rcount ) | |
1310 { | |
1311 struct RXMsgData* mx = NULL; | |
1312 char* message = NULL; | |
1313 int msglen = 0; | |
1314 int msgflags = 0; | |
1315 int msgtype = 0; | |
1316 | |
1317 if ( ( rcount == 1 ) || ( records[0]->fcount < 2 ) || ( records[1]->fcount == 0 ) || ( records[1]->fields[0]->len == 0 ) ) { | |
1318 /* packet contains no message or an empty message */ | |
1319 return; | |
1320 } | |
1321 | |
1322 message = records[1]->fields[0]->data; | |
1323 msglen = strlen( message ); | |
1324 | |
1325 /* strip off dummy domain */ | |
1326 mxit_strip_domain( records[0]->fields[0]->data ); | |
1327 | |
1328 #ifdef DEBUG_PROTOCOL | |
1329 purple_debug_info( MXIT_PLUGIN_ID, "Message received from '%s'\n", records[0]->fields[0]->data ); | |
1330 #endif | |
1331 | |
1332 /* decode message flags (if any) */ | |
1333 if ( records[0]->fcount >= 5 ) | |
1334 msgflags = atoi( records[0]->fields[4]->data ); | |
1335 msgtype = atoi( records[0]->fields[2]->data ); | |
1336 | |
1337 if ( msgflags & CP_MSG_ENCRYPTED ) { | |
1338 /* this is an encrypted message. we do not currently support those so ignore it */ | |
1339 PurpleBuddy* buddy; | |
1340 const char* name; | |
1341 char msg[128]; | |
1342 | |
1343 buddy = purple_find_buddy( session->acc, records[0]->fields[0]->data ); | |
1344 if ( buddy ) | |
1345 name = purple_buddy_get_alias( buddy ); | |
1346 else | |
1347 name = records[0]->fields[0]->data; | |
1348 g_snprintf( msg, sizeof( msg ), "%s sent you an encrypted message, but it is not supported on this client.", name ); | |
1349 mxit_popup( PURPLE_NOTIFY_MSG_WARNING, _( "Message Error" ), _( msg ) ); | |
1350 return; | |
1351 } | |
1352 | |
1353 /* create and initialise new markup struct */ | |
1354 mx = g_new0( struct RXMsgData, 1 ); | |
1355 mx->msg = g_string_sized_new( msglen ); | |
1356 mx->session = session; | |
1357 mx->from = g_strdup( records[0]->fields[0]->data ); | |
1358 mx->timestamp = atoi( records[0]->fields[1]->data ); | |
1359 mx->got_img = FALSE; | |
1360 mx->chatid = -1; | |
1361 mx->img_count = 0; | |
1362 | |
1363 /* update list of active chats */ | |
1364 if ( !find_active_chat( session->active_chats, mx->from ) ) { | |
1365 session->active_chats = g_list_append( session->active_chats, g_strdup( mx->from ) ); | |
1366 } | |
1367 | |
1368 if ( is_multimx_contact( session, mx->from ) ) { | |
1369 /* this is a MultiMx chatroom message */ | |
1370 multimx_message_received( mx, message, msglen, msgtype, msgflags ); | |
1371 } | |
1372 else { | |
1373 mxit_parse_markup( mx, message, msglen, msgtype, msgflags ); | |
1374 } | |
1375 | |
1376 /* we are now done parsing the message */ | |
1377 mx->converted = TRUE; | |
1378 if ( mx->img_count == 0 ) { | |
1379 /* we have all the data we need for this message to be displayed now. */ | |
1380 mxit_show_message( mx ); | |
1381 } | |
1382 else { | |
1383 /* this means there are still images outstanding for this message and | |
1384 * still need to wait for them before we can display the message. | |
1385 * so the image received callback function will eventually display | |
1386 * the message. */ | |
1387 } | |
1388 } | |
1389 | |
1390 | |
1391 /*------------------------------------------------------------------------ | |
1392 * Process a received subscription request packet. | |
1393 * | |
1394 * @param session The MXit session object | |
1395 * @param records The packet's data records | |
1396 * @param rcount The number of data records | |
1397 */ | |
1398 static void mxit_parse_cmd_new_sub( struct MXitSession* session, struct record** records, int rcount ) | |
1399 { | |
1400 struct contact* contact; | |
1401 struct record* rec; | |
1402 int i; | |
1403 | |
1404 purple_debug_info( MXIT_PLUGIN_ID, "mxit_parse_cmd_new_sub (%i recs)\n", rcount ); | |
1405 | |
1406 for ( i = 0; i < rcount; i++ ) { | |
1407 rec = records[i]; | |
1408 | |
1409 if ( rec->fcount < 4 ) { | |
1410 purple_debug_error( MXIT_PLUGIN_ID, "BAD SUBSCRIPTION RECORD! %i fields\n", rec->fcount ); | |
1411 break; | |
1412 } | |
1413 | |
1414 /* build up a new contact info struct */ | |
1415 contact = g_new0( struct contact, 1 ); | |
1416 | |
1417 strcpy( contact->username, rec->fields[0]->data ); | |
1418 mxit_strip_domain( contact->username ); /* remove dummy domain */ | |
1419 strcpy( contact->alias, rec->fields[1]->data ); | |
1420 contact->type = atoi( rec->fields[2]->data ); | |
1421 | |
1422 if ( rec->fcount >= 5 ) { | |
1423 /* there is a personal invite message attached */ | |
1424 contact->msg = strdup( rec->fields[4]->data ); | |
1425 } | |
1426 else | |
1427 contact->msg = NULL; | |
1428 | |
1429 /* handle the subscription */ | |
1430 if ( contact-> type == MXIT_TYPE_MULTIMX ) { /* subscription to a MultiMX room */ | |
1431 char* creator = NULL; | |
1432 | |
1433 if ( rec->fcount >= 6 ) | |
1434 creator = rec->fields[5]->data; | |
1435 | |
1436 multimx_invite( session, contact, creator ); | |
1437 } | |
1438 else | |
1439 mxit_new_subscription( session, contact ); | |
1440 } | |
1441 } | |
1442 | |
1443 | |
1444 /*------------------------------------------------------------------------ | |
1445 * Process a received contact update packet. | |
1446 * | |
1447 * @param session The MXit session object | |
1448 * @param records The packet's data records | |
1449 * @param rcount The number of data records | |
1450 */ | |
1451 static void mxit_parse_cmd_contact( struct MXitSession* session, struct record** records, int rcount ) | |
1452 { | |
1453 struct contact* contact = NULL; | |
1454 struct record* rec; | |
1455 int i; | |
1456 | |
1457 purple_debug_info( MXIT_PLUGIN_ID, "mxit_parse_cmd_contact (%i recs)\n", rcount ); | |
1458 | |
1459 for ( i = 0; i < rcount; i++ ) { | |
1460 rec = records[i]; | |
1461 | |
1462 if ( rec->fcount < 6 ) { | |
1463 purple_debug_error( MXIT_PLUGIN_ID, "BAD CONTACT RECORD! %i fields\n", rec->fcount ); | |
1464 break; | |
1465 } | |
1466 | |
1467 /* build up a new contact info struct */ | |
1468 contact = g_new0( struct contact, 1 ); | |
1469 | |
1470 strcpy( contact->groupname, rec->fields[0]->data ); | |
1471 strcpy( contact->username, rec->fields[1]->data ); | |
1472 mxit_strip_domain( contact->username ); /* remove dummy domain */ | |
1473 strcpy( contact->alias, rec->fields[2]->data ); | |
1474 | |
1475 contact->presence = atoi( rec->fields[3]->data ); | |
1476 contact->type = atoi( rec->fields[4]->data ); | |
1477 contact->mood = atoi( rec->fields[5]->data ); | |
1478 | |
1479 if ( rec->fcount > 6 ) { | |
1480 /* added in protocol 5.9.0 - flags & subtype */ | |
1481 contact->flags = atoi( rec->fields[6]->data ); | |
1482 contact->subtype = rec->fields[7]->data[0]; | |
1483 } | |
1484 | |
1485 /* add the contact to the buddy list */ | |
1486 if ( contact-> type == MXIT_TYPE_MULTIMX ) /* contact is a MultiMX room */ | |
1487 multimx_created( session, contact ); | |
1488 else | |
1489 mxit_update_contact( session, contact ); | |
1490 } | |
1491 | |
1492 if ( !( session->flags & MXIT_FLAG_FIRSTROSTER ) ) { | |
1493 session->flags |= MXIT_FLAG_FIRSTROSTER; | |
1494 mxit_update_blist( session ); | |
1495 } | |
1496 } | |
1497 | |
1498 | |
1499 /*------------------------------------------------------------------------ | |
1500 * Process a received presence update packet. | |
1501 * | |
1502 * @param session The MXit session object | |
1503 * @param records The packet's data records | |
1504 * @param rcount The number of data records | |
1505 */ | |
1506 static void mxit_parse_cmd_presence( struct MXitSession* session, struct record** records, int rcount ) | |
1507 { | |
1508 struct record* rec; | |
1509 int i; | |
1510 | |
1511 purple_debug_info( MXIT_PLUGIN_ID, "mxit_parse_cmd_presence (%i recs)\n", rcount ); | |
1512 | |
1513 for ( i = 0; i < rcount; i++ ) { | |
1514 rec = records[i]; | |
1515 | |
1516 if ( rec->fcount < 6 ) { | |
1517 purple_debug_error( MXIT_PLUGIN_ID, "BAD PRESENCE RECORD! %i fields\n", rec->fcount ); | |
1518 break; | |
1519 } | |
1520 | |
1521 /* | |
1522 * The format of the record is: | |
1523 * contactAddressN\1presenceN\1\moodN\1customMoodN\1statusMsgN\1avatarIdN | |
1524 */ | |
1525 mxit_strip_domain( rec->fields[0]->data ); /* contactAddress */ | |
1526 | |
1527 mxit_update_buddy_presence( session, rec->fields[0]->data, atoi( rec->fields[1]->data ), atoi( rec->fields[2]->data ), | |
1528 rec->fields[3]->data, rec->fields[4]->data, rec->fields[5]->data ); | |
1529 } | |
1530 } | |
1531 | |
1532 | |
1533 /*------------------------------------------------------------------------ | |
1534 * Process a received extended profile packet. | |
1535 * | |
1536 * @param session The MXit session object | |
1537 * @param records The packet's data records | |
1538 * @param rcount The number of data records | |
1539 */ | |
1540 static void mxit_parse_cmd_extprofile( struct MXitSession* session, struct record** records, int rcount ) | |
1541 { | |
1542 const char* mxitId = records[0]->fields[0]->data; | |
1543 struct MXitProfile* profile = NULL; | |
1544 int count; | |
1545 int i; | |
1546 | |
1547 purple_debug_info( MXIT_PLUGIN_ID, "mxit_parse_cmd_extprofile: profile for '%s'\n", mxitId ); | |
1548 | |
1549 profile = g_new0( struct MXitProfile, 1 ); | |
1550 | |
1551 /* set the count for attributes */ | |
1552 count = atoi( records[0]->fields[1]->data ); | |
1553 | |
1554 for ( i = 0; i < count; i++ ) { | |
1555 char* fname; | |
1556 char* fvalue; | |
1557 char* fstatus; | |
1558 int f = ( i * 3 ) + 2; | |
1559 | |
1560 fname = records[0]->fields[f]->data; /* field name */ | |
1561 fvalue = records[0]->fields[f + 1]->data; /* field value */ | |
1562 fstatus = records[0]->fields[f + 2]->data; /* field status */ | |
1563 | |
1564 /* first check the status on the returned attribute */ | |
1565 if ( fstatus[0] != '0' ) { | |
1566 /* error: attribute requested was NOT found */ | |
1567 purple_debug_error( MXIT_PLUGIN_ID, "Bad profile status on attribute '%s' \n", fname ); | |
1568 continue; | |
1569 } | |
1570 | |
1571 if ( strcmp( CP_PROFILE_BIRTHDATE, fname ) == 0 ) { | |
1572 /* birthdate */ | |
1573 if ( records[0]->fields[f + 1]->len > 10 ) { | |
1574 fvalue[10] = '\0'; | |
1575 records[0]->fields[f + 1]->len = 10; | |
1576 } | |
1577 memcpy( profile->birthday, fvalue, records[0]->fields[f + 1]->len ); | |
1578 } | |
1579 else if ( strcmp( CP_PROFILE_GENDER, fname ) == 0 ) { | |
1580 /* gender */ | |
1581 profile->male = ( fvalue[0] == '1' ); | |
1582 } | |
1583 else if ( strcmp( CP_PROFILE_HIDENUMBER, fname ) == 0 ) { | |
1584 /* hide number */ | |
1585 profile->hidden = ( fvalue[0] == '1' ); | |
1586 } | |
1587 else if ( strcmp( CP_PROFILE_FULLNAME, fname ) == 0 ) { | |
1588 /* nickname */ | |
1589 g_strlcpy( profile->nickname, fvalue, sizeof( profile->nickname ) ); | |
1590 } | |
1591 else if ( strcmp( CP_PROFILE_AVATAR, fname ) == 0 ) { | |
1592 /* avatar id, we just ingore it cause we dont need it */ | |
1593 } | |
1594 else if ( strcmp( CP_PROFILE_TITLE, fname ) == 0 ) { | |
1595 /* title */ | |
1596 g_strlcpy( profile->title, fvalue, sizeof( profile->title ) ); | |
1597 } | |
1598 else if ( strcmp( CP_PROFILE_FIRSTNAME, fname ) == 0 ) { | |
1599 /* first name */ | |
1600 g_strlcpy( profile->firstname, fvalue, sizeof( profile->firstname ) ); | |
1601 } | |
1602 else if ( strcmp( CP_PROFILE_LASTNAME, fname ) == 0 ) { | |
1603 /* last name */ | |
1604 g_strlcpy( profile->lastname, fvalue, sizeof( profile->lastname ) ); | |
1605 } | |
1606 else if ( strcmp( CP_PROFILE_EMAIL, fname ) == 0 ) { | |
1607 /* email address */ | |
1608 g_strlcpy( profile->email, fvalue, sizeof( profile->email ) ); | |
1609 } | |
1610 else if ( strcmp( CP_PROFILE_MOBILENR, fname ) == 0 ) { | |
1611 /* mobile number */ | |
1612 g_strlcpy( profile->mobilenr, fvalue, sizeof( profile->mobilenr ) ); | |
1613 } | |
1614 else { | |
1615 /* invalid profile attribute */ | |
1616 purple_debug_error( MXIT_PLUGIN_ID, "Invalid profile attribute received '%s' \n", fname ); | |
1617 } | |
1618 } | |
1619 | |
1620 if ( records[0]->fields[0]->len == 0 ) { | |
1621 /* no MXit id provided, so this must be our own profile information */ | |
1622 if ( session->profile ) | |
1623 g_free( session->profile ); | |
1624 session->profile = profile; | |
1625 } | |
1626 else { | |
1627 /* display other user's profile */ | |
1628 mxit_show_profile( session, mxitId, profile ); | |
1629 | |
1630 /* cleanup */ | |
1631 g_free( profile ); | |
1632 } | |
1633 } | |
1634 | |
1635 | |
1636 /*------------------------------------------------------------------------ | |
1637 * Return the length of a multimedia chunk | |
1638 * | |
1639 * @return The actual chunk data length in bytes | |
1640 */ | |
1641 static int get_chunk_len( const char* chunkdata ) | |
1642 { | |
1643 int* sizeptr; | |
1644 | |
1645 sizeptr = (int*) &chunkdata[1]; /* we skip the first byte (type field) */ | |
1646 | |
1647 return ntohl( *sizeptr ); | |
1648 } | |
1649 | |
1650 | |
1651 /*------------------------------------------------------------------------ | |
1652 * Process a received multimedia packet. | |
1653 * | |
1654 * @param session The MXit session object | |
1655 * @param records The packet's data records | |
1656 * @param rcount The number of data records | |
1657 */ | |
1658 static void mxit_parse_cmd_media( struct MXitSession* session, struct record** records, int rcount ) | |
1659 { | |
1660 char type; | |
1661 int size; | |
1662 | |
1663 type = records[0]->fields[0]->data[0]; | |
1664 size = get_chunk_len( records[0]->fields[0]->data ); | |
1665 | |
1666 purple_debug_info( MXIT_PLUGIN_ID, "mxit_parse_cmd_media (%i records) (%i bytes)\n", rcount, size ); | |
1667 | |
1668 /* supported chunked data types */ | |
1669 switch ( type ) { | |
1670 case CP_CHUNK_CUSTOM : /* custom resource */ | |
1671 { | |
1672 struct cr_chunk chunk; | |
1673 | |
1674 /* decode the chunked data */ | |
1675 memset( &chunk, 0, sizeof( struct cr_chunk ) ); | |
1676 mxit_chunk_parse_cr( &records[0]->fields[0]->data[sizeof( char ) + sizeof( int )], records[0]->fields[0]->len, &chunk ); | |
1677 | |
1678 purple_debug_info( MXIT_PLUGIN_ID, "chunk info id=%s handle=%s op=%i\n", chunk.id, chunk.handle, chunk.operation ); | |
1679 | |
1680 /* this is a splash-screen operation */ | |
1681 if ( strcmp( chunk.handle, HANDLE_SPLASH2 ) == 0 ) { | |
1682 if ( chunk.operation == CR_OP_UPDATE ) { /* update the splash-screen */ | |
1683 struct splash_chunk *splash = chunk.resources->data; // TODO: Fix - assuming 1st resource is splash | |
1684 gboolean clickable = ( g_list_length( chunk.resources ) > 1 ); // TODO: Fix - if 2 resources, then is clickable | |
1685 | |
1686 if ( splash != NULL ) | |
1687 splash_update( session, chunk.id, splash->data, splash->datalen, clickable ); | |
1688 } | |
1689 else if ( chunk.operation == CR_OP_REMOVE ) /* remove the splash-screen */ | |
1690 splash_remove( session ); | |
1691 } | |
1692 | |
1693 /* cleanup custom resources */ | |
1694 g_list_foreach( chunk.resources, (GFunc)g_free, NULL ); | |
1695 | |
1696 } | |
1697 break; | |
1698 | |
1699 case CP_CHUNK_OFFER : /* file offer */ | |
1700 { | |
1701 struct offerfile_chunk chunk; | |
1702 | |
1703 /* decode the chunked data */ | |
1704 memset( &chunk, 0, sizeof( struct offerfile_chunk ) ); | |
1705 mxit_chunk_parse_offer( &records[0]->fields[0]->data[sizeof( char ) + sizeof( int )], records[0]->fields[0]->len, &chunk ); | |
1706 | |
1707 /* process the offer */ | |
1708 mxit_xfer_rx_offer( session, chunk.username, chunk.filename, chunk.filesize, chunk.fileid ); | |
1709 } | |
1710 break; | |
1711 | |
1712 case CP_CHUNK_GET : /* get file response */ | |
1713 { | |
1714 struct getfile_chunk chunk; | |
1715 | |
1716 /* decode the chunked data */ | |
1717 memset( &chunk, 0, sizeof( struct getfile_chunk ) ); | |
1718 mxit_chunk_parse_get( &records[0]->fields[0]->data[sizeof( char ) + sizeof( int )], records[0]->fields[0]->len, &chunk ); | |
1719 | |
1720 /* process the getfile */ | |
1721 mxit_xfer_rx_file( session, chunk.fileid, chunk.data, chunk.length ); | |
1722 } | |
1723 break; | |
1724 | |
1725 case CP_CHUNK_GET_AVATAR : /* get avatars */ | |
1726 { | |
1727 struct getavatar_chunk chunk; | |
1728 | |
1729 /* decode the chunked data */ | |
1730 memset( &chunk, 0, sizeof ( struct getavatar_chunk ) ); | |
1731 mxit_chunk_parse_get_avatar( &records[0]->fields[0]->data[sizeof( char ) + sizeof( int )], records[0]->fields[0]->len, &chunk ); | |
1732 | |
1733 /* update avatar image */ | |
1734 if ( chunk.data ) { | |
1735 purple_debug_info( MXIT_PLUGIN_ID, "updating avatar for contact '%s'\n", chunk.mxitid ); | |
1736 purple_buddy_icons_set_for_user( session->acc, chunk.mxitid, g_memdup( chunk.data, chunk.length), chunk.length, chunk.avatarid ); | |
1737 } | |
1738 | |
1739 } | |
1740 break; | |
1741 | |
1742 case CP_CHUNK_SET_AVATAR : | |
1743 /* this is a reply packet to a set avatar request. no action is required */ | |
1744 break; | |
1745 | |
1746 case CP_CHUNK_DIRECT_SND : | |
1747 /* this is a ack for a file send. no action is required */ | |
1748 break; | |
1749 | |
1750 case CP_CHUNK_RECIEVED : | |
1751 /* this is a ack for a file received. no action is required */ | |
1752 break; | |
1753 | |
1754 default : | |
1755 purple_debug_error( MXIT_PLUGIN_ID, "Unsupported chunked data packet type received (%i)\n", type ); | |
1756 break; | |
1757 } | |
1758 } | |
1759 | |
1760 | |
1761 /*------------------------------------------------------------------------ | |
1762 * Handle a redirect sent from the MXit server. | |
1763 * | |
1764 * @param session The MXit session object | |
1765 * @param url The redirect information | |
1766 */ | |
1767 static void mxit_perform_redirect( struct MXitSession* session, const char* url ) | |
1768 { | |
1769 gchar** parts; | |
1770 gchar** host; | |
1771 int type; | |
1772 | |
1773 purple_debug_info( MXIT_PLUGIN_ID, "mxit_perform_redirect: %s\n", url ); | |
1774 | |
1775 /* tokenize the URL string */ | |
1776 parts = g_strsplit( url, ";", 0 ); | |
1777 | |
1778 /* Part 1: protocol://host:port */ | |
1779 host = g_strsplit( parts[0], ":", 4 ); | |
1780 if ( strcmp( host[0], "socket" ) == 0 ) { | |
1781 /* redirect to a MXit socket proxy */ | |
1782 g_strlcpy( session->server, &host[1][2], sizeof( session->server ) ); | |
1783 session->port = atoi( host[2] ); | |
1784 } | |
1785 else { | |
1786 purple_connection_error( session->con, _( "Cannot perform redirect using the specified protocol" ) ); | |
1787 goto redirect_fail; | |
1788 } | |
1789 | |
1790 /* Part 2: type of redirect */ | |
1791 type = atoi( parts[1] ); | |
1792 if ( type == CP_REDIRECT_PERMANENT ) { | |
1793 /* permanent redirect, so save new MXit server and port */ | |
1794 purple_account_set_string( session->acc, MXIT_CONFIG_SERVER_ADDR, session->server ); | |
1795 purple_account_set_int( session->acc, MXIT_CONFIG_SERVER_PORT, session->port ); | |
1796 } | |
1797 | |
1798 /* Part 3: message (optional) */ | |
1799 if ( parts[2] != NULL ) | |
1800 purple_connection_notice( session->con, parts[2] ); | |
1801 | |
1802 purple_debug_info( MXIT_PLUGIN_ID, "mxit_perform_redirect: %s redirect to %s:%i\n", | |
1803 ( type == CP_REDIRECT_PERMANENT ) ? "Permanent" : "Temporary", session->server, session->port ); | |
1804 | |
1805 /* perform the re-connect to the new MXit server */ | |
1806 mxit_reconnect( session ); | |
1807 | |
1808 redirect_fail: | |
1809 g_strfreev( parts ); | |
1810 g_strfreev( host ); | |
1811 } | |
1812 | |
1813 | |
1814 /*------------------------------------------------------------------------ | |
1815 * Process a success response received from the MXit server. | |
1816 * | |
1817 * @param session The MXit session object | |
1818 * @param packet The received packet | |
1819 */ | |
1820 static int process_success_response( struct MXitSession* session, struct rx_packet* packet ) | |
1821 { | |
1822 /* ignore ping/poll packets */ | |
1823 if ( ( packet->cmd != CP_CMD_PING ) && ( packet->cmd != CP_CMD_POLL ) ) | |
1824 session->last_rx = time( NULL ); | |
1825 | |
1826 /* | |
1827 * when we pass the packet records to the next level for parsing | |
1828 * we minus 3 records because 1) the first record is the packet | |
1829 * type 2) packet reply status 3) the last record is bogus | |
1830 */ | |
1831 | |
1832 /* packet command */ | |
1833 switch ( packet->cmd ) { | |
1834 | |
1835 case CP_CMD_REGISTER : | |
1836 /* fall through, when registeration successful, MXit will auto login */ | |
1837 case CP_CMD_LOGIN : | |
1838 /* login response */ | |
1839 if ( !( session->flags & MXIT_FLAG_LOGGEDIN ) ) { | |
1840 mxit_parse_cmd_login( session, &packet->records[2], packet->rcount - 3 ); | |
1841 } | |
1842 break; | |
1843 | |
1844 case CP_CMD_LOGOUT : | |
1845 /* logout response */ | |
1846 session->flags &= ~MXIT_FLAG_LOGGEDIN; | |
1847 purple_account_disconnect( session->acc ); | |
1848 | |
1849 /* note: | |
1850 * we do not prompt the user here for a reconnect, because this could be the user | |
1851 * logging in with his phone. so we just disconnect the account otherwise | |
1852 * mxit will start to bounce between the phone and pidgin. also could be a valid | |
1853 * disconnect selected by the user. | |
1854 */ | |
1855 return -1; | |
1856 | |
1857 case CP_CMD_CONTACT : | |
1858 /* contact update */ | |
1859 mxit_parse_cmd_contact( session, &packet->records[2], packet->rcount - 3 ); | |
1860 break; | |
1861 | |
1862 case CP_CMD_PRESENCE : | |
1863 /* presence update */ | |
1864 mxit_parse_cmd_presence(session, &packet->records[2], packet->rcount - 3 ); | |
1865 break; | |
1866 | |
1867 case CP_CMD_RX_MSG : | |
1868 /* incoming message (no bogus record) */ | |
1869 mxit_parse_cmd_message( session, &packet->records[2], packet->rcount - 2 ); | |
1870 break; | |
1871 | |
1872 case CP_CMD_NEW_SUB : | |
1873 /* new subscription request */ | |
1874 mxit_parse_cmd_new_sub( session, &packet->records[2], packet->rcount - 3 ); | |
1875 break; | |
1876 | |
1877 case CP_CMD_MEDIA : | |
1878 /* multi-media message */ | |
1879 mxit_parse_cmd_media( session, &packet->records[2], packet->rcount - 2 ); | |
1880 break; | |
1881 | |
1882 case CP_CMD_EXTPROFILE_GET : | |
1883 /* profile update */ | |
1884 mxit_parse_cmd_extprofile( session, &packet->records[2], packet->rcount - 2 ); | |
1885 break; | |
1886 | |
1887 case CP_CMD_MOOD : | |
1888 /* mood update */ | |
1889 case CP_CMD_UPDATE : | |
1890 /* update contact information */ | |
1891 case CP_CMD_ALLOW : | |
1892 /* allow subscription ack */ | |
1893 case CP_CMD_DENY : | |
1894 /* deny subscription ack */ | |
1895 case CP_CMD_INVITE : | |
1896 /* invite contact ack */ | |
1897 case CP_CMD_REMOVE : | |
1898 /* remove contact ack */ | |
1899 case CP_CMD_TX_MSG : | |
1900 /* outgoing message ack */ | |
1901 case CP_CMD_STATUS : | |
1902 /* presence update ack */ | |
1903 case CP_CMD_GRPCHAT_CREATE : | |
1904 /* create groupchat */ | |
1905 case CP_CMD_GRPCHAT_INVITE : | |
1906 /* groupchat invite */ | |
1907 case CP_CMD_PING : | |
1908 /* ping reply */ | |
1909 case CP_CMD_POLL : | |
1910 /* HTTP poll reply */ | |
1911 case CP_CMD_EXTPROFILE_SET : | |
1912 /* profile update */ | |
1913 case CP_CMD_SPLASHCLICK : | |
1914 /* splash-screen clickthrough */ | |
1915 break; | |
1916 | |
1917 default : | |
1918 /* unknown packet */ | |
1919 purple_debug_error( MXIT_PLUGIN_ID, "Received unknown client packet (cmd = %i)\n", packet->cmd ); | |
1920 } | |
1921 | |
1922 return 0; | |
1923 } | |
1924 | |
1925 | |
1926 /*------------------------------------------------------------------------ | |
1927 * Process an error response received from the MXit server. | |
1928 * | |
1929 * @param session The MXit session object | |
1930 * @param packet The received packet | |
1931 */ | |
1932 static int process_error_response( struct MXitSession* session, struct rx_packet* packet ) | |
1933 { | |
1934 char errmsg[256]; | |
1935 const char* errdesc; | |
1936 | |
1937 /* set the error description to be shown to the user */ | |
1938 if ( packet->errmsg ) | |
1939 errdesc = packet->errmsg; | |
1940 else | |
1941 errdesc = "An internal MXit server error occurred."; | |
1942 | |
1943 purple_debug_info( MXIT_PLUGIN_ID, "Error Reply %i:%s\n", packet->errcode, errdesc ); | |
1944 | |
1945 if ( packet->errcode == MXIT_ERRCODE_LOGGEDOUT ) { | |
1946 /* we are not currently logged in, so we need to reconnect */ | |
1947 purple_connection_error( session->con, _( errmsg ) ); | |
1948 } | |
1949 | |
1950 /* packet command */ | |
1951 switch ( packet->cmd ) { | |
1952 | |
1953 case CP_CMD_REGISTER : | |
1954 case CP_CMD_LOGIN : | |
1955 if ( packet->errcode == MXIT_ERRCODE_REDIRECT ) { | |
1956 mxit_perform_redirect( session, packet->errmsg ); | |
1957 return 0; | |
1958 } | |
1959 else { | |
1960 sprintf( errmsg, "Login error: %s (%i)", errdesc, packet->errcode ); | |
1961 purple_connection_error( session->con, _( errmsg ) ); | |
1962 return -1; | |
1963 } | |
1964 case CP_CMD_LOGOUT : | |
1965 sprintf( errmsg, "Logout error: %s (%i)", errdesc, packet->errcode ); | |
1966 purple_connection_error_reason( session->con, PURPLE_CONNECTION_ERROR_NAME_IN_USE, _( errmsg ) ); | |
1967 return -1; | |
1968 case CP_CMD_CONTACT : | |
1969 mxit_popup( PURPLE_NOTIFY_MSG_WARNING, _( "Contact Error" ), _( errdesc ) ); | |
1970 break; | |
1971 case CP_CMD_RX_MSG : | |
1972 mxit_popup( PURPLE_NOTIFY_MSG_WARNING, _( "Message Error" ), _( errdesc ) ); | |
1973 break; | |
1974 case CP_CMD_TX_MSG : | |
1975 mxit_popup( PURPLE_NOTIFY_MSG_WARNING, _( "Message Sending Error" ), _( errdesc ) ); | |
1976 break; | |
1977 case CP_CMD_STATUS : | |
1978 mxit_popup( PURPLE_NOTIFY_MSG_WARNING, _( "Status Error" ), _( errdesc ) ); | |
1979 break; | |
1980 case CP_CMD_MOOD : | |
1981 mxit_popup( PURPLE_NOTIFY_MSG_WARNING, _( "Mood Error" ), _( errdesc ) ); | |
1982 break; | |
1983 case CP_CMD_KICK : | |
1984 /* | |
1985 * the MXit server sends this packet if we were idle for too long. | |
1986 * to stop the server from closing this connection we need to resend | |
1987 * the login packet. | |
1988 */ | |
1989 mxit_send_login( session ); | |
1990 break; | |
1991 case CP_CMD_INVITE : | |
1992 mxit_popup( PURPLE_NOTIFY_MSG_WARNING, _( "Invitation Error" ), _( errdesc ) ); | |
1993 break; | |
1994 case CP_CMD_REMOVE : | |
1995 mxit_popup( PURPLE_NOTIFY_MSG_WARNING, _( "Contact Removal Error" ), _( errdesc ) ); | |
1996 break; | |
1997 case CP_CMD_ALLOW : | |
1998 case CP_CMD_DENY : | |
1999 mxit_popup( PURPLE_NOTIFY_MSG_WARNING, _( "Subscription Error" ), _( errdesc ) ); | |
2000 break; | |
2001 case CP_CMD_UPDATE : | |
2002 mxit_popup( PURPLE_NOTIFY_MSG_WARNING, _( "Contact Update Error" ), _( errdesc ) ); | |
2003 break; | |
2004 case CP_CMD_MEDIA : | |
2005 mxit_popup( PURPLE_NOTIFY_MSG_WARNING, _( "File Transfer Error" ), _( errdesc ) ); | |
2006 break; | |
2007 case CP_CMD_GRPCHAT_CREATE : | |
2008 mxit_popup( PURPLE_NOTIFY_MSG_WARNING, _( "Cannot create MultiMx room" ), _( errdesc ) ); | |
2009 break; | |
2010 case CP_CMD_GRPCHAT_INVITE : | |
2011 mxit_popup( PURPLE_NOTIFY_MSG_WARNING, _( "MultiMx Invitation Error" ), _( errdesc ) ); | |
2012 break; | |
2013 case CP_CMD_EXTPROFILE_GET : | |
2014 case CP_CMD_EXTPROFILE_SET : | |
2015 mxit_popup( PURPLE_NOTIFY_MSG_WARNING, _( "Profile Error" ), _( errdesc ) ); | |
2016 break; | |
2017 case CP_CMD_SPLASHCLICK : | |
2018 /* ignore error */ | |
2019 break; | |
2020 case CP_CMD_PING : | |
2021 case CP_CMD_POLL : | |
2022 break; | |
2023 default : | |
2024 mxit_popup( PURPLE_NOTIFY_MSG_ERROR, _( "Error" ), _( errdesc ) ); | |
2025 break; | |
2026 } | |
2027 | |
2028 return 0; | |
2029 } | |
2030 | |
2031 | |
2032 /*======================================================================================================================== | |
2033 * Low-level Packet receive | |
2034 */ | |
2035 | |
2036 #ifdef DEBUG_PROTOCOL | |
2037 /*------------------------------------------------------------------------ | |
2038 * Dump a received packet structure. | |
2039 * | |
2040 * @param p The received packet | |
2041 */ | |
2042 static void dump_packet( struct rx_packet* p ) | |
2043 { | |
2044 struct record* r = NULL; | |
2045 struct field* f = NULL; | |
2046 int i; | |
2047 int j; | |
2048 | |
2049 purple_debug_info( MXIT_PLUGIN_ID, "PACKET DUMP: (%i records)\n", p->rcount ); | |
2050 | |
2051 for ( i = 0; i < p->rcount; i++ ) { | |
2052 r = p->records[i]; | |
2053 purple_debug_info( MXIT_PLUGIN_ID, "RECORD: (%i fields)\n", r->fcount ); | |
2054 | |
2055 for ( j = 0; j < r->fcount; j++ ) { | |
2056 f = r->fields[j]; | |
2057 purple_debug_info( MXIT_PLUGIN_ID, "\tFIELD: (len=%i) '%s' \n", f->len, f->data ); | |
2058 } | |
2059 } | |
2060 } | |
2061 #endif | |
2062 | |
2063 | |
2064 /*------------------------------------------------------------------------ | |
2065 * Free up memory used by a packet structure. | |
2066 * | |
2067 * @param p The received packet | |
2068 */ | |
2069 static void free_rx_packet( struct rx_packet* p ) | |
2070 { | |
2071 struct record* r = NULL; | |
2072 struct field* f = NULL; | |
2073 int i; | |
2074 int j; | |
2075 | |
2076 for ( i = 0; i < p->rcount; i++ ) { | |
2077 r = p->records[i]; | |
2078 | |
2079 for ( j = 0; j < r->fcount; j++ ) { | |
2080 g_free( f ); | |
2081 } | |
2082 g_free( r->fields ); | |
2083 g_free( r ); | |
2084 } | |
2085 g_free( p->records ); | |
2086 } | |
2087 | |
2088 | |
2089 /*------------------------------------------------------------------------ | |
2090 * Add a new field to a record. | |
2091 * | |
2092 * @param r Parent record object | |
2093 * @return The newly created field | |
2094 */ | |
2095 static struct field* add_field( struct record* r ) | |
2096 { | |
2097 struct field* field; | |
2098 | |
2099 field = g_new0( struct field, 1 ); | |
2100 | |
2101 r->fields = realloc( r->fields, sizeof( struct field* ) * ( r->fcount + 1 ) ); | |
2102 r->fields[r->fcount] = field; | |
2103 r->fcount++; | |
2104 | |
2105 return field; | |
2106 } | |
2107 | |
2108 | |
2109 /*------------------------------------------------------------------------ | |
2110 * Add a new record to a packet. | |
2111 * | |
2112 * @param p The packet object | |
2113 * @return The newly created record | |
2114 */ | |
2115 static struct record* add_record( struct rx_packet* p ) | |
2116 { | |
2117 struct record* rec; | |
2118 | |
2119 rec = g_new0( struct record, 1 ); | |
2120 | |
2121 p->records = realloc( p->records, sizeof( struct record* ) * ( p->rcount + 1 ) ); | |
2122 p->records[p->rcount] = rec; | |
2123 p->rcount++; | |
2124 | |
2125 return rec; | |
2126 } | |
2127 | |
2128 | |
2129 /*------------------------------------------------------------------------ | |
2130 * Parse the received byte stream into a proper client protocol packet. | |
2131 * | |
2132 * @param session The MXit session object | |
2133 * @return Success (0) or Failure (!0) | |
2134 */ | |
2135 int mxit_parse_packet( struct MXitSession* session ) | |
2136 { | |
2137 struct rx_packet packet; | |
2138 struct record* rec; | |
2139 struct field* field; | |
2140 gboolean pbreak; | |
2141 unsigned int i; | |
2142 int res = 0; | |
2143 | |
2144 #ifdef DEBUG_PROTOCOL | |
2145 purple_debug_info( MXIT_PLUGIN_ID, "Received packet (%i bytes)\n", session->rx_i ); | |
2146 dump_bytes( session, session->rx_dbuf, session->rx_i ); | |
2147 #endif | |
2148 | |
2149 i = 0; | |
2150 while ( i < session->rx_i ) { | |
2151 | |
2152 /* create first record and field */ | |
2153 rec = NULL; | |
2154 field = NULL; | |
2155 memset( &packet, 0x00, sizeof( struct rx_packet ) ); | |
2156 rec = add_record( &packet ); | |
2157 pbreak = FALSE; | |
2158 | |
2159 /* break up the received packet into fields and records for easy parsing */ | |
2160 while ( ( i < session->rx_i ) && ( !pbreak ) ) { | |
2161 | |
2162 switch ( session->rx_dbuf[i] ) { | |
2163 case CP_SOCK_REC_TERM : | |
2164 /* new record */ | |
2165 if ( packet.rcount == 1 ) { | |
2166 /* packet command */ | |
2167 packet.cmd = atoi( packet.records[0]->fields[0]->data ); | |
2168 } | |
2169 else if ( packet.rcount == 2 ) { | |
2170 /* special case: binary multimedia packets should not be parsed here */ | |
2171 if ( packet.cmd == CP_CMD_MEDIA ) { | |
2172 /* add the chunked to new record */ | |
2173 rec = add_record( &packet ); | |
2174 field = add_field( rec ); | |
2175 field->data = &session->rx_dbuf[i + 1]; | |
2176 field->len = session->rx_i - i; | |
2177 /* now skip the binary data */ | |
2178 res = get_chunk_len( field->data ); | |
2179 /* determine if we have more packets */ | |
2180 if ( res + 6 + i < session->rx_i ) { | |
2181 /* we have more than one packet in this stream */ | |
2182 i += res + 6; | |
2183 pbreak = TRUE; | |
2184 } | |
2185 else { | |
2186 i = session->rx_i; | |
2187 } | |
2188 } | |
2189 } | |
2190 else if ( !field ) { | |
2191 field = add_field( rec ); | |
2192 field->data = &session->rx_dbuf[i]; | |
2193 } | |
2194 session->rx_dbuf[i] = '\0'; | |
2195 rec = add_record( &packet ); | |
2196 field = NULL; | |
2197 | |
2198 break; | |
2199 case CP_FLD_TERM : | |
2200 /* new field */ | |
2201 session->rx_dbuf[i] = '\0'; | |
2202 if ( !field ) { | |
2203 field = add_field( rec ); | |
2204 field->data = &session->rx_dbuf[i]; | |
2205 } | |
2206 field = NULL; | |
2207 break; | |
2208 case CP_PKT_TERM : | |
2209 /* packet is done! */ | |
2210 session->rx_dbuf[i] = '\0'; | |
2211 pbreak = TRUE; | |
2212 break; | |
2213 default : | |
2214 /* skip non special characters */ | |
2215 if ( !field ) { | |
2216 field = add_field( rec ); | |
2217 field->data = &session->rx_dbuf[i]; | |
2218 } | |
2219 field->len++; | |
2220 break; | |
2221 } | |
2222 | |
2223 i++; | |
2224 } | |
2225 | |
2226 if ( packet.rcount < 2 ) { | |
2227 /* bad packet */ | |
2228 purple_connection_error( session->con, _( "Invalid packet received from MXit." ) ); | |
2229 free_rx_packet( &packet ); | |
2230 continue; | |
2231 } | |
2232 | |
2233 session->rx_dbuf[session->rx_i] = '\0'; | |
2234 packet.errcode = atoi( packet.records[1]->fields[0]->data ); | |
2235 | |
2236 purple_debug_info( MXIT_PLUGIN_ID, "Packet received CMD:%i (%i)\n", packet.cmd, packet.errcode ); | |
2237 #ifdef DEBUG_PROTOCOL | |
2238 /* debug */ | |
2239 dump_packet( &packet ); | |
2240 #endif | |
2241 | |
2242 /* reset the out ack */ | |
2243 if ( session->outack == packet.cmd ) { | |
2244 /* outstanding ack received from mxit server */ | |
2245 session->outack = 0; | |
2246 } | |
2247 | |
2248 /* check packet status */ | |
2249 if ( packet.errcode != MXIT_ERRCODE_SUCCESS ) { | |
2250 /* error reply! */ | |
2251 if ( ( packet.records[1]->fcount > 1 ) && ( packet.records[1]->fields[1]->data ) ) | |
2252 packet.errmsg = packet.records[1]->fields[1]->data; | |
2253 else | |
2254 packet.errmsg = NULL; | |
2255 | |
2256 res = process_error_response( session, &packet ); | |
2257 } | |
2258 else { | |
2259 /* success reply! */ | |
2260 res = process_success_response( session, &packet ); | |
2261 } | |
2262 | |
2263 /* free up the packet resources */ | |
2264 free_rx_packet( &packet ); | |
2265 } | |
2266 | |
2267 if ( session->outack == 0 ) | |
2268 mxit_manage_queue( session ); | |
2269 | |
2270 return res; | |
2271 } | |
2272 | |
2273 | |
2274 /*------------------------------------------------------------------------ | |
2275 * Callback when data is received from the MXit server. | |
2276 * | |
2277 * @param user_data The MXit session object | |
2278 * @param source The file-descriptor on which data was received | |
2279 * @param cond Condition which caused the callback (PURPLE_INPUT_READ) | |
2280 */ | |
2281 void mxit_cb_rx( gpointer user_data, gint source, PurpleInputCondition cond ) | |
2282 { | |
2283 struct MXitSession* session = (struct MXitSession*) user_data; | |
2284 char ch; | |
2285 int res; | |
2286 int len; | |
2287 | |
2288 if ( session->rx_state == RX_STATE_RLEN ) { | |
2289 /* we are reading in the packet length */ | |
2290 len = read( session->fd, &ch, 1 ); | |
2291 if ( len < 0 ) { | |
2292 /* connection error */ | |
2293 purple_connection_error( session->con, _( "A connection error occurred to MXit. (read stage 0x01)" ) ); | |
2294 return; | |
2295 } | |
2296 else if ( len == 0 ) { | |
2297 /* connection closed */ | |
2298 purple_connection_error( session->con, _( "A connection error occurred to MXit. (read stage 0x02)" ) ); | |
2299 return; | |
2300 } | |
2301 else { | |
2302 /* byte read */ | |
2303 if ( ch == CP_REC_TERM ) { | |
2304 /* the end of the length record found */ | |
2305 session->rx_lbuf[session->rx_i] = '\0'; | |
2306 session->rx_res = atoi( &session->rx_lbuf[3] ); | |
2307 if ( session->rx_res > CP_MAX_PACKET ) { | |
2308 purple_connection_error( session->con, _( "A connection error occurred to MXit. (read stage 0x03)" ) ); | |
2309 } | |
2310 session->rx_state = RX_STATE_DATA; | |
2311 session->rx_i = 0; | |
2312 } | |
2313 else { | |
2314 /* still part of the packet length record */ | |
2315 session->rx_lbuf[session->rx_i] = ch; | |
2316 session->rx_i++; | |
2317 if ( session->rx_i >= sizeof( session->rx_lbuf ) ) { | |
2318 /* malformed packet length record (too long) */ | |
2319 purple_connection_error( session->con, _( "A connection error occurred to MXit. (read stage 0x04)" ) ); | |
2320 return; | |
2321 } | |
2322 } | |
2323 } | |
2324 } | |
2325 else if ( session->rx_state == RX_STATE_DATA ) { | |
2326 /* we are reading in the packet data */ | |
2327 len = read( session->fd, &session->rx_dbuf[session->rx_i], session->rx_res ); | |
2328 if ( len < 0 ) { | |
2329 /* connection error */ | |
2330 purple_connection_error( session->con, _( "A connection error occurred to MXit. (read stage 0x05)" ) ); | |
2331 return; | |
2332 } | |
2333 else if ( len == 0 ) { | |
2334 /* connection closed */ | |
2335 purple_connection_error( session->con, _( "A connection error occurred to MXit. (read stage 0x06)" ) ); | |
2336 return; | |
2337 } | |
2338 else { | |
2339 /* data read */ | |
2340 session->rx_i += len; | |
2341 session->rx_res -= len; | |
2342 | |
2343 if ( session->rx_res == 0 ) { | |
2344 /* ok, so now we have read in the whole packet */ | |
2345 session->rx_state = RX_STATE_PROC; | |
2346 } | |
2347 } | |
2348 } | |
2349 | |
2350 if ( session->rx_state == RX_STATE_PROC ) { | |
2351 /* we have a full packet, which we now need to process */ | |
2352 res = mxit_parse_packet( session ); | |
2353 | |
2354 if ( res == 0 ) { | |
2355 /* we are still logged in */ | |
2356 session->rx_state = RX_STATE_RLEN; | |
2357 session->rx_res = 0; | |
2358 session->rx_i = 0; | |
2359 } | |
2360 } | |
2361 } | |
2362 | |
2363 | |
2364 /*------------------------------------------------------------------------ | |
2365 * Log the user off MXit and close the connection | |
2366 * | |
2367 * @param session The MXit session object | |
2368 */ | |
2369 void mxit_close_connection( struct MXitSession* session ) | |
2370 { | |
2371 purple_debug_info( MXIT_PLUGIN_ID, "mxit_close_connection\n" ); | |
2372 | |
2373 if ( !( session->flags & MXIT_FLAG_CONNECTED ) ) { | |
2374 /* we are already closed */ | |
2375 return; | |
2376 } | |
2377 else if ( session->flags & MXIT_FLAG_LOGGEDIN ) { | |
2378 /* we are currently logged in so we need to send a logout packet */ | |
2379 if ( !session->http ) { | |
2380 mxit_send_logout( session ); | |
2381 } | |
2382 session->flags &= ~MXIT_FLAG_LOGGEDIN; | |
2383 } | |
2384 session->flags &= ~MXIT_FLAG_CONNECTED; | |
2385 | |
2386 /* cancel outstanding HTTP request */ | |
2387 if ( ( session->http ) && ( session->http_out_req ) ) { | |
2388 purple_util_fetch_url_cancel( (PurpleUtilFetchUrlData*) session->http_out_req ); | |
2389 session->http_out_req = NULL; | |
2390 } | |
2391 | |
2392 /* remove the input cb function */ | |
2393 if ( session->con->inpa ) { | |
2394 purple_input_remove( session->con->inpa ); | |
2395 session->con->inpa = 0; | |
2396 } | |
2397 | |
2398 /* remove HTTP poll timer */ | |
2399 if ( session->http_timer_id > 0 ) | |
2400 purple_timeout_remove( session->http_timer_id ); | |
2401 | |
2402 /* remove queue manager timer */ | |
2403 if ( session->q_timer > 0 ) | |
2404 purple_timeout_remove( session->q_timer ); | |
2405 | |
2406 /* remove all groupchat rooms */ | |
2407 while ( session->rooms != NULL ) { | |
2408 struct multimx* multimx = (struct multimx *) session->rooms->data; | |
2409 | |
2410 session->rooms = g_list_remove( session->rooms, multimx ); | |
2411 | |
2412 free( multimx ); | |
2413 } | |
2414 g_list_free( session->rooms ); | |
2415 session->rooms = NULL; | |
2416 | |
2417 /* remove all rx chats names */ | |
2418 while ( session->active_chats != NULL ) { | |
2419 char* chat = (char*) session->active_chats->data; | |
2420 | |
2421 session->active_chats = g_list_remove( session->active_chats, chat ); | |
2422 | |
2423 g_free( chat ); | |
2424 } | |
2425 g_list_free( session->active_chats ); | |
2426 session->active_chats = NULL; | |
2427 | |
2428 /* free profile information */ | |
2429 if ( session->profile ) | |
2430 free( session->profile ); | |
2431 | |
2432 /* free custom emoticons */ | |
2433 mxit_free_emoticon_cache( session ); | |
2434 | |
2435 /* free allocated memory */ | |
2436 g_free( session->encpwd ); | |
2437 session->encpwd = NULL; | |
2438 | |
2439 /* flush all the commands still in the queue */ | |
2440 flush_queue( session ); | |
2441 } | |
2442 |