Mercurial > pidgin.yaz
view libpurple/protocols/msn/soap.c @ 20419:c2b406b4ccd7
Port a custom emoticon crash fix that seemed to get lost somewhere along
the way in the msnp14 branch - probably back in the SVN days
author | Stu Tomlinson <stu@nosnilmot.com> |
---|---|
date | Sun, 20 May 2007 15:59:10 +0000 |
parents | 4c5d68e93ef8 |
children | 2a5c1de05e0a |
line wrap: on
line source
/** * @file soap.c * SOAP connection related process * Author * MaYuan<mayuan2006@gmail.com> * purple * * Purple is the legal property of its developers, whose names are too numerous * to list here. Please refer to the COPYRIGHT file distributed with this * source distribution. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "msn.h" #include "soap.h" /*define this Macro to debug soap server action*/ #undef MSN_SOAP_DEBUG /*local function prototype*/ void msn_soap_set_process_step(MsnSoapConn *soapconn, MsnSoapStep step); /*setup the soap process step*/ void msn_soap_set_process_step(MsnSoapConn *soapconn, MsnSoapStep step) { soapconn->step = step; } //msn_soap_new(MsnSession *session,gpointer data,int sslconn) /*new a soap connection*/ MsnSoapConn * msn_soap_new(MsnSession *session,gpointer data,int sslconn) { MsnSoapConn *soapconn; soapconn = g_new0(MsnSoapConn, 1); soapconn->session = session; soapconn->parent = data; soapconn->ssl_conn = sslconn; soapconn->gsc = NULL; soapconn->input_handler = -1; soapconn->output_handler = -1; msn_soap_set_process_step(soapconn,MSN_SOAP_UNCONNECTED); soapconn->soap_queue = g_queue_new(); return soapconn; } /*ssl soap connect callback*/ void msn_soap_connect_cb(gpointer data, PurpleSslConnection *gsc, PurpleInputCondition cond) { MsnSoapConn * soapconn; MsnSession *session; purple_debug_info("MaYuan","Soap connection connected!\n"); soapconn = data; g_return_if_fail(soapconn != NULL); session = soapconn->session; g_return_if_fail(session != NULL); soapconn->gsc = gsc; /*connection callback*/ if(soapconn->connect_cb != NULL){ soapconn->connect_cb(data,gsc,cond); } msn_soap_set_process_step(soapconn,MSN_SOAP_CONNECTED); /*we do the SOAP request here*/ msn_soap_post_head_request(soapconn); } /*ssl soap error callback*/ static void msn_soap_error_cb(PurpleSslConnection *gsc, PurpleSslErrorType error, void *data) { MsnSoapConn * soapconn = data; g_return_if_fail(data != NULL); purple_debug_info("MaYuan","Soap connection error!\n"); msn_soap_set_process_step(soapconn, MSN_SOAP_UNCONNECTED); /*error callback*/ if(soapconn->error_cb != NULL){ soapconn->error_cb(gsc,error,data); } } /*init the soap connection*/ void msn_soap_init(MsnSoapConn *soapconn,char * host,int ssl, PurpleSslInputFunction connect_cb, PurpleSslErrorFunction error_cb) { purple_debug_info("MaYuan","msn_soap_init...\n"); soapconn->login_host = g_strdup(host); soapconn->ssl_conn = ssl; soapconn->connect_cb = connect_cb; soapconn->error_cb = error_cb; } /*connect the soap connection*/ void msn_soap_connect(MsnSoapConn *soapconn) { if(soapconn->ssl_conn){ purple_ssl_connect(soapconn->session->account, soapconn->login_host, PURPLE_SSL_DEFAULT_PORT, msn_soap_connect_cb, msn_soap_error_cb, soapconn); }else{ } msn_soap_set_process_step(soapconn,MSN_SOAP_CONNECTING); } /*close the soap connection*/ void msn_soap_close(MsnSoapConn *soapconn) { if(soapconn->ssl_conn){ if(soapconn->gsc != NULL){ purple_ssl_close(soapconn->gsc); soapconn->gsc = NULL; } }else{ } msn_soap_set_process_step(soapconn,MSN_SOAP_UNCONNECTED); } /*clean the unhandled SOAP request*/ void msn_soap_clean_unhandled_request(MsnSoapConn *soapconn) { MsnSoapReq *request; g_return_if_fail(soapconn != NULL); while ((request = g_queue_pop_head(soapconn->soap_queue)) != NULL){ msn_soap_request_free(request); } } /*destroy the soap connection*/ void msn_soap_destroy(MsnSoapConn *soapconn) { if(soapconn->login_host) g_free(soapconn->login_host); if(soapconn->login_path) g_free(soapconn->login_path); /*remove the write handler*/ if (soapconn->output_handler > 0){ purple_input_remove(soapconn->output_handler); } /*remove the read handler*/ if (soapconn->input_handler > 0){ purple_input_remove(soapconn->input_handler); } msn_soap_free_read_buf(soapconn); msn_soap_free_write_buf(soapconn); /*close ssl connection*/ msn_soap_close(soapconn); /*process the unhandled soap request*/ msn_soap_clean_unhandled_request(soapconn); g_queue_free(soapconn->soap_queue); g_free(soapconn); } /*check the soap is connected? * if connected return 1 */ int msn_soap_connected(MsnSoapConn *soapconn) { if(soapconn->ssl_conn){ return (soapconn->gsc == NULL? 0 : 1); } return(soapconn->fd>0? 1 : 0); } /*read and append the content to the buffer*/ static gssize msn_soap_read(MsnSoapConn *soapconn) { gssize len,requested_len; char temp_buf[MSN_SOAP_READ_BUFF_SIZE]; // requested_len = (soapconn->need_to_read > 0) ? soapconn->need_to_read : MSN_SOAP_READ_BUFF_SIZE; requested_len = MSN_SOAP_READ_BUFF_SIZE; if(soapconn->ssl_conn){ len = purple_ssl_read(soapconn->gsc, temp_buf,requested_len); }else{ len = read(soapconn->fd, temp_buf,requested_len); } if(len >0){ soapconn->read_buf = g_realloc(soapconn->read_buf, soapconn->read_len + len + 1); memcpy(soapconn->read_buf + soapconn->read_len, temp_buf, len); soapconn->read_len += len; soapconn->read_buf[soapconn->read_len] = '\0'; } #ifdef MSN_SOAP_DEBUG purple_debug_info("MaYuan","++soap ssl read:{%d}\n",len); purple_debug_info("MaYuan","nexus ssl read:{%s}\n",soapconn->read_buf); #endif return len; } /*read the whole SOAP server response*/ void msn_soap_read_cb(gpointer data, gint source, PurpleInputCondition cond) { MsnSoapConn *soapconn = data; MsnSession *session; int len; char * body_start,*body_len; char *length_start,*length_end; // purple_debug_misc("MaYuan", "soap read cb\n"); session = soapconn->session; g_return_if_fail(session != NULL); if (soapconn->input_handler == -1){ soapconn->input_handler = purple_input_add(soapconn->gsc->fd, PURPLE_INPUT_READ, msn_soap_read_cb, soapconn); } /*read the request header*/ len = msn_soap_read(soapconn); if (len < 0 && errno == EAGAIN){ return; }else if (len < 0) { purple_debug_error("msn", "read Error!len:%d\n",len); purple_input_remove(soapconn->input_handler); soapconn->input_handler = -1; g_free(soapconn->read_buf); soapconn->read_buf = NULL; soapconn->read_len = 0; /* TODO: error handling */ return; } if(soapconn->read_buf == NULL){ return; } if (strstr(soapconn->read_buf, "HTTP/1.1 302") != NULL) { /* Redirect. */ char *location, *c; purple_debug_error("MaYuan", "soap redirect\n"); location = strstr(soapconn->read_buf, "Location: "); if (location == NULL) { msn_soap_free_read_buf(soapconn); return; } location = strchr(location, ' ') + 1; if ((c = strchr(location, '\r')) != NULL) *c = '\0'; /* Skip the http:// */ if ((c = strchr(location, '/')) != NULL) location = c + 2; if ((c = strchr(location, '/')) != NULL) { g_free(soapconn->login_path); soapconn->login_path = g_strdup(c); *c = '\0'; } g_free(soapconn->login_host); soapconn->login_host = g_strdup(location); purple_ssl_connect(session->account, soapconn->login_host, PURPLE_SSL_DEFAULT_PORT, msn_soap_connect_cb, msn_soap_error_cb, soapconn); } /* Another case of redirection, active on May, 2007 See http://msnpiki.msnfanatic.com/index.php/MSNP13:SOAPTweener#Redirect */ else if (strstr(soapconn->read_buf, "<faultcode>psf:Redirect</faultcode>") != NULL) { char *location, *c; location = strstr(soapconn->read_buf, "<psf:redirectUrl>"); /* Omit the tag preceding the URL */ location += strlen("<psf:redirectUrl>"); location = strstr(location, ":/"); if (location == NULL) { msn_soap_free_read_buf(soapconn); return; } location += strlen("://"); /* Skip http:// or https:// */ if ( (c = strstr(location, "</psf:redirectUrl>")) != NULL ) *c = '\0'; if ( (c = strstr(location, "/")) != NULL ) { g_free(soapconn->login_path); soapconn->login_path = g_strdup(c); *c = '\0'; } g_free(soapconn->login_host); soapconn->login_host = g_strdup(location); purple_ssl_connect(session->account, soapconn->login_host, PURPLE_SSL_DEFAULT_PORT, msn_soap_connect_cb, msn_soap_error_cb, soapconn); } else if (strstr(soapconn->read_buf, "HTTP/1.1 401 Unauthorized") != NULL) { const char *error; purple_debug_error("MaYuan", "soap 401\n"); if ((error = strstr(soapconn->read_buf, "WWW-Authenticate")) != NULL) { if ((error = strstr(error, "cbtxt=")) != NULL) { const char *c; char *temp; error += strlen("cbtxt="); if ((c = strchr(error, '\n')) == NULL) c = error + strlen(error); temp = g_strndup(error, c - error); error = purple_url_decode(temp); g_free(temp); } } msn_session_set_error(session, MSN_ERROR_SERV_UNAVAILABLE, error); } else if (strstr(soapconn->read_buf, "HTTP/1.1 503 Service Unavailable")) { msn_session_set_error(session, MSN_ERROR_SERV_UNAVAILABLE, NULL); } else if ((strstr(soapconn->read_buf, "HTTP/1.1 200 OK")) ||(strstr(soapconn->read_buf, "HTTP/1.1 500"))) { /*OK! process the SOAP body*/ body_start = (char *)g_strstr_len(soapconn->read_buf, soapconn->read_len,"\r\n\r\n"); if(!body_start){ return; } body_start += 4; // purple_debug_misc("msn", "Soap Read: {%s}\n", soapconn->read_buf); /* we read the content-length*/ length_start = strstr(soapconn->read_buf, "Content-Length: "); length_start += strlen("Content-Length: "); length_end = strstr(length_start, "\r\n"); body_len = g_strndup(length_start,length_end - length_start); /*setup the conn body */ soapconn->body = body_start; soapconn->body_len = atoi(body_len); #ifdef MSN_SOAP_DEBUG purple_debug_misc("MaYuan","SOAP Read length :%d,body len:%d\n",soapconn->read_len,soapconn->body_len); #endif soapconn->need_to_read = (body_start - soapconn->read_buf +soapconn->body_len) - soapconn->read_len; if(soapconn->need_to_read >0){ return; } g_free(body_len); /*remove the read handler*/ purple_input_remove(soapconn->input_handler); soapconn->input_handler = -1; /* * close the soap connection,if more soap request came, * Just reconnect to do it, * * To solve the problem described below: * When I post the soap request in one socket one after the other, * The first read is ok, But the second soap read always got 0 bytes, * Weird! * */ msn_soap_close(soapconn); /*call the read callback*/ if(soapconn->read_cb != NULL){ soapconn->read_cb(soapconn,source,0); } } return; } void msn_soap_free_read_buf(MsnSoapConn *soapconn) { if(soapconn->read_buf){ g_free(soapconn->read_buf); } soapconn->read_buf = NULL; soapconn->read_len = 0; soapconn->need_to_read = 0; } void msn_soap_free_write_buf(MsnSoapConn *soapconn) { if(soapconn->write_buf){ g_free(soapconn->write_buf); } soapconn->write_buf = NULL; soapconn->written_len = 0; } /*Soap write process func*/ static void msn_soap_write_cb(gpointer data, gint source, PurpleInputCondition cond) { MsnSoapConn *soapconn = data; int len, total_len; g_return_if_fail(soapconn != NULL); if(soapconn->write_buf == NULL){ purple_debug_error("MaYuan","soap buffer is NULL\n"); purple_input_remove(soapconn->output_handler); soapconn->output_handler = -1; return; } total_len = strlen(soapconn->write_buf); /* * write the content to SSL server, */ len = purple_ssl_write(soapconn->gsc, soapconn->write_buf + soapconn->written_len, total_len - soapconn->written_len); if (len < 0 && errno == EAGAIN) return; else if (len <= 0){ /*SSL write error!*/ purple_input_remove(soapconn->output_handler); soapconn->output_handler = -1; /* TODO: notify of the error */ return; } soapconn->written_len += len; if (soapconn->written_len < total_len) return; purple_input_remove(soapconn->output_handler); soapconn->output_handler = -1; /*clear the write buff*/ msn_soap_free_write_buf(soapconn); /* Write finish! * callback for write done */ if(soapconn->written_cb != NULL){ soapconn->written_cb(soapconn, source, 0); } /*maybe we need to read the input?*/ msn_soap_read_cb(soapconn,source,0); } /*write the buffer to SOAP connection*/ void msn_soap_write(MsnSoapConn * soapconn, char *write_buf, PurpleInputFunction written_cb) { soapconn->write_buf = write_buf; soapconn->written_len = 0; soapconn->written_cb = written_cb; /*clear the read buffer first*/ /*start the write*/ soapconn->output_handler = purple_input_add(soapconn->gsc->fd, PURPLE_INPUT_WRITE, msn_soap_write_cb, soapconn); msn_soap_write_cb(soapconn, soapconn->gsc->fd, PURPLE_INPUT_WRITE); } /* New a soap request*/ MsnSoapReq * msn_soap_request_new(const char *host,const char *post_url,const char *soap_action, const char *body, PurpleInputFunction read_cb,PurpleInputFunction written_cb) { MsnSoapReq *request; request = g_new0(MsnSoapReq, 1); request->id = 0; request->login_host = g_strdup(host); request->login_path = g_strdup(post_url); request->soap_action = g_strdup(soap_action); request->body = g_strdup(body); request->read_cb = read_cb; request->written_cb = written_cb; return request; } /*free a soap request*/ void msn_soap_request_free(MsnSoapReq *request) { g_return_if_fail(request != NULL); g_free(request->login_host); g_free(request->login_path); g_free(request->soap_action); g_free(request->body); request->read_cb = NULL; request->written_cb = NULL; g_free(request); } /*post the soap request queue's head request*/ void msn_soap_post_head_request(MsnSoapConn *soapconn) { g_return_if_fail(soapconn->soap_queue != NULL); if(!g_queue_is_empty(soapconn->soap_queue)){ MsnSoapReq *request; if((request = g_queue_pop_head(soapconn->soap_queue)) != NULL){ msn_soap_post_request(soapconn,request); } } msn_soap_set_process_step(soapconn,MSN_SOAP_CONNECTED_IDLE); } /*post the soap request , * if not connected, Connected first. */ void msn_soap_post(MsnSoapConn *soapconn,MsnSoapReq *request, MsnSoapConnectInitFunction msn_soap_init_func) { if(request != NULL){ g_queue_push_tail(soapconn->soap_queue, request); } if(!msn_soap_connected(soapconn)&&(soapconn->step == MSN_SOAP_UNCONNECTED) &&(!g_queue_is_empty(soapconn->soap_queue))){ /*not connected?and we have something to process connect it first*/ purple_debug_info("Ma Yuan","soap is not connected!\n"); msn_soap_init_func(soapconn); msn_soap_connect(soapconn); return; } purple_debug_info("Ma Yuan","soap connected!\n"); /*if connected, what we only needed to do is to queue the request, * when SOAP request in the queue processed done, will do this command. * we just waiting... * If we send the request this time,error may occure */ #if 0 if(soapconn->step == MSN_SOAP_CONNECTED_IDLE){ msn_soap_post_head_request(soapconn); } #endif } /*Post the soap request action*/ void msn_soap_post_request(MsnSoapConn *soapconn,MsnSoapReq *request) { char * soap_head = NULL; char * request_str = NULL; purple_debug_info("MaYuan","msn_soap_post_request()...\n"); msn_soap_set_process_step(soapconn,MSN_SOAP_PROCESSING); soap_head = g_strdup_printf( "POST %s HTTP/1.1\r\n" "SOAPAction: %s\r\n" "Content-Type:text/xml; charset=utf-8\r\n" "Cookie: MSPAuth=%s\r\n" "User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)\r\n" "Accept: */*\r\n" "Host: %s\r\n" "Content-Length: %d\r\n" "Connection: Keep-Alive\r\n" "Cache-Control: no-cache\r\n\r\n", request->login_path, request->soap_action, soapconn->session->passport_info.mspauth, request->login_host, strlen(request->body) ); request_str = g_strdup_printf("%s%s", soap_head,request->body); g_free(soap_head); #ifdef MSN_SOAP_DEBUG purple_debug_info("MaYuan","send to server{%s}\n",request_str); #endif /*free read buffer*/ msn_soap_free_read_buf(soapconn); /*post it to server*/ msn_soap_write(soapconn,request_str,request->written_cb); }