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