DR AAF CADI integration
[dmaap/datarouter.git] / datarouter-node / src / main / java / org / onap / dmaap / datarouter / node / NodeConfig.java
1 /*******************************************************************************
2  * ============LICENSE_START==================================================
3  * * org.onap.dmaap
4  * * ===========================================================================
5  * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
6  * * ===========================================================================
7  * * Licensed under the Apache License, Version 2.0 (the "License");
8  * * you may not use this file except in compliance with the License.
9  * * You may obtain a copy of the License at
10  * *
11  *  *      http://www.apache.org/licenses/LICENSE-2.0
12  * *
13  *  * Unless required by applicable law or agreed to in writing, software
14  * * distributed under the License is distributed on an "AS IS" BASIS,
15  * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * * See the License for the specific language governing permissions and
17  * * limitations under the License.
18  * * ============LICENSE_END====================================================
19  * *
20  * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
21  * *
22  ******************************************************************************/
23
24
25 package org.onap.dmaap.datarouter.node;
26
27 import java.io.File;
28 import java.util.Arrays;
29 import java.util.HashSet;
30 import java.util.Hashtable;
31 import java.util.Vector;
32 import org.apache.log4j.Logger;
33
34 /**
35  * Processed configuration for this node.
36  * <p>
37  * The NodeConfig represents a processed configuration from the Data Router provisioning server.  Each time
38  * configuration data is received from the provisioning server, a new NodeConfig is created and the previous one
39  * discarded.
40  */
41 public class NodeConfig {
42     private static Logger logger = Logger.getLogger("org.onap.dmaap.datarouter.node.NodeConfig");
43     /**
44      * Raw configuration entry for a data router node
45      */
46     public static class ProvNode {
47
48         private String cname;
49
50         /**
51          * Construct a node configuration entry.
52          *
53          * @param cname The cname of the node.
54          */
55         public ProvNode(String cname) {
56             this.cname = cname;
57         }
58
59         /**
60          * Get the cname of the node
61          */
62         public String getCName() {
63             return (cname);
64         }
65     }
66
67     /**
68      * Raw configuration entry for a provisioning parameter
69      */
70     public static class ProvParam {
71
72         private String name;
73         private String value;
74
75         /**
76          * Construct a provisioning parameter configuration entry.
77          *
78          * @param name The name of the parameter.
79          * @param value The value of the parameter.
80          */
81         public ProvParam(String name, String value) {
82             this.name = name;
83             this.value = value;
84         }
85
86         /**
87          * Get the name of the parameter.
88          */
89         public String getName() {
90             return (name);
91         }
92
93         /**
94          * Get the value of the parameter.
95          */
96         public String getValue() {
97             return (value);
98         }
99     }
100
101     /**
102      * Raw configuration entry for a data feed.
103      */
104     public static class ProvFeed {
105
106         private String id;
107         private String logdata;
108         private String status;
109         private String createdDate;
110         /*
111          * AAF changes: TDP EPIC US# 307413
112          * Passing aafInstance from to identify legacy/AAF feeds
113          */
114         private String aafInstance;
115
116         /**
117          * Construct a feed configuration entry.
118          *
119          * @param id The feed ID of the entry.
120          * @param logdata String for log entries about the entry.
121          * @param status The reason why this feed cannot be used (Feed has been deleted, Feed has been suspended) or
122          * null if it is valid.
123          */
124         public ProvFeed(String id, String logdata, String status, String createdDate, String aafInstance) {
125             this.id = id;
126             this.logdata = logdata;
127             this.status = status;
128             this.createdDate = createdDate;
129             this.aafInstance = aafInstance;
130         }
131
132         /**
133          * Get the created date of the data feed.
134          */
135         public String getCreatedDate()
136         {
137             return(createdDate);
138         }
139
140         /**
141          * Get the aafInstance of the data feed.
142          */
143         public String getAafInstance() {
144             return aafInstance;
145         }
146
147         /**
148          * Get the feed id of the data feed.
149          */
150         public String getId() {
151             return (id);
152         }
153
154         /**
155          * Get the log data of the data feed.
156          */
157         public String getLogData() {
158             return (logdata);
159         }
160
161         /**
162          * Get the status of the data feed.
163          */
164         public String getStatus() {
165             return (status);
166         }
167     }
168
169     /**
170      * Raw configuration entry for a feed user.
171      */
172     public static class ProvFeedUser {
173
174         private String feedid;
175         private String user;
176         private String credentials;
177
178         /**
179          * Construct a feed user configuration entry
180          *
181          * @param feedid The feed id.
182          * @param user The user that will publish to the feed.
183          * @param credentials The Authorization header the user will use to publish.
184          */
185         public ProvFeedUser(String feedid, String user, String credentials) {
186             this.feedid = feedid;
187             this.user = user;
188             this.credentials = credentials;
189         }
190
191         /**
192          * Get the feed id of the feed user.
193          */
194         public String getFeedId() {
195             return (feedid);
196         }
197
198         /**
199          * Get the user for the feed user.
200          */
201         public String getUser() {
202             return (user);
203         }
204
205         /**
206          * Get the credentials for the feed user.
207          */
208         public String getCredentials() {
209             return (credentials);
210         }
211     }
212
213     /**
214      * Raw configuration entry for a feed subnet
215      */
216     public static class ProvFeedSubnet {
217
218         private String feedid;
219         private String cidr;
220
221         /**
222          * Construct a feed subnet configuration entry
223          *
224          * @param feedid The feed ID
225          * @param cidr The CIDR allowed to publish to the feed.
226          */
227         public ProvFeedSubnet(String feedid, String cidr) {
228             this.feedid = feedid;
229             this.cidr = cidr;
230         }
231
232         /**
233          * Get the feed id of the feed subnet.
234          */
235         public String getFeedId() {
236             return (feedid);
237         }
238
239         /**
240          * Get the CIDR of the feed subnet.
241          */
242         public String getCidr() {
243             return (cidr);
244         }
245     }
246
247     /**
248      * Raw configuration entry for a subscription
249      */
250     public static class ProvSubscription {
251
252         private String subid;
253         private String feedid;
254         private String url;
255         private String authuser;
256         private String credentials;
257         private boolean metaonly;
258         private boolean use100;
259         private boolean privilegedSubscriber;
260         private boolean followRedirect;
261         private boolean decompress;
262
263         /**
264          * Construct a subscription configuration entry
265          *
266          * @param subid The subscription ID
267          * @param feedid The feed ID
268          * @param url The base delivery URL (not including the fileid)
269          * @param authuser The user in the credentials used to deliver
270          * @param credentials The credentials used to authenticate to the delivery URL exactly as they go in the
271          * Authorization header.
272          * @param metaonly Is this a meta data only subscription?
273          * @param use100 Should we send Expect: 100-continue?
274          * @param privilegedSubscriber Can we wait to receive a delete file call before deleting file
275          * @param followRedirect Is follow redirect of destination enabled?
276          * @param decompress To see if they want their information compressed or decompressed
277          */
278         public ProvSubscription(String subid, String feedid, String url, String authuser, String credentials, boolean metaonly, boolean use100, boolean privilegedSubscriber, boolean followRedirect, boolean decompress) {
279             this.subid = subid;
280             this.feedid = feedid;
281             this.url = url;
282             this.authuser = authuser;
283             this.credentials = credentials;
284             this.metaonly = metaonly;
285             this.use100 = use100;
286             this.privilegedSubscriber = privilegedSubscriber;
287             this.followRedirect = followRedirect;
288             this.decompress = decompress;
289         }
290
291         /**
292          * Get the subscription ID
293          */
294         public String getSubId() {
295             return (subid);
296         }
297
298         /**
299          * Get the feed ID
300          */
301         public String getFeedId() {
302             return (feedid);
303         }
304
305         /**
306          * Get the delivery URL
307          */
308         public String getURL() {
309             return (url);
310         }
311
312         /**
313          * Get the user
314          */
315         public String getAuthUser() {
316             return (authuser);
317         }
318
319         /**
320          * Get the delivery credentials
321          */
322         public String getCredentials() {
323             return (credentials);
324         }
325
326         /**
327          * Is this a meta data only subscription?
328          */
329         public boolean isMetaDataOnly() {
330             return (metaonly);
331         }
332
333         /**
334          * Should we send Expect: 100-continue?
335          */
336         public boolean isUsing100() {
337             return (use100);
338         }
339
340         /**
341          * Can we wait to receive a delete file call before deleting file
342          */
343         public boolean isPrivilegedSubscriber() {
344             return (privilegedSubscriber);
345         }
346
347         /**
348          * Should i decompress the file before sending it on
349         */
350         public boolean isDecompress() {
351             return (decompress);
352         }
353
354         /**
355          *  New field is added - FOLLOW_REDIRECTS feature iTrack:DATARTR-17 - 1706
356          *      Get the followRedirect of this destination
357          */
358         boolean getFollowRedirect() {
359             return(followRedirect);
360         }
361     }
362
363     /**
364      * Raw configuration entry for controlled ingress to the data router node
365      */
366     public static class ProvForceIngress {
367
368         private String feedid;
369         private String subnet;
370         private String user;
371         private String[] nodes;
372
373         /**
374          * Construct a forced ingress configuration entry
375          *
376          * @param feedid The feed ID that this entry applies to
377          * @param subnet The CIDR for which publisher IP addresses this entry applies to or "" if it applies to all
378          * publisher IP addresses
379          * @param user The publishing user this entry applies to or "" if it applies to all publishing users.
380          * @param nodes The array of FQDNs of the data router nodes to redirect publication attempts to.
381          */
382         public ProvForceIngress(String feedid, String subnet, String user, String[] nodes) {
383             this.feedid = feedid;
384             this.subnet = subnet;
385             this.user = user;
386             //Sonar fix
387             if(nodes == null) {
388                 this.nodes = new String[0];
389             } else {
390                 this.nodes = Arrays.copyOf(nodes, nodes.length);
391             }
392         }
393
394         /**
395          * Get the feed ID
396          */
397         public String getFeedId() {
398             return (feedid);
399         }
400
401         /**
402          * Get the subnet
403          */
404         public String getSubnet() {
405             return (subnet);
406         }
407
408         /**
409          * Get the user
410          */
411         public String getUser() {
412             return (user);
413         }
414
415         /**
416          * Get the node
417          */
418         public String[] getNodes() {
419             return (nodes);
420         }
421     }
422
423     /**
424      * Raw configuration entry for controlled egress from the data router
425      */
426     public static class ProvForceEgress {
427
428         private String subid;
429         private String node;
430
431         /**
432          * Construct a forced egress configuration entry
433          *
434          * @param subid The subscription ID the subscription with forced egress
435          * @param node The node handling deliveries for this subscription
436          */
437         public ProvForceEgress(String subid, String node) {
438             this.subid = subid;
439             this.node = node;
440         }
441
442         /**
443          * Get the subscription ID
444          */
445         public String getSubId() {
446             return (subid);
447         }
448
449         /**
450          * Get the node
451          */
452         public String getNode() {
453             return (node);
454         }
455     }
456
457     /**
458      * Raw configuration entry for routing within the data router network
459      */
460     public static class ProvHop {
461
462         private String from;
463         private String to;
464         private String via;
465
466         /**
467          * A human readable description of this entry
468          */
469         public String toString() {
470             return ("Hop " + from + "->" + to + " via " + via);
471         }
472
473         /**
474          * Construct a hop entry
475          *
476          * @param from The FQDN of the node with the data to be delivered
477          * @param to The FQDN of the node that will deliver to the subscriber
478          * @param via The FQDN of the node where the from node should send the data
479          */
480         public ProvHop(String from, String to, String via) {
481             this.from = from;
482             this.to = to;
483             this.via = via;
484         }
485
486         /**
487          * Get the from node
488          */
489         public String getFrom() {
490             return (from);
491         }
492
493         /**
494          * Get the to node
495          */
496         public String getTo() {
497             return (to);
498         }
499
500         /**
501          * Get the next intermediate node
502          */
503         public String getVia() {
504             return (via);
505         }
506     }
507
508     private static class Redirection {
509
510         SubnetMatcher snm;
511         String user;
512         String[] nodes;
513     }
514
515     private static class Feed {
516
517         String loginfo;
518         String status;
519         SubnetMatcher[] subnets;
520         Hashtable<String, String> authusers = new Hashtable<String, String>();
521         Redirection[] redirections;
522         Target[] targets;
523         String createdDate;
524         String aafInstance;
525     }
526
527     private Hashtable<String, String> params = new Hashtable<>();
528     private Hashtable<String, Feed> feeds = new Hashtable<>();
529     private Hashtable<String, DestInfo> nodeinfo = new Hashtable<>();
530     private Hashtable<String, DestInfo> subinfo = new Hashtable<>();
531     private Hashtable<String, IsFrom> nodes = new Hashtable<>();
532     private Hashtable<String, ProvSubscription> provSubscriptions = new Hashtable<>();
533     private String myname;
534     private String myauth;
535     private DestInfo[] alldests;
536     private int rrcntr;
537
538     /**
539      * Process the raw provisioning data to configure this node
540      *
541      * @param pd The parsed provisioning data
542      * @param myname My name as seen by external systems
543      * @param spooldir The directory where temporary files live
544      * @param port The port number for URLs
545      * @param nodeauthkey The keying string used to generate node authentication credentials
546      */
547     public NodeConfig(ProvData pd, String myname, String spooldir, int port, String nodeauthkey) {
548         this.myname = myname;
549         for (ProvParam p : pd.getParams()) {
550             params.put(p.getName(), p.getValue());
551         }
552         Vector<DestInfo> destInfos = new Vector<>();
553         myauth = NodeUtils.getNodeAuthHdr(myname, nodeauthkey);
554         for (ProvNode pn : pd.getNodes()) {
555             String cName = pn.getCName();
556             if (nodeinfo.get(cName) != null) {
557                 continue;
558             }
559             String auth = NodeUtils.getNodeAuthHdr(cName, nodeauthkey);
560             DestInfo di = new DestInfo("n:" + cName, spooldir + "/n/" + cName, null, "n2n-" + cName,
561                     "https://" + cName + ":" + port + "/internal/publish", cName, myauth, false, true, false, false, false);
562             (new File(di.getSpool())).mkdirs();
563             destInfos.add(di);
564             nodeinfo.put(cName, di);
565             nodes.put(auth, new IsFrom(cName));
566         }
567         PathFinder pf = new PathFinder(myname, nodeinfo.keySet().toArray(new String[0]), pd.getHops());
568         Hashtable<String, Vector<Redirection>> rdtab = new Hashtable<>();
569         for (ProvForceIngress pfi : pd.getForceIngress()) {
570             Vector<Redirection> v = rdtab.get(pfi.getFeedId());
571             if (v == null) {
572                 v = new Vector<>();
573                 rdtab.put(pfi.getFeedId(), v);
574             }
575             Redirection r = new Redirection();
576             if (pfi.getSubnet() != null) {
577                 r.snm = new SubnetMatcher(pfi.getSubnet());
578             }
579             r.user = pfi.getUser();
580             r.nodes = pfi.getNodes();
581             v.add(r);
582         }
583         Hashtable<String, Hashtable<String, String>> pfutab = new Hashtable<>();
584         for (ProvFeedUser pfu : pd.getFeedUsers()) {
585             Hashtable<String, String> t = pfutab.get(pfu.getFeedId());
586             if (t == null) {
587                 t = new Hashtable<>();
588                 pfutab.put(pfu.getFeedId(), t);
589             }
590             t.put(pfu.getCredentials(), pfu.getUser());
591         }
592         Hashtable<String, String> egrtab = new Hashtable<>();
593         for (ProvForceEgress pfe : pd.getForceEgress()) {
594             if (pfe.getNode().equals(myname) || nodeinfo.get(pfe.getNode()) == null) {
595                 continue;
596             }
597             egrtab.put(pfe.getSubId(), pfe.getNode());
598         }
599         Hashtable<String, Vector<SubnetMatcher>> pfstab = new Hashtable<>();
600         for (ProvFeedSubnet pfs : pd.getFeedSubnets()) {
601             Vector<SubnetMatcher> v = pfstab.get(pfs.getFeedId());
602             if (v == null) {
603                 v = new Vector<>();
604                 pfstab.put(pfs.getFeedId(), v);
605             }
606             v.add(new SubnetMatcher(pfs.getCidr()));
607         }
608         Hashtable<String, StringBuffer> feedTargets = new Hashtable<>();
609         HashSet<String> allfeeds = new HashSet<>();
610         for (ProvFeed pfx : pd.getFeeds()) {
611             if (pfx.getStatus() == null) {
612                 allfeeds.add(pfx.getId());
613             }
614         }
615         for (ProvSubscription provSubscription : pd.getSubscriptions()) {
616             String subId = provSubscription.getSubId();
617             String feedId = provSubscription.getFeedId();
618             if (!allfeeds.contains(feedId)) {
619                 continue;
620             }
621             if (subinfo.get(subId) != null) {
622                 continue;
623             }
624             int sididx = 999;
625             try {
626                 sididx = Integer.parseInt(subId);
627                 sididx -= sididx % 100;
628             } catch (Exception e) {
629                 logger.error("NODE0517 Exception NodeConfig: "+e);
630             }
631             String subscriptionDirectory = sididx + "/" + subId;
632             DestInfo destinationInfo = new DestInfo("s:" + subId,
633                     spooldir + "/s/" + subscriptionDirectory, provSubscription);
634             (new File(destinationInfo.getSpool())).mkdirs();
635             destInfos.add(destinationInfo);
636             provSubscriptions.put(subId, provSubscription);
637             subinfo.put(subId, destinationInfo);
638             String egr = egrtab.get(subId);
639             if (egr != null) {
640                 subId = pf.getPath(egr) + subId;
641             }
642             StringBuffer sb = feedTargets.get(feedId);
643             if (sb == null) {
644                 sb = new StringBuffer();
645                 feedTargets.put(feedId, sb);
646             }
647             sb.append(' ').append(subId);
648         }
649         alldests = destInfos.toArray(new DestInfo[0]);
650         for (ProvFeed pfx : pd.getFeeds()) {
651             String fid = pfx.getId();
652             Feed f = feeds.get(fid);
653             if (f != null) {
654                 continue;
655             }
656             f = new Feed();
657             feeds.put(fid, f);
658             f.createdDate = pfx.getCreatedDate();
659             f.loginfo = pfx.getLogData();
660             f.status = pfx.getStatus();
661             /*
662              * AAF changes: TDP EPIC US# 307413
663              * Passing aafInstance from ProvFeed to identify legacy/AAF feeds
664              */
665             f.aafInstance = pfx.getAafInstance();
666             Vector<SubnetMatcher> v1 = pfstab.get(fid);
667             if (v1 == null) {
668                 f.subnets = new SubnetMatcher[0];
669             } else {
670                 f.subnets = v1.toArray(new SubnetMatcher[0]);
671             }
672             Hashtable<String, String> h1 = pfutab.get(fid);
673             if (h1 == null) {
674                 h1 = new Hashtable<String, String>();
675             }
676             f.authusers = h1;
677             Vector<Redirection> v2 = rdtab.get(fid);
678             if (v2 == null) {
679                 f.redirections = new Redirection[0];
680             } else {
681                 f.redirections = v2.toArray(new Redirection[0]);
682             }
683             StringBuffer sb = feedTargets.get(fid);
684             if (sb == null) {
685                 f.targets = new Target[0];
686             } else {
687                 f.targets = parseRouting(sb.toString());
688             }
689         }
690     }
691
692     /**
693      * Parse a target string into an array of targets
694      *
695      * @param routing Target string
696      * @return Array of targets.
697      */
698     public Target[] parseRouting(String routing) {
699         routing = routing.trim();
700         if ("".equals(routing)) {
701             return (new Target[0]);
702         }
703         String[] xx = routing.split("\\s+");
704         Hashtable<String, Target> tmap = new Hashtable<String, Target>();
705         HashSet<String> subset = new HashSet<String>();
706         Vector<Target> tv = new Vector<Target>();
707         Target[] ret = new Target[xx.length];
708         for (int i = 0; i < xx.length; i++) {
709             String t = xx[i];
710             int j = t.indexOf('/');
711             if (j == -1) {
712                 DestInfo di = subinfo.get(t);
713                 if (di == null) {
714                     tv.add(new Target(null, t));
715                 } else {
716                     if (!subset.contains(t)) {
717                         subset.add(t);
718                         tv.add(new Target(di, null));
719                     }
720                 }
721             } else {
722                 String node = t.substring(0, j);
723                 String rtg = t.substring(j + 1);
724                 DestInfo di = nodeinfo.get(node);
725                 if (di == null) {
726                     tv.add(new Target(null, t));
727                 } else {
728                     Target tt = tmap.get(node);
729                     if (tt == null) {
730                         tt = new Target(di, rtg);
731                         tmap.put(node, tt);
732                         tv.add(tt);
733                     } else {
734                         tt.addRouting(rtg);
735                     }
736                 }
737             }
738         }
739         return (tv.toArray(new Target[0]));
740     }
741
742     /**
743      * Check whether this is a valid node-to-node transfer
744      *
745      * @param credentials Credentials offered by the supposed node
746      * @param ip IP address the request came from
747      */
748     public boolean isAnotherNode(String credentials, String ip) {
749         IsFrom n = nodes.get(credentials);
750         return (n != null && n.isFrom(ip));
751     }
752
753     /**
754      * Check whether publication is allowed.
755      *
756      * @param feedid The ID of the feed being requested.
757      * @param credentials The offered credentials
758      * @param ip The requesting IP address
759      */
760     public String isPublishPermitted(String feedid, String credentials, String ip) {
761         Feed f = feeds.get(feedid);
762         String nf = "Feed does not exist";
763         if (f != null) {
764             nf = f.status;
765         }
766         if (nf != null) {
767             return (nf);
768         }
769         String user = f.authusers.get(credentials);
770         if (user == null) {
771             return ("Publisher not permitted for this feed");
772         }
773         if (f.subnets.length == 0) {
774             return (null);
775         }
776         byte[] addr = NodeUtils.getInetAddress(ip);
777         for (SubnetMatcher snm : f.subnets) {
778             if (snm.matches(addr)) {
779                 return (null);
780             }
781         }
782         return ("Publisher not permitted for this feed");
783     }
784
785     /**
786      * Check whether delete file is allowed.
787      *
788      * @param subId The ID of the subscription being requested.
789      */
790     public boolean isDeletePermitted(String subId) {
791         ProvSubscription provSubscription = provSubscriptions.get(subId);
792         return provSubscription.isPrivilegedSubscriber();
793     }
794
795     /**
796      * Check whether publication is allowed for AAF Feed.
797      * @param feedid The ID of the feed being requested.
798      * @param ip The requesting IP address
799      */
800     public String isPublishPermitted(String feedid, String ip) {
801         Feed f = feeds.get(feedid);
802         String nf = "Feed does not exist";
803         if (f != null) {
804             nf = f.status;
805         }
806         if (nf != null) {
807             return(nf);
808         }
809         if (f.subnets.length == 0) {
810             return(null);
811         }
812         byte[] addr = NodeUtils.getInetAddress(ip);
813         for (SubnetMatcher snm: f.subnets) {
814             if (snm.matches(addr)) {
815                 return(null);
816             }
817         }
818         return("Publisher not permitted for this feed");
819     }
820
821     /**
822      * Get authenticated user
823      */
824     public String getAuthUser(String feedid, String credentials) {
825         return (feeds.get(feedid).authusers.get(credentials));
826     }
827
828     /**
829      * AAF changes: TDP EPIC US# 307413
830      * Check AAF_instance for feed ID
831      * @param feedid    The ID of the feed specified
832      */
833     public String getAafInstance(String feedid) {
834         Feed f = feeds.get(feedid);
835         return f.aafInstance;
836     }
837
838     /**
839      * Check if the request should be redirected to a different ingress node
840      */
841     public String getIngressNode(String feedid, String user, String ip) {
842         Feed f = feeds.get(feedid);
843         if (f.redirections.length == 0) {
844             return (null);
845         }
846         byte[] addr = NodeUtils.getInetAddress(ip);
847         for (Redirection r : f.redirections) {
848             if (r.user != null && !user.equals(r.user)) {
849                 continue;
850             }
851             if (r.snm != null && !r.snm.matches(addr)) {
852                 continue;
853             }
854             for (String n : r.nodes) {
855                 if (myname.equals(n)) {
856                     return (null);
857                 }
858             }
859             if (r.nodes.length == 0) {
860                 return (null);
861             }
862             return (r.nodes[rrcntr++ % r.nodes.length]);
863         }
864         return (null);
865     }
866
867     /**
868      * Get a provisioned configuration parameter
869      */
870     public String getProvParam(String name) {
871         return (params.get(name));
872     }
873
874     /**
875      * Get all the DestInfos
876      */
877     public DestInfo[] getAllDests() {
878         return (alldests);
879     }
880
881     /**
882      * Get the targets for a feed
883      *
884      * @param feedid The feed ID
885      * @return The targets this feed should be delivered to
886      */
887     public Target[] getTargets(String feedid) {
888         if (feedid == null) {
889             return (new Target[0]);
890         }
891         Feed f = feeds.get(feedid);
892         if (f == null) {
893             return (new Target[0]);
894         }
895         return (f.targets);
896     }
897
898     /**
899      * Get the creation date for a feed
900      * @param feedid The feed ID
901      * @return the timestamp of creation date of feed id passed
902      */
903     public String getCreatedDate(String feedid) {
904         Feed f = feeds.get(feedid);
905         return(f.createdDate);
906     }
907
908     /**
909      * Get the feed ID for a subscription
910      *
911      * @param subid The subscription ID
912      * @return The feed ID
913      */
914     public String getFeedId(String subid) {
915         DestInfo di = subinfo.get(subid);
916         if (di == null) {
917             return (null);
918         }
919         return (di.getLogData());
920     }
921
922     /**
923      * Get the spool directory for a subscription
924      *
925      * @param subid The subscription ID
926      * @return The spool directory
927      */
928     public String getSpoolDir(String subid) {
929         DestInfo di = subinfo.get(subid);
930         if (di == null) {
931             return (null);
932         }
933         return (di.getSpool());
934     }
935
936     /**
937      * Get the Authorization value this node uses
938      *
939      * @return The Authorization header value for this node
940      */
941     public String getMyAuth() {
942         return (myauth);
943     }
944
945 }