Add a 'request_id' to TID requests and responses
[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 <tr_name_internal.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   tr_comm_memb_set_expiry(newmemb, expiry);
250
251   existing=tr_comm_table_find_idp_memb_origin(ctab,
252                                               tr_idp_realm_get_id(realm),
253                                               tr_comm_get_id(comm),
254                                               tr_comm_memb_get_origin(newmemb));
255   tr_comm_add_if_shorter(ctab, existing, newmemb); /* takes newmemb out of tmp_ctx if needed */
256
257   talloc_free(tmp_ctx);
258 }
259
260 /* does not take responsibility for freeing RP realm */
261 void tr_comm_add_rp_realm(TR_COMM_TABLE *ctab,
262                           TR_COMM *comm,
263                           TR_RP_REALM *realm,
264                           unsigned int interval,
265                           json_t *provenance,
266                           struct timespec *expiry)
267 {
268   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
269   TR_COMM_MEMB *newmemb=tr_comm_memb_new(tmp_ctx);
270   TR_COMM_MEMB *existing=NULL;
271
272   if (newmemb==NULL) {
273     tr_err("tr_comm_add_idp_realm: unable to allocate new membership record.");
274     talloc_free(tmp_ctx);
275     return;
276   }
277
278   tr_comm_memb_set_rp_realm(newmemb, realm);
279   tr_comm_memb_set_comm(newmemb, comm);
280   tr_comm_memb_set_interval(newmemb, interval);
281   tr_comm_memb_set_provenance(newmemb, provenance);
282   tr_comm_memb_set_expiry(newmemb, expiry);
283
284   existing=tr_comm_table_find_rp_memb_origin(ctab,
285                                              tr_rp_realm_get_id(realm),
286                                              tr_comm_get_id(comm),
287                                              tr_comm_memb_get_origin(newmemb));
288   tr_comm_add_if_shorter(ctab, existing, newmemb); /* takes newmemb out of tmp_ctx if needed */
289   talloc_free(tmp_ctx);
290 }
291
292 static TR_COMM *tr_comm_tail(TR_COMM *comm)
293 {
294   if (comm==NULL)
295     return comm;
296
297   while (comm->next!=NULL)
298     comm=comm->next;
299   return comm;
300 }
301
302 /* All list members are in the talloc context of the head.
303  * This will require careful thought if entries are ever removed
304  * Call like comms=tr_comm_add_func(comms, new_comm);
305  * or just use the tr_comm_add(comms, new) macro. */
306 #define tr_comm_add(comms, new) ((comms)=tr_comm_add_func((comms), (new)))
307 static TR_COMM *tr_comm_add_func(TR_COMM *comms, TR_COMM *new)
308 {
309   if (comms==NULL)
310     comms=new;
311   else {
312     tr_comm_tail(comms)->next=new;
313     while(new!=NULL) {
314       talloc_steal(comms, new);
315       new=new->next;
316     }
317   }
318   return comms;
319 }
320
321 /* Guarantees comm is not in the list, not an error if it was't there.
322  * Does not free the removed element, nor change its talloc context. */
323 #define tr_comm_remove(comms, c) ((comms)=tr_comm_remove_func((comms), (c)))
324 static TR_COMM *tr_comm_remove_func(TR_COMM *comms, TR_COMM *remove)
325 {
326   TALLOC_CTX *list_ctx=talloc_parent(comms); /* in case we need to remove the head */
327   TR_COMM *this=NULL;
328
329   if (comms==NULL)
330     return NULL;
331
332   if (comms==remove) {
333     /* if we're removing the head, put the next element (if present) into the context
334      * the list head was in. */
335     comms=comms->next;
336     if (comms!=NULL) {
337       talloc_steal(list_ctx, comms);
338       /* now put all the other elements in the context of the list head */
339       for (this=comms->next; this!=NULL; this=this->next)
340         talloc_steal(comms, this);
341     }
342   } else {
343     /* not removing the head; no need to play with contexts */
344     for (this=comms; this->next!=NULL; this=this->next) {
345       if (this->next==remove) {
346         this->next=remove->next;
347         break;
348       }
349     }
350   }
351   return comms;
352 }
353
354 /* remove any with zero refcount 
355  * Call via macro. */
356 #define tr_comm_sweep(head) ((head)=tr_comm_sweep_func((head)))
357 static TR_COMM *tr_comm_sweep_func(TR_COMM *head)
358 {
359   TR_COMM *comm=NULL;
360   TR_COMM *old_next=NULL;
361
362   if (head==NULL)
363     return NULL;
364
365   while ((head!=NULL) && (head->refcount==0)) {
366     comm=head; /* keep a pointer so we can remove it */
367     tr_comm_remove(head, comm); /* use this to get talloc contexts right */
368     tr_comm_free(comm);
369   }
370
371   if (head==NULL)
372     return NULL;
373
374   /* will not remove the head here, that has already been done */
375   for (comm=head; (comm!=NULL) && (comm->next!=NULL); comm=comm->next) {
376     if (comm->next->refcount==0) {
377       old_next=comm->next;
378       tr_comm_remove(head, comm->next); /* changes comm->next, may make it null */
379       tr_comm_free(old_next);
380     }
381   }
382
383   return head;
384 }
385
386 TR_IDP_REALM *tr_comm_find_idp(TR_COMM_TABLE *ctab, TR_COMM *comm, TR_NAME *idp_realm)
387 {
388   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
389   TR_COMM_ITER *iter=NULL;
390   TR_IDP_REALM *this_idp=NULL;
391
392   if ((NULL==ctab) || (NULL==comm) || (NULL==idp_realm)) {
393     talloc_free(tmp_ctx);
394     return NULL;
395   }
396
397   iter=tr_comm_iter_new(tmp_ctx);
398   for (this_idp=tr_idp_realm_iter_first(iter, ctab, tr_comm_get_id(comm));
399        this_idp!=NULL;
400        this_idp=tr_idp_realm_iter_next(iter)) {
401     if (0==tr_name_cmp(idp_realm, tr_idp_realm_get_id(this_idp))) {
402       tr_debug("tr_comm_find_idp: Found IdP %s in community %s.", idp_realm->buf, tr_comm_get_id(comm)->buf);
403       talloc_free(tmp_ctx);
404       return this_idp;
405     }
406   }
407   tr_debug("tr_comm_find_idp: Unable to find IdP %s in community %s.", idp_realm->buf, tr_comm_get_id(comm)->buf);
408   talloc_free(tmp_ctx);
409   return NULL;
410 }
411
412 TR_RP_REALM *tr_comm_find_rp (TR_COMM_TABLE *ctab, TR_COMM *comm, TR_NAME *rp_realm)
413 {
414   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
415   TR_COMM_ITER *iter=NULL;
416   TR_RP_REALM *this_rp=NULL;
417
418   if ((NULL==ctab) || (NULL==comm) || (NULL==rp_realm)) {
419     talloc_free(tmp_ctx);
420     return NULL;
421   }
422
423   iter=tr_comm_iter_new(tmp_ctx);
424   for (this_rp=tr_rp_realm_iter_first(iter, ctab, tr_comm_get_id(comm));
425        this_rp!=NULL;
426        this_rp=tr_rp_realm_iter_next(iter)) {
427     if (0==tr_name_cmp(rp_realm, tr_rp_realm_get_id(this_rp))) {
428       tr_debug("tr_comm_find_rp: Found RP %s in community %s.", rp_realm->buf, tr_comm_get_id(comm)->buf);
429       talloc_free(tmp_ctx);
430       return this_rp;
431     }
432   }
433   tr_debug("tr_comm_find_rp: Unable to find RP %s in community %s.", rp_realm->buf, tr_comm_get_id(comm)->buf);
434   talloc_free(tmp_ctx);
435   return NULL;
436 }
437
438 static TR_COMM *tr_comm_lookup(TR_COMM *comms, TR_NAME *comm_name) 
439 {
440   TR_COMM *cfg_comm = NULL;
441
442   for (cfg_comm = comms; NULL != cfg_comm; cfg_comm = cfg_comm->next) {
443     if (0==tr_name_cmp(cfg_comm->id, comm_name))
444       return cfg_comm;
445   }
446   return NULL;
447 }
448
449 TR_COMM_ITER *tr_comm_iter_new(TALLOC_CTX *mem_ctx)
450 {
451   TR_COMM_ITER *iter=talloc(mem_ctx, TR_COMM_ITER);
452   if (iter!=NULL) {
453     iter->cur_comm=NULL;
454     iter->cur_memb=NULL;
455     iter->cur_orig_head=NULL;
456     iter->match=NULL;
457     iter->realm=NULL;
458   }
459   return iter;
460 }
461
462 void tr_comm_iter_free(TR_COMM_ITER *iter)
463 {
464   talloc_free(iter);
465 }
466
467
468 TR_COMM *tr_comm_iter_first(TR_COMM_ITER *iter, TR_COMM_TABLE *ctab, TR_NAME *realm)
469 {
470   iter->match=realm;
471
472   /* find memberships for this realm */
473   for (iter->cur_memb=ctab->memberships;
474        iter->cur_memb!=NULL;
475        iter->cur_memb=iter->cur_memb->next) {
476     if (0==tr_name_cmp(iter->match, tr_comm_memb_get_realm_id(iter->cur_memb)))
477       return tr_comm_memb_get_comm(iter->cur_memb);
478   }
479   return NULL;
480 }
481
482 TR_COMM *tr_comm_iter_next(TR_COMM_ITER *iter)
483 {
484   for (iter->cur_memb=iter->cur_memb->next;
485        iter->cur_memb!=NULL;
486        iter->cur_memb=iter->cur_memb->next) {
487     if (0==tr_name_cmp(iter->match, tr_comm_memb_get_realm_id(iter->cur_memb)))
488       return tr_comm_memb_get_comm(iter->cur_memb);
489   }
490   return NULL;
491 }
492
493 /* iterate only over RPs */
494 TR_COMM *tr_comm_iter_first_rp(TR_COMM_ITER *iter, TR_COMM_TABLE *ctab, TR_NAME *realm)
495 {
496   iter->match=realm;
497
498   /* find memberships for this realm */
499   for (iter->cur_memb=ctab->memberships;
500        iter->cur_memb!=NULL;
501        iter->cur_memb=iter->cur_memb->next) {
502     if ((tr_comm_memb_get_rp_realm(iter->cur_memb)!=NULL) &&
503         (0==tr_name_cmp(iter->match, tr_comm_memb_get_realm_id(iter->cur_memb))))
504       return tr_comm_memb_get_comm(iter->cur_memb);
505   }
506   return NULL;
507 }
508
509 TR_COMM *tr_comm_iter_next_rp(TR_COMM_ITER *iter)
510 {
511   for (iter->cur_memb=iter->cur_memb->next;
512        iter->cur_memb!=NULL;
513        iter->cur_memb=iter->cur_memb->next) {
514     if ((tr_comm_memb_get_rp_realm(iter->cur_memb)!=NULL) &&
515         (0==tr_name_cmp(iter->match, tr_comm_memb_get_realm_id(iter->cur_memb))))
516       return tr_comm_memb_get_comm(iter->cur_memb);
517   }
518   return NULL;
519 }
520
521 /* iterate only over IDPs */
522 TR_COMM *tr_comm_iter_first_idp(TR_COMM_ITER *iter, TR_COMM_TABLE *ctab, TR_NAME *realm)
523 {
524   iter->match=realm;
525
526   /* find memberships for this realm */
527   for (iter->cur_memb=ctab->memberships;
528        iter->cur_memb!=NULL;
529        iter->cur_memb=iter->cur_memb->next) {
530     if ((tr_comm_memb_get_idp_realm(iter->cur_memb)!=NULL) &&
531         (0==tr_name_cmp(iter->match, tr_comm_memb_get_realm_id(iter->cur_memb))))
532       return tr_comm_memb_get_comm(iter->cur_memb);
533   }
534   return NULL;
535 }
536
537 TR_COMM *tr_comm_iter_next_idp(TR_COMM_ITER *iter)
538 {
539   for (iter->cur_memb=iter->cur_memb->next;
540        iter->cur_memb!=NULL;
541        iter->cur_memb=iter->cur_memb->next) {
542     if ((tr_comm_memb_get_idp_realm(iter->cur_memb)!=NULL) &&
543         (0==tr_name_cmp(iter->match, tr_comm_memb_get_realm_id(iter->cur_memb))))
544       return tr_comm_memb_get_comm(iter->cur_memb);
545   }
546   return NULL;
547 }
548
549 static TR_REALM *tr_realm_new(TALLOC_CTX *mem_ctx)
550 {
551   TR_REALM *realm=talloc(mem_ctx, TR_REALM);
552   if (realm!=NULL) {
553     realm->role=TR_ROLE_UNKNOWN;
554     realm->rp=NULL;
555     realm->idp=NULL;
556   }
557   return realm;
558 }
559
560 static void tr_realm_free(TR_REALM *realm)
561 {
562   talloc_free(realm);
563 }
564
565 static void tr_realm_set_rp(TR_REALM *realm, TR_RP_REALM *rp)
566 {
567   if (realm->idp!=NULL)
568     realm->idp=NULL;
569   realm->role=TR_ROLE_RP;
570   realm->rp=rp;
571 }
572
573 static void tr_realm_set_idp(TR_REALM *realm, TR_IDP_REALM *idp)
574 {
575   if (realm->rp!=NULL)
576     realm->rp=NULL;
577   realm->role=TR_ROLE_IDP;
578   realm->idp=idp;
579 }
580
581 TR_NAME *tr_realm_get_id(TR_REALM *realm)
582 {
583   switch (realm->role) {
584   case TR_ROLE_RP:
585     return tr_rp_realm_get_id(realm->rp);
586   case TR_ROLE_IDP:
587     return tr_idp_realm_get_id(realm->idp);
588   default:
589     break;
590   }
591   return NULL;
592 }
593
594 TR_NAME *tr_realm_dup_id(TR_REALM *realm)
595 {
596   return tr_dup_name(tr_realm_get_id(realm));
597 }
598
599 /* Iterate over either sort of realm. Do not free the TR_REALM returned. It becomes
600  * undefined/invalid after the next operation affecting the iterator. */
601 TR_REALM *tr_realm_iter_first(TR_COMM_ITER *iter, TR_COMM_TABLE *ctab, TR_NAME *comm)
602 {
603   iter->match=comm;
604   if (iter->realm==NULL)
605     iter->realm=tr_realm_new(iter);
606   if (iter->realm==NULL)
607     return NULL;
608
609   /* find memberships for this comm */
610   for (iter->cur_memb=ctab->memberships;
611        iter->cur_memb!=NULL;
612        iter->cur_memb=iter->cur_memb->next) {
613     if (0==tr_name_cmp(iter->match,
614                        tr_comm_get_id(tr_comm_memb_get_comm(iter->cur_memb)))) {
615       /* found a match, determine whether it's an rp realm or an idp realm */
616       if (tr_comm_memb_get_rp_realm(iter->cur_memb)!=NULL)
617         tr_realm_set_rp(iter->realm, tr_comm_memb_get_rp_realm(iter->cur_memb));
618       else if (tr_comm_memb_get_idp_realm(iter->cur_memb)!=NULL)
619         tr_realm_set_idp(iter->realm, tr_comm_memb_get_idp_realm(iter->cur_memb));
620       else {
621         if (iter->realm!=NULL)
622           tr_realm_free(iter->realm);
623         iter->realm=NULL;
624       }
625       return iter->realm;
626     }
627   }
628   if (iter->realm!=NULL)
629     tr_realm_free(iter->realm);
630   iter->realm=NULL;
631   return NULL;
632 }
633
634 TR_REALM *tr_realm_iter_next(TR_COMM_ITER *iter)
635 {
636   if (iter->realm==NULL)
637     return NULL;
638
639   /* find memberships for this comm */
640   for (iter->cur_memb=iter->cur_memb->next;
641        iter->cur_memb!=NULL;
642        iter->cur_memb=iter->cur_memb->next) {
643     if (0==tr_name_cmp(iter->match,
644                        tr_comm_get_id(tr_comm_memb_get_comm(iter->cur_memb)))) {
645       /* found a match, determine whether it's an rp realm or an idp realm */
646       if (tr_comm_memb_get_rp_realm(iter->cur_memb)!=NULL)
647         tr_realm_set_rp(iter->realm, tr_comm_memb_get_rp_realm(iter->cur_memb));
648       else if (tr_comm_memb_get_idp_realm(iter->cur_memb)!=NULL)
649         tr_realm_set_idp(iter->realm, tr_comm_memb_get_idp_realm(iter->cur_memb));
650       else {
651         if (iter->realm!=NULL)
652           tr_realm_free(iter->realm);
653         iter->realm=NULL;
654       }
655       return iter->realm;
656     }
657   }
658   if (iter->realm!=NULL)
659     tr_realm_free(iter->realm);
660   iter->realm=NULL;
661   return NULL;
662 }
663
664 TR_RP_REALM *tr_rp_realm_iter_first(TR_COMM_ITER *iter, TR_COMM_TABLE *ctab, TR_NAME *comm)
665 {
666   iter->match=comm;
667
668   /* find memberships for this comm */
669   for (iter->cur_memb=ctab->memberships;
670        iter->cur_memb!=NULL;
671        iter->cur_memb=iter->cur_memb->next) {
672     if ((tr_comm_memb_get_rp_realm(iter->cur_memb)!=NULL) &&
673         (0==tr_name_cmp(iter->match, tr_comm_get_id(tr_comm_memb_get_comm(iter->cur_memb)))))
674       return tr_comm_memb_get_rp_realm(iter->cur_memb);
675   }
676   return NULL;
677 }
678
679 TR_RP_REALM *tr_rp_realm_iter_next(TR_COMM_ITER *iter)
680 {
681   for (iter->cur_memb=iter->cur_memb->next;
682        iter->cur_memb!=NULL;
683        iter->cur_memb=iter->cur_memb->next) {
684     if ((tr_comm_memb_get_rp_realm(iter->cur_memb)!=NULL) &&
685         (0==tr_name_cmp(iter->match, tr_comm_get_id(tr_comm_memb_get_comm(iter->cur_memb)))))
686       return tr_comm_memb_get_rp_realm(iter->cur_memb);
687   }
688   return NULL;
689 }
690
691 TR_IDP_REALM *tr_idp_realm_iter_first(TR_COMM_ITER *iter, TR_COMM_TABLE *ctab, TR_NAME *comm)
692 {
693   iter->match=comm;
694
695   /* find memberships for this comm */
696   for (iter->cur_memb=ctab->memberships;
697        iter->cur_memb!=NULL;
698        iter->cur_memb=iter->cur_memb->next) {
699     if ((tr_comm_memb_get_idp_realm(iter->cur_memb)!=NULL) &&
700         (0==tr_name_cmp(iter->match, tr_comm_get_id(tr_comm_memb_get_comm(iter->cur_memb)))))
701       return tr_comm_memb_get_idp_realm(iter->cur_memb);
702   }
703   return NULL;
704 }
705
706 TR_IDP_REALM *tr_idp_realm_iter_next(TR_COMM_ITER *iter)
707 {
708   for (iter->cur_memb=iter->cur_memb->next;
709        iter->cur_memb!=NULL;
710        iter->cur_memb=iter->cur_memb->next) {
711     if ((tr_comm_memb_get_idp_realm(iter->cur_memb)!=NULL) &&
712         (0==tr_name_cmp(iter->match, tr_comm_get_id(tr_comm_memb_get_comm(iter->cur_memb)))))
713       return tr_comm_memb_get_idp_realm(iter->cur_memb);
714   }
715   return NULL;
716 }
717
718 /* iterators for all communities in a table */
719 TR_COMM *tr_comm_table_iter_first(TR_COMM_ITER *iter, TR_COMM_TABLE *ctab)
720 {
721   iter->cur_comm=ctab->comms;
722   return iter->cur_comm;
723 }
724
725 TR_COMM *tr_comm_table_iter_next(TR_COMM_ITER *iter)
726 {
727   return iter->cur_comm=iter->cur_comm->next;
728 }
729
730 const char *tr_comm_type_to_str(TR_COMM_TYPE type)
731 {
732   const char *s=NULL;
733   switch(type) {
734   case TR_COMM_UNKNOWN:
735     s="unknown";
736     break;
737   case TR_COMM_APC:
738     s="apc";
739     break;
740   case TR_COMM_COI:
741     s="coi";
742     break;
743   default:
744     s="invalid";
745   }
746   return s;
747 }
748
749 /* iterate along the origin list for this member */
750 TR_COMM_MEMB *tr_comm_memb_iter_first(TR_COMM_ITER *iter, TR_COMM_MEMB *memb)
751 {
752   iter->cur_memb=memb;
753   return iter->cur_memb;
754 }
755
756 TR_COMM_MEMB *tr_comm_memb_iter_next(TR_COMM_ITER *iter)
757 {
758   if (iter->cur_memb!=NULL)
759     iter->cur_memb=iter->cur_memb->origin_next;
760   return iter->cur_memb;
761 }
762
763
764 /* iterate over all memberships in the table */
765 TR_COMM_MEMB *tr_comm_memb_iter_all_first(TR_COMM_ITER *iter, TR_COMM_TABLE *ctab)
766 {
767   iter->cur_memb=ctab->memberships;
768   iter->cur_orig_head=ctab->memberships;
769   return iter->cur_memb;
770 }
771
772 TR_COMM_MEMB *tr_comm_memb_iter_all_next(TR_COMM_ITER *iter)
773 {
774   if (iter->cur_memb->next==NULL) {
775     if (iter->cur_orig_head->next==NULL) {
776       /* we're done */
777       return NULL;
778     } else {
779       iter->cur_memb=iter->cur_orig_head->next;
780       iter->cur_orig_head=iter->cur_orig_head->next;
781     }
782   } else {
783     iter->cur_memb=iter->cur_memb->origin_next;
784   }
785   return iter->cur_memb;
786 }
787
788
789 TR_COMM_TYPE tr_comm_type_from_str(const char *s)
790 {
791   if (strcmp(s, "apc")==0)
792     return TR_COMM_APC;
793   if (strcmp(s,"coi")==0)
794     return TR_COMM_COI;
795   return TR_COMM_UNKNOWN;
796 }
797
798
799 static int tr_comm_memb_destructor(void *obj)
800 {
801   TR_COMM_MEMB *memb=talloc_get_type_abort(obj, TR_COMM_MEMB);
802   if (memb->origin!=NULL)
803     tr_free_name(memb->origin);
804
805   if (memb->rp!=NULL)
806     tr_rp_realm_decref(memb->rp);
807   if (memb->idp!=NULL)
808     tr_idp_realm_decref(memb->idp);
809   if (memb->comm!=NULL)
810     tr_comm_decref(memb->comm);
811   if (memb->provenance!=NULL)
812     json_decref(memb->provenance);
813   return 0;
814 }
815
816 TR_COMM_MEMB *tr_comm_memb_new(TALLOC_CTX *mem_ctx)
817 {
818   TR_COMM_MEMB *memb=talloc(mem_ctx, TR_COMM_MEMB);
819   if (memb!=NULL) {
820     memb->next=NULL;
821     memb->origin_next=NULL;
822     memb->idp=NULL;
823     memb->rp=NULL;
824     memb->comm=NULL;
825     memb->origin=NULL;
826     memb->provenance=NULL;
827     memb->interval=0;
828     memb->triggered=0;
829     memb->times_expired=0;
830     memb->expiry=talloc(memb, struct timespec);
831     if (memb->expiry==NULL) {
832       talloc_free(memb);
833       return NULL;
834     }
835     *(memb->expiry)=(struct timespec){0,0};
836     talloc_set_destructor((void *)memb, tr_comm_memb_destructor);
837   }
838   return memb;
839 }
840
841 void tr_comm_memb_free(TR_COMM_MEMB *memb)
842 {
843   talloc_free(memb);
844 }
845
846 /* Returns 0 if they are the same, nonzero if they differ.
847  * Ignores expiry, triggered, and times_expired, next pointers */
848 int tr_comm_memb_cmp(TR_COMM_MEMB *m1, TR_COMM_MEMB *m2)
849 {
850   if ((m1->idp==m2->idp) &&
851       (m1->rp==m2->rp) &&
852       (m1->comm==m2->comm) &&
853       (tr_comm_memb_provenance_cmp(m1, m2, 0)==0) &&
854       (m1->interval==m2->interval))
855     return 0;
856   return 1;
857 }
858
859 TR_REALM_ROLE tr_comm_memb_get_role(TR_COMM_MEMB *memb)
860 {
861   if (memb->rp!=NULL)
862     return TR_ROLE_RP;
863   if (memb->idp!=NULL)
864     return TR_ROLE_IDP;
865   return TR_ROLE_UNKNOWN;
866 }
867
868 void tr_comm_memb_set_rp_realm(TR_COMM_MEMB *memb, TR_RP_REALM *realm)
869 {
870   if (memb->idp!=NULL) {
871     tr_idp_realm_decref(memb->idp);
872     memb->idp=NULL;
873   }
874   if (memb->rp!=NULL)
875     tr_rp_realm_decref(memb->rp);
876
877
878   memb->rp=realm;
879   tr_rp_realm_incref(realm);
880 }
881
882 TR_RP_REALM *tr_comm_memb_get_rp_realm(TR_COMM_MEMB *memb)
883 {
884   return memb->rp;
885 }
886
887 void tr_comm_memb_set_idp_realm(TR_COMM_MEMB *memb, TR_IDP_REALM *realm)
888 {
889   if (memb->rp!=NULL) {
890     tr_rp_realm_decref(memb->rp);
891     memb->rp=NULL;
892   }
893   if (memb->idp!=NULL)
894     tr_idp_realm_decref(memb->idp);
895
896   memb->idp=realm;
897   tr_idp_realm_incref(realm);
898 }
899
900 TR_IDP_REALM *tr_comm_memb_get_idp_realm(TR_COMM_MEMB *memb)
901 {
902   return memb->idp;
903 }
904
905 void tr_comm_memb_set_comm(TR_COMM_MEMB *memb, TR_COMM *comm)
906 {
907   if (memb->comm!=NULL)
908     tr_comm_decref(memb->comm);
909   memb->comm=comm;
910   tr_comm_incref(comm);
911 }
912
913 TR_COMM *tr_comm_memb_get_comm(TR_COMM_MEMB *memb)
914 {
915   return memb->comm;
916 }
917
918 static void tr_comm_memb_set_origin(TR_COMM_MEMB *memb, TR_NAME *origin)
919 {
920   if (memb->origin!=NULL)
921     tr_free_name(memb->origin);
922   memb->origin=origin;
923 }
924
925 TR_NAME *tr_comm_memb_get_origin(TR_COMM_MEMB *memb)
926 {
927   return memb->origin;
928 }
929
930 TR_NAME *tr_comm_memb_dup_origin(TR_COMM_MEMB *memb)
931 {
932   if (memb->origin!=NULL)
933     return tr_dup_name(memb->origin);
934   return NULL;
935 }
936
937 json_t *tr_comm_memb_get_provenance(TR_COMM_MEMB *memb)
938 {
939   if (memb!=NULL)
940     return memb->provenance;
941   return NULL;
942 }
943
944 void tr_comm_memb_set_provenance(TR_COMM_MEMB *memb, json_t *prov)
945 {
946   const char *s=NULL;
947
948   if (memb->provenance)
949     json_decref(memb->provenance);
950
951   memb->provenance=prov;
952   if (prov!=NULL) {
953     json_incref(prov);
954
955     /* next line sets origin to NULL if provenance is empty because jansson
956      * routines return NULL on error */
957     s=json_string_value(json_array_get(prov, 0));
958     if (s==NULL)
959       tr_comm_memb_set_origin(memb, NULL);
960     else
961       memb->origin=tr_new_name(s);
962   } else {
963     tr_comm_memb_set_origin(memb, NULL);
964   }
965 }
966
967 void tr_comm_memb_add_to_provenance(TR_COMM_MEMB *memb, TR_NAME *hop)
968 {
969   if (memb->provenance==NULL) {
970     memb->provenance=json_array();
971     if (memb->provenance==NULL) {
972       tr_err("tr_comm_memb_add_to_provenance: unable to allocate provenance list.");
973       return;
974     }
975     /* this is the first entry in the provenance, so it is the origin */
976     tr_comm_memb_set_origin(memb,tr_dup_name(hop));
977     if (memb->origin==NULL) {
978       tr_err("tr_comm_memb_add_to_provenance: unable to allocate origin.");
979       json_decref(memb->provenance);
980       memb->provenance=NULL;
981       return;
982     }
983   }
984   if (0!=json_array_append_new(memb->provenance, tr_name_to_json_string(hop)))
985     tr_err("tr_comm_memb_add_to_provenance: unable to extend provenance list.");
986 }
987
988 size_t tr_comm_memb_provenance_len(TR_COMM_MEMB *memb)
989 {
990   if (memb->provenance==NULL)
991     return 0;
992   return json_array_size(memb->provenance);
993 }
994
995 void tr_comm_memb_set_interval(TR_COMM_MEMB *memb, unsigned int interval)
996 {
997   memb->interval=interval;
998 }
999
1000 unsigned int tr_comm_memb_get_interval(TR_COMM_MEMB *memb)
1001 {
1002   return memb->interval;
1003 }
1004
1005 void tr_comm_memb_set_expiry(TR_COMM_MEMB *memb, struct timespec *time)
1006 {
1007   if (time==NULL)
1008     *(memb->expiry)=(struct timespec){0,0};
1009   else {
1010     memb->expiry->tv_sec=time->tv_sec;
1011     memb->expiry->tv_nsec=time->tv_nsec;
1012   }
1013 }
1014
1015 struct timespec *tr_comm_memb_get_expiry(TR_COMM_MEMB *memb)
1016 {
1017   return memb->expiry;
1018 }
1019
1020 int tr_comm_memb_is_expired(TR_COMM_MEMB *memb, struct timespec *curtime)
1021 {
1022   tr_debug("tr_comm_memb_is_expired: (cur->tv_sec>memb->expiry->tv_sec)=(%u > %u)=%s",
1023            curtime->tv_sec,
1024            memb->expiry->tv_sec,
1025            (curtime->tv_sec > memb->expiry->tv_sec)?"true":"false");
1026
1027   return ((curtime->tv_sec > memb->expiry->tv_sec)
1028          || ((curtime->tv_sec == memb->expiry->tv_sec)
1029             &&(curtime->tv_nsec >= memb->expiry->tv_nsec)));
1030 }
1031
1032 void tr_comm_memb_set_triggered(TR_COMM_MEMB *memb, int trig)
1033 {
1034   memb->triggered=trig;
1035 }
1036
1037 int tr_comm_memb_is_triggered(TR_COMM_MEMB *memb)
1038 {
1039   return memb->triggered;
1040 }
1041
1042 void tr_comm_memb_reset_times_expired(TR_COMM_MEMB *memb)
1043 {
1044   memb->times_expired=0;
1045 }
1046
1047 /* bumps the expiration count */
1048 void tr_comm_memb_expire(TR_COMM_MEMB *memb)
1049 {
1050   /* avoid overflow */
1051   if (memb->times_expired+1>memb->times_expired)
1052     memb->times_expired++;
1053 }
1054
1055 unsigned int tr_comm_memb_get_times_expired(TR_COMM_MEMB *memb)
1056 {
1057   return memb->times_expired;
1058 }
1059
1060 TR_COMM_TABLE *tr_comm_table_new(TALLOC_CTX *mem_ctx)
1061 {
1062   TR_COMM_TABLE *ctab=talloc(mem_ctx, TR_COMM_TABLE);
1063   if (ctab!=NULL) {
1064     ctab->comms=NULL;
1065     ctab->memberships=NULL;
1066     ctab->idp_realms=NULL;
1067     ctab->rp_realms=NULL;
1068   }
1069   return ctab;
1070 }
1071
1072 void tr_comm_table_free(TR_COMM_TABLE *ctab)
1073 {
1074   talloc_free(ctab);
1075 }
1076
1077 static TR_REALM_ROLE tr_comm_memb_role(TR_COMM_MEMB *memb)
1078 {
1079   if (memb->rp!=NULL)
1080     return TR_ROLE_RP;
1081   if (memb->idp!=NULL)
1082     return TR_ROLE_IDP;
1083
1084   return TR_ROLE_UNKNOWN;
1085 }
1086
1087 void tr_comm_table_add_memb(TR_COMM_TABLE *ctab, TR_COMM_MEMB *new)
1088 {
1089   TR_COMM_MEMB *cur=NULL;
1090
1091   /* TODO: further validate the member (must have valid comm and realm) */
1092   if ((new->next!=NULL) || (new->origin_next!=NULL)) {
1093     tr_debug("tr_comm_table_add_memb: attempting to add member already in a list.");
1094   }
1095
1096   /* handle the empty list case */
1097   if (ctab->memberships==NULL) {
1098     ctab->memberships=new;
1099     talloc_steal(ctab, new);
1100     return;
1101   }
1102
1103   /* The list was not empty. See if we already have a membership for this realm/comm/role */
1104   switch (tr_comm_memb_role(new)) {
1105   case TR_ROLE_RP:
1106     cur=tr_comm_table_find_rp_memb(ctab,
1107                                    tr_rp_realm_get_id(tr_comm_memb_get_rp_realm(new)),
1108                                    tr_comm_get_id(tr_comm_memb_get_comm(new)));
1109     break;
1110   case TR_ROLE_IDP:
1111     cur=tr_comm_table_find_idp_memb(ctab,
1112                                     tr_idp_realm_get_id(tr_comm_memb_get_idp_realm(new)),
1113                                     tr_comm_get_id(tr_comm_memb_get_comm(new)));
1114     break;
1115   case TR_ROLE_UNKNOWN:
1116   default:
1117     tr_err("tr_comm_table_add_memb: realm with unknown role added.");
1118     cur=NULL;
1119   }
1120
1121   if (cur==NULL) {
1122     /* no entry for this realm/comm/role, tack it on the end */
1123     for (cur=ctab->memberships; cur->next!=NULL; cur=cur->next) { }
1124     cur->next=new;
1125   } else {
1126     /* Found an entry. Add to the end of its same-origin list. */
1127     while (cur->origin_next!=NULL) {
1128       cur=cur->origin_next;
1129     }
1130     cur->origin_next=new;
1131   }
1132
1133   talloc_steal(ctab, new);
1134 }
1135
1136 /* Remove memb from ctab. Do not free anything. Do nothing if memb not in ctab. */
1137 void tr_comm_table_remove_memb(TR_COMM_TABLE *ctab, TR_COMM_MEMB *memb)
1138 {
1139   TR_COMM_MEMB *cur=NULL; /* for walking the main list */
1140   TR_COMM_MEMB *orig_cur=NULL; /* for walking the origin list */
1141
1142   if ((memb==NULL) || (ctab->memberships==NULL))
1143     return;
1144
1145   /* see if it's the first member */
1146   if (ctab->memberships==memb) {
1147     if (memb->origin_next!=NULL) {
1148       memb->origin_next->next=memb->next;
1149       ctab->memberships=memb->origin_next;
1150     } else
1151       ctab->memberships=memb->next;
1152
1153     return;
1154   }
1155
1156   /* see if it's in first member's origin list */
1157   for (orig_cur=ctab->memberships;
1158        orig_cur->origin_next!=NULL;
1159        orig_cur=orig_cur->origin_next) {
1160     if (orig_cur->origin_next==memb) {
1161       orig_cur->origin_next=memb->origin_next;
1162       return;
1163     }
1164   }
1165
1166   /* now we have to walk the rest of the tree */
1167   for (cur=ctab->memberships; cur->next!=NULL; cur=cur->next) {
1168     if (cur->next==memb) {
1169       /* it matched an entry on the main list */
1170       if (memb->origin_next==NULL)
1171         cur->next=memb->next; /* no origin list, just drop memb */
1172       else {
1173         /* replace the entry in the main list with the next element on the origin list */
1174         memb->origin_next->next=memb->next;
1175         cur->next=memb->origin_next;
1176       }
1177       return;
1178     } else {
1179       /* it was not on the main list, walk the origin list */
1180       for (orig_cur=cur; orig_cur->origin_next!=NULL; orig_cur=orig_cur->origin_next) {
1181         if (orig_cur->origin_next==memb) {
1182           orig_cur->origin_next=memb->origin_next;
1183           return; /* just drop the element from the origin list */
1184         }
1185       }
1186     }
1187   }
1188   /* if we got here, cur->next was null. Still have to check the origin_next list */
1189   for (orig_cur=cur; orig_cur->origin_next!=NULL; orig_cur=orig_cur->origin_next) {
1190     if (orig_cur->origin_next==memb) {
1191       orig_cur->origin_next=memb->origin_next;
1192       return; /* just drop the element from the origin list */
1193     }
1194   }
1195 }
1196
1197 TR_NAME *tr_comm_memb_get_realm_id(TR_COMM_MEMB *memb)
1198 {
1199   if (memb->rp!=NULL)
1200     return tr_rp_realm_get_id(memb->rp);
1201   else
1202     return tr_idp_realm_get_id(memb->idp);
1203 }
1204
1205 /* find a membership from any origin */
1206 TR_COMM_MEMB *tr_comm_table_find_memb(TR_COMM_TABLE *ctab, TR_NAME *realm, TR_NAME *comm)
1207 {
1208   TR_COMM_MEMB *cur=NULL;
1209   TR_NAME *cur_realm_name=NULL;
1210
1211   for (cur=ctab->memberships; cur!=NULL; cur=cur->next) {
1212     cur_realm_name=tr_comm_memb_get_realm_id(cur);
1213     if (cur_realm_name==NULL) {
1214       tr_warning("tr_comm_table_find: encountered realm with no name.");
1215       continue;
1216     }
1217     if ((0==tr_name_cmp(realm, cur_realm_name)) &&
1218         (0==tr_name_cmp(comm, tr_comm_get_id(tr_comm_memb_get_comm(cur))))) {
1219       return cur;
1220     }
1221   }
1222   return NULL;
1223 }
1224
1225 /* find a membership from a particular origin */
1226 TR_COMM_MEMB *tr_comm_table_find_memb_origin(TR_COMM_TABLE *ctab, TR_NAME *realm, TR_NAME *comm, TR_NAME *origin)
1227 {
1228   TR_NAME *cur_orig=NULL;
1229   TR_COMM_MEMB *cur=tr_comm_table_find_memb(ctab, realm, comm);
1230   if (cur==NULL)
1231     return NULL; /* no match */
1232
1233   /* had a match for comm/realm; find origin match */
1234   while (cur!=NULL) {
1235     if (((origin==NULL) && (cur_orig==NULL)) ||
1236         ((origin!=NULL) && (cur_orig!=NULL) && (0==tr_name_cmp(origin, cur_orig))))
1237       return cur; /* found a match */
1238     cur=cur->origin_next;
1239   }
1240   return NULL; /* no match */
1241 }
1242
1243
1244 /* find an idp membership regardless of its origin */
1245 TR_COMM_MEMB *tr_comm_table_find_idp_memb(TR_COMM_TABLE *ctab, TR_NAME *realm, TR_NAME *comm)
1246 {
1247   TR_COMM_MEMB *cur=NULL;
1248   TR_IDP_REALM *idp_realm=NULL;
1249
1250   for (cur=ctab->memberships; cur!=NULL; cur=cur->next) {
1251     idp_realm=tr_comm_memb_get_idp_realm(cur);
1252     if (idp_realm==NULL)
1253       continue; /* was not an idp */
1254
1255     if ((0==tr_name_cmp(realm, idp_realm->realm_id)) &&
1256         (0==tr_name_cmp(comm, tr_comm_get_id(tr_comm_memb_get_comm(cur))))) {
1257       return cur;
1258     }
1259   }
1260   return NULL;
1261 }
1262
1263 /* find an idp membership from a particular origin */
1264 TR_COMM_MEMB *tr_comm_table_find_idp_memb_origin(TR_COMM_TABLE *ctab, TR_NAME *realm, TR_NAME *comm, TR_NAME *origin)
1265 {
1266   TR_NAME *cur_orig=NULL;
1267   TR_COMM_MEMB *cur=tr_comm_table_find_idp_memb(ctab, realm, comm);
1268   if (cur==NULL)
1269     return NULL; /* no match */
1270
1271   /* had a match for comm/realm; find origin match */
1272   while (cur!=NULL) {
1273     cur_orig=tr_comm_memb_get_origin(cur);
1274     if (((origin==NULL) && (cur_orig==NULL)) ||
1275         ((origin!=NULL) && (cur_orig!=NULL) && (0==tr_name_cmp(origin, cur_orig))))
1276       return cur; /* found a match */
1277     cur=cur->origin_next;
1278   }
1279   return NULL; /* no match */
1280 }
1281
1282 /* find an rp membership from any origin */
1283 TR_COMM_MEMB *tr_comm_table_find_rp_memb(TR_COMM_TABLE *ctab, TR_NAME *realm, TR_NAME *comm)
1284 {
1285   TR_COMM_MEMB *cur=NULL;
1286   TR_RP_REALM *rp_realm=NULL;
1287
1288   for (cur=ctab->memberships; cur!=NULL; cur=cur->next) {
1289     rp_realm=tr_comm_memb_get_rp_realm(cur);
1290     if (rp_realm==NULL)
1291       continue; /* was not an rp */
1292
1293     if ((0==tr_name_cmp(realm, tr_rp_realm_get_id(rp_realm))) &&
1294         (0==tr_name_cmp(comm, tr_comm_get_id(tr_comm_memb_get_comm(cur))))) {
1295       return cur;
1296     }
1297   }
1298   return NULL;
1299 }
1300
1301 /* find an rp membership from a particular origin */
1302 TR_COMM_MEMB *tr_comm_table_find_rp_memb_origin(TR_COMM_TABLE *ctab, TR_NAME *realm, TR_NAME *comm, TR_NAME *origin)
1303 {
1304   TR_NAME *cur_orig=NULL;
1305   TR_COMM_MEMB *cur=tr_comm_table_find_rp_memb(ctab, realm, comm);
1306   if (cur==NULL)
1307     return NULL; /* no match */
1308
1309   /* had a match for comm/realm; find origin match */
1310   while (cur!=NULL) {
1311     cur_orig=tr_comm_memb_get_origin(cur);
1312     if (((origin==NULL) && (cur_orig==NULL)) ||
1313         ((origin!=NULL) && (cur_orig!=NULL) && (0==tr_name_cmp(origin, cur_orig))))
1314       return cur; /* found a match */
1315     cur=cur->origin_next;
1316   }
1317   return NULL; /* no match */
1318 }
1319
1320 TR_COMM *tr_comm_table_find_comm(TR_COMM_TABLE *ctab, TR_NAME *comm_id)
1321 {
1322   return tr_comm_lookup(ctab->comms, comm_id);
1323 }
1324
1325 void tr_comm_table_add_comm(TR_COMM_TABLE *ctab, TR_COMM *new)
1326 {
1327   tr_comm_add(ctab->comms, new);
1328   if (ctab->comms!=NULL)
1329     talloc_steal(ctab, ctab->comms); /* make sure it's in the right context */
1330 }
1331
1332 void tr_comm_table_remove_comm(TR_COMM_TABLE *ctab, TR_COMM *comm)
1333 {
1334   tr_comm_remove(ctab->comms, comm);
1335 }
1336
1337 TR_RP_REALM *tr_comm_table_find_rp_realm(TR_COMM_TABLE *ctab, TR_NAME *realm_id)
1338 {
1339   return tr_rp_realm_lookup(ctab->rp_realms, realm_id);
1340 }
1341
1342 void tr_comm_table_add_rp_realm(TR_COMM_TABLE *ctab, TR_RP_REALM *new)
1343 {
1344   tr_rp_realm_add(ctab->rp_realms, new);
1345   if (ctab->rp_realms!=NULL)
1346     talloc_steal(ctab, ctab->rp_realms); /* make sure it's in the right context */
1347 }
1348
1349 void tr_comm_table_remove_rp_realm(TR_COMM_TABLE *ctab, TR_RP_REALM *realm)
1350 {
1351   tr_rp_realm_remove(ctab->rp_realms, realm);
1352 }
1353
1354 TR_IDP_REALM *tr_comm_table_find_idp_realm(TR_COMM_TABLE *ctab, TR_NAME *realm_id)
1355 {
1356   return tr_idp_realm_lookup(ctab->idp_realms, realm_id);
1357 }
1358
1359 void tr_comm_table_add_idp_realm(TR_COMM_TABLE *ctab, TR_IDP_REALM *new)
1360 {
1361   tr_idp_realm_add(ctab->idp_realms, new);
1362   if (ctab->idp_realms!=NULL)
1363     talloc_steal(ctab, ctab->idp_realms); /* make sure it's in the right context */
1364 }
1365
1366 void tr_comm_table_remove_idp_realm(TR_COMM_TABLE *ctab, TR_IDP_REALM *realm)
1367 {
1368   tr_idp_realm_remove(ctab->idp_realms, realm);
1369 }
1370
1371
1372 /* how many communities in the table? */
1373 size_t tr_comm_table_size(TR_COMM_TABLE *ctab)
1374 {
1375   size_t count=0;
1376   TR_COMM *this=ctab->comms;
1377   while(this!=NULL) {
1378     this=this->next;
1379     count++;
1380   }
1381   return count;
1382 }
1383
1384 /* clean up unreferenced realms, etc */
1385 void tr_comm_table_sweep(TR_COMM_TABLE *ctab)
1386 {
1387   tr_rp_realm_sweep(ctab->rp_realms);
1388   tr_idp_realm_sweep(ctab->idp_realms);
1389   tr_comm_sweep(ctab->comms);
1390 }
1391
1392
1393 const char *tr_realm_role_to_str(TR_REALM_ROLE role)
1394 {
1395   switch(role) {
1396   case TR_ROLE_IDP:
1397     return "idp";
1398   case TR_ROLE_RP:
1399     return "rp";
1400   default:
1401     return NULL;
1402   }
1403 }
1404
1405 TR_REALM_ROLE tr_realm_role_from_str(const char *s)
1406 {
1407   if (strcmp(s, "idp")==0)
1408     return TR_ROLE_IDP;
1409   if (strcmp(s, "rp")==0)
1410     return TR_ROLE_RP;
1411   return TR_ROLE_UNKNOWN;
1412 }
1413
1414 static char *tr_comm_table_append_provenance(char *ctable_s, json_t *prov)
1415 {
1416   const char *s=NULL;
1417   char *tmp=NULL;
1418   size_t ii=0;
1419
1420   for (ii=0; ii<json_array_size(prov); ii++) {
1421     s=json_string_value(json_array_get(prov, ii));
1422     if (s!=NULL) {
1423       tmp=talloc_asprintf_append(ctable_s, "%s%s", s, ((ii + 1) == json_array_size(prov)) ? "" : ", ");
1424       if (tmp==NULL)
1425         return NULL;
1426       ctable_s=tmp;
1427     }
1428   }
1429   return ctable_s;
1430 }
1431
1432 char *tr_comm_table_to_str(TALLOC_CTX *mem_ctx, TR_COMM_TABLE *ctab)
1433 {
1434   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
1435   char *ctable_s=NULL;
1436   char *tmp=NULL;
1437 #define append_on_success_helper(tab,tmp,expr) if(NULL==((tmp)=(expr))){(tab)=NULL;goto cleanup;}(tab)=(tmp)
1438
1439   TR_COMM_MEMB *p1=NULL; /* for walking the main list */
1440   TR_COMM_MEMB *p2=NULL; /* for walking the same-origin lists */
1441
1442   ctable_s=talloc_asprintf(tmp_ctx, ">> Membership table start <<\n");
1443   if (ctable_s==NULL)
1444     goto cleanup;
1445
1446   for (p1=ctab->memberships; p1!=NULL; p1=p1->next) {
1447     append_on_success_helper(
1448         ctable_s, tmp,
1449         talloc_asprintf_append(ctable_s, "* %s %s/%s\n  %s (%p) - prov: ",
1450                                tr_realm_role_to_str(tr_comm_memb_get_role(p1)),
1451                                tr_comm_memb_get_realm_id(p1)->buf,
1452                                tr_comm_get_id(tr_comm_memb_get_comm(p1))->buf,
1453                                (tr_comm_memb_get_origin(p1)==NULL)?"null origin":(tr_comm_memb_get_origin(p1)->buf),
1454                                p1));
1455
1456     append_on_success_helper(ctable_s, tmp, tr_comm_table_append_provenance(ctable_s, p1->provenance));
1457
1458     append_on_success_helper(ctable_s, tmp, talloc_strdup_append_buffer(ctable_s, "\n"));
1459
1460     for (p2=p1->origin_next; p2!=NULL; p2=p2->origin_next) {
1461       append_on_success_helper(
1462           ctable_s, tmp,
1463           talloc_asprintf_append(ctable_s, "  %s (%p) - prov: ",
1464           (tr_comm_memb_get_origin(p2)==NULL)?"null origin":(tr_comm_memb_get_origin(p2)->buf),
1465               p2));
1466       append_on_success_helper(ctable_s, tmp, tr_comm_table_append_provenance(ctable_s, p2->provenance));
1467       append_on_success_helper(ctable_s, tmp, talloc_strdup_append_buffer(ctable_s, "\n"));
1468     }
1469     append_on_success_helper(ctable_s, tmp, talloc_strdup_append_buffer(ctable_s, "\n"));
1470   }
1471
1472 cleanup:
1473   if (ctable_s!=NULL)
1474     talloc_steal(mem_ctx, ctable_s);
1475
1476   talloc_free(tmp_ctx);
1477   return ctable_s;
1478 }
1479
1480 void tr_comm_table_print(FILE *f, TR_COMM_TABLE *ctab)
1481 {
1482   char *s=tr_comm_table_to_str(NULL, ctab);
1483   if (s!=NULL) {
1484     tr_debug("%s", s);
1485     talloc_free(s);
1486   }
1487 }