comparison libpurple/protocols/msn/oim.c @ 20398:61d6a3dfbb3c

propagate from branch 'im.pidgin.rlaager.merging.msnp13-and-sf-1621854-4-rlaager-whitespace' (head 51cec0ffea45c8589dcb7bf0f9e36e749ed43017) to branch 'im.pidgin.rlaager.merging.msnp13-and-pidgin' (head d0d075250a037e5d0a268a39501bf169465061a4)
author Richard Laager <rlaager@wiktel.com>
date Sun, 15 Apr 2007 03:56:08 +0000
parents 4a099e4d0d09
children 4ddc27c18781
comparison
equal deleted inserted replaced
20397:6ac7e33fdabf 20398:61d6a3dfbb3c
1 /**
2 * @file oim.c
3 * get and send MSN offline Instant Message via SOAP request
4 * Author
5 * MaYuan<mayuan2006@gmail.com>
6 * gaim
7 *
8 * Gaim is the legal property of its developers, whose names are too numerous
9 * to list here. Please refer to the COPYRIGHT file distributed with this
10 * source distribution.
11 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 */
26 #include "msn.h"
27 #include "soap.h"
28 #include "oim.h"
29 #include "msnutils.h"
30
31 /*Local Function Prototype*/
32 static void msn_oim_post_single_get_msg(MsnOim *oim,const char *msgid);
33 static MsnOimSendReq *msn_oim_new_send_req(const char *from_member,
34 const char *friendname,
35 const char* to_member,
36 gint send_seq,
37 const char *msg);
38 static void msn_oim_retrieve_connect_init(MsnSoapConn *soapconn);
39 static void msn_oim_send_connect_init(MsnSoapConn *soapconn);
40 static void msn_oim_free_send_req(MsnOimSendReq *req);
41 static void msn_oim_report_to_user(MsnOim *oim, char *msg_str);
42 static void msn_oim_get_process(MsnOim *oim, char *oim_msg);
43 static char *msn_oim_msg_to_str(MsnOim *oim, const char *body);
44 const void msn_oim_send_process(MsnOim *oim, const char *body, int len);
45
46 /*new a OIM object*/
47 MsnOim *
48 msn_oim_new(MsnSession *session)
49 {
50 MsnOim *oim;
51
52 oim = g_new0(MsnOim, 1);
53 oim->session = session;
54 oim->retrieveconn = msn_soap_new(session,oim,1);
55
56 oim->oim_list = NULL;
57 oim->sendconn = msn_soap_new(session,oim,1);
58 oim->run_id = rand_guid();
59 oim->challenge = NULL;
60 oim->send_queue = g_queue_new();
61 oim->send_seq = 1;
62 return oim;
63 }
64
65 /*destroy the oim object*/
66 void
67 msn_oim_destroy(MsnOim *oim)
68 {
69 MsnOimSendReq *request;
70
71 gaim_debug_info("OIM","destroy the OIM \n");
72 msn_soap_destroy(oim->retrieveconn);
73 msn_soap_destroy(oim->sendconn);
74 g_free(oim->run_id);
75 g_free(oim->challenge);
76
77 while((request = g_queue_pop_head(oim->send_queue)) != NULL){
78 msn_oim_free_send_req(request);
79 }
80 g_queue_free(oim->send_queue);
81
82 g_free(oim);
83 }
84
85 static MsnOimSendReq *
86 msn_oim_new_send_req(const char *from_member, const char*friendname,
87 const char* to_member, gint send_seq,
88 const char *msg)
89 {
90 MsnOimSendReq *request;
91
92 request = g_new0(MsnOimSendReq, 1);
93 request->from_member =g_strdup(from_member);
94 request->friendname = g_strdup(friendname);
95 request->to_member = g_strdup(to_member);
96 request->send_seq = send_seq;
97 request->oim_msg = g_strdup(msg);
98 return request;
99 }
100
101 static void
102 msn_oim_free_send_req(MsnOimSendReq *req)
103 {
104 g_return_if_fail(req != NULL);
105
106 g_free(req->from_member);
107 g_free(req->friendname);
108 g_free(req->to_member);
109 g_free(req->oim_msg);
110
111 g_free(req);
112 }
113
114 /****************************************
115 * OIM send SOAP request
116 * **************************************/
117 /*encode the message to OIM Message Format*/
118 static char *
119 msn_oim_msg_to_str(MsnOim *oim, const char *body)
120 {
121 char *oim_body,*oim_base64;
122
123 gaim_debug_info("MaYuan","encode OIM Message...\n");
124 oim_base64 = gaim_base64_encode((const guchar *)body, strlen(body));
125 gaim_debug_info("MaYuan","encoded base64 body:{%s}\n",oim_base64);
126 oim_body = g_strdup_printf(MSN_OIM_MSG_TEMPLATE,
127 oim->run_id,oim->send_seq,oim_base64);
128
129 return oim_body;
130 }
131
132 /*oim SOAP server login error*/
133 static void
134 msn_oim_send_error_cb(GaimSslConnection *gsc, GaimSslErrorType error, void *data)
135 {
136 MsnSoapConn *soapconn = data;
137 MsnSession *session;
138
139 session = soapconn->session;
140 g_return_if_fail(session != NULL);
141
142 msn_session_set_error(session, MSN_ERROR_SERV_DOWN, _("Unable to connect to OIM server"));
143 }
144
145 /*msn oim SOAP server connect process*/
146 static void
147 msn_oim_send_connect_cb(gpointer data, GaimSslConnection *gsc,
148 GaimInputCondition cond)
149 {
150 MsnSoapConn *soapconn = data;
151 MsnSession * session;
152 MsnOim *oim;
153
154 oim = soapconn->parent;
155 g_return_if_fail(oim != NULL);
156
157 session = oim->session;
158 g_return_if_fail(session != NULL);
159 }
160
161 /*
162 * Process the send return SOAP string
163 * If got SOAP Fault,get the lock key,and resend it.
164 */
165 const void
166 msn_oim_send_process(MsnOim *oim, const char *body, int len)
167 {
168 xmlnode *responseNode, *bodyNode;
169 xmlnode *faultNode, *faultCodeNode, *faultstringNode;
170 xmlnode *detailNode, *challengeNode;
171 char *faultCodeStr, *faultstring;
172
173 responseNode = xmlnode_from_str(body,len);
174 g_return_if_fail(responseNode != NULL);
175 bodyNode = xmlnode_get_child(responseNode,"Body");
176 faultNode = xmlnode_get_child(bodyNode,"Fault");
177 if(faultNode == NULL){
178 /*Send OK! return*/
179 MsnOimSendReq *request;
180
181 gaim_debug_info("MaYuan","send OIM OK!");
182 xmlnode_free(responseNode);
183 request = g_queue_pop_head(oim->send_queue);
184 msn_oim_free_send_req(request);
185 /*send next buffered Offline Message*/
186 msn_soap_post(oim->sendconn,NULL,msn_oim_send_connect_init);
187 return;
188 }
189 /*get the challenge,and repost it*/
190 faultCodeNode = xmlnode_get_child(faultNode,"faultcode");
191 if(faultCodeNode == NULL){
192 gaim_debug_info("MaYuan","faultcode Node is NULL\n");
193 goto oim_send_process_fail;
194 }
195 faultCodeStr = xmlnode_get_data(faultCodeNode);
196 gaim_debug_info("MaYuan","fault code:{%s}\n",faultCodeStr);
197
198 if(strcmp(faultCodeStr,"q0:AuthenticationFailed")){
199 /*other Fault Reason?*/
200 goto oim_send_process_fail;
201 }
202
203 faultstringNode = xmlnode_get_child(faultNode,"faultstring");
204 faultstring = xmlnode_get_data(faultstringNode);
205 gaim_debug_info("MaYuan","fault string :{%s}\n",faultstring);
206
207 /* lock key fault reason,
208 * compute the challenge and resend it
209 */
210 detailNode = xmlnode_get_child(faultNode, "detail");
211 if(detailNode == NULL){
212 goto oim_send_process_fail;
213 }
214 challengeNode = xmlnode_get_child(detailNode,"LockKeyChallenge");
215
216 g_free(oim->challenge);
217 oim->challenge = xmlnode_get_data(challengeNode);
218 gaim_debug_info("MaYuan","lockkey:{%s}\n",oim->challenge);
219
220 /*repost the send*/
221 gaim_debug_info("MaYuan","prepare to repost the send...\n");
222 msn_oim_send_msg(oim);
223
224 oim_send_process_fail:
225 xmlnode_free(responseNode);
226 return ;
227 }
228
229 static void
230 msn_oim_send_read_cb(gpointer data, GaimSslConnection *gsc,
231 GaimInputCondition cond)
232 {
233 MsnSoapConn * soapconn = data;
234 MsnSession *session = soapconn->session;
235 MsnOim * oim;
236
237 g_return_if_fail(session != NULL);
238 oim = soapconn->session->oim;
239 g_return_if_fail(oim != NULL);
240
241 gaim_debug_info("MaYuan","read buffer:{%s}\n",soapconn->body);
242 msn_oim_send_process(oim,soapconn->body,soapconn->body_len);
243 }
244
245 static void
246 msn_oim_send_written_cb(gpointer data, gint source, GaimInputCondition cond)
247 {
248 MsnSoapConn * soapconn = data;
249
250 soapconn->read_cb = msn_oim_send_read_cb;
251 // msn_soap_read_cb(data,source,cond);
252 }
253
254 void
255 msn_oim_prep_send_msg_info(MsnOim *oim, const char *membername,
256 const char* friendname, const char *tomember,
257 const char * msg)
258 {
259 MsnOimSendReq *request;
260
261 g_return_if_fail(oim != NULL);
262
263 request = msn_oim_new_send_req(membername,friendname,tomember,oim->send_seq,msg);
264 g_queue_push_tail(oim->send_queue,request);
265 }
266
267 /*post send single message request to oim server*/
268 void
269 msn_oim_send_msg(MsnOim *oim)
270 {
271 MsnSoapReq *soap_request;
272 MsnOimSendReq *oim_request;
273 char *soap_body,*mspauth;
274 char *msg_body;
275 char buf[33];
276
277 g_return_if_fail(oim != NULL);
278 oim_request = g_queue_pop_head(oim->send_queue);
279 g_return_if_fail(oim_request != NULL);
280
281 gaim_debug_info("MaYuan","send single OIM Message\n");
282 mspauth = g_strdup_printf("t=%s&amp;p=%s",
283 oim->session->passport_info.t,
284 oim->session->passport_info.p
285 );
286 g_queue_push_head(oim->send_queue,oim_request);
287
288 /* if we got the challenge lock key, we compute it
289 * else we go for the SOAP fault and resend it.
290 */
291 if(oim->challenge != NULL){
292 msn_handle_chl(oim->challenge, buf);
293 }else{
294 gaim_debug_info("MaYuan","no lock key challenge,wait for SOAP Fault and Resend\n");
295 buf[0]='\0';
296 }
297 gaim_debug_info("MaYuan","get the lock key challenge {%s}\n",buf);
298
299 msg_body = msn_oim_msg_to_str(oim, oim_request->oim_msg);
300 soap_body = g_strdup_printf(MSN_OIM_SEND_TEMPLATE,
301 oim_request->from_member,
302 oim_request->friendname,
303 oim_request->to_member,
304 mspauth,
305 MSNP13_WLM_PRODUCT_ID,
306 buf,
307 oim_request->send_seq,
308 msg_body
309 );
310 soap_request = msn_soap_request_new(MSN_OIM_SEND_HOST,
311 MSN_OIM_SEND_URL,MSN_OIM_SEND_SOAP_ACTION,
312 soap_body,
313 msn_oim_send_read_cb,
314 msn_oim_send_written_cb);
315 g_free(mspauth);
316 g_free(msg_body);
317 g_free(soap_body);
318
319 /*increase the offline Sequence control*/
320 if(oim->challenge != NULL){
321 oim->send_seq++;
322 }
323 msn_soap_post(oim->sendconn,soap_request,msn_oim_send_connect_init);
324 }
325
326 /****************************************
327 * OIM delete SOAP request
328 * **************************************/
329 static void
330 msn_oim_delete_read_cb(gpointer data, GaimSslConnection *gsc,
331 GaimInputCondition cond)
332 {
333 MsnSoapConn * soapconn = data;
334
335 gaim_debug_info("MaYuan","OIM delete read buffer:{%s}\n",soapconn->body);
336
337 msn_soap_free_read_buf(soapconn);
338 /*get next single Offline Message*/
339 msn_soap_post(soapconn,NULL,msn_oim_retrieve_connect_init);
340 }
341
342 static void
343 msn_oim_delete_written_cb(gpointer data, gint source, GaimInputCondition cond)
344 {
345 MsnSoapConn * soapconn = data;
346
347 soapconn->read_cb = msn_oim_delete_read_cb;
348 }
349
350 /*Post to get the Offline Instant Message*/
351 static void
352 msn_oim_post_delete_msg(MsnOim *oim,const char *msgid)
353 {
354 MsnSoapReq *soap_request;
355 const char *soap_body,*t,*p;
356
357 g_return_if_fail(oim != NULL);
358 g_return_if_fail(msgid != NULL);
359
360 gaim_debug_info("MaYuan","Delete single OIM Message {%s}\n",msgid);
361 t = oim->session->passport_info.t;
362 p = oim->session->passport_info.p;
363
364 soap_body = g_strdup_printf(MSN_OIM_DEL_TEMPLATE,
365 t,
366 p,
367 msgid
368 );
369 soap_request = msn_soap_request_new(MSN_OIM_RETRIEVE_HOST,
370 MSN_OIM_RETRIEVE_URL,MSN_OIM_DEL_SOAP_ACTION,
371 soap_body,
372 msn_oim_delete_read_cb,
373 msn_oim_delete_written_cb);
374 msn_soap_post(oim->retrieveconn,soap_request,msn_oim_retrieve_connect_init);
375 }
376
377 /****************************************
378 * OIM get SOAP request
379 * **************************************/
380 /*oim SOAP server login error*/
381 static void
382 msn_oim_get_error_cb(GaimSslConnection *gsc, GaimSslErrorType error, void *data)
383 {
384 MsnSoapConn *soapconn = data;
385 MsnSession *session;
386
387 session = soapconn->session;
388 g_return_if_fail(session != NULL);
389 msn_soap_clean_unhandled_request(soapconn);
390
391 // msn_session_set_error(session, MSN_ERROR_SERV_DOWN, _("Unable to connect to OIM server"));
392 }
393
394 /*msn oim SOAP server connect process*/
395 static void
396 msn_oim_get_connect_cb(gpointer data, GaimSslConnection *gsc,
397 GaimInputCondition cond)
398 {
399 MsnSoapConn *soapconn = data;
400 MsnSession * session;
401 MsnOim *oim;
402
403 oim = soapconn->parent;
404 g_return_if_fail(oim != NULL);
405
406 session = oim->session;
407 g_return_if_fail(session != NULL);
408
409 gaim_debug_info("MaYuan","oim get SOAP Server connected!\n");
410 }
411
412 /*Post the Offline Instant Message to User Conversation*/
413 static void
414 msn_oim_report_to_user(MsnOim *oim, char *msg_str)
415 {
416 MsnMessage *message;
417 char *date,*from,*decode_msg;
418 gsize body_len;
419 char **tokens;
420 char *start,*end;
421 int has_nick = 0;
422 char *passport_str, *passport;
423 char *msg_id;
424
425 message = msn_message_new(MSN_MSG_UNKNOWN);
426
427 msn_message_parse_payload(message, msg_str, strlen(msg_str),
428 MSG_OIM_LINE_DEM, MSG_OIM_BODY_DEM);
429 gaim_debug_info("MaYuan","oim body:{%s}\n",message->body);
430 decode_msg = gaim_base64_decode(message->body,&body_len);
431 date = (char *)g_hash_table_lookup(message->attr_table, "Date");
432 from = (char *)g_hash_table_lookup(message->attr_table, "From");
433 if(strstr(from," ")){
434 has_nick = 1;
435 }
436 if(has_nick){
437 tokens = g_strsplit(from , " " , 2);
438 passport_str = g_strdup(tokens[1]);
439 gaim_debug_info("MaYuan","oim Date:{%s},nickname:{%s},tokens[1]:{%s} passport{%s}\n",
440 date,tokens[0],tokens[1],passport_str);
441 g_strfreev(tokens);
442 }else{
443 passport_str = g_strdup(from);
444 gaim_debug_info("MaYuan","oim Date:{%s},passport{%s}\n",
445 date,passport_str);
446 }
447 start = strstr(passport_str,"<");
448 start += 1;
449 end = strstr(passport_str,">");
450 passport = g_strndup(start,end - start);
451 g_free(passport_str);
452 gaim_debug_info("MaYuan","oim Date:{%s},passport{%s}\n",date,passport);
453
454 msn_session_report_user(oim->session,passport,decode_msg,GAIM_MESSAGE_SYSTEM);
455
456 /*Now get the oim message ID from the oim_list.
457 * and append to read list to prepare for deleting the Offline Message when sign out
458 */
459 if(oim->oim_list != NULL){
460 msg_id = oim->oim_list->data;
461 msn_oim_post_delete_msg(oim,msg_id);
462 oim->oim_list = g_list_remove(oim->oim_list, oim->oim_list->data);
463 g_free(msg_id);
464 }
465
466 g_free(passport);
467 }
468
469 /* Parse the XML data,
470 * prepare to report the OIM to user
471 */
472 static void
473 msn_oim_get_process(MsnOim *oim, char *oim_msg)
474 {
475 xmlnode *oimNode,*bodyNode,*responseNode,*msgNode;
476 char *msg_data,*msg_str;
477
478 oimNode = xmlnode_from_str(oim_msg, strlen(oim_msg));
479 bodyNode = xmlnode_get_child(oimNode,"Body");
480 responseNode = xmlnode_get_child(bodyNode,"GetMessageResponse");
481 msgNode = xmlnode_get_child(responseNode,"GetMessageResult");
482 msg_data = xmlnode_get_data(msgNode);
483 msg_str = g_strdup(msg_data);
484 g_free(msg_data);
485 gaim_debug_info("OIM","msg:{%s}\n",msg_str);
486 msn_oim_report_to_user(oim,msg_str);
487
488 g_free(msg_str);
489 }
490
491 static void
492 msn_oim_get_read_cb(gpointer data, GaimSslConnection *gsc,
493 GaimInputCondition cond)
494 {
495 MsnSoapConn * soapconn = data;
496 MsnOim * oim = soapconn->session->oim;
497
498 gaim_debug_info("MaYuan","OIM get read buffer:{%s}\n",soapconn->body);
499
500 /*we need to process the read message!*/
501 msn_oim_get_process(oim,soapconn->body);
502 msn_soap_free_read_buf(soapconn);
503
504 /*get next single Offline Message*/
505 msn_soap_post(soapconn,NULL,msn_oim_retrieve_connect_init);
506 }
507
508 static void
509 msn_oim_get_written_cb(gpointer data, gint source, GaimInputCondition cond)
510 {
511 MsnSoapConn * soapconn = data;
512
513 soapconn->read_cb = msn_oim_get_read_cb;
514 // msn_soap_read_cb(data,source,cond);
515 }
516
517 /* parse the oim XML data
518 * and post it to the soap server to get the Offline Message
519 * */
520 void
521 msn_parse_oim_msg(MsnOim *oim,const char *xmlmsg)
522 {
523 xmlnode *mdNode,*mNode,*ENode,*INode,*rtNode,*nNode;
524 char *passport,*rTime,*msgid,*nickname;
525
526 mdNode = xmlnode_from_str(xmlmsg, strlen(xmlmsg));
527 for(mNode = xmlnode_get_child(mdNode, "M"); mNode;
528 mNode = xmlnode_get_next_twin(mNode)){
529 /*email Node*/
530 ENode = xmlnode_get_child(mNode,"E");
531 passport = xmlnode_get_data(ENode);
532 /*Index */
533 INode = xmlnode_get_child(mNode,"I");
534 msgid = xmlnode_get_data(INode);
535 /*Nickname*/
536 nNode = xmlnode_get_child(mNode,"N");
537 nickname = xmlnode_get_data(nNode);
538 /*receive time*/
539 rtNode = xmlnode_get_child(mNode,"RT");
540 if(rtNode != NULL)
541 rTime = xmlnode_get_data(rtNode);
542 /* gaim_debug_info("MaYuan","E:{%s},I:{%s},rTime:{%s}\n",passport,msgid,rTime);*/
543
544 oim->oim_list = g_list_append(oim->oim_list,msgid);
545 msn_oim_post_single_get_msg(oim,msgid);
546 g_free(passport);
547 // g_free(msgid);
548 g_free(rTime);
549 g_free(nickname);
550 }
551 }
552
553 /*Post to get the Offline Instant Message*/
554 static void
555 msn_oim_post_single_get_msg(MsnOim *oim,const char *msgid)
556 {
557 MsnSoapReq *soap_request;
558 const char *soap_body,*t,*p;
559
560 gaim_debug_info("MaYuan","Get single OIM Message\n");
561 t = oim->session->passport_info.t;
562 p = oim->session->passport_info.p;
563
564 soap_body = g_strdup_printf(MSN_OIM_GET_TEMPLATE,
565 t,
566 p,
567 msgid
568 );
569 soap_request = msn_soap_request_new(MSN_OIM_RETRIEVE_HOST,
570 MSN_OIM_RETRIEVE_URL,MSN_OIM_GET_SOAP_ACTION,
571 soap_body,
572 msn_oim_get_read_cb,
573 msn_oim_get_written_cb);
574 msn_soap_post(oim->retrieveconn,soap_request,msn_oim_retrieve_connect_init);
575 }
576
577 /*msn oim retrieve server connect init */
578 static void
579 msn_oim_retrieve_connect_init(MsnSoapConn *soapconn)
580 {
581 gaim_debug_info("MaYuan","msn_oim_connect...\n");
582 msn_soap_init(soapconn,MSN_OIM_RETRIEVE_HOST,1,
583 msn_oim_get_connect_cb,
584 msn_oim_get_error_cb);
585 }
586
587 /*Msn OIM Send Server Connect Init Function*/
588 static void
589 msn_oim_send_connect_init(MsnSoapConn *sendconn)
590 {
591 gaim_debug_info("MaYuan","msn oim send connect init...\n");
592 msn_soap_init(sendconn,MSN_OIM_SEND_HOST,1,
593 msn_oim_send_connect_cb,
594 msn_oim_send_error_cb);
595 }
596
597 /*endof oim.c*/