14192
|
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
|
|
2 /*
|
|
3 * gaim
|
|
4 *
|
|
5 * Copyright (C) 1998-2001, Mark Spencer <markster@marko.net>
|
|
6 * Some code borrowed from GtkZephyr, by
|
|
7 * Jag/Sean Dilda <agrajag@linuxpower.org>/<smdilda@unity.ncsu.edu>
|
|
8 * http://gtkzephyr.linuxpower.org/
|
|
9 *
|
|
10 * Some code borrowed from kzephyr, by
|
|
11 * Chris Colohan <colohan+@cs.cmu.edu>
|
|
12 *
|
|
13 * This program is free software; you can redistribute it and/or modify
|
|
14 * it under the terms of the GNU General Public License as published by
|
|
15 * the Free Software Foundation; either version 2 of the License, or
|
|
16 * (at your option) any later version.
|
|
17 *
|
|
18 * This program is distributed in the hope that it will be useful,
|
|
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
21 * GNU General Public License for more details.
|
|
22 *
|
|
23 * You should have received a copy of the GNU General Public License
|
|
24 * along with this program; if not, write to the Free Software
|
|
25 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
26 *
|
|
27
|
|
28 */
|
|
29 /* XXX eww */
|
|
30 #include "core/internal.h"
|
|
31
|
|
32 #include "accountopt.h"
|
|
33 #include "debug.h"
|
|
34 #include "notify.h"
|
|
35 #include "prpl.h"
|
|
36 #include "server.h"
|
|
37 #include "util.h"
|
|
38 #include "cmds.h"
|
|
39 #include "privacy.h"
|
|
40 #include "version.h"
|
|
41
|
|
42 #include "zephyr.h"
|
|
43 #include "internal.h"
|
|
44
|
|
45 #include <strings.h>
|
|
46
|
|
47 #define ZEPHYR_FALLBACK_CHARSET "ISO-8859-1"
|
|
48
|
|
49 /* these are deliberately high, since most people don't send multiple "PING"s */
|
|
50 #define ZEPHYR_TYPING_SEND_TIMEOUT 15
|
|
51 #define ZEPHYR_TYPING_RECV_TIMEOUT 10
|
|
52 #define ZEPHYR_FD_READ 0
|
|
53 #define ZEPHYR_FD_WRITE 1
|
|
54
|
|
55 extern Code_t ZGetLocations(ZLocations_t *, int *);
|
|
56 extern Code_t ZSetLocation(char *);
|
|
57 extern Code_t ZUnsetLocation();
|
|
58 extern Code_t ZGetSubscriptions(ZSubscription_t *, int*);
|
|
59 extern char __Zephyr_realm[];
|
|
60 typedef struct _zframe zframe;
|
|
61 typedef struct _zephyr_triple zephyr_triple;
|
|
62 typedef struct _zephyr_account zephyr_account;
|
|
63 typedef struct _parse_tree parse_tree;
|
|
64
|
|
65 typedef enum {
|
|
66 GAIM_ZEPHYR_NONE, /* Non-kerberized ZEPH0.2 */
|
|
67 GAIM_ZEPHYR_KRB4, /* ZEPH0.2 w/ KRB4 support */
|
|
68 GAIM_ZEPHYR_TZC, /* tzc executable proxy */
|
|
69 GAIM_ZEPHYR_INTERGALACTIC_KRB4, /* Kerberized ZEPH0.3 */
|
|
70 } zephyr_connection_type;
|
|
71
|
|
72 struct _zephyr_account {
|
|
73 GaimAccount* account;
|
|
74 char *username;
|
|
75 char *realm;
|
|
76 char *encoding;
|
|
77 char* galaxy; /* not yet useful */
|
|
78 char* krbtkfile; /* not yet useful */
|
|
79 guint32 nottimer;
|
|
80 guint32 loctimer;
|
|
81 GList *pending_zloc_names;
|
|
82 GSList *subscrips;
|
|
83 int last_id;
|
|
84 unsigned short port;
|
|
85 char ourhost[HOST_NAME_MAX + 1];
|
|
86 char ourhostcanon[HOST_NAME_MAX + 1];
|
|
87 zephyr_connection_type connection_type;
|
|
88 int totzc[2];
|
|
89 int fromtzc[2];
|
|
90 char *exposure;
|
|
91 pid_t tzc_pid;
|
|
92 gchar *away;
|
|
93 };
|
|
94
|
|
95 #define MAXCHILDREN 20
|
|
96
|
|
97 struct _parse_tree {
|
|
98 gchar* contents;
|
|
99 parse_tree *children[MAXCHILDREN];
|
|
100 int num_children;
|
|
101 };
|
|
102
|
|
103 parse_tree null_parse_tree = {
|
|
104 "",
|
|
105 {NULL},
|
|
106 0,
|
|
107 };
|
|
108
|
|
109 #define use_none(zephyr) ((zephyr->connection_type == GAIM_ZEPHYR_NONE)?1:0)
|
|
110 #define use_krb4(zephyr) ((zephyr->connection_type == GAIM_ZEPHYR_KRB4)?1:0)
|
|
111 #define use_tzc(zephyr) ((zephyr->connection_type == GAIM_ZEPHYR_TZC)?1:0)
|
|
112
|
|
113 #define use_zeph02(zephyr) ( (zephyr->connection_type == GAIM_ZEPHYR_NONE)?1: ((zephyr->connection_type == GAIM_ZEPHYR_KRB4)?1:0))
|
|
114
|
|
115 /* struct I need for zephyr_to_html */
|
|
116 struct _zframe {
|
|
117 /* true for everything but @color, since inside the parens of that one is
|
|
118 * the color. */
|
|
119 gboolean has_closer;
|
|
120 /* </i>, </font>, </b>, etc. */
|
|
121 char *closing;
|
|
122 /* text including the opening html thingie. */
|
|
123 GString *text;
|
|
124 struct _zframe *enclosing;
|
|
125 };
|
|
126
|
|
127 struct _zephyr_triple {
|
|
128 char *class;
|
|
129 char *instance;
|
|
130 char *recipient;
|
|
131 char *name;
|
|
132 gboolean open;
|
|
133 int id;
|
|
134 };
|
|
135
|
|
136 #define z_call(func) if (func != ZERR_NONE)\
|
|
137 return;
|
|
138 #define z_call_r(func) if (func != ZERR_NONE)\
|
|
139 return TRUE;
|
|
140
|
|
141 #define z_call_s(func, err) if (func != ZERR_NONE) {\
|
|
142 gaim_connection_error(gc, err);\
|
|
143 return;\
|
|
144 }
|
|
145
|
|
146 #ifdef WIN32
|
|
147 extern const char *username;
|
|
148 #endif
|
|
149
|
|
150 static Code_t zephyr_subscribe_to(zephyr_account* zephyr, char* class, char *instance, char *recipient, char* galaxy) {
|
|
151
|
|
152 if (use_tzc(zephyr)) {
|
|
153 /* ((tzcfodder . subscribe) ("class" "instance" "recipient")) */
|
|
154 gchar *zsubstr = g_strdup_printf("((tzcfodder . subscribe) (\"%s\" \"%s\" \"%s\"))\n",class,instance,recipient);
|
|
155 write(zephyr->totzc[ZEPHYR_FD_WRITE],zsubstr,strlen(zsubstr));
|
|
156 g_free(zsubstr);
|
|
157 return ZERR_NONE;
|
|
158 }
|
|
159 else {
|
|
160 if (use_zeph02(zephyr)) {
|
|
161 ZSubscription_t sub;
|
|
162 sub.zsub_class = class;
|
|
163 sub.zsub_classinst = instance;
|
|
164 sub.zsub_recipient = recipient;
|
|
165 return ZSubscribeTo(&sub,1,0);
|
|
166 } else {
|
|
167 /* This should not happen */
|
|
168 return -1;
|
|
169 }
|
|
170 }
|
|
171 return -1;
|
|
172 }
|
|
173
|
|
174 char *local_zephyr_normalize(zephyr_account* zephyr,const char *);
|
|
175 static void zephyr_chat_set_topic(GaimConnection * gc, int id, const char *topic);
|
|
176 char* zephyr_tzc_deescape_str(const char *message);
|
|
177
|
|
178 static char *zephyr_strip_local_realm(zephyr_account* zephyr,const char* user){
|
|
179 /*
|
|
180 Takes in a username of the form username or username@realm
|
|
181 and returns:
|
|
182 username, if there is no realm, or the realm is the local realm
|
|
183 or:
|
|
184 username@realm if there is a realm and it is foreign
|
|
185 */
|
|
186 char *tmp = g_strdup(user);
|
|
187 char *at = strchr(tmp,'@');
|
|
188 if (at && !g_ascii_strcasecmp(at+1,zephyr->realm)) {
|
|
189 /* We're passed in a username of the form user@users-realm */
|
|
190 char* tmp2;
|
|
191 *at = '\0';
|
|
192 tmp2 = g_strdup(tmp);
|
|
193 g_free(tmp);
|
|
194 return tmp2;
|
|
195 }
|
|
196 else {
|
|
197 /* We're passed in a username of the form user or user@foreign-realm */
|
|
198 return tmp;
|
|
199 }
|
|
200 }
|
|
201
|
|
202 /* this is so bad, and if Zephyr weren't so fucked up to begin with I
|
|
203 * wouldn't do this. but it is so i will. */
|
|
204
|
|
205 /* just for debugging */
|
|
206 static void handle_unknown(ZNotice_t notice)
|
|
207 {
|
|
208 gaim_debug_error("zephyr","z_packet: %s\n", notice.z_packet);
|
|
209 gaim_debug_error("zephyr","z_version: %s\n", notice.z_version);
|
|
210 gaim_debug_error("zephyr","z_kind: %d\n", (int)(notice.z_kind));
|
|
211 gaim_debug_error("zephyr","z_class: %s\n", notice.z_class);
|
|
212 gaim_debug_error("zephyr","z_class_inst: %s\n", notice.z_class_inst);
|
|
213 gaim_debug_error("zephyr","z_opcode: %s\n", notice.z_opcode);
|
|
214 gaim_debug_error("zephyr","z_sender: %s\n", notice.z_sender);
|
|
215 gaim_debug_error("zephyr","z_recipient: %s\n", notice.z_recipient);
|
|
216 gaim_debug_error("zephyr","z_message: %s\n", notice.z_message);
|
|
217 gaim_debug_error("zephyr","z_message_len: %d\n", notice.z_message_len);
|
|
218 }
|
|
219
|
|
220
|
|
221 static zephyr_triple *new_triple(zephyr_account *zephyr,const char *c, const char *i, const char *r)
|
|
222 {
|
|
223 zephyr_triple *zt;
|
|
224
|
|
225 zt = g_new0(zephyr_triple, 1);
|
|
226 zt->class = g_strdup(c);
|
|
227 zt->instance = g_strdup(i);
|
|
228 zt->recipient = g_strdup(r);
|
|
229 zt->name = g_strdup_printf("%s,%s,%s", c, i?i:"", r?r:"");
|
|
230 zt->id = ++(zephyr->last_id);
|
|
231 zt->open = FALSE;
|
|
232 return zt;
|
|
233 }
|
|
234
|
|
235 static void free_triple(zephyr_triple * zt)
|
|
236 {
|
|
237 g_free(zt->class);
|
|
238 g_free(zt->instance);
|
|
239 g_free(zt->recipient);
|
|
240 g_free(zt->name);
|
|
241 g_free(zt);
|
|
242 }
|
|
243
|
|
244 /* returns true if zt1 is a subset of zt2. This function is used to
|
|
245 determine whether a zephyr sent to zt1 should be placed in the chat
|
|
246 with triple zt2
|
|
247
|
|
248 zt1 is a subset of zt2
|
|
249 iff. the classnames are identical ignoring case
|
|
250 AND. the instance names are identical (ignoring case), or zt2->instance is *.
|
|
251 AND. the recipient names are identical
|
|
252 */
|
|
253
|
|
254 static gboolean triple_subset(zephyr_triple * zt1, zephyr_triple * zt2)
|
|
255 {
|
|
256
|
|
257 if (!zt2) {
|
|
258 gaim_debug_error("zephyr","zt2 doesn't exist\n");
|
|
259 return FALSE;
|
|
260 }
|
|
261 if (!zt1) {
|
|
262 gaim_debug_error("zephyr","zt1 doesn't exist\n");
|
|
263 return FALSE;
|
|
264 }
|
|
265 if (!(zt1->class)) {
|
|
266 gaim_debug_error("zephyr","zt1c doesn't exist\n");
|
|
267 return FALSE;
|
|
268 }
|
|
269 if (!(zt1->instance)) {
|
|
270 gaim_debug_error("zephyr","zt1i doesn't exist\n");
|
|
271 return FALSE;
|
|
272 }
|
|
273 if (!(zt1->recipient)) {
|
|
274 gaim_debug_error("zephyr","zt1r doesn't exist\n");
|
|
275 return FALSE;
|
|
276 }
|
|
277 if (!(zt2->class)) {
|
|
278 gaim_debug_error("zephyr","zt2c doesn't exist\n");
|
|
279 return FALSE;
|
|
280 }
|
|
281 if (!(zt2->recipient)) {
|
|
282 gaim_debug_error("zephyr","zt2r doesn't exist\n");
|
|
283 return FALSE;
|
|
284 }
|
|
285 if (!(zt2->instance)) {
|
|
286 gaim_debug_error("zephyr","zt2i doesn't exist\n");
|
|
287 return FALSE;
|
|
288 }
|
|
289
|
|
290 if (g_ascii_strcasecmp(zt2->class, zt1->class)) {
|
|
291 return FALSE;
|
|
292 }
|
|
293 if (g_ascii_strcasecmp(zt2->instance, zt1->instance) && g_ascii_strcasecmp(zt2->instance, "*")) {
|
|
294 return FALSE;
|
|
295 }
|
|
296 if (g_ascii_strcasecmp(zt2->recipient, zt1->recipient)) {
|
|
297 return FALSE;
|
|
298 }
|
|
299 gaim_debug_info("zephyr","<%s,%s,%s> is in <%s,%s,%s>\n",zt1->class,zt1->instance,zt1->recipient,zt2->class,zt2->instance,zt2->recipient);
|
|
300 return TRUE;
|
|
301 }
|
|
302
|
|
303 static zephyr_triple *find_sub_by_triple(zephyr_account *zephyr,zephyr_triple * zt)
|
|
304 {
|
|
305 zephyr_triple *curr_t;
|
|
306 GSList *curr = zephyr->subscrips;
|
|
307
|
|
308 while (curr) {
|
|
309 curr_t = curr->data;
|
|
310 if (triple_subset(zt, curr_t))
|
|
311 return curr_t;
|
|
312 curr = curr->next;
|
|
313 }
|
|
314 return NULL;
|
|
315 }
|
|
316
|
|
317 static zephyr_triple *find_sub_by_id(zephyr_account *zephyr,int id)
|
|
318 {
|
|
319 zephyr_triple *zt;
|
|
320 GSList *curr = zephyr->subscrips;
|
|
321
|
|
322 while (curr) {
|
|
323 zt = curr->data;
|
|
324 if (zt->id == id)
|
|
325 return zt;
|
|
326 curr = curr->next;
|
|
327 }
|
|
328 return NULL;
|
|
329 }
|
|
330
|
|
331 /*
|
|
332 Converts strings to utf-8 if necessary using user specified encoding
|
|
333 */
|
|
334
|
|
335 static gchar *zephyr_recv_convert(GaimConnection *gc,gchar *string, int len)
|
|
336 {
|
|
337 gchar *utf8;
|
|
338 GError *err = NULL;
|
|
339 zephyr_account *zephyr = gc->proto_data;
|
|
340 if (g_utf8_validate(string, len, NULL)) {
|
|
341 return g_strdup(string);
|
|
342 } else {
|
|
343 utf8 = g_convert(string, len, "UTF-8", zephyr->encoding, NULL, NULL, &err);
|
|
344 if (err) {
|
|
345 gaim_debug_error("zephyr", "recv conversion error: %s\n", err->message);
|
|
346 utf8 = g_strdup(_("(There was an error converting this message. Check the 'Encoding' option in the Account Editor)"));
|
|
347 g_error_free(err);
|
|
348 }
|
|
349
|
|
350 return utf8;
|
|
351 }
|
|
352 }
|
|
353
|
|
354 /* utility macros that are useful for zephyr_to_html */
|
|
355
|
|
356 #define IS_OPENER(c) ((c == '{') || (c == '[') || (c == '(') || (c == '<'))
|
|
357 #define IS_CLOSER(c) ((c == '}') || (c == ']') || (c == ')') || (c == '>'))
|
|
358
|
|
359 /* This parses HTML formatting (put out by one of the gtkimhtml widgets
|
|
360 And converts it to zephyr formatting.
|
|
361 It currently deals properly with <b>, <br>, <i>, <font face=...>, <font color=...>,
|
|
362 It ignores <font back=...>
|
|
363 It does
|
|
364 <font size = "1 or 2" -> @small
|
|
365 3 or 4 @medium()
|
|
366 5,6, or 7 @large()
|
|
367 <a href is dealt with by ignoring the description and outputting the link
|
|
368 */
|
|
369
|
|
370 static char *html_to_zephyr(const char *message)
|
|
371 {
|
|
372 int len, cnt, retcount;
|
|
373 char *ret;
|
|
374
|
|
375 len = strlen(message);
|
|
376 if (!len)
|
|
377 return g_strdup("");
|
|
378
|
|
379 ret = g_new0(char, len * 3);
|
|
380
|
|
381 bzero(ret, len * 3);
|
|
382 retcount = 0;
|
|
383 cnt = 0;
|
|
384 gaim_debug_info("zephyr","html received %s\n",message);
|
|
385 while (cnt <= len) {
|
|
386 if (message[cnt] == '<') {
|
|
387 if (!g_ascii_strncasecmp(message + cnt + 1, "i>", 2)) {
|
|
388 strncpy(ret + retcount, "@i(", 3);
|
|
389 cnt += 3;
|
|
390 retcount += 3;
|
|
391 } else if (!g_ascii_strncasecmp(message + cnt + 1, "b>", 2)) {
|
|
392 strncpy(ret + retcount, "@b(", 3);
|
|
393 cnt += 3;
|
|
394 retcount += 3;
|
|
395 } else if (!g_ascii_strncasecmp(message + cnt + 1, "br>", 3)) {
|
|
396 strncpy(ret + retcount, "\n", 1);
|
|
397 cnt += 4;
|
|
398 retcount += 1;
|
|
399 } else if (!g_ascii_strncasecmp(message + cnt + 1, "a href=\"mailto:", 15)) {
|
|
400 cnt += 16;
|
|
401 while ((message[cnt] != '\0') && g_ascii_strncasecmp(message + cnt, "\">", 2) != 0) {
|
|
402 ret[retcount] = message[cnt];
|
|
403 retcount++;
|
|
404 cnt++;
|
|
405 }
|
|
406 if (message[cnt] != '\0')
|
|
407 cnt += 2;
|
|
408 /* ignore descriptive string */
|
|
409 while ((message[cnt] != '\0') && g_ascii_strncasecmp(message + cnt, "</a>", 4) != 0) {
|
|
410 cnt++;
|
|
411 }
|
|
412 if (message[cnt] != '\0')
|
|
413 cnt += 4;
|
|
414 } else if (!g_ascii_strncasecmp(message + cnt + 1, "a href=\"", 8)) {
|
|
415 cnt += 9;
|
|
416 while ((message[cnt] != '\0') && g_ascii_strncasecmp(message + cnt, "\">", 2) != 0) {
|
|
417 ret[retcount] = message[cnt];
|
|
418 retcount++;
|
|
419 cnt++;
|
|
420 }
|
|
421 if (message[cnt] != '\0')
|
|
422 cnt += 2;
|
|
423 /* ignore descriptive string */
|
|
424 while ((message[cnt] != '\0') && g_ascii_strncasecmp(message + cnt, "</a>", 4) != 0) {
|
|
425 cnt++;
|
|
426 }
|
|
427 if (message[cnt] != '\0')
|
|
428 cnt += 4;
|
|
429 } else if (!g_ascii_strncasecmp(message + cnt + 1, "font", 4)) {
|
|
430 cnt += 5;
|
|
431 while ((message[cnt] != '\0') && (message[cnt] != ' '))
|
|
432 cnt++;
|
|
433 if ((message[cnt] != '\0') && !g_ascii_strncasecmp(message + cnt, "color=\"", 7)) {
|
|
434 cnt += 7;
|
|
435 strncpy(ret + retcount, "@color(", 7);
|
|
436 retcount += 7;
|
|
437 while ((message[cnt] != '\0') && g_ascii_strncasecmp(message + cnt, "\">", 2) != 0) {
|
|
438 ret[retcount] = message[cnt];
|
|
439 retcount++;
|
|
440 cnt++;
|
|
441 }
|
|
442 ret[retcount] = ')';
|
|
443 retcount++;
|
|
444 if (message[cnt] != '\0')
|
|
445 cnt += 2;
|
|
446 } else if (!g_ascii_strncasecmp(message + cnt, "face=\"", 6)) {
|
|
447 cnt += 6;
|
|
448 strncpy(ret + retcount, "@font(", 6);
|
|
449 retcount += 6;
|
|
450 while ((message[cnt] != '\0') && g_ascii_strncasecmp(message + cnt, "\">", 2) != 0) {
|
|
451 ret[retcount] = message[cnt];
|
|
452 retcount++;
|
|
453 cnt++;
|
|
454 }
|
|
455 ret[retcount] = ')';
|
|
456 retcount++;
|
|
457 if (message[cnt] != '\0')
|
|
458 cnt += 2;
|
|
459 } else if (!g_ascii_strncasecmp(message + cnt, "size=\"", 6)) {
|
|
460 cnt += 6;
|
|
461 if ((message[cnt] == '1') || (message[cnt] == '2')) {
|
|
462 strncpy(ret + retcount, "@small(", 7);
|
|
463 retcount += 7;
|
|
464 } else if ((message[cnt] == '3')
|
|
465 || (message[cnt] == '4')) {
|
|
466 strncpy(ret + retcount, "@medium(", 8);
|
|
467 retcount += 8;
|
|
468 } else if ((message[cnt] == '5')
|
|
469 || (message[cnt] == '6')
|
|
470 || (message[cnt] == '7')) {
|
|
471 strncpy(ret + retcount, "@large(", 7);
|
|
472 retcount += 7;
|
|
473 }
|
|
474 cnt += 3;
|
|
475 } else {
|
|
476 /* Drop all unrecognized/misparsed font tags */
|
|
477 while ((message[cnt] != '\0') && g_ascii_strncasecmp(message + cnt, "\">", 2) != 0) {
|
|
478 cnt++;
|
|
479 }
|
|
480 if (message[cnt] != '\0')
|
|
481 cnt += 2;
|
|
482 }
|
|
483 } else if (!g_ascii_strncasecmp(message + cnt + 1, "/i>", 3)
|
|
484 || !g_ascii_strncasecmp(message + cnt + 1, "/b>", 3)) {
|
|
485 cnt += 4;
|
|
486 ret[retcount] = ')';
|
|
487 retcount++;
|
|
488 } else if (!g_ascii_strncasecmp(message + cnt + 1, "/font>", 6)) {
|
|
489 cnt += 7;
|
|
490 strncpy(ret + retcount, "@font(fixed)", 12);
|
|
491 retcount += 12;
|
|
492 } else {
|
|
493 /* Catch all for all unrecognized/misparsed <foo> tage */
|
|
494 while ((message[cnt] != '\0') && (message[cnt] != '>')) {
|
|
495 ret[retcount] = message[cnt];
|
|
496 retcount++;
|
|
497 cnt++;
|
|
498 }
|
|
499 }
|
|
500 } else {
|
|
501 /* Duh */
|
|
502 ret[retcount] = message[cnt];
|
|
503 retcount++;
|
|
504 cnt++;
|
|
505 }
|
|
506 }
|
|
507 gaim_debug_info("zephyr","zephyr outputted %s\n",ret);
|
|
508 return ret;
|
|
509 }
|
|
510
|
|
511 /* this parses zephyr formatting and converts it to html. For example, if
|
|
512 * you pass in "@{@color(blue)@i(hello)}" you should get out
|
|
513 * "<font color=blue><i>hello</i></font>". */
|
|
514 static char *zephyr_to_html(char *message)
|
|
515 {
|
|
516 int len, cnt;
|
|
517 zframe *frames, *curr;
|
|
518 char *ret;
|
|
519
|
|
520 frames = g_new(zframe, 1);
|
|
521 frames->text = g_string_new("");
|
|
522 frames->enclosing = NULL;
|
|
523 frames->closing = "";
|
|
524 frames->has_closer = FALSE;
|
|
525
|
|
526 len = strlen(message);
|
|
527 cnt = 0;
|
|
528 while (cnt <= len) {
|
|
529 if (message[cnt] == '@') {
|
|
530 zframe *new_f;
|
|
531 char *buf;
|
|
532 int end;
|
|
533
|
|
534 for (end = 1; (cnt + end) <= len && !IS_OPENER(message[cnt + end])
|
|
535 && !IS_CLOSER(message[cnt + end]); end++);
|
|
536 buf = g_new0(char, end);
|
|
537
|
|
538 if (end) {
|
|
539 g_snprintf(buf, end, "%s", message + cnt + 1);
|
|
540 }
|
|
541 if (!g_ascii_strcasecmp(buf, "italic") || !g_ascii_strcasecmp(buf, "i")) {
|
|
542 new_f = g_new(zframe, 1);
|
|
543 new_f->enclosing = frames;
|
|
544 new_f->text = g_string_new("<i>");
|
|
545 new_f->closing = "</i>";
|
|
546 new_f->has_closer = TRUE;
|
|
547 frames = new_f;
|
|
548 cnt += end + 1; /* cnt points to char after opener */
|
|
549 } else if (!g_ascii_strcasecmp(buf, "small")) {
|
|
550 new_f = g_new(zframe, 1);
|
|
551 new_f->enclosing = frames;
|
|
552 new_f->text = g_string_new("<font size=\"1\">");
|
|
553 new_f->closing = "</font>";
|
|
554 frames = new_f;
|
|
555 cnt += end + 1;
|
|
556 } else if (!g_ascii_strcasecmp(buf, "medium")) {
|
|
557 new_f = g_new(zframe, 1);
|
|
558 new_f->enclosing = frames;
|
|
559 new_f->text = g_string_new("<font size=\"3\">");
|
|
560 new_f->closing = "</font>";
|
|
561 frames = new_f;
|
|
562 cnt += end + 1;
|
|
563 } else if (!g_ascii_strcasecmp(buf, "large")) {
|
|
564 new_f = g_new(zframe, 1);
|
|
565 new_f->enclosing = frames;
|
|
566 new_f->text = g_string_new("<font size=\"7\">");
|
|
567 new_f->closing = "</font>";
|
|
568 frames = new_f;
|
|
569 cnt += end + 1;
|
|
570 } else if (!g_ascii_strcasecmp(buf, "bold")
|
|
571 || !g_ascii_strcasecmp(buf, "b")) {
|
|
572 new_f = g_new(zframe, 1);
|
|
573 new_f->enclosing = frames;
|
|
574 new_f->text = g_string_new("<b>");
|
|
575 new_f->closing = "</b>";
|
|
576 new_f->has_closer = TRUE;
|
|
577 frames = new_f;
|
|
578 cnt += end + 1;
|
|
579 } else if (!g_ascii_strcasecmp(buf, "font")) {
|
|
580 cnt += end + 1;
|
|
581 new_f = g_new(zframe, 1);
|
|
582 new_f->enclosing = frames;
|
|
583 new_f->text = g_string_new("<font face=");
|
|
584 for (; (cnt <= len) && !IS_CLOSER(message[cnt]); cnt++) {
|
|
585 g_string_append_c(new_f->text, message[cnt]);
|
|
586 }
|
|
587 cnt++; /* point to char after closer */
|
|
588 g_string_append_c(new_f->text, '>');
|
|
589 new_f->closing = "</font>";
|
|
590 new_f->has_closer = FALSE;
|
|
591 frames = new_f;
|
|
592 } else if (!g_ascii_strcasecmp(buf, "color")) {
|
|
593 cnt += end + 1;
|
|
594 new_f = g_new(zframe, 1);
|
|
595 new_f->enclosing = frames;
|
|
596 new_f->text = g_string_new("<font color=");
|
|
597 for (; (cnt <= len) && !IS_CLOSER(message[cnt]); cnt++) {
|
|
598 g_string_append_c(new_f->text, message[cnt]);
|
|
599 }
|
|
600 cnt++; /* point to char after closer */
|
|
601 g_string_append_c(new_f->text, '>');
|
|
602 new_f->closing = "</font>";
|
|
603 new_f->has_closer = FALSE;
|
|
604 frames = new_f;
|
|
605 } else if (!g_ascii_strcasecmp(buf, "")) {
|
|
606 new_f = g_new(zframe, 1);
|
|
607 new_f->enclosing = frames;
|
|
608 new_f->text = g_string_new("");
|
|
609 new_f->closing = "";
|
|
610 new_f->has_closer = TRUE;
|
|
611 frames = new_f;
|
|
612 cnt += end + 1; /* cnt points to char after opener */
|
|
613 } else {
|
|
614 if ((cnt + end) > len) {
|
|
615 g_string_append_c(frames->text, '@');
|
|
616 cnt++;
|
|
617 } else if (IS_CLOSER(message[cnt + end])) {
|
|
618 /* We have @chars..closer . This is
|
|
619 merely a sequence of chars that isn't a formatting tag
|
|
620 */
|
|
621 int tmp = cnt;
|
|
622
|
|
623 while (tmp <= cnt + end) {
|
|
624 g_string_append_c(frames->text, message[tmp]);
|
|
625 tmp++;
|
|
626 }
|
|
627 cnt += end + 1;
|
|
628 } else {
|
|
629 /* unrecognized thingie. act like it's not there, but we
|
|
630 * still need to take care of the corresponding closer,
|
|
631 * make a frame that does nothing. */
|
|
632 new_f = g_new(zframe, 1);
|
|
633 new_f->enclosing = frames;
|
|
634 new_f->text = g_string_new("");
|
|
635 new_f->closing = "";
|
|
636 new_f->has_closer = TRUE;
|
|
637 frames = new_f;
|
|
638 cnt += end + 1; /* cnt points to char after opener */
|
|
639 }
|
|
640 }
|
|
641 } else if (IS_CLOSER(message[cnt])) {
|
|
642 zframe *popped;
|
|
643 gboolean last_had_closer;
|
|
644
|
|
645 if (frames && frames->enclosing) {
|
|
646 do {
|
|
647 popped = frames;
|
|
648 frames = frames->enclosing;
|
|
649 g_string_append(frames->text, popped->text->str);
|
|
650 g_string_append(frames->text, popped->closing);
|
|
651 g_string_free(popped->text, TRUE);
|
|
652 last_had_closer = popped->has_closer;
|
|
653 g_free(popped);
|
|
654 } while (frames && frames->enclosing && !last_had_closer);
|
|
655 } else {
|
|
656 g_string_append_c(frames->text, message[cnt]);
|
|
657 }
|
|
658 cnt++;
|
|
659 } else if (message[cnt] == '\n') {
|
|
660 g_string_append(frames->text, "<br>");
|
|
661 cnt++;
|
|
662 } else {
|
|
663 g_string_append_c(frames->text, message[cnt++]);
|
|
664 }
|
|
665 }
|
|
666 /* go through all the stuff that they didn't close */
|
|
667 while (frames->enclosing) {
|
|
668 curr = frames;
|
|
669 g_string_append(frames->enclosing->text, frames->text->str);
|
|
670 g_string_append(frames->enclosing->text, frames->closing);
|
|
671 g_string_free(frames->text, TRUE);
|
|
672 frames = frames->enclosing;
|
|
673 g_free(curr);
|
|
674 }
|
|
675 ret = frames->text->str;
|
|
676 g_string_free(frames->text, FALSE);
|
|
677 g_free(frames);
|
|
678 return ret;
|
|
679 }
|
|
680
|
|
681 static gboolean pending_zloc(zephyr_account *zephyr,char *who)
|
|
682 {
|
|
683 GList *curr;
|
|
684
|
|
685 for (curr = zephyr->pending_zloc_names; curr != NULL; curr = curr->next) {
|
|
686 char* normalized_who = local_zephyr_normalize(zephyr,who);
|
|
687 if (!g_ascii_strcasecmp(normalized_who, (char *)curr->data)) {
|
|
688 g_free((char *)curr->data);
|
|
689 zephyr->pending_zloc_names = g_list_remove(zephyr->pending_zloc_names, curr->data);
|
|
690 return TRUE;
|
|
691 }
|
|
692 }
|
|
693 return FALSE;
|
|
694 }
|
|
695
|
|
696 /* Called when the server notifies us a message couldn't get sent */
|
|
697
|
|
698 static void message_failed(GaimConnection *gc, ZNotice_t notice, struct sockaddr_in from)
|
|
699 {
|
|
700 if (g_ascii_strcasecmp(notice.z_class, "message")) {
|
|
701 gchar* chat_failed = g_strdup_printf(_("Unable to send to chat %s,%s,%s"),notice.z_class,notice.z_class_inst,notice.z_recipient);
|
|
702 gaim_notify_error(gc,"",chat_failed,NULL);
|
|
703 g_free(chat_failed);
|
|
704 } else {
|
|
705 gaim_notify_error(gc, notice.z_recipient, _("User is offline"), NULL);
|
|
706 }
|
|
707 }
|
|
708
|
|
709 static void handle_message(GaimConnection *gc,ZNotice_t notice)
|
|
710 {
|
|
711 zephyr_account* zephyr = gc->proto_data;
|
|
712
|
|
713 if (!g_ascii_strcasecmp(notice.z_class, LOGIN_CLASS)) {
|
|
714 /* well, we'll be updating in 20 seconds anyway, might as well ignore this. */
|
|
715 } else if (!g_ascii_strcasecmp(notice.z_class, LOCATE_CLASS)) {
|
|
716 if (!g_ascii_strcasecmp(notice.z_opcode, LOCATE_LOCATE)) {
|
|
717 int nlocs;
|
|
718 char *user;
|
|
719 GaimBuddy *b;
|
|
720 /* XXX add real error reporting */
|
|
721 if (ZParseLocations(¬ice, NULL, &nlocs, &user) != ZERR_NONE)
|
|
722 return;
|
|
723
|
|
724 if ((b = gaim_find_buddy(gc->account, user)) == NULL) {
|
|
725 char* stripped_user = zephyr_strip_local_realm(zephyr,user);
|
|
726 b = gaim_find_buddy(gc->account,stripped_user);
|
|
727 g_free(stripped_user);
|
|
728 }
|
|
729 if ((b && pending_zloc(zephyr,b->name)) || pending_zloc(zephyr,user)) {
|
|
730 ZLocations_t locs;
|
|
731 int one = 1;
|
|
732 GString *str = g_string_new("");
|
|
733
|
|
734 g_string_append_printf(str, _("<b>User:</b> %s<br>"), b ? b->name : user);
|
|
735 if (b && b->alias)
|
|
736 g_string_append_printf(str, _("<b>Alias:</b> %s<br>"), b->alias);
|
|
737 if (!nlocs) {
|
|
738 g_string_append_printf(str, _("<br>Hidden or not logged-in"));
|
|
739 }
|
|
740 for (; nlocs > 0; nlocs--) {
|
|
741 /* XXX add real error reporting */
|
|
742 ZGetLocations(&locs, &one);
|
|
743 g_string_append_printf(str, _("<br>At %s since %s"), locs.host, locs.time);
|
|
744 }
|
|
745 gaim_notify_userinfo(gc, b ? b->name : user,
|
|
746 str->str, NULL, NULL);
|
|
747 g_string_free(str, TRUE);
|
|
748 } else {
|
|
749 if (nlocs>0)
|
|
750 gaim_prpl_got_user_status(gc->account, b ? b->name : user, "available", NULL);
|
|
751 else
|
|
752 gaim_prpl_got_user_status(gc->account, b ? b->name : user, "offline", NULL);
|
|
753 }
|
|
754
|
|
755 g_free(user);
|
|
756 }
|
|
757 } else {
|
|
758 char *buf, *buf2, *buf3;
|
|
759 char *send_inst;
|
|
760 GaimConversation *gconv1;
|
|
761 GaimConvChat *gcc;
|
|
762 char *ptr = (char *) notice.z_message + (strlen(notice.z_message) + 1);
|
|
763 int len;
|
|
764 char *sendertmp = g_strdup_printf("%s", zephyr->username);
|
|
765 int signature_length = strlen(notice.z_message);
|
|
766 int message_has_no_body = 0;
|
|
767 GaimMessageFlags flags = 0;
|
|
768 gchar *tmpescape;
|
|
769
|
|
770 /* Need to deal with 0 length messages to handle typing notification (OPCODE) ping messages */
|
|
771 /* One field zephyrs would have caused gaim to crash */
|
|
772 if ( (notice.z_message_len == 0) || (signature_length >= notice.z_message_len - 1)) {
|
|
773 message_has_no_body = 1;
|
|
774 len = 0;
|
|
775 gaim_debug_info("zephyr","message_size %d %d %d\n",len,notice.z_message_len,signature_length);
|
|
776 buf3 = g_strdup("");
|
|
777
|
|
778 } else {
|
|
779 len = notice.z_message_len - ( signature_length +1);
|
|
780 gaim_debug_info("zephyr","message_size %d %d %d\n",len,notice.z_message_len,signature_length);
|
|
781 buf = g_malloc(len + 1);
|
|
782 g_snprintf(buf, len + 1, "%s", ptr);
|
|
783 g_strchomp(buf);
|
|
784 tmpescape = g_markup_escape_text(buf, -1);
|
|
785 g_free(buf);
|
|
786 buf2 = zephyr_to_html(tmpescape);
|
|
787 buf3 = zephyr_recv_convert(gc,buf2, strlen(buf2));
|
|
788 g_free(buf2);
|
|
789 g_free(tmpescape);
|
|
790 }
|
|
791
|
|
792 if (!g_ascii_strcasecmp(notice.z_class, "MESSAGE") && !g_ascii_strcasecmp(notice.z_class_inst, "PERSONAL")
|
|
793 && !g_ascii_strcasecmp(notice.z_recipient,zephyr->username)) {
|
|
794 gchar* stripped_sender;
|
|
795 if (!g_ascii_strcasecmp(notice.z_message, "Automated reply:"))
|
|
796 flags |= GAIM_MESSAGE_AUTO_RESP;
|
|
797 stripped_sender = zephyr_strip_local_realm(zephyr,notice.z_sender);
|
|
798
|
|
799 if (!g_ascii_strcasecmp(notice.z_opcode,"PING"))
|
|
800 serv_got_typing(gc,stripped_sender,ZEPHYR_TYPING_RECV_TIMEOUT, GAIM_TYPING);
|
|
801 else {
|
|
802 /* Based on the values of
|
|
803 account->permit_deny,
|
|
804 account->permit, account>deny , and
|
|
805 the buddylist */
|
|
806
|
|
807 GSList* l;
|
|
808 gboolean in_deny;
|
|
809
|
|
810 switch (gc->account->perm_deny) {
|
|
811 case GAIM_PRIVACY_ALLOW_ALL:
|
|
812 in_deny = 0; break;
|
|
813 case GAIM_PRIVACY_DENY_ALL:
|
|
814 in_deny = 1; break;
|
|
815 case GAIM_PRIVACY_ALLOW_USERS: /* See if stripped_sender is in gc->account->permit and allow appropriately */
|
|
816 in_deny = 1;
|
|
817 for(l=gc->account->permit;l!=NULL;l=l->next) {
|
|
818 if (!gaim_utf8_strcasecmp(stripped_sender, gaim_normalize(gc->account, (char *)l->data))) {
|
|
819 in_deny=0;
|
|
820 break;
|
|
821 }
|
|
822 }
|
|
823 break;
|
|
824 case GAIM_PRIVACY_DENY_USERS: /* See if stripped_sender is in gc->account->deny and deny if so */
|
|
825 in_deny = 0;
|
|
826 for(l=gc->account->deny;l!=NULL;l=l->next) {
|
|
827 if (!gaim_utf8_strcasecmp(stripped_sender, gaim_normalize(gc->account, (char *)l->data))) {
|
|
828 in_deny=1;
|
|
829 break;
|
|
830 }
|
|
831 }
|
|
832 break;
|
|
833 case GAIM_PRIVACY_ALLOW_BUDDYLIST:
|
|
834 in_deny = 1;
|
|
835 if (gaim_find_buddy(gc->account,stripped_sender)!=NULL) {
|
|
836 in_deny = 0;
|
|
837 }
|
|
838 break;
|
|
839 default:
|
|
840 in_deny=0; break;
|
|
841 }
|
|
842
|
|
843 if (!in_deny) {
|
|
844 serv_got_im(gc, stripped_sender, buf3, flags, time(NULL));
|
|
845 }
|
|
846 }
|
|
847
|
|
848 g_free(stripped_sender);
|
|
849 } else {
|
|
850 zephyr_triple *zt1, *zt2;
|
|
851 gchar *send_inst_utf8;
|
|
852 zephyr_account *zephyr = gc->proto_data;
|
|
853 zt1 = new_triple(gc->proto_data,notice.z_class, notice.z_class_inst, notice.z_recipient);
|
|
854 zt2 = find_sub_by_triple(gc->proto_data,zt1);
|
|
855 if (!zt2) {
|
|
856 /* This is a server supplied subscription */
|
|
857 zephyr->subscrips = g_slist_append(zephyr->subscrips, new_triple(zephyr,zt1->class,zt1->instance,zt1->recipient));
|
|
858 zt2 = find_sub_by_triple(gc->proto_data,zt1);
|
|
859 }
|
|
860
|
|
861 if (!zt2->open) {
|
|
862 zt2->open = TRUE;
|
|
863 serv_got_joined_chat(gc, zt2->id, zt2->name);
|
|
864 zephyr_chat_set_topic(gc,zt2->id,notice.z_class_inst);
|
|
865 }
|
|
866 g_free(sendertmp); /* fix memory leak? */
|
|
867 /* If the person is in the default Realm, then strip the
|
|
868 Realm from the sender field */
|
|
869 sendertmp = zephyr_strip_local_realm(zephyr,notice.z_sender);
|
|
870 send_inst = g_strdup_printf("%s %s",sendertmp,notice.z_class_inst);
|
|
871 send_inst_utf8 = zephyr_recv_convert(gc,send_inst, strlen(send_inst));
|
|
872 if (!send_inst_utf8) {
|
|
873 gaim_debug_error("zephyr","send_inst %s became null\n", send_inst);
|
|
874 send_inst_utf8 = "malformed instance";
|
|
875 }
|
|
876
|
|
877 serv_got_chat_in(gc, zt2->id, send_inst_utf8, 0, buf3, time(NULL));
|
|
878 g_free(send_inst);
|
|
879 gconv1 = gaim_find_conversation_with_account(GAIM_CONV_TYPE_CHAT,
|
|
880 zt2->name, gc->account);
|
|
881 gcc = gaim_conversation_get_chat_data(gconv1);
|
|
882
|
|
883 if (!gaim_conv_chat_find_user(gcc, sendertmp)) {
|
|
884 /* force interpretation in network byte order */
|
|
885 unsigned char *addrs = (unsigned char *)&(notice.z_sender_addr.s_addr);
|
|
886 gchar* ipaddr = g_strdup_printf("%hhd.%hhd.%hhd.%hhd", (unsigned char)addrs[0],
|
|
887 (unsigned char)addrs[1], (unsigned char)addrs[2],
|
|
888 (unsigned char) addrs[3]);
|
|
889
|
|
890 gaim_conv_chat_add_user(gcc, sendertmp, ipaddr, GAIM_CBFLAGS_NONE, TRUE);
|
|
891 g_free(ipaddr); /* fix memory leak? */
|
|
892
|
|
893 }
|
|
894 g_free(sendertmp);
|
|
895 g_free(send_inst_utf8);
|
|
896
|
|
897 free_triple(zt1);
|
|
898 }
|
|
899 g_free(buf3);
|
|
900
|
|
901 }
|
|
902 }
|
|
903
|
|
904 static int free_parse_tree(parse_tree* tree) {
|
|
905 if (!tree) {
|
|
906 return 0;
|
|
907 }
|
|
908 else {
|
|
909 int i;
|
|
910 if (tree->children) {
|
|
911 for(i=0;i<tree->num_children;i++){
|
|
912 if (tree->children[i]) {
|
|
913 free_parse_tree(tree->children[i]);
|
|
914 g_free(tree->children[i]);
|
|
915 }
|
|
916 }
|
|
917 }
|
|
918 if ((tree != &null_parse_tree) && (tree->contents != NULL))
|
|
919 g_free(tree->contents);
|
|
920
|
|
921 }
|
|
922 return 0;
|
|
923 }
|
|
924
|
|
925 static parse_tree *tree_child(parse_tree* tree,int index) {
|
|
926 if (index < tree->num_children) {
|
|
927 return tree->children[index];
|
|
928 } else {
|
|
929 return &null_parse_tree;
|
|
930 }
|
|
931 }
|
|
932
|
|
933 static parse_tree *find_node(parse_tree* ptree,gchar* key)
|
|
934 {
|
|
935 gchar* tc;
|
|
936
|
|
937 if (!ptree || ! key)
|
|
938 return &null_parse_tree;
|
|
939
|
|
940 tc = tree_child(ptree,0)->contents;
|
|
941
|
|
942 if (ptree->num_children > 0 && tc && !strcasecmp(tc, key)) {
|
|
943 return ptree;
|
|
944 } else {
|
|
945 parse_tree *result = &null_parse_tree;
|
|
946 int i;
|
|
947 for(i = 0; i < ptree->num_children; i++) {
|
|
948 result = find_node(ptree->children[i],key);
|
|
949 if(result != &null_parse_tree) {
|
|
950 break;
|
|
951 }
|
|
952 }
|
|
953 return result;
|
|
954 }
|
|
955 }
|
|
956
|
|
957 static parse_tree *parse_buffer(gchar* source, gboolean do_parse) {
|
|
958
|
|
959 parse_tree *ptree = g_new0(parse_tree,1);
|
|
960 ptree->contents = NULL;
|
|
961 ptree->num_children=0;
|
|
962 if (do_parse) {
|
|
963 unsigned int p = 0;
|
|
964 while(p < strlen(source)) {
|
|
965 unsigned int end;
|
|
966 gchar *newstr;
|
|
967
|
|
968 /* Eat white space: */
|
|
969 if(g_ascii_isspace(source[p]) || source[p] == '\001') {
|
|
970 p++;
|
|
971 continue;
|
|
972 }
|
|
973
|
|
974 /* Skip comments */
|
|
975 if(source[p] == ';') {
|
|
976 while(source[p] != '\n' && p < strlen(source)) {
|
|
977 p++;
|
|
978 }
|
|
979 continue;
|
|
980 }
|
|
981
|
|
982 if(source[p] == '(') {
|
|
983 int nesting = 0;
|
|
984 gboolean in_quote = FALSE;
|
|
985 gboolean escape_next = FALSE;
|
|
986 p++;
|
|
987 end = p;
|
|
988 while(!(source[end] == ')' && nesting == 0 && !in_quote) && end < strlen(source)) {
|
|
989 if(!escape_next) {
|
|
990 if(source[end] == '\\') {
|
|
991 escape_next = TRUE;
|
|
992 }
|
|
993 if(!in_quote) {
|
|
994 if(source[end] == '(') {
|
|
995 nesting++;
|
|
996 }
|
|
997 if(source[end] == ')') {
|
|
998 nesting--;
|
|
999 }
|
|
1000 }
|
|
1001 if(source[end] == '"') {
|
|
1002 in_quote = !in_quote;
|
|
1003 }
|
|
1004 } else {
|
|
1005 escape_next = FALSE;
|
|
1006 }
|
|
1007 end++;
|
|
1008 }
|
|
1009 do_parse = TRUE;
|
|
1010
|
|
1011 } else {
|
|
1012 gchar end_char;
|
|
1013 if(source[p] == '"') {
|
|
1014 end_char = '"';
|
|
1015 p++;
|
|
1016 } else {
|
|
1017 end_char = ' ';
|
|
1018 }
|
|
1019 do_parse = FALSE;
|
|
1020
|
|
1021 end = p;
|
|
1022 while(source[end] != end_char && end < strlen(source)) {
|
|
1023 if(source[end] == '\\')
|
|
1024 end++;
|
|
1025 end++;
|
|
1026 }
|
|
1027 }
|
|
1028 newstr = g_new0(gchar, end+1-p);
|
|
1029 strncpy(newstr,source+p,end-p);
|
|
1030 if (ptree->num_children < MAXCHILDREN) {
|
|
1031 /* In case we surpass maxchildren, ignore this */
|
|
1032 ptree->children[ptree->num_children++] = parse_buffer( newstr, do_parse);
|
|
1033 } else {
|
|
1034 gaim_debug_error("zephyr","too many children in tzc output. skipping\n");
|
|
1035 }
|
|
1036 g_free(newstr);
|
|
1037 p = end + 1;
|
|
1038 }
|
|
1039 return ptree;
|
|
1040 } else {
|
|
1041 /* XXX does this have to be strdup'd */
|
|
1042 ptree->contents = g_strdup(source);
|
|
1043 return ptree;
|
|
1044 }
|
|
1045 }
|
|
1046
|
|
1047 static parse_tree *read_from_tzc(zephyr_account* zephyr){
|
|
1048 struct timeval tv;
|
|
1049 fd_set rfds;
|
|
1050 int bufsize = 2048;
|
|
1051 char *buf = (char *)calloc(bufsize, 1);
|
|
1052 char *bufcur = buf;
|
|
1053 int selected = 0;
|
|
1054 parse_tree *incoming_msg;
|
|
1055
|
|
1056 FD_ZERO(&rfds);
|
|
1057 FD_SET(zephyr->fromtzc[ZEPHYR_FD_READ], &rfds);
|
|
1058 tv.tv_sec = 0;
|
|
1059 tv.tv_usec = 0;
|
|
1060 incoming_msg=NULL;
|
|
1061
|
|
1062 while (select(zephyr->fromtzc[ZEPHYR_FD_READ] + 1, &rfds, NULL, NULL, &tv)) {
|
|
1063 selected = 1;
|
|
1064 read(zephyr->fromtzc[ZEPHYR_FD_READ], bufcur, 1);
|
|
1065 bufcur++;
|
|
1066 if ((bufcur - buf) > (bufsize - 1)) {
|
|
1067 if ((buf = realloc(buf, bufsize * 2)) == NULL) {
|
|
1068 gaim_debug_error("zephyr","Ran out of memory");
|
|
1069 exit(-1);
|
|
1070 } else {
|
|
1071 bufcur = buf + bufsize;
|
|
1072 bufsize *= 2;
|
|
1073 }
|
|
1074 }
|
|
1075 }
|
|
1076 *bufcur = '\0';
|
|
1077
|
|
1078 if (selected) {
|
|
1079 incoming_msg = parse_buffer(buf,TRUE);
|
|
1080 }
|
|
1081 free(buf);
|
|
1082 return incoming_msg;
|
|
1083 }
|
|
1084
|
|
1085 static gint check_notify_tzc(gpointer data)
|
|
1086 {
|
|
1087 GaimConnection *gc = (GaimConnection *)data;
|
|
1088 zephyr_account* zephyr = gc->proto_data;
|
|
1089 parse_tree *newparsetree = read_from_tzc(zephyr);
|
|
1090 if (newparsetree != NULL) {
|
|
1091 gchar *spewtype;
|
|
1092 if ( (spewtype = tree_child(find_node(newparsetree,"tzcspew"),2)->contents) ) {
|
|
1093 if (!g_ascii_strncasecmp(spewtype,"message",7)) {
|
|
1094 ZNotice_t notice;
|
|
1095 parse_tree *msgnode = tree_child(find_node(newparsetree,"message"),2);
|
|
1096 parse_tree *bodynode = tree_child(msgnode,1);
|
|
1097 /* char *zsig = g_strdup(" "); */ /* gaim doesn't care about zsigs */
|
|
1098 char *msg = zephyr_tzc_deescape_str(bodynode->contents);
|
|
1099 size_t bufsize = strlen(msg) + 3;
|
|
1100 char *buf = g_new0(char,bufsize);
|
|
1101 g_snprintf(buf,1+strlen(msg)+2," %c%s",'\0',msg);
|
|
1102 bzero((char *)¬ice, sizeof(notice));
|
|
1103 notice.z_kind = ACKED;
|
|
1104 notice.z_port = 0;
|
|
1105 notice.z_opcode = tree_child(find_node(newparsetree,"opcode"),2)->contents;
|
|
1106 notice.z_class = zephyr_tzc_deescape_str(tree_child(find_node(newparsetree,"class"),2)->contents);
|
|
1107 notice.z_class_inst = tree_child(find_node(newparsetree,"instance"),2)->contents;
|
|
1108 notice.z_recipient = local_zephyr_normalize(zephyr,tree_child(find_node(newparsetree,"recipient"),2)->contents);
|
|
1109 notice.z_sender = local_zephyr_normalize(zephyr,tree_child(find_node(newparsetree,"sender"),2)->contents);
|
|
1110 notice.z_default_format = "Class $class, Instance $instance:\n" "To: @bold($recipient) at $time $date\n" "From: @bold($1) <$sender>\n\n$2";
|
|
1111 notice.z_message_len = strlen(msg) + 3;
|
|
1112 notice.z_message = buf;
|
|
1113 handle_message(gc, notice);
|
|
1114 g_free(msg);
|
|
1115 /* g_free(zsig); */
|
|
1116 g_free(buf);
|
|
1117 /* free_parse_tree(msgnode);
|
|
1118 free_parse_tree(bodynode);
|
|
1119 g_free(msg);
|
|
1120 g_free(zsig);
|
|
1121 g_free(buf);
|
|
1122 */
|
|
1123 }
|
|
1124 else if (!g_ascii_strncasecmp(spewtype,"zlocation",9)) {
|
|
1125 /* check_loc or zephyr_zloc respectively */
|
|
1126 /* XXX fix */
|
|
1127 char *user;
|
|
1128 GaimBuddy *b;
|
|
1129 int nlocs = 0;
|
|
1130 parse_tree *locations;
|
|
1131 gchar *locval;
|
|
1132 user = tree_child(find_node(newparsetree,"user"),2)->contents;
|
|
1133
|
|
1134 if ((b = gaim_find_buddy(gc->account, user)) == NULL) {
|
|
1135 gchar *stripped_user = zephyr_strip_local_realm(zephyr,user);
|
|
1136 b = gaim_find_buddy(gc->account, stripped_user);
|
|
1137 g_free(stripped_user);
|
|
1138 }
|
|
1139 locations = find_node(newparsetree,"locations");
|
|
1140 locval = tree_child(tree_child(tree_child(tree_child(locations,2),0),0),2)->contents;
|
|
1141
|
|
1142 if (!locval || !g_ascii_strcasecmp(locval," ") || (strlen(locval) == 0)) {
|
|
1143 nlocs = 0;
|
|
1144 } else {
|
|
1145 nlocs = 1;
|
|
1146 }
|
|
1147
|
|
1148 if ((b && pending_zloc(zephyr,b->name)) || pending_zloc(zephyr,user) || pending_zloc(zephyr,local_zephyr_normalize(zephyr,user))){
|
|
1149 GString *str = g_string_new("");
|
|
1150
|
|
1151 g_string_append_printf(str, _("<b>User:</b> %s<br>"), b ? b->name : user);
|
|
1152 if (b && b->alias)
|
|
1153 g_string_append_printf(str, _("<b>Alias:</b> %s<br>"), b->alias);
|
|
1154
|
|
1155 if (!nlocs) {
|
|
1156 g_string_append_printf(str, _("<br>Hidden or not logged-in"));
|
|
1157 } else {
|
|
1158 g_string_append_printf(str, _("<br>At %s since %s"),
|
|
1159 tree_child(tree_child(tree_child(tree_child(locations,2),0),0),2)->contents,
|
|
1160 tree_child(tree_child(tree_child(tree_child(locations,2),0),2),2)->contents);
|
|
1161 }
|
|
1162
|
|
1163 gaim_notify_userinfo(gc, b ? b->name : user,
|
|
1164 str->str, NULL, NULL);
|
|
1165 g_string_free(str, TRUE);
|
|
1166 } else {
|
|
1167 if (nlocs>0)
|
|
1168 gaim_prpl_got_user_status(gc->account, b ? b->name : user, "available", NULL);
|
|
1169 else
|
|
1170 gaim_prpl_got_user_status(gc->account, b ? b->name : user, "offline", NULL);
|
|
1171 }
|
|
1172 }
|
|
1173 else if (!g_ascii_strncasecmp(spewtype,"subscribed",10)) {
|
|
1174 }
|
|
1175 else if (!g_ascii_strncasecmp(spewtype,"start",5)) {
|
|
1176 }
|
|
1177 else if (!g_ascii_strncasecmp(spewtype,"error",5)) {
|
|
1178 /* XXX handle */
|
|
1179 }
|
|
1180 } else {
|
|
1181 }
|
|
1182 } else {
|
|
1183 }
|
|
1184
|
|
1185 free_parse_tree(newparsetree);
|
|
1186 return TRUE;
|
|
1187 }
|
|
1188
|
|
1189 static gint check_notify_zeph02(gpointer data)
|
|
1190 {
|
|
1191 /* XXX add real error reporting */
|
|
1192 GaimConnection *gc = (GaimConnection*) data;
|
|
1193 while (ZPending()) {
|
|
1194 ZNotice_t notice;
|
|
1195 struct sockaddr_in from;
|
|
1196 /* XXX add real error reporting */
|
|
1197
|
|
1198 z_call_r(ZReceiveNotice(¬ice, &from));
|
|
1199
|
|
1200 switch (notice.z_kind) {
|
|
1201 case UNSAFE:
|
|
1202 case UNACKED:
|
|
1203 case ACKED:
|
|
1204 handle_message(gc,notice);
|
|
1205 break;
|
|
1206 case SERVACK:
|
|
1207 if (!(g_ascii_strcasecmp(notice.z_message, ZSRVACK_NOTSENT))) {
|
|
1208 message_failed(gc,notice, from);
|
|
1209 }
|
|
1210 break;
|
|
1211 case CLIENTACK:
|
|
1212 gaim_debug_error("zephyr", "Client ack received\n");
|
|
1213 default:
|
|
1214 /* we'll just ignore things for now */
|
|
1215 handle_unknown(notice);
|
|
1216 gaim_debug_error("zephyr", "Unhandled notice.\n");
|
|
1217 break;
|
|
1218 }
|
|
1219 /* XXX add real error reporting */
|
|
1220 ZFreeNotice(¬ice);
|
|
1221 }
|
|
1222
|
|
1223 return TRUE;
|
|
1224 }
|
|
1225
|
|
1226 #ifdef WIN32
|
|
1227
|
|
1228 static gint check_loc(gpointer_data)
|
|
1229 {
|
|
1230 GaimBlistNode *gnode, *cnode, *bnode;
|
|
1231 ZLocations_t locations;
|
|
1232 int numlocs;
|
|
1233 int one = 1;
|
|
1234
|
|
1235 for (gnode = gaim_get_blist()->root; gnode; gnode = gnode->next) {
|
|
1236 if (!GAIM_BLIST_NODE_IS_GROUP(gnode))
|
|
1237 continue;
|
|
1238 for (cnode = gnode->child; cnode; cnode = cnode->next) {
|
|
1239 if (!GAIM_BLIST_NODE_IS_CONTACT(cnode))
|
|
1240 continue;
|
|
1241 for (bnode = cnode->child; bnode; bnode = bnode->next) {
|
|
1242 GaimBuddy *b = (GaimBuddy *) bnode;
|
|
1243
|
|
1244 if (!GAIM_BLIST_NODE_IS_BUDDY(bnode))
|
|
1245 continue;
|
|
1246 if (b->account->gc == zgc) {
|
|
1247 char *chk;
|
|
1248 chk = local_zephyr_normalize(b->name);
|
|
1249 ZLocateUser(chk,&numlocs, ZAUTH);
|
|
1250 if (numlocs) {
|
|
1251 int i;
|
|
1252 for(i=0;i<numlocs;i++) {
|
|
1253 ZGetLocations(&locations,&one);
|
|
1254 serv_got_update(zgc,b->name,1,0,0,0,0);
|
|
1255 }
|
|
1256 }
|
|
1257 }
|
|
1258 }
|
|
1259 }
|
|
1260 }
|
|
1261 return TRUE;
|
|
1262 }
|
|
1263
|
|
1264 #else
|
|
1265
|
|
1266 static gint check_loc(gpointer data)
|
|
1267 {
|
|
1268 GaimBlistNode *gnode, *cnode, *bnode;
|
|
1269 ZAsyncLocateData_t ald;
|
|
1270 GaimConnection *gc = (GaimConnection *)data;
|
|
1271 zephyr_account *zephyr = gc->proto_data;
|
|
1272
|
|
1273 if (use_zeph02(zephyr)) {
|
|
1274 ald.user = NULL;
|
|
1275 memset(&(ald.uid), 0, sizeof(ZUnique_Id_t));
|
|
1276 ald.version = NULL;
|
|
1277 }
|
|
1278
|
|
1279 for (gnode = gaim_get_blist()->root; gnode; gnode = gnode->next) {
|
|
1280 if (!GAIM_BLIST_NODE_IS_GROUP(gnode))
|
|
1281 continue;
|
|
1282 for (cnode = gnode->child; cnode; cnode = cnode->next) {
|
|
1283 if (!GAIM_BLIST_NODE_IS_CONTACT(cnode))
|
|
1284 continue;
|
|
1285 for (bnode = cnode->child; bnode; bnode = bnode->next) {
|
|
1286 GaimBuddy *b = (GaimBuddy *) bnode;
|
|
1287
|
|
1288 if (!GAIM_BLIST_NODE_IS_BUDDY(bnode))
|
|
1289 continue;
|
|
1290 if (b->account->gc == gc) {
|
|
1291 const char *chk;
|
|
1292
|
|
1293 chk = local_zephyr_normalize(zephyr,b->name);
|
|
1294 gaim_debug_info("zephyr","chk: %s b->name %s\n",chk,b->name);
|
|
1295 /* XXX add real error reporting */
|
|
1296 /* doesn't matter if this fails or not; we'll just move on to the next one */
|
|
1297 if (use_zeph02(zephyr)) {
|
|
1298 #ifdef WIN32
|
|
1299 int numlocs;
|
|
1300 int one=1;
|
|
1301 ZLocateUser(chk,&numlocs,ZAUTH);
|
|
1302 if (numlocs) {
|
|
1303 int i;
|
|
1304 for(i=0;i<numlocs;i++) {
|
|
1305 ZGetLocations(&locations,&one);
|
|
1306 if (nlocs>0)
|
|
1307 gaim_prpl_got_user_status(gc->account,b->name,"available",NULL);
|
|
1308 else
|
|
1309 gaim_prpl_got_user_status(gc->account,b->name,"offline",NULL);
|
|
1310 }
|
|
1311 }
|
|
1312 #else
|
|
1313 ZRequestLocations(chk, &ald, UNACKED, ZAUTH);
|
|
1314 g_free(ald.user);
|
|
1315 g_free(ald.version);
|
|
1316 #endif /* WIN32 */
|
|
1317 } else
|
|
1318 if (use_tzc(zephyr)) {
|
|
1319 gchar *zlocstr = g_strdup_printf("((tzcfodder . zlocate) \"%s\")\n",chk);
|
|
1320 write(zephyr->totzc[ZEPHYR_FD_WRITE],zlocstr,strlen(zlocstr));
|
|
1321 g_free(zlocstr);
|
|
1322 }
|
|
1323 }
|
|
1324 }
|
|
1325 }
|
|
1326 }
|
|
1327
|
|
1328 return TRUE;
|
|
1329 }
|
|
1330
|
|
1331 #endif /* WIN32 */
|
|
1332
|
|
1333 static char *get_exposure_level()
|
|
1334 {
|
|
1335 /* XXX add real error reporting */
|
|
1336 char *exposure = ZGetVariable("exposure");
|
|
1337
|
|
1338 if (!exposure)
|
|
1339 return EXPOSE_REALMVIS;
|
|
1340 if (!g_ascii_strcasecmp(exposure, EXPOSE_NONE))
|
|
1341 return EXPOSE_NONE;
|
|
1342 if (!g_ascii_strcasecmp(exposure, EXPOSE_OPSTAFF))
|
|
1343 return EXPOSE_OPSTAFF;
|
|
1344 if (!g_ascii_strcasecmp(exposure, EXPOSE_REALMANN))
|
|
1345 return EXPOSE_REALMANN;
|
|
1346 if (!g_ascii_strcasecmp(exposure, EXPOSE_NETVIS))
|
|
1347 return EXPOSE_NETVIS;
|
|
1348 if (!g_ascii_strcasecmp(exposure, EXPOSE_NETANN))
|
|
1349 return EXPOSE_NETANN;
|
|
1350 return EXPOSE_REALMVIS;
|
|
1351 }
|
|
1352
|
|
1353 static void strip_comments(char *str)
|
|
1354 {
|
|
1355 char *tmp = strchr(str, '#');
|
|
1356
|
|
1357 if (tmp)
|
|
1358 *tmp = '\0';
|
|
1359 g_strchug(str);
|
|
1360 g_strchomp(str);
|
|
1361 }
|
|
1362
|
|
1363 static void zephyr_inithosts(zephyr_account *zephyr)
|
|
1364 {
|
|
1365 /* XXX This code may not be Win32 clean */
|
|
1366 struct hostent *hent;
|
|
1367
|
|
1368 if (gethostname(zephyr->ourhost, sizeof(zephyr->ourhost)) == -1) {
|
|
1369 gaim_debug_error("zephyr", "unable to retrieve hostname, %%host%% and %%canon%% will be wrong in subscriptions and have been set to unknown\n");
|
|
1370 g_strlcpy(zephyr->ourhost, "unknown", sizeof(zephyr->ourhost));
|
|
1371 g_strlcpy(zephyr->ourhostcanon, "unknown", sizeof(zephyr->ourhostcanon));
|
|
1372 return;
|
|
1373 }
|
|
1374
|
|
1375 if (!(hent = gethostbyname(zephyr->ourhost))) {
|
|
1376 gaim_debug_error("zephyr", "unable to resolve hostname, %%canon%% will be wrong in subscriptions.and has been set to the value of %%host%%, %s\n",zephyr->ourhost);
|
|
1377 g_strlcpy(zephyr->ourhostcanon, zephyr->ourhost, sizeof(zephyr->ourhostcanon));
|
|
1378 return;
|
|
1379 }
|
|
1380
|
|
1381 g_strlcpy(zephyr->ourhostcanon, hent->h_name, sizeof(zephyr->ourhostcanon));
|
|
1382
|
|
1383 return;
|
|
1384 }
|
|
1385
|
|
1386 static void process_zsubs(zephyr_account *zephyr)
|
|
1387 {
|
|
1388 /* Loads zephyr chats "(subscriptions) from ~/.zephyr.subs, and
|
|
1389 registers (subscribes to) them on the server */
|
|
1390
|
|
1391 /* XXX deal with unsubscriptions */
|
|
1392 /* XXX deal with punts */
|
|
1393
|
|
1394 FILE *f;
|
|
1395 gchar *fname;
|
|
1396 gchar buff[BUFSIZ];
|
|
1397
|
|
1398 fname = g_strdup_printf("%s/.zephyr.subs", gaim_home_dir());
|
|
1399 f = g_fopen(fname, "r");
|
|
1400 if (f) {
|
|
1401 char **triple;
|
|
1402 char *recip;
|
|
1403 char *z_class;
|
|
1404 char *z_instance;
|
|
1405 char *z_galaxy = NULL;
|
|
1406
|
|
1407 while (fgets(buff, BUFSIZ, f)) {
|
|
1408 strip_comments(buff);
|
|
1409 if (buff[0]) {
|
|
1410 triple = g_strsplit(buff, ",", 3);
|
|
1411 if (triple[0] && triple[1]) {
|
|
1412 char *tmp = g_strdup_printf("%s", zephyr->username);
|
|
1413 char *atptr;
|
|
1414
|
|
1415 z_class = triple[0];
|
|
1416 z_instance = triple[1];
|
|
1417 if (triple[2] == NULL) {
|
|
1418 recip = g_malloc0(1);
|
|
1419 } else if (!g_ascii_strcasecmp(triple[2], "%me%")) {
|
|
1420 recip = g_strdup_printf("%s", zephyr->username);
|
|
1421 } else if (!g_ascii_strcasecmp(triple[2], "*")) {
|
|
1422 /* wildcard
|
|
1423 * form of class,instance,* */
|
|
1424 recip = g_malloc0(1);
|
|
1425 } else if (!g_ascii_strcasecmp(triple[2], tmp)) {
|
|
1426 /* form of class,instance,aatharuv@ATHENA.MIT.EDU */
|
|
1427 recip = g_strdup(triple[2]);
|
|
1428 } else if ((atptr = strchr(triple[2], '@')) != NULL) {
|
|
1429 /* form of class,instance,*@ANDREW.CMU.EDU
|
|
1430 * class,instance,@ANDREW.CMU.EDU
|
|
1431 * If realm is local realm, blank recipient, else
|
|
1432 * @REALM-NAME
|
|
1433 */
|
|
1434 char *realmat = g_strdup_printf("@%s",zephyr->realm);
|
|
1435
|
|
1436 if (!g_ascii_strcasecmp(atptr, realmat))
|
|
1437 recip = g_malloc0(1);
|
|
1438 else
|
|
1439 recip = g_strdup(atptr);
|
|
1440 g_free(realmat);
|
|
1441 } else {
|
|
1442 recip = g_strdup(triple[2]);
|
|
1443 }
|
|
1444 g_free(tmp);
|
|
1445
|
|
1446 if (!g_ascii_strcasecmp(triple[0],"%host%")) {
|
|
1447 z_class = g_strdup(zephyr->ourhost);
|
|
1448 } else if (!g_ascii_strcasecmp(triple[0],"%canon%")) {
|
|
1449 z_class = g_strdup(zephyr->ourhostcanon);
|
|
1450 } else {
|
|
1451 z_class = g_strdup(triple[0]);
|
|
1452 }
|
|
1453
|
|
1454 if (!g_ascii_strcasecmp(triple[1],"%host%")) {
|
|
1455 z_instance = g_strdup(zephyr->ourhost);
|
|
1456 } else if (!g_ascii_strcasecmp(triple[1],"%canon%")) {
|
|
1457 z_instance = g_strdup(zephyr->ourhostcanon);
|
|
1458 } else {
|
|
1459 z_instance = g_strdup(triple[1]);
|
|
1460 }
|
|
1461
|
|
1462 /* There should be some sort of error report listing classes that couldn't be subbed to.
|
|
1463 Not important right now though */
|
|
1464
|
|
1465 if (zephyr_subscribe_to(zephyr,z_class, z_instance, recip,z_galaxy) != ZERR_NONE) {
|
|
1466
|
|
1467 gaim_debug_error("zephyr", "Couldn't subscribe to %s, %s, %s\n", z_class,z_instance,recip);
|
|
1468 }
|
|
1469
|
|
1470 zephyr->subscrips = g_slist_append(zephyr->subscrips, new_triple(zephyr,z_class,z_instance,recip));
|
|
1471 /* g_hash_table_destroy(sub_hash_table); */
|
|
1472 g_free(z_instance);
|
|
1473 g_free(z_class);
|
|
1474 g_free(recip);
|
|
1475 }
|
|
1476 g_strfreev(triple);
|
|
1477 }
|
|
1478 }
|
|
1479 fclose(f);
|
|
1480 }
|
|
1481 }
|
|
1482
|
|
1483 static void process_anyone(GaimConnection *gc)
|
|
1484 {
|
|
1485 FILE *fd;
|
|
1486 gchar buff[BUFSIZ], *filename;
|
|
1487 GaimGroup *g;
|
|
1488 GaimBuddy *b;
|
|
1489
|
|
1490 if (!(g = gaim_find_group(_("Anyone")))) {
|
|
1491 g = gaim_group_new(_("Anyone"));
|
|
1492 gaim_blist_add_group(g, NULL);
|
|
1493 }
|
|
1494
|
|
1495 filename = g_strconcat(gaim_home_dir(), "/.anyone", NULL);
|
|
1496 if ((fd = g_fopen(filename, "r")) != NULL) {
|
|
1497 while (fgets(buff, BUFSIZ, fd)) {
|
|
1498 strip_comments(buff);
|
|
1499 if (buff[0]) {
|
|
1500 if (!(b = gaim_find_buddy(gc->account, buff))) {
|
|
1501 char *stripped_user = zephyr_strip_local_realm(gc->proto_data,buff);
|
|
1502 gaim_debug_info("zephyr","stripped_user %s\n",stripped_user);
|
|
1503 if (!(b = gaim_find_buddy(gc->account,stripped_user))){
|
|
1504 b = gaim_buddy_new(gc->account, stripped_user, NULL);
|
|
1505 gaim_blist_add_buddy(b, NULL, g, NULL);
|
|
1506 }
|
|
1507 g_free(stripped_user);
|
|
1508 }
|
|
1509 }
|
|
1510 }
|
|
1511 fclose(fd);
|
|
1512 }
|
|
1513 g_free(filename);
|
|
1514 }
|
|
1515
|
|
1516 static char* normalize_zephyr_exposure(const char* exposure) {
|
|
1517 char *exp2 = g_strstrip(g_ascii_strup(exposure,-1));
|
|
1518
|
|
1519 if (!exp2)
|
|
1520 return EXPOSE_REALMVIS;
|
|
1521 if (!g_ascii_strcasecmp(exp2, EXPOSE_NONE))
|
|
1522 return EXPOSE_NONE;
|
|
1523 if (!g_ascii_strcasecmp(exp2, EXPOSE_OPSTAFF))
|
|
1524 return EXPOSE_OPSTAFF;
|
|
1525 if (!g_ascii_strcasecmp(exp2, EXPOSE_REALMANN))
|
|
1526 return EXPOSE_REALMANN;
|
|
1527 if (!g_ascii_strcasecmp(exp2, EXPOSE_NETVIS))
|
|
1528 return EXPOSE_NETVIS;
|
|
1529 if (!g_ascii_strcasecmp(exp2, EXPOSE_NETANN))
|
|
1530 return EXPOSE_NETANN;
|
|
1531 return EXPOSE_REALMVIS;
|
|
1532 }
|
|
1533
|
|
1534 static void zephyr_login(GaimAccount * account)
|
|
1535 {
|
|
1536 GaimConnection *gc;
|
|
1537 zephyr_account *zephyr;
|
|
1538 gboolean read_anyone;
|
|
1539 gboolean read_zsubs;
|
|
1540 gchar *exposure;
|
|
1541
|
|
1542 gc = gaim_account_get_connection(account);
|
|
1543 read_anyone = gaim_account_get_bool(gc->account,"read_anyone",TRUE);
|
|
1544 read_zsubs = gaim_account_get_bool(gc->account,"read_zsubs",TRUE);
|
|
1545 exposure = (gchar *)gaim_account_get_string(gc->account, "exposure_level", EXPOSE_REALMVIS);
|
|
1546
|
|
1547 #ifdef WIN32
|
|
1548 username = gaim_account_get_username(account);
|
|
1549 #endif
|
|
1550 gc->flags |= GAIM_CONNECTION_HTML | GAIM_CONNECTION_NO_BGCOLOR | GAIM_CONNECTION_NO_URLDESC;
|
|
1551 gc->proto_data = zephyr=g_new0(zephyr_account,1);
|
|
1552
|
|
1553 zephyr->account = account;
|
|
1554
|
|
1555 /* Make sure that the exposure (visibility) is set to a sane value */
|
|
1556 zephyr->exposure=g_strdup(normalize_zephyr_exposure(exposure));
|
|
1557
|
|
1558 if (gaim_account_get_bool(gc->account,"use_tzc",0)) {
|
|
1559 zephyr->connection_type = GAIM_ZEPHYR_TZC;
|
|
1560 } else {
|
|
1561 zephyr->connection_type = GAIM_ZEPHYR_KRB4;
|
|
1562 }
|
|
1563
|
|
1564 zephyr->encoding = (char *)gaim_account_get_string(gc->account, "encoding", ZEPHYR_FALLBACK_CHARSET);
|
|
1565 gaim_connection_update_progress(gc, _("Connecting"), 0, 8);
|
|
1566
|
|
1567 /* XXX z_call_s should actually try to report the com_err determined error */
|
|
1568 if (use_tzc(zephyr)) {
|
|
1569 pid_t pid;
|
|
1570 /* gaim_connection_error(gc,"tzc not supported yet"); */
|
|
1571 if ((pipe(zephyr->totzc) != 0) || (pipe(zephyr->fromtzc) != 0)) {
|
|
1572 gaim_debug_error("zephyr", "pipe creation failed. killing\n");
|
|
1573 exit(-1);
|
|
1574 }
|
|
1575
|
|
1576 pid = fork();
|
|
1577
|
|
1578 if (pid == -1) {
|
|
1579 gaim_debug_error("zephyr", "forking failed\n");
|
|
1580 exit(-1);
|
|
1581 }
|
|
1582 if (pid == 0) {
|
|
1583 unsigned int i=0;
|
|
1584 gboolean found_ps = FALSE;
|
|
1585 gchar ** tzc_cmd_array = g_strsplit(gaim_account_get_string(gc->account,"tzc_command","/usr/bin/tzc -e %s")," ",0);
|
|
1586 if (close(1) == -1) {
|
|
1587 gaim_debug_error("zephyr", "stdout couldn't be closed. dying\n");
|
|
1588 exit(-1);
|
|
1589 }
|
|
1590 if (dup2(zephyr->fromtzc[1], 1) == -1) {
|
|
1591 gaim_debug_error("zephyr", "dup2 of stdout failed \n");
|
|
1592 exit(-1);
|
|
1593 }
|
|
1594 if (close(zephyr->fromtzc[1]) == -1) {
|
|
1595 gaim_debug_error("zephyr", "closing of piped stdout failed\n");
|
|
1596 exit(-1);
|
|
1597 }
|
|
1598 if (close(0) == -1) {
|
|
1599 gaim_debug_error("zephyr", "stdin couldn't be closed. dying\n");
|
|
1600 exit(-1);
|
|
1601 }
|
|
1602 if (dup2(zephyr->totzc[0], 0) == -1) {
|
|
1603 gaim_debug_error("zephyr", "dup2 of stdin failed \n");
|
|
1604 exit(-1);
|
|
1605 }
|
|
1606 if (close(zephyr->totzc[0]) == -1) {
|
|
1607 gaim_debug_error("zephyr", "closing of piped stdin failed\n");
|
|
1608 exit(-1);
|
|
1609 }
|
|
1610 /* tzc_command should really be of the form
|
|
1611 path/to/tzc -e %s
|
|
1612 or
|
|
1613 ssh username@hostname pathtotzc -e %s
|
|
1614 -- this should not require a password, and ideally should be kerberized ssh --
|
|
1615 or
|
|
1616 fsh username@hostname pathtotzc -e %s
|
|
1617 */
|
|
1618 while(tzc_cmd_array[i] != NULL){
|
|
1619 if (!g_ascii_strncasecmp(tzc_cmd_array[i],"%s",2)) {
|
|
1620 /* fprintf(stderr,"replacing %%s with %s\n",zephyr->exposure); */
|
|
1621 tzc_cmd_array[i] = g_strdup(zephyr->exposure);
|
|
1622 found_ps = TRUE;
|
|
1623
|
|
1624 } else {
|
|
1625 /* fprintf(stderr,"keeping %s\n",tzc_cmd_array[i]); */
|
|
1626 }
|
|
1627 i++;
|
|
1628 }
|
|
1629
|
|
1630 if (!found_ps) {
|
|
1631 gaim_connection_error(gc,"Tzc command needs %s to set the exposure\n");
|
|
1632 return;
|
|
1633 }
|
|
1634
|
|
1635 execvp(tzc_cmd_array[0], tzc_cmd_array);
|
|
1636 }
|
|
1637 else {
|
|
1638 fd_set rfds;
|
|
1639 int bufsize = 2048;
|
|
1640 char *buf = (char *)calloc(bufsize, 1);
|
|
1641 char *bufcur = buf;
|
|
1642 struct timeval tv;
|
|
1643 char *ptr;
|
|
1644 int parenlevel=0;
|
|
1645 char* tempstr;
|
|
1646 int tempstridx;
|
|
1647
|
|
1648 zephyr->tzc_pid = pid;
|
|
1649 /* wait till we have data to read from ssh */
|
|
1650 FD_ZERO(&rfds);
|
|
1651 FD_SET(zephyr->fromtzc[ZEPHYR_FD_READ], &rfds);
|
|
1652
|
|
1653 tv.tv_sec = 10;
|
|
1654 tv.tv_usec = 0;
|
|
1655
|
|
1656 gaim_debug_info("zephyr", "about to read from tzc\n");
|
|
1657
|
|
1658 select(zephyr->fromtzc[ZEPHYR_FD_READ] + 1, &rfds, NULL, NULL, NULL);
|
|
1659
|
|
1660 FD_ZERO(&rfds);
|
|
1661 FD_SET(zephyr->fromtzc[ZEPHYR_FD_READ], &rfds);
|
|
1662 while (select(zephyr->fromtzc[ZEPHYR_FD_READ] + 1, &rfds, NULL, NULL, &tv)) {
|
|
1663 read(zephyr->fromtzc[ZEPHYR_FD_READ], bufcur, 1);
|
|
1664 bufcur++;
|
|
1665 if ((bufcur - buf) > (bufsize - 1)) {
|
|
1666 if ((buf = realloc(buf, bufsize * 2)) == NULL) {
|
|
1667 exit(-1);
|
|
1668 } else {
|
|
1669 bufcur = buf + bufsize;
|
|
1670 bufsize *= 2;
|
|
1671 }
|
|
1672 }
|
|
1673 FD_ZERO(&rfds);
|
|
1674 FD_SET(zephyr->fromtzc[ZEPHYR_FD_READ], &rfds);
|
|
1675 tv.tv_sec = 10;
|
|
1676 tv.tv_usec = 0;
|
|
1677
|
|
1678 }
|
|
1679 /* fprintf(stderr, "read from tzc\n"); */
|
|
1680 *bufcur = '\0';
|
|
1681 ptr = buf;
|
|
1682
|
|
1683 /* ignore all tzcoutput till we've received the first (*/
|
|
1684 while (ptr < bufcur && (*ptr !='(')) {
|
|
1685 ptr++;
|
|
1686 }
|
|
1687 if (ptr >=bufcur) {
|
|
1688 gaim_connection_error(gc,"invalid output by tzc (or bad parsing code)");
|
|
1689 free(buf);
|
|
1690 return;
|
|
1691 }
|
|
1692
|
|
1693 while(ptr < bufcur) {
|
|
1694 if (*ptr == '(') {
|
|
1695 parenlevel++;
|
|
1696 }
|
|
1697 else if (*ptr == ')') {
|
|
1698 parenlevel--;
|
|
1699 }
|
|
1700 gaim_debug_info("zephyr","tzc parenlevel is %d\n",parenlevel);
|
|
1701 switch (parenlevel) {
|
|
1702 case 0:
|
|
1703 break;
|
|
1704 case 1:
|
|
1705 /* Search for next beginning (, or for the ending */
|
|
1706 ptr++;
|
|
1707 while((*ptr != '(') && (*ptr != ')') && (ptr <bufcur))
|
|
1708 ptr++;
|
|
1709 if (ptr >= bufcur)
|
|
1710 gaim_debug_error("zephyr","tzc parsing error\n");
|
|
1711 break;
|
|
1712 case 2:
|
|
1713 /* You are probably at
|
|
1714 (foo . bar ) or (foo . "bar") or (foo . chars) or (foo . numbers) or (foo . () )
|
|
1715 Parse all the data between the first and last f, and move past )
|
|
1716 */
|
|
1717 tempstr = g_malloc0(20000);
|
|
1718 tempstridx=0;
|
|
1719 while(parenlevel >1) {
|
|
1720 ptr++;
|
|
1721 if (*ptr == '(')
|
|
1722 parenlevel++;
|
|
1723 if (*ptr == ')')
|
|
1724 parenlevel--;
|
|
1725 if (parenlevel > 1) {
|
|
1726 tempstr[tempstridx++]=*ptr;
|
|
1727 } else {
|
|
1728 ptr++;
|
|
1729 }
|
|
1730 }
|
|
1731 gaim_debug_info("zephyr","tempstr parsed\n");
|
|
1732 /* tempstr should now be a tempstridx length string containing all characters
|
|
1733 from that after the first ( to the one before the last paren ). */
|
|
1734 /* We should have the following possible lisp strings but we don't care
|
|
1735 (tzcspew . start) (version . "something") (pid . number)*/
|
|
1736 /* We care about 'zephyrid . "username@REALM.NAME"' and 'exposure . "SOMETHING"' */
|
|
1737 tempstridx=0;
|
|
1738 if (!g_ascii_strncasecmp(tempstr,"zephyrid",8)) {
|
|
1739 gchar* username = g_malloc0(100);
|
|
1740 int username_idx=0;
|
|
1741 char *realm;
|
|
1742 gaim_debug_info("zephyr","zephyrid found\n");
|
|
1743 tempstridx+=8;
|
|
1744 while(tempstr[tempstridx] !='"' && tempstridx < 20000)
|
|
1745 tempstridx++;
|
|
1746 tempstridx++;
|
|
1747 while(tempstr[tempstridx] !='"' && tempstridx < 20000)
|
|
1748 username[username_idx++]=tempstr[tempstridx++];
|
|
1749
|
|
1750 zephyr->username = g_strdup_printf("%s",username);
|
|
1751 if ((realm = strchr(username,'@')))
|
|
1752 zephyr->realm = g_strdup_printf("%s",realm+1);
|
|
1753 else {
|
|
1754 realm = (gchar *)gaim_account_get_string(gc->account,"realm","");
|
|
1755 if (!*realm) {
|
|
1756 realm = "local-realm";
|
|
1757 }
|
|
1758 zephyr->realm = g_strdup(realm);
|
|
1759 g_strlcpy(__Zephyr_realm, (const char*)zephyr->realm, REALM_SZ-1);
|
|
1760 }
|
|
1761 /* else {
|
|
1762 zephyr->realm = g_strdup("local-realm");
|
|
1763 }*/
|
|
1764
|
|
1765 g_free(username);
|
|
1766 } else {
|
|
1767 gaim_debug_info("zephyr", "something that's not zephyr id found %s\n",tempstr);
|
|
1768 }
|
|
1769
|
|
1770 /* We don't care about anything else yet */
|
|
1771 g_free(tempstr);
|
|
1772 break;
|
|
1773 default:
|
|
1774 gaim_debug_info("zephyr","parenlevel is not 1 or 2\n");
|
|
1775 /* This shouldn't be happening */
|
|
1776 break;
|
|
1777 }
|
|
1778 if (parenlevel==0)
|
|
1779 break;
|
|
1780 } /* while (ptr < bufcur) */
|
|
1781 gaim_debug_info("zephyr", "tzc startup done\n");
|
|
1782 free(buf);
|
|
1783 }
|
|
1784 }
|
|
1785 else if ( use_zeph02(zephyr)) {
|
|
1786 gchar* realm;
|
|
1787 z_call_s(ZInitialize(), "Couldn't initialize zephyr");
|
|
1788 z_call_s(ZOpenPort(&(zephyr->port)), "Couldn't open port");
|
|
1789 z_call_s(ZSetLocation((char *)zephyr->exposure), "Couldn't set location");
|
|
1790
|
|
1791 realm = (gchar *)gaim_account_get_string(gc->account,"realm","");
|
|
1792 if (!*realm) {
|
|
1793 realm = ZGetRealm();
|
|
1794 }
|
|
1795 zephyr->realm = g_strdup(realm);
|
|
1796 g_strlcpy(__Zephyr_realm, (const char*)zephyr->realm, REALM_SZ-1);
|
|
1797 zephyr->username = g_strdup(ZGetSender());
|
|
1798
|
|
1799 /* zephyr->realm = g_strdup(ZGetRealm()); */
|
|
1800 gaim_debug_info("zephyr","realm: %s\n",zephyr->realm);
|
|
1801 }
|
|
1802 else {
|
|
1803 gaim_connection_error(gc,"Only ZEPH0.2 supported currently");
|
|
1804 return;
|
|
1805 }
|
|
1806 gaim_debug_info("zephyr","does it get here\n");
|
|
1807 gaim_debug_info("zephyr"," realm: %s username:%s\n", zephyr->realm, zephyr->username);
|
|
1808
|
|
1809 /* For now */
|
|
1810 zephyr->galaxy = NULL;
|
|
1811 zephyr->krbtkfile = NULL;
|
|
1812 zephyr_inithosts(zephyr);
|
|
1813
|
|
1814 if (zephyr_subscribe_to(zephyr,"MESSAGE","PERSONAL",zephyr->username,NULL) != ZERR_NONE) {
|
|
1815 /* XXX don't translate this yet. It could be written better */
|
|
1816 /* XXX error messages could be handled with more detail */
|
|
1817 gaim_notify_error(account->gc, NULL,
|
|
1818 "Unable to subscribe to messages", "Unable to subscribe to initial messages");
|
|
1819 return;
|
|
1820 }
|
|
1821
|
|
1822 gaim_connection_set_state(gc, GAIM_CONNECTED);
|
|
1823
|
|
1824 if (read_anyone)
|
|
1825 process_anyone(gc);
|
|
1826 if (read_zsubs)
|
|
1827 process_zsubs(zephyr);
|
|
1828
|
|
1829 if (use_zeph02(zephyr)) {
|
|
1830 zephyr->nottimer = gaim_timeout_add(100, check_notify_zeph02, gc);
|
|
1831 } else if (use_tzc(zephyr)) {
|
|
1832 zephyr->nottimer = gaim_timeout_add(100, check_notify_tzc, gc);
|
|
1833 }
|
|
1834 zephyr->loctimer = gaim_timeout_add(20000, check_loc, gc);
|
|
1835
|
|
1836 }
|
|
1837
|
|
1838 static void write_zsubs(zephyr_account *zephyr)
|
|
1839 {
|
|
1840 /* Exports subscription (chat) list back to
|
|
1841 * .zephyr.subs
|
|
1842 * XXX deal with %host%, %canon%, unsubscriptions, and negative subscriptions (punts?)
|
|
1843 */
|
|
1844
|
|
1845 GSList *s = zephyr->subscrips;
|
|
1846 zephyr_triple *zt;
|
|
1847 FILE *fd;
|
|
1848 char *fname;
|
|
1849
|
|
1850 char **triple;
|
|
1851
|
|
1852 fname = g_strdup_printf("%s/.zephyr.subs", gaim_home_dir());
|
|
1853 fd = g_fopen(fname, "w");
|
|
1854
|
|
1855 if (!fd) {
|
|
1856 g_free(fname);
|
|
1857 return;
|
|
1858 }
|
|
1859
|
|
1860 while (s) {
|
|
1861 char *zclass, *zinst, *zrecip;
|
|
1862 zt = s->data;
|
|
1863 triple = g_strsplit(zt->name, ",", 3);
|
|
1864
|
|
1865 /* deal with classes */
|
|
1866 if (!g_ascii_strcasecmp(triple[0],zephyr->ourhost)) {
|
|
1867 zclass = g_strdup("%host%");
|
|
1868 } else if (!g_ascii_strcasecmp(triple[0],zephyr->ourhostcanon)) {
|
|
1869 zclass = g_strdup("%canon%");
|
|
1870 } else {
|
|
1871 zclass = g_strdup(triple[0]);
|
|
1872 }
|
|
1873
|
|
1874 /* deal with instances */
|
|
1875
|
|
1876 if (!g_ascii_strcasecmp(triple[1],zephyr->ourhost)) {
|
|
1877 zinst = g_strdup("%host%");
|
|
1878 } else if (!g_ascii_strcasecmp(triple[1],zephyr->ourhostcanon)) {
|
|
1879 zinst = g_strdup("%canon%");;
|
|
1880 } else {
|
|
1881 zinst = g_strdup(triple[1]);
|
|
1882 }
|
|
1883
|
|
1884 /* deal with recipients */
|
|
1885 if (triple[2] == NULL) {
|
|
1886 zrecip = g_strdup("*");
|
|
1887 } else if (!g_ascii_strcasecmp(triple[2],"")){
|
|
1888 zrecip = g_strdup("*");
|
|
1889 } else if (!g_ascii_strcasecmp(triple[2], zephyr->username)) {
|
|
1890 zrecip = g_strdup("%me%");
|
|
1891 } else {
|
|
1892 zrecip = g_strdup(triple[2]);
|
|
1893 }
|
|
1894
|
|
1895 fprintf(fd, "%s,%s,%s\n",zclass,zinst,zrecip);
|
|
1896
|
|
1897 g_free(zclass);
|
|
1898 g_free(zinst);
|
|
1899 g_free(zrecip);
|
|
1900 g_free(triple);
|
|
1901 s = s->next;
|
|
1902 }
|
|
1903 g_free(fname);
|
|
1904 fclose(fd);
|
|
1905 }
|
|
1906
|
|
1907 static void write_anyone(GaimConnection *gc)
|
|
1908 {
|
|
1909 GaimBlistNode *gnode, *cnode, *bnode;
|
|
1910 GaimBuddy *b;
|
|
1911 char *fname;
|
|
1912 FILE *fd;
|
|
1913 zephyr_account* zephyr = gc->proto_data;
|
|
1914 fname = g_strdup_printf("%s/.anyone", gaim_home_dir());
|
|
1915 fd = g_fopen(fname, "w");
|
|
1916 if (!fd) {
|
|
1917 g_free(fname);
|
|
1918 return;
|
|
1919 }
|
|
1920
|
|
1921 for (gnode = gaim_get_blist()->root; gnode; gnode = gnode->next) {
|
|
1922 if (!GAIM_BLIST_NODE_IS_GROUP(gnode))
|
|
1923 continue;
|
|
1924 for (cnode = gnode->child; cnode; cnode = cnode->next) {
|
|
1925 if (!GAIM_BLIST_NODE_IS_CONTACT(cnode))
|
|
1926 continue;
|
|
1927 for (bnode = cnode->child; bnode; bnode = bnode->next) {
|
|
1928 if (!GAIM_BLIST_NODE_IS_BUDDY(bnode))
|
|
1929 continue;
|
|
1930 b = (GaimBuddy *) bnode;
|
|
1931 if (b->account == gc->account) {
|
|
1932 gchar *stripped_user = zephyr_strip_local_realm(zephyr,b->name);
|
|
1933 fprintf(fd, "%s\n", stripped_user);
|
|
1934 g_free(stripped_user);
|
|
1935 }
|
|
1936 }
|
|
1937 }
|
|
1938 }
|
|
1939
|
|
1940 fclose(fd);
|
|
1941 g_free(fname);
|
|
1942 }
|
|
1943
|
|
1944 static void zephyr_close(GaimConnection * gc)
|
|
1945 {
|
|
1946 GList *l;
|
|
1947 GSList *s;
|
|
1948 zephyr_account *zephyr = gc->proto_data;
|
|
1949 pid_t tzc_pid = zephyr->tzc_pid;
|
|
1950
|
|
1951 l = zephyr->pending_zloc_names;
|
|
1952 while (l) {
|
|
1953 g_free((char *)l->data);
|
|
1954 l = l->next;
|
|
1955 }
|
|
1956 g_list_free(zephyr->pending_zloc_names);
|
|
1957
|
|
1958 if (gaim_account_get_bool(gc->account, "write_anyone", FALSE))
|
|
1959 write_anyone(gc);
|
|
1960
|
|
1961 if (gaim_account_get_bool(gc->account, "write_zsubs", FALSE))
|
|
1962 write_zsubs(gc->proto_data);
|
|
1963
|
|
1964 s = zephyr->subscrips;
|
|
1965 while (s) {
|
|
1966 free_triple((zephyr_triple *) s->data);
|
|
1967 s = s->next;
|
|
1968 }
|
|
1969 g_slist_free(zephyr->subscrips);
|
|
1970
|
|
1971 if (zephyr->nottimer)
|
|
1972 gaim_timeout_remove(zephyr->nottimer);
|
|
1973 zephyr->nottimer = 0;
|
|
1974 if (zephyr->loctimer)
|
|
1975 gaim_timeout_remove(zephyr->loctimer);
|
|
1976 zephyr->loctimer = 0;
|
|
1977 gc = NULL;
|
|
1978 if (use_zeph02(zephyr)) {
|
|
1979 z_call(ZCancelSubscriptions(0));
|
|
1980 z_call(ZUnsetLocation());
|
|
1981 z_call(ZClosePort());
|
|
1982 } else {
|
|
1983 /* assume tzc */
|
|
1984 if (kill(tzc_pid,SIGTERM) == -1) {
|
|
1985 int err=errno;
|
|
1986 if (err==EINVAL) {
|
|
1987 gaim_debug_error("zephyr","An invalid signal was specified when killing tzc\n");
|
|
1988 }
|
|
1989 else if (err==ESRCH) {
|
|
1990 gaim_debug_error("zephyr","Tzc's pid didn't exist while killing tzc\n");
|
|
1991 }
|
|
1992 else if (err==EPERM) {
|
|
1993 gaim_debug_error("zephyr","gaim didn't have permission to kill tzc\n");
|
|
1994 }
|
|
1995 else {
|
|
1996 gaim_debug_error("zephyr","miscellaneous error while attempting to close tzc\n");
|
|
1997 }
|
|
1998 }
|
|
1999 }
|
|
2000 }
|
|
2001
|
|
2002 static int zephyr_send_message(zephyr_account *zephyr,char* zclass, char* instance, char* recipient, const char *im,
|
|
2003 const char *sig, char *opcode) ;
|
|
2004
|
|
2005 static const char * zephyr_get_signature()
|
|
2006 {
|
|
2007 /* XXX add zephyr error reporting */
|
|
2008 const char * sig =ZGetVariable("zwrite-signature");
|
|
2009 if (!sig) {
|
|
2010 sig = g_get_real_name();
|
|
2011 }
|
|
2012 return sig;
|
|
2013 }
|
|
2014
|
|
2015 static int zephyr_chat_send(GaimConnection * gc, int id, const char *im, GaimMessageFlags flags)
|
|
2016 {
|
|
2017 zephyr_triple *zt;
|
|
2018 const char *sig;
|
|
2019 GaimConversation *gconv1;
|
|
2020 GaimConvChat *gcc;
|
|
2021 char *inst;
|
|
2022 char *recipient;
|
|
2023 zephyr_account *zephyr = gc->proto_data;
|
|
2024
|
|
2025 zt = find_sub_by_id(gc->proto_data,id);
|
|
2026 if (!zt)
|
|
2027 /* this should never happen. */
|
|
2028 return -EINVAL;
|
|
2029
|
|
2030 sig = zephyr_get_signature();
|
|
2031
|
|
2032 gconv1 = gaim_find_conversation_with_account(GAIM_CONV_TYPE_CHAT, zt->name,
|
|
2033 gc->account);
|
|
2034 gcc = gaim_conversation_get_chat_data(gconv1);
|
|
2035
|
|
2036 if (!(inst = (char *)gaim_conv_chat_get_topic(gcc)))
|
|
2037 inst = g_strdup("PERSONAL");
|
|
2038
|
|
2039 if (!g_ascii_strcasecmp(zt->recipient, "*"))
|
|
2040 recipient = local_zephyr_normalize(zephyr,"");
|
|
2041 else
|
|
2042 recipient = local_zephyr_normalize(zephyr,zt->recipient);
|
|
2043
|
|
2044 zephyr_send_message(zephyr,zt->class,inst,recipient,im,sig,"");
|
|
2045 return 0;
|
|
2046 }
|
|
2047
|
|
2048
|
|
2049 static int zephyr_send_im(GaimConnection * gc, const char *who, const char *im, GaimMessageFlags flags)
|
|
2050 {
|
|
2051 const char *sig;
|
|
2052 zephyr_account *zephyr = gc->proto_data;
|
|
2053 if (flags & GAIM_MESSAGE_AUTO_RESP)
|
|
2054 sig = "Automated reply:";
|
|
2055 else {
|
|
2056 sig = zephyr_get_signature();
|
|
2057 }
|
|
2058 zephyr_send_message(zephyr,"MESSAGE","PERSONAL",local_zephyr_normalize(zephyr,who),im,sig,"");
|
|
2059
|
|
2060 return 1;
|
|
2061 }
|
|
2062
|
|
2063 /* Munge the outgoing zephyr so that any quotes or backslashes are
|
|
2064 escaped and do not confuse tzc: */
|
|
2065
|
|
2066 static char* zephyr_tzc_escape_msg(const char *message)
|
|
2067 {
|
|
2068 int pos = 0;
|
|
2069 int pos2 = 0;
|
|
2070 char *newmsg;
|
|
2071
|
|
2072 if (message && (strlen(message) > 0)) {
|
|
2073 newmsg = g_new0(char,1+strlen(message)*2);
|
|
2074 while(pos < strlen(message)) {
|
|
2075 if (message[pos]=='\\') {
|
|
2076 newmsg[pos2]='\\';
|
|
2077 newmsg[pos2+1]='\\';
|
|
2078 pos2+=2;
|
|
2079 }
|
|
2080 else if (message[pos]=='"') {
|
|
2081 newmsg[pos2]='\\';
|
|
2082 newmsg[pos2+1]='"';
|
|
2083 pos2+=2;
|
|
2084 }
|
|
2085 else {
|
|
2086 newmsg[pos2] = message[pos];
|
|
2087 pos2++;
|
|
2088 }
|
|
2089 pos++;
|
|
2090 }
|
|
2091 } else {
|
|
2092 newmsg = g_strdup("");
|
|
2093 }
|
|
2094 /* fprintf(stderr,"newmsg %s message %s\n",newmsg,message); */
|
|
2095 return newmsg;
|
|
2096 }
|
|
2097
|
|
2098 char* zephyr_tzc_deescape_str(const char *message)
|
|
2099 {
|
|
2100 int pos = 0;
|
|
2101 int pos2 = 0;
|
|
2102 char *newmsg;
|
|
2103
|
|
2104 if (message && (strlen(message) > 0)) {
|
|
2105 newmsg = g_new0(char,strlen(message)+1);
|
|
2106 while(pos < strlen(message)) {
|
|
2107 if (message[pos]=='\\') {
|
|
2108 pos++;
|
|
2109 }
|
|
2110 newmsg[pos2] = message[pos];
|
|
2111 pos++;pos2++;
|
|
2112 }
|
|
2113 newmsg[pos2]='\0';
|
|
2114 } else {
|
|
2115 newmsg = g_strdup("");
|
|
2116 }
|
|
2117
|
|
2118 return newmsg;
|
|
2119 }
|
|
2120
|
|
2121 static int zephyr_send_message(zephyr_account *zephyr,char* zclass, char* instance, char* recipient, const char *im,
|
|
2122 const char *sig, char *opcode)
|
|
2123 {
|
|
2124
|
|
2125 /* (From the tzc source)
|
|
2126 * emacs sends something of the form:
|
|
2127 * ((class . "MESSAGE")
|
|
2128 * (auth . t)
|
|
2129 * (recipients ("PERSONAL" . "bovik") ("test" . ""))
|
|
2130 * (sender . "bovik")
|
|
2131 * (message . ("Harry Bovik" "my zgram"))
|
|
2132 * )
|
|
2133 */
|
|
2134 char *html_buf;
|
|
2135 char *html_buf2;
|
|
2136 html_buf = html_to_zephyr(im);
|
|
2137 html_buf2 = gaim_unescape_html(html_buf);
|
|
2138
|
|
2139 if(use_tzc(zephyr)) {
|
|
2140 char* zsendstr;
|
|
2141 /* CMU cclub tzc doesn't grok opcodes for now */
|
|
2142 char* tzc_sig = zephyr_tzc_escape_msg(sig);
|
|
2143 char *tzc_body = zephyr_tzc_escape_msg(html_buf2);
|
|
2144 zsendstr = g_strdup_printf("((tzcfodder . send) (class . \"%s\") (auth . t) (recipients (\"%s\" . \"%s\")) (message . (\"%s\" \"%s\")) ) \n",
|
|
2145 zclass, instance, recipient, tzc_sig, tzc_body);
|
|
2146 /* fprintf(stderr,"zsendstr = %s\n",zsendstr); */
|
|
2147 write(zephyr->totzc[ZEPHYR_FD_WRITE],zsendstr,strlen(zsendstr));
|
|
2148 g_free(zsendstr);
|
|
2149 } else if (use_zeph02(zephyr)) {
|
|
2150 ZNotice_t notice;
|
|
2151 char *buf = g_strdup_printf("%s%c%s", sig, '\0', html_buf2);
|
|
2152 bzero((char *)¬ice, sizeof(notice));
|
|
2153
|
|
2154 notice.z_kind = ACKED;
|
|
2155 notice.z_port = 0;
|
|
2156 notice.z_opcode = "";
|
|
2157 notice.z_class = zclass;
|
|
2158 notice.z_class_inst = instance;
|
|
2159 notice.z_recipient = recipient;
|
|
2160 notice.z_sender = 0;
|
|
2161 notice.z_default_format = "Class $class, Instance $instance:\n" "To: @bold($recipient) at $time $date\n" "From: @bold($1) <$sender>\n\n$2";
|
|
2162 notice.z_message_len = strlen(html_buf2) + strlen(sig) + 2;
|
|
2163 notice.z_message = buf;
|
|
2164 notice.z_opcode = g_strdup(opcode);
|
|
2165 gaim_debug_info("zephyr","About to send notice");
|
|
2166 if (! ZSendNotice(¬ice, ZAUTH) == ZERR_NONE) {
|
|
2167 /* XXX handle errors here */
|
|
2168 return 0;
|
|
2169 }
|
|
2170 gaim_debug_info("zephyr","notice sent");
|
|
2171 g_free(buf);
|
|
2172 }
|
|
2173
|
|
2174 g_free(html_buf2);
|
|
2175 g_free(html_buf);
|
|
2176
|
|
2177 return 1;
|
|
2178 }
|
|
2179
|
|
2180 char *local_zephyr_normalize(zephyr_account *zephyr,const char *orig)
|
|
2181 {
|
|
2182 /*
|
|
2183 Basically the inverse of zephyr_strip_local_realm
|
|
2184 */
|
|
2185 char* buf;
|
|
2186
|
|
2187 if (!g_ascii_strcasecmp(orig, "")) {
|
|
2188 return g_strdup("");
|
|
2189 }
|
|
2190
|
|
2191 if (strchr(orig,'@')) {
|
|
2192 buf = g_strdup_printf("%s",orig);
|
|
2193 } else {
|
|
2194 buf = g_strdup_printf("%s@%s",orig,zephyr->realm);
|
|
2195 }
|
|
2196 return buf;
|
|
2197 }
|
|
2198
|
|
2199 static void zephyr_zloc(GaimConnection *gc, const char *who)
|
|
2200 {
|
|
2201 ZAsyncLocateData_t ald;
|
|
2202 zephyr_account *zephyr = gc->proto_data;
|
|
2203 gchar* normalized_who = local_zephyr_normalize(zephyr,who);
|
|
2204
|
|
2205 if (use_zeph02(zephyr)) {
|
|
2206 if (ZRequestLocations(normalized_who, &ald, UNACKED, ZAUTH) == ZERR_NONE) {
|
|
2207 zephyr->pending_zloc_names = g_list_append(zephyr->pending_zloc_names,
|
|
2208 g_strdup(normalized_who));
|
|
2209 } else {
|
|
2210 /* XXX deal with errors somehow */
|
|
2211 }
|
|
2212 } else if (use_tzc(zephyr)) {
|
|
2213 char* zlocstr = g_strdup_printf("((tzcfodder . zlocate) \"%s\")\n",normalized_who);
|
|
2214 zephyr->pending_zloc_names = g_list_append(zephyr->pending_zloc_names, g_strdup(normalized_who));
|
|
2215 write(zephyr->totzc[ZEPHYR_FD_WRITE],zlocstr,strlen(zlocstr));
|
|
2216 g_free(zlocstr);
|
|
2217 }
|
|
2218 }
|
|
2219
|
|
2220 static void zephyr_set_status(GaimAccount *account, GaimStatus *status) {
|
|
2221 zephyr_account *zephyr = gaim_account_get_connection(account)->proto_data;
|
|
2222 GaimStatusPrimitive primitive = gaim_status_type_get_primitive(gaim_status_get_type(status));
|
|
2223
|
|
2224 if (zephyr->away) {
|
|
2225 g_free(zephyr->away);
|
|
2226 zephyr->away=NULL;
|
|
2227 }
|
|
2228
|
|
2229 if (primitive == GAIM_STATUS_AWAY) {
|
|
2230 zephyr->away = g_strdup(gaim_status_get_attr_string(status,"message"));
|
|
2231 }
|
|
2232 else if (primitive == GAIM_STATUS_AVAILABLE) {
|
|
2233 if (use_zeph02(zephyr)) {
|
|
2234 ZSetLocation(zephyr->exposure);
|
|
2235 }
|
|
2236 else {
|
|
2237 char *zexpstr = g_strdup_printf("((tzcfodder . set-location) (hostname . \"%s\") (exposure . \"%s\"))\n",zephyr->ourhost,zephyr->exposure);
|
|
2238 write(zephyr->totzc[ZEPHYR_FD_WRITE],zexpstr,strlen(zexpstr));
|
|
2239 g_free(zexpstr);
|
|
2240 }
|
|
2241 }
|
|
2242 else if (primitive == GAIM_STATUS_INVISIBLE) {
|
|
2243 /* XXX handle errors */
|
|
2244 if (use_zeph02(zephyr)) {
|
|
2245 ZSetLocation(EXPOSE_OPSTAFF);
|
|
2246 } else {
|
|
2247 char *zexpstr = g_strdup_printf("((tzcfodder . set-location) (hostname . \"%s\") (exposure . \"%s\"))\n",zephyr->ourhost,EXPOSE_OPSTAFF);
|
|
2248 write(zephyr->totzc[ZEPHYR_FD_WRITE],zexpstr,strlen(zexpstr));
|
|
2249 g_free(zexpstr);
|
|
2250 }
|
|
2251 }
|
|
2252 }
|
|
2253
|
|
2254 static GList *zephyr_status_types(GaimAccount *account)
|
|
2255 {
|
|
2256 GaimStatusType *type;
|
|
2257 GList *types = NULL;
|
|
2258
|
|
2259 /* zephyr has several exposures
|
|
2260 NONE (where you are hidden, and zephyrs to you are in practice silently dropped -- yes this is wrong)
|
|
2261 OPSTAFF "hidden"
|
|
2262 REALM-VISIBLE visible to people in local realm
|
|
2263 REALM-ANNOUNCED REALM-VISIBLE+ plus your logins/logouts are announced to <login,username,*>
|
|
2264 NET-VISIBLE REALM-ANNOUNCED, plus visible to people in foreign realm
|
|
2265 NET-ANNOUNCED NET-VISIBLE, plus logins/logouts are announced to <login,username,*>
|
|
2266
|
|
2267 Online will set the user to the exposure they have in their options (defaulting to REALM-VISIBLE),
|
|
2268 Hidden, will set the user's exposure to OPSTAFF
|
|
2269
|
|
2270 Away won't change their exposure but will set an auto away message (for IMs only)
|
|
2271 */
|
|
2272
|
|
2273 type = gaim_status_type_new(GAIM_STATUS_AVAILABLE, NULL, NULL, TRUE);
|
|
2274 types = g_list_append(types,type);
|
|
2275
|
|
2276 type = gaim_status_type_new(GAIM_STATUS_INVISIBLE, NULL, NULL, TRUE);
|
|
2277 types = g_list_append(types,type);
|
|
2278
|
|
2279 type = gaim_status_type_new_with_attrs(
|
|
2280 GAIM_STATUS_AWAY, NULL, NULL, TRUE, TRUE, FALSE,
|
|
2281 "message", _("Message"), gaim_value_new(GAIM_TYPE_STRING),
|
|
2282 NULL);
|
|
2283 types = g_list_append(types, type);
|
|
2284
|
|
2285 type = gaim_status_type_new(GAIM_STATUS_OFFLINE, NULL, NULL, TRUE);
|
|
2286 types = g_list_append(types,type);
|
|
2287
|
|
2288 return types;
|
|
2289 }
|
|
2290
|
|
2291 static GList *zephyr_chat_info(GaimConnection * gc)
|
|
2292 {
|
|
2293 GList *m = NULL;
|
|
2294 struct proto_chat_entry *pce;
|
|
2295
|
|
2296 pce = g_new0(struct proto_chat_entry, 1);
|
|
2297
|
|
2298 pce->label = _("_Class:");
|
|
2299 pce->identifier = "class";
|
|
2300 m = g_list_append(m, pce);
|
|
2301
|
|
2302 pce = g_new0(struct proto_chat_entry, 1);
|
|
2303
|
|
2304 pce->label = _("_Instance:");
|
|
2305 pce->identifier = "instance";
|
|
2306 m = g_list_append(m, pce);
|
|
2307
|
|
2308 pce = g_new0(struct proto_chat_entry, 1);
|
|
2309
|
|
2310 pce->label = _("_Recipient:");
|
|
2311 pce->identifier = "recipient";
|
|
2312 m = g_list_append(m, pce);
|
|
2313
|
|
2314 return m;
|
|
2315 }
|
|
2316
|
|
2317 /* Called when the server notifies us a message couldn't get sent */
|
|
2318
|
|
2319 static void zephyr_subscribe_failed(GaimConnection *gc,char * z_class, char *z_instance, char * z_recipient, char* z_galaxy)
|
|
2320 {
|
|
2321 gchar* subscribe_failed = g_strdup_printf(_("Attempt to subscribe to %s,%s,%s failed"), z_class, z_instance,z_recipient);
|
|
2322 gaim_notify_error(gc,"", subscribe_failed, NULL);
|
|
2323 g_free(subscribe_failed);
|
|
2324 }
|
|
2325
|
|
2326 static char *zephyr_get_chat_name(GHashTable *data) {
|
|
2327 gchar* zclass = g_hash_table_lookup(data,"class");
|
|
2328 gchar* inst = g_hash_table_lookup(data,"instance");
|
|
2329 gchar* recipient = g_hash_table_lookup(data, "recipient");
|
|
2330 if (!zclass) /* This should never happen */
|
|
2331 zclass = "";
|
|
2332 if (!inst)
|
|
2333 inst = "*";
|
|
2334 if (!recipient)
|
|
2335 recipient = "";
|
|
2336 return g_strdup_printf("%s,%s,%s",zclass,inst,recipient);
|
|
2337 }
|
|
2338
|
|
2339
|
|
2340 static void zephyr_join_chat(GaimConnection * gc, GHashTable * data)
|
|
2341 {
|
|
2342 /* ZSubscription_t sub; */
|
|
2343 zephyr_triple *zt1, *zt2;
|
|
2344 const char *classname;
|
|
2345 const char *instname;
|
|
2346 const char *recip;
|
|
2347 zephyr_account *zephyr=gc->proto_data;
|
|
2348 classname = g_hash_table_lookup(data, "class");
|
|
2349 instname = g_hash_table_lookup(data, "instance");
|
|
2350 recip = g_hash_table_lookup(data, "recipient");
|
|
2351
|
|
2352
|
|
2353 if (!classname)
|
|
2354 return;
|
|
2355
|
|
2356 if (!g_ascii_strcasecmp(classname,"%host%"))
|
|
2357 classname = g_strdup(zephyr->ourhost);
|
|
2358 if (!g_ascii_strcasecmp(classname,"%canon%"))
|
|
2359 classname = g_strdup(zephyr->ourhostcanon);
|
|
2360
|
|
2361 if (!instname || !strlen(instname))
|
|
2362 instname = "*";
|
|
2363
|
|
2364 if (!g_ascii_strcasecmp(instname,"%host%"))
|
|
2365 instname = g_strdup(zephyr->ourhost);
|
|
2366 if (!g_ascii_strcasecmp(instname,"%canon%"))
|
|
2367 instname = g_strdup(zephyr->ourhostcanon);
|
|
2368
|
|
2369 if (!recip || (*recip == '*'))
|
|
2370 recip = "";
|
|
2371 if (!g_ascii_strcasecmp(recip, "%me%"))
|
|
2372 recip = zephyr->username;
|
|
2373
|
|
2374 zt1 = new_triple(gc->proto_data,classname, instname, recip);
|
|
2375 zt2 = find_sub_by_triple(gc->proto_data,zt1);
|
|
2376 if (zt2) {
|
|
2377 free_triple(zt1);
|
|
2378 if (!zt2->open) {
|
|
2379 if (!g_ascii_strcasecmp(instname,"*"))
|
|
2380 instname = "PERSONAL";
|
|
2381 serv_got_joined_chat(gc, zt2->id, zt2->name);
|
|
2382 zephyr_chat_set_topic(gc,zt2->id,instname);
|
|
2383 zt2->open = TRUE;
|
|
2384 }
|
|
2385 return;
|
|
2386 }
|
|
2387
|
|
2388 /* sub.zsub_class = zt1->class;
|
|
2389 sub.zsub_classinst = zt1->instance;
|
|
2390 sub.zsub_recipient = zt1->recipient; */
|
|
2391
|
|
2392 if (zephyr_subscribe_to(zephyr,zt1->class,zt1->instance,zt1->recipient,NULL) != ZERR_NONE) {
|
|
2393 /* XXX output better subscription information */
|
|
2394 zephyr_subscribe_failed(gc,zt1->class,zt1->instance,zt1->recipient,NULL);
|
|
2395 free_triple(zt1);
|
|
2396 return;
|
|
2397 }
|
|
2398
|
|
2399 zephyr->subscrips = g_slist_append(zephyr->subscrips, zt1);
|
|
2400 zt1->open = TRUE;
|
|
2401 serv_got_joined_chat(gc, zt1->id, zt1->name);
|
|
2402 if (!g_ascii_strcasecmp(instname,"*"))
|
|
2403 instname = "PERSONAL";
|
|
2404 zephyr_chat_set_topic(gc,zt1->id,instname);
|
|
2405 }
|
|
2406
|
|
2407 static void zephyr_chat_leave(GaimConnection * gc, int id)
|
|
2408 {
|
|
2409 zephyr_triple *zt;
|
|
2410 zephyr_account *zephyr = gc->proto_data;
|
|
2411 zt = find_sub_by_id(zephyr,id);
|
|
2412
|
|
2413 if (zt) {
|
|
2414 zt->open = FALSE;
|
|
2415 zt->id = ++(zephyr->last_id);
|
|
2416 }
|
|
2417 }
|
|
2418
|
|
2419 static GaimChat *zephyr_find_blist_chat(GaimAccount *account, const char *name)
|
|
2420 {
|
|
2421 GaimBlistNode *gnode, *cnode;
|
|
2422
|
|
2423 /* XXX needs to be %host%,%canon%, and %me% clean */
|
|
2424 for(gnode = gaim_get_blist()->root; gnode; gnode = gnode->next) {
|
|
2425 for(cnode = gnode->child; cnode; cnode = cnode->next) {
|
|
2426 GaimChat *chat = (GaimChat*)cnode;
|
|
2427 char *zclass, *inst, *recip;
|
|
2428 char** triple;
|
|
2429 if(!GAIM_BLIST_NODE_IS_CHAT(cnode))
|
|
2430 continue;
|
|
2431 if(chat->account !=account)
|
|
2432 continue;
|
|
2433 if(!(zclass = g_hash_table_lookup(chat->components, "class")))
|
|
2434 continue;
|
|
2435 if(!(inst = g_hash_table_lookup(chat->components, "instance")))
|
|
2436 inst = g_strdup("");
|
|
2437 if(!(recip = g_hash_table_lookup(chat->components, "recipient")))
|
|
2438 recip = g_strdup("");
|
|
2439 /* gaim_debug_info("zephyr","in zephyr_find_blist_chat name: %s\n",name?name:""); */
|
|
2440 triple = g_strsplit(name,",",3);
|
|
2441 if (!g_ascii_strcasecmp(triple[0],zclass) && !g_ascii_strcasecmp(triple[1],inst) && !g_ascii_strcasecmp(triple[2],recip))
|
|
2442 return chat;
|
|
2443
|
|
2444 }
|
|
2445 }
|
|
2446 return NULL;
|
|
2447 }
|
|
2448 static const char *zephyr_list_icon(GaimAccount * a, GaimBuddy * b)
|
|
2449 {
|
|
2450 return "zephyr";
|
|
2451 }
|
|
2452
|
|
2453 static unsigned int zephyr_send_typing(GaimConnection *gc, const char *who, GaimTypingState state) {
|
|
2454 gchar *recipient;
|
|
2455 zephyr_account *zephyr = gc->proto_data;
|
|
2456 if (use_tzc(zephyr))
|
|
2457 return 0;
|
|
2458
|
|
2459 if (state == GAIM_NOT_TYPING)
|
|
2460 return 0;
|
|
2461
|
|
2462 /* XXX We probably should care if this fails. Or maybe we don't want to */
|
|
2463 if (!who) {
|
|
2464 gaim_debug_info("zephyr", "who is null\n");
|
|
2465 recipient = local_zephyr_normalize(zephyr,"");
|
|
2466 } else {
|
|
2467 char *comma = strrchr(who, ',');
|
|
2468 /* Don't ping broadcast (chat) recipients */
|
|
2469 /* The strrchr case finds a realm-stripped broadcast subscription
|
|
2470 e.g. comma is the last character in the string */
|
|
2471 if (comma && ( (*(comma+1) == '\0') || (*(comma+1) == '@')))
|
|
2472 return 0;
|
|
2473
|
|
2474 recipient = local_zephyr_normalize(zephyr,who);
|
|
2475 }
|
|
2476
|
|
2477 gaim_debug_info("zephyr","about to send typing notification to %s\n",recipient);
|
|
2478 zephyr_send_message(zephyr,"MESSAGE","PERSONAL",recipient,"","","PING");
|
|
2479 gaim_debug_info("zephyr","sent typing notification\n");
|
|
2480
|
|
2481 /*
|
|
2482 * TODO: Is this correct? It means we will call
|
|
2483 * serv_send_typing(gc, who, GAIM_TYPING) once every 15 seconds
|
|
2484 * until the Gaim user stops typing.
|
|
2485 */
|
|
2486 return ZEPHYR_TYPING_SEND_TIMEOUT;
|
|
2487 }
|
|
2488
|
|
2489
|
|
2490
|
|
2491 static void zephyr_chat_set_topic(GaimConnection * gc, int id, const char *topic)
|
|
2492 {
|
|
2493 zephyr_triple *zt;
|
|
2494 GaimConversation *gconv;
|
|
2495 GaimConvChat *gcc;
|
|
2496 gchar *topic_utf8;
|
|
2497 zephyr_account* zephyr = gc->proto_data;
|
|
2498 char *sender = (char *)zephyr->username;
|
|
2499
|
|
2500 zt = find_sub_by_id(gc->proto_data,id);
|
|
2501 /* find_sub_by_id can return NULL */
|
|
2502 if (!zt)
|
|
2503 return;
|
|
2504 gconv = gaim_find_conversation_with_account(GAIM_CONV_TYPE_CHAT, zt->name,
|
|
2505 gc->account);
|
|
2506 gcc = gaim_conversation_get_chat_data(gconv);
|
|
2507
|
|
2508 topic_utf8 = zephyr_recv_convert(gc,(gchar *)topic,strlen(topic));
|
|
2509 gaim_conv_chat_set_topic(gcc,sender,topic_utf8);
|
|
2510 g_free(topic_utf8);
|
|
2511 return;
|
|
2512 }
|
|
2513
|
|
2514 /* commands */
|
|
2515
|
|
2516 static GaimCmdRet zephyr_gaim_cmd_msg(GaimConversation *conv,
|
|
2517 const char *cmd, char **args, char **error, void *data)
|
|
2518 {
|
|
2519 char *recipient;
|
|
2520 zephyr_account *zephyr = gaim_conversation_get_gc(conv)->proto_data;
|
|
2521 if (!g_ascii_strcasecmp(args[0],"*"))
|
|
2522 return GAIM_CMD_RET_FAILED; /* "*" is not a valid argument */
|
|
2523 else
|
|
2524 recipient = local_zephyr_normalize(zephyr,args[0]);
|
|
2525
|
|
2526 if (strlen(recipient) < 1)
|
|
2527 return GAIM_CMD_RET_FAILED; /* a null recipient is a chat message, not an IM */
|
|
2528
|
|
2529 if (zephyr_send_message(zephyr,"MESSAGE","PERSONAL",recipient,args[1],zephyr_get_signature(),""))
|
|
2530 return GAIM_CMD_RET_OK;
|
|
2531 else
|
|
2532 return GAIM_CMD_RET_FAILED;
|
|
2533 }
|
|
2534
|
|
2535 static GaimCmdRet zephyr_gaim_cmd_zlocate(GaimConversation *conv,
|
|
2536 const char *cmd, char **args, char **error, void *data)
|
|
2537 {
|
|
2538 zephyr_zloc(gaim_conversation_get_gc(conv),args[0]);
|
|
2539 return GAIM_CMD_RET_OK;
|
|
2540 }
|
|
2541
|
|
2542 static GaimCmdRet zephyr_gaim_cmd_instance(GaimConversation *conv,
|
|
2543 const char *cmd, char **args, char **error, void *data)
|
|
2544 {
|
|
2545 /* Currently it sets the instance with leading spaces and
|
|
2546 * all. This might not be the best thing to do, though having
|
|
2547 * one word isn't ideal either. */
|
|
2548
|
|
2549 GaimConvChat *gcc = gaim_conversation_get_chat_data(conv);
|
|
2550 int id = gcc->id;
|
|
2551 const char* instance = args[0];
|
|
2552 zephyr_chat_set_topic(gaim_conversation_get_gc(conv),id,instance);
|
|
2553 return GAIM_CMD_RET_OK;
|
|
2554 }
|
|
2555
|
|
2556 static GaimCmdRet zephyr_gaim_cmd_joinchat_cir(GaimConversation *conv,
|
|
2557 const char *cmd, char **args, char **error, void *data)
|
|
2558 {
|
|
2559 /* Join a new zephyr chat */
|
|
2560 GHashTable *triple = g_hash_table_new(NULL,NULL);
|
|
2561 g_hash_table_insert(triple,"class",args[0]);
|
|
2562 g_hash_table_insert(triple,"instance",args[1]);
|
|
2563 g_hash_table_insert(triple,"recipient",args[2]);
|
|
2564 zephyr_join_chat(gaim_conversation_get_gc(conv),triple);
|
|
2565 return GAIM_CMD_RET_OK;
|
|
2566 }
|
|
2567
|
|
2568 static GaimCmdRet zephyr_gaim_cmd_zi(GaimConversation *conv,
|
|
2569 const char *cmd, char **args, char **error, void *data)
|
|
2570 {
|
|
2571 /* args = instance, message */
|
|
2572 zephyr_account *zephyr = gaim_conversation_get_gc(conv)->proto_data;
|
|
2573 if ( zephyr_send_message(zephyr,"message",args[0],"",args[1],zephyr_get_signature(),""))
|
|
2574 return GAIM_CMD_RET_OK;
|
|
2575 else
|
|
2576 return GAIM_CMD_RET_FAILED;
|
|
2577 }
|
|
2578
|
|
2579 static GaimCmdRet zephyr_gaim_cmd_zci(GaimConversation *conv,
|
|
2580 const char *cmd, char **args, char **error, void *data)
|
|
2581 {
|
|
2582 /* args = class, instance, message */
|
|
2583 zephyr_account *zephyr = gaim_conversation_get_gc(conv)->proto_data;
|
|
2584 if ( zephyr_send_message(zephyr,args[0],args[1],"",args[2],zephyr_get_signature(),""))
|
|
2585 return GAIM_CMD_RET_OK;
|
|
2586 else
|
|
2587 return GAIM_CMD_RET_FAILED;
|
|
2588 }
|
|
2589
|
|
2590 static GaimCmdRet zephyr_gaim_cmd_zcir(GaimConversation *conv,
|
|
2591 const char *cmd, char **args, char **error, void *data)
|
|
2592 {
|
|
2593 /* args = class, instance, recipient, message */
|
|
2594 zephyr_account *zephyr = gaim_conversation_get_gc(conv)->proto_data;
|
|
2595 if ( zephyr_send_message(zephyr,args[0],args[1],args[2],args[3],zephyr_get_signature(),""))
|
|
2596 return GAIM_CMD_RET_OK;
|
|
2597 else
|
|
2598 return GAIM_CMD_RET_FAILED;
|
|
2599 }
|
|
2600
|
|
2601 static GaimCmdRet zephyr_gaim_cmd_zir(GaimConversation *conv,
|
|
2602 const char *cmd, char **args, char **error, void *data)
|
|
2603 {
|
|
2604 /* args = instance, recipient, message */
|
|
2605 zephyr_account *zephyr = gaim_conversation_get_gc(conv)->proto_data;
|
|
2606 if ( zephyr_send_message(zephyr,"message",args[0],args[1],args[2],zephyr_get_signature(),""))
|
|
2607 return GAIM_CMD_RET_OK;
|
|
2608 else
|
|
2609 return GAIM_CMD_RET_FAILED;
|
|
2610 }
|
|
2611
|
|
2612 static GaimCmdRet zephyr_gaim_cmd_zc(GaimConversation *conv,
|
|
2613 const char *cmd, char **args, char **error, void *data)
|
|
2614 {
|
|
2615 /* args = class, message */
|
|
2616 zephyr_account *zephyr = gaim_conversation_get_gc(conv)->proto_data;
|
|
2617 if ( zephyr_send_message(zephyr,args[0],"PERSONAL","",args[1],zephyr_get_signature(),""))
|
|
2618 return GAIM_CMD_RET_OK;
|
|
2619 else
|
|
2620 return GAIM_CMD_RET_FAILED;
|
|
2621 }
|
|
2622
|
|
2623 static void zephyr_register_slash_commands()
|
|
2624 {
|
|
2625
|
|
2626 gaim_cmd_register("msg","ws", GAIM_CMD_P_PRPL,
|
|
2627 GAIM_CMD_FLAG_IM | GAIM_CMD_FLAG_CHAT | GAIM_CMD_FLAG_PRPL_ONLY,
|
|
2628 "prpl-zephyr",
|
|
2629 zephyr_gaim_cmd_msg, _("msg <nick> <message>: Send a private message to a user"), NULL);
|
|
2630
|
|
2631 gaim_cmd_register("zlocate","w", GAIM_CMD_P_PRPL,
|
|
2632 GAIM_CMD_FLAG_IM | GAIM_CMD_FLAG_CHAT | GAIM_CMD_FLAG_PRPL_ONLY,
|
|
2633 "prpl-zephyr",
|
|
2634 zephyr_gaim_cmd_zlocate, _("zlocate <nick>: Locate user"), NULL);
|
|
2635
|
|
2636 gaim_cmd_register("zl","w", GAIM_CMD_P_PRPL,
|
|
2637 GAIM_CMD_FLAG_IM | GAIM_CMD_FLAG_CHAT | GAIM_CMD_FLAG_PRPL_ONLY,
|
|
2638 "prpl-zephyr",
|
|
2639 zephyr_gaim_cmd_zlocate, _("zl <nick>: Locate user"), NULL);
|
|
2640
|
|
2641 gaim_cmd_register("instance","s", GAIM_CMD_P_PRPL,
|
|
2642 GAIM_CMD_FLAG_CHAT | GAIM_CMD_FLAG_PRPL_ONLY,
|
|
2643 "prpl-zephyr",
|
|
2644 zephyr_gaim_cmd_instance, _("instance <instance>: Set the instance to be used on this class"), NULL);
|
|
2645
|
|
2646 gaim_cmd_register("inst","s", GAIM_CMD_P_PRPL,
|
|
2647 GAIM_CMD_FLAG_CHAT | GAIM_CMD_FLAG_PRPL_ONLY,
|
|
2648 "prpl-zephyr",
|
|
2649 zephyr_gaim_cmd_instance, _("inst <instance>: Set the instance to be used on this class"), NULL);
|
|
2650
|
|
2651 gaim_cmd_register("topic","s", GAIM_CMD_P_PRPL,
|
|
2652 GAIM_CMD_FLAG_CHAT | GAIM_CMD_FLAG_PRPL_ONLY,
|
|
2653 "prpl-zephyr",
|
|
2654 zephyr_gaim_cmd_instance, _("topic <instance>: Set the instance to be used on this class"), NULL);
|
|
2655
|
|
2656 gaim_cmd_register("sub", "www", GAIM_CMD_P_PRPL,
|
|
2657 GAIM_CMD_FLAG_IM | GAIM_CMD_FLAG_CHAT | GAIM_CMD_FLAG_PRPL_ONLY,
|
|
2658 "prpl-zephyr",
|
|
2659 zephyr_gaim_cmd_joinchat_cir,
|
|
2660 _("sub <class> <instance> <recipient>: Join a new chat"), NULL);
|
|
2661
|
|
2662 gaim_cmd_register("zi","ws", GAIM_CMD_P_PRPL,
|
|
2663 GAIM_CMD_FLAG_IM | GAIM_CMD_FLAG_CHAT | GAIM_CMD_FLAG_PRPL_ONLY,
|
|
2664 "prpl-zephyr",
|
|
2665 zephyr_gaim_cmd_zi, _("zi <instance>: Send a message to <message,<i>instance</i>,*>"), NULL);
|
|
2666
|
|
2667 gaim_cmd_register("zci","wws",GAIM_CMD_P_PRPL,
|
|
2668 GAIM_CMD_FLAG_IM | GAIM_CMD_FLAG_CHAT | GAIM_CMD_FLAG_PRPL_ONLY,
|
|
2669 "prpl-zephyr",
|
|
2670 zephyr_gaim_cmd_zci,
|
|
2671 _("zci <class> <instance>: Send a message to <<i>class</i>,<i>instance</i>,*>"), NULL);
|
|
2672
|
|
2673 gaim_cmd_register("zcir","wwws",GAIM_CMD_P_PRPL,
|
|
2674 GAIM_CMD_FLAG_IM | GAIM_CMD_FLAG_CHAT | GAIM_CMD_FLAG_PRPL_ONLY,
|
|
2675 "prpl-zephyr",
|
|
2676 zephyr_gaim_cmd_zcir,
|
|
2677 _("zcir <class> <instance> <recipient>: Send a message to <<i>class</i>,<i>instance</i>,<i>recipient</i>>"), NULL);
|
|
2678
|
|
2679 gaim_cmd_register("zir","wws",GAIM_CMD_P_PRPL,
|
|
2680 GAIM_CMD_FLAG_IM | GAIM_CMD_FLAG_CHAT | GAIM_CMD_FLAG_PRPL_ONLY,
|
|
2681 "prpl-zephyr",
|
|
2682 zephyr_gaim_cmd_zir,
|
|
2683 _("zir <instance> <recipient>: Send a message to <MESSAGE,<i>instance</i>,<i>recipient</i>>"), NULL);
|
|
2684
|
|
2685 gaim_cmd_register("zc","ws", GAIM_CMD_P_PRPL,
|
|
2686 GAIM_CMD_FLAG_IM | GAIM_CMD_FLAG_CHAT | GAIM_CMD_FLAG_PRPL_ONLY,
|
|
2687 "prpl-zephyr",
|
|
2688 zephyr_gaim_cmd_zc, _("zc <class>: Send a message to <<i>class</i>,PERSONAL,*>"), NULL);
|
|
2689
|
|
2690 }
|
|
2691
|
|
2692
|
|
2693 static void
|
|
2694 zephyr_add_deny(GaimConnection *gc, const char *who)
|
|
2695 {
|
|
2696 gaim_privacy_deny_add(gc->account,who,1);
|
|
2697 }
|
|
2698
|
|
2699 static void
|
|
2700 zephyr_remove_deny(GaimConnection *gc, const char *who)
|
|
2701 {
|
|
2702 gaim_privacy_deny_remove(gc->account,who,1);
|
|
2703 }
|
|
2704
|
|
2705 static void
|
|
2706 zephyr_add_permit(GaimConnection *gc, const char *who)
|
|
2707 {
|
|
2708 gaim_privacy_permit_add(gc->account,who,1);
|
|
2709 }
|
|
2710
|
|
2711 static void
|
|
2712 zephyr_remove_permit(GaimConnection *gc, const char *who)
|
|
2713 {
|
|
2714 gaim_privacy_permit_remove(gc->account,who,1);
|
|
2715 }
|
|
2716
|
|
2717 static void
|
|
2718 zephyr_set_permit_deny(GaimConnection *gc)
|
|
2719 {
|
|
2720 /* This doesn't have to do anything, since really, we can just check account->perm_deny when deciding whether to di */
|
|
2721 return;
|
|
2722 }
|
|
2723 static int zephyr_resubscribe(GaimConnection *gc)
|
|
2724 {
|
|
2725 /* Resubscribe to the in-memory list of subscriptions and also
|
|
2726 unsubscriptions*/
|
|
2727 zephyr_account *zephyr = gc->proto_data;
|
|
2728 GSList *s = zephyr->subscrips;
|
|
2729 zephyr_triple *zt;
|
|
2730 while (s) {
|
|
2731 zt = s->data;
|
|
2732 /* XXX We really should care if this fails */
|
|
2733 zephyr_subscribe_to(zephyr,zt->class,zt->instance,zt->recipient,NULL);
|
|
2734 s = s->next;
|
|
2735 }
|
|
2736 /* XXX handle unsubscriptions */
|
|
2737 return 1;
|
|
2738 }
|
|
2739
|
|
2740
|
|
2741 static void zephyr_action_resubscribe(GaimPluginAction *action)
|
|
2742 {
|
|
2743
|
|
2744 GaimConnection *gc = (GaimConnection *) action->context;
|
|
2745 zephyr_resubscribe(gc);
|
|
2746 }
|
|
2747
|
|
2748
|
|
2749 static void zephyr_action_get_subs_from_server(GaimPluginAction *action)
|
|
2750 {
|
|
2751 GaimConnection *gc = (GaimConnection *) action->context;
|
|
2752 zephyr_account *zephyr = gc->proto_data;
|
|
2753 gchar *title;
|
|
2754 int retval, nsubs, one,i;
|
|
2755 ZSubscription_t subs;
|
|
2756 if (use_zeph02(zephyr)) {
|
|
2757 GString* subout = g_string_new("Subscription list<br>");
|
|
2758
|
|
2759 title = g_strdup_printf("Server subscriptions for %s", zephyr->username);
|
|
2760
|
|
2761 if (zephyr->port == 0) {
|
|
2762 gaim_debug_error("zephyr", "error while retrieving port");
|
|
2763 return;
|
|
2764 }
|
|
2765 if ((retval = ZRetrieveSubscriptions(zephyr->port,&nsubs)) != ZERR_NONE) {
|
|
2766 /* XXX better error handling */
|
|
2767 gaim_debug_error("zephyr", "error while retrieving subscriptions from server");
|
|
2768 return;
|
|
2769 }
|
|
2770 for(i=0;i<nsubs;i++) {
|
|
2771 one = 1;
|
|
2772 if ((retval = ZGetSubscriptions(&subs,&one)) != ZERR_NONE) {
|
|
2773 /* XXX better error handling */
|
|
2774 gaim_debug_error("zephyr", "error while retrieving individual subscription");
|
|
2775 return;
|
|
2776 }
|
|
2777 g_string_append_printf(subout, "Class %s Instance %s Recipient %s<br>",
|
|
2778 subs.zsub_class, subs.zsub_classinst,
|
|
2779 subs.zsub_recipient);
|
|
2780 }
|
|
2781 gaim_notify_formatted(gc, title, title, NULL, subout->str, NULL, NULL);
|
|
2782 } else {
|
|
2783 /* XXX fix */
|
|
2784 gaim_notify_error(gc,"","tzc doesn't support this action",NULL);
|
|
2785 }
|
|
2786 }
|
|
2787
|
|
2788
|
|
2789 static GList *zephyr_actions(GaimPlugin *plugin, gpointer context)
|
|
2790 {
|
|
2791 GList *list = NULL;
|
|
2792 GaimPluginAction *act = NULL;
|
|
2793
|
|
2794 act = gaim_plugin_action_new(_("Resubscribe"), zephyr_action_resubscribe);
|
|
2795 list = g_list_append(list, act);
|
|
2796
|
|
2797 act = gaim_plugin_action_new(_("Retrieve subscriptions from server"), zephyr_action_get_subs_from_server);
|
|
2798 list = g_list_append(list,act);
|
|
2799
|
|
2800 return list;
|
|
2801 }
|
|
2802
|
|
2803 static GaimPlugin *my_protocol = NULL;
|
|
2804
|
|
2805 static GaimPluginProtocolInfo prpl_info = {
|
|
2806 OPT_PROTO_CHAT_TOPIC | OPT_PROTO_NO_PASSWORD,
|
|
2807 NULL, /* ??? user_splits */
|
|
2808 NULL, /* ??? protocol_options */
|
|
2809 NO_BUDDY_ICONS,
|
|
2810 zephyr_list_icon,
|
|
2811 NULL, /* ??? list_emblems */
|
|
2812 NULL, /* ??? status_text */
|
|
2813 NULL, /* ??? tooltip_text */
|
|
2814 zephyr_status_types, /* status_types */
|
|
2815 NULL, /* ??? blist_node_menu - probably all useful actions are already handled*/
|
|
2816 zephyr_chat_info, /* chat_info */
|
|
2817 NULL, /* chat_info_defaults */
|
|
2818 zephyr_login, /* login */
|
|
2819 zephyr_close, /* close */
|
|
2820 zephyr_send_im, /* send_im */
|
|
2821 NULL, /* XXX set info (Location?) */
|
|
2822 zephyr_send_typing, /* send_typing */
|
|
2823 zephyr_zloc, /* get_info */
|
|
2824 zephyr_set_status, /* set_status */
|
|
2825 NULL, /* ??? set idle */
|
|
2826 NULL, /* change password */
|
|
2827 NULL, /* add_buddy */
|
|
2828 NULL, /* add_buddies */
|
|
2829 NULL, /* remove_buddy */
|
|
2830 NULL, /* remove_buddies */
|
|
2831 zephyr_add_permit, /* add_permit */
|
|
2832 zephyr_add_deny, /* add_deny */
|
|
2833 zephyr_remove_permit, /* remove_permit */
|
|
2834 zephyr_remove_deny, /* remove_deny */
|
|
2835 zephyr_set_permit_deny, /* set_permit_deny */
|
|
2836 zephyr_join_chat, /* join_chat */
|
|
2837 NULL, /* reject_chat -- No chat invites*/
|
|
2838 zephyr_get_chat_name, /* get_chat_name */
|
|
2839 NULL, /* chat_invite -- No chat invites*/
|
|
2840 zephyr_chat_leave, /* chat_leave */
|
|
2841 NULL, /* chat_whisper -- No "whispering"*/
|
|
2842 zephyr_chat_send, /* chat_send */
|
|
2843 NULL, /* keepalive -- Not necessary*/
|
|
2844 NULL, /* register_user -- Not supported*/
|
|
2845 NULL, /* XXX get_cb_info */
|
|
2846 NULL, /* get_cb_away */
|
|
2847 NULL, /* alias_buddy */
|
|
2848 NULL, /* group_buddy */
|
|
2849 NULL, /* rename_group */
|
|
2850 NULL, /* buddy_free */
|
|
2851 NULL, /* convo_closed */
|
|
2852 NULL, /* normalize */
|
|
2853 NULL, /* XXX set_buddy_icon */
|
|
2854 NULL, /* remove_group */
|
|
2855 NULL, /* XXX get_cb_real_name */
|
|
2856 zephyr_chat_set_topic, /* set_chat_topic */
|
|
2857 zephyr_find_blist_chat, /* find_blist_chat */
|
|
2858 NULL, /* roomlist_get_list */
|
|
2859 NULL, /* roomlist_cancel */
|
|
2860 NULL, /* roomlist_expand_category */
|
|
2861 NULL, /* can_receive_file */
|
|
2862 NULL, /* send_file */
|
|
2863 NULL, /* new_xfer */
|
|
2864 NULL, /* offline_message */
|
|
2865 NULL, /* whiteboard_prpl_ops */
|
|
2866 };
|
|
2867
|
|
2868 static GaimPluginInfo info = {
|
|
2869 GAIM_PLUGIN_MAGIC,
|
|
2870 GAIM_MAJOR_VERSION,
|
|
2871 GAIM_MINOR_VERSION,
|
|
2872 GAIM_PLUGIN_PROTOCOL, /**< type */
|
|
2873 NULL, /**< ui_requirement */
|
|
2874 0, /**< flags */
|
|
2875 NULL, /**< dependencies */
|
|
2876 GAIM_PRIORITY_DEFAULT, /**< priority */
|
|
2877
|
|
2878 "prpl-zephyr", /**< id */
|
|
2879 "Zephyr", /**< name */
|
|
2880 VERSION, /**< version */
|
|
2881 /** summary */
|
|
2882 N_("Zephyr Protocol Plugin"),
|
|
2883 /** description */
|
|
2884 N_("Zephyr Protocol Plugin"),
|
|
2885 NULL, /**< author */
|
|
2886 GAIM_WEBSITE, /**< homepage */
|
|
2887
|
|
2888 NULL, /**< load */
|
|
2889 NULL, /**< unload */
|
|
2890 NULL, /**< destroy */
|
|
2891
|
|
2892 NULL, /**< ui_info */
|
|
2893 &prpl_info, /**< extra_info */
|
|
2894 NULL,
|
|
2895 zephyr_actions
|
|
2896 };
|
|
2897
|
|
2898 static void init_plugin(GaimPlugin * plugin)
|
|
2899 {
|
|
2900 GaimAccountOption *option;
|
|
2901 char *tmp = get_exposure_level();
|
|
2902
|
|
2903 option = gaim_account_option_bool_new(_("Use tzc"), "use_tzc", FALSE);
|
|
2904 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
|
|
2905
|
|
2906 option = gaim_account_option_string_new(_("tzc command"), "tzc_command", "/usr/bin/tzc -e %s");
|
|
2907 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
|
|
2908
|
|
2909 option = gaim_account_option_bool_new(_("Export to .anyone"), "write_anyone", FALSE);
|
|
2910 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
|
|
2911
|
|
2912 option = gaim_account_option_bool_new(_("Export to .zephyr.subs"), "write_zsubs", FALSE);
|
|
2913 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
|
|
2914
|
|
2915 option = gaim_account_option_bool_new(_("Import from .anyone"), "read_anyone", TRUE);
|
|
2916 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
|
|
2917
|
|
2918 option = gaim_account_option_bool_new(_("Import from .zephyr.subs"), "read_zsubs", TRUE);
|
|
2919 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
|
|
2920
|
|
2921 option = gaim_account_option_string_new(_("Realm"), "realm", "");
|
|
2922 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
|
|
2923
|
|
2924 option = gaim_account_option_string_new(_("Exposure"), "exposure_level", tmp?tmp: EXPOSE_REALMVIS);
|
|
2925 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
|
|
2926
|
|
2927 option = gaim_account_option_string_new(_("Encoding"), "encoding", ZEPHYR_FALLBACK_CHARSET);
|
|
2928 prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
|
|
2929
|
|
2930 my_protocol = plugin;
|
|
2931 zephyr_register_slash_commands();
|
|
2932 }
|
|
2933
|
|
2934 GAIM_INIT_PLUGIN(zephyr, init_plugin, info);
|