[gaim-migrate @ 5229]
[pidgin-git.git] / src / protocols / zephyr / zephyr.c
blob910f8aefd1bc7ac1ff4a0c459313f364e1552b69
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
2 /*
3 * gaim
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/
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26 #ifdef HAVE_CONFIG_H
27 #include "config.h"
28 #endif
30 #include <string.h>
31 #include <stdlib.h>
32 #include <errno.h>
33 #include "gaim.h"
34 #include "prpl.h"
35 #include "zephyr/zephyr.h"
37 extern Code_t ZGetLocations(ZLocations_t *, int *);
38 extern Code_t ZSetLocation(char *);
39 extern Code_t ZUnsetLocation();
41 typedef struct _zframe zframe;
42 typedef struct _zephyr_triple zephyr_triple;
44 /* struct I need for zephyr_to_html */
45 struct _zframe {
46 /* true for everything but @color, since inside the parens of that one is
47 * the color. */
48 gboolean has_closer;
49 /* </i>, </font>, </b>, etc. */
50 char *closing;
51 /* text including the opening html thingie. */
52 GString *text;
53 struct _zframe *enclosing;
56 struct _zephyr_triple {
57 char *class;
58 char *instance;
59 char *recipient;
60 char *name;
61 gboolean open;
62 int id;
65 #define z_call(func) if (func != ZERR_NONE)\
66 return;
67 #define z_call_r(func) if (func != ZERR_NONE)\
68 return TRUE;
69 #define z_call_s(func, err) if (func != ZERR_NONE) {\
70 hide_login_progress(zgc, err);\
71 signoff(zgc);\
72 return;\
75 static char *zephyr_normalize(const char *);
77 /* this is so bad, and if Zephyr weren't so fucked up to begin with I
78 * wouldn't do this. but it is so i will. */
79 static guint32 nottimer = 0;
80 static guint32 loctimer = 0;
81 struct gaim_connection *zgc = NULL;
82 static GList *pending_zloc_names = NULL;
83 static GSList *subscrips = NULL;
84 static int last_id = 0;
86 /* just for debugging
87 static void handle_unknown(ZNotice_t notice)
89 debug_printf("z_packet: %s\n", notice.z_packet);
90 debug_printf("z_version: %s\n", notice.z_version);
91 debug_printf("z_kind: %d\n", notice.z_kind);
92 debug_printf("z_class: %s\n", notice.z_class);
93 debug_printf("z_class_inst: %s\n", notice.z_class_inst);
94 debug_printf("z_opcode: %s\n", notice.z_opcode);
95 debug_printf("z_sender: %s\n", notice.z_sender);
96 debug_printf("z_recipient: %s\n", notice.z_recipient);
97 debug_printf("z_message: %s\n", notice.z_message);
98 debug_printf("z_message_len: %d\n", notice.z_message_len);
99 debug_printf("\n");
103 static zephyr_triple *new_triple(const char *c, const char *i, const char *r)
105 zephyr_triple *zt;
106 zt = g_new0(zephyr_triple, 1);
107 zt->class = g_strdup(c);
108 zt->instance = g_strdup(i);
109 zt->recipient = g_strdup(r);
110 zt->name = g_strdup_printf("%s,%s,%s", c, i, r);
111 zt->id = ++last_id;
112 zt->open = FALSE;
113 return zt;
116 static void free_triple(zephyr_triple *zt)
118 g_free(zt->class);
119 g_free(zt->instance);
120 g_free(zt->recipient);
121 g_free(zt->name);
122 g_free(zt);
125 /* returns true if zt1 is a subset of zt2, i.e. zt2 has the same thing or
126 * wildcards in each field of zt1. */
127 static gboolean triple_subset(zephyr_triple *zt1, zephyr_triple *zt2)
129 if (g_ascii_strcasecmp(zt2->class, zt1->class) &&
130 g_ascii_strcasecmp(zt2->class, "*")) {
131 return FALSE;
133 if (g_ascii_strcasecmp(zt2->instance, zt1->instance) &&
134 g_ascii_strcasecmp(zt2->instance, "*")) {
135 return FALSE;
137 if (g_ascii_strcasecmp(zt2->recipient, zt1->recipient) &&
138 g_ascii_strcasecmp(zt2->recipient, "*")) {
139 return FALSE;
141 return TRUE;
144 static zephyr_triple *find_sub_by_triple(zephyr_triple *zt)
146 zephyr_triple *curr_t;
147 GSList *curr = subscrips;
148 while (curr) {
149 curr_t = curr->data;
150 if (triple_subset(zt, curr_t))
151 return curr_t;
152 curr = curr->next;
154 return NULL;
157 static zephyr_triple *find_sub_by_id(int id)
159 zephyr_triple *zt;
160 GSList *curr = subscrips;
161 while (curr) {
162 zt = curr->data;
163 if (zt->id == id)
164 return zt;
165 curr = curr->next;
167 return NULL;
170 /* utility macros that are useful for zephyr_to_html */
172 #define IS_OPENER(c) ((c == '{') || (c == '[') || (c == '(') || (c == '<'))
173 #define IS_CLOSER(c) ((c == '}') || (c == ']') || (c == ')') || (c == '>'))
175 /* this parses zephyr formatting and converts it to html. For example, if
176 * you pass in "@{@color(blue)@i(hello)}" you should get out
177 * "<font color=blue><i>hello</i></font>". */
178 static char *zephyr_to_html(char *message)
180 int len, cnt;
181 zframe *frames, *curr;
182 char *ret;
184 frames = g_new(zframe, 1);
185 frames->text = g_string_new("");
186 frames->enclosing = NULL;
187 frames->closing = "";
188 frames->has_closer = FALSE;
190 len = strlen(message);
191 cnt = 0;
192 while (cnt <= len) {
193 if (message[cnt] == '@') {
194 zframe *new_f;
195 char *buf;
196 int end;
197 for (end=1; (cnt+end) <= len &&
198 !IS_OPENER(message[cnt+end]); end++);
199 buf = g_new0(char, end);
200 if (end) {
201 g_snprintf(buf, end, "%s", message+cnt+1);
203 if (!g_ascii_strcasecmp(buf, "italic") ||
204 !g_ascii_strcasecmp(buf, "i")) {
205 new_f = g_new(zframe, 1);
206 new_f->enclosing = frames;
207 new_f->text = g_string_new("<i>");
208 new_f->closing = "</i>";
209 new_f->has_closer = TRUE;
210 frames = new_f;
211 cnt += end+1; /* cnt points to char after opener */
212 } else if (!g_ascii_strcasecmp(buf, "bold")
213 || !g_ascii_strcasecmp(buf, "b")) {
214 new_f = g_new(zframe, 1);
215 new_f->enclosing = frames;
216 new_f->text = g_string_new("<b>");
217 new_f->closing = "</b>";
218 new_f->has_closer = TRUE;
219 frames = new_f;
220 cnt += end+1;
221 } else if (!g_ascii_strcasecmp(buf, "color")) {
222 cnt += end+1;
223 new_f = g_new(zframe, 1);
224 new_f->enclosing = frames;
225 new_f->text = g_string_new("<font color=");
226 for (; (cnt <= len) && !IS_CLOSER(message[cnt]); cnt++) {
227 g_string_append_c(new_f->text, message[cnt]);
229 cnt++; /* point to char after closer */
230 g_string_append_c(new_f->text, '>');
231 new_f->closing = "</font>";
232 new_f->has_closer = FALSE;
233 frames = new_f;
234 } else if (!g_ascii_strcasecmp(buf, "")) {
235 new_f = g_new(zframe, 1);
236 new_f->enclosing = frames;
237 new_f->text = g_string_new("");
238 new_f->closing = "";
239 new_f->has_closer = TRUE;
240 frames = new_f;
241 cnt += end+1; /* cnt points to char after opener */
242 } else {
243 if ((cnt+end) > len) {
244 g_string_append_c(frames->text, '@');
245 cnt++;
246 } else {
247 /* unrecognized thingie. act like it's not there, but we
248 * still need to take care of the corresponding closer,
249 * make a frame that does nothing. */
250 new_f = g_new(zframe, 1);
251 new_f->enclosing = frames;
252 new_f->text = g_string_new("");
253 new_f->closing = "";
254 new_f->has_closer = TRUE;
255 frames = new_f;
256 cnt += end+1; /* cnt points to char after opener */
259 } else if (IS_CLOSER(message[cnt])) {
260 zframe *popped;
261 gboolean last_had_closer;
262 if (frames->enclosing) {
263 do {
264 popped = frames;
265 frames = frames->enclosing;
266 g_string_append(frames->text, popped->text->str);
267 g_string_append(frames->text, popped->closing);
268 g_string_free(popped->text, TRUE);
269 last_had_closer = popped->has_closer;
270 g_free(popped);
271 } while (frames && frames->enclosing && !last_had_closer);
272 } else {
273 g_string_append_c(frames->text, message[cnt]);
275 cnt++;
276 } else if (message[cnt] == '\n') {
277 g_string_append(frames->text, "<br>");
278 cnt++;
279 } else {
280 g_string_append_c(frames->text, message[cnt++]);
283 /* go through all the stuff that they didn't close */
284 while (frames->enclosing) {
285 curr = frames;
286 g_string_append(frames->enclosing->text, frames->text->str);
287 g_string_append(frames->enclosing->text, frames->closing);
288 g_string_free(frames->text, TRUE);
289 frames = frames->enclosing;
290 g_free(curr);
292 ret = frames->text->str;
293 g_string_free(frames->text, FALSE);
294 g_free(frames);
295 return ret;
298 static gboolean pending_zloc(char *who)
300 GList *curr;
301 for (curr = pending_zloc_names; curr != NULL; curr = curr->next) {
302 if (!g_ascii_strcasecmp(who, (char*)curr->data)) {
303 g_free((char*)curr->data);
304 pending_zloc_names = g_list_remove(pending_zloc_names, curr->data);
305 return TRUE;
308 return FALSE;
311 static void handle_message(ZNotice_t notice, struct sockaddr_in from)
313 if (!g_ascii_strcasecmp(notice.z_class, LOGIN_CLASS)) {
314 /* well, we'll be updating in 20 seconds anyway, might as well ignore this. */
315 } else if (!g_ascii_strcasecmp(notice.z_class, LOCATE_CLASS)) {
316 if (!g_ascii_strcasecmp(notice.z_opcode, LOCATE_LOCATE)) {
317 int nlocs;
318 char *user;
319 struct buddy *b;
321 if (ZParseLocations(&notice, NULL, &nlocs, &user) != ZERR_NONE)
322 return;
323 if ((b = gaim_find_buddy(zgc->account, user)) == NULL) {
324 char *e = strchr(user, '@');
325 if (e) *e = '\0';
326 b = gaim_find_buddy(zgc->account, user);
328 if (!b) {
329 free(user);
330 return;
332 if (pending_zloc(b->name)) {
333 ZLocations_t locs;
334 int one = 1;
335 GString *str = g_string_new("");
336 g_string_append_printf(str, "<b>User:</b> %s<br>"
337 "<b>Alias:</b> %s<br>",
338 b->name, b->alias);
339 if (!nlocs) {
340 g_string_append_printf(str, "<br>Hidden or not logged-in");
342 for (; nlocs > 0; nlocs--) {
343 ZGetLocations(&locs, &one);
344 g_string_append_printf(str, "<br>At %s since %s", locs.host,
345 locs.time);
347 g_show_info_text(NULL, NULL, 2, str->str, NULL);
348 g_string_free(str, TRUE);
349 } else
350 serv_got_update(zgc, b->name, nlocs, 0, 0, 0, 0);
352 free(user);
354 } else {
355 char *buf, *buf2;
356 char *send_inst;
357 char *realmptr;
358 char *sendertmp;
359 char *ptr = notice.z_message + strlen(notice.z_message) + 1;
360 int len = notice.z_message_len - (ptr - notice.z_message);
361 int away;
362 if (len > 0) {
363 buf = g_malloc(len + 1);
364 g_snprintf(buf, len + 1, "%s", ptr);
365 g_strchomp(buf);
366 buf2 = zephyr_to_html(buf);
367 g_free(buf);
368 if (!g_ascii_strcasecmp(notice.z_class, "MESSAGE") &&
369 !g_ascii_strcasecmp(notice.z_class_inst, "PERSONAL")) {
370 if (!g_ascii_strcasecmp(notice.z_message, "Automated reply:"))
371 away = TRUE;
372 else
373 away = FALSE;
374 serv_got_im(zgc, notice.z_sender, buf2, 0, time(NULL), -1);
375 } else {
376 zephyr_triple *zt1, *zt2;
377 zt1 = new_triple(notice.z_class, notice.z_class_inst,
378 notice.z_recipient);
379 zt2 = find_sub_by_triple(zt1);
380 if (!zt2) {
381 /* we shouldn't be subscribed to this message. ignore. */
382 } else {
383 if (!zt2->open) {
384 zt2->open = TRUE;
385 serv_got_joined_chat(zgc, zt2->id, zt2->name);
387 /* If the person is in the default Realm, then strip the
388 Realm from the sender field */
389 sendertmp = g_strdup_printf("%s",notice.z_sender);
390 if ((realmptr = strchr(sendertmp,'@')) != NULL) {
391 realmptr++;
392 if (!g_ascii_strcasecmp(realmptr,ZGetRealm())) {
393 realmptr--;
394 sprintf(realmptr,"%c",'\0');
395 send_inst = g_strdup_printf("%s %s",sendertmp,
396 notice.z_class_inst);
397 } else {
398 send_inst = g_strdup_printf("%s %s",notice.z_sender,
399 notice.z_class_inst);
401 } else {
402 send_inst = g_strdup_printf("%s %s",sendertmp,notice.z_class_inst);
404 serv_got_chat_in(zgc, zt2->id, send_inst, FALSE,
405 buf2, time(NULL));
406 g_free(sendertmp);
407 g_free(send_inst);
409 free_triple(zt1);
411 g_free(buf2);
416 static gint check_notify(gpointer data)
418 while (ZPending()) {
419 ZNotice_t notice;
420 struct sockaddr_in from;
421 z_call_r(ZReceiveNotice(&notice, &from));
423 switch (notice.z_kind) {
424 case UNSAFE:
425 case UNACKED:
426 case ACKED:
427 handle_message(notice, from);
428 break;
429 default:
430 /* we'll just ignore things for now */
431 debug_printf("ZEPHYR: Unhandled Notice\n");
432 break;
435 ZFreeNotice(&notice);
438 return TRUE;
441 static gint check_loc(gpointer data)
443 GaimBlistNode *gnode,*bnode;
444 ZAsyncLocateData_t ald;
446 ald.user = NULL;
447 memset(&(ald.uid), 0, sizeof(ZUnique_Id_t));
448 ald.version = NULL;
450 for(gnode = gaim_get_blist()->root; gnode; gnode = gnode->next) {
451 if(!GAIM_BLIST_NODE_IS_GROUP(gnode))
452 continue;
453 for(bnode = gnode->child; bnode; bnode = bnode->next) {
454 struct buddy *b = (struct buddy *)bnode;
455 if(!GAIM_BLIST_NODE_IS_BUDDY(bnode))
456 continue;
457 if(b->account->gc == zgc) {
458 char *chk;
459 chk = zephyr_normalize(b->name);
460 /* doesn't matter if this fails or not; we'll just move on to the next one */
461 ZRequestLocations(chk, &ald, UNACKED, ZAUTH);
462 free(ald.user);
463 free(ald.version);
468 return TRUE;
471 static char *get_exposure_level()
473 char *exposure = ZGetVariable("exposure");
475 if (!exposure)
476 return EXPOSE_REALMVIS;
477 if (!g_ascii_strcasecmp(exposure, EXPOSE_NONE))
478 return EXPOSE_NONE;
479 if (!g_ascii_strcasecmp(exposure, EXPOSE_OPSTAFF))
480 return EXPOSE_OPSTAFF;
481 if (!g_ascii_strcasecmp(exposure, EXPOSE_REALMANN))
482 return EXPOSE_REALMANN;
483 if (!g_ascii_strcasecmp(exposure, EXPOSE_NETVIS))
484 return EXPOSE_NETVIS;
485 if (!g_ascii_strcasecmp(exposure, EXPOSE_NETANN))
486 return EXPOSE_NETANN;
487 return EXPOSE_REALMVIS;
490 static void strip_comments(char *str)
492 char *tmp = strchr(str, '#');
493 if (tmp)
494 *tmp = '\0';
495 g_strchug(str);
496 g_strchomp(str);
499 static void process_zsubs()
501 FILE *f;
502 gchar *fname;
503 gchar buff[BUFSIZ];
505 fname = g_strdup_printf("%s/.zephyr.subs", gaim_home_dir());
506 f = fopen(fname, "r");
507 if (f) {
508 char **triple;
509 ZSubscription_t sub;
510 char *recip;
511 while (fgets(buff, BUFSIZ, f)) {
512 strip_comments(buff);
513 if (buff[0]) {
514 triple = g_strsplit(buff, ",", 3);
515 if (triple[0] && triple[1] ) {
516 /* char *tmp = g_strdup_printf("%s@%s", g_getenv("USER"),
517 ZGetRealm());*/
518 char *tmp = g_strdup_printf("%s",ZGetSender());
519 char *atptr;
520 sub.zsub_class = triple[0];
521 sub.zsub_classinst = triple[1];
522 if(triple[2] == NULL) {
523 recip = g_malloc0(1);
524 } else if (!g_ascii_strcasecmp(triple[2], "%me%")) {
525 recip = g_strdup_printf("%s",ZGetSender());
526 } else if (!g_ascii_strcasecmp(triple[2], "*")) {
527 /* wildcard
528 * form of class,instance,* */
529 recip = g_malloc0(1);
530 } else if (!g_ascii_strcasecmp(triple[2], tmp)) {
531 /* form of class,instance,aatharuv@ATHENA.MIT.EDU */
532 recip = g_strdup(triple[2]);
533 } else if ((atptr = strchr(triple[2], '@')) != NULL) {
534 /* form of class,instance,*@ANDREW.CMU.EDU
535 * class,instance,@ANDREW.CMU.EDU
536 * If realm is local realm, blank recipient, else
537 * @REALM-NAME
539 char *realmat = g_strdup_printf("@%s", ZGetRealm());
540 if (!g_ascii_strcasecmp(atptr, realmat))
541 recip = g_malloc0(1);
542 else
543 recip = g_strdup(atptr);
544 g_free(realmat);
545 } else {
546 recip = g_strdup(triple[2]);
548 g_free(tmp);
549 sub.zsub_recipient = recip;
550 if (ZSubscribeTo(&sub, 1, 0) != ZERR_NONE) {
551 debug_printf("Zephyr: Couldn't subscribe to %s, %s, "
552 "%s\n",
553 sub.zsub_class,
554 sub.zsub_classinst,
555 sub.zsub_recipient);
557 subscrips = g_slist_append(subscrips,
558 new_triple(triple[0], triple[1], recip));
559 g_free(recip);
561 g_strfreev(triple);
567 static void process_anyone()
569 FILE *fd;
570 gchar buff[BUFSIZ], *filename;
571 struct group *g;
572 struct buddy *b;
574 if (!(g = gaim_find_group(_("Anyone")))) {
575 g = gaim_group_new(_("Anyone"));
576 gaim_blist_add_group(g, NULL);
579 filename = g_strconcat(gaim_home_dir(), "/.anyone", NULL);
580 if ((fd = fopen(filename, "r")) != NULL) {
581 while (fgets(buff, BUFSIZ, fd)) {
582 strip_comments(buff);
583 if (buff[0]) {
584 b = gaim_buddy_new(zgc->account, buff, NULL);
585 gaim_blist_add_buddy(b, g, NULL);
588 fclose(fd);
590 g_free(filename);
593 static void zephyr_login(struct gaim_account *account)
595 ZSubscription_t sub;
597 if (zgc) {
598 do_error_dialog("Already logged in with Zephyr", "Because Zephyr uses your system username, you are unable to "
599 "have multiple accounts on it when logged in as the same user.", GAIM_ERROR);
600 return;
603 zgc = new_gaim_conn(account);
605 z_call_s(ZInitialize(), "Couldn't initialize zephyr");
606 z_call_s(ZOpenPort(NULL), "Couldn't open port");
607 z_call_s(ZSetLocation(get_exposure_level()), "Couldn't set location");
609 sub.zsub_class = "MESSAGE";
610 sub.zsub_classinst = "PERSONAL";
611 sub.zsub_recipient = ZGetSender();
613 /* we don't care if this fails. i'm lying right now. */
614 if (ZSubscribeTo(&sub, 1, 0) != ZERR_NONE) {
615 debug_printf("Zephyr: Couldn't subscribe to messages!\n");
618 account_online(zgc);
619 serv_finish_login(zgc);
621 process_anyone();
622 process_zsubs();
624 nottimer = g_timeout_add(100, check_notify, NULL);
625 loctimer = g_timeout_add(20000, check_loc, NULL);
628 static void write_zsubs()
630 GSList *s = subscrips;
631 zephyr_triple *zt;
632 FILE *fd;
633 char *fname;
635 char** triple;
636 fname = g_strdup_printf("%s/.zephyr.subs", gaim_home_dir());
637 fd = fopen(fname, "w");
639 if (!fd) {
640 g_free(fname);
641 return;
644 while (s) {
645 zt = s->data;
646 triple = g_strsplit(zt->name,",",3);
647 if (triple[2] != NULL) {
648 if (!g_ascii_strcasecmp(triple[2], "")) {
649 fprintf(fd, "%s,%s,*\n", triple[0], triple[1]);
650 } else if (!g_ascii_strcasecmp(triple[2], ZGetSender())) {
651 fprintf(fd, "%s,%s,%%me%%\n",triple[0],triple[1]);
652 } else {
653 fprintf(fd, "%s\n", zt->name);
655 } else {
656 fprintf(fd, "%s,%s,*\n",triple[0], triple[1]);
658 g_free(triple);
659 s = s->next;
661 g_free(fname);
662 fclose(fd);
665 static void write_anyone()
667 GaimBlistNode *gnode,*bnode;
668 struct buddy *b;
669 char *ptr, *fname, *ptr2;
670 FILE *fd;
672 fname = g_strdup_printf("%s/.anyone", gaim_home_dir());
673 fd = fopen(fname, "w");
674 if (!fd) {
675 g_free(fname);
676 return;
679 for(gnode = gaim_get_blist()->root; gnode; gnode = gnode->next) {
680 if(!GAIM_BLIST_NODE_IS_GROUP(gnode))
681 continue;
682 for(bnode = gnode->child; bnode; bnode = bnode->next) {
683 if(!GAIM_BLIST_NODE_IS_BUDDY(bnode))
684 continue;
685 b = (struct buddy *)bnode;
686 if(b->account->gc == zgc) {
687 if ((ptr = strchr(b->name, '@')) != NULL) {
688 ptr2 = ptr + 1;
689 /* We should only strip the realm name if the principal
690 is in the user's realm
692 if (!g_ascii_strcasecmp(ptr2,ZGetRealm())) {
693 *ptr = '\0';
696 fprintf(fd, "%s\n", b->name);
697 if (ptr)
698 *ptr = '@';
703 fclose(fd);
704 g_free(fname);
707 static void zephyr_close(struct gaim_connection *gc)
709 GList *l;
710 GSList *s;
711 l = pending_zloc_names;
712 while (l) {
713 g_free((char*)l->data);
714 l = l->next;
716 g_list_free(pending_zloc_names);
718 write_anyone();
719 write_zsubs();
721 s = subscrips;
722 while (s) {
723 free_triple((zephyr_triple*)s->data);
724 s = s->next;
726 g_slist_free(subscrips);
728 if (nottimer)
729 g_source_remove(nottimer);
730 nottimer = 0;
731 if (loctimer)
732 g_source_remove(loctimer);
733 loctimer = 0;
734 zgc = NULL;
735 z_call(ZCancelSubscriptions(0));
736 z_call(ZUnsetLocation());
737 z_call(ZClosePort());
740 static void zephyr_add_buddy(struct gaim_connection *gc, const char *buddy) { }
741 static void zephyr_remove_buddy(struct gaim_connection *gc, char *buddy, char *group) { }
743 static int zephyr_chat_send(struct gaim_connection *gc, int id, char *im)
745 ZNotice_t notice;
746 zephyr_triple *zt;
747 char *buf;
748 const char *sig;
750 zt = find_sub_by_id(id);
751 if (!zt)
752 /* this should never happen. */
753 return -EINVAL;
755 sig = ZGetVariable("zwrite-signature");
756 if (!sig) {
757 sig = g_get_real_name();
759 buf = g_strdup_printf("%s%c%s", sig, '\0', im);
761 bzero((char *)&notice, sizeof(notice));
762 notice.z_kind = ACKED;
763 notice.z_port = 0;
764 notice.z_opcode = "";
765 notice.z_class = zt->class;
766 notice.z_class_inst = zt->instance;
767 if (!g_ascii_strcasecmp(zt->recipient, "*"))
768 notice.z_recipient = zephyr_normalize("");
769 else
770 notice.z_recipient = zephyr_normalize(zt->recipient);
771 notice.z_sender = 0;
772 notice.z_default_format =
773 "Class $class, Instance $instance:\n"
774 "To: @bold($recipient) at $time $date\n"
775 "From: @bold($1) <$sender>\n\n$2";
776 notice.z_message_len = strlen(im) + strlen(sig) + 4;
777 notice.z_message = buf;
778 ZSendNotice(&notice, ZAUTH);
779 g_free(buf);
780 return 0;
783 static int zephyr_send_im(struct gaim_connection *gc, char *who, char *im, int len, int flags) {
784 ZNotice_t notice;
785 char *buf;
786 const char *sig;
788 if (flags & IM_FLAG_AWAY)
789 sig = "Automated reply:";
790 else {
791 sig = ZGetVariable("zwrite-signature");
792 if (!sig) {
793 sig = g_get_real_name();
796 buf = g_strdup_printf("%s%c%s", sig, '\0', im);
798 bzero((char *)&notice, sizeof(notice));
799 notice.z_kind = ACKED;
800 notice.z_port = 0;
801 notice.z_opcode = "";
802 notice.z_class = "MESSAGE";
803 notice.z_class_inst = "PERSONAL";
804 notice.z_sender = 0;
805 notice.z_recipient = who;
806 notice.z_default_format =
807 "Class $class, Instance $instance:\n"
808 "To: @bold($recipient) at $time $date\n"
809 "From: @bold($1) <$sender>\n\n$2";
810 notice.z_message_len = strlen(im) + strlen(sig) + 4;
811 notice.z_message = buf;
812 ZSendNotice(&notice, ZAUTH);
813 g_free(buf);
814 return 1;
817 static char *zephyr_normalize(const char *orig)
819 static char buf[80];
820 if (strchr(orig, '@')) {
821 g_snprintf(buf, 80, "%s", orig);
822 } else {
823 g_snprintf(buf, 80, "%s@%s", orig, ZGetRealm());
825 return buf;
828 static void zephyr_zloc(struct gaim_connection *gc, char *who)
830 ZAsyncLocateData_t ald;
832 if (ZRequestLocations(zephyr_normalize(who), &ald, UNACKED, ZAUTH)
833 != ZERR_NONE) {
834 return;
836 pending_zloc_names = g_list_append(pending_zloc_names,
837 g_strdup(zephyr_normalize(who)));
840 static GList *zephyr_buddy_menu(struct gaim_connection *gc, char *who)
842 GList *m = NULL;
843 struct proto_buddy_menu *pbm;
845 pbm = g_new0(struct proto_buddy_menu, 1);
846 pbm->label = _("ZLocate");
847 pbm->callback = zephyr_zloc;
848 pbm->gc = gc;
849 m = g_list_append(m, pbm);
851 return m;
854 static void zephyr_set_away(struct gaim_connection *gc, char *state, char *msg)
856 if (gc->away) {
857 g_free(gc->away);
858 gc->away = NULL;
861 if (!g_ascii_strcasecmp(state, "Hidden")) {
862 ZSetLocation(EXPOSE_OPSTAFF);
863 gc->away = g_strdup("");
864 } else if (!g_ascii_strcasecmp(state, "Online"))
865 ZSetLocation(get_exposure_level());
866 else /* state is GAIM_AWAY_CUSTOM */ if (msg)
867 gc->away = g_strdup(msg);
870 static GList *zephyr_away_states(struct gaim_connection *gc)
872 GList *m = NULL;
874 m = g_list_append(m, "Online");
875 m = g_list_append(m, GAIM_AWAY_CUSTOM);
876 m = g_list_append(m, "Hidden");
878 return m;
881 static GList *zephyr_chat_info(struct gaim_connection *gc) {
882 GList *m = NULL;
883 struct proto_chat_entry *pce;
885 pce = g_new0(struct proto_chat_entry, 1);
886 pce->label = _("Class:");
887 m = g_list_append(m, pce);
889 pce = g_new0(struct proto_chat_entry, 1);
890 pce->label = _("Instance:");
891 m = g_list_append(m, pce);
893 pce = g_new0(struct proto_chat_entry, 1);
894 pce->label = _("Recipient:");
895 m = g_list_append(m, pce);
897 return m;
900 static void zephyr_join_chat(struct gaim_connection *gc, GList *data)
902 ZSubscription_t sub;
903 zephyr_triple *zt1, *zt2;
904 const char *classname;
905 const char *instname;
906 const char *recip;
908 if (!data || !data->next || !data->next->next)
909 return;
911 classname = data->data;
912 instname = data->next->data;
913 recip = data->next->next->data;
914 if (!g_ascii_strcasecmp(recip, "%me%"))
915 recip = ZGetSender();
917 zt1 = new_triple(classname, instname, recip);
918 zt2 = find_sub_by_triple(zt1);
919 if (zt2) {
920 free_triple(zt1);
921 if (!zt2->open)
922 serv_got_joined_chat(gc, zt2->id, zt2->name);
923 return;
926 sub.zsub_class = zt1->class;
927 sub.zsub_classinst = zt1->instance;
928 sub.zsub_recipient = zt1->recipient;
930 if (ZSubscribeTo(&sub, 1, 0) != ZERR_NONE) {
931 free_triple(zt1);
932 return;
935 subscrips = g_slist_append(subscrips, zt1);
936 zt1->open = TRUE;
937 serv_got_joined_chat(gc, zt1->id, zt1->name);
940 static void zephyr_chat_leave(struct gaim_connection *gc, int id)
942 zephyr_triple *zt;
943 zt = find_sub_by_id(id);
944 if (zt) {
945 zt->open = FALSE;
946 zt->id = ++last_id;
950 static struct prpl *my_protocol = NULL;
952 void zephyr_init(struct prpl *ret)
954 ret->protocol = PROTO_ZEPHYR;
955 ret->options = OPT_PROTO_NO_PASSWORD;
956 ret->name = g_strdup("Zephyr");
957 ret->login = zephyr_login;
958 ret->close = zephyr_close;
959 ret->add_buddy = zephyr_add_buddy;
960 ret->remove_buddy = zephyr_remove_buddy;
961 ret->send_im = zephyr_send_im;
962 ret->get_info = zephyr_zloc;
963 ret->normalize = zephyr_normalize;
964 ret->buddy_menu = zephyr_buddy_menu;
965 ret->away_states = zephyr_away_states;
966 ret->set_away = zephyr_set_away;
967 ret->chat_info = zephyr_chat_info;
968 ret->join_chat = zephyr_join_chat;
969 ret->chat_send = zephyr_chat_send;
970 ret->chat_leave = zephyr_chat_leave;
972 my_protocol = ret;
975 #ifndef STATIC
977 G_MODULE_EXPORT void gaim_prpl_init(struct prpl *prpl)
979 zephyr_init(prpl);
980 prpl->plug->desc.api_version = PLUGIN_API_VERSION;
984 #endif