📄 simple.c
字号:
/** * @file simple.c * * purple * * Copyright (C) 2005 Thomas Butter <butter@uni-mannheim.de> * * *** * Thanks to Google's Summer of Code Program and the helpful mentors * *** * * 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 "accountopt.h"#include "blist.h"#include "conversation.h"#include "dnsquery.h"#include "debug.h"#include "notify.h"#include "privacy.h"#include "prpl.h"#include "plugin.h"#include "util.h"#include "version.h"#include "network.h"#include "xmlnode.h"#include "simple.h"#include "sipmsg.h"#include "dnssrv.h"#include "ntlm.h"static char *gentag() { return g_strdup_printf("%04d%04d", rand() & 0xFFFF, rand() & 0xFFFF);}static char *genbranch() { return g_strdup_printf("z9hG4bK%04X%04X%04X%04X%04X", rand() & 0xFFFF, rand() & 0xFFFF, rand() & 0xFFFF, rand() & 0xFFFF, rand() & 0xFFFF);}static char *gencallid() { return g_strdup_printf("%04Xg%04Xa%04Xi%04Xm%04Xt%04Xb%04Xx%04Xx", rand() & 0xFFFF, rand() & 0xFFFF, rand() & 0xFFFF, rand() & 0xFFFF, rand() & 0xFFFF, rand() & 0xFFFF, rand() & 0xFFFF, rand() & 0xFFFF);}static const char *simple_list_icon(PurpleAccount *a, PurpleBuddy *b) { return "simple";}static void simple_keep_alive(PurpleConnection *gc) { struct simple_account_data *sip = gc->proto_data; if(sip->udp) { /* in case of UDP send a packet only with a 0 byte to remain in the NAT table */ gchar buf[2] = {0, 0}; purple_debug_info("simple", "sending keep alive\n"); sendto(sip->fd, buf, 1, 0, (struct sockaddr*)&sip->serveraddr, sizeof(struct sockaddr_in)); } return;}static gboolean process_register_response(struct simple_account_data *sip, struct sipmsg *msg, struct transaction *tc);static void send_notify(struct simple_account_data *sip, struct simple_watcher *);static void send_publish(struct simple_account_data *sip);static void do_notifies(struct simple_account_data *sip) { GSList *tmp = sip->watcher; purple_debug_info("simple", "do_notifies()\n"); if((sip->republish != -1) || sip->republish < time(NULL)) { if(purple_account_get_bool(sip->account, "dopublish", TRUE)) { send_publish(sip); } } while(tmp) { purple_debug_info("simple", "notifying %s\n", ((struct simple_watcher*)tmp->data)->name); send_notify(sip, tmp->data); tmp = tmp->next; }}static void simple_set_status(PurpleAccount *account, PurpleStatus *status) { PurpleStatusPrimitive primitive = purple_status_type_get_primitive(purple_status_get_type(status)); struct simple_account_data *sip = NULL; if (!purple_status_is_active(status)) return; if (account->gc) sip = account->gc->proto_data; if (sip) { g_free(sip->status); if (primitive == PURPLE_STATUS_AVAILABLE) sip->status = g_strdup("available"); else sip->status = g_strdup("busy"); do_notifies(sip); }}static struct sip_connection *connection_find(struct simple_account_data *sip, int fd) { struct sip_connection *ret = NULL; GSList *entry = sip->openconns; while(entry) { ret = entry->data; if(ret->fd == fd) return ret; entry = entry->next; } return NULL;}static struct simple_watcher *watcher_find(struct simple_account_data *sip, const gchar *name) { struct simple_watcher *watcher; GSList *entry = sip->watcher; while(entry) { watcher = entry->data; if(!strcmp(name, watcher->name)) return watcher; entry = entry->next; } return NULL;}static struct simple_watcher *watcher_create(struct simple_account_data *sip, const gchar *name, const gchar *callid, const gchar *ourtag, const gchar *theirtag, gboolean needsxpidf) { struct simple_watcher *watcher = g_new0(struct simple_watcher, 1); watcher->name = g_strdup(name); watcher->dialog.callid = g_strdup(callid); watcher->dialog.ourtag = g_strdup(ourtag); watcher->dialog.theirtag = g_strdup(theirtag); watcher->needsxpidf = needsxpidf; sip->watcher = g_slist_append(sip->watcher, watcher); return watcher;}static void watcher_remove(struct simple_account_data *sip, const gchar *name) { struct simple_watcher *watcher = watcher_find(sip, name); sip->watcher = g_slist_remove(sip->watcher, watcher); g_free(watcher->name); g_free(watcher->dialog.callid); g_free(watcher->dialog.ourtag); g_free(watcher->dialog.theirtag); g_free(watcher);}static struct sip_connection *connection_create(struct simple_account_data *sip, int fd) { struct sip_connection *ret = g_new0(struct sip_connection, 1); ret->fd = fd; sip->openconns = g_slist_append(sip->openconns, ret); return ret;}static void connection_remove(struct simple_account_data *sip, int fd) { struct sip_connection *conn = connection_find(sip, fd); sip->openconns = g_slist_remove(sip->openconns, conn); if(conn->inputhandler) purple_input_remove(conn->inputhandler); g_free(conn->inbuf); g_free(conn);}static void connection_free_all(struct simple_account_data *sip) { struct sip_connection *ret = NULL; GSList *entry = sip->openconns; while(entry) { ret = entry->data; connection_remove(sip, ret->fd); entry = sip->openconns; }}static void simple_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group){ struct simple_account_data *sip = (struct simple_account_data *)gc->proto_data; struct simple_buddy *b; if(strncmp("sip:", buddy->name, 4)) { gchar *buf = g_strdup_printf("sip:%s", buddy->name); purple_blist_rename_buddy(buddy, buf); g_free(buf); } if(!g_hash_table_lookup(sip->buddies, buddy->name)) { b = g_new0(struct simple_buddy, 1); purple_debug_info("simple", "simple_add_buddy %s\n", buddy->name); b->name = g_strdup(buddy->name); g_hash_table_insert(sip->buddies, b->name, b); } else { purple_debug_info("simple", "buddy %s already in internal list\n", buddy->name); }}static void simple_get_buddies(PurpleConnection *gc) { PurpleBlistNode *gnode, *cnode, *bnode; purple_debug_info("simple", "simple_get_buddies\n"); for(gnode = purple_get_blist()->root; gnode; gnode = gnode->next) { if(!PURPLE_BLIST_NODE_IS_GROUP(gnode)) continue; for(cnode = gnode->child; cnode; cnode = cnode->next) { if(!PURPLE_BLIST_NODE_IS_CONTACT(cnode)) continue; for(bnode = cnode->child; bnode; bnode = bnode->next) { if(!PURPLE_BLIST_NODE_IS_BUDDY(bnode)) continue; if(((PurpleBuddy*)bnode)->account == gc->account) simple_add_buddy(gc, (PurpleBuddy*)bnode, (PurpleGroup *)gnode); } } }}static void simple_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group){ struct simple_account_data *sip = (struct simple_account_data *)gc->proto_data; struct simple_buddy *b = g_hash_table_lookup(sip->buddies, buddy->name); g_hash_table_remove(sip->buddies, buddy->name); g_free(b->name); g_free(b);}static GList *simple_status_types(PurpleAccount *acc) { PurpleStatusType *type; GList *types = NULL; type = purple_status_type_new_with_attrs( PURPLE_STATUS_AVAILABLE, NULL, NULL, TRUE, TRUE, FALSE, "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING), NULL); types = g_list_append(types, type); type = purple_status_type_new_full( PURPLE_STATUS_OFFLINE, NULL, NULL, TRUE, TRUE, FALSE); types = g_list_append(types, type); return types;}static gchar *auth_header(struct simple_account_data *sip, struct sip_auth *auth, const gchar *method, const gchar *target) { gchar noncecount[9]; gchar *response; gchar *ret; gchar *tmp; const char *authdomain; const char *authuser; authdomain = purple_account_get_string(sip->account, "authdomain", ""); authuser = purple_account_get_string(sip->account, "authuser", sip->username); if(!authuser || strlen(authuser) < 1) { authuser = sip->username; } if(auth->type == 1) { /* Digest */ sprintf(noncecount, "%08d", auth->nc++); response = purple_cipher_http_digest_calculate_response( "md5", method, target, NULL, NULL, auth->nonce, noncecount, NULL, auth->digest_session_key); purple_debug(PURPLE_DEBUG_MISC, "simple", "response %s\n", response); ret = g_strdup_printf("Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", uri=\"%s\", nc=\"%s\", response=\"%s\"", authuser, auth->realm, auth->nonce, target, noncecount, response); g_free(response); return ret; } else if(auth->type == 2) { /* NTLM */ if(auth->nc == 3 && auth->nonce) { /* TODO: Don't hardcode "purple" as the hostname */ ret = purple_ntlm_gen_type3(authuser, sip->password, "purple", authdomain, (const guint8 *)auth->nonce, &auth->flags); tmp = g_strdup_printf("NTLM qop=\"auth\", opaque=\"%s\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"%s\"", auth->opaque, auth->realm, auth->target, ret); g_free(ret); return tmp; } tmp = g_strdup_printf("NTLM qop=\"auth\", realm=\"%s\", targetname=\"%s\", gssapi-data=\"\"", auth->realm, auth->target); return tmp; } sprintf(noncecount, "%08d", auth->nc++); response = purple_cipher_http_digest_calculate_response( "md5", method, target, NULL, NULL, auth->nonce, noncecount, NULL, auth->digest_session_key); purple_debug(PURPLE_DEBUG_MISC, "simple", "response %s\n", response); ret = g_strdup_printf("Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", uri=\"%s\", nc=\"%s\", response=\"%s\"", authuser, auth->realm, auth->nonce, target, noncecount, response); g_free(response); return ret;}static char *parse_attribute(const char *attrname, const char *source) { const char *tmp, *tmp2; char *retval = NULL; int len = strlen(attrname); if(!strncmp(source, attrname, len)) { tmp = source + len; tmp2 = g_strstr_len(tmp, strlen(tmp), "\""); if(tmp2) retval = g_strndup(tmp, tmp2 - tmp); else retval = g_strdup(tmp); } return retval;}static void fill_auth(struct simple_account_data *sip, gchar *hdr, struct sip_auth *auth) { int i = 0; const char *authuser; char *tmp; gchar **parts; authuser = purple_account_get_string(sip->account, "authuser", sip->username); if(!authuser || strlen(authuser) < 1) { authuser = sip->username; } if(!hdr) { purple_debug_error("simple", "fill_auth: hdr==NULL\n"); return; } if(!g_strncasecmp(hdr, "NTLM", 4)) { purple_debug_info("simple", "found NTLM\n"); auth->type = 2; parts = g_strsplit(hdr+5, "\", ", 0); i = 0; while(parts[i]) { purple_debug_info("simple", "parts[i] %s\n", parts[i]); if((tmp = parse_attribute("gssapi-data=\"", parts[i]))) { auth->nonce = g_memdup(purple_ntlm_parse_type2(tmp, &auth->flags), 8); g_free(tmp); } if((tmp = parse_attribute("targetname=\"", parts[i]))) { auth->target = tmp; } else if((tmp = parse_attribute("realm=\"", parts[i]))) { auth->realm = tmp; } else if((tmp = parse_attribute("opaque=\"", parts[i]))) { auth->opaque = tmp; } i++; } g_strfreev(parts); auth->nc = 1; if(!strstr(hdr, "gssapi-data")) { auth->nc = 1; } else { auth->nc = 3; } return; } auth->type = 1; parts = g_strsplit(hdr, " ", 0); while(parts[i]) { if((tmp = parse_attribute("nonce=\"", parts[i]))) { auth->nonce = tmp; } else if((tmp = parse_attribute("realm=\"", parts[i]))) { auth->realm = tmp; } i++; } g_strfreev(parts); purple_debug(PURPLE_DEBUG_MISC, "simple", "nonce: %s realm: %s\n", auth->nonce ? auth->nonce : "(null)", auth->realm ? auth->realm : "(null)"); if(auth->realm) { auth->digest_session_key = purple_cipher_http_digest_calculate_session_key( "md5", authuser, auth->realm, sip->password, auth->nonce, NULL); auth->nc = 1; }}static void simple_canwrite_cb(gpointer data, gint source, PurpleInputCondition cond) { PurpleConnection *gc = data; struct simple_account_data *sip = gc->proto_data; gsize max_write; gssize written; max_write = purple_circ_buffer_get_max_read(sip->txbuf); if(max_write == 0) { purple_input_remove(sip->tx_handler); sip->tx_handler = 0; return; } written = write(sip->fd, sip->txbuf->outptr, max_write); if(written < 0 && errno == EAGAIN) written = 0; else if(written <= 0) { /*TODO: do we really want to disconnect on a failure to write?*/ purple_connection_error(gc, _("Could not write")); return; } purple_circ_buffer_mark_read(sip->txbuf, written);}static void simple_input_cb(gpointer data, gint source, PurpleInputCondition cond);static void send_later_cb(gpointer data, gint source, const gchar *error) { PurpleConnection *gc = data; struct simple_account_data *sip; struct sip_connection *conn; if (!PURPLE_CONNECTION_IS_VALID(gc)) { if (source >= 0) close(source); return; } if(source < 0) { purple_connection_error(gc, _("Could not connect")); return; } sip = gc->proto_data; sip->fd = source; sip->connecting = FALSE; simple_canwrite_cb(gc, sip->fd, PURPLE_INPUT_WRITE); /* If there is more to write now, we need to register a handler */ if(sip->txbuf->bufused > 0) sip->tx_handler = purple_input_add(sip->fd, PURPLE_INPUT_WRITE, simple_canwrite_cb, gc); conn = connection_create(sip, source); conn->inputhandler = purple_input_add(sip->fd, PURPLE_INPUT_READ, simple_input_cb, gc);}static void sendlater(PurpleConnection *gc, const char *buf) { struct simple_account_data *sip = gc->proto_data; if(!sip->connecting) { purple_debug_info("simple", "connecting to %s port %d\n", sip->realhostname ? sip->realhostname : "{NULL}", sip->realport); if (purple_proxy_connect(gc, sip->account, sip->realhostname, sip->realport, send_later_cb, gc) == NULL) { purple_connection_error(gc, _("Couldn't create socket")); } sip->connecting = TRUE; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -