source: zephyr.c @ 1a30f05

release-1.10release-1.8release-1.9
Last change on this file since 1a30f05 was fe3b017, checked in by David Benjamin <davidben@mit.edu>, 14 years ago
Correctly set the realm in outgoing zwrite errors Don't set the zwrite realm, as that only confuses things. Also take the realm (which we now don't set) into account when extracting the recipient.
  • Property mode set to 100644
File size: 36.3 KB
Line 
1#include <stdlib.h>
2#include <unistd.h>
3#include <sys/types.h>
4#include <sys/wait.h>
5#include <sys/stat.h>
6#include <string.h>
7#include "owl.h"
8
9#ifdef HAVE_LIBZEPHYR
10static GList *deferred_subs = NULL;
11
12typedef struct _owl_sub_list {                            /* noproto */
13  ZSubscription_t *subs;
14  int nsubs;
15} owl_sub_list;
16
17Code_t ZResetAuthentication(void);
18#endif
19
20#define HM_SVC_FALLBACK         htons((unsigned short) 2104)
21
22static char *owl_zephyr_dotfile(const char *name, const char *input)
23{
24  if (input != NULL)
25    return g_strdup(input);
26  else
27    return g_strdup_printf("%s/%s", owl_global_get_homedir(&g), name);
28}
29
30#ifdef HAVE_LIBZEPHYR
31void owl_zephyr_initialize(void)
32{
33  Code_t ret;
34  struct servent *sp;
35  struct sockaddr_in sin;
36  ZNotice_t req;
37
38  /*
39   * Code modified from libzephyr's ZhmStat.c
40   *
41   * Modified to add the fd to our select loop, rather than hanging
42   * until we get an ack.
43   */
44
45  if ((ret = ZOpenPort(NULL)) != ZERR_NONE) {
46    owl_function_error("Error opening Zephyr port: %s", error_message(ret));
47    return;
48  }
49
50  (void) memset(&sin, 0, sizeof(struct sockaddr_in));
51
52  sp = getservbyname(HM_SVCNAME, "udp");
53
54  sin.sin_port = (sp) ? sp->s_port : HM_SVC_FALLBACK;
55  sin.sin_family = AF_INET;
56
57  sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
58
59  (void) memset(&req, 0, sizeof(req));
60  req.z_kind = STAT;
61  req.z_port = 0;
62  req.z_class = zstr(HM_STAT_CLASS);
63  req.z_class_inst = zstr(HM_STAT_CLIENT);
64  req.z_opcode = zstr(HM_GIMMESTATS);
65  req.z_sender = zstr("");
66  req.z_recipient = zstr("");
67  req.z_default_format = zstr("");
68  req.z_message_len = 0;
69
70  if ((ret = ZSetDestAddr(&sin)) != ZERR_NONE) {
71    owl_function_error("Initializing Zephyr: %s", error_message(ret));
72    return;
73  }
74
75  if ((ret = ZSendNotice(&req, ZNOAUTH)) != ZERR_NONE) {
76    owl_function_error("Initializing Zephyr: %s", error_message(ret));
77    return;
78  }
79
80  owl_select_add_io_dispatch(ZGetFD(), OWL_IO_READ|OWL_IO_EXCEPT, &owl_zephyr_finish_initialization, NULL, NULL);
81}
82
83void owl_zephyr_finish_initialization(const owl_io_dispatch *d, void *data) {
84  Code_t code;
85  char *perl;
86
87  owl_select_remove_io_dispatch(d);
88
89  ZClosePort();
90
91  if ((code = ZInitialize()) != ZERR_NONE) {
92    owl_function_error("Initializing Zephyr: %s", error_message(code));
93    return;
94  }
95
96  if ((code = ZOpenPort(NULL)) != ZERR_NONE) {
97    owl_function_error("Initializing Zephyr: %s", error_message(code));
98    return;
99  }
100
101  owl_select_add_io_dispatch(ZGetFD(), OWL_IO_READ|OWL_IO_EXCEPT, &owl_zephyr_process_events, NULL, NULL);
102
103  owl_global_set_havezephyr(&g);
104
105  if(g.load_initial_subs) {
106    owl_zephyr_load_initial_subs();
107  }
108  while(deferred_subs != NULL) {
109    owl_sub_list *subs = deferred_subs->data;
110    owl_function_debugmsg("Loading %d deferred subs.", subs->nsubs);
111    owl_zephyr_loadsubs_helper(subs->subs, subs->nsubs);
112    deferred_subs = g_list_delete_link(deferred_subs, deferred_subs);
113    g_free(subs);
114  }
115
116  /* zlog in if we need to */
117  if (owl_global_is_startuplogin(&g)) {
118    owl_function_debugmsg("startup: doing zlog in");
119    owl_zephyr_zlog_in();
120  }
121  /* check pseudo-logins if we need to */
122  if (owl_global_is_pseudologins(&g)) {
123    owl_function_debugmsg("startup: checking pseudo-logins");
124    owl_function_zephyr_buddy_check(0);
125  }
126
127  perl = owl_perlconfig_execute("BarnOwl::Zephyr::_zephyr_startup()");
128  g_free(perl);
129
130  owl_select_add_pre_select_action(owl_zephyr_pre_select_action, NULL, NULL);
131}
132
133void owl_zephyr_load_initial_subs(void) {
134  int ret_sd, ret_bd, ret_u;
135
136  owl_function_debugmsg("startup: loading initial zephyr subs");
137
138  /* load default subscriptions */
139  ret_sd = owl_zephyr_loaddefaultsubs();
140
141  /* load Barnowl default subscriptions */
142  ret_bd = owl_zephyr_loadbarnowldefaultsubs();
143
144  /* load subscriptions from subs file */
145  ret_u = owl_zephyr_loadsubs(NULL, 0);
146
147  if (ret_sd || ret_bd || ret_u) {
148    owl_function_error("Error loading zephyr subscriptions");
149  }
150
151  /* load login subscriptions */
152  if (owl_global_is_loginsubs(&g)) {
153    owl_function_debugmsg("startup: loading login subs");
154    owl_function_loadloginsubs(NULL);
155  }
156}
157#else
158void owl_zephyr_initialize(void)
159{
160}
161#endif
162
163
164int owl_zephyr_shutdown(void)
165{
166#ifdef HAVE_LIBZEPHYR
167  if(owl_global_is_havezephyr(&g)) {
168    unsuball();
169    ZClosePort();
170  }
171#endif
172  return 0;
173}
174
175int owl_zephyr_zpending(void)
176{
177#ifdef HAVE_LIBZEPHYR
178  Code_t code;
179  if(owl_global_is_havezephyr(&g)) {
180    if((code = ZPending()) < 0) {
181      owl_function_debugmsg("Error (%s) in ZPending()\n",
182                            error_message(code));
183      return 0;
184    }
185    return code;
186  }
187#endif
188  return 0;
189}
190
191const char *owl_zephyr_get_realm(void)
192{
193#ifdef HAVE_LIBZEPHYR
194  if (owl_global_is_havezephyr(&g))
195    return(ZGetRealm());
196#endif
197  return "";
198}
199
200const char *owl_zephyr_get_sender(void)
201{
202#ifdef HAVE_LIBZEPHYR
203  if (owl_global_is_havezephyr(&g))
204    return(ZGetSender());
205#endif
206  return "";
207}
208
209#ifdef HAVE_LIBZEPHYR
210int owl_zephyr_loadsubs_helper(ZSubscription_t subs[], int count)
211{
212  int ret = 0;
213  Code_t code;
214
215  if (owl_global_is_havezephyr(&g)) {
216    int i;
217    /* sub without defaults */
218    code = ZSubscribeToSansDefaults(subs, count, 0);
219    if (code != ZERR_NONE) {
220      owl_function_error("Error subscribing to zephyr notifications: %s",
221                         error_message(code));
222      ret=-2;
223    }
224
225    /* free stuff */
226    for (i=0; i<count; i++) {
227      g_free(subs[i].zsub_class);
228      g_free(subs[i].zsub_classinst);
229      g_free(subs[i].zsub_recipient);
230    }
231
232    g_free(subs);
233  } else {
234    owl_sub_list *s = g_new(owl_sub_list, 1);
235    s->subs = subs;
236    s->nsubs = count;
237    deferred_subs = g_list_append(deferred_subs, s);
238  }
239
240  return ret;
241}
242#endif
243
244/* Load zephyr subscriptions from 'filename'.  If 'filename' is NULL,
245 * the default file $HOME/.zephyr.subs will be used.
246 *
247 * Returns 0 on success.  If the file does not exist, return -1 if
248 * 'error_on_nofile' is 1, otherwise return 0.  Return -1 if the file
249 * exists but can not be read.  Return -2 if there is a failure from
250 * zephyr to load the subscriptions.
251 */
252int owl_zephyr_loadsubs(const char *filename, int error_on_nofile)
253{
254#ifdef HAVE_LIBZEPHYR
255  FILE *file;
256  char *tmp, *start;
257  char *buffer = NULL;
258  char *subsfile;
259  ZSubscription_t *subs;
260  int subSize = 1024;
261  int count;
262  struct stat statbuff;
263
264  subs = g_new(ZSubscription_t, subSize);
265  subsfile = owl_zephyr_dotfile(".zephyr.subs", filename);
266
267  if (stat(subsfile, &statbuff) != 0) {
268    g_free(subsfile);
269    if (error_on_nofile == 1)
270      return -1;
271    return 0;
272  }
273
274  ZResetAuthentication();
275  count = 0;
276  file = fopen(subsfile, "r");
277  g_free(subsfile);
278  if (!file)
279    return -1;
280  while (owl_getline(&buffer, file)) {
281    if (buffer[0] == '#' || buffer[0] == '\n')
282        continue;
283
284    if (buffer[0] == '-')
285      start = buffer + 1;
286    else
287      start = buffer;
288
289    if (count >= subSize) {
290      subSize *= 2;
291      subs = g_renew(ZSubscription_t, subs, subSize);
292    }
293   
294    /* add it to the list of subs */
295    if ((tmp = strtok(start, ",\n\r")) == NULL)
296      continue;
297    subs[count].zsub_class = g_strdup(tmp);
298    if ((tmp=strtok(NULL, ",\n\r")) == NULL)
299      continue;
300    subs[count].zsub_classinst = g_strdup(tmp);
301    if ((tmp = strtok(NULL, " \t\n\r")) == NULL)
302      continue;
303    subs[count].zsub_recipient = g_strdup(tmp);
304
305    /* if it started with '-' then add it to the global punt list, and
306     * remove it from the list of subs. */
307    if (buffer[0] == '-') {
308      owl_function_zpunt(subs[count].zsub_class, subs[count].zsub_classinst, subs[count].zsub_recipient, 0);
309      g_free(subs[count].zsub_class);
310      g_free(subs[count].zsub_classinst);
311      g_free(subs[count].zsub_recipient);
312    } else {
313      count++;
314    }
315  }
316  fclose(file);
317  if (buffer)
318    g_free(buffer);
319
320  return owl_zephyr_loadsubs_helper(subs, count);
321#else
322  return 0;
323#endif
324}
325
326/* Load default Barnowl subscriptions
327 *
328 * Returns 0 on success.
329 * Return -2 if there is a failure from zephyr to load the subscriptions.
330 */
331int owl_zephyr_loadbarnowldefaultsubs(void)
332{
333#ifdef HAVE_LIBZEPHYR
334  ZSubscription_t *subs;
335  int subSize = 10; /* Max Barnowl default subs we allow */
336  int count, ret;
337
338  subs = g_new(ZSubscription_t, subSize);
339  ZResetAuthentication();
340  count=0;
341
342  subs[count].zsub_class=g_strdup("message");
343  subs[count].zsub_classinst=g_strdup("*");
344  subs[count].zsub_recipient=g_strdup("%me%");
345  count++;
346
347  ret = owl_zephyr_loadsubs_helper(subs, count);
348  return(ret);
349#else
350  return(0);
351#endif
352}
353
354int owl_zephyr_loaddefaultsubs(void)
355{
356#ifdef HAVE_LIBZEPHYR
357  Code_t ret;
358
359  if (owl_global_is_havezephyr(&g)) {
360    ZSubscription_t subs[10];
361
362    ret = ZSubscribeTo(subs, 0, 0);
363    if (ret != ZERR_NONE) {
364      owl_function_error("Error subscribing to default zephyr notifications: %s.",
365                           error_message(ret));
366      return(-1);
367    }
368  }
369  return(0);
370#else
371  return(0);
372#endif
373}
374
375int owl_zephyr_loadloginsubs(const char *filename)
376{
377#ifdef HAVE_LIBZEPHYR
378  FILE *file;
379  ZSubscription_t *subs;
380  int numSubs = 100;
381  char *subsfile;
382  char *buffer = NULL;
383  int count;
384  struct stat statbuff;
385
386  subs = g_new(ZSubscription_t, numSubs);
387  subsfile = owl_zephyr_dotfile(".anyone", filename);
388
389  if (stat(subsfile, &statbuff) == -1) {
390    g_free(subs);
391    g_free(subsfile);
392    return 0;
393  }
394
395  ZResetAuthentication();
396  count = 0;
397  file = fopen(subsfile, "r");
398  g_free(subsfile);
399  if (file) {
400    while (owl_getline_chomp(&buffer, file)) {
401      if (buffer[0] == '\0' || buffer[0] == '#')
402        continue;
403
404      if (count == numSubs) {
405        numSubs *= 2;
406        subs = g_renew(ZSubscription_t, subs, numSubs);
407      }
408
409      subs[count].zsub_class = g_strdup("login");
410      subs[count].zsub_recipient = g_strdup("*");
411      subs[count].zsub_classinst = long_zuser(buffer);
412
413      count++;
414    }
415    fclose(file);
416  } else {
417    return 0;
418  }
419  if (buffer)
420    g_free(buffer);
421
422  return owl_zephyr_loadsubs_helper(subs, count);
423#else
424  return 0;
425#endif
426}
427
428void unsuball(void)
429{
430#if HAVE_LIBZEPHYR
431  Code_t ret;
432
433  ZResetAuthentication();
434  ret = ZCancelSubscriptions(0);
435  if (ret != ZERR_NONE)
436    owl_function_error("Zephyr: Cancelling subscriptions: %s",
437                       error_message(ret));
438#endif
439}
440
441int owl_zephyr_sub(const char *class, const char *inst, const char *recip)
442{
443#ifdef HAVE_LIBZEPHYR
444  ZSubscription_t subs[5];
445  Code_t ret;
446
447  subs[0].zsub_class=zstr(class);
448  subs[0].zsub_classinst=zstr(inst);
449  subs[0].zsub_recipient=zstr(recip);
450
451  ZResetAuthentication();
452  ret = ZSubscribeTo(subs, 1, 0);
453  if (ret != ZERR_NONE) {
454    owl_function_error("Error subbing to <%s,%s,%s>: %s",
455                       class, inst, recip,
456                       error_message(ret));
457    return(-2);
458  }
459  return(0);
460#else
461  return(0);
462#endif
463}
464
465
466int owl_zephyr_unsub(const char *class, const char *inst, const char *recip)
467{
468#ifdef HAVE_LIBZEPHYR
469  ZSubscription_t subs[5];
470  Code_t ret;
471
472  subs[0].zsub_class=zstr(class);
473  subs[0].zsub_classinst=zstr(inst);
474  subs[0].zsub_recipient=zstr(recip);
475
476  ZResetAuthentication();
477  ret = ZUnsubscribeTo(subs, 1, 0);
478  if (ret != ZERR_NONE) {
479    owl_function_error("Error unsubbing from <%s,%s,%s>: %s",
480                       class, inst, recip,
481                       error_message(ret));
482    return(-2);
483  }
484  return(0);
485#else
486  return(0);
487#endif
488}
489
490/* return a pointer to the data in the Jth field, (NULL terminated by
491 * definition).  Caller must free the return.
492 */
493#ifdef HAVE_LIBZEPHYR
494char *owl_zephyr_get_field(const ZNotice_t *n, int j)
495{
496  int i, count, save;
497
498  /* If there's no message here, just run along now */
499  if (n->z_message_len == 0)
500    return(g_strdup(""));
501
502  count=save=0;
503  for (i=0; i<n->z_message_len; i++) {
504    if (n->z_message[i]=='\0') {
505      count++;
506      if (count==j) {
507        /* just found the end of the field we're looking for */
508        return(g_strdup(n->z_message+save));
509      } else {
510        save=i+1;
511      }
512    }
513  }
514  /* catch the last field, which might not be null terminated */
515  if (count==j-1) {
516    return g_strndup(n->z_message + save, n->z_message_len - save);
517  }
518
519  return(g_strdup(""));
520}
521
522char *owl_zephyr_get_field_as_utf8(const ZNotice_t *n, int j)
523{
524  int i, count, save;
525
526  /* If there's no message here, just run along now */
527  if (n->z_message_len == 0)
528    return(g_strdup(""));
529
530  count=save=0;
531  for (i = 0; i < n->z_message_len; i++) {
532    if (n->z_message[i]=='\0') {
533      count++;
534      if (count == j) {
535        /* just found the end of the field we're looking for */
536        return(owl_validate_or_convert(n->z_message + save));
537      } else {
538        save = i + 1;
539      }
540    }
541  }
542  /* catch the last field, which might not be null terminated */
543  if (count == j - 1) {
544    char *tmp, *out;
545    tmp = g_strndup(n->z_message + save, n->z_message_len - save);
546    out = owl_validate_or_convert(tmp);
547    g_free(tmp);
548    return out;
549  }
550
551  return(g_strdup(""));
552}
553#else
554char *owl_zephyr_get_field(void *n, int j)
555{
556  return(g_strdup(""));
557}
558char *owl_zephyr_get_field_as_utf8(void *n, int j)
559{
560  return owl_zephyr_get_field(n, j);
561}
562#endif
563
564
565#ifdef HAVE_LIBZEPHYR
566int owl_zephyr_get_num_fields(const ZNotice_t *n)
567{
568  int i, fields;
569
570  if(n->z_message_len == 0)
571    return 0;
572
573  fields=1;
574  for (i=0; i<n->z_message_len; i++) {
575    if (n->z_message[i]=='\0') fields++;
576  }
577 
578  return(fields);
579}
580#else
581int owl_zephyr_get_num_fields(const void *n)
582{
583  return(0);
584}
585#endif
586
587#ifdef HAVE_LIBZEPHYR
588/* return a pointer to the message, place the message length in k
589 * caller must free the return
590 */
591char *owl_zephyr_get_message(const ZNotice_t *n, const owl_message *m)
592{
593#define OWL_NFIELDS     5
594  int i;
595  char *fields[OWL_NFIELDS + 1];
596  char *msg = NULL;
597
598  /* don't let ping messages have a body */
599  if (!strcasecmp(n->z_opcode, "ping")) {
600    return(g_strdup(""));
601  }
602
603  for(i = 0; i < OWL_NFIELDS; i++)
604    fields[i + 1] = owl_zephyr_get_field(n, i + 1);
605
606  /* deal with MIT NOC messages */
607  if (!strcasecmp(n->z_default_format, "@center(@bold(NOC Message))\n\n@bold(Sender:) $1 <$sender>\n@bold(Time:  ) $time\n\n@italic($opcode service on $instance $3.) $4\n")) {
608
609    msg = g_strdup_printf("%s service on %s %s\n%s", n->z_opcode, n->z_class_inst, fields[3], fields[4]);
610  }
611  /* deal with MIT Discuss messages */
612  else if (!strcasecmp(n->z_default_format, "New transaction [$1] entered in $2\nFrom: $3 ($5)\nSubject: $4") ||
613           !strcasecmp(n->z_default_format, "New transaction [$1] entered in $2\nFrom: $3\nSubject: $4")) {
614   
615    msg = g_strdup_printf("New transaction [%s] entered in %s\nFrom: %s (%s)\nSubject: %s",
616                          fields[1], fields[2], fields[3], fields[5], fields[4]);
617  }
618  /* deal with MIT Moira messages */
619  else if (!strcasecmp(n->z_default_format, "MOIRA $instance on $fromhost:\n $message\n")) {
620    msg = g_strdup_printf("MOIRA %s on %s: %s",
621                          n->z_class_inst,
622                          owl_message_get_hostname(m),
623                          fields[1]);
624  } else {
625    if (owl_zephyr_get_num_fields(n) == 1)
626      msg = g_strdup(fields[1]);
627    else
628      msg = g_strdup(fields[2]);
629  }
630
631  for (i = 0; i < OWL_NFIELDS; i++)
632    g_free(fields[i + 1]);
633
634  return msg;
635}
636#endif
637
638#ifdef HAVE_LIBZEPHYR
639const char *owl_zephyr_get_zsig(const ZNotice_t *n, int *k)
640{
641  /* return a pointer to the zsig if there is one */
642
643  /* message length 0? No zsig */
644  if (n->z_message_len==0) {
645    *k=0;
646    return("");
647  }
648
649  /* If there's only one field, no zsig */
650  if (owl_zephyr_get_num_fields(n) == 1) {
651    *k=0;
652    return("");
653  }
654
655  /* Everything else is field 1 */
656  *k=strlen(n->z_message);
657  return(n->z_message);
658}
659#else
660const char *owl_zephyr_get_zsig(const void *n, int *k)
661{
662  return("");
663}
664#endif
665
666int send_zephyr(const char *opcode, const char *zsig, const char *class, const char *instance, const char *recipient, const char *message)
667{
668#ifdef HAVE_LIBZEPHYR
669  Code_t ret;
670  ZNotice_t notice;
671   
672  memset(&notice, 0, sizeof(notice));
673
674  ZResetAuthentication();
675
676  if (!zsig) zsig="";
677 
678  notice.z_kind=ACKED;
679  notice.z_port=0;
680  notice.z_class=zstr(class);
681  notice.z_class_inst=zstr(instance);
682  notice.z_sender=NULL;
683  if (!strcmp(recipient, "*") || !strcmp(recipient, "@")) {
684    notice.z_recipient=zstr("");
685    if (*owl_global_get_zsender(&g))
686        notice.z_sender=zstr(owl_global_get_zsender(&g));
687  } else {
688    notice.z_recipient=zstr(recipient);
689  }
690  notice.z_default_format=zstr("Class $class, Instance $instance:\nTo: @bold($recipient) at $time $date\nFrom: @bold{$1 <$sender>}\n\n$2");
691  if (opcode) notice.z_opcode=zstr(opcode);
692
693  notice.z_message_len=strlen(zsig)+1+strlen(message);
694  notice.z_message=g_new(char, notice.z_message_len+10);
695  strcpy(notice.z_message, zsig);
696  memcpy(notice.z_message+strlen(zsig)+1, message, strlen(message));
697
698  /* ret=ZSendNotice(&notice, ZAUTH); */
699  ret=ZSrvSendNotice(&notice, ZAUTH, send_zephyr_helper);
700 
701  /* free then check the return */
702  g_free(notice.z_message);
703  ZFreeNotice(&notice);
704  if (ret != ZERR_NONE) {
705    owl_function_error("Error sending zephyr: %s", error_message(ret));
706    return(ret);
707  }
708  return(0);
709#else
710  return(0);
711#endif
712}
713
714#ifdef HAVE_LIBZEPHYR
715Code_t send_zephyr_helper(ZNotice_t *notice, char *buf, int len, int wait)
716{
717  return(ZSendPacket(buf, len, 0));
718}
719#endif
720
721void send_ping(const char *to, const char *zclass, const char *zinstance)
722{
723#ifdef HAVE_LIBZEPHYR
724  send_zephyr("PING", "", zclass, zinstance, to, "");
725#endif
726}
727
728#ifdef HAVE_LIBZEPHYR
729void owl_zephyr_handle_ack(const ZNotice_t *retnotice)
730{
731  char *tmp;
732 
733  /* if it's an HMACK ignore it */
734  if (retnotice->z_kind == HMACK) return;
735
736  if (retnotice->z_kind == SERVNAK) {
737    owl_function_error("Authorization failure sending zephyr");
738  } else if ((retnotice->z_kind != SERVACK) || !retnotice->z_message_len) {
739    owl_function_error("Detected server failure while receiving acknowledgement");
740  } else if (!strcmp(retnotice->z_message, ZSRVACK_SENT)) {
741    if (!strcasecmp(retnotice->z_opcode, "ping")) {
742      return;
743    } else {
744      if (strcasecmp(retnotice->z_recipient, ""))
745      { /* personal */
746        tmp=short_zuser(retnotice->z_recipient);
747        if(!strcasecmp(retnotice->z_class, "message") &&
748           !strcasecmp(retnotice->z_class_inst, "personal")) {
749          owl_function_makemsg("Message sent to %s.", tmp);
750        } else if(!strcasecmp(retnotice->z_class, "message")) { /* instanced, but not classed, personal */
751          owl_function_makemsg("Message sent to %s on -i %s\n", tmp, retnotice->z_class_inst);
752        } else { /* classed personal */
753          owl_function_makemsg("Message sent to %s on -c %s -i %s\n", tmp, retnotice->z_class, retnotice->z_class_inst);
754        }
755        g_free(tmp);
756      } else {
757        /* class / instance message */
758          owl_function_makemsg("Message sent to -c %s -i %s\n", retnotice->z_class, retnotice->z_class_inst);
759      }
760    }
761  } else if (!strcmp(retnotice->z_message, ZSRVACK_NOTSENT)) {
762    if (retnotice->z_recipient == NULL
763        || *retnotice->z_recipient == 0
764        || *retnotice->z_recipient == '@') {
765      char *buff;
766      owl_function_error("No one subscribed to class %s", retnotice->z_class);
767      buff = g_strdup_printf("Could not send message to class %s: no one subscribed.\n", retnotice->z_class);
768      owl_function_adminmsg("", buff);
769      g_free(buff);
770    } else {
771      char *buff;
772      owl_zwrite zw;
773
774      tmp = short_zuser(retnotice->z_recipient);
775      owl_function_error("%s: Not logged in or subscribing.", tmp);
776      /*
777       * These error messages are often over 80 chars, but users who want to
778       * see the whole thing can scroll to the side, and for those with wide
779       * terminals or who don't care, not splitting saves a line in the UI
780       */
781      if(strcasecmp(retnotice->z_class, "message")) {
782        buff = g_strdup_printf(
783                 "Could not send message to %s: "
784                 "not logged in or subscribing to class %s, instance %s.\n",
785                 tmp,
786                 retnotice->z_class,
787                 retnotice->z_class_inst);
788      } else if(strcasecmp(retnotice->z_class_inst, "personal")) {
789        buff = g_strdup_printf(
790                 "Could not send message to %s: "
791                 "not logged in or subscribing to instance %s.\n",
792                 tmp,
793                 retnotice->z_class_inst);
794      } else {
795        buff = g_strdup_printf(
796                 "Could not send message to %s: "
797                 "not logged in or subscribing to messages.\n",
798                 tmp);
799      }
800      owl_function_adminmsg("", buff);
801
802      memset(&zw, 0, sizeof(zw));
803      zw.class = g_strdup(retnotice->z_class);
804      zw.inst  = g_strdup(retnotice->z_class_inst);
805      zw.realm = g_strdup("");
806      zw.opcode = g_strdup(retnotice->z_opcode);
807      zw.zsig   = g_strdup("");
808      owl_list_create(&(zw.recips));
809      owl_list_append_element(&(zw.recips), g_strdup(retnotice->z_recipient));
810
811      owl_log_outgoing_zephyr_error(&zw, buff);
812
813      owl_zwrite_cleanup(&zw);
814      g_free(buff);
815      g_free(tmp);
816    }
817  } else {
818    owl_function_error("Internal error on ack (%s)", retnotice->z_message);
819  }
820}
821#else
822void owl_zephyr_handle_ack(const void *retnotice)
823{
824}
825#endif
826
827#ifdef HAVE_LIBZEPHYR
828int owl_zephyr_notice_is_ack(const ZNotice_t *n)
829{
830  if (n->z_kind == SERVNAK || n->z_kind == SERVACK || n->z_kind == HMACK) {
831    if (!strcasecmp(n->z_class, LOGIN_CLASS)) return(0);
832    return(1);
833  }
834  return(0);
835}
836#else
837int owl_zephyr_notice_is_ack(const void *n)
838{
839  return(0);
840}
841#endif
842 
843void owl_zephyr_zaway(const owl_message *m)
844{
845#ifdef HAVE_LIBZEPHYR
846  char *tmpbuff, *myuser, *to;
847  owl_zwrite *z;
848 
849  /* bail if it doesn't look like a message we should reply to.  Some
850   * of this defined by the way zaway(1) works
851   */
852  if (strcasecmp(owl_message_get_class(m), "message")) return;
853  if (strcasecmp(owl_message_get_recipient(m), ZGetSender())) return;
854  if (!strcasecmp(owl_message_get_sender(m), "")) return;
855  if (!strcasecmp(owl_message_get_opcode(m), "ping")) return;
856  if (!strcasecmp(owl_message_get_opcode(m), "auto")) return;
857  if (!strcasecmp(owl_message_get_zsig(m), "Automated reply:")) return;
858  if (!strcasecmp(owl_message_get_sender(m), ZGetSender())) return;
859  if (owl_message_get_attribute_value(m, "isauto")) return;
860
861  if (owl_global_is_smartstrip(&g)) {
862    to=owl_zephyr_smartstripped_user(owl_message_get_sender(m));
863  } else {
864    to=g_strdup(owl_message_get_sender(m));
865  }
866
867  send_zephyr("",
868              "Automated reply:",
869              owl_message_get_class(m),
870              owl_message_get_instance(m),
871              to,
872              owl_global_get_zaway_msg(&g));
873
874  myuser=short_zuser(to);
875  if (!strcasecmp(owl_message_get_instance(m), "personal")) {
876    tmpbuff = owl_string_build_quoted("zwrite %q", myuser);
877  } else {
878    tmpbuff = owl_string_build_quoted("zwrite -i %q %q", owl_message_get_instance(m), myuser);
879  }
880  g_free(myuser);
881  g_free(to);
882
883  z = owl_zwrite_new(tmpbuff);
884  g_free(tmpbuff);
885  if (z == NULL) {
886    owl_function_error("Error creating outgoing zephyr.");
887    return;
888  }
889  owl_zwrite_set_message(z, owl_global_get_zaway_msg(&g));
890  owl_zwrite_set_zsig(z, "Automated reply:");
891
892  /* display the message as an admin message in the receive window */
893  owl_function_add_outgoing_zephyrs(z);
894  owl_zwrite_delete(z);
895#endif
896}
897
898#ifdef HAVE_LIBZEPHYR
899void owl_zephyr_hackaway_cr(ZNotice_t *n)
900{
901  /* replace \r's with ' '.  Gross-ish */
902  int i;
903
904  for (i=0; i<n->z_message_len; i++) {
905    if (n->z_message[i]=='\r') {
906      n->z_message[i]=' ';
907    }
908  }
909}
910#endif
911
912char *owl_zephyr_zlocate(const char *user, int auth)
913{
914#ifdef HAVE_LIBZEPHYR
915  int ret, numlocs;
916  int one = 1;
917  ZLocations_t locations;
918  char *myuser;
919  char *p, *result;
920
921  ZResetAuthentication();
922  ret = ZLocateUser(zstr(user), &numlocs, auth ? ZAUTH : ZNOAUTH);
923  if (ret != ZERR_NONE)
924    return g_strdup_printf("Error locating user %s: %s\n",
925                           user, error_message(ret));
926
927  myuser = short_zuser(user);
928  if (numlocs == 0) {
929    result = g_strdup_printf("%s: Hidden or not logged in\n", myuser);
930  } else {
931    result = g_strdup("");
932    for (; numlocs; numlocs--) {
933      ZGetLocations(&locations, &one);
934      p = g_strdup_printf("%s%s: %s\t%s\t%s\n",
935                          result, myuser,
936                          locations.host ? locations.host : "?",
937                          locations.tty ? locations.tty : "?",
938                          locations.time ? locations.time : "?");
939      g_free(result);
940      result = p;
941    }
942  }
943  g_free(myuser);
944
945  return result;
946#else
947  return g_strdup("");
948#endif
949}
950
951void owl_zephyr_addsub(const char *filename, const char *class, const char *inst, const char *recip)
952{
953#ifdef HAVE_LIBZEPHYR
954  char *line, *subsfile, *s = NULL;
955  FILE *file;
956  int duplicate = 0;
957
958  line = owl_zephyr_makesubline(class, inst, recip);
959  subsfile = owl_zephyr_dotfile(".zephyr.subs", filename);
960
961  /* if the file already exists, check to see if the sub is already there */
962  file = fopen(subsfile, "r");
963  if (file) {
964    while (owl_getline(&s, file)) {
965      if (strcasecmp(s, line) == 0) {
966        owl_function_error("Subscription already present in %s", subsfile);
967        duplicate++;
968      }
969    }
970    fclose(file);
971    g_free(s);
972  }
973
974  if (!duplicate) {
975    file = fopen(subsfile, "a");
976    if (file) {
977      fputs(line, file);
978      fclose(file);
979      owl_function_makemsg("Subscription added");
980    } else {
981      owl_function_error("Error opening file %s for writing", subsfile);
982    }
983  }
984
985  g_free(line);
986#endif
987}
988
989void owl_zephyr_delsub(const char *filename, const char *class, const char *inst, const char *recip)
990{
991#ifdef HAVE_LIBZEPHYR
992  char *line, *subsfile;
993  int linesdeleted;
994 
995  line=owl_zephyr_makesubline(class, inst, recip);
996  line[strlen(line)-1]='\0';
997
998  subsfile = owl_zephyr_dotfile(".zephyr.subs", filename);
999 
1000  linesdeleted = owl_util_file_deleteline(subsfile, line, 1);
1001  if (linesdeleted > 0) {
1002    owl_function_makemsg("Subscription removed");
1003  } else if (linesdeleted == 0) {
1004    owl_function_error("No subscription present in %s", subsfile);
1005  }
1006  g_free(subsfile);
1007  g_free(line);
1008#endif
1009}
1010
1011/* caller must free the return */
1012char *owl_zephyr_makesubline(const char *class, const char *inst, const char *recip)
1013{
1014  return g_strdup_printf("%s,%s,%s\n", class, inst, !strcmp(recip, "") ? "*" : recip);
1015}
1016
1017
1018void owl_zephyr_zlog_in(void)
1019{
1020#ifdef HAVE_LIBZEPHYR
1021  char *exposure, *eset;
1022  Code_t ret;
1023
1024  ZResetAuthentication();
1025
1026  eset = EXPOSE_REALMVIS;
1027  exposure = ZGetVariable(zstr("exposure"));
1028  if (exposure)
1029    exposure = ZParseExposureLevel(exposure);
1030  if (exposure)
1031    eset = exposure;
1032   
1033  ret = ZSetLocation(eset);
1034  if (ret != ZERR_NONE)
1035    owl_function_error("Error setting location: %s", error_message(ret));
1036#endif
1037}
1038
1039void owl_zephyr_zlog_out(void)
1040{
1041#ifdef HAVE_LIBZEPHYR
1042  Code_t ret;
1043
1044  ZResetAuthentication();
1045  ret = ZUnsetLocation();
1046  if (ret != ZERR_NONE)
1047    owl_function_error("Error unsetting location: %s", error_message(ret));
1048#endif
1049}
1050
1051void owl_zephyr_addbuddy(const char *name)
1052{
1053  char *filename;
1054  FILE *file;
1055 
1056  filename = owl_zephyr_dotfile(".anyone", NULL);
1057  file = fopen(filename, "a");
1058  g_free(filename);
1059  if (!file) {
1060    owl_function_error("Error opening zephyr buddy file for append");
1061    return;
1062  }
1063  fprintf(file, "%s\n", name);
1064  fclose(file);
1065}
1066
1067void owl_zephyr_delbuddy(const char *name)
1068{
1069  char *filename;
1070
1071  filename = owl_zephyr_dotfile(".anyone", NULL);
1072  owl_util_file_deleteline(filename, name, 0);
1073  g_free(filename);
1074}
1075
1076/* return auth string */
1077#ifdef HAVE_LIBZEPHYR
1078const char *owl_zephyr_get_authstr(const ZNotice_t *n)
1079{
1080
1081  if (!n) return("UNKNOWN");
1082
1083  if (n->z_auth == ZAUTH_FAILED) {
1084    return ("FAILED");
1085  } else if (n->z_auth == ZAUTH_NO) {
1086    return ("NO");
1087  } else if (n->z_auth == ZAUTH_YES) {
1088    return ("YES");
1089  } else {
1090    return ("UNKNOWN");
1091  }           
1092}
1093#else
1094const char *owl_zephyr_get_authstr(const void *n)
1095{
1096  return("");
1097}
1098#endif
1099
1100/* Returns a buffer of subscriptions or an error message.  Caller must
1101 * free the return.
1102 */
1103char *owl_zephyr_getsubs(void)
1104{
1105#ifdef HAVE_LIBZEPHYR
1106  Code_t ret;
1107  int num, i, one;
1108  ZSubscription_t sub;
1109  GString *buf;
1110
1111  ret = ZRetrieveSubscriptions(0, &num);
1112  if (ret != ZERR_NONE)
1113    return g_strdup_printf("Zephyr: Requesting subscriptions: %s\n", error_message(ret));
1114  if (num == 0)
1115    return g_strdup("Zephyr: No subscriptions retrieved\n");
1116
1117  buf = g_string_new("");
1118  for (i=0; i<num; i++) {
1119    one = 1;
1120    if ((ret = ZGetSubscriptions(&sub, &one)) != ZERR_NONE) {
1121      ZFlushSubscriptions();
1122      g_string_free(buf, true);
1123      return g_strdup_printf("Zephyr: Getting subscriptions: %s\n", error_message(ret));
1124    } else {
1125      /* g_string_append_printf would be backwards. */
1126      char *tmp = g_strdup_printf("<%s,%s,%s>\n",
1127                                  sub.zsub_class,
1128                                  sub.zsub_classinst,
1129                                  sub.zsub_recipient);
1130      g_string_prepend(buf, tmp);
1131      g_free(tmp);
1132    }
1133  }
1134
1135  ZFlushSubscriptions();
1136  return g_string_free(buf, false);
1137#else
1138  return(g_strdup("Zephyr not available"));
1139#endif
1140}
1141
1142const char *owl_zephyr_get_variable(const char *var)
1143{
1144#ifdef HAVE_LIBZEPHYR
1145  return(ZGetVariable(zstr(var)));
1146#else
1147  return("");
1148#endif
1149}
1150
1151void owl_zephyr_set_locationinfo(const char *host, const char *val)
1152{
1153#ifdef HAVE_LIBZEPHYR
1154  ZInitLocationInfo(zstr(host), zstr(val));
1155#endif
1156}
1157 
1158/* Strip a local realm fron the zephyr user name.
1159 * The caller must free the return
1160 */
1161char *short_zuser(const char *in)
1162{
1163  char *ptr = strrchr(in, '@');
1164  if (ptr && (ptr[1] == '\0' || !strcasecmp(ptr+1, owl_zephyr_get_realm()))) {
1165    return g_strndup(in, ptr - in);
1166  }
1167  return g_strdup(in);
1168}
1169
1170/* Append a local realm to the zephyr user name if necessary.
1171 * The caller must free the return.
1172 */
1173char *long_zuser(const char *in)
1174{
1175  char *ptr = strrchr(in, '@');
1176  if (ptr) {
1177    if (ptr[1])
1178      return g_strdup(in);
1179    /* Ends in @, so assume default realm. */
1180    return g_strdup_printf("%s%s", in, owl_zephyr_get_realm());
1181  }
1182  return g_strdup_printf("%s@%s", in, owl_zephyr_get_realm());
1183}
1184
1185/* Return the realm of the zephyr user name. Caller does /not/ free the return.
1186 * The string is valid at least as long as the input is.
1187 */
1188const char *zuser_realm(const char *in)
1189{
1190  char *ptr = strrchr(in, '@');
1191  /* If the name has an @ and does not end with @, use that. Otherwise, take
1192   * the default realm. */
1193  return (ptr && ptr[1]) ? (ptr+1) : owl_zephyr_get_realm();
1194}
1195
1196/* strip out the instance from a zsender's principal.  Preserves the
1197 * realm if present.  Leave host/ and daemon/ krb5 principals
1198 * alone. Also leave rcmd. and daemon. krb4 principals alone. The
1199 * caller must free the return.
1200 */
1201char *owl_zephyr_smartstripped_user(const char *in)
1202{
1203  char *slash, *dot, *realm, *out;
1204
1205  out = g_strdup(in);
1206
1207  /* bail immeaditly if we don't have to do any work */
1208  slash = strchr(out, '/');
1209  dot = strchr(out, '.');
1210  if (!slash && !dot) {
1211    return(out);
1212  }
1213
1214  if (!strncasecmp(out, OWL_ZEPHYR_NOSTRIP_HOST, strlen(OWL_ZEPHYR_NOSTRIP_HOST)) ||
1215      !strncasecmp(out, OWL_ZEPHYR_NOSTRIP_RCMD, strlen(OWL_ZEPHYR_NOSTRIP_RCMD)) ||
1216      !strncasecmp(out, OWL_ZEPHYR_NOSTRIP_DAEMON5, strlen(OWL_ZEPHYR_NOSTRIP_DAEMON5)) ||
1217      !strncasecmp(out, OWL_ZEPHYR_NOSTRIP_DAEMON4, strlen(OWL_ZEPHYR_NOSTRIP_DAEMON4))) {
1218    return(out);
1219  }
1220
1221  realm = strchr(out, '@');
1222  if (!slash && dot && realm && (dot > realm)) {
1223    /* There's no '/', and the first '.' is in the realm */
1224    return(out);
1225  }
1226
1227  /* remove the realm from out, but hold on to it */
1228  if (realm) realm[0]='\0';
1229
1230  /* strip */
1231  if (slash) slash[0] = '\0';  /* krb5 style user/instance */
1232  else if (dot) dot[0] = '\0'; /* krb4 style user.instance */
1233
1234  /* reattach the realm if we had one */
1235  if (realm) {
1236    strcat(out, "@");
1237    strcat(out, realm+1);
1238  }
1239
1240  return(out);
1241}
1242
1243/* read the list of users in 'filename' as a .anyone file, and put the
1244 * names of the zephyr users in the list 'in'.  If 'filename' is NULL,
1245 * use the default .anyone file in the users home directory.  Returns
1246 * -1 on failure, 0 on success.
1247 */
1248int owl_zephyr_get_anyone_list(owl_list *in, const char *filename)
1249{
1250#ifdef HAVE_LIBZEPHYR
1251  char *ourfile, *tmp, *s = NULL;
1252  FILE *f;
1253
1254  ourfile = owl_zephyr_dotfile(".anyone", filename);
1255
1256  f = fopen(ourfile, "r");
1257  if (!f) {
1258    owl_function_error("Error opening file %s: %s", ourfile, strerror(errno) ? strerror(errno) : "");
1259    g_free(ourfile);
1260    return -1;
1261  }
1262  g_free(ourfile);
1263
1264  while (owl_getline_chomp(&s, f)) {
1265    /* ignore comments, blank lines etc. */
1266    if (s[0] == '#' || s[0] == '\0')
1267      continue;
1268
1269    /* ignore from # on */
1270    tmp = strchr(s, '#');
1271    if (tmp)
1272      tmp[0] = '\0';
1273
1274    /* ignore from SPC */
1275    tmp = strchr(s, ' ');
1276    if (tmp)
1277      tmp[0] = '\0';
1278
1279    owl_list_append_element(in, long_zuser(s));
1280  }
1281  g_free(s);
1282  fclose(f);
1283  return 0;
1284#else
1285  return -1;
1286#endif
1287}
1288
1289#ifdef HAVE_LIBZEPHYR
1290void owl_zephyr_process_pseudologin(ZNotice_t *n)
1291{
1292  owl_message *m;
1293  owl_zbuddylist *zbl;
1294  GList **zaldlist;
1295  GList *zaldptr;
1296  ZAsyncLocateData_t *zald = NULL;
1297  ZLocations_t location;
1298  int numlocs, ret, notify;
1299
1300  /* Find a ZALD to match this notice. */
1301  zaldlist = owl_global_get_zaldlist(&g);
1302  zaldptr = g_list_first(*zaldlist);
1303  while (zaldptr) {
1304    if (ZCompareALDPred(n, zaldptr->data)) {
1305      zald = zaldptr->data;
1306      *zaldlist = g_list_remove(*zaldlist, zaldptr->data);
1307      break;
1308    }
1309    zaldptr = g_list_next(zaldptr);
1310  }
1311  if (zald) {
1312    /* Deal with notice. */
1313    notify = owl_global_get_pseudologin_notify(&g);
1314    zbl = owl_global_get_zephyr_buddylist(&g);
1315    ret = ZParseLocations(n, zald, &numlocs, NULL);
1316    if (ret == ZERR_NONE) {
1317      if (numlocs > 0 && !owl_zbuddylist_contains_user(zbl, zald->user)) {
1318        if (notify) {
1319          numlocs = 1;
1320          ret = ZGetLocations(&location, &numlocs);
1321          if (ret == ZERR_NONE) {
1322            /* Send a PSEUDO LOGIN! */
1323            m = g_new(owl_message, 1);
1324            owl_message_create_pseudo_zlogin(m, 0, zald->user,
1325                                             location.host,
1326                                             location.time,
1327                                             location.tty);
1328            owl_global_messagequeue_addmsg(&g, m);
1329          }
1330          owl_zbuddylist_adduser(zbl, zald->user);
1331          owl_function_debugmsg("owl_function_zephyr_buddy_check: login for %s ", zald->user);
1332        }
1333      } else if (numlocs == 0 && owl_zbuddylist_contains_user(zbl, zald->user)) {
1334        /* Send a PSEUDO LOGOUT! */
1335        if (notify) {
1336          m = g_new(owl_message, 1);
1337          owl_message_create_pseudo_zlogin(m, 1, zald->user, "", "", "");
1338          owl_global_messagequeue_addmsg(&g, m);
1339        }
1340        owl_zbuddylist_deluser(zbl, zald->user);
1341        owl_function_debugmsg("owl_function_zephyr_buddy_check: logout for %s ", zald->user);
1342      }
1343    }
1344    ZFreeALD(zald);
1345    g_free(zald);
1346  }
1347}
1348#else
1349void owl_zephyr_process_pseudologin(void *n)
1350{
1351}
1352#endif
1353
1354void owl_zephyr_buddycheck_timer(owl_timer *t, void *data)
1355{
1356  if (owl_global_is_pseudologins(&g)) {
1357    owl_function_debugmsg("Doing zephyr buddy check");
1358    owl_function_zephyr_buddy_check(1);
1359  } else {
1360    owl_function_debugmsg("Warning: owl_zephyr_buddycheck_timer call pointless; timer should have been disabled");
1361  }
1362}
1363
1364/*
1365 * Process zephyrgrams from libzephyr's queue. To prevent starvation,
1366 * process a maximum of OWL_MAX_ZEPHYRGRAMS_TO_PROCESS.
1367 *
1368 * Returns the number of zephyrgrams processed.
1369 */
1370
1371#define OWL_MAX_ZEPHYRGRAMS_TO_PROCESS 20
1372
1373static int _owl_zephyr_process_events(void)
1374{
1375  int zpendcount=0;
1376#ifdef HAVE_LIBZEPHYR
1377  ZNotice_t notice;
1378  Code_t code;
1379  owl_message *m=NULL;
1380
1381  while(owl_zephyr_zpending() && zpendcount < OWL_MAX_ZEPHYRGRAMS_TO_PROCESS) {
1382    if (owl_zephyr_zpending()) {
1383      if ((code = ZReceiveNotice(&notice, NULL)) != ZERR_NONE) {
1384        owl_function_debugmsg("Error: %s while calling ZReceiveNotice\n",
1385                              error_message(code));
1386        continue;
1387      }
1388      zpendcount++;
1389
1390      /* is this an ack from a zephyr we sent? */
1391      if (owl_zephyr_notice_is_ack(&notice)) {
1392        owl_zephyr_handle_ack(&notice);
1393        ZFreeNotice(&notice);
1394        continue;
1395      }
1396
1397      /* if it's a ping and we're not viewing pings then skip it */
1398      if (!owl_global_is_rxping(&g) && !strcasecmp(notice.z_opcode, "ping")) {
1399        ZFreeNotice(&notice);
1400        continue;
1401      }
1402
1403      /* if it is a LOCATE message, it's for pseudologins. */
1404      if (strcmp(notice.z_opcode, LOCATE_LOCATE) == 0) {
1405        owl_zephyr_process_pseudologin(&notice);
1406        ZFreeNotice(&notice);
1407        continue;
1408      }
1409
1410      /* create the new message */
1411      m=g_new(owl_message, 1);
1412      owl_message_create_from_znotice(m, &notice);
1413
1414      owl_global_messagequeue_addmsg(&g, m);
1415    }
1416  }
1417#endif
1418  return zpendcount;
1419}
1420
1421void owl_zephyr_process_events(const owl_io_dispatch *d, void *data)
1422{
1423  _owl_zephyr_process_events();
1424}
1425
1426int owl_zephyr_pre_select_action(owl_ps_action *a, void *p)
1427{
1428  return _owl_zephyr_process_events();
1429}
Note: See TracBrowser for help on using the repository browser.