?? jabber.c
字號:
/* * purple - Jabber Protocol Plugin * * Copyright (C) 2003, Nathan Walp <faceprint@faceprint.com> * * 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 "internal.h"#include "account.h"#include "accountopt.h"#include "blist.h"#include "cmds.h"#include "connection.h"#include "conversation.h"#include "debug.h"#include "dnssrv.h"#include "message.h"#include "notify.h"#include "pluginpref.h"#include "proxy.h"#include "prpl.h"#include "request.h"#include "server.h"#include "util.h"#include "version.h"#include "xmlnode.h"#include "auth.h"#include "buddy.h"#include "chat.h"#include "disco.h"#include "google.h"#include "iq.h"#include "jutil.h"#include "message.h"#include "parser.h"#include "presence.h"#include "jabber.h"#include "roster.h"#include "si.h"#include "xdata.h"#define JABBER_CONNECT_STEPS (js->gsc ? 8 : 5)static PurplePlugin *my_protocol = NULL;static void jabber_stream_init(JabberStream *js){ char *open_stream; open_stream = g_strdup_printf("<stream:stream to='%s' " "xmlns='jabber:client' " "xmlns:stream='http://etherx.jabber.org/streams' " "version='1.0'>", js->user->domain); /* setup the parser fresh for each stream */ jabber_parser_setup(js); jabber_send_raw(js, open_stream, -1); js->reinit = FALSE; g_free(open_stream);}static voidjabber_session_initialized_cb(JabberStream *js, xmlnode *packet, gpointer data){ const char *type = xmlnode_get_attrib(packet, "type"); if(type && !strcmp(type, "result")) { jabber_stream_set_state(js, JABBER_STREAM_CONNECTED); } else { purple_connection_error(js->gc, _("Error initializing session")); }}static void jabber_session_init(JabberStream *js){ JabberIq *iq = jabber_iq_new(js, JABBER_IQ_SET); xmlnode *session; jabber_iq_set_callback(iq, jabber_session_initialized_cb, NULL); session = xmlnode_new_child(iq->node, "session"); xmlnode_set_namespace(session, "urn:ietf:params:xml:ns:xmpp-session"); jabber_iq_send(iq);}static void jabber_bind_result_cb(JabberStream *js, xmlnode *packet, gpointer data){ const char *type = xmlnode_get_attrib(packet, "type"); xmlnode *bind; if(type && !strcmp(type, "result") && (bind = xmlnode_get_child_with_namespace(packet, "bind", "urn:ietf:params:xml:ns:xmpp-bind"))) { xmlnode *jid; char *full_jid; if((jid = xmlnode_get_child(bind, "jid")) && (full_jid = xmlnode_get_data(jid))) { JabberBuddy *my_jb = NULL; jabber_id_free(js->user); if(!(js->user = jabber_id_new(full_jid))) { purple_connection_error(js->gc, _("Invalid response from server.")); } if((my_jb = jabber_buddy_find(js, full_jid, TRUE))) my_jb->subscription |= JABBER_SUB_BOTH; g_free(full_jid); } } else { char *msg = jabber_parse_error(js, packet); purple_connection_error(js->gc, msg); g_free(msg); } jabber_session_init(js);}static void jabber_stream_features_parse(JabberStream *js, xmlnode *packet){ if(xmlnode_get_child(packet, "starttls")) { if(jabber_process_starttls(js, packet)) return; } if(js->registration) { jabber_register_start(js); } else if(xmlnode_get_child(packet, "mechanisms")) { jabber_auth_start(js, packet); } else if(xmlnode_get_child(packet, "bind")) { xmlnode *bind, *resource; JabberIq *iq = jabber_iq_new(js, JABBER_IQ_SET); bind = xmlnode_new_child(iq->node, "bind"); xmlnode_set_namespace(bind, "urn:ietf:params:xml:ns:xmpp-bind"); resource = xmlnode_new_child(bind, "resource"); xmlnode_insert_data(resource, js->user->resource, -1); jabber_iq_set_callback(iq, jabber_bind_result_cb, NULL); jabber_iq_send(iq); } else /* if(xmlnode_get_child_with_namespace(packet, "auth")) */ { /* If we get an empty stream:features packet, or we explicitly get * an auth feature with namespace http://jabber.org/features/iq-auth * we should revert back to iq:auth authentication, even though we're * connecting to an XMPP server. */ js->auth_type = JABBER_AUTH_IQ_AUTH; jabber_stream_set_state(js, JABBER_STREAM_AUTHENTICATING); }}static void jabber_stream_handle_error(JabberStream *js, xmlnode *packet){ char *msg = jabber_parse_error(js, packet); purple_connection_error(js->gc, msg); g_free(msg);}static void tls_init(JabberStream *js);void jabber_process_packet(JabberStream *js, xmlnode *packet){ const char *xmlns; purple_signal_emit(my_protocol, "jabber-receiving-xmlnode", js->gc, &packet); /* if the signal leaves us with a null packet, we're done */ if(NULL == packet) return; xmlns = xmlnode_get_namespace(packet); if(!strcmp(packet->name, "iq")) { jabber_iq_parse(js, packet); } else if(!strcmp(packet->name, "presence")) { jabber_presence_parse(js, packet); } else if(!strcmp(packet->name, "message")) { jabber_message_parse(js, packet); } else if(!strcmp(packet->name, "stream:features")) { jabber_stream_features_parse(js, packet); } else if (!strcmp(packet->name, "features") && !strcmp(xmlns, "http://etherx.jabber.org/streams")) { jabber_stream_features_parse(js, packet); } else if(!strcmp(packet->name, "stream:error") || (!strcmp(packet->name, "error") && !strcmp(xmlns, "http://etherx.jabber.org/streams"))) { jabber_stream_handle_error(js, packet); } else if(!strcmp(packet->name, "challenge")) { if(js->state == JABBER_STREAM_AUTHENTICATING) jabber_auth_handle_challenge(js, packet); } else if(!strcmp(packet->name, "success")) { if(js->state == JABBER_STREAM_AUTHENTICATING) jabber_auth_handle_success(js, packet); } else if(!strcmp(packet->name, "failure")) { if(js->state == JABBER_STREAM_AUTHENTICATING) jabber_auth_handle_failure(js, packet); } else if(!strcmp(packet->name, "proceed")) { if(js->state == JABBER_STREAM_AUTHENTICATING && !js->gsc) tls_init(js); } else { purple_debug(PURPLE_DEBUG_WARNING, "jabber", "Unknown packet: %s\n", packet->name); }}static int jabber_do_send(JabberStream *js, const char *data, int len){ int ret; if (js->gsc) ret = purple_ssl_write(js->gsc, data, len); else ret = write(js->fd, data, len); return ret;}static void jabber_send_cb(gpointer data, gint source, PurpleInputCondition cond){ JabberStream *js = data; int ret, writelen; writelen = purple_circ_buffer_get_max_read(js->write_buffer); if (writelen == 0) { purple_input_remove(js->writeh); js->writeh = 0; return; } ret = jabber_do_send(js, js->write_buffer->outptr, writelen); if (ret < 0 && errno == EAGAIN) return; else if (ret <= 0) { purple_connection_error(js->gc, _("Write error")); return; } purple_circ_buffer_mark_read(js->write_buffer, ret);}void jabber_send_raw(JabberStream *js, const char *data, int len){ int ret; /* because printing a tab to debug every minute gets old */ if(strcmp(data, "\t")) purple_debug(PURPLE_DEBUG_MISC, "jabber", "Sending%s: %s\n", js->gsc ? " (ssl)" : "", data); /* If we've got a security layer, we need to encode the data, * splitting it on the maximum buffer length negotiated */ purple_signal_emit(my_protocol, "jabber-sending-text", js->gc, &data); if (data == NULL) return; #ifdef HAVE_CYRUS_SASL if (js->sasl_maxbuf>0) { int pos; if (!js->gsc && js->fd<0) return; pos = 0; if (len == -1) len = strlen(data); while (pos < len) { int towrite; const char *out; unsigned olen; if ((len - pos) < js->sasl_maxbuf) towrite = len - pos; else towrite = js->sasl_maxbuf; sasl_encode(js->sasl, &data[pos], towrite, &out, &olen); pos += towrite; if (js->writeh == 0) ret = jabber_do_send(js, out, olen); else { ret = -1; errno = EAGAIN; } if (ret < 0 && errno != EAGAIN) purple_connection_error(js->gc, _("Write error")); else if (ret < olen) { if (ret < 0) ret = 0; if (js->writeh == 0) js->writeh = purple_input_add( js->gsc ? js->gsc->fd : js->fd, PURPLE_INPUT_WRITE, jabber_send_cb, js); purple_circ_buffer_append(js->write_buffer, out + ret, olen - ret); } } return; }#endif if (len == -1) len = strlen(data); if (js->writeh == 0) ret = jabber_do_send(js, data, len); else { ret = -1; errno = EAGAIN; } if (ret < 0 && errno != EAGAIN) purple_connection_error(js->gc, _("Write error")); else if (ret < len) { if (ret < 0) ret = 0; if (js->writeh == 0) js->writeh = purple_input_add( js->gsc ? js->gsc->fd : js->fd, PURPLE_INPUT_WRITE, jabber_send_cb, js); purple_circ_buffer_append(js->write_buffer, data + ret, len - ret); } return;}int jabber_prpl_send_raw(PurpleConnection *gc, const char *buf, int len){ JabberStream *js = (JabberStream*)gc->proto_data; jabber_send_raw(js, buf, len); return len;}void jabber_send(JabberStream *js, xmlnode *packet){ char *txt; int len; purple_signal_emit(my_protocol, "jabber-sending-xmlnode", js->gc, &packet); /* if we get NULL back, we're done processing */ if(NULL == packet) return; txt = xmlnode_to_str(packet, &len); jabber_send_raw(js, txt, len); g_free(txt);}void jabber_keepalive(PurpleConnection *gc){ jabber_send_raw(gc->proto_data, "\t", -1);}static voidjabber_recv_cb_ssl(gpointer data, PurpleSslConnection *gsc, PurpleInputCondition cond){ PurpleConnection *gc = data; JabberStream *js = gc->proto_data; int len; static char buf[4096]; /* TODO: It should be possible to make this check unnecessary */ if(!PURPLE_CONNECTION_IS_VALID(gc)) { purple_ssl_close(gsc); return; } while((len = purple_ssl_read(gsc, buf, sizeof(buf) - 1)) > 0) { buf[len] = '\0'; purple_debug(PURPLE_DEBUG_INFO, "jabber", "Recv (ssl)(%d): %s\n", len, buf); jabber_parser_process(js, buf, len); if(js->reinit) jabber_stream_init(js); } if(errno == EAGAIN) return; else purple_connection_error(gc, _("Read Error"));}static voidjabber_recv_cb(gpointer data, gint source, PurpleInputCondition condition){ PurpleConnection *gc = data; JabberStream *js = gc->proto_data; int len; static char buf[4096]; if(!PURPLE_CONNECTION_IS_VALID(gc)) return; if((len = read(js->fd, buf, sizeof(buf) - 1)) > 0) {#ifdef HAVE_CYRUS_SASL if (js->sasl_maxbuf>0) { const char *out; unsigned int olen; sasl_decode(js->sasl, buf, len, &out, &olen); if (olen>0) { purple_debug(PURPLE_DEBUG_INFO, "jabber", "RecvSASL (%u): %s\n", olen, out); jabber_parser_process(js,out,olen); if(js->reinit) jabber_stream_init(js); } return; }#endif buf[len] = '\0'; purple_debug(PURPLE_DEBUG_INFO, "jabber", "Recv (%d): %s\n", len, buf); jabber_parser_process(js, buf, len); if(js->reinit) jabber_stream_init(js); } else if(errno == EAGAIN) { return; } else { purple_connection_error(gc, _("Read Error")); }}static voidjabber_login_callback_ssl(gpointer data, PurpleSslConnection *gsc, PurpleInputCondition cond){ PurpleConnection *gc = data; JabberStream *js; /* TODO: It should be possible to make this check unnecessary */ if(!PURPLE_CONNECTION_IS_VALID(gc)) { purple_ssl_close(gsc); return; } js = gc->proto_data; if(js->state == JABBER_STREAM_CONNECTING) jabber_send_raw(js, "<?xml version='1.0' ?>", -1); jabber_stream_set_state(js, JABBER_STREAM_INITIALIZING); purple_ssl_input_add(gsc, jabber_recv_cb_ssl, gc);}static voidjabber_login_callback(gpointer data, gint source, const gchar *error){ PurpleConnection *gc = data; JabberStream *js = gc->proto_data; if (source < 0) { gchar *tmp; tmp = g_strdup_printf(_("Could not establish a connection with the server:\n%s"), error); purple_connection_error(gc, tmp); g_free(tmp); return; }
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -