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