]> asedeno.scripts.mit.edu Git - 1ts-debian.git/blob - zephyr/server/subscr.c
65aba3e6c2fbefbcbeecd24aa886db0792215a0a
[1ts-debian.git] / zephyr / server / subscr.c
1 /* This file is part of the Project Athena Zephyr Notification System.
2  * It contains functions for managing subscription lists.
3  *
4  *      Created by:     John T. Kohl
5  *
6  *      $Source: /afs/dev.mit.edu/source/repository/athena/lib/zephyr/server/subscr.c,v $
7  *      $Author$
8  *
9  *      Copyright (c) 1987,1988 by the Massachusetts Institute of Technology.
10  *      For copying and distribution information, see the file
11  *      "mit-copyright.h". 
12  */
13
14 #include <zephyr/mit-copyright.h>
15 #include "zserver.h"
16
17 #ifndef lint
18 #ifndef SABER
19 static const char rcsid_subscr_c[] = "$Id$";
20 #endif
21 #endif
22
23 /*
24  * The subscription manager.
25  *
26  * External functions:
27  *
28  * Code_t subscr_subscribe(who, notice)
29  *      Client *who;
30  *      ZNotice_t *notice;
31  *
32  * Code_t subscr_cancel(sin, notice)
33  *      struct sockaddr_in *sin;
34  *      ZNotice_t *notice;
35  *
36  * Code_t subscr_cancel_client(client)
37  *      Client *client;
38  *
39  * Code_t subscr_cancel_host(addr)
40  *      struct in_addr *addr;
41  *
42  * Client *subscr_match_list(notice)
43  *      ZNotice_t *notice;
44  *
45  * void subscr_free_list(list)
46  *      Client *list;
47  *
48  * void subscr_sendlist(notice, auth, who)
49  *      ZNotice_t *notice;
50  *      int auth;
51  *      struct sockaddr_in *who;
52  *
53  * Code_t subscr_send_subs(client, vers)
54  *      Client *client;
55  *      char *vers;
56  *
57  * Code_t subscr_def_subs(who)
58  *      Client *who;
59  *
60  * void subscr_reset();
61  *
62  */
63
64 #ifdef HAVE_KRB4
65 C_Block serv_key;
66 Sched   serv_ksched;
67 #endif
68
69 /* for compatibility when sending subscription information to old clients */
70
71 #ifdef OLD_COMPAT
72 #define OLD_ZEPHYR_VERSION      "ZEPH0.0"
73 #define OLD_CLIENT_INCOMPSUBS   "INCOMP"
74 static void old_compat_subscr_sendlist(ZNotice_t *notice, int auth,
75                                        struct sockaddr_in *who);
76 extern int old_compat_count_subscr;     /* counter of old use */
77 #endif /* OLD_COMPAT */
78 #ifdef NEW_COMPAT
79 #define NEW_OLD_ZEPHYR_VERSION  "ZEPH0.1"
80 static void new_old_compat_subscr_sendlist(ZNotice_t *notice, int auth,
81                                            struct sockaddr_in *who); 
82 extern int new_compat_count_subscr;     /* counter of old use */
83 #endif /* NEW_COMPAT */
84
85 static Code_t add_subscriptions(Client *who, Destlist *subs_queue,
86                                 ZNotice_t *notice, Server *server);
87 static Destlist *extract_subscriptions(ZNotice_t *notice);
88 static void free_subscriptions(Destlist *subs);
89 static void free_subscription(Destlist *sub);
90 static char **subscr_marshal_subs(ZNotice_t *notice, int auth,
91                                   struct sockaddr_in *who,
92                                   int *found);
93 static Destlist *subscr_copy_def_subs(char *person);
94 static Code_t subscr_realm_sendit(Client *who, Destlist *subs,
95                                   ZNotice_t *notice, ZRealm *realm);
96 static void subscr_unsub_sendit(Client *who, Destlist *subs, 
97                                 ZRealm *realm);
98
99 static int defaults_read = 0;           /* set to 1 if the default subs
100                                            are in memory */
101 static ZNotice_t default_notice;        /* contains default subscriptions */
102
103 String *wildcard_instance;
104 String *empty;
105
106 /* WARNING: make sure this is the same as the number of strings you */
107 /* plan to hand back to the user in response to a subscription check, */
108 /* else you will lose.  See subscr_sendlist() */  
109 #define NUM_FIELDS      3
110
111 /*
112  * subscribe the client to types described in notice.
113  */
114
115 Code_t
116 subscr_subscribe(Client *who,
117                  ZNotice_t *notice,
118                  Server *server)
119 {
120     Destlist *subs;
121
122     subs = extract_subscriptions(notice);
123     return add_subscriptions(who, subs, notice, server);
124 }
125
126 static Code_t
127 add_subscriptions(Client *who,
128                   Destlist *subs,
129                   ZNotice_t *notice,
130                   Server *server)
131 {
132     Destlist *next;
133     Code_t retval;
134     Acl *acl;
135     String *sender;
136     ZRealm *realm = NULL;
137
138     if (!subs)
139         return ZERR_NONE;       /* no subscr -> no error */
140
141     sender = make_string(notice->z_sender, 0);
142
143     /* Loop over the new subscriptions. */
144     for (; subs; subs = next) {
145         next = subs->next;
146 #if 0
147         zdbug ((LOG_DEBUG, "subscr: %s/%s/%s", subs->dest.classname->string,
148                 subs->dest.inst->string, subs->dest.recip->string));
149 #endif
150         /* check the recipient for a realm which isn't ours */
151         realm = NULL;
152         if (subs->dest.recip->string[0] == '@' &&
153             strcmp((subs->dest.recip->string + 1), ZGetRealm()) != 0)
154             realm = realm_get_realm_by_name(subs->dest.recip->string + 1);
155         if (!bdumping) {
156             if (subs->dest.recip != empty && subs->dest.recip != sender
157                 && subs->dest.recip->string[0] != '@') {
158                 syslog(LOG_WARNING, "subscr unauth %s recipient %s",
159                        sender->string, subs->dest.recip->string);
160                 free_subscription(subs); /* free this one - denied */
161                 continue; /* the for loop */
162             }
163             acl = class_get_acl(subs->dest.classname);
164             if (acl && !realm) {
165                 if (!access_check(sender->string, acl, SUBSCRIBE)) {
166                     syslog(LOG_WARNING, "subscr unauth %s class %s",
167                            sender->string, subs->dest.classname->string);
168                     free_subscription(subs); /* free this one - denied */
169                     continue; /* the for loop */
170                 }
171                 if (wildcard_instance == subs->dest.inst) {
172                     if (!access_check(sender->string, acl, INSTWILD)) {
173                         syslog(LOG_WARNING,
174                                "subscr unauth %s class %s wild inst",
175                                sender->string, subs->dest.classname->string);
176                         free_subscription(subs); /* free this one - denied */
177                         continue; /* the for loop */
178                     }
179                 }
180             }
181         }
182         if (realm && !bdumping) {
183                 retval = subscr_realm_sendit(who, subs, notice, realm);
184                 if (retval != ZERR_NONE) {
185                     free_subscription(subs);
186                     continue; /* the for loop */
187             } else {
188                     /* Indicates we leaked traffic back to our realm */
189                     free_subscription(subs); /* free this one, wil get from
190                                                 ADD */
191             }
192         } else {
193           retval = triplet_register(who, &subs->dest, NULL);
194           if (retval != ZERR_NONE) {
195               if (retval == ZSRV_CLASSXISTS) {
196                   free_subscription(subs); /* free this one */
197               } else {
198                   free_subscriptions(subs);
199                   free_string(sender);
200                   return retval;
201               }
202           } else {
203               /* If realm, let the REALM_ADD_SUBSCRIBE do insertion */
204               Destlist_insert(&who->subs, subs);
205           }
206         }
207     }
208
209     free_string(sender);
210     return ZERR_NONE;
211 }
212
213 /*
214  * add default subscriptions to the client's subscription chain.
215  */
216
217 Code_t
218 subscr_def_subs(Client *who)
219 {
220     Destlist *subs;
221
222     subs = subscr_copy_def_subs(who->principal->string);
223     return add_subscriptions(who, subs, &default_notice, NULL);
224 }
225
226 void
227 subscr_reset(void)
228 {
229 #if 0
230     zdbug((LOG_DEBUG, "subscr_reset()"));
231 #endif
232     free(default_notice.z_message);
233     default_notice.z_message = NULL;
234     defaults_read = 0;
235 }
236
237 static Destlist *
238 subscr_copy_def_subs(char *person)
239 {
240     int retval, fd;
241     struct stat statbuf;
242     char *def_sub_area, *cp;
243     Destlist *subs, *sub;
244
245     if (!defaults_read) {
246 #if 0
247         zdbug((LOG_DEBUG, "reading default subscription file"));
248 #endif
249         fd = open(subs_file, O_RDONLY, 0666);
250         if (fd < 0) {
251             syslog(LOG_ERR, "can't open %s:%m", subs_file);
252             return NULL;
253         }
254         retval = fstat(fd, &statbuf);
255         if (retval < 0) {
256             syslog(LOG_ERR, "fstat failure on %s:%m", subs_file);
257             close(fd);
258             return NULL;
259         }
260         def_sub_area = (char *) malloc(statbuf.st_size + 1);
261         if (!def_sub_area) {
262             syslog(LOG_ERR, "no mem copy_def_subs");
263             close(fd);
264             return NULL;
265         }
266         retval = read(fd, def_sub_area, (size_t) statbuf.st_size);
267         if (retval != statbuf.st_size) {
268             syslog(LOG_ERR, "short read in copy_def_subs");
269             close(fd);
270             return NULL;
271         }
272
273         close(fd);
274         def_sub_area[statbuf.st_size] = '\0'; /* null-terminate it */
275
276         /*
277            def_subs_area now points to a buffer full of subscription info.
278            Each line of the stuff is of the form:
279            class,inst,recipient
280
281            Commas and newlines may not appear as part of the class,
282            instance, or recipient. XXX!
283            */
284
285         /* split up the subscription info */
286         for (cp = def_sub_area; cp < def_sub_area + statbuf.st_size; cp++) {
287             if (*cp == '\n' || *cp == ',')
288                 *cp = '\0';
289         }
290         default_notice.z_message = def_sub_area;
291         default_notice.z_message_len = statbuf.st_size + 1;
292         default_notice.z_auth = 1;
293         defaults_read = 1;
294     }
295
296     /* needed later for access_check() */
297     default_notice.z_sender = person;
298     subs = extract_subscriptions(&default_notice);
299     /* replace any non-* recipients with "person" */
300
301     for (sub = subs; sub; sub = sub->next) {
302         /* if not a wildcard, replace it with person */
303         if (strcmp(sub->dest.recip->string, "*")) {
304             free_string(sub->dest.recip);
305             sub->dest.recip = make_string(person, 0);
306         } else {                /* replace with null recipient */
307             free_string(sub->dest.recip);
308             sub->dest.recip = dup_string(empty);
309         }
310     }
311     return subs;
312 }
313
314 /*
315  * Cancel a specific set of subscriptions.
316  */
317
318 Code_t
319 subscr_cancel(struct sockaddr_in *sin,
320               ZNotice_t *notice)
321 {
322     ZRealm *realm;
323     Client *who;
324     Destlist *cancel_subs, *subs, *cancel_next, *client_subs, *client_next;
325     Code_t retval;
326     int found = 0;
327
328 #if 0
329     zdbug((LOG_DEBUG,"subscr_cancel"));
330 #endif
331     who = client_find(&sin->sin_addr, notice->z_port);
332     if (!who)
333         return ZSRV_NOCLT;
334
335     if (!who->subs)
336         return ZSRV_NOSUB;
337
338     cancel_subs = extract_subscriptions(notice);
339     if (!cancel_subs)
340         return ZERR_NONE;       /* no subscr -> no error */
341
342     for (subs = cancel_subs; subs; subs = cancel_next) {
343         cancel_next = subs->next;
344         for (client_subs = who->subs; client_subs; client_subs = client_next) {
345             client_next = client_subs->next;
346             if (ZDest_eq(&client_subs->dest, &subs->dest)) {
347                 Destlist_delete(client_subs);
348                 retval = triplet_deregister(who, &client_subs->dest, NULL);
349                 if (retval == ZSRV_EMPTYCLASS &&
350                     client_subs->dest.recip->string[0] == '@') {
351                     realm =
352                         realm_get_realm_by_name(client_subs->dest.recip->string
353                                                 + 1);
354                     if (realm)
355                         subscr_unsub_sendit(who, client_subs, realm);
356                     realm = NULL;
357                 }
358                 free_subscription(client_subs);
359                 found = 1;
360                 break;
361             }
362         }
363     }
364
365     free_subscriptions(cancel_subs);
366
367     if (found) {
368 #if 0
369         zdbug((LOG_DEBUG, "found & removed"));
370 #endif
371         return ZERR_NONE;
372     } else {
373 #if 0
374         zdbug((LOG_DEBUG, "not found"));
375 #endif
376         return ZSRV_NOSUB;
377     }
378 }
379
380 Code_t
381 subscr_realm_cancel(struct sockaddr_in *sin,
382                     ZNotice_t *notice,
383                     ZRealm *realm)
384 {
385     Destlist *cancel_subs, *subs, *client_subs, *next, *next2;
386     Code_t retval;
387     int found = 0;
388
389     if (!realm)
390         return ZSRV_NORLM;
391
392     if (!realm->subs)
393         return ZSRV_NOSUB;
394
395     cancel_subs = extract_subscriptions(notice);
396     if (!cancel_subs)
397         return ZERR_NONE;       /* no subscr -> no error */
398
399     for (subs = cancel_subs; subs; subs = next) {
400         next = subs->next;
401         for (client_subs = realm->subs; client_subs; client_subs = next2) {
402             next2 = client_subs->next;
403             if (ZDest_eq(&client_subs->dest, &subs->dest)) {
404                 Destlist_delete(client_subs);
405                 retval = triplet_deregister(realm->client, &client_subs->dest, realm);
406                 free_subscription(client_subs);
407                 found = 1;
408                 break;
409             }
410         }
411     }
412
413     free_subscriptions(cancel_subs);
414
415     if (found) {
416 #if 0
417         zdbug((LOG_DEBUG, "found & removed"));
418 #endif
419         return ZERR_NONE;
420     } else {
421 #if 0
422         zdbug((LOG_DEBUG, "not found"));
423 #endif
424         return ZSRV_NOSUB;
425     }
426 }
427
428 /*
429  * Cancel all the subscriptions for this client.
430  */
431
432 void
433 subscr_cancel_client(Client *client)
434 {
435     Destlist *subs, *next;
436     Code_t retval;
437     ZRealm *realm;
438
439 #if 0
440     zdbug((LOG_DEBUG,"subscr_cancel_client %s",
441            inet_ntoa(client->addr.sin_addr)));
442 #endif
443     if (!client->subs)
444         return;
445
446     for (subs = client->subs; subs; subs = next) {
447         next = subs->next;
448 #if 0
449         zdbug((LOG_DEBUG,"sub_can %s", subs->dest.classname->string));
450 #endif
451         retval = triplet_deregister(client, &subs->dest, NULL);
452         if (retval == ZSRV_EMPTYCLASS &&
453             subs->dest.recip->string[0] == '@') {
454             realm = realm_get_realm_by_name(subs->dest.recip->string + 1);
455             if (realm)
456                 subscr_unsub_sendit(client, subs, realm);
457             realm = NULL;
458         }
459         free_subscription(subs);
460     }
461
462     client->subs = NULL;
463 }
464
465 /*
466  * Send the requester a list of his current subscriptions
467  */
468
469 void
470 subscr_sendlist(ZNotice_t *notice,
471                 int auth,
472                 struct sockaddr_in *who)
473 {
474     char **answer;
475     int found;
476     struct sockaddr_in send_to_who;
477     Code_t retval;
478
479 #ifdef OLD_COMPAT
480     if (strcmp(notice->z_version, OLD_ZEPHYR_VERSION) == 0) {
481         /* we are talking to an old client; use the old-style
482            acknowledgement-message */
483         old_compat_subscr_sendlist(notice, auth, who);
484         return;
485     }
486 #endif /* OLD_COMPAT */
487 #ifdef NEW_COMPAT
488     if (strcmp(notice->z_version, NEW_OLD_ZEPHYR_VERSION) == 0) {
489         /* we are talking to a new old client; use the new-old-style
490            acknowledgement-message */
491         new_old_compat_subscr_sendlist(notice, auth, who);
492         return;
493     }
494 #endif /* NEW_COMPAT */
495     answer = subscr_marshal_subs(notice, auth, who, &found);
496     send_to_who = *who;
497     send_to_who.sin_port = notice->z_port;  /* Return port */
498
499     retval = ZSetDestAddr(&send_to_who);
500     if (retval != ZERR_NONE) {
501         syslog(LOG_WARNING, "subscr_sendlist set addr: %s",
502                error_message(retval));
503         if (answer)
504             free(answer);
505         return;
506     }
507
508     /* XXX for now, don't do authentication */
509     auth = 0;
510
511     notice->z_kind = ACKED;
512
513     /* use xmit_frag() to send each piece of the notice */
514
515     retval = ZSrvSendRawList(notice, answer, found * NUM_FIELDS, xmit_frag);
516     if (retval != ZERR_NONE)
517         syslog(LOG_WARNING, "subscr_sendlist xmit: %s", error_message(retval));
518     if (answer)
519         free(answer);
520 }
521
522 static char **
523 subscr_marshal_subs(ZNotice_t *notice,
524                     int auth,
525                     struct sockaddr_in *who,
526                     int *found)
527 {
528     char **answer = NULL;
529     unsigned short temp;
530     Code_t retval;
531     Client *client;
532     Destlist *subs = NULL, *sub;
533     int i;
534     int defsubs = 0;
535
536 #if 0
537     zdbug((LOG_DEBUG, "subscr_marshal"));
538 #endif
539     *found = 0;
540     
541     /* Note that the following code is an incredible crock! */
542         
543     /* We cannot send multiple packets as acknowledgements to the client,
544        since the hostmanager will ignore the later packets.  So we need
545        to send directly to the client. */
546
547     /* Make our own copy so we can send directly back to the client */
548     /* RSF 11/07/87 */
549
550     if (strcmp(notice->z_opcode, CLIENT_GIMMESUBS) == 0) {
551         /* If the client has requested his current subscriptions,
552            the message field of the notice contains the port number
553            of the client for which the sender desires the subscription
554            list.  The port field is the port of the sender. */
555
556         retval = ZReadAscii16(notice->z_message, notice->z_message_len, &temp);
557         if (retval != ZERR_NONE) {
558             syslog(LOG_WARNING, "subscr_marshal read port num: %s",
559                    error_message(retval));
560             return(NULL);
561         }
562
563         client = client_find(&who->sin_addr, htons(temp));
564
565         if (client)
566             subs = client->subs;
567     } else if (strcmp(notice->z_opcode, CLIENT_GIMMEDEFS) == 0) {
568 #if 0
569         zdbug((LOG_DEBUG, "gimmedefs"));
570 #endif
571         /* subscr_copy_def_subs allocates new pointer rings, so
572            it must be freed when finished.
573            the string areas pointed to are static, however.*/
574         subs = subscr_copy_def_subs(notice->z_sender);
575         defsubs = 1;
576     } else {
577         syslog(LOG_ERR, "subscr_marshal bogus opcode %s",
578                notice->z_opcode);
579         return(NULL);
580     }
581
582     if (subs) {
583
584         /* check authenticity here.  The user must be authentic to get
585            a list of subscriptions. If he is not subscribed to
586            anything, this if-clause fails, and he gets a response
587            indicating no subscriptions.
588            if retrieving default subscriptions, don't care about
589            authentication. */
590
591         if (!auth && !defsubs)
592             return(NULL);
593         if (!defsubs) {
594             if (client && (strcmp(client->principal->string,
595                                   notice->z_sender) != 0)) {
596                 zdbug ((LOG_DEBUG,
597                         "subscr_marshal: %s requests subs for %s at %s/%d",
598                         notice->z_sender, client->principal->string,
599                         inet_ntoa(who->sin_addr), ntohs(who->sin_port)));
600                 return 0;
601             }
602         }
603
604         for (sub = subs; sub; sub = sub->next)
605             (*found)++;
606
607         /* found is now the number of subscriptions */
608
609         /* coalesce the subscription information into a list of char *'s */
610         answer = (char **) malloc((*found) * NUM_FIELDS * sizeof(char *));
611         if (answer == NULL) {
612             syslog(LOG_ERR, "subscr no mem(answer)");
613             *found = 0;
614         } else {
615             i = 0;
616             for (sub = subs; sub; sub = sub->next) {
617                 answer[i * NUM_FIELDS] = sub->dest.classname->string;
618                 answer[i * NUM_FIELDS + 1] = sub->dest.inst->string;
619                 answer[i * NUM_FIELDS + 2] = sub->dest.recip->string;
620                 i++;
621             }
622         }
623     }
624     if (defsubs)
625         free_subscriptions(subs);
626     return answer;
627 }
628
629 #ifdef NEW_COMPAT
630 static void
631 new_old_compat_subscr_sendlist(notice, auth, who)
632     ZNotice_t *notice;
633     int auth;
634     struct sockaddr_in *who;
635 {
636     Code_t retval;
637     ZNotice_t reply;
638     ZPacket_t reppacket;
639     int packlen, found, count, initfound, zerofound;
640     char buf[64];
641     const char **answer;
642     struct sockaddr_in send_to_who;
643     int i;
644
645     new_compat_count_subscr++;
646
647     syslog(LOG_INFO, "new old subscr, %s", inet_ntoa(who->sin_addr));
648     reply = *notice;
649     reply.z_kind = SERVACK;
650     reply.z_authent_len = 0; /* save some space */
651     reply.z_auth = 0;
652
653     send_to_who = *who;
654     send_to_who.sin_port = notice->z_port;  /* Return port */
655
656     retval = ZSetDestAddr(&send_to_who);
657     if (retval != ZERR_NONE) {
658         syslog(LOG_WARNING, "new_old_subscr_sendlist set addr: %s",
659                error_message(retval));
660         return;
661     }
662
663     /* retrieve  the subscriptions */
664     answer = subscr_marshal_subs(notice, auth, who, &found);
665
666     /* note that when there are no subscriptions, found == 0, so
667        we needn't worry about answer being NULL since
668        ZFormatSmallRawNoticeList won't reference the pointer */
669
670     /* send 5 at a time until we are finished */
671     count = found?((found-1) / 5 + 1):1;        /* total # to be sent */
672     i = 0;                                      /* pkt # counter */
673 #if 0
674     zdbug((LOG_DEBUG,"Found %d subscriptions for %d packets", found, count));
675 #endif
676     initfound = found;
677     zerofound = (found == 0);
678     while (found > 0 || zerofound) {
679         packlen = sizeof(reppacket);
680         sprintf(buf, "%d/%d", ++i, count);
681         reply.z_opcode = buf;
682         retval = ZFormatSmallRawNoticeList(&reply,
683                                            answer + (initfound - found)
684                                             * NUM_FIELDS,
685                                            ((found > 5) ? 5 : found)
686                                             * NUM_FIELDS,
687                                            reppacket, &packlen);
688         if (retval != ZERR_NONE) {
689             syslog(LOG_ERR, "subscr_sendlist format: %s",
690                    error_message(retval));
691             if (answer)
692                 free(answer);
693             return;
694         }
695         retval = ZSendPacket(reppacket, packlen, 0);
696         if (retval != ZERR_NONE) {
697             syslog(LOG_WARNING, "subscr_sendlist xmit: %s",
698                    error_message(retval));
699             if (answer)
700                 free(answer);
701             return;
702         }
703         found -= 5;
704         zerofound = 0;
705     }
706 #if 0
707     zdbug((LOG_DEBUG,"subscr_sendlist acked"));
708 #endif
709     if (answer)
710         free(answer);
711 }
712 #endif /* NEW_COMPAT */
713
714 #ifdef OLD_COMPAT
715 static void
716 old_compat_subscr_sendlist(notice, auth, who)
717     ZNotice_t *notice;
718     int auth;
719     struct sockaddr_in *who;
720 {
721     Client *client = client_find(&who->sin_addr, notice->z_port);
722     Destlist *subs;
723     Code_t retval;
724     ZNotice_t reply;
725     ZPacket_t reppacket;
726     int packlen, i, found = 0;
727     char **answer = NULL;
728
729     old_compat_count_subscr++;
730
731     syslog(LOG_INFO, "old old subscr, %s", inet_ntoa(who->sin_addr));
732     if (client && client->subs) {
733
734         /* check authenticity here.  The user must be authentic to get
735            a list of subscriptions. If he is not subscribed to
736            anything, the above test fails, and he gets a response
737            indicating no subscriptions */
738
739         if (!auth) {
740             clt_ack(notice, who, AUTH_FAILED);
741             return;
742         }
743
744         for (subs = client->subs; subs; subs = subs->next)
745             found++;
746         /* found is now the number of subscriptions */
747
748         /* coalesce the subscription information into a list of char *'s */
749         answer = (char **) malloc(found * NUM_FIELDS * sizeof(char *));
750         if (!answer) {
751             syslog(LOG_ERR, "old_subscr_sendlist no mem(answer)");
752             found = 0;
753         } else {
754             i = 0;
755             for (subs = client->subs; subs; subs = subs->next) {
756                 answer[i*NUM_FIELDS] = subs->dest.classname->string;
757                 answer[i*NUM_FIELDS + 1] = subs->dest.inst->string;
758                 answer[i*NUM_FIELDS + 2] = subs->dest.recip->string;
759                 i++;
760             }
761         }
762     }
763
764     /* note that when there are no subscriptions, found == 0, so
765        we needn't worry about answer being NULL */
766
767     reply = *notice;
768     reply.z_kind = SERVACK;
769     reply.z_authent_len = 0; /* save some space */
770     reply.z_auth = 0;
771
772     /* if it's too long, chop off one at a time till it fits */
773     while ((retval = ZFormatSmallRawNoticeList(&reply, answer,
774                                                found * NUM_FIELDS,
775                                                reppacket,
776                                                &packlen)) != ZERR_PKTLEN) {
777         found--;
778         reply.z_opcode = OLD_CLIENT_INCOMPSUBS;
779     }
780     if (retval != ZERR_NONE) {
781         syslog(LOG_ERR, "old_subscr_sendlist format: %s",
782                error_message(retval));
783         if (answer)
784             free(answer);
785         return;
786     }
787     retval = ZSetDestAddr(who);
788     if (retval != ZERR_NONE) {
789         syslog(LOG_WARNING, "subscr_sendlist set addr: %s",
790                error_message(retval));
791         if (answer)
792             free(answer);
793         return;
794     }
795     retval = ZSendPacket(reppacket, packlen, 0);
796     if (retval != ZERR_NONE) {
797         syslog(LOG_WARNING, "subscr_sendlist xmit: %s",
798                error_message(retval));
799         if (answer)
800             free(answer);
801         return;
802     }
803 #if 0
804     zdbug((LOG_DEBUG,"subscr_sendlist acked"));
805 #endif
806     if (answer)
807         free(answer);
808 }
809 #endif /* OLD_COMPAT */
810
811 /*
812  * Send the client's subscriptions to another server
813  */
814
815 /* version is currently unused; if necessary later versions may key off it
816    to determine what to send to the peer (protocol changes) */
817
818 /*ARGSUSED*/
819 Code_t
820 subscr_send_subs(Client *client)
821 {
822     int i = 0;
823     Destlist *subs;
824 #ifdef HAVE_KRB5
825     char buf[512];
826     unsigned char *bufp;
827 #else
828 #ifdef HAVE_KRB4
829     char buf[512];
830     C_Block cblock;
831 #endif /* HAVE_KRB4 */
832 #endif
833     char buf2[512];
834     char *list[7 * NUM_FIELDS];
835     int num = 0;
836     Code_t retval;
837
838 #if 0
839     zdbug((LOG_DEBUG, "send_subs"));
840 #endif
841     sprintf(buf2, "%d",ntohs(client->addr.sin_port));
842
843     list[num++] = buf2;
844
845 #ifdef HAVE_KRB5
846 #ifdef HAVE_KRB4 /* XXX make this optional for server transition time */
847     if (Z_enctype(client->session_keyblock) == ENCTYPE_DES_CBC_CRC) {
848         bufp = malloc(Z_keylen(client->session_keyblock));
849         if (bufp == NULL) {
850             syslog(LOG_WARNING, "subscr_send_subs: cannot allocate memory for DES keyblock: %m");
851             return errno;
852         }
853         des_ecb_encrypt((C_Block *)Z_keydata(client->session_keyblock), (C_Block *)bufp, serv_ksched.s, DES_ENCRYPT);
854         retval = ZMakeAscii(buf, sizeof(buf), bufp, Z_keylen(client->session_keyblock));
855     } else {
856 #endif
857         bufp = malloc(Z_keylen(client->session_keyblock) + 8); /* + enctype
858                                                                 + length */
859         if (bufp == NULL) {
860             syslog(LOG_WARNING, "subscr_send_subs: cannot allocate memory for keyblock: %m");
861             return errno;
862         }
863         *(krb5_enctype *)&bufp[0] = htonl(Z_enctype(client->session_keyblock));
864         *(u_int32_t *)&bufp[4] = htonl(Z_keylen(client->session_keyblock));
865         memcpy(&bufp[8], Z_keydata(client->session_keyblock), Z_keylen(client->session_keyblock));
866
867         retval = ZMakeZcode(buf, sizeof(buf), bufp, Z_keylen(client->session_keyblock) + 8);
868 #ifdef HAVE_KRB4
869     }
870 #endif /* HAVE_KRB4 */
871 #else /* HAVE_KRB5 */
872 #ifdef HAVE_KRB4
873     des_ecb_encrypt(client->session_key, cblock, serv_ksched.s, DES_ENCRYPT);
874
875     retval = ZMakeAscii(buf, sizeof(buf), cblock, sizeof(C_Block));
876 #endif /* HAVE_KRB4 */
877 #endif /* HAVE_KRB5 */    
878
879 #if defined(HAVE_KRB4) || defined(HAVE_KRB5)
880     if (retval != ZERR_NONE) {
881 #if 0
882         zdbug((LOG_DEBUG,"zmakeascii failed: %s", error_message(retval)));
883 #endif
884     } else {
885         list[num++] = buf;
886 #if 0
887         zdbug((LOG_DEBUG, "cblock %s", buf));
888 #endif
889     }           
890 #endif /* HAVE_KRB4 || HAVE_KRB5*/
891     retval = bdump_send_list_tcp(SERVACK, &client->addr, ZEPHYR_ADMIN_CLASS,
892                                  num > 1 ? "CBLOCK" : "", ADMIN_NEWCLT,
893                                  client->principal->string, "", list, num);
894     if (retval != ZERR_NONE) {
895         syslog(LOG_ERR, "subscr_send_subs newclt: %s", error_message(retval));
896         return retval;
897     }
898
899     if (!client->subs)
900         return ZERR_NONE;
901
902     for (subs = client->subs; subs; subs = subs->next) {
903         /* for each subscription */
904         list[i * NUM_FIELDS] = subs->dest.classname->string;
905         list[i * NUM_FIELDS + 1] = subs->dest.inst->string;
906         list[i * NUM_FIELDS + 2] = subs->dest.recip->string;
907         i++;
908         if (i >= 7) {
909             /* we only put 7 in each packet, so we don't run out of room */
910             retval = bdump_send_list_tcp(ACKED, &client->addr,
911                                          ZEPHYR_CTL_CLASS, "",
912                                          CLIENT_SUBSCRIBE, "", "", list,
913                                          i * NUM_FIELDS);
914             if (retval != ZERR_NONE) {
915                 syslog(LOG_ERR, "subscr_send_subs subs: %s",
916                        error_message(retval));
917                 return retval;
918             }
919             i = 0;
920         }
921     }
922     if (i) {
923         retval = bdump_send_list_tcp(ACKED, &client->addr, ZEPHYR_CTL_CLASS,
924                                      "", CLIENT_SUBSCRIBE, "", "", list,
925                                      i * NUM_FIELDS);
926         if (retval != ZERR_NONE) {
927             syslog(LOG_ERR, "subscr_send_subs subs: %s",
928                    error_message(retval));
929             return retval;
930         }
931     }
932
933     return ZERR_NONE;
934 }
935
936 /*
937  * free the memory allocated for the list of subscriptions.
938  */
939
940 /*
941  * free the memory allocated for one subscription.
942  */
943
944 static void
945 free_subscription(Destlist *sub)
946 {
947     free_string(sub->dest.classname);
948     free_string(sub->dest.inst);
949     free_string(sub->dest.recip);
950     free(sub);
951 }
952
953 static void
954 free_subscriptions(Destlist *subs)
955 {
956     Destlist *next;
957
958     for (; subs; subs = next) {
959         next = subs->next;
960         free_subscription (subs);
961     }
962 }
963
964 #define ADVANCE(xx)     { cp += (strlen(cp) + 1); \
965                   if (cp >= notice->z_message + notice->z_message_len) { \
966                           syslog(LOG_WARNING, "malformed subscription %d", \
967                                  xx); \
968                           return subs; \
969                   }}
970
971 /*
972  * Parse the message body, returning a linked list of subscriptions, or
973  * NULL if there are no subscriptions there.
974  */
975
976 static Destlist *
977 extract_subscriptions(ZNotice_t *notice)
978 {
979     Destlist *subs = NULL, *sub;
980     char *recip, *class_name, *classinst;
981     char *cp = notice->z_message;
982
983     /* parse the data area for the subscriptions */
984     while (cp < notice->z_message + notice->z_message_len) {
985         class_name = cp;
986         if (*cp == '\0')            /* we've exhausted the subscriptions */
987             return(subs);
988         ADVANCE(1);
989         classinst = cp;
990         ADVANCE(2);
991         recip = cp;
992 #if 0
993         zdbug((LOG_DEBUG, "ext_sub: CLS %s INST %s RCPT %s",
994                class_name, classinst, cp));
995 #endif
996         cp += (strlen(cp) + 1);
997         if (cp > notice->z_message + notice->z_message_len) {
998             syslog(LOG_WARNING, "malformed sub 3");
999             return subs;
1000         }
1001         sub = (Destlist *) malloc(sizeof(Destlist));
1002         if (!sub) {
1003             syslog(LOG_WARNING, "ex_subs: no mem 2");
1004             return subs;
1005         }
1006         sub->dest.classname = make_string(class_name, 1);
1007         sub->dest.inst = make_string(classinst, 1);
1008         /* Nuke @REALM if REALM is us. */
1009         if (recip[0] == '@' && !strcmp(recip + 1, ZGetRealm()))
1010             sub->dest.recip = make_string("", 0);
1011         else
1012             sub->dest.recip = make_string(recip, 0);
1013         Destlist_insert(&subs, sub);
1014     }
1015     return subs;
1016 }
1017
1018 /*
1019  * print subscriptions in subs onto fp.
1020  * assumed to be called with SIGFPE blocked
1021  * (true if called from signal handler)
1022  */
1023
1024 void
1025 subscr_dump_subs(FILE *fp,
1026                  Destlist *subs)
1027 {
1028     if (!subs)                  /* no subscriptions to dump */
1029         return;
1030
1031     for (; subs; subs = subs->next) {
1032         fputs("\t'", fp);
1033         dump_quote(subs->dest.classname->string, fp);
1034         fputs("' '", fp);
1035         dump_quote(subs->dest.inst->string, fp);
1036         fputs("' '", fp);
1037         dump_quote(subs->dest.recip->string, fp);
1038         fputs("'\n", fp);
1039     }
1040 }
1041
1042 #define I_ADVANCE(xx)   { cp += (strlen(cp) + 1); \
1043                   if (cp >= notice->z_message + notice->z_message_len) { \
1044                           syslog(LOG_WARNING, "malformed subscription %d", \
1045                                  xx); \
1046                           return (ZERR_NONE); \
1047                   }}
1048
1049 /* As it exists, this function expects to take only the first sub from the 
1050  * Destlist. At some point, it and the calling code should be replaced */
1051 static Code_t
1052 subscr_realm_sendit(Client *who,
1053                     Destlist *subs,
1054                     ZNotice_t *notice,
1055                     ZRealm *realm)
1056 {
1057   ZNotice_t snotice;
1058   char *pack;
1059   int packlen;
1060   char **text;
1061   Code_t retval;
1062   char addr[16];          /* xxx.xxx.xxx.xxx max */
1063   char port[16];
1064   
1065 #if 0
1066   zdbug((LOG_DEBUG, "subscr_rlm_sendit"));
1067 #endif
1068
1069
1070   if ((text=(char **)malloc((NUM_FIELDS + 2)*sizeof(char *))) == (char **)0) {
1071       syslog(LOG_ERR, "subscr_rlm_sendit malloc");
1072       return(ENOMEM);
1073   }
1074   /* convert the address to a string of the form x.x.x.x/port */
1075   strcpy(addr, inet_ntoa(who->addr.sin_addr));
1076   if ((retval = ZMakeAscii(port, sizeof(port), (unsigned char *) 
1077                            &who->addr.sin_port, sizeof(u_short))) != ZERR_NONE) 
1078     {
1079       syslog(LOG_ERR, "subscr_rlm_sendit make ascii: %s",
1080              error_message(retval));
1081       return(ZERR_NONE);
1082     }
1083   text[0] = addr;
1084   text[1] = port;
1085
1086   text[2] = subs->dest.classname->string;
1087   text[3] = subs->dest.inst->string;
1088   text[4] = subs->dest.recip->string;
1089   
1090   zdbug((LOG_DEBUG, "subscr_realm_sendit %s/%s (%s) %s,%s,%s\n",
1091          text[0], text[1], who->principal->string, text[2], text[3], text[4]));
1092   
1093   /* format snotice */
1094   memset (&snotice, 0, sizeof(snotice));
1095   snotice.z_class_inst = ZEPHYR_CTL_REALM;
1096   snotice.z_opcode = REALM_REQ_SUBSCRIBE;
1097   snotice.z_port = srv_addr.sin_port;
1098
1099   snotice.z_class = ZEPHYR_CTL_CLASS;
1100
1101   snotice.z_recipient = "";
1102   snotice.z_kind = ACKED;
1103   snotice.z_num_other_fields = 0;
1104   snotice.z_default_format = "";
1105   snotice.z_sender = who->principal->string;
1106   snotice.z_recipient = notice->z_recipient;
1107   snotice.z_default_format = notice->z_default_format;
1108   
1109   if ((retval = ZFormatNoticeList(&snotice, text, NUM_FIELDS + 2,
1110                                   &pack, &packlen, ZNOAUTH)) != ZERR_NONE) 
1111     {
1112       syslog(LOG_WARNING, "subscr_rlm_sendit format: %s",
1113              error_message(retval));
1114       free(text);
1115       return(ZERR_NONE);
1116     }
1117   free(text);
1118   
1119   if ((retval = ZParseNotice(pack, packlen, &snotice)) != ZERR_NONE) {
1120     syslog(LOG_WARNING, "subscr_rlm_sendit parse: %s",
1121            error_message(retval));
1122     free(pack);
1123     return(ZERR_NONE);
1124   }
1125   
1126 #if 0
1127   zdbug((LOG_DEBUG,"subscr_rlm_sendit len: %d", snotice.z_message_len));
1128 #endif
1129   realm_handoff(&snotice, 1, &(who->addr), realm, 0);
1130   free(pack);
1131   
1132   return(ZERR_NONE);
1133 }
1134
1135 /* Called from subscr_realm and subscr_foreign_user */
1136 static Code_t
1137 subscr_add_raw(Client *client,
1138                ZRealm *realm,
1139                Destlist *newsubs)
1140 {
1141   Destlist *subs, *subs2, **head;
1142   Code_t retval;
1143
1144 #if 0
1145   zdbug((LOG_DEBUG, "subscr_add_raw"));
1146 #endif
1147   head = (realm) ? &realm->subs : &client->subs;
1148
1149   /* Loop over the new subscriptions. */
1150   for (subs = newsubs; subs; subs = subs2) {
1151     subs2 = subs->next;
1152 #ifdef DEBUG
1153     zdbug((LOG_DEBUG,"subscr_add_raw: %s/%s/%s", subs->dest.classname->string, subs->dest.inst->string, subs->dest.recip->string));
1154     if (realm)
1155       zdbug((LOG_DEBUG,"subscr_add_raw: realm is %s", realm->name));
1156 #endif
1157     retval = triplet_register(client, &subs->dest, realm);
1158     if (retval != ZERR_NONE) {
1159         free_subscription(subs);
1160         if (retval == ZSRV_CLASSXISTS) {
1161             continue;
1162         } else {
1163             free_subscriptions(subs2);
1164             return retval;
1165         }
1166     } else {
1167       if (!realm) {
1168         ZRealm *remrealm = 
1169           realm_get_realm_by_name(subs->dest.recip->string + 1);
1170         if (remrealm) {
1171           Destlist *sub = (Destlist *) malloc(sizeof(Destlist));
1172           if (!sub) {
1173             syslog(LOG_WARNING, "subscr_add_raw: no mem");
1174           } else {
1175             sub->dest.classname = make_string(subs->dest.classname->string, 0);
1176             sub->dest.inst = make_string(subs->dest.inst->string, 0);
1177             sub->dest.recip = make_string(subs->dest.recip->string, 0);
1178 #if 1
1179             zdbug ((LOG_DEBUG, "subscr: add %s/%s/%s in %s",
1180                     sub->dest.classname->string, sub->dest.inst->string, 
1181                     sub->dest.recip->string, remrealm->name));
1182 #endif
1183             Destlist_insert(&remrealm->remsubs, sub);
1184           }
1185         }
1186       }
1187     }
1188     Destlist_insert(head, subs);
1189   }
1190   return ZERR_NONE;
1191 }
1192
1193 /* Called from bdump_recv_loop to decapsulate realm subs */
1194 Code_t
1195 subscr_realm(ZRealm *realm,
1196              ZNotice_t *notice)
1197 {
1198         Destlist  *newsubs;
1199
1200         newsubs = extract_subscriptions(notice);
1201
1202         if (!newsubs) {
1203                 syslog(LOG_WARNING, "empty subs in subscr_realm");
1204                 return(ZERR_NONE);
1205         }
1206
1207         return(subscr_add_raw(realm->client, realm, newsubs));
1208 }
1209
1210 /* Like realm_sendit, this only takes one item from subs */
1211 static void
1212 subscr_unsub_sendit(Client *who,
1213                     Destlist *subs,
1214                     ZRealm *realm)
1215 {
1216   ZNotice_t unotice;
1217   Code_t retval;
1218   char **list;
1219   char *pack;
1220   int packlen;
1221   Destlist *subsp, *subsn;
1222
1223   for (subsp = realm->remsubs; subsp; subsp = subsn) {
1224     subsn = subsp->next;
1225     if (ZDest_eq(&subs->dest, &subsp->dest)) {
1226 #if 1
1227       zdbug ((LOG_DEBUG, "subscr: del %s/%s/%s in %s",
1228               subsp->dest.classname->string, subsp->dest.inst->string, 
1229               subsp->dest.recip->string, realm->name));
1230 #endif
1231       Destlist_delete(subsp);
1232       free_subscription(subsp);
1233       break;
1234     }
1235   }
1236
1237   if ((list=(char **)malloc((NUM_FIELDS)*sizeof(char *))) == (char **)0) {
1238       syslog(LOG_ERR, "subscr_unsub_sendit malloc");
1239       return;
1240   }
1241
1242   list[0] = subs->dest.classname->string;
1243   list[1] = subs->dest.inst->string;
1244   list[2] = "";
1245
1246   unotice.z_class = ZEPHYR_CTL_CLASS;
1247   unotice.z_class_inst = ZEPHYR_CTL_REALM;
1248   unotice.z_opcode = REALM_UNSUBSCRIBE;
1249   unotice.z_recipient = "";
1250   unotice.z_kind = ACKED;
1251
1252   unotice.z_sender = "";
1253   unotice.z_port = srv_addr.sin_port;
1254   unotice.z_num_other_fields = 0;
1255   unotice.z_default_format = "";
1256
1257   if ((retval = ZFormatNoticeList(&unotice, list, NUM_FIELDS, &pack, &packlen, ZNOAUTH)) != ZERR_NONE) {
1258     syslog(LOG_WARNING, "subscr_unsub_sendit format: %s",
1259            error_message(retval));
1260     free(list);
1261     return;
1262   }
1263   free(list);
1264
1265   if ((retval = ZParseNotice(pack, packlen, &unotice)) != ZERR_NONE) {
1266     syslog(LOG_WARNING, "subscr_unsub_sendit parse: %s",
1267            error_message(retval));
1268     free(pack);
1269     return;
1270   }
1271   realm_handoff(&unotice, 1, who ? &(who->addr) : NULL, realm, 0);
1272   free(pack);
1273 }
1274
1275 /* Called from bump_send_loop by way of realm_send_realms */
1276 Code_t
1277 subscr_send_realm_subs(ZRealm *realm)
1278 {
1279   int i = 0;
1280   Destlist *subs, *next;
1281   char buf[512];
1282   char *list[7 * NUM_FIELDS];
1283   int num = 0;
1284   Code_t retval;
1285
1286 #if 0
1287   zdbug((LOG_DEBUG, "send_realm_subs"));
1288 #endif
1289
1290   strcpy(buf, realm->name);
1291   list[num++] = buf;
1292
1293   retval = bdump_send_list_tcp(SERVACK, &srv_addr, ZEPHYR_ADMIN_CLASS,
1294                                "", ADMIN_NEWREALM, "", "", list, num);
1295   if (retval != ZERR_NONE) {
1296     syslog(LOG_ERR, "subscr_send_realm_subs newclt: %s", error_message(retval));
1297     return retval;
1298   }
1299   
1300   if (!realm->subs)
1301     return ZERR_NONE;
1302
1303   for (subs=realm->subs; subs; subs = next) {
1304     next = subs->next;
1305 #ifdef DEBUG
1306     zdbug ((LOG_DEBUG, "send_realm_subs: %s/%s/%s", subs->dest.classname->string,
1307             subs->dest.inst->string, subs->dest.recip->string));
1308 #endif
1309     /* for each subscription */
1310     list[i * NUM_FIELDS] = subs->dest.classname->string;
1311     list[i * NUM_FIELDS + 1] = subs->dest.inst->string;
1312     list[i * NUM_FIELDS + 2] = subs->dest.recip->string;
1313     i++;
1314     if (i >= 7) {
1315       /* we only put 7 in each packet, so we don't run out of room */
1316       retval = bdump_send_list_tcp(ACKED, &srv_addr,
1317                                    ZEPHYR_CTL_CLASS, "",
1318                                    REALM_SUBSCRIBE, "", "", list,
1319                                    i * NUM_FIELDS);
1320       if (retval != ZERR_NONE) {
1321         syslog(LOG_ERR, "subscr_send_realm_subs subs: %s",
1322                error_message(retval));
1323         return retval;
1324       }
1325       i = 0;
1326     }
1327   }
1328   if (i) {
1329     retval = bdump_send_list_tcp(ACKED, &srv_addr, ZEPHYR_CTL_CLASS,
1330                                  "", REALM_SUBSCRIBE, "", "", list,
1331                                  i * NUM_FIELDS);
1332     if (retval != ZERR_NONE) {
1333       syslog(LOG_ERR, "subscr_send_realm_subs subs: %s",
1334              error_message(retval));
1335       return retval;
1336     }
1337   }
1338
1339   return ZERR_NONE;
1340 }
1341
1342 Code_t
1343 subscr_realm_subs(ZRealm *realm)
1344 {
1345   Destlist *subs, *next;
1346   char *text[2 + NUM_FIELDS];
1347   unsigned short num = 0;
1348   Code_t retval;
1349   ZNotice_t snotice;
1350   char *pack;
1351   int packlen;
1352   Client **clientp;
1353   char port[16];
1354
1355 #if 0
1356   zdbug((LOG_DEBUG, "realm_subs"));
1357 #endif
1358
1359   if (!realm->remsubs)
1360     return ZERR_NONE;
1361
1362   for (subs=realm->remsubs; subs; subs = next) {
1363     next = subs->next;
1364 #ifdef DEBUG
1365     zdbug ((LOG_DEBUG, "realm_subs: %s/%s/%s", subs->dest.classname->string,
1366             subs->dest.inst->string, subs->dest.recip->string));
1367 #endif
1368
1369     num = 0;
1370     if ((retval = ZMakeAscii(port, sizeof(port), (unsigned char *) 
1371                              &num, sizeof(u_short))) != ZERR_NONE) 
1372       {
1373         syslog(LOG_ERR, "subscr_rlm_sendit make ascii: %s",
1374                error_message(retval));
1375         return(ZERR_NONE);
1376       }
1377
1378     text[0] = "0.0.0.0";
1379     text[1] = port;
1380     text[2] = subs->dest.classname->string;
1381     text[3] = subs->dest.inst->string;
1382     text[4] = subs->dest.recip->string;
1383
1384     /* format snotice */
1385     snotice.z_class_inst = ZEPHYR_CTL_REALM;
1386     snotice.z_opcode = REALM_REQ_SUBSCRIBE;
1387     snotice.z_port = 0;
1388     snotice.z_class = ZEPHYR_CTL_CLASS;
1389
1390     snotice.z_recipient = "";
1391     snotice.z_kind = ACKED;
1392     snotice.z_num_other_fields = 0;
1393     snotice.z_default_format = "";
1394     /* Evil. In the event this is ACL'd, pick a user who is subscribed and
1395        resubmit them as the sender. */
1396     clientp = triplet_lookup(&subs->dest);
1397     if (!clientp)
1398       snotice.z_sender = "";
1399     else
1400       snotice.z_sender = (*clientp)->principal->string;
1401     snotice.z_default_format = "";
1402
1403     if ((retval = ZFormatNoticeList(&snotice, text, NUM_FIELDS + 2,
1404                                     &pack, &packlen, ZNOAUTH)) != ZERR_NONE) 
1405       {
1406         syslog(LOG_WARNING, "subscr_rlm_subs format: %s",
1407                error_message(retval));
1408         return(ZERR_NONE);
1409       }
1410   
1411     if ((retval = ZParseNotice(pack, packlen, &snotice)) != ZERR_NONE) {
1412       syslog(LOG_WARNING, "subscr_rlm_subs parse: %s",
1413              error_message(retval));
1414       free(pack);
1415       return(ZERR_NONE);
1416     }
1417     realm_handoff(&snotice, 1, NULL, realm, 0);
1418     free(pack);
1419   }
1420
1421   return ZERR_NONE;
1422 }
1423
1424 /* Called from subscr_foreign_user for REALM_REQ_SUBSCRIBE */
1425 static Code_t
1426 subscr_check_foreign_subs(ZNotice_t *notice,
1427                           struct sockaddr_in *who,
1428                           Server *server,
1429                           ZRealm *realm,
1430                           Destlist *newsubs)
1431 {
1432     Destlist *subs, *next;
1433     Acl *acl;
1434     char **text;
1435     int found = 0;
1436     ZNotice_t snotice;
1437     char *pack, *cp;
1438     int packlen;
1439     Code_t retval;
1440     String *sender;
1441
1442     for (subs = newsubs; subs; subs = subs->next)
1443         found++;
1444
1445     if (found == 0)
1446         return(ZERR_NONE);
1447   
1448     sender = make_string(notice->z_sender, 0);
1449     
1450     if ((text = (char **)malloc((found * NUM_FIELDS + 2) * sizeof(char *))) 
1451         == (char **) 0) {
1452         syslog(LOG_ERR, "subscr_ck_forn_subs no mem(text)");
1453         free_string(sender);
1454         return(ENOMEM);
1455     }
1456
1457     /* grab the client information from the incoming message */
1458     cp = notice->z_message;
1459     text[0] = cp;
1460
1461     I_ADVANCE(2);
1462     text[1] = cp;
1463
1464     I_ADVANCE(3);
1465
1466     found = 0;
1467     for (subs = newsubs; subs; subs = next) {
1468         ZRealm *rlm;
1469         next=subs->next;
1470         if (subs->dest.recip->string[0] != '\0') {
1471           rlm = realm_which_realm(who);
1472           syslog(LOG_WARNING, "subscr bad recip %s by %s (%s)",
1473                  subs->dest.recip->string,
1474                  sender->string, rlm->name);
1475           continue;
1476         }
1477         acl = class_get_acl(subs->dest.classname);
1478         if (acl) {
1479             rlm = realm_which_realm(who); 
1480             if (rlm && server == me_server) { 
1481                 if (!realm_sender_in_realm(rlm->name, sender->string)) { 
1482                     syslog(LOG_WARNING, "subscr auth not verifiable %s (%s) class %s",
1483                            sender->string, rlm->name, 
1484                            subs->dest.classname->string);
1485                     free_subscriptions(newsubs);
1486                     free_string(sender);
1487                     free(text);
1488                     return ZSRV_CLASSRESTRICTED;
1489                 } 
1490             } 
1491             if (!access_check(sender->string, acl, SUBSCRIBE)) {
1492                 syslog(LOG_WARNING, "subscr unauth %s class %s",
1493                        sender->string, subs->dest.classname->string);
1494                 continue; /* the for loop */
1495             }
1496             if (wildcard_instance == subs->dest.inst) {
1497                 if (!access_check(sender->string, acl, INSTWILD)) {
1498                     syslog(LOG_WARNING,
1499                            "subscr unauth %s class %s wild inst",
1500                            sender->string, subs->dest.classname->string);
1501                     continue;
1502                 }
1503             }
1504         }
1505
1506         /* okay to subscribe.  save for return trip */
1507         text[found*NUM_FIELDS + 2] = subs->dest.classname->string;
1508         text[found*NUM_FIELDS + 3] = subs->dest.inst->string;
1509         text[found*NUM_FIELDS + 4] = "";
1510         found++;
1511         
1512         retval = triplet_register(realm->client, &subs->dest, realm);
1513 #ifdef DEBUG
1514         zdbug ((LOG_DEBUG, "ck_frn_subs: %s/%s/%s", subs->dest.classname->string,
1515                 subs->dest.inst->string, subs->dest.recip->string));
1516 #endif
1517
1518         if (retval != ZERR_NONE) {
1519             if (retval == ZSRV_CLASSXISTS) {
1520                 continue;
1521             } else {
1522                 free_subscriptions(newsubs); /* subs->next XXX */
1523                 free_string(sender);
1524                 free(text);
1525                 return retval;
1526             }
1527         }
1528         Destlist_insert(&realm->subs, subs);
1529     }
1530     /* don't send confirmation if we're not the initial server contacted */
1531     if (!(server_which_server(who) || found == 0)) {
1532         snotice = *notice;
1533         snotice.z_opcode = REALM_ADD_SUBSCRIBE;
1534         snotice.z_class_inst = ZEPHYR_CTL_REALM;
1535         snotice.z_port = srv_addr.sin_port;
1536         if ((retval = ZFormatNoticeList(&snotice, text, found * NUM_FIELDS + 2, &pack, &packlen, ZNOAUTH)) != ZERR_NONE) {
1537             syslog(LOG_WARNING, "subscr_ck_forn_subs format: %s",
1538                    error_message(retval));
1539             free_string(sender);
1540             free(text);
1541             return(ZERR_NONE);      
1542         }
1543         if ((retval = ZParseNotice(pack, packlen, &snotice)) != ZERR_NONE) {
1544             syslog(LOG_WARNING, "subscr_ck_forn_subs parse: %s",
1545                    error_message(retval));
1546             free_string(sender);
1547             free(text);
1548             free(pack);
1549             return(ZERR_NONE);
1550         }
1551         realm_handoff(&snotice, 1, who, realm, 0);
1552         free(pack);
1553     }
1554     free_string(sender);
1555     free(text);
1556     return ZERR_NONE;
1557 }
1558
1559 /* Called from realm_control_dispatch for REALM_REQ/ADD_SUBSCRIBE */
1560 Code_t subscr_foreign_user(ZNotice_t *notice,
1561                            struct sockaddr_in *who,
1562                            Server *server,
1563                            ZRealm *realm)
1564 {
1565   Destlist *newsubs, *temp;
1566   Code_t status;
1567   Client *client;
1568   ZNotice_t snotice;
1569   struct sockaddr_in newwho;
1570   char *cp, *tp0, *tp1;
1571   char rlm_recipient[REALM_SZ + 1];
1572   
1573 #if 0
1574   zdbug((LOG_DEBUG, "subscr_foreign_user"));
1575 #endif
1576   
1577   tp0 = cp = notice->z_message;
1578   
1579   newwho.sin_addr.s_addr = inet_addr(cp);
1580   if (newwho.sin_addr.s_addr == -1) {
1581     syslog(LOG_ERR, "malformed addr from %s", notice->z_sender);
1582     return(ZERR_NONE);
1583   }
1584
1585   I_ADVANCE(0);
1586   tp1 = cp;
1587   
1588   snotice = *notice;
1589   
1590   if ((status = ZReadAscii(cp, strlen(cp), (unsigned char *)&snotice.z_port, sizeof(u_short)))
1591       != ZERR_NONE) 
1592     {
1593       syslog(LOG_ERR, "subscr_foreign_user read ascii: %s",
1594              error_message(status));
1595       return(ZERR_NONE);
1596     }
1597
1598   I_ADVANCE(1);
1599   
1600   snotice.z_message = cp;
1601   snotice.z_message_len = notice->z_message_len - (cp - notice->z_message);
1602
1603   newsubs = extract_subscriptions(&snotice);
1604   if (!newsubs) {
1605     syslog(LOG_WARNING, "empty subscr for %s", notice->z_sender);
1606     return(ZERR_NONE);
1607   }
1608
1609   if (!strcmp(snotice.z_opcode, REALM_ADD_SUBSCRIBE)) {
1610     /* this was approved by the other realm, add subscriptions */
1611     
1612     if (!strcmp(tp0, "0.0.0.0")) {
1613       /* skip bogus ADD reply from subscr_realm_subs */
1614       zdbug((LOG_DEBUG, "subscr_foreign_user ADD skipped"));
1615       return(ZERR_NONE);
1616     }
1617
1618     zdbug((LOG_DEBUG, "subscr_foreign_user ADD %s/%s", tp0, tp1));
1619     client = client_find(&newwho.sin_addr, snotice.z_port);
1620     if (client == (Client *)0) {
1621       syslog(LOG_WARNING, "no client at %s/%d",
1622              inet_ntoa(newwho.sin_addr), ntohs(snotice.z_port));
1623       free_subscriptions(newsubs);
1624       return(ZERR_NONE);
1625     }
1626     
1627     /* translate the recipient to represent the foreign realm */
1628     sprintf(rlm_recipient, "@%s", realm->name);
1629     for (temp = newsubs; temp; temp = temp->next) {
1630 #if 0
1631       syslog(LOG_DEBUG, "in foreign_user: class is %s", temp->dest.classname->string);
1632 #endif      
1633         temp->dest.recip = make_string(rlm_recipient, 0);
1634     }
1635     
1636     status = subscr_add_raw(client, (ZRealm *)0, newsubs);
1637   } else if (!strcmp(snotice.z_opcode, REALM_REQ_SUBSCRIBE)) {
1638     zdbug((LOG_DEBUG, "subscr_foreign_user REQ %s/%s", tp0, tp1));
1639     status = subscr_check_foreign_subs(notice, who, server, realm, newsubs);
1640   } else {
1641     syslog(LOG_ERR, "bogus opcode %s in subscr_forn_user",
1642            snotice.z_opcode);
1643     status = ZERR_NONE;
1644   }
1645   return(status);
1646 }
1647