Fix community table sweep / removal. Trust router now stable.
[trust_router.git] / common / tr_comm.c
1 /*
2  * Copyright (c) 2012, JANET(UK)
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  *
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * 3. Neither the name of JANET(UK) nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24  * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
25  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
26  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
29  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
31  * OF THE POSSIBILITY OF SUCH DAMAGE.
32  *
33  */
34
35 #include <stdio.h>
36 #include <jansson.h>
37 #include <talloc.h>
38 #include <sys/time.h>
39
40 #include <tr_rp.h>
41 #include <tr_idp.h>
42 #include <trust_router/tr_name.h>
43 #include <tr_comm.h>
44 #include <tr_debug.h>
45
46
47 static int tr_comm_destructor(void *obj)
48 {
49   TR_COMM *comm=talloc_get_type_abort(obj, TR_COMM);
50   if (comm->id!=NULL)
51     tr_free_name(comm->id);
52   if (comm->owner_realm!=NULL)
53     tr_free_name(comm->owner_realm);
54   if (comm->owner_contact!=NULL)
55     tr_free_name(comm->owner_contact);
56   return 0;
57 }
58
59 TR_COMM *tr_comm_new(TALLOC_CTX *mem_ctx)
60 {
61   TR_COMM *comm=talloc(mem_ctx, TR_COMM);
62   if (comm!=NULL) {
63     comm->next=NULL;
64     comm->id=NULL;
65     comm->type=TR_COMM_UNKNOWN;
66     comm->apcs=NULL;
67     comm->owner_realm=NULL;
68     comm->owner_contact=NULL;
69     comm->expiration_interval=0;
70     comm->refcount=0;
71     talloc_set_destructor((void *)comm, tr_comm_destructor);
72   }
73   return comm;
74 }
75
76 void tr_comm_free(TR_COMM *comm)
77 {
78   talloc_free(comm);
79 }
80
81 void tr_comm_set_id(TR_COMM *comm, TR_NAME *id)
82 {
83   if (comm->id != NULL)
84     tr_free_name(comm->id);
85   comm->id=id;
86 }
87
88 void tr_comm_incref(TR_COMM *comm)
89 {
90   comm->refcount++;
91 }
92
93 void tr_comm_decref(TR_COMM *comm)
94 {
95   if (comm->refcount>0)
96     comm->refcount--;
97 }
98
99 void tr_comm_set_apcs(TR_COMM *comm, TR_APC *apc)
100 {
101   if (comm->apcs!=NULL)
102     tr_apc_free(comm->apcs);
103   comm->apcs=apc;
104   talloc_steal(comm, apc);
105 }
106
107 TR_APC *tr_comm_get_apcs(TR_COMM *comm)
108 {
109   return comm->apcs;
110 }
111
112 TR_NAME *tr_comm_get_id(TR_COMM *comm)
113 {
114   return comm->id;
115 }
116
117 TR_NAME *tr_comm_dup_id(TR_COMM *comm)
118 {
119   return tr_dup_name(comm->id);
120 }
121
122 void tr_comm_set_type(TR_COMM *comm, TR_COMM_TYPE type)
123 {
124   comm->type=type;
125 }
126
127 TR_COMM_TYPE tr_comm_get_type(TR_COMM *comm)
128 {
129   return comm->type;
130 }
131
132 void tr_comm_set_owner_realm(TR_COMM *comm, TR_NAME *realm)
133 {
134   if (comm->owner_realm!=NULL)
135     tr_free_name(comm->owner_realm);
136   comm->owner_realm=realm;
137 }
138
139 TR_NAME *tr_comm_get_owner_realm(TR_COMM *comm)
140 {
141   return comm->owner_realm;
142 }
143
144 TR_NAME *tr_comm_dup_owner_realm(TR_COMM *comm)
145 {
146   return tr_dup_name(comm->owner_realm);
147 }
148
149 void tr_comm_set_owner_contact(TR_COMM *comm, TR_NAME *contact)
150 {
151   if (comm->owner_contact != NULL)
152     tr_free_name(comm->owner_contact);
153   comm->owner_contact=contact;
154 }
155
156 TR_NAME *tr_comm_get_owner_contact(TR_COMM *comm)
157 {
158   return comm->owner_contact;
159 }
160
161 TR_NAME *tr_comm_dup_owner_contact(TR_COMM *comm)
162 {
163   return tr_dup_name(comm->owner_contact);
164 }
165
166 unsigned int tr_comm_get_refcount(TR_COMM *comm)
167 {
168   return comm->refcount;
169 }
170
171 /* 0 if equivalent, nonzero if different, only considers
172  * nhops last hops (nhops==0 means consider all, nhops==1
173  * only considers last hop) */
174 static int tr_comm_memb_provenance_cmp(TR_COMM_MEMB *m1, TR_COMM_MEMB *m2, int nhops)
175 {
176   size_t ii;
177
178   if ((m1->provenance==NULL) || (m2->provenance==NULL))
179     return m1->provenance!=m2->provenance; /* return 0 if both null, 1 if only one null */
180
181   if (json_array_size(m1->provenance)!=json_array_size(m2->provenance))
182     return 1;
183
184   if (nhops==0)
185     nhops=json_array_size(m1->provenance); /* same as size(m2->provenance) */
186
187   for (ii=0; ii<json_array_size(m1->provenance); ii++) {
188     if (0==strcmp(json_string_value(json_array_get(m1->provenance, ii)),
189                   json_string_value(json_array_get(m2->provenance, ii)))) {
190       return 0;
191     }
192   }
193   return 1;
194 }
195
196 /* Accepts an update that either came from the same peer as the previous
197  * origin, has a shorter provenance list, or can replace an expired
198  * membership. Otherwise keeps the existing one.
199  * On replacement, frees the old member and moves new member to ctab's
200  * context. Caller should not free newmemb except by freeing its original
201  * context. */
202 static void tr_comm_add_if_shorter(TR_COMM_TABLE *ctab, TR_COMM_MEMB *existing, TR_COMM_MEMB *newmemb)
203 {
204   int accept=0;
205
206   if (existing==NULL) {
207     /* not in the table */
208     tr_comm_table_add_memb(ctab, newmemb);
209   } else {
210     if (0==tr_comm_memb_provenance_cmp(existing, newmemb, 1))
211       accept=1; /* always accept a replacement from the same peer */
212     else if (tr_comm_memb_provenance_len(newmemb) < tr_comm_memb_provenance_len(existing))
213       accept=1; /* accept a shorter provenance */
214     else if (existing->times_expired>0)
215       accept=1;
216     else
217       accept=0;
218
219     if (accept) {
220       tr_comm_table_remove_memb(ctab, existing);
221       tr_comm_memb_free(existing);
222       tr_comm_table_add_memb(ctab, newmemb);
223     }
224   }
225 }
226
227 /* does not take responsibility for freeing IDP realm */
228 void tr_comm_add_idp_realm(TR_COMM_TABLE *ctab,
229                            TR_COMM *comm,
230                            TR_IDP_REALM *realm,
231                            unsigned int interval,
232                            json_t *provenance,
233                            struct timespec *expiry)
234 {
235   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
236   TR_COMM_MEMB *newmemb=tr_comm_memb_new(tmp_ctx);
237   TR_COMM_MEMB *existing=NULL;
238
239   if (newmemb==NULL) {
240     tr_err("tr_comm_add_idp_realm: unable to allocate new membership record.");
241     talloc_free(tmp_ctx);
242     return;
243   }
244
245   tr_comm_memb_set_idp_realm(newmemb, realm);
246   tr_comm_memb_set_comm(newmemb, comm);
247   tr_comm_memb_set_interval(newmemb, interval);
248   tr_comm_memb_set_provenance(newmemb, provenance);
249
250   existing=tr_comm_table_find_idp_memb_origin(ctab,
251                                               tr_idp_realm_get_id(realm),
252                                               tr_comm_get_id(comm),
253                                               tr_comm_memb_get_origin(newmemb));
254   tr_comm_add_if_shorter(ctab, existing, newmemb); /* takes newmemb out of tmp_ctx if needed */
255
256   talloc_free(tmp_ctx);
257 }
258
259 /* does not take responsibility for freeing RP realm */
260 void tr_comm_add_rp_realm(TR_COMM_TABLE *ctab,
261                           TR_COMM *comm,
262                           TR_RP_REALM *realm,
263                           unsigned int interval,
264                           json_t *provenance,
265                           struct timespec *expiry)
266 {
267   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
268   TR_COMM_MEMB *newmemb=tr_comm_memb_new(tmp_ctx);
269   TR_COMM_MEMB *existing=NULL;
270
271   if (newmemb==NULL) {
272     tr_err("tr_comm_add_idp_realm: unable to allocate new membership record.");
273     talloc_free(tmp_ctx);
274     return;
275   }
276
277   tr_comm_memb_set_rp_realm(newmemb, realm);
278   tr_comm_memb_set_comm(newmemb, comm);
279   tr_comm_memb_set_interval(newmemb, interval);
280   tr_comm_memb_set_provenance(newmemb, provenance);
281
282   existing=tr_comm_table_find_rp_memb_origin(ctab,
283                                              tr_rp_realm_get_id(realm),
284                                              tr_comm_get_id(comm),
285                                              tr_comm_memb_get_origin(newmemb));
286   tr_comm_add_if_shorter(ctab, existing, newmemb); /* takes newmemb out of tmp_ctx if needed */
287   talloc_free(tmp_ctx);
288 }
289
290 static TR_COMM *tr_comm_tail(TR_COMM *comm)
291 {
292   if (comm==NULL)
293     return comm;
294
295   while (comm->next!=NULL)
296     comm=comm->next;
297   return comm;
298 }
299
300 /* All list members are in the talloc context of the head.
301  * This will require careful thought if entries are ever removed
302  * Call like comms=tr_comm_add_func(comms, new_comm);
303  * or just use the tr_comm_add(comms, new) macro. */
304 #define tr_comm_add(comms, new) ((comms)=tr_comm_add_func((comms), (new)))
305 static TR_COMM *tr_comm_add_func(TR_COMM *comms, TR_COMM *new)
306 {
307   if (comms==NULL)
308     comms=new;
309   else {
310     tr_comm_tail(comms)->next=new;
311     while(new!=NULL) {
312       talloc_steal(comms, new);
313       new=new->next;
314     }
315   }
316   return comms;
317 }
318
319 /* Guarantees comm is not in the list, not an error if it was't there.
320  * Does not free the removed element, nor change its talloc context. */
321 #define tr_comm_remove(comms, c) ((comms)=tr_comm_remove_func((comms), (c)))
322 static TR_COMM *tr_comm_remove_func(TR_COMM *comms, TR_COMM *remove)
323 {
324   TALLOC_CTX *list_ctx=talloc_parent(comms); /* in case we need to remove the head */
325   TR_COMM *this=NULL;
326
327   if (comms==NULL)
328     return NULL;
329
330   if (comms==remove) {
331     /* if we're removing the head, put the next element (if present) into the context
332      * the list head was in. */
333     comms=comms->next;
334     if (comms!=NULL) {
335       talloc_steal(list_ctx, comms);
336       /* now put all the other elements in the context of the list head */
337       for (this=comms->next; this!=NULL; this=this->next)
338         talloc_steal(comms, this);
339     }
340   } else {
341     /* not removing the head; no need to play with contexts */
342     for (this=comms; this->next!=NULL; this=this->next) {
343       if (this->next==remove) {
344         this->next=remove->next;
345         break;
346       }
347     }
348   }
349   return comms;
350 }
351
352 /* remove any with zero refcount 
353  * Call via macro. */
354 #define tr_comm_sweep(head) ((head)=tr_comm_sweep_func((head)))
355 static TR_COMM *tr_comm_sweep_func(TR_COMM *head)
356 {
357   TR_COMM *comm=NULL;
358   TR_COMM *old_next=NULL;
359
360   if (head==NULL)
361     return NULL;
362
363   while ((head!=NULL) && (head->refcount==0)) {
364     comm=head; /* keep a pointer so we can remove it */
365     tr_comm_remove(head, comm); /* use this to get talloc contexts right */
366     tr_comm_free(comm);
367   }
368
369   if (head==NULL)
370     return NULL;
371
372   /* will not remove the head here, that has already been done */
373   for (comm=head; comm->next!=NULL; comm=comm->next) {
374     if (comm->next->refcount==0) {
375       old_next=comm->next;
376       tr_comm_remove(head, comm->next); /* changes comm->next */
377       tr_comm_free(old_next);
378     }
379   }
380
381   return head;
382 }
383
384 TR_IDP_REALM *tr_comm_find_idp(TR_COMM_TABLE *ctab, TR_COMM *comm, TR_NAME *idp_realm)
385 {
386   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
387   TR_COMM_ITER *iter=NULL;
388   TR_IDP_REALM *this_idp=NULL;
389
390   if ((NULL==ctab) || (NULL==comm) || (NULL==idp_realm)) {
391     talloc_free(tmp_ctx);
392     return NULL;
393   }
394
395   iter=tr_comm_iter_new(tmp_ctx);
396   for (this_idp=tr_idp_realm_iter_first(iter, ctab, tr_comm_get_id(comm));
397        this_idp!=NULL;
398        this_idp=tr_idp_realm_iter_next(iter)) {
399     if (0==tr_name_cmp(idp_realm, tr_idp_realm_get_id(this_idp))) {
400       tr_debug("tr_comm_find_idp: Found IdP %s in community %s.", idp_realm->buf, tr_comm_get_id(comm)->buf);
401       talloc_free(tmp_ctx);
402       return this_idp;
403     }
404   }
405   tr_debug("tr_comm_find_idp: Unable to find IdP %s in community %s.", idp_realm->buf, tr_comm_get_id(comm)->buf);
406   talloc_free(tmp_ctx);
407   return NULL;
408 }
409
410 TR_RP_REALM *tr_comm_find_rp (TR_COMM_TABLE *ctab, TR_COMM *comm, TR_NAME *rp_realm)
411 {
412   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
413   TR_COMM_ITER *iter=NULL;
414   TR_RP_REALM *this_rp=NULL;
415
416   if ((NULL==ctab) || (NULL==comm) || (NULL==rp_realm)) {
417     talloc_free(tmp_ctx);
418     return NULL;
419   }
420
421   iter=tr_comm_iter_new(tmp_ctx);
422   for (this_rp=tr_rp_realm_iter_first(iter, ctab, tr_comm_get_id(comm));
423        this_rp!=NULL;
424        this_rp=tr_rp_realm_iter_next(iter)) {
425     if (0==tr_name_cmp(rp_realm, tr_rp_realm_get_id(this_rp))) {
426       tr_debug("tr_comm_find_rp: Found RP %s in community %s.", rp_realm->buf, tr_comm_get_id(comm)->buf);
427       talloc_free(tmp_ctx);
428       return this_rp;
429     }
430   }
431   tr_debug("tr_comm_find_rp: Unable to find RP %s in community %s.", rp_realm->buf, tr_comm_get_id(comm)->buf);
432   talloc_free(tmp_ctx);
433   return NULL;
434 }
435
436 static TR_COMM *tr_comm_lookup(TR_COMM *comms, TR_NAME *comm_name) 
437 {
438   TR_COMM *cfg_comm = NULL;
439
440   for (cfg_comm = comms; NULL != cfg_comm; cfg_comm = cfg_comm->next) {
441     if (0==tr_name_cmp(cfg_comm->id, comm_name))
442       return cfg_comm;
443   }
444   return NULL;
445 }
446
447 TR_COMM_ITER *tr_comm_iter_new(TALLOC_CTX *mem_ctx)
448 {
449   TR_COMM_ITER *iter=talloc(mem_ctx, TR_COMM_ITER);
450   if (iter!=NULL) {
451     iter->cur_comm=NULL;
452     iter->cur_memb=NULL;
453     iter->cur_orig_head=NULL;
454     iter->match=NULL;
455     iter->realm=NULL;
456   }
457   return iter;
458 }
459
460 void tr_comm_iter_free(TR_COMM_ITER *iter)
461 {
462   talloc_free(iter);
463 }
464
465
466 TR_COMM *tr_comm_iter_first(TR_COMM_ITER *iter, TR_COMM_TABLE *ctab, TR_NAME *realm)
467 {
468   iter->match=realm;
469
470   /* find memberships for this realm */
471   for (iter->cur_memb=ctab->memberships;
472        iter->cur_memb!=NULL;
473        iter->cur_memb=iter->cur_memb->next) {
474     if (0==tr_name_cmp(iter->match, tr_comm_memb_get_realm_id(iter->cur_memb)))
475       return tr_comm_memb_get_comm(iter->cur_memb);
476   }
477   return NULL;
478 }
479
480 TR_COMM *tr_comm_iter_next(TR_COMM_ITER *iter)
481 {
482   for (iter->cur_memb=iter->cur_memb->next;
483        iter->cur_memb!=NULL;
484        iter->cur_memb=iter->cur_memb->next) {
485     if (0==tr_name_cmp(iter->match, tr_comm_memb_get_realm_id(iter->cur_memb)))
486       return tr_comm_memb_get_comm(iter->cur_memb);
487   }
488   return NULL;
489 }
490
491 /* iterate only over RPs */
492 TR_COMM *tr_comm_iter_first_rp(TR_COMM_ITER *iter, TR_COMM_TABLE *ctab, TR_NAME *realm)
493 {
494   iter->match=realm;
495
496   /* find memberships for this realm */
497   for (iter->cur_memb=ctab->memberships;
498        iter->cur_memb!=NULL;
499        iter->cur_memb=iter->cur_memb->next) {
500     if ((tr_comm_memb_get_rp_realm(iter->cur_memb)!=NULL) &&
501         (0==tr_name_cmp(iter->match, tr_comm_memb_get_realm_id(iter->cur_memb))))
502       return tr_comm_memb_get_comm(iter->cur_memb);
503   }
504   return NULL;
505 }
506
507 TR_COMM *tr_comm_iter_next_rp(TR_COMM_ITER *iter)
508 {
509   for (iter->cur_memb=iter->cur_memb->next;
510        iter->cur_memb!=NULL;
511        iter->cur_memb=iter->cur_memb->next) {
512     if ((tr_comm_memb_get_rp_realm(iter->cur_memb)!=NULL) &&
513         (0==tr_name_cmp(iter->match, tr_comm_memb_get_realm_id(iter->cur_memb))))
514       return tr_comm_memb_get_comm(iter->cur_memb);
515   }
516   return NULL;
517 }
518
519 /* iterate only over IDPs */
520 TR_COMM *tr_comm_iter_first_idp(TR_COMM_ITER *iter, TR_COMM_TABLE *ctab, TR_NAME *realm)
521 {
522   iter->match=realm;
523
524   /* find memberships for this realm */
525   for (iter->cur_memb=ctab->memberships;
526        iter->cur_memb!=NULL;
527        iter->cur_memb=iter->cur_memb->next) {
528     if ((tr_comm_memb_get_idp_realm(iter->cur_memb)!=NULL) &&
529         (0==tr_name_cmp(iter->match, tr_comm_memb_get_realm_id(iter->cur_memb))))
530       return tr_comm_memb_get_comm(iter->cur_memb);
531   }
532   return NULL;
533 }
534
535 TR_COMM *tr_comm_iter_next_idp(TR_COMM_ITER *iter)
536 {
537   for (iter->cur_memb=iter->cur_memb->next;
538        iter->cur_memb!=NULL;
539        iter->cur_memb=iter->cur_memb->next) {
540     if ((tr_comm_memb_get_idp_realm(iter->cur_memb)!=NULL) &&
541         (0==tr_name_cmp(iter->match, tr_comm_memb_get_realm_id(iter->cur_memb))))
542       return tr_comm_memb_get_comm(iter->cur_memb);
543   }
544   return NULL;
545 }
546
547 static TR_REALM *tr_realm_new(TALLOC_CTX *mem_ctx)
548 {
549   TR_REALM *realm=talloc(mem_ctx, TR_REALM);
550   if (realm!=NULL) {
551     realm->role=TR_ROLE_UNKNOWN;
552     realm->rp=NULL;
553     realm->idp=NULL;
554   }
555   return realm;
556 }
557
558 static void tr_realm_free(TR_REALM *realm)
559 {
560   talloc_free(realm);
561 }
562
563 static void tr_realm_set_rp(TR_REALM *realm, TR_RP_REALM *rp)
564 {
565   if (realm->idp!=NULL)
566     realm->idp=NULL;
567   realm->role=TR_ROLE_RP;
568   realm->rp=rp;
569 }
570
571 static void tr_realm_set_idp(TR_REALM *realm, TR_IDP_REALM *idp)
572 {
573   if (realm->rp!=NULL)
574     realm->rp=NULL;
575   realm->role=TR_ROLE_IDP;
576   realm->idp=idp;
577 }
578
579 TR_NAME *tr_realm_get_id(TR_REALM *realm)
580 {
581   switch (realm->role) {
582   case TR_ROLE_RP:
583     return tr_rp_realm_get_id(realm->rp);
584   case TR_ROLE_IDP:
585     return tr_idp_realm_get_id(realm->idp);
586   default:
587     break;
588   }
589   return NULL;
590 }
591
592 TR_NAME *tr_realm_dup_id(TR_REALM *realm)
593 {
594   return tr_dup_name(tr_realm_get_id(realm));
595 }
596
597 /* Iterate over either sort of realm. Do not free the TR_REALM returned. It becomes
598  * undefined/invalid after the next operation affecting the iterator. */
599 TR_REALM *tr_realm_iter_first(TR_COMM_ITER *iter, TR_COMM_TABLE *ctab, TR_NAME *comm)
600 {
601   iter->match=comm;
602   if (iter->realm==NULL)
603     iter->realm=tr_realm_new(iter);
604   if (iter->realm==NULL)
605     return NULL;
606
607   /* find memberships for this comm */
608   for (iter->cur_memb=ctab->memberships;
609        iter->cur_memb!=NULL;
610        iter->cur_memb=iter->cur_memb->next) {
611     if (0==tr_name_cmp(iter->match,
612                        tr_comm_get_id(tr_comm_memb_get_comm(iter->cur_memb)))) {
613       /* found a match, determine whether it's an rp realm or an idp realm */
614       if (tr_comm_memb_get_rp_realm(iter->cur_memb)!=NULL)
615         tr_realm_set_rp(iter->realm, tr_comm_memb_get_rp_realm(iter->cur_memb));
616       else if (tr_comm_memb_get_idp_realm(iter->cur_memb)!=NULL)
617         tr_realm_set_idp(iter->realm, tr_comm_memb_get_idp_realm(iter->cur_memb));
618       else {
619         if (iter->realm!=NULL)
620           tr_realm_free(iter->realm);
621         iter->realm=NULL;
622       }
623       return iter->realm;
624     }
625   }
626   if (iter->realm!=NULL)
627     tr_realm_free(iter->realm);
628   iter->realm=NULL;
629   return NULL;
630 }
631
632 TR_REALM *tr_realm_iter_next(TR_COMM_ITER *iter)
633 {
634   if (iter->realm==NULL)
635     return NULL;
636
637   /* find memberships for this comm */
638   for (iter->cur_memb=iter->cur_memb->next;
639        iter->cur_memb!=NULL;
640        iter->cur_memb=iter->cur_memb->next) {
641     if (0==tr_name_cmp(iter->match,
642                        tr_comm_get_id(tr_comm_memb_get_comm(iter->cur_memb)))) {
643       /* found a match, determine whether it's an rp realm or an idp realm */
644       if (tr_comm_memb_get_rp_realm(iter->cur_memb)!=NULL)
645         tr_realm_set_rp(iter->realm, tr_comm_memb_get_rp_realm(iter->cur_memb));
646       else if (tr_comm_memb_get_idp_realm(iter->cur_memb)!=NULL)
647         tr_realm_set_idp(iter->realm, tr_comm_memb_get_idp_realm(iter->cur_memb));
648       else {
649         if (iter->realm!=NULL)
650           tr_realm_free(iter->realm);
651         iter->realm=NULL;
652       }
653       return iter->realm;
654     }
655   }
656   if (iter->realm!=NULL)
657     tr_realm_free(iter->realm);
658   iter->realm=NULL;
659   return NULL;
660 }
661
662 TR_RP_REALM *tr_rp_realm_iter_first(TR_COMM_ITER *iter, TR_COMM_TABLE *ctab, TR_NAME *comm)
663 {
664   iter->match=comm;
665
666   /* find memberships for this comm */
667   for (iter->cur_memb=ctab->memberships;
668        iter->cur_memb!=NULL;
669        iter->cur_memb=iter->cur_memb->next) {
670     if ((tr_comm_memb_get_rp_realm(iter->cur_memb)!=NULL) &&
671         (0==tr_name_cmp(iter->match, tr_comm_get_id(tr_comm_memb_get_comm(iter->cur_memb)))))
672       return tr_comm_memb_get_rp_realm(iter->cur_memb);
673   }
674   return NULL;
675 }
676
677 TR_RP_REALM *tr_rp_realm_iter_next(TR_COMM_ITER *iter)
678 {
679   for (iter->cur_memb=iter->cur_memb->next;
680        iter->cur_memb!=NULL;
681        iter->cur_memb=iter->cur_memb->next) {
682     if ((tr_comm_memb_get_rp_realm(iter->cur_memb)!=NULL) &&
683         (0==tr_name_cmp(iter->match, tr_comm_get_id(tr_comm_memb_get_comm(iter->cur_memb)))))
684       return tr_comm_memb_get_rp_realm(iter->cur_memb);
685   }
686   return NULL;
687 }
688
689 TR_IDP_REALM *tr_idp_realm_iter_first(TR_COMM_ITER *iter, TR_COMM_TABLE *ctab, TR_NAME *comm)
690 {
691   iter->match=comm;
692
693   /* find memberships for this comm */
694   for (iter->cur_memb=ctab->memberships;
695        iter->cur_memb!=NULL;
696        iter->cur_memb=iter->cur_memb->next) {
697     if ((tr_comm_memb_get_idp_realm(iter->cur_memb)!=NULL) &&
698         (0==tr_name_cmp(iter->match, tr_comm_get_id(tr_comm_memb_get_comm(iter->cur_memb)))))
699       return tr_comm_memb_get_idp_realm(iter->cur_memb);
700   }
701   return NULL;
702 }
703
704 TR_IDP_REALM *tr_idp_realm_iter_next(TR_COMM_ITER *iter)
705 {
706   for (iter->cur_memb=iter->cur_memb->next;
707        iter->cur_memb!=NULL;
708        iter->cur_memb=iter->cur_memb->next) {
709     if ((tr_comm_memb_get_idp_realm(iter->cur_memb)!=NULL) &&
710         (0==tr_name_cmp(iter->match, tr_comm_get_id(tr_comm_memb_get_comm(iter->cur_memb)))))
711       return tr_comm_memb_get_idp_realm(iter->cur_memb);
712   }
713   return NULL;
714 }
715
716 /* iterators for all communities in a table */
717 TR_COMM *tr_comm_table_iter_first(TR_COMM_ITER *iter, TR_COMM_TABLE *ctab)
718 {
719   iter->cur_comm=ctab->comms;
720   return iter->cur_comm;
721 }
722
723 TR_COMM *tr_comm_table_iter_next(TR_COMM_ITER *iter)
724 {
725   return iter->cur_comm=iter->cur_comm->next;
726 }
727
728 const char *tr_comm_type_to_str(TR_COMM_TYPE type)
729 {
730   const char *s=NULL;
731   switch(type) {
732   case TR_COMM_UNKNOWN:
733     s="unknown";
734     break;
735   case TR_COMM_APC:
736     s="apc";
737     break;
738   case TR_COMM_COI:
739     s="coi";
740     break;
741   default:
742     s="invalid";
743   }
744   return s;
745 }
746
747 /* iterate along the origin list for this member */
748 TR_COMM_MEMB *tr_comm_memb_iter_first(TR_COMM_ITER *iter, TR_COMM_MEMB *memb)
749 {
750   iter->cur_memb=memb;
751   return iter->cur_memb;
752 }
753
754 TR_COMM_MEMB *tr_comm_memb_iter_next(TR_COMM_ITER *iter)
755 {
756   if (iter->cur_memb!=NULL)
757     iter->cur_memb=iter->cur_memb->origin_next;
758   return iter->cur_memb;
759 }
760
761
762 /* iterate over all memberships in the table */
763 TR_COMM_MEMB *tr_comm_memb_iter_all_first(TR_COMM_ITER *iter, TR_COMM_TABLE *ctab)
764 {
765   iter->cur_memb=ctab->memberships;
766   iter->cur_orig_head=ctab->memberships;
767   return iter->cur_memb;
768 }
769
770 TR_COMM_MEMB *tr_comm_memb_iter_all_next(TR_COMM_ITER *iter)
771 {
772   if (iter->cur_memb->next==NULL) {
773     if (iter->cur_orig_head->next==NULL) {
774       /* we're done */
775       return NULL;
776     } else {
777       iter->cur_memb=iter->cur_orig_head->next;
778       iter->cur_orig_head=iter->cur_orig_head->next;
779     }
780   } else {
781     iter->cur_memb=iter->cur_memb->origin_next;
782   }
783   return iter->cur_memb;
784 }
785
786
787 TR_COMM_TYPE tr_comm_type_from_str(const char *s)
788 {
789   if (strcmp(s, "apc")==0)
790     return TR_COMM_APC;
791   if (strcmp(s,"coi")==0)
792     return TR_COMM_COI;
793   return TR_COMM_UNKNOWN;
794 }
795
796
797 static int tr_comm_memb_destructor(void *obj)
798 {
799   TR_COMM_MEMB *memb=talloc_get_type_abort(obj, TR_COMM_MEMB);
800   if (memb->origin!=NULL)
801     tr_free_name(memb->origin);
802
803   if (memb->rp!=NULL)
804     tr_rp_realm_decref(memb->rp);
805   if (memb->idp!=NULL)
806     tr_idp_realm_decref(memb->idp);
807   if (memb->comm!=NULL)
808     tr_comm_decref(memb->comm);
809   if (memb->provenance!=NULL)
810     json_decref(memb->provenance);
811   return 0;
812 }
813
814 TR_COMM_MEMB *tr_comm_memb_new(TALLOC_CTX *mem_ctx)
815 {
816   TR_COMM_MEMB *memb=talloc(mem_ctx, TR_COMM_MEMB);
817   if (memb!=NULL) {
818     memb->next=NULL;
819     memb->origin_next=NULL;
820     memb->idp=NULL;
821     memb->rp=NULL;
822     memb->comm=NULL;
823     memb->origin=NULL;
824     memb->provenance=NULL;
825     memb->interval=0;
826     memb->triggered=0;
827     memb->times_expired=0;
828     memb->expiry=talloc(memb, struct timespec);
829     if (memb->expiry==NULL) {
830       talloc_free(memb);
831       return NULL;
832     }
833     *(memb->expiry)=(struct timespec){0,0};
834     talloc_set_destructor(memb, tr_comm_memb_destructor);
835   }
836   return memb;
837 }
838
839 void tr_comm_memb_free(TR_COMM_MEMB *memb)
840 {
841   talloc_free(memb);
842 }
843
844 /* Returns 0 if they are the same, nonzero if they differ.
845  * Ignores expiry, triggered, and times_expired, next pointers */
846 int tr_comm_memb_cmp(TR_COMM_MEMB *m1, TR_COMM_MEMB *m2)
847 {
848   if ((m1->idp==m2->idp) &&
849       (m1->rp==m2->rp) &&
850       (m1->comm==m2->comm) &&
851       (tr_comm_memb_provenance_cmp(m1, m2, 0)==0) &&
852       (m1->interval==m2->interval))
853     return 0;
854   return 1;
855 }
856
857 TR_REALM_ROLE tr_comm_memb_get_role(TR_COMM_MEMB *memb)
858 {
859   if (memb->rp!=NULL)
860     return TR_ROLE_RP;
861   if (memb->idp!=NULL)
862     return TR_ROLE_IDP;
863   return TR_ROLE_UNKNOWN;
864 }
865
866 void tr_comm_memb_set_rp_realm(TR_COMM_MEMB *memb, TR_RP_REALM *realm)
867 {
868   if (memb->idp!=NULL) {
869     tr_idp_realm_decref(memb->idp);
870     memb->idp=NULL;
871   }
872   if (memb->rp!=NULL)
873     tr_rp_realm_decref(memb->rp);
874
875
876   memb->rp=realm;
877   tr_rp_realm_incref(realm);
878 }
879
880 TR_RP_REALM *tr_comm_memb_get_rp_realm(TR_COMM_MEMB *memb)
881 {
882   return memb->rp;
883 }
884
885 void tr_comm_memb_set_idp_realm(TR_COMM_MEMB *memb, TR_IDP_REALM *realm)
886 {
887   if (memb->rp!=NULL) {
888     tr_rp_realm_decref(memb->rp);
889     memb->rp=NULL;
890   }
891   if (memb->idp!=NULL)
892     tr_idp_realm_decref(memb->idp);
893
894   memb->idp=realm;
895   tr_idp_realm_incref(realm);
896 }
897
898 TR_IDP_REALM *tr_comm_memb_get_idp_realm(TR_COMM_MEMB *memb)
899 {
900   return memb->idp;
901 }
902
903 void tr_comm_memb_set_comm(TR_COMM_MEMB *memb, TR_COMM *comm)
904 {
905   if (memb->comm!=NULL)
906     tr_comm_decref(memb->comm);
907   memb->comm=comm;
908   tr_comm_incref(comm);
909 }
910
911 TR_COMM *tr_comm_memb_get_comm(TR_COMM_MEMB *memb)
912 {
913   return memb->comm;
914 }
915
916 static void tr_comm_memb_set_origin(TR_COMM_MEMB *memb, TR_NAME *origin)
917 {
918   if (memb->origin!=NULL)
919     tr_free_name(memb->origin);
920   memb->origin=origin;
921 }
922
923 TR_NAME *tr_comm_memb_get_origin(TR_COMM_MEMB *memb)
924 {
925   return memb->origin;
926 }
927
928 TR_NAME *tr_comm_memb_dup_origin(TR_COMM_MEMB *memb)
929 {
930   if (memb->origin!=NULL)
931     return tr_dup_name(memb->origin);
932   return NULL;
933 }
934
935 json_t *tr_comm_memb_get_provenance(TR_COMM_MEMB *memb)
936 {
937   if (memb!=NULL)
938     return memb->provenance;
939   return NULL;
940 }
941
942 void tr_comm_memb_set_provenance(TR_COMM_MEMB *memb, json_t *prov)
943 {
944   const char *s=NULL;
945
946   if (memb->provenance)
947     json_decref(memb->provenance);
948
949   memb->provenance=prov;
950   if (prov!=NULL) {
951     json_incref(prov);
952
953     /* next line sets origin to NULL if provenance is empty because jansson
954      * routines return NULL on error */
955     s=json_string_value(json_array_get(prov, 0));
956     if (s==NULL)
957       tr_comm_memb_set_origin(memb, NULL);
958     else
959       memb->origin=tr_new_name(s);
960   } else {
961     tr_comm_memb_set_origin(memb, NULL);
962   }
963 }
964
965 void tr_comm_memb_add_to_provenance(TR_COMM_MEMB *memb, TR_NAME *hop)
966 {
967   if (memb->provenance==NULL) {
968     memb->provenance=json_array();
969     if (memb->provenance==NULL) {
970       tr_err("tr_comm_memb_add_to_provenance: unable to allocate provenance list.");
971       return;
972     }
973     /* this is the first entry in the provenance, so it is the origin */
974     tr_comm_memb_set_origin(memb,tr_dup_name(hop));
975     if (memb->origin==NULL) {
976       tr_err("tr_comm_memb_add_to_provenance: unable to allocate origin.");
977       json_decref(memb->provenance);
978       memb->provenance=NULL;
979       return;
980     }
981   }
982   if (0!=json_array_append_new(memb->provenance, tr_name_to_json_string(hop)))
983     tr_err("tr_comm_memb_add_to_provenance: unable to extend provenance list.");
984 }
985
986 size_t tr_comm_memb_provenance_len(TR_COMM_MEMB *memb)
987 {
988   if (memb->provenance==NULL)
989     return 0;
990   return json_array_size(memb->provenance);
991 }
992
993 void tr_comm_memb_set_interval(TR_COMM_MEMB *memb, unsigned int interval)
994 {
995   memb->interval=interval;
996 }
997
998 unsigned int tr_comm_memb_get_interval(TR_COMM_MEMB *memb)
999 {
1000   return memb->interval;
1001 }
1002
1003 void tr_comm_memb_set_expiry(TR_COMM_MEMB *memb, struct timespec *time)
1004 {
1005   if (time==NULL)
1006     *(memb->expiry)=(struct timespec){0,0};
1007   else {
1008     memb->expiry->tv_sec=time->tv_sec;
1009     memb->expiry->tv_nsec=time->tv_nsec;
1010   }
1011 }
1012
1013 struct timespec *tr_comm_memb_get_expiry(TR_COMM_MEMB *memb)
1014 {
1015   return memb->expiry;
1016 }
1017
1018 int tr_comm_memb_is_expired(TR_COMM_MEMB *memb, struct timespec *curtime)
1019 {
1020   return ((curtime->tv_sec > memb->expiry->tv_sec)
1021          || ((curtime->tv_sec == memb->expiry->tv_sec)
1022             &&(curtime->tv_nsec >= memb->expiry->tv_nsec)));
1023 }
1024
1025 void tr_comm_memb_set_triggered(TR_COMM_MEMB *memb, int trig)
1026 {
1027   memb->triggered=trig;
1028 }
1029
1030 int tr_comm_memb_is_triggered(TR_COMM_MEMB *memb)
1031 {
1032   return memb->triggered;
1033 }
1034
1035 void tr_comm_memb_reset_times_expired(TR_COMM_MEMB *memb)
1036 {
1037   memb->times_expired=0;
1038 }
1039
1040 /* bumps the expiration count */
1041 void tr_comm_memb_expire(TR_COMM_MEMB *memb)
1042 {
1043   /* avoid overflow */
1044   if (memb->times_expired+1>memb->times_expired)
1045     memb->times_expired++;
1046 }
1047
1048 unsigned int tr_comm_memb_get_times_expired(TR_COMM_MEMB *memb)
1049 {
1050   return memb->times_expired;
1051 }
1052
1053 TR_COMM_TABLE *tr_comm_table_new(TALLOC_CTX *mem_ctx)
1054 {
1055   TR_COMM_TABLE *ctab=talloc(mem_ctx, TR_COMM_TABLE);
1056   if (ctab!=NULL) {
1057     ctab->comms=NULL;
1058     ctab->memberships=NULL;
1059     ctab->idp_realms=NULL;
1060     ctab->rp_realms=NULL;
1061   }
1062   return ctab;
1063 }
1064
1065 void tr_comm_table_free(TR_COMM_TABLE *ctab)
1066 {
1067   talloc_free(ctab);
1068 }
1069
1070 static TR_REALM_ROLE tr_comm_memb_role(TR_COMM_MEMB *memb)
1071 {
1072   if (memb->rp!=NULL)
1073     return TR_ROLE_RP;
1074   if (memb->idp!=NULL)
1075     return TR_ROLE_IDP;
1076
1077   return TR_ROLE_UNKNOWN;
1078 }
1079
1080 void tr_comm_table_add_memb(TR_COMM_TABLE *ctab, TR_COMM_MEMB *new)
1081 {
1082   TR_COMM_MEMB *cur=NULL;
1083
1084   /* TODO: further validate the member (must have valid comm and realm) */
1085   if ((new->next!=NULL) || (new->origin_next!=NULL)) {
1086     tr_debug("tr_comm_table_add_memb: attempting to add member already in a list.");
1087   }
1088
1089   /* handle the empty list case */
1090   if (ctab->memberships==NULL) {
1091     ctab->memberships=new;
1092     talloc_steal(ctab, new);
1093     return;
1094   }
1095
1096   /* The list was not empty. See if we already have a membership for this realm/comm/role */
1097   switch (tr_comm_memb_role(new)) {
1098   case TR_ROLE_RP:
1099     cur=tr_comm_table_find_rp_memb(ctab,
1100                                    tr_rp_realm_get_id(tr_comm_memb_get_rp_realm(new)),
1101                                    tr_comm_get_id(tr_comm_memb_get_comm(new)));
1102     break;
1103   case TR_ROLE_IDP:
1104     cur=tr_comm_table_find_idp_memb(ctab,
1105                                     tr_idp_realm_get_id(tr_comm_memb_get_idp_realm(new)),
1106                                     tr_comm_get_id(tr_comm_memb_get_comm(new)));
1107     break;
1108   case TR_ROLE_UNKNOWN:
1109   default:
1110     tr_err("tr_comm_table_add_memb: realm with unknown role added.");
1111     cur=NULL;
1112   }
1113
1114   if (cur==NULL) {
1115     /* no entry for this realm/comm/role, tack it on the end */
1116     for (cur=ctab->memberships; cur->next!=NULL; cur=cur->next) { }
1117     cur->next=new;
1118   } else {
1119     /* Found an entry. Add to the end of its same-origin list. */
1120     while (cur->origin_next!=NULL) {
1121       cur=cur->origin_next;
1122     }
1123     cur->origin_next=new;
1124   }
1125
1126   talloc_steal(ctab, new);
1127 }
1128
1129 /* Remove memb from ctab. Do not free anything. Do nothing if memb not in ctab. */
1130 void tr_comm_table_remove_memb(TR_COMM_TABLE *ctab, TR_COMM_MEMB *memb)
1131 {
1132   TR_COMM_MEMB *cur=NULL; /* for walking the main list */
1133   TR_COMM_MEMB *orig_cur=NULL; /* for walking the origin list */
1134
1135   if ((memb==NULL) || (ctab->memberships==NULL))
1136     return;
1137
1138   /* see if it's the first member */
1139   if (ctab->memberships==memb) {
1140     if (memb->origin_next!=NULL) {
1141       memb->origin_next->next=memb->next;
1142       ctab->memberships=memb->origin_next;
1143     } else
1144       ctab->memberships=memb->next;
1145
1146     return;
1147   }
1148
1149   /* see if it's in first member's origin list */
1150   for (orig_cur=ctab->memberships;
1151        orig_cur->origin_next!=NULL;
1152        orig_cur=orig_cur->origin_next) {
1153     if (orig_cur->origin_next==memb) {
1154       orig_cur->origin_next=memb->origin_next;
1155       return;
1156     }
1157   }
1158
1159   /* now we have to walk the rest of the tree */
1160   for (cur=ctab->memberships; cur->next!=NULL; cur=cur->next) {
1161     if (cur->next==memb) {
1162       /* it matched an entry on the main list */
1163       if (memb->origin_next==NULL)
1164         cur->next=memb->next; /* no origin list, just drop memb */
1165       else {
1166         /* replace the entry in the main list with the next element on the origin list */
1167         memb->origin_next->next=memb->next;
1168         cur->next=memb->origin_next;
1169       }
1170       return;
1171     } else {
1172       /* it was not on the main list, walk the origin list */
1173       for (orig_cur=cur; orig_cur->origin_next!=NULL; orig_cur=orig_cur->origin_next) {
1174         if (orig_cur->origin_next==memb) {
1175           orig_cur->origin_next=memb->origin_next;
1176           return; /* just drop the element from the origin list */
1177         }
1178       }
1179     }
1180   }
1181   /* if we got here, cur->next was null. Still have to check the origin_next list */
1182   for (orig_cur=cur; orig_cur->origin_next!=NULL; orig_cur=orig_cur->origin_next) {
1183     if (orig_cur->origin_next==memb) {
1184       orig_cur->origin_next=memb->origin_next;
1185       return; /* just drop the element from the origin list */
1186     }
1187   }
1188 }
1189
1190 TR_NAME *tr_comm_memb_get_realm_id(TR_COMM_MEMB *memb)
1191 {
1192   if (memb->rp!=NULL)
1193     return tr_rp_realm_get_id(memb->rp);
1194   else
1195     return tr_idp_realm_get_id(memb->idp);
1196 }
1197
1198 /* find a membership from any origin */
1199 TR_COMM_MEMB *tr_comm_table_find_memb(TR_COMM_TABLE *ctab, TR_NAME *realm, TR_NAME *comm)
1200 {
1201   TR_COMM_MEMB *cur=NULL;
1202   TR_NAME *cur_realm_name=NULL;
1203
1204   for (cur=ctab->memberships; cur!=NULL; cur=cur->next) {
1205     cur_realm_name=tr_comm_memb_get_realm_id(cur);
1206     if (cur_realm_name==NULL) {
1207       tr_warning("tr_comm_table_find: encountered realm with no name.");
1208       continue;
1209     }
1210     if ((0==tr_name_cmp(realm, cur_realm_name)) &&
1211         (0==tr_name_cmp(comm, tr_comm_get_id(tr_comm_memb_get_comm(cur))))) {
1212       return cur;
1213     }
1214   }
1215   return NULL;
1216 }
1217
1218 /* find a membership from a particular origin */
1219 TR_COMM_MEMB *tr_comm_table_find_memb_origin(TR_COMM_TABLE *ctab, TR_NAME *realm, TR_NAME *comm, TR_NAME *origin)
1220 {
1221   TR_NAME *cur_orig=NULL;
1222   TR_COMM_MEMB *cur=tr_comm_table_find_memb(ctab, realm, comm);
1223   if (cur==NULL)
1224     return NULL; /* no match */
1225
1226   /* had a match for comm/realm; find origin match */
1227   while (cur!=NULL) {
1228     if (((origin==NULL) && (cur_orig==NULL)) ||
1229         ((origin!=NULL) && (cur_orig!=NULL) && (0==tr_name_cmp(origin, cur_orig))))
1230       return cur; /* found a match */
1231     cur=cur->origin_next;
1232   }
1233   return NULL; /* no match */
1234 }
1235
1236
1237 /* find an idp membership regardless of its origin */
1238 TR_COMM_MEMB *tr_comm_table_find_idp_memb(TR_COMM_TABLE *ctab, TR_NAME *realm, TR_NAME *comm)
1239 {
1240   TR_COMM_MEMB *cur=NULL;
1241   TR_IDP_REALM *idp_realm=NULL;
1242
1243   for (cur=ctab->memberships; cur!=NULL; cur=cur->next) {
1244     idp_realm=tr_comm_memb_get_idp_realm(cur);
1245     if (idp_realm==NULL)
1246       continue; /* was not an idp */
1247
1248     if ((0==tr_name_cmp(realm, idp_realm->realm_id)) &&
1249         (0==tr_name_cmp(comm, tr_comm_get_id(tr_comm_memb_get_comm(cur))))) {
1250       return cur;
1251     }
1252   }
1253   return NULL;
1254 }
1255
1256 /* find an idp membership from a particular origin */
1257 TR_COMM_MEMB *tr_comm_table_find_idp_memb_origin(TR_COMM_TABLE *ctab, TR_NAME *realm, TR_NAME *comm, TR_NAME *origin)
1258 {
1259   TR_NAME *cur_orig=NULL;
1260   TR_COMM_MEMB *cur=tr_comm_table_find_idp_memb(ctab, realm, comm);
1261   if (cur==NULL)
1262     return NULL; /* no match */
1263
1264   /* had a match for comm/realm; find origin match */
1265   while (cur!=NULL) {
1266     cur_orig=tr_comm_memb_get_origin(cur);
1267     if (((origin==NULL) && (cur_orig==NULL)) ||
1268         ((origin!=NULL) && (cur_orig!=NULL) && (0==tr_name_cmp(origin, cur_orig))))
1269       return cur; /* found a match */
1270     cur=cur->origin_next;
1271   }
1272   return NULL; /* no match */
1273 }
1274
1275 /* find an rp membership from any origin */
1276 TR_COMM_MEMB *tr_comm_table_find_rp_memb(TR_COMM_TABLE *ctab, TR_NAME *realm, TR_NAME *comm)
1277 {
1278   TR_COMM_MEMB *cur=NULL;
1279   TR_RP_REALM *rp_realm=NULL;
1280
1281   for (cur=ctab->memberships; cur!=NULL; cur=cur->next) {
1282     rp_realm=tr_comm_memb_get_rp_realm(cur);
1283     if (rp_realm==NULL)
1284       continue; /* was not an rp */
1285
1286     if ((0==tr_name_cmp(realm, tr_rp_realm_get_id(rp_realm))) &&
1287         (0==tr_name_cmp(comm, tr_comm_get_id(tr_comm_memb_get_comm(cur))))) {
1288       return cur;
1289     }
1290   }
1291   return NULL;
1292 }
1293
1294 /* find an rp membership from a particular origin */
1295 TR_COMM_MEMB *tr_comm_table_find_rp_memb_origin(TR_COMM_TABLE *ctab, TR_NAME *realm, TR_NAME *comm, TR_NAME *origin)
1296 {
1297   TR_NAME *cur_orig=NULL;
1298   TR_COMM_MEMB *cur=tr_comm_table_find_rp_memb(ctab, realm, comm);
1299   if (cur==NULL)
1300     return NULL; /* no match */
1301
1302   /* had a match for comm/realm; find origin match */
1303   while (cur!=NULL) {
1304     cur_orig=tr_comm_memb_get_origin(cur);
1305     if (((origin==NULL) && (cur_orig==NULL)) ||
1306         ((origin!=NULL) && (cur_orig!=NULL) && (0==tr_name_cmp(origin, cur_orig))))
1307       return cur; /* found a match */
1308     cur=cur->origin_next;
1309   }
1310   return NULL; /* no match */
1311 }
1312
1313 TR_COMM *tr_comm_table_find_comm(TR_COMM_TABLE *ctab, TR_NAME *comm_id)
1314 {
1315   return tr_comm_lookup(ctab->comms, comm_id);
1316 }
1317
1318 void tr_comm_table_add_comm(TR_COMM_TABLE *ctab, TR_COMM *new)
1319 {
1320   tr_comm_add(ctab->comms, new);
1321   if (ctab->comms!=NULL)
1322     talloc_steal(ctab, ctab->comms); /* make sure it's in the right context */
1323 }
1324
1325 void tr_comm_table_remove_comm(TR_COMM_TABLE *ctab, TR_COMM *comm)
1326 {
1327   tr_comm_remove(ctab->comms, comm);
1328 }
1329
1330 TR_RP_REALM *tr_comm_table_find_rp_realm(TR_COMM_TABLE *ctab, TR_NAME *realm_id)
1331 {
1332   return tr_rp_realm_lookup(ctab->rp_realms, realm_id);
1333 }
1334
1335 void tr_comm_table_add_rp_realm(TR_COMM_TABLE *ctab, TR_RP_REALM *new)
1336 {
1337   tr_rp_realm_add(ctab->rp_realms, new);
1338   if (ctab->rp_realms!=NULL)
1339     talloc_steal(ctab, ctab->rp_realms); /* make sure it's in the right context */
1340 }
1341
1342 void tr_comm_table_remove_rp_realm(TR_COMM_TABLE *ctab, TR_RP_REALM *realm)
1343 {
1344   tr_rp_realm_remove(ctab->rp_realms, realm);
1345 }
1346
1347 TR_IDP_REALM *tr_comm_table_find_idp_realm(TR_COMM_TABLE *ctab, TR_NAME *realm_id)
1348 {
1349   return tr_idp_realm_lookup(ctab->idp_realms, realm_id);
1350 }
1351
1352 void tr_comm_table_add_idp_realm(TR_COMM_TABLE *ctab, TR_IDP_REALM *new)
1353 {
1354   tr_idp_realm_add(ctab->idp_realms, new);
1355   if (ctab->idp_realms!=NULL)
1356     talloc_steal(ctab, ctab->idp_realms); /* make sure it's in the right context */
1357 }
1358
1359 void tr_comm_table_remove_idp_realm(TR_COMM_TABLE *ctab, TR_IDP_REALM *realm)
1360 {
1361   tr_idp_realm_remove(ctab->idp_realms, realm);
1362 }
1363
1364
1365 /* how many communities in the table? */
1366 size_t tr_comm_table_size(TR_COMM_TABLE *ctab)
1367 {
1368   size_t count=0;
1369   TR_COMM *this=ctab->comms;
1370   while(this!=NULL) {
1371     this=this->next;
1372     count++;
1373   }
1374   return count;
1375 }
1376
1377 /* clean up unreferenced realms, etc */
1378 void tr_comm_table_sweep(TR_COMM_TABLE *ctab)
1379 {
1380   tr_rp_realm_sweep(ctab->rp_realms);
1381   tr_idp_realm_sweep(ctab->idp_realms);
1382   tr_comm_sweep(ctab->comms);
1383 }
1384
1385
1386 const char *tr_realm_role_to_str(TR_REALM_ROLE role)
1387 {
1388   switch(role) {
1389   case TR_ROLE_IDP:
1390     return "idp";
1391   case TR_ROLE_RP:
1392     return "rp";
1393   default:
1394     return NULL;
1395   }
1396 }
1397
1398 TR_REALM_ROLE tr_realm_role_from_str(const char *s)
1399 {
1400   if (strcmp(s, "idp")==0)
1401     return TR_ROLE_IDP;
1402   if (strcmp(s, "rp")==0)
1403     return TR_ROLE_RP;
1404   return TR_ROLE_UNKNOWN;
1405 }
1406
1407 void tr_comm_table_print(FILE *f, TR_COMM_TABLE *ctab)
1408 {
1409   TR_COMM_MEMB *p1=NULL; /* for walking the main list */
1410   TR_COMM_MEMB *p2=NULL; /* for walking the same-origin lists */
1411
1412   fprintf(f, ">> Membership table start <<\n");
1413   for (p1=ctab->memberships; p1!=NULL; p1=p1->next) {
1414     fprintf(f, "* %s %s/%s\n  %s (%p)\n",
1415             tr_realm_role_to_str(tr_comm_memb_get_role(p1)),
1416             tr_comm_memb_get_realm_id(p1)->buf,
1417             tr_comm_get_id(tr_comm_memb_get_comm(p1))->buf,
1418             (tr_comm_memb_get_origin(p1)==NULL)?"null origin":(tr_comm_memb_get_origin(p1)->buf),
1419             p1);
1420     for (p2=p1->origin_next; p2!=NULL; p2=p2->origin_next) {
1421       fprintf(f, "  %s (%p)\n",
1422               (tr_comm_memb_get_origin(p2)==NULL)?"null origin":(tr_comm_memb_get_origin(p2)->buf),
1423               p2);
1424     }
1425     fprintf(f, "\n");
1426   }
1427 }