52d93f17b6443b9578feee3e213da0ff426e57f9
[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 <jansson.h>
36 #include <talloc.h>
37 #include <sys/time.h>
38
39 #include <tr_rp.h>
40 #include <tr_idp.h>
41 #include <trust_router/tr_name.h>
42 #include <tr_comm.h>
43 #include <tr_debug.h>
44
45
46 /* static prototypes */
47 static TR_NAME *tr_comm_memb_get_realm_id(TR_COMM_MEMB *memb);
48
49
50 static int tr_comm_destructor(void *obj)
51 {
52   TR_COMM *comm=talloc_get_type_abort(obj, TR_COMM);
53   if (comm->id!=NULL)
54     tr_free_name(comm->id);
55   if (comm->owner_realm!=NULL)
56     tr_free_name(comm->owner_realm);
57   if (comm->owner_contact!=NULL)
58     tr_free_name(comm->owner_contact);
59   return 0;
60 }
61
62 TR_COMM *tr_comm_new(TALLOC_CTX *mem_ctx)
63 {
64   TR_COMM *comm=talloc(mem_ctx, TR_COMM);
65   if (comm!=NULL) {
66     comm->next=NULL;
67     comm->id=NULL;
68     comm->type=TR_COMM_UNKNOWN;
69     comm->apcs=NULL;
70     comm->owner_realm=NULL;
71     comm->owner_contact=NULL;
72     comm->expiration_interval=0;
73     comm->refcount=0;
74     talloc_set_destructor((void *)comm, tr_comm_destructor);
75   }
76   return comm;
77 }
78
79 void tr_comm_free(TR_COMM *comm)
80 {
81   talloc_free(comm);
82 }
83
84 void tr_comm_set_id(TR_COMM *comm, TR_NAME *id)
85 {
86   if (comm->id != NULL)
87     tr_free_name(comm->id);
88   comm->id=id;
89 }
90
91 void tr_comm_incref(TR_COMM *comm)
92 {
93   comm->refcount++;
94 }
95
96 void tr_comm_decref(TR_COMM *comm)
97 {
98   if (comm->refcount>0)
99     comm->refcount--;
100 }
101
102 void tr_comm_set_apcs(TR_COMM *comm, TR_APC *apc)
103 {
104   if (comm->apcs!=NULL)
105     tr_apc_free(comm->apcs);
106   comm->apcs=apc;
107   talloc_steal(comm, apc);
108 }
109
110 TR_APC *tr_comm_get_apcs(TR_COMM *comm)
111 {
112   return comm->apcs;
113 }
114
115 TR_NAME *tr_comm_get_id(TR_COMM *comm)
116 {
117   return comm->id;
118 }
119
120 TR_NAME *tr_comm_dup_id(TR_COMM *comm)
121 {
122   return tr_dup_name(comm->id);
123 }
124
125 void tr_comm_set_type(TR_COMM *comm, TR_COMM_TYPE type)
126 {
127   comm->type=type;
128 }
129
130 TR_COMM_TYPE tr_comm_get_type(TR_COMM *comm)
131 {
132   return comm->type;
133 }
134
135 void tr_comm_set_owner_realm(TR_COMM *comm, TR_NAME *realm)
136 {
137   if (comm->owner_realm!=NULL)
138     tr_free_name(comm->owner_realm);
139   comm->owner_realm=realm;
140 }
141
142 TR_NAME *tr_comm_get_owner_realm(TR_COMM *comm)
143 {
144   return comm->owner_realm;
145 }
146
147 TR_NAME *tr_comm_dup_owner_realm(TR_COMM *comm)
148 {
149   return tr_dup_name(comm->owner_realm);
150 }
151
152 void tr_comm_set_owner_contact(TR_COMM *comm, TR_NAME *contact)
153 {
154   if (comm->owner_contact != NULL)
155     tr_free_name(comm->owner_contact);
156   comm->owner_contact=contact;
157 }
158
159 TR_NAME *tr_comm_get_owner_contact(TR_COMM *comm)
160 {
161   return comm->owner_contact;
162 }
163
164 TR_NAME *tr_comm_dup_owner_contact(TR_COMM *comm)
165 {
166   return tr_dup_name(comm->owner_contact);
167 }
168
169 unsigned int tr_comm_get_refcount(TR_COMM *comm)
170 {
171   return comm->refcount;
172 }
173
174 /* add to the table if it's a new membership or has a shorter
175  * provenance list than our existing membership */
176 static void tr_comm_add_if_shorter(TR_COMM_TABLE *ctab, TR_COMM_MEMB *existing, TR_COMM_MEMB *newmemb)
177 {
178   if (existing==NULL) {
179     /* not in the table */
180     tr_comm_table_add_memb(ctab, newmemb);
181   } else {
182     /* Had an entry. Replace if we have shorter provenance. */
183     if (tr_comm_memb_provenance_len(newmemb) < tr_comm_memb_provenance_len(existing)) {
184       tr_comm_table_remove_memb(ctab, existing);
185       tr_comm_memb_free(existing);
186       tr_comm_table_add_memb(ctab, newmemb);
187     } 
188   }
189 }
190
191 /* does not take responsibility for freeing IDP realm */
192 void tr_comm_add_idp_realm(TR_COMM_TABLE *ctab,
193                            TR_COMM *comm,
194                            TR_IDP_REALM *realm,
195                            json_t *provenance,
196                            struct timespec *expiry)
197 {
198   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
199   TR_COMM_MEMB *newmemb=tr_comm_memb_new(tmp_ctx);
200   TR_COMM_MEMB *existing=NULL;
201
202   if (newmemb==NULL) {
203     tr_err("tr_comm_add_idp_realm: unable to allocate new membership record.");
204     talloc_free(tmp_ctx);
205     return;
206   }
207
208   tr_comm_memb_set_idp_realm(newmemb, realm);
209   tr_comm_memb_set_comm(newmemb, comm);
210   tr_comm_memb_set_provenance(newmemb, provenance);
211   tr_comm_memb_set_expiry(newmemb, expiry);
212
213   existing=tr_comm_table_find_idp_memb_origin(ctab,
214                                               tr_idp_realm_get_id(realm),
215                                               tr_comm_get_id(comm),
216                                               tr_comm_memb_get_origin(newmemb));
217   tr_comm_add_if_shorter(ctab, existing, newmemb); /* takes newmemb out of tmp_ctx if needed */
218   talloc_free(tmp_ctx);
219 }
220
221 /* does not take responsibility for freeing RP realm */
222 void tr_comm_add_rp_realm(TR_COMM_TABLE *ctab,
223                           TR_COMM *comm,
224                           TR_RP_REALM *realm,
225                           json_t *provenance,
226                           struct timespec *expiry)
227 {
228   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
229   TR_COMM_MEMB *newmemb=tr_comm_memb_new(tmp_ctx);
230   TR_COMM_MEMB *existing=NULL;
231
232   if (newmemb==NULL) {
233     tr_err("tr_comm_add_idp_realm: unable to allocate new membership record.");
234     talloc_free(tmp_ctx);
235     return;
236   }
237
238   tr_comm_memb_set_rp_realm(newmemb, realm);
239   tr_comm_memb_set_comm(newmemb, comm);
240   tr_comm_memb_set_provenance(newmemb, provenance);
241   tr_comm_memb_set_expiry(newmemb, expiry);
242
243   existing=tr_comm_table_find_rp_memb_origin(ctab,
244                                              tr_rp_realm_get_id(realm),
245                                              tr_comm_get_id(comm),
246                                              tr_comm_memb_get_origin(newmemb));
247   tr_comm_add_if_shorter(ctab, existing, newmemb); /* takes newmemb out of tmp_ctx if needed */
248   talloc_free(tmp_ctx);
249 }
250
251 static TR_COMM *tr_comm_tail(TR_COMM *comm)
252 {
253   if (comm==NULL)
254     return comm;
255
256   while (comm->next!=NULL)
257     comm=comm->next;
258   return comm;
259 }
260
261 /* All list members are in the talloc context of the head.
262  * This will require careful thought if entries are ever removed
263  * Call like comms=tr_comm_add_func(comms, new_comm);
264  * or just use the tr_comm_add(comms, new) macro. */
265 #define tr_comm_add(comms, new) ((comms)=tr_comm_add_func((comms), (new)))
266 static TR_COMM *tr_comm_add_func(TR_COMM *comms, TR_COMM *new)
267 {
268   if (comms==NULL)
269     comms=new;
270   else {
271     tr_comm_tail(comms)->next=new;
272     while(new!=NULL) {
273       talloc_steal(comms, new);
274       new=new->next;
275     }
276   }
277   return comms;
278 }
279
280 /* Guarantees comm is not in the list, not an error if it was't there.
281  * Does not free the removed element, nor change its talloc context. */
282 #define tr_comm_remove(comms, c) ((comms)=tr_comm_remove_func((comms), (c)))
283 static TR_COMM *tr_comm_remove_func(TR_COMM *comms, TR_COMM *remove)
284 {
285   TALLOC_CTX *list_ctx=talloc_parent(comms); /* in case we need to remove the head */
286   TR_COMM *this=NULL;
287
288   if (comms==NULL)
289     return NULL;
290
291   if (comms==remove) {
292     /* if we're removing the head, put the next element (if present) into the context
293      * the list head was in. */
294     comms=comms->next;
295     if (comms!=NULL) {
296       talloc_steal(list_ctx, comms->next);
297       /* now put all the other elements in the context of the list head */
298       for (this=comms->next; this!=NULL; this=this->next)
299         talloc_steal(comms, this);
300     }
301   } else {
302     /* not removing the head; no need to play with contexts */
303     for (this=comms; this->next!=NULL; this=this->next) {
304       if (this->next==remove)
305         this->next=remove->next;
306     }
307   }
308   return comms;
309 }
310
311 TR_IDP_REALM *tr_comm_find_idp(TR_COMM_TABLE *ctab, TR_COMM *comm, TR_NAME *idp_realm)
312 {
313   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
314   TR_COMM_ITER *iter=NULL;
315   TR_IDP_REALM *this_idp=NULL;
316
317   if ((NULL==ctab) || (NULL==comm) || (NULL==idp_realm)) {
318     talloc_free(tmp_ctx);
319     return NULL;
320   }
321
322   iter=tr_comm_iter_new(tmp_ctx);
323   for (this_idp=tr_idp_realm_iter_first(iter, ctab, tr_comm_get_id(comm));
324        this_idp!=NULL;
325        this_idp=tr_idp_realm_iter_next(iter)) {
326     if (0==tr_name_cmp(idp_realm, tr_idp_realm_get_id(this_idp))) {
327       tr_debug("tr_comm_find_idp: Found IdP %s in community %s.", idp_realm->buf, tr_comm_get_id(comm)->buf);
328       talloc_free(tmp_ctx);
329       return this_idp;
330     }
331   }
332   tr_debug("tr_comm_find_idp: Unable to find IdP %s in community %s.", idp_realm->buf, tr_comm_get_id(comm)->buf);
333   talloc_free(tmp_ctx);
334   return NULL;
335 }
336
337 TR_RP_REALM *tr_comm_find_rp (TR_COMM_TABLE *ctab, TR_COMM *comm, TR_NAME *rp_realm)
338 {
339   TALLOC_CTX *tmp_ctx=talloc_new(NULL);
340   TR_COMM_ITER *iter=NULL;
341   TR_RP_REALM *this_rp=NULL;
342
343   if ((NULL==ctab) || (NULL==comm) || (NULL==rp_realm)) {
344     talloc_free(tmp_ctx);
345     return NULL;
346   }
347
348   iter=tr_comm_iter_new(tmp_ctx);
349   for (this_rp=tr_rp_realm_iter_first(iter, ctab, tr_comm_get_id(comm));
350        this_rp!=NULL;
351        this_rp=tr_rp_realm_iter_next(iter)) {
352     if (0==tr_name_cmp(rp_realm, tr_rp_realm_get_id(this_rp))) {
353       tr_debug("tr_comm_find_rp: Found RP %s in community %s.", rp_realm->buf, tr_comm_get_id(comm)->buf);
354       talloc_free(tmp_ctx);
355       return this_rp;
356     }
357   }
358   tr_debug("tr_comm_find_rp: Unable to find RP %s in community %s.", rp_realm->buf, tr_comm_get_id(comm)->buf);
359   talloc_free(tmp_ctx);
360   return NULL;
361 }
362
363 static TR_COMM *tr_comm_lookup(TR_COMM *comms, TR_NAME *comm_name) 
364 {
365   TR_COMM *cfg_comm = NULL;
366
367   for (cfg_comm = comms; NULL != cfg_comm; cfg_comm = cfg_comm->next) {
368     if (0==tr_name_cmp(cfg_comm->id, comm_name))
369       return cfg_comm;
370   }
371   return NULL;
372 }
373
374 TR_COMM_ITER *tr_comm_iter_new(TALLOC_CTX *mem_ctx)
375 {
376   TR_COMM_ITER *iter=talloc(mem_ctx, TR_COMM_ITER);
377   if (iter!=NULL) {
378     iter->cur_comm=NULL;
379     iter->cur_memb=NULL;
380     iter->match=NULL;
381     iter->realm=NULL;
382   }
383   return iter;
384 }
385
386 void tr_comm_iter_free(TR_COMM_ITER *iter)
387 {
388   talloc_free(iter);
389 }
390
391
392 TR_COMM *tr_comm_iter_first(TR_COMM_ITER *iter, TR_COMM_TABLE *ctab, TR_NAME *realm)
393 {
394   iter->match=realm;
395
396   /* find memberships for this realm */
397   for (iter->cur_memb=ctab->memberships;
398        iter->cur_memb!=NULL;
399        iter->cur_memb=iter->cur_memb->next) {
400     if (0==tr_name_cmp(iter->match, tr_comm_memb_get_realm_id(iter->cur_memb)))
401       return tr_comm_memb_get_comm(iter->cur_memb);
402   }
403   return NULL;
404 }
405
406 TR_COMM *tr_comm_iter_next(TR_COMM_ITER *iter)
407 {
408   for (iter->cur_memb=iter->cur_memb->next;
409        iter->cur_memb!=NULL;
410        iter->cur_memb=iter->cur_memb->next) {
411     if (0==tr_name_cmp(iter->match, tr_comm_memb_get_realm_id(iter->cur_memb)))
412       return tr_comm_memb_get_comm(iter->cur_memb);
413   }
414   return NULL;
415 }
416
417 /* iterate only over RPs */
418 TR_COMM *tr_comm_iter_first_rp(TR_COMM_ITER *iter, TR_COMM_TABLE *ctab, TR_NAME *realm)
419 {
420   iter->match=realm;
421
422   /* find memberships for this realm */
423   for (iter->cur_memb=ctab->memberships;
424        iter->cur_memb!=NULL;
425        iter->cur_memb=iter->cur_memb->next) {
426     if ((tr_comm_memb_get_rp_realm(iter->cur_memb)!=NULL) &&
427         (0==tr_name_cmp(iter->match, tr_comm_memb_get_realm_id(iter->cur_memb))))
428       return tr_comm_memb_get_comm(iter->cur_memb);
429   }
430   return NULL;
431 }
432
433 TR_COMM *tr_comm_iter_next_rp(TR_COMM_ITER *iter)
434 {
435   for (iter->cur_memb=iter->cur_memb->next;
436        iter->cur_memb!=NULL;
437        iter->cur_memb=iter->cur_memb->next) {
438     if ((tr_comm_memb_get_rp_realm(iter->cur_memb)!=NULL) &&
439         (0==tr_name_cmp(iter->match, tr_comm_memb_get_realm_id(iter->cur_memb))))
440       return tr_comm_memb_get_comm(iter->cur_memb);
441   }
442   return NULL;
443 }
444
445 /* iterate only over IDPs */
446 TR_COMM *tr_comm_iter_first_idp(TR_COMM_ITER *iter, TR_COMM_TABLE *ctab, TR_NAME *realm)
447 {
448   iter->match=realm;
449
450   /* find memberships for this realm */
451   for (iter->cur_memb=ctab->memberships;
452        iter->cur_memb!=NULL;
453        iter->cur_memb=iter->cur_memb->next) {
454     if ((tr_comm_memb_get_idp_realm(iter->cur_memb)!=NULL) &&
455         (0==tr_name_cmp(iter->match, tr_comm_memb_get_realm_id(iter->cur_memb))))
456       return tr_comm_memb_get_comm(iter->cur_memb);
457   }
458   return NULL;
459 }
460
461 TR_COMM *tr_comm_iter_next_idp(TR_COMM_ITER *iter)
462 {
463   for (iter->cur_memb=iter->cur_memb->next;
464        iter->cur_memb!=NULL;
465        iter->cur_memb=iter->cur_memb->next) {
466     if ((tr_comm_memb_get_idp_realm(iter->cur_memb)!=NULL) &&
467         (0==tr_name_cmp(iter->match, tr_comm_memb_get_realm_id(iter->cur_memb))))
468       return tr_comm_memb_get_comm(iter->cur_memb);
469   }
470   return NULL;
471 }
472
473 static TR_REALM *tr_realm_new(TALLOC_CTX *mem_ctx)
474 {
475   TR_REALM *realm=talloc(mem_ctx, TR_REALM);
476   if (realm!=NULL) {
477     realm->role=TR_ROLE_UNKNOWN;
478     realm->rp=NULL;
479     realm->idp=NULL;
480   }
481   return realm;
482 }
483
484 static void tr_realm_free(TR_REALM *realm)
485 {
486   talloc_free(realm);
487 }
488
489 static void tr_realm_set_rp(TR_REALM *realm, TR_RP_REALM *rp)
490 {
491   if (realm->idp!=NULL)
492     realm->idp=NULL;
493   realm->role=TR_ROLE_RP;
494   realm->rp=rp;
495 }
496
497 static void tr_realm_set_idp(TR_REALM *realm, TR_IDP_REALM *idp)
498 {
499   if (realm->rp!=NULL)
500     realm->rp=NULL;
501   realm->role=TR_ROLE_IDP;
502   realm->idp=idp;
503 }
504
505 TR_NAME *tr_realm_get_id(TR_REALM *realm)
506 {
507   switch (realm->role) {
508   case TR_ROLE_RP:
509     return tr_rp_realm_get_id(realm->rp);
510   case TR_ROLE_IDP:
511     return tr_idp_realm_get_id(realm->idp);
512   default:
513     break;
514   }
515   return NULL;
516 }
517
518 TR_NAME *tr_realm_dup_id(TR_REALM *realm)
519 {
520   return tr_dup_name(tr_realm_get_id(realm));
521 }
522
523 /* Iterate over either sort of realm. Do not free the TR_REALM returned. It becomes
524  * undefined/invalid after the next operation affecting the iterator. */
525 TR_REALM *tr_realm_iter_first(TR_COMM_ITER *iter, TR_COMM_TABLE *ctab, TR_NAME *comm)
526 {
527   iter->match=comm;
528   if (iter->realm==NULL)
529     iter->realm=tr_realm_new(iter);
530   if (iter->realm==NULL)
531     return NULL;
532
533   /* find memberships for this comm */
534   for (iter->cur_memb=ctab->memberships;
535        iter->cur_memb!=NULL;
536        iter->cur_memb=iter->cur_memb->next) {
537     if (0==tr_name_cmp(iter->match,
538                        tr_comm_get_id(tr_comm_memb_get_comm(iter->cur_memb)))) {
539       /* found a match, determine whether it's an rp realm or an idp realm */
540       if (tr_comm_memb_get_rp_realm(iter->cur_memb)!=NULL)
541         tr_realm_set_rp(iter->realm, tr_comm_memb_get_rp_realm(iter->cur_memb));
542       else if (tr_comm_memb_get_idp_realm(iter->cur_memb)!=NULL)
543         tr_realm_set_idp(iter->realm, tr_comm_memb_get_idp_realm(iter->cur_memb));
544       else {
545         if (iter->realm!=NULL)
546           tr_realm_free(iter->realm);
547         iter->realm=NULL;
548       }
549       return iter->realm;
550     }
551   }
552   if (iter->realm!=NULL)
553     tr_realm_free(iter->realm);
554   iter->realm=NULL;
555   return NULL;
556 }
557
558 TR_REALM *tr_realm_iter_next(TR_COMM_ITER *iter)
559 {
560   if (iter->realm==NULL)
561     return NULL;
562
563   /* find memberships for this comm */
564   for (iter->cur_memb=iter->cur_memb->next;
565        iter->cur_memb!=NULL;
566        iter->cur_memb=iter->cur_memb->next) {
567     if (0==tr_name_cmp(iter->match,
568                        tr_comm_get_id(tr_comm_memb_get_comm(iter->cur_memb)))) {
569       /* found a match, determine whether it's an rp realm or an idp realm */
570       if (tr_comm_memb_get_rp_realm(iter->cur_memb)!=NULL)
571         tr_realm_set_rp(iter->realm, tr_comm_memb_get_rp_realm(iter->cur_memb));
572       else if (tr_comm_memb_get_idp_realm(iter->cur_memb)!=NULL)
573         tr_realm_set_idp(iter->realm, tr_comm_memb_get_idp_realm(iter->cur_memb));
574       else {
575         if (iter->realm!=NULL)
576           tr_realm_free(iter->realm);
577         iter->realm=NULL;
578       }
579       return iter->realm;
580     }
581   }
582   if (iter->realm!=NULL)
583     tr_realm_free(iter->realm);
584   iter->realm=NULL;
585   return NULL;
586 }
587
588 TR_RP_REALM *tr_rp_realm_iter_first(TR_COMM_ITER *iter, TR_COMM_TABLE *ctab, TR_NAME *comm)
589 {
590   iter->match=comm;
591
592   /* find memberships for this comm */
593   for (iter->cur_memb=ctab->memberships;
594        iter->cur_memb!=NULL;
595        iter->cur_memb=iter->cur_memb->next) {
596     if ((tr_comm_memb_get_rp_realm(iter->cur_memb)!=NULL) &&
597         (0==tr_name_cmp(iter->match, tr_comm_get_id(tr_comm_memb_get_comm(iter->cur_memb)))))
598       return tr_comm_memb_get_rp_realm(iter->cur_memb);
599   }
600   return NULL;
601 }
602
603 TR_RP_REALM *tr_rp_realm_iter_next(TR_COMM_ITER *iter)
604 {
605   for (iter->cur_memb=iter->cur_memb->next;
606        iter->cur_memb!=NULL;
607        iter->cur_memb=iter->cur_memb->next) {
608     if ((tr_comm_memb_get_rp_realm(iter->cur_memb)!=NULL) &&
609         (0==tr_name_cmp(iter->match, tr_comm_get_id(tr_comm_memb_get_comm(iter->cur_memb)))))
610       return tr_comm_memb_get_rp_realm(iter->cur_memb);
611   }
612   return NULL;
613 }
614
615 TR_IDP_REALM *tr_idp_realm_iter_first(TR_COMM_ITER *iter, TR_COMM_TABLE *ctab, TR_NAME *comm)
616 {
617   iter->match=comm;
618
619   /* find memberships for this comm */
620   for (iter->cur_memb=ctab->memberships;
621        iter->cur_memb!=NULL;
622        iter->cur_memb=iter->cur_memb->next) {
623     if ((tr_comm_memb_get_idp_realm(iter->cur_memb)!=NULL) &&
624         (0==tr_name_cmp(iter->match, tr_comm_get_id(tr_comm_memb_get_comm(iter->cur_memb)))))
625       return tr_comm_memb_get_idp_realm(iter->cur_memb);
626   }
627   return NULL;
628 }
629
630 TR_IDP_REALM *tr_idp_realm_iter_next(TR_COMM_ITER *iter)
631 {
632   for (iter->cur_memb=iter->cur_memb->next;
633        iter->cur_memb!=NULL;
634        iter->cur_memb=iter->cur_memb->next) {
635     if ((tr_comm_memb_get_idp_realm(iter->cur_memb)!=NULL) &&
636         (0==tr_name_cmp(iter->match, tr_comm_get_id(tr_comm_memb_get_comm(iter->cur_memb)))))
637       return tr_comm_memb_get_idp_realm(iter->cur_memb);
638   }
639   return NULL;
640 }
641
642 /* iterators for all communities in a table */
643 TR_COMM *tr_comm_table_iter_first(TR_COMM_ITER *iter, TR_COMM_TABLE *ctab)
644 {
645   iter->cur_comm=ctab->comms;
646   return iter->cur_comm;
647 }
648
649 TR_COMM *tr_comm_table_iter_next(TR_COMM_ITER *iter)
650 {
651   return iter->cur_comm=iter->cur_comm->next;
652 }
653
654 const char *tr_comm_type_to_str(TR_COMM_TYPE type)
655 {
656   const char *s=NULL;
657   switch(type) {
658   case TR_COMM_UNKNOWN:
659     s="unknown";
660     break;
661   case TR_COMM_APC:
662     s="apc";
663     break;
664   case TR_COMM_COI:
665     s="coi";
666     break;
667   default:
668     s="invalid";
669   }
670   return s;
671 }
672
673 TR_COMM_MEMB *tr_comm_memb_iter_first(TR_COMM_ITER *iter, TR_COMM_MEMB *memb)
674 {
675   iter->cur_memb=memb;
676   return iter->cur_memb;
677 }
678
679 TR_COMM_MEMB *tr_comm_memb_iter_next(TR_COMM_ITER *iter)
680 {
681   if (iter->cur_memb!=NULL)
682     iter->cur_memb=iter->cur_memb->origin_next;
683   return iter->cur_memb;
684 }
685
686 TR_COMM_TYPE tr_comm_type_from_str(const char *s)
687 {
688   if (strcmp(s, "apc")==0)
689     return TR_COMM_APC;
690   if (strcmp(s,"coi")==0)
691     return TR_COMM_COI;
692   return TR_COMM_UNKNOWN;
693 }
694
695
696 static int tr_comm_memb_destructor(void *obj)
697 {
698   TR_COMM_MEMB *memb=talloc_get_type_abort(obj, TR_COMM_MEMB);
699   if (memb->origin!=NULL)
700     tr_free_name(memb->origin);
701
702   if (memb->rp!=NULL)
703     tr_rp_realm_decref(memb->rp);
704   if (memb->idp!=NULL)
705     tr_idp_realm_decref(memb->idp);
706   if (memb->comm!=NULL)
707     tr_comm_decref(memb->comm);
708   if (memb->provenance!=NULL)
709     json_decref(memb->provenance);
710   return 0;
711 }
712
713 TR_COMM_MEMB *tr_comm_memb_new(TALLOC_CTX *mem_ctx)
714 {
715   TR_COMM_MEMB *memb=talloc(mem_ctx, TR_COMM_MEMB);
716   if (memb!=NULL) {
717     memb->next=NULL;
718     memb->origin_next=NULL;
719     memb->idp=NULL;
720     memb->rp=NULL;
721     memb->comm=NULL;
722     memb->origin=NULL;
723     memb->provenance=NULL;
724     memb->interval=0;
725     memb->triggered=0;
726     memb->expiry=talloc(memb, struct timespec);
727     if (memb->expiry==NULL) {
728       talloc_free(memb);
729       return NULL;
730     }
731     *(memb->expiry)=(struct timespec){0,0};
732     talloc_set_destructor(memb, tr_comm_memb_destructor);
733   }
734   return memb;
735 }
736
737 void tr_comm_memb_free(TR_COMM_MEMB *memb)
738 {
739   talloc_free(memb);
740 }
741
742 TR_REALM_ROLE tr_comm_memb_get_role(TR_COMM_MEMB *memb)
743 {
744   if (memb->rp!=NULL)
745     return TR_ROLE_RP;
746   if (memb->idp!=NULL)
747     return TR_ROLE_IDP;
748   return TR_ROLE_UNKNOWN;
749 }
750
751 void tr_comm_memb_set_rp_realm(TR_COMM_MEMB *memb, TR_RP_REALM *realm)
752 {
753   if (memb->idp!=NULL) {
754     tr_idp_realm_decref(memb->idp);
755     memb->idp=NULL;
756   }
757   if (memb->rp!=NULL)
758     tr_rp_realm_decref(memb->rp);
759
760
761   memb->rp=realm;
762   tr_rp_realm_incref(realm);
763 }
764
765 TR_RP_REALM *tr_comm_memb_get_rp_realm(TR_COMM_MEMB *memb)
766 {
767   return memb->rp;
768 }
769
770 void tr_comm_memb_set_idp_realm(TR_COMM_MEMB *memb, TR_IDP_REALM *realm)
771 {
772   if (memb->rp!=NULL) {
773     tr_rp_realm_decref(memb->rp);
774     memb->rp=NULL;
775   }
776   if (memb->idp!=NULL)
777     tr_idp_realm_decref(memb->idp);
778
779   memb->idp=realm;
780   tr_idp_realm_incref(realm);
781 }
782
783 TR_IDP_REALM *tr_comm_memb_get_idp_realm(TR_COMM_MEMB *memb)
784 {
785   return memb->idp;
786 }
787
788 void tr_comm_memb_set_comm(TR_COMM_MEMB *memb, TR_COMM *comm)
789 {
790   if (memb->comm!=NULL)
791     tr_comm_decref(memb->comm);
792   memb->comm=comm;
793   tr_comm_incref(comm);
794 }
795
796 TR_COMM *tr_comm_memb_get_comm(TR_COMM_MEMB *memb)
797 {
798   return memb->comm;
799 }
800
801 static void tr_comm_memb_set_origin(TR_COMM_MEMB *memb, TR_NAME *origin)
802 {
803   if (memb->origin!=NULL)
804     tr_free_name(memb->origin);
805   memb->origin=origin;
806 }
807
808 TR_NAME *tr_comm_memb_get_origin(TR_COMM_MEMB *memb)
809 {
810   return memb->origin;
811 }
812
813 TR_NAME *tr_comm_memb_dup_origin(TR_COMM_MEMB *memb)
814 {
815   if (memb->origin!=NULL)
816     return tr_dup_name(memb->origin);
817   return NULL;
818 }
819
820 json_t *tr_comm_memb_get_provenance(TR_COMM_MEMB *memb)
821 {
822   if (memb!=NULL)
823     return memb->provenance;
824   return NULL;
825 }
826
827 void tr_comm_memb_set_provenance(TR_COMM_MEMB *memb, json_t *prov)
828 {
829   if (memb->provenance)
830     json_decref(memb->provenance);
831
832   memb->provenance=prov;
833   if (prov!=NULL) {
834     json_incref(prov);
835
836     /* next line sets origin to NULL if provenance is empty because jansson
837      * routines return NULL on error */
838     memb->origin=tr_new_name(json_string_value(json_array_get(prov, 0)));
839   } else {
840     tr_comm_memb_set_origin(memb, NULL);
841   }
842 }
843
844 void tr_comm_memb_add_to_provenance(TR_COMM_MEMB *memb, TR_NAME *hop)
845 {
846   if (memb->provenance==NULL) {
847     memb->provenance=json_array();
848     if (memb->provenance==NULL) {
849       tr_err("tr_comm_memb_add_to_provenance: unable to allocate provenance list.");
850       return;
851     }
852     /* this is the first entry in the provenance, so it is the origin */
853     tr_comm_memb_set_origin(memb,tr_dup_name(hop));
854     if (memb->origin==NULL) {
855       tr_err("tr_comm_memb_add_to_provenance: unable to allocate origin.");
856       json_decref(memb->provenance);
857       memb->provenance=NULL;
858       return;
859     }
860   }
861   if (0!=json_array_append_new(memb->provenance, tr_name_to_json_string(hop)))
862     tr_err("tr_comm_memb_add_to_provenance: unable to extend provenance list.");
863 }
864
865 size_t tr_comm_memb_provenance_len(TR_COMM_MEMB *memb)
866 {
867   if (memb->provenance==NULL)
868     return 0;
869   return json_array_size(memb->provenance);
870 }
871
872 void tr_comm_memb_set_interval(TR_COMM_MEMB *memb, unsigned int interval)
873 {
874   memb->interval=interval;
875 }
876
877 unsigned int tr_comm_memb_get_interval(TR_COMM_MEMB *memb)
878 {
879   return memb->interval;
880 }
881
882 void tr_comm_memb_set_expiry(TR_COMM_MEMB *memb, struct timespec *time)
883 {
884   if (time==NULL)
885     *(memb->expiry)=(struct timespec){0,0};
886   else {
887     memb->expiry->tv_sec=time->tv_sec;
888     memb->expiry->tv_nsec=time->tv_nsec;
889   }
890 }
891
892 struct timespec *tr_comm_memb_get_expiry(TR_COMM_MEMB *memb)
893 {
894   return memb->expiry;
895 }
896
897 int tr_comm_memb_is_expired(TR_COMM_MEMB *memb, struct timespec *curtime)
898 {
899   return ((curtime->tv_sec > memb->expiry->tv_sec)
900          || ((curtime->tv_sec == memb->expiry->tv_sec)
901             &&(curtime->tv_nsec >= memb->expiry->tv_nsec)));
902 }
903
904 void tr_comm_set_triggered(TR_COMM_MEMB *memb, int trig)
905 {
906   memb->triggered=trig;
907 }
908
909 int tr_comm_is_triggered(TR_COMM_MEMB *memb)
910 {
911   return memb->triggered;
912 }
913
914
915 TR_COMM_TABLE *tr_comm_table_new(TALLOC_CTX *mem_ctx)
916 {
917   TR_COMM_TABLE *ctab=talloc(mem_ctx, TR_COMM_TABLE);
918   if (ctab!=NULL) {
919     ctab->comms=NULL;
920     ctab->memberships=NULL;
921     ctab->idp_realms=NULL;
922     ctab->rp_realms=NULL;
923   }
924   return ctab;
925 }
926
927 void tr_comm_table_free(TR_COMM_TABLE *ctab)
928 {
929   talloc_free(ctab);
930 }
931
932 static TR_REALM_ROLE tr_comm_memb_role(TR_COMM_MEMB *memb)
933 {
934   if (memb->rp!=NULL)
935     return TR_ROLE_RP;
936   if (memb->idp!=NULL)
937     return TR_ROLE_IDP;
938
939   return TR_ROLE_UNKNOWN;
940 }
941
942 void tr_comm_table_add_memb(TR_COMM_TABLE *ctab, TR_COMM_MEMB *new)
943 {
944   TR_COMM_MEMB *cur=NULL;
945
946   /* TODO: validate the member (must have valid comm and realm) */
947
948   /* handle the empty list case */
949   if (ctab->memberships==NULL) {
950     ctab->memberships=new;
951     talloc_steal(ctab, new);
952     return;
953   }
954
955   /* The list was not empty. See if we already have a membership for this realm/comm/role */
956   switch (tr_comm_memb_role(new)) {
957   case TR_ROLE_RP:
958     cur=tr_comm_table_find_rp_memb(ctab,
959                                    tr_rp_realm_get_id(tr_comm_memb_get_rp_realm(new)),
960                                    tr_comm_get_id(tr_comm_memb_get_comm(new)));
961     break;
962   case TR_ROLE_IDP:
963     cur=tr_comm_table_find_idp_memb(ctab,
964                                     tr_idp_realm_get_id(tr_comm_memb_get_idp_realm(new)),
965                                     tr_comm_get_id(tr_comm_memb_get_comm(new)));
966     break;
967   case TR_ROLE_UNKNOWN:
968   default:
969     tr_err("tr_comm_table_add_memb: realm with unknown role added.");
970     cur=NULL;
971   }
972
973   if (cur==NULL) {
974     /* no entry for this realm/comm/role, tack it on the end */
975     for (cur=ctab->memberships; cur->next!=NULL; cur=cur->next) { }
976     cur->next=new;
977   } else {
978     /* Found an entry. Add to the end of its same-origin list. */
979     while (cur->origin_next!=NULL) {
980       cur=cur->origin_next;
981     }
982     cur->origin_next=new;
983   }
984
985   talloc_steal(ctab, new);
986 }
987
988 /* Remove memb from ctab. Do not free anything. Do nothing if memb not in ctab. */
989 void tr_comm_table_remove_memb(TR_COMM_TABLE *ctab, TR_COMM_MEMB *memb)
990 {
991   TR_COMM_MEMB *cur=NULL; /* for walking the main list */
992   TR_COMM_MEMB *orig_cur=NULL; /* for walking the origin list */
993
994   if ((memb==NULL) || (ctab->memberships==NULL))
995     return;
996
997   /* see if it's the first member */
998   if (ctab->memberships==memb) {
999     if (memb->origin_next!=NULL) {
1000       memb->origin_next->next=ctab->memberships->next;
1001       ctab->memberships=memb->origin_next;
1002     } else
1003       ctab->memberships=memb->next;
1004
1005     return;
1006   }
1007
1008   /* see if it's in first member's origin list */
1009   for (orig_cur=ctab->memberships->origin_next;
1010        orig_cur!=NULL;
1011        orig_cur=ctab->memberships->origin_next) {
1012     if (orig_cur==memb) {
1013       orig_cur->origin_next=memb->origin_next;
1014       return;
1015     }
1016   }
1017
1018   /* now we have to walk the rest of the tree */
1019   for (cur=ctab->memberships; cur->next!=NULL; cur=cur->next) {
1020     if (cur->next==memb) {
1021       /* it matched an entry on the main list */
1022       if (memb->origin_next!=NULL) {
1023         /* replace the entry in the main list with the next element on the origin list */
1024         memb->origin_next->next=memb->next;
1025         cur->next=memb->origin_next;
1026       } else
1027         cur->next=memb->next; /* no origin list, just drop memb */
1028       return;
1029     } else {
1030       /* it was not on the main list, walk the origin list */
1031       for (orig_cur=cur; orig_cur->next!=NULL; orig_cur=orig_cur->next) {
1032         if (orig_cur->next==memb) {
1033           orig_cur->next=memb->next;
1034           return; /* just drop the element from the origin list */
1035         }
1036       }
1037     }
1038   }
1039 }
1040
1041 static TR_NAME *tr_comm_memb_get_realm_id(TR_COMM_MEMB *memb)
1042 {
1043   if (memb->rp!=NULL)
1044     return tr_rp_realm_get_id(memb->rp);
1045   else
1046     return tr_idp_realm_get_id(memb->idp);
1047 }
1048
1049 /* find a membership from any origin */
1050 TR_COMM_MEMB *tr_comm_table_find_memb(TR_COMM_TABLE *ctab, TR_NAME *realm, TR_NAME *comm)
1051 {
1052   TR_COMM_MEMB *cur=NULL;
1053   TR_NAME *cur_realm_name=NULL;
1054
1055   for (cur=ctab->memberships; cur!=NULL; cur=cur->next) {
1056     cur_realm_name=tr_comm_memb_get_realm_id(cur);
1057     if (cur_realm_name==NULL) {
1058       tr_warning("tr_comm_table_find: encountered realm with no name.");
1059       continue;
1060     }
1061     if ((0==tr_name_cmp(realm, cur_realm_name)) &&
1062         (0==tr_name_cmp(comm, tr_comm_get_id(tr_comm_memb_get_comm(cur))))) {
1063       return cur;
1064     }
1065   }
1066   return NULL;
1067 }
1068
1069 /* find a membership from a particular origin */
1070 TR_COMM_MEMB *tr_comm_table_find_memb_origin(TR_COMM_TABLE *ctab, TR_NAME *realm, TR_NAME *comm, TR_NAME *origin)
1071 {
1072   TR_NAME *cur_orig=NULL;
1073   TR_COMM_MEMB *cur=tr_comm_table_find_memb(ctab, realm, comm);
1074   if (cur==NULL)
1075     return NULL; /* no match */
1076
1077   /* had a match for comm/realm; find origin match */
1078   while (cur!=NULL) {
1079     if (((origin==NULL) && (cur_orig==NULL)) ||
1080         ((origin!=NULL) && (cur_orig!=NULL) && (0==tr_name_cmp(origin, cur_orig))))
1081       return cur; /* found a match */
1082     cur=cur->origin_next;
1083   }
1084   return NULL; /* no match */
1085 }
1086
1087
1088 /* find an idp membership regardless of its origin */
1089 TR_COMM_MEMB *tr_comm_table_find_idp_memb(TR_COMM_TABLE *ctab, TR_NAME *realm, TR_NAME *comm)
1090 {
1091   TR_COMM_MEMB *cur=NULL;
1092   TR_IDP_REALM *idp_realm=NULL;
1093
1094   for (cur=ctab->memberships; cur!=NULL; cur=cur->next) {
1095     idp_realm=tr_comm_memb_get_idp_realm(cur);
1096     if (idp_realm==NULL)
1097       continue; /* was not an idp */
1098
1099     if ((0==tr_name_cmp(realm, idp_realm->realm_id)) &&
1100         (0==tr_name_cmp(comm, tr_comm_get_id(tr_comm_memb_get_comm(cur))))) {
1101       return cur;
1102     }
1103   }
1104   return NULL;
1105 }
1106
1107 /* find an idp membership from a particular origin */
1108 TR_COMM_MEMB *tr_comm_table_find_idp_memb_origin(TR_COMM_TABLE *ctab, TR_NAME *realm, TR_NAME *comm, TR_NAME *origin)
1109 {
1110   TR_NAME *cur_orig=NULL;
1111   TR_COMM_MEMB *cur=tr_comm_table_find_idp_memb(ctab, realm, comm);
1112   if (cur==NULL)
1113     return NULL; /* no match */
1114
1115   /* had a match for comm/realm; find origin match */
1116   while (cur!=NULL) {
1117     cur_orig=tr_comm_memb_get_origin(cur);
1118     if (((origin==NULL) && (cur_orig==NULL)) ||
1119         ((origin!=NULL) && (cur_orig!=NULL) && (0==tr_name_cmp(origin, cur_orig))))
1120       return cur; /* found a match */
1121     cur=cur->origin_next;
1122   }
1123   return NULL; /* no match */
1124 }
1125
1126 /* find an rp membership from any origin */
1127 TR_COMM_MEMB *tr_comm_table_find_rp_memb(TR_COMM_TABLE *ctab, TR_NAME *realm, TR_NAME *comm)
1128 {
1129   TR_COMM_MEMB *cur=NULL;
1130   TR_RP_REALM *rp_realm=NULL;
1131
1132   for (cur=ctab->memberships; cur!=NULL; cur=cur->next) {
1133     rp_realm=tr_comm_memb_get_rp_realm(cur);
1134     if (rp_realm==NULL)
1135       continue; /* was not an rp */
1136
1137     if ((0==tr_name_cmp(realm, tr_rp_realm_get_id(rp_realm))) &&
1138         (0==tr_name_cmp(comm, tr_comm_get_id(tr_comm_memb_get_comm(cur))))) {
1139       return cur;
1140     }
1141   }
1142   return NULL;
1143 }
1144
1145 /* find an rp membership from a particular origin */
1146 TR_COMM_MEMB *tr_comm_table_find_rp_memb_origin(TR_COMM_TABLE *ctab, TR_NAME *realm, TR_NAME *comm, TR_NAME *origin)
1147 {
1148   TR_NAME *cur_orig=NULL;
1149   TR_COMM_MEMB *cur=tr_comm_table_find_rp_memb(ctab, realm, comm);
1150   if (cur==NULL)
1151     return NULL; /* no match */
1152
1153   /* had a match for comm/realm; find origin match */
1154   while (cur!=NULL) {
1155     cur_orig=tr_comm_memb_get_origin(cur);
1156     if (((origin==NULL) && (cur_orig==NULL)) ||
1157         ((origin!=NULL) && (cur_orig!=NULL) && (0==tr_name_cmp(origin, cur_orig))))
1158       return cur; /* found a match */
1159     cur=cur->origin_next;
1160   }
1161   return NULL; /* no match */
1162 }
1163
1164 TR_COMM *tr_comm_table_find_comm(TR_COMM_TABLE *ctab, TR_NAME *comm_id)
1165 {
1166   return tr_comm_lookup(ctab->comms, comm_id);
1167 }
1168
1169 void tr_comm_table_add_comm(TR_COMM_TABLE *ctab, TR_COMM *new)
1170 {
1171   tr_comm_add(ctab->comms, new);
1172 }
1173
1174 void tr_comm_table_remove_comm(TR_COMM_TABLE *ctab, TR_COMM *comm)
1175 {
1176   tr_comm_remove(ctab->comms, comm);
1177 }
1178
1179 /* how many communities in the table? */
1180 size_t tr_comm_table_size(TR_COMM_TABLE *ctab)
1181 {
1182   size_t count=0;
1183   TR_COMM *this=ctab->comms;
1184   while(this!=NULL) {
1185     this=this->next;
1186     count++;
1187   }
1188   return count;
1189 }
1190
1191
1192 const char *tr_realm_role_to_str(TR_REALM_ROLE role)
1193 {
1194   switch(role) {
1195   case TR_ROLE_IDP:
1196     return "idp";
1197   case TR_ROLE_RP:
1198     return "rp";
1199   default:
1200     return NULL;
1201   }
1202 }
1203
1204 TR_REALM_ROLE tr_realm_role_from_str(const char *s)
1205 {
1206   if (strcmp(s, "idp")==0)
1207     return TR_ROLE_IDP;
1208   if (strcmp(s, "rp")==0)
1209     return TR_ROLE_RP;
1210   return TR_ROLE_UNKNOWN;
1211 }
1212