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