📄 zephyr.c
字号:
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- *//* * purple * * Copyright (C) 1998-2001, Mark Spencer <markster@marko.net> * Some code borrowed from GtkZephyr, by * Jag/Sean Dilda <agrajag@linuxpower.org>/<smdilda@unity.ncsu.edu> * http://gtkzephyr.linuxpower.org/ * * Some code borrowed from kzephyr, by * Chris Colohan <colohan+@cs.cmu.edu> * * 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 "libpurple/internal.h"#include "accountopt.h"#include "debug.h"#include "notify.h"#include "prpl.h"#include "server.h"#include "util.h"#include "cmds.h"#include "privacy.h"#include "version.h"#include "zephyr.h"#include "internal.h"#include <strings.h>#define ZEPHYR_FALLBACK_CHARSET "ISO-8859-1"/* these are deliberately high, since most people don't send multiple "PING"s */#define ZEPHYR_TYPING_SEND_TIMEOUT 15#define ZEPHYR_TYPING_RECV_TIMEOUT 10#define ZEPHYR_FD_READ 0#define ZEPHYR_FD_WRITE 1extern Code_t ZGetLocations(ZLocations_t *, int *);extern Code_t ZSetLocation(char *);extern Code_t ZUnsetLocation();extern Code_t ZGetSubscriptions(ZSubscription_t *, int*);extern char __Zephyr_realm[];typedef struct _zframe zframe;typedef struct _zephyr_triple zephyr_triple;typedef struct _zephyr_account zephyr_account;typedef struct _parse_tree parse_tree;typedef enum { PURPLE_ZEPHYR_NONE, /* Non-kerberized ZEPH0.2 */ PURPLE_ZEPHYR_KRB4, /* ZEPH0.2 w/ KRB4 support */ PURPLE_ZEPHYR_TZC, /* tzc executable proxy */ PURPLE_ZEPHYR_INTERGALACTIC_KRB4, /* Kerberized ZEPH0.3 */} zephyr_connection_type;struct _zephyr_account { PurpleAccount* account; char *username; char *realm; char *encoding; char* galaxy; /* not yet useful */ char* krbtkfile; /* not yet useful */ guint32 nottimer; guint32 loctimer; GList *pending_zloc_names; GSList *subscrips; int last_id; unsigned short port; char ourhost[HOST_NAME_MAX + 1]; char ourhostcanon[HOST_NAME_MAX + 1]; zephyr_connection_type connection_type; int totzc[2]; int fromtzc[2]; char *exposure; pid_t tzc_pid; gchar *away;};#define MAXCHILDREN 20struct _parse_tree { gchar* contents; parse_tree *children[MAXCHILDREN]; int num_children;};parse_tree null_parse_tree = { "", {NULL}, 0,};#define use_none(zephyr) ((zephyr->connection_type == PURPLE_ZEPHYR_NONE)?1:0)#define use_krb4(zephyr) ((zephyr->connection_type == PURPLE_ZEPHYR_KRB4)?1:0)#define use_tzc(zephyr) ((zephyr->connection_type == PURPLE_ZEPHYR_TZC)?1:0)#define use_zeph02(zephyr) ( (zephyr->connection_type == PURPLE_ZEPHYR_NONE)?1: ((zephyr->connection_type == PURPLE_ZEPHYR_KRB4)?1:0))/* struct I need for zephyr_to_html */struct _zframe { /* true for everything but @color, since inside the parens of that one is * the color. */ gboolean has_closer; /* @i, @b, etc. */ const char *env; /* }=1, ]=2, )=4, >=8 */ int closer_mask; /* }, ], ), > */ char *closer; /* </i>, </font>, </b>, etc. */ const char *closing; /* text including the opening html thingie. */ GString *text; /* href for links */ gboolean is_href; GString *href; struct _zframe *enclosing;};struct _zephyr_triple { char *class; char *instance; char *recipient; char *name; gboolean open; int id;};#define z_call(func) if (func != ZERR_NONE)\ return;#define z_call_r(func) if (func != ZERR_NONE)\ return TRUE;#define z_call_s(func, err) if (func != ZERR_NONE) {\ purple_connection_error(gc, err);\ return;\ }#ifdef WIN32extern const char *username;#endifstatic Code_t zephyr_subscribe_to(zephyr_account* zephyr, char* class, char *instance, char *recipient, char* galaxy) { if (use_tzc(zephyr)) { /* ((tzcfodder . subscribe) ("class" "instance" "recipient")) */ gchar *zsubstr = g_strdup_printf("((tzcfodder . subscribe) (\"%s\" \"%s\" \"%s\"))\n",class,instance,recipient); write(zephyr->totzc[ZEPHYR_FD_WRITE],zsubstr,strlen(zsubstr)); g_free(zsubstr); return ZERR_NONE; } else { if (use_zeph02(zephyr)) { ZSubscription_t sub; sub.zsub_class = class; sub.zsub_classinst = instance; sub.zsub_recipient = recipient; return ZSubscribeTo(&sub,1,0); } else { /* This should not happen */ return -1; } } return -1;}char *local_zephyr_normalize(zephyr_account* zephyr,const char *);static void zephyr_chat_set_topic(PurpleConnection * gc, int id, const char *topic);char* zephyr_tzc_deescape_str(const char *message);static char *zephyr_strip_local_realm(zephyr_account* zephyr,const char* user){ /* Takes in a username of the form username or username@realm and returns: username, if there is no realm, or the realm is the local realm or: username@realm if there is a realm and it is foreign */ char *tmp = g_strdup(user); char *at = strchr(tmp,'@'); if (at && !g_ascii_strcasecmp(at+1,zephyr->realm)) { /* We're passed in a username of the form user@users-realm */ char* tmp2; *at = '\0'; tmp2 = g_strdup(tmp); g_free(tmp); return tmp2; } else { /* We're passed in a username of the form user or user@foreign-realm */ return tmp; }}/* this is so bad, and if Zephyr weren't so fucked up to begin with I * wouldn't do this. but it is so i will. *//* just for debugging */static void handle_unknown(ZNotice_t notice){ purple_debug_error("zephyr","z_packet: %s\n", notice.z_packet); purple_debug_error("zephyr","z_version: %s\n", notice.z_version); purple_debug_error("zephyr","z_kind: %d\n", (int)(notice.z_kind)); purple_debug_error("zephyr","z_class: %s\n", notice.z_class); purple_debug_error("zephyr","z_class_inst: %s\n", notice.z_class_inst); purple_debug_error("zephyr","z_opcode: %s\n", notice.z_opcode); purple_debug_error("zephyr","z_sender: %s\n", notice.z_sender); purple_debug_error("zephyr","z_recipient: %s\n", notice.z_recipient); purple_debug_error("zephyr","z_message: %s\n", notice.z_message); purple_debug_error("zephyr","z_message_len: %d\n", notice.z_message_len);}static zephyr_triple *new_triple(zephyr_account *zephyr,const char *c, const char *i, const char *r){ zephyr_triple *zt; zt = g_new0(zephyr_triple, 1); zt->class = g_strdup(c); zt->instance = g_strdup(i); zt->recipient = g_strdup(r); zt->name = g_strdup_printf("%s,%s,%s", c, i?i:"", r?r:""); zt->id = ++(zephyr->last_id); zt->open = FALSE; return zt;}static void free_triple(zephyr_triple * zt){ g_free(zt->class); g_free(zt->instance); g_free(zt->recipient); g_free(zt->name); g_free(zt);}/* returns true if zt1 is a subset of zt2. This function is used to determine whether a zephyr sent to zt1 should be placed in the chat with triple zt2 zt1 is a subset of zt2 iff. the classnames are identical ignoring case AND. the instance names are identical (ignoring case), or zt2->instance is *. AND. the recipient names are identical*/static gboolean triple_subset(zephyr_triple * zt1, zephyr_triple * zt2){ if (!zt2) { purple_debug_error("zephyr","zt2 doesn't exist\n"); return FALSE; } if (!zt1) { purple_debug_error("zephyr","zt1 doesn't exist\n"); return FALSE; } if (!(zt1->class)) { purple_debug_error("zephyr","zt1c doesn't exist\n"); return FALSE; } if (!(zt1->instance)) { purple_debug_error("zephyr","zt1i doesn't exist\n"); return FALSE; } if (!(zt1->recipient)) { purple_debug_error("zephyr","zt1r doesn't exist\n"); return FALSE; } if (!(zt2->class)) { purple_debug_error("zephyr","zt2c doesn't exist\n"); return FALSE; } if (!(zt2->recipient)) { purple_debug_error("zephyr","zt2r doesn't exist\n"); return FALSE; } if (!(zt2->instance)) { purple_debug_error("zephyr","zt2i doesn't exist\n"); return FALSE; } if (g_ascii_strcasecmp(zt2->class, zt1->class)) { return FALSE; } if (g_ascii_strcasecmp(zt2->instance, zt1->instance) && g_ascii_strcasecmp(zt2->instance, "*")) { return FALSE; } if (g_ascii_strcasecmp(zt2->recipient, zt1->recipient)) { return FALSE; } purple_debug_info("zephyr","<%s,%s,%s> is in <%s,%s,%s>\n",zt1->class,zt1->instance,zt1->recipient,zt2->class,zt2->instance,zt2->recipient); return TRUE;}static zephyr_triple *find_sub_by_triple(zephyr_account *zephyr,zephyr_triple * zt){ zephyr_triple *curr_t; GSList *curr = zephyr->subscrips; while (curr) { curr_t = curr->data; if (triple_subset(zt, curr_t)) return curr_t; curr = curr->next; } return NULL;}static zephyr_triple *find_sub_by_id(zephyr_account *zephyr,int id){ zephyr_triple *zt; GSList *curr = zephyr->subscrips; while (curr) { zt = curr->data; if (zt->id == id) return zt; curr = curr->next; } return NULL;}/* Converts strings to utf-8 if necessary using user specified encoding*/static gchar *zephyr_recv_convert(PurpleConnection *gc,gchar *string, int len){ gchar *utf8; GError *err = NULL; zephyr_account *zephyr = gc->proto_data; if (g_utf8_validate(string, len, NULL)) { return g_strdup(string); } else { utf8 = g_convert(string, len, "UTF-8", zephyr->encoding, NULL, NULL, &err); if (err) { purple_debug_error("zephyr", "recv conversion error: %s\n", err->message); utf8 = g_strdup(_("(There was an error converting this message. Check the 'Encoding' option in the Account Editor)")); g_error_free(err); } return utf8; }}/* This parses HTML formatting (put out by one of the gtkimhtml widgets And converts it to zephyr formatting. It currently deals properly with <b>, <br>, <i>, <font face=...>, <font color=...>, It ignores <font back=...> It does <font size = "1 or 2" -> @small 3 or 4 @medium() 5,6, or 7 @large() <a href is dealt with by outputting "description <link>" or just "description" as appropriate*/static char *html_to_zephyr(const char *message){ zframe *frames, *new_f; char *ret; if (*message == '\0') return g_strdup(""); frames = g_new(zframe, 1); frames->text = g_string_new(""); frames->href = NULL; frames->is_href = FALSE; frames->enclosing = NULL; frames->closing = NULL; frames->env = ""; frames->has_closer = FALSE; frames->closer_mask = 15; purple_debug_info("zephyr","html received %s\n",message); while (*message) { if (frames->closing && !g_ascii_strncasecmp(message, frames->closing, strlen(frames->closing))) { zframe *popped; message += strlen(frames->closing); popped = frames; frames = frames->enclosing; if (popped->is_href) { frames->href = popped->text; } else { g_string_append(frames->text, popped->env); if (popped->has_closer) { g_string_append_c(frames->text, (popped->closer_mask & 1) ? '{' : (popped->closer_mask & 2) ? '[' : (popped->closer_mask & 4) ? '(' : '<'); } g_string_append(frames->text, popped->text->str); if (popped->href) { int text_len = strlen(popped->text->str), href_len = strlen(popped->href->str); if (!((text_len == href_len && !strncmp(popped->href->str, popped->text->str, text_len)) || (7 + text_len == href_len && !strncmp(popped->href->str, "http://", 7) && !strncmp(popped->href->str + 7, popped->text->str, text_len)) || (7 + text_len == href_len && !strncmp(popped->href->str, "mailto:", 7) && !strncmp(popped->href->str + 7, popped->text->str, text_len)))) { g_string_append(frames->text, " <"); g_string_append(frames->text, popped->href->str); if (popped->closer_mask & ~8) { g_string_append_c(frames->text, '>'); popped->closer_mask &= ~8; } else { g_string_append(frames->text, "@{>}"); } } g_string_free(popped->href, TRUE); } if (popped->has_closer) { g_string_append_c(frames->text, (popped->closer_mask & 1) ? '}' : (popped->closer_mask & 2) ? ']' : (popped->closer_mask & 4) ? ')' : '>'); } if (!popped->has_closer) frames->closer_mask = popped->closer_mask; g_string_free(popped->text, TRUE); } g_free(popped); } else if (*message == '<') { if (!g_ascii_strncasecmp(message + 1, "i>", 2)) { new_f = g_new(zframe, 1); new_f->enclosing = frames; new_f->text = g_string_new(""); new_f->href = NULL; new_f->is_href = FALSE; new_f->closing = "</i>";
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -